1: <?php
2:
3: 4: 5:
6: class Quform_Session
7: {
8: 9: 10:
11: protected $name;
12:
13: 14: 15: 16: 17:
18: protected $table;
19:
20: 21: 22:
23: protected $id;
24:
25: 26: 27: 28: 29:
30: protected $data = array();
31:
32: 33: 34: 35: 36:
37: protected $lifetime;
38:
39: 40: 41: 42: 43:
44: protected $started = false;
45:
46: 47: 48: 49: 50:
51: protected $dirty = false;
52:
53: 54: 55: 56: 57:
58: protected function getTableName()
59: {
60: global $wpdb;
61:
62: return $wpdb->prefix . 'quform_sessions';
63: }
64:
65: 66: 67: 68: 69: 70:
71: protected function read($sessionId)
72: {
73: global $wpdb;
74: $data = '';
75:
76: $session = $wpdb->get_row($wpdb->prepare("SELECT * FROM " . $this->getTableName() . " WHERE id = %s", $sessionId), ARRAY_A);
77:
78: if ( ! is_null($session) && isset($session['payload'])) {
79: $data = base64_decode($session['payload']);
80: }
81:
82: return $data;
83: }
84:
85: 86: 87: 88: 89: 90:
91: protected function write($sessionId, $data)
92: {
93: global $wpdb;
94:
95: if (apply_filters('quform_suppress_session_write_errors', true)) {
96: $suppress_errors = $wpdb->suppress_errors();
97: }
98:
99: $query = "INSERT INTO {$this->getTableName()} (`id`, `payload`, `last_activity`) VALUES (%s, %s, %s)
100: ON DUPLICATE KEY UPDATE `payload` = VALUES(`payload`), `last_activity` = VALUES(`last_activity`)";
101:
102: $wpdb->query($wpdb->prepare($query, $sessionId, base64_encode($data), time()));
103:
104: if (isset($suppress_errors)) {
105: $wpdb->suppress_errors($suppress_errors);
106: }
107: }
108:
109: 110: 111: 112: 113:
114: protected function destroy($sessionId)
115: {
116: global $wpdb;
117:
118: $wpdb->delete($this->getTableName(), array('id' => $sessionId));
119: }
120:
121: 122: 123:
124: public function gc()
125: {
126: global $wpdb;
127:
128: $wpdb->query("DELETE FROM " . $this->getTableName() . " WHERE last_activity <= " . (time() - $this->lifetime));
129: }
130:
131: 132: 133: 134: 135:
136: public function setId($id)
137: {
138: $this->id = $id;
139: }
140:
141: 142: 143: 144: 145:
146: public function getId()
147: {
148: return $this->id;
149: }
150:
151: 152: 153: 154: 155: 156:
157: public function isValidId($id)
158: {
159: return is_string($id) && preg_match('/^[a-zA-Z0-9]{40}$/', $id);
160: }
161:
162: 163: 164: 165: 166:
167: protected function generateSessionId()
168: {
169: return Quform::randomString(40);
170: }
171:
172: 173: 174: 175: 176:
177: public function start()
178: {
179: $this->name = 'quform_session_' . COOKIEHASH;
180: $this->lifetime = apply_filters('quform_session_lifetime', 86400);
181:
182: $id = Quform::get($_COOKIE, $this->name);
183:
184: if ( ! $this->isValidId($id)) {
185: $id = $this->generateSessionId();
186:
187: $action = is_admin() ? 'admin_init' : 'send_headers';
188:
189: add_action($action, array($this, 'setSessionCookie'));
190: }
191:
192: $this->setId($id);
193:
194: $data = $this->read($this->getId());
195:
196: $this->data = Quform::isNonEmptyString($data) ? unserialize($data) : array();
197:
198: if ( ! $this->has('_token')) {
199: $this->regenerateToken();
200: }
201:
202: return $this->started = true;
203: }
204:
205: 206: 207:
208: public function setSessionCookie() {
209: $set = true;
210:
211: if (wp_doing_ajax() && Quform::get($_GET, 'action') != 'quform_support_page_caching') {
212: $set = false;
213: }
214:
215: if (!is_admin() && is_404()) {
216: $set = false;
217: }
218:
219: if (apply_filters('quform_set_session_cookie', $set)) {
220: $this->setSessionIdCookie($this->getId());
221: }
222: }
223:
224: 225: 226: 227: 228:
229: protected function setSessionIdCookie($id)
230: {
231: $expire = apply_filters('quform_session_cookie_expire', 0);
232: $secure = apply_filters('quform_session_cookie_secure', is_ssl());
233: $httpOnly = apply_filters('quform_session_cookie_http_only', true);
234: $sameSite = apply_filters('quform_session_cookie_same_site', $secure ? 'None' : 'Lax');
235:
236: Quform::setCookieHeader($this->name, $id, $expire, $secure, $httpOnly, $sameSite);
237: }
238:
239: 240: 241:
242: public function save()
243: {
244: if (isset($_SERVER['REQUEST_URI']) && strpos($_SERVER['REQUEST_URI'], '/.well-known/acme-challenge/') === 0) {
245: return;
246: }
247:
248: if ($this->dirty && $this->started) {
249: $this->write($this->id, serialize($this->data));
250: $this->dirty = false;
251: }
252: }
253:
254: 255: 256:
257: public function regenerateToken()
258: {
259: $this->put('_token', Quform::randomString(40));
260: }
261:
262: 263: 264: 265: 266:
267: public function getToken()
268: {
269: return $this->get('_token');
270: }
271:
272: 273: 274: 275: 276: 277:
278: public function has($key)
279: {
280: return ! is_null($this->get($key));
281: }
282:
283: 284: 285: 286: 287: 288: 289:
290: public function get($key = null, $default = null)
291: {
292: return Quform::get($this->data, $key, $default);
293: }
294:
295: 296: 297: 298: 299: 300:
301: public function set($key, $value)
302: {
303: Quform::set($this->data, $key, $value);
304: $this->dirty = true;
305: }
306:
307: 308: 309: 310: 311: 312: 313:
314: public function put($key, $value = null)
315: {
316: if ( ! is_array($key)) $key = array($key => $value);
317:
318: foreach ($key as $arrayKey => $arrayValue) {
319: $this->set($arrayKey, $arrayValue);
320: }
321: }
322:
323: 324: 325: 326: 327:
328: public function forget($keys)
329: {
330: Quform::forget($this->data, $keys);
331: $this->dirty = true;
332: }
333:
334: 335: 336:
337: protected function scheduleGc()
338: {
339: if ( ! wp_next_scheduled('quform_session_gc')) {
340: wp_schedule_event(time() + (12 * HOUR_IN_SECONDS), 'twicedaily', 'quform_session_gc');
341: }
342: }
343:
344: 345: 346:
347: protected function unscheduleGc()
348: {
349: if ($timestamp = wp_next_scheduled('quform_session_gc')) {
350: wp_unschedule_event($timestamp, 'quform_session_gc');
351: }
352: }
353:
354: 355: 356:
357: public function activate()
358: {
359: global $wpdb;
360:
361: require_once ABSPATH . 'wp-admin/includes/upgrade.php';
362:
363: $sql = "CREATE TABLE " . $this->getTableName() . " (
364: id VARCHAR(40) NOT NULL,
365: payload longtext NOT NULL,
366: last_activity INT UNSIGNED NOT NULL,
367: UNIQUE KEY id (id)
368: ) " . $wpdb->get_charset_collate() . ";";
369:
370: dbDelta($sql);
371:
372: $this->scheduleGc();
373: }
374:
375: 376: 377:
378: public function deactivate()
379: {
380: $this->unscheduleGc();
381: $this->gc();
382: }
383:
384: 385: 386:
387: public function uninstall()
388: {
389: global $wpdb;
390:
391: $this->unscheduleGc();
392:
393: $wpdb->query("DROP TABLE IF EXISTS " . $this->getTableName());
394: }
395:
396: 397: 398: 399: 400: 401:
402: public function dropTableOnSiteDeletion($tables)
403: {
404: $tables[] = $this->getTableName();
405:
406: return $tables;
407: }
408: }
409: