PluginProbe ʕ •ᴥ•ʔ
Download Manager / trunk
Download Manager vtrunk
3.3.62 3.3.61 3.3.60 3.3.59 3.3.58 3.3.57 3.3.56 trunk 2.1.3 2.3.0 2.5.96 2.5.97 2.6.2 2.6.96 2.8.3 2.9.99 3.0.4 3.1.05 3.1.07 3.1.08 3.1.11 3.1.12 3.1.14 3.1.17 3.1.18 3.1.22 3.1.23 3.1.24 3.1.25 3.1.26 3.1.27 3.1.28 3.2.04 3.2.13 3.2.14 3.2.16 3.2.18 3.2.19 3.2.21 3.2.22 3.2.23 3.2.24 3.2.25 3.2.27 3.2.28 3.2.29 3.2.30 3.2.31 3.2.32 3.2.33 3.2.34 3.2.35 3.2.37 3.2.38 3.2.39 3.2.40 3.2.41 3.2.42 3.2.43 3.2.44 3.2.45 3.2.46 3.2.47 3.2.48 3.2.49 3.2.50 3.2.51 3.2.52 3.2.53 3.2.54 3.2.55 3.2.56 3.2.57 3.2.58 3.2.59 3.2.60 3.2.61 3.2.63 3.2.64 3.2.65 3.2.66 3.2.67 3.2.68 3.2.69 3.2.70 3.2.71 3.2.72 3.2.73 3.2.74 3.2.75 3.2.76 3.2.77 3.2.78 3.2.79 3.2.80 3.2.81 3.2.82 3.2.83 3.2.84 3.2.85 3.2.86 3.2.87 3.2.88 3.2.89 3.2.90 3.2.91 3.2.92 3.2.93 3.2.94 3.2.95 3.2.96 3.2.97 3.2.98 3.2.99 3.3.00 3.3.01 3.3.02 3.3.03 3.3.04 3.3.05 3.3.06 3.3.07 3.3.08 3.3.09 3.3.10 3.3.11 3.3.12 3.3.13 3.3.14 3.3.15 3.3.16 3.3.17 3.3.18 3.3.19 3.3.20 3.3.21 3.3.22 3.3.23 3.3.24 3.3.25 3.3.26 3.3.27 3.3.28 3.3.29 3.3.30 3.3.31 3.3.32 3.3.33 3.3.34 3.3.35 3.3.36 3.3.37 3.3.38 3.3.39 3.3.40 3.3.41 3.3.42 3.3.43 3.3.44 3.3.45 3.3.46 3.3.47 3.3.48 3.3.49 3.3.50 3.3.51 3.3.52 3.3.53 3.3.54 3.3.55
download-manager / src / __ / Session.php
download-manager / src / __ Last commit date
HTML 1 year ago views 5 months ago Apply.php 6 months ago Cron.php 1 year ago CronJob.php 7 months ago CronJobs.php 2 months ago Crypt.php 1 month ago DownloadStats.php 5 months ago Email.php 5 days ago EmailCron.php 1 year ago FileSystem.php 1 year ago Installer.php 14 hours ago Messages.php 1 year ago Query.php 4 months ago Session.php 14 hours ago Settings.php 4 years ago SimpleMath.php 4 years ago TempStorage.php 14 hours ago Template.php 5 months ago UI.php 6 months ago Updater.php 4 years ago UserAgent.php 2 years ago __.php 1 month ago __MailUI.php 3 years ago
Session.php
300 lines
1 <?php
2 /**
3 * Session Management Class
4 *
5 * Handles user session data with database or file-based storage.
6 * Uses cookie-based device ID for session tracking.
7 *
8 * @package WPDM
9 * @subpackage Core
10 * @author WPDM Team
11 * @since 4.7.9
12 * @version 3.3.39
13 *
14 * @updated 2024-12-23
15 * @changelog Added function_exists() checks for WordPress functions
16 * Added fallbacks for early initialization scenarios
17 * Fixed namespace prefix for global PHP functions
18 * Improved compatibility with various server configurations
19 */
20
21 namespace WPDM\__;
22
23 class Session
24 {
25 private static $data = []; // In-memory cache
26 public static $deviceID = null;
27 private static $store;
28 private static $initialized = false;
29
30 /**
31 * Initialize session - call once per request
32 */
33 static function init()
34 {
35 if (self::$initialized) return;
36 self::$initialized = true;
37
38 // get_option should be available, but fallback to 'db' if not
39 self::$store = \function_exists('get_option') ? \get_option('__wpdm_tmp_storage', 'db') : 'db';
40 self::initDeviceID();
41
42 if (self::$store === 'file') {
43 self::loadFileSession();
44 \register_shutdown_function([__CLASS__, 'saveSession']);
45 }
46 //wp_die(self::deviceID());
47 }
48
49 /**
50 * Constructor for backward compatibility
51 */
52 function __construct()
53 {
54 self::init();
55 }
56
57 /**
58 * Get or generate device ID - cookie-first approach
59 */
60 private static function initDeviceID()
61 {
62 // Check cached value
63 if (self::$deviceID) return self::$deviceID;
64
65 // Check existing cookie
66 if (!empty($_COOKIE['__wpdm_client'])) {
67 self::$deviceID = __::sanitize_var($_COOKIE['__wpdm_client'], 'alphanum');
68 return self::$deviceID;
69 }
70
71 // Generate new ID (random is more reliable than IP+UA)
72 // Use wp_generate_password if available, otherwise fallback to PHP random
73 if (\function_exists('wp_generate_password')) {
74 $deviceID = \wp_generate_password(32, false);
75 } else {
76 // Fallback for early initialization before WordPress is fully loaded
77 $deviceID = \bin2hex(\random_bytes(16));
78 }
79 self::$deviceID = $deviceID;
80 self::setDeviceCookie($deviceID);
81
82 return self::$deviceID;
83 }
84
85 /**
86 * Set device cookie with proper domain and security flags
87 */
88 private static function setDeviceCookie($deviceID)
89 {
90 if (\defined('WPDM_ACCEPT_COOKIE') && WPDM_ACCEPT_COOKIE === false) return;
91
92 // Check if apply_filters is available (WordPress fully loaded)
93 if (\function_exists('apply_filters') && !\apply_filters('wpdm_user_accept_cookies', true)) return;
94
95 // Get domain - with fallback for early initialization
96 if (\function_exists('home_url')) {
97 $domain = \wp_parse_url(\home_url(), PHP_URL_HOST);
98 } else {
99 // Fallback: parse from server variables
100 $domain = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '';
101 $domain = \preg_replace('/:\d+$/', '', $domain); // Remove port if present
102 }
103
104 $secure = \function_exists('is_ssl') ? \is_ssl() : (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
105 @\setcookie('__wpdm_client', $deviceID, 0, "/", $domain, $secure, true);
106 $_COOKIE['__wpdm_client'] = $deviceID;
107 }
108
109 /**
110 * Get or set device ID
111 *
112 * @param string|null $deviceID Optional device ID to set
113 * @return string Current device ID
114 */
115 static function deviceID($deviceID = null)
116 {
117 if ($deviceID) {
118 self::$deviceID = __::sanitize_var($deviceID, 'alphanum');
119 self::setDeviceCookie(self::$deviceID);
120 } elseif (!self::$deviceID) {
121 self::init();
122 }
123 return self::$deviceID;
124 }
125
126 /**
127 * Set session value - with in-memory caching
128 *
129 * @param string $name Session key
130 * @param mixed $value Session value
131 * @param int $expire Expiration time in seconds (default 30 minutes)
132 */
133 static function set($name, $value, $expire = 1800)
134 {
135 if (!$name) return;
136 if (!self::$initialized) self::init();
137
138 $expireTime = \time() + $expire;
139
140 // Always cache in memory
141 self::$data[$name] = ['value' => $value, 'expire' => $expireTime];
142
143 if (self::$store === 'file') {
144 // File storage saves on shutdown
145 return;
146 }
147
148 // DB storage. REPLACE INTO only de-duplicates on a PRIMARY/UNIQUE
149 // collision; (deviceID, name) is not unique here, so REPLACE would keep
150 // inserting duplicate rows on every set(). Delete-then-insert (both
151 // index-backed by `name_device`) guarantees a single row per key.
152 global $wpdb;
153 if ($value) {
154 $wpdb->delete("{$wpdb->prefix}ahm_sessions", ['deviceID' => self::$deviceID, 'name' => $name]);
155 $wpdb->insert("{$wpdb->prefix}ahm_sessions", [
156 'deviceID' => self::$deviceID,
157 'name' => $name,
158 'value' => \maybe_serialize($value),
159 'lastAccess' => \time(),
160 'expire' => $expireTime,
161 ]);
162 } else {
163 self::clear($name);
164 }
165 }
166
167 /**
168 * Get session value - with in-memory caching
169 *
170 * @param string $name Session key
171 * @return mixed|null Session value or null if not found/expired
172 */
173 static function get($name)
174 {
175 if (!self::$initialized) self::init();
176
177 // Check in-memory cache first
178 if (isset(self::$data[$name])) {
179 $cached = self::$data[$name];
180 if ($cached['expire'] > \time()) {
181 return $cached['value'];
182 }
183 // Expired - remove from cache
184 unset(self::$data[$name]);
185 return null;
186 }
187
188 if (self::$store === 'file') {
189 return null; // File storage loads everything upfront
190 }
191
192 // DB storage - use prepared statement
193 global $wpdb;
194 $value = $wpdb->get_var($wpdb->prepare(
195 "SELECT value FROM {$wpdb->prefix}ahm_sessions
196 WHERE deviceID = %s AND name = %s AND expire > %d",
197 self::$deviceID, $name, \time()
198 ));
199
200 if ($value !== null) {
201 $unserialized = \maybe_unserialize($value);
202 // Cache for subsequent calls in same request
203 self::$data[$name] = ['value' => $unserialized, 'expire' => \time() + 300];
204 return $unserialized;
205 }
206
207 return null;
208 }
209
210 /**
211 * Clear session data
212 *
213 * @param string $name Optional key to clear. Empty clears all.
214 */
215 static function clear($name = '')
216 {
217 if (!self::$initialized) self::init();
218
219 global $wpdb;
220
221 if ($name === '') {
222 self::$data = [];
223 if (self::$store !== 'file') {
224 $wpdb->delete("{$wpdb->prefix}ahm_sessions", ['deviceID' => self::$deviceID]);
225 }
226 } else {
227 unset(self::$data[$name]);
228 if (self::$store !== 'file') {
229 $wpdb->delete("{$wpdb->prefix}ahm_sessions", ['deviceID' => self::$deviceID, 'name' => $name]);
230 }
231 }
232 }
233
234 /**
235 * Cleanup expired sessions - call via cron
236 */
237 static function cleanup()
238 {
239 global $wpdb;
240 $wpdb->query($wpdb->prepare(
241 "DELETE FROM {$wpdb->prefix}ahm_sessions WHERE expire < %d AND deviceID != 'alldevice'",
242 \time()
243 ));
244 }
245
246 /**
247 * Reset all sessions except 'alldevice' temp rows and durable download-key rows
248 */
249 static function reset()
250 {
251 global $wpdb;
252 $wpdb->query("DELETE FROM {$wpdb->prefix}ahm_sessions WHERE deviceID NOT IN ('alldevice', '" . TempStorage::DURABLE_SCOPE . "')");
253 }
254
255 /**
256 * Debug: Show session data
257 */
258 static function show()
259 {
260 wpdmprecho(self::$data);
261 }
262
263 /**
264 * Load session data from file storage
265 */
266 private static function loadFileSession()
267 {
268 $file = WPDM_CACHE_DIR . "/session-" . self::$deviceID . ".txt";
269 $realpath = \realpath($file);
270 if ($realpath && \file_exists($realpath) && \substr_count($realpath, WPDM_CACHE_DIR)) {
271 $data = \file_get_contents($realpath);
272 $data = Crypt::decrypt($data, true);
273 self::$data = \is_array($data) ? $data : [];
274 }
275 }
276
277 /**
278 * Save session data to file storage (called on shutdown)
279 */
280 static function saveSession()
281 {
282 if (self::$store !== 'file' || empty(self::$data)) return;
283
284 // Filter out expired entries before saving
285 $now = \time();
286 self::$data = \array_filter(self::$data, function($v) use ($now) {
287 return $v['expire'] > $now;
288 });
289
290 if (empty(self::$data)) return;
291
292 if (!\file_exists(WPDM_CACHE_DIR)) {
293 @\mkdir(WPDM_CACHE_DIR, 0755, true);
294 }
295
296 $data = Crypt::encrypt(self::$data);
297 \file_put_contents(WPDM_CACHE_DIR . 'session-' . self::$deviceID . '.txt', $data);
298 }
299 }
300