/[hyperestraier]/upstream/0.5.2/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 /upstream/0.5.2/estmaster.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 9 - (hide annotations)
Wed Aug 3 15:21:15 2005 UTC (18 years, 9 months ago) by dpavlin
File MIME type: text/plain
File size: 130773 byte(s)
import upstream version 0.5.2

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

  ViewVC Help
Powered by ViewVC 1.1.26