diff options
author | Jani Taskinen <jani@php.net> | 2007-07-25 23:47:33 +0000 |
---|---|---|
committer | Jani Taskinen <jani@php.net> | 2007-07-25 23:47:33 +0000 |
commit | f3c2a33f4232b60b67e584ad94aeea3c3f8f7cf4 (patch) | |
tree | 4bef837b820227aafd16043ef700601588d9ac23 /ext/mysqlnd/mysqlnd_loaddata.c | |
parent | e0849ab010c287912986698230cb02ef74bddfb8 (diff) | |
download | php-git-f3c2a33f4232b60b67e584ad94aeea3c3f8f7cf4.tar.gz |
- Moved mysqnd to it's own directory: It is sort of an extension itself.
(Similar to what ext/libxml is..)
Diffstat (limited to 'ext/mysqlnd/mysqlnd_loaddata.c')
-rw-r--r-- | ext/mysqlnd/mysqlnd_loaddata.c | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/ext/mysqlnd/mysqlnd_loaddata.c b/ext/mysqlnd/mysqlnd_loaddata.c new file mode 100644 index 0000000000..ce7a27bcf4 --- /dev/null +++ b/ext/mysqlnd/mysqlnd_loaddata.c @@ -0,0 +1,334 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 6 | + +----------------------------------------------------------------------+ + | Copyright (c) 2006-2007 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Georg Richter <georg@mysql.com> | + | Andrey Hristov <andrey@mysql.com> | + | Ulf Wendel <uwendel@mysql.com> | + +----------------------------------------------------------------------+ +*/ + +#include "php.h" +#include "php_globals.h" +#include "mysqlnd.h" +#include "mysqlnd_wireprotocol.h" +#include "mysqlnd_priv.h" + +enum_func_status mysqlnd_simple_command_handle_response(MYSQLND *conn, + enum php_mysql_packet_type ok_packet, + zend_bool silent, enum php_mysqlnd_server_command command + TSRMLS_DC); + + +#define ALLOC_CALLBACK_ARGS(a, b, c)\ +if (c) {\ + a = (zval ***)safe_emalloc(c, sizeof(zval **), 0);\ + for (i = b; i < c; i++) {\ + a[i] = emalloc(sizeof(zval *));\ + MAKE_STD_ZVAL(*a[i]);\ + }\ +} + +#define FREE_CALLBACK_ARGS(a, b, c)\ +if (a) {\ + for (i=b; i < c; i++) {\ + zval_ptr_dtor(a[i]);\ + efree(a[i]);\ + }\ + efree(a);\ +} + +/* {{{ mysqlnd_local_infile_init */ +static +int mysqlnd_local_infile_init(void **ptr, char *filename, void **userdata TSRMLS_DC) +{ + MYSQLND_INFILE_INFO *info; + php_stream_context *context = NULL; + + *ptr= info= ((MYSQLND_INFILE_INFO *)ecalloc(1, sizeof(MYSQLND_INFILE_INFO))); + + /* check open_basedir */ + if (PG(open_basedir)) { + if (php_check_open_basedir_ex(filename, 0 TSRMLS_CC) == -1) { + strcpy(info->error_msg, "open_basedir restriction in effect. Unable to open file"); + info->error_no = CR_UNKNOWN_ERROR; + return 1; + } + } + + info->filename = filename; + info->fd = php_stream_open_wrapper_ex((char *)filename, "r", 0, NULL, context); + + if (info->fd == NULL) { + snprintf((char *)info->error_msg, sizeof(info->error_msg), "Can't find file '%-.64s'.", filename); + info->error_no = MYSQLND_EE_FILENOTFOUND; + return 1; + } + + return 0; +} +/* }}} */ + + +/* {{{ mysqlnd_local_infile_read */ +static +int mysqlnd_local_infile_read(void *ptr, char *buf, uint buf_len TSRMLS_DC) +{ + MYSQLND_INFILE_INFO *info = (MYSQLND_INFILE_INFO *)ptr; + int count; + + /* default processing */ + if (!info->callback) { + count = (int)php_stream_read(info->fd, buf, buf_len); + + if (count < 0) { + strcpy(info->error_msg, "Error reading file"); + info->error_no = MYSQLND_EE_READ; + } + + return count; + } else { + zval ***callback_args; + zval *retval; + zval *fp; + int argc = 4; + int i; + long rc; + + ALLOC_CALLBACK_ARGS(callback_args, 1, argc); + + /* set parameters: filepointer, buffer, buffer_len, errormsg */ + + MAKE_STD_ZVAL(fp); + php_stream_to_zval(info->fd, fp); + callback_args[0] = &fp; + ZVAL_STRING(*callback_args[1], "", 1); + ZVAL_LONG(*callback_args[2], buf_len); + ZVAL_STRING(*callback_args[3], "", 1); + + if (call_user_function_ex(EG(function_table), + NULL, + info->callback, + &retval, + argc, + callback_args, + 0, + NULL TSRMLS_CC) == SUCCESS) { + + rc = Z_LVAL_P(retval); + zval_ptr_dtor(&retval); + + if (rc > 0) { + const char * msg = NULL; + if (rc >= 0 && rc != Z_STRLEN_P(*callback_args[1])) { + msg = "Mismatch between the return value of the callback and the content " + "length of the buffer."; + php_error_docref(NULL TSRMLS_CC, E_WARNING, msg); + rc = -1; + } else if (Z_STRLEN_P(*callback_args[1]) > buf_len) { + /* check buffer overflow */ + msg = "Too much data returned"; + rc = -1; + } else { + memcpy(buf, Z_STRVAL_P(*callback_args[1]), MIN(rc, Z_STRLEN_P(*callback_args[1]))); + } + if (rc == -1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, msg); + strcpy(info->error_msg, msg); + info->error_no = MYSQLND_EE_READ; + } + } else if (rc < 0) { + strncpy(info->error_msg, Z_STRVAL_P(*callback_args[3]), MYSQLND_ERRMSG_SIZE); + info->error_no = MYSQLND_EE_READ; + } + } else { + strcpy(info->error_msg, "Can't execute load data local init callback function"); + info->error_no = MYSQLND_EE_READ; + rc = -1; + } + + efree(fp); + FREE_CALLBACK_ARGS(callback_args, 1, argc); + return rc; + + } +} +/* }}} */ + + +/* {{{ mysqlnd_local_infile_error */ +static +int mysqlnd_local_infile_error(void *ptr, char *error_buf, uint error_buf_len TSRMLS_DC) +{ + MYSQLND_INFILE_INFO *info = (MYSQLND_INFILE_INFO *)ptr; + + if (info) { + strncpy(error_buf, info->error_msg, error_buf_len); + + return info->error_no; + } + + strncpy(error_buf, "Unknown error", error_buf_len); + return CR_UNKNOWN_ERROR; +} +/* }}} */ + + +/* {{{ mysqlnd_local_infile_end */ +static +void mysqlnd_local_infile_end(void *ptr TSRMLS_DC) +{ + MYSQLND_INFILE_INFO *info = (MYSQLND_INFILE_INFO *)ptr; + + if (info) { + /* php_stream_close segfaults on NULL */ + if (info->fd) { + php_stream_close(info->fd); + } + efree(info); + } +} +/* }}} */ + + +/* {{{ mysqlnd_local_infile_default */ +PHPAPI void mysqlnd_local_infile_default(MYSQLND *conn, zend_bool free_callback) +{ + conn->infile.local_infile_init = mysqlnd_local_infile_init; + conn->infile.local_infile_read = mysqlnd_local_infile_read; + conn->infile.local_infile_error = mysqlnd_local_infile_error; + conn->infile.local_infile_end = mysqlnd_local_infile_end; + conn->infile.userdata = NULL; + if (free_callback == TRUE && conn->infile.callback) { + zval_ptr_dtor(&conn->infile.callback); + conn->infile.callback = NULL; + } +} +/* }}} */ + +/* {{{ mysqlnd_set_local_infile_handler */ +PHPAPI void mysqlnd_set_local_infile_handler(MYSQLND * const conn, const char * const funcname) +{ + if (!conn->infile.callback) { + MAKE_STD_ZVAL(conn->infile.callback); + } else { + zval_dtor(conn->infile.callback); + } + ZVAL_STRING(conn->infile.callback, (char*) funcname, 1); +} +/* }}} */ + + +static const char *lost_conn = "Lost connection to MySQL server during LOAD DATA of local file"; + + +/* {{{ mysqlnd_handle_local_infile */ +enum_func_status +mysqlnd_handle_local_infile(MYSQLND *conn, const char *filename, zend_bool *is_warning TSRMLS_DC) +{ + char *buf; + char empty_packet[MYSQLND_HEADER_SIZE]; + enum_func_status result = FAIL; + uint buflen = 4096; + void *info = NULL; + int bufsize; + size_t ret; + MYSQLND_INFILE infile; + + if (!(conn->options.flags & CLIENT_LOCAL_FILES)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "LOAD DATA LOCAL INFILE forbidden"); + /* write empty packet to server */ + ret = mysqlnd_stream_write_w_header(conn, empty_packet, 0 TSRMLS_CC); + *is_warning = TRUE; + goto infile_error; + } + + /* check if we have valid functions */ + if (!conn->infile.local_infile_init || !conn->infile.local_infile_read || + !conn->infile.local_infile_error || !conn->infile.local_infile_end) { + mysqlnd_local_infile_default(conn, FALSE); + } + + infile = conn->infile; + /* allocate buffer for reading data */ + buf = (char *)ecalloc(1, buflen); + + *is_warning = FALSE; + + /* init handler: allocate read buffer and open file */ + if (infile.local_infile_init(&info, (char *)filename, conn->infile.userdata TSRMLS_CC)) { + *is_warning = TRUE; + /* error occured */ + strcpy(conn->error_info.sqlstate, UNKNOWN_SQLSTATE); + conn->error_info.error_no = + infile.local_infile_error(info, conn->error_info.error, + sizeof(conn->error_info.error) TSRMLS_CC); + /* write empty packet to server */ + ret = mysqlnd_stream_write_w_header(conn, empty_packet, 0 TSRMLS_CC); + goto infile_error; + } + + /* pass callback handler */ + if (infile.callback) { + MYSQLND_INFILE_INFO *ptr = (MYSQLND_INFILE_INFO *)info; + ptr->callback = infile.callback; + } + + + /* read data */ + while ((bufsize = infile.local_infile_read (info, buf + MYSQLND_HEADER_SIZE, + buflen - MYSQLND_HEADER_SIZE TSRMLS_CC)) > 0) { + if ((ret = mysqlnd_stream_write_w_header(conn, buf, bufsize TSRMLS_CC)) < 0) { + SET_CLIENT_ERROR(conn->error_info, CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn); + goto infile_error; + } + } + + /* send empty packet for eof */ + if ((ret = mysqlnd_stream_write_w_header(conn, empty_packet, 0 TSRMLS_CC)) < 0) { + SET_CLIENT_ERROR(conn->error_info, CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn); + goto infile_error; + } + + /* error during read occured */ + if (bufsize < 0) { + *is_warning = TRUE; + strcpy(conn->error_info.sqlstate, UNKNOWN_SQLSTATE); + conn->error_info.error_no = infile.local_infile_error(info, conn->error_info.error, + sizeof(conn->error_info.error) TSRMLS_CC); + goto infile_error; + } + + result = PASS; + +infile_error: + /* get response from server and update upsert values */ + if (FAIL == mysqlnd_simple_command_handle_response(conn, PROT_OK_PACKET, FALSE, COM_QUERY TSRMLS_CC)) { + result = FAIL; + goto infile_error; + } + + (*conn->infile.local_infile_end)(info TSRMLS_CC); + efree(buf); + return result; +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ |