]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/DbSession.php
more numeric pagename fixes.
[SourceForge/phpwiki.git] / lib / DbSession.php
1 <?php rcs_id('$Id: DbSession.php,v 1.10 2004-04-18 01:11:51 rurban Exp $');
2
3 /**
4  * Store sessions data in Pear DB / ADODB ....
5  *
6  * History
7  *
8  * Originally by Stanislav Shramko <stanis@movingmail.com>
9  * Minor rewrite by Reini Urban <rurban@x-ray.at> for Phpwiki.
10  * Quasi-major rewrite/decruft/fix by Jeff Dairiki <dairiki@dairiki.org>.
11  */
12 class DB_Session
13 {
14     var $_backend;
15     /**
16      * Constructor
17      *
18      * @param mixed $dbh
19      * Pear DB handle, or WikiDB object (from which the Pear DB handle will
20      * be extracted.
21      *
22      * @param string $table
23      * Name of SQL table containing session data.
24      */
25     function DB_Session(&$dbh, $table = 'session') {
26         // Coerce WikiDB to PearDB or ADODB.
27         // Todo: adodb/dba handlers
28         $db_type = $GLOBALS['DBParams']['dbtype'];
29         if (isa($dbh, 'WikiDB')) {
30             $backend = &$dbh->_backend;
31             $db_type = substr(get_class($dbh),7);
32             $class = "DB_Session_".$db_type;
33             if (class_exists($class)) {
34                 $this->_backend = new $class(&$backend->_dbh, $table);
35                 return $this->_backend;
36             }
37         }
38         return false;
39         
40         //Fixme: E_USER_WARNING ignored!
41         trigger_error(sprintf(
42 _("Your WikiDB DB backend '%s' cannot be used for DB_Session. Set USE_DB_SESSION to false."),
43                              $db_type), E_USER_WARNING);
44     }
45     
46     function currentSessions() {
47         return $this->_backend->currentSessions();
48     }
49     function query($sql) {
50         return $this->_backend->query($sql);
51     }
52     function quote($string) {
53         return $this->_backend->quote($string);
54     }
55
56 }
57
58 class DB_Session_SQL
59 extends DB_Session
60 {
61     var $_backend_type = "SQL";
62
63     function DB_Session_SQL ($dbh, $table) {
64
65         $this->_dbh = &$dbh;
66         $this->_table = $table;
67
68         ini_set('session.save_handler','user');
69         session_module_name('user'); // new style
70         session_set_save_handler(array(&$this, 'open'),
71                                  array(&$this, 'close'),
72                                  array(&$this, 'read'),
73                                  array(&$this, 'write'),
74                                  array(&$this, 'destroy'),
75                                  array(&$this, 'gc'));
76         return $this;
77     }
78
79     function _connect() {
80         $dbh = &$this->_dbh;
81         $this->_connected = (bool)$dbh->connection;
82         if (!$this->_connected) {
83             $res = $dbh->connect($dbh->dsn);
84             if (DB::isError($res)) {
85                 error_log("PhpWiki::DB_Session::_connect: " . $res->getMessage());
86             }
87         }
88         return $dbh;
89     }
90     
91     function query($sql) {
92         return $this->_dbh->query($sql);
93     }
94
95     function quote($string) {
96         return $this->_dbh->quote($string);
97     }
98
99     function _disconnect() {
100         if (0 and $this->_connected)
101             $this->_dbh->disconnect();
102     }
103
104     /**
105      * Opens a session.
106      *
107      * Actually this function is a fake for session_set_save_handle.
108      * @param  string $save_path a path to stored files
109      * @param  string $session_name a name of the concrete file
110      * @return boolean true just a variable to notify PHP that everything 
111      * is good.
112      * @access private
113      */
114     function open ($save_path, $session_name) {
115         //$this->log("_open($save_path, $session_name)");
116         return true;
117     }
118
119     /**
120      * Closes a session.
121      *
122      * This function is called just after <i>write</i> call.
123      *
124      * @return boolean true just a variable to notify PHP that everything 
125      * is good.
126      * @access private
127      */
128     function close() {
129         //$this->log("_close()");
130         return true;
131     }
132
133     /**
134      * Reads the session data from DB.
135      *
136      * @param  string $id an id of current session
137      * @return string
138      * @access private
139      */
140     function read ($id) {
141         //$this->log("_read($id)");
142         $dbh = &$this->_connect();
143         $table = $this->_table;
144         $qid = $dbh->quote($id);
145     
146         $res = $dbh->getOne("SELECT sess_data FROM $table WHERE sess_id=$qid");
147
148         $this->_disconnect();
149         if (DB::isError($res) || empty($res))
150             return '';
151         if (preg_match('|^[a-zA-Z0-9/+=]+$|', $res))
152             $res = base64_decode($res);
153         return $res;
154     }
155   
156     /**
157      * Saves the session data into DB.
158      *
159      * Just  a  comment:       The  "write"  handler  is  not 
160      * executed until after the output stream is closed. Thus,
161      * output from debugging statements in the "write" handler
162      * will  never be seen in the browser. If debugging output
163      * is  necessary, it is suggested that the debug output be
164      * written to a file instead.
165      *
166      * @param  string $id
167      * @param  string $sess_data
168      * @return boolean true if data saved successfully  and false
169      * otherwise.
170      * @access private
171      */
172     function write ($id, $sess_data) {
173         
174         $dbh = &$this->_connect();
175         $table = $this->_table;
176         $qid = $dbh->quote($id);
177         $qip = $dbh->quote($GLOBALS['HTTP_SERVER_VARS']['REMOTE_ADDR']);
178         $time = time();
179
180         // postgres can't handle binary data in a TEXT field.
181         if (isa($dbh, 'DB_pgsql'))
182             $sess_data = base64_encode($sess_data);
183         $qdata = $dbh->quote($sess_data);
184         
185         $res = $dbh->query("UPDATE $table"
186                            . " SET sess_data=$qdata, sess_date=$time, sess_ip=$qip"
187                            . " WHERE sess_id=$qid");
188
189         if ($dbh->affectedRows() == 0)
190             $res = $dbh->query("INSERT INTO $table"
191                                . " (sess_id, sess_data, sess_date, sess_ip)"
192                                . " VALUES ($qid, $qdata, $time, $qip)");
193
194         $this->_disconnect();
195         return ! DB::isError($res);
196     }
197
198     /**
199      * Destroys a session.
200      *
201      * Removes a session from the table.
202      *
203      * @param  string $id
204      * @return boolean true 
205      * @access private
206      */
207     function destroy ($id) {
208         $dbh = &$this->_connect();
209         $table = $this->_table;
210         $qid = $dbh->quote($id);
211
212         $dbh->query("DELETE FROM $table WHERE sess_id=$qid");
213
214         $this->_disconnect();
215         return true;     
216     }
217
218     /**
219      * Cleans out all expired sessions.
220      *
221      * @param  int $maxlifetime session's time to live.
222      * @return boolean true
223      * @access private
224      */
225     function gc ($maxlifetime) {
226         $dbh = &$this->_connect();
227         $table = $this->_table;
228         $threshold = time() - $maxlifetime;
229
230         $dbh->query("DELETE FROM $table WHERE sess_date < $threshold");
231
232         $this->_disconnect();
233         return true;
234     }
235
236     // WhoIsOnline support
237     // TODO: ip-accesstime dynamic blocking API
238     function currentSessions() {
239         $sessions = array();
240         $dbh = &$this->_connect();
241         $table = $this->_table;
242         $res = $this->query("SELECT sess_data,sess_date,sess_ip FROM $table ORDER BY sess_date DESC");
243         if (DB::isError($res) || empty($res))
244             return $sessions;
245         while ($row = $res->fetchRow()) {
246             $data = $row['sess_data'];
247             $date = $row['sess_date'];
248             $ip   = $row['sess_ip'];
249             if (preg_match('|^[a-zA-Z0-9/+=]+$|', $data))
250                 $data = base64_decode($data);
251             // session_data contains the <variable name> + "|" + <packed string>
252             // we need just the wiki_user object (might be array as well)
253             $user = strstr($data,"wiki_user|");
254             $sessions[] = array('wiki_user' => substr($user,10), // from "O:" onwards
255                                 'date' => $date,
256                                 'ip'   => $ip);
257         }
258         $this->_disconnect();
259         return $sessions;
260     }
261 }
262
263 // self-written adodb-sessions
264 class DB_Session_ADODB
265 extends DB_Session
266 {
267     var $_backend_type = "ADODB";
268
269     function DB_Session_ADODB ($dbh, $table) {
270
271         $this->_dbh = &$dbh;
272         $this->_table = $table;
273
274         ini_set('session.save_handler','user');
275         session_module_name('user'); // new style
276         session_set_save_handler(array(&$this, 'open'),
277                                  array(&$this, 'close'),
278                                  array(&$this, 'read'),
279                                  array(&$this, 'write'),
280                                  array(&$this, 'destroy'),
281                                  array(&$this, 'gc'));
282         return $this;
283     }
284
285     function _connect() {
286         global $DBParams;
287         static $parsed = false;
288         $dbh = &$this->_dbh;
289         if (!$dbh) {
290             if (!$parsed) $parsed = parseDSN($DBParams['dsn']);
291             $this->_dbh = &ADONewConnection($parsed['phptype']); // Probably only MySql works just now
292             $conn = $this->_dbh->Connect($parsed['hostspec'],$parsed['username'], 
293                                          $parsed['password'], $parsed['database']);
294             $dbh = &$this->_dbh;                             
295         }
296         return $dbh;
297     }
298     
299     function query($sql) {
300         return $this->_dbh->Execute($sql);
301     }
302
303     function quote($string) {
304         return $this->_dbh->qstr($string);
305     }
306
307     function _disconnect() {
308         if (0 and $this->_dbh)
309             $this->_dbh->close();
310     }
311
312     /**
313      * Opens a session.
314      *
315      * Actually this function is a fake for session_set_save_handle.
316      * @param  string $save_path a path to stored files
317      * @param  string $session_name a name of the concrete file
318      * @return boolean true just a variable to notify PHP that everything 
319      * is good.
320      * @access private
321      */
322     function open ($save_path, $session_name) {
323         //$this->log("_open($save_path, $session_name)");
324         return true;
325     }
326
327     /**
328      * Closes a session.
329      *
330      * This function is called just after <i>write</i> call.
331      *
332      * @return boolean true just a variable to notify PHP that everything 
333      * is good.
334      * @access private
335      */
336     function close() {
337         //$this->log("_close()");
338         return true;
339     }
340
341     /**
342      * Reads the session data from DB.
343      *
344      * @param  string $id an id of current session
345      * @return string
346      * @access private
347      */
348     function read ($id) {
349         //$this->log("_read($id)");
350         $dbh = &$this->_connect();
351         $table = $this->_table;
352         $qid = $dbh->qstr($id);
353         $res = '';
354         $row = $dbh->GetRow("SELECT sess_data FROM $table WHERE sess_id=$qid");
355         if ($row)
356             $res = $row[0];
357         $this->_disconnect();
358         if (!empty($res) and preg_match('|^[a-zA-Z0-9/+=]+$|', $res))
359             $res = base64_decode($res);
360         return $res;
361     }
362   
363     /**
364      * Saves the session data into DB.
365      *
366      * Just  a  comment:       The  "write"  handler  is  not 
367      * executed until after the output stream is closed. Thus,
368      * output from debugging statements in the "write" handler
369      * will  never be seen in the browser. If debugging output
370      * is  necessary, it is suggested that the debug output be
371      * written to a file instead.
372      *
373      * @param  string $id
374      * @param  string $sess_data
375      * @return boolean true if data saved successfully  and false
376      * otherwise.
377      * @access private
378      */
379     function write ($id, $sess_data) {
380         
381         $dbh = &$this->_connect();
382         $table = $this->_table;
383         $qid = $dbh->qstr($id);
384         $qip = $dbh->qstr($GLOBALS['HTTP_SERVER_VARS']['REMOTE_ADDR']);
385         $time = time();
386
387         // postgres can't handle binary data in a TEXT field.
388         if (isa($dbh, 'ADODB_postgres64'))
389             $sess_data = base64_encode($sess_data);
390         $qdata = $dbh->qstr($sess_data);
391         $rs = $dbh->Execute("UPDATE $table"
392                            . " SET sess_data=$qdata, sess_date=$time, sess_ip=$qip"
393                            . " WHERE sess_id=$qid");
394         if (! $dbh->Affected_Rows() )
395             $rs = $dbh->Execute("INSERT INTO $table"
396                                . " (sess_id, sess_data, sess_date, sess_ip)"
397                                . " VALUES ($qid, $qdata, $time, $qip)");
398         $result = ! $rs->EOF;
399         if ($result) $rs->free();                        
400         $this->_disconnect();
401         return $result;
402     }
403
404     /**
405      * Destroys a session.
406      *
407      * Removes a session from the table.
408      *
409      * @param  string $id
410      * @return boolean true 
411      * @access private
412      */
413     function destroy ($id) {
414         $dbh = &$this->_connect();
415         $table = $this->_table;
416         $qid = $dbh->qstr($id);
417
418         $dbh->Execute("DELETE FROM $table WHERE sess_id=$qid");
419
420         $this->_disconnect();
421         return true;     
422     }
423
424     /**
425      * Cleans out all expired sessions.
426      *
427      * @param  int $maxlifetime session's time to live.
428      * @return boolean true
429      * @access private
430      */
431     function gc ($maxlifetime) {
432         $dbh = &$this->_connect();
433         $table = $this->_table;
434         $threshold = time() - $maxlifetime;
435
436         $dbh->Execute("DELETE FROM $table WHERE sess_date < $threshold");
437
438         $this->_disconnect();
439         return true;
440     }
441
442     // WhoIsOnline support. 
443     // TODO: ip-accesstime dynamic blocking API
444     function currentSessions() {
445         $sessions = array();
446         $dbh = &$this->_connect();
447         $table = $this->_table;
448         $rs = $this->Execute("SELECT sess_data,sess_date,sess_ip FROM $table ORDER BY sess_date DESC");
449         if ($rs->EOF) {
450             $rs->free();
451             return $sessions;
452         }
453         while (!$rs->EOF) {
454             $row = $rs->fetchRow();
455             $data = $row[0];
456             $date = $row[1];
457             $ip   = $row[2];
458             if (preg_match('|^[a-zA-Z0-9/+=]+$|', $data))
459                 $data = base64_decode($data);
460             // session_data contains the <variable name> + "|" + <packed string>
461             // we need just the wiki_user object (might be array as well)
462             $user = strstr($data,"wiki_user|");
463             $sessions[] = array('wiki_user' => substr($user,10), // from "O:" onwards
464                                 'date' => $date,
465                                 'ip' => $ip);
466             $rs->MoveNext();
467         }
468         $rs->free();
469         $this->_disconnect();
470         return $sessions;
471     }
472 }
473
474 /** DBA Sessions
475  *  session:
476  *    Index: session_id
477  *   Values: date : IP : data
478  */
479 class DB_Session_dba
480 extends DB_Session
481 {
482     var $_backend_type = "dba";
483
484     function DB_Session_dba ($dbh, $table) {
485         $this->_dbh = &$dbh;
486         ini_set('session.save_handler','user');
487         session_module_name('user'); // new style
488         session_set_save_handler(array(&$this, 'open'),
489                                  array(&$this, 'close'),
490                                  array(&$this, 'read'),
491                                  array(&$this, 'write'),
492                                  array(&$this, 'destroy'),
493                                  array(&$this, 'gc'));
494         return $this;
495     }
496
497     function quote($str) { return $str; }
498     function query($sql) { return false; }
499
500     function _connect() {
501         global $DBParams;
502         $dbh = &$this->_dbh;
503         if (!$dbh) {
504             $directory = '/tmp';
505             $prefix = 'wiki_';
506             $dba_handler = 'gdbm';
507             $timeout = 20;
508             extract($DBParams);
509             $dbfile = "$directory/$prefix" . 'session' . '.' . $dba_handler;
510             $dbh = new DbaDatabase($dbfile, false, $dba_handler);
511             $dbh->set_timeout($timeout);
512             if (!$dbh->open('c')) {
513                 trigger_error(sprintf(_("%s: Can't open dba database"), $dbfile), E_USER_ERROR);
514                 global $request;
515                 $request->finish(fmt("%s: Can't open dba database", $dbfile));
516             }
517             $this->_dbh = &$dbh;
518         }
519         return $dbh;
520     }
521
522     function _disconnect() {
523         if (0 and isset($this->_dbh))
524             $this->_dbh->close();
525     }
526
527     function open ($save_path, $session_name) {
528         $dbh = &$this->_connect();
529         $dbh->open();
530     }
531
532     function close() {
533         $dbh = &$this->_connect();
534         $dbh->close();
535     }
536
537     function read ($id) {
538         $dbh = &$this->_connect();
539         $result = $dbh->get($id);
540         if (!$result) {
541             return false;
542         }
543         list(,,$packed) = explode(':', $result, 3);
544         $data = unserialize($packed);
545         $this->_disconnect();
546         return $data;
547     }
548   
549     function write ($id, $sess_data) {
550         $dbh = &$this->_connect();
551         $time = time();
552         $ip = $GLOBALS['HTTP_SERVER_VARS']['REMOTE_ADDR'];
553         $dbh->set($id,$time.':'.$ip.':'.$sess_data);
554         $this->_disconnect();
555         return true;
556     }
557
558     function destroy ($id) {
559         $dbh = &$this->_connect();
560         $dbh->delete($id);
561         $this->_disconnect();
562         return true;
563     }
564
565     function gc ($maxlifetime) {
566         $dbh = &$this->_connect();
567         $threshold = time() - $maxlifetime;
568         for ($id = $dbh->firstkey(); $id !== false; $id = $dbh->nextkey()) {
569             $result = $dbh->get($id);
570             list($date,,) = explode(':', $result, 3);
571             //$dbh->query("DELETE FROM $table WHERE sess_date < $threshold");
572             if ($date < $threshold)
573                 $dbh->delete($id);
574         }
575         $this->_disconnect();
576         return true;
577     }
578
579     // WhoIsOnline support. 
580     // TODO: ip-accesstime dynamic blocking API
581     function currentSessions() {
582         $sessions = array();
583         $dbh = &$this->_connect();
584         for ($id = $dbh->firstkey(); $id !== false; $id = $dbh->nextkey()) {
585             $result = $dbh->get($id);
586             list($date,$ip,$packed) = explode(':', $result, 3);
587             $data = unserialize($packed);
588             // session_data contains the <variable name> + "|" + <packed string>
589             // we need just the wiki_user object (might be array as well)
590             $user = strstr($data,"wiki_user|");
591             $sessions[] = array('wiki_user' => substr($user,10), // from "O:" onwards
592                                 'date' => $date,
593                                 'ip' => $ip);
594         }
595         $this->_disconnect();
596         return $sessions;
597     }
598 }
599
600
601 // Local Variables:
602 // mode: php
603 // tab-width: 8
604 // c-basic-offset: 4
605 // c-hanging-comment-ender-p: nil
606 // indent-tabs-mode: nil
607 // End:
608 ?>