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    | http://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    | Authors:                                                             |
14    |          Israel Ekpo <iekpo@php.net>                                 |
15    |          Omar Shaban <omars@php.net>                                 |
16    +----------------------------------------------------------------------+
17 */
18 
19 #include "php_solr.h"
20 
21 /* {{{ PHP_SOLR_API int solr_init_options(solr_client_options_t *options) */
solr_init_options(solr_client_options_t * options)22 PHP_SOLR_API int solr_init_options(solr_client_options_t *options)
23 {
24 	solr_string_init(&(options->hostname));
25 	solr_string_init(&(options->path));
26 	solr_string_init(&(options->http_auth_credentials));
27 	solr_string_init(&(options->proxy_hostname));
28 	solr_string_init(&(options->proxy_auth_credentials));
29 	solr_string_init(&(options->ssl_cert));
30 	solr_string_init(&(options->ssl_key));
31 	solr_string_init(&(options->ssl_keypassword));
32 	solr_string_init(&(options->ssl_cainfo));
33 	solr_string_init(&(options->ssl_capath));
34 
35 	solr_string_init(&(options->qs_delimiter));
36 	solr_string_init(&(options->response_writer));
37 
38 	solr_string_init(&(options->update_url));
39 	solr_string_init(&(options->search_url));
40 	solr_string_init(&(options->thread_url));
41 	solr_string_init(&(options->ping_url));
42 	solr_string_init(&(options->terms_url));
43 	solr_string_init(&(options->system_url));
44 	solr_string_init(&(options->get_url));
45 
46 	solr_string_init(&(options->update_servlet));
47 	solr_string_init(&(options->search_servlet));
48 	solr_string_init(&(options->thread_servlet));
49 	solr_string_init(&(options->ping_servlet));
50 	solr_string_init(&(options->terms_servlet));
51 	solr_string_init(&(options->system_servlet));
52 	solr_string_init(&(options->get_servlet));
53 
54 	return SUCCESS;
55 }
56 /* }}} */
57 
58 
59 /* {{{ static void solr_set_initial_handle_options(solr_curl_t **sch, solr_client_options_t *options) */
solr_set_initial_curl_handle_options(solr_curl_t ** sch_ptr,solr_client_options_t * options)60 static void solr_set_initial_curl_handle_options(solr_curl_t **sch_ptr, solr_client_options_t *options)
61 {
62 	solr_curl_t * sch = *sch_ptr;
63 
64 	/** Setup all the required CURL options here **/
65 	curl_easy_setopt(sch->curl_handle, CURLOPT_NOPROGRESS,        1L);
66 	curl_easy_setopt(sch->curl_handle, CURLOPT_VERBOSE,           1L);
67 	curl_easy_setopt(sch->curl_handle, CURLOPT_ERRORBUFFER,       sch->err.str);
68 	curl_easy_setopt(sch->curl_handle, CURLOPT_WRITEFUNCTION,     solr_curl_write);
69 	curl_easy_setopt(sch->curl_handle, CURLOPT_WRITEDATA,         (void *) sch);
70 	curl_easy_setopt(sch->curl_handle, CURLOPT_HEADERFUNCTION,    solr_curl_write_header);
71 	curl_easy_setopt(sch->curl_handle, CURLOPT_HEADERDATA,        (void *) sch);
72 	curl_easy_setopt(sch->curl_handle, CURLOPT_DEBUGFUNCTION,     solr_curl_debug_callback);
73 	curl_easy_setopt(sch->curl_handle, CURLOPT_DEBUGDATA,         (void *) sch);
74 
75 	curl_easy_setopt(sch->curl_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
76 
77 /**
78 #if LIBCURL_VERSION_NUM >= 0x071304
79 	curl_easy_setopt(sch->curl_handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP);
80 #endif
81 */
82 	curl_easy_setopt(sch->curl_handle, CURLOPT_DNS_CACHE_TIMEOUT, 120L);
83 	curl_easy_setopt(sch->curl_handle, CURLOPT_FOLLOWLOCATION, 1L);
84 	curl_easy_setopt(sch->curl_handle, CURLOPT_MAXREDIRS, 16L); /* prevent infinite redirects  */
85 	curl_easy_setopt(sch->curl_handle, CURLOPT_UNRESTRICTED_AUTH, 0L);
86 	curl_easy_setopt(sch->curl_handle, CURLOPT_ACCEPT_ENCODING, "");
87 
88 #ifdef ZTS
89 	curl_easy_setopt(sch->curl_handle, CURLOPT_NOSIGNAL, 1L); /** Needed in multi-threaded environments **/
90 #endif
91 
92 	curl_easy_setopt(sch->curl_handle, CURLOPT_TIMEOUT, options->timeout);
93 
94 	curl_easy_setopt(sch->curl_handle, CURLOPT_USERAGENT, SOLR_CLIENT_USER_AGENT);
95 
96 	/* If the login and password options are passed, send the authentication headers */
97 	if (options->http_auth_credentials.len) {
98 
99 		curl_easy_setopt(sch->curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
100 
101 		curl_easy_setopt(sch->curl_handle, CURLOPT_USERPWD, options->http_auth_credentials.str);
102 	}
103 
104 	/* If there is proxy info, send it too */
105 	if (options->proxy_hostname.len) {
106 
107 		curl_easy_setopt(sch->curl_handle, CURLOPT_PROXY, options->proxy_hostname.str);
108 
109 		if (options->proxy_port) {
110 
111 			curl_easy_setopt(sch->curl_handle, CURLOPT_PROXYPORT, options->proxy_port);
112 		}
113 
114 		if (options->proxy_auth_credentials.len) {
115 
116 			curl_easy_setopt(sch->curl_handle, CURLOPT_PROXYUSERPWD, options->proxy_auth_credentials.str);
117 		}
118 	}
119 
120 	 /*
121 	 * When negotiating an SSL connection, the server sends a certificate indicating its identity.
122 	 * cURL verifies whether the certificate is authentic
123 	 */
124 	if (options->secure) {
125 
126 		/* The name of the PEM-formatted private key and private certificate concatenated */
127 		if (options->ssl_cert.len) {
128 
129 			curl_easy_setopt(sch->curl_handle, CURLOPT_SSLCERT, options->ssl_cert.str);
130 
131 			curl_easy_setopt(sch->curl_handle, CURLOPT_SSLCERTTYPE, "PEM");
132 		}
133 
134 		/* The name of the PEM-formatted private key, if certificate and private key are separate */
135 		if (options->ssl_key.len) {
136 
137 			curl_easy_setopt(sch->curl_handle, CURLOPT_SSLKEY, options->ssl_key.str);
138 
139 			curl_easy_setopt(sch->curl_handle, CURLOPT_SSLKEYTYPE, "PEM");
140 		}
141 
142 		/* Password for the PEM-formatted private key */
143 		if (options->ssl_keypassword.len) {
144 
145 			curl_easy_setopt(sch->curl_handle, CURLOPT_KEYPASSWD, options->ssl_keypassword.str);
146 		}
147 
148 		/* The file holding one or more CA certificates to verify the peer with */
149 		if (options->ssl_cainfo.len) {
150 
151 			curl_easy_setopt(sch->curl_handle, CURLOPT_CAINFO, options->ssl_cainfo.str);
152 		}
153 
154 		/* The directory holding multiple CA certificates to verify the peer with */
155 		if (options->ssl_capath.len) {
156 
157 			curl_easy_setopt(sch->curl_handle, CURLOPT_CAPATH , options->ssl_capath.str);
158 		}
159 
160 		/*
161 		 * Whether curl verifies the authenticity of the host name of server
162 		 * Based on the Common name section of the certificate
163 		 */
164 		if (options->ssl_verify_peer && options->ssl_verify_host) {
165 
166 			curl_easy_setopt(sch->curl_handle, CURLOPT_SSL_VERIFYHOST, options->ssl_verify_host);
167 		}
168 
169 		/*
170 		 * Verify the authenticity of the peer's certificate
171 		 * This authentication is based on a chain of digital signatures,
172 		 * rooted in certification authority (CA) certificates.
173 		 *
174 		 * If something is not right, the connection will not be valid
175 		 */
176 		curl_easy_setopt(sch->curl_handle, CURLOPT_SSL_VERIFYPEER, options->ssl_verify_peer);
177 	}
178 }
179 /* }}} */
180 
181 /* {{{ PHP_SOLR_API int solr_init_handle(solr_curl_t *sch, solr_client_options_t *options) */
solr_init_handle(solr_curl_t * sch,solr_client_options_t * options)182 PHP_SOLR_API int solr_init_handle(solr_curl_t *sch, solr_client_options_t *options)
183 {
184 	sch->response_header.response_code = 0L;
185 
186 	memset(sch->err.str, 0, sizeof(sch->err.str));
187 
188 	sch->curl_handle = curl_easy_init();
189 
190 	if (NULL == sch->curl_handle) {
191 
192 		return FAILURE;
193 	}
194 
195 	sch->result_code = CURLE_OK;
196 
197 	sch->handle_status = 1;
198 
199 	solr_string_init(&(sch->request_header.buffer));
200 	solr_string_init(&(sch->request_body.buffer));
201 	solr_string_init(&(sch->request_body_debug.buffer));
202 	solr_string_init(&(sch->response_header.buffer));
203 	solr_string_init(&(sch->response_body.buffer));
204 	solr_string_init(&(sch->debug_data_buffer));
205 
206 	solr_set_initial_curl_handle_options(&(sch), options);
207 
208 	return SUCCESS;
209 }
210 /* }}} */
211 
212 /**
213  * solr_curl_write
214  *
215  * Called by libcurl as soon as there is response body data received that needs to be saved.
216  *
217  * The size of the data pointed to by *data is size multiplied with nmemb, it will not be zero terminated
218  *
219  * The *ctx parameter is the solr_curl pointer
220  */
221 /* {{{ size_t solr_curl_write(solr_char_t *data, size_t size, size_t nmemb, void *ctx) */
solr_curl_write(solr_char_t * data,size_t size,size_t nmemb,void * ctx)222 size_t solr_curl_write(solr_char_t *data, size_t size, size_t nmemb, void *ctx)
223 {
224 	size_t length = (size * nmemb);
225 	solr_curl_t *sch = (solr_curl_t *) ctx;
226 
227 	solr_string_appends(&(sch->response_body.buffer), data, length);
228 
229 	return length;
230 }
231 /* }}} */
232 
233 /**
234  * solr_curl_write_header
235  *
236  * Called by libcurl as soon as it has received header data
237  *
238  * It will be called once for each header and only complete header lines are passed on to the callback.
239  *
240  * Note: header lines may not be null-terminated so beware.
241  *
242  * The *ctx parameter is the solr_curl pointer
243  */
244 /* {{{ size_t solr_curl_write_header(solr_char_t *data, size_t size, size_t nmemb, void *ctx) */
solr_curl_write_header(solr_char_t * data,size_t size,size_t nmemb,void * ctx)245 size_t solr_curl_write_header(solr_char_t *data, size_t size, size_t nmemb, void *ctx)
246 {
247 	size_t length = (size * nmemb);
248 	solr_curl_t *sch = (solr_curl_t *) ctx;
249 
250 	solr_string_appends(&(sch->response_header.buffer), data, length);
251 
252 	return length;
253 }
254 /* }}} */
255 
256 /**
257  * solr_curl_debug_callback
258  *
259  * The infotype parameter is one of :
260  *
261  * 0 = CURLINFO_TEXT
262  * 1 = CURLINFO_HEADER_IN   (Response Headers)
263  * 2 = CURLINFO_HEADER_OUT  (Request Headers)
264  * 3 = CURLINFO_DATA_IN		(Response Body)
265  * 4 = CURLINFO_DATA_OUT	(Request Body)
266  * 5 = CURLINFO_SSL_DATA_IN
267  * 6 = CURLINFO_SSL_DATA_OUT
268  *
269  * The debug_data parameter is not null-terminated but is exactly size characters
270  *
271  * The ctx parameter is a solr_curl * pointer
272  */
273 /* {{{ int solr_curl_debug_callback(CURL *curl_handle, curl_infotype infotype, solr_char_t *debug_data, size_t size, void *ctx) */
solr_curl_debug_callback(CURL * curl_handle,curl_infotype infotype,solr_char_t * debug_data,size_t size,void * ctx)274 int solr_curl_debug_callback(CURL *curl_handle, curl_infotype infotype, solr_char_t *debug_data, size_t size, void *ctx)
275 {
276 	solr_curl_t *sch = (solr_curl_t *) ctx;
277 
278 	/* TODO : Check why this function is still called after destructor has been called */
279 	if (!sch->handle_status)
280 	{
281 		return 0;
282 	}
283 
284 	switch(infotype)
285 	{
286 		case CURLINFO_HEADER_OUT : /* Capture the Actual Request Headers Sent to Server */
287 		{
288 			solr_string_appends(&(sch->request_header.buffer), debug_data, size);
289 		}
290 		break;
291 
292 		case CURLINFO_DATA_OUT : /* Capture the Actual Request Body Sent to Server */
293 		{
294 			solr_string_appends(&(sch->request_body_debug.buffer), debug_data, size);
295 		}
296 		break;
297 
298 		default :
299 		{
300 			/* do nothing */
301 		}
302 		break;
303 	}
304 
305     /* Captures ALL debug information */
306 	solr_string_appends(&(sch->debug_data_buffer), debug_data, size);
307 
308 	return 0;
309 }
310 /* }}} */
311 
solr_curl_request_reset(solr_curl_t * sch,solr_client_options_t * options)312 PHP_SOLR_API void solr_curl_request_reset(solr_curl_t *sch, solr_client_options_t *options) {
313     /* Reset the buffers */
314     solr_string_free(&sch->request_header.buffer);
315     solr_string_free(&sch->request_body_debug.buffer);
316     solr_string_free(&sch->response_body.buffer);
317     solr_string_free(&sch->response_header.buffer);
318     solr_string_free(&sch->debug_data_buffer);
319 
320     curl_easy_reset(sch->curl_handle);
321 
322     solr_set_initial_curl_handle_options(&(sch), options);
323 
324     /* Reset the CURL options if the handle is reused */
325     curl_easy_setopt(sch->curl_handle, CURLOPT_HEADER, 0L);
326     curl_easy_setopt(sch->curl_handle, CURLOPT_POST, 0L);
327     curl_easy_setopt(sch->curl_handle, CURLOPT_HTTPGET, 0L);
328     curl_easy_setopt(sch->curl_handle, CURLOPT_NOBODY, 0L);
329 
330     curl_easy_setopt(sch->curl_handle, CURLOPT_POSTFIELDSIZE, 0L);
331     curl_easy_setopt(sch->curl_handle, CURLOPT_POSTFIELDS, NULL);
332     curl_easy_setopt(sch->curl_handle, CURLOPT_URL, NULL);
333     curl_easy_setopt(sch->curl_handle, CURLOPT_HTTPHEADER, NULL);
334 }
335 
solr_curl_init_header_list()336 PHP_SOLR_API solr_http_header_list_t *solr_curl_init_header_list()
337 {
338     solr_http_header_list_t *header_list = NULL;
339     header_list = curl_slist_append(header_list, "Accept-Charset: utf-8");
340     header_list = curl_slist_append(header_list, "Keep-Alive: 300");
341     header_list = curl_slist_append(header_list, "Connection: keep-alive");
342     /* Disable the Expect: 100-continue header. Jetty gets confused with this header */
343     header_list = curl_slist_append(header_list, "Expect:");
344     return header_list;
345 }
346 
solr_is_request_successful(CURLcode info_status,solr_curl_t * sch)347 PHP_SOLR_API int solr_is_request_successful(CURLcode info_status, solr_curl_t *sch)
348 {
349     int return_status = SUCCESS;
350 
351     if (info_status != CURLE_OK) {
352         solr_throw_exception_ex(
353                 solr_ce_SolrClientException,
354                 SOLR_ERROR_1004,
355                 SOLR_FILE_LINE_FUNC,
356                 "HTTP Transfer status could not be retrieved successfully"
357         );
358         return_status = FAILURE;
359     }
360 
361     if (sch->result_code != CURLE_OK)
362     {
363         solr_throw_exception_ex(
364                 solr_ce_SolrClientException,
365                 SOLR_ERROR_1004,
366                 SOLR_FILE_LINE_FUNC,
367                 "Solr HTTP Error %d: '%s' ",
368                 sch->result_code,
369                 curl_easy_strerror(sch->result_code)
370         );
371         return_status = FAILURE;
372     }
373 
374     if (sch->response_header.response_code != 200L)
375     {
376         return_status = FAILURE;
377     }
378     return return_status;
379 }
380 
solr_make_update_stream_request(solr_client_t * client,solr_ustream_t * stream_data,solr_string_t * request_params)381 PHP_SOLR_API int solr_make_update_stream_request(solr_client_t *client, solr_ustream_t* stream_data, solr_string_t *request_params)
382 {
383     solr_curl_t *sch = &(client->handle);
384     solr_client_options_t *options = &(client->options);
385     int return_status = SUCCESS;
386     CURLcode info_status = CURLE_OK;
387     struct curl_httppost *formpost = NULL, *lastptr = NULL;
388     int is_binary = stream_data->content_type == SOLR_EXTRACT_CONTENT_STREAM;
389     solr_string_t content_type_header;
390 
391     solr_http_header_list_t *header_list = solr_curl_init_header_list();
392     solr_curl_request_reset(sch, options);
393 
394     solr_string_appendc(&(options->extract_url), '&');
395     solr_string_append_solr_string(&(options->extract_url), request_params);
396 
397     curl_easy_setopt(sch->curl_handle, CURLOPT_URL, options->extract_url.str);
398 
399     if (is_binary) {
400         solr_string_init(&content_type_header);
401         solr_string_appends(&content_type_header, "Content-Type: ", sizeof("Content-Type: ")-1);
402         solr_string_append_solr_string(&content_type_header, &(stream_data->content_info->stream_info.mime_type));
403 
404         header_list = curl_slist_append(header_list, content_type_header.str);
405         curl_easy_setopt(sch->curl_handle, CURLOPT_POSTFIELDS, stream_data->content_info->stream_info.binary_content.str);
406         curl_easy_setopt(sch->curl_handle, CURLOPT_POSTFIELDSIZE, stream_data->content_info->stream_info.binary_content.len);
407         solr_string_free_ex(&content_type_header);
408     } else{
409         curl_formadd(&formpost, &lastptr,
410                 CURLFORM_COPYNAME, "PHPSOLRCLIENT",
411                 CURLFORM_FILE, (const char *) stream_data->content_info->filename.str,
412                 CURLFORM_END
413         );
414         curl_easy_setopt(sch->curl_handle, CURLOPT_HTTPPOST, formpost);
415     }
416 
417     curl_easy_setopt(sch->curl_handle, CURLOPT_HTTPHEADER, header_list);
418     sch->result_code = curl_easy_perform(sch->curl_handle);
419 
420     info_status = curl_easy_getinfo(sch->curl_handle, CURLINFO_RESPONSE_CODE, &(sch->response_header.response_code));
421     return_status = solr_is_request_successful(info_status, sch);
422 
423     curl_slist_free_all(header_list);
424 
425     if (!is_binary) {
426         curl_formfree(formpost);
427     }
428 
429     return return_status;
430 }
431 
432 /* {{{ PHP_SOLR_API int solr_make_request(solr_client_t *client, solr_request_type_t request_type) */
solr_make_request(solr_client_t * client,solr_request_type_t request_type)433 PHP_SOLR_API int solr_make_request(solr_client_t *client, solr_request_type_t request_type)
434 {
435 	solr_curl_t *sch = &(client->handle);
436 	solr_client_options_t *options = &(client->options);
437 	solr_http_header_list_t *header_list = NULL;
438 	int return_status = SUCCESS;
439 	CURLcode info_status = CURLE_OK;
440 
441 	solr_curl_request_reset(sch, options);
442 	header_list = solr_curl_init_header_list();
443 
444 	switch(request_type)
445 	{
446 		case SOLR_REQUEST_SEARCH : /* HTTP FORM POST */
447 		{
448 			header_list = curl_slist_append(header_list, "Content-Type: application/x-www-form-urlencoded;charset=UTF-8");
449 
450 			curl_easy_setopt(sch->curl_handle, CURLOPT_POST,    1L);
451 
452 			curl_easy_setopt(sch->curl_handle, CURLOPT_URL, options->search_url.str);
453 			curl_easy_setopt(sch->curl_handle, CURLOPT_HTTPHEADER, header_list);
454 			curl_easy_setopt(sch->curl_handle, CURLOPT_POSTFIELDSIZE, sch->request_body.buffer.len);
455 			curl_easy_setopt(sch->curl_handle, CURLOPT_POSTFIELDS, sch->request_body.buffer.str);
456 		}
457 		break;
458 
459 		case SOLR_REQUEST_TERMS : /* HTTP FORM POST */
460 		{
461 			header_list = curl_slist_append(header_list, "Content-Type: application/x-www-form-urlencoded;charset=UTF-8");
462 
463 			curl_easy_setopt(sch->curl_handle, CURLOPT_POST,    1L);
464 
465 			curl_easy_setopt(sch->curl_handle, CURLOPT_URL, options->terms_url.str);
466 			curl_easy_setopt(sch->curl_handle, CURLOPT_HTTPHEADER, header_list);
467 			curl_easy_setopt(sch->curl_handle, CURLOPT_POSTFIELDSIZE, sch->request_body.buffer.len);
468 			curl_easy_setopt(sch->curl_handle, CURLOPT_POSTFIELDS, sch->request_body.buffer.str);
469 		}
470 		break;
471 
472 		case SOLR_REQUEST_UPDATE : /* HTTP XML POST */
473 		{
474 		    header_list = curl_slist_append(header_list, "Content-Type: text/xml;charset=UTF-8");
475 
476 			curl_easy_setopt(sch->curl_handle, CURLOPT_POST,    1L);
477 
478 			curl_easy_setopt(sch->curl_handle, CURLOPT_URL, options->update_url.str);
479 			curl_easy_setopt(sch->curl_handle, CURLOPT_HTTPHEADER, header_list);
480 			curl_easy_setopt(sch->curl_handle, CURLOPT_POSTFIELDSIZE, sch->request_body.buffer.len);
481 			curl_easy_setopt(sch->curl_handle, CURLOPT_POSTFIELDS, sch->request_body.buffer.str);
482 
483 		}
484 		break;
485 
486 		case SOLR_REQUEST_THREADS : /* HTTP GET */
487 		{
488 			curl_easy_setopt(sch->curl_handle, CURLOPT_HTTPGET, 1L);
489 			curl_easy_setopt(sch->curl_handle, CURLOPT_URL, options->thread_url.str);
490 			curl_easy_setopt(sch->curl_handle, CURLOPT_HTTPHEADER, header_list);
491 
492 		}
493 		break;
494 
495 		case SOLR_REQUEST_PING :	/* HTTP HEAD REQUEST. */
496 		{
497 			curl_easy_setopt(sch->curl_handle, CURLOPT_HEADER,  1L);
498 			curl_easy_setopt(sch->curl_handle, CURLOPT_NOBODY,  1L);
499 
500 			curl_easy_setopt(sch->curl_handle, CURLOPT_URL, options->ping_url.str);
501 			curl_easy_setopt(sch->curl_handle, CURLOPT_HTTPHEADER, header_list);
502 		}
503 		break;
504 
505 		case SOLR_REQUEST_SYSTEM:		/* HTTP GET to fetch system info */
506 		{
507 			curl_easy_setopt(sch->curl_handle, CURLOPT_HTTPGET, 1L);
508 			curl_easy_setopt(sch->curl_handle, CURLOPT_URL, options->system_url.str);
509 			curl_easy_setopt(sch->curl_handle, CURLOPT_HTTPHEADER, header_list);
510 		}
511 		break;
512 
513 		case SOLR_REQUEST_GET:
514 		    solr_string_appendc(&(options->get_url), '&');
515 		    solr_string_append_solr_string(&(options->get_url), &(sch->request_body.buffer));
516 		    curl_easy_setopt(sch->curl_handle, CURLOPT_HTTPGET, 1L);
517 		    curl_easy_setopt(sch->curl_handle, CURLOPT_URL, options->get_url.str);
518 		    curl_easy_setopt(sch->curl_handle, CURLOPT_HTTPHEADER, header_list);
519 		break;
520 		default :
521 		{
522 			return_status = FAILURE;
523 		}
524 		break;
525 	}
526 
527 	sch->result_code = curl_easy_perform(sch->curl_handle);
528 
529 	info_status = curl_easy_getinfo(sch->curl_handle, CURLINFO_RESPONSE_CODE, &(sch->response_header.response_code));
530 
531 	return_status = solr_is_request_successful(info_status, sch);
532 
533 	curl_slist_free_all(header_list);
534 
535 	return return_status;
536 }
537 /* }}} */
538 
539 
540 /** Deallocates memory associated with the Solr CURL Handle. **/
541 /* {{{ PHP_SOLR_API void solr_free_handle(solr_curl_t *sch) */
solr_free_handle(solr_curl_t * sch)542 PHP_SOLR_API void solr_free_handle(solr_curl_t *sch)
543 {
544 	solr_string_free(&((sch)->request_header.buffer));
545 	solr_string_free(&((sch)->request_body.buffer));
546 	solr_string_free(&((sch)->request_body_debug.buffer));
547 	solr_string_free(&((sch)->response_header.buffer));
548 	solr_string_free(&((sch)->response_body.buffer));
549 	solr_string_free(&((sch)->debug_data_buffer));
550 
551 	sch->handle_status = 0;
552 
553     curl_easy_cleanup((sch)->curl_handle);
554 
555     sch->curl_handle = NULL;
556 }
557 /* }}} */
558 
559 /* {{{ PHP_SOLR_API void solr_free_options(solr_client_options_t *options) */
solr_free_options(solr_client_options_t * options)560 PHP_SOLR_API void solr_free_options(solr_client_options_t *options)
561 {
562 	solr_string_free(&((options)->hostname));
563 	solr_string_free(&((options)->http_auth_credentials));
564 	solr_string_free(&((options)->path));
565 	solr_string_free(&((options)->proxy_auth_credentials));
566 	solr_string_free(&((options)->ssl_cert));
567 	solr_string_free(&((options)->ssl_key));
568 	solr_string_free(&((options)->ssl_keypassword));
569 	solr_string_free(&((options)->ssl_cainfo));
570 	solr_string_free(&((options)->ssl_capath));
571 	solr_string_free(&((options)->proxy_hostname));
572 
573 	solr_string_free(&((options)->qs_delimiter));
574 	solr_string_free(&((options)->response_writer));
575 
576 	solr_string_free(&((options)->update_url));
577 	solr_string_free(&((options)->search_url));
578 	solr_string_free(&((options)->thread_url));
579 	solr_string_free(&((options)->ping_url));
580 	solr_string_free(&((options)->terms_url));
581 	solr_string_free(&((options)->system_url));
582 	solr_string_free(&((options)->get_url));
583 	solr_string_free(&((options)->extract_url));
584 
585 
586 	solr_string_free(&((options)->update_servlet));
587 	solr_string_free(&((options)->search_servlet));
588 	solr_string_free(&((options)->thread_servlet));
589 	solr_string_free(&((options)->ping_servlet));
590 	solr_string_free(&((options)->terms_servlet));
591 	solr_string_free(&((options)->system_servlet));
592 	solr_string_free(&((options)->get_servlet));
593 	solr_string_free(&((options)->extract_servlet));
594 }
595 /* }}} */
596 
597 /* {{{ PHP_SOLR_API void solr_destroy_client(void *client) */
solr_destroy_client(zval * client)598 PHP_SOLR_API void solr_destroy_client(zval *client)
599 {
600     solr_client_t *solr_client = (solr_client_t *) Z_PTR_P(client);
601 
602     if (solr_client)
603     {
604         solr_free_options(&(solr_client->options));
605 
606         solr_free_handle(&(solr_client->handle));
607         pefree(solr_client, SOLR_CLIENT_PERSISTENT);
608         solr_client = NULL;
609     }
610 }
611 /* }}} */
612 
613 /* {{{ PHP_SOLR_API int solr_get_xml_error(solr_string_t buffer, solr_exception_t *exceptionData) */
solr_get_xml_error(solr_string_t buffer,solr_exception_t * exceptionData)614 PHP_SOLR_API int solr_get_xml_error(solr_string_t buffer, solr_exception_t *exceptionData)
615 {
616     xmlDoc *doc = xmlReadMemory((const char *)buffer.str, buffer.len, NULL, "UTF-8", XML_PARSE_RECOVER);
617 
618     xmlXPathContext *xpathContext = NULL;
619     xmlXPathObject *xpathObject = NULL;
620     xmlChar *xpathExpression = (unsigned char *)"/response/lst[@name='error']";
621     xmlNode * nodeCurser;
622     const unsigned char * nodePropName = (const unsigned char *)"name";
623     if (!doc)
624     {
625         php_error_docref(NULL, E_WARNING, "Error loading XML document");
626         return 1;
627     }
628 
629     /* Create xpath evaluation context */
630     xpathContext = xmlXPathNewContext(doc);
631     if(xpathContext == NULL)
632     {
633         php_error_docref(NULL, E_WARNING, "Error creating xml xpath context");
634         xmlFreeDoc(doc);
635         return 1;
636     }
637 
638     /* Evaluate xpath expression */
639     xpathObject = xmlXPathEvalExpression(xpathExpression, xpathContext);
640     if(xpathObject == NULL){
641         php_error_docref(NULL, E_WARNING, "Error evaluating xml xpath expression");
642         xmlFreeDoc(doc);
643         return 1;
644     }
645     if(!xpathObject->nodesetval){
646         php_error_docref(NULL, E_WARNING, "Xpath Error: no elements found");
647         xmlXPathFreeObject(xpathObject);
648         xmlFreeDoc(doc);
649         return 1;
650     }
651     nodeCurser = xpathObject->nodesetval->nodeTab[0]->children;
652 
653     while (nodeCurser != NULL)
654     {
655         if (xmlHasProp(nodeCurser, nodePropName))
656         {
657             if (strcmp((const char *)xmlGetProp(nodeCurser,nodePropName), (const char *)"msg") == 0)
658             {
659                 exceptionData->message = (solr_char_t *)estrdup((const char *)nodeCurser->children->content);
660             } else if(strcmp((const char *)xmlGetProp(nodeCurser,nodePropName),"code") == 0) {
661                 exceptionData->code = atoi((const char *)nodeCurser->children->content);
662             } else if(strcmp( (const char *)xmlGetProp(nodeCurser, nodePropName), (const char *) "trace") == 0) {
663                 exceptionData->message = (solr_char_t *)estrdup((const char *)nodeCurser->children->content);
664             }
665         }
666         nodeCurser = nodeCurser->next;
667     }
668 
669     xmlXPathFreeObject(xpathObject);
670     xmlXPathFreeContext(xpathContext);
671 
672     xmlFreeDoc(doc);
673     return 0;
674 }
675 /* }}} */
676 
677 /* {{{ PHP_SOLR_API int hydrate_error_zval(zval *response, solr_exception_t *exceptionData) */
hydrate_error_zval(zval * response,solr_exception_t * exceptionData)678 PHP_SOLR_API int hydrate_error_zval(zval *response, solr_exception_t *exceptionData)
679 {
680     zval *error_p;
681 
682     zval *msg_zv_p=(zval *) NULL, *code_zv_p = (zval *) NULL;
683     zend_string *msg_key_str = zend_string_init("msg", sizeof("msg")-1, 1);
684     zend_string *code_key_str = zend_string_init("code", sizeof("code")-1, 1);
685     zend_string *error_key_str = zend_string_init("error", sizeof("error")-1, 1);
686     zend_string *trace_key_str = zend_string_init("trace", sizeof("trace")-1, 1);
687     int return_code = 0;
688 
689     if ( (error_p = zend_hash_find( Z_ARRVAL_P(response), error_key_str)) != NULL)
690     {
691         if (zend_hash_exists(HASH_OF(error_p), msg_key_str))
692         {
693             if ((msg_zv_p = zend_hash_find(Z_ARRVAL_P(error_p), msg_key_str)) != NULL)
694             {
695                 exceptionData->message = (solr_char_t *)estrdup(Z_STRVAL(*msg_zv_p));
696             } else {
697                 php_error_docref(NULL, E_NOTICE, "Undefined variable: %s", "msg");
698                 return_code = 1;
699                 goto solr_error_func_return_end;
700             }
701         } else if (zend_hash_exists(HASH_OF(error_p), trace_key_str)) {
702             if ((msg_zv_p = zend_hash_find(Z_ARRVAL_P(error_p), trace_key_str)) != NULL)
703             {
704                 exceptionData->message = (solr_char_t *)estrdup(Z_STRVAL(*msg_zv_p));
705             } else {
706                 php_error_docref(NULL, E_NOTICE, "Undefined variable: %s", "trace");
707                 return_code = 1;
708                 goto solr_error_func_return_end;
709             }
710         } else{
711             php_error_docref(NULL, E_NOTICE, "Unable to find %s in error response zval", "message or trace" );
712             return_code = 1;
713             goto solr_error_func_return_end;
714         }
715 
716         if ((code_zv_p = zend_hash_find(Z_ARRVAL_P(error_p), code_key_str)) != NULL)
717         {
718             exceptionData->code = (int)Z_LVAL_P(code_zv_p);
719         } else {
720             php_error_docref(NULL, E_NOTICE, "Unable to find element with key %s in error response zval","code" );
721             return_code = 1;
722             goto solr_error_func_return_end;
723         }
724     } else {
725         php_error_docref(NULL, E_NOTICE, "Unable to find %s in error response", "error element" );
726         return_code = 1;
727         goto solr_error_func_return_end;
728     }
729 solr_error_func_return_end:
730     zend_string_release(msg_key_str);
731     zend_string_release(code_key_str);
732     zend_string_release(error_key_str);
733     zend_string_release(trace_key_str);
734 
735     return return_code;
736 }
737 /* }}} */
738 
739 /* {{{ PHP_SOLR_API int solr_get_json_error(solr_string_t buffer, solr_exception_t *exceptionData) */
solr_get_json_error(solr_string_t buffer,solr_exception_t * exceptionData)740 PHP_SOLR_API int solr_get_json_error(solr_string_t buffer, solr_exception_t *exceptionData)
741 {
742     zval json_response;
743     zval *error_p;
744     zval *msg_zv_p=(zval *)NULL,*code_zv_p=(zval *)NULL;
745 
746     HashTable *errorHashTable;
747 
748     char * key = "error";
749     long nSize = 1000;
750     int return_code = 0;
751 
752     zend_string *msg_key_str = zend_string_init("msg", sizeof("msg")-1, 1);
753     zend_string *code_key_str = zend_string_init("code", sizeof("code")-1, 1);
754     zend_string *error_key_str = zend_string_init("error", sizeof("error")-1, 1);
755     zend_string *trace_key_str = zend_string_init("trace", sizeof("trace")-1, 1);
756 
757     php_json_decode(&json_response, (char *) buffer.str, buffer.len, 1, 1024L);
758 
759     if (Z_TYPE(json_response) == IS_NULL)
760     {
761         zval_ptr_dtor(&json_response);
762         php_error_docref(NULL, E_WARNING, "Unable to parse Solr Server Error Response. JSON serialization error");
763         return 1;
764     }
765 
766     ALLOC_HASHTABLE(errorHashTable);
767     zend_hash_init(errorHashTable, nSize, NULL, NULL, 0);
768 
769     if ( (error_p = zend_hash_find( Z_ARRVAL(json_response), error_key_str)) != NULL)
770     {
771         if ((code_zv_p = zend_hash_find(Z_ARRVAL_P(error_p), code_key_str)) != NULL)
772         {
773             exceptionData->code = (int)Z_LVAL_P(code_zv_p);
774         } else {
775             php_error_docref(NULL, E_NOTICE, "Unable to find %s in json error response","code" );
776         }
777         if (zend_hash_exists(HASH_OF(error_p), msg_key_str))
778         {
779             if ((msg_zv_p = zend_hash_find(Z_ARRVAL_P(error_p), msg_key_str)) != NULL)
780             {
781                 exceptionData->message = (solr_char_t *)estrdup(Z_STRVAL_P(msg_zv_p));
782             }
783         } else if (!exceptionData->message && zend_hash_exists(HASH_OF(error_p), trace_key_str)) {
784             if ((msg_zv_p = zend_hash_find(Z_ARRVAL_P(error_p), trace_key_str)) != NULL)
785             {
786                 exceptionData->message = (solr_char_t *)estrdup(Z_STRVAL_P(msg_zv_p));
787             } else {
788                 php_error_docref(NULL, E_NOTICE, "Undefined variable: %s","trace" );
789             }
790         } else {
791             php_error_docref(NULL, E_NOTICE, "Unable to find %s in error response zval","message" );
792             return_code = 1;
793             goto solr_error_func_return_end;
794         }
795 
796     }else{
797         php_error_docref(NULL, E_NOTICE, "Undefined variable: %s",key );
798     }
799 solr_error_func_return_end:
800     zend_string_release(msg_key_str);
801     zend_string_release(code_key_str);
802     zend_string_release(error_key_str);
803     zend_string_release(trace_key_str);
804     zval_ptr_dtor(&json_response);
805     zend_hash_destroy(errorHashTable);
806     FREE_HASHTABLE(errorHashTable);
807     return return_code;
808 }
809 /* }}} */
810 
811 /* {{{ PHP_SOLR_API int solr_get_phpnative_error(solr_string_t buffer, solr_exception_t *exceptionData) */
solr_get_phpnative_error(solr_string_t buffer,solr_exception_t * exceptionData)812 PHP_SOLR_API int solr_get_phpnative_error(solr_string_t buffer, solr_exception_t *exceptionData)
813 {
814 
815     zval *response_obj;
816     php_unserialize_data_t var_hash;
817     const unsigned char * raw_resp = (const unsigned char *) buffer.str;
818     const unsigned char * str_end = (const unsigned char *) (buffer.str + buffer.len);
819 
820     ALLOC_INIT_ZVAL(response_obj);
821     PHP_VAR_UNSERIALIZE_INIT(var_hash);
822 
823     if(!php_var_unserialize(response_obj, &raw_resp, str_end, &var_hash)) {
824         /* There is a known issue, that solr responses will not always be
825          * with the dictated response format, as jetty or tomcat may return errors in their format
826          */
827         PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
828         zval_ptr_dtor(response_obj);
829 #ifdef PHP_7
830     efree(response_obj);
831 #endif
832         return 1;
833     }
834     hydrate_error_zval(response_obj, exceptionData);
835     PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
836     zval_ptr_dtor(response_obj);
837 #ifdef PHP_7
838     efree(response_obj);
839 #endif
840     return 0;
841 }
842 /* }}} */
843 
844 /* {{{ PHP_SOLR_API void solr_throw_solr_server_exception(solr_client_t *client,const char * requestType)
845      parse the solr server response and throw a SolrServerException */
solr_throw_solr_server_exception(solr_client_t * client,const char * requestType)846 PHP_SOLR_API void solr_throw_solr_server_exception(solr_client_t *client,const char * requestType)
847 {
848     const char * response_writer = (char *) client->options.response_writer.str;
849     solr_exception_t *exceptionData;
850     exceptionData = (solr_exception_t*) emalloc(sizeof(solr_exception_t ));
851     exceptionData->code = 0;
852     memset(exceptionData, 0, sizeof(solr_exception_t));
853     if( 0 == strcmp(response_writer, SOLR_XML_RESPONSE_WRITER)){
854 
855         if(solr_get_xml_error(client->handle.response_body.buffer, exceptionData) != SUCCESS)
856         {
857             /* fallback to normal exception */
858             solr_throw_exception_ex(solr_ce_SolrClientException, SOLR_ERROR_1010, SOLR_FILE_LINE_FUNC, SOLR_ERROR_1010_MSG, requestType, SOLR_RESPONSE_CODE_BODY);
859             return;
860         }
861     }
862 
863     if( 0 == strcmp(response_writer, SOLR_JSON_RESPONSE_WRITER))
864     {
865         if(solr_get_json_error(client->handle.response_body.buffer, exceptionData) != SUCCESS)
866         {
867             solr_throw_exception_ex(solr_ce_SolrClientException, SOLR_ERROR_1010, SOLR_FILE_LINE_FUNC, SOLR_ERROR_1010_MSG, requestType, SOLR_RESPONSE_CODE_BODY);
868         }
869     }
870 
871     if( 0 == strcmp(response_writer, SOLR_PHP_NATIVE_RESPONSE_WRITER) || 0 == strcmp(response_writer, SOLR_PHP_SERIALIZED_RESPONSE_WRITER))
872     {
873         if(solr_get_phpnative_error(client->handle.response_body.buffer,exceptionData) != SUCCESS)
874         {
875             php_error_docref(NULL, E_NOTICE, "Unable to parse serialized php response" );
876         }
877     }
878 
879     if(exceptionData->code == 0){
880         solr_throw_exception_ex(solr_ce_SolrClientException, SOLR_ERROR_1010, SOLR_FILE_LINE_FUNC, SOLR_ERROR_1010_MSG, requestType, SOLR_RESPONSE_CODE_BODY);
881     }else if (exceptionData->code > 0 && exceptionData->message){
882         solr_throw_exception_ex(solr_ce_SolrServerException, exceptionData->code, SOLR_FILE_LINE_FUNC, exceptionData->message);
883     } else {
884         php_error_docref(NULL, E_ERROR, "Unable to parse solr exception message, Internal Error" );
885     }
886 
887     if(exceptionData->message != NULL)
888     {
889         efree(exceptionData->message);
890     }
891 
892     efree(exceptionData);
893 }
894 /* }}} */
895 
896 /*
897  * Local variables:
898  * tab-width: 4
899  * c-basic-offset: 4
900  * End:
901  * vim600: fdm=marker
902  * vim: noet sw=4 ts=4
903  */
904