/[hyperestraier]/trunk/estmaster.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/estmaster.c

Parent Directory Parent Directory | Revision Log Revision Log


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

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

  ViewVC Help
Powered by ViewVC 1.1.26