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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (hide annotations)
Fri Jul 29 21:52:03 2005 UTC (18 years, 10 months ago) by dpavlin
File MIME type: text/plain
File size: 98431 byte(s)
import of HyperEstraier 0.5.0

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

  ViewVC Help
Powered by ViewVC 1.1.26