/[hyperestraier]/trunk/estmaster.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/estmaster.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: 130498 byte(s)
make working copy from version 0.5.1

1 dpavlin 2 /*************************************************************************************************
2     * The master of node servers
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 "mastermod.h"
18    
19     #define SERVNAME "estmaster" /* name of the server */
20     #define IOBUFSIZ 8192 /* size of a buffer for I/O */
21     #define RECVMAX 1024 * 1024 * 32 /* max length of data to receive */
22     #define SPCACHEMNUM 131072 /* max number of the special cache */
23     #define IFLUSHSEC 60 /* idle time to start flushing */
24     #define IFLUSHNUM 5000 /* number of keys per flushing */
25     #define DEFMAXSRCH 10 /* default max number of retrieved documents */
26     #define SELFCREDIT 10000 /* credit of the node itself */
27     #define FCLOSECONNNUM 10 /* number of over connections for forced closing */
28     #define FCLOSEWAITMAX 10 /* wait time for forced closing */
29     #define FTERMWAITMAX 60 /* wait time for forced termination */
30    
31     #define MASTERLOC "/master" /* location of the URL of the master */
32     #define NODEPREFIX "/node/" /* prefix of URLs of nodes */
33     #define MASTERUILOC "/masterui" /* location of user intarfaces */
34     #define DATTRNDURL "#nodeurl" /* name of the pseudo-attribute of the node URL */
35     #define DATTRNDLABEL "#nodelabel" /* name of the pseudo-attribute of the node label */
36     #define DELIMSTR "{{!}}" /* delimiter string */
37     #define DATTRLFILE "_lfile" /* name of the attribute of the local file name */
38    
39     typedef struct { /* type of structure for a communication */
40     int clsock; /* socket between client */
41     char claddr[ADDRBUFSIZ]; /* address of the client */
42     int clport; /* port number of the client */
43     } TARGCOMM;
44    
45     enum { /* enumeration for HTTP method */
46     HM_HEAD, /* HEAD */
47     HM_GET, /* GET */
48     HM_POST, /* POST */
49     HM_UNKNOWN /* unknown */
50     };
51    
52     typedef struct { /* type of structure for a request */
53     const char *claddr; /* address of the client */
54     int clport; /* port number of the client */
55     int method; /* method */
56     char *target; /* target path */
57     char *name; /* user name */
58     char *passwd; /* password */
59     time_t ims; /* if-modified-since header */
60     char *ctype; /* content-type header */
61     char *estvia; /* x-estraier-via header */
62     char *host; /* host header */
63     char *body; /* entity body */
64     CBMAP *params; /* parameters */
65     } REQUEST;
66    
67     typedef struct { /* type of structure for a local search */
68     REQUEST *req; /* request object */
69     int alive; /* whether to be alive */
70     ESTCOND *cond; /* search condition */
71     CBMAP *hints; /* hints about result */
72     int max; /* max number of retrieved documents */
73     NODE *node; /* self node object */
74     RESMAP *resmap; /* documents in result */
75     CBLIST *words; /* words in the search phrase */
76     int hnum; /* number of hits */
77     } TARGLCSRCH;
78    
79     typedef struct { /* type of structure for a remote search */
80     REQUEST *req; /* request object */
81     int alive; /* whether to be alive */
82     const char *myurl; /* url of the local node */
83     ESTCOND *cond; /* search condition */
84     CBMAP *hints; /* hints about result */
85     int max; /* max number of retrieved documents */
86     NODE *node; /* self node object */
87     RESMAP *resmap; /* documents in result */
88     const char *url; /* URL of the link */
89     int credit; /* credit of the link */
90     char *label; /* label of the link */
91     int depth; /* depth of meta search */
92     int hnum; /* number of hits */
93     int dnum; /* number of documents */
94     int wnum; /* number of words */
95     double size; /* size of the database */
96     } TARGRMSRCH;
97    
98     enum { /* enumeration for UI operations */
99     UI_VIEWMASTER, /* view master operations */
100     UI_SHUTDOWN, /* shutdown the master server */
101     UI_VIEWUSERS, /* view user operations */
102     UI_NEWUSER, /* create a user */
103     UI_DELUSER, /* delete a user */
104     UI_VIEWNODES, /* view node operations */
105     UI_NEWNODE, /* create a node */
106     UI_DELNODE, /* delete a node */
107     UI_EDITNODE, /* edit a node */
108     UI_NONE /* none */
109     };
110    
111    
112     /* global variables */
113     const char *g_progname; /* program name */
114     int g_sigterm = FALSE; /* flag for termination signal */
115     int g_sigrestart = FALSE; /* flag for restarting signal */
116     const char *g_rootdir = NULL; /* path of the root directory */
117     const char *g_hostname = NULL; /* fully qualified host name */
118     int g_portnum = 0; /* port number of TCP */
119     int g_runmode = 0; /* runnning mode */
120     int g_authmode = 0; /* authorization mode */
121     int g_maxconn = 0; /* maximum number of connections */
122     int g_sessiontimeout = 0; /* timeout of a session, in seconds */
123     int g_searchtimeout = 0; /* timeout of search, in seconds */
124     int g_searchdepth = 0; /* depth of search */
125     const char *g_proxyhost = NULL; /* host name of the proxy */
126     int g_proxyport = 0; /* port number of the proxy */
127     int g_loglevel = 0; /* logging level */
128     const char *g_docroot = NULL; /* path of the document root directory */
129     const char *g_indexfile = NULL; /* name of directory index files */
130     CBMAP *g_trustednodes = NULL; /* addresses of trusted nodes */
131     int g_denyuntrusted = FALSE; /* whether to deny untrusted nodes */
132     int g_cachesize = 0; /* maximum size of the index cache */
133     const char *g_specialcache = NULL; /* name of the attribute of special cache */
134     int g_snipwwidth = 0; /* whole width of the snippet */
135     int g_sniphwidth = 0; /* width of beginning of the text */
136     int g_snipawidth = 0; /* width around each highlighted word */
137     const char *g_uilprefix = NULL; /* prefix of the local URI of each document */
138     const char *g_uigprefix = NULL; /* prefix of the global URI of each document */
139     const char *g_uigsuffix = NULL; /* suffix added to the global URI of each document */
140     const char *g_uidirindex = NULL; /* name of the directory index file */
141     CBLIST *g_uireplaces = NULL; /* expressions to replace the URI of each document */
142     CBLIST *g_uicondattrs = NULL; /* attributes for search condition */
143     CBLIST *g_uiextattrs = NULL; /* extra attributes to be shown */
144     int g_bgmode = FALSE; /* whether to be foreground mode */
145     int g_stmode = FALSE; /* whether to be single thread mode */
146     DEPOT *g_metadb = NULL; /* meta database */
147     UMGR *g_umgr = NULL; /* user manager */
148     NMGR *g_nmgr = NULL; /* node manager */
149     RWLOCK *g_runlock = NULL; /* read-write lock for running threads */
150     RWLOCK *g_mgrlock = NULL; /* read-write lock for handling managers */
151     const char *g_bordstr = NULL; /* border string */
152     int g_svsock = -1; /* server socket */
153    
154    
155     /* function prototypes */
156     int main(int argc, char **argv);
157     static void setsignals(void);
158     static void sigtermhandler(int num);
159     static void usage(void);
160     static int runinit(int argc, char **argv);
161     static int runstart(int argc, char **argv);
162     static int runstop(int argc, char **argv);
163     static int rununittest(int argc, char **argv);
164     static int runcrypt(int argc, char **argv);
165     static void die(const char *format, ...);
166     static void startup(void);
167     static const char *skiplabel(const char *str);
168     static void cleanup(void);
169     static int procinit(const char *rootdir, int ex);
170     static int procstart(const char *rootdir);
171     static int procstop(const char *rootdir);
172     static int procunittest(const char *rootdir);
173     static int proccrypt(const char *key, const char *hash);
174     static void dispatch(void);
175     static void *flushnode(void *targ);
176     static void *communicate(void *targ);
177     static void setparams(CBMAP *params, const char *src);
178     static void addservinfo(CBDATUM *datum);
179     static void senderror(int clsock, REQUEST *req, int code, const char *message);
180     static int isbanned(USER *user);
181     static int ismasteradmin(REQUEST *req, USER *user);
182     static int isnodeadmin(NODE *node, REQUEST *req, USER *user);
183     static int isnodeuser(NODE *node, REQUEST *req, USER *user);
184     static void sendautherror(int clsock, REQUEST *req, const char *realm);
185     static void sendmasterdata(int clsock, REQUEST *req, USER *user);
186     static void sendnodedata(int clsock, REQUEST *req, USER *user, const char *path);
187     static void setdocorigin(ESTDOC *doc, NODE *node);
188     static void mergehints(CBMAP *total, CBMAP *local);
189     static int islooproute(const char *url, REQUEST *req);
190     static void *searchlocal(void *targ);
191     static void *searchremote(void *targ);
192     static void catdocdata(CBDATUM *datum, RESDOC *resdoc, CBLIST *words);
193     static void catdocdataui(CBDATUM *datum, RESDOC *resdoc, CBLIST *words, const char *condstr);
194     static char *makeshownuri(const char *uri);
195     static void sendnodecmdinform(int clsock, REQUEST *req, NODE *node);
196     static void sendnodecmdsearch(int clsock, REQUEST *req, NODE *node);
197     static void sendnodecmdgetdoc(int clsock, REQUEST *req, NODE *node);
198     static void sendnodecmdgetdocattr(int clsock, REQUEST *req, NODE *node);
199     static void sendnodecmduritoid(int clsock, REQUEST *req, NODE *node);
200     static void sendnodecmdputdoc(int clsock, REQUEST *req, NODE *node);
201     static void sendnodecmdoutdoc(int clsock, REQUEST *req, NODE *node);
202     static void sendnodecmdsetuser(int clsock, REQUEST *req, NODE *node);
203     static void sendnodecmdsetlink(int clsock, REQUEST *req, NODE *node);
204     static void sendnodecmdsearchui(int clsock, REQUEST *req, NODE *node);
205     static void sendmasteruidata(int clsock, REQUEST *req, USER *user);
206     static void sendfiledata(int clsock, REQUEST *req);
207     static char *makelocalpath(const char *target);
208    
209    
210     /* main routine */
211     int main(int argc, char **argv){
212     const char *tmp;
213     int rv;
214     if((tmp = getenv("ESTDBGFD")) != NULL) dpdbgfd = atoi(tmp);
215     cbstdiobin();
216     g_progname = argv[0];
217     g_sigterm = FALSE;
218     if(argc < 2) usage();
219     rv = 0;
220     if(!strcmp(argv[1], "init")){
221     rv = runinit(argc, argv);
222     } else if(!strcmp(argv[1], "start")){
223     rv = runstart(argc, argv);
224     } else if(!strcmp(argv[1], "stop")){
225     rv = runstop(argc, argv);
226     } else if(!strcmp(argv[1], "unittest")){
227     rv = rununittest(argc, argv);
228     } else if(!strcmp(argv[1], "crypt")){
229     rv = runcrypt(argc, argv);
230     } else {
231     usage();
232     }
233     return rv;
234     }
235    
236    
237     /* set signal handlers */
238     static void setsignals(void){
239     signal(1, sigtermhandler);
240     signal(2, sigtermhandler);
241     signal(3, sigtermhandler);
242     signal(13, SIG_IGN);
243     signal(15, sigtermhandler);
244     g_sigterm = FALSE;
245     g_sigrestart = FALSE;
246     }
247    
248    
249     /* handler of termination signal */
250     static void sigtermhandler(int num){
251     g_sigterm = TRUE;
252     if(num == 1) g_sigrestart = TRUE;
253     }
254    
255    
256     /* print the usage and exit */
257     static void usage(void){
258     fprintf(stderr, "%s: the master of node servers\n", g_progname);
259     fprintf(stderr, "\n");
260     fprintf(stderr, "usage:\n");
261     fprintf(stderr, " %s init [-ex] rootdir\n", g_progname);
262     fprintf(stderr, " %s start [-bg] [-st] rootdir\n", g_progname);
263     fprintf(stderr, " %s stop rootdir\n", g_progname);
264     fprintf(stderr, " %s unittest rootdir\n", g_progname);
265     fprintf(stderr, " %s crypt key [hash]\n", g_progname);
266     fprintf(stderr, "\n");
267     exit(1);
268     }
269    
270    
271     /* parse arguments of the init command */
272     static int runinit(int argc, char **argv){
273     char *rootdir;
274     int i, ex, rv;
275     rootdir = NULL;
276     ex = FALSE;
277     for(i = 2; i < argc; i++){
278     if(!rootdir && argv[i][0] == '-'){
279     if(!strcmp(argv[i], "-ex")){
280     ex = TRUE;
281     } else {
282     usage();
283     }
284     } else if(!rootdir){
285     rootdir = argv[i];
286     } else {
287     usage();
288     }
289     }
290     if(!rootdir) usage();
291     rv = procinit(rootdir, ex);
292     return rv;
293     }
294    
295    
296     /* parse arguments of the start command */
297     static int runstart(int argc, char **argv){
298     char *rootdir;
299     int i, rv;
300     rootdir = NULL;
301     for(i = 2; i < argc; i++){
302     if(!rootdir && argv[i][0] == '-'){
303     if(!strcmp(argv[i], "-bg")){
304     g_bgmode = TRUE;
305     } else if(!strcmp(argv[i], "-st")){
306     g_stmode = TRUE;
307     } else {
308     usage();
309     }
310     } else if(!rootdir){
311     rootdir = argv[i];
312     } else {
313     usage();
314     }
315     }
316     if(!rootdir) usage();
317     rv = procstart(rootdir);
318     return rv;
319     }
320    
321    
322     /* parse arguments of the stop command */
323     static int runstop(int argc, char **argv){
324     char *rootdir;
325     int i, rv;
326     rootdir = NULL;
327     for(i = 2; i < argc; i++){
328     if(!rootdir && argv[i][0] == '-'){
329     usage();
330     } else if(!rootdir){
331     rootdir = argv[i];
332     } else {
333     usage();
334     }
335     }
336     if(!rootdir) usage();
337     rv = procstop(rootdir);
338     return rv;
339     }
340    
341    
342     /* parse arguments of the unittest command */
343     static int rununittest(int argc, char **argv){
344     char *rootdir;
345     int i, rv;
346     rootdir = NULL;
347     for(i = 2; i < argc; i++){
348     if(!rootdir && argv[i][0] == '-'){
349     usage();
350     } else if(!rootdir){
351     rootdir = argv[i];
352     } else {
353     usage();
354     }
355     }
356     if(!rootdir) usage();
357     rv = procunittest(rootdir);
358     return rv;
359     }
360    
361    
362     /* parse arguments of the crypt command */
363     static int runcrypt(int argc, char **argv){
364     char *key, *hash;
365     int i, rv;
366     key = NULL;
367     hash = NULL;
368     for(i = 2; i < argc; i++){
369     if(!key && argv[i][0] == '-'){
370     usage();
371     } else if(!key){
372     key = argv[i];
373     } else if(!hash){
374     hash = argv[i];
375     } else {
376     usage();
377     }
378     }
379     if(!key) usage();
380     rv = proccrypt(key, hash);
381     return rv;
382     }
383    
384    
385     /* print formatted error string and exit */
386     static void die(const char *format, ...){
387     va_list ap;
388     va_start(ap, format);
389     fprintf(stderr, "%s: ", g_progname);
390     vfprintf(stderr, format, ap);
391     fputc('\n', stderr);
392     fflush(stderr);
393     va_end(ap);
394     exit(1);
395     }
396    
397    
398     /* initialize the global variables */
399     static void startup(void){
400     NODE *node;
401     CBLIST *lines, *elems;
402     struct stat sbuf;
403     const char *rp, *name;
404     char path[URIBUFSIZ], numbuf[NUMBUFSIZ];
405     int i, j, pid, omode;
406     if(stat(g_rootdir, &sbuf) == -1)
407     die("the server root directory (%s) could not open", g_rootdir);
408     sprintf(path, "%s%c%s", g_rootdir, ESTPATHCHR, CONFFILE);
409     if(!(lines = cbreadlines(path))) die("the configuration file (%s) could not open", path);
410     cbglobalgc(lines, (void (*)(void *))cblistclose);
411     g_trustednodes = cbmapopenex(MINIBNUM);
412     cbglobalgc(g_trustednodes, (void (*)(void *))cbmapclose);
413     g_uireplaces = cblistopen();
414     cbglobalgc(g_uireplaces, (void (*)(void *))cblistclose);
415     g_uicondattrs = cblistopen();
416     cbglobalgc(g_uicondattrs, (void (*)(void *))cblistclose);
417     g_uiextattrs = cblistopen();
418     cbglobalgc(g_uiextattrs, (void (*)(void *))cblistclose);
419     for(i = 0; i < cblistnum(lines); i++){
420     rp = cblistval(lines, i, NULL);
421     if(cbstrfwimatch(rp, "hostname:")){
422     g_hostname = skiplabel(rp);
423     } else if(cbstrfwimatch(rp, "portnum:")){
424     g_portnum = atoi(skiplabel(rp));
425     } else if(cbstrfwimatch(rp, "runmode:")){
426     g_runmode = atoi(skiplabel(rp));
427     } else if(cbstrfwimatch(rp, "authmode:")){
428     g_authmode = atoi(skiplabel(rp));
429     } else if(cbstrfwimatch(rp, "maxconn:")){
430     g_maxconn = atoi(skiplabel(rp));
431     } else if(cbstrfwimatch(rp, "sessiontimeout:")){
432     g_sessiontimeout = atoi(skiplabel(rp));
433     } else if(cbstrfwimatch(rp, "searchtimeout:")){
434     g_searchtimeout = atoi(skiplabel(rp));
435     } else if(cbstrfwimatch(rp, "searchdepth:")){
436     g_searchdepth = atoi(skiplabel(rp));
437     } else if(cbstrfwimatch(rp, "proxyhost:")){
438     g_proxyhost = skiplabel(rp);
439     } else if(cbstrfwimatch(rp, "proxyport:")){
440     g_proxyport = atoi(skiplabel(rp));
441     } else if(cbstrfwimatch(rp, "loglevel:")){
442     g_loglevel = atoi(skiplabel(rp));
443     } else if(cbstrfwimatch(rp, "docroot:")){
444     g_docroot = skiplabel(rp);
445     } else if(cbstrfwimatch(rp, "indexfile:")){
446     g_indexfile = skiplabel(rp);
447     } else if(cbstrfwimatch(rp, "trustednodes:")){
448     elems = cbsplit(skiplabel(rp), -1, " ,");
449     for(j = 0; j < cblistnum(elems); j++){
450     rp = cblistval(elems, j, NULL);
451     if(rp != '\0') cbmapput(g_trustednodes, rp, -1, "", 0, TRUE);
452     }
453     cblistclose(elems);
454     } else if(cbstrfwimatch(rp, "denyuntrusted:")){
455     g_denyuntrusted = atoi(skiplabel(rp)) > 0;
456     } else if(cbstrfwimatch(rp, "cachesize:")){
457     g_cachesize = atoi(skiplabel(rp));
458     } else if(cbstrfwimatch(rp, "specialcache:")){
459     g_specialcache = skiplabel(rp);
460     } else if(cbstrfwimatch(rp, "snipwwidth:")){
461     g_snipwwidth = atoi(skiplabel(rp));
462     } else if(cbstrfwimatch(rp, "sniphwidth:")){
463     g_sniphwidth = atoi(skiplabel(rp));
464     } else if(cbstrfwimatch(rp, "snipawidth:")){
465     g_snipawidth = atoi(skiplabel(rp));
466     } else if(cbstrfwimatch(rp, "uilprefix:")){
467     g_uilprefix = skiplabel(rp);
468     } else if(cbstrfwimatch(rp, "uigprefix:")){
469     g_uigprefix = skiplabel(rp);
470     } else if(cbstrfwimatch(rp, "uigsuffix:")){
471     g_uigsuffix = skiplabel(rp);
472     } else if(cbstrfwimatch(rp, "uidirindex:")){
473     g_uidirindex = skiplabel(rp);
474     } else if(cbstrfwimatch(rp, "uireplace:")){
475     cblistpush(g_uireplaces, skiplabel(rp), -1);
476     } else if(cbstrfwimatch(rp, "uicondattr:")){
477     cblistpush(g_uicondattrs, skiplabel(rp), -1);
478     } else if(cbstrfwimatch(rp, "uiextattr:")){
479     cblistpush(g_uiextattrs, skiplabel(rp), -1);
480     }
481     }
482     if(!g_hostname) die("hostname is undefined");
483     if(g_portnum < 1) die("portnum is undefined");
484     if(g_runmode < RM_NORMAL || g_runmode > RM_RDONLY) die("runmode is undefined");
485     if(g_authmode < AM_NONE || g_authmode > AM_ALL) die("authmode is undefined");
486     if(g_maxconn < 1) die("maxconn is undefined");
487     if(g_sessiontimeout < 1) die("sessiontimeout is undefined");
488     if(g_searchtimeout < 1) die("searchtimeout is undefined");
489     if(g_searchdepth < 0) die("searchdepth is undefined");
490     if(!g_proxyhost) die("proxyhost is undefined");
491     if(g_proxyhost[0] != '\0' && g_proxyport < 1) die("proxyport is undefined");
492     if(g_loglevel < LL_DEBUG || g_loglevel > LL_NONE) die("loglevel is undefined");
493     if(!g_docroot) die("docroot is undefined");
494     if(!g_indexfile) die("indexfile is undefined");
495     if(g_cachesize < 0) die("cachesize is undefined");
496     if(!g_specialcache) die("indexfile is undefined");
497     if(g_snipwwidth < 0) die("snipwwidth is undefined");
498     if(g_sniphwidth < 0) die("sniphwidth is undefined");
499     if(g_snipawidth < 0) die("snipawidth is undefined");
500     if(!g_uilprefix) die("uilprefix is undefined");
501     if(!g_uigprefix) die("uigprefix is undefined");
502     if(!g_uigsuffix) die("uigsuffix is undefined");
503     if(!g_uidirindex) die("uidirindex is undefined");
504     if((pid = lockerpid(g_rootdir)) > 0)
505     die("other process (pid:%d) has opened the database", pid);
506     if(g_bgmode && !be_daemon(g_rootdir)) die("the process could not be a daemon");
507     sprintf(path, "%s%c%s", g_rootdir, ESTPATHCHR, METAFILE);
508     omode = (g_runmode == RM_RDONLY ? DP_OREADER : DP_OWRITER) | DP_OLCKNB;
509     if(!(g_metadb = dpopen(path, omode, MINIBNUM))){
510     if(dpecode == DP_ELOCK){
511     die("other process (pid:%d) has opened the database", lockerpid(g_rootdir));
512     } else {
513     die("the meta database file (%s) could not open", path);
514     }
515     }
516     pid = (int)getpid();
517     sprintf(path, "%s%c%s", g_rootdir, ESTPATHCHR, PIDFILE);
518     sprintf(numbuf, "%d\n", pid);
519     cbwritefile(path, numbuf, -1);
520     if(!log_open(g_rootdir, g_loglevel)){
521     unlink(path);
522     dpclose(g_metadb);
523     die("starting logging failed");
524     }
525     log_print(LL_INFO, "starting the master process (pid:%d)", pid);
526     g_umgr = umgr_new(g_rootdir);
527     g_nmgr = nmgr_new(g_rootdir);
528     if(!umgr_load(g_umgr) || !nmgr_load(g_nmgr, g_runmode != RM_RDONLY)){
529     unlink(path);
530     nmgr_delete(g_nmgr);
531     umgr_delete(g_umgr);
532     dpclose(g_metadb);
533     log_print(LL_ERROR, "loading users and nodes failed");
534     die("loading users and nodes failed");
535     }
536     g_runlock = rwlock_new();
537     g_mgrlock = rwlock_new();
538     if((g_svsock = est_get_server_sock(NULL, g_portnum)) == -1){
539     unlink(path);
540     rwlock_delete(g_mgrlock);
541     rwlock_delete(g_runlock);
542     nmgr_delete(g_nmgr);
543     umgr_delete(g_umgr);
544     dpclose(g_metadb);
545     log_print(LL_ERROR, "initializing network failed");
546     die("initializing network failed");
547     }
548     if(g_docroot[0] != '\0'){
549     if(stat(g_docroot, &sbuf) == 0){
550     log_print(LL_INFO, "letting the directory (%s) be public", g_docroot);
551     } else {
552     log_print(LL_WARN, "missing the directory (%s)", g_docroot);
553     }
554     }
555     g_bordstr = est_border_str();
556     elems = nmgr_names(g_nmgr);
557     for(i = 0; i < cblistnum(elems); i++){
558     if(!(name = cblistval(elems, i, NULL)) || !(node = nmgr_get(g_nmgr, name))) continue;
559     est_mtdb_set_cache_size(node->db, g_cachesize * 1024 * 1024, -1, -1);
560     if(g_specialcache[0] != '\0')
561     est_mtdb_set_special_cache(node->db, g_specialcache, SPCACHEMNUM);
562     }
563     cblistclose(elems);
564     }
565    
566    
567     /* skip the label of a line */
568     static const char *skiplabel(const char *str){
569     if(!(str = strchr(str, ':'))) return "";
570     str++;
571     while(*str != '\0' && (*str == ' ' || *str == '\t')){
572     str++;
573     }
574     return str;
575     }
576    
577    
578     /* clean up resources */
579     static void cleanup(void){
580     char path[URIBUFSIZ];
581     if(g_svsock != -1) est_sock_down(g_svsock);
582     rwlock_delete(g_mgrlock);
583     rwlock_delete(g_runlock);
584     nmgr_delete(g_nmgr);
585     umgr_delete(g_umgr);
586     sprintf(path, "%s%c%s", g_rootdir, ESTPATHCHR, STOPFILE);
587     unlink(path);
588     sprintf(path, "%s%c%s", g_rootdir, ESTPATHCHR, PIDFILE);
589     unlink(path);
590     dpclose(g_metadb);
591     log_print(LL_INFO, "the master process finished");
592     }
593    
594    
595     /* perform the init command */
596     static int procinit(const char *rootdir, int ex){
597     UMGR *umgr;
598     NMGR *nmgr;
599     NODE *node;
600     ESTDOC *doc;
601     char *tmp;
602     int err;
603     setsignals();
604     if(!master_init(rootdir)){
605     log_print(LL_ERROR, "initializing the root directory failed");
606     return 1;
607     }
608     log_open(rootdir, LL_INFO);
609     log_print(LL_INFO, "the root directory created");
610     umgr = umgr_new(rootdir);
611     tmp = est_make_crypt("admin");
612     umgr_put(umgr, "admin", tmp, "s", "Carolus Magnus", "Administrator");
613     free(tmp);
614     if(ex){
615     tmp = est_make_crypt("john");
616     umgr_put(umgr, "john", tmp, "", "John Doe", "Normal User");
617     free(tmp);
618     tmp = est_make_crypt("dick");
619     umgr_put(umgr, "dick", tmp, "", "Richard Roe", "Normal User");
620     free(tmp);
621     tmp = est_make_crypt("lupin");
622     umgr_put(umgr, "lupin", tmp, "b", "Arsene Lupin", "Banned User");
623     free(tmp);
624     }
625     err = FALSE;
626     if(!umgr_delete(umgr)) err = TRUE;
627     nmgr = nmgr_new(rootdir);
628     if(ex){
629     nmgr_put(nmgr, "sample1", TRUE);
630     if((node = nmgr_get(nmgr, "sample1")) != NULL){
631     free(node->label);
632     node->label = cbmemdup("Sample Node One", -1);
633     cbmapput(node->admins, "john", -1, "", 0, TRUE);
634     cbmapput(node->users, "john", -1, "", 0, TRUE);
635     cbmapput(node->users, "dick", -1, "", 0, TRUE);
636     cbmapput(node->users, "lupin", -1, "", 0, TRUE);
637     node_set_link(node, "http://localhost:1978/node/sample2",
638     "Sample-Node-Two", 4000);
639     doc = est_doc_new();
640     est_doc_add_attr(doc, ESTDATTRURI, "http://localhost/foo.html");
641     est_doc_add_text(doc, "You may my glories and my state dispose,");
642     est_doc_add_text(doc, "But not my griefs; still am I king of those.");
643     est_doc_add_text(doc, "(Give it up, Yo! Give it up, Yo!)");
644     est_mtdb_put_doc(node->db, doc, 0);
645     est_doc_delete(doc);
646     doc = est_doc_new();
647     est_doc_add_attr(doc, ESTDATTRURI, "http://localhost/bar.html");
648     est_doc_add_text(doc, "The faster I go, the behinder I get.");
649     est_doc_add_text(doc, "(Give it up, Yo! Give it up, Yo!)");
650     est_mtdb_put_doc(node->db, doc, 0);
651     est_doc_delete(doc);
652     } else {
653     err = TRUE;
654     }
655     nmgr_put(nmgr, "sample2", TRUE);
656     if((node = nmgr_get(nmgr, "sample2")) != NULL){
657     free(node->label);
658     node->label = cbmemdup("Sample Node Two", -1);
659     cbmapput(node->users, "john", -1, "", 0, TRUE);
660     node_set_link(node, "http://localhost:1978/node/sample1",
661     "Sample-Node-One", 8000);
662     doc = est_doc_new();
663     est_doc_add_attr(doc, ESTDATTRURI, "http://localhost/foo.html");
664     est_doc_add_text(doc, "He that is giddy thinks the world turns round.");
665     est_doc_add_text(doc, "(Give it up, Yo! Give it up, Yo!)");
666     est_mtdb_put_doc(node->db, doc, 0);
667     est_doc_delete(doc);
668     } else {
669     err = TRUE;
670     }
671     }
672     if(!nmgr_delete(nmgr)) err = TRUE;
673     return err ? 1 : 0;
674     }
675    
676    
677     /* perform the start command */
678     static int procstart(const char *rootdir){
679     char *path;
680     path = est_realpath(rootdir);
681     cbglobalgc(path, free);
682     g_rootdir = path;
683     if(!est_init_net_env()) die("could not initialize network environment");
684     do {
685     setsignals();
686     startup();
687     dispatch();
688     cleanup();
689     } while(g_sigrestart);
690     est_free_net_env();
691     return 0;
692     }
693    
694    
695     /* perform the stop command */
696     static int procstop(const char *rootdir){
697     struct stat sbuf;
698     char path[URIBUFSIZ], *buf;
699     int pid;
700     sprintf(path, "%s%c%s", rootdir, ESTPATHCHR, PIDFILE);
701     if(!(buf = cbreadfile(path, NULL))) die("not running");
702     pid = atoi(buf);
703     free(buf);
704     if(pid < 1) die("not running");
705     if(est_kill(pid, 15)){
706     do {
707     est_usleep(1000 * 500);
708     if(!est_kill(pid, 13) && errno == ESRCH) break;
709     } while(TRUE);
710     } else {
711     sprintf(path, "%s%c%s", rootdir, ESTPATHCHR, STOPFILE);
712     if(!cbwritefile(path, "OH, MY GOODNESS!\n", -1)) die("killing failed");
713     do {
714     est_usleep(1000 * 500);
715     if(stat(path, &sbuf) == -1 && errno == ENOENT) break;
716     } while(TRUE);
717     }
718     return 0;
719     }
720    
721    
722     /* perform the unittest command */
723     static int procunittest(const char *rootdir){
724     UMGR *umgr;
725     USER *user;
726     NMGR *nmgr;
727     NODE *node;
728     RWLOCK *rwlock;
729     RESMAP *resmap;
730     RESDOC **resdocs;
731     ESTNODERES *nres;
732     ESTRESDOC *rdoc;
733     ESTDOC *doc;
734     CBLIST *list;
735     CBMAP *attrs;
736     const char *name, *path;
737     char *str;
738     int i, err, rnum;
739     setsignals();
740     if(procinit(rootdir, TRUE) != 0) return 1;
741     log_open(rootdir, LL_DEBUG);
742     err = FALSE;
743     umgr = umgr_new(rootdir);
744     umgr_load(umgr);
745     umgr_put(umgr, "mikio", "x", "", "Mikio Hirabayashi", "Test");
746     umgr_put(umgr, "mikio", "x", "", "Mikio Hirabayashi", "Test");
747     list = umgr_names(umgr);
748     for(i = 0; i < cblistnum(list); i++){
749     name = cblistval(list, i, NULL);
750     if(!(user = umgr_get(umgr, name))){
751     err = TRUE;
752     continue;
753     }
754     if(i % 2 == 0) user_make_sess(user);
755     if(i % 3 == 0){
756     user_set_sess_val(user, "oda", "nobunaga");
757     user_set_sess_val(user, "hashiba", "hideyoshi");
758     user_set_sess_val(user, "ieyasu", "tokugawa");
759     user_set_sess_val(user, "oda", NULL);
760     free(user_sess_val(user, "oda"));
761     free(user_sess_val(user, "hashiba"));
762     user_clear_sess(user);
763     free(user_sess_val(user, "ieyasu"));
764     }
765     }
766     cblistclose(list);
767     umgr_out(umgr, "mikio");
768     umgr_out(umgr, "hoge");
769     if(!umgr_delete(umgr)) err = TRUE;
770     nmgr = nmgr_new(rootdir);
771     nmgr_load(nmgr, TRUE);
772     nmgr_put(nmgr, "eagle", TRUE);
773     nmgr_put(nmgr, "shark", TRUE);
774     nmgr_put(nmgr, "panther", TRUE);
775     nmgr_put(nmgr, "i n v a l i d", TRUE);
776     list = nmgr_names(nmgr);
777     for(i = 0; i < cblistnum(list); i++){
778     path = cblistval(list, i, NULL);
779     if(!(node = nmgr_get(nmgr, path))){
780     err = TRUE;
781     continue;
782     }
783     doc = est_doc_new();
784     est_doc_add_attr(doc, ESTDATTRURI, "file:///home/mikio/sample1.html");
785     est_doc_add_text(doc, "Happy Hacking!");
786     if(!est_mtdb_put_doc(node->db, doc, 0)) err = TRUE;
787     est_doc_delete(doc);
788     doc = est_doc_new();
789     est_doc_add_attr(doc, ESTDATTRURI, "file:///home/mikio/sample2.txt");
790     est_doc_add_text(doc, "The savior becomes the victim.");
791     if(!est_mtdb_put_doc(node->db, doc, 0)) err = TRUE;
792     est_doc_delete(doc);
793     node_set_link(node, "http://hoge.com/node/hoge1", "a", 100);
794     node_set_link(node, "http://hoge.com/node/hoge2", "b", 200);
795     node_set_link(node, "http://hoge.com/node/hoge2", "c", 300);
796     }
797     cblistclose(list);
798     nmgr_out(nmgr, "eagle");
799     nmgr_out(nmgr, "hoge");
800     nmgr_delete(nmgr);
801     rwlock = rwlock_new();
802     if(!rwlock_lock(rwlock, TRUE)) err = TRUE;
803     if(!rwlock_unlock(rwlock)) err = TRUE;
804     if(!rwlock_lock(rwlock, FALSE)) err = TRUE;
805     if(!rwlock_unlock(rwlock)) err = TRUE;
806     rwlock_delete(rwlock);
807     resmap = resmap_new();
808     doc = est_doc_new();
809     est_doc_add_attr(doc, ESTDATTRURI, "http://tako.ika/one.html");
810     est_doc_add_text(doc, "This is a pen.");
811     resmap_put(resmap, 100, doc, NULL, NULL);
812     doc = est_doc_new();
813     est_doc_add_attr(doc, ESTDATTRURI, "http://tako.ika/two.html");
814     est_doc_add_text(doc, "Love is stranger.");
815     resmap_put(resmap, 200, doc, NULL, NULL);
816     doc = est_doc_new();
817     est_doc_add_attr(doc, ESTDATTRURI, "http://tako.ika/001.html");
818     resmap_put(resmap, 1, doc, NULL, cbmemdup("one", -1));
819     doc = est_doc_new();
820     est_doc_add_attr(doc, ESTDATTRURI, "http://tako.ika/006.html");
821     resmap_put(resmap, 6, doc, NULL, cbmemdup("six", -1));
822     doc = est_doc_new();
823     est_doc_add_attr(doc, ESTDATTRURI, "http://tako.ika/003.html");
824     resmap_put(resmap, 3, doc, NULL, cbmemdup("three", -1));
825     doc = est_doc_new();
826     est_doc_add_attr(doc, ESTDATTRURI, "http://tako.ika/004.html");
827     resmap_put(resmap, 4, doc, NULL, cbmemdup("four", -1));
828     doc = est_doc_new();
829     est_doc_add_attr(doc, ESTDATTRURI, "http://tako.ika/002.html");
830     resmap_put(resmap, 2, doc, NULL, cbmemdup("two", -1));
831     doc = est_doc_new();
832     est_doc_add_attr(doc, ESTDATTRURI, "http://tako.ika/005.html");
833     resmap_put(resmap, 1, doc, NULL, cbmemdup("five", -1));
834     resdocs = resmap_list(resmap, &rnum);
835     for(i = 0; i < rnum; i++){
836     log_print(LL_DEBUG, "result: uri=%s score=%d body=%s",
837     est_doc_attr(resdocs[i]->doc, ESTDATTRURI), resdocs[i]->score,
838     resdocs[i]->body ? resdocs[i]->body : "(null)");
839     }
840     free(resdocs);
841     resmap_delete(resmap);
842     nres = est_noderes_new();
843     for(i = 0; i < 128; i++){
844     attrs = cbmapopenex(MINIBNUM);
845     str = cbsprintf("http://big/bigger/biggest/%d", i + 1);
846     if(i % 10 != 0) cbmapput(attrs, ESTDATTRURI, -1, str, -1, FALSE);
847     free(str);
848     str = cbsprintf("This is %d\n", i + 1);
849     est_noderes_add_doc(nres, attrs, str);
850     }
851     for(i = 0; i < 100 && est_noderes_shift(nres, &attrs, &str); i++){
852     free(str);
853     cbmapclose(attrs);
854     }
855     for(i = 0; i < est_noderes_doc_num(nres); i++){
856     rdoc = est_noderes_get_doc(nres, i);
857     log_print(LL_DEBUG, "%s", est_resdoc_uri(rdoc));
858     }
859     for(i = 0; i < 2048; i++){
860     attrs = cbmapopenex(MINIBNUM);
861     str = cbsprintf("http://big/bigger/biggest/%d", i + 1);
862     if(i % 10 != 0) cbmapput(attrs, ESTDATTRURI, -1, str, -1, FALSE);
863     free(str);
864     str = cbsprintf("This is %d\n", i + 1);
865     est_noderes_add_doc(nres, attrs, str);
866     }
867     est_noderes_delete(nres);
868     return err ? 1 : 0;
869     }
870    
871    
872     /* perform the crypt command */
873     static int proccrypt(const char *key, const char *hash){
874     char *tmp;
875     if(hash){
876     if(!est_match_crypt(key, hash)){
877     fprintf(stderr, "%s and %s do not match", key, hash);
878     return 1;
879     }
880     } else {
881     tmp = est_make_crypt(key);
882     printf("%s\n", tmp);
883     free(tmp);
884     }
885     return 0;
886     }
887    
888    
889     /* listen the socket and dispatch processes */
890     static void dispatch(void){
891     pthread_t th;
892     fd_set rfds;
893     struct timeval tv;
894     struct stat sbuf;
895     char *stfile, claddr[ADDRBUFSIZ];
896     int clsock, clport, isec, cnum;
897     time_t start;
898     TARGCOMM *targ;
899     log_print(LL_INFO, "waiting for requests");
900     stfile = cbsprintf("%s%c%s", g_rootdir, ESTPATHCHR, STOPFILE);
901     isec = 0;
902     start = -1;
903     while(TRUE){
904     FD_ZERO(&rfds);
905     FD_SET(g_svsock, &rfds);
906     tv.tv_sec = 1;
907     tv.tv_usec = 0;
908     errno = 0;
909     if(select(g_svsock + 1, &rfds, NULL, NULL, &tv) > 0 && FD_ISSET(g_svsock, &rfds)){
910     clsock = est_accept_conn(g_svsock, claddr, &clport);
911     switch(clsock){
912     case -1:
913     log_print(LL_ERROR, "accepting connection failed");
914     break;
915     case 0:
916     log_print(LL_WARN, "accepting connection interrupted by a signal");
917     break;
918     default:
919     if(rwlock_rnum(g_runlock) > g_maxconn + FCLOSECONNNUM){
920     log_print(LL_WARN, "doing forced shutdown due to jam-up");
921     est_sock_down(clsock);
922     } else {
923     targ = cbmalloc(sizeof(TARGCOMM));
924     targ->clsock = clsock;
925     sprintf(targ->claddr, "%s", claddr);
926     targ->clport = clport;
927     if(g_stmode){
928     communicate(targ);
929     } else {
930     if(pthread_create(&th, NULL, communicate, targ) == 0){
931     if(pthread_detach(th) != 0) log_print(LL_ERROR, "detachting thread failed");
932     } else {
933     log_print(LL_WARN, "creating thread failed");
934     est_sock_down(clsock);
935     free(targ);
936     }
937     }
938     }
939     break;
940     }
941     isec = 0;
942     } else {
943     if(errno != EINTR) isec++;
944     if(stat(stfile, &sbuf) == 0){
945     log_print(LL_INFO, "the stop file detected");
946     g_sigterm = TRUE;
947     }
948     }
949     if(g_sigterm){
950     if(rwlock_rnum(g_runlock) < 1){
951     break;
952     } else if(start == -1){
953     start = time(NULL);
954     } else if(time(NULL) - start > 1){
955     break;
956     }
957     }
958     if(isec >= IFLUSHSEC && g_runmode != RM_RDONLY){
959     if(g_stmode){
960     flushnode(NULL);
961     } else {
962     if(pthread_create(&th, NULL, flushnode, NULL) == 0){
963     if(pthread_detach(th) != 0) log_print(LL_ERROR, "detachting thread failed");
964     } else {
965     log_print(LL_WARN, "creating thread failed");
966     }
967     }
968     }
969     }
970     free(stfile);
971     log_print(LL_INFO, "shutting down");
972     start = time(NULL);
973     while((cnum = rwlock_rnum(g_runlock)) > 0){
974     log_print(LL_INFO, "waiting for children: %d", cnum);
975     est_usleep(1000 * 1000);
976     if(time(NULL) - start > FCLOSEWAITMAX && g_svsock != -1){
977     est_sock_down(g_svsock);
978     g_svsock = -1;
979     log_print(LL_WARN, "doing forced shutdown of the server socket");
980     }
981     if(time(NULL) - start > FTERMWAITMAX){
982     log_print(LL_WARN, "doing forced termination of the server process");
983     break;
984     }
985     }
986     est_usleep(1000 * 100);
987     }
988    
989    
990     /* flush one of nodes */
991     static void *flushnode(void *targ){
992     static pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
993     static int cnt = 0;
994     CBLIST *list;
995     NODE *node;
996     if(pthread_mutex_trylock(&mymutex) != 0) return NULL;
997     if(!rwlock_lock(g_runlock, FALSE)){
998     log_print(LL_ERROR, "locking failed");
999     pthread_mutex_unlock(&mymutex);
1000     return NULL;
1001     }
1002     if(!rwlock_lock(g_mgrlock, FALSE)){
1003     rwlock_unlock(g_runlock);
1004     pthread_mutex_unlock(&mymutex);
1005     log_print(LL_ERROR, "locking failed");
1006     return NULL;
1007     }
1008     list = nmgr_names(g_nmgr);
1009     if(cblistnum(list) > 0){
1010     if((node = nmgr_get(g_nmgr, cblistval(list, cnt++ % cblistnum(list), NULL))) != NULL){
1011     if(!est_mtdb_flush(node->db, est_mtdb_cache_num(node->db) > 0 ? IFLUSHNUM : -1))
1012     log_print(LL_ERROR, "flushing failed");
1013     }
1014     }
1015     cblistclose(list);
1016     if(!rwlock_unlock(g_mgrlock)) log_print(LL_ERROR, "unlocking failed");
1017     if(!rwlock_unlock(g_runlock)) log_print(LL_ERROR, "unlocking failed");
1018     if(pthread_mutex_unlock(&mymutex) != 0) log_print(LL_ERROR, "unlocking failed");
1019     return NULL;
1020     }
1021    
1022    
1023     /* communicate with a client */
1024     static void *communicate(void *targ){
1025     TARGCOMM *myarg;
1026     REQUEST req;
1027     USER *user;
1028     const char *cstr;
1029     char ibuf[IOBUFSIZ], *tmp, *bp, *ep;
1030     int clsock, len, clen;
1031     myarg = (TARGCOMM *)targ;
1032     clsock = myarg->clsock;
1033     if(!rwlock_lock(g_runlock, FALSE)){
1034     est_sock_down(myarg->clsock);
1035     free(myarg);
1036     log_print(LL_ERROR, "locking failed");
1037     return NULL;
1038     }
1039     est_sock_recv_line(clsock, ibuf, IOBUFSIZ - 1);
1040     log_print(LL_INFO, "[%s:%d]: %s", myarg->claddr, myarg->clport, ibuf);
1041     if(cbstrfwmatch(ibuf, "HEAD ")){
1042     req.method = HM_HEAD;
1043     } else if(cbstrfwmatch(ibuf, "GET ")){
1044     req.method = HM_GET;
1045     } else if(cbstrfwmatch(ibuf, "POST ")){
1046     req.method = HM_POST;
1047     } else {
1048     req.method = HM_UNKNOWN;
1049     }
1050     req.claddr = myarg->claddr;
1051     req.clport = myarg->clport;
1052     req.target = NULL;
1053     req.name = NULL;
1054     req.passwd = NULL;
1055     req.ims = -1;
1056     req.ctype = NULL;
1057     req.estvia = NULL;
1058     req.host = NULL;
1059     req.body = NULL;
1060     req.params = cbmapopenex(MINIBNUM);
1061     if((bp = strchr(ibuf, ' ')) != NULL){
1062     while(*bp == ' '){
1063     bp++;
1064     }
1065     while(bp[0] == '/' && bp[1] == '/'){
1066     bp++;
1067     }
1068     if((ep = strchr(bp, ' ')) != NULL){
1069     req.target = cbmemdup(bp, ep - bp);
1070     } else {
1071     req.target = cbmemdup(bp, -1);
1072     }
1073     if((bp = strchr(req.target, '?')) != NULL){
1074     *(bp++) = '\0';
1075     setparams(req.params, bp);
1076     }
1077     }
1078     if(!req.target) req.target = cbmemdup("/", 1);
1079     clen = -1;
1080     do {
1081     len = est_sock_recv_line(clsock, ibuf, IOBUFSIZ - 1);
1082     if(len > 0) log_print(LL_DEBUG, "[%s:%d]: + %s", myarg->claddr, myarg->clport, ibuf);
1083     if(cbstrfwimatch(ibuf, "Content-Length:")){
1084     clen = atoi(skiplabel(ibuf));
1085     } else if(cbstrfwimatch(ibuf, "Content-Type:")){
1086     req.ctype = cbmemdup(skiplabel(ibuf), -1);
1087     } else if(cbstrfwimatch(ibuf, "X-Estraier-Via:")){
1088     if(req.estvia){
1089     tmp = cbsprintf("%s, %s", req.estvia, skiplabel(ibuf));
1090     free(req.estvia);
1091     req.estvia = tmp;
1092     } else {
1093     req.estvia = cbmemdup(skiplabel(ibuf), -1);
1094     }
1095     } else if(cbstrfwimatch(ibuf, "Host:")){
1096     req.host = cbmemdup(skiplabel(ibuf), -1);
1097     } else if(cbstrfwimatch(ibuf, "Authorization:")){
1098     cstr = skiplabel(ibuf);
1099     if(cbstrfwimatch(cstr, "basic") && (cstr = strchr(cstr, ' ')) != NULL){
1100     while(*cstr == ' '){
1101     cstr++;
1102     }
1103     bp = cbbasedecode(cstr, NULL);
1104     if((ep = strchr(bp, ':')) != NULL) *(ep++) = '\0';
1105     req.name = cbmemdup(bp, -1);
1106     req.passwd = cbmemdup(ep ? ep : "", -1);
1107     free(bp);
1108     }
1109     } else if(cbstrfwimatch(ibuf, "If-Modified-Since")){
1110     req.ims = cbstrmktime(skiplabel(ibuf));
1111     }
1112     } while(len > 0);
1113     if(clen > RECVMAX) clen = RECVMAX;
1114     if(req.method == HM_POST && clen > 0){
1115     req.body = est_sock_recv_all(clsock, clen);
1116     if(req.ctype && cbstrfwimatch(req.ctype, ESTFORMTYPE)) setparams(req.params, req.body);
1117     est_sock_recv_void(clsock);
1118     }
1119     user = NULL;
1120     if(rwlock_lock(g_mgrlock, FALSE)){
1121     if(req.name) user = umgr_get(g_umgr, req.name);
1122     if(user && !est_match_crypt(req.passwd, user->passwd)) user = NULL;
1123     if(!rwlock_unlock(g_mgrlock)) log_print(LL_ERROR, "unlocking failed");
1124     } else {
1125     log_print(LL_ERROR, "locking failed");
1126     }
1127     if(g_sigterm){
1128     senderror(clsock, &req, 503, "Service Unavailable (shutting down)");
1129     } else if(rwlock_rnum(g_runlock) > g_maxconn){
1130     senderror(clsock, &req, 503, "Service Unavailable (too busy)");
1131     } else if(req.method == HM_UNKNOWN){
1132     senderror(clsock, &req, 501, "Not Implemented");
1133     } else if(req.method == HM_POST && clen < 0){
1134     senderror(clsock, &req, 411, "Length Required");
1135     } else if(req.method == HM_POST && clen >= RECVMAX){
1136     senderror(clsock, &req, 413, "Request Entity Too Large");
1137     } else if(req.target[0] != '/' || strstr(req.target, "/../")){
1138     senderror(clsock, &req, 400, "Bad Request");
1139     } else if(g_denyuntrusted && !cbmapget(g_trustednodes, req.claddr, -1, NULL)){
1140     senderror(clsock, &req, 403, "Forbidden");
1141     } else if(!strcmp(req.target, MASTERLOC)){
1142     sendmasterdata(clsock, &req, user);
1143     } else if(cbstrfwmatch(req.target, NODEPREFIX)){
1144     sendnodedata(clsock, &req, user, req.target + strlen(NODEPREFIX));
1145     } else if(!strcmp(req.target, MASTERUILOC)){
1146     sendmasteruidata(clsock, &req, user);
1147     } else {
1148     sendfiledata(clsock, &req);
1149     }
1150     cbmapclose(req.params);
1151     free(req.body);
1152     free(req.host);
1153     free(req.estvia);
1154     free(req.ctype);
1155     free(req.name);
1156     free(req.passwd);
1157     free(req.target);
1158     est_sock_down(myarg->clsock);
1159     free(myarg);
1160     if(!rwlock_unlock(g_runlock)) log_print(LL_ERROR, "unlocking failed");
1161     return NULL;
1162     }
1163    
1164    
1165     /* set parameters into a map object */
1166     static void setparams(CBMAP *params, const char *src){
1167     CBLIST *pairs;
1168     char *key, *val, *dkey, *dval;
1169     int i;
1170     pairs = cbsplit(src, -1, "&");
1171     for(i = 0; i < cblistnum(pairs); i++){
1172     key = cbmemdup(cblistval(pairs, i, NULL), -1);
1173     if((val = strchr(key, '=')) != NULL){
1174     *(val++) = '\0';
1175     dkey = cburldecode(key, NULL);
1176     dval = cburldecode(val, NULL);
1177     cbmapput(params, dkey, -1, dval, -1, FALSE);
1178     free(dval);
1179     free(dkey);
1180     }
1181     free(key);
1182     }
1183     cblistclose(pairs);
1184     }
1185    
1186    
1187     /* add the server information to a datum object */
1188     static void addservinfo(CBDATUM *datum){
1189     char *tmp;
1190     tmp = cbdatestrhttp(time(NULL), 0);
1191     est_datum_printf(datum, "Date: %s\r\n", tmp);
1192     free(tmp);
1193     est_datum_printf(datum, "Server: %s/%s\r\n", SERVNAME, est_version);
1194     est_datum_printf(datum, "Cache-Control: no-cache, must-revalidate, no-transform\r\n");
1195     est_datum_printf(datum, "Pragma: no-cache\r\n");
1196     est_datum_printf(datum, "Connection: close\r\n");
1197     }
1198    
1199    
1200     /* send error message */
1201     static void senderror(int clsock, REQUEST *req, int code, const char *message){
1202     CBDATUM *datum;
1203     datum = cbdatumopen("", 0);
1204     est_datum_printf(datum, "HTTP/1.0 %d %s\r\n", code, message);
1205     addservinfo(datum);
1206     est_datum_printf(datum, "Content-Type: text/plain; charset=UTF-8\r\n");
1207     est_datum_printf(datum, "\r\n");
1208     if(req->method != HM_HEAD && code != 304) est_datum_printf(datum, "%s\n", message);
1209     est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum));
1210     cbdatumclose(datum);
1211     log_print(LL_WARN, "[%s:%d]: %d %s", req->claddr, req->clport, code, message);
1212     }
1213    
1214    
1215     /* send authenticate error message */
1216     static void sendautherror(int clsock, REQUEST *req, const char *realm){
1217     CBDATUM *datum;
1218     datum = cbdatumopen("", 0);
1219     est_datum_printf(datum, "HTTP/1.0 401 Unauthorized\r\n");
1220     addservinfo(datum);
1221     est_datum_printf(datum, "Content-Type: text/plain; charset=UTF-8\r\n");
1222     est_datum_printf(datum, "WWW-Authenticate: Basic realm=\"%s\"\r\n", realm);
1223     est_datum_printf(datum, "\r\n");
1224     if(req->method != HM_HEAD) est_datum_printf(datum, "Unauthorized\n");
1225     est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum));
1226     cbdatumclose(datum);
1227     log_print(LL_WARN, "[%s:%d]: 401 Unauthorized", req->claddr, req->clport);
1228     }
1229    
1230    
1231     /* check whether a user is banned */
1232     static int isbanned(USER *user){
1233     if(user && strchr(user->flags, 'b')) return TRUE;
1234     return FALSE;
1235     }
1236    
1237    
1238     /* check whether a user is an administrator of the master */
1239     static int ismasteradmin(REQUEST *req, USER *user){
1240     if(isbanned(user)) return FALSE;
1241     if(cbmapget(g_trustednodes, req->claddr, -1, NULL)) return TRUE;
1242     if(user && strchr(user->flags, 's')) return TRUE;
1243     return FALSE;
1244     }
1245    
1246    
1247     /* check whether a user is an administrator of a node */
1248     static int isnodeadmin(NODE *node, REQUEST *req, USER *user){
1249     if(isbanned(user)) return FALSE;
1250     if(ismasteradmin(req, user)) return TRUE;
1251     if(g_authmode == AM_NONE) return TRUE;
1252     if(user && cbmapget(node->admins, user->name, -1, NULL)) return TRUE;
1253     return FALSE;
1254     }
1255    
1256    
1257     /* check whether a user is a user of a node */
1258     static int isnodeuser(NODE *node, REQUEST *req, USER *user){
1259     if(isbanned(user)) return FALSE;
1260     if(isnodeadmin(node, req, user)) return TRUE;
1261     if(g_authmode == AM_NONE || g_authmode == AM_ADMIN) return TRUE;
1262     if(user && cbmapget(node->users, user->name, -1, NULL)) return TRUE;
1263     return FALSE;
1264     }
1265    
1266    
1267     /* send the master data */
1268     static void sendmasterdata(int clsock, REQUEST *req, USER *user){
1269     CBDATUM *datum;
1270     CBLIST *list;
1271     USER *tuser;
1272     NODE *tnode;
1273     const char *act, *name, *passwd, *flags, *fname, *misc, *label;
1274     char *tmp;
1275     int i;
1276     if(!rwlock_lock(g_mgrlock, TRUE)){
1277     log_print(LL_ERROR, "locking failed");
1278     senderror(clsock, req, 500, "Internal Server Error (locking failed)");
1279     return;
1280     }
1281     if(!ismasteradmin(req, user)){
1282     sendautherror(clsock, req, "Super User");
1283     if(!rwlock_unlock(g_mgrlock)) log_print(LL_ERROR, "unlocking failed");
1284     return;
1285     }
1286     if(!(act = cbmapget(req->params, "action", -1, NULL))) act = "";
1287     if(!(name = cbmapget(req->params, "name", -1, NULL))) name = "";
1288     if(!(passwd = cbmapget(req->params, "passwd", -1, NULL))) passwd = "";
1289     if(!(flags = cbmapget(req->params, "flags", -1, NULL))) flags = "";
1290     if(!(fname = cbmapget(req->params, "fname", -1, NULL))) fname = "";
1291     if(!(misc = cbmapget(req->params, "misc", -1, NULL))) misc = "";
1292     if(!(label = cbmapget(req->params, "label", -1, NULL))) label = "";
1293     if(!strcmp(act, "shutdown")){
1294     datum = cbdatumopen("", 0);
1295     est_datum_printf(datum, "HTTP/1.0 202 Accepted\r\n");
1296     addservinfo(datum);
1297     est_datum_printf(datum, "Content-Type: text/plain; charset=UTF-8\r\n");
1298     est_datum_printf(datum, "\r\n");
1299     est_datum_printf(datum, "Accepted\n");
1300     est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum));
1301     g_sigterm = TRUE;
1302     cbdatumclose(datum);
1303     log_print(LL_DEBUG, "[%s:%d]: 202 (accepted - shutdown)", req->claddr, req->clport);
1304     } else if(!strcmp(act, "userlist")){
1305     datum = cbdatumopen("", 0);
1306     est_datum_printf(datum, "HTTP/1.0 200 OK\r\n");
1307     addservinfo(datum);
1308     est_datum_printf(datum, "Content-Type: text/plain; charset=UTF-8\r\n");
1309     est_datum_printf(datum, "\r\n");
1310     list = umgr_names(g_umgr);
1311     for(i = 0; i < cblistnum(list); i++){
1312     if(!(name = cblistval(list, i, NULL)) || !(tuser = umgr_get(g_umgr, name))) continue;
1313     est_datum_printf(datum, "%s\t%s\t%s\t%s\t%s\n",
1314     tuser->name, tuser->passwd, tuser->flags, tuser->fname, tuser->misc);
1315     }
1316     cblistclose(list);
1317     est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum));
1318     cbdatumclose(datum);
1319     log_print(LL_DEBUG, "[%s:%d]: 200 (ok - userlist)", req->claddr, req->clport);
1320     } else if(!strcmp(act, "useradd") && name[0] != '\0' && passwd[0] != '\0'){
1321     tmp = est_make_crypt(passwd);
1322     if(g_runmode == RM_RDONLY){
1323     senderror(clsock, req, 503, "Service Unavailable (read only)");
1324     } else if(umgr_put(g_umgr, name, tmp, flags, fname, misc)){
1325     datum = cbdatumopen("", 0);
1326     est_datum_printf(datum, "HTTP/1.0 200 OK\r\n");
1327     addservinfo(datum);
1328     est_datum_printf(datum, "Content-Type: text/plain; charset=UTF-8\r\n");
1329     est_datum_printf(datum, "\r\n");
1330     est_datum_printf(datum, "OK\n");
1331     est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum));
1332     cbdatumclose(datum);
1333     log_print(LL_DEBUG, "[%s:%d]: 200 (ok - useradd)", req->claddr, req->clport);
1334     } else {
1335     senderror(clsock, req, 400, "Bad Request (maybe, the user already exists)");
1336     }
1337     free(tmp);
1338     } else if(!strcmp(act, "userdel") && name[0] != '\0'){
1339     if(g_runmode == RM_RDONLY){
1340     senderror(clsock, req, 503, "Service Unavailable (read only)");
1341     } else if(umgr_out(g_umgr, name)){
1342     datum = cbdatumopen("", 0);
1343     est_datum_printf(datum, "HTTP/1.0 200 OK\r\n");
1344     addservinfo(datum);
1345     est_datum_printf(datum, "Content-Type: text/plain; charset=UTF-8\r\n");
1346     est_datum_printf(datum, "\r\n");
1347     est_datum_printf(datum, "OK\n");
1348     est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum));
1349     cbdatumclose(datum);
1350     log_print(LL_DEBUG, "[%s:%d]: 200 (ok - userdel)", req->claddr, req->clport);
1351     } else {
1352     senderror(clsock, req, 400, "Bad Request (maybe, the user does not exist)");
1353     }
1354     } else if(!strcmp(act, "nodelist")){
1355     datum = cbdatumopen("", 0);
1356     est_datum_printf(datum, "HTTP/1.0 200 OK\r\n");
1357     addservinfo(datum);
1358     est_datum_printf(datum, "Content-Type: text/plain; charset=UTF-8\r\n");
1359     est_datum_printf(datum, "\r\n");
1360     list = nmgr_names(g_nmgr);
1361     for(i = 0; i < cblistnum(list); i++){
1362     if(!(name = cblistval(list, i, NULL)) || !(tnode = nmgr_get(g_nmgr, name))) continue;
1363     est_datum_printf(datum, "%s\t%s\t%d\t%d\t%.0f\n", tnode->name, tnode->label,
1364     est_mtdb_doc_num(tnode->db), est_mtdb_word_num(tnode->db),
1365     est_mtdb_size(tnode->db));
1366     }
1367     cblistclose(list);
1368     est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum));
1369     cbdatumclose(datum);
1370     log_print(LL_DEBUG, "[%s:%d]: 200 (ok - nodelist)", req->claddr, req->clport);
1371     } else if(!strcmp(act, "nodeadd") && name[0] != '\0' && label[0] != '\0'){
1372     if(g_runmode == RM_RDONLY){
1373     senderror(clsock, req, 503, "Service Unavailable (read only)");
1374     } else if(nmgr_put(g_nmgr, name, TRUE)){
1375     if((tnode = nmgr_get(g_nmgr, name)) != NULL){
1376     free(tnode->label);
1377     tnode->label = cbmemdup(label, -1);
1378     }
1379     nmgr_sync(g_nmgr, FALSE);
1380     datum = cbdatumopen("", 0);
1381     est_datum_printf(datum, "HTTP/1.0 200 OK\r\n");
1382     addservinfo(datum);
1383     est_datum_printf(datum, "Content-Type: text/plain; charset=UTF-8\r\n");
1384     est_datum_printf(datum, "\r\n");
1385     est_datum_printf(datum, "OK\n");
1386     est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum));
1387     cbdatumclose(datum);
1388     log_print(LL_DEBUG, "[%s:%d]: 200 (ok - nodeadd)", req->claddr, req->clport);
1389     } else {
1390     senderror(clsock, req, 400, "Bad Request (maybe, the node already exists)");
1391     }
1392     } else if(!strcmp(act, "nodedel") && name[0] != '\0'){
1393     if(g_runmode == RM_RDONLY){
1394     senderror(clsock, req, 503, "Service Unavailable (read only)");
1395     } else if(nmgr_out(g_nmgr, name)){
1396     datum = cbdatumopen("", 0);
1397     est_datum_printf(datum, "HTTP/1.0 200 OK\r\n");
1398     addservinfo(datum);
1399     est_datum_printf(datum, "Content-Type: text/plain; charset=UTF-8\r\n");
1400     est_datum_printf(datum, "\r\n");
1401     est_datum_printf(datum, "OK\n");
1402     est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum));
1403     cbdatumclose(datum);
1404     log_print(LL_DEBUG, "[%s:%d]: 200 (ok - nodedel)", req->claddr, req->clport);
1405     } else {
1406     senderror(clsock, req, 400, "Bad Request (maybe, the node does not exist)");
1407     }
1408     } else {
1409     senderror(clsock, req, 400, "Bad Request (the action is invalid or lack of parameters)");
1410     }
1411     if(!rwlock_unlock(g_mgrlock)) log_print(LL_ERROR, "unlocking failed");
1412     }
1413    
1414    
1415     /* send a node data */
1416     static void sendnodedata(int clsock, REQUEST *req, USER *user, const char *path){
1417     CBDATUM *datum;
1418     CBLIST *list;
1419     NODE *node;
1420     const char *cmd;
1421     char *pbuf, *pv;
1422     int i;
1423     pbuf = cbmemdup(path, -1);
1424     if((pv = strchr(path, '/')) != NULL){
1425     *pv = '\0';
1426     cmd = pv + 1;
1427     } else {
1428     cmd = "";
1429     }
1430     while(*cmd == '/'){
1431     cmd++;
1432     }
1433     if(!rwlock_lock(g_mgrlock, cmd[0] == '_')){
1434     log_print(LL_ERROR, "locking failed");
1435     senderror(clsock, req, 500, "Internal Server Error (locking failed)");
1436     free(pbuf);
1437     return;
1438     }
1439     if(!(node = nmgr_get(g_nmgr, path))){
1440     if(!strcmp(req->target, NODEPREFIX)){
1441     datum = cbdatumopen("", 0);
1442     est_datum_printf(datum, "HTTP/1.0 200 OK\r\n");
1443     addservinfo(datum);
1444     est_datum_printf(datum, "Content-Type: text/plain; charset=UTF-8\r\n");
1445     est_datum_printf(datum, "\r\n");
1446     if(req->method != HM_HEAD){
1447     list = nmgr_names(g_nmgr);
1448     for(i = 0; i < cblistnum(list); i++){
1449     est_datum_printf(datum, "%s\n", cblistval(list, i, NULL));
1450     }
1451     cblistclose(list);
1452     }
1453     est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum));
1454     cbdatumclose(datum);
1455     } else {
1456     senderror(clsock, req, 404, "Not Found (the node does not exist)");
1457     }
1458     } else if(!strcmp(cmd, "inform")){
1459     if(isnodeuser(node, req, user)){
1460     sendnodecmdinform(clsock, req, node);
1461     } else {
1462     sendautherror(clsock, req, node->name);
1463     }
1464     } else if(!strcmp(cmd, "search")){
1465     if(isnodeuser(node, req, user)){
1466     sendnodecmdsearch(clsock, req, node);
1467     } else {
1468     sendautherror(clsock, req, node->name);
1469     }
1470     } else if(!strcmp(cmd, "get_doc")){
1471     if(isnodeuser(node, req, user)){
1472     sendnodecmdgetdoc(clsock, req, node);
1473     } else {
1474     sendautherror(clsock, req, node->name);
1475     }
1476     } else if(!strcmp(cmd, "get_doc_attr")){
1477     if(isnodeuser(node, req, user)){
1478     sendnodecmdgetdocattr(clsock, req, node);
1479     } else {
1480     sendautherror(clsock, req, node->name);
1481     }
1482     } else if(!strcmp(cmd, "uri_to_id")){
1483     if(isnodeuser(node, req, user)){
1484     sendnodecmduritoid(clsock, req, node);
1485     } else {
1486     sendautherror(clsock, req, node->name);
1487     }
1488     } else if(!strcmp(cmd, "put_doc")){
1489     if(g_runmode == RM_RDONLY){
1490     senderror(clsock, req, 503, "Service Unavailable (read only)");
1491     } else if(isnodeadmin(node, req, user)){
1492     sendnodecmdputdoc(clsock, req, node);
1493     } else {
1494     sendautherror(clsock, req, node->name);
1495     }
1496     } else if(!strcmp(cmd, "out_doc")){
1497     if(g_runmode == RM_RDONLY){
1498     senderror(clsock, req, 503, "Service Unavailable (read only)");
1499     } else if(isnodeadmin(node, req, user)){
1500     sendnodecmdoutdoc(clsock, req, node);
1501     } else {
1502     sendautherror(clsock, req, node->name);
1503     }
1504     } else if(!strcmp(cmd, "_set_user")){
1505     if(g_runmode == RM_RDONLY){
1506     senderror(clsock, req, 503, "Service Unavailable (read only)");
1507     } else if(isnodeadmin(node, req, user)){
1508     sendnodecmdsetuser(clsock, req, node);
1509     } else {
1510     sendautherror(clsock, req, node->name);
1511     }
1512     } else if(!strcmp(cmd, "_set_link")){
1513     if(g_runmode == RM_RDONLY){
1514     senderror(clsock, req, 503, "Service Unavailable (read only)");
1515     } else if(isnodeadmin(node, req, user)){
1516     sendnodecmdsetlink(clsock, req, node);
1517     } else {
1518     sendautherror(clsock, req, node->name);
1519     }
1520     } else if(!strcmp(cmd, "searchui")){
1521     if(isnodeuser(node, req, user)){
1522     sendnodecmdsearchui(clsock, req, node);
1523     } else {
1524     sendautherror(clsock, req, node->name);
1525     }
1526     } else {
1527     senderror(clsock, req, 400, "Bad Request (the command is invalid)");
1528     }
1529     free(pbuf);
1530     if(!rwlock_unlock(g_mgrlock)) log_print(LL_ERROR, "unlocking failed");
1531     }
1532    
1533    
1534     /* set the pseudo-attribute of the original node to a document */
1535     static void setdocorigin(ESTDOC *doc, NODE *node){
1536     char pbuf[URIBUFSIZ];
1537     sprintf(pbuf, "http://%s:%d%s%s", g_hostname, g_portnum, NODEPREFIX, node->name);
1538     est_doc_add_attr(doc, DATTRNDURL, pbuf);
1539     est_doc_add_attr(doc, DATTRNDLABEL, node->label);
1540     }
1541    
1542    
1543     /* merge local hints to total hints */
1544     static void mergehints(CBMAP *total, CBMAP *local){
1545     static pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
1546     const char *kbuf, *vbuf;
1547     char numbuf[NUMBUFSIZ];
1548     int ksiz, num, vsiz;
1549     if(pthread_mutex_lock(&mymutex) != 0) return;
1550     cbmapiterinit(local);
1551     while((kbuf = cbmapiternext(local, &ksiz)) != NULL){
1552     num = atoi(cbmapget(local, kbuf, ksiz, NULL));
1553     if((vbuf = cbmapget(total, kbuf, ksiz, NULL)) != NULL){
1554     num += atoi(vbuf);
1555     }
1556     vsiz = sprintf(numbuf, "%d", num);
1557     cbmapput(total, kbuf, ksiz, numbuf, vsiz, TRUE);
1558     }
1559     pthread_mutex_unlock(&mymutex);
1560     }
1561    
1562    
1563     /* check whether the request is looping */
1564     static int islooproute(const char *url, REQUEST *req){
1565     CBLIST *list;
1566     const char *rp;
1567     int i;
1568     assert(url && req);
1569     if(!req->estvia) return FALSE;
1570     list = cbsplit(req->estvia, -1, ",");
1571     for(i = 0; i < cblistnum(list); i++){
1572     rp = cblistval(list, i, NULL);
1573     while(*rp == ' ' || *rp == '\t'){
1574     rp++;
1575     }
1576     if(!strcmp(url, rp)){
1577     cblistclose(list);
1578     return TRUE;
1579     }
1580     }
1581     cblistclose(list);
1582     return FALSE;
1583     }
1584    
1585    
1586     /* search a local index */
1587     static void *searchlocal(void *targ){
1588     REQUEST *req;
1589     ESTCOND *cond;
1590     ESTDOC *doc;
1591     NODE *node;
1592     RESMAP *resmap;
1593     CBMAP *hints, *myhints;
1594     CBLIST *words;
1595     const char *kbuf;
1596     int i, max, *res, rnum, cnt, ksiz;
1597     req = ((TARGLCSRCH *)targ)->req;
1598     cond = ((TARGLCSRCH *)targ)->cond;
1599     node = ((TARGLCSRCH *)targ)->node;
1600     hints = ((TARGLCSRCH *)targ)->hints;
1601     max = ((TARGLCSRCH *)targ)->max;
1602     resmap = ((TARGLCSRCH *)targ)->resmap;
1603     words = ((TARGLCSRCH *)targ)->words;
1604     log_print(LL_DEBUG, "[%s:%d]: local search: %s",
1605     req->claddr, req->clport, est_mtdb_name(node->db));
1606     myhints = cbmapopenex(MINIBNUM);
1607     res = est_mtdb_search(node->db, cond, &rnum, myhints);
1608     mergehints(hints, myhints);
1609     cnt = 0;
1610     for(i = 0; i < rnum && cnt < max; i++){
1611     if(!(doc = est_mtdb_get_doc(node->db, res[i], 0))) continue;
1612     setdocorigin(doc, node);
1613     resmap_put(resmap, SELFCREDIT * (max - cnt), doc, NULL, NULL);
1614     cnt++;
1615     }
1616     free(res);
1617     ((TARGLCSRCH *)targ)->hnum = atoi(cbmapget(myhints, "", 0, NULL));
1618     cbmapiterinit(myhints);
1619     while((kbuf = cbmapiternext(myhints, &ksiz)) != NULL){
1620     if(ksiz > 0) cblistpush(words, kbuf, ksiz);
1621     }
1622     cbmapclose(myhints);
1623     return NULL;
1624     }
1625    
1626    
1627     /* search a remote index */
1628     static void *searchremote(void *targ){
1629     REQUEST *req;
1630     ESTCOND *cond;
1631     NODE *node;
1632     RESMAP *resmap;
1633     ESTNODE *tnode;
1634     ESTNODERES *nres;
1635     CBMAP *hints, *attrs, *myhints, *whints;
1636     CBLIST *list;
1637     const char *myurl, *url, *label, *vbuf, *pv;
1638     char numbuf[NUMBUFSIZ], *snippet;
1639     int i, max, credit, depth, len, cnt;
1640     req = ((TARGRMSRCH *)targ)->req;
1641     myurl = ((TARGRMSRCH *)targ)->myurl;
1642     cond = ((TARGRMSRCH *)targ)->cond;
1643     node = ((TARGRMSRCH *)targ)->node;
1644     hints = ((TARGRMSRCH *)targ)->hints;
1645     max = ((TARGRMSRCH *)targ)->max;
1646     resmap = ((TARGRMSRCH *)targ)->resmap;
1647     url = ((TARGRMSRCH *)targ)->url;
1648     label = ((TARGRMSRCH *)targ)->label;
1649     credit = ((TARGRMSRCH *)targ)->credit;
1650     depth = ((TARGRMSRCH *)targ)->depth;
1651     if(!strcmp(myurl, url) || islooproute(url, req)){
1652     log_print(LL_DEBUG, "[%s:%d]: omitting request loop (link): %s",
1653     req->claddr, req->clport, url);
1654     return NULL;
1655     }
1656     log_print(LL_DEBUG, "[%s:%d]: remote search: %s", req->claddr, req->clport, url);
1657     tnode = est_node_new(url);
1658     if(g_proxyhost[0] != '\0') est_node_set_proxy(tnode, g_proxyhost, g_proxyport);
1659     est_node_set_timeout(tnode, g_searchtimeout);
1660     if(req->estvia) est_node_add_header(tnode, ESTHTHVIA, req->estvia);
1661     est_node_add_header(tnode, ESTHTHVIA, myurl);
1662     if((nres = est_node_search(tnode, cond, depth - 1)) != NULL){
1663     cnt = 0;
1664     while(est_noderes_shift(nres, &attrs, &snippet)){
1665     resmap_put(resmap, credit * (max - cnt), NULL, attrs, snippet);
1666     cnt++;
1667     }
1668     whints = cbmapopenex(MINIBNUM);
1669     myhints = est_noderes_hints(nres);
1670     if((vbuf = cbmapget(myhints, "HIT", -1, NULL)) != NULL){
1671     cbmapput(whints, "", 0, vbuf, -1, FALSE);
1672     ((TARGRMSRCH *)targ)->hnum = atoi(vbuf);
1673     } else {
1674     cbmapput(whints, "", 0, "0", -1, FALSE);
1675     }
1676     if((vbuf = cbmapget(myhints, "DOCNUM", -1, NULL)) != NULL)
1677     ((TARGRMSRCH *)targ)->dnum = atoi(vbuf);
1678     if((vbuf = cbmapget(myhints, "WORDNUM", -1, NULL)) != NULL)
1679     ((TARGRMSRCH *)targ)->wnum = atoi(vbuf);
1680     if((vbuf = cbmapget(myhints, "LINK#0", -1, NULL)) != NULL){
1681     list = cbsplit(vbuf, -1, "\t");
1682     if(cblistnum(list) == 7)
1683     ((TARGRMSRCH *)targ)->size = strtod(cblistval(list, 5, NULL), NULL);
1684     cblistclose(list);
1685     }
1686     for(i = 0; i < cbmaprnum(myhints); i++){
1687     len = sprintf(numbuf, "HINT#%d", i + 1);
1688     if((vbuf = cbmapget(myhints, numbuf, len, NULL)) != NULL &&
1689     (pv = strchr(vbuf, '\t')) != NULL && pv > vbuf){
1690     cbmapput(whints, vbuf, pv - vbuf, pv + 1, -1, FALSE);
1691     } else {
1692     break;
1693     }
1694     }
1695     mergehints(hints, whints);
1696     cbmapclose(whints);
1697     est_noderes_delete(nres);
1698     } else {
1699     log_print(LL_WARN, "[%s:%d]: connection failed: %s", req->claddr, req->clport, url);
1700     }
1701     est_node_delete(tnode);
1702     return NULL;
1703     }
1704    
1705    
1706     /* concatenate a document data to an output buffer */
1707     static void catdocdata(CBDATUM *datum, RESDOC *resdoc, CBLIST *words){
1708     CBLIST *list;
1709     const char *vbuf;
1710     char *snippet;
1711     int i;
1712     if(resdoc->doc){
1713     list = est_doc_attr_names(resdoc->doc);
1714     for(i = 0; i < cblistnum(list); i++){
1715     vbuf = cblistval(list, i, NULL);
1716     est_datum_printf(datum, "%s=%s\n", vbuf, est_doc_attr(resdoc->doc, vbuf));
1717     }
1718     cblistclose(list);
1719     } else if(resdoc->attrs){
1720     list = cbmapkeys(resdoc->attrs);
1721     cblistsort(list);
1722     for(i = 0; i < cblistnum(list); i++){
1723     vbuf = cblistval(list, i, NULL);
1724     est_datum_printf(datum, "%s=%s\n", vbuf, cbmapget(resdoc->attrs, vbuf, -1, NULL));
1725     }
1726     cblistclose(list);
1727     }
1728     est_datum_printf(datum, "\n");
1729     if(resdoc->body){
1730     cbdatumcat(datum, resdoc->body, -1);
1731     } else if(resdoc->doc){
1732     if(g_snipwwidth > 0){
1733     snippet = est_doc_make_snippet(resdoc->doc, words,
1734     g_snipwwidth, g_sniphwidth, g_snipawidth);
1735     cbdatumcat(datum, snippet, -1);
1736     free(snippet);
1737     }
1738     }
1739     }
1740    
1741    
1742     /* concatenate a document data to an output buffer for search interface */
1743     static void catdocdataui(CBDATUM *datum, RESDOC *resdoc, CBLIST *words, const char *condstr){
1744     CBLIST *lines;
1745     const char *uri, *file, *title, *ndurl, *ndlabel, *vbuf;
1746     char *turi, buf[URIBUFSIZ], *pv, *snippet, *word;
1747     int i, j, vsiz, ld;
1748     est_datum_printf(datum, "<dl class=\"doc\">\n");
1749     if(!(uri = resdoc->doc ? est_doc_attr(resdoc->doc, ESTDATTRURI) :
1750     cbmapget(resdoc->attrs, ESTDATTRURI, -1, NULL))) uri = ".";
1751     turi = makeshownuri(uri);
1752     if((file = strrchr(turi, '/')) != NULL) file++;
1753     title = resdoc->doc ? est_doc_attr(resdoc->doc, ESTDATTRTITLE) :
1754     cbmapget(resdoc->attrs, ESTDATTRTITLE, -1, NULL);
1755     if(!title) title = resdoc->doc ? est_doc_attr(resdoc->doc, DATTRLFILE) :
1756     cbmapget(resdoc->attrs, DATTRLFILE, -1, NULL);
1757     if(!title) title = file;
1758     if(!title || title[0] == '\0') title = "(no title)";
1759     ndurl = resdoc->doc ? est_doc_attr(resdoc->doc, DATTRNDURL) :
1760     cbmapget(resdoc->attrs, DATTRNDURL, -1, NULL);
1761     if(!ndurl) ndurl = ".";
1762     ndlabel = resdoc->doc ? est_doc_attr(resdoc->doc, DATTRNDLABEL) :
1763     cbmapget(resdoc->attrs, DATTRNDLABEL, -1, NULL);
1764     if(!ndlabel) ndlabel = "(unknown)";
1765     est_datum_printf(datum, "<dt class=\"title\"><a href=\"%@\" class=\"title\">%@</a>"
1766     " <a href=\"%@/searchui?%s\" class=\"ndlabel\">(%@)</a></dt>\n",
1767     turi, title, ndurl, condstr, ndlabel);
1768     for(i = 0; i < cblistnum(g_uiextattrs); i++){
1769     sprintf(buf, "%s", cblistval(g_uiextattrs, i, NULL));
1770     if(!(pv = strchr(buf, '|'))) continue;
1771     *(pv++) = '\0';
1772     vbuf = resdoc->doc ? est_doc_attr(resdoc->doc, buf) : cbmapget(resdoc->attrs, buf, -1, NULL);
1773     if(!vbuf) continue;
1774     est_datum_printf(datum, "<dd class=\"attr\"><span class=\"aname\">%@:</span> %@</dd>\n",
1775     pv, vbuf);
1776     }
1777     if(g_snipwwidth > 0){
1778     est_datum_printf(datum, "<dd class=\"snippet\">");
1779     snippet = resdoc->doc ? est_doc_make_snippet(resdoc->doc, words, g_snipwwidth,
1780     g_sniphwidth, g_snipawidth) : NULL;
1781     lines = cbsplit(snippet ? snippet: resdoc->body, -1, "\n");
1782     ld = TRUE;
1783     for(j = 0; j < cblistnum(lines); j++){
1784     vbuf = cblistval(lines, j, &vsiz);
1785     word = cbmemdup(vbuf, vsiz);
1786     if(vsiz < 1){
1787     if(!ld) est_datum_printf(datum, " ... ");
1788     ld = TRUE;
1789     } else if((pv = strchr(word, '\t')) != NULL){
1790     *pv = '\0';
1791     est_datum_printf(datum, "<strong>%@</strong>", word);
1792     ld = FALSE;
1793     } else {
1794     est_datum_printf(datum, "%@", word);
1795     ld = FALSE;
1796     }
1797     free(word);
1798     }
1799     cblistclose(lines);
1800     free(snippet);
1801     est_datum_printf(datum, "</dd>\n");
1802     }
1803     est_datum_printf(datum, "<dd class=\"uri\">%@</dd>\n", turi);
1804     free(turi);
1805     est_datum_printf(datum, "</dl>\n");
1806     }
1807    
1808    
1809     /* make a URI to be shown */
1810     static char *makeshownuri(const char *uri){
1811     const char *prefix;
1812     char *turi, *file, *bef, *aft, *pv, *nuri, *wp;
1813     int i;
1814     if(cbstrfwimatch(uri, g_uilprefix)) uri += strlen(g_uilprefix);
1815     prefix = g_uigprefix;
1816     if(cbstrfwimatch(uri, "file://") || cbstrfwimatch(uri, "ftp://") ||
1817     cbstrfwimatch(uri, "http://") || cbstrfwimatch(uri, "https://")) prefix = "";
1818     turi = cbsprintf("%s%s%s", prefix, uri, g_uigsuffix);
1819     if(g_uidirindex[0] != '\0' && (file = strrchr(turi, '/')) != NULL &&
1820     !cbstricmp(file + 1, g_uidirindex)){
1821     file[1] = '\0';
1822     }
1823     for(i = 0; i < cblistnum(g_uireplaces); i++){
1824     bef = cbmemdup(cblistval(g_uireplaces, i, NULL), -1);
1825     if((pv = strstr(bef, "{{!}}")) != NULL){
1826     *pv = '\0';
1827     aft = pv + 5;
1828     } else {
1829     aft = "";
1830     }
1831     if((pv = strstr(turi, bef)) != NULL){
1832     nuri = cbmalloc(strlen(turi) + strlen(aft) + 1);
1833     wp = nuri;
1834     memcpy(wp, turi, pv - turi);
1835     wp += pv - turi;
1836     wp += sprintf(wp, "%s", aft);
1837     sprintf(wp, "%s", pv + strlen(bef));
1838     free(turi);
1839     turi = nuri;
1840     }
1841     free(bef);
1842     }
1843     return turi;
1844     }
1845    
1846    
1847     /* send the result of search command */
1848     static void sendnodecmdinform(int clsock, REQUEST *req, NODE *node){
1849     CBDATUM *datum;
1850     const char *kbuf;
1851     int ksiz;
1852     if(pthread_mutex_lock(&(node->mutex)) != 0){
1853     log_print(LL_ERROR, "locking failed");
1854     senderror(clsock, req, 500, "Internal Server Error (locking failed)");
1855     return;
1856     }
1857     datum = cbdatumopen("", 0);
1858     est_datum_printf(datum, "HTTP/1.0 200 OK\r\n");
1859     addservinfo(datum);
1860     est_datum_printf(datum, "Content-Type: %s; charset=UTF-8\r\n", ESTINFORMTYPE);
1861     est_datum_printf(datum, "\r\n");
1862     est_datum_printf(datum, "%s\t%s\t%d\t%d\t%.0f\n", node->name, node->label,
1863     est_mtdb_doc_num(node->db), est_mtdb_word_num(node->db),
1864     est_mtdb_size(node->db));
1865     est_datum_printf(datum, "\n");
1866     cbmapiterinit(node->admins);
1867     while((kbuf = cbmapiternext(node->admins, NULL)) != NULL){
1868     if(kbuf[0] != '\0') est_datum_printf(datum, "%s\n", kbuf);
1869     }
1870     est_datum_printf(datum, "\n");
1871     cbmapiterinit(node->users);
1872     while((kbuf = cbmapiternext(node->users, NULL)) != NULL){
1873     if(kbuf[0] != '\0') est_datum_printf(datum, "%s\n", kbuf);
1874     }
1875     est_datum_printf(datum, "\n");
1876     cbmapiterinit(node->links);
1877     while((kbuf = cbmapiternext(node->links, &ksiz)) != NULL){
1878     if(kbuf[0] != '\0')
1879     est_datum_printf(datum, "%s\t%s\n", kbuf, cbmapget(node->links, kbuf, ksiz, NULL));
1880     }
1881     est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum));
1882     cbdatumclose(datum);
1883     log_print(LL_DEBUG, "[%s:%d]: 200 (ok - inform)", req->claddr, req->clport);
1884     if(pthread_mutex_unlock(&(node->mutex)) != 0) log_print(LL_ERROR, "unlocking failed");
1885     }
1886    
1887    
1888     /* send the result of search command */
1889     static void sendnodecmdsearch(int clsock, REQUEST *req, NODE *node){
1890     pthread_t lth, *rths;
1891     TARGLCSRCH ltarg;
1892     TARGRMSRCH *rtargs;
1893     RESMAP *resmap;
1894     RESDOC **resdocs;
1895     ESTCOND *cond;
1896     CBMAP *hints;
1897     CBDATUM *datum;
1898     const char *tmp, *url, *label, *kbuf;
1899     char *myurl;
1900     int i, num, max, depth, dnum, wnum, ksiz, rnum;
1901     double curtime;
1902     myurl = cbsprintf("http://%s:%d/node/%s", g_hostname, g_portnum, node->name);
1903     if(islooproute(myurl, req)){
1904     log_print(LL_DEBUG, "[%s:%d]: omitting request loop (self): %s",
1905     req->claddr, req->clport, myurl);
1906     free(myurl);
1907     senderror(clsock, req, 400, "Bad Request (the request loops)");
1908     return;
1909     }
1910     if(pthread_mutex_lock(&(node->mutex)) != 0){
1911     log_print(LL_ERROR, "locking failed");
1912     senderror(clsock, req, 500, "Internal Server Error (locking failed)");
1913     return;
1914     }
1915     cond = est_cond_new();
1916     max = DEFMAXSRCH;
1917     depth = 0;
1918     if((tmp = cbmapget(req->params, "phrase", -1, NULL)) != NULL) est_cond_set_phrase(cond, tmp);
1919     if((tmp = cbmapget(req->params, "attr", -1, NULL)) != NULL) est_cond_set_phrase(cond, tmp);
1920     if((tmp = cbmapget(req->params, "attr1", -1, NULL)) != NULL) est_cond_add_attr(cond, tmp);
1921     if((tmp = cbmapget(req->params, "attr2", -1, NULL)) != NULL) est_cond_add_attr(cond, tmp);
1922     if((tmp = cbmapget(req->params, "attr3", -1, NULL)) != NULL) est_cond_add_attr(cond, tmp);
1923     if((tmp = cbmapget(req->params, "attr4", -1, NULL)) != NULL) est_cond_add_attr(cond, tmp);
1924     if((tmp = cbmapget(req->params, "attr5", -1, NULL)) != NULL) est_cond_add_attr(cond, tmp);
1925     if((tmp = cbmapget(req->params, "attr6", -1, NULL)) != NULL) est_cond_add_attr(cond, tmp);
1926     if((tmp = cbmapget(req->params, "attr7", -1, NULL)) != NULL) est_cond_add_attr(cond, tmp);
1927     if((tmp = cbmapget(req->params, "attr8", -1, NULL)) != NULL) est_cond_add_attr(cond, tmp);
1928     if((tmp = cbmapget(req->params, "attr9", -1, NULL)) != NULL) est_cond_add_attr(cond, tmp);
1929     if((tmp = cbmapget(req->params, "order", -1, NULL)) != NULL) est_cond_set_order(cond, tmp);
1930     if((tmp = cbmapget(req->params, "max", -1, NULL)) && (num = atoi(tmp)) >= 0) max = num;
1931     est_cond_set_max(cond, max + 1);
1932     if((tmp = cbmapget(req->params, "options", -1, NULL)) && (num = atoi(tmp)) > 0)
1933     est_cond_set_options(cond, num);
1934     if((tmp = cbmapget(req->params, "depth", -1, NULL)) && (num = atoi(tmp)) > 0) depth = num;
1935     if(depth >= g_searchdepth) depth = g_searchdepth;
1936     resmap = resmap_new();
1937     hints = cbmapopenex(MINIBNUM);
1938     curtime = est_gettimeofday();
1939     ltarg.req = req;
1940     ltarg.alive = TRUE;
1941     ltarg.cond = cond;
1942     ltarg.hints = hints;
1943     ltarg.max = max + 1;
1944     ltarg.node = node;
1945     ltarg.resmap = resmap;
1946     ltarg.words = cblistopen();
1947     ltarg.hnum = 0;
1948     if(g_stmode){
1949     searchlocal(&ltarg);
1950     ltarg.alive = FALSE;
1951     } else if(pthread_create(&lth, NULL, searchlocal, &ltarg) != 0){
1952     log_print(LL_WARN, "creating thread failed");
1953     ltarg.alive = FALSE;
1954     }
1955     rths = cbmalloc(cbmaprnum(node->links) * sizeof(pthread_t) + 1);
1956     rtargs = cbmalloc(cbmaprnum(node->links) * sizeof(TARGRMSRCH) + 1);
1957     cbmapiterinit(node->links);
1958     for(i = 0; (url = cbmapiternext(node->links, NULL)) != NULL; i++){
1959     rtargs[i].req = req;
1960     rtargs[i].alive = TRUE;
1961     rtargs[i].myurl = myurl;
1962     rtargs[i].cond = cond;
1963     rtargs[i].hints = hints;
1964     rtargs[i].max = max;
1965     rtargs[i].node = node;
1966     rtargs[i].resmap = resmap;
1967     rtargs[i].url = url;
1968     label = cbmapget(node->links, url, -1, NULL);
1969     if((tmp = strchr(label, '\t')) != NULL){
1970     rtargs[i].label = cbmemdup(label, tmp - label);
1971     rtargs[i].credit = atoi(tmp + 1);
1972     } else {
1973     rtargs[i].label = cbmemdup(label, -1);
1974     rtargs[i].credit = 0;
1975     }
1976     rtargs[i].depth = depth;
1977     rtargs[i].hnum = 0;
1978     rtargs[i].dnum = 0;
1979     rtargs[i].wnum = 0;
1980     rtargs[i].size = 0.0;
1981     if(depth < 1){
1982     rtargs[i].alive = FALSE;
1983     rtargs[i].hnum = -1;
1984     rtargs[i].dnum = -1;
1985     rtargs[i].wnum = -1;
1986     rtargs[i].size = -1.0;
1987     } else if(g_stmode){
1988     searchremote(&rtargs[i]);
1989     rtargs[i].alive = FALSE;
1990     } else if(pthread_create(rths + i, NULL, searchremote, rtargs + i) != 0){
1991     log_print(LL_WARN, "creating thread failed");
1992     rtargs[i].alive = FALSE;
1993     }
1994     }
1995     if(pthread_mutex_unlock(&(node->mutex)) != 0) log_print(LL_ERROR, "unlocking failed");
1996     dnum = 0;
1997     wnum = 0;
1998     if(ltarg.alive){
1999     if(pthread_join(lth, NULL) == 0){
2000     dnum = est_mtdb_doc_num(node->db);
2001     wnum = est_mtdb_word_num(node->db);
2002     } else {
2003     log_print(LL_ERROR, "joining thread failed");
2004     }
2005     } else if(g_stmode){
2006     dnum = est_mtdb_doc_num(node->db);
2007     wnum = est_mtdb_word_num(node->db);
2008     }
2009     for(i = 0; i < cbmaprnum(node->links); i++){
2010     if(rtargs[i].alive){
2011     if(pthread_join(rths[i], NULL) == 0){
2012     dnum += rtargs[i].dnum;
2013     wnum += rtargs[i].wnum;
2014     } else {
2015     log_print(LL_ERROR, "joining thread failed");
2016     }
2017     } else if(g_stmode && depth > 0){
2018     dnum += rtargs[i].dnum;
2019     wnum += rtargs[i].wnum;
2020     }
2021     }
2022     curtime = est_gettimeofday() - curtime;
2023     datum = cbdatumopen("", 0);
2024     est_datum_printf(datum, "HTTP/1.0 200 OK\r\n");
2025     addservinfo(datum);
2026     est_datum_printf(datum, "Content-Type: %s; charset=UTF-8\r\n", ESTRESULTTYPE);
2027     est_datum_printf(datum, "\r\n");
2028     est_datum_printf(datum, "%s\n", g_bordstr);
2029     est_datum_printf(datum, "VERSION\t%s\n", _EST_PROTVER);
2030     est_datum_printf(datum, "NODE\thttp://%s:%d%s%s\n",
2031     g_hostname, g_portnum, NODEPREFIX, node->name);
2032     tmp = cbmapget(hints, "", 0, NULL);
2033     est_datum_printf(datum, "HIT\t%s\n", tmp ? tmp : "0");
2034     cbmapiterinit(hints);
2035     num = 1;
2036     while((kbuf = cbmapiternext(hints, &ksiz)) != NULL){
2037     if(ksiz < 1) continue;
2038     est_datum_printf(datum, "HINT#%d\t%s\t%s\n", num, kbuf, cbmapget(hints, kbuf, ksiz, NULL));
2039     num++;
2040     }
2041     est_datum_printf(datum, "DOCNUM\t%d\n", dnum);
2042     est_datum_printf(datum, "WORDNUM\t%d\n", wnum);
2043     est_datum_printf(datum, "TIME\t%.3f\n", curtime / 1000.0);
2044     est_datum_printf(datum, "LINK#0\thttp://%s:%d%s%s\t",
2045     g_hostname, g_portnum, NODEPREFIX, node->name);
2046     est_datum_printf(datum, "%s\t%d\t%d\t%d\t%.0f\t%d\n",
2047     node->label, SELFCREDIT, est_mtdb_doc_num(node->db),
2048     est_mtdb_word_num(node->db), est_mtdb_size(node->db), ltarg.hnum);
2049     for(i = 0; i < cbmaprnum(node->links); i++){
2050     est_datum_printf(datum, "LINK#%d\t%s\t%s\t%d\t%d\t%d\t%.0f\t%d\n",
2051     i + 1, rtargs[i].url, rtargs[i].label, rtargs[i].credit,
2052     rtargs[i].dnum, rtargs[i].wnum, rtargs[i].size, rtargs[i].hnum);
2053     }
2054     est_datum_printf(datum, "VIEW\tSNIPPET\n");
2055     est_datum_printf(datum, "\n");
2056     est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum));
2057     cbdatumsetsize(datum, 0);
2058     resdocs = resmap_list(resmap, &rnum);
2059     for(i = 0; i < rnum && i < max; i++){
2060     est_datum_printf(datum, "%s\n", g_bordstr);
2061     catdocdata(datum, resdocs[i], ltarg.words);
2062     }
2063     free(resdocs);
2064     est_datum_printf(datum, "%s:END\n", g_bordstr);
2065     est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum));
2066     cbdatumclose(datum);
2067     log_print(LL_DEBUG, "[%s:%d]: 200 (ok - search)", req->claddr, req->clport);
2068     cblistclose(ltarg.words);
2069     for(i = 0; i < cbmaprnum(node->links); i++){
2070     free(rtargs[i].label);
2071     }
2072     free(rtargs);
2073     free(rths);
2074     cbmapclose(hints);
2075     resmap_delete(resmap);
2076     est_cond_delete(cond);
2077     free(myurl);
2078     }
2079    
2080    
2081     /* send the result of get_doc command */
2082     static void sendnodecmdgetdoc(int clsock, REQUEST *req, NODE *node){
2083     ESTDOC *doc;
2084     CBDATUM *datum;
2085     const char *tmp, *uri;
2086     char *draft;
2087     int id;
2088     id = (tmp = cbmapget(req->params, "id", -1, NULL)) ? atoi(tmp) : 0;
2089     if(!(uri = cbmapget(req->params, "uri", -1, NULL))) uri = "";
2090     if(id < 1 && uri[0] == '\0'){
2091     senderror(clsock, req, 400, "Bad Request (the parameters lack)");
2092     return;
2093     }
2094     if(id < 1) id = est_mtdb_uri_to_id(node->db, uri);
2095     if(id > 0 && (doc = est_mtdb_get_doc(node->db, id, 0)) != NULL){
2096     setdocorigin(doc, node);
2097     draft = est_doc_dump_draft(doc);
2098     datum = cbdatumopen("", 0);
2099     est_datum_printf(datum, "HTTP/1.0 200 OK\r\n");
2100     addservinfo(datum);
2101     est_datum_printf(datum, "Content-Type: %s; charset=UTF-8\r\n", ESTDRAFTTYPE);
2102     est_datum_printf(datum, "\r\n");
2103     cbdatumcat(datum, draft, -1);
2104     est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum));
2105     cbdatumclose(datum);
2106     log_print(LL_DEBUG, "[%s:%d]: 200 (ok - get_doc)", req->claddr, req->clport);
2107     free(draft);
2108     est_doc_delete(doc);
2109     } else {
2110     log_print(LL_WARN, "DB-ERROR: %s", est_err_msg(est_mtdb_error(node->db)));
2111     senderror(clsock, req, 400, "Bad Request (maybe, the document does not exists)");
2112     }
2113     }
2114    
2115    
2116     /* send the result of get_doc_attr command */
2117     static void sendnodecmdgetdocattr(int clsock, REQUEST *req, NODE *node){
2118     CBDATUM *datum;
2119     const char *tmp, *uri, *attr;
2120     char *value;
2121     int id;
2122     id = (tmp = cbmapget(req->params, "id", -1, NULL)) ? atoi(tmp) : 0;
2123     if(!(uri = cbmapget(req->params, "uri", -1, NULL))) uri = "";
2124     if(!(attr = cbmapget(req->params, "attr", -1, NULL))) attr = "";
2125     if((id < 1 && uri[0] == '\0') || attr[0] == '\0'){
2126     senderror(clsock, req, 400, "Bad Request (the parameters lack)");
2127     return;
2128     }
2129     if(id < 1) id = est_mtdb_uri_to_id(node->db, uri);
2130     if(id > 0 && (value = est_mtdb_get_doc_attr(node->db, id, attr)) != NULL){
2131     datum = cbdatumopen("", 0);
2132     est_datum_printf(datum, "HTTP/1.0 200 OK\r\n");
2133     addservinfo(datum);
2134     est_datum_printf(datum, "Content-Type: text/plain; charset=UTF-8\r\n");
2135     est_datum_printf(datum, "\r\n");
2136     est_datum_printf(datum, "%s\n", value);
2137     est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum));
2138     cbdatumclose(datum);
2139     log_print(LL_DEBUG, "[%s:%d]: 200 (ok - get_doc_attr)", req->claddr, req->clport);
2140     free(value);
2141     } else {
2142     log_print(LL_WARN, "DB-ERROR: %s", est_err_msg(est_mtdb_error(node->db)));
2143     senderror(clsock, req, 400,
2144     "Bad Request (maybe, the document or the attribute does not exists)");
2145     }
2146     }
2147    
2148    
2149     /* send the result of uri_to_id command */
2150     static void sendnodecmduritoid(int clsock, REQUEST *req, NODE *node){
2151     CBDATUM *datum;
2152     const char *uri;
2153     int id;
2154     uri = cbmapget(req->params, "uri", -1, NULL);
2155     if(!uri){
2156     senderror(clsock, req, 400, "Bad Request (the parameters lack)");
2157     return;
2158     }
2159     if((id = est_mtdb_uri_to_id(node->db, uri)) > 0){
2160     datum = cbdatumopen("", 0);
2161     est_datum_printf(datum, "HTTP/1.0 200 OK\r\n");
2162     addservinfo(datum);
2163     est_datum_printf(datum, "Content-Type: text/plain; charset=UTF-8\r\n");
2164     est_datum_printf(datum, "\r\n");
2165     est_datum_printf(datum, "%d\n", id);
2166     est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum));
2167     cbdatumclose(datum);
2168     log_print(LL_DEBUG, "[%s:%d]: 200 (ok - uri_to_id)", req->claddr, req->clport);
2169     } else {
2170     log_print(LL_WARN, "DB-ERROR: %s", est_err_msg(est_mtdb_error(node->db)));
2171     senderror(clsock, req, 400, "Bad Request (maybe, the URI does not registered)");
2172     }
2173     }
2174    
2175    
2176     /* send the result of pub_doc command */
2177     static void sendnodecmdputdoc(int clsock, REQUEST *req, NODE *node){
2178     ESTDOC *doc;
2179     CBDATUM *datum;
2180     const char *draft;
2181     if(req->ctype && cbstrfwimatch(req->ctype, ESTDRAFTTYPE)){
2182     draft = req->body;
2183     } else {
2184     draft = cbmapget(req->params, "draft", -1, NULL);
2185     }
2186     if(!draft){
2187     senderror(clsock, req, 400, "Bad Request (the parameters lack)");
2188     return;
2189     }
2190     doc = est_doc_new_from_draft(draft);
2191     if(est_mtdb_put_doc(node->db, doc, ESTPDCLEAN)){
2192     datum = cbdatumopen("", 0);
2193     est_datum_printf(datum, "HTTP/1.0 200 OK\r\n");
2194     addservinfo(datum);
2195     est_datum_printf(datum, "Content-Type: text/plain; charset=UTF-8\r\n");
2196     est_datum_printf(datum, "\r\n");
2197     est_datum_printf(datum, "OK\n");
2198     est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum));
2199     cbdatumclose(datum);
2200     log_print(LL_DEBUG, "[%s:%d]: 200 (ok - put_doc)", req->claddr, req->clport);
2201     } else {
2202     log_print(LL_ERROR, "DB-ERROR: %s", est_err_msg(est_mtdb_error(node->db)));
2203     if(!est_doc_attr(doc, ESTDATTRURI)){
2204     senderror(clsock, req, 400, "Bad Request (maybe, the document has not the URI)");
2205     } else {
2206     senderror(clsock, req, 400, "Bad Request (maybe, the database has a fatal error)");
2207     }
2208     }
2209     est_doc_delete(doc);
2210     }
2211    
2212    
2213     /* send the result of out_doc command */
2214     static void sendnodecmdoutdoc(int clsock, REQUEST *req, NODE *node){
2215     CBDATUM *datum;
2216     const char *tmp, *uri;
2217     int id;
2218     id = (tmp = cbmapget(req->params, "id", -1, NULL)) ? atoi(tmp) : 0;
2219     if(!(uri = cbmapget(req->params, "uri", -1, NULL))) uri = "";
2220     if(id < 1 && uri[0] == '\0'){
2221     senderror(clsock, req, 400, "Bad Request (the parameters lack)");
2222     return;
2223     }
2224     if(id < 1) id = est_mtdb_uri_to_id(node->db, uri);
2225     if(id > 0 && est_mtdb_out_doc(node->db, id, ESTODCLEAN)){
2226     datum = cbdatumopen("", 0);
2227     est_datum_printf(datum, "HTTP/1.0 200 OK\r\n");
2228     addservinfo(datum);
2229     est_datum_printf(datum, "Content-Type: text/plain; charset=UTF-8\r\n");
2230     est_datum_printf(datum, "\r\n");
2231     est_datum_printf(datum, "OK\n");
2232     est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum));
2233     cbdatumclose(datum);
2234     log_print(LL_DEBUG, "[%s:%d]: 200 (ok - out_doc)", req->claddr, req->clport);
2235     } else {
2236     log_print(LL_WARN, "DB-ERROR: %s", est_err_msg(est_mtdb_error(node->db)));
2237     senderror(clsock, req, 400, "Bad Request (maybe, the document does not exist)");
2238     }
2239     }
2240    
2241    
2242     /* send the result of _set_user command */
2243     static void sendnodecmdsetuser(int clsock, REQUEST *req, NODE *node){
2244     CBDATUM *datum;
2245     const char *name, *tmp;
2246     int mode;
2247     name = cbmapget(req->params, "name", -1, NULL);
2248     mode = (tmp = cbmapget(req->params, "mode", -1, NULL)) && tmp[0] != '\0' ? atoi(tmp) : -1;
2249     if(!name || name[0] == '\0' || mode < 0 || mode > 2){
2250     senderror(clsock, req, 400, "Bad Request (the parameters lack)");
2251     return;
2252     }
2253     if(!check_alnum_name(name)){
2254     senderror(clsock, req, 400, "Bad Request (the name is invalid)");
2255     return;
2256     }
2257     switch(mode){
2258     case 1:
2259     cbmapput(node->admins, name, -1, "", 0, FALSE);
2260     break;
2261     case 2:
2262     cbmapput(node->users, name, -1, "", 0, FALSE);
2263     break;
2264     default:
2265     cbmapout(node->admins, name, -1);
2266     cbmapout(node->users, name, -1);
2267     break;
2268     }
2269     datum = cbdatumopen("", 0);
2270     est_datum_printf(datum, "HTTP/1.0 200 OK\r\n");
2271     addservinfo(datum);
2272     est_datum_printf(datum, "Content-Type: text/plain; charset=UTF-8\r\n");
2273     est_datum_printf(datum, "\r\n");
2274     est_datum_printf(datum, "OK\n");
2275     est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum));
2276     cbdatumclose(datum);
2277     log_print(LL_DEBUG, "[%s:%d]: 200 (ok - _set_user)", req->claddr, req->clport);
2278     }
2279    
2280    
2281     /* send the result of _set_link command */
2282     static void sendnodecmdsetlink(int clsock, REQUEST *req, NODE *node){
2283     CBDATUM *datum;
2284     const char *url, *label, *tmp;
2285     int credit;
2286     url = cbmapget(req->params, "url", -1, NULL);
2287     label = cbmapget(req->params, "label", -1, NULL);
2288     credit = (tmp = cbmapget(req->params, "credit", -1, NULL)) ? atoi(tmp) : -1;
2289     if(!url || url[0] == '\0'){
2290     senderror(clsock, req, 400, "Bad Request (the parameters lack)");
2291     return;
2292     }
2293     node_set_link(node, url, label, credit);
2294     datum = cbdatumopen("", 0);
2295     est_datum_printf(datum, "HTTP/1.0 200 OK\r\n");
2296     addservinfo(datum);
2297     est_datum_printf(datum, "Content-Type: text/plain; charset=UTF-8\r\n");
2298     est_datum_printf(datum, "\r\n");
2299     est_datum_printf(datum, "OK\n");
2300     est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum));
2301     cbdatumclose(datum);
2302     log_print(LL_DEBUG, "[%s:%d]: 200 (ok - _set_link)", req->claddr, req->clport);
2303     }
2304    
2305    
2306     /* send the result of ui command */
2307     static void sendnodecmdsearchui(int clsock, REQUEST *req, NODE *node){
2308     pthread_t lth, *rths;
2309     TARGLCSRCH ltarg;
2310     TARGRMSRCH *rtargs;
2311     RESMAP *resmap;
2312     RESDOC **resdocs;
2313     ESTCOND *cond;
2314     CBMAP *hints;
2315     const CBLIST *attrs;
2316     CBDATUM *condbuf, *datum;
2317     const char *tmp, *url, *label, *kbuf, *phrase, *order;
2318     char *myurl;
2319     int i, num, max, depth, page, top, dnum, wnum, hnum, ksiz, rnum, end;
2320     double curtime;
2321     myurl = cbsprintf("http://%s:%d/node/%s", g_hostname, g_portnum, node->name);
2322     if(islooproute(myurl, req)){
2323     log_print(LL_DEBUG, "[%s:%d]: omitting request loop (self): %s",
2324     req->claddr, req->clport, myurl);
2325     free(myurl);
2326     senderror(clsock, req, 400, "Bad Request (the request loops)");
2327     return;
2328     }
2329     if(pthread_mutex_lock(&(node->mutex)) != 0){
2330     log_print(LL_ERROR, "locking failed");
2331     senderror(clsock, req, 500, "Internal Server Error (locking failed)");
2332     return;
2333     }
2334     cond = est_cond_new();
2335     max = DEFMAXSRCH;
2336     depth = 0;
2337     page = 0;
2338     if((tmp = cbmapget(req->params, "page", -1, NULL)) && (num = atoi(tmp)) > 0) page = num;
2339     if((tmp = cbmapget(req->params, "phrase", -1, NULL)) != NULL) est_cond_set_phrase(cond, tmp);
2340     if((tmp = cbmapget(req->params, "attr", -1, NULL)) != NULL) est_cond_set_phrase(cond, tmp);
2341     if((tmp = cbmapget(req->params, "attr1", -1, NULL)) != NULL) est_cond_add_attr(cond, tmp);
2342     if((tmp = cbmapget(req->params, "attr2", -1, NULL)) != NULL) est_cond_add_attr(cond, tmp);
2343     if((tmp = cbmapget(req->params, "attr3", -1, NULL)) != NULL) est_cond_add_attr(cond, tmp);
2344     if((tmp = cbmapget(req->params, "order", -1, NULL)) != NULL) est_cond_set_order(cond, tmp);
2345     if((tmp = cbmapget(req->params, "max", -1, NULL)) && (num = atoi(tmp)) >= 0) max = num;
2346     est_cond_set_max(cond, max + page * max + 1);
2347     if((tmp = cbmapget(req->params, "depth", -1, NULL)) && (num = atoi(tmp)) > 0) depth = num;
2348     if(depth >= g_searchdepth) depth = g_searchdepth;
2349     top = !est_cond_phrase(cond) && !est_cond_attrs(cond);
2350     phrase = est_cond_phrase(cond);
2351     attrs = est_cond_attrs(cond);
2352     order = est_cond_order(cond);
2353     condbuf = cbdatumopen("", 0);
2354     est_datum_printf(condbuf, "phrase=%?", phrase ? phrase : "");
2355     if(attrs){
2356     for(i = 0; i < cblistnum(attrs); i++){
2357     tmp = cblistval(attrs, i, NULL);
2358     if(tmp[0] != '\0') est_datum_printf(condbuf, "&amp;attr%d=%?", i + 1, tmp);
2359     }
2360     }
2361     if(order && order[0] != '\0') est_datum_printf(condbuf, "&amp;order=%?", order);
2362     if(max > 0) est_datum_printf(condbuf, "&amp;max=%d", max);
2363     if(depth > 0) est_datum_printf(condbuf, "&amp;depth=%d", depth);
2364     resmap = resmap_new();
2365     hints = cbmapopenex(MINIBNUM);
2366     curtime = est_gettimeofday();
2367     ltarg.req = req;
2368     ltarg.alive = TRUE;
2369     ltarg.cond = cond;
2370     ltarg.hints = hints;
2371     ltarg.max = max + page * max + 1;
2372     ltarg.node = node;
2373     ltarg.resmap = resmap;
2374     ltarg.words = cblistopen();
2375     ltarg.hnum = 0;
2376     if(top){
2377     ltarg.alive = FALSE;
2378     } else if(g_stmode){
2379     searchlocal(&ltarg);
2380     ltarg.alive = FALSE;
2381     } else if(pthread_create(&lth, NULL, searchlocal, &ltarg) != 0){
2382     log_print(LL_WARN, "creating thread failed");
2383     ltarg.alive = FALSE;
2384     }
2385     rths = cbmalloc(cbmaprnum(node->links) * sizeof(pthread_t) + 1);
2386     rtargs = cbmalloc(cbmaprnum(node->links) * sizeof(TARGRMSRCH) + 1);
2387     cbmapiterinit(node->links);
2388     for(i = 0; (url = cbmapiternext(node->links, NULL)) != NULL; i++){
2389     rtargs[i].req = req;
2390     rtargs[i].alive = TRUE;
2391     rtargs[i].myurl = myurl;
2392     rtargs[i].cond = cond;
2393     rtargs[i].hints = hints;
2394     rtargs[i].max = max + page * max + 1;
2395     rtargs[i].node = node;
2396     rtargs[i].resmap = resmap;
2397     rtargs[i].url = url;
2398     label = cbmapget(node->links, url, -1, NULL);
2399     if((tmp = strchr(label, '\t')) != NULL){
2400     rtargs[i].label = cbmemdup(label, tmp - label);
2401     rtargs[i].credit = atoi(tmp + 1);
2402     } else {
2403     rtargs[i].label = cbmemdup(label, -1);
2404     rtargs[i].credit = 0;
2405     }
2406     rtargs[i].depth = depth;
2407     rtargs[i].hnum = 0;
2408     rtargs[i].dnum = 0;
2409     rtargs[i].wnum = 0;
2410     rtargs[i].size = 0.0;
2411     if(top || depth < 1){
2412     rtargs[i].alive = FALSE;
2413     rtargs[i].hnum = -1;
2414     rtargs[i].dnum = -1;
2415     rtargs[i].wnum = -1;
2416     rtargs[i].size = -1.0;
2417     } else if(g_stmode){
2418     searchremote(&rtargs[i]);
2419     rtargs[i].alive = FALSE;
2420     } else if(pthread_create(rths + i, NULL, searchremote, rtargs + i) != 0){
2421     log_print(LL_WARN, "creating thread failed");
2422     rtargs[i].alive = FALSE;
2423     }
2424     }
2425     if(pthread_mutex_unlock(&(node->mutex)) != 0) log_print(LL_ERROR, "unlocking failed");
2426     dnum = 0;
2427     wnum = 0;
2428     if(ltarg.alive){
2429     if(pthread_join(lth, NULL) == 0){
2430     dnum = est_mtdb_doc_num(node->db);
2431     wnum = est_mtdb_word_num(node->db);
2432     } else {
2433     log_print(LL_ERROR, "joining thread failed");
2434     }
2435     } else if(!top && g_stmode){
2436     dnum = est_mtdb_doc_num(node->db);
2437     wnum = est_mtdb_word_num(node->db);
2438     }
2439     for(i = 0; i < cbmaprnum(node->links); i++){
2440     if(rtargs[i].alive){
2441     if(pthread_join(rths[i], NULL) == 0){
2442     dnum += rtargs[i].dnum;
2443     wnum += rtargs[i].wnum;
2444     } else {
2445     log_print(LL_ERROR, "joining thread failed");
2446     }
2447     } else if(!top && g_stmode && depth > 0){
2448     dnum += rtargs[i].dnum;
2449     wnum += rtargs[i].wnum;
2450     }
2451     }
2452     hnum = (tmp = cbmapget(hints, "", 0, NULL)) ? atoi(tmp) : 0;
2453     curtime = est_gettimeofday() - curtime;
2454     datum = cbdatumopen("", 0);
2455     est_datum_printf(datum, "HTTP/1.0 200 OK\r\n");
2456     addservinfo(datum);
2457     est_datum_printf(datum, "Content-Type: text/html; charset=UTF-8\r\n");
2458     est_datum_printf(datum, "\r\n");
2459     est_datum_printf(datum, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
2460     est_datum_printf(datum, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\""
2461     " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n");
2462     est_datum_printf(datum, "<html xmlns=\"http://www.w3.org/1999/xhtml\""
2463     " xml:lang=\"en\" lang=\"en\">\n");
2464     est_datum_printf(datum, "<head>\n");
2465     est_datum_printf(datum, "<meta http-equiv=\"Content-Language\" content=\"en\" />\n");
2466     est_datum_printf(datum, "<meta http-equiv=\"Content-Type\""
2467     " content=\"text/html; charset=UTF-8\" />\n");
2468     est_datum_printf(datum, "<meta http-equiv=\"Content-Style-Type\" content=\"text/css\" />\n");
2469     est_datum_printf(datum, "<link rel=\"contents\" href=\"%@%@/searchui\" />\n",
2470     NODEPREFIX, node->name);
2471     est_datum_printf(datum, "<title>Search Interface of %@</title>\n", node->label);
2472     est_datum_printf(datum, "<style type=\"text/css\">"
2473     "html { margin: 0em 0em; padding 0em 0em; background: #eeeeee none; }\n");
2474     est_datum_printf(datum, "body { margin: 1em 1em; padding 0em 0em;");
2475     est_datum_printf(datum, " background: #eeeeee none; color: #111111;");
2476     est_datum_printf(datum, " font-style: normal; font-weight: normal; }\n");
2477     est_datum_printf(datum, "h1 { font-size: large; }\n");
2478     est_datum_printf(datum, "a { color: #0022aa; text-decoration: none; }\n");
2479     est_datum_printf(datum, "a:hover,a:focus { color: #0033ee; text-decoration: underline; }\n");
2480     est_datum_printf(datum, "table { padding: 1pt 2pt 1pt 2pt; border: none;"
2481     " margin: 0.3em 0.3em; border-collapse: collapse; }\n");
2482     est_datum_printf(datum, "th { padding: 1pt 4pt 1pt 4pt; border-style: none;"
2483     " text-align: right; vertical-align: top; font-size: smaller; }\n");
2484     est_datum_printf(datum, "td { padding: 1pt 4pt 1pt 4pt;"
2485     " text-align: left; vertical-align: top; }\n");
2486     est_datum_printf(datum, "td.conditions { padding-right: 1.5em; vertical-align:top; }\n");
2487     est_datum_printf(datum, "td.hints { padding-left: .5em; vertical-align:top; }\n");
2488     est_datum_printf(datum, "td.delimiter { margin: 0em 0em; padding: 0em 0em;"
2489     " width: 0pt; border: 1pt solid #aaaaaa; }\n");
2490     est_datum_printf(datum, "dl.doc { margin: 1.3em 0em; }\n");
2491     est_datum_printf(datum, "dt.title { margin: 0em 0.5em; }\n");
2492     est_datum_printf(datum, "dd.snippet { margin: 0em 1.5em; }\n");
2493     est_datum_printf(datum, "dd.attr { margin: 0em 1.3em; }\n");
2494     est_datum_printf(datum, "dd.uri { margin: 0em 1.2em; }\n");
2495     est_datum_printf(datum, "a.title { text-decoration: underline; }\n");
2496     est_datum_printf(datum, "a.ndlabel { font-size: smaller; }\n");
2497     est_datum_printf(datum, "dd.snippet { font-size: smaller; color: #333333; }\n");
2498     est_datum_printf(datum, "dd.snippet strong { color: #000000; }\n");
2499     est_datum_printf(datum, "dd.attr { font-size: smaller; }\n");
2500     est_datum_printf(datum, "dd.uri { font-size: smaller; color: #007744; }\n");
2501     est_datum_printf(datum, "div.paging { margin: 1.5em 1.0em; text-align: right; }\n");
2502     est_datum_printf(datum, "a.navi { margin: 0em 0.2em; }\n");
2503     est_datum_printf(datum, "span.void { margin: 0em 0.2em; color: #888888; }\n");
2504     est_datum_printf(datum, "div.logo { text-align: right;"
2505     " font-size: smaller; color: #444444; }\n");
2506     est_datum_printf(datum, "</style>\n");
2507     est_datum_printf(datum, "</head>\n");
2508     est_datum_printf(datum, "<body>\n");
2509     est_datum_printf(datum, "<h1>Search Interface of %@</h1>\n", node->label);
2510     est_datum_printf(datum, "<hr />\n");
2511     est_datum_printf(datum, "<table summary=\"controller\" class=\"controller\">\n");
2512     est_datum_printf(datum, "<tr>\n");
2513     est_datum_printf(datum, "<td class=\"conditions\">\n");
2514     est_datum_printf(datum, "<form method=\"get\" action=\"%@%@/searchui\">\n",
2515     NODEPREFIX, node->name);
2516     est_datum_printf(datum, "<table summary=\"conditions\">\n");
2517     est_datum_printf(datum, "<tr>\n");
2518     est_datum_printf(datum, "<th abbr=\"phrase\">phrase:</th>\n");
2519     est_datum_printf(datum, "<td>");
2520     est_datum_printf(datum, "<input type=\"text\" name=\"phrase\" value=\"%@\" size=\"32\" />",
2521     phrase ? phrase : "");
2522     est_datum_printf(datum, "</td>\n");
2523     est_datum_printf(datum, "</tr>\n");
2524     for(i = 0; i < 3; i++){
2525     est_datum_printf(datum, "<tr>\n");
2526     tmp = attrs ? cblistval(attrs, i, NULL) : NULL;
2527     est_datum_printf(datum, "<th abbr=\"attr%d\">attribute:</th>\n", i + 1);
2528     est_datum_printf(datum, "<td>");
2529     est_datum_printf(datum, "<input type=\"text\" name=\"attr%d\" value=\"%@\" size=\"32\" />",
2530     i + 1, tmp ? tmp : "");
2531     est_datum_printf(datum, "</td>\n");
2532     est_datum_printf(datum, "</tr>\n");
2533     }
2534     est_datum_printf(datum, "<tr>\n");
2535     est_datum_printf(datum, "<th abbr=\"order\">order:</th>\n");
2536     est_datum_printf(datum, "<td>");
2537     est_datum_printf(datum, "<input type=\"text\" name=\"order\" value=\"%@\" size=\"24\" />",
2538     order ? order : "");
2539     est_datum_printf(datum, "</td>\n");
2540     est_datum_printf(datum, "</tr>\n");
2541     est_datum_printf(datum, "<tr>\n");
2542     est_datum_printf(datum, "<th abbr=\"max\">max:</th>\n");
2543     est_datum_printf(datum, "<td>");
2544     est_datum_printf(datum, "<select name=\"max\">");
2545     for(i = 10; i <= 100; i += 10){
2546     if(i >= 50 && i < 100) continue;
2547     est_datum_printf(datum, "<option value=\"%d\"%s>%d</option>",
2548     i, max == i ? " selected=\"selected\"" : "", i);
2549     }
2550     est_datum_printf(datum, "</select>");
2551     est_datum_printf(datum, "</td>\n");
2552     est_datum_printf(datum, "</tr>\n");
2553     est_datum_printf(datum, "<tr>\n");
2554     est_datum_printf(datum, "<th abbr=\"depth\">depth:</th>\n");
2555     est_datum_printf(datum, "<td>");
2556     est_datum_printf(datum, "<select name=\"depth\">");
2557     for(i = 0; i <= 3; i++){
2558     est_datum_printf(datum, "<option value=\"%d\"%s>%d</option>",
2559     i, depth == i ? " selected=\"selected\"" : "", i);
2560     }
2561     est_datum_printf(datum, "</select>");
2562     est_datum_printf(datum, "</td>\n");
2563     est_datum_printf(datum, "</tr>\n");
2564     est_datum_printf(datum, "<tr>\n");
2565     est_datum_printf(datum, "<th abbr=\"\"></th>\n");
2566     est_datum_printf(datum, "<td>");
2567     est_datum_printf(datum, "<input type=\"submit\" value=\"search\" />");
2568     est_datum_printf(datum, "</td>\n");
2569     est_datum_printf(datum, "</tr>\n");
2570     est_datum_printf(datum, "</table>\n");
2571     est_datum_printf(datum, "</form>\n");
2572     est_datum_printf(datum, "</td>\n");
2573     est_datum_printf(datum, "<td class=\"delimiter\"></td>\n");
2574     est_datum_printf(datum, "<td class=\"hints\">\n");
2575     est_datum_printf(datum, "<table summary=\"hints\">\n");
2576     est_datum_printf(datum, "<tr>\n");
2577     est_datum_printf(datum, "<th abbr=\"hit\">hit:</th>\n");
2578     est_datum_printf(datum, "<td>%d</td>\n", hnum);
2579     est_datum_printf(datum, "</tr>\n");
2580     cbmapiterinit(hints);
2581     num = 1;
2582     while((kbuf = cbmapiternext(hints, &ksiz)) != NULL){
2583     if(ksiz < 1) continue;
2584     est_datum_printf(datum, "<tr>\n");
2585     est_datum_printf(datum, "<th abbr=\"hint#%d\">hint#%d:</th>\n", num, num);
2586     est_datum_printf(datum, "<td>%@ (%@)</td>\n", kbuf, cbmapget(hints, kbuf, ksiz, NULL));
2587     est_datum_printf(datum, "</tr>\n");
2588     num++;
2589     }
2590     est_datum_printf(datum, "<tr>\n");
2591     est_datum_printf(datum, "<th abbr=\"docnum\">docnum:</th>\n");
2592     est_datum_printf(datum, "<td>%d</td>\n", dnum);
2593     est_datum_printf(datum, "</tr>\n");
2594     est_datum_printf(datum, "<tr>\n");
2595     est_datum_printf(datum, "<th abbr=\"wordnum\">wordnum:</th>\n");
2596     est_datum_printf(datum, "<td>%d</td>\n", wnum);
2597     est_datum_printf(datum, "</tr>\n");
2598     est_datum_printf(datum, "<tr>\n");
2599     est_datum_printf(datum, "<th abbr=\"time\">time:</th>\n");
2600     est_datum_printf(datum, "<td>%.3f</td>\n", curtime / 1000.0);
2601     est_datum_printf(datum, "</tr>\n");
2602     est_datum_printf(datum, "<tr>\n");
2603     est_datum_printf(datum, "<th abbr=\"link#0\">link#0:</th>\n");
2604     est_datum_printf(datum, "<td>");
2605     est_datum_printf(datum, "<a href=\"http://%@:%d%@%@/searchui?%s\">%@</a> (%d)",
2606     g_hostname, g_portnum, NODEPREFIX, node->name, cbdatumptr(condbuf),
2607     node->label, ltarg.hnum);
2608     est_datum_printf(datum, "</td>\n");
2609     est_datum_printf(datum, "</tr>\n");
2610     for(i = 0; i < cbmaprnum(node->links); i++){
2611     est_datum_printf(datum, "<tr>\n");
2612     est_datum_printf(datum, "<th abbr=\"link#%d\">link#%d:</th>\n", i + 1, i + 1);
2613     est_datum_printf(datum, "<td>");
2614     est_datum_printf(datum, "<a href=\"%@/searchui?%s\">%@</a>",
2615     rtargs[i].url, cbdatumptr(condbuf), rtargs[i].label);
2616     if(rtargs[i].hnum >= 0) est_datum_printf(datum, " (%d)", rtargs[i].hnum);
2617     est_datum_printf(datum, "</td>\n");
2618     est_datum_printf(datum, "</tr>\n");
2619     }
2620     est_datum_printf(datum, "</table>\n");
2621     est_datum_printf(datum, "</td>\n");
2622     est_datum_printf(datum, "</tr>\n");
2623     est_datum_printf(datum, "</table>\n");
2624    
2625     est_datum_printf(datum, "<hr />\n");
2626     est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum));
2627     cbdatumsetsize(datum, 0);
2628     if(!top){
2629     resdocs = resmap_list(resmap, &rnum);
2630     end = page * max + max;
2631     for(i = page * max; i < rnum && i < end; i++){
2632     catdocdataui(datum, resdocs[i], ltarg.words, cbdatumptr(condbuf));
2633     }
2634     free(resdocs);
2635     if(rnum < 1) est_datum_printf(datum, "<p>The conditions did not match any documents.</p>\n");
2636     est_datum_printf(datum, "<div class=\"paging\">\n");
2637     if(page > 0){
2638     est_datum_printf(datum, "<a href=\"%@%@/searchui?%s&amp;page=%d\" class=\"navi\">"
2639     "PREV</a>\n", NODEPREFIX, node->name, cbdatumptr(condbuf), page - 1);
2640     } else {
2641     est_datum_printf(datum, "<span class=\"void\">PREV</span>\n");
2642     }
2643     if(hnum > end){
2644     est_datum_printf(datum, "<a href=\"%@%@/searchui?%s&amp;page=%d\" class=\"navi\">"
2645     "NEXT</a>\n", NODEPREFIX, node->name, cbdatumptr(condbuf), page + 1);
2646     } else {
2647     est_datum_printf(datum, "<span class=\"void\">NEXT</span>\n");
2648     }
2649     est_datum_printf(datum, "</div>\n");
2650     est_datum_printf(datum, "<hr />\n");
2651     }
2652     est_datum_printf(datum, "<div class=\"logo\">Powered by Hyper Estraier %@.</div>\n",
2653     est_version);
2654     est_datum_printf(datum, "</body>\n");
2655     est_datum_printf(datum, "</html>\n");
2656     est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum));
2657     cbdatumclose(datum);
2658     log_print(LL_DEBUG, "[%s:%d]: 200 (ok - search)", req->claddr, req->clport);
2659     cblistclose(ltarg.words);
2660     for(i = 0; i < cbmaprnum(node->links); i++){
2661     free(rtargs[i].label);
2662     }
2663     free(rtargs);
2664     free(rths);
2665     cbmapclose(hints);
2666     resmap_delete(resmap);
2667     cbdatumclose(condbuf);
2668     est_cond_delete(cond);
2669     free(myurl);
2670     }
2671    
2672    
2673     /* send administration interface data */
2674     static void sendmasteruidata(int clsock, REQUEST *req, USER *user){
2675     CBDATUM *datum;
2676     CBLIST *list;
2677     USER *tuser;
2678     NODE *tnode;
2679     const char *tmp, *name, *passwd, *flags, *fname, *misc, *label, *admins, *users, *links;
2680     char *pbuf, *pv, *nv;
2681     int i, action, sure, vml, vul, vnl, vne;
2682     action = UI_NONE;
2683     if((tmp = cbmapget(req->params, "action", -1, NULL)) != NULL) action = atoi(tmp);
2684     sure = FALSE;
2685     if((tmp = cbmapget(req->params, "sure", -1, NULL)) != NULL) sure = atoi(tmp) > 0;
2686     if(!(name = cbmapget(req->params, "name", -1, NULL))) name = "";
2687     if(!(passwd = cbmapget(req->params, "passwd", -1, NULL))) passwd = "";
2688     if(!(flags = cbmapget(req->params, "flags", -1, NULL))) flags = "";
2689     if(!(fname = cbmapget(req->params, "fname", -1, NULL))) fname = "";
2690     if(!(misc = cbmapget(req->params, "misc", -1, NULL))) misc = "";
2691     if(!(label = cbmapget(req->params, "label", -1, NULL))) label = "";
2692     admins = cbmapget(req->params, "admins", -1, NULL);
2693     users = cbmapget(req->params, "users", -1, NULL);
2694     links = cbmapget(req->params, "links", -1, NULL);
2695     if(!rwlock_lock(g_mgrlock, TRUE)){
2696     log_print(LL_ERROR, "locking failed");
2697     senderror(clsock, req, 500, "Internal Server Error (locking failed)");
2698     return;
2699     }
2700     if(!ismasteradmin(req, user)){
2701     sendautherror(clsock, req, "Super User");
2702     if(!rwlock_unlock(g_mgrlock)) log_print(LL_ERROR, "unlocking failed");
2703     return;
2704     }
2705     datum = cbdatumopen("", 0);
2706     est_datum_printf(datum, "HTTP/1.0 200 OK\r\n");
2707     addservinfo(datum);
2708     est_datum_printf(datum, "Content-Type: text/html; charset=UTF-8\r\n");
2709     est_datum_printf(datum, "\r\n");
2710     est_datum_printf(datum, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
2711     est_datum_printf(datum, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\""
2712     " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n");
2713     est_datum_printf(datum, "<html xmlns=\"http://www.w3.org/1999/xhtml\""
2714     " xml:lang=\"en\" lang=\"en\">\n");
2715     est_datum_printf(datum, "<head>\n");
2716     est_datum_printf(datum, "<meta http-equiv=\"Content-Language\" content=\"en\" />\n");
2717     est_datum_printf(datum, "<meta http-equiv=\"Content-Type\""
2718     " content=\"text/html; charset=UTF-8\" />\n");
2719     est_datum_printf(datum, "<meta http-equiv=\"Content-Style-Type\" content=\"text/css\" />\n");
2720     est_datum_printf(datum, "<link rel=\"contents\" href=\"%@\" />\n", MASTERUILOC);
2721     est_datum_printf(datum, "<title>Administration Interface of Hyper Estraier</title>\n");
2722     est_datum_printf(datum, "<style type=\"text/css\">"
2723     "html { margin: 0em 0em; padding 0em 0em; background: #eeeeee none; }\n");
2724     est_datum_printf(datum, "body { margin: 2em 2em; padding 0em 0em;");
2725     est_datum_printf(datum, " background: #eeeeee none; color: #111111;");
2726     est_datum_printf(datum, " font-style: normal; font-weight: normal; }\n");
2727     est_datum_printf(datum, "h1 { font-size: x-large; }\n");
2728     est_datum_printf(datum, "a { color: #0022aa; text-decoration: none; }\n");
2729     est_datum_printf(datum, "a:hover,a:focus { color: #0033ee; text-decoration: underline; }\n");
2730     est_datum_printf(datum, "table { padding: 1pt 2pt 1pt 2pt; border: none;"
2731     " margin: 1.5em 1.5em; border-collapse: collapse; }\n");
2732     est_datum_printf(datum, "th { padding: 1pt 4pt 1pt 4pt; border-style: none;"
2733     " text-align: left; vertical-align: bottom; font-size: smaller; }\n");
2734     est_datum_printf(datum, "td { padding: 1pt 4pt 1pt 4pt; border: 1pt solid #555555;"
2735     " text-align: left; vertical-align: top; }\n");
2736     est_datum_printf(datum, "div.form { margin: 1.0em 1.5em; }\n");
2737     est_datum_printf(datum, "div.logo { text-align: right; font-size: smaller; }\n");
2738     est_datum_printf(datum, "dlv.hidden { display: none; }\n");
2739     est_datum_printf(datum, "dl.tlist { margin: 1em 1.5em; }\n");
2740     est_datum_printf(datum, "dl.tlist dt { font-size: smaller; font-weight: bold; }\n");
2741     est_datum_printf(datum, "</style>\n");
2742     est_datum_printf(datum, "</head>\n");
2743     est_datum_printf(datum, "<body>\n");
2744     est_datum_printf(datum, "<h1>Administration Interface</h1>\n");
2745     est_datum_printf(datum, "<ul>\n");
2746     vml = FALSE;
2747     vul = FALSE;
2748     vnl = FALSE;
2749     vne = FALSE;
2750     switch(action){
2751     case UI_VIEWMASTER:
2752     case UI_SHUTDOWN:
2753     vml = TRUE;
2754     break;
2755     case UI_VIEWUSERS:
2756     case UI_NEWUSER:
2757     case UI_DELUSER:
2758     vul = TRUE;
2759     break;
2760     case UI_VIEWNODES:
2761     case UI_NEWNODE:
2762     case UI_DELNODE:
2763     vnl = TRUE;
2764     break;
2765     case UI_EDITNODE:
2766     vne = TRUE;
2767     break;
2768     default:
2769     break;
2770     }
2771     if(vml){
2772     est_datum_printf(datum, "<li><strong><a href=\"%@?action=%d\">Manage Master"
2773     "</a></strong></li>\n", MASTERUILOC, UI_VIEWMASTER);
2774     } else {
2775     est_datum_printf(datum, "<li><a href=\"%@?action=%d\">Manage Master</a></li>\n",
2776     MASTERUILOC, UI_VIEWMASTER);
2777     }
2778     if(vul){
2779     est_datum_printf(datum, "<li><strong><a href=\"%@?action=%d\">Manage Users"
2780     "</strong></a></li>\n", MASTERUILOC, UI_VIEWUSERS);
2781     } else {
2782     est_datum_printf(datum, "<li><a href=\"%@?action=%d\">Manage Users</a></li>\n",
2783     MASTERUILOC, UI_VIEWUSERS);
2784     }
2785     if(vnl || vne){
2786     est_datum_printf(datum, "<li><strong><a href=\"%@?action=%d\">Manage Nodes"
2787     "</a></strong></li>\n", MASTERUILOC, UI_VIEWNODES);
2788     } else {
2789     est_datum_printf(datum, "<li><a href=\"%@?action=%d\">Manage Nodes</a></li>\n",
2790     MASTERUILOC, UI_VIEWNODES);
2791     }
2792     est_datum_printf(datum, "</ul>\n");
2793     est_datum_printf(datum, "<hr />\n");
2794     switch(action){
2795     case UI_SHUTDOWN:
2796     if(sure){
2797     g_sigterm = TRUE;
2798     est_datum_printf(datum, "<p>Good Bye!</p>\n");
2799     } else {
2800     est_datum_printf(datum, "<form method=\"post\" action=\"%@\">\n", MASTERUILOC);
2801     est_datum_printf(datum, "<div class=\"form\">\n");
2802     est_datum_printf(datum, "confirmation to shutdown the master server:\n");
2803     est_datum_printf(datum, "<input type=\"submit\" value=\"shutdown\" />\n");
2804     est_datum_printf(datum, "<input type=\"hidden\" name=\"action\" value=\"%d\" />\n",
2805     UI_SHUTDOWN);
2806     est_datum_printf(datum, "<input type=\"hidden\" name=\"sure\" value=\"1\" />\n");
2807     est_datum_printf(datum, "</div>\n");
2808     est_datum_printf(datum, "</form>\n");
2809     }
2810     est_datum_printf(datum, "<hr />\n");
2811     break;
2812     case UI_NEWUSER:
2813     pbuf = est_make_crypt(passwd);
2814     if(g_runmode == RM_RDONLY){
2815     est_datum_printf(datum, "<p>The server is in read only mode.</p>\n");
2816     } else if(name[0] == '\0' || passwd[0] == '\0'){
2817     est_datum_printf(datum, "<p>The name and the password sould not be empty.</p>\n");
2818     } else if((tuser = umgr_get(g_umgr, name)) != NULL){
2819     est_datum_printf(datum, "<p>The name should not be duplicated.</p>\n");
2820     } else if(!check_alnum_name(name)){
2821     est_datum_printf(datum, "<p>The name can include alphanumeric characters only.</p>\n");
2822     } else if(umgr_put(g_umgr, name, pbuf, flags, fname, misc)){
2823     est_datum_printf(datum, "<p><strong>%@</strong> was created successfully.</p>\n", name);
2824     } else {
2825     est_datum_printf(datum, "<p>Some errors occurred.</p>\n");
2826     }
2827     free(pbuf);
2828     est_datum_printf(datum, "<hr />\n");
2829     break;
2830     case UI_DELUSER:
2831     if(g_runmode == RM_RDONLY){
2832     est_datum_printf(datum, "<p>The server is in read only mode.</p>\n");
2833     } else if(sure){
2834     if(umgr_out(g_umgr, name)){
2835     est_datum_printf(datum, "<p><strong>%@</strong> was deleted successfully.</p>\n", name);
2836     } else {
2837     est_datum_printf(datum, "<p>Some errors occurred.</p>\n");
2838     }
2839     } else {
2840     est_datum_printf(datum, "<form method=\"post\" action=\"%@\">\n", MASTERUILOC);
2841     est_datum_printf(datum, "<div class=\"form\">\n");
2842     est_datum_printf(datum, "confirmation to delete <strong>%@</strong>:\n", name);
2843     est_datum_printf(datum, "<input type=\"submit\" value=\"delete\" />\n");
2844     est_datum_printf(datum, "<input type=\"hidden\" name=\"action\" value=\"%d\" />\n",
2845     UI_DELUSER);
2846     est_datum_printf(datum, "<input type=\"hidden\" name=\"name\" value=\"%@\" />\n", name);
2847     est_datum_printf(datum, "<input type=\"hidden\" name=\"sure\" value=\"1\" />\n");
2848     est_datum_printf(datum, "</div>\n");
2849     est_datum_printf(datum, "</form>\n");
2850     }
2851     est_datum_printf(datum, "<hr />\n");
2852     break;
2853     case UI_NEWNODE:
2854     if(g_runmode == RM_RDONLY){
2855     est_datum_printf(datum, "<p>The server is in read only mode.</p>\n");
2856     } else if(name[0] == '\0' || label[0] == '\0'){
2857     est_datum_printf(datum, "<p>The name and the label sould not be empty.</p>\n");
2858     } else if((tnode = nmgr_get(g_nmgr, name)) != NULL){
2859     est_datum_printf(datum, "<p>The name should not be duplicated.</p>\n");
2860     } else if(!check_alnum_name(name)){
2861     est_datum_printf(datum, "<p>The name can include alphanumeric characters only.</p>\n");
2862     } else if(nmgr_put(g_nmgr, name, TRUE)){
2863     if((tnode = nmgr_get(g_nmgr, name)) != NULL){
2864     free(tnode->label);
2865     tnode->label = cbmemdup(label, -1);
2866     }
2867     nmgr_sync(g_nmgr, FALSE);
2868     est_datum_printf(datum, "<p><strong>%@</strong> was created successfully.</p>\n", name);
2869     } else {
2870     est_datum_printf(datum, "<p>Some errors occurred.</p>\n");
2871     }
2872     est_datum_printf(datum, "<hr />\n");
2873     break;
2874     case UI_DELNODE:
2875     if(g_runmode == RM_RDONLY){
2876     est_datum_printf(datum, "<p>The server is in read only mode.</p>\n");
2877     } else if(sure){
2878     if(nmgr_out(g_nmgr, name)){
2879     est_datum_printf(datum, "<p><strong>%@</strong> was deleted successfully.</p>\n", name);
2880     } else {
2881     est_datum_printf(datum, "<p>Some errors occurred.</p>\n");
2882     }
2883     } else {
2884     est_datum_printf(datum, "<form method=\"post\" action=\"%@\">\n", MASTERUILOC);
2885     est_datum_printf(datum, "<div class=\"form\">\n");
2886     est_datum_printf(datum, "confirmation to delete <strong>%@</strong>:\n", name);
2887     est_datum_printf(datum, "<input type=\"submit\" value=\"delete\" />\n");
2888     est_datum_printf(datum, "<input type=\"hidden\" name=\"action\" value=\"%d\" />\n",
2889     UI_DELNODE);
2890     est_datum_printf(datum, "<input type=\"hidden\" name=\"name\" value=\"%@\" />\n", name);
2891     est_datum_printf(datum, "<input type=\"hidden\" name=\"sure\" value=\"1\" />\n");
2892     est_datum_printf(datum, "</div>\n");
2893     est_datum_printf(datum, "</form>\n");
2894     }
2895     est_datum_printf(datum, "<hr />\n");
2896     break;
2897     case UI_EDITNODE:
2898     if(!(tnode = nmgr_get(g_nmgr, name))){
2899     est_datum_printf(datum, "<p>Some errors occurred.</p>\n");
2900     est_datum_printf(datum, "<hr />\n");
2901     } else if(admins){
2902     cbmapclose(tnode->admins);
2903     tnode->admins = cbmapopenex(MINIBNUM);
2904     list = cbsplit(admins, -1, "\r\n");
2905     for(i = 0; i < cblistnum(list); i++){
2906     pbuf = cbmemdup(cblistval(list, i, NULL), -1);
2907     cbstrtrim(pbuf);
2908     if(pbuf[0] != '\0' && check_alnum_name(pbuf))
2909     cbmapput(tnode->admins, pbuf, -1, "", 0, TRUE);
2910     free(pbuf);
2911     }
2912     cblistclose(list);
2913     est_datum_printf(datum, "<p>The list of administrators was updated.</p>\n");
2914     est_datum_printf(datum, "<hr />\n");
2915     } else if(users){
2916     cbmapclose(tnode->users);
2917     tnode->users = cbmapopenex(MINIBNUM);
2918     list = cbsplit(users, -1, "\r\n");
2919     for(i = 0; i < cblistnum(list); i++){
2920     pbuf = cbmemdup(cblistval(list, i, NULL), -1);
2921     cbstrtrim(pbuf);
2922     if(pbuf[0] != '\0' && check_alnum_name(pbuf))
2923     cbmapput(tnode->users, pbuf, -1, "", 0, TRUE);
2924     free(pbuf);
2925     }
2926     cblistclose(list);
2927     est_datum_printf(datum, "<p>The list of normal users was updated.</p>\n");
2928     est_datum_printf(datum, "<hr />\n");
2929     } else if(links){
2930     cbmapclose(tnode->links);
2931     tnode->links = cbmapopenex(MINIBNUM);
2932     list = cbsplit(links, -1, "\r\n");
2933     for(i = 0; i < cblistnum(list); i++){
2934     pbuf = cbmemdup(cblistval(list, i, NULL), -1);
2935     cbstrtrim(pbuf);
2936     if((pv = strstr(pbuf, DELIMSTR)) != NULL && (nv = strstr(pv + 1, DELIMSTR)) != NULL){
2937     *pv = '\0';
2938     pv += strlen(DELIMSTR);
2939     *nv = '\0';
2940     nv += strlen(DELIMSTR);
2941     node_set_link(tnode, pbuf, pv, atoi(nv));
2942     }
2943     free(pbuf);
2944     }
2945     cblistclose(list);
2946    
2947     est_datum_printf(datum, "<p>The list of links was updated.</p>\n");
2948     est_datum_printf(datum, "<hr />\n");
2949     }
2950     break;
2951     default:
2952     break;
2953     }
2954     if(vml){
2955     est_datum_printf(datum, "<ul>\n");
2956     est_datum_printf(datum, "<li>");
2957     est_datum_printf(datum, "<a href=\"%@?action=%d\">SHUTDOWN</a>", MASTERUILOC, UI_SHUTDOWN);
2958     est_datum_printf(datum, "</li>\n");
2959     est_datum_printf(datum, "</ul>\n");
2960     est_datum_printf(datum, "<hr />\n");
2961     }
2962     if(vul){
2963     list = umgr_names(g_umgr);
2964     est_datum_printf(datum, "<table summary=\"nodes\">\n");
2965     est_datum_printf(datum, "<tr>\n");
2966     est_datum_printf(datum, "<th abbr=\"name\">name</th>\n");
2967     est_datum_printf(datum, "<th abbr=\"passwd\">password</th>\n");
2968     est_datum_printf(datum, "<th abbr=\"flags\">flags</th>\n");
2969     est_datum_printf(datum, "<th abbr=\"fname\">full name</th>\n");
2970     est_datum_printf(datum, "<th abbr=\"misc\">misc</th>\n");
2971     est_datum_printf(datum, "<th abbr=\"actions\">actions</th>\n");
2972     est_datum_printf(datum, "</tr>\n");
2973     for(i = 0; i < cblistnum(list); i++){
2974     if(!(tmp = cblistval(list, i, NULL)) || !(tuser = umgr_get(g_umgr, tmp))) continue;
2975     pbuf = cbmemdup(tuser->passwd, -1);
2976     if(strlen(pbuf) > 8) sprintf(pbuf + 4, "...");
2977     est_datum_printf(datum, "<tr>\n");
2978     est_datum_printf(datum, "<td>%@</td>\n", tuser->name);
2979     est_datum_printf(datum, "<td>%@</td>\n", pbuf);
2980     est_datum_printf(datum, "<td>%@</td>\n", tuser->flags);
2981     est_datum_printf(datum, "<td>%@</td>\n", tuser->fname);
2982     est_datum_printf(datum, "<td>%@</td>\n", tuser->misc);
2983     est_datum_printf(datum, "<td>");
2984     est_datum_printf(datum, "<a href=\"%@?action=%d&amp;name=%?\">DELE</a>",
2985     MASTERUILOC, UI_DELUSER, tuser->name);
2986     est_datum_printf(datum, "</td>\n");
2987     est_datum_printf(datum, "</tr>\n");
2988     free(pbuf);
2989     }
2990     est_datum_printf(datum, "</table>\n");
2991     cblistclose(list);
2992     est_datum_printf(datum, "<form method=\"post\" action=\"%@\">\n", MASTERUILOC);
2993     est_datum_printf(datum, "<div class=\"form\">\n");
2994     est_datum_printf(datum, "<input type=\"text\" name=\"name\" value=\"\" size=\"12\" />\n");
2995     est_datum_printf(datum, "<input type=\"password\" name=\"passwd\" value=\"\""
2996     " size=\"12\" />\n");
2997     est_datum_printf(datum, "<input type=\"text\" name=\"flags\" value=\"\" size=\"4\" />\n");
2998     est_datum_printf(datum, "<input type=\"text\" name=\"fname\" value=\"\" size=\"12\" />\n");
2999     est_datum_printf(datum, "<input type=\"text\" name=\"misc\" value=\"\" size=\"12\" />\n");
3000     est_datum_printf(datum, "<input type=\"submit\" value=\"create\" />\n");
3001     est_datum_printf(datum, "<input type=\"hidden\" name=\"action\" value=\"%d\" />\n",
3002     UI_NEWUSER);
3003     est_datum_printf(datum, "</div>\n");
3004     est_datum_printf(datum, "</form>\n");
3005     est_datum_printf(datum, "<hr />\n");
3006     }
3007     if(vnl){
3008     list = nmgr_names(g_nmgr);
3009     est_datum_printf(datum, "<table summary=\"nodes\">\n");
3010     est_datum_printf(datum, "<tr>\n");
3011     est_datum_printf(datum, "<th abbr=\"name\">name</th>\n");
3012     est_datum_printf(datum, "<th abbr=\"label\">label</th>\n");
3013     est_datum_printf(datum, "<th abbr=\"docnum\">#docs</th>\n");
3014     est_datum_printf(datum, "<th abbr=\"wordnum\">#words</th>\n");
3015     est_datum_printf(datum, "<th abbr=\"size\">size</th>\n");
3016     est_datum_printf(datum, "<th abbr=\"adminnum\">#a</th>\n");
3017     est_datum_printf(datum, "<th abbr=\"usernum\">#u</th>\n");
3018     est_datum_printf(datum, "<th abbr=\"linknum\">#l</th>\n");
3019     est_datum_printf(datum, "<th abbr=\"actions\">actions</th>\n");
3020     est_datum_printf(datum, "</tr>\n");
3021     for(i = 0; i < cblistnum(list); i++){
3022     if(!(tmp = cblistval(list, i, NULL)) || !(tnode = nmgr_get(g_nmgr, tmp))) continue;
3023     est_datum_printf(datum, "<tr>\n");
3024     est_datum_printf(datum, "<td>%@</td>\n", tnode->name);
3025     est_datum_printf(datum, "<td>%@</td>\n", tnode->label);
3026     est_datum_printf(datum, "<td>%d</td>\n", est_mtdb_doc_num(tnode->db));
3027     est_datum_printf(datum, "<td>%d</td>\n", est_mtdb_word_num(tnode->db));
3028     est_datum_printf(datum, "<td>%.1f</td>\n", est_mtdb_size(tnode->db) / 1024.0 / 1024.0);
3029     est_datum_printf(datum, "<td>%d</td>\n", cbmaprnum(tnode->admins));
3030     est_datum_printf(datum, "<td>%d</td>\n", cbmaprnum(tnode->users));
3031     est_datum_printf(datum, "<td>%d</td>\n", cbmaprnum(tnode->links));
3032     est_datum_printf(datum, "<td>");
3033     est_datum_printf(datum, "<a href=\"%@?action=%d&amp;name=%?\">EDIT</a>",
3034     MASTERUILOC, UI_EDITNODE, tnode->name);
3035     est_datum_printf(datum, " / <a href=\"%@?action=%d&amp;name=%?\">DELE</a>",
3036     MASTERUILOC, UI_DELNODE, tnode->name);
3037     est_datum_printf(datum, "</td>\n");
3038     est_datum_printf(datum, "</tr>\n");
3039     }
3040     est_datum_printf(datum, "</table>\n");
3041     cblistclose(list);
3042     est_datum_printf(datum, "<form method=\"post\" action=\"%@\">\n", MASTERUILOC);
3043     est_datum_printf(datum, "<div class=\"form\">\n");
3044     est_datum_printf(datum, "<input type=\"text\" name=\"name\" value=\"\" size=\"12\" />\n");
3045     est_datum_printf(datum, "<input type=\"text\" name=\"label\" value=\"\" size=\"24\" />\n");
3046     est_datum_printf(datum, "<input type=\"submit\" value=\"create\" />\n");
3047     est_datum_printf(datum, "<input type=\"hidden\" name=\"action\" value=\"%d\" />\n",
3048     UI_NEWNODE);
3049     est_datum_printf(datum, "</div>\n");
3050     est_datum_printf(datum, "</form>\n");
3051     est_datum_printf(datum, "<hr />\n");
3052     }
3053     if(vne){
3054     if((tnode = nmgr_get(g_nmgr, name)) != NULL){
3055     est_datum_printf(datum, "<dl class=\"tlist\">\n");
3056     est_datum_printf(datum, "<dt>name</dt>\n");
3057     est_datum_printf(datum, "<dd>%@</dd>\n", tnode->name);
3058     est_datum_printf(datum, "</dl>\n");
3059     est_datum_printf(datum, "<dl class=\"tlist\">\n");
3060     est_datum_printf(datum, "<dt>label</dt>\n");
3061     est_datum_printf(datum, "<dd>%@</dd>\n", tnode->label);
3062     est_datum_printf(datum, "</dl>\n");
3063     est_datum_printf(datum, "<dl class=\"tlist\">\n");
3064     est_datum_printf(datum, "<dt>number of documents</dt>\n");
3065     est_datum_printf(datum, "<dd>%d</dd>\n", est_mtdb_doc_num(tnode->db));
3066     est_datum_printf(datum, "</dl>\n");
3067     est_datum_printf(datum, "<dl class=\"tlist\">\n");
3068     est_datum_printf(datum, "<dt>number of unique words</dt>\n");
3069     est_datum_printf(datum, "<dd>%d</dd>\n", est_mtdb_word_num(tnode->db));
3070     est_datum_printf(datum, "</dl>\n");
3071     est_datum_printf(datum, "<dl class=\"tlist\">\n");
3072     est_datum_printf(datum, "<dt>size of the database</dt>\n");
3073     est_datum_printf(datum, "<dd>%.1fMB</dd>\n", est_mtdb_size(tnode->db) / 1024.0 / 1024.0);
3074     est_datum_printf(datum, "</dl>\n");
3075     est_datum_printf(datum, "<form method=\"post\" action=\"%@\">\n", MASTERUILOC);
3076     est_datum_printf(datum, "<dl class=\"tlist\">\n");
3077     est_datum_printf(datum, "<dt>list of administrators</dt>\n");
3078     est_datum_printf(datum, "<dd>");
3079     est_datum_printf(datum, "<textarea name=\"admins\" cols=\"80\" rows=\"5\">");
3080     cbmapiterinit(tnode->admins);
3081     while((tmp = cbmapiternext(tnode->admins, NULL)) != NULL){
3082     est_datum_printf(datum, "%@\n", tmp);
3083     }
3084     est_datum_printf(datum, "</textarea>");
3085     est_datum_printf(datum, "</dd>\n");
3086     est_datum_printf(datum, "<dd>");
3087     est_datum_printf(datum, "<input type=\"submit\" value=\"change\" />");
3088     est_datum_printf(datum, "</dd>\n");
3089     est_datum_printf(datum, "</dl>\n");
3090     est_datum_printf(datum, "<div class=\"hidden\">\n");
3091     est_datum_printf(datum, "<input type=\"hidden\" name=\"action\" value=\"%d\" />\n",
3092     UI_EDITNODE);
3093     est_datum_printf(datum, "<input type=\"hidden\" name=\"name\" value=\"%@\" />\n", name);
3094     est_datum_printf(datum, "</div>\n");
3095     est_datum_printf(datum, "</form>\n");
3096     est_datum_printf(datum, "<form method=\"post\" action=\"%@\">\n", MASTERUILOC);
3097     est_datum_printf(datum, "<dl class=\"tlist\">\n");
3098     est_datum_printf(datum, "<dt>list of normal users</dt>\n");
3099     est_datum_printf(datum, "<dd>");
3100     est_datum_printf(datum, "<textarea name=\"users\" cols=\"80\" rows=\"5\">");
3101     cbmapiterinit(tnode->users);
3102     while((tmp = cbmapiternext(tnode->users, NULL)) != NULL){
3103     est_datum_printf(datum, "%@\n", tmp);
3104     }
3105     est_datum_printf(datum, "</textarea>");
3106     est_datum_printf(datum, "</dd>\n");
3107     est_datum_printf(datum, "<dd>");
3108     est_datum_printf(datum, "<input type=\"submit\" value=\"change\" />");
3109     est_datum_printf(datum, "</dd>\n");
3110     est_datum_printf(datum, "</dl>\n");
3111     est_datum_printf(datum, "<div class=\"hidden\">\n");
3112     est_datum_printf(datum, "<input type=\"hidden\" name=\"action\" value=\"%d\" />\n",
3113     UI_EDITNODE);
3114     est_datum_printf(datum, "<input type=\"hidden\" name=\"name\" value=\"%@\" />\n", name);
3115     est_datum_printf(datum, "</div>\n");
3116     est_datum_printf(datum, "</form>\n");
3117     est_datum_printf(datum, "<form method=\"post\" action=\"%@\">\n", MASTERUILOC);
3118     est_datum_printf(datum, "<dl class=\"tlist\">\n");
3119     est_datum_printf(datum, "<dt>list of links</dt>\n");
3120     est_datum_printf(datum, "<dd>");
3121     est_datum_printf(datum, "<textarea name=\"links\" cols=\"80\" rows=\"5\">");
3122     cbmapiterinit(tnode->links);
3123     while((tmp = cbmapiternext(tnode->links, NULL)) != NULL){
3124     pbuf = cbmemdup(cbmapget(tnode->links, tmp, -1, NULL), -1);
3125     if((pv = strchr(pbuf, '\t')) != NULL){
3126     *(pv++) = '\0';
3127     est_datum_printf(datum, "%@%@%@%@%@\n", tmp, DELIMSTR, pbuf, DELIMSTR, pv);
3128     }
3129     free(pbuf);
3130     }
3131     est_datum_printf(datum, "</textarea>");
3132     est_datum_printf(datum, "</dd>\n");
3133     est_datum_printf(datum, "<dd>");
3134     est_datum_printf(datum, "<input type=\"submit\" value=\"change\" />");
3135     est_datum_printf(datum, "</dd>\n");
3136     est_datum_printf(datum, "</dl>\n");
3137     est_datum_printf(datum, "<div class=\"hidden\">\n");
3138     est_datum_printf(datum, "<input type=\"hidden\" name=\"action\" value=\"%d\" />\n",
3139     UI_EDITNODE);
3140     est_datum_printf(datum, "<input type=\"hidden\" name=\"name\" value=\"%@\" />\n", name);
3141     est_datum_printf(datum, "</div>\n");
3142     est_datum_printf(datum, "</form>\n");
3143     } else {
3144     est_datum_printf(datum, "<p>Some errors occured.</p>\n");
3145     }
3146     est_datum_printf(datum, "<hr />\n");
3147     }
3148     est_datum_printf(datum, "<div class=\"logo\">Powered by Hyper Estraier %@.</div>\n",
3149     est_version);
3150     est_datum_printf(datum, "</body>\n");
3151     est_datum_printf(datum, "</html>\n");
3152     est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum));
3153     cbdatumclose(datum);
3154     log_print(LL_DEBUG, "[%s:%d]: 200 (ok - ui:%d)", req->claddr, req->clport, action);
3155     if(!rwlock_unlock(g_mgrlock)) log_print(LL_ERROR, "unlocking failed");
3156     }
3157    
3158    
3159     /* send a file data */
3160     static void sendfiledata(int clsock, REQUEST *req){
3161     struct stat sbuf;
3162     CBLIST *list;
3163     CBDATUM *datum;
3164     const char *elem;
3165     char *path, *index, pbuf[URIBUFSIZ], ibuf[IOBUFSIZ], *ext, *tmp;
3166     int i, fd, len, fdir;
3167     time_t ims;
3168     double size;
3169     if(g_docroot[0] == '\0'){
3170     senderror(clsock, req, 403, "Forbidden");
3171     return;
3172     }
3173     path = makelocalpath(req->target);
3174     if(g_indexfile[0] != '\0'){
3175     index = cbsprintf("%s%c%s", path, ESTPATHCHR, g_indexfile);
3176     if(stat(index, &sbuf) == 0 && cbstrbwmatch(req->target, "/")){
3177     free(path);
3178     path = index;
3179     index = NULL;
3180     }
3181     free(index);
3182     }
3183     if((list = cbdirlist(path)) != NULL){
3184     datum = cbdatumopen("", 0);
3185     if(cbstrbwmatch(req->target, "/")){
3186     cblistsort(list);
3187     est_datum_printf(datum, "HTTP/1.0 200 OK\r\n");
3188     addservinfo(datum);
3189     est_datum_printf(datum, "Content-Type: text/html\r\n");
3190     est_datum_printf(datum, "\r\n");
3191     if(req->method != HM_HEAD){
3192     est_datum_printf(datum, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
3193     est_datum_printf(datum, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\""
3194     " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n");
3195     est_datum_printf(datum, "<html xmlns=\"http://www.w3.org/1999/xhtml\""
3196     " xml:lang=\"en\" lang=\"en\">\n");
3197     est_datum_printf(datum, "<head>\n");
3198     est_datum_printf(datum, "<meta http-equiv=\"Content-Type\" content=\"text/html\" />\n");
3199     est_datum_printf(datum, "<meta http-equiv=\"Content-Style-Type\""
3200     " content=\"text/css\" />\n");
3201     est_datum_printf(datum, "<title>%@</title>\n", req->target);
3202     est_datum_printf(datum, "<style type=\"text/css\">"
3203     "html { margin: 0em 0em; padding 0em 0em;"
3204     " background: #eeeeee none; }\n");
3205     est_datum_printf(datum, "body { margin: 2em 2em; padding 0em 0em;");
3206     est_datum_printf(datum, " background: #eeeeee none; color: #111111;");
3207     est_datum_printf(datum, " font-style: normal; font-weight: normal; }\n");
3208     est_datum_printf(datum, "a { color: #0022aa; text-decoration: none; }\n");
3209     est_datum_printf(datum, "a:hover,a:focus { color: #0033ee;"
3210     " text-decoration: underline; }\n");
3211     est_datum_printf(datum, "</style>\n");
3212     est_datum_printf(datum, "</head>\n");
3213     est_datum_printf(datum, "<body>\n");
3214     est_datum_printf(datum, "<h1>Index of %@</h1>\n", req->target);
3215     est_datum_printf(datum, "<hr />\n");
3216     est_datum_printf(datum, "<ul>\n");
3217     if(strcmp(req->target, "/"))
3218     est_datum_printf(datum, "<li><a href=\"../\">../</a></li>\n");
3219     for(i = 0; i < cblistnum(list); i++){
3220     elem = cblistval(list, i, NULL);
3221     if(!strcmp(elem, ESTCDIRSTR) || !strcmp(elem, ESTPDIRSTR)) continue;
3222     sprintf(pbuf, "%s%c%s", path, ESTPATHCHR, elem);
3223     if(!cbfilestat(pbuf, &fdir, NULL, NULL)) continue;
3224     est_datum_printf(datum, "<li><a href=\"%?%s\">%@%s</a></li>\n",
3225     elem, fdir ? "/" : "", elem, fdir ? "/" : "");
3226     }
3227     est_datum_printf(datum, "</ul>\n");
3228     est_datum_printf(datum, "<hr />\n");
3229     est_datum_printf(datum, "<address>%@/%@ at %@:%d</address>\n",
3230     SERVNAME, est_version, g_hostname, g_portnum);
3231     est_datum_printf(datum, "</body>\n");
3232     est_datum_printf(datum, "</html>\n");
3233     }
3234     log_print(LL_DEBUG, "[%s:%d]: 200 (ok - directory)", req->claddr, req->clport);
3235     } else {
3236     cblistsort(list);
3237     est_datum_printf(datum, "HTTP/1.0 301 Moved Parmanently\r\n");
3238     addservinfo(datum);
3239     if(req->host){
3240     est_datum_printf(datum, "Location: http://%s%s/\r\n", req->host, req->target);
3241     } else {
3242     est_datum_printf(datum, "Location: http://%s:%d%s/\r\n",
3243     g_hostname, g_portnum, req->target);
3244     }
3245     est_datum_printf(datum, "Content-Type: text/plain\r\n");
3246     est_datum_printf(datum, "\r\n");
3247     if(req->method != HM_HEAD) est_datum_printf(datum, "Go to %s/\n", req->target);
3248     log_print(LL_DEBUG, "[%s:%d]: 301 (moved parmanently)", req->claddr, req->clport);
3249     }
3250     est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum));
3251     cbdatumclose(datum);
3252     cblistclose(list);
3253     } else if((fd = open(path, O_RDONLY, 0)) != -1){
3254     if(fstat(fd, &sbuf) == 0){
3255     ims = sbuf.st_mtime;
3256     size = sbuf.st_size;
3257     } else {
3258     ims = -1;
3259     size = -1.0;
3260     }
3261     if(req->ims > 0 && ims <= req->ims){
3262     senderror(clsock, req, 304, "Not Modified");
3263     } else {
3264     datum = cbdatumopen("", 0);
3265     est_datum_printf(datum, "HTTP/1.0 200 OK\r\n");
3266     addservinfo(datum);
3267     if(ims > 0){
3268     tmp = cbdatestrhttp(ims, 0);
3269     est_datum_printf(datum, "Last-Modified: %s\r\n", tmp);
3270     free(tmp);
3271     }
3272     if(size >= 0.0) est_datum_printf(datum, "Content-Length: %.0f\r\n", size);
3273     ext = strrchr(path, ESTPATHCHR);
3274     ext = strrchr(ext ? ext : path, '.');
3275     est_datum_printf(datum, "Content-Type: %s\r\n", est_ext_type(ext ? ext : ""));
3276     est_datum_printf(datum, "\r\n");
3277     est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum));
3278     if(req->method != HM_HEAD){
3279     while((len = read(fd, ibuf, IOBUFSIZ)) > 0 && !g_sigterm){
3280     send(clsock, ibuf, len, 0);
3281     }
3282     }
3283     cbdatumclose(datum);
3284     log_print(LL_DEBUG, "[%s:%d]: 200 (ok - file)", req->claddr, req->clport);
3285     }
3286     close(fd);
3287     } else {
3288     senderror(clsock, req, 404, "Not Found");
3289     }
3290     free(path);
3291     }
3292    
3293    
3294     /* make the local path of a target */
3295     static char *makelocalpath(const char *target){
3296     CBLIST *list;
3297     CBDATUM *datum;
3298     const char *elem;
3299     char *tmp;
3300     int i;
3301     datum = cbdatumopen(g_docroot, -1);
3302     list = cbsplit(target, -1, "/");
3303     for(i = 0; i < cblistnum(list); i++){
3304     elem = cblistval(list, i, NULL);
3305     if(elem[0] == '\0') continue;
3306     tmp = cburldecode(elem, NULL);
3307     est_datum_printf(datum, "%c%s", ESTPATHCHR, tmp);
3308     free(tmp);
3309     }
3310     cblistclose(list);
3311     return cbdatumtomalloc(datum, NULL);
3312     }
3313    
3314    
3315    
3316     /* END OF FILE */

  ViewVC Help
Powered by ViewVC 1.1.26