1/* 2 +----------------------------------------------------------------------+ 3 | PHP Version 5 | 4 +----------------------------------------------------------------------+ 5 | Copyright (c) 1997-2013 The PHP Group | 6 +----------------------------------------------------------------------+ 7 | This source file is subject to version 3.01 of the PHP license, | 8 | that is bundled with this package in the file LICENSE, and is | 9 | available through the world-wide-web at the following url: | 10 | http://www.php.net/license/3_01.txt | 11 | If you did not receive a copy of the PHP license and are unable to | 12 | obtain it through the world-wide-web, please send a note to | 13 | license@php.net so we can mail you a copy immediately. | 14 +----------------------------------------------------------------------+ 15 | Authors: John Coggeshall <john@php.net> | 16 | Wez Furlong <wez@thebrainroom.com> | 17 +----------------------------------------------------------------------+ 18 */ 19 20/* $Id$ */ 21 22#include "php.h" 23 24#if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) 25 26#include "ext/session/php_session.h" 27#include "ext/standard/php_lcg.h" 28#include <sqlite.h> 29#define SQLITE_RETVAL(__r) ((__r) == SQLITE_OK ? SUCCESS : FAILURE) 30#define PS_SQLITE_DATA sqlite *db = (sqlite*)PS_GET_MOD_DATA() 31extern int sqlite_encode_binary(const unsigned char *in, int n, unsigned char *out); 32extern int sqlite_decode_binary(const unsigned char *in, unsigned char *out); 33 34PS_FUNCS(sqlite); 35 36ps_module ps_mod_sqlite = { 37 PS_MOD(sqlite) 38}; 39 40PS_OPEN_FUNC(sqlite) 41{ 42 char *errmsg = NULL; 43 sqlite *db; 44 45 /* TODO: do we need a safe_mode check here? */ 46 db = sqlite_open(save_path, 0666, &errmsg); 47 if (db == NULL) { 48 php_error_docref(NULL TSRMLS_CC, E_WARNING, 49 "SQLite: failed to open/create session database `%s' - %s", save_path, errmsg); 50 sqlite_freemem(errmsg); 51 return FAILURE; 52 } 53 54 /* allow up to 1 minute when busy */ 55 sqlite_busy_timeout(db, 60000); 56 57 sqlite_exec(db, "PRAGMA default_synchronous = OFF", NULL, NULL, NULL); 58 sqlite_exec(db, "PRAGMA count_changes = OFF", NULL, NULL, NULL); 59 60 /* This will fail if the table already exists, but that's not a big problem. I'm 61 unclear as to how to check for a table's existence in SQLite -- that would be better here. */ 62 sqlite_exec(db, 63 "CREATE TABLE session_data (" 64 " sess_id PRIMARY KEY," 65 " value TEXT, " 66 " updated INTEGER " 67 ")", NULL, NULL, NULL); 68 69 PS_SET_MOD_DATA(db); 70 71 return SUCCESS; 72} 73 74PS_CLOSE_FUNC(sqlite) 75{ 76 PS_SQLITE_DATA; 77 78 sqlite_close(db); 79 80 return SUCCESS; 81} 82 83PS_READ_FUNC(sqlite) 84{ 85 PS_SQLITE_DATA; 86 char *query; 87 const char *tail; 88 sqlite_vm *vm; 89 int colcount, result; 90 const char **rowdata, **colnames; 91 char *error; 92 93 *val = NULL; 94 *vallen = 0; 95 96 query = sqlite_mprintf("SELECT value FROM session_data WHERE sess_id='%q' LIMIT 1", key); 97 if (query == NULL) { 98 /* no memory */ 99 return FAILURE; 100 } 101 102 if (sqlite_compile(db, query, &tail, &vm, &error) != SQLITE_OK) { 103 php_error_docref(NULL TSRMLS_CC, E_WARNING, "SQLite: Could not compile session read query: %s", error); 104 sqlite_freemem(error); 105 sqlite_freemem(query); 106 return FAILURE; 107 } 108 109 switch ((result = sqlite_step(vm, &colcount, &rowdata, &colnames))) { 110 case SQLITE_ROW: 111 if (rowdata[0] != NULL) { 112 *vallen = strlen(rowdata[0]); 113 if (*vallen) { 114 *val = emalloc(*vallen); 115 *vallen = sqlite_decode_binary(rowdata[0], *val); 116 (*val)[*vallen] = '\0'; 117 } else { 118 *val = STR_EMPTY_ALLOC(); 119 } 120 } 121 break; 122 default: 123 sqlite_freemem(error); 124 error = NULL; 125 } 126 127 if (SQLITE_OK != sqlite_finalize(vm, &error)) { 128 php_error_docref(NULL TSRMLS_CC, E_WARNING, "SQLite: session read: error %s", error); 129 sqlite_freemem(error); 130 error = NULL; 131 } 132 133 sqlite_freemem(query); 134 135 return *val == NULL ? FAILURE : SUCCESS; 136} 137 138PS_WRITE_FUNC(sqlite) 139{ 140 PS_SQLITE_DATA; 141 char *error; 142 time_t t; 143 char *binary; 144 int binlen; 145 int rv; 146 147 t = time(NULL); 148 149 binary = safe_emalloc(1 + vallen / 254, 257, 3); 150 binlen = sqlite_encode_binary((const unsigned char*)val, vallen, binary); 151 152 rv = sqlite_exec_printf(db, "REPLACE INTO session_data VALUES('%q', '%q', %d)", NULL, NULL, &error, key, binary, t); 153 if (rv != SQLITE_OK) { 154 php_error_docref(NULL TSRMLS_CC, E_WARNING, "SQLite: session write query failed: %s", error); 155 sqlite_freemem(error); 156 } 157 efree(binary); 158 159 return SQLITE_RETVAL(rv); 160} 161 162PS_DESTROY_FUNC(sqlite) 163{ 164 int rv; 165 PS_SQLITE_DATA; 166 167 rv = sqlite_exec_printf(db, "DELETE FROM session_data WHERE sess_id='%q'", NULL, NULL, NULL, key); 168 169 return SQLITE_RETVAL(rv); 170} 171 172PS_GC_FUNC(sqlite) 173{ 174 PS_SQLITE_DATA; 175 int rv; 176 time_t t = time(NULL); 177 178 rv = sqlite_exec_printf(db, 179 "DELETE FROM session_data WHERE (%d - updated) > %d", 180 NULL, NULL, NULL, t, maxlifetime); 181 182 /* because SQLite does not actually clear the deleted data from the database 183 * we need to occassionaly do so manually to prevent the sessions database 184 * from growing endlessly. 185 */ 186 if ((int) ((float) PS(gc_divisor) * PS(gc_divisor) * php_combined_lcg(TSRMLS_C)) < PS(gc_probability)) { 187 rv = sqlite_exec_printf(db, "VACUUM", NULL, NULL, NULL); 188 } 189 return SQLITE_RETVAL(rv); 190} 191 192#endif /* HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION) */ 193 194/* 195 * Local variables: 196 * tab-width: 4 197 * c-basic-offset: 4 198 * End: 199 * vim600: sw=4 ts=4 fdm=marker 200 * vim<600: sw=4 ts=4 201 */ 202