1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Wez Furlong <wez@php.net> |
14 +----------------------------------------------------------------------+
15 */
16
17 #ifdef HAVE_CONFIG_H
18 #include <config.h>
19 #endif
20
21 #include "php.h"
22 #include "php_ini.h"
23 #include "ext/standard/info.h"
24 #include "SAPI.h"
25 #include "ext/pdo/php_pdo.h"
26 #include "ext/pdo/php_pdo_driver.h"
27 #include "php_pdo_sqlite.h"
28 #include "php_pdo_sqlite_int.h"
29 #include "zend_exceptions.h"
30 #include "pdo_sqlite_arginfo.h"
31
32 static zend_class_entry *pdosqlite_ce;
33
34 /* {{{ pdo_sqlite_deps */
35 static const zend_module_dep pdo_sqlite_deps[] = {
36 ZEND_MOD_REQUIRED("pdo")
37 ZEND_MOD_END
38 };
39 /* }}} */
40
41 /* {{{ pdo_sqlite_module_entry */
42 zend_module_entry pdo_sqlite_module_entry = {
43 STANDARD_MODULE_HEADER_EX, NULL,
44 pdo_sqlite_deps,
45 "pdo_sqlite",
46 NULL,
47 PHP_MINIT(pdo_sqlite),
48 PHP_MSHUTDOWN(pdo_sqlite),
49 NULL,
50 NULL,
51 PHP_MINFO(pdo_sqlite),
52 PHP_PDO_SQLITE_VERSION,
53 STANDARD_MODULE_PROPERTIES
54 };
55 /* }}} */
56
57 #if defined(COMPILE_DL_PDO_SQLITE) || defined(COMPILE_DL_PDO_SQLITE_EXTERNAL)
58 ZEND_GET_MODULE(pdo_sqlite)
59 #endif
60
61 /* proto bool PdoSqlite::createFunction(string $function_name, callable $callback, int $num_args = -1, int $flags = 0)
62 Creates a function that can be used in a query
63 */
PHP_METHOD(Pdo_Sqlite,createFunction)64 PHP_METHOD(Pdo_Sqlite, createFunction)
65 {
66 pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);
67 }
68
69 #ifndef PDO_SQLITE_OMIT_LOAD_EXTENSION
70 /* Attempts to load an SQLite extension library. */
PHP_METHOD(Pdo_Sqlite,loadExtension)71 PHP_METHOD(Pdo_Sqlite, loadExtension)
72 {
73 char *extension, *errtext = NULL;
74 char fullpath[MAXPATHLEN];
75 size_t extension_len;
76
77 pdo_dbh_t *dbh;
78 pdo_sqlite_db_handle *db_handle;
79
80 if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &extension, &extension_len) == FAILURE) {
81 RETURN_THROWS();
82 }
83
84 if (extension_len == 0) {
85 zend_argument_must_not_be_empty_error(1);
86 RETURN_THROWS();
87 }
88
89 dbh = Z_PDO_DBH_P(ZEND_THIS);
90 PDO_CONSTRUCT_CHECK;
91
92 db_handle = (pdo_sqlite_db_handle *)dbh->driver_data;
93
94 #ifdef ZTS
95 if ((strncmp(sapi_module.name, "cgi", 3) != 0) &&
96 (strcmp(sapi_module.name, "cli") != 0) &&
97 (strncmp(sapi_module.name, "embed", 5) != 0)
98 ) {
99 zend_throw_exception_ex(php_pdo_get_exception(), 0, "Not supported in multithreaded Web servers");
100 RETURN_THROWS();
101 }
102 #endif
103
104 if (!VCWD_REALPATH(extension, fullpath)) {
105 zend_throw_exception_ex(php_pdo_get_exception(), 0, "Unable to load extension \"%s\"", extension);
106 RETURN_THROWS();
107 }
108
109 sqlite3 *sqlite_handle;
110 sqlite_handle = db_handle->db;
111
112 /* This only enables extension loading for the C api, not for SQL */
113 sqlite3_db_config(sqlite_handle, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, NULL);
114
115 if (sqlite3_load_extension(sqlite_handle, fullpath, 0, &errtext) != SQLITE_OK) {
116 zend_throw_exception_ex(php_pdo_get_exception(), 0, "Unable to load extension \"%s\"", errtext);
117 sqlite3_free(errtext);
118 sqlite3_db_config(sqlite_handle, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 0, NULL);
119 RETURN_THROWS();
120 }
121
122 /* We disable extension loading for a vague feeling of safety. This is probably not necessary
123 as extensions can only be loaded through C code, not through SQL, and if someone can get
124 some C code to run on the server, they can do anything.*/
125 sqlite3_db_config(sqlite_handle, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 0, NULL);
126 }
127 #endif
128
129 typedef struct {
130 sqlite3_blob *blob;
131 size_t position;
132 size_t size;
133 int flags;
134 } php_stream_pdosqlite3_data;
135
php_pdosqlite3_stream_write(php_stream * stream,const char * buf,size_t count)136 static ssize_t php_pdosqlite3_stream_write(php_stream *stream, const char *buf, size_t count)
137 {
138 php_stream_pdosqlite3_data *sqlite3_stream = (php_stream_pdosqlite3_data *) stream->abstract;
139
140 if (sqlite3_stream->flags & SQLITE_OPEN_READONLY) {
141 php_error_docref(NULL, E_WARNING, "Can't write to blob stream: is open as read only");
142 return -1;
143 }
144
145 if (sqlite3_stream->position + count > sqlite3_stream->size) {
146 php_error_docref(NULL, E_WARNING, "It is not possible to increase the size of a BLOB");
147 return -1;
148 }
149
150 if (sqlite3_blob_write(sqlite3_stream->blob, buf, count, sqlite3_stream->position) != SQLITE_OK) {
151 return -1;
152 }
153
154 if (sqlite3_stream->position + count >= sqlite3_stream->size) {
155 stream->eof = 1;
156 sqlite3_stream->position = sqlite3_stream->size;
157 }
158 else {
159 sqlite3_stream->position += count;
160 }
161
162 return count;
163 }
164
php_pdosqlite3_stream_read(php_stream * stream,char * buf,size_t count)165 static ssize_t php_pdosqlite3_stream_read(php_stream *stream, char *buf, size_t count)
166 {
167 php_stream_pdosqlite3_data *sqlite3_stream = (php_stream_pdosqlite3_data *) stream->abstract;
168
169 if (sqlite3_stream->position + count >= sqlite3_stream->size) {
170 count = sqlite3_stream->size - sqlite3_stream->position;
171 stream->eof = 1;
172 }
173 if (count) {
174 if (sqlite3_blob_read(sqlite3_stream->blob, buf, count, sqlite3_stream->position) != SQLITE_OK) {
175 return -1;
176 }
177 sqlite3_stream->position += count;
178 }
179 return count;
180 }
181
php_pdosqlite3_stream_close(php_stream * stream,int close_handle)182 static int php_pdosqlite3_stream_close(php_stream *stream, int close_handle)
183 {
184 php_stream_pdosqlite3_data *sqlite3_stream = (php_stream_pdosqlite3_data *) stream->abstract;
185
186 if (sqlite3_blob_close(sqlite3_stream->blob) != SQLITE_OK) {
187 /* Error occurred, but it still closed */
188 }
189
190 efree(sqlite3_stream);
191
192 return 0;
193 }
194
php_pdosqlite3_stream_flush(php_stream * stream)195 static int php_pdosqlite3_stream_flush(php_stream *stream)
196 {
197 /* do nothing */
198 return 0;
199 }
200
php_pdosqlite3_stream_seek(php_stream * stream,zend_off_t offset,int whence,zend_off_t * newoffs)201 static int php_pdosqlite3_stream_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs)
202 {
203 php_stream_pdosqlite3_data *sqlite3_stream = (php_stream_pdosqlite3_data *) stream->abstract;
204
205 switch(whence) {
206 case SEEK_CUR:
207 if (offset < 0) {
208 if (sqlite3_stream->position < (size_t)(-offset)) {
209 sqlite3_stream->position = 0;
210 *newoffs = -1;
211 return -1;
212 } else {
213 sqlite3_stream->position = sqlite3_stream->position + offset;
214 *newoffs = sqlite3_stream->position;
215 stream->eof = 0;
216 return 0;
217 }
218 } else {
219 if (sqlite3_stream->position + (size_t)(offset) > sqlite3_stream->size) {
220 sqlite3_stream->position = sqlite3_stream->size;
221 *newoffs = -1;
222 return -1;
223 } else {
224 sqlite3_stream->position = sqlite3_stream->position + offset;
225 *newoffs = sqlite3_stream->position;
226 stream->eof = 0;
227 return 0;
228 }
229 }
230 case SEEK_SET:
231 if (sqlite3_stream->size < (size_t)(offset)) {
232 sqlite3_stream->position = sqlite3_stream->size;
233 *newoffs = -1;
234 return -1;
235 } else {
236 sqlite3_stream->position = offset;
237 *newoffs = sqlite3_stream->position;
238 stream->eof = 0;
239 return 0;
240 }
241 case SEEK_END:
242 if (offset > 0) {
243 sqlite3_stream->position = sqlite3_stream->size;
244 *newoffs = -1;
245 return -1;
246 } else if (sqlite3_stream->size < (size_t)(-offset)) {
247 sqlite3_stream->position = 0;
248 *newoffs = -1;
249 return -1;
250 } else {
251 sqlite3_stream->position = sqlite3_stream->size + offset;
252 *newoffs = sqlite3_stream->position;
253 stream->eof = 0;
254 return 0;
255 }
256 default:
257 *newoffs = sqlite3_stream->position;
258 return -1;
259 }
260 }
261
php_pdosqlite3_stream_cast(php_stream * stream,int castas,void ** ret)262 static int php_pdosqlite3_stream_cast(php_stream *stream, int castas, void **ret)
263 {
264 return FAILURE;
265 }
266
php_pdosqlite3_stream_stat(php_stream * stream,php_stream_statbuf * ssb)267 static int php_pdosqlite3_stream_stat(php_stream *stream, php_stream_statbuf *ssb)
268 {
269 php_stream_pdosqlite3_data *sqlite3_stream = (php_stream_pdosqlite3_data *) stream->abstract;
270 ssb->sb.st_size = sqlite3_stream->size;
271 return 0;
272 }
273
274 static const php_stream_ops php_stream_pdosqlite3_ops = {
275 php_pdosqlite3_stream_write,
276 php_pdosqlite3_stream_read,
277 php_pdosqlite3_stream_close,
278 php_pdosqlite3_stream_flush,
279 "PDOSQLite",
280 php_pdosqlite3_stream_seek,
281 php_pdosqlite3_stream_cast,
282 php_pdosqlite3_stream_stat,
283 NULL
284 };
285
286 /* Open a blob as a stream which we can read / write to. */
PHP_METHOD(Pdo_Sqlite,openBlob)287 PHP_METHOD(Pdo_Sqlite, openBlob)
288 {
289 char *table, *column, *dbname = "main", *mode = "rb";
290 size_t table_len, column_len, dbname_len;
291 zend_long rowid, flags = SQLITE_OPEN_READONLY, sqlite_flags = 0;
292 sqlite3_blob *blob = NULL;
293 php_stream_pdosqlite3_data *sqlite3_stream;
294 php_stream *stream;
295
296 pdo_dbh_t *dbh;
297 pdo_sqlite_db_handle *db_handle;
298
299 dbh = Z_PDO_DBH_P(ZEND_THIS);
300 PDO_CONSTRUCT_CHECK;
301 db_handle = (pdo_sqlite_db_handle *)dbh->driver_data;
302
303 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ppl|pl", &table, &table_len, &column, &column_len, &rowid, &dbname, &dbname_len, &flags) == FAILURE) {
304 RETURN_THROWS();
305 }
306
307 sqlite3 *sqlite_handle;
308 sqlite_handle = db_handle->db;
309 sqlite_flags = (flags & SQLITE_OPEN_READWRITE) ? 1 : 0;
310
311 if (sqlite3_blob_open(sqlite_handle, dbname, table, column, rowid, sqlite_flags, &blob) != SQLITE_OK) {
312 zend_error(E_WARNING, "Unable to open blob: %s", sqlite3_errmsg(sqlite_handle));
313 RETURN_FALSE;
314 }
315
316 sqlite3_stream = emalloc(sizeof(php_stream_pdosqlite3_data));
317 sqlite3_stream->blob = blob;
318 sqlite3_stream->flags = flags;
319 sqlite3_stream->position = 0;
320 sqlite3_stream->size = sqlite3_blob_bytes(blob);
321
322 if (sqlite_flags != 0) {
323 mode = "r+b";
324 }
325
326 stream = php_stream_alloc(&php_stream_pdosqlite3_ops, sqlite3_stream, 0, mode);
327
328 if (stream) {
329 php_stream_to_zval(stream, return_value);
330 } else {
331 RETURN_FALSE;
332 }
333 }
334
php_sqlite_collation_callback(void * context,int string1_len,const void * string1,int string2_len,const void * string2)335 static int php_sqlite_collation_callback(void *context, int string1_len, const void *string1,
336 int string2_len, const void *string2)
337 {
338 int ret = 0;
339 zval zargs[2];
340 zval retval;
341 struct pdo_sqlite_collation *collation = (struct pdo_sqlite_collation*) context;
342
343 // Prepare the arguments.
344 ZVAL_STRINGL(&zargs[0], (char *) string1, string1_len);
345 ZVAL_STRINGL(&zargs[1], (char *) string2, string2_len);
346
347 zend_call_known_fcc(&collation->callback, &retval, /* argc */ 2, zargs, /* named_params */ NULL);
348
349 if (!Z_ISUNDEF(retval)) {
350 if (Z_TYPE(retval) != IS_LONG) {
351 zend_string *func_name = get_active_function_or_method_name();
352 zend_type_error("%s(): Return value of the callback must be of type int, %s returned",
353 ZSTR_VAL(func_name), zend_zval_value_name(&retval));
354 zend_string_release(func_name);
355 return FAILURE;
356 }
357 if (Z_LVAL(retval) > 0) {
358 ret = 1;
359 } else if (Z_LVAL(retval) < 0) {
360 ret = -1;
361 }
362 zval_ptr_dtor(&retval);
363 }
364
365 zval_ptr_dtor(&zargs[0]);
366 zval_ptr_dtor(&zargs[1]);
367
368 return ret;
369 }
370
PHP_METHOD(Pdo_Sqlite,createAggregate)371 PHP_METHOD(Pdo_Sqlite, createAggregate)
372 {
373 pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);
374 }
375
PHP_METHOD(Pdo_Sqlite,createCollation)376 PHP_METHOD(Pdo_Sqlite, createCollation)
377 {
378 pdo_sqlite_create_collation_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_sqlite_collation_callback);
379 }
380
381 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(pdo_sqlite)382 PHP_MINIT_FUNCTION(pdo_sqlite)
383 {
384 #ifdef SQLITE_DETERMINISTIC
385 REGISTER_PDO_CLASS_CONST_LONG("SQLITE_DETERMINISTIC", (zend_long)SQLITE_DETERMINISTIC);
386 #endif
387
388 REGISTER_PDO_CLASS_CONST_LONG("SQLITE_ATTR_OPEN_FLAGS", (zend_long)PDO_SQLITE_ATTR_OPEN_FLAGS);
389 REGISTER_PDO_CLASS_CONST_LONG("SQLITE_OPEN_READONLY", (zend_long)SQLITE_OPEN_READONLY);
390 REGISTER_PDO_CLASS_CONST_LONG("SQLITE_OPEN_READWRITE", (zend_long)SQLITE_OPEN_READWRITE);
391 REGISTER_PDO_CLASS_CONST_LONG("SQLITE_OPEN_CREATE", (zend_long)SQLITE_OPEN_CREATE);
392 REGISTER_PDO_CLASS_CONST_LONG("SQLITE_ATTR_READONLY_STATEMENT", (zend_long)PDO_SQLITE_ATTR_READONLY_STATEMENT);
393 REGISTER_PDO_CLASS_CONST_LONG("SQLITE_ATTR_EXTENDED_RESULT_CODES", (zend_long)PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES);
394
395 pdosqlite_ce = register_class_Pdo_Sqlite(pdo_dbh_ce);
396 pdosqlite_ce->create_object = pdo_dbh_new;
397
398 if (php_pdo_register_driver(&pdo_sqlite_driver) == FAILURE) {
399 return FAILURE;
400 }
401
402 return php_pdo_register_driver_specific_ce(&pdo_sqlite_driver, pdosqlite_ce);
403 }
404 /* }}} */
405
406 /* {{{ PHP_MSHUTDOWN_FUNCTION */
PHP_MSHUTDOWN_FUNCTION(pdo_sqlite)407 PHP_MSHUTDOWN_FUNCTION(pdo_sqlite)
408 {
409 php_pdo_unregister_driver(&pdo_sqlite_driver);
410 return SUCCESS;
411 }
412 /* }}} */
413
414 /* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(pdo_sqlite)415 PHP_MINFO_FUNCTION(pdo_sqlite)
416 {
417 php_info_print_table_start();
418 php_info_print_table_row(2, "PDO Driver for SQLite 3.x", "enabled");
419 php_info_print_table_row(2, "SQLite Library", sqlite3_libversion());
420 php_info_print_table_end();
421 }
422 /* }}} */
423