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