xref: /PHP-5.3/ext/mysqlnd/mysqlnd_loaddata.c (revision bc11e6fd)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 2006-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: Georg Richter <georg@mysql.com>                             |
16   |          Andrey Hristov <andrey@mysql.com>                           |
17   |          Ulf Wendel <uwendel@mysql.com>                              |
18   +----------------------------------------------------------------------+
19 */
20 
21 #include "php.h"
22 #include "php_globals.h"
23 #include "mysqlnd.h"
24 #include "mysqlnd_wireprotocol.h"
25 #include "mysqlnd_priv.h"
26 #include "mysqlnd_debug.h"
27 
28 
29 #define ALLOC_CALLBACK_ARGS(a, b, c)\
30 if (c) {\
31 	a = (zval ***)safe_emalloc(c, sizeof(zval **), 0);\
32 	for (i = b; i < c; i++) {\
33 		a[i] = mnd_emalloc(sizeof(zval *));\
34 		MAKE_STD_ZVAL(*a[i]);\
35 	}\
36 }
37 
38 #define FREE_CALLBACK_ARGS(a, b, c)\
39 if (a) {\
40 	for (i = b; i < c; i++) {\
41 		zval_ptr_dtor(a[i]);\
42 		mnd_efree(a[i]);\
43 	}\
44 	mnd_efree(a);\
45 }
46 
47 /* {{{ mysqlnd_local_infile_init */
48 static
mysqlnd_local_infile_init(void ** ptr,char * filename,void ** userdata TSRMLS_DC)49 int mysqlnd_local_infile_init(void **ptr, char *filename, void **userdata TSRMLS_DC)
50 {
51 	MYSQLND_INFILE_INFO	*info;
52 	php_stream_context	*context = NULL;
53 
54 	DBG_ENTER("mysqlnd_local_infile_init");
55 
56 	info = ((MYSQLND_INFILE_INFO *)mnd_ecalloc(1, sizeof(MYSQLND_INFILE_INFO)));
57 	if (!info) {
58 		DBG_RETURN(1);
59 	}
60 
61 	*ptr = info;
62 
63 	/* check open_basedir */
64 	if (PG(open_basedir)) {
65 		if (php_check_open_basedir_ex(filename, 0 TSRMLS_CC) == -1) {
66 			strcpy(info->error_msg, "open_basedir restriction in effect. Unable to open file");
67 			info->error_no = CR_UNKNOWN_ERROR;
68 			DBG_RETURN(1);
69 		}
70 	}
71 
72 	info->filename = filename;
73 	info->fd = php_stream_open_wrapper_ex((char *)filename, "r", 0, NULL, context);
74 
75 	if (info->fd == NULL) {
76 		snprintf((char *)info->error_msg, sizeof(info->error_msg), "Can't find file '%-.64s'.", filename);
77 		info->error_no = MYSQLND_EE_FILENOTFOUND;
78 		DBG_RETURN(1);
79 	}
80 
81 	DBG_RETURN(0);
82 }
83 /* }}} */
84 
85 
86 /* {{{ mysqlnd_local_infile_read */
87 static
mysqlnd_local_infile_read(void * ptr,char * buf,unsigned int buf_len TSRMLS_DC)88 int mysqlnd_local_infile_read(void *ptr, char *buf, unsigned int buf_len TSRMLS_DC)
89 {
90 	MYSQLND_INFILE_INFO	*info = (MYSQLND_INFILE_INFO *)ptr;
91 	int count;
92 
93 	DBG_ENTER("mysqlnd_local_infile_read");
94 
95 	count = (int)php_stream_read(info->fd, buf, buf_len);
96 
97 	if (count < 0) {
98 		strcpy(info->error_msg, "Error reading file");
99 		info->error_no = CR_UNKNOWN_ERROR;
100 	}
101 
102 	DBG_RETURN(count);
103 }
104 /* }}} */
105 
106 
107 /* {{{ mysqlnd_local_infile_error */
108 static
mysqlnd_local_infile_error(void * ptr,char * error_buf,unsigned int error_buf_len TSRMLS_DC)109 int	mysqlnd_local_infile_error(void *ptr, char *error_buf, unsigned int error_buf_len TSRMLS_DC)
110 {
111 	MYSQLND_INFILE_INFO	*info = (MYSQLND_INFILE_INFO *)ptr;
112 
113 	DBG_ENTER("mysqlnd_local_infile_error");
114 
115 	if (info) {
116 		strlcpy(error_buf, info->error_msg, error_buf_len);
117 		DBG_INF_FMT("have info, %d", info->error_no);
118 		DBG_RETURN(info->error_no);
119 	}
120 
121 	strlcpy(error_buf, "Unknown error", error_buf_len);
122 	DBG_INF_FMT("no info, %d", CR_UNKNOWN_ERROR);
123 	DBG_RETURN(CR_UNKNOWN_ERROR);
124 }
125 /* }}} */
126 
127 
128 /* {{{ mysqlnd_local_infile_end */
129 static
mysqlnd_local_infile_end(void * ptr TSRMLS_DC)130 void mysqlnd_local_infile_end(void *ptr TSRMLS_DC)
131 {
132 	MYSQLND_INFILE_INFO	*info = (MYSQLND_INFILE_INFO *)ptr;
133 
134 	if (info) {
135 		/* php_stream_close segfaults on NULL */
136 		if (info->fd) {
137 			php_stream_close(info->fd);
138 			info->fd = NULL;
139 		}
140 		mnd_efree(info);
141 	}
142 }
143 /* }}} */
144 
145 
146 /* {{{ mysqlnd_local_infile_default */
mysqlnd_local_infile_default(MYSQLND * conn)147 PHPAPI void mysqlnd_local_infile_default(MYSQLND *conn)
148 {
149 	conn->infile.local_infile_init = mysqlnd_local_infile_init;
150 	conn->infile.local_infile_read = mysqlnd_local_infile_read;
151 	conn->infile.local_infile_error = mysqlnd_local_infile_error;
152 	conn->infile.local_infile_end = mysqlnd_local_infile_end;
153 }
154 /* }}} */
155 
156 /* {{{ mysqlnd_set_local_infile_handler */
mysqlnd_set_local_infile_handler(MYSQLND * const conn,const char * const funcname)157 PHPAPI void mysqlnd_set_local_infile_handler(MYSQLND * const conn, const char * const funcname)
158 {
159 	if (!conn->infile.callback) {
160 		MAKE_STD_ZVAL(conn->infile.callback);
161 	} else {
162 		zval_dtor(conn->infile.callback);
163 	}
164 	ZVAL_STRING(conn->infile.callback, (char*) funcname, 1);
165 }
166 /* }}} */
167 
168 
169 static const char *lost_conn = "Lost connection to MySQL server during LOAD DATA of local file";
170 
171 
172 /* {{{ mysqlnd_handle_local_infile */
173 enum_func_status
mysqlnd_handle_local_infile(MYSQLND * conn,const char * filename,zend_bool * is_warning TSRMLS_DC)174 mysqlnd_handle_local_infile(MYSQLND *conn, const char *filename, zend_bool *is_warning TSRMLS_DC)
175 {
176 	char				*buf = NULL;
177 	char				empty_packet[MYSQLND_HEADER_SIZE];
178 	enum_func_status	result = FAIL;
179 	unsigned int		buflen = 4096;
180 	void				*info = NULL;
181 	int					bufsize;
182 	size_t				ret;
183 	MYSQLND_INFILE		infile;
184 
185 	DBG_ENTER("mysqlnd_handle_local_infile");
186 
187 	if (!(conn->options.flags & CLIENT_LOCAL_FILES)) {
188 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "LOAD DATA LOCAL INFILE forbidden");
189 		/* write empty packet to server */
190 		ret = conn->net->m.send(conn, empty_packet, 0 TSRMLS_CC);
191 		*is_warning = TRUE;
192 		goto infile_error;
193 	}
194 
195 	infile = conn->infile;
196 	/* allocate buffer for reading data */
197 	buf = (char *)mnd_ecalloc(1, buflen);
198 
199 	*is_warning = FALSE;
200 
201 	/* init handler: allocate read buffer and open file */
202 	if (infile.local_infile_init(&info, (char *)filename, conn->infile.userdata TSRMLS_CC)) {
203 		*is_warning = TRUE;
204 		/* error occured */
205 		strcpy(conn->error_info.sqlstate, UNKNOWN_SQLSTATE);
206 		conn->error_info.error_no =
207 				infile.local_infile_error(info, conn->error_info.error, sizeof(conn->error_info.error) TSRMLS_CC);
208 		/* write empty packet to server */
209 		ret = conn->net->m.send(conn, empty_packet, 0 TSRMLS_CC);
210 		goto infile_error;
211 	}
212 
213 	/* read data */
214 	while ((bufsize = infile.local_infile_read (info, buf + MYSQLND_HEADER_SIZE, buflen - MYSQLND_HEADER_SIZE TSRMLS_CC)) > 0) {
215 		if ((ret = conn->net->m.send(conn, buf, bufsize TSRMLS_CC)) == 0) {
216 			DBG_ERR_FMT("Error during read : %d %s %s", CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
217 			SET_CLIENT_ERROR(conn->error_info, CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
218 			goto infile_error;
219 		}
220 	}
221 
222 	/* send empty packet for eof */
223 	if ((ret = conn->net->m.send(conn, empty_packet, 0 TSRMLS_CC)) == 0) {
224 		SET_CLIENT_ERROR(conn->error_info, CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
225 		goto infile_error;
226 	}
227 
228 	/* error during read occured */
229 	if (bufsize < 0) {
230 		*is_warning = TRUE;
231 		DBG_ERR_FMT("Bufsize < 0, warning,  %d %s %s", CR_SERVER_LOST, UNKNOWN_SQLSTATE, lost_conn);
232 		strcpy(conn->error_info.sqlstate, UNKNOWN_SQLSTATE);
233 		conn->error_info.error_no =
234 				infile.local_infile_error(info, conn->error_info.error, sizeof(conn->error_info.error) TSRMLS_CC);
235 		goto infile_error;
236 	}
237 
238 	result = PASS;
239 
240 infile_error:
241 	/* get response from server and update upsert values */
242 	if (FAIL == conn->m->simple_command_handle_response(conn, PROT_OK_PACKET, FALSE, COM_QUERY, FALSE TSRMLS_CC)) {
243 		result = FAIL;
244 	}
245 
246 	(*conn->infile.local_infile_end)(info TSRMLS_CC);
247 	if (buf) {
248 		mnd_efree(buf);
249 	}
250 	DBG_INF_FMT("%s", result == PASS? "PASS":"FAIL");
251 	DBG_RETURN(result);
252 }
253 /* }}} */
254 
255 /*
256  * Local variables:
257  * tab-width: 4
258  * c-basic-offset: 4
259  * End:
260  * vim600: noet sw=4 ts=4 fdm=marker
261  * vim<600: noet sw=4 ts=4
262  */
263