PluginProbe
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions / 111017
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions v111017
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 111017, at includes/classes/files-in.inc.php

1,253 lines 112.6 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`, `check_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["check_user"] = ($creating) ? @$create["check_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 $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;
77 $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;
78 $using_amazon_storage = ($using_amazon_s3_storage || $using_amazon_cf_storage) ? true : false; /* Either/or? */
79 /**/
80 $excluded = apply_filters ("ws_plugin__s2member_check_file_download_access_excluded", false, get_defined_vars ());
81 $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;
82 $checking_user = ($excluded || $valid_file_download_key || ($creating && (!isset ($req["check_user"]) || !filter_var ($req["check_user"], FILTER_VALIDATE_BOOLEAN)) && (!isset ($req["count_against_user"]) || !filter_var ($req["count_against_user"], FILTER_VALIDATE_BOOLEAN)))) ? false : true;
83 $updating_user_counter = (!$checking_user || ($creating && (!isset ($req["count_against_user"]) || !filter_var ($req["count_against_user"], FILTER_VALIDATE_BOOLEAN)))) ? false : true;
84 /**/
85 if (($serving || $creating) && $checking_user) /* In either case, the following routines apply whenever we ARE ``$checking_user``. */
86 {
87 if (!$using_amazon_storage && !file_exists ($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"] . "/" . $req["file_download"]))
88 {
89 if ($serving) /* We only need this section when/if we're actually serving. */
90 status_header(404) . header ("Content-Type: text/html; charset=utf-8") . eval ('while (@ob_end_clean ());') #
91 . exit (_x ('<strong>404: Sorry, file not found.</strong> Please contact Support for assistance.', "s2member-front", "s2member"));
92 /**/
93 else /* Else return false. */
94 return false;
95 }
96 /**/
97 else if ($req["file_download_key"] && is_string ($req["file_download_key"]) && !$valid_file_download_key)
98 {
99 if ($serving) /* We only need this section when/if we're actually serving. */
100 status_header(503) . header ("Content-Type: text/html; charset=utf-8") . eval ('while (@ob_end_clean ());') #
101 . exit (_x ('<strong>503 ( Invalid Key ):</strong> Sorry, your access to this file has expired. Please contact Support for assistance.', "s2member-front", "s2member"));
102 /**/
103 else /* Else return false. */
104 return false;
105 }
106 /**/
107 else if ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page"] || ($file_downloads_enabled_by_site_owner = $min_level_4_downloads = c_ws_plugin__s2member_files::min_level_4_downloads ()) === false)
108 {
109 if ($serving) /* We only need remote functionality when/if we're actually serving. */
110 if (!has_filter ("ws_plugin__s2member_check_file_download_access_user", "c_ws_plugin__s2member_files_in::check_file_remote_authorization"))
111 add_filter ("ws_plugin__s2member_check_file_download_access_user", "c_ws_plugin__s2member_files_in::check_file_remote_authorization", 10, 2);
112 /**/
113 if ($creating) /* We only need remote functionality when/if we're actually serving. */
114 if (has_filter ("ws_plugin__s2member_check_file_download_access_user", "c_ws_plugin__s2member_files_in::check_file_remote_authorization"))
115 remove_filter ("ws_plugin__s2member_check_file_download_access_user", "c_ws_plugin__s2member_files_in::check_file_remote_authorization", 10, 2);
116 /**/
117 if ((isset ($file_downloads_enabled_by_site_owner, $min_level_4_downloads) && $file_downloads_enabled_by_site_owner === false) || ($file_downloads_enabled_by_site_owner = $min_level_4_downloads = c_ws_plugin__s2member_files::min_level_4_downloads ()) === false)
118 {
119 if ($serving) /* We only need this section when/if we're actually serving. */
120 status_header(503) . header ("Content-Type: text/html; charset=utf-8") . eval ('while (@ob_end_clean ());') #
121 . exit (_x ('<strong>503: Basic File Downloads are NOT enabled yet.</strong> Please contact Support for assistance. If you are the site owner, please configure: <code>s2Member -> Download Options -> Basic Download Restrictions</code>.', "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) || !is_array ($user_file_downloads = c_ws_plugin__s2member_files::user_downloads ($user)) || (!$user->has_cap ("administrator") && (!$user_file_downloads["allowed"] || !$user_file_downloads["allowed_days"])))
128 {
129 if (preg_match ("/^access[_\-]s2member[_\-]level([0-9]+)\//", $req["file_download"], $m) && strlen ($level_req = $m[1]) && (!is_object ($user) || empty ($user->ID) || !$user->has_cap ("access_s2member_level" . $level_req)))
130 {
131 if ($serving) /* We only need this section when/if we're actually serving. */
132 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 ();
133 /**/
134 else /* Else return false. */
135 return false;
136 }
137 /**/
138 else if (preg_match ("/^access[_\-]s2member[_\-]ccap[_\-](.+?)\//", $req["file_download"], $m) && strlen ($ccap_req = preg_replace ("/-/", "_", $m[1])) && (!is_object ($user) || empty ($user->ID) || !$user->has_cap ("access_s2member_ccap_" . $ccap_req)))
139 {
140 if ($serving) /* We only need this section when/if we're actually serving. */
141 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 ();
142 /**/
143 else /* Else return false. */
144 return false;
145 }
146 /**/
147 else if ($serving) /* We only need this section when/if we're actually serving. */
148 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 ();
149 /**/
150 else /* Else return false. */
151 return false;
152 }
153 /**/
154 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))
155 {
156 if ($serving) /* We only need this section when/if we're actually serving. */
157 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 ();
158 /**/
159 else /* Else return false. */
160 return false;
161 }
162 /**/
163 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))
164 {
165 if ($serving) /* We only need this section when/if we're actually serving. */
166 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 ();
167 /**/
168 else /* Else return false. */
169 return false;
170 }
171 /**/
172 else if ($serving || $creating) /* In either case, the following routines apply. */
173 {
174 $user_previous_file_downloads = 0; /* Downloads the User has already; in current period/cycle. */
175 $user_already_downloaded_this_file = $user_already_downloaded_a_streaming_variation_of_this_file = false;
176 /**/
177 $user_file_download_access_log = (array)get_user_option ("s2member_file_download_access_log", $user_id);
178 $user_file_download_access_arc = (array)get_user_option ("s2member_file_download_access_arc", $user_id);
179 /**/
180 $streaming_file_extns = c_ws_plugin__s2member_utils_strings::preg_quote_deep ($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["streaming_file_extns"], "/");
181 $streaming_variations = /* Only count one streaming media file variation. */ "/\.(" . implode ("|", $streaming_file_extns) . ")$/i";
182 /**/
183 $max_download_period_days = c_ws_plugin__s2member_files::max_download_period (); /* Max download period; counted in days. */
184 /**/
185 foreach ($user_file_download_access_log as $user_file_download_access_log_entry_key => $user_file_download_access_log_entry)
186 {
187 if (strtotime ($user_file_download_access_log_entry["date"]) < strtotime ("-" . $max_download_period_days . " days"))
188 {
189 unset($user_file_download_access_log[$user_file_download_access_log_entry_key]);
190 $user_file_download_access_arc[] = $user_file_download_access_log_entry;
191 }
192 else if (strtotime ($user_file_download_access_log_entry["date"]) >= strtotime ("-" . $user_file_downloads["allowed_days"] . " days"))
193 {
194 $user_previous_file_downloads++; /* A previous file that counts against this User/Member, since it's already in the log. */
195 /**/
196 if ($user_file_download_access_log_entry["file"] === $req["file_download"]) /* Already downloaded this file before? */
197 $user_already_downloaded_this_file = true; /* Already downloaded this file? If yes, mark this flag as true. */
198 /**/
199 else if (preg_replace ($streaming_variations, "", $user_file_download_access_log_entry["file"]) === preg_replace ($streaming_variations, "", $req["file_download"]))
200 $user_already_downloaded_this_file = $user_already_downloaded_a_streaming_variation_of_this_file = true;
201 }
202 }
203 /**/
204 if (!$user_already_downloaded_this_file && !$user_already_downloaded_a_streaming_variation_of_this_file && !$user->has_cap ("administrator") && $user_previous_file_downloads >= $user_file_downloads["allowed"])
205 {
206 if ($serving) /* We only need this section when/if we're actually serving. */
207 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 ();
208 /**/
209 else /* Else return false. */
210 return false;
211 }
212 /**/
213 else if (!$user_already_downloaded_this_file && !$user_already_downloaded_a_streaming_variation_of_this_file)
214 $user_file_download_access_log[] = array ("date" => date ("Y-m-d"), "file" => $req["file_download"]);
215 /**/
216 if ($updating_user_counter) /* By default, we do NOT update the counter when a URL is being created; but this behavior can be modified. */
217 {
218 update_user_option ($user_id, "s2member_file_download_access_arc", c_ws_plugin__s2member_utils_arrays::array_unique ($user_file_download_access_arc));
219 update_user_option ($user_id, "s2member_file_download_access_log", c_ws_plugin__s2member_utils_arrays::array_unique ($user_file_download_access_log));
220 }
221 }
222 }
223 }
224 else /* Otherwise, we're either NOT ``$checking_user``; or permission was granted with a valid File Download Key. */
225 {
226 if (!$using_amazon_storage && !file_exists ($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"] . "/" . $req["file_download"]))
227 {
228 if ($serving) /* We only need this section when/if we're actually serving. */
229 status_header(404) . header ("Content-Type: text/html; charset=utf-8") . eval ('while (@ob_end_clean ());') #
230 . exit (_x ('<strong>404: Sorry, file not found.</strong> Please contact Support for assistance.', "s2member-front", "s2member"));
231 /**/
232 else /* Else return false. */
233 return false;
234 }
235 }
236 /**/
237 if ($serving || $creating) /* In either case, the following routines apply. */
238 {
239 $basename = basename ($req["file_download"]);
240 $mimetypes = parse_ini_file (dirname (dirname (dirname (__FILE__))) . "/includes/mime-types.ini");
241 $extension = strtolower (substr ($req["file_download"], strrpos ($req["file_download"], ".") + 1));
242 /**/
243 $key = ($req["file_download_key"] && is_string ($req["file_download_key"])) ? $req["file_download_key"] : false;
244 /**/
245 $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);
246 $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);
247 $ssl = (isset ($req["file_ssl"])) ? filter_var ($req["file_ssl"], FILTER_VALIDATE_BOOLEAN) : ((is_ssl ()) ? true : false);
248 $storage = ($req["file_storage"] && is_string ($req["file_storage"])) ? strtolower ($req["file_storage"]) : false;
249 $remote = (isset ($req["file_remote"])) ? filter_var ($req["file_remote"], FILTER_VALIDATE_BOOLEAN) : false;
250 /**/
251 $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"]);
252 $rewrite_base = ($req["file_rewrite_base"] && is_string ($req["file_rewrite_base"])) ? $req["file_rewrite_base"] : false;
253 $rewrite = $rewriting = (!$rewrite_base && isset ($req["file_rewrite"])) ? filter_var ($req["file_rewrite"], FILTER_VALIDATE_BOOLEAN) : (($rewrite_base) ? true : false);
254 /**/
255 $skip_confirmation = (isset ($req["skip_confirmation"])) ? filter_var ($req["skip_confirmation"], FILTER_VALIDATE_BOOLEAN) : false;
256 $url_to_storage_source = (isset ($req["url_to_storage_source"])) ? filter_var ($req["url_to_storage_source"], FILTER_VALIDATE_BOOLEAN) : false;
257 /**/
258 $pathinfo = (!$using_amazon_storage) ? pathinfo (($file = $GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"] . "/" . $req["file_download"])) : array ();
259 $mimetype = ($mimetypes[$extension]) ? $mimetypes[$extension] : "application/octet-stream";
260 $length = (!$using_amazon_storage && $file) ? filesize ($file) : -1;
261 /**/
262 eval('foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v;');
263 do_action ("ws_plugin__s2member_during_file_download_access", get_defined_vars ());
264 unset ($__refs, $__v); /* Unset defined __refs, __v. */
265 /**/
266 if ($using_amazon_s3_storage && ($serving || ($creating && $url_to_storage_source)))
267 {
268 if ($serving) /* We only need this section when/if we're actually serving. */
269 wp_redirect(c_ws_plugin__s2member_files_in::amazon_s3_url ($req["file_download"], $stream, $inline, $ssl, $basename, $mimetype)) . exit ();
270 /**/
271 else /* Else return File Download URL. */
272 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 ());
273 }
274 /**/
275 else if ($using_amazon_cf_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_cf_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_cf_url ($req["file_download"], $stream, $inline, $ssl, $basename, $mimetype), get_defined_vars ());
282 }
283 /**/
284 else if ($creating && $rewriting) /* Creating a rewrite URL, pointing to local storage. */
285 {
286 $url = ($rewrite_base) ? rtrim ($rewrite_base, "/") : rtrim ($rewrite_base_guess, "/");
287 $url .= (isset ($req["file_download_key"])) ? (($key) ? "/s2member-file-download-key-" . $key : "") : "";
288 $url .= (isset ($req["file_stream"])) ? (($stream) ? "/s2member-file-stream" : "/s2member-file-stream-no") : "";
289 $url .= (isset ($req["file_inline"])) ? (($inline) ? "/s2member-file-inline" : "/s2member-file-inline-no") : "";
290 $url .= (isset ($req["file_storage"])) ? (($storage) ? "/s2member-file-storage-" . $storage : "") : "";
291 $url .= (isset ($req["file_remote"])) ? (($remote) ? "/s2member-file-remote" : "/s2member-file-remote-no") : "";
292 $url .= (isset ($req["skip_confirmation"])) ? (($skip_confirmation) ? "/s2member-skip-confirmation" : "/s2member-skip-confirmation-no") : "";
293 /**/
294 $url = $url . "/" . $req["file_download"]; /* File Download Access URL via `mod_rewrite` functionality. */
295 $url = ($ssl) ? preg_replace ("/^https?/", "https", $url) : preg_replace ("/^https?/", "http", $url);
296 /**/
297 return apply_filters ("ws_plugin__s2member_file_download_access_url", $url, get_defined_vars ());
298 }
299 /**/
300 else if ($creating) /* Else we're creating a URL w/ a query-string; w/ local storage. */
301 {
302 /* * Note: we don't URL encode unreserved chars. Improves media player compatibility. */
303 $_url_e_key = ($key) ? c_ws_plugin__s2member_utils_strings::urldecode_ur_chars_deep (urlencode ($key)) : "";
304 $_url_e_storage = ($storage) ? c_ws_plugin__s2member_utils_strings::urldecode_ur_chars_deep (urlencode ($storage)) : "";
305 $_url_e_file = c_ws_plugin__s2member_utils_strings::urldecode_ur_chars_deep (urlencode ($req["file_download"]));
306 /**/
307 $url = (isset ($req["file_download_key"])) ? (($key && $_url_e_key) ? "&s2member_file_download_key=" . $_url_e_key : "") : "";
308 $url .= (isset ($req["file_stream"])) ? (($stream) ? "&s2member_file_stream=yes" : "&s2member_file_stream=no") : "";
309 $url .= (isset ($req["file_inline"])) ? (($inline) ? "&s2member_file_inline=yes" : "&s2member_file_inline=no") : "";
310 $url .= (isset ($req["file_storage"])) ? (($storage && $_url_e_storage) ? "&s2member_file_storage=" . $_url_e_storage : "") : "";
311 $url .= (isset ($req["file_remote"])) ? (($remote) ? "&s2member_file_remote=yes" : "&s2member_file_remote=no") : "";
312 $url .= (isset ($req["skip_confirmation"])) ? (($skip_confirmation) ? "&s2member_skip_confirmation=yes" : "&s2member_skip_confirmation=no") : "";
313 /**/
314 $url = site_url ("/?" . ltrim ($url . "&s2member_file_download=/" . $_url_e_file, "&"));
315 $url = ($ssl) ? preg_replace ("/^https?/", "https", $url) : preg_replace ("/^https?/", "http", $url);
316 /**/
317 return apply_filters ("ws_plugin__s2member_file_download_access_url", $url, get_defined_vars ());
318 }
319 /**/
320 else /* Else, ``if ($serving)`` , use local storage option (default). */
321 {
322 @set_time_limit(0) . @ini_set ("zlib.output_compression", 0);
323 /**/
324 status_header(200); /* 200 OK status header. */
325 /**/
326 header("Accept-Ranges: none");
327 header("Content-Encoding: none");
328 header("Content-Type: " . $mimetype);
329 header("Expires: " . gmdate ("D, d M Y H:i:s", strtotime ("-1 week")) . " GMT");
330 header("Last-Modified: " . gmdate ("D, d M Y H:i:s") . " GMT");
331 header("Cache-Control: no-cache, must-revalidate, max-age=0");
332 header ("Cache-Control: post-check=0, pre-check=0", false);
333 header("Pragma: no-cache");
334 /**/
335 header('Content-Disposition: ' . (($inline) ? "inline" : "attachment") . '; filename="' . $basename . '"');
336 /**/
337 eval('while (@ob_end_clean ());'); /* End/clean any output buffers that may exist already. */
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) . header ("Content-Type: text/html; charset=utf-8") . eval ('while (@ob_end_clean ());') #
369 . exit (_x ('<strong>503: Access denied.</strong> Invalid File Download specs.', "s2member-front", "s2member"));
370 /**/
371 else if ($creating) /* We only need this section when/if we're creating a URL. */
372 return false;
373 /**/
374 do_action ("ws_plugin__s2member_after_file_download_access", get_defined_vars ());
375 /**/
376 return ($creating) ? false : null; /* If creating, false. */
377 }
378 /**
379 * Generates a File Download URL for access to a file protected by s2Member.
380 *
381 * @package s2Member\Files
382 * @since 110926
383 *
384 * @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.
385 * 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`, `check_user`.
386 * @param bool $get_streamer_array Optional. Defaults to `false`. If `true`, this function will return an array with the following elements: `streamer`, `file`, `url`. For further details, please review this section in your Dashboard: `s2Member -> Download Options -> JW Player® & RTMP Protocol Examples`.
387 * @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.
388 *
389 * @see s2Member\API_Functions\s2member_file_download_url()
390 */
391 public static function create_file_download_url ($config = FALSE, $get_streamer_array = FALSE)
392 {
393 eval('foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v;');
394 do_action ("ws_plugin__s2member_before_create_file_download_url", get_defined_vars ()); /* Be VERY careful, if you use this Hook. */
395 unset ($__refs, $__v); /* Unset defined __refs, __v. */
396 /**/
397 $config = (is_array ($config)) ? $config : array (); /* This absolutely MUST be an array. */
398 /**/
399 $config["file_download"] = (isset ($config["file_download"]) && is_string ($config["file_download"])) ? trim ($config["file_download"], "/") : @$config["file_download"];
400 $config["file_download_key"] = (isset ($config["file_download"]) && is_string ($config["file_download"]) && !empty ($config["file_download_key"])) ? 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"];
401 /**/
402 $config["url_to_storage_source"] = /* Force a streaming URL here via ``$get_streamer_array``? */ ($get_streamer_array) ? true : @$config["url_to_storage_source"];
403 $config["file_stream"] = /* Force a streaming URL here via ``$get_streamer_array``? */ ($get_streamer_array) ? true : @$config["file_stream"];
404 /**/
405 if (($_url = c_ws_plugin__s2member_files_in::check_file_download_access (($create_file_download_url = $config))) /* Successfully created a URL to the file? */)
406 {
407 eval('foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v;');
408 do_action ("ws_plugin__s2member_during_create_file_download_url", get_defined_vars ()); /* Be VERY careful, if you use this Hook. */
409 unset ($__refs, $__v); /* Unset defined __refs, __v. */
410 /**/
411 $extension = strtolower (substr ($config["file_download"], strrpos ($config["file_download"], ".") + 1));
412 $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);
413 $ssl = (isset ($config["file_ssl"])) ? filter_var ($config["file_ssl"], FILTER_VALIDATE_BOOLEAN) : ((is_ssl ()) ? true : false);
414 /**/
415 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, "check_user" => false)))))
416 $return = array ("streamer" => $streamer, "file" => preg_replace ("/^" . preg_quote ($streamer, "/") . "\//", "", $_url), "url" => preg_replace ("/^.+?\:/", (($ssl) ? "https:" : "http:"), $url));
417 /**/
418 else if ($get_streamer_array && $streaming && is_array ($ups = c_ws_plugin__s2member_utils_urls::parse_url ($_url)) && isset ($ups["scheme"], $ups["host"]) && ($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, "check_user" => false)))))
419 $return = array ("streamer" => $streamer, "file" => preg_replace ("/^" . preg_quote ($streamer, "/") . "\//", "", $_url), "url" => preg_replace ("/^.+?\:/", (($ssl) ? "https:" : "http:"), $url));
420 /**/
421 else if ($get_streamer_array) /* Else, we MUST return false here, unable to acquire streamer/file. */
422 $return = false; /* We MUST return false here, unable to acquire streamer. */
423 /**/
424 else /* Else return URL string ( ``$get_streamer_array`` is false ). */
425 $return = $_url; /* Else return URL string. */
426 }
427 /**/
428 return apply_filters ("ws_plugin__s2member_create_file_download_url", ((isset ($return)) ? $return : false), get_defined_vars ());
429 }
430 /**
431 * Checks Header Authorization for Remote File Downloads.
432 *
433 * @package s2Member\Files
434 * @since 110926
435 *
436 * @attaches-to: ``add_filter("ws_plugin__s2member_check_file_download_access_user");``
437 *
438 * @param obj $user Expects a WP_User object passed in by the Filter.
439 * @return obj A `WP_User` object, possibly obtained through Header Authorization.
440 */
441 public static function check_file_remote_authorization ($user = FALSE)
442 {
443 eval('foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v;');
444 do_action ("ws_plugin__s2member_before_check_file_remote_authorization", get_defined_vars ());
445 unset ($__refs, $__v); /* Unset defined __refs, __v. */
446 /**/
447 $_g = c_ws_plugin__s2member_utils_strings::trim_deep (stripslashes_deep (((!empty ($_GET)) ? $_GET : array ())));
448 /**/
449 if (!is_object ($user) && isset ($_g["s2member_file_remote"]) && filter_var ($_g["s2member_file_remote"], FILTER_VALIDATE_BOOLEAN))
450 {
451 do_action ("ws_plugin__s2member_during_check_file_remote_authorization_before", get_defined_vars ());
452 /**/
453 if (empty ($_SERVER["PHP_AUTH_USER"]) || empty ($_SERVER["PHP_AUTH_PW"]) || !user_pass_ok ($_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"]))
454 {
455 header('WWW-Authenticate: Basic realm="' . c_ws_plugin__s2member_utils_strings::esc_dq (strip_tags (_x ("Members Only", "s2member-front", "s2member"))) . '"');
456 /**/
457 status_header(401); /* Send an unauthorized 401 status header now. */
458 header("Content-Type: text/html; charset=utf-8"); /* Content-Type with UTF-8. */
459 eval('while (@ob_end_clean ());'); /* End/clean any output buffers that may exist. */
460 /**/
461 exit(_x ('<strong>401:</strong> Sorry, access denied.', "s2member-front", "s2member"));
462 }
463 else if (is_object ($_user = new WP_User ($_SERVER["PHP_AUTH_USER"])) && !empty ($_user->ID))
464 $user = $_user; /* Now assign ``$user``. */
465 /**/
466 do_action ("ws_plugin__s2member_during_check_file_remote_authorization_after", get_defined_vars ());
467 }
468 /**/
469 return apply_filters ("ws_plugin__s2member_check_file_remote_authorization", $user, get_defined_vars ());
470 }
471 /**
472 * Checks a File Download Key for validity.
473 *
474 * @package s2Member\Files
475 * @since 110926
476 *
477 * @param str $file Input File Download to validate.
478 * @param str $key Input File Download Key to validate.
479 * @return bool True if valid, else false.
480 */
481 public static function check_file_download_key ($file = FALSE, $key = FALSE)
482 {
483 eval('foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v;');
484 do_action ("_ws_plugin__s2member_before_check_file_download_key", get_defined_vars ());
485 unset ($__refs, $__v); /* Unset defined __refs, __v. */
486 /**/
487 if ($file && is_string ($file) && ($file = trim ($file, "/")) && $key && is_string ($key)) /* Requirements to properly validate. */
488 {
489 if ($key === c_ws_plugin__s2member_files::file_download_key ($file) || $key === c_ws_plugin__s2member_files::file_download_key ("/" . $file))
490 $valid = true; /* File Download Key is valid. */
491 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"))
492 $valid = true; /* File Download Key is valid. */
493 else if ($key === c_ws_plugin__s2member_files::file_download_key ($file, "universal") || $key === c_ws_plugin__s2member_files::file_download_key ("/" . $file, "universal"))
494 $valid = true; /* File Download Key is valid. */
495 }
496 /**/
497 return apply_filters ("ws_plugin__s2member_check_file_download_key", ((isset ($valid) && $valid) ? true : false), get_defined_vars ());
498 }
499 /**
500 * Creates an Amazon® S3 HMAC-SHA1 signature.
501 *
502 * @package s2Member\Files
503 * @since 110524RC
504 *
505 * @param str $string Input string/data, to be signed by this routine.
506 * @return str An HMAC-SHA1 signature for Amazon® S3.
507 */
508 public static function amazon_s3_sign ($string = FALSE)
509 {
510 $s3c["secret_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_secret_key"];
511 /**/
512 return c_ws_plugin__s2member_utils_strings::hmac_sha1_sign ((string)$string, $s3c["secret_key"]);
513 }
514 /**
515 * Creates an Amazon® S3 HMAC-SHA1 signature URL.
516 *
517 * @package s2Member\Files
518 * @since 110926
519 *
520 * @param str $file Input file path, to be signed by this routine.
521 * @param bool $stream Is this resource file to be served as streaming media?
522 * @param bool $inline Is this resource file to be served inline, or no?
523 * @param bool $ssl Is this resource file to be served via SSL, or no?
524 * @param str $basename The absolute basename of the resource file.
525 * @param str $mimetype The MIME content-type of the resource file.
526 * @return str An HMAC-SHA1 signature URL for Amazon® S3.
527 */
528 public static function amazon_s3_url ($file = FALSE, $stream = FALSE, $inline = FALSE, $ssl = FALSE, $basename = FALSE, $mimetype = FALSE)
529 {
530 $file = trim ((string)$file, "/"); /* Trim / force string. */
531 /**/
532 foreach ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"] as $option => $option_value)
533 if (preg_match ("/^amazon_s3_files_/", $option) && ($option = preg_replace ("/^amazon_s3_files_/", "", $option)))
534 $s3c[$option] = $option_value;
535 /**/
536 $s3c["expires"] = strtotime ("+" . apply_filters ("ws_plugin__s2member_amazon_s3_file_expires_time", "30 seconds", get_defined_vars ()));
537 /**/
538 $s3_file = add_query_arg (c_ws_plugin__s2member_utils_strings::urldecode_ur_chars_deep (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);
539 $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);
540 $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));
541 /**/
542 $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;
543 return add_query_arg (c_ws_plugin__s2member_utils_strings::urldecode_ur_chars_deep /* Don't encode unreserved chars. Maximizes media player compatibility. */
544 (urlencode_deep (array ("AWSAccessKeyId" => $s3c["access_key"], "Expires" => $s3c["expires"], "Signature" => $s3_signature))), $s3_url);
545 }
546 /**
547 * Auto-configures an Amazon® S3 Bucket's ACLs.
548 *
549 * @package s2Member\Files
550 * @since 110926
551 *
552 * @return array Array containing a true `success` element on success, else a failure array.
553 * Failure array will contain a failure `code`, and a failure `message`.
554 */
555 public static function amazon_s3_auto_configure_acls ()
556 {
557 foreach ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"] as $option => $option_value)
558 if (preg_match ("/^amazon_s3_files_/", $option) && ($option = preg_replace ("/^amazon_s3_files_/", "", $option)))
559 $s3c[$option] = $option_value;
560 /**/
561 $cfc["distros_s3_access_id"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_cf_files_distros_s3_access_id"];
562 /**/
563 if ($s3c["bucket"] && $s3c["access_key"] && $s3c["secret_key"]) /* Must have Amazon® S3 Bucket/Keys. */
564 {
565 $s3_date = gmdate ("D, d M Y H:i:s") . " GMT";
566 $s3_location = ((strtolower ($s3c["bucket"]) !== $s3c["bucket"])) ? "/" . $s3c["bucket"] . "/?acl" : "/?acl";
567 $s3_domain = ((strtolower ($s3c["bucket"]) !== $s3c["bucket"])) ? "s3.amazonaws.com" : $s3c["bucket"] . ".s3.amazonaws.com";
568 $s3_signature = base64_encode (c_ws_plugin__s2member_files_in::amazon_s3_sign ("GET\n\n\n" . $s3_date . "\n/" . $s3c["bucket"] . "/?acl"));
569 $s3_args = array ("method" => "GET", "headers" => array ("Host" => $s3_domain, "Date" => $s3_date, "Authorization" => "AWS " . $s3c["access_key"] . ":" . $s3_signature));
570 /**/
571 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)
572 {
573 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"))))
574 {
575 $s3_owner = array ("access_id" => trim ($s3_owner_id_tag[1]), "display_name" => trim ($s3_owner_display_name_tag[1]));
576 $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>';
577 $s3_signature = base64_encode (c_ws_plugin__s2member_files_in::amazon_s3_sign ("PUT\n\napplication/xml\n" . $s3_date . "\n/" . $s3c["bucket"] . "/?acl"));
578 $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));
579 /**/
580 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)
581 {
582 $s3_location = ((strtolower ($s3c["bucket"]) !== $s3c["bucket"])) ? "/" . $s3c["bucket"] . "/?policy" : "/?policy";
583 $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"]) . '/*"}]}';
584 $s3_signature = base64_encode (c_ws_plugin__s2member_files_in::amazon_s3_sign ("PUT\n\napplication/json\n" . $s3_date . "\n/" . $s3c["bucket"] . "/?policy"));
585 $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));
586 /**/
587 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. */)))
588 {
589 $s3_location = ((strtolower ($s3c["bucket"]) !== $s3c["bucket"])) ? "/" . $s3c["bucket"] . "/crossdomain.xml" : "/crossdomain.xml";
590 $s3_policy_xml = trim (c_ws_plugin__s2member_utilities::evl (file_get_contents (dirname (dirname (__FILE__)) . "/templates/cfg-files/s2-cross-xml.php")));
591 $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"));
592 $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));
593 /**/
594 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)
595 return array ("success" => true, "code" => null, "message" => null); /* Successfully configured Amazon® S3 Bucket ACLs and Policy. */
596 /**/
597 else if (isset ($s3_response["code"], $s3_response["message"]))
598 /* 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. */
599 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"]));
600 /**/
601 else /* Else, we use a default error code and message. */
602 return array ("success" => false, "code" => -94, "message" => _x ("Unable to update existing Amazon® S3 Cross-Domain Policy. Connection failed.", "s2member-admin", "s2member"));
603 }
604 else if (isset ($s3_response["code"], $s3_response["message"]))
605 /* 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. */
606 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"]));
607 /**/
608 else /* Else, we use a default error code and message. */
609 return array ("success" => false, "code" => -95, "message" => _x ("Unable to update existing Amazon® S3 Bucket Policy. Connection failed.", "s2member-admin", "s2member"));
610 }
611 else if (isset ($s3_response["code"], $s3_response["message"]))
612 /* 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. */
613 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"]));
614 /**/
615 else /* Else, we use a default error code and message. */
616 return array ("success" => false, "code" => -96, "message" => _x ("Unable to update existing Amazon® S3 Bucket ACLs. Connection failed.", "s2member-admin", "s2member"));
617 }
618 else /* Else, we use a default error code and message. */
619 return array ("success" => false, "code" => -97, "message" => _x ("Unable to acquire/read existing Amazon® S3 Bucket ACLs. Unexpected response.", "s2member-admin", "s2member"));
620 }
621 else if (isset ($s3_response["code"], $s3_response["message"]))
622 /* 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. */
623 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"]));
624 /**/
625 else /* Else, we use a default error code and message. */
626 return array ("success" => false, "code" => -98, "message" => _x ("Unable to acquire existing Amazon® S3 Bucket ACLs. Connection failed.", "s2member-admin", "s2member"));
627 }
628 else /* Else, we use a default error code and message. */
629 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"));
630 }
631 /**
632 * Creates an Amazon® CloudFront HMAC-SHA1 signature.
633 *
634 * @package s2Member\Files
635 * @since 110926
636 *
637 * @param str $string Input string/data, to be signed by this routine.
638 * @return str An HMAC-SHA1 signature for Amazon® CloudFront.
639 */
640 public static function amazon_cf_sign ($string = FALSE)
641 {
642 $cfc["secret_key"] = $s3c["secret_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_secret_key"];
643 /**/
644 return c_ws_plugin__s2member_utils_strings::hmac_sha1_sign ((string)$string, ($cfc["secret_key"] = $s3c["secret_key"]));
645 }
646 /**
647 * Creates an Amazon® CloudFront RSA-SHA1 signature.
648 *
649 * @package s2Member\Files
650 * @since 110926
651 *
652 * @param str $string Input string/data, to be signed by this routine.
653 * @return str|bool An RSA-SHA1 signature for Amazon® CloudFront, else false on failure.
654 *
655 * @todo Double underscores *( i.e. base64 padding chars )* in the signature seem to cause issues for Amazon® CloudFront?
656 * See ticket: {@link https://forums.aws.amazon.com/thread.jspa?messageID=286182&#286182}
657 */
658 public static function amazon_cf_rsa_sign ($string = FALSE)
659 {
660 $cfc["private_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_cf_files_private_key"];
661 /**/
662 return c_ws_plugin__s2member_utils_strings::rsa_sha1_sign ((string)$string, $cfc["private_key"]);
663 }
664 /**
665 * Creates an Amazon® CloudFront RSA-SHA1 signature URL.
666 *
667 * @package s2Member\Files
668 * @since 110926
669 *
670 * @param str $file Input file path, to be signed by this routine.
671 * @param bool $stream Is this resource file to be served as streaming media?
672 * @param bool $inline Is this resource file to be served inline, or no?
673 * @param bool $ssl Is this resource file to be served via SSL, or no?
674 * @param str $basename The absolute basename of the resource file.
675 * @param str $mimetype The MIME content-type of the resource file.
676 * @return str An RSA-SHA1 signature URL for Amazon® CloudFront.
677 */
678 public static function amazon_cf_url ($file = FALSE, $stream = FALSE, $inline = FALSE, $ssl = FALSE, $basename = FALSE, $mimetype = FALSE)
679 {
680 $file = trim ((string)$file, "/"); /* Trim / force string. */
681 /**/
682 foreach ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"] as $option => $option_value)
683 if (preg_match ("/^amazon_cf_files_/", $option) && ($option = preg_replace ("/^amazon_cf_files_/", "", $option)))
684 $cfc[$option] = $option_value;
685 /**/
686 $cfc["expires"] = strtotime ("+" . apply_filters ("ws_plugin__s2member_amazon_cf_file_expires_time", "24 hours", get_defined_vars ()));
687 /**/
688 $cf_extn = strtolower (substr ($file, strrpos ($file, ".") + 1)); /* Parses the file extension out so we can scan it in some special scenarios. */
689 $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 ()));
690 $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;
691 $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;
692 $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 ( will NOT jive ). */
693 $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"] . '}}}]}';
694 /**/
695 $cf_signature = c_ws_plugin__s2member_files_in::amazon_cf_rsa_sign ($cf_policy);
696 $cf_base64_url_safe_policy = c_ws_plugin__s2member_utils_strings::base64_url_safe_encode ($cf_policy, array ("+", "=", "/"), array ("-", "_", "~"), false);
697 $cf_base64_url_safe_signature = c_ws_plugin__s2member_utils_strings::base64_url_safe_encode ($cf_signature, array ("+", "=", "/"), array ("-", "_", "~"), false);
698 /**/
699 return add_query_arg (c_ws_plugin__s2member_utils_strings::urldecode_ur_chars_deep /* Don't encode unreserved chars. Maximizes media player compatibility. */
700 (urlencode_deep (array ("Policy" => $cf_base64_url_safe_policy, "Signature" => $cf_base64_url_safe_signature, "Key-Pair-Id" => $cfc["private_key_id"]))), $cf_url);
701 }
702 /**
703 * Auto-configures Amazon® S3/CloudFront distros.
704 *
705 * @package s2Member\Files
706 * @since 110926
707 *
708 * @return array Array containing a true `success` element on success, else a failure array.
709 * Failure array will contain a failure `code`, and a failure `message`.
710 */
711 public static function amazon_cf_auto_configure_distros ()
712 {
713 foreach ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"] as $option => $option_value)
714 if (preg_match ("/^amazon_cf_files_/", $option) && ($option = preg_replace ("/^amazon_cf_files_/", "", $option)))
715 $cfc[$option] = $option_value;
716 /**/
717 $s3c["bucket"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_bucket"];
718 $cfc["access_key"] = $s3c["access_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_access_key"];
719 $cfc["secret_key"] = $s3c["secret_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_secret_key"];
720 /**/
721 if ($s3c["bucket"] && $s3c["access_key"] && $s3c["secret_key"]) /* We MUST have an Amazon® S3 Bucket and Keys. */
722 {
723 if ($cfc["private_key"] && $cfc["private_key_id"]) /* We MUST have Amazon® CloudFront Keys in order to auto-configure. */
724 {
725 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)))
726 {
727 if (!$cfc["distro_downloads_id"] || ($cfc["distro_downloads_id"] && $cf_get_response && !$cf_get_response["success"] && $cf_get_response["code"] === 404))
728 $cf_distro_downloads_clear = true; /* Clear, ready for a new one. */
729 /**/
730 else if ($cfc["distro_downloads_id"] && $cf_get_response && $cf_get_response["success"] && !$cf_get_response["deployed"])
731 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"));
732 /**/
733 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"])
734 $cf_distro_downloads_clear = true; /* Clear, ready for a new one. */
735 /**/
736 else if (isset ($cf_del_response["code"], $cf_del_response["message"]))
737 /* 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. */
738 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"]));
739 /**/
740 if (isset ($cf_distro_downloads_clear) && $cf_distro_downloads_clear) /* Successfully cleared? Ready for a new one? */
741 {
742 unset ($cf_get_response, $cf_del_response); /* Unset these before processing additional routines. Prevents problems in error reporting. */
743 /**/
744 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)))
745 {
746 if (!$cfc["distro_streaming_id"] || ($cfc["distro_streaming_id"] && $cf_get_response && !$cf_get_response["success"] && $cf_get_response["code"] === 404))
747 $cf_distro_streaming_clear = true; /* Clear, ready for a new one. */
748 /**/
749 else if ($cfc["distro_streaming_id"] && $cf_get_response && $cf_get_response["success"] && !$cf_get_response["deployed"])
750 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"));
751 /**/
752 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"])
753 $cf_distro_streaming_clear = true; /* Clear, ready for a new one. */
754 /**/
755 else if (isset ($cf_del_response["code"], $cf_del_response["message"]))
756 /* 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. */
757 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"]));
758 /**/
759 if (isset ($cf_distro_streaming_clear) && $cf_distro_streaming_clear) /* Successfully cleared? Ready for a new one? */
760 {
761 unset ($cf_get_response, $cf_del_response); /* Unset these before processing additional routines. Prevents problems in error reporting. */
762 /**/
763 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)))
764 {
765 if (!$cfc["distros_access_id"] || ($cfc["distros_access_id"] && $cf_get_response && !$cf_get_response["success"] && $cf_get_response["code"] === 404))
766 $cf_distros_access_clear = true; /* Clear, ready for a new one. */
767 /**/
768 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"])
769 $cf_distros_access_clear = true; /* Clear, ready for a new one. */
770 /**/
771 else if (isset ($cf_del_response["code"], $cf_del_response["message"]))
772 /* 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. */
773 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"]));
774 /**/
775 if (isset ($cf_distros_access_clear) && $cf_distros_access_clear) /* Successfully cleared? Ready for a new one? */
776 {
777 unset ($cf_get_response, $cf_del_response); /* Unset these before processing additional routines. Prevents problems in error reporting. */
778 /**/
779 $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" => ""));
780 $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" => "");
781 c_ws_plugin__s2member_menu_pages::update_all_options ($cf_options, true, false, false, false, false);
782 /**/
783 if (($cf_response = c_ws_plugin__s2member_files_in::amazon_cf_create_distros_access_origin_identity ()) && $cf_response["success"])
784 {
785 $cfc = array_merge ($cfc, array ("distros_access_id" => $cf_response["distros_access_id"], "distros_s3_access_id" => $cf_response["distros_s3_access_id"]));
786 $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"]);
787 c_ws_plugin__s2member_menu_pages::update_all_options ($cf_options, true, false, false, false, false);
788 /**/
789 if (($cf_response = c_ws_plugin__s2member_files_in::amazon_cf_create_distro ("downloads")) && $cf_response["success"])
790 {
791 $cfc = array_merge ($cfc, array ("distro_downloads_id" => $cf_response["distro_downloads_id"], "distro_downloads_dname" => $cf_response["distro_downloads_dname"]));
792 $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"]);
793 c_ws_plugin__s2member_menu_pages::update_all_options ($cf_options, true, false, false, false, false);
794 /**/
795 if (($cf_response = c_ws_plugin__s2member_files_in::amazon_cf_create_distro ("streaming")) && $cf_response["success"])
796 {
797 $cfc = array_merge ($cfc, array ("distro_streaming_id" => $cf_response["distro_streaming_id"], "distro_streaming_dname" => $cf_response["distro_streaming_dname"]));
798 $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"]);
799 c_ws_plugin__s2member_menu_pages::update_all_options ($cf_options, true, false, false, false, false);
800 /**/
801 if (($s3_response = c_ws_plugin__s2member_files_in::amazon_s3_auto_configure_acls ()) && $s3_response["success"])
802 {
803 $cfc = array_merge ($cfc, array ("distros_auto_config_status" => "success"));
804 $cf_options = array ("ws_plugin__s2member_amazon_cf_files_distros_auto_config_status" => "configured");
805 c_ws_plugin__s2member_menu_pages::update_all_options ( /* Now configured! */$cf_options, true, false, false, false, false);
806 /**/
807 return array ("success" => true, "code" => null, "message" => null); /* Successfully configured Amazon® S3/CloudFront distros. */
808 }
809 /**/
810 else if (isset ($s3_response["code"], $s3_response["message"]))
811 /* 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. */
812 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"]));
813 /**/
814 else /* Else, we use a default error code and message. */
815 return array ("success" => false, "code" => -88, "message" => _x ("Unable to update existing Amazon® S3 ACLs. Connection failed.", "s2member-admin", "s2member"));
816 }
817 else if (isset ($cf_response["code"], $cf_response["message"]))
818 /* 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. */
819 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"]));
820 /**/
821 else /* Else, we use a default error code and message. */
822 return array ("success" => false, "code" => -89, "message" => _x ("Unable to create Amazon® CloudFront Streaming Distro. Connection failed.", "s2member-admin", "s2member"));
823 }
824 else if (isset ($cf_response["code"], $cf_response["message"]))
825 /* 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. */
826 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"]));
827 /**/
828 else /* Else, we use a default error code and message. */
829 return array ("success" => false, "code" => -90, "message" => _x ("Unable to create Amazon® CloudFront Downloads Distro. Connection failed.", "s2member-admin", "s2member"));
830 }
831 else if (isset ($cf_response["code"], $cf_response["message"]))
832 /* 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. */
833 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"]));
834 /**/
835 else /* Else, we use a default error code and message. */
836 return array ("success" => false, "code" => -91, "message" => _x ("Unable to create Amazon® CloudFront Origin Access Identity. Connection failed.", "s2member-admin", "s2member"));
837 }
838 else /* Else, we use a default error code and message. */
839 return array ("success" => false, "code" => -92, "message" => _x ("Unable to clear existing Amazon® CloudFront Origin Access Identity.", "s2member-admin", "s2member"));
840 }
841 else if (isset ($cf_get_response["code"], $cf_get_response["message"]))
842 /* 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. */
843 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"]));
844 /**/
845 else /* Else, we use a default error code and message. */
846 return array ("success" => false, "code" => -93, "message" => _x ("Unable to acquire existing Amazon® CloudFront Origin Access Identity. Connection failed.", "s2member-admin", "s2member"));
847 }
848 else /* Else, we use a default error code and message. */
849 return array ("success" => false, "code" => -94, "message" => _x ("Unable to clear existing Amazon® CloudFront Streaming Distro.", "s2member-admin", "s2member"));
850 }
851 else if (isset ($cf_get_response["code"], $cf_get_response["message"]))
852 /* 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. */
853 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"]));
854 /**/
855 else /* Else, we use a default error code and message. */
856 return array ("success" => false, "code" => -95, "message" => _x ("Unable to acquire existing Amazon® CloudFront Streaming Distro. Connection failed.", "s2member-admin", "s2member"));
857 }
858 else /* Else, we use a default error code and message. */
859 return array ("success" => false, "code" => -96, "message" => _x ("Unable to clear existing Amazon® CloudFront Downloads Distro.", "s2member-admin", "s2member"));
860 }
861 else if (isset ($cf_get_response["code"], $cf_get_response["message"]))
862 /* 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. */
863 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"]));
864 /**/
865 else /* Else, we use a default error code and message. */
866 return array ("success" => false, "code" => -97, "message" => _x ("Unable to acquire existing Amazon® CloudFront Downloads Distro. Connection failed.", "s2member-admin", "s2member"));
867 }
868 else /* Else, we use a default error code and message. */
869 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"));
870 }
871 else /* Else, we use a default error code and message. */
872 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"));
873 }
874 /**
875 * Acquires an Amazon® S3/CloudFront Access Origin Identity.
876 *
877 * @package s2Member\Files
878 * @since 110926
879 *
880 * @param str $access_id Required. An Origin Access ID.
881 * @return array Array containing a true `success` and `etag`, `xml` elements on success, else a failure array.
882 * Failure array will contain a failure `code`, and a failure `message`.
883 */
884 public static function amazon_cf_get_access_origin_identity ($access_id = FALSE)
885 {
886 if ($access_id && is_string ($access_id)) /* Valid parameters? */
887 {
888 foreach ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"] as $option => $option_value)
889 if (preg_match ("/^amazon_cf_files_/", $option) && ($option = preg_replace ("/^amazon_cf_files_/", "", $option)))
890 $cfc[$option] = $option_value;
891 /**/
892 $s3c["bucket"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_bucket"];
893 $cfc["access_key"] = $s3c["access_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_access_key"];
894 $cfc["secret_key"] = $s3c["secret_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_secret_key"];
895 /**/
896 $cf_domain = "cloudfront.amazonaws.com";
897 $cf_date = gmdate ("D, d M Y H:i:s") . " GMT";
898 $cf_location = "/2010-11-01/origin-access-identity/cloudfront/" . $access_id;
899 $cf_signature = base64_encode (c_ws_plugin__s2member_files_in::amazon_cf_sign ($cf_date));
900 $cf_args = array ("method" => "GET", "headers" => array ("Host" => $cf_domain, "Date" => $cf_date, "Authorization" => "AWS " . $cfc["access_key"] . ":" . $cf_signature));
901 /**/
902 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"]))))
903 {
904 if ($cf_response["code"] === 200 && !empty ($cf_response["headers"]["etag"]) && !empty ($cf_response["body"]))
905 return array ("success" => true, "code" => null, "message" => null, "etag" => trim ($cf_response["headers"]["etag"]), "xml" => trim ($cf_response["body"]));
906 /**/
907 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. */
908 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"]));
909 }
910 else if (isset ($cf_response["code"], $cf_response["message"]))
911 /* 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. */
912 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"]));
913 /**/
914 else /* Else, we use a default error code and message. */
915 return array ("success" => false, "code" => -98, "message" => _x ("Unable to acquire existing Amazon® CloudFront Origin Access Identity. Connection failed.", "s2member-admin", "s2member"));
916 }
917 else /* Else, we use a default error code and message. */
918 return array ("success" => false, "code" => -99, "message" => _x ("Unable to acquire existing Amazon® CloudFront Origin Access Identity. Invalid Access ID.", "s2member-admin", "s2member"));
919 }
920 /**
921 * Deletes an Amazon® S3/CloudFront Access Origin Identity.
922 *
923 * @package s2Member\Files
924 * @since 110926
925 *
926 * @param str $access_id Required. An Origin Access ID.
927 * @param str $access_id_etag Required. An Origin Access ETag header.
928 * @param str $access_id_xml Required. An Origin Access Identity's XML configuration.
929 * @return array Array containing a true `success` element on success, else a failure array.
930 * Failure array will contain a failure `code`, and a failure `message`.
931 */
932 public static function amazon_cf_del_access_origin_identity ($access_id = FALSE, $access_id_etag = FALSE, $access_id_xml = FALSE)
933 {
934 if ($access_id && is_string ($access_id) && $access_id_etag && is_string ($access_id_etag) && $access_id_xml && is_string ($access_id_xml))
935 {
936 foreach ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"] as $option => $option_value)
937 if (preg_match ("/^amazon_cf_files_/", $option) && ($option = preg_replace ("/^amazon_cf_files_/", "", $option)))
938 $cfc[$option] = $option_value;
939 /**/
940 $s3c["bucket"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_bucket"];
941 $cfc["access_key"] = $s3c["access_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_access_key"];
942 $cfc["secret_key"] = $s3c["secret_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_secret_key"];
943 /**/
944 $cf_domain = "cloudfront.amazonaws.com";
945 $cf_date = gmdate ("D, d M Y H:i:s") . " GMT";
946 $cf_location = "/2010-11-01/origin-access-identity/cloudfront/" . $access_id;
947 $cf_signature = base64_encode (c_ws_plugin__s2member_files_in::amazon_cf_sign ($cf_date));
948 $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));
949 /**/
950 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. */))
951 return array ("success" => true, "code" => null, "message" => null); /* Deleted successfully. */
952 /**/
953 else if (isset ($cf_response["code"], $cf_response["message"]))
954 /* 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. */
955 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"]));
956 /**/
957 else /* Else, we use a default error code and message. */
958 return array ("success" => false, "code" => -98, "message" => _x ("Unable to delete existing Amazon® CloudFront Origin Access Identity. Connection failed.", "s2member-admin", "s2member"));
959 }
960 else /* Else, we use a default error code and message. */
961 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"));
962 }
963 /**
964 * Creates an Amazon® S3/CloudFront Access Origin Identity for all Distros.
965 *
966 * @package s2Member\Files
967 * @since 110926
968 *
969 * @return array Array containing a true `success` and `distros_access_id`, `distros_s3_access_id` elements on success, else a failure array.
970 * Failure array will contain a failure `code`, and a failure `message`.
971 */
972 public static function amazon_cf_create_distros_access_origin_identity ()
973 {
974 foreach ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"] as $option => $option_value)
975 if (preg_match ("/^amazon_cf_files_/", $option) && ($option = preg_replace ("/^amazon_cf_files_/", "", $option)))
976 $cfc[$option] = $option_value;
977 /**/
978 $s3c["bucket"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_bucket"];
979 $cfc["access_key"] = $s3c["access_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_access_key"];
980 $cfc["secret_key"] = $s3c["secret_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_secret_key"];
981 /**/
982 $cf_domain = "cloudfront.amazonaws.com";
983 $cf_date = gmdate ("D, d M Y H:i:s") . " GMT";
984 $cf_location = "/2010-11-01/origin-access-identity/cloudfront";
985 $cf_signature = base64_encode (c_ws_plugin__s2member_files_in::amazon_cf_sign ($cf_date));
986 $cf_distros_access_reference = time () . "." . md5 ("access" . $s3c["bucket"] . $s3c["access_key"] . $s3c["secret_key"] . $cfc["private_key"] . $cfc["private_key_id"]);
987 $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>';
988 $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));
989 /**/
990 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. */))
991 {
992 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))
993 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]));
994 /**/
995 else /* Else, we use a default error code and message. */
996 return array ("success" => false, "code" => -98, "message" => _x ("Unable to create/read Amazon® CloudFront Origin Access Identity. Unexpected response.", "s2member-admin", "s2member"));
997 }
998 else if (isset ($cf_response["code"], $cf_response["message"]))
999 /* 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. */
1000 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"]));
1001 /**/
1002 else /* Else, we use a default error code and message. */
1003 return array ("success" => false, "code" => -99, "message" => _x ("Unable to create Amazon® CloudFront Origin Access Identity. Connection failed.", "s2member-admin", "s2member"));
1004 }
1005 /**
1006 * Acquires an Amazon® S3/CloudFront Distro.
1007 *
1008 * @package s2Member\Files
1009 * @since 110926
1010 *
1011 * @param str $distro_id Required. A Distro ID.
1012 * @param str $distro_type Required: `downloads|streaming`.
1013 * @return array Array containing a true `success` and `etag`, `xml`, `deployed` elements on success, else a failure array.
1014 * Failure array will contain a failure `code`, and a failure `message`.
1015 */
1016 public static function amazon_cf_get_distro ($distro_id = FALSE, $distro_type = FALSE)
1017 {
1018 if ($distro_id && is_string ($distro_id) && $distro_type && is_string ($distro_type) && in_array ($distro_type, array ("downloads", "streaming")))
1019 {
1020 foreach ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"] as $option => $option_value)
1021 if (preg_match ("/^amazon_cf_files_/", $option) && ($option = preg_replace ("/^amazon_cf_files_/", "", $option)))
1022 $cfc[$option] = $option_value;
1023 /**/
1024 $s3c["bucket"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_bucket"];
1025 $cfc["access_key"] = $s3c["access_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_access_key"];
1026 $cfc["secret_key"] = $s3c["secret_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_secret_key"];
1027 /**/
1028 $cf_domain = "cloudfront.amazonaws.com";
1029 $cf_date = gmdate ("D, d M Y H:i:s") . " GMT";
1030 $cf_signature = base64_encode (c_ws_plugin__s2member_files_in::amazon_cf_sign ($cf_date));
1031 $cf_location = ($distro_type === "streaming") ? "/2010-11-01/streaming-distribution/" . $distro_id : "/2010-11-01/distribution/" . $distro_id;
1032 $cf_args = array ("method" => "GET", "headers" => array ("Host" => $cf_domain, "Date" => $cf_date, "Authorization" => "AWS " . $cfc["access_key"] . ":" . $cf_signature));
1033 /**/
1034 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"]))))
1035 {
1036 if ($cf_response["code"] === 200 && !empty ($cf_response["headers"]["etag"]) && !empty ($cf_response["body"]))
1037 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));
1038 /**/
1039 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. */
1040 return array ("success" => false, "code" => $cf_response["code"], "message" => sprintf (_x ("Existing Amazon® CloudFront Distro NOT found. %s", "s2member-admin", "s2member"), $cf_response["message"]));
1041 }
1042 else if (isset ($cf_response["code"], $cf_response["message"]))
1043 /* 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. */
1044 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"]));
1045 /**/
1046 else /* Else, we use a default error code and message. */
1047 return array ("success" => false, "code" => -98, "message" => _x ("Unable to acquire existing Amazon® CloudFront Distro. Connection failed.", "s2member-admin", "s2member"));
1048 }
1049 else /* Else, we use a default error code and message. */
1050 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"));
1051 }
1052 /**
1053 * Disables an Amazon® S3/CloudFront Distro.
1054 *
1055 * @package s2Member\Files
1056 * @since 110926
1057 *
1058 * @param str $distro_id Required. A Distro ID.
1059 * @param str $distro_id_etag Required. A Distro ETag header.
1060 * @param str $distro_id_xml Required. A Distro's XML configuration.
1061 * @return array Array containing a true `success` and `etag`, `xml`, `deployed` elements on success, else a failure array.
1062 * Failure array will contain a failure `code`, and a failure `message`.
1063 */
1064 public static function amazon_cf_disable_distro ($distro_id = FALSE, $distro_id_etag = FALSE, $distro_id_xml = FALSE)
1065 {
1066 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. */
1067 && ($distro_id_type = (stripos ($distro_id_xml, "<StreamingDistribution") !== false) ? "streaming" : ((stripos ($distro_id_xml, "<Distribution") !== false) ? "downloads" : false)) #
1068 && preg_match ("/\<CallerReference\>(.+?)\<\/CallerReference\>/is", $distro_id_xml, $distro_id_reference_tag) && ($distro_id_reference = $distro_id_reference_tag[1]))
1069 {
1070 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. */
1071 {
1072 if (stripos ($distro_id_xml, "<Status>Deployed</Status>") !== false) /* Check distro status before we even begin processing. */
1073 {
1074 foreach ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"] as $option => $option_value)
1075 if (preg_match ("/^amazon_cf_files_/", $option) && ($option = preg_replace ("/^amazon_cf_files_/", "", $option)))
1076 $cfc[$option] = $option_value;
1077 /**/
1078 $s3c["bucket"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_bucket"];
1079 $cfc["access_key"] = $s3c["access_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_access_key"];
1080 $cfc["secret_key"] = $s3c["secret_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_secret_key"];
1081 /**/
1082 $cf_domain = "cloudfront.amazonaws.com";
1083 $cf_date = gmdate ("D, d M Y H:i:s") . " GMT";
1084 $cf_signature = base64_encode (c_ws_plugin__s2member_files_in::amazon_cf_sign ($cf_date));
1085 $cf_location = ($distro_id_type === "streaming") ? "/2010-11-01/streaming-distribution/" . $distro_id . "/config" : "/2010-11-01/distribution/" . $distro_id . "/config";
1086 $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>';
1087 $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));
1088 /**/
1089 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"]))
1090 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));
1091 /**/
1092 else if (isset ($cf_response["code"], $cf_response["message"]))
1093 /* 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. */
1094 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"]));
1095 /**/
1096 else /* Else, we use a default error code and message. */
1097 return array ("success" => false, "code" => -97, "message" => _x ("Unable to disable existing Amazon® CloudFront Distro. Connection failed.", "s2member-admin", "s2member"));
1098 }
1099 else /* Else, we use a default error code and message. */
1100 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"));
1101 }
1102 else /* Else, we use a default error code and message. */
1103 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));
1104 }
1105 else /* Else, we use a default error code and message. */
1106 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"));
1107 }
1108 /**
1109 * Deletes an Amazon® S3/CloudFront Distro.
1110 *
1111 * @package s2Member\Files
1112 * @since 110926
1113 *
1114 * @param str $distro_id Required. A Distro ID.
1115 * @param str $distro_id_etag Required. A Distro ETag header.
1116 * @param str $distro_id_xml Required. A Distro's XML configuration.
1117 * @return array Array containing a true `success` element on success, else a failure array.
1118 * Failure array will contain a failure `code`, and a failure `message`.
1119 */
1120 public static function amazon_cf_del_distro ($distro_id = FALSE, $distro_id_etag = FALSE, $distro_id_xml = FALSE)
1121 {
1122 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. */
1123 && ($distro_id_type = (stripos ($distro_id_xml, "<StreamingDistribution") !== false) ? "streaming" : ((stripos ($distro_id_xml, "<Distribution") !== false) ? "downloads" : false)) #
1124 && preg_match ("/\<CallerReference\>(.+?)\<\/CallerReference\>/is", $distro_id_xml, $distro_id_reference_tag) && ($distro_id_reference = $distro_id_reference_tag[1]))
1125 {
1126 if (stripos ($distro_id_xml, "<Status>Deployed</Status>") !== false) /* Check distro status before we even begin processing this deletion. */
1127 {
1128 if (($cf_response = c_ws_plugin__s2member_files_in::amazon_cf_disable_distro ($distro_id, $distro_id_etag, $distro_id_xml)) && $cf_response["success"])
1129 {
1130 if (($cf_response = c_ws_plugin__s2member_files_in::amazon_cf_get_distro ($distro_id, $distro_id_type)) && $cf_response["success"] && $cf_response["deployed"])
1131 {
1132 foreach ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"] as $option => $option_value)
1133 if (preg_match ("/^amazon_cf_files_/", $option) && ($option = preg_replace ("/^amazon_cf_files_/", "", $option)))
1134 $cfc[$option] = $option_value;
1135 /**/
1136 $s3c["bucket"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_bucket"];
1137 $cfc["access_key"] = $s3c["access_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_access_key"];
1138 $cfc["secret_key"] = $s3c["secret_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_secret_key"];
1139 /**/
1140 $cf_domain = "cloudfront.amazonaws.com";
1141 $cf_date = gmdate ("D, d M Y H:i:s") . " GMT";
1142 $cf_signature = base64_encode (c_ws_plugin__s2member_files_in::amazon_cf_sign ($cf_date));
1143 $cf_location = ($distro_id_type === "streaming") ? "/2010-11-01/streaming-distribution/" . $distro_id : "/2010-11-01/distribution/" . $distro_id;
1144 $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));
1145 /**/
1146 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. */))
1147 return array ("success" => true, "code" => null, "message" => null); /* Deleted successfully. */
1148 /**/
1149 else if (isset ($cf_response["code"], $cf_response["message"]))
1150 /* 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. */
1151 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"]));
1152 /**/
1153 else /* Else, we use a default error code and message. */
1154 return array ("success" => false, "code" => -94, "message" => _x ("Unable to delete existing Amazon® CloudFront Distro. Connection failed.", "s2member-admin", "s2member"));
1155 }
1156 else if (isset ($cf_response["success"], $cf_response["deployed"]) && $cf_response["success"] && !$cf_response["deployed"])
1157 /* 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. */
1158 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"));
1159 /**/
1160 else if (isset ($cf_response["code"], $cf_response["message"]))
1161 /* 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. */
1162 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"]));
1163 /**/
1164 else /* Else, we use a default error code and message. */
1165 return array ("success" => false, "code" => -96, "message" => _x ("Unable to check status of existing Amazon® CloudFront Distro. Connection failed.", "s2member-admin", "s2member"));
1166 }
1167 else if (isset ($cf_response["code"], $cf_response["message"]))
1168 /* 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. */
1169 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"]));
1170 /**/
1171 else /* Else, we use a default error code and message. */
1172 return array ("success" => false, "code" => -97, "message" => _x ("Unable to disable existing Amazon® CloudFront Distro. Connection failed.", "s2member-admin", "s2member"));
1173 }
1174 else /* Else, we use a default error code and message. */
1175 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"));
1176 }
1177 else /* Else, we use a default error code and message. */
1178 return array ("success" => false, "code" => -99, "message" => _x ("Unable to delete existing Amazon® CloudFront Distro. Invalid Distro ID or ETag.", "s2member-admin", "s2member"));
1179 }
1180 /**
1181 * Creates an Amazon® S3/CloudFront Distro.
1182 *
1183 * @package s2Member\Files
1184 * @since 110926
1185 *
1186 * @param str $distro_type Required: `downloads|streaming`.
1187 * @return array Array containing a true `success` and `distro_[distro_type]_id`, `distro_[distro_type]_dname` elements on success, else a failure array.
1188 * Failure array will contain a failure `code`, and a failure `message`.
1189 */
1190 public static function amazon_cf_create_distro ($distro_type = FALSE)
1191 {
1192 if ($distro_type && is_string ($distro_type) && in_array ($distro_type, array ("downloads", "streaming")))
1193 {
1194 foreach ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"] as $option => $option_value)
1195 if (preg_match ("/^amazon_cf_files_/", $option) && ($option = preg_replace ("/^amazon_cf_files_/", "", $option)))
1196 $cfc[$option] = $option_value;
1197 /**/
1198 $s3c["bucket"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_bucket"];
1199 $cfc["access_key"] = $s3c["access_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_access_key"];
1200 $cfc["secret_key"] = $s3c["secret_key"] = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_secret_key"];
1201 /**/
1202 $cf_domain = "cloudfront.amazonaws.com";
1203 $cf_date = gmdate ("D, d M Y H:i:s") . " GMT";
1204 $cf_signature = base64_encode (c_ws_plugin__s2member_files_in::amazon_cf_sign ($cf_date));
1205 /**/
1206 if ($distro_type === "downloads") /* Create a `downloads` Distro? This uses a different XML schema. */
1207 {
1208 $cf_location = "/2010-11-01/distribution"; /* Create distro. */
1209 $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"]);
1210 $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>';
1211 $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));
1212 /**/
1213 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. */))
1214 {
1215 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))
1216 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]));
1217 else /* Else, we use a default error code and message. */
1218 return array ("success" => false, "code" => -97, "message" => _x ("Unable to create/read Amazon® CloudFront Downloads Distro. Unexpected response.", "s2member-admin", "s2member"));
1219 }
1220 else if (isset ($cf_response["code"], $cf_response["message"]))
1221 /* 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. */
1222 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"]));
1223 else /* Else, we use a default error code and message. */
1224 return array ("success" => false, "code" => -98, "message" => _x ("Unable to create Amazon® CloudFront Downloads Distro. Connection failed.", "s2member-admin", "s2member"));
1225 }
1226 /**/
1227 else if ($distro_type === "streaming") /* Create a `streaming` Distro? A different XML schema. */
1228 {
1229 $cf_location = "/2010-11-01/streaming-distribution"; /* Create streaming distro. */
1230 $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"]);
1231 $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>';
1232 $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));
1233 /**/
1234 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. */))
1235 {
1236 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))
1237 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]));
1238 else /* Else, we use a default error code and message. */
1239 return array ("success" => false, "code" => -97, "message" => _x ("Unable to create/read Amazon® CloudFront Streaming Distro. Unexpected response.", "s2member-admin", "s2member"));
1240 }
1241 else if (isset ($cf_response["code"], $cf_response["message"]))
1242 /* 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. */
1243 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"]));
1244 else /* Else, we use a default error code and message. */
1245 return array ("success" => false, "code" => -98, "message" => _x ("Unable to create Amazon® CloudFront Streaming Distro. Connection failed.", "s2member-admin", "s2member"));
1246 }
1247 }
1248 else /* Else, we use a default error code and message. */
1249 return array ("success" => false, "code" => -99, "message" => _x ("Unable to create Amazon® CloudFront Distro. Invalid Distro type.", "s2member-admin", "s2member"));
1250 }
1251 }
1252 }
1253 ?>