/[hyperestraier]/trunk/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

Annotation of /trunk/estnode.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3 - (hide annotations)
Fri Jul 29 21:57:20 2005 UTC (18 years, 9 months ago) by dpavlin
File MIME type: text/plain
File size: 46153 byte(s)
make working copy from version 0.5.1

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

  ViewVC Help
Powered by ViewVC 1.1.26