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

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