/[hyperestraier]/upstream/0.5.3/estnode.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /upstream/0.5.3/estnode.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 10 - (show annotations)
Wed Aug 3 15:25:48 2005 UTC (18 years, 10 months ago) by dpavlin
File MIME type: text/plain
File size: 47312 byte(s)
import of upstream 0.5.3

1 /*************************************************************************************************
2 * Implementation of the node API
3 * Copyright (C) 2004-2005 Mikio Hirabayashi
4 * This file is part of Hyper Estraier.
5 * Hyper Estraier is free software; you can redistribute it and/or modify it under the terms of
6 * the GNU Lesser General Public License as published by the Free Software Foundation; either
7 * version 2.1 of the License or any later version. Hyper Estraier is distributed in the hope
8 * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
10 * License for more details.
11 * You should have received a copy of the GNU Lesser General Public License along with Hyper
12 * Estraier; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
13 * Boston, MA 02111-1307 USA.
14 *************************************************************************************************/
15
16
17 #include "estraier.h"
18 #include "estmtdb.h"
19 #include "estnode.h"
20 #include "myconf.h"
21
22 #if defined(_SYS_MSVC_) || defined(_SYS_MINGW_)
23 #define socklen_t int
24 #define in_addr_t int
25 #elif defined(_SYS_MACOSX_)
26 #define socklen_t int
27 #endif
28
29 #define ESTNUMBUFSIZ 32 /* size of a buffer for a number */
30 #define ESTPATHBUFSIZ 4096 /* size of a buffer for a path */
31 #define ESTIOBUFSIZ 8192 /* size of a buffer for I/O */
32 #define ESTMINIBNUM 31 /* bucket number of map for attributes */
33 #define ESTLISTUNIT 64 /* allocation unit number of a list */
34 #define ESTDNHOLDSEC 300 /* holding time of domain names */
35 #define ESTDNHOLDNUM 4096 /* holding number of domain names */
36
37 typedef struct { /* type of structure for interaction of a URL */
38 int alive; /* whether to be alive */
39 pthread_cond_t *cond; /* condition variable */
40 const char *url; /* URL */
41 const char *pxhost; /* host name of proxy */
42 int pxport; /* port number of proxy */
43 const char *auth; /* authority */
44 const CBLIST *reqheads; /* request headers */
45 const char *reqbody; /* request body */
46 int rbsiz; /* size of the request body */
47 int *rescodep; /* pointer to a variable for status code */
48 CBMAP *resheads; /* response headers */
49 CBDATUM *resbody; /* response body */
50 } TARGSHUTTLE;
51
52
53 /* private function prototypes */
54 static int est_sock_close(int sock);
55 static int est_inet_aton(const char *cp, struct in_addr *inp);
56 static void *est_url_shuttle_impl(void *targ);
57 static void est_sockpt_down(void *sp);
58 static int est_node_set_info(ESTNODE *node);
59 static void est_parse_search_header(ESTNODERES *nres, const char *str);
60 static void est_parse_search_body(ESTNODERES *nres, char *str);
61
62
63
64 /*************************************************************************************************
65 * API for the network environment
66 *************************************************************************************************/
67
68
69 /* Cache of host addresses */
70 CBMAP *est_host_addrs = NULL;
71 int est_host_attrs_cnt = 0;
72 pthread_mutex_t est_host_addrs_mutex = PTHREAD_MUTEX_INITIALIZER;
73
74
75 /* Initialize the networking environment. */
76 int est_init_net_env(void){
77 #if defined(_SYS_MSVC_) || defined(_SYS_MINGW_)
78 WSADATA wsaData;
79 if(pthread_mutex_lock(&est_host_addrs_mutex) != 0) return FALSE;
80 if(WSAStartup(MAKEWORD(2,0), &wsaData) != 0){
81 pthread_mutex_unlock(&est_host_addrs_mutex);
82 return FALSE;
83 }
84 if(est_host_attrs_cnt < 1) est_host_addrs = cbmapopenex(ESTDNHOLDNUM + 1);
85 est_host_attrs_cnt++;
86 pthread_mutex_unlock(&est_host_addrs_mutex);
87 return TRUE;
88 #else
89 if(pthread_mutex_lock(&est_host_addrs_mutex) != 0) return FALSE;
90 if(est_host_attrs_cnt < 1) est_host_addrs = cbmapopenex(ESTDNHOLDNUM + 1);
91 est_host_attrs_cnt++;
92 pthread_mutex_unlock(&est_host_addrs_mutex);
93 return TRUE;
94 #endif
95 }
96
97
98 /* Free the networking environment. */
99 void est_free_net_env(void){
100 #if defined(_SYS_MSVC_) || defined(_SYS_MINGW_)
101 if(pthread_mutex_lock(&est_host_addrs_mutex) != 0) return;
102 if(est_host_attrs_cnt < 1){
103 pthread_mutex_unlock(&est_host_addrs_mutex);
104 return;
105 }
106 est_host_attrs_cnt--;
107 if(est_host_attrs_cnt < 1){
108 cbmapclose(est_host_addrs);
109 est_host_addrs = NULL;
110 }
111 WSACleanup();
112 pthread_mutex_unlock(&est_host_addrs_mutex);
113 #else
114 if(pthread_mutex_lock(&est_host_addrs_mutex) != 0) return;
115 if(est_host_attrs_cnt < 1){
116 pthread_mutex_unlock(&est_host_addrs_mutex);
117 return;
118 }
119 est_host_attrs_cnt--;
120 if(est_host_attrs_cnt < 1){
121 cbmapclose(est_host_addrs);
122 est_host_addrs = NULL;
123 }
124 pthread_mutex_unlock(&est_host_addrs_mutex);
125 #endif
126 }
127
128
129
130 /*************************************************************************************************
131 * API for search result of node
132 *************************************************************************************************/
133
134
135 /* Delete a node result object. */
136 void est_noderes_delete(ESTNODERES *nres){
137 int i;
138 assert(nres);
139 cbmapclose(nres->hints);
140 for(i = nres->top; i < nres->top + nres->dnum; i++){
141 free(nres->docs[i].snippet);
142 cbmapclose(nres->docs[i].attrs);
143 }
144 free(nres->docs);
145 free(nres);
146 }
147
148
149 /* Get a map object for hints of a node result object. */
150 CBMAP *est_noderes_hints(ESTNODERES *nres){
151 assert(nres);
152 return nres->hints;
153 }
154
155
156 /* Get the number of documents in a node result object. */
157 int est_noderes_doc_num(ESTNODERES *nres){
158 assert(nres);
159 return nres->dnum;
160 }
161
162
163 /* Refer a result document object in a node result object. */
164 ESTRESDOC *est_noderes_get_doc(ESTNODERES *nres, int index){
165 assert(nres && index >= 0);
166 if(index >= nres->dnum) return NULL;
167 return nres->docs + (nres->top + index);
168 }
169
170
171 /* Get the URI of a result document object. */
172 const char *est_resdoc_uri(ESTRESDOC *rdoc){
173 assert(rdoc);
174 return rdoc->uri;
175 }
176
177
178 /* Get a list of attribute names of a result document object. */
179 CBLIST *est_resdoc_attr_names(ESTRESDOC *rdoc){
180 CBLIST *names;
181 assert(rdoc);
182 names = cbmapkeys(rdoc->attrs);
183 cblistsort(names);
184 return names;
185 }
186
187
188 /* Get the value of an attribute of a result document object. */
189 const char *est_resdoc_attr(ESTRESDOC *rdoc, const char *name){
190 assert(rdoc && name);
191 return cbmapget(rdoc->attrs, name, -1, NULL);
192 }
193
194
195 /* Get the value of an attribute of a result document object. */
196 const char *est_resdoc_snippet(ESTRESDOC *rdoc){
197 assert(rdoc);
198 return rdoc->snippet;
199 }
200
201
202
203 /*************************************************************************************************
204 * API for node
205 *************************************************************************************************/
206
207
208 /* Create a node connection object. */
209 ESTNODE *est_node_new(const char *url){
210 ESTNODE *node;
211 node = cbmalloc(sizeof(ESTNODE));
212 node->url = cbmemdup(url, -1);
213 node->pxhost = NULL;
214 node->pxport = 0;
215 node->timeout = -1;
216 node->auth = NULL;
217 node->name = NULL;
218 node->label = NULL;
219 node->dnum = -1;
220 node->wnum = -1;
221 node->size = -1.0;
222 node->status = 0;
223 node->heads = cbmapopenex(ESTMINIBNUM);
224 return node;
225 }
226
227
228 /* Destroy a node connection object. */
229 void est_node_delete(ESTNODE *node){
230 assert(node);
231 cbmapclose(node->heads);
232 free(node->label);
233 free(node->name);
234 free(node->auth);
235 free(node->pxhost);
236 free(node->url);
237 free(node);
238 }
239
240
241 /* Set the proxy information of a node connection object. */
242 void est_node_set_proxy(ESTNODE *node, const char *host, int port){
243 assert(node && host && port >= 0);
244 free(node->pxhost);
245 node->pxhost = cbmemdup(host, -1);
246 node->pxport = port;
247 }
248
249
250 /* Set timeout of a connection. */
251 void est_node_set_timeout(ESTNODE *node, int sec){
252 assert(node && sec >= 0);
253 node->timeout = sec;
254 }
255
256
257 /* Set the authoririty information of a node connection object. */
258 void est_node_set_auth(ESTNODE *node, const char *name, const char *passwd){
259 assert(node && name && passwd);
260 free(node->auth);
261 node->auth = cbsprintf("%s:%s", name, passwd);
262 }
263
264
265 /* Get the status code of the last request of a node. */
266 int est_node_status(ESTNODE *node){
267 assert(node);
268 return node->status;
269 }
270
271
272 /* Add a document to a node. */
273 int est_node_put_doc(ESTNODE *node, ESTDOC *doc){
274 CBLIST *reqheads;
275 const char *kbuf;
276 char url[ESTPATHBUFSIZ], *vbuf, *reqbody;
277 int rescode, err, ksiz;
278 assert(node && doc);
279 err = FALSE;
280 sprintf(url, "%s/put_doc", node->url);
281 reqheads = cblistopen();
282 if(cbmaprnum(node->heads) > 0){
283 cbmapiterinit(node->heads);
284 while((kbuf = cbmapiternext(node->heads, &ksiz)) != NULL){
285 vbuf = cbsprintf("%s: %s", kbuf, cbmapget(node->heads, kbuf, ksiz, NULL));
286 cblistpush(reqheads, vbuf, -1);
287 free(vbuf);
288 }
289 }
290 cblistpush(reqheads, "Content-Type: " ESTDRAFTTYPE, -1);
291 reqbody = est_doc_dump_draft(doc);
292 if(!est_url_shuttle(url, node->pxhost, node->pxport, node->timeout, node->auth, reqheads,
293 reqbody, strlen(reqbody), &rescode, NULL, NULL)){
294 node->status = -1;
295 err = TRUE;
296 }
297 if(!err){
298 node->status = rescode;
299 if(rescode != 200) err = TRUE;
300 }
301 free(reqbody);
302 cblistclose(reqheads);
303 return err ? FALSE : TRUE;
304 }
305
306
307 /* Remove a document from a node. */
308 int est_node_out_doc(ESTNODE *node, int id){
309 CBLIST *reqheads;
310 CBDATUM *reqbody;
311 const char *kbuf;
312 char url[ESTPATHBUFSIZ], *vbuf;
313 int rescode, err, ksiz;
314 assert(node && id > 0);
315 err = FALSE;
316 sprintf(url, "%s/out_doc", node->url);
317 reqheads = cblistopen();
318 if(cbmaprnum(node->heads) > 0){
319 cbmapiterinit(node->heads);
320 while((kbuf = cbmapiternext(node->heads, &ksiz)) != NULL){
321 vbuf = cbsprintf("%s: %s", kbuf, cbmapget(node->heads, kbuf, ksiz, NULL));
322 cblistpush(reqheads, vbuf, -1);
323 free(vbuf);
324 }
325 }
326 cblistpush(reqheads, "Content-Type: " ESTFORMTYPE, -1);
327 reqbody = cbdatumopen("", 0);
328 est_datum_printf(reqbody, "id=%d", id);
329 if(!est_url_shuttle(url, node->pxhost, node->pxport, node->timeout, node->auth, reqheads,
330 cbdatumptr(reqbody), cbdatumsize(reqbody), &rescode, NULL, NULL)){
331 node->status = -1;
332 err = TRUE;
333 }
334 if(!err){
335 node->status = rescode;
336 if(rescode != 200) err = TRUE;
337 }
338 cbdatumclose(reqbody);
339 cblistclose(reqheads);
340 return err ? FALSE : TRUE;
341 }
342
343
344 /* Remove a document specified by URI from a node. */
345 int est_node_out_doc_by_uri(ESTNODE *node, const char *uri){
346 CBLIST *reqheads;
347 CBDATUM *reqbody;
348 const char *kbuf;
349 char url[ESTPATHBUFSIZ], *vbuf;
350 int rescode, err, ksiz;
351 assert(node && uri);
352 err = FALSE;
353 sprintf(url, "%s/out_doc", node->url);
354 reqheads = cblistopen();
355 if(cbmaprnum(node->heads) > 0){
356 cbmapiterinit(node->heads);
357 while((kbuf = cbmapiternext(node->heads, &ksiz)) != NULL){
358 vbuf = cbsprintf("%s: %s", kbuf, cbmapget(node->heads, kbuf, ksiz, NULL));
359 cblistpush(reqheads, vbuf, -1);
360 free(vbuf);
361 }
362 }
363 cblistpush(reqheads, "Content-Type: " ESTFORMTYPE, -1);
364 reqbody = cbdatumopen("", 0);
365 est_datum_printf(reqbody, "uri=%?", uri);
366 if(!est_url_shuttle(url, node->pxhost, node->pxport, node->timeout, node->auth, reqheads,
367 cbdatumptr(reqbody), cbdatumsize(reqbody), &rescode, NULL, NULL)){
368 node->status = -1;
369 err = TRUE;
370 }
371 if(!err){
372 node->status = rescode;
373 if(rescode != 200) err = TRUE;
374 }
375 cbdatumclose(reqbody);
376 cblistclose(reqheads);
377 return err ? FALSE : TRUE;
378 }
379
380
381 /* Retrieve a document in a node. */
382 ESTDOC *est_node_get_doc(ESTNODE *node, int id){
383 ESTDOC *doc;
384 CBLIST *reqheads;
385 CBDATUM *reqbody, *resbody;
386 const char *kbuf;
387 char url[ESTPATHBUFSIZ], *vbuf;
388 int rescode, err, ksiz;
389 assert(node && id > 0);
390 err = FALSE;
391 sprintf(url, "%s/get_doc", node->url);
392 reqheads = cblistopen();
393 if(cbmaprnum(node->heads) > 0){
394 cbmapiterinit(node->heads);
395 while((kbuf = cbmapiternext(node->heads, &ksiz)) != NULL){
396 vbuf = cbsprintf("%s: %s", kbuf, cbmapget(node->heads, kbuf, ksiz, NULL));
397 cblistpush(reqheads, vbuf, -1);
398 free(vbuf);
399 }
400 }
401 cblistpush(reqheads, "Content-Type: " ESTFORMTYPE, -1);
402 reqbody = cbdatumopen("", 0);
403 est_datum_printf(reqbody, "id=%d", id);
404 resbody = cbdatumopen("", 0);
405 if(!est_url_shuttle(url, node->pxhost, node->pxport, node->timeout, node->auth, reqheads,
406 cbdatumptr(reqbody), cbdatumsize(reqbody), &rescode, NULL, resbody)){
407 node->status = -1;
408 err = TRUE;
409 }
410 if(!err){
411 node->status = rescode;
412 if(rescode != 200) err = TRUE;
413 }
414 doc = err ? NULL : est_doc_new_from_draft(cbdatumptr(resbody));
415 cbdatumclose(resbody);
416 cbdatumclose(reqbody);
417 cblistclose(reqheads);
418 return doc;
419 }
420
421
422 /* Retrieve a document specified by URI in a node. */
423 ESTDOC *est_node_get_doc_by_uri(ESTNODE *node, const char *uri){
424 ESTDOC *doc;
425 CBLIST *reqheads;
426 CBDATUM *reqbody, *resbody;
427 const char *kbuf;
428 char url[ESTPATHBUFSIZ], *vbuf;
429 int rescode, err, ksiz;
430 assert(node && uri);
431 err = FALSE;
432 sprintf(url, "%s/get_doc", node->url);
433 reqheads = cblistopen();
434 if(cbmaprnum(node->heads) > 0){
435 cbmapiterinit(node->heads);
436 while((kbuf = cbmapiternext(node->heads, &ksiz)) != NULL){
437 vbuf = cbsprintf("%s: %s", kbuf, cbmapget(node->heads, kbuf, ksiz, NULL));
438 cblistpush(reqheads, vbuf, -1);
439 free(vbuf);
440 }
441 }
442 cblistpush(reqheads, "Content-Type: " ESTFORMTYPE, -1);
443 reqbody = cbdatumopen("", 0);
444 est_datum_printf(reqbody, "uri=%?", uri);
445 resbody = cbdatumopen("", 0);
446 if(!est_url_shuttle(url, node->pxhost, node->pxport, node->timeout, node->auth, reqheads,
447 cbdatumptr(reqbody), cbdatumsize(reqbody), &rescode, NULL, resbody)){
448 node->status = -1;
449 err = TRUE;
450 }
451 if(!err){
452 node->status = rescode;
453 if(rescode != 200) err = TRUE;
454 }
455 doc = err ? NULL : est_doc_new_from_draft(cbdatumptr(resbody));
456 cbdatumclose(resbody);
457 cbdatumclose(reqbody);
458 cblistclose(reqheads);
459 return doc;
460 }
461
462
463 /* Retrieve the value of an attribute of a document in a node. */
464 char *est_node_get_doc_attr(ESTNODE *node, int id, const char *name){
465 CBLIST *reqheads;
466 CBDATUM *reqbody, *resbody;
467 const char *kbuf;
468 char url[ESTPATHBUFSIZ], *vbuf;
469 int rescode, err, ksiz;
470 assert(node && id > 0);
471 err = FALSE;
472 sprintf(url, "%s/get_doc_attr", node->url);
473 reqheads = cblistopen();
474 if(cbmaprnum(node->heads) > 0){
475 cbmapiterinit(node->heads);
476 while((kbuf = cbmapiternext(node->heads, &ksiz)) != NULL){
477 vbuf = cbsprintf("%s: %s", kbuf, cbmapget(node->heads, kbuf, ksiz, NULL));
478 cblistpush(reqheads, vbuf, -1);
479 free(vbuf);
480 }
481 }
482 cblistpush(reqheads, "Content-Type: " ESTFORMTYPE, -1);
483 reqbody = cbdatumopen("", 0);
484 est_datum_printf(reqbody, "id=%d&attr=%?", id, name);
485 resbody = cbdatumopen("", 0);
486 if(!est_url_shuttle(url, node->pxhost, node->pxport, node->timeout, node->auth, reqheads,
487 cbdatumptr(reqbody), cbdatumsize(reqbody), &rescode, NULL, resbody)){
488 node->status = -1;
489 err = TRUE;
490 }
491 if(!err){
492 node->status = rescode;
493 if(rescode != 200) err = TRUE;
494 }
495 if(err){
496 cbdatumclose(resbody);
497 vbuf = NULL;
498 } else {
499 vbuf = cbdatumtomalloc(resbody, NULL);
500 cbstrtrim(vbuf);
501 }
502 cbdatumclose(reqbody);
503 cblistclose(reqheads);
504 return vbuf;
505 }
506
507
508 /* Retrieve the value of an attribute of a document specified by URI in a node. */
509 char *est_node_get_doc_attr_by_uri(ESTNODE *node, const char *uri, const char *name){
510 CBLIST *reqheads;
511 CBDATUM *reqbody, *resbody;
512 const char *kbuf;
513 char url[ESTPATHBUFSIZ], *vbuf;
514 int rescode, err, ksiz;
515 assert(node && uri);
516 err = FALSE;
517 sprintf(url, "%s/get_doc_attr", node->url);
518 reqheads = cblistopen();
519 if(cbmaprnum(node->heads) > 0){
520 cbmapiterinit(node->heads);
521 while((kbuf = cbmapiternext(node->heads, &ksiz)) != NULL){
522 vbuf = cbsprintf("%s: %s", kbuf, cbmapget(node->heads, kbuf, ksiz, NULL));
523 cblistpush(reqheads, vbuf, -1);
524 free(vbuf);
525 }
526 }
527 cblistpush(reqheads, "Content-Type: " ESTFORMTYPE, -1);
528 reqbody = cbdatumopen("", 0);
529 est_datum_printf(reqbody, "uri=%?&attr=%?", uri, name);
530 resbody = cbdatumopen("", 0);
531 if(!est_url_shuttle(url, node->pxhost, node->pxport, node->timeout, node->auth, reqheads,
532 cbdatumptr(reqbody), cbdatumsize(reqbody), &rescode, NULL, resbody)){
533 node->status = -1;
534 err = TRUE;
535 }
536 if(!err){
537 node->status = rescode;
538 if(rescode != 200) err = TRUE;
539 }
540 if(err){
541 cbdatumclose(resbody);
542 vbuf = NULL;
543 } else {
544 vbuf = cbdatumtomalloc(resbody, NULL);
545 cbstrtrim(vbuf);
546 }
547 cbdatumclose(reqbody);
548 cblistclose(reqheads);
549 return vbuf;
550 }
551
552
553 /* Get the ID of a document spacified by URI. */
554 int est_node_uri_to_id(ESTNODE *node, const char *uri){
555 CBLIST *reqheads;
556 CBDATUM *reqbody, *resbody;
557 const char *kbuf;
558 char url[ESTPATHBUFSIZ], *vbuf;
559 int rescode, err, ksiz, id;
560 assert(node && uri);
561 err = FALSE;
562 sprintf(url, "%s/uri_to_id", node->url);
563 reqheads = cblistopen();
564 if(cbmaprnum(node->heads) > 0){
565 cbmapiterinit(node->heads);
566 while((kbuf = cbmapiternext(node->heads, &ksiz)) != NULL){
567 vbuf = cbsprintf("%s: %s", kbuf, cbmapget(node->heads, kbuf, ksiz, NULL));
568 cblistpush(reqheads, vbuf, -1);
569 free(vbuf);
570 }
571 }
572 cblistpush(reqheads, "Content-Type: " ESTFORMTYPE, -1);
573 reqbody = cbdatumopen("", 0);
574 est_datum_printf(reqbody, "uri=%?", uri);
575 resbody = cbdatumopen("", 0);
576 if(!est_url_shuttle(url, node->pxhost, node->pxport, node->timeout, node->auth, reqheads,
577 cbdatumptr(reqbody), cbdatumsize(reqbody), &rescode, NULL, resbody)){
578 node->status = -1;
579 err = TRUE;
580 }
581 if(!err){
582 node->status = rescode;
583 if(rescode != 200) err = TRUE;
584 }
585 id = err ? -1 : atoi(cbdatumptr(resbody));
586 cbdatumclose(resbody);
587 cbdatumclose(reqbody);
588 cblistclose(reqheads);
589 return id;
590 }
591
592
593 /* Get the name of a node. */
594 const char *est_node_name(ESTNODE *node){
595 assert(node);
596 if(node->name) return node->name;
597 est_node_set_info(node);
598 return node->name;
599 }
600
601
602 /* Get the label of a node. */
603 const char *est_node_label(ESTNODE *node){
604 assert(node);
605 if(node->label) return node->label;
606 est_node_set_info(node);
607 return node->label;
608 }
609
610
611 /* Get the number of documents in a node. */
612 int est_node_doc_num(ESTNODE *node){
613 assert(node);
614 if(node->dnum >= 0) return node->dnum;
615 est_node_set_info(node);
616 return node->dnum;
617 }
618
619
620 /* Get the number of words in a node. */
621 int est_node_word_num(ESTNODE *node){
622 assert(node);
623 if(node->wnum >= 0) return node->wnum;
624 est_node_set_info(node);
625 return node->wnum;
626 }
627
628
629 /* Get the size of the datbase of a node. */
630 double est_node_size(ESTNODE *node){
631 assert(node);
632 if(node->size >= 0.0) return node->size;
633 est_node_set_info(node);
634 return node->size;
635 }
636
637
638 /* Search documents corresponding a condition for a node. */
639 ESTNODERES *est_node_search(ESTNODE *node, ESTCOND *cond, int depth){
640 ESTNODERES *nres;
641 const CBLIST *attrs;
642 CBLIST *reqheads;
643 CBDATUM *reqbody, *resbody;
644 const char *kbuf, *phrase, *order;
645 char buf[ESTPATHBUFSIZ], *vbuf, *ptr, *pv, *ep;
646 int i, rescode, err, ksiz, max, plen, part, end;
647 assert(node && cond && depth >= 0);
648 err = FALSE;
649 sprintf(buf, "%s/search", node->url);
650 reqheads = cblistopen();
651 if(cbmaprnum(node->heads) > 0){
652 cbmapiterinit(node->heads);
653 while((kbuf = cbmapiternext(node->heads, &ksiz)) != NULL){
654 vbuf = cbsprintf("%s: %s", kbuf, cbmapget(node->heads, kbuf, ksiz, NULL));
655 cblistpush(reqheads, vbuf, -1);
656 free(vbuf);
657 }
658 }
659 cblistpush(reqheads, "Content-Type: " ESTFORMTYPE, -1);
660 reqbody = cbdatumopen("", 0);
661 if((phrase = est_cond_phrase(cond)) != NULL) est_datum_printf(reqbody, "phrase=%?", phrase);
662 if((attrs = est_cond_attrs(cond)) != NULL){
663 for(i = 0; i < cblistnum(attrs); i++){
664 if(cbdatumsize(reqbody) > 0) cbdatumcat(reqbody, "&", 1);
665 est_datum_printf(reqbody, "attr%d=%?", i + 1, cblistval(attrs, i, NULL));
666 }
667 }
668 if((max = est_cond_max(cond)) >= 0){
669 if(cbdatumsize(reqbody) > 0) cbdatumcat(reqbody, "&", 1);
670 est_datum_printf(reqbody, "max=%d", max);
671 } else {
672 if(cbdatumsize(reqbody) > 0) cbdatumcat(reqbody, "&", 1);
673 est_datum_printf(reqbody, "max=%d", INT_MAX / 2);
674 }
675 if((order = est_cond_order(cond)) != NULL){
676 if(cbdatumsize(reqbody) > 0) cbdatumcat(reqbody, "&", 1);
677 est_datum_printf(reqbody, "order=%?", order);
678 }
679 if(cbdatumsize(reqbody) > 0) cbdatumcat(reqbody, "&", 1);
680 est_datum_printf(reqbody, "options=%d", est_cond_options(cond));
681 if(cbdatumsize(reqbody) > 0) cbdatumcat(reqbody, "&", 1);
682 est_datum_printf(reqbody, "depth=%d", depth);
683 resbody = cbdatumopen("", 0);
684 if(!est_url_shuttle(buf, node->pxhost, node->pxport, node->timeout, node->auth, reqheads,
685 cbdatumptr(reqbody), cbdatumsize(reqbody), &rescode, NULL, resbody)){
686 node->status = -1;
687 err = TRUE;
688 }
689 if(!err){
690 node->status = rescode;
691 if(rescode != 200) err = TRUE;
692 }
693 if(err){
694 cbdatumclose(resbody);
695 cbdatumclose(reqbody);
696 cblistclose(reqheads);
697 return NULL;
698 }
699 ptr = cbdatumtomalloc(resbody, NULL);
700 if(!(pv = strchr(ptr, '\n')) || pv <= ptr + 3){
701 free(ptr);
702 cbdatumclose(reqbody);
703 cblistclose(reqheads);
704 return NULL;
705 }
706 *pv = '\0';
707 if(pv[-1] == '\r') pv[-1] = '\0';
708 plen = strlen(ptr);
709 pv++;
710 ep = pv;
711 nres = est_noderes_new();
712 part = 0;
713 end = FALSE;
714 while(*ep != '\0'){
715 if(*ep == *ptr && cbstrfwmatch(ep, ptr) && ep[-1] == '\n' &&
716 (ep[plen] == '\r' || ep[plen] == '\n' || ep[plen] == ':')){
717 *ep = '\0';
718 if(part == 0){
719 est_parse_search_header(nres, pv);
720 } else {
721 est_parse_search_body(nres, pv);
722 }
723 ep += plen;
724 if(cbstrfwmatch(ep, ":END")){
725 end = TRUE;
726 break;
727 }
728 if(*ep == '\r') ep++;
729 if(*ep == '\n') ep++;
730 pv = ep;
731 part++;
732 } else {
733 ep++;
734 }
735 }
736 free(ptr);
737 cbdatumclose(reqbody);
738 cblistclose(reqheads);
739 if(!end){
740 est_noderes_delete(nres);
741 return NULL;
742 }
743 return nres;
744 }
745
746
747 /* Manage a user account of a node. */
748 int est_node_set_user(ESTNODE *node, const char *name, int mode){
749 CBLIST *reqheads;
750 CBDATUM *reqbody, *resbody;
751 const char *kbuf;
752 char url[ESTPATHBUFSIZ], *vbuf;
753 int rescode, err, ksiz;
754 assert(node && name);
755 err = FALSE;
756 sprintf(url, "%s/_set_user", node->url);
757 reqheads = cblistopen();
758 if(cbmaprnum(node->heads) > 0){
759 cbmapiterinit(node->heads);
760 while((kbuf = cbmapiternext(node->heads, &ksiz)) != NULL){
761 vbuf = cbsprintf("%s: %s", kbuf, cbmapget(node->heads, kbuf, ksiz, NULL));
762 cblistpush(reqheads, vbuf, -1);
763 free(vbuf);
764 }
765 }
766 cblistpush(reqheads, "Content-Type: " ESTFORMTYPE, -1);
767 reqbody = cbdatumopen("", 0);
768 est_datum_printf(reqbody, "name=%?&mode=%d", name, mode);
769 resbody = cbdatumopen("", 0);
770 if(!est_url_shuttle(url, node->pxhost, node->pxport, node->timeout, node->auth, reqheads,
771 cbdatumptr(reqbody), cbdatumsize(reqbody), &rescode, NULL, resbody)){
772 node->status = -1;
773 err = TRUE;
774 }
775 if(!err){
776 node->status = rescode;
777 if(rescode != 200) err = TRUE;
778 }
779 cbdatumclose(resbody);
780 cbdatumclose(reqbody);
781 cblistclose(reqheads);
782 return err ? FALSE : TRUE;
783 }
784
785
786 /* Manage a link of a node. */
787 int est_node_set_link(ESTNODE *node, const char *url, const char *label, int credit){
788 CBLIST *reqheads;
789 CBDATUM *reqbody, *resbody;
790 const char *kbuf;
791 char myurl[ESTPATHBUFSIZ], *vbuf;
792 int rescode, err, ksiz;
793 assert(node && url && label);
794 err = FALSE;
795 sprintf(myurl, "%s/_set_link", node->url);
796 reqheads = cblistopen();
797 if(cbmaprnum(node->heads) > 0){
798 cbmapiterinit(node->heads);
799 while((kbuf = cbmapiternext(node->heads, &ksiz)) != NULL){
800 vbuf = cbsprintf("%s: %s", kbuf, cbmapget(node->heads, kbuf, ksiz, NULL));
801 cblistpush(reqheads, vbuf, -1);
802 free(vbuf);
803 }
804 }
805 cblistpush(reqheads, "Content-Type: " ESTFORMTYPE, -1);
806 reqbody = cbdatumopen("", 0);
807 if(credit >= 0){
808 est_datum_printf(reqbody, "url=%?&label=%?&credit=%d", url, label, credit);
809 } else {
810 est_datum_printf(reqbody, "url=%?&label=%?", url, label);
811 }
812 resbody = cbdatumopen("", 0);
813 if(!est_url_shuttle(myurl, node->pxhost, node->pxport, node->timeout, node->auth, reqheads,
814 cbdatumptr(reqbody), cbdatumsize(reqbody), &rescode, NULL, resbody)){
815 node->status = -1;
816 err = TRUE;
817 }
818 if(!err){
819 node->status = rescode;
820 if(rescode != 200) err = TRUE;
821 }
822 cbdatumclose(resbody);
823 cbdatumclose(reqbody);
824 cblistclose(reqheads);
825 return err ? FALSE : TRUE;
826 }
827
828
829
830 /*************************************************************************************************
831 * features for experts
832 *************************************************************************************************/
833
834
835 /* Get the name of this host. */
836 const char *est_get_host_name(void){
837 static char host[ESTPATHBUFSIZ];
838 static int first = TRUE;
839 if(!est_host_addrs) return "127.0.0.1";
840 if(first){
841 first = FALSE;
842 if(gethostname(host, ESTPATHBUFSIZ - 1) == -1) return "127.0.0.1";
843 return host;
844 }
845 return host;
846 }
847
848
849 /* Get the address of a host. */
850 char *est_get_host_addr(const char *name){
851 struct hostent *info;
852 const char *addr;
853 char *buf, *pv, vbuf[64];
854 int i, ost, nsiz, asiz, vsiz;
855 assert(name);
856 if(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &ost) != 0) return NULL;
857 if(pthread_mutex_lock(&est_host_addrs_mutex) != 0){
858 pthread_setcancelstate(ost, NULL);
859 return NULL;
860 }
861 if(!est_host_addrs){
862 pthread_mutex_unlock(&est_host_addrs_mutex);
863 pthread_setcancelstate(ost, NULL);
864 return NULL;
865 }
866 nsiz = strlen(name);
867 buf = NULL;
868 if((addr = cbmapget(est_host_addrs, name, nsiz, &asiz)) != NULL){
869 buf = cbmemdup(addr, asiz);
870 if((pv = strchr(buf, '\t')) != NULL){
871 *pv = '\0';
872 if((int)time(NULL) - atoi(pv + 1) > ESTDNHOLDSEC){
873 free(buf);
874 buf = NULL;
875 }
876 }
877 cbmapmove(est_host_addrs, name, nsiz, FALSE);
878 }
879 pthread_mutex_unlock(&est_host_addrs_mutex);
880 pthread_setcancelstate(ost, NULL);
881 if(buf){
882 if(buf[0] != '\0') return buf;
883 free(buf);
884 return NULL;
885 }
886 if((info = gethostbyname(name)) != NULL && info->h_addr_list[0]){
887 addr = inet_ntoa(*(struct in_addr *)info->h_addr_list[0]);
888 } else {
889 addr = NULL;
890 }
891 buf = addr ? cbmemdup(addr, -1) : NULL;
892 if(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &ost) == 0){
893 if(pthread_mutex_lock(&est_host_addrs_mutex) == 0){
894 vsiz = sprintf(vbuf, "%s\t%d", addr ? addr : "", (int)time(NULL));
895 cbmapput(est_host_addrs, name, nsiz, vbuf, vsiz, TRUE);
896 if(cbmaprnum(est_host_addrs) > ESTDNHOLDNUM){
897 cbmapiterinit(est_host_addrs);
898 for(i = 0; i < ESTDNHOLDNUM / 4 &&
899 (addr = cbmapiternext(est_host_addrs, &asiz)) != NULL; i++){
900 cbmapout(est_host_addrs, addr, asiz);
901 }
902 }
903 pthread_mutex_unlock(&est_host_addrs_mutex);
904 }
905 pthread_setcancelstate(ost, NULL);
906 }
907 return buf;
908 }
909
910
911 /* Get a server socket of an address and a port. */
912 int est_get_server_sock(const char *addr, int port){
913 struct sockaddr_in address;
914 struct linger li;
915 int ost, sock, optone;
916 assert(port > 0);
917 if(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &ost) != 0) return -1;
918 memset(&address, 0, sizeof(address));
919 address.sin_family = AF_INET;
920 if(!est_inet_aton(addr ? addr : "0.0.0.0", &address.sin_addr)){
921 pthread_setcancelstate(ost, NULL);
922 return -1;
923 }
924 address.sin_port = htons(port);
925 if((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1){
926 pthread_setcancelstate(ost, NULL);
927 return -1;
928 }
929 li.l_onoff = 1;
930 li.l_linger = 100;
931 optone = 1;
932 if(setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *)&li, sizeof(li)) == -1 ||
933 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&optone, sizeof(optone)) == -1){
934 est_sock_close(sock);
935 pthread_setcancelstate(ost, NULL);
936 return -1;
937 }
938 if(bind(sock, (struct sockaddr *)&address, sizeof(address)) == -1 ||
939 listen(sock, SOMAXCONN) == -1){
940 est_sock_close(sock);
941 pthread_setcancelstate(ost, NULL);
942 return -1;
943 }
944 pthread_setcancelstate(ost, NULL);
945 return sock;
946 }
947
948
949 /* Accept a connection from a client. */
950 int est_accept_conn(int sock, char *abuf, int *pp){
951 struct sockaddr_in address;
952 socklen_t socklen;
953 int clsock;
954 assert(sock >= 0);
955 socklen = sizeof(address);
956 if((clsock = accept(sock, (struct sockaddr *)&address, &socklen)) >= 0){
957 if(abuf) sprintf(abuf, "%s", inet_ntoa(address.sin_addr));
958 if(pp) *pp = (int)ntohs(address.sin_port);
959 return clsock;
960 }
961 return (errno == EINTR || errno == EAGAIN) ? 0 : -1;
962 }
963
964
965 /* Get a client socket to an address and a port. */
966 int est_get_client_sock(const char *addr, int port){
967 struct sockaddr_in address;
968 struct linger li;
969 int ost, sock;
970 assert(addr && port >= 0);
971 if(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &ost) != 0) return -1;
972 memset(&address, 0, sizeof(address));
973 address.sin_family = AF_INET;
974 if(!est_inet_aton(addr, &address.sin_addr)){
975 pthread_setcancelstate(ost, NULL);
976 return -1;
977 }
978 address.sin_port = htons(port);
979 if((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1){
980 pthread_setcancelstate(ost, NULL);
981 return -1;
982 }
983 li.l_onoff = 1;
984 li.l_linger = 100;
985 if(setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *)&li, sizeof(li)) == -1){
986 est_sock_close(sock);
987 pthread_setcancelstate(ost, NULL);
988 return -1;
989 }
990 if(connect(sock, (struct sockaddr *)&address, sizeof(address)) == -1){
991 est_sock_close(sock);
992 pthread_setcancelstate(ost, NULL);
993 return -1;
994 }
995 pthread_setcancelstate(ost, NULL);
996 return sock;
997 }
998
999
1000 /* Shutdown and close a socket. */
1001 void est_sock_down(int sock){
1002 assert(sock >= 0);
1003 shutdown(sock, 2);
1004 est_sock_close(sock);
1005 }
1006
1007
1008 /* Receive all data from a socket. */
1009 char *est_sock_recv_all(int sock, int len){
1010 char *buf;
1011 int i, bs;
1012 assert(sock >= 0 && len >= 0);
1013 buf = cbmalloc(len + 1);
1014 for(i = 0; i < len && (bs = recv(sock, buf + i, len - i, 0)) != 0; i += bs){
1015 if(bs == -1 && errno != EINTR){
1016 free(buf);
1017 return NULL;
1018 }
1019 }
1020 buf[i] = '\0';
1021 return buf;
1022 }
1023
1024
1025 /* Receive a line from a socket. */
1026 int est_sock_recv_line(int sock, char *buf, int max){
1027 char *wp;
1028 assert(sock >= 0 && buf && max > 0);
1029 max--;
1030 wp = buf;
1031 while(wp < buf + max){
1032 switch(recv(sock, wp, 1, 0)){
1033 case -1:
1034 if(errno != EINTR){
1035 *wp = '\0';
1036 return wp - buf;
1037 }
1038 break;
1039 case 0:
1040 *wp = '\0';
1041 return wp - buf;
1042 default:
1043 switch(*wp){
1044 case '\r':
1045 break;
1046 case '\n':
1047 *wp = '\0';
1048 return wp - buf;
1049 default:
1050 wp++;
1051 break;
1052 }
1053 break;
1054 }
1055 }
1056 *wp = '\0';
1057 return wp - buf;
1058 }
1059
1060
1061 /* Receive void data from a socket. */
1062 void est_sock_recv_void(int sock){
1063 fd_set rfds;
1064 struct timeval tv;
1065 char ibuf[ESTIOBUFSIZ];
1066 assert(sock >= 0);
1067 FD_ZERO(&rfds);
1068 FD_SET(sock, &rfds);
1069 tv.tv_sec = 0;
1070 tv.tv_usec = 0;
1071 if(select(sock + 1, &rfds, NULL, NULL, &tv) > 0 && FD_ISSET(sock, &rfds))
1072 recv(sock, ibuf, ESTIOBUFSIZ, 0);
1073 }
1074
1075
1076 /* Write all data into a socket. */
1077 void est_sock_send_all(int sock, const char *buf, int len){
1078 const char *rp;
1079 int rv, wb;
1080 assert(sock >= 0 && buf && len >= 0);
1081 rp = buf;
1082 rv = 0;
1083 do {
1084 wb = send(sock, rp, len, 0);
1085 switch(wb){
1086 case -1: if(errno != EINTR) return;
1087 case 0: break;
1088 default:
1089 rp += wb;
1090 len -= wb;
1091 rv += wb;
1092 break;
1093 }
1094 } while(len > 0);
1095 }
1096
1097
1098 /* Perform formatted output into a datum object. */
1099 void est_datum_printf(CBDATUM *datum, const char *format, ...){
1100 va_list ap;
1101 char *tmp, cbuf[ESTNUMBUFSIZ], tbuf[ESTNUMBUFSIZ*2];
1102 unsigned char c;
1103 int cblen, tlen;
1104 assert(datum && format);
1105 va_start(ap, format);
1106 while(*format != '\0'){
1107 if(*format == '%'){
1108 cbuf[0] = '%';
1109 cblen = 1;
1110 format++;
1111 while(strchr("0123456789 .+-", *format) && *format != '\0' && cblen < ESTNUMBUFSIZ - 1){
1112 cbuf[cblen++] = *format;
1113 format++;
1114 }
1115 cbuf[cblen++] = *format;
1116 cbuf[cblen] = '\0';
1117 switch(*format){
1118 case 's':
1119 tmp = va_arg(ap, char *);
1120 if(!tmp) tmp = "(null)";
1121 cbdatumcat(datum, tmp, -1);
1122 break;
1123 case 'd':
1124 tlen = sprintf(tbuf, cbuf, va_arg(ap, int));
1125 cbdatumcat(datum, tbuf, tlen);
1126 break;
1127 case 'o': case 'u': case 'x': case 'X': case 'c':
1128 tlen = sprintf(tbuf, cbuf, va_arg(ap, unsigned int));
1129 cbdatumcat(datum, tbuf, tlen);
1130 break;
1131 case 'e': case 'E': case 'f': case 'g': case 'G':
1132 tlen = sprintf(tbuf, cbuf, va_arg(ap, double));
1133 cbdatumcat(datum, tbuf, tlen);
1134 break;
1135 case '@':
1136 tmp = va_arg(ap, char *);
1137 if(!tmp) tmp = "(null)";
1138 while(*tmp){
1139 switch(*tmp){
1140 case '&': cbdatumcat(datum, "&amp;", 5); break;
1141 case '<': cbdatumcat(datum, "&lt;", 4); break;
1142 case '>': cbdatumcat(datum, "&gt;", 4); break;
1143 case '"': cbdatumcat(datum, "&quot;", 6); break;
1144 default:
1145 if(!((*tmp >= 0 && *tmp <= 0x8) || (*tmp >= 0x0e && *tmp <= 0x1f)))
1146 cbdatumcat(datum, tmp, 1);
1147 break;
1148 }
1149 tmp++;
1150 }
1151 break;
1152 case '?':
1153 tmp = va_arg(ap, char *);
1154 if(!tmp) tmp = "(null)";
1155 while(*tmp){
1156 c = *(unsigned char *)tmp;
1157 if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
1158 (c >= '0' && c <= '9') || (c != '\0' && strchr("_-.", c))){
1159 cbdatumcat(datum, tmp, 1);
1160 } else {
1161 tlen = sprintf(tbuf, "%%%02X", c);
1162 cbdatumcat(datum, tbuf, tlen);
1163 }
1164 tmp++;
1165 }
1166 break;
1167 case '%':
1168 cbdatumcat(datum, "%", 1);
1169 break;
1170 }
1171 } else {
1172 cbdatumcat(datum, format, 1);
1173 }
1174 format++;
1175 }
1176 va_end(ap);
1177 }
1178
1179
1180 /* Perform an interaction of a URL. */
1181 int est_url_shuttle(const char *url, const char *pxhost, int pxport, int outsec,
1182 const char *auth, const CBLIST *reqheads, const char *reqbody, int rbsiz,
1183 int *rescodep, CBMAP *resheads, CBDATUM *resbody){
1184 pthread_t th;
1185 pthread_mutex_t mutex;
1186 pthread_cond_t cond;
1187 struct timespec timeout;
1188 TARGSHUTTLE targ;
1189 int err, rv;
1190 void *rvp;
1191 pthread_mutex_init(&mutex, NULL);
1192 pthread_cond_init(&cond, NULL);
1193 targ.alive = TRUE;
1194 targ.cond = &cond;
1195 targ.url = url;
1196 targ.pxhost = pxhost;
1197 targ.pxport = pxport;
1198 targ.auth = auth;
1199 targ.reqheads = reqheads;
1200 targ.reqbody = reqbody;
1201 targ.rbsiz = rbsiz;
1202 targ.rescodep = rescodep;
1203 targ.resheads = resheads;
1204 targ.resbody = resbody;
1205 err = FALSE;
1206 if(outsec >= 0){
1207 if(pthread_mutex_lock(&mutex) == 0){
1208 if(pthread_create(&th, NULL, est_url_shuttle_impl, &targ) == 0){
1209 timeout.tv_sec = time(NULL) + outsec;
1210 timeout.tv_nsec = 1000 * 1000 * 500;
1211 rv = 0;
1212 while(targ.alive && rv != ETIMEDOUT){
1213 rv = pthread_cond_timedwait(&cond, &mutex, &timeout);
1214 }
1215 if(rv == ETIMEDOUT){
1216 pthread_cancel(th);
1217 pthread_join(th, NULL);
1218 err = TRUE;
1219 } else if(pthread_join(th, &rvp) != 0 || rvp != NULL){
1220 err = TRUE;
1221 }
1222 } else {
1223 err = TRUE;
1224 }
1225 pthread_mutex_unlock(&mutex);
1226 } else {
1227 err = TRUE;
1228 }
1229 } else {
1230 if(est_url_shuttle_impl(&targ) != NULL) err = TRUE;
1231 }
1232 if(pthread_mutex_destroy(&mutex) != 0) err = TRUE;
1233 if(pthread_cond_destroy(&cond) != 0) err = TRUE;
1234 return err ? FALSE : TRUE;
1235 }
1236
1237
1238 /* Add a header to a node connection object. */
1239 void est_node_add_header(ESTNODE *node, const char *name, const char *value){
1240 const char *vbuf;
1241 int len;
1242 assert(node && name);
1243 len = strlen(name);
1244 if(value){
1245 if((vbuf = cbmapget(node->heads, name, len, NULL)) != NULL){
1246 cbmapputcat(node->heads, name, len, ", ", 2);
1247 cbmapputcat(node->heads, name, len, value, -1);
1248 } else {
1249 cbmapput(node->heads, name, len, value, -1, FALSE);
1250 }
1251 } else {
1252 cbmapout(node->heads, name, len);
1253 }
1254 }
1255
1256
1257 /* Create a node result object. */
1258 ESTNODERES *est_noderes_new(void){
1259 ESTNODERES *nres;
1260 nres = cbmalloc(sizeof(ESTNODERES));
1261 nres->top = 0;
1262 nres->max = ESTLISTUNIT;
1263 nres->docs = cbmalloc(sizeof(ESTRESDOC) * nres->max);
1264 nres->dnum = 0;
1265 nres->hints = cbmapopenex(ESTMINIBNUM);
1266 return nres;
1267 }
1268
1269
1270 /* Add a document information to a node result object. */
1271 void est_noderes_add_doc(ESTNODERES *nres, CBMAP *attrs, char *snippet){
1272 const char *uri;
1273 assert(nres && attrs && snippet);
1274 if(!(uri = cbmapget(attrs, ESTDATTRURI, -1, NULL))){
1275 free(snippet);
1276 cbmapclose(attrs);
1277 return;
1278 }
1279 if(nres->top + nres->dnum >= nres->max){
1280 nres->max *= 2;
1281 nres->docs = cbrealloc(nres->docs, nres->max * sizeof(ESTRESDOC));
1282 }
1283 nres->docs[nres->top+nres->dnum].uri = uri;
1284 nres->docs[nres->top+nres->dnum].attrs = attrs;
1285 nres->docs[nres->top+nres->dnum].snippet = snippet;
1286 nres->dnum++;
1287 }
1288
1289
1290 /* Remove the top of result document objects in a node result object. */
1291 int est_noderes_shift(ESTNODERES *nres, CBMAP **attrp, char **snippetp){
1292 assert(nres && attrp && snippetp);
1293 if(nres->dnum < 1) return FALSE;
1294 *attrp = nres->docs[nres->top].attrs;
1295 *snippetp = nres->docs[nres->top].snippet;
1296 nres->top++;
1297 nres->dnum--;
1298 return TRUE;
1299 }
1300
1301
1302
1303 /*************************************************************************************************
1304 * private objects
1305 *************************************************************************************************/
1306
1307
1308 /* Close a socket.
1309 `sock' specifies a socket.
1310 The return value is 0 if success, else it is -1. */
1311 static int est_sock_close(int sock){
1312 #if defined(_SYS_MSVC_) || defined(_SYS_MINGW_)
1313 assert(sock >= 0);
1314 return closesocket(sock);
1315 #else
1316 assert(sock >= 0);
1317 return close(sock);
1318 #endif
1319 }
1320
1321
1322 /* Convert a host address to network binary data.
1323 `cp' specifies a host address.
1324 `inp' specifies the pointer to an structure into which the result is to be stored.
1325 The return value is true if success, else it is false. */
1326 static int est_inet_aton(const char *cp, struct in_addr *inp){
1327 #if defined(_SYS_MSVC_) || defined(_SYS_MINGW_)
1328 in_addr_t in;
1329 assert(cp && inp);
1330 if((in = inet_addr(cp)) == INADDR_NONE){
1331 if(!strcmp(cp, "255.255.255.255")){
1332 inp->s_addr = in;
1333 return TRUE;
1334 }
1335 return FALSE;
1336 }
1337 inp->s_addr = in;
1338 return TRUE;
1339 #else
1340 assert(cp && inp);
1341 return inet_aton(cp, inp);
1342 #endif
1343 }
1344
1345
1346 /* Perform the communication which can be canceled.
1347 `targ' specifies the pointer to a closure arguments.
1348 The return value is `NULL' if sucessful or non `NULL' value on error. */
1349 static void *est_url_shuttle_impl(void *targ){
1350 const CBLIST *reqheads;
1351 CBMAP *resheads, *elems;
1352 CBDATUM *resbody, *datum;
1353 const char *url, *pxhost, *auth, *reqbody, *tmp, *scheme, *host, *path, *query, *rp;
1354 char *addr, *enc, iobuf[ESTIOBUFSIZ], name[ESTIOBUFSIZ], *pv;
1355 int i, pxport, rbsiz, *rescodep, port, sock, *sp, size, nsiz;
1356 assert(targ);
1357 url = ((TARGSHUTTLE *)targ)->url;
1358 pxhost = ((TARGSHUTTLE *)targ)->pxhost;
1359 pxport = ((TARGSHUTTLE *)targ)->pxport;
1360 auth = ((TARGSHUTTLE *)targ)->auth;
1361 reqheads = ((TARGSHUTTLE *)targ)->reqheads;
1362 reqbody = ((TARGSHUTTLE *)targ)->reqbody;
1363 rbsiz = ((TARGSHUTTLE *)targ)->rbsiz;
1364 rescodep = ((TARGSHUTTLE *)targ)->rescodep;
1365 resheads = ((TARGSHUTTLE *)targ)->resheads;
1366 resbody = ((TARGSHUTTLE *)targ)->resbody;
1367 elems = cburlbreak(url);
1368 pthread_cleanup_push((void (*)(void *))cbmapclose, elems);
1369 scheme = cbmapget(elems, "scheme", -1, NULL);
1370 host = cbmapget(elems, "host", -1, NULL);
1371 port = (tmp = cbmapget(elems, "port", -1, NULL)) ? atoi(tmp) : 80;
1372 if(!auth) auth = cbmapget(elems, "authority", -1, NULL);
1373 if(!(path = cbmapget(elems, "path", -1, NULL))) path = "/";
1374 if(!(query = cbmapget(elems, "query", -1, NULL))) query = "";
1375 if(!scheme || cbstricmp(scheme, "http") || !host || port < 1 ||
1376 !(addr = est_get_host_addr(pxhost ? pxhost : host))){
1377 ((TARGSHUTTLE *)targ)->alive = FALSE;
1378 pthread_cond_signal(((TARGSHUTTLE *)targ)->cond);
1379 return "error";
1380 }
1381 pthread_cleanup_push((void (*)(void *))free, addr);
1382 if((sock = est_get_client_sock(addr, pxhost ? pxport : port)) == -1){
1383 ((TARGSHUTTLE *)targ)->alive = FALSE;
1384 pthread_cond_signal(((TARGSHUTTLE *)targ)->cond);
1385 return "error";
1386 }
1387 sp = cbmalloc(sizeof(int));
1388 *sp = sock;
1389 pthread_cleanup_push((void (*)(void *))est_sockpt_down, sp);
1390 datum = cbdatumopen("", 0);
1391 pthread_cleanup_push((void (*)(void *))cbdatumclose, datum);
1392 if(pxhost){
1393 est_datum_printf(datum, "%s %s HTTP/1.0\r\n", reqbody ? "POST" : "GET", url);
1394 } else if(reqbody){
1395 est_datum_printf(datum, "POST %s HTTP/1.0\r\n", path);
1396 } else if(query[0] != 0){
1397 est_datum_printf(datum, "GET %s?%s HTTP/1.0\r\n", path, query);
1398 } else {
1399 est_datum_printf(datum, "GET %s HTTP/1.0\r\n", path);
1400 }
1401 est_datum_printf(datum, "Host: %s:%d\r\n", host, port);
1402 est_datum_printf(datum, "Connection: close\r\n", path);
1403 est_datum_printf(datum, "User-Agent: %s/%s\r\n", ESTAGENTNAME, est_version);
1404 if(auth){
1405 enc = cbbaseencode(auth, -1);
1406 est_datum_printf(datum, "Authorization: Basic %s\r\n", enc);
1407 free(enc);
1408 }
1409 if(reqbody) est_datum_printf(datum, "Content-Length: %d\r\n", rbsiz);
1410 if(reqheads){
1411 for(i = 0; i < cblistnum(reqheads); i++){
1412 rp = cblistval(reqheads, i, &size);
1413 est_datum_printf(datum, rp, size);
1414 est_datum_printf(datum, "\r\n", 2);
1415 }
1416 }
1417 est_datum_printf(datum, "\r\n");
1418 est_sock_send_all(sock, cbdatumptr(datum), cbdatumsize(datum));
1419 if(reqbody) est_sock_send_all(sock, reqbody, rbsiz);
1420 if((size = est_sock_recv_line(sock, iobuf, ESTIOBUFSIZ - 1)) < 1 ||
1421 !cbstrfwmatch(iobuf, "HTTP/") || !(rp = strchr(iobuf, ' '))){
1422 ((TARGSHUTTLE *)targ)->alive = FALSE;
1423 pthread_cond_signal(((TARGSHUTTLE *)targ)->cond);
1424 return "error";
1425 }
1426 rp++;
1427 if(rescodep) *rescodep = atoi(rp);
1428 if(resheads) cbmapput(resheads, "", 0, iobuf, size, TRUE);
1429 name[0] = '\0';
1430 nsiz = 0;
1431 while((size = est_sock_recv_line(sock, iobuf, ESTIOBUFSIZ - 1)) > 0){
1432 if(resheads){
1433 if(iobuf[0] == ' ' || iobuf[0] == '\t'){
1434 if(name[0] != '\0'){
1435 iobuf[0] = ' ';
1436 cbmapputcat(resheads, name, nsiz, iobuf, size);
1437 }
1438 } else if((rp = strchr(iobuf, ':')) > iobuf){
1439 nsiz = rp - iobuf;
1440 memcpy(name, iobuf, nsiz);
1441 name[nsiz] = '\0';
1442 for(pv = name; *pv != '\0'; pv++){
1443 if(*pv >= 'A'&& *pv <= 'Z') *pv = *pv + ('a' - 'A');
1444 }
1445 rp++;
1446 if(*rp == ' ' || *rp == '\t') rp++;
1447 if(cbmapget(resheads, name, nsiz, NULL)){
1448 cbmapputcat(resheads, name, nsiz, ", ", 2);
1449 cbmapputcat(resheads, name, nsiz, pv, -1);
1450 } else {
1451 cbmapput(resheads, name, nsiz, rp, -1, TRUE);
1452 }
1453 }
1454 }
1455 }
1456 while((size = recv(sock, iobuf, ESTIOBUFSIZ, 0)) > 0){
1457 if(resbody) cbdatumcat(resbody, iobuf, size);
1458 }
1459 pthread_cleanup_pop(1);
1460 pthread_cleanup_pop(1);
1461 pthread_cleanup_pop(1);
1462 pthread_cleanup_pop(1);
1463 ((TARGSHUTTLE *)targ)->alive = FALSE;
1464 pthread_cond_signal(((TARGSHUTTLE *)targ)->cond);
1465 return NULL;
1466 }
1467
1468
1469 /* Release the socket of a pointer.
1470 `sp' specifies the pointer to a variable of a file descriptor. */
1471 static void est_sockpt_down(void *sp){
1472 est_sock_down(*(int *)sp);
1473 free(sp);
1474 }
1475
1476
1477 /* Set meta informations of a node.
1478 `node' specifies a node connection object.
1479 The return value is true if success, else it is false. */
1480 static int est_node_set_info(ESTNODE *node){
1481 CBLIST *reqheads, *elems;
1482 CBDATUM *resbody;
1483 const char *kbuf, *ptr;
1484 char url[ESTPATHBUFSIZ], *vbuf, *pv;
1485 int rescode, err, ksiz;
1486 assert(node);
1487 err = FALSE;
1488 sprintf(url, "%s/inform", node->url);
1489 reqheads = cblistopen();
1490 if(cbmaprnum(node->heads) > 0){
1491 cbmapiterinit(node->heads);
1492 while((kbuf = cbmapiternext(node->heads, &ksiz)) != NULL){
1493 vbuf = cbsprintf("%s: %s", kbuf, cbmapget(node->heads, kbuf, ksiz, NULL));
1494 cblistpush(reqheads, vbuf, -1);
1495 free(vbuf);
1496 }
1497 }
1498 node->dnum = -1;
1499 node->wnum = -1;
1500 node->size = -1.0;
1501 resbody = cbdatumopen("", 0);
1502 if(!est_url_shuttle(url, node->pxhost, node->pxport, node->timeout, node->auth, reqheads,
1503 NULL, -1, &rescode, NULL, resbody)){
1504 node->status = -1;
1505 err = TRUE;
1506 }
1507 if(!err){
1508 node->status = rescode;
1509 if(rescode != 200) err = TRUE;
1510 }
1511 if(!err){
1512 ptr = cbdatumptr(resbody);
1513 if((pv = strchr(ptr, '\n')) != NULL){
1514 elems = cbsplit(ptr, pv - ptr, "\t");
1515 if(cblistnum(elems) == 5){
1516 if(!node->name) node->name = cbmemdup(cblistval(elems, 0, NULL), -1);
1517 if(!node->label) node->label = cbmemdup(cblistval(elems, 1, NULL), -1);
1518 node->dnum = atoi(cblistval(elems, 2, NULL));
1519 node->wnum = atoi(cblistval(elems, 3, NULL));
1520 node->size = strtod(cblistval(elems, 4, NULL), NULL);
1521 if(node->dnum < 0){
1522 node->dnum = -1;
1523 err = TRUE;
1524 }
1525 if(node->wnum < 0){
1526 node->wnum = -1;
1527 err = TRUE;
1528 }
1529 if(node->size < 0.0){
1530 node->size = -1.0;
1531 err = TRUE;
1532 }
1533 } else {
1534 err = TRUE;
1535 }
1536 cblistclose(elems);
1537 } else {
1538 err = TRUE;
1539 }
1540 }
1541 cbdatumclose(resbody);
1542 cblistclose(reqheads);
1543 return err ? FALSE : TRUE;
1544 }
1545
1546
1547 /* Parse the header of a result data.
1548 `nres' specifies a node result object.
1549 `str' specifies the header of a result data. */
1550 static void est_parse_search_header(ESTNODERES *nres, const char *str){
1551 CBLIST *lines;
1552 const char *line, *pv;
1553 int i;
1554 assert(nres && str);
1555 lines = cbsplit(str, -1, "\r\n");
1556 for(i = 0; i < cblistnum(lines); i++){
1557 line = cblistval(lines, i, NULL);
1558 if(!(pv = strchr(line, '\t')) || pv == line || pv[1] == '\0') continue;
1559 cbmapput(nres->hints, line, pv - line, pv + 1, -1, FALSE);
1560 }
1561 cblistclose(lines);
1562 }
1563
1564
1565 /* Parse a body part of a result data.
1566 `nres' specifies a node result object.
1567 `str' specifies a body part of a result data. */
1568 static void est_parse_search_body(ESTNODERES *nres, char *str){
1569 CBMAP *attrs;
1570 char *pv, *ep, *mp;
1571 pv = str;
1572 attrs = cbmapopenex(ESTMINIBNUM);
1573 while(TRUE){
1574 if(!(ep = strchr(pv, '\n')) || ep == pv) break;
1575 *ep = '\0';
1576 cbstrtrim(pv);
1577 if(*pv == '\0') break;
1578 if((mp = strchr(pv, '=')) != NULL){
1579 *mp = '\0';
1580 cbmapput(attrs, pv, -1, mp + 1, -1, TRUE);
1581 }
1582 pv = ep + 1;
1583 }
1584 while(*pv == '\r' || *pv == '\n'){
1585 pv++;
1586 }
1587 est_noderes_add_doc(nres, attrs, cbmemdup(pv, -1));
1588 }
1589
1590
1591
1592 /* END OF FILE */

  ViewVC Help
Powered by ViewVC 1.1.26