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

Contents of /upstream/0.5.3/estmaster.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 10 - (show annotations)
Wed Aug 3 15:25:48 2005 UTC (18 years, 9 months ago) by dpavlin
File MIME type: text/plain
File size: 130773 byte(s)
import of upstream 0.5.3

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

  ViewVC Help
Powered by ViewVC 1.1.26