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

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