/[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

Contents of /upstream/0.5.0/estmaster.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (show 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 /*************************************************************************************************
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