summaryrefslogtreecommitdiff
path: root/ext/mysqlnd/mysqlnd_loaddata.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/mysqlnd/mysqlnd_loaddata.c')
-rw-r--r--ext/mysqlnd/mysqlnd_loaddata.c334
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
+ */