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