PluginProbe
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions / 110815
s2Member – Excellent for All Kinds of Memberships, Content Restriction Paywalls & Member Access Subscriptions v110815
260913 260909 260829 260814 260805 110710 110731 110812 110815 110912 110913 110915 110926 110927 111002 111003 111011 111017 111029 111105 111206 111216 111220 120213 120219 All 188 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 110815, at includes/classes/files-in.inc.php

300 lines 19.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * File Download routines for s2Member ( inner processing routines ).
4 *
5 * Copyright: © 2009-2011
6 * {@link http://www.websharks-inc.com/ WebSharks, Inc.}
7 * ( coded in the USA )
8 *
9 * Released under the terms of the GNU General Public License.
10 * You should have received a copy of the GNU General Public License,
11 * along with this software. In the main directory, see: /licensing/
12 * If not, see: {@link http://www.gnu.org/licenses/}.
13 *
14 * @package s2Member\Files
15 * @since 3.5
16 */
17 if (realpath (__FILE__) === realpath ($_SERVER["SCRIPT_FILENAME"]))
18 exit ("Do not access this file directly.");
19 /**/
20 if (!class_exists ("c_ws_plugin__s2member_files_in"))
21 {
22 /**
23 * File Download routines for s2Member ( inner processing routines ).
24 *
25 * @package s2Member\Files
26 * @since 3.5
27 */
28 class c_ws_plugin__s2member_files_in
29 {
30 /**
31 * Handles Download Access permissions.
32 *
33 * @package s2Member\Files
34 * @since 3.5
35 *
36 * @attaches-to: ``add_action("init");``
37 *
38 * @return null Or exits script execution after serving a File Download.
39 */
40 public static function check_file_download_access ()
41 {
42 do_action ("ws_plugin__s2member_before_file_download_access", get_defined_vars ());
43 /**/
44 if (!empty ($_GET["s2member_file_download"]) && strpos ($_GET["s2member_file_download"], "..") === false)
45 {
46 $excluded = apply_filters ("ws_plugin__s2member_check_file_download_access_excluded", false, get_defined_vars ());
47 /**/
48 if (!($using_amazon_s3_storage = 0) && $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_bucket"] && $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_access_key"] && $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_secret_key"])
49 $using_amazon_s3_storage = true; /* Amazon® S3 storage has been configured! */
50 /**/
51 if (!$excluded && (empty ($_GET["s2member_file_download_key"]) || (!empty ($_GET["s2member_file_download_key"]) && !($file_download_key_is_valid = ($_GET["s2member_file_download_key"] === c_ws_plugin__s2member_files::file_download_key ($_GET["s2member_file_download"]) || $_GET["s2member_file_download_key"] === c_ws_plugin__s2member_files::file_download_key ($_GET["s2member_file_download"], "ip-forever") || $_GET["s2member_file_download_key"] === c_ws_plugin__s2member_files::file_download_key ($_GET["s2member_file_download"], "universal"))))))
52 {
53 $_GET["s2member_file_download"] = trim ($_GET["s2member_file_download"], "/"); /* Trim slashes after Key comparison. */
54 /**/
55 if (!$using_amazon_s3_storage && !file_exists ($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"] . "/" . $_GET["s2member_file_download"]))
56 {
57 header ("HTTP/1.0 404 Not Found"); /* The file does NOT even exist. */
58 exit ("404: Sorry, file not found. Please contact Support for assistance.");
59 }
60 /**/
61 else if (!empty ($_GET["s2member_file_download_key"]) && !$file_download_key_is_valid) /* Invalid Key? */
62 {
63 header ("HTTP/1.0 503 Service Temporarily Unavailable"); /* Invalid Download Keys are handled separately. */
64 exit ("503 ( Invalid Key ): Sorry, your access to this file has expired. Please contact Support for assistance.");
65 }
66 /**/
67 else if ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page"]) /* Is a Membership Options Page configured? */
68 /* This file will be processed WITHOUT a Download Key, using Membership Level Access ( w/ possible Custom Capabilities ). */
69 {
70 if (!has_filter ("ws_plugin__s2member_check_file_download_access_user", "c_ws_plugin__s2member_files_in::_file_remote_authorization"))
71 add_filter ("ws_plugin__s2member_check_file_download_access_user", "c_ws_plugin__s2member_files_in::_file_remote_authorization", 10, 2);
72 /**/
73 if (($file_download_access_is_allowed = $min_level_4_downloads = c_ws_plugin__s2member_files::min_level_4_downloads ()) === false)
74 {
75 header ("HTTP/1.0 503 Service Temporarily Unavailable"); /* File downloads are NOT yet configured? */
76 exit ("503: Sorry, File Downloads are NOT enabled yet. Please contact Support for assistance. If you are the site owner, please configure: `s2Member -> Download Options -> Basic Download Restrictions`.");
77 }
78 /**/
79 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 ())) || !($user_id = $user->ID))
80 {
81 if (preg_match ("/^access[_\-]s2member[_\-]level([0-9]+)\//", $_GET["s2member_file_download"], $m))
82 {
83 $level_req = $m[1]; /* Which Level does this require? */
84 if (wp_redirect (add_query_arg (urlencode_deep (array ("s2member_seeking" => "file-" . $_GET["s2member_file_download"], "s2member_level_req" => $level_req)), get_page_link ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page"])), apply_filters ("ws_plugin__s2member_content_redirect_status", 301, get_defined_vars ())) !== "nill")
85 exit ();
86 }
87 else if (preg_match ("/^access[_\-]s2member[_\-]ccap[_\-](.+?)\//", $_GET["s2member_file_download"], $m))
88 {
89 $ccap_req = preg_replace ("/-/", "_", $m[1]); /* Which Capability does this require? */
90 if (wp_redirect (add_query_arg (urlencode_deep (array ("s2member_seeking" => "file-" . $_GET["s2member_file_download"], "s2member_ccap_req" => $ccap_req)), get_page_link ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page"])), apply_filters ("ws_plugin__s2member_content_redirect_status", 301, get_defined_vars ())) !== "nill")
91 exit ();
92 }
93 else if (wp_redirect (add_query_arg (urlencode_deep (array ("s2member_seeking" => "file-" . $_GET["s2member_file_download"], "s2member_level_req" => (string)$min_level_4_downloads)), get_page_link ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page"])), apply_filters ("ws_plugin__s2member_content_redirect_status", 301, get_defined_vars ())) !== "nill")
94 exit ();
95 }
96 /**/
97 else if ((!is_array ($file_downloads = c_ws_plugin__s2member_files::user_downloads ($user)) || !$file_downloads["allowed"] || !$file_downloads["allowed_days"])/**/
98 && wp_redirect (add_query_arg (urlencode_deep (array ("s2member_seeking" => "file-" . $_GET["s2member_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 ())) !== "nill")
99 exit ();
100 /**/
101 else if (preg_match ("/^access[_\-]s2member[_\-]level([0-9]+)\//", $_GET["s2member_file_download"], $m))
102 {
103 $level_req = $m[1]; /* Which Level does this require? */
104 if (!$user->has_cap ("access_s2member_level" . $level_req) /* Does the User have access to this Level? */
105 && wp_redirect (add_query_arg (urlencode_deep (array ("s2member_seeking" => "file-" . $_GET["s2member_file_download"], "s2member_level_req" => $level_req)), get_page_link ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page"])), apply_filters ("ws_plugin__s2member_content_redirect_status", 301, get_defined_vars ())) !== "nill")
106 exit ();
107 }
108 /**/
109 else if (preg_match ("/^access[_\-]s2member[_\-]ccap[_\-](.+?)\//", $_GET["s2member_file_download"], $m))
110 {
111 $ccap_req = preg_replace ("/-/", "_", $m[1]); /* Which Capability does this require? */
112 if (!$user->has_cap ("access_s2member_ccap_" . $ccap_req) /* Does the User have access to this Custom Capability? */
113 && wp_redirect (add_query_arg (urlencode_deep (array ("s2member_seeking" => "file-" . $_GET["s2member_file_download"], "s2member_ccap_req" => $ccap_req)), get_page_link ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["membership_options_page"])), apply_filters ("ws_plugin__s2member_content_redirect_status", 301, get_defined_vars ())) !== "nill")
114 exit ();
115 }
116 /**/
117 $already_downloaded = false; /* Initialize this to a false value. */
118 $previous_file_downloads = 0; /* Here we're going to count how many downloads they've performed. */
119 $max_days_logged = c_ws_plugin__s2member_files::max_download_period (); /* Longest period/days. */
120 $file_download_access_log = (array)get_user_option ("s2member_file_download_access_log", $user_id);
121 $file_download_access_arc = (array)get_user_option ("s2member_file_download_access_arc", $user_id);
122 /**/
123 foreach ($file_download_access_log as $file_download_access_log_entry_key => $file_download_access_log_entry)
124 {
125 if (strtotime ($file_download_access_log_entry["date"]) < strtotime ("-" . $max_days_logged . " days"))
126 {
127 unset ($file_download_access_log[$file_download_access_log_entry_key]);
128 $file_download_access_arc[] = $file_download_access_log_entry;
129 }
130 else if (strtotime ($file_download_access_log_entry["date"]) >= strtotime ("-" . $file_downloads["allowed_days"] . " days"))
131 {
132 $previous_file_downloads++;
133 if ($file_download_access_log_entry["file"] === $_GET["s2member_file_download"])
134 $already_downloaded = true;
135 }
136 }
137 /**/
138 if (!$already_downloaded && $previous_file_downloads >= $file_downloads["allowed"] /* They have NOT already downloaded this file, and they're over their limit. */
139 && wp_redirect (add_query_arg (urlencode_deep (array ("s2member_seeking" => "file-" . $_GET["s2member_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 ())) !== "nill")
140 exit ();
141 /**/
142 if (!$already_downloaded) /* Only add this file to the log if they have not already downloaded it. */
143 $file_download_access_log[] = array ("date" => date ("Y-m-d"), "file" => $_GET["s2member_file_download"]);
144 /**/
145 update_user_option ($user_id, "s2member_file_download_access_arc", c_ws_plugin__s2member_utils_arrays::array_unique ($file_download_access_arc));
146 update_user_option ($user_id, "s2member_file_download_access_log", c_ws_plugin__s2member_utils_arrays::array_unique ($file_download_access_log));
147 }
148 }
149 else /* Otherwise... it's either $excluded; or permission was granted with a valid Download Key. */
150 {
151 $_GET["s2member_file_download"] = trim ($_GET["s2member_file_download"], "/");
152 /**/
153 if (!$using_amazon_s3_storage && !file_exists ($GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"] . "/" . $_GET["s2member_file_download"]))
154 {
155 header ("HTTP/1.0 404 Not Found"); /* The file does NOT even exist. */
156 exit ("404: Sorry, file not found. Please contact Support for assistance.");
157 }
158 }
159 /*
160 Here we are going to put together all of the File Download information.
161 */
162 $extension = strtolower (substr ($_GET["s2member_file_download"], strrpos ($_GET["s2member_file_download"], ".") + 1)); /* To lowercase. */
163 /**/
164 $mimetypes = parse_ini_file (dirname (dirname (dirname (__FILE__))) . "/includes/mime-types.ini"); /* Types provided by: `mime-types.ini`. */
165 $mimetype = ($mimetypes[$extension]) ? $mimetypes[$extension] : "application/octet-stream"; /* Lookup the MIME type for this file extension. */
166 /**/
167 $inline = (!empty ($_GET["s2member_file_inline"]) || in_array ($extension, preg_split ("/[\r\n\t\s;,]+/", $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["file_download_inline_extensions"]))) ? true : false;
168 /**/
169 $basename = basename ($_GET["s2member_file_download"]); /* The actual name of this File Download ( i.e. the basename ); including its file extension too. */
170 /**/
171 $pathinfo = (!$using_amazon_s3_storage) ? pathinfo (($file = $GLOBALS["WS_PLUGIN__"]["s2member"]["c"]["files_dir"] . "/" . $_GET["s2member_file_download"])) : array ();
172 $length = (!$using_amazon_s3_storage && $file) ? filesize ($file) : -1; /* The overall file size, in bytes. */
173 /**/
174 eval ('foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v;');
175 do_action ("ws_plugin__s2member_during_file_download_access", get_defined_vars ());
176 unset ($__refs, $__v); /* Unset defined __refs, __v. */
177 /**/
178 if ($using_amazon_s3_storage) /* Using Amazon® S3 storage? In this case, we use an authenticated redirection to S3 storage. */
179 {
180 $amazon_s3_file_expires = strtotime ("+" . apply_filters ("ws_plugin__s2member_amazon_s3_file_expires_time", "30 seconds", get_defined_vars ()));
181 /**/
182 $amazon_s3_file = "/" . $_GET["s2member_file_download"] . "?response-cache-control=" . urlencode (($amazon_s3_cache_control = "no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0")) . "&response-content-disposition=" . urlencode (($amazon_s3_content_disposition = (($inline) ? "inline" : "attachment") . '; filename="' . $basename . '"')) . "&response-content-type=" . urlencode (($amazon_s3_content_type = $mimetype)) . "&response-expires=" . urlencode (($amazon_s3_expires = gmdate ("D, d M Y H:i:s", strtotime ("-1 week")) . " GMT"));
183 $amazon_s3_raw_file = "/" . $_GET["s2member_file_download"] . "?response-cache-control=" . $amazon_s3_cache_control . "&response-content-disposition=" . $amazon_s3_content_disposition . "&response-content-type=" . $amazon_s3_content_type . "&response-expires=" . $amazon_s3_expires;
184 $amazon_s3_signature = base64_encode (c_ws_plugin__s2member_files_in::amazon_s3_sign ("GET\n\n\n" . $amazon_s3_file_expires . "\n" . "/" . $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_bucket"] . $amazon_s3_raw_file));
185 /**/
186 $amazon_s3_redirection_url = "http://" . $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_bucket"] . ".s3.amazonaws.com" . $amazon_s3_file;
187 if (strtolower ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_bucket"]) !== $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_bucket"])
188 $amazon_s3_redirection_url = "http://s3.amazonaws.com/" . $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_bucket"] . $amazon_s3_file;
189 /**/
190 $amazon_s3_redirection_url .= "&AWSAccessKeyId=" . urlencode ($GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_access_key"]);
191 $amazon_s3_redirection_url .= "&Expires=" . urlencode ($amazon_s3_file_expires);
192 $amazon_s3_redirection_url .= "&Signature=" . urlencode ($amazon_s3_signature);
193 /**/
194 wp_redirect ($amazon_s3_redirection_url); /* 302 redirection. */
195 /**/
196 exit (); /* Clean exit. */
197 }
198 /**/
199 else /* Else, using localized storage ( default ). */
200 {
201 @set_time_limit (0); /* Unlimited. */
202 @ini_set ("zlib.output_compression", 0);
203 /**/
204 header ("Accept-Ranges: none");
205 header ("Content-Encoding: none");
206 header ("Content-Type: " . $mimetype);
207 header ("Expires: " . gmdate ("D, d M Y H:i:s", strtotime ("-1 week")) . " GMT");
208 header ("Last-Modified: " . gmdate ("D, d M Y H:i:s") . " GMT");
209 header ("Cache-Control: no-cache, must-revalidate, max-age=0");
210 header ("Cache-Control: post-check=0, pre-check=0", false);
211 header ("Pragma: no-cache");
212 /**/
213 header ('Content-Disposition: ' . (($inline) ? "inline" : "attachment") . '; filename="' . $basename . '"');
214 /**/
215 if ($length && apply_filters ("ws_plugin__s2member_stream_file_downloads", true, get_defined_vars ()) && ($stream = fopen ($file, "rb")))
216 {
217 $_stream_w_content_length = (preg_match ("/^win/i", PHP_OS)) ? false : true; /* Windows® IIS does not jive here. */
218 /* Windows® IIS doesn't seem to like it when both `Content-Length` and `Transfer-Encoding: chunked` are sent together. */
219 if (apply_filters ("ws_plugin__s2member_stream_file_downloads_w_content_length", $_stream_w_content_length, get_defined_vars ()))
220 header ("Content-Length: " . $length);
221 /**/
222 header ("Transfer-Encoding: chunked"); /* Uses `Transfer-Encoding: chunked` for simulated streaming. */
223 /**/
224 eval ('while (@ob_end_clean ());'); /* End/clean all output buffers that may or may not exist. */
225 /**/
226 while (!feof ($stream) && ($chunk_size = strlen ($data = fread ($stream, 2097152))))
227 eval ('echo dechex ($chunk_size) . "\r\n". $data . "\r\n"; @flush ();');
228 /**/
229 fclose ($stream);
230 /**/
231 exit ("0\r\n\r\n");
232 }
233 else if ($length) /* Else `file_get_contents()`. */
234 {
235 header ("Content-Length: " . $length);
236 /**/
237 exit (file_get_contents ($file));
238 }
239 else
240 exit (); /* Empty file. */
241 }
242 }
243 /**/
244 do_action ("ws_plugin__s2member_after_file_download_access", get_defined_vars ());
245 }
246 /**
247 * Creates an Amazon® S3 HMAC-SHA1 signature.
248 *
249 * @package s2Member\Files
250 * @since 110524RC
251 *
252 * @param str $data Input data, to be signed by this routine.
253 * @return str An HMAC-SHA1 signature for Amazon® S3.
254 */
255 public static function amazon_s3_sign ($data = FALSE)
256 {
257 $key = $GLOBALS["WS_PLUGIN__"]["s2member"]["o"]["amazon_s3_files_secret_key"];
258 $key = str_pad (((strlen ($key) > 64) ? pack ('H*', sha1 ($key)) : $key), 64, chr (0x00));
259 return pack ('H*', sha1 (($key ^ str_repeat (chr (0x5c), 64)) . pack ('H*', sha1 (($key ^ str_repeat (chr (0x36), 64)) . $data))));
260 }
261 /**
262 * A sort of callback function that handles Header Authorization for File Downloads.
263 *
264 * @package s2Member\Files
265 * @since 3.5
266 *
267 * @attaches-to: ``add_filter("ws_plugin__s2member_check_file_download_access_user");``
268 *
269 * @param obj $user Expects a WP_User object passed in by the Filter.
270 * @return obj A WP_User object, possibly obtained through Header Authorization.
271 */
272 public static function _file_remote_authorization ($user = FALSE)
273 {
274 eval ('foreach(array_keys(get_defined_vars())as$__v)$__refs[$__v]=&$$__v;');
275 do_action ("_ws_plugin__s2member_before_file_remote_authorization", get_defined_vars ());
276 unset ($__refs, $__v); /* Unset defined __refs, __v. */
277 /**/
278 if (!is_object ($user) && !empty ($_GET["s2member_file_remote"])) /* Use Header Authorization? */
279 {
280 do_action ("_ws_plugin__s2member_during_file_remote_authorization_before", get_defined_vars ());
281 /**/
282 if (empty ($_SERVER["PHP_AUTH_USER"]) || empty ($_SERVER["PHP_AUTH_PW"]) || !user_pass_ok ($_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"]))
283 {
284 header ('WWW-Authenticate: Basic realm="Members Only"');
285 header ("HTTP/1.0 401 Unauthorized");
286 exit ("Access Denied");
287 }
288 else if (is_object ($_user = new WP_User ($_SERVER["PHP_AUTH_USER"])) && $_user->ID)
289 {
290 $user = $_user; /* Now assign $user. */
291 }
292 /**/
293 do_action ("_ws_plugin__s2member_during_file_remote_authorization_after", get_defined_vars ());
294 }
295 /**/
296 return apply_filters ("_ws_plugin__s2member_file_remote_authorization", $user, get_defined_vars ());
297 }
298 }
299 }
300 ?>