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

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