PluginProbe
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions / 110926
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions v110926
260917 260913 260909 260829 260814 260805 110710 110731 110812 110815 110912 110913 110915 110926 110927 111002 111003 111011 111017 111029 111105 111206 111216 111220 120213 All 189 releases
s2member / includes / classes / files-in.inc.php

files-in.inc.php in s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions 110926, at includes/classes/files-in.inc.php

1,284 lines 113.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * File Download routines for s2Member ( inner processing routines ).
4 *
5 * Copyright: © 2009-2011
6 * {@link http://www.websharks-inc.com/ WebSharks, Inc.}
7 * ( coded in the USA )
8 *
9 * Released under the terms of the GNU General Public License.
10 * You should have received a copy of the GNU General Public License,
11 * along with this software. In the main directory, see: /licensing/
12 * If not, see: {@link http://www.gnu.org/licenses/}.
13 *
14 * @package s2Member\Files
15 * @since 3.5
16 */
17 if (realpath (__FILE__) === realpath ($_SERVER["SCRIPT_FILENAME"]))
18 exit("Do not access this file directly.");
19 /**/
20 if (!class_exists ("c_ws_plugin__s2member_files_in"))
21 {
22 /**
23 * File Download routines for s2Member ( inner processing routines ).
24 *
25 * @package s2Member\Files
26 * @since 3.5
27 */
28 class c_ws_plugin__s2member_files_in
29 {
30 /**
31 * Handles Download Access permissions.
32 *
33 * @package s2Member\Files
34 * @since 3.5
35 *
36 * @attaches-to: ``add_action("init");``
37 * @also-called-by: API Function {@link s2Member\API_Functions\s2member_file_download_url()}, w/ ``$create_file_download_url`` param.
38 *
39 * @param array $create_file_download_url Optional. If this function is called directly, we can pass arguments through this array.
40 * Possible array elements: `file_download` *(required)*, `file_download_key`, `file_stream`, `file_inline`, `file_storage`, `file_remote`, `file_ssl`, `file_rewrite`, `file_rewrite_base`, `skip_confirmation`, `url_to_storage_source`, `count_against_user`, `ignore_user`.
41 * @return null|str If called directly with ``$create_file_download_url``, returns a string with the URL, based on configuration.
42 * Else, this function may exit script execution after serving a File Download.
43 */
44 public static function check_file_download_access ($create_file_download_url = FALSE)
45 {
46 eval('foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v;');
47 do_action ("ws_plugin__s2member_before_file_download_access", get_defined_vars ());
48 unset ($__refs, $__v); /* Unset defined __refs, __v. */
49 /**/
50 $_g = !empty ($_GET) ? $_GET : array ();
51 $_g = c_ws_plugin__s2member_utils_strings::trim_deep (stripslashes_deep ($_g));
52 /**/
53 $creating = (is_array ($create = $create_file_download_url)) ? true : false; /* Creating URL? */
54 $serving = (!$creating) ? true : false; /* If NOT creating a File Download URL, we're serving one. */
55 /**/
56 $req["file_download"] = ($creating) ? @$create["file_download"] : @$_g["s2member_file_download"];
57 $req["file_download_key"] = ($creating) ? @$create["file_download_key"] : @$_g["s2member_file_download_key"];
58 /**/
59 $req["file_stream"] = ($creating) ? @$create["file_stream"] : @$_g["s2member_file_stream"];
60 $req["file_inline"] = ($creating) ? @$create["file_inline"] : @$_g["s2member_file_inline"];
61 $req["file_storage"] = ($creating) ? @$create["file_storage"] : @$_g["s2member_file_storage"];
62 $req["file_remote"] = ($creating) ? @$create["file_remote"] : @$_g["s2member_file_remote"];
63 $req["file_ssl"] = ($creating) ? @$create["file_ssl"] : @$_g["s2member_file_ssl"];
64 /**/
65 $req["file_rewrite"] = ($creating) ? @$create["file_rewrite"] : /* N/A. */ null;
66 $req["file_rewrite_base"] = ($creating) ? @$create["file_rewrite_base"] : /* N/A. */ null;
67 /**/
68 $req["skip_confirmation"] = ($creating) ? @$create["skip_confirmation"] : /* N/A. */ null;
69 $req["url_to_storage_source"] = ($creating) ? @$create["url_to_storage_source"] : /* N/A. */ null;
70 $req["count_against_user"] = ($creating) ? @$create["count_against_user"] : /* N/A. */ null;
71 $req["ignore_user"] = ($creating) ? @$create["ignore_user"] : /* N/A. */ null;
72 /**/
73 if ($req["file_download"] && is_string ($req["file_download"]) && ($req["file_download"] = trim ($req["file_download"], "/")))
74 if (strpos ($req["file_download"], "..") === false && strpos (basename ($req["file_download"]), ".") !== 0)
75 {
76 if ($serving) /* We only need this section when/if we're actually serving. */
77 @set_time_limit(0) . @ini_set ("zlib.output_compression", 0) . eval ('while (@ob_end_clean ());');
78 /**/
79 $using_amazon_s3_storage = ((!$req["file_storage"] || strcasecmp ((string)$req["file_storage"], "s3") === 0) && c_ws_plugin__s2member_utils_conds::using_amazon_s3_storage ()) ? true : false;
80 $using_amazon_cf_storage = ((!$req["file_storage"] || strcasecmp ((string)$req["file_storage"], "cf") === 0) && c_ws_plugin__s2member_utils_conds::using_amazon_cf_storage ()) ? true : false;
81 $using_amazon_storage = ($using_amazon_s3_storage || $using_amazon_cf_storage) ? true : false; /* Either/or? */
82 /**/
83 $excluded = apply_filters ("ws_plugin__s2member_check_file_download_access_excluded", false, get_defined_vars ());
84 $valid_file_download_key = ($req["file_download_key"] && is_string ($req["file_download_key"])) ? c_ws_plugin__s2member_files_in::check_file_download_key ($req["file_download"], $req["file_download_key"]) : false;
85 $checking_user = ($excluded || $valid_file_download_key || ($creating && isset ($req["ignore_user"]) && filter_var ($req["ignore_user"], FILTER_VALIDATE_BOOLEAN))) ? false : true; /* Checking User? */
86 $updating_user_counter = (!$checking_user || ($creating && (!isset ($req["count_against_user"]) || !filter_var ($req["count_against_user"], FILTER_VALIDATE_BOOLEAN)))) ? false : true;
87 /**/
88 if (($serving || $creating) && $checking_user) /* In either case, the following routines apply whenever we ARE ``$checking_user``. */
89 {
90 if (!$using_amazon_storage && !file_exists ($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"] . "/" . $req["file_download"]))
91 {
92 if ($serving) /* We only need this section when/if we're actually serving. */
93 status_header(404) . exit (_x ("404: Sorry, file not found. Please contact Support for assistance.", "s2member-front", "s2member"));
94 /**/
95 else /* Else return false. */
96 return false;
97 }
98 /**/
99 else if ($req["file_download_key"] && is_string ($req["file_download_key"]) && !$valid_file_download_key)
100 {
101 if ($serving) /* We only need this section when/if we're actually serving. */
102 status_header(503) . exit (_x ("503 ( Invalid Key ): Sorry, your access to this file has expired. Please contact Support for assistance.", "s2member-front", "s2member"));
103 /**/
104 else /* Else return false. */
105 return false;
106 }
107 /**/
108 else if ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page"] || ($file_downloads_enabled = $min_level_4_downloads = c_ws_plugin__s2member_files::min_level_4_downloads ()) === false)
109 {
110 if ($serving) /* We only need remote functionality when/if we're actually serving. */
111 if (!has_filter ("ws_plugin__s2member_check_file_download_access_user", "c_ws_plugin__s2member_files_in::check_file_remote_authorization"))
112 add_filter ("ws_plugin__s2member_check_file_download_access_user", "c_ws_plugin__s2member_files_in::check_file_remote_authorization", 10, 2);
113 /**/
114 if ($creating) /* We only need remote functionality when/if we're actually serving. */
115 if (has_filter ("ws_plugin__s2member_check_file_download_access_user", "c_ws_plugin__s2member_files_in::check_file_remote_authorization"))
116 remove_filter ("ws_plugin__s2member_check_file_download_access_user", "c_ws_plugin__s2member_files_in::check_file_remote_authorization", 10, 2);
117 /**/
118 if ((isset ($file_downloads_enabled, $min_level_4_downloads) && $file_downloads_enabled === false) || ($file_downloads_enabled = $min_level_4_downloads = c_ws_plugin__s2member_files::min_level_4_downloads ()) === false)
119 {
120 if ($serving) /* We only need this section when/if we're actually serving. */
121 status_header(503) . exit (_x ("503: Sorry, File Downloads are NOT enabled yet. Please contact Support for assistance. If you are the site owner, please configure: `s2Member -> Download Options -> Basic Download Restrictions`.", "s2member-front", "s2member"));
122 /**/
123 else /* Else return false. */
124 return false;
125 }
126 /**/
127 else if (!is_object ($user = apply_filters ("ws_plugin__s2member_check_file_download_access_user", ((is_user_logged_in ()) ? wp_get_current_user () : false), get_defined_vars ())) || empty ($user->ID) || !($user_id = $user->ID))
128 {
129 if (preg_match ("/^access[_\-]s2member[_\-]level([0-9]+)\//", $req["file_download"], $m))
130 {
131 $level_req = $m[1]; /* Memebership Level required. */
132 /**/
133 if ($serving) /* We only need this section when/if we're actually serving. */
134 wp_redirect (add_query_arg (urlencode_deep (array ("s2member_seeking" => "file-" . $req["file_download"], "s2member_level_req" => $level_req)), get_page_link ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page"])), apply_filters ("ws_plugin__s2member_content_redirect_status", 301, get_defined_vars ())) . exit ();
135 /**/
136 else /* Else return false. */
137 return false;
138 }
139 /**/
140 else if (preg_match ("/^access[_\-]s2member[_\-]ccap[_\-](.+?)\//", $req["file_download"], $m))
141 {
142 $ccap_req = preg_replace ("/-/", "_", $m[1]); /* Custom Capability required. */
143 /**/
144 if ($serving) /* We only need this section when/if we're actually serving. */
145 wp_redirect (add_query_arg (urlencode_deep (array ("s2member_seeking" => "file-" . $req["file_download"], "s2member_ccap_req" => $ccap_req)), get_page_link ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page"])), apply_filters ("ws_plugin__s2member_content_redirect_status", 301, get_defined_vars ())) . exit ();
146 /**/
147 else /* Else return false. */
148 return false;
149 }
150 /**/
151 else if ($serving) /* We only need this section when/if we're actually serving. */
152 wp_redirect (add_query_arg (urlencode_deep (array ("s2member_seeking" => "file-" . $req["file_download"], "s2member_level_req" => (string)$min_level_4_downloads)), get_page_link ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page"])), apply_filters ("ws_plugin__s2member_content_redirect_status", 301, get_defined_vars ())) . exit ();
153 /**/
154 else /* Else return false. */
155 return false;
156 }
157 /**/
158 else if (!is_array ($file_downloads = c_ws_plugin__s2member_files::user_downloads ($user)) || (!$user->has_cap ("administrator") && (!$file_downloads["allowed"] || !$file_downloads["allowed_days"])))
159 {
160 if ($serving) /* We only need this section when/if we're actually serving. */
161 wp_redirect (add_query_arg (urlencode_deep (array ("s2member_seeking" => "file-" . $req["file_download"])), get_page_link ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["file_download_limit_exceeded_page"])), apply_filters ("ws_plugin__s2member_content_redirect_status", 301, get_defined_vars ())) . exit ();
162 /**/
163 else /* Else return false. */
164 return false;
165 }
166 /**/
167 else if (preg_match ("/^access[_\-]s2member[_\-]level([0-9]+)\//", $req["file_download"], $m) && strlen ($level_req = $m[1]) && !$user->has_cap ("access_s2member_level" . $level_req))
168 {
169 if ($serving) /* We only need this section when/if we're actually serving. */
170 wp_redirect (add_query_arg (urlencode_deep (array ("s2member_seeking" => "file-" . $req["file_download"], "s2member_level_req" => $level_req)), get_page_link ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page"])), apply_filters ("ws_plugin__s2member_content_redirect_status", 301, get_defined_vars ())) . exit ();
171 /**/
172 else /* Else return false. */
173 return false;
174 }
175 /**/
176 else if (preg_match ("/^access[_\-]s2member[_\-]ccap[_\-](.+?)\//", $req["file_download"], $m) && strlen ($ccap_req = preg_replace ("/-/", "_", $m[1])) && !$user->has_cap ("access_s2member_ccap_" . $ccap_req))
177 {
178 if ($serving) /* We only need this section when/if we're actually serving. */
179 wp_redirect (add_query_arg (urlencode_deep (array ("s2member_seeking" => "file-" . $req["file_download"], "s2member_ccap_req" => $ccap_req)), get_page_link ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page"])), apply_filters ("ws_plugin__s2member_content_redirect_status", 301, get_defined_vars ())) . exit ();
180 /**/
181 else /* Else return false. */
182 return false;
183 }
184 /**/
185 else if ($serving || $creating) /* In either case, the following routines apply. */
186 {
187 $already_downloaded = $streaming_variation_already_downloaded = false;
188 $previous_file_downloads = 0; /* Here we're going to count how many downloads they have. */
189 $max_days_logged = c_ws_plugin__s2member_files::max_download_period (); /* Max period/days. */
190 $file_download_access_log = (array)get_user_option ("s2member_file_download_access_log", $user_id);
191 $file_download_access_arc = (array)get_user_option ("s2member_file_download_access_arc", $user_id);
192 $streaming_file_extns = c_ws_plugin__s2member_utils_strings::preg_quote_deep ($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["streaming_file_extns"], "/");
193 $streaming_variations = /* Only count one streaming media file variation. */ "/\.(" . implode ("|", $streaming_file_extns) . ")$/i";
194 /**/
195 foreach ($file_download_access_log as $file_download_access_log_entry_key => $file_download_access_log_entry)
196 {
197 if (strtotime ($file_download_access_log_entry["date"]) < strtotime ("-" . $max_days_logged . " days"))
198 {
199 unset($file_download_access_log[$file_download_access_log_entry_key]);
200 $file_download_access_arc[] = $file_download_access_log_entry;
201 }
202 else if (strtotime ($file_download_access_log_entry["date"]) >= strtotime ("-" . $file_downloads["allowed_days"] . " days"))
203 {
204 $previous_file_downloads++; /* A previous file that counts. */
205 /**/
206 if ($file_download_access_log_entry["file"] === $req["file_download"]) /* Already downloaded this file? Or another streaming variation? */
207 $already_downloaded = true; /* Already downloaded this file? Or another streaming variation? */
208 /**/
209 else if (preg_replace ($streaming_variations, "", $file_download_access_log_entry["file"]) === preg_replace ($streaming_variations, "", $req["file_download"]))
210 $already_downloaded = $streaming_variation_already_downloaded = true;
211 }
212 }
213 /**/
214 if (!$already_downloaded && !$streaming_variation_already_downloaded && !$user->has_cap ("administrator") && $previous_file_downloads >= $file_downloads["allowed"])
215 {
216 if ($serving) /* We only need this section when/if we're actually serving. */
217 wp_redirect (add_query_arg (urlencode_deep (array ("s2member_seeking" => "file-" . $req["file_download"])), get_page_link ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["file_download_limit_exceeded_page"])), apply_filters ("ws_plugin__s2member_content_redirect_status", 301, get_defined_vars ())) . exit ();
218 /**/
219 else /* Else return false. */
220 return false;
221 }
222 /**/
223 else if (!$already_downloaded && !$streaming_variation_already_downloaded)
224 $file_download_access_log[] = array ("date" => date ("Y-m-d"), "file" => $req["file_download"]);
225 /**/
226 if ($updating_user_counter) /* By default, we do NOT update the counter when a URL is being created; but this behavior can be modified. */
227 {
228 update_user_option ($user_id, "s2member_file_download_access_arc", c_ws_plugin__s2member_utils_arrays::array_unique ($file_download_access_arc));
229 update_user_option ($user_id, "s2member_file_download_access_log", c_ws_plugin__s2member_utils_arrays::array_unique ($file_download_access_log));
230 }
231 }
232 }
233 }
234 else /* Otherwise, we're either NOT ``$checking_user``; or permission was granted with a valid File Download Key. */
235 {
236 if (!$using_amazon_storage && !file_exists ($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"] . "/" . $req["file_download"]))
237 {
238 if ($serving) /* We only need this section when/if we're actually serving. */
239 status_header(404) . exit (_x ("404: Sorry, file not found. Please contact Support for assistance.", "s2member-front", "s2member"));
240 /**/
241 else /* Else return false. */
242 return false;
243 }
244 }
245 /**/
246 if ($serving || $creating) /* In either case, the following routines apply. */
247 {
248 $basename = basename ($req["file_download"]);
249 $mimetypes = parse_ini_file (dirname (dirname (dirname (__FILE__))) . "/includes/mime-types.ini");
250 $extension = strtolower (substr ($req["file_download"], strrpos ($req["file_download"], ".") + 1));
251 /**/
252 $key = ($req["file_download_key"] && is_string ($req["file_download_key"])) ? $req["file_download_key"] : false;
253 /**/
254 $stream = (isset ($req["file_stream"])) ? filter_var ($req["file_stream"], FILTER_VALIDATE_BOOLEAN) : ((in_array ($extension, preg_split ("/[\r\n\t\s;,]+/", $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["file_download_stream_extensions"]))) ? true : false);
255 $inline = (!$stream && isset ($req["file_inline"])) ? filter_var ($req["file_inline"], FILTER_VALIDATE_BOOLEAN) : (($stream || in_array ($extension, preg_split ("/[\r\n\t\s;,]+/", $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["file_download_inline_extensions"]))) ? true : false);
256 $ssl = (isset ($req["file_ssl"])) ? filter_var ($req["file_ssl"], FILTER_VALIDATE_BOOLEAN) : ((is_ssl ()) ? true : false);
257 $storage = ($req["file_storage"] && is_string ($req["file_storage"])) ? strtolower ($req["file_storage"]) : false;
258 $remote = (isset ($req["file_remote"])) ? filter_var ($req["file_remote"], FILTER_VALIDATE_BOOLEAN) : false;
259 /**/
260 $rewrite_base_guess = dirname ($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["dir_url"]) . "/" . c_ws_plugin__s2member_utils_dirs::basename_dir_app_data ($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"]);
261 $rewrite_base = ($req["file_rewrite_base"] && is_string ($req["file_rewrite_base"])) ? $req["file_rewrite_base"] : false;
262 $rewrite = $rewriting = (!$rewrite_base && isset ($req["file_rewrite"])) ? filter_var ($req["file_rewrite"], FILTER_VALIDATE_BOOLEAN) : (($rewrite_base) ? true : false);
263 /**/
264 $skip_confirmation = (isset ($req["skip_confirmation"])) ? filter_var ($req["skip_confirmation"], FILTER_VALIDATE_BOOLEAN) : false;
265 $url_to_storage_source = (isset ($req["url_to_storage_source"])) ? filter_var ($req["url_to_storage_source"], FILTER_VALIDATE_BOOLEAN) : false;
266 /**/
267 $pathinfo = (!$using_amazon_storage) ? pathinfo (($file = $GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"] . "/" . $req["file_download"])) : array ();
268 $mimetype = ($mimetypes[$extension]) ? $mimetypes[$extension] : "application/octet-stream";
269 $length = (!$using_amazon_storage && $file) ? filesize ($file) : -1;
270 /**/
271 eval('foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v;');
272 do_action ("ws_plugin__s2member_during_file_download_access", get_defined_vars ());
273 unset ($__refs, $__v); /* Unset defined __refs, __v. */
274 /**/
275 if ($using_amazon_s3_storage && ($serving || ($creating && $url_to_storage_source)))
276 {
277 if ($serving) /* We only need this section when/if we're actually serving. */
278 wp_redirect(c_ws_plugin__s2member_files_in::amazon_s3_url ($req["file_download"], $stream, $inline, $ssl, $basename, $mimetype)) . exit ();
279 /**/
280 else /* Else return File Download URL. */
281 return apply_filters ("ws_plugin__s2member_file_download_access_url", c_ws_plugin__s2member_files_in::amazon_s3_url ($req["file_download"], $stream, $inline, $ssl, $basename, $mimetype), get_defined_vars ());
282 }
283 /**/
284 else if ($using_amazon_cf_storage && ($serving || ($creating && $url_to_storage_source)))
285 {
286 if ($serving) /* We only need this section when/if we're actually serving. */
287 wp_redirect(c_ws_plugin__s2member_files_in::amazon_cf_url ($req["file_download"], $stream, $inline, $ssl, $basename, $mimetype)) . exit ();
288 /**/
289 else /* Else return File Download URL. */
290 return apply_filters ("ws_plugin__s2member_file_download_access_url", c_ws_plugin__s2member_files_in::amazon_cf_url ($req["file_download"], $stream, $inline, $ssl, $basename, $mimetype), get_defined_vars ());
291 }
292 /**/
293 else if ($creating && $rewriting) /* Creating a rewrite URL, pointing to local storage. */
294 {
295 $url = ($rewrite_base) ? rtrim ($rewrite_base, "/") : rtrim ($rewrite_base_guess, "/");
296 $url .= (isset ($req["file_download_key"])) ? (($key) ? "/s2member-file-download-key-" . $key : "") : "";
297 $url .= (isset ($req["file_stream"])) ? (($stream) ? "/s2member-file-stream" : "/s2member-file-stream-no") : "";
298 $url .= (isset ($req["file_inline"])) ? (($inline) ? "/s2member-file-inline" : "/s2member-file-inline-no") : "";
299 $url .= (isset ($req["file_storage"])) ? (($storage) ? "/s2member-file-storage-" . $storage : "") : "";
300 $url .= (isset ($req["file_remote"])) ? (($remote) ? "/s2member-file-remote" : "/s2member-file-remote-no") : "";
301 $url .= (isset ($req["skip_confirmation"])) ? (($skip_confirmation) ? "/s2member-skip-confirmation" : "/s2member-skip-confirmation-no") : "";
302 /**/
303 $url = $url . "/" . $req["file_download"]; /* File Download Access URL via `mod_rewrite` functionality. */
304 $url = ($ssl) ? preg_replace ("/^https?/", "https", $url) : preg_replace ("/^https?/", "http", $url);
305 /**/
306 return apply_filters ("ws_plugin__s2member_file_download_access_url", $url, get_defined_vars ());
307 }
308 /**/
309 else if ($creating) /* Else we're creating a URL w/ a query-string, pointing to local storage. */
310 {
311 $url = (isset ($req["file_download_key"])) ? (($key) ? "&s2member_file_download_key=" . urlencode ($key) : "") : "";
312 $url .= (isset ($req["file_stream"])) ? (($stream) ? "&s2member_file_stream=yes" : "&s2member_file_stream=no") : "";
313 $url .= (isset ($req["file_inline"])) ? (($inline) ? "&s2member_file_inline=yes" : "&s2member_file_inline=no") : "";
314 $url .= (isset ($req["file_storage"])) ? (($storage) ? "&s2member_file_storage=" . urlencode ($storage) : "") : "";
315 $url .= (isset ($req["file_remote"])) ? (($remote) ? "&s2member_file_remote=yes" : "&s2member_file_remote=no") : "";
316 $url .= (isset ($req["skip_confirmation"])) ? (($skip_confirmation) ? "&s2member_skip_confirmation=yes" : "&s2member_skip_confirmation=no") : "";
317 /**/
318 $url = site_url ("/?" . ltrim ($url . "&s2member_file_download=/" . urlencode ($req["file_download"]), "&"));
319 $url = ($ssl) ? preg_replace ("/^https?/", "https", $url) : preg_replace ("/^https?/", "http", $url);
320 /**/
321 return apply_filters ("ws_plugin__s2member_file_download_access_url", $url, get_defined_vars ());
322 }
323 /**/
324 else /* Else, ``if ($serving)`` , use local storage option (default). */
325 {
326 status_header(200);
327 /**/
328 header("Accept-Ranges: none");
329 header("Content-Encoding: none");
330 header("Content-Type: " . $mimetype);
331 header("Expires: " . gmdate ("D, d M Y H:i:s", strtotime ("-1 week")) . " GMT");
332 header("Last-Modified: " . gmdate ("D, d M Y H:i:s") . " GMT");
333 header("Cache-Control: no-cache, must-revalidate, max-age=0");
334 header ("Cache-Control: post-check=0, pre-check=0", false);
335 header("Pragma: no-cache");
336 /**/
337 header('Content-Disposition: ' . (($inline) ? "inline" : "attachment") . '; filename="' . $basename . '"');
338 /**/
339 if ($length && apply_filters ("ws_plugin__s2member_chunk_file_downloads", true, get_defined_vars ()) && is_resource ($resource = fopen ($file, "rb")))
340 {
341 $_chunk_size = apply_filters ("ws_plugin__s2member_chunk_file_downloads_w_chunk_size", 2097152, get_defined_vars ());
342 $_chunk_w_content_length = (stripos (PHP_OS, "win") === 0 && (!function_exists ("apache_get_version") || apache_get_version () === false)) ? false : true;
343 /**/
344 /* Windows® IIS doesn't seem to like it when both `Content-Length` and `Transfer-Encoding: chunked` are sent together. */
345 if (apply_filters ("ws_plugin__s2member_chunk_file_downloads_w_content_length", $_chunk_w_content_length, get_defined_vars ()))
346 header("Content-Length: " . $length);
347 /**/
348 header("Transfer-Encoding: chunked"); /* Uses `Transfer-Encoding: chunked` for simulated streaming. */
349 /**/
350 while (!feof ($resource) && ($chunk_size = strlen ($data = fread ($resource, $_chunk_size))))
351 eval('echo dechex ($chunk_size) . "\r\n". $data . "\r\n"; @flush ();');
352 /**/
353 fclose($resource) . exit ("0\r\n\r\n");
354 }
355 else if ($length) /* Else, use: ``file_get_contents()``. */
356 {
357 header("Content-Length: " . $length) . exit (file_get_contents ($file));
358 }
359 else /* Else, we have an empty file with no length. */
360 {
361 header("Content-Length: 0") . exit ();
362 }
363 }
364 }
365 }
366 /**/
367 else if ($serving && $req["file_download"]) /* We only need this section when/if we're actually serving. */
368 status_header(503) . exit (_x ("503: Access denied. Invalid File Download specs.", "s2member-front", "s2member"));
369 /**/
370 else if ($creating) /* We only need this section when/if we're creating a URL. */
371 return false;
372 /**/
373 do_action ("ws_plugin__s2member_after_file_download_access", get_defined_vars ());
374 /**/
375 return ($creating) ? false : null; /* If creating, false. */
376 }
377 /**
378 * Generates a File Download URL for access to a file protected by s2Member.
379 *
380 * @package s2Member\Files
381 * @since 110926
382 *
383 * @param array $config Required. This is an array of configuration options associated with permissions being checked against the current User/Member; and also the actual URL generated by this routine.
384 * Possible ``$config`` array elements: `file_download` *(required)*, `file_download_key`, `file_stream`, `file_inline`, `file_storage`, `file_remote`, `file_ssl`, `file_rewrite`, `file_rewrite_base`, `skip_confirmation`, `url_to_storage_source`, `count_against_user`, `ignore_user`.
385 * @return str A File Download URL string on success; or an array on success, with elements `streamer`, `file`, `url` when/if ``$get_streamer_array`` is true; else false on any type of failure.
386 * Note: by default, this function DOES consider the current User/Member. If `ignore_user` is NOT set, or is set to false, this function MAY very well return false, when/if the current User/Member is NOT allowed access to the `file_download`, based on configuration.
387 */
388 public static function create_file_download_url ($config = FALSE, $get_streamer_array = FALSE)
389 {
390 eval('foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v;');
391 do_action ("ws_plugin__s2member_before_create_file_download_url", get_defined_vars ()); /* Be VERY careful, if you use this Hook. */
392 unset ($__refs, $__v); /* Unset defined __refs, __v. */
393 /**/
394 $config = (is_array ($config)) ? $config : array (); /* This absolutely MUST be an array. */
395 /**/
396 $config["file_download"] = (isset ($config["file_download"]) && is_string ($config["file_download"])) ? trim ($config["file_download"], "/") : @$config["file_download"];
397 $config["file_download_key"] = (isset ($config["file_download"]) && is_string ($config["file_download"]) && !empty ($config["file_download_key"])) /* Automatically generate a File Download Key here? */
398 ? c_ws_plugin__s2member_files::file_download_key ($config["file_download"], ((in_array ($config["file_download_key"], array ("ip-forever", "universal", "cache-compatible"))) ? $config["file_download_key"] : false)) : @$config["file_download_key"];
399 /**/
400 $config["url_to_storage_source"] = /* Force a streaming URL here via ``$get_streamer_array``? */ ($get_streamer_array) ? true : @$config["url_to_storage_source"];
401 $config["file_stream"] = /* Force a streaming URL here via ``$get_streamer_array``? */ ($get_streamer_array) ? true : @$config["file_stream"];
402 /**/
403 if (($_url = c_ws_plugin__s2member_files_in::check_file_download_access (($create_file_download_url = $config))) /* Successfully created a URL to the file? */)
404 {
405 eval('foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v;');
406 do_action ("ws_plugin__s2member_during_create_file_download_url", get_defined_vars ()); /* Be VERY careful, if you use this Hook. */
407 unset ($__refs, $__v); /* Unset defined __refs, __v. */
408 /**/
409 $extension = strtolower (substr ($config["file_download"], strrpos ($config["file_download"], ".") + 1));
410 $streaming = (isset ($config["file_stream"])) ? filter_var ($config["file_stream"], FILTER_VALIDATE_BOOLEAN) : ((in_array ($extension, preg_split ("/[\r\n\t\s;,]+/", $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["file_download_stream_extensions"]))) ? true : false);
411 $ssl = (isset ($config["file_ssl"])) ? filter_var ($config["file_ssl"], FILTER_VALIDATE_BOOLEAN) : ((is_ssl ()) ? true : false);
412 /**/
413 if ($get_streamer_array && $streaming && ($cfx = "/cfx/st") && ($cfx_pos = strpos ($_url, $cfx)) !== false && ($streamer = substr ($_url, 0, $cfx_pos + strlen ($cfx))) && ($url = c_ws_plugin__s2member_files_in::check_file_download_access (array_merge ($config, array ("file_stream" => false, "ignore_user" => true)))))
414 $return = array ("streamer" => $streamer, "file" => preg_replace ("/^" . preg_quote ($streamer, "/") . "\//", "", $_url), "url" => preg_replace ("/^.+?\:/", (($ssl) ? "https:" : "http:"), $url));
415 /**/
416 else if ($get_streamer_array && $streaming && is_array ($ups = @parse_url ($_url)) && ($streamer = $ups["scheme"] . "://" . $ups["host"] . ((!empty ($ups["port"])) ? ":" . $ups["port"] : "")) && ($url = c_ws_plugin__s2member_files_in::check_file_download_access (array_merge ($config, array ("file_stream" => false, "ignore_user" => true)))))
417 $return = array ("streamer" => $streamer, "file" => preg_replace ("/^" . preg_quote ($streamer, "/") . "\//", "", $_url), "url" => preg_replace ("/^.+?\:/", (($ssl) ? "https:" : "http:"), $url));
418 /**/
419 else if ($get_streamer_array) /* Else, we MUST return false here, unable to acquire streamer/file. */
420 $return = false; /* We MUST return false here, unable to acquire streamer. */
421 /**/
422 else /* Else return URL string ( ``$get_streamer_array`` is false ). */
423 $return = $_url; /* Else return URL string. */
424 }
425 /**/
426 return apply_filters ("ws_plugin__s2member_create_file_download_url", ((isset ($return)) ? $return : false), get_defined_vars ());
427 }
428 /**
429 * Checks Header Authorization for Remote File Downloads.
430 *
431 * @package s2Member\Files
432 * @since 110926
433 *
434 * @attaches-to: ``add_filter("ws_plugin__s2member_check_file_download_access_user");``
435 *
436 * @param obj $user Expects a WP_User object passed in by the Filter.
437 * @return obj A `WP_User` object, possibly obtained through Header Authorization.
438 */
439 public static function check_file_remote_authorization ($user = FALSE)
440 {
441 eval('foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v;');
442 do_action ("ws_plugin__s2member_before_check_file_remote_authorization", get_defined_vars ());
443 unset ($__refs, $__v); /* Unset defined __refs, __v. */
444 /**/
445 $_g = c_ws_plugin__s2member_utils_strings::trim_deep (stripslashes_deep (((!empty ($_GET)) ? $_GET : array ())));
446 /**/
447 if (!is_object ($user) && isset ($_g["s2member_file_remote"]) && filter_var ($_g["s2member_file_remote"], FILTER_VALIDATE_BOOLEAN))
448 {
449 do_action ("ws_plugin__s2member_during_check_file_remote_authorization_before", get_defined_vars ());
450 /**/
451 if (empty ($_SERVER["PHP_AUTH_USER"]) || empty ($_SERVER["PHP_AUTH_PW"]) || !user_pass_ok ($_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"]))
452 {
453 header('WWW-Authenticate: Basic realm="' . _x ("Members Only", "s2member-front", "s2member") . '"');
454 status_header(401); /* Unauthorized status header. */
455 exit(_x ("Access Denied", "s2member-front", "s2member"));
456 }
457 else if (is_object ($_user = new WP_User ($_SERVER["PHP_AUTH_USER"])) && !empty ($_user->ID))
458 $user = $_user; /* Now assign ``$user``. */
459 /**/
460 do_action ("ws_plugin__s2member_during_check_file_remote_authorization_after", get_defined_vars ());
461 }
462 /**/
463 return apply_filters ("ws_plugin__s2member_check_file_remote_authorization", $user, get_defined_vars ());
464 }
465 /**
466 * Checks a File Download Key for validity.
467 *
468 * @package s2Member\Files
469 * @since 110926
470 *
471 * @param str $file Input File Download to validate.
472 * @param str $key Input File Download Key to validate.
473 * @return bool True if valid, else false.
474 */
475 public static function check_file_download_key ($file = FALSE, $key = FALSE)
476 {
477 eval('foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v;');
478 do_action ("_ws_plugin__s2member_before_check_file_download_key", get_defined_vars ());
479 unset ($__refs, $__v); /* Unset defined __refs, __v. */
480 /**/
481 if ($file && is_string ($file) && ($file = trim ($file, "/")) && $key && is_string ($key)) /* Requirements to properly validate. */
482 {
483 if ($key === c_ws_plugin__s2member_files::file_download_key ($file) || $key === c_ws_plugin__s2member_files::file_download_key ("/" . $file))
484 $valid = true; /* File Download Key is valid. */
485 else if ($key === c_ws_plugin__s2member_files::file_download_key ($file, "ip-forever") || $key === c_ws_plugin__s2member_files::file_download_key ("/" . $file, "ip-forever"))
486 $valid = true; /* File Download Key is valid. */
487 else if ($key === c_ws_plugin__s2member_files::file_download_key ($file, "universal") || $key === c_ws_plugin__s2member_files::file_download_key ("/" . $file, "universal"))
488 $valid = true; /* File Download Key is valid. */
489 }
490 /**/
491 return apply_filters ("ws_plugin__s2member_check_file_download_key", ((isset ($valid) && $valid) ? true : false), get_defined_vars ());
492 }
493 /**
494 * Creates an Amazon® S3 HMAC-SHA1 signature.
495 *
496 * @package s2Member\Files
497 * @since 110524RC
498 *
499 * @param str $string Input string/data, to be signed by this routine.
500 * @return str An HMAC-SHA1 signature for Amazon® S3.
501 */
502 public static function amazon_s3_sign ($string = FALSE)
503 {
504 $s3c["secret_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_secret_key"];
505 /**/
506 $s3_signature = str_pad (((strlen ($s3c["secret_key"]) > 64) ? pack ('H*', sha1 ($s3c["secret_key"])) : $s3c["secret_key"]), 64, chr (0x00));
507 return pack ('H*', sha1 (($s3_signature ^ str_repeat (chr (0x5c), 64)) . pack ('H*', sha1 (($s3_signature ^ str_repeat (chr (0x36), 64)) . (string)$string))));
508 }
509 /**
510 * Creates an Amazon® S3 HMAC-SHA1 signature URL.
511 *
512 * @package s2Member\Files
513 * @since 110926
514 *
515 * @param str $file Input file path, to be signed by this routine.
516 * @param bool $stream Is this resource file to be served as streaming media?
517 * @param bool $inline Is this resource file to be served inline, or no?
518 * @param bool $ssl Is this resource file to be served via SSL, or no?
519 * @param str $basename The absolute basename of the resource file.
520 * @param str $mimetype The MIME content-type of the resource file.
521 * @return str An HMAC-SHA1 signature URL for Amazon® S3.
522 */
523 public static function amazon_s3_url ($file = FALSE, $stream = FALSE, $inline = FALSE, $ssl = FALSE, $basename = FALSE, $mimetype = FALSE)
524 {
525 $file = trim ((string)$file, "/"); /* Trim / force string. */
526 /**/
527 foreach ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"] as $option => $option_value)
528 if (preg_match ("/^amazon_s3_files_/", $option) && ($option = preg_replace ("/^amazon_s3_files_/", "", $option)))
529 $s3c[$option] = $option_value;
530 /**/
531 $s3c["expires"] = strtotime ("+" . apply_filters ("ws_plugin__s2member_amazon_s3_file_expires_time", "30 seconds", get_defined_vars ()));
532 /**/
533 $s3_file = add_query_arg (urlencode_deep (array ("response-cache-control" => ($s3_cache_control = "no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0"), "response-content-disposition" => ($s3_content_disposition = (((bool)$inline) ? "inline" : "attachment") . '; filename="' . (string)$basename . '"'), "response-content-type" => ($s3_content_type = (string)$mimetype), "response-expires" => ($s3_expires = gmdate ("D, d M Y H:i:s", strtotime ("-1 week")) . " GMT"))), "/" . $file);
534 $s3_raw_file = add_query_arg (array ("response-cache-control" => $s3_cache_control, "response-content-disposition" => $s3_content_disposition, "response-content-type" => $s3_content_type, "response-expires" => $s3_expires), "/" . $file);
535 $s3_signature = base64_encode (c_ws_plugin__s2member_files_in::amazon_s3_sign ("GET\n\n\n" . $s3c["expires"] . "\n" . "/" . $s3c["bucket"] . $s3_raw_file));
536 /**/
537 $s3_url = ((strtolower ($s3c["bucket"]) !== $s3c["bucket"])) ? "http" . (($ssl) ? "s" : "") . "://s3.amazonaws.com/" . $s3c["bucket"] . $s3_file : "http" . (($ssl) ? "s" : "") . "://" . $s3c["bucket"] . ".s3.amazonaws.com" . $s3_file;
538 return add_query_arg (urlencode_deep (array ("AWSAccessKeyId" => $s3c["access_key"], "Expires" => $s3c["expires"], "Signature" => $s3_signature)), $s3_url);
539 }
540 /**
541 * Auto-configures an Amazon® S3 Bucket's ACLs.
542 *
543 * @package s2Member\Files
544 * @since 110926
545 *
546 * @return array Array containing a true `success` element on success, else a failure array.
547 * Failure array will contain a failure `code`, and a failure `message`.
548 */
549 public static function amazon_s3_auto_configure_acls ()
550 {
551 foreach ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"] as $option => $option_value)
552 if (preg_match ("/^amazon_s3_files_/", $option) && ($option = preg_replace ("/^amazon_s3_files_/", "", $option)))
553 $s3c[$option] = $option_value;
554 /**/
555 $cfc["distros_s3_access_id"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_cf_files_distros_s3_access_id"];
556 /**/
557 if ($s3c["bucket"] && $s3c["access_key"] && $s3c["secret_key"]) /* Must have Amazon® S3 Bucket/Keys. */
558 {
559 $s3_date = gmdate ("D, d M Y H:i:s") . " GMT";
560 $s3_location = ((strtolower ($s3c["bucket"]) !== $s3c["bucket"])) ? "/" . $s3c["bucket"] . "/?acl" : "/?acl";
561 $s3_domain = ((strtolower ($s3c["bucket"]) !== $s3c["bucket"])) ? "s3.amazonaws.com" : $s3c["bucket"] . ".s3.amazonaws.com";
562 $s3_signature = base64_encode (c_ws_plugin__s2member_files_in::amazon_s3_sign ("GET\n\n\n" . $s3_date . "\n/" . $s3c["bucket"] . "/?acl"));
563 $s3_args = array ("method" => "GET", "headers" => array ("Host" => $s3_domain, "Date" => $s3_date, "Authorization" => "AWS " . $s3c["access_key"] . ":" . $s3_signature));
564 /**/
565 if (($s3_response = c_ws_plugin__s2member_utils_urls::remote ("https://" . $s3_domain . $s3_location, false, array_merge ($s3_args, array ("timeout" => 20)), "array")) && $s3_response["code"] === 200)
566 {
567 if (preg_match ("/\<Owner\>(.+?)\<\/Owner\>/is", $s3_response["body"], $s3_owner_tag) && preg_match ("/\<ID\>(.+?)\<\/ID\>/is", $s3_owner_tag[1], $s3_owner_id_tag) && (preg_match ("/\<DisplayName\>(.*?)\<\/DisplayName\>/is", $s3_owner_tag[1], $s3_owner_display_name_tag) || ($s3_owner_display_name_tag = array ("-", "Owner"))))
568 {
569 $s3_owner = array ("access_id" => trim ($s3_owner_id_tag[1]), "display_name" => trim ($s3_owner_display_name_tag[1]));
570 $s3_acls_xml = '<AccessControlPolicy><Owner><ID>' . esc_html ($s3_owner["access_id"]) . '</ID><DisplayName>' . esc_html ($s3_owner["display_name"]) . '</DisplayName></Owner><AccessControlList><Grant><Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser"><ID>' . esc_html ($s3_owner["access_id"]) . '</ID><DisplayName>' . esc_html ($s3_owner["display_name"]) . '</DisplayName></Grantee><Permission>FULL_CONTROL</Permission></Grant>' . (($cfc["distros_s3_access_id"]) ? '<Grant><Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser"><ID>' . esc_html ($cfc["distros_s3_access_id"]) . '</ID><DisplayName>s2Member/CloudFront</DisplayName></Grantee><Permission>READ</Permission></Grant>' : '') . '</AccessControlList></AccessControlPolicy>';
571 $s3_signature = base64_encode (c_ws_plugin__s2member_files_in::amazon_s3_sign ("PUT\n\napplication/xml\n" . $s3_date . "\n/" . $s3c["bucket"] . "/?acl"));
572 $s3_args = array ("method" => "PUT", "body" => $s3_acls_xml, "headers" => array ("Host" => $s3_domain, "Content-Type" => "application/xml", "Date" => $s3_date, "Authorization" => "AWS " . $s3c["access_key"] . ":" . $s3_signature));
573 /**/
574 if (($s3_response = c_ws_plugin__s2member_utils_urls::remote ("https://" . $s3_domain . $s3_location, false, array_merge ($s3_args, array ("timeout" => 20)), "array")) && $s3_response["code"] === 200)
575 {
576 $s3_location = ((strtolower ($s3c["bucket"]) !== $s3c["bucket"])) ? "/" . $s3c["bucket"] . "/?policy" : "/?policy";
577 $s3_policy_json = '{"Version":"2008-10-17","Id":"' . md5 ("s2Member/CloudFront") . '","Statement":[{"Sid":"s2Member/CloudFront","Effect":"Allow","Principal":{"CanonicalUser":"' . c_ws_plugin__s2member_utils_strings::esc_dq ($cfc["distros_s3_access_id"]) . '"},"Action":"s3:GetObject","Resource":"arn:aws:s3:::' . c_ws_plugin__s2member_utils_strings::esc_dq ($s3c["bucket"]) . '/*"}]}';
578 $s3_signature = base64_encode (c_ws_plugin__s2member_files_in::amazon_s3_sign ("PUT\n\napplication/json\n" . $s3_date . "\n/" . $s3c["bucket"] . "/?policy"));
579 $s3_args = array ("method" => "PUT", "body" => $s3_policy_json, "headers" => array ("Host" => $s3_domain, "Content-Type" => "application/json", "Date" => $s3_date, "Authorization" => "AWS " . $s3c["access_key"] . ":" . $s3_signature));
580 /**/
581 if (!$cfc["distros_s3_access_id"] || (($s3_response = c_ws_plugin__s2member_utils_urls::remote ("https://" . $s3_domain . $s3_location, false, array_merge ($s3_args, array ("timeout" => 20)), "array")) && ($s3_response["code"] === 200 || $s3_response["code"] === 204 /* Also OK. */)))
582 {
583 $s3_location = ((strtolower ($s3c["bucket"]) !== $s3c["bucket"])) ? "/" . $s3c["bucket"] . "/crossdomain.xml" : "/crossdomain.xml";
584 $s3_policy_xml = trim (c_ws_plugin__s2member_utilities::evl (file_get_contents (dirname (dirname (__FILE__)) . "/templates/cfg-files/s2-cross-xml.php")));
585 $s3_signature = base64_encode (c_ws_plugin__s2member_files_in::amazon_s3_sign ("PUT\n\ntext/xml\n" . $s3_date . "\nx-amz-acl:public-read\n/" . $s3c["bucket"] . "/crossdomain.xml"));
586 $s3_args = array ("method" => "PUT", "body" => $s3_policy_xml, "headers" => array ("Host" => $s3_domain, "Content-Type" => "text/xml", "Date" => $s3_date, "X-Amz-Acl" => "public-read", "Authorization" => "AWS " . $s3c["access_key"] . ":" . $s3_signature));
587 /**/
588 if (($s3_response = c_ws_plugin__s2member_utils_urls::remote ("https://" . $s3_domain . $s3_location, false, array_merge ($s3_args, array ("timeout" => 20)), "array")) && $s3_response["code"] === 200)
589 return array ("success" => true, "code" => null, "message" => null); /* Successfully configured Amazon® S3 Bucket ACLs and Policy. */
590 /**/
591 else if (isset ($s3_response["code"], $s3_response["message"]))
592 /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® S3 API call. Feel free to exclude `%s` if you like. */
593 return array ("success" => false, "code" => $s3_response["code"], "message" => sprintf (_x ("Unable to update existing Amazon® S3 Cross-Domain Policy. %s", "s2member-admin", "s2member"), $s3_response["message"]));
594 /**/
595 else /* Else, we use a default error code and message. */
596 return array ("success" => false, "code" => -94, "message" => _x ("Unable to update existing Amazon® S3 Cross-Domain Policy. Connection failed.", "s2member-admin", "s2member"));
597 }
598 else if (isset ($s3_response["code"], $s3_response["message"]))
599 /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® S3 API call. Feel free to exclude `%s` if you like. */
600 return array ("success" => false, "code" => $s3_response["code"], "message" => sprintf (_x ("Unable to update existing Amazon® S3 Bucket Policy. %s", "s2member-admin", "s2member"), $s3_response["message"]));
601 /**/
602 else /* Else, we use a default error code and message. */
603 return array ("success" => false, "code" => -95, "message" => _x ("Unable to update existing Amazon® S3 Bucket Policy. Connection failed.", "s2member-admin", "s2member"));
604 }
605 else if (isset ($s3_response["code"], $s3_response["message"]))
606 /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® S3 API call. Feel free to exclude `%s` if you like. */
607 return array ("success" => false, "code" => $s3_response["code"], "message" => sprintf (_x ("Unable to update existing Amazon® S3 Bucket ACLs. %s", "s2member-admin", "s2member"), $s3_response["message"]));
608 /**/
609 else /* Else, we use a default error code and message. */
610 return array ("success" => false, "code" => -96, "message" => _x ("Unable to update existing Amazon® S3 Bucket ACLs. Connection failed.", "s2member-admin", "s2member"));
611 }
612 else /* Else, we use a default error code and message. */
613 return array ("success" => false, "code" => -97, "message" => _x ("Unable to acquire/read existing Amazon® S3 Bucket ACLs. Unexpected response.", "s2member-admin", "s2member"));
614 }
615 else if (isset ($s3_response["code"], $s3_response["message"]))
616 /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® S3 API call. Feel free to exclude `%s` if you like. */
617 return array ("success" => false, "code" => $s3_response["code"], "message" => sprintf (_x ("Unable to acquire existing Amazon® S3 Bucket ACLs. %s", "s2member-admin", "s2member"), $s3_response["message"]));
618 /**/
619 else /* Else, we use a default error code and message. */
620 return array ("success" => false, "code" => -98, "message" => _x ("Unable to acquire existing Amazon® S3 Bucket ACLs. Connection failed.", "s2member-admin", "s2member"));
621 }
622 else /* Else, we use a default error code and message. */
623 return array ("success" => false, "code" => -99, "message" => _x ("Unable to auto-configure existing Amazon® S3 Bucket ACLs. Incomplete Amazon® S3 configuration options. Missing one of: Amazon® S3 Bucket, Access Key, or Secret Key.", "s2member-admin", "s2member"));
624 }
625 /**
626 * Creates an Amazon® CloudFront HMAC-SHA1 signature.
627 *
628 * @package s2Member\Files
629 * @since 110926
630 *
631 * @param str $string Input string/data, to be signed by this routine.
632 * @return str An HMAC-SHA1 signature for Amazon® CloudFront.
633 */
634 public static function amazon_cf_sign ($string = FALSE)
635 {
636 $cfc["secret_key"] = $s3c["secret_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_secret_key"];
637 /**/
638 $cf_signature = str_pad (((strlen ($cfc["secret_key"]) > 64) ? pack ('H*', sha1 ($cfc["secret_key"])) : $cfc["secret_key"]), 64, chr (0x00));
639 return pack ('H*', sha1 (($cf_signature ^ str_repeat (chr (0x5c), 64)) . pack ('H*', sha1 (($cf_signature ^ str_repeat (chr (0x36), 64)) . (string)$string))));
640 }
641 /**
642 * Creates an Amazon® CloudFront RSA-SHA1 signature.
643 *
644 * @package s2Member\Files
645 * @since 110926
646 *
647 * @param str $string Input string/data, to be signed by this routine.
648 * @return str|bool An RSA-SHA1 signature for Amazon® CloudFront, else false on failure.
649 */
650 public static function amazon_cf_rsa_sign ($string = FALSE)
651 {
652 $cfc["private_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_cf_files_private_key"];
653 /**/
654 if (stripos (PHP_SAPI, "apache") !== false) /* PHP is running as an Apache module? */
655 if ( /* Some `mod_php` installs have issues with OpenSSL version conflicts. See: <http://www.apachelounge.com/viewtopic.php?t=4180>. */
656 ($signature = c_ws_plugin__s2member_files_in::amazon_cf_rsa_shell_sign ($string)) /* Also try some other OpenSSL locations on Windows®. */
657 || (stripos (PHP_OS, "win") === 0 && ($signature = c_ws_plugin__s2member_files_in::amazon_cf_rsa_shell_sign ($string, "C:\openssl-win32\bin\openssl.exe"))) #
658 || (stripos (PHP_OS, "win") === 0 && ($signature = c_ws_plugin__s2member_files_in::amazon_cf_rsa_shell_sign ($string, "C:\openssl-win64\bin\openssl.exe"))))
659 return $signature;
660 /**/
661 if (function_exists ("openssl_get_privatekey") && function_exists ("openssl_sign") && is_resource ($private_key = openssl_get_privatekey ($cfc["private_key"])))
662 openssl_sign ((string)$string, $signature, $private_key, OPENSSL_ALGO_SHA1) . openssl_free_key ($private_key);
663 /**/
664 else if (function_exists ("shell_exec")) /* Last ditch effort here. */
665 $signature = c_ws_plugin__s2member_files_in::amazon_cf_rsa_shell_sign ($string);
666 /**/
667 return (!empty ($signature)) ? $signature : false;
668 }
669 /**
670 * Creates an Amazon® CloudFront RSA-SHA1 signature via ``shell_exec()`` to `openssl`.
671 *
672 * @package s2Member\Files
673 * @since 110926
674 *
675 * @param str $string Input string/data, to be signed by this routine.
676 * @param str $openssl Optional. A specific absolute path to OpenSSL application.
677 * @return str|bool An RSA-SHA1 signature for Amazon® CloudFront, else false on failure.
678 */
679 public static function amazon_cf_rsa_shell_sign ($string = FALSE, $openssl = FALSE)
680 {
681 $cfc["private_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_cf_files_private_key"];
682 /**/
683 if (function_exists ("shell_exec") && ($openssl = (($openssl && is_string ($openssl)) ? $openssl : "openssl")) && ($esa = "escapeshellarg"))
684 {
685 file_put_contents (($cf_string_file = get_temp_dir () . wp_unique_filename (get_temp_dir (), md5 (time () . "cf-string") . ".tmp")), (string)$string);
686 file_put_contents (($cf_private_key_file = get_temp_dir () . wp_unique_filename (get_temp_dir (), md5 (time () . "cf-private-key") . ".tmp")), $cfc["private_key"]);
687 file_put_contents (($cf_rsa_sha1_sig_file = get_temp_dir () . wp_unique_filename (get_temp_dir (), md5 (time () . "cf-rsa-sha1-signature-file") . ".tmp")), "");
688 /**/
689 @shell_exec($esa ($openssl) . " sha1 -sign " . $esa ($cf_private_key_file) . " -out " . $esa ($cf_rsa_sha1_sig_file) . " " . $esa ($cf_string_file));
690 $signature = trim (file_get_contents ($cf_rsa_sha1_sig_file)); /* Now try to obtain signature file contents. */
691 unlink($cf_rsa_sha1_sig_file) . unlink ($cf_private_key_file) . unlink ($cf_string_file); /* Cleanup. */
692 }
693 /**/
694 return (!empty ($signature)) ? $signature : false;
695 }
696 /**
697 * Creates an Amazon® CloudFront RSA-SHA1 signature URL.
698 *
699 * @package s2Member\Files
700 * @since 110926
701 *
702 * @param str $file Input file path, to be signed by this routine.
703 * @param bool $stream Is this resource file to be served as streaming media?
704 * @param bool $inline Is this resource file to be served inline, or no?
705 * @param bool $ssl Is this resource file to be served via SSL, or no?
706 * @param str $basename The absolute basename of the resource file.
707 * @param str $mimetype The MIME content-type of the resource file.
708 * @return str An RSA-SHA1 signature URL for Amazon® CloudFront.
709 */
710 public static function amazon_cf_url ($file = FALSE, $stream = FALSE, $inline = FALSE, $ssl = FALSE, $basename = FALSE, $mimetype = FALSE)
711 {
712 $file = trim ((string)$file, "/"); /* Trim / force string. */
713 /**/
714 foreach ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"] as $option => $option_value)
715 if (preg_match ("/^amazon_cf_files_/", $option) && ($option = preg_replace ("/^amazon_cf_files_/", "", $option)))
716 $cfc[$option] = $option_value;
717 /**/
718 $cfc["expires"] = strtotime ("+" . apply_filters ("ws_plugin__s2member_amazon_cf_file_expires_time", "24 hours", get_defined_vars ()));
719 /**/
720 $cf_extn = strtolower (substr ($file, strrpos ($file, ".") + 1)); /* Parses the file extension out so we can scan it in some special scenarios. */
721 $cf_stream_extn_resource_exclusions = array_unique ((array)apply_filters ("ws_plugin__s2member_amazon_cf_file_streaming_extension_resource_exclusions", array ("mp3" /* MP3 files should NOT include an extension in their resource reference. */), get_defined_vars ()));
722 $cf_resource = ($stream) ? ((in_array ($cf_extn, $cf_stream_extn_resource_exclusions)) ? substr ($file, 0, strrpos ($file, ".")) : $file) : "http" . (($ssl) ? "s" : "") . "://" . (($cfc["distro_downloads_cname"]) ? $cfc["distro_downloads_cname"] : $cfc["distro_downloads_dname"]) . "/" . $file;
723 $cf_url = ($stream) ? "rtmp" . (($ssl) ? "e" : "") . "://" . (($cfc["distro_streaming_cname"]) ? $cfc["distro_streaming_cname"] : $cfc["distro_streaming_dname"]) . "/cfx/st/" . $file : "http" . (($ssl) ? "s" : "") . "://" . (($cfc["distro_downloads_cname"]) ? $cfc["distro_downloads_cname"] : $cfc["distro_downloads_dname"]) . "/" . $file;
724 $cf_ip_res = (stripos ($_SERVER["HTTP_HOST"], "localhost") === false && strpos ($_SERVER["HTTP_HOST"], "127.0.0.1") === false && (!defined ("LOCALHOST") || !LOCALHOST)) ? true : false; /* Don NOT restrict access to a particular IP during `localhost` development. The IP will NOT be the same one Amazon® CloudFront sees ( so will NOT jive ). */
725 $cf_policy = '{"Statement":[{"Resource":"' . c_ws_plugin__s2member_utils_strings::esc_dq ($cf_resource) . '","Condition":{' . (($cf_ip_res) ? '"IpAddress":{"AWS:SourceIp":"' . c_ws_plugin__s2member_utils_strings::esc_dq ($_SERVER["REMOTE_ADDR"]) . '/32"},' : '') . '"DateLessThan":{"AWS:EpochTime":' . (int)$cfc["expires"] . '}}}]}';
726 /**/
727 $cf_signature = c_ws_plugin__s2member_files_in::amazon_cf_rsa_sign ($cf_policy);
728 $cf_base64_url_safe_policy = c_ws_plugin__s2member_utils_strings::base64_url_safe_encode ($cf_policy, array ("+", "=", "/"), array ("-", "_", "~"), false);
729 $cf_base64_url_safe_signature = c_ws_plugin__s2member_utils_strings::base64_url_safe_encode ($cf_signature, array ("+", "=", "/"), array ("-", "_", "~"), false);
730 /**/
731 return add_query_arg (urlencode_deep (array ("Policy" => $cf_base64_url_safe_policy, "Signature" => $cf_base64_url_safe_signature, "Key-Pair-Id" => $cfc["private_key_id"])), $cf_url);
732 }
733 /**
734 * Auto-configures Amazon® S3/CloudFront distros.
735 *
736 * @package s2Member\Files
737 * @since 110926
738 *
739 * @return array Array containing a true `success` element on success, else a failure array.
740 * Failure array will contain a failure `code`, and a failure `message`.
741 */
742 public static function amazon_cf_auto_configure_distros ()
743 {
744 foreach ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"] as $option => $option_value)
745 if (preg_match ("/^amazon_cf_files_/", $option) && ($option = preg_replace ("/^amazon_cf_files_/", "", $option)))
746 $cfc[$option] = $option_value;
747 /**/
748 $s3c["bucket"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_bucket"];
749 $cfc["access_key"] = $s3c["access_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_access_key"];
750 $cfc["secret_key"] = $s3c["secret_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_secret_key"];
751 /**/
752 if ($s3c["bucket"] && $s3c["access_key"] && $s3c["secret_key"]) /* We MUST have an Amazon® S3 Bucket and Keys. */
753 {
754 if ($cfc["private_key"] && $cfc["private_key_id"]) /* We MUST have Amazon® CloudFront Keys in order to auto-configure. */
755 {
756 if (!$cfc["distro_downloads_id"] || ($cfc["distro_downloads_id"] && ($cf_get_response = c_ws_plugin__s2member_files_in::amazon_cf_get_distro ($cfc["distro_downloads_id"], "downloads")) && ($cf_get_response["success"] || $cf_get_response["code"] === 404)))
757 {
758 if (!$cfc["distro_downloads_id"] || ($cfc["distro_downloads_id"] && $cf_get_response && !$cf_get_response["success"] && $cf_get_response["code"] === 404))
759 $cf_distro_downloads_clear = true; /* Clear, ready for a new one. */
760 /**/
761 else if ($cfc["distro_downloads_id"] && $cf_get_response && $cf_get_response["success"] && !$cf_get_response["deployed"])
762 return array ("success" => false, "code" => -86, "message" => _x ("Unable to delete existing Amazon® CloudFront Downloads Distro. Still in a `pending` state. Please wait 15 minutes, then try again. There is a certain process that s2Member must strictly adhere to when re-configuring your Amazon® CloudFront Distros. You may have to tick the auto-configure checkbox again, and re-run s2Member's auto-configuration routine many times, because s2Member will likely run into several `pending` challenges, as it works to completely re-configure your Amazon® CloudFront Distros for you. Thanks for your patience. Please wait 15 minutes, then try again.", "s2member-admin", "s2member"));
763 /**/
764 else if ($cfc["distro_downloads_id"] && $cf_get_response && $cf_get_response["success"] && $cf_get_response["deployed"] && ($cf_del_response = c_ws_plugin__s2member_files_in::amazon_cf_del_distro ($cfc["distro_downloads_id"], $cf_get_response["etag"], $cf_get_response["xml"])) && $cf_del_response["success"])
765 $cf_distro_downloads_clear = true; /* Clear, ready for a new one. */
766 /**/
767 else if (isset ($cf_del_response["code"], $cf_del_response["message"]))
768 /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® CloudFront API call. Feel free to exclude `%s` if you like. */
769 return array ("success" => false, "code" => $cf_del_response["code"], "message" => sprintf (_x ("Unable to delete existing Amazon® CloudFront Downloads Distro. %s", "s2member-admin", "s2member"), $cf_del_response["message"]));
770 /**/
771 if (isset ($cf_distro_downloads_clear) && $cf_distro_downloads_clear) /* Successfully cleared? Ready for a new one? */
772 {
773 unset ($cf_get_response, $cf_del_response); /* Unset these before processing additional routines. Prevents problems in error reporting. */
774 /**/
775 if (!$cfc["distro_streaming_id"] || ($cfc["distro_streaming_id"] && ($cf_get_response = c_ws_plugin__s2member_files_in::amazon_cf_get_distro ($cfc["distro_streaming_id"], "streaming")) && ($cf_get_response["success"] || $cf_get_response["code"] === 404)))
776 {
777 if (!$cfc["distro_streaming_id"] || ($cfc["distro_streaming_id"] && $cf_get_response && !$cf_get_response["success"] && $cf_get_response["code"] === 404))
778 $cf_distro_streaming_clear = true; /* Clear, ready for a new one. */
779 /**/
780 else if ($cfc["distro_streaming_id"] && $cf_get_response && $cf_get_response["success"] && !$cf_get_response["deployed"])
781 return array ("success" => false, "code" => -87, "message" => _x ("Unable to delete existing Amazon® CloudFront Streaming Distro. Still in a `pending` state. Please wait 15 minutes, then try again. There is a certain process that s2Member must strictly adhere to when re-configuring your Amazon® CloudFront Distros. You may have to tick the auto-configure checkbox again, and re-run s2Member's auto-configuration routine many times, because s2Member will likely run into several `pending` challenges, as it works to completely re-configure your Amazon® CloudFront Distros for you. Thanks for your patience. Please wait 15 minutes, then try again.", "s2member-admin", "s2member"));
782 /**/
783 else if ($cfc["distro_streaming_id"] && $cf_get_response && $cf_get_response["success"] && $cf_get_response["deployed"] && ($cf_del_response = c_ws_plugin__s2member_files_in::amazon_cf_del_distro ($cfc["distro_streaming_id"], $cf_get_response["etag"], $cf_get_response["xml"])) && $cf_del_response["success"])
784 $cf_distro_streaming_clear = true; /* Clear, ready for a new one. */
785 /**/
786 else if (isset ($cf_del_response["code"], $cf_del_response["message"]))
787 /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® CloudFront API call. Feel free to exclude `%s` if you like. */
788 return array ("success" => false, "code" => $cf_del_response["code"], "message" => sprintf (_x ("Unable to delete existing Amazon® CloudFront Streaming Distro. %s", "s2member-admin", "s2member"), $cf_del_response["message"]));
789 /**/
790 if (isset ($cf_distro_streaming_clear) && $cf_distro_streaming_clear) /* Successfully cleared? Ready for a new one? */
791 {
792 unset ($cf_get_response, $cf_del_response); /* Unset these before processing additional routines. Prevents problems in error reporting. */
793 /**/
794 if (!$cfc["distros_access_id"] || ($cfc["distros_access_id"] && ($cf_get_response = c_ws_plugin__s2member_files_in::amazon_cf_get_access_origin_identity ($cfc["distros_access_id"])) && ($cf_get_response["success"] || $cf_get_response["code"] === 404)))
795 {
796 if (!$cfc["distros_access_id"] || ($cfc["distros_access_id"] && $cf_get_response && !$cf_get_response["success"] && $cf_get_response["code"] === 404))
797 $cf_distros_access_clear = true; /* Clear, ready for a new one. */
798 /**/
799 else if ($cfc["distros_access_id"] && $cf_get_response && $cf_get_response["success"] && ($cf_del_response = c_ws_plugin__s2member_files_in::amazon_cf_del_access_origin_identity ($cfc["distros_access_id"], $cf_get_response["etag"], $cf_get_response["xml"])) && $cf_del_response["success"])
800 $cf_distros_access_clear = true; /* Clear, ready for a new one. */
801 /**/
802 else if (isset ($cf_del_response["code"], $cf_del_response["message"]))
803 /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® CloudFront API call. Feel free to exclude `%s` if you like. */
804 return array ("success" => false, "code" => $cf_del_response["code"], "message" => sprintf (_x ("Unable to delete existing Amazon® CloudFront Origin Access Identity. %s", "s2member-admin", "s2member"), $cf_del_response["message"]));
805 /**/
806 if (isset ($cf_distros_access_clear) && $cf_distros_access_clear) /* Successfully cleared? Ready for a new one? */
807 {
808 unset ($cf_get_response, $cf_del_response); /* Unset these before processing additional routines. Prevents problems in error reporting. */
809 /**/
810 $cfc = array_merge ($cfc, array ("distros_access_id" => "", "distros_s3_access_id" => "", "distro_downloads_id" => "", "distro_downloads_dname" => "", "distro_streaming_id" => "", "distro_streaming_dname" => "", "distros_auto_config_status" => ""));
811 $cf_options = array ("ws_plugin__s2member_amazon_cf_files_distros_access_id" => "", "ws_plugin__s2member_amazon_cf_files_distros_s3_access_id" => "", "ws_plugin__s2member_amazon_cf_files_distro_downloads_id" => "", "ws_plugin__s2member_amazon_cf_files_distro_downloads_dname" => "", "ws_plugin__s2member_amazon_cf_files_distro_streaming_id" => "", "ws_plugin__s2member_amazon_cf_files_distro_streaming_dname" => "", "ws_plugin__s2member_amazon_cf_files_distros_auto_config_status" => "");
812 c_ws_plugin__s2member_menu_pages::update_all_options ($cf_options, true, false, false, false, false);
813 /**/
814 if (($cf_response = c_ws_plugin__s2member_files_in::amazon_cf_create_distros_access_origin_identity ()) && $cf_response["success"])
815 {
816 $cfc = array_merge ($cfc, array ("distros_access_id" => $cf_response["distros_access_id"], "distros_s3_access_id" => $cf_response["distros_s3_access_id"]));
817 $cf_options = array ("ws_plugin__s2member_amazon_cf_files_distros_access_id" => $cf_response["distros_access_id"], "ws_plugin__s2member_amazon_cf_files_distros_s3_access_id" => $cf_response["distros_s3_access_id"]);
818 c_ws_plugin__s2member_menu_pages::update_all_options ($cf_options, true, false, false, false, false);
819 /**/
820 if (($cf_response = c_ws_plugin__s2member_files_in::amazon_cf_create_distro ("downloads")) && $cf_response["success"])
821 {
822 $cfc = array_merge ($cfc, array ("distro_downloads_id" => $cf_response["distro_downloads_id"], "distro_downloads_dname" => $cf_response["distro_downloads_dname"]));
823 $cf_options = array ("ws_plugin__s2member_amazon_cf_files_distro_downloads_id" => $cf_response["distro_downloads_id"], "ws_plugin__s2member_amazon_cf_files_distro_downloads_dname" => $cf_response["distro_downloads_dname"]);
824 c_ws_plugin__s2member_menu_pages::update_all_options ($cf_options, true, false, false, false, false);
825 /**/
826 if (($cf_response = c_ws_plugin__s2member_files_in::amazon_cf_create_distro ("streaming")) && $cf_response["success"])
827 {
828 $cfc = array_merge ($cfc, array ("distro_streaming_id" => $cf_response["distro_streaming_id"], "distro_streaming_dname" => $cf_response["distro_streaming_dname"]));
829 $cf_options = array ("ws_plugin__s2member_amazon_cf_files_distro_streaming_id" => $cf_response["distro_streaming_id"], "ws_plugin__s2member_amazon_cf_files_distro_streaming_dname" => $cf_response["distro_streaming_dname"]);
830 c_ws_plugin__s2member_menu_pages::update_all_options ($cf_options, true, false, false, false, false);
831 /**/
832 if (($s3_response = c_ws_plugin__s2member_files_in::amazon_s3_auto_configure_acls ()) && $s3_response["success"])
833 {
834 $cfc = array_merge ($cfc, array ("distros_auto_config_status" => "success"));
835 $cf_options = array ("ws_plugin__s2member_amazon_cf_files_distros_auto_config_status" => "configured");
836 c_ws_plugin__s2member_menu_pages::update_all_options ( /* Now configured! */$cf_options, true, false, false, false, false);
837 /**/
838 return array ("success" => true, "code" => null, "message" => null); /* Successfully configured Amazon® S3/CloudFront distros. */
839 }
840 /**/
841 else if (isset ($s3_response["code"], $s3_response["message"]))
842 /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® S3 API call. Feel free to exclude `%s` if you like. */
843 return array ("success" => false, "code" => $s3_response["code"], "message" => sprintf (_x ("Unable to update existing Amazon® S3 ACLs. %s", "s2member-admin", "s2member"), $s3_response["message"]));
844 /**/
845 else /* Else, we use a default error code and message. */
846 return array ("success" => false, "code" => -88, "message" => _x ("Unable to update existing Amazon® S3 ACLs. Connection failed.", "s2member-admin", "s2member"));
847 }
848 else if (isset ($cf_response["code"], $cf_response["message"]))
849 /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® CloudFront API call. Feel free to exclude `%s` if you like. */
850 return array ("success" => false, "code" => $cf_response["code"], "message" => sprintf (_x ("Unable to create Amazon® CloudFront Streaming Distro. %s", "s2member-admin", "s2member"), $cf_response["message"]));
851 /**/
852 else /* Else, we use a default error code and message. */
853 return array ("success" => false, "code" => -89, "message" => _x ("Unable to create Amazon® CloudFront Streaming Distro. Connection failed.", "s2member-admin", "s2member"));
854 }
855 else if (isset ($cf_response["code"], $cf_response["message"]))
856 /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® CloudFront API call. Feel free to exclude `%s` if you like. */
857 return array ("success" => false, "code" => $cf_response["code"], "message" => sprintf (_x ("Unable to create Amazon® CloudFront Downloads Distro. %s", "s2member-admin", "s2member"), $cf_response["message"]));
858 /**/
859 else /* Else, we use a default error code and message. */
860 return array ("success" => false, "code" => -90, "message" => _x ("Unable to create Amazon® CloudFront Downloads Distro. Connection failed.", "s2member-admin", "s2member"));
861 }
862 else if (isset ($cf_response["code"], $cf_response["message"]))
863 /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® CloudFront API call. Feel free to exclude `%s` if you like. */
864 return array ("success" => false, "code" => $cf_response["code"], "message" => sprintf (_x ("Unable to create Amazon® CloudFront Origin Access Identity. %s", "s2member-admin", "s2member"), $cf_response["message"]));
865 /**/
866 else /* Else, we use a default error code and message. */
867 return array ("success" => false, "code" => -91, "message" => _x ("Unable to create Amazon® CloudFront Origin Access Identity. Connection failed.", "s2member-admin", "s2member"));
868 }
869 else /* Else, we use a default error code and message. */
870 return array ("success" => false, "code" => -92, "message" => _x ("Unable to clear existing Amazon® CloudFront Origin Access Identity.", "s2member-admin", "s2member"));
871 }
872 else if (isset ($cf_get_response["code"], $cf_get_response["message"]))
873 /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® CloudFront API call. Feel free to exclude `%s` if you like. */
874 return array ("success" => false, "code" => $cf_get_response["code"], "message" => sprintf (_x ("Unable to acquire existing Amazon® CloudFront Origin Access Identity. %s", "s2member-admin", "s2member"), $cf_get_response["message"]));
875 /**/
876 else /* Else, we use a default error code and message. */
877 return array ("success" => false, "code" => -93, "message" => _x ("Unable to acquire existing Amazon® CloudFront Origin Access Identity. Connection failed.", "s2member-admin", "s2member"));
878 }
879 else /* Else, we use a default error code and message. */
880 return array ("success" => false, "code" => -94, "message" => _x ("Unable to clear existing Amazon® CloudFront Streaming Distro.", "s2member-admin", "s2member"));
881 }
882 else if (isset ($cf_get_response["code"], $cf_get_response["message"]))
883 /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® CloudFront API call. Feel free to exclude `%s` if you like. */
884 return array ("success" => false, "code" => $cf_get_response["code"], "message" => sprintf (_x ("Unable to acquire existing Amazon® CloudFront Streaming Distro. %s", "s2member-admin", "s2member"), $cf_get_response["message"]));
885 /**/
886 else /* Else, we use a default error code and message. */
887 return array ("success" => false, "code" => -95, "message" => _x ("Unable to acquire existing Amazon® CloudFront Streaming Distro. Connection failed.", "s2member-admin", "s2member"));
888 }
889 else /* Else, we use a default error code and message. */
890 return array ("success" => false, "code" => -96, "message" => _x ("Unable to clear existing Amazon® CloudFront Downloads Distro.", "s2member-admin", "s2member"));
891 }
892 else if (isset ($cf_get_response["code"], $cf_get_response["message"]))
893 /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® CloudFront API call. Feel free to exclude `%s` if you like. */
894 return array ("success" => false, "code" => $cf_get_response["code"], "message" => sprintf (_x ("Unable to acquire existing Amazon® CloudFront Downloads Distro. %s", "s2member-admin", "s2member"), $cf_get_response["message"]));
895 /**/
896 else /* Else, we use a default error code and message. */
897 return array ("success" => false, "code" => -97, "message" => _x ("Unable to acquire existing Amazon® CloudFront Downloads Distro. Connection failed.", "s2member-admin", "s2member"));
898 }
899 else /* Else, we use a default error code and message. */
900 return array ("success" => false, "code" => -98, "message" => _x ("Unable to auto-configure Amazon® CloudFront Distros. Incomplete Amazon® CloudFront configuration options. Missing of one: Amazon® CloudFront Private Key-Pair-ID, or Private Key file contents.", "s2member-admin", "s2member"));
901 }
902 else /* Else, we use a default error code and message. */
903 return array ("success" => false, "code" => -99, "message" => _x ("Unable to auto-configure Amazon® S3/CloudFront Distros. Incomplete Amazon® S3 configuration options. Missing one of: Amazon® S3 Bucket, Access Key, or Secret Key. You must provide s2Member with an Amazon® S3 configuration before enabling CloudFront.", "s2member-admin", "s2member"));
904 }
905 /**
906 * Acquires an Amazon® S3/CloudFront Access Origin Identity.
907 *
908 * @package s2Member\Files
909 * @since 110926
910 *
911 * @param str $access_id Required. An Origin Access ID.
912 * @return array Array containing a true `success` and `etag`, `xml` elements on success, else a failure array.
913 * Failure array will contain a failure `code`, and a failure `message`.
914 */
915 public static function amazon_cf_get_access_origin_identity ($access_id = FALSE)
916 {
917 if ($access_id && is_string ($access_id)) /* Valid parameters? */
918 {
919 foreach ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"] as $option => $option_value)
920 if (preg_match ("/^amazon_cf_files_/", $option) && ($option = preg_replace ("/^amazon_cf_files_/", "", $option)))
921 $cfc[$option] = $option_value;
922 /**/
923 $s3c["bucket"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_bucket"];
924 $cfc["access_key"] = $s3c["access_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_access_key"];
925 $cfc["secret_key"] = $s3c["secret_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_secret_key"];
926 /**/
927 $cf_domain = "cloudfront.amazonaws.com";
928 $cf_date = gmdate ("D, d M Y H:i:s") . " GMT";
929 $cf_location = "/2010-11-01/origin-access-identity/cloudfront/" . $access_id;
930 $cf_signature = base64_encode (c_ws_plugin__s2member_files_in::amazon_cf_sign ($cf_date));
931 $cf_args = array ("method" => "GET", "headers" => array ("Host" => $cf_domain, "Date" => $cf_date, "Authorization" => "AWS " . $cfc["access_key"] . ":" . $cf_signature));
932 /**/
933 if (($cf_response = c_ws_plugin__s2member_utils_urls::remote ("https://" . $cf_domain . $cf_location, false, array_merge ($cf_args, array ("timeout" => 20)), "array")) && (($cf_response["code"] === 404 && $cf_response["message"]) || ($cf_response["code"] === 200 && !empty ($cf_response["headers"]["etag"]) && !empty ($cf_response["body"]))))
934 {
935 if ($cf_response["code"] === 200 && !empty ($cf_response["headers"]["etag"]) && !empty ($cf_response["body"]))
936 return array ("success" => true, "code" => null, "message" => null, "etag" => trim ($cf_response["headers"]["etag"]), "xml" => trim ($cf_response["body"]));
937 /**/
938 else /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® CloudFront API call. Feel free to exclude `%s` if you like. */
939 return array ("success" => false, "code" => $cf_response["code"], "message" => sprintf (_x ("Existing Amazon® CloudFront Origin Access Identity NOT found. %s", "s2member-admin", "s2member"), $cf_response["message"]));
940 }
941 else if (isset ($cf_response["code"], $cf_response["message"]))
942 /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® CloudFront API call. Feel free to exclude `%s` if you like. */
943 return array ("success" => false, "code" => $cf_response["code"], "message" => sprintf (_x ("Unable to acquire existing Amazon® CloudFront Origin Access Identity. %s", "s2member-admin", "s2member"), $cf_response["message"]));
944 /**/
945 else /* Else, we use a default error code and message. */
946 return array ("success" => false, "code" => -98, "message" => _x ("Unable to acquire existing Amazon® CloudFront Origin Access Identity. Connection failed.", "s2member-admin", "s2member"));
947 }
948 else /* Else, we use a default error code and message. */
949 return array ("success" => false, "code" => -99, "message" => _x ("Unable to acquire existing Amazon® CloudFront Origin Access Identity. Invalid Access ID.", "s2member-admin", "s2member"));
950 }
951 /**
952 * Deletes an Amazon® S3/CloudFront Access Origin Identity.
953 *
954 * @package s2Member\Files
955 * @since 110926
956 *
957 * @param str $access_id Required. An Origin Access ID.
958 * @param str $access_id_etag Required. An Origin Access ETag header.
959 * @param str $access_id_xml Required. An Origin Access Identity's XML configuration.
960 * @return array Array containing a true `success` element on success, else a failure array.
961 * Failure array will contain a failure `code`, and a failure `message`.
962 */
963 public static function amazon_cf_del_access_origin_identity ($access_id = FALSE, $access_id_etag = FALSE, $access_id_xml = FALSE)
964 {
965 if ($access_id && is_string ($access_id) && $access_id_etag && is_string ($access_id_etag) && $access_id_xml && is_string ($access_id_xml))
966 {
967 foreach ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"] as $option => $option_value)
968 if (preg_match ("/^amazon_cf_files_/", $option) && ($option = preg_replace ("/^amazon_cf_files_/", "", $option)))
969 $cfc[$option] = $option_value;
970 /**/
971 $s3c["bucket"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_bucket"];
972 $cfc["access_key"] = $s3c["access_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_access_key"];
973 $cfc["secret_key"] = $s3c["secret_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_secret_key"];
974 /**/
975 $cf_domain = "cloudfront.amazonaws.com";
976 $cf_date = gmdate ("D, d M Y H:i:s") . " GMT";
977 $cf_location = "/2010-11-01/origin-access-identity/cloudfront/" . $access_id;
978 $cf_signature = base64_encode (c_ws_plugin__s2member_files_in::amazon_cf_sign ($cf_date));
979 $cf_args = array ("method" => "DELETE", "headers" => array ("Host" => $cf_domain, "Date" => $cf_date, "If-Match" => $access_id_etag, "Authorization" => "AWS " . $cfc["access_key"] . ":" . $cf_signature));
980 /**/
981 if (($cf_response = c_ws_plugin__s2member_utils_urls::remote ("https://" . $cf_domain . $cf_location, false, array_merge ($cf_args, array ("timeout" => 20)), "array")) && ($cf_response["code"] === 200 || $cf_response["code"] === 204 /* Deleted. */))
982 return array ("success" => true, "code" => null, "message" => null); /* Deleted successfully. */
983 /**/
984 else if (isset ($cf_response["code"], $cf_response["message"]))
985 /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® CloudFront API call. Feel free to exclude `%s` if you like. */
986 return array ("success" => false, "code" => $cf_response["code"], "message" => sprintf (_x ("Unable to delete existing Amazon® CloudFront Origin Access Identity. %s", "s2member-admin", "s2member"), $cf_response["message"]));
987 /**/
988 else /* Else, we use a default error code and message. */
989 return array ("success" => false, "code" => -98, "message" => _x ("Unable to delete existing Amazon® CloudFront Origin Access Identity. Connection failed.", "s2member-admin", "s2member"));
990 }
991 else /* Else, we use a default error code and message. */
992 return array ("success" => false, "code" => -99, "message" => _x ("Unable to delete existing Amazon® CloudFront Origin Access Identity. Invalid Access ID, ETag, or XML config.", "s2member-admin", "s2member"));
993 }
994 /**
995 * Creates an Amazon® S3/CloudFront Access Origin Identity for all Distros.
996 *
997 * @package s2Member\Files
998 * @since 110926
999 *
1000 * @return array Array containing a true `success` and `distros_access_id`, `distros_s3_access_id` elements on success, else a failure array.
1001 * Failure array will contain a failure `code`, and a failure `message`.
1002 */
1003 public static function amazon_cf_create_distros_access_origin_identity ()
1004 {
1005 foreach ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"] as $option => $option_value)
1006 if (preg_match ("/^amazon_cf_files_/", $option) && ($option = preg_replace ("/^amazon_cf_files_/", "", $option)))
1007 $cfc[$option] = $option_value;
1008 /**/
1009 $s3c["bucket"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_bucket"];
1010 $cfc["access_key"] = $s3c["access_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_access_key"];
1011 $cfc["secret_key"] = $s3c["secret_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_secret_key"];
1012 /**/
1013 $cf_domain = "cloudfront.amazonaws.com";
1014 $cf_date = gmdate ("D, d M Y H:i:s") . " GMT";
1015 $cf_location = "/2010-11-01/origin-access-identity/cloudfront";
1016 $cf_signature = base64_encode (c_ws_plugin__s2member_files_in::amazon_cf_sign ($cf_date));
1017 $cf_distros_access_reference = time () . "." . md5 ("access" . $s3c["bucket"] . $s3c["access_key"] . $s3c["secret_key"] . $cfc["private_key"] . $cfc["private_key_id"]);
1018 $cf_distros_access_xml = '<?xml version="1.0" encoding="UTF-8"?><CloudFrontOriginAccessIdentityConfig xmlns="http://cloudfront.amazonaws.com/doc/2010-11-01/"><CallerReference>' . esc_html ($cf_distros_access_reference) . '</CallerReference><Comment>' . esc_html (sprintf (_x ("Created by s2Member, for S3 Bucket: %s.", "s2member-admin", "s2member"), $s3c["bucket"])) . '</Comment></CloudFrontOriginAccessIdentityConfig>';
1019 $cf_args = array ("method" => "POST", "body" => $cf_distros_access_xml, "headers" => array ("Host" => $cf_domain, "Content-Type" => "application/xml", "Date" => $cf_date, "Authorization" => "AWS " . $cfc["access_key"] . ":" . $cf_signature));
1020 /**/
1021 if (($cf_response = c_ws_plugin__s2member_utils_urls::remote ("https://" . $cf_domain . $cf_location, false, array_merge ($cf_args, array ("timeout" => 20)), "array")) && ($cf_response["code"] === 200 || $cf_response["code"] === 201 /* Created. */))
1022 {
1023 if (preg_match ("/\<CloudFrontOriginAccessIdentity.*?\>(.+?)\<\/CloudFrontOriginAccessIdentity\>/is", $cf_response["body"], $cf_distros_access_tag) && preg_match ("/\<Id\>(.+?)\<\/Id\>/is", $cf_distros_access_tag[1], $cf_distros_access_id_tag) && preg_match ("/\<S3CanonicalUserId\>(.+?)\<\/S3CanonicalUserId\>/is", $cf_distros_access_tag[1], $cf_distros_s3_access_id_tag))
1024 return array ("success" => true, "code" => null, "message" => null, "distros_access_id" => trim ($cf_distros_access_id_tag[1]), "distros_s3_access_id" => trim ($cf_distros_s3_access_id_tag[1]));
1025 /**/
1026 else /* Else, we use a default error code and message. */
1027 return array ("success" => false, "code" => -98, "message" => _x ("Unable to create/read Amazon® CloudFront Origin Access Identity. Unexpected response.", "s2member-admin", "s2member"));
1028 }
1029 else if (isset ($cf_response["code"], $cf_response["message"]))
1030 /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® CloudFront API call. Feel free to exclude `%s` if you like. */
1031 return array ("success" => false, "code" => $cf_response["code"], "message" => sprintf (_x ("Unable to create Amazon® CloudFront Origin Access Identity. %s", "s2member-admin", "s2member"), $cf_response["message"]));
1032 /**/
1033 else /* Else, we use a default error code and message. */
1034 return array ("success" => false, "code" => -99, "message" => _x ("Unable to create Amazon® CloudFront Origin Access Identity. Connection failed.", "s2member-admin", "s2member"));
1035 }
1036 /**
1037 * Acquires an Amazon® S3/CloudFront Distro.
1038 *
1039 * @package s2Member\Files
1040 * @since 110926
1041 *
1042 * @param str $distro_id Required. A Distro ID.
1043 * @param str $distro_type Required: `downloads|streaming`.
1044 * @return array Array containing a true `success` and `etag`, `xml`, `deployed` elements on success, else a failure array.
1045 * Failure array will contain a failure `code`, and a failure `message`.
1046 */
1047 public static function amazon_cf_get_distro ($distro_id = FALSE, $distro_type = FALSE)
1048 {
1049 if ($distro_id && is_string ($distro_id) && $distro_type && is_string ($distro_type) && in_array ($distro_type, array ("downloads", "streaming")))
1050 {
1051 foreach ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"] as $option => $option_value)
1052 if (preg_match ("/^amazon_cf_files_/", $option) && ($option = preg_replace ("/^amazon_cf_files_/", "", $option)))
1053 $cfc[$option] = $option_value;
1054 /**/
1055 $s3c["bucket"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_bucket"];
1056 $cfc["access_key"] = $s3c["access_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_access_key"];
1057 $cfc["secret_key"] = $s3c["secret_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_secret_key"];
1058 /**/
1059 $cf_domain = "cloudfront.amazonaws.com";
1060 $cf_date = gmdate ("D, d M Y H:i:s") . " GMT";
1061 $cf_signature = base64_encode (c_ws_plugin__s2member_files_in::amazon_cf_sign ($cf_date));
1062 $cf_location = ($distro_type === "streaming") ? "/2010-11-01/streaming-distribution/" . $distro_id : "/2010-11-01/distribution/" . $distro_id;
1063 $cf_args = array ("method" => "GET", "headers" => array ("Host" => $cf_domain, "Date" => $cf_date, "Authorization" => "AWS " . $cfc["access_key"] . ":" . $cf_signature));
1064 /**/
1065 if (($cf_response = c_ws_plugin__s2member_utils_urls::remote ("https://" . $cf_domain . $cf_location, false, array_merge ($cf_args, array ("timeout" => 20)), "array")) && (($cf_response["code"] === 404 && $cf_response["message"]) || ($cf_response["code"] === 200 && !empty ($cf_response["headers"]["etag"]) && !empty ($cf_response["body"]))))
1066 {
1067 if ($cf_response["code"] === 200 && !empty ($cf_response["headers"]["etag"]) && !empty ($cf_response["body"]))
1068 return array ("success" => true, "code" => null, "message" => null, "etag" => trim ($cf_response["headers"]["etag"]), "xml" => trim ($cf_response["body"]), "deployed" => ((stripos ($cf_response["body"], "<Status>Deployed</Status>") !== false) ? true : false));
1069 /**/
1070 else /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® CloudFront API call. Feel free to exclude `%s` if you like. */
1071 return array ("success" => false, "code" => $cf_response["code"], "message" => sprintf (_x ("Existing Amazon® CloudFront Distro NOT found. %s", "s2member-admin", "s2member"), $cf_response["message"]));
1072 }
1073 else if (isset ($cf_response["code"], $cf_response["message"]))
1074 /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® CloudFront API call. Feel free to exclude `%s` if you like. */
1075 return array ("success" => false, "code" => $cf_response["code"], "message" => sprintf (_x ("Unable to acquire existing Amazon® CloudFront Distro. %s", "s2member-admin", "s2member"), $cf_response["message"]));
1076 /**/
1077 else /* Else, we use a default error code and message. */
1078 return array ("success" => false, "code" => -98, "message" => _x ("Unable to acquire existing Amazon® CloudFront Distro. Connection failed.", "s2member-admin", "s2member"));
1079 }
1080 else /* Else, we use a default error code and message. */
1081 return array ("success" => false, "code" => -99, "message" => _x ("Unable to acquire existing Amazon® CloudFront Distro. Invalid Distro ID and/or Distro type.", "s2member-admin", "s2member"));
1082 }
1083 /**
1084 * Disables an Amazon® S3/CloudFront Distro.
1085 *
1086 * @package s2Member\Files
1087 * @since 110926
1088 *
1089 * @param str $distro_id Required. A Distro ID.
1090 * @param str $distro_id_etag Required. A Distro ETag header.
1091 * @param str $distro_id_xml Required. A Distro's XML configuration.
1092 * @return array Array containing a true `success` and `etag`, `xml`, `deployed` elements on success, else a failure array.
1093 * Failure array will contain a failure `code`, and a failure `message`.
1094 */
1095 public static function amazon_cf_disable_distro ($distro_id = FALSE, $distro_id_etag = FALSE, $distro_id_xml = FALSE)
1096 {
1097 if ($distro_id && is_string ($distro_id) && $distro_id_etag && is_string ($distro_id_etag) && $distro_id_xml && is_string ($distro_id_xml) /* Parse type/reference too. */
1098 && ($distro_id_type = (stripos ($distro_id_xml, "<StreamingDistribution") !== false) ? "streaming" : ((stripos ($distro_id_xml, "<Distribution") !== false) ? "downloads" : false)) #
1099 && preg_match ("/\<CallerReference\>(.+?)\<\/CallerReference\>/is", $distro_id_xml, $distro_id_reference_tag) && ($distro_id_reference = $distro_id_reference_tag[1]))
1100 {
1101 if (stripos ($distro_id_xml, "<Enabled>false</Enabled>") === false) /* Only if it has NOT already been disabled. We do NOT need to do it again. */
1102 {
1103 if (stripos ($distro_id_xml, "<Status>Deployed</Status>") !== false) /* Check distro status before we even begin processing. */
1104 {
1105 foreach ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"] as $option => $option_value)
1106 if (preg_match ("/^amazon_cf_files_/", $option) && ($option = preg_replace ("/^amazon_cf_files_/", "", $option)))
1107 $cfc[$option] = $option_value;
1108 /**/
1109 $s3c["bucket"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_bucket"];
1110 $cfc["access_key"] = $s3c["access_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_access_key"];
1111 $cfc["secret_key"] = $s3c["secret_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_secret_key"];
1112 /**/
1113 $cf_domain = "cloudfront.amazonaws.com";
1114 $cf_date = gmdate ("D, d M Y H:i:s") . " GMT";
1115 $cf_signature = base64_encode (c_ws_plugin__s2member_files_in::amazon_cf_sign ($cf_date));
1116 $cf_location = ($distro_id_type === "streaming") ? "/2010-11-01/streaming-distribution/" . $distro_id . "/config" : "/2010-11-01/distribution/" . $distro_id . "/config";
1117 $cf_distro_xml = ($distro_id_type === "streaming") ? '<?xml version="1.0" encoding="UTF-8"?><StreamingDistributionConfig xmlns="http://cloudfront.amazonaws.com/doc/2010-11-01/"><S3Origin><DNSName>' . esc_html ($s3c["bucket"]) . '.s3.amazonaws.com</DNSName></S3Origin><CallerReference>' . esc_html ($distro_id_reference) . '</CallerReference><Enabled>false</Enabled><TrustedSigners><Self/></TrustedSigners></StreamingDistributionConfig>' : '<?xml version="1.0" encoding="UTF-8"?><DistributionConfig xmlns="http://cloudfront.amazonaws.com/doc/2010-11-01/"><S3Origin><DNSName>' . esc_html ($s3c["bucket"]) . '.s3.amazonaws.com</DNSName></S3Origin><CallerReference>' . esc_html ($distro_id_reference) . '</CallerReference><Enabled>false</Enabled><TrustedSigners><Self/></TrustedSigners></DistributionConfig>';
1118 $cf_args = array ("method" => "PUT", "body" => $cf_distro_xml, "headers" => array ("Host" => $cf_domain, "Content-Type" => "application/xml", "Date" => $cf_date, "If-Match" => $distro_id_etag, "Authorization" => "AWS " . $cfc["access_key"] . ":" . $cf_signature));
1119 /**/
1120 if (($cf_response = c_ws_plugin__s2member_utils_urls::remote ("https://" . $cf_domain . $cf_location, false, array_merge ($cf_args, array ("timeout" => 20)), "array")) && $cf_response["code"] === 200 && !empty ($cf_response["headers"]["etag"]) && !empty ($cf_response["body"]))
1121 return array ("success" => true, "code" => null, "message" => null, "etag" => trim ($cf_response["headers"]["etag"]), "xml" => trim ($cf_response["body"]), "deployed" => ((stripos ($cf_response["body"], "<Status>Deployed</Status>") !== false) ? true : false));
1122 /**/
1123 else if (isset ($cf_response["code"], $cf_response["message"]))
1124 /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® CloudFront API call. Feel free to exclude `%s` if you like. */
1125 return array ("success" => false, "code" => $cf_response["code"], "message" => sprintf (_x ("Unable to disable existing Amazon® CloudFront Distro. %s", "s2member-admin", "s2member"), $cf_response["message"]));
1126 /**/
1127 else /* Else, we use a default error code and message. */
1128 return array ("success" => false, "code" => -97, "message" => _x ("Unable to disable existing Amazon® CloudFront Distro. Connection failed.", "s2member-admin", "s2member"));
1129 }
1130 else /* Else, we use a default error code and message. */
1131 return array ("success" => false, "code" => -98, "message" => _x ("Existing Amazon® CloudFront Distro cannot be disabled at this time. Still in a `pending` state. Please wait 15 minutes, then try again. There is a certain process that s2Member must strictly adhere to when re-configuring your Amazon® CloudFront Distros. You may have to tick the auto-configure checkbox again, and re-run s2Member's auto-configuration routine many times, because s2Member will likely run into several `pending` challenges, as it works to completely re-configure your Amazon® CloudFront Distros for you. Thanks for your patience. Please wait 15 minutes, then try again.", "s2member-admin", "s2member"));
1132 }
1133 else /* Else, we use a default error code and message. */
1134 return array ("success" => true, "code" => null, "message" => null, "etag" => $distro_id_etag, "xml" => $distro_id_xml, "deployed" => ((stripos ($distro_id_xml, "<Status>Deployed</Status>") !== false) ? true : false));
1135 }
1136 else /* Else, we use a default error code and message. */
1137 return array ("success" => false, "code" => -99, "message" => _x ("Unable to disable existing Amazon® CloudFront Distro. Invalid Distro ID, ETag, or XML config.", "s2member-admin", "s2member"));
1138 }
1139 /**
1140 * Deletes an Amazon® S3/CloudFront Distro.
1141 *
1142 * @package s2Member\Files
1143 * @since 110926
1144 *
1145 * @param str $distro_id Required. A Distro ID.
1146 * @param str $distro_id_etag Required. A Distro ETag header.
1147 * @param str $distro_id_xml Required. A Distro's XML configuration.
1148 * @return array Array containing a true `success` element on success, else a failure array.
1149 * Failure array will contain a failure `code`, and a failure `message`.
1150 */
1151 public static function amazon_cf_del_distro ($distro_id = FALSE, $distro_id_etag = FALSE, $distro_id_xml = FALSE)
1152 {
1153 if ($distro_id && is_string ($distro_id) && $distro_id_etag && is_string ($distro_id_etag) && $distro_id_xml && is_string ($distro_id_xml) /* Parse type/reference too. */
1154 && ($distro_id_type = (stripos ($distro_id_xml, "<StreamingDistribution") !== false) ? "streaming" : ((stripos ($distro_id_xml, "<Distribution") !== false) ? "downloads" : false)) #
1155 && preg_match ("/\<CallerReference\>(.+?)\<\/CallerReference\>/is", $distro_id_xml, $distro_id_reference_tag) && ($distro_id_reference = $distro_id_reference_tag[1]))
1156 {
1157 if (stripos ($distro_id_xml, "<Status>Deployed</Status>") !== false) /* Check distro status before we even begin processing this deletion. */
1158 {
1159 if (($cf_response = c_ws_plugin__s2member_files_in::amazon_cf_disable_distro ($distro_id, $distro_id_etag, $distro_id_xml)) && $cf_response["success"])
1160 {
1161 if (($cf_response = c_ws_plugin__s2member_files_in::amazon_cf_get_distro ($distro_id, $distro_id_type)) && $cf_response["success"] && $cf_response["deployed"])
1162 {
1163 foreach ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"] as $option => $option_value)
1164 if (preg_match ("/^amazon_cf_files_/", $option) && ($option = preg_replace ("/^amazon_cf_files_/", "", $option)))
1165 $cfc[$option] = $option_value;
1166 /**/
1167 $s3c["bucket"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_bucket"];
1168 $cfc["access_key"] = $s3c["access_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_access_key"];
1169 $cfc["secret_key"] = $s3c["secret_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_secret_key"];
1170 /**/
1171 $cf_domain = "cloudfront.amazonaws.com";
1172 $cf_date = gmdate ("D, d M Y H:i:s") . " GMT";
1173 $cf_signature = base64_encode (c_ws_plugin__s2member_files_in::amazon_cf_sign ($cf_date));
1174 $cf_location = ($distro_id_type === "streaming") ? "/2010-11-01/streaming-distribution/" . $distro_id : "/2010-11-01/distribution/" . $distro_id;
1175 $cf_args = array ("method" => "DELETE", "headers" => array ("Host" => $cf_domain, "Date" => $cf_date, "If-Match" => $cf_response["etag"], "Authorization" => "AWS " . $cfc["access_key"] . ":" . $cf_signature));
1176 /**/
1177 if (($cf_response = c_ws_plugin__s2member_utils_urls::remote ("https://" . $cf_domain . $cf_location, false, array_merge ($cf_args, array ("timeout" => 20)), "array")) && ($cf_response["code"] === 200 || $cf_response["code"] === 204 /* Deleted. */))
1178 return array ("success" => true, "code" => null, "message" => null); /* Deleted successfully. */
1179 /**/
1180 else if (isset ($cf_response["code"], $cf_response["message"]))
1181 /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® CloudFront API call. Feel free to exclude `%s` if you like. */
1182 return array ("success" => false, "code" => $cf_response["code"], "message" => sprintf (_x ("Unable to delete existing Amazon® CloudFront Distro. %s", "s2member-admin", "s2member"), $cf_response["message"]));
1183 /**/
1184 else /* Else, we use a default error code and message. */
1185 return array ("success" => false, "code" => -94, "message" => _x ("Unable to delete existing Amazon® CloudFront Distro. Connection failed.", "s2member-admin", "s2member"));
1186 }
1187 else if (isset ($cf_response["success"], $cf_response["deployed"]) && $cf_response["success"] && !$cf_response["deployed"])
1188 /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® CloudFront API call. Feel free to exclude `%s` if you like. */
1189 return array ("success" => false, "code" => -95, "message" => _x ("Existing Amazon® CloudFront Distro cannot be deleted at this time. Still in a `pending` state after having been disabled by s2Member. Please wait 15 minutes, then try again. There is a certain process that s2Member must strictly adhere to when re-configuring your Amazon® CloudFront Distros. You may have to tick the auto-configure checkbox again, and re-run s2Member's auto-configuration routine many times, because s2Member will likely run into several `pending` challenges, as it works to completely re-configure your Amazon® CloudFront Distros for you. Thanks for your patience. Please wait 15 minutes, then try again.", "s2member-admin", "s2member"));
1190 /**/
1191 else if (isset ($cf_response["code"], $cf_response["message"]))
1192 /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® CloudFront API call. Feel free to exclude `%s` if you like. */
1193 return array ("success" => false, "code" => $cf_response["code"], "message" => sprintf (_x ("Unable to check status of existing Amazon® CloudFront Distro. %s", "s2member-admin", "s2member"), $cf_response["message"]));
1194 /**/
1195 else /* Else, we use a default error code and message. */
1196 return array ("success" => false, "code" => -96, "message" => _x ("Unable to check status of existing Amazon® CloudFront Distro. Connection failed.", "s2member-admin", "s2member"));
1197 }
1198 else if (isset ($cf_response["code"], $cf_response["message"]))
1199 /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® CloudFront API call. Feel free to exclude `%s` if you like. */
1200 return array ("success" => false, "code" => $cf_response["code"], "message" => sprintf (_x ("Unable to disable existing Amazon® CloudFront Distro. %s", "s2member-admin", "s2member"), $cf_response["message"]));
1201 /**/
1202 else /* Else, we use a default error code and message. */
1203 return array ("success" => false, "code" => -97, "message" => _x ("Unable to disable existing Amazon® CloudFront Distro. Connection failed.", "s2member-admin", "s2member"));
1204 }
1205 else /* Else, we use a default error code and message. */
1206 return array ("success" => false, "code" => -98, "message" => _x ("Existing Amazon® CloudFront Distro cannot be deleted at this time. Still in a `pending` state. Please wait 15 minutes, then try again. There is a certain process that s2Member must strictly adhere to when re-configuring your Amazon® CloudFront Distros. You may have to tick the auto-configure checkbox again, and re-run s2Member's auto-configuration routine many times, because s2Member will likely run into several `pending` challenges, as it works to completely re-configure your Amazon® CloudFront Distros for you. Thanks for your patience. Please wait 15 minutes, then try again.", "s2member-admin", "s2member"));
1207 }
1208 else /* Else, we use a default error code and message. */
1209 return array ("success" => false, "code" => -99, "message" => _x ("Unable to delete existing Amazon® CloudFront Distro. Invalid Distro ID or ETag.", "s2member-admin", "s2member"));
1210 }
1211 /**
1212 * Creates an Amazon® S3/CloudFront Distro.
1213 *
1214 * @package s2Member\Files
1215 * @since 110926
1216 *
1217 * @param str $distro_type Required: `downloads|streaming`.
1218 * @return array Array containing a true `success` and `distro_[distro_type]_id`, `distro_[distro_type]_dname` elements on success, else a failure array.
1219 * Failure array will contain a failure `code`, and a failure `message`.
1220 */
1221 public static function amazon_cf_create_distro ($distro_type = FALSE)
1222 {
1223 if ($distro_type && is_string ($distro_type) && in_array ($distro_type, array ("downloads", "streaming")))
1224 {
1225 foreach ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"] as $option => $option_value)
1226 if (preg_match ("/^amazon_cf_files_/", $option) && ($option = preg_replace ("/^amazon_cf_files_/", "", $option)))
1227 $cfc[$option] = $option_value;
1228 /**/
1229 $s3c["bucket"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_bucket"];
1230 $cfc["access_key"] = $s3c["access_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_access_key"];
1231 $cfc["secret_key"] = $s3c["secret_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_secret_key"];
1232 /**/
1233 $cf_domain = "cloudfront.amazonaws.com";
1234 $cf_date = gmdate ("D, d M Y H:i:s") . " GMT";
1235 $cf_signature = base64_encode (c_ws_plugin__s2member_files_in::amazon_cf_sign ($cf_date));
1236 /**/
1237 if ($distro_type === "downloads") /* Create a `downloads` Distro? This uses a different XML schema. */
1238 {
1239 $cf_location = "/2010-11-01/distribution"; /* Create distro. */
1240 $cf_distro_downloads_reference = time () . "." . md5 ("downloads" . $s3c["bucket"] . $s3c["access_key"] . $s3c["secret_key"] . $cfc["private_key"] . $cfc["private_key_id"] . $cfc["distro_downloads_cname"]);
1241 $cf_distro_downloads_xml = '<?xml version="1.0" encoding="UTF-8"?><DistributionConfig xmlns="http://cloudfront.amazonaws.com/doc/2010-11-01/"><S3Origin><DNSName>' . esc_html ($s3c["bucket"]) . '.s3.amazonaws.com</DNSName><OriginAccessIdentity>origin-access-identity/cloudfront/' . esc_html ($cfc["distros_access_id"]) . '</OriginAccessIdentity></S3Origin><CallerReference>' . esc_html ($cf_distro_downloads_reference) . '</CallerReference>' . (($cfc["distro_downloads_cname"]) ? '<CNAME>' . esc_html ($cfc["distro_downloads_cname"]) . '</CNAME>' : '') . '<Comment>' . esc_html (sprintf (_x ("Created by s2Member, for S3 Bucket: %s.", "s2member-admin", "s2member"), $s3c["bucket"])) . '</Comment><Enabled>true</Enabled><DefaultRootObject>index.html</DefaultRootObject><TrustedSigners><Self/></TrustedSigners></DistributionConfig>';
1242 $cf_args = array ("method" => "POST", "body" => $cf_distro_downloads_xml, "headers" => array ("Host" => $cf_domain, "Content-Type" => "application/xml", "Date" => $cf_date, "Authorization" => "AWS " . $cfc["access_key"] . ":" . $cf_signature));
1243 /**/
1244 if (($cf_response = c_ws_plugin__s2member_utils_urls::remote ("https://" . $cf_domain . $cf_location, false, array_merge ($cf_args, array ("timeout" => 20)), "array")) && ($cf_response["code"] === 200 || $cf_response["code"] === 201 /* Created. */))
1245 {
1246 if (preg_match ("/\<Distribution.*?\>(.+?)\<\/Distribution\>/is", $cf_response["body"], $cf_distro_downloads_tag) && preg_match ("/\<Id\>(.+?)\<\/Id\>/is", $cf_distro_downloads_tag[1], $cf_distro_downloads_id_tag) && preg_match ("/\<DomainName\>(.+?)\<\/DomainName\>/is", $cf_distro_downloads_tag[1], $cf_distro_downloads_dname_tag))
1247 return array ("success" => true, "code" => null, "message" => null, "distro_downloads_id" => trim ($cf_distro_downloads_id_tag[1]), "distro_downloads_dname" => trim ($cf_distro_downloads_dname_tag[1]));
1248 else /* Else, we use a default error code and message. */
1249 return array ("success" => false, "code" => -97, "message" => _x ("Unable to create/read Amazon® CloudFront Downloads Distro. Unexpected response.", "s2member-admin", "s2member"));
1250 }
1251 else if (isset ($cf_response["code"], $cf_response["message"]))
1252 /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® CloudFront API call. Feel free to exclude `%s` if you like. */
1253 return array ("success" => false, "code" => $cf_response["code"], "message" => sprintf (_x ("Unable to create Amazon® CloudFront Downloads Distro. %s", "s2member-admin", "s2member"), $cf_response["message"]));
1254 else /* Else, we use a default error code and message. */
1255 return array ("success" => false, "code" => -98, "message" => _x ("Unable to create Amazon® CloudFront Downloads Distro. Connection failed.", "s2member-admin", "s2member"));
1256 }
1257 /**/
1258 else if ($distro_type === "streaming") /* Create a `streaming` Distro? A different XML schema. */
1259 {
1260 $cf_location = "/2010-11-01/streaming-distribution"; /* Create streaming distro. */
1261 $cf_distro_streaming_reference = time () . "." . md5 ("streaming" . $s3c["bucket"] . $s3c["access_key"] . $s3c["secret_key"] . $cfc["private_key"] . $cfc["private_key_id"] . $cfc["distro_streaming_cname"]);
1262 $cf_distro_streaming_xml = '<?xml version="1.0" encoding="UTF-8"?><StreamingDistributionConfig xmlns="http://cloudfront.amazonaws.com/doc/2010-11-01/"><S3Origin><DNSName>' . esc_html ($s3c["bucket"]) . '.s3.amazonaws.com</DNSName><OriginAccessIdentity>origin-access-identity/cloudfront/' . esc_html ($cfc["distros_access_id"]) . '</OriginAccessIdentity></S3Origin><CallerReference>' . esc_html ($cf_distro_streaming_reference) . '</CallerReference>' . (($cfc["distro_streaming_cname"]) ? '<CNAME>' . esc_html ($cfc["distro_streaming_cname"]) . '</CNAME>' : '') . '<Comment>' . esc_html (sprintf (_x ("Created by s2Member, for S3 Bucket: %s.", "s2member-admin", "s2member"), $s3c["bucket"])) . '</Comment><Enabled>true</Enabled><DefaultRootObject>index.html</DefaultRootObject><TrustedSigners><Self/></TrustedSigners></StreamingDistributionConfig>';
1263 $cf_args = array ("method" => "POST", "body" => $cf_distro_streaming_xml, "headers" => array ("Host" => $cf_domain, "Content-Type" => "application/xml", "Date" => $cf_date, "Authorization" => "AWS " . $cfc["access_key"] . ":" . $cf_signature));
1264 /**/
1265 if (($cf_response = c_ws_plugin__s2member_utils_urls::remote ("https://" . $cf_domain . $cf_location, false, array_merge ($cf_args, array ("timeout" => 20)), "array")) && ($cf_response["code"] === 200 || $cf_response["code"] === 201 /* Created. */))
1266 {
1267 if (preg_match ("/\<StreamingDistribution.*?\>(.+?)\<\/StreamingDistribution\>/is", $cf_response["body"], $cf_distro_streaming_tag) && preg_match ("/\<Id\>(.+?)\<\/Id\>/is", $cf_distro_streaming_tag[1], $cf_distro_streaming_id_tag) && preg_match ("/\<DomainName\>(.+?)\<\/DomainName\>/is", $cf_distro_streaming_tag[1], $cf_distro_streaming_dname_tag))
1268 return array ("success" => true, "code" => null, "message" => null, "distro_streaming_id" => trim ($cf_distro_streaming_id_tag[1]), "distro_streaming_dname" => trim ($cf_distro_streaming_dname_tag[1]));
1269 else /* Else, we use a default error code and message. */
1270 return array ("success" => false, "code" => -97, "message" => _x ("Unable to create/read Amazon® CloudFront Streaming Distro. Unexpected response.", "s2member-admin", "s2member"));
1271 }
1272 else if (isset ($cf_response["code"], $cf_response["message"]))
1273 /* translators: In this translation, `%s` may be filled with an English message, which comes from the Amazon® CloudFront API call. Feel free to exclude `%s` if you like. */
1274 return array ("success" => false, "code" => $cf_response["code"], "message" => sprintf (_x ("Unable to create Amazon® CloudFront Streaming Distro. %s", "s2member-admin", "s2member"), $cf_response["message"]));
1275 else /* Else, we use a default error code and message. */
1276 return array ("success" => false, "code" => -98, "message" => _x ("Unable to create Amazon® CloudFront Streaming Distro. Connection failed.", "s2member-admin", "s2member"));
1277 }
1278 }
1279 else /* Else, we use a default error code and message. */
1280 return array ("success" => false, "code" => -99, "message" => _x ("Unable to create Amazon® CloudFront Distro. Invalid Distro type.", "s2member-admin", "s2member"));
1281 }
1282 }
1283 }
1284 ?>