/************************************************************************************************* * The master of node servers * Copyright (C) 2004-2007 Mikio Hirabayashi * This file is part of Hyper Estraier. * Hyper Estraier is free software; you can redistribute it and/or modify it under the terms of * the GNU Lesser General Public License as published by the Free Software Foundation; either * version 2.1 of the License or any later version. Hyper Estraier is distributed in the hope * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * You should have received a copy of the GNU Lesser General Public License along with Hyper * Estraier; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include "mastermod.h" #include "myimage.dat" #define SERVNAME "estmaster" /* name of the server */ #define IOBUFSIZ 8192 /* size of a buffer for I/O */ #define SPCACHEMNUM 131072 /* max number of the special cache */ #define IREFRESHSEC 3 /* wait time for refreshing and charging */ #define ICHARGENUM 16 /* number of keys per charging */ #define IFLUSHNUM 32768 /* number of keys per flushing */ #define LINKMASKMAX 9 /* maximum number of link masks */ #define LCSRCHRESMIN 31 /* minimum number of result to get from local */ #define RMSRCHRESMIN 16 /* minimum number of result to get from local */ #define FCLOSECONNNUM 10 /* number of over connections for forced closing */ #define FCLOSEWAITMAX 10 /* wait time for forced closing */ #define FTERMWAITMAX 60 /* wait time for forced termination */ #define HELPERTRYNUM 3 /* number of trys of helper command */ #define UICACHELIFE 1800 /* maximum age of image cache data */ #define IMGCACHELIFE (3600 * 24) /* maximum age of image cache data */ #define MASTERLOC "/master" /* location of the URL of the master */ #define NODEPREFIX "/node/" /* prefix of URLs of nodes */ #define MASTERUILOC "/master_ui" /* location of administration user intarfaces */ #define FAVICONLOC "/favicon.ico" /* location of the favorite icon */ #define IMAGEPREFIX "/image/" /* prefix of URLs of images */ #define SEARCHUICMD "search_ui" /* name of search user interface */ #define SEARCHATOMCMD "search_atom" /* name of Atom feed interface */ #define SEARCHRSSCMD "search_rss" /* name of RSS feed interface */ #define OPENSEARCHCMD "opensearch" /* name of OpenSearch Description */ #define BIGICONNAME "bigicon.png" /* name of the big icon */ #define CANVASNAME "canvas.png" /* name of the backgroud pattern */ #define DELIMSTR "{{!}}" /* delimiter string */ #define UIMIMETYPE "text/html; charset=UTF-8" /* media type of search_ui */ #define ATOMMIMETYPE "application/atom+xml" /* media type of search_atom */ #define RSSMIMETYPE "application/rdf+xml" /* media type of search_rss */ #define OSRCHMIMETYPE "application/opensearchdescription+xml" /* media type of opensearch */ #define DIRMIMETYPE "text/html" /* media type of directory index */ #define MENUMIMETYPE "text/html" /* media type of the menu page */ #define DEFMIMETYPE "application/octet-stream" /* media type by default */ typedef struct { /* type of structure for a communication */ int clsock; /* socket between client */ char claddr[ADDRBUFSIZ]; /* address of the client */ int clport; /* port number of the client */ } TARGCOMM; enum { /* enumeration for HTTP method */ HM_HEAD, /* HEAD */ HM_GET, /* GET */ HM_POST, /* POST */ HM_OPTIONS, /* OPTIONS */ HM_UNKNOWN /* unknown */ }; typedef struct { /* type of structure for a request */ const char *claddr; /* address of the client */ int clport; /* port number of the client */ char prefix[HOSTBUFSIZ]; /* prefix of the server */ int method; /* method */ char *target; /* target path */ char *name; /* user name */ char *passwd; /* password */ time_t ims; /* if-modified-since header */ int gzip; /* whether to accept gzip encoding */ int deflate; /* whether to accept deflate encoding */ char *ctype; /* content-type header */ char *referer; /* referer header */ char *estvia; /* x-estraier-via header */ char *body; /* entity body */ CBMAP *params; /* parameters */ time_t now; /* current time */ int reload; /* whether to be reloaded */ } REQUEST; typedef struct { /* type of structure for a local search */ REQUEST *req; /* request object */ int alive; /* whether to be alive */ ESTCOND *cond; /* search condition */ CBMAP *hints; /* hints about result */ int max; /* max number of retrieved documents */ NODE *node; /* self node object */ RESMAP *resmap; /* documents in result */ CBLIST *words; /* words in the search phrase */ int hnum; /* number of hits */ int mhnum; /* number of mishits */ double itime; /* index elapsed time */ double etime; /* total elapsed time */ } TARGLCSRCH; typedef struct { /* type of structure for a remote search */ REQUEST *req; /* request object */ int alive; /* whether to be alive */ const char *myurl; /* url of the local node */ ESTCOND *cond; /* search condition */ CBMAP *hints; /* hints about result */ int max; /* max number of retrieved documents */ NODE *node; /* self node object */ RESMAP *resmap; /* documents in result */ const char *url; /* URL of the link */ int credit; /* credit of the link */ char *label; /* label of the link */ int depth; /* depth of meta search */ int wwidth; /* while width of a snippet */ int hwidth; /* top width of a snippet */ int awidth; /* around width of a snippet */ int hnum; /* number of hits */ int dnum; /* number of documents */ int wnum; /* number of words */ double etime; /* elapsed time */ double size; /* size of the database */ time_t mtime; /* modification time */ } TARGRMSRCH; enum { /* enumeration for UI operations */ UI_VIEWMASTER, /* view master operations */ UI_SHUTDOWN, /* shutdown the master server */ UI_SYNCNODES, /* synchronize all nodes */ UI_BACKUPDB, /* backup the database */ UI_VIEWUSERS, /* view user operations */ UI_NEWUSER, /* create a user */ UI_DELUSER, /* delete a user */ UI_VIEWNODES, /* view node operations */ UI_NEWNODE, /* create a node */ UI_DELENODE, /* delete a node */ UI_EDITNODE, /* edit a node */ UI_STATNODE, /* view search history of a node */ UI_NONE /* none */ }; /* global variables */ const char *g_progname; /* program name */ int g_sigterm = FALSE; /* flag for termination signal */ int g_sigrestart = FALSE; /* flag for restarting signal */ int g_sigsync = FALSE; /* flag for synchronus signal */ int g_sigback = FALSE; /* flag for backup signal */ const char *g_rootdir = NULL; /* path of the root directory */ const char *g_bindaddr = NULL; /* binding address of TCP */ int g_portnum = 0; /* port number of TCP */ const char *g_publicurl = NULL; /* public URL */ int g_runmode = 0; /* runnning mode */ int g_authmode = 0; /* authorization mode */ int g_recvmax = 0; /* maximum length of data to receive */ int g_maxconn = 0; /* maximum number of connections */ int g_idleflush = 0; /* idle time to start flushing */ int g_idlesync = 0; /* idle time to start synchronizing */ int g_sessiontimeout = 0; /* timeout of a session, in seconds */ int g_searchtimeout = 0; /* timeout of search, in seconds */ int g_searchmax = 0; /* maximum number of documents to send */ int g_searchdepth = 0; /* depth of meta search */ int g_rateuri = FALSE; /* whether to rate URI for scoring */ int g_mergemethod = 0; /* merge method */ const char *g_proxyhost = NULL; /* host name of the proxy */ int g_proxyport = 0; /* port number of the proxy */ const char *g_logfile = NULL; /* path of the log file */ int g_loglevel = 0; /* logging level */ const char *g_backupcmd = NULL; /* path of the backup command */ int g_scalepred = 0; /* scale prediction */ int g_scoreexpr = 0; /* score expression */ CBLIST *g_attrindexes = NULL; /* expressions of attribute indexes */ const char *g_docroot = NULL; /* path of the document root directory */ const char *g_indexfile = NULL; /* name of the directory index file */ CBMAP *g_trustednodes = NULL; /* addresses of trusted nodes */ int g_denyuntrusted = FALSE; /* whether to deny untrusted nodes */ double g_cachesize = 0.0; /* maximum size of the index cache */ int g_cacheanum = 0; /* max of cached records for document attributes */ int g_cachetnum = 0; /* max of cached records for document texts */ int g_cachernum = 0; /* max of cached records for occurrence results */ const char *g_specialcache = NULL; /* name of the attribute of special cache */ double g_helpershift = 0.0; /* lower limit of cache usage size to use the helper */ int g_wildmax = -1; /* maximum number of extension of wild cards */ int g_limittextsize = 0; /* text size limitation */ int g_snipwwidth = 0; /* whole width of the snippet */ int g_sniphwidth = 0; /* width of beginning of the text */ int g_snipawidth = 0; /* width around each highlighted word */ int g_scancheck = FALSE; /* whether to check documents by scanning */ int g_smlrvnum = 0; /* number of keywords for similarity search */ int g_extdelay = 0; /* number of documents for delay of extraction */ const char *g_adminemail = NULL; /* e-mail address of the administrator */ CBLIST *g_uireplaces = NULL; /* expressions to replace the URI of each document */ CBLIST *g_uiextattrs = NULL; /* extra attributes to be shown */ int g_uiphraseform = 0; /* mode of phrase form */ const char *g_uismlrtune = NULL; /* tuning parameters for similarity search */ int g_bgmode = FALSE; /* whether to be foreground mode */ int g_romode = FALSE; /* whether to be read only mode */ int g_stmode = FALSE; /* whether to be single thread mode */ char g_hostname[HOSTBUFSIZ]; /* host name of the server */ DEPOT *g_metadb = NULL; /* meta database */ DEPOT *g_dfdb = NULL; /* document frequency database */ UMGR *g_umgr = NULL; /* user manager */ NMGR *g_nmgr = NULL; /* node manager */ RWLOCK *g_runlock = NULL; /* read-write lock for running threads */ RWLOCK *g_mgrlock = NULL; /* read-write lock for handling managers */ pthread_mutex_t g_backlock = PTHREAD_MUTEX_INITIALIZER; /* mutex for background tasks */ const char *g_bordstr = NULL; /* border string */ int g_svsock = -1; /* server socket */ int g_accesscount = 0; /* count of all access */ time_t g_startdate = 0; /* start date of the server */ double g_cacheratio = 0.0; /* worst usage ratio of the index cache */ /* function prototypes */ int main(int argc, char **argv); static int realmain(int argc, char **argv); static void setsignals(void); static void sigtermhandler(int num); static void usage(void); static int runinit(int argc, char **argv); static int runstart(int argc, char **argv); static int runstop(int argc, char **argv); static int rununittest(int argc, char **argv); static int runcrypt(int argc, char **argv); static void die(const char *format, ...); static void startup(void); static const char *skiplabel(const char *str); static void cleanup(void); static int procinit(const char *rootdir, int ex); static int procstart(const char *rootdir); static int procstop(const char *rootdir); static int procunittest(const char *rootdir); static int proccrypt(const char *key, const char *hash); static void dispatch(void); static void *refreshnode(void *targ); static void *flushnode(void *targ); static void *syncnodes(void *targ); static void backupdb(void); static void *communicate(void *targ); static void setparams(CBMAP *params, const char *src); static void addservinfo(CBDATUM *datum, time_t now, int ccage); static void senderror(int clsock, REQUEST *req, int code, const char *message); static void sendautherror(int clsock, REQUEST *req, const char *realm); static void sendnotmoderror(int clsock, REQUEST *req); static void sendheadonly(int clsock, REQUEST *req, const char *type); static int isbanned(USER *user); static int ismasteradmin(REQUEST *req, USER *user); static int isnodeadmin(NODE *node, REQUEST *req, USER *user); static int isnodeuser(NODE *node, REQUEST *req, USER *user); static int getnodeoptions(void); static void sendoptions(int clsock, REQUEST *req, USER *user); static void sendmasterdata(int clsock, REQUEST *req, USER *user); static void sendnodedata(int clsock, REQUEST *req, USER *user, const char *path); static void setdocorigin(ESTDOC *doc, REQUEST *req, NODE *node, int score); static void mergehints(CBMAP *total, CBMAP *local); static int islooproute(const char *url, REQUEST *req); static void *searchlocal(void *targ); static void *searchremote(void *targ); static void setsimilarphrase(ESTCOND *cond, const char *url, int id); static void catdocdata(CBDATUM *datum, NODE *node, RESDOC *resdoc, CBLIST *words, int wwidth, int hwidth, int awidth); static void catdocdataui(CBDATUM *datum, int num, RESDOC *resdoc, CBLIST *words, REQUEST *req, NODE *node, const char *condstr, const char *simcondstr); static void catdocdataatom(CBDATUM *datum, RESDOC *resdoc, CBLIST *words, NODE *node); static void catdocdatarss(CBDATUM *datum, RESDOC *resdoc, CBLIST *words, NODE *node); static char *makeshownuri(const char *uri); static void sendnodecmdinform(int clsock, REQUEST *req, NODE *node); static int sendnodecmdinformhelper(int clsock, REQUEST *req, NODE *node); static void sendnodecmdcacheusage(int clsock, REQUEST *req, NODE *node); static void sendnodecmdsearch(int clsock, REQUEST *req, NODE *node); static int sendnodecmdsearchhelper(int clsock, REQUEST *req, NODE *node, const char *myurl); static void sendnodecmdlist(int clsock, REQUEST *req, NODE *node); static void sendnodecmdgetdoc(int clsock, REQUEST *req, NODE *node); static int sendnodecmdgetdochelper(int clsock, REQUEST *req, NODE *node, int id, const char *uri); static void sendnodecmdgetdocattr(int clsock, REQUEST *req, NODE *node); static int sendnodecmdgetdocattrhelper(int clsock, REQUEST *req, NODE *node, int id, const char *uri, const char *attr); static void sendnodecmdetchdoc(int clsock, REQUEST *req, NODE *node); static int sendnodecmdetchdochelper(int clsock, REQUEST *req, NODE *node, int id, const char *uri); static void sendnodecmduritoid(int clsock, REQUEST *req, NODE *node); static int sendnodecmduritoidhelper(int clsock, REQUEST *req, NODE *node, const char *uri); static void sendnodecmdputdoc(int clsock, REQUEST *req, NODE *node); static void sendnodecmdoutdoc(int clsock, REQUEST *req, NODE *node); static void sendnodecmdeditdoc(int clsock, REQUEST *req, NODE *node); static void sendnodecmdsync(int clsock, REQUEST *req, NODE *node); static void sendnodecmdoptimize(int clsock, REQUEST *req, NODE *node); static void sendnodecmdsetuser(int clsock, REQUEST *req, NODE *node); static void sendnodecmdsetlink(int clsock, REQUEST *req, NODE *node); static void sendnodecmdsearchui(int clsock, REQUEST *req, NODE *node); static void sendnodecmdsearchatom(int clsock, REQUEST *req, NODE *node); static void sendnodecmdsearchrss(int clsock, REQUEST *req, NODE *node); static void sendnodecmdopensearch(int clsock, REQUEST *req, NODE *node); static void sendmasteruidata(int clsock, REQUEST *req, USER *user); static void sendfavicondata(int clsock, REQUEST *req); static void sendimagedata(int clsock, REQUEST *req, const char *name); static void sendfiledata(int clsock, REQUEST *req); static char *makelocalpath(const char *target); static void sendmenudata(int clsock, REQUEST *req); /* for windows service */ #if defined(MYWINSERV) SERVICE_STATUS_HANDLE g_svstat = 0; static void svmain(int argc, char **argv); static int sendstatus(int curstate, int exitcode, int svexitcode, int check, int hint); static void svfinish(int code); static void svhandle(int code); static void svmain(int argc, char **argv){ if(!(g_svstat = RegisterServiceCtrlHandler(SERVNAME, (LPHANDLER_FUNCTION)svhandle))){ svfinish(GetLastError()); return; } if(!sendstatus(SERVICE_START_PENDING, 0, 0, 1, 5000)){ svfinish(GetLastError()); return; } est_init_net_env(); startup(); if(!sendstatus(SERVICE_RUNNING, 0, 0, 0, 0)){ svfinish(GetLastError()); return; } dispatch(); cleanup(); svfinish(0); } static int sendstatus(int curstate, int exitcode, int svexitcode, int check, int hint){ SERVICE_STATUS st; st.dwServiceType = SERVICE_WIN32_OWN_PROCESS; st.dwCurrentState = curstate; if(curstate == SERVICE_START_PENDING){ st.dwControlsAccepted = 0; } else { st.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; } if(svexitcode == 0){ st.dwWin32ExitCode = exitcode; } else { st.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; } st.dwServiceSpecificExitCode = svexitcode; st.dwCheckPoint = check; st.dwWaitHint = hint; if(!SetServiceStatus(g_svstat, &st)){ g_sigterm = TRUE; return FALSE; } return TRUE; } static void svfinish(int code){ if(g_svstat) sendstatus(SERVICE_STOPPED, code, 0, 0, 0); } static void svhandle(int code){ switch(code){ case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_SHUTDOWN: sendstatus(SERVICE_STOP_PENDING, 0, 0, 1, 5000); g_sigterm = TRUE; break; case SERVICE_CONTROL_INTERROGATE: sendstatus(0, 0, 0, 0, 0); break; } } #endif /* main routine */ int main(int argc, char **argv){ #if defined(MYWINSERV) SERVICE_TABLE_ENTRY table[] = { { SERVNAME, (LPSERVICE_MAIN_FUNCTION)svmain }, { NULL, NULL } }; est_proc_env_reset(); g_progname = argv[0]; g_sigterm = FALSE; g_rootdir = argc >= 2 ? argv[1] : "casket"; if(!StartServiceCtrlDispatcher(table)) die("not called by service controll manager"); return 0; #else return realmain(argc, argv); #endif } /* real main routine */ static int realmain(int argc, char **argv){ const char *tmp; int rv; if((tmp = getenv("ESTDBGFD")) != NULL) dpdbgfd = atoi(tmp); est_proc_env_reset(); g_progname = argv[0]; g_sigterm = FALSE; if(argc < 2) usage(); rv = 0; if(!strcmp(argv[1], "init")){ rv = runinit(argc, argv); } else if(!strcmp(argv[1], "start")){ rv = runstart(argc, argv); } else if(!strcmp(argv[1], "stop")){ rv = runstop(argc, argv); } else if(!strcmp(argv[1], "unittest")){ rv = rununittest(argc, argv); } else if(!strcmp(argv[1], "crypt")){ rv = runcrypt(argc, argv); } else { usage(); } return rv; } /* set signal handlers */ static void setsignals(void){ est_signal(1, sigtermhandler); est_signal(2, sigtermhandler); est_signal(3, sigtermhandler); est_signal(13, SIG_IGN); est_signal(14, SIG_IGN); est_signal(15, sigtermhandler); g_sigterm = FALSE; g_sigrestart = FALSE; g_sigsync = FALSE; g_sigback = FALSE; } /* handler of termination signal */ static void sigtermhandler(int num){ g_sigterm = TRUE; if(num == 1 && g_bgmode) g_sigrestart = TRUE; } /* print the usage and exit */ static void usage(void){ fprintf(stderr, "%s: the master of node servers\n", g_progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s init [-ex] rootdir\n", g_progname); fprintf(stderr, " %s start [-bg] [-ro] [-st] rootdir\n", g_progname); fprintf(stderr, " %s stop rootdir\n", g_progname); fprintf(stderr, " %s unittest rootdir\n", g_progname); fprintf(stderr, " %s crypt key [hash]\n", g_progname); fprintf(stderr, "\n"); exit(1); } /* parse arguments of the init command */ static int runinit(int argc, char **argv){ char *rootdir; int i, ex, rv; rootdir = NULL; ex = FALSE; for(i = 2; i < argc; i++){ if(!rootdir && argv[i][0] == '-'){ if(!strcmp(argv[i], "-ex")){ ex = TRUE; } else { usage(); } } else if(!rootdir){ rootdir = argv[i]; } else { usage(); } } if(!rootdir) usage(); rv = procinit(rootdir, ex); return rv; } /* parse arguments of the start command */ static int runstart(int argc, char **argv){ char *rootdir; int i, rv; rootdir = NULL; for(i = 2; i < argc; i++){ if(!rootdir && argv[i][0] == '-'){ if(!strcmp(argv[i], "-bg")){ g_bgmode = TRUE; } else if(!strcmp(argv[i], "-ro")){ g_romode = TRUE; } else if(!strcmp(argv[i], "-st")){ g_stmode = TRUE; } else { usage(); } } else if(!rootdir){ rootdir = argv[i]; } else { usage(); } } if(!rootdir) usage(); rv = procstart(rootdir); return rv; } /* parse arguments of the stop command */ static int runstop(int argc, char **argv){ char *rootdir; int i, rv; rootdir = NULL; for(i = 2; i < argc; i++){ if(!rootdir && argv[i][0] == '-'){ usage(); } else if(!rootdir){ rootdir = argv[i]; } else { usage(); } } if(!rootdir) usage(); rv = procstop(rootdir); return rv; } /* parse arguments of the unittest command */ static int rununittest(int argc, char **argv){ char *rootdir; int i, rv; rootdir = NULL; for(i = 2; i < argc; i++){ if(!rootdir && argv[i][0] == '-'){ usage(); } else if(!rootdir){ rootdir = argv[i]; } else { usage(); } } if(!rootdir) usage(); rv = procunittest(rootdir); return rv; } /* parse arguments of the crypt command */ static int runcrypt(int argc, char **argv){ char *key, *hash; int i, rv; key = NULL; hash = NULL; for(i = 2; i < argc; i++){ if(!key && argv[i][0] == '-'){ usage(); } else if(!key){ key = argv[i]; } else if(!hash){ hash = argv[i]; } else { usage(); } } if(!key) usage(); rv = proccrypt(key, hash); return rv; } /* print formatted error string and exit */ static void die(const char *format, ...){ va_list ap; char msgbuf[URIBUFSIZ]; va_start(ap, format); vsprintf(msgbuf, format, ap); if(log_open(g_rootdir, g_logfile ? g_logfile : LOGFILE, LL_INFO, FALSE)){ log_print(LL_ERROR, "startup failed: %s", msgbuf); } else { fprintf(stderr, "%s: %s\n", g_progname, msgbuf); fflush(stderr); } va_end(ap); exit(1); } /* initialize the global variables */ static void startup(void){ NODE *node; ESTMTDB *db; CBLIST *lines, *elems; struct stat sbuf; const char *rp, *name; char path[URIBUFSIZ], numbuf[NUMBUFSIZ]; int i, ecode, pid, omode; if(stat(g_rootdir, &sbuf) == -1) die("the server root directory (%s) could not open", g_rootdir); sprintf(path, "%s%c%s", g_rootdir, ESTPATHCHR, CONFFILE); if(!(lines = cbreadlines(path))) die("the configuration file (%s) could not open", path); cbglobalgc(lines, (void (*)(void *))cblistclose); g_attrindexes = cblistopen(); cbglobalgc(g_attrindexes, (void (*)(void *))cblistclose); g_trustednodes = cbmapopenex(MINIBNUM); cbglobalgc(g_trustednodes, (void (*)(void *))cbmapclose); g_uireplaces = cblistopen(); cbglobalgc(g_uireplaces, (void (*)(void *))cblistclose); g_uiextattrs = cblistopen(); cbglobalgc(g_uiextattrs, (void (*)(void *))cblistclose); for(i = 0; i < cblistnum(lines); i++){ rp = cblistval(lines, i, NULL); if(cbstrfwimatch(rp, "bindaddr:")){ g_bindaddr = skiplabel(rp); } else if(cbstrfwimatch(rp, "portnum:")){ g_portnum = atoi(skiplabel(rp)); } else if(cbstrfwimatch(rp, "publicurl:")){ g_publicurl = skiplabel(rp); } else if(cbstrfwimatch(rp, "runmode:")){ g_runmode = atoi(skiplabel(rp)); } else if(cbstrfwimatch(rp, "authmode:")){ g_authmode = atoi(skiplabel(rp)); } else if(cbstrfwimatch(rp, "recvmax:")){ g_recvmax = atoi(skiplabel(rp)) * 1024; } else if(cbstrfwimatch(rp, "maxconn:")){ g_maxconn = atoi(skiplabel(rp)); } else if(cbstrfwimatch(rp, "idleflush:")){ g_idleflush = atoi(skiplabel(rp)); } else if(cbstrfwimatch(rp, "idlesync:")){ g_idlesync = atoi(skiplabel(rp)); } else if(cbstrfwimatch(rp, "sessiontimeout:")){ g_sessiontimeout = atoi(skiplabel(rp)); } else if(cbstrfwimatch(rp, "searchtimeout:")){ g_searchtimeout = atoi(skiplabel(rp)); } else if(cbstrfwimatch(rp, "searchmax:")){ g_searchmax = atoi(skiplabel(rp)); } else if(cbstrfwimatch(rp, "searchdepth:")){ g_searchdepth = atoi(skiplabel(rp)); } else if(cbstrfwimatch(rp, "rateuri:")){ g_rateuri = atoi(skiplabel(rp)) > 0; } else if(cbstrfwimatch(rp, "mergemethod:")){ g_mergemethod = atoi(skiplabel(rp)); } else if(cbstrfwimatch(rp, "proxyhost:")){ g_proxyhost = skiplabel(rp); } else if(cbstrfwimatch(rp, "proxyport:")){ g_proxyport = atoi(skiplabel(rp)); } else if(cbstrfwimatch(rp, "logfile:")){ g_logfile = skiplabel(rp); } else if(cbstrfwimatch(rp, "loglevel:")){ g_loglevel = atoi(skiplabel(rp)); } else if(cbstrfwimatch(rp, "backupcmd:")){ g_backupcmd = skiplabel(rp); } else if(cbstrfwimatch(rp, "scalepred:")){ g_scalepred = atoi(skiplabel(rp)); } else if(cbstrfwimatch(rp, "scoreexpr:")){ g_scoreexpr = atoi(skiplabel(rp)); } else if(cbstrfwimatch(rp, "attrindex:")){ cblistpush(g_attrindexes, skiplabel(rp), -1); } else if(cbstrfwimatch(rp, "docroot:")){ g_docroot = skiplabel(rp); } else if(cbstrfwimatch(rp, "indexfile:")){ g_indexfile = skiplabel(rp); } else if(cbstrfwimatch(rp, "trustednode:")){ cbmapput(g_trustednodes, skiplabel(rp), -1, "", 0, TRUE); } else if(cbstrfwimatch(rp, "denyuntrusted:")){ g_denyuntrusted = atoi(skiplabel(rp)) > 0; } else if(cbstrfwimatch(rp, "cachesize:")){ g_cachesize = strtod(skiplabel(rp), NULL) * 1024 * 1024; } else if(cbstrfwimatch(rp, "cacheanum:")){ g_cacheanum = atoi(skiplabel(rp)); } else if(cbstrfwimatch(rp, "cachetnum:")){ g_cachetnum = atoi(skiplabel(rp)); } else if(cbstrfwimatch(rp, "cachernum:")){ g_cachernum = atoi(skiplabel(rp)); } else if(cbstrfwimatch(rp, "specialcache:")){ g_specialcache = skiplabel(rp); } else if(cbstrfwimatch(rp, "helpershift:")){ g_helpershift = strtod(skiplabel(rp), NULL); } else if(cbstrfwimatch(rp, "wildmax:")){ g_wildmax = atoi(skiplabel(rp)); } else if(cbstrfwimatch(rp, "limittextsize:")){ g_limittextsize = atoi(skiplabel(rp)) * 1024; } else if(cbstrfwimatch(rp, "snipwwidth:")){ g_snipwwidth = atoi(skiplabel(rp)); } else if(cbstrfwimatch(rp, "sniphwidth:")){ g_sniphwidth = atoi(skiplabel(rp)); } else if(cbstrfwimatch(rp, "snipawidth:")){ g_snipawidth = atoi(skiplabel(rp)); } else if(cbstrfwimatch(rp, "scancheck:")){ g_scancheck = atoi(skiplabel(rp)) > 0; } else if(cbstrfwimatch(rp, "smlrvnum:")){ g_smlrvnum = atoi(skiplabel(rp)); } else if(cbstrfwimatch(rp, "extdelay:")){ g_extdelay = atoi(skiplabel(rp)); } else if(cbstrfwimatch(rp, "adminemail:")){ g_adminemail = skiplabel(rp); } else if(cbstrfwimatch(rp, "uireplace:")){ cblistpush(g_uireplaces, skiplabel(rp), -1); } else if(cbstrfwimatch(rp, "uiextattr:")){ cblistpush(g_uiextattrs, skiplabel(rp), -1); } else if(cbstrfwimatch(rp, "uiphraseform:")){ g_uiphraseform = atoi(skiplabel(rp)); } else if(cbstrfwimatch(rp, "uismlrtune:")){ g_uismlrtune = skiplabel(rp); } } if(!g_bindaddr) die("bindaddr is undefined"); if(g_portnum < 1) die("portnum is undefined"); if(!g_publicurl) die("publicurl is undefined"); if(g_runmode < RM_NORMAL || g_runmode > RM_RDONLY) die("runmode is undefined"); if(g_authmode < AM_NONE || g_authmode > AM_ALL) die("authmode is undefined"); if(g_recvmax < 1) die("recvmax is undefined"); if(g_maxconn < 1) die("maxconn is undefined"); if(g_idleflush < 1) die("idleflush is undefined"); if(g_idlesync < 1) die("idlesync is undefined"); if(g_sessiontimeout < 1) die("sessiontimeout is undefined"); if(g_searchtimeout < 1) die("searchtimeout is undefined"); if(g_searchmax < 1) die("searchmax is undefined"); if(g_searchdepth < 0) die("searchdepth is undefined"); if(g_mergemethod < MM_SCORE || g_mergemethod > MM_RANK) die("mergemethod is undefined"); if(!g_proxyhost) die("proxyhost is undefined"); if(g_proxyhost[0] != '\0' && g_proxyport < 1) die("proxyport is undefined"); if(!g_logfile) die("logfile is undefined"); if(g_loglevel < LL_DEBUG || g_loglevel > LL_NONE) die("loglevel is undefined"); if(!g_docroot) die("docroot is undefined"); if(!g_indexfile) die("indexfile is undefined"); if(!g_backupcmd) die("backupcmd is undefined"); if(g_scalepred < SP_SMALL || g_scalepred > SP_HUGE) die("scalepred is undefined"); if(g_scoreexpr < SE_VOID || g_scoreexpr > SE_ASIS) die("scoreexpr is undefined"); if(g_cachesize < 0.0) die("cachesize is undefined"); if(g_cacheanum < 0) die("cacheanum is undefined"); if(g_cachetnum < 0) die("cachetnum is undefined"); if(g_cachernum < 0) die("cachernum is undefined"); if(!g_specialcache) die("indexfile is undefined"); if(g_helpershift < 0) die("helpershift is undefined"); if(g_wildmax < 0) die("wildmax is undefined"); if(g_limittextsize < 1) die("limittextsize is undefined"); if(g_snipwwidth < 0) die("snipwwidth is undefined"); if(g_sniphwidth < 0) die("sniphwidth is undefined"); if(g_snipawidth < 0) die("snipawidth is undefined"); if(g_smlrvnum < 0) die("smlrvnum is undefined"); if(!g_adminemail) die("adminemail is undefined"); if(g_uiphraseform < 1) die("uiphraseform is undefined"); if(!g_uismlrtune) die("uismlrtune is undefined"); if(g_romode) g_runmode = RM_RDONLY; if((pid = lockerpid(g_rootdir)) > 0) die("another process (pid:%d) has opened the database", pid); if(!log_open(g_rootdir, g_logfile, LL_CHECK, FALSE)) die("the log file (%s) could not open", g_logfile); sprintf(path, "%s%c%s", g_rootdir, ESTPATHCHR, METAFILE); if(!(g_metadb = dpopen(path, DP_OREADER, -1))){ if(dpecode == DP_ELOCK){ die("other process (pid:%d) has opened the database", lockerpid(g_rootdir)); } else { die("the meta database file (%s) could not open", path); } } sprintf(path, "%s%c%s", g_rootdir, ESTPATHCHR, NODEDIR); if(!(elems = cbdirlist(path))){ dpclose(g_metadb); die("the node directory (%s) could not open", path); } for(i = 0; i < cblistnum(elems); i++){ name = cblistval(elems, i, NULL); if(!strcmp(name, ESTCDIRSTR) || !strcmp(name, ESTPDIRSTR)) continue; sprintf(path, "%s%c%s%c%s", g_rootdir, ESTPATHCHR, NODEDIR, ESTPATHCHR, name); if((db = est_mtdb_open(path, ESTDBREADER, &ecode)) != NULL){ if(!est_mtdb_close(db, &ecode)){ cblistclose(elems); dpclose(g_metadb); die("the database (%s) could not close", path); } } else { cblistclose(elems); dpclose(g_metadb); die("the database (%s) could not open", path); } } cblistclose(elems); dpclose(g_metadb); if(g_bgmode && !be_daemon(g_rootdir)) die("the process could not be a daemon"); sprintf(g_hostname, "%s", est_get_host_name()); sprintf(path, "%s%c%s", g_rootdir, ESTPATHCHR, METAFILE); omode = (g_runmode == RM_RDONLY ? DP_OREADER : DP_OWRITER) | DP_OLCKNB; if(!(g_metadb = dpopen(path, omode, -1))){ if(dpecode == DP_ELOCK){ die("other process (pid:%d) has opened the database", lockerpid(g_rootdir)); } else { die("the meta database file (%s) could not open", path); } } pid = (int)getpid(); sprintf(path, "%s%c%s", g_rootdir, ESTPATHCHR, PIDFILE); sprintf(numbuf, "%d\n", pid); cbwritefile(path, numbuf, -1); if(!log_open(g_rootdir, g_logfile, g_loglevel, FALSE)){ unlink(path); dpclose(g_metadb); die("starting logging failed"); } log_print(LL_INFO, "starting the master process (pid:%d)", pid); g_umgr = umgr_new(g_rootdir); g_nmgr = nmgr_new(g_rootdir); if(!umgr_load(g_umgr) || !nmgr_load(g_nmgr, g_runmode != RM_RDONLY)){ unlink(path); nmgr_delete(g_nmgr); umgr_delete(g_umgr); dpclose(g_metadb); log_print(LL_ERROR, "loading users and nodes failed"); die("loading users and nodes failed"); } for(i = 0; i < cblistnum(g_attrindexes); i++){ name = cblistval(g_attrindexes, i, NULL); if(!(rp = strstr(name, "{{!}}"))) continue; memcpy(path, name, rp - name); path[rp-name] = '\0'; nmgr_add_aidx(g_nmgr, path, rp + 5); } g_runlock = rwlock_new(); g_mgrlock = rwlock_new(); if((g_svsock = est_get_server_sock(g_bindaddr, g_portnum)) == -1){ unlink(path); rwlock_delete(g_mgrlock); rwlock_delete(g_runlock); nmgr_delete(g_nmgr); umgr_delete(g_umgr); dpclose(g_metadb); log_print(LL_ERROR, "initializing network failed"); die("initializing network failed"); } g_accesscount = 0; g_startdate = time(NULL); if(g_docroot[0] != '\0'){ if(stat(g_docroot, &sbuf) == 0){ log_print(LL_INFO, "letting the directory (%s) be public", g_docroot); } else { log_print(LL_WARN, "missing the directory (%s)", g_docroot); } } sprintf(path, "%s%c%s", g_rootdir, ESTPATHCHR, DFDBFILE); g_dfdb = dpopen(path, DP_OREADER, -1); g_bordstr = est_border_str(); elems = nmgr_names(g_nmgr); for(i = 0; i < cblistnum(elems); i++){ if(!(name = cblistval(elems, i, NULL)) || !(node = nmgr_get(g_nmgr, name))) continue; est_mtdb_set_cache_size(node->db, g_cachesize, g_cacheanum, g_cachetnum, g_cachernum); if(g_specialcache[0] != '\0') est_mtdb_set_special_cache(node->db, g_specialcache, SPCACHEMNUM); est_mtdb_set_wildmax(node->db, g_wildmax); if(g_dfdb) est_mtdb_set_dfdb(node->db, g_dfdb); } cblistclose(elems); } /* skip the label of a line */ static const char *skiplabel(const char *str){ if(!(str = strchr(str, ':'))) return ""; str++; while(*str != '\0' && (*str == ' ' || *str == '\t')){ str++; } return str; } /* clean up resources */ static void cleanup(void){ char path[URIBUFSIZ]; if(g_svsock != -1) est_sock_down(g_svsock); rwlock_delete(g_mgrlock); rwlock_delete(g_runlock); nmgr_delete(g_nmgr); umgr_delete(g_umgr); sprintf(path, "%s%c%s", g_rootdir, ESTPATHCHR, STOPFILE); unlink(path); sprintf(path, "%s%c%s", g_rootdir, ESTPATHCHR, PIDFILE); unlink(path); dpclose(g_metadb); if(g_dfdb) dpclose(g_dfdb); log_print(LL_INFO, "the master process finished"); } /* perform the init command */ static int procinit(const char *rootdir, int ex){ UMGR *umgr; NMGR *nmgr; NODE *node; ESTDOC *doc; char *tmp; int err; setsignals(); if(!master_init(rootdir)){ log_print(LL_ERROR, "initializing the root directory failed"); return 1; } log_open(rootdir, LOGFILE, LL_INFO, FALSE); log_print(LL_INFO, "the root directory created"); umgr = umgr_new(rootdir); tmp = est_make_crypt("admin"); umgr_put(umgr, "admin", tmp, "s", "Carolus Magnus", "Administrator"); free(tmp); if(ex){ tmp = est_make_crypt("john"); umgr_put(umgr, "john", tmp, "", "John Doe", "Normal User"); free(tmp); tmp = est_make_crypt("dick"); umgr_put(umgr, "dick", tmp, "", "Richard Roe", "Normal User"); free(tmp); tmp = est_make_crypt("lupin"); umgr_put(umgr, "lupin", tmp, "b", "Arsene Lupin", "Banned User"); free(tmp); } err = FALSE; if(!umgr_delete(umgr)) err = TRUE; nmgr = nmgr_new(rootdir); if(ex){ nmgr_put(nmgr, "sample1", TRUE, 0); if((node = nmgr_get(nmgr, "sample1")) != NULL){ free(node->label); node->label = cbmemdup("Sample Node One", -1); cbmapput(node->admins, "john", -1, "", 0, TRUE); cbmapput(node->users, "john", -1, "", 0, TRUE); cbmapput(node->users, "dick", -1, "", 0, TRUE); cbmapput(node->users, "lupin", -1, "", 0, TRUE); node_set_link(node, "http://localhost:1978/node/sample2", "Sample-Node-Two", 4000); doc = est_doc_new(); est_doc_add_attr(doc, ESTDATTRURI, "http://localhost/foo.html"); est_doc_add_text(doc, "You may my glories and my state dispose,"); est_doc_add_text(doc, "But not my griefs; still am I king of those."); est_doc_add_text(doc, "(Give it up, Yo! Give it up, Yo!)"); est_mtdb_put_doc(node->db, doc, 0); est_doc_delete(doc); doc = est_doc_new(); est_doc_add_attr(doc, ESTDATTRURI, "http://localhost/bar.html"); est_doc_add_text(doc, "The faster I go, the behinder I get."); est_doc_add_text(doc, "(Give it up, Yo! Give it up, Yo!)"); est_mtdb_put_doc(node->db, doc, 0); est_doc_delete(doc); } else { err = TRUE; } nmgr_put(nmgr, "sample2", TRUE, 0); if((node = nmgr_get(nmgr, "sample2")) != NULL){ free(node->label); node->label = cbmemdup("Sample Node Two", -1); cbmapput(node->users, "john", -1, "", 0, TRUE); node_set_link(node, "http://localhost:1978/node/sample1", "Sample-Node-One", 8000); doc = est_doc_new(); est_doc_add_attr(doc, ESTDATTRURI, "http://localhost/foo.html"); est_doc_add_text(doc, "He that is giddy thinks the world turns round."); est_doc_add_text(doc, "(Give it up, Yo! Give it up, Yo!)"); est_mtdb_put_doc(node->db, doc, 0); est_doc_delete(doc); } else { err = TRUE; } } if(!nmgr_delete(nmgr)) err = TRUE; return err ? 1 : 0; } /* perform the start command */ static int procstart(const char *rootdir){ char *path; path = est_realpath(rootdir); cbglobalgc(path, free); g_rootdir = path; if(!est_init_net_env()) die("could not initialize network environment"); atexit(est_free_net_env); do { setsignals(); startup(); dispatch(); cleanup(); } while(g_sigrestart); return 0; } /* perform the stop command */ static int procstop(const char *rootdir){ struct stat sbuf; char path[URIBUFSIZ], *buf; int pid; sprintf(path, "%s%c%s", rootdir, ESTPATHCHR, PIDFILE); if(!(buf = cbreadfile(path, NULL))) die("not running"); pid = atoi(buf); free(buf); if(pid < 1) die("not running"); if(est_kill(pid, 15)){ do { est_usleep(1000 * 500); if(!est_kill(pid, 13) && errno == ESRCH) break; } while(TRUE); } else { sprintf(path, "%s%c%s", rootdir, ESTPATHCHR, STOPFILE); if(!cbwritefile(path, "OH, MY GOODNESS!\n", -1)) die("killing failed"); do { est_usleep(1000 * 500); if(stat(path, &sbuf) == -1 && errno == ENOENT) break; } while(TRUE); } return 0; } /* perform the unittest command */ static int procunittest(const char *rootdir){ UMGR *umgr; USER *user; NMGR *nmgr; NODE *node; RWLOCK *rwlock; RESMAP *resmap; RESDOC **resdocs; ESTNODERES *nres; ESTRESDOC *rdoc; ESTDOC *doc; CBLIST *list; CBMAP *attrs; const char *name, *path; char *str; int i, err, rnum; setsignals(); if(procinit(rootdir, TRUE) != 0) return 1; log_open(rootdir, LOGFILE, LL_DEBUG, FALSE); err = FALSE; umgr = umgr_new(rootdir); umgr_load(umgr); umgr_put(umgr, "mikio", "x", "", "Mikio Hirabayashi", "Test"); umgr_put(umgr, "mikio", "x", "", "Mikio Hirabayashi", "Test"); list = umgr_names(umgr); for(i = 0; i < cblistnum(list); i++){ name = cblistval(list, i, NULL); if(!(user = umgr_get(umgr, name))){ err = TRUE; continue; } if(i % 2 == 0) user_make_sess(user); if(i % 3 == 0){ user_set_sess_val(user, "oda", "nobunaga"); user_set_sess_val(user, "hashiba", "hideyoshi"); user_set_sess_val(user, "ieyasu", "tokugawa"); user_set_sess_val(user, "oda", NULL); free(user_sess_val(user, "oda")); free(user_sess_val(user, "hashiba")); user_clear_sess(user); free(user_sess_val(user, "ieyasu")); } } cblistclose(list); umgr_out(umgr, "mikio"); umgr_out(umgr, "hoge"); if(!umgr_delete(umgr)) err = TRUE; nmgr = nmgr_new(rootdir); nmgr_load(nmgr, TRUE); nmgr_put(nmgr, "eagle", TRUE, 0); nmgr_put(nmgr, "shark", TRUE, 0); nmgr_put(nmgr, "panther", TRUE, 0); nmgr_put(nmgr, "i n v a l i d", TRUE, 0); list = nmgr_names(nmgr); for(i = 0; i < cblistnum(list); i++){ path = cblistval(list, i, NULL); if(!(node = nmgr_get(nmgr, path))){ err = TRUE; continue; } doc = est_doc_new(); est_doc_add_attr(doc, ESTDATTRURI, "file:///home/mikio/sample1.html"); est_doc_add_text(doc, "Happy Hacking!"); if(!est_mtdb_put_doc(node->db, doc, 0)) err = TRUE; est_doc_delete(doc); doc = est_doc_new(); est_doc_add_attr(doc, ESTDATTRURI, "file:///home/mikio/sample2.txt"); est_doc_add_text(doc, "The savior becomes the victim."); if(!est_mtdb_put_doc(node->db, doc, 0)) err = TRUE; est_doc_delete(doc); node_set_link(node, "http://hoge.com/node/hoge1", "a", 100); node_set_link(node, "http://hoge.com/node/hoge2", "b", 200); node_set_link(node, "http://hoge.com/node/hoge2", "c", 300); } cblistclose(list); nmgr_out(nmgr, "eagle"); nmgr_out(nmgr, "hoge"); nmgr_delete(nmgr); rwlock = rwlock_new(); if(!rwlock_lock(rwlock, TRUE)) err = TRUE; if(!rwlock_unlock(rwlock)) err = TRUE; if(!rwlock_lock(rwlock, FALSE)) err = TRUE; if(!rwlock_unlock(rwlock)) err = TRUE; rwlock_delete(rwlock); resmap = resmap_new(); doc = est_doc_new(); est_doc_add_attr(doc, ESTDATTRURI, "http://tako.ika/one.html"); est_doc_add_text(doc, "This is a pen."); resmap_put(resmap, 100, doc, NULL, NULL); doc = est_doc_new(); est_doc_add_attr(doc, ESTDATTRURI, "http://tako.ika/two.html"); est_doc_add_text(doc, "Love is stranger."); resmap_put(resmap, 200, doc, NULL, NULL); doc = est_doc_new(); est_doc_add_attr(doc, ESTDATTRURI, "http://tako.ika/001.html"); resmap_put(resmap, 1, doc, NULL, cbmemdup("one", -1)); doc = est_doc_new(); est_doc_add_attr(doc, ESTDATTRURI, "http://tako.ika/006.html"); resmap_put(resmap, 6, doc, NULL, cbmemdup("six", -1)); doc = est_doc_new(); est_doc_add_attr(doc, ESTDATTRURI, "http://tako.ika/003.html"); resmap_put(resmap, 3, doc, NULL, cbmemdup("three", -1)); doc = est_doc_new(); est_doc_add_attr(doc, ESTDATTRURI, "http://tako.ika/004.html"); resmap_put(resmap, 4, doc, NULL, cbmemdup("four", -1)); doc = est_doc_new(); est_doc_add_attr(doc, ESTDATTRURI, "http://tako.ika/002.html"); resmap_put(resmap, 2, doc, NULL, cbmemdup("two", -1)); doc = est_doc_new(); est_doc_add_attr(doc, ESTDATTRURI, "http://tako.ika/005.html"); resmap_put(resmap, 1, doc, NULL, cbmemdup("five", -1)); resdocs = resmap_list(resmap, &rnum, NULL, NULL); for(i = 0; i < rnum; i++){ log_print(LL_DEBUG, "result: uri=%s score=%d body=%s", est_doc_attr(resdocs[i]->doc, ESTDATTRURI), resdocs[i]->score, resdocs[i]->body ? resdocs[i]->body : "(null)"); } free(resdocs); resmap_delete(resmap); nres = est_noderes_new(); for(i = 0; i < 128; i++){ attrs = cbmapopenex(MINIBNUM); str = cbsprintf("http://big/bigger/biggest/%d", i + 1); if(i % 10 != 0) cbmapput(attrs, ESTDATTRURI, -1, str, -1, FALSE); free(str); str = cbsprintf("This is %d\n", i + 1); est_noderes_add_doc(nres, attrs, str); } for(i = 0; i < 100 && est_noderes_shift_doc(nres, &attrs, &str); i++){ free(str); cbmapclose(attrs); } for(i = 0; i < est_noderes_doc_num(nres); i++){ rdoc = est_noderes_get_doc(nres, i); log_print(LL_DEBUG, "%s", est_resdoc_uri(rdoc)); } for(i = 0; i < 2048; i++){ attrs = cbmapopenex(MINIBNUM); str = cbsprintf("http://big/bigger/biggest/%d", i + 1); if(i % 10 != 0) cbmapput(attrs, ESTDATTRURI, -1, str, -1, FALSE); free(str); str = cbsprintf("This is %d\n", i + 1); est_noderes_add_doc(nres, attrs, str); } est_noderes_delete(nres); if(!err) log_print(LL_INFO, "finished successfully"); return err ? 1 : 0; } /* perform the crypt command */ static int proccrypt(const char *key, const char *hash){ char *tmp; if(hash){ if(!est_match_crypt(key, hash)){ fprintf(stderr, "%s and %s do not match", key, hash); return 1; } } else { tmp = est_make_crypt(key); printf("%s\n", tmp); free(tmp); } return 0; } /* listen the socket and dispatch processes */ static void dispatch(void){ pthread_t th; fd_set rfds; struct timeval tv; struct stat sbuf; char *stfile, claddr[ADDRBUFSIZ]; int clsock, clport, isec, cnum; time_t start; TARGCOMM *targ; log_print(LL_INFO, "waiting for requests"); stfile = cbsprintf("%s%c%s", g_rootdir, ESTPATHCHR, STOPFILE); isec = 0; start = -1; while(TRUE){ FD_ZERO(&rfds); FD_SET(g_svsock, &rfds); tv.tv_sec = 1; tv.tv_usec = 0; errno = 0; if(select(g_svsock + 1, &rfds, NULL, NULL, &tv) > 0 && FD_ISSET(g_svsock, &rfds)){ clsock = est_accept_conn(g_svsock, claddr, &clport); switch(clsock){ case -1: log_print(LL_ERROR, "accepting connection failed"); break; case 0: log_print(LL_WARN, "accepting connection interrupted by a signal"); break; default: if(rwlock_rnum(g_runlock) > g_maxconn + FCLOSECONNNUM){ log_print(LL_WARN, "doing forced shutdown due to jam-up"); est_sock_down(clsock); } else { g_accesscount++; targ = cbmalloc(sizeof(TARGCOMM)); targ->clsock = clsock; sprintf(targ->claddr, "%s", claddr); targ->clport = clport; if(g_stmode){ communicate(targ); } else { if(pthread_create(&th, NULL, communicate, targ) == 0){ if(pthread_detach(th) != 0) log_print(LL_ERROR, "detachting thread failed"); } else { log_print(LL_WARN, "creating thread failed"); est_sock_down(clsock); free(targ); } } } break; } isec = 0; } else { if(errno != EINTR) isec++; if(stat(stfile, &sbuf) == 0){ log_print(LL_INFO, "the stop file detected"); g_sigterm = TRUE; } } if(g_sigterm){ if(rwlock_rnum(g_runlock) < 1){ break; } else if(start == -1){ start = time(NULL); } else if(time(NULL) - start > 1){ break; } } if(isec >= IREFRESHSEC && g_runmode != RM_RDONLY){ if(g_stmode){ refreshnode(NULL); } else { if(pthread_create(&th, NULL, refreshnode, NULL) == 0){ if(pthread_detach(th) != 0) log_print(LL_ERROR, "detachting thread failed"); } else { log_print(LL_WARN, "creating thread failed"); } } est_usleep(1000 * 100); } if(isec >= g_idleflush * (1.0 - g_cacheratio) && (g_cacheratio > 0.0 || isec % (g_idleflush / 2 + 1) == 0)){ if(g_stmode){ flushnode(NULL); } else { if(pthread_create(&th, NULL, flushnode, NULL) == 0){ if(pthread_detach(th) != 0) log_print(LL_ERROR, "detachting thread failed"); } else { log_print(LL_WARN, "creating thread failed"); } } est_usleep(1000 * 100); } if(g_sigsync || g_sigback || isec >= g_idlesync){ if(g_cacheratio > 0.0){ if(g_stmode){ flushnode(NULL); } else { if(pthread_create(&th, NULL, flushnode, NULL) == 0){ if(pthread_detach(th) != 0) log_print(LL_ERROR, "detachting thread failed"); } else { log_print(LL_WARN, "creating thread failed"); } } } else { if(g_stmode){ syncnodes(NULL); } else { if(pthread_create(&th, NULL, syncnodes, NULL) == 0){ if(pthread_detach(th) != 0) log_print(LL_ERROR, "detachting thread failed"); } else { log_print(LL_WARN, "creating thread failed"); } } g_sigsync = FALSE; isec = 0; if(g_sigback){ backupdb(); g_sigback = FALSE; } } est_usleep(1000 * 100); } } free(stfile); log_print(LL_INFO, "shutting down"); start = time(NULL); while((cnum = rwlock_rnum(g_runlock)) > 0){ log_print(LL_INFO, "waiting for children: %d", cnum); est_usleep(1000 * 1000); if(time(NULL) - start > FCLOSEWAITMAX && g_svsock != -1){ est_sock_down(g_svsock); g_svsock = -1; log_print(LL_WARN, "doing forced shutdown of the server socket"); } if(time(NULL) - start > FTERMWAITMAX){ log_print(LL_WARN, "doing forced termination of the server process"); break; } } est_usleep(1000 * 100); } /* refresh or charge result cache of one of nodes */ static void *refreshnode(void *targ){ static int cnt = 0; CBLIST *list; NODE *node; if(pthread_mutex_trylock(&g_backlock) != 0) return NULL; if(!rwlock_lock(g_runlock, FALSE)){ log_print(LL_ERROR, "locking failed"); pthread_mutex_unlock(&g_backlock); return NULL; } if(!rwlock_lock(g_mgrlock, FALSE)){ rwlock_unlock(g_runlock); pthread_mutex_unlock(&g_backlock); log_print(LL_ERROR, "locking failed"); return NULL; } list = nmgr_names(g_nmgr); if(cblistnum(list) > 0){ if((node = nmgr_get(g_nmgr, cblistval(list, cnt++ % cblistnum(list), NULL))) != NULL){ if(node->dirty){ log_print(LL_INFO, "refreshing a node: %s", node->name); est_mtdb_refresh_rescc(node->db); node->dirty = FALSE; } else { est_mtdb_charge_rescc(node->db, ICHARGENUM); } } } cblistclose(list); if(!rwlock_unlock(g_mgrlock)) log_print(LL_ERROR, "unlocking failed"); if(!rwlock_unlock(g_runlock)) log_print(LL_ERROR, "unlocking failed"); if(pthread_mutex_unlock(&g_backlock) != 0) log_print(LL_ERROR, "unlocking failed"); return NULL; } /* flush one of nodes */ static void *flushnode(void *targ){ static int cnt = 0; CBLIST *list; NODE *node; int size; double msiz; if(g_runmode == RM_RDONLY) return NULL; if(pthread_mutex_trylock(&g_backlock) != 0) return NULL; if(!rwlock_lock(g_runlock, FALSE)){ log_print(LL_ERROR, "locking failed"); pthread_mutex_unlock(&g_backlock); return NULL; } if(!rwlock_lock(g_mgrlock, FALSE)){ rwlock_unlock(g_runlock); pthread_mutex_unlock(&g_backlock); log_print(LL_ERROR, "locking failed"); return NULL; } list = nmgr_names(g_nmgr); msiz = 0.0; if(cblistnum(list) > 0){ if((node = nmgr_get(g_nmgr, cblistval(list, cnt++ % cblistnum(list), NULL))) != NULL){ if(!est_mtdb_flush(node->db, est_mtdb_cache_num(node->db) > 0 ? IFLUSHNUM : -1)) log_print(LL_ERROR, "flushing failed"); if((size = est_mtdb_used_cache_size(node->db)) > msiz) msiz = size; } } g_cacheratio = msiz / g_cachesize; cblistclose(list); if(!rwlock_unlock(g_mgrlock)) log_print(LL_ERROR, "unlocking failed"); if(!rwlock_unlock(g_runlock)) log_print(LL_ERROR, "unlocking failed"); if(pthread_mutex_unlock(&g_backlock) != 0) log_print(LL_ERROR, "unlocking failed"); return NULL; } /* synchronize all nodes */ static void *syncnodes(void *targ){ if(g_runmode == RM_RDONLY) return NULL; if(pthread_mutex_lock(&g_backlock) != 0) return NULL; if(!rwlock_lock(g_runlock, FALSE)){ log_print(LL_ERROR, "locking failed"); pthread_mutex_unlock(&g_backlock); return NULL; } if(!rwlock_lock(g_mgrlock, TRUE)){ rwlock_unlock(g_runlock); pthread_mutex_unlock(&g_backlock); log_print(LL_ERROR, "locking failed"); return NULL; } umgr_sync(g_umgr); nmgr_sync(g_nmgr, TRUE); if(!dpsync(g_metadb)) log_print(LL_ERROR, "synchronizing the meda database failed"); if(!rwlock_unlock(g_mgrlock)) log_print(LL_ERROR, "unlocking failed"); if(!rwlock_unlock(g_runlock)) log_print(LL_ERROR, "unlocking failed"); if(pthread_mutex_unlock(&g_backlock) != 0) log_print(LL_ERROR, "unlocking failed"); return NULL; } /* backup the database */ static void backupdb(void){ char cmd[URIBUFSIZ], *tmp; if(g_backupcmd[0] == '\0') return; syncnodes(NULL); if(!rwlock_lock(g_runlock, FALSE)){ log_print(LL_ERROR, "locking failed"); return; } if(!rwlock_lock(g_mgrlock, TRUE)){ rwlock_unlock(g_runlock); log_print(LL_ERROR, "locking failed"); return; } log_print(LL_INFO, "running the backup command"); tmp = est_realpath(g_indexfile); sprintf(cmd, "%s \"%s\"", g_backupcmd, g_rootdir); free(tmp); system(cmd); if(!rwlock_unlock(g_mgrlock)) log_print(LL_ERROR, "unlocking failed"); if(!rwlock_unlock(g_runlock)) log_print(LL_ERROR, "unlocking failed"); } /* communicate with a client */ static void *communicate(void *targ){ TARGCOMM *myarg; REQUEST req; USER *user; const char *cstr; char ibuf[IOBUFSIZ], *tmp, *bp, *ep; int clsock, len, clen; myarg = (TARGCOMM *)targ; clsock = myarg->clsock; if(!rwlock_lock(g_runlock, FALSE)){ est_sock_down(myarg->clsock); free(myarg); log_print(LL_ERROR, "locking failed"); return NULL; } est_sock_recv_line(clsock, ibuf, IOBUFSIZ - 1); log_print(LL_INFO, "[%s:%d]: %s", myarg->claddr, myarg->clport, ibuf); if(cbstrfwmatch(ibuf, "HEAD ")){ req.method = HM_HEAD; } else if(cbstrfwmatch(ibuf, "GET ")){ req.method = HM_GET; } else if(cbstrfwmatch(ibuf, "POST ")){ req.method = HM_POST; } else if(cbstrfwmatch(ibuf, "OPTIONS ")){ req.method = HM_OPTIONS; } else { req.method = HM_UNKNOWN; } req.claddr = myarg->claddr; req.clport = myarg->clport; sprintf(req.prefix, "http://%s:%d", g_hostname, g_portnum); req.target = NULL; req.name = NULL; req.passwd = NULL; req.ims = -1; req.gzip = FALSE; req.deflate = FALSE; req.ctype = NULL; req.referer = NULL; req.estvia = NULL; req.body = NULL; req.params = cbmapopenex(MINIBNUM); req.now = time(NULL); req.reload = FALSE; if((bp = strchr(ibuf, ' ')) != NULL){ while(*bp == ' '){ bp++; } while(bp[0] == '/' && bp[1] == '/'){ bp++; } if((ep = strchr(bp, ' ')) != NULL){ req.target = cbmemdup(bp, ep - bp); } else { req.target = cbmemdup(bp, -1); } if((bp = strchr(req.target, '?')) != NULL){ *(bp++) = '\0'; setparams(req.params, bp); } } if(!req.target) req.target = cbmemdup("/", 1); clen = -1; do { len = est_sock_recv_line(clsock, ibuf, IOBUFSIZ - 1); if(len > 0) log_print(LL_DEBUG, "[%s:%d]: + %s", myarg->claddr, myarg->clport, ibuf); if(cbstrfwimatch(ibuf, "Host:")){ ibuf[HOSTBUFSIZ-1] = '\0'; sprintf(req.prefix, "http://%s", skiplabel(ibuf)); } else if(cbstrfwimatch(ibuf, "Content-Length:")){ clen = atoi(skiplabel(ibuf)); } else if(cbstrfwimatch(ibuf, "Content-Type:")){ req.ctype = cbmemdup(skiplabel(ibuf), -1); } else if(cbstrfwimatch(ibuf, "Referer:")){ req.referer = cbmemdup(skiplabel(ibuf), -1); } else if(cbstrfwimatch(ibuf, "X-Estraier-Via:")){ if(req.estvia){ tmp = cbsprintf("%s, %s", req.estvia, skiplabel(ibuf)); free(req.estvia); req.estvia = tmp; } else { req.estvia = cbmemdup(skiplabel(ibuf), -1); } } else if(cbstrfwimatch(ibuf, "Authorization:")){ cstr = skiplabel(ibuf); if(cbstrfwimatch(cstr, "basic") && (cstr = strchr(cstr, ' ')) != NULL){ while(*cstr == ' '){ cstr++; } bp = cbbasedecode(cstr, NULL); if((ep = strchr(bp, ':')) != NULL) *(ep++) = '\0'; req.name = cbmemdup(bp, -1); req.passwd = cbmemdup(ep ? ep : "", -1); free(bp); } } else if(cbstrfwimatch(ibuf, "If-Modified-Since:")){ req.ims = cbstrmktime(skiplabel(ibuf)); } else if(cbstrfwimatch(ibuf, "Accept-Encoding:")){ cstr = skiplabel(ibuf); if(ESTUSEZLIB && strstr(cstr, "gzip")) req.gzip = TRUE; if(ESTUSEZLIB && strstr(cstr, "deflate")) req.deflate = TRUE; } } while(len > 0); if(g_publicurl[0] != '\0') sprintf(req.prefix, "%s", g_publicurl); if(clen > g_recvmax) clen = g_recvmax; if(req.method == HM_POST && clen > 0){ req.body = est_sock_recv_all(clsock, clen); if(req.ctype && cbstrfwimatch(req.ctype, ESTFORMTYPE)) setparams(req.params, req.body); est_sock_recv_void(clsock); } if(req.ims > 0 && req.ims < req.now - UICACHELIFE) req.reload = TRUE; user = NULL; if(rwlock_lock(g_mgrlock, FALSE)){ if(req.name) user = umgr_get(g_umgr, req.name); if(user && !est_match_crypt(req.passwd, user->passwd)) user = NULL; if(!rwlock_unlock(g_mgrlock)) log_print(LL_ERROR, "unlocking failed"); } else { log_print(LL_ERROR, "locking failed"); } if(g_sigterm){ senderror(clsock, &req, 503, "Service Unavailable (shutting down)"); } else if(rwlock_rnum(g_runlock) > g_maxconn){ senderror(clsock, &req, 503, "Service Unavailable (too busy)"); } else if(req.method == HM_UNKNOWN){ senderror(clsock, &req, 501, "Not Implemented"); } else if(req.method == HM_POST && clen < 0){ senderror(clsock, &req, 411, "Length Required"); } else if(req.method == HM_POST && clen >= g_recvmax){ senderror(clsock, &req, 413, "Request Entity Too Large"); } else if(req.target[0] != '/' || strstr(req.target, "/../")){ senderror(clsock, &req, 400, "Bad Request (invalid URL)"); } else if(g_denyuntrusted && !cbmapget(g_trustednodes, req.claddr, -1, NULL)){ senderror(clsock, &req, 403, "Forbidden (untrusted node)"); } else if(req.method == HM_OPTIONS){ sendoptions(clsock, &req, user); } else if(!strcmp(req.target, MASTERLOC)){ sendmasterdata(clsock, &req, user); } else if(cbstrfwmatch(req.target, NODEPREFIX)){ sendnodedata(clsock, &req, user, req.target + strlen(NODEPREFIX)); } else if(!strcmp(req.target, MASTERUILOC)){ sendmasteruidata(clsock, &req, user); } else if(!strcmp(req.target, FAVICONLOC)){ sendfavicondata(clsock, &req); } else if(cbstrfwmatch(req.target, IMAGEPREFIX)){ sendimagedata(clsock, &req, req.target + strlen(IMAGEPREFIX)); } else if(g_docroot[0] != '\0'){ sendfiledata(clsock, &req); } else if(!strcmp(req.target, "/")){ sendmenudata(clsock, &req); } else { senderror(clsock, &req, 404, "Not Found"); } cbmapclose(req.params); free(req.body); free(req.estvia); free(req.referer); free(req.ctype); free(req.name); free(req.passwd); free(req.target); est_sock_down(myarg->clsock); free(myarg); if(!rwlock_unlock(g_runlock)) log_print(LL_ERROR, "unlocking failed"); return NULL; } /* set parameters into a map object */ static void setparams(CBMAP *params, const char *src){ CBLIST *pairs; char *key, *val, *dkey, *dval; int i; pairs = cbsplit(src, -1, "&"); for(i = 0; i < cblistnum(pairs); i++){ key = cbmemdup(cblistval(pairs, i, NULL), -1); if((val = strchr(key, '=')) != NULL){ *(val++) = '\0'; dkey = cburldecode(key, NULL); dval = cburldecode(val, NULL); cbmapput(params, dkey, -1, dval, -1, FALSE); free(dval); free(dkey); } free(key); } cblistclose(pairs); } /* add the server information to a datum object */ static void addservinfo(CBDATUM *datum, time_t now, int ccage){ char *tmp; tmp = cbdatestrhttp(now, 0); cbdatumprintf(datum, "Date: %s\r\n", tmp); free(tmp); cbdatumprintf(datum, "Server: %s/%s\r\n", SERVNAME, est_version); if(ccage < 1){ cbdatumprintf(datum, "Cache-Control: no-cache, must-revalidate, no-transform\r\n"); cbdatumprintf(datum, "Pragma: no-cache\r\n"); } else { cbdatumprintf(datum, "Cache-Control: max-age=%d, no-transform\r\n", ccage); tmp = cbdatestrhttp(now + ccage, 0); cbdatumprintf(datum, "Expires: %s\r\n", tmp); free(tmp); } cbdatumprintf(datum, "Connection: close\r\n"); } /* send error message */ static void senderror(int clsock, REQUEST *req, int code, const char *message){ CBDATUM *datum; datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 %d %s\r\n", code, message); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); cbdatumprintf(datum, "\r\n"); if(req->method != HM_HEAD) cbdatumprintf(datum, "%s\n", message); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: %d %s", req->claddr, req->clport, code, message); } /* send authenticate error message */ static void sendautherror(int clsock, REQUEST *req, const char *realm){ CBDATUM *datum; datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 401 Unauthorized\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); cbdatumprintf(datum, "WWW-Authenticate: Basic realm=\"%s\"\r\n", realm); cbdatumprintf(datum, "\r\n"); if(req->method != HM_HEAD) cbdatumprintf(datum, "Unauthorized\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 401 Unauthorized", req->claddr, req->clport); } /* send not modified error message */ static void sendnotmoderror(int clsock, REQUEST *req){ CBDATUM *datum; datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 304 Not Modified\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "\r\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 304 Not Modified", req->claddr, req->clport); } /* send header only message */ static void sendheadonly(int clsock, REQUEST *req, const char *type){ CBDATUM *datum; datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: %s\r\n", type); cbdatumprintf(datum, "\r\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (head)", req->claddr, req->clport); } /* check whether a user is banned */ static int isbanned(USER *user){ if(user && strchr(user->flags, 'b')) return TRUE; return FALSE; } /* check whether a user is an administrator of the master */ static int ismasteradmin(REQUEST *req, USER *user){ if(isbanned(user)) return FALSE; if(cbmapget(g_trustednodes, req->claddr, -1, NULL)) return TRUE; if(user && strchr(user->flags, 's')) return TRUE; return FALSE; } /* check whether a user is an administrator of a node */ static int isnodeadmin(NODE *node, REQUEST *req, USER *user){ if(isbanned(user)) return FALSE; if(ismasteradmin(req, user)) return TRUE; if(g_authmode == AM_NONE) return TRUE; if(user && cbmapget(node->admins, user->name, -1, NULL)) return TRUE; return FALSE; } /* check whether a user is a user of a node */ static int isnodeuser(NODE *node, REQUEST *req, USER *user){ if(isbanned(user)) return FALSE; if(isnodeadmin(node, req, user)) return TRUE; if(g_authmode == AM_NONE || g_authmode == AM_ADMIN) return TRUE; if(user && cbmapget(node->users, user->name, -1, NULL)) return TRUE; return FALSE; } /* get the options for node database */ static int getnodeoptions(void){ int options; options = 0; switch(g_scalepred){ case SP_SMALL: options |= ESTDBSMALL; break; default: break; case SP_LARGE: options |= ESTDBLARGE; break; case SP_HUGE: options |= ESTDBHUGE; break; } switch(g_scoreexpr){ case SE_VOID: options |= ESTDBSCVOID; break; default: break; case SE_INT: options |= ESTDBSCINT; break; case SE_ASIS: options |= ESTDBSCASIS; break; } return options; } /* send the option messages */ static void sendoptions(int clsock, REQUEST *req, USER *user){ CBDATUM *datum; datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Allow: GET,HEAD,POST,OPTIONS\r\n"); cbdatumprintf(datum, "\r\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (options)", req->claddr, req->clport); } /* send the master data */ static void sendmasterdata(int clsock, REQUEST *req, USER *user){ CBDATUM *datum; CBLIST *list; USER *tuser; NODE *tnode; const char *act, *name, *passwd, *flags, *fname, *misc, *label; char *tmp; int i; if(!rwlock_lock(g_mgrlock, TRUE)){ log_print(LL_ERROR, "locking failed"); senderror(clsock, req, 500, "Internal Server Error (locking failed)"); return; } if(req->referer && !cbstrfwmatch(req->referer, req->prefix)){ senderror(clsock, req, 403, "Forbidden (invalid referrer)"); if(!rwlock_unlock(g_mgrlock)) log_print(LL_ERROR, "unlocking failed"); return; } if(!ismasteradmin(req, user)){ sendautherror(clsock, req, "Super User"); if(!rwlock_unlock(g_mgrlock)) log_print(LL_ERROR, "unlocking failed"); return; } if(!(act = cbmapget(req->params, "action", -1, NULL))) act = ""; if(!(name = cbmapget(req->params, "name", -1, NULL))) name = ""; if(!(passwd = cbmapget(req->params, "passwd", -1, NULL))) passwd = ""; if(!(flags = cbmapget(req->params, "flags", -1, NULL))) flags = ""; if(!(fname = cbmapget(req->params, "fname", -1, NULL))) fname = ""; if(!(misc = cbmapget(req->params, "misc", -1, NULL))) misc = ""; if(!(label = cbmapget(req->params, "label", -1, NULL))) label = ""; if(!strcmp(act, "shutdown")){ datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 202 Accepted\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); cbdatumprintf(datum, "\r\n"); cbdatumprintf(datum, "Accepted\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); g_sigterm = TRUE; cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 202 (accepted - shutdown)", req->claddr, req->clport); } else if(!strcmp(act, "sync")){ datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 202 Accepted\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); cbdatumprintf(datum, "\r\n"); cbdatumprintf(datum, "Accepted\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); g_sigsync = TRUE; cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 202 (accepted - sync)", req->claddr, req->clport); } else if(!strcmp(act, "backup")){ datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 202 Accepted\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); cbdatumprintf(datum, "\r\n"); cbdatumprintf(datum, "Accepted\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); g_sigback = TRUE; cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 202 (accepted - backup)", req->claddr, req->clport); } else if(!strcmp(act, "userlist")){ datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); cbdatumprintf(datum, "\r\n"); list = umgr_names(g_umgr); for(i = 0; i < cblistnum(list); i++){ if(!(name = cblistval(list, i, NULL)) || !(tuser = umgr_get(g_umgr, name))) continue; cbdatumprintf(datum, "%s\t%s\t%s\t%s\t%s\n", tuser->name, tuser->passwd, tuser->flags, tuser->fname, tuser->misc); } cblistclose(list); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (userlist)", req->claddr, req->clport); } else if(!strcmp(act, "useradd") && name[0] != '\0' && passwd[0] != '\0'){ tmp = est_make_crypt(passwd); if(g_runmode == RM_RDONLY){ senderror(clsock, req, 503, "Service Unavailable (read only)"); } else if(umgr_put(g_umgr, name, tmp, flags, fname, misc)){ datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); cbdatumprintf(datum, "\r\n"); cbdatumprintf(datum, "OK\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (useradd)", req->claddr, req->clport); } else { senderror(clsock, req, 400, "Bad Request (maybe, the user already exists)"); } free(tmp); } else if(!strcmp(act, "userdel") && name[0] != '\0'){ if(g_runmode == RM_RDONLY){ senderror(clsock, req, 503, "Service Unavailable (read only)"); } else if(umgr_out(g_umgr, name)){ datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); cbdatumprintf(datum, "\r\n"); cbdatumprintf(datum, "OK\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (userdel)", req->claddr, req->clport); } else { senderror(clsock, req, 400, "Bad Request (maybe, the user does not exist)"); } } else if(!strcmp(act, "nodelist")){ datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); cbdatumprintf(datum, "\r\n"); list = nmgr_names(g_nmgr); for(i = 0; i < cblistnum(list); i++){ if(!(name = cblistval(list, i, NULL)) || !(tnode = nmgr_get(g_nmgr, name))) continue; cbdatumprintf(datum, "%s\t%s\t%d\t%d\t%.0f\n", tnode->name, tnode->label, est_mtdb_doc_num(tnode->db), est_mtdb_word_num(tnode->db), est_mtdb_size(tnode->db)); } cblistclose(list); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (nodelist)", req->claddr, req->clport); } else if(!strcmp(act, "nodeadd") && name[0] != '\0'){ if(g_runmode == RM_RDONLY){ senderror(clsock, req, 503, "Service Unavailable (read only)"); } else if(nmgr_put(g_nmgr, name, TRUE, getnodeoptions())){ if((tnode = nmgr_get(g_nmgr, name)) != NULL){ free(tnode->label); tnode->label = cbmemdup(label[0] != '\0' ? label : name, -1); } nmgr_sync(g_nmgr, FALSE); datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); cbdatumprintf(datum, "\r\n"); cbdatumprintf(datum, "OK\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (nodeadd)", req->claddr, req->clport); } else { senderror(clsock, req, 400, "Bad Request (maybe, the node already exists)"); } } else if(!strcmp(act, "nodedel") && name[0] != '\0'){ if(g_runmode == RM_RDONLY){ senderror(clsock, req, 503, "Service Unavailable (read only)"); } else if(nmgr_out(g_nmgr, name)){ datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); cbdatumprintf(datum, "\r\n"); cbdatumprintf(datum, "OK\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (nodedel)", req->claddr, req->clport); } else { senderror(clsock, req, 400, "Bad Request (maybe, the node does not exist)"); } } else if(!strcmp(act, "nodeclr") && name[0] != '\0'){ if(g_runmode == RM_RDONLY){ senderror(clsock, req, 503, "Service Unavailable (read only)"); } else if(nmgr_clear(g_nmgr, name, getnodeoptions())){ datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); cbdatumprintf(datum, "\r\n"); cbdatumprintf(datum, "OK\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (nodeclr)", req->claddr, req->clport); } else { senderror(clsock, req, 400, "Bad Request (maybe, the node does not exist)"); } } else if(!strcmp(act, "logrtt")){ if(log_rotate(g_rootdir, g_logfile)){ datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); cbdatumprintf(datum, "\r\n"); cbdatumprintf(datum, "OK\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (nodeclr)", req->claddr, req->clport); } else { senderror(clsock, req, 500, "Internal Server Error (log rotation failed)"); } } else { senderror(clsock, req, 400, "Bad Request (the action is invalid or lack of parameters)"); } if(!rwlock_unlock(g_mgrlock)) log_print(LL_ERROR, "unlocking failed"); } /* send a node data */ static void sendnodedata(int clsock, REQUEST *req, USER *user, const char *path){ CBDATUM *datum; CBLIST *list; NODE *node; const char *cmd; char *pbuf, *pv; int i; pbuf = cbmemdup(path, -1); if((pv = strchr(path, '/')) != NULL){ *pv = '\0'; cmd = pv + 1; } else { cmd = ""; } while(*cmd == '/'){ cmd++; } if(!rwlock_lock(g_mgrlock, cmd[0] == '_')){ log_print(LL_ERROR, "locking failed"); senderror(clsock, req, 500, "Internal Server Error (locking failed)"); free(pbuf); return; } if(!(node = nmgr_get(g_nmgr, path))){ if(!strcmp(req->target, NODEPREFIX)){ datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); cbdatumprintf(datum, "\r\n"); if(req->method != HM_HEAD){ list = nmgr_names(g_nmgr); for(i = 0; i < cblistnum(list); i++){ cbdatumprintf(datum, "%s\n", cblistval(list, i, NULL)); } cblistclose(list); } est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); } else { senderror(clsock, req, 404, "Not Found (the node does not exist)"); } } else if(!strcmp(cmd, "inform")){ if(!isnodeuser(node, req, user)){ sendautherror(clsock, req, node->name); } else { est_mtdb_interrupt(node->db); sendnodecmdinform(clsock, req, node); } } else if(!strcmp(cmd, "cacheusage")){ if(!isnodeuser(node, req, user)){ sendautherror(clsock, req, node->name); } else { sendnodecmdcacheusage(clsock, req, node); } } else if(!strcmp(cmd, "search")){ if(!isnodeuser(node, req, user)){ sendautherror(clsock, req, node->name); } else { est_mtdb_interrupt(node->db); sendnodecmdsearch(clsock, req, node); } } else if(!strcmp(cmd, "list")){ if(!isnodeuser(node, req, user)){ sendautherror(clsock, req, node->name); } else { est_mtdb_interrupt(node->db); sendnodecmdlist(clsock, req, node); } } else if(!strcmp(cmd, "get_doc")){ if(!isnodeuser(node, req, user)){ sendautherror(clsock, req, node->name); } else { est_mtdb_interrupt(node->db); sendnodecmdgetdoc(clsock, req, node); } } else if(!strcmp(cmd, "get_doc_attr")){ if(!isnodeuser(node, req, user)){ sendautherror(clsock, req, node->name); } else { est_mtdb_interrupt(node->db); sendnodecmdgetdocattr(clsock, req, node); } } else if(!strcmp(cmd, "etch_doc")){ if(!isnodeuser(node, req, user)){ sendautherror(clsock, req, node->name); } else { est_mtdb_interrupt(node->db); sendnodecmdetchdoc(clsock, req, node); } } else if(!strcmp(cmd, "uri_to_id")){ if(!isnodeuser(node, req, user)){ sendautherror(clsock, req, node->name); } else { est_mtdb_interrupt(node->db); sendnodecmduritoid(clsock, req, node); } } else if(!strcmp(cmd, "put_doc")){ if(g_runmode == RM_RDONLY){ senderror(clsock, req, 503, "Service Unavailable (read only)"); } else if(req->referer && !cbstrfwmatch(req->referer, req->prefix)){ senderror(clsock, req, 403, "Forbidden (invalid referrer)"); } else if(!isnodeadmin(node, req, user)){ sendautherror(clsock, req, node->name); } else { est_mtdb_interrupt(node->db); sendnodecmdputdoc(clsock, req, node); } } else if(!strcmp(cmd, "out_doc")){ if(g_runmode == RM_RDONLY){ senderror(clsock, req, 503, "Service Unavailable (read only)"); } else if(req->referer && !cbstrfwmatch(req->referer, req->prefix)){ senderror(clsock, req, 403, "Forbidden (invalid referrer)"); } else if(!isnodeadmin(node, req, user)){ sendautherror(clsock, req, node->name); } else { est_mtdb_interrupt(node->db); sendnodecmdoutdoc(clsock, req, node); } } else if(!strcmp(cmd, "edit_doc")){ if(g_runmode == RM_RDONLY){ senderror(clsock, req, 503, "Service Unavailable (read only)"); } else if(req->referer && !cbstrfwmatch(req->referer, req->prefix)){ senderror(clsock, req, 403, "Forbidden (invalid referrer)"); } else if(!isnodeadmin(node, req, user)){ sendautherror(clsock, req, node->name); } else { est_mtdb_interrupt(node->db); sendnodecmdeditdoc(clsock, req, node); } } else if(!strcmp(cmd, "sync")){ if(g_runmode == RM_RDONLY){ senderror(clsock, req, 503, "Service Unavailable (read only)"); } else if(req->referer && !cbstrfwmatch(req->referer, req->prefix)){ senderror(clsock, req, 403, "Forbidden (invalid referrer)"); } else if(!isnodeadmin(node, req, user)){ sendautherror(clsock, req, node->name); } else { est_mtdb_interrupt(node->db); sendnodecmdsync(clsock, req, node); } } else if(!strcmp(cmd, "optimize")){ if(g_runmode == RM_RDONLY){ senderror(clsock, req, 503, "Service Unavailable (read only)"); } else if(req->referer && !cbstrfwmatch(req->referer, req->prefix)){ senderror(clsock, req, 403, "Forbidden (invalid referrer)"); } else if(!isnodeadmin(node, req, user)){ sendautherror(clsock, req, node->name); } else { est_mtdb_interrupt(node->db); sendnodecmdoptimize(clsock, req, node); } } else if(!strcmp(cmd, "_set_user")){ if(g_runmode == RM_RDONLY){ senderror(clsock, req, 503, "Service Unavailable (read only)"); } else if(req->referer && !cbstrfwmatch(req->referer, req->prefix)){ senderror(clsock, req, 403, "Forbidden (invalid referrer)"); } else if(!isnodeadmin(node, req, user)){ sendautherror(clsock, req, node->name); } else { est_mtdb_interrupt(node->db); sendnodecmdsetuser(clsock, req, node); } } else if(!strcmp(cmd, "_set_link")){ if(g_runmode == RM_RDONLY){ senderror(clsock, req, 503, "Service Unavailable (read only)"); } else if(req->referer && !cbstrfwmatch(req->referer, req->prefix)){ senderror(clsock, req, 403, "Forbidden (invalid referrer)"); } else if(!isnodeadmin(node, req, user)){ sendautherror(clsock, req, node->name); } else { est_mtdb_interrupt(node->db); sendnodecmdsetlink(clsock, req, node); } } else if(!strcmp(cmd, SEARCHUICMD)){ if(req->ims > 0 && node->mtime <= req->ims && !req->reload){ sendnotmoderror(clsock, req); } else if(!isnodeuser(node, req, user)){ sendautherror(clsock, req, node->name); } else if(req->method == HM_HEAD){ sendheadonly(clsock, req, UIMIMETYPE); } else { est_mtdb_interrupt(node->db); sendnodecmdsearchui(clsock, req, node); } } else if(!strcmp(cmd, SEARCHATOMCMD)){ if(req->ims > 0 && node->mtime <= req->ims && !req->reload){ sendnotmoderror(clsock, req); } else if(!isnodeuser(node, req, user)){ sendautherror(clsock, req, node->name); } else if(req->method == HM_HEAD){ sendheadonly(clsock, req, ATOMMIMETYPE); } else { est_mtdb_interrupt(node->db); sendnodecmdsearchatom(clsock, req, node); } } else if(!strcmp(cmd, SEARCHRSSCMD)){ if(req->ims > 0 && node->mtime <= req->ims && !req->reload){ sendnotmoderror(clsock, req); } else if(!isnodeuser(node, req, user)){ sendautherror(clsock, req, node->name); } else if(req->method == HM_HEAD){ sendheadonly(clsock, req, RSSMIMETYPE); } else { est_mtdb_interrupt(node->db); sendnodecmdsearchrss(clsock, req, node); } } else if(!strcmp(cmd, OPENSEARCHCMD)){ if(req->ims > 0 && node->mtime <= req->ims && !req->reload){ sendnotmoderror(clsock, req); } else if(!isnodeuser(node, req, user)){ sendautherror(clsock, req, node->name); } else if(req->method == HM_HEAD){ sendheadonly(clsock, req, OSRCHMIMETYPE); } else { est_mtdb_interrupt(node->db); sendnodecmdopensearch(clsock, req, node); } } else { senderror(clsock, req, 400, "Bad Request (the command is invalid)"); } free(pbuf); if(!rwlock_unlock(g_mgrlock)) log_print(LL_ERROR, "unlocking failed"); } /* set the pseudo-attribute of the original node to a document */ static void setdocorigin(ESTDOC *doc, REQUEST *req, NODE *node, int score){ char pbuf[URIBUFSIZ]; sprintf(pbuf, "%s%s%s", req->prefix, NODEPREFIX, node->name); est_doc_add_attr(doc, DATTRNDURL, pbuf); est_doc_add_attr(doc, DATTRNDLABEL, node->label); if(score >= 0){ sprintf(pbuf, "%d", score); est_doc_add_attr(doc, DATTRNDSCORE, pbuf); } } /* merge local hints to total hints */ static void mergehints(CBMAP *total, CBMAP *local){ static pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER; const char *kbuf, *vbuf; char numbuf[NUMBUFSIZ]; int ksiz, num, vsiz; if(pthread_mutex_lock(&mymutex) != 0) return; cbmapiterinit(local); while((kbuf = cbmapiternext(local, &ksiz)) != NULL){ num = atoi(cbmapiterval(kbuf, NULL)); if((vbuf = cbmapget(total, kbuf, ksiz, NULL)) != NULL) num += atoi(vbuf); vsiz = sprintf(numbuf, "%d", num); cbmapput(total, kbuf, ksiz, numbuf, vsiz, TRUE); } pthread_mutex_unlock(&mymutex); } /* check whether the request is looping */ static int islooproute(const char *url, REQUEST *req){ CBLIST *list; const char *rp; int i; assert(url && req); if(!req->estvia) return FALSE; list = cbsplit(req->estvia, -1, ","); for(i = 0; i < cblistnum(list); i++){ rp = cblistval(list, i, NULL); while(*rp == ' ' || *rp == '\t'){ rp++; } if(!strcmp(url, rp)){ cblistclose(list); return TRUE; } } cblistclose(list); return FALSE; } /* search a local index */ static void *searchlocal(void *targ){ REQUEST *req; ESTCOND *cond; ESTDOC *doc; NODE *node; RESMAP *resmap; CBMAP *hints, *myhints; CBLIST *words, *mywords; const char *phrase, *vbuf; int i, max, *res, rnum, hits, miss, cnt, rateuri, score, down, vsiz; double curtime, weight; req = ((TARGLCSRCH *)targ)->req; cond = ((TARGLCSRCH *)targ)->cond; node = ((TARGLCSRCH *)targ)->node; hints = ((TARGLCSRCH *)targ)->hints; max = ((TARGLCSRCH *)targ)->max; if(max < LCSRCHRESMIN) max = LCSRCHRESMIN; est_cond_set_max(cond, max); resmap = ((TARGLCSRCH *)targ)->resmap; words = ((TARGLCSRCH *)targ)->words; log_print(LL_DEBUG, "[%s:%d]: local search: %s", req->claddr, req->clport, est_mtdb_name(node->db)); curtime = est_gettimeofday(); myhints = cbmapopenex(MINIBNUM); est_cond_set_options(cond, ESTCONDSCFB); res = est_mtdb_search(node->db, cond, &rnum, myhints); hits = (vbuf = cbmapget(myhints, "", 0, NULL)) ? atoi(vbuf) : rnum; if(max >= 0 && hits < max + 1 && est_cond_auxiliary_word(cond, "")){ free(res); est_cond_set_auxiliary(cond, -1); res = est_mtdb_search(node->db, cond, &rnum, myhints); hits = (vbuf = cbmapget(myhints, "", 0, NULL)) ? atoi(vbuf) : rnum; } miss = 0; mergehints(hints, myhints); ((TARGLCSRCH *)targ)->itime = est_gettimeofday() - curtime; cnt = 0; phrase = est_cond_phrase(cond); rateuri = g_rateuri && !(phrase && cbstrfwmatch(phrase, ESTOPSIMILAR)); for(i = 0; i < rnum && cnt < max; i++){ if(!(doc = est_mtdb_get_doc(node->db, res[i], 0))) continue; if(g_scancheck && phrase && phrase[0] != '[' && phrase[0] != '*' && !est_mtdb_scan_doc(node->db, doc, cond)){ est_doc_delete(doc); miss++; continue; } score = est_cond_score(cond, i); if(rateuri && g_scoreexpr != SE_ASIS){ if((vbuf = est_doc_attr(doc, ESTDATTRURI)) != NULL){ if(score < 100) score = 100; down = 4; if(cbstrfwimatch(vbuf, "file://")){ vbuf += 7; } else if(cbstrfwimatch(vbuf, "ftp://")){ vbuf += 6; } else if(cbstrfwimatch(vbuf, "http://")){ vbuf += 7; } else if(cbstrfwimatch(vbuf, "https://")){ vbuf += 8; } else { down += 3; } while(vbuf[0] != '\0'){ if(vbuf[0] == '?' || vbuf[0] == '#'){ down++; break; } if(vbuf[0] == '/' && vbuf[1] != '\0') down++; vbuf++; } score *= 8.0 / (double)down; } else { score = 0; } } setdocorigin(doc, req, node, score); if(g_scoreexpr != SE_ASIS){ weight = 1.0; if((vbuf = est_doc_attr(doc, ESTDATTRWEIGHT)) != NULL){ weight = strtod(vbuf, NULL); weight = weight >= 0.01 ? weight : 0.01; } weight /= 10.0; switch(g_mergemethod){ case MM_SCORE: score = score * weight; break; case MM_SCRK: score = score * (max * 2 - cnt) * weight; break; case MM_RANK: score = SELFCREDIT * (max - cnt); break; } } resmap_put(resmap, score, doc, NULL, NULL); cnt++; } free(res); ((TARGLCSRCH *)targ)->hnum = atoi(cbmapget(myhints, "", 0, NULL)); ((TARGLCSRCH *)targ)->mhnum = miss; mywords = est_hints_to_words(myhints); cbmapiterinit(myhints); for(i = 0; i < cblistnum(mywords); i++){ vbuf = cblistval(mywords, i, &vsiz); if(vsiz > 0) cblistpush(words, vbuf, vsiz); } cblistclose(mywords); cbmapclose(myhints); ((TARGLCSRCH *)targ)->etime = est_gettimeofday() - curtime; return NULL; } /* search a remote index */ static void *searchremote(void *targ){ REQUEST *req; ESTCOND *cond; NODE *node; RESMAP *resmap; ESTNODE *tnode; ESTNODERES *nres; CBMAP *hints, *attrs, *myhints, *whints; CBLIST *list; const char *myurl, *url, *label, *vbuf, *pv; char numbuf[NUMBUFSIZ], *snippet; int i, max, credit, depth, wwidth, hwidth, awidth, len, cnt, score; double curtime; req = ((TARGRMSRCH *)targ)->req; myurl = ((TARGRMSRCH *)targ)->myurl; cond = ((TARGRMSRCH *)targ)->cond; node = ((TARGRMSRCH *)targ)->node; hints = ((TARGRMSRCH *)targ)->hints; max = ((TARGRMSRCH *)targ)->max; if(max < RMSRCHRESMIN) max = RMSRCHRESMIN; est_cond_set_max(cond, max); resmap = ((TARGRMSRCH *)targ)->resmap; url = ((TARGRMSRCH *)targ)->url; label = ((TARGRMSRCH *)targ)->label; credit = ((TARGRMSRCH *)targ)->credit; depth = ((TARGRMSRCH *)targ)->depth; wwidth = ((TARGRMSRCH *)targ)->wwidth; hwidth = ((TARGRMSRCH *)targ)->hwidth; awidth = ((TARGRMSRCH *)targ)->awidth; if(!strcmp(myurl, url) || islooproute(url, req)){ log_print(LL_DEBUG, "[%s:%d]: omitting request loop (link): %s", req->claddr, req->clport, url); return NULL; } log_print(LL_DEBUG, "[%s:%d]: remote search: %s", req->claddr, req->clport, url); curtime = est_gettimeofday(); tnode = est_node_new(url); if(g_proxyhost[0] != '\0') est_node_set_proxy(tnode, g_proxyhost, g_proxyport); est_node_set_timeout(tnode, g_searchtimeout); if(req->estvia) est_node_add_header(tnode, ESTHTHVIA, req->estvia); est_node_add_header(tnode, ESTHTHVIA, myurl); est_node_set_snippet_width(tnode, wwidth, hwidth, awidth); if((nres = est_node_search(tnode, cond, depth - 1)) != NULL){ cnt = 0; while(est_noderes_shift_doc(nres, &attrs, &snippet)){ switch(g_mergemethod){ case MM_SCORE: score = (vbuf = cbmapget(attrs, DATTRNDSCORE, -1, NULL)) != NULL ? atoi(vbuf) : 0; score = score * (credit / (double)SELFCREDIT); resmap_put(resmap, score, NULL, attrs, snippet); break; case MM_SCRK: score = (vbuf = cbmapget(attrs, DATTRNDSCORE, -1, NULL)) != NULL ? atoi(vbuf) : 0; score = score * (credit / (double)SELFCREDIT) * (max * 2 - cnt); resmap_put(resmap, score, NULL, attrs, snippet); break; case MM_RANK: resmap_put(resmap, credit * (max - cnt), NULL, attrs, snippet); break; } cnt++; } whints = cbmapopenex(MINIBNUM); myhints = est_noderes_hints(nres); if((vbuf = cbmapget(myhints, "HIT", -1, NULL)) != NULL){ cbmapput(whints, "", 0, vbuf, -1, FALSE); ((TARGRMSRCH *)targ)->hnum = atoi(vbuf); } else { cbmapput(whints, "", 0, "0", -1, FALSE); } if((vbuf = cbmapget(myhints, "DOCNUM", -1, NULL)) != NULL) ((TARGRMSRCH *)targ)->dnum = atoi(vbuf); if((vbuf = cbmapget(myhints, "WORDNUM", -1, NULL)) != NULL) ((TARGRMSRCH *)targ)->wnum = atoi(vbuf); if((vbuf = cbmapget(myhints, "LINK#0", -1, NULL)) != NULL){ list = cbsplit(vbuf, -1, "\t"); if(cblistnum(list) == 7) ((TARGRMSRCH *)targ)->size = strtod(cblistval(list, 5, NULL), NULL); cblistclose(list); } for(i = 0; i < cbmaprnum(myhints); i++){ len = sprintf(numbuf, "HINT#%d", i + 1); if((vbuf = cbmapget(myhints, numbuf, len, NULL)) != NULL && (pv = strchr(vbuf, '\t')) != NULL && pv > vbuf){ cbmapput(whints, vbuf, pv - vbuf, pv + 1, -1, FALSE); } else { break; } } mergehints(hints, whints); cbmapclose(whints); ((TARGRMSRCH *)targ)->mtime = est_noderes_mdate(nres); est_noderes_delete(nres); } else { log_print(LL_WARN, "[%s:%d]: connection failed: %s", req->claddr, req->clport, url); } est_node_delete(tnode); ((TARGRMSRCH *)targ)->etime = est_gettimeofday() - curtime; return NULL; } /* set the phrase for similar search of a document */ static void setsimilarphrase(ESTCOND *cond, const char *url, int id){ ESTNODE *tnode; CBMAP *kwords; CBDATUM *pbuf; const char *kbuf, *vbuf; int ksiz, vsiz; tnode = est_node_new(url); if(g_proxyhost[0] != '\0') est_node_set_proxy(tnode, g_proxyhost, g_proxyport); est_node_set_timeout(tnode, g_searchtimeout); if((kwords = est_node_etch_doc(tnode, id)) != NULL){ pbuf = cbdatumopen(ESTOPSIMILAR " ", -1); cbdatumcat(pbuf, g_uismlrtune, -1); cbmapiterinit(kwords); while((kbuf = cbmapiternext(kwords, &ksiz)) != NULL){ vbuf = cbmapiterval(kbuf, &vsiz); cbdatumcat(pbuf, " WITH ", -1); cbdatumcat(pbuf, vbuf, vsiz); cbdatumcat(pbuf, " ", 1); cbdatumcat(pbuf, kbuf, ksiz); } est_cond_set_phrase(cond, cbdatumptr(pbuf)); cbdatumclose(pbuf); cbmapclose(kwords); } est_node_delete(tnode); } /* concatenate a document data to an output buffer */ static void catdocdata(CBDATUM *datum, NODE *node, RESDOC *resdoc, CBLIST *words, int wwidth, int hwidth, int awidth){ CBMAP *kwords; CBLIST *list; const CBLIST *texts; const char *kbuf, *vbuf; char *snippet; int i, id, ksiz, vsiz; if(resdoc->doc){ list = est_doc_attr_names(resdoc->doc); for(i = 0; i < cblistnum(list); i++){ vbuf = cblistval(list, i, NULL); cbdatumprintf(datum, "%s=%s\n", vbuf, est_doc_attr(resdoc->doc, vbuf)); } cblistclose(list); if(g_smlrvnum > 0){ cbdatumprintf(datum, "%s", ESTDCNTLVECTOR); id = est_doc_id(resdoc->doc); kwords = id > 0 ? est_mtdb_get_keywords(node->db, id) : NULL; if(!kwords) kwords = est_mtdb_etch_doc(node->db, resdoc->doc, g_smlrvnum); cbmapiterinit(kwords); while((kbuf = cbmapiternext(kwords, &ksiz)) != NULL){ cbdatumcat(datum, "\t", 1); cbdatumcat(datum, kbuf, ksiz); cbdatumcat(datum, "\t", 1); vbuf = cbmapiterval(kbuf, &vsiz); cbdatumcat(datum, vbuf, vsiz); } cbmapclose(kwords); cbdatumcat(datum, "\n", 1); } } else if(resdoc->attrs){ list = cbmapkeys(resdoc->attrs); cblistsort(list); for(i = 0; i < cblistnum(list); i++){ vbuf = cblistval(list, i, NULL); if(vbuf[0] == '%') continue; cbdatumprintf(datum, "%s=%s\n", vbuf, cbmapget(resdoc->attrs, vbuf, -1, NULL)); } cblistclose(list); if((vbuf = cbmapget(resdoc->attrs, ESTDCNTLVECTOR, -1, NULL)) != NULL) cbdatumprintf(datum, "%s\t%s\n", ESTDCNTLVECTOR, vbuf); } cbdatumcat(datum, "\n", 1); if(resdoc->body){ cbdatumcat(datum, resdoc->body, -1); } else if(resdoc->doc){ if(wwidth < 0){ texts = est_doc_texts(resdoc->doc); for(i = 0; i < cblistnum(texts); i++){ vbuf = cblistval(texts, i, &vsiz); cbdatumcat(datum, vbuf, vsiz); cbdatumcat(datum, "\n", 1); } vbuf = est_doc_hidden_texts(resdoc->doc); if(vbuf[0] != '\0') cbdatumprintf(datum, "\t%s\n", vbuf); } else if(wwidth > 0){ snippet = est_doc_make_snippet(resdoc->doc, words, wwidth, hwidth, awidth); cbdatumcat(datum, snippet, -1); free(snippet); } } } /* concatenate a document data to an output buffer for search user interface */ static void catdocdataui(CBDATUM *datum, int num, RESDOC *resdoc, CBLIST *words, REQUEST *req, NODE *node, const char *condstr, const char *simcondstr){ CBLIST *lines; const char *uri, *file, *title, *type, *ndurl, *ndlabel, *vbuf; char *turi, buf[URIBUFSIZ], *pv, *snippet, *word; int i, id, vsiz, ld; cbdatumprintf(datum, "
\n", num); if(!(uri = resdoc->doc ? est_doc_attr(resdoc->doc, ESTDATTRURI) : cbmapget(resdoc->attrs, ESTDATTRURI, -1, NULL))) uri = "."; turi = makeshownuri(uri); if((file = strrchr(turi, '/')) != NULL) file++; title = resdoc->doc ? est_doc_attr(resdoc->doc, ESTDATTRTITLE) : cbmapget(resdoc->attrs, ESTDATTRTITLE, -1, NULL); if(!title) title = resdoc->doc ? est_doc_attr(resdoc->doc, DATTRLFILE) : cbmapget(resdoc->attrs, DATTRLFILE, -1, NULL); if(!title) title = file; if(!title || title[0] == '\0') title = "(no title)"; type = resdoc->doc ? est_doc_attr(resdoc->doc, ESTDATTRTYPE) : cbmapget(resdoc->attrs, ESTDATTRTYPE, -1, NULL); if(!type || type[0] == '\0') type = DEFMIMETYPE; ndurl = resdoc->doc ? est_doc_attr(resdoc->doc, DATTRNDURL) : cbmapget(resdoc->attrs, DATTRNDURL, -1, NULL); if(!ndurl) ndurl = "."; ndlabel = resdoc->doc ? est_doc_attr(resdoc->doc, DATTRNDLABEL) : cbmapget(resdoc->attrs, DATTRNDLABEL, -1, NULL); vbuf = resdoc->doc ? est_doc_attr(resdoc->doc, ESTDATTRID) : cbmapget(resdoc->attrs, ESTDATTRID, -1, NULL); id = vbuf ? atoi(vbuf) : -1; if(!ndlabel) ndlabel = "(unknown)"; cbdatumprintf(datum, "
", num); cbdatumprintf(datum, "%@", turi, type, num, num, title); cbdatumprintf(datum, " (%@)", ndurl, SEARCHUICMD, condstr, UIMIMETYPE, num, num, ndlabel); cbdatumprintf(datum, "
\n"); for(i = 0; i < cblistnum(g_uiextattrs); i++){ sprintf(buf, "%s", cblistval(g_uiextattrs, i, NULL)); if(!(pv = strchr(buf, '|'))) continue; *(pv++) = '\0'; vbuf = resdoc->doc ? est_doc_attr(resdoc->doc, buf) : cbmapget(resdoc->attrs, buf, -1, NULL); if(!vbuf) continue; cbdatumprintf(datum, "
", num, i + 1); cbdatumprintf(datum, "%@: ", num, i + 1, pv); cbdatumprintf(datum, "%@", num, i + 1, vbuf); cbdatumprintf(datum, "
\n"); } if(g_snipwwidth > 0){ cbdatumprintf(datum, "
", num); snippet = resdoc->doc ? est_doc_make_snippet(resdoc->doc, words, g_snipwwidth, g_sniphwidth, g_snipawidth) : NULL; lines = cbsplit(snippet ? snippet: resdoc->body, -1, "\n"); ld = TRUE; for(i = 0; i < cblistnum(lines); i++){ vbuf = cblistval(lines, i, &vsiz); word = cbmemdup(vbuf, vsiz); if(vsiz < 1){ if(!ld) cbdatumprintf(datum, " ... "); ld = TRUE; } else if((pv = strchr(word, '\t')) != NULL){ *pv = '\0'; cbdatumprintf(datum, "%@", word); ld = FALSE; } else { cbdatumprintf(datum, "%@", word); ld = FALSE; } free(word); } cblistclose(lines); free(snippet); cbdatumprintf(datum, "
\n"); } cbdatumprintf(datum, "
", num); cbdatumprintf(datum, "%@", num, turi); if(g_smlrvnum > 0 && id > 0) cbdatumprintf(datum, " - [similar]", req->prefix, NODEPREFIX, node->name, SEARCHUICMD, simcondstr, id, ndurl); cbdatumprintf(datum, "
\n"); free(turi); cbdatumprintf(datum, "
\n"); } /* concatenate a document data to an output buffer for Atom feed interface */ static void catdocdataatom(CBDATUM *datum, RESDOC *resdoc, CBLIST *words, NODE *node){ CBLIST *lines; const char *uri, *file, *title, *author, *type, *vbuf; char *turi, *dstr, *pv, *snippet, *word; int i, vsiz, ld; time_t mtime, ctime; if(!(uri = resdoc->doc ? est_doc_attr(resdoc->doc, ESTDATTRURI) : cbmapget(resdoc->attrs, ESTDATTRURI, -1, NULL))) uri = "."; turi = makeshownuri(uri); if((file = strrchr(turi, '/')) != NULL) file++; title = resdoc->doc ? est_doc_attr(resdoc->doc, ESTDATTRTITLE) : cbmapget(resdoc->attrs, ESTDATTRTITLE, -1, NULL); if(!title) title = resdoc->doc ? est_doc_attr(resdoc->doc, DATTRLFILE) : cbmapget(resdoc->attrs, DATTRLFILE, -1, NULL); if(!title) title = file; if(!title || title[0] == '\0') title = "(no title)"; author = resdoc->doc ? est_doc_attr(resdoc->doc, ESTDATTRAUTHOR) : cbmapget(resdoc->attrs, ESTDATTRAUTHOR, -1, NULL); if(!author || author[0] == '\0') author = "(anonymous)"; type = resdoc->doc ? est_doc_attr(resdoc->doc, ESTDATTRTYPE) : cbmapget(resdoc->attrs, ESTDATTRTYPE, -1, NULL); if(!type || type[0] == '\0') type = DEFMIMETYPE; vbuf = resdoc->doc ? est_doc_attr(resdoc->doc, ESTDATTRMDATE) : cbmapget(resdoc->attrs, ESTDATTRMDATE, -1, NULL); mtime = vbuf && vbuf[0] != '\0' ? cbstrmktime(vbuf) : node->mtime; vbuf = resdoc->doc ? est_doc_attr(resdoc->doc, ESTDATTRCDATE) : cbmapget(resdoc->attrs, ESTDATTRCDATE, -1, NULL); ctime = vbuf && vbuf[0] != '\0' ? cbstrmktime(vbuf) : -1; cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "%@\n", turi); cbdatumprintf(datum, "%@\n", title); cbdatumprintf(datum, "%@\n", author); dstr = cbdatestrwww(mtime, 0); cbdatumprintf(datum, "%@\n", dstr); free(dstr); if(ctime > 0){ dstr = cbdatestrwww(ctime, 0); cbdatumprintf(datum, "%@\n", dstr); free(dstr); } vbuf = resdoc->doc ? est_doc_attr(resdoc->doc, ESTDATTRGENRE) : cbmapget(resdoc->attrs, ESTDATTRGENRE, -1, NULL); if(vbuf && vbuf[0] != '\0') cbdatumprintf(datum, "\n", vbuf); cbdatumprintf(datum, "\n", type, turi); if(g_snipwwidth > 0){ cbdatumprintf(datum, ""); cbdatumprintf(datum, ""); snippet = resdoc->doc ? est_doc_make_snippet(resdoc->doc, words, g_snipwwidth, g_sniphwidth, g_snipawidth) : NULL; lines = cbsplit(snippet ? snippet: resdoc->body, -1, "\n"); ld = TRUE; for(i = 0; i < cblistnum(lines); i++){ vbuf = cblistval(lines, i, &vsiz); word = cbmemdup(vbuf, vsiz); if(vsiz < 1){ if(!ld) cbdatumprintf(datum, " ... "); ld = TRUE; } else if((pv = strchr(word, '\t')) != NULL){ *pv = '\0'; cbdatumprintf(datum, "%@", word); ld = FALSE; } else { cbdatumprintf(datum, "%@", word); ld = FALSE; } free(word); } cblistclose(lines); free(snippet); cbdatumprintf(datum, ""); cbdatumprintf(datum, "\n"); } vbuf = resdoc->doc ? est_doc_attr(resdoc->doc, DATTRNDURL) : cbmapget(resdoc->attrs, DATTRNDURL, -1, NULL); if(vbuf && vbuf[0] != '\0') cbdatumprintf(datum, "%@\n", vbuf); vbuf = resdoc->doc ? est_doc_attr(resdoc->doc, DATTRNDLABEL) : cbmapget(resdoc->attrs, DATTRNDLABEL, -1, NULL); if(vbuf && vbuf[0] != '\0') cbdatumprintf(datum, "%@\n", vbuf); vbuf = resdoc->doc ? est_doc_attr(resdoc->doc, DATTRNDSCORE) : cbmapget(resdoc->attrs, DATTRNDSCORE, -1, NULL); if(vbuf && vbuf[0] != '\0') cbdatumprintf(datum, "%@\n", vbuf); vbuf = resdoc->doc ? est_doc_attr(resdoc->doc, ESTDATTRID) : cbmapget(resdoc->attrs, ESTDATTRID, -1, NULL); if(vbuf && vbuf[0] != '\0') cbdatumprintf(datum, "%@\n", vbuf); cbdatumprintf(datum, "\n"); free(turi); } /* concatenate a document data to an output buffer for RSS feed interface */ static void catdocdatarss(CBDATUM *datum, RESDOC *resdoc, CBLIST *words, NODE *node){ CBLIST *lines; const char *uri, *file, *title, *author, *type, *vbuf; char *turi, *dstr, *pv, *snippet, *word; int i, vsiz, ld; time_t mtime; if(!(uri = resdoc->doc ? est_doc_attr(resdoc->doc, ESTDATTRURI) : cbmapget(resdoc->attrs, ESTDATTRURI, -1, NULL))) uri = "."; turi = makeshownuri(uri); if((file = strrchr(turi, '/')) != NULL) file++; title = resdoc->doc ? est_doc_attr(resdoc->doc, ESTDATTRTITLE) : cbmapget(resdoc->attrs, ESTDATTRTITLE, -1, NULL); if(!title) title = resdoc->doc ? est_doc_attr(resdoc->doc, DATTRLFILE) : cbmapget(resdoc->attrs, DATTRLFILE, -1, NULL); if(!title) title = file; if(!title || title[0] == '\0') title = "(no title)"; author = resdoc->doc ? est_doc_attr(resdoc->doc, ESTDATTRAUTHOR) : cbmapget(resdoc->attrs, ESTDATTRAUTHOR, -1, NULL); if(!author || author[0] == '\0') author = "(anonymous)"; type = resdoc->doc ? est_doc_attr(resdoc->doc, ESTDATTRTYPE) : cbmapget(resdoc->attrs, ESTDATTRTYPE, -1, NULL); if(!type || type[0] == '\0') type = DEFMIMETYPE; vbuf = resdoc->doc ? est_doc_attr(resdoc->doc, ESTDATTRMDATE) : cbmapget(resdoc->attrs, ESTDATTRMDATE, -1, NULL); mtime = vbuf && vbuf[0] != '\0' ? cbstrmktime(vbuf) : node->mtime; cbdatumprintf(datum, "\n", turi); cbdatumprintf(datum, "%@\n", title); cbdatumprintf(datum, "%@\n", turi); cbdatumprintf(datum, "%@\n", title); vbuf = resdoc->doc ? est_doc_attr(resdoc->doc, ESTDATTRGENRE) : cbmapget(resdoc->attrs, ESTDATTRGENRE, -1, NULL); if(vbuf && vbuf[0] != '\0') cbdatumprintf(datum, "%@\n", vbuf); cbdatumprintf(datum, "%@\n", author); dstr = cbdatestrwww(mtime, 0); cbdatumprintf(datum, "%@\n", dstr); free(dstr); cbdatumprintf(datum, "%@\n", type); vbuf = resdoc->doc ? est_doc_attr(resdoc->doc, ESTDATTRLANG) : cbmapget(resdoc->attrs, ESTDATTRLANG, -1, NULL); if(vbuf && vbuf[0] != '\0') cbdatumprintf(datum, "%@\n", vbuf); if(g_snipwwidth > 0){ cbdatumprintf(datum, ""); snippet = resdoc->doc ? est_doc_make_snippet(resdoc->doc, words, g_snipwwidth, g_sniphwidth, g_snipawidth) : NULL; lines = cbsplit(snippet ? snippet: resdoc->body, -1, "\n"); ld = TRUE; for(i = 0; i < cblistnum(lines); i++){ vbuf = cblistval(lines, i, &vsiz); word = cbmemdup(vbuf, vsiz); if(vsiz < 1){ if(!ld) cbdatumprintf(datum, " ... "); ld = TRUE; } else if((pv = strchr(word, '\t')) != NULL){ *pv = '\0'; cbdatumprintf(datum, "%@", word); ld = FALSE; } else { cbdatumprintf(datum, "%@", word); ld = FALSE; } free(word); } cblistclose(lines); free(snippet); cbdatumprintf(datum, "\n"); } vbuf = resdoc->doc ? est_doc_attr(resdoc->doc, DATTRNDURL) : cbmapget(resdoc->attrs, DATTRNDURL, -1, NULL); if(vbuf && vbuf[0] != '\0') cbdatumprintf(datum, "%@\n", vbuf); vbuf = resdoc->doc ? est_doc_attr(resdoc->doc, DATTRNDLABEL) : cbmapget(resdoc->attrs, DATTRNDLABEL, -1, NULL); if(vbuf && vbuf[0] != '\0') cbdatumprintf(datum, "%@\n", vbuf); vbuf = resdoc->doc ? est_doc_attr(resdoc->doc, DATTRNDSCORE) : cbmapget(resdoc->attrs, DATTRNDSCORE, -1, NULL); if(vbuf && vbuf[0] != '\0') cbdatumprintf(datum, "%@\n", vbuf); vbuf = resdoc->doc ? est_doc_attr(resdoc->doc, ESTDATTRID) : cbmapget(resdoc->attrs, ESTDATTRID, -1, NULL); if(vbuf && vbuf[0] != '\0') cbdatumprintf(datum, "%@\n", vbuf); cbdatumprintf(datum, "\n"); free(turi); } /* make a URI to be shown */ static char *makeshownuri(const char *uri){ char *turi, *bef, *aft, *pv, *nuri; int i; turi = cbmemdup(uri, -1); for(i = 0; i < cblistnum(g_uireplaces); i++){ bef = cbmemdup(cblistval(g_uireplaces, i, NULL), -1); if((pv = strstr(bef, "{{!}}")) != NULL){ *pv = '\0'; aft = pv + 5; } else { aft = ""; } nuri = est_regex_replace(turi, bef, aft); free(turi); turi = nuri; free(bef); } return turi; } /* send the result of the search command */ static void sendnodecmdinform(int clsock, REQUEST *req, NODE *node){ CBDATUM *datum; const char *kbuf; if(est_mtdb_cache_usage(node->db) >= g_helpershift && sendnodecmdinformhelper(clsock, req, node)) return; if(pthread_mutex_lock(&(node->mutex)) != 0){ log_print(LL_ERROR, "locking failed"); senderror(clsock, req, 500, "Internal Server Error (locking failed)"); return; } datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: %s; charset=UTF-8\r\n", ESTINFORMTYPE); cbdatumprintf(datum, "\r\n"); cbdatumprintf(datum, "%s\t%s\t%d\t%d\t%.0f\n", node->name, node->label, est_mtdb_doc_num(node->db), est_mtdb_word_num(node->db), est_mtdb_size(node->db)); cbdatumprintf(datum, "\n"); cbmapiterinit(node->admins); while((kbuf = cbmapiternext(node->admins, NULL)) != NULL){ if(kbuf[0] != '\0') cbdatumprintf(datum, "%s\n", kbuf); } cbdatumprintf(datum, "\n"); cbmapiterinit(node->users); while((kbuf = cbmapiternext(node->users, NULL)) != NULL){ if(kbuf[0] != '\0') cbdatumprintf(datum, "%s\n", kbuf); } cbdatumprintf(datum, "\n"); cbmapiterinit(node->links); while((kbuf = cbmapiternext(node->links, NULL)) != NULL){ if(kbuf[0] != '\0') cbdatumprintf(datum, "%s\t%s\n", kbuf, cbmapiterval(kbuf, NULL)); } est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (inform)", req->claddr, req->clport); if(pthread_mutex_unlock(&(node->mutex)) != 0) log_print(LL_ERROR, "unlocking failed"); } /* send the result of the inform helper command */ static int sendnodecmdinformhelper(int clsock, REQUEST *req, NODE *node){ CBDATUM *datum; char *dbuf, *ebuf, dbpath[URIBUFSIZ], opath[URIBUFSIZ]; const char *kbuf; int i, dsiz, err, dnum, wnum; double fsiz; if(pthread_mutex_lock(&(node->mutex)) != 0){ log_print(LL_ERROR, "locking failed"); return FALSE; } datum = cbdatumopen(NULL, -1); sprintf(dbpath, "%s%c%s%c%s", g_rootdir, ESTPATHCHR, NODEDIR, ESTPATHCHR, node->name); cbdatumprintf(datum, "%s \"%s\"", INFORMHELPER, dbpath); sprintf(opath, "%s%chelper-%d", dbpath, ESTPATHCHR, g_accesscount); cbdatumprintf(datum, " \"%s\"", opath); log_print(LL_DEBUG, "[%s:%d]: calling the inform helper: %s", req->claddr, req->clport, cbdatumptr(datum)); for(i = 0; i < HELPERTRYNUM; i++){ system(cbdatumptr(datum)); if((dbuf = cbreadfile(opath, &dsiz)) != NULL) break; est_usleep(1000 * 100); } cbdatumclose(datum); err = FALSE; if(dbuf){ datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: %s; charset=UTF-8\r\n", ESTINFORMTYPE); ebuf = cbdatestrhttp(node->mtime, 0); cbdatumprintf(datum, "Last-Modified: %s\r\n", ebuf); free(ebuf); cbdatumprintf(datum, "\r\n"); kbuf = dbuf; dnum = atoi(kbuf); wnum = 0; fsiz = 0.0; if((kbuf = strchr(kbuf, '\t')) != NULL){ kbuf++; wnum = atoi(kbuf); if((kbuf = strchr(kbuf, '\t')) != NULL){ kbuf++; fsiz = strtod(kbuf, NULL); } } cbdatumprintf(datum, "%s\t%s\t%d\t%d\t%.0f\n", node->name, node->label, dnum, wnum, fsiz); cbdatumprintf(datum, "\n"); cbmapiterinit(node->admins); while((kbuf = cbmapiternext(node->admins, NULL)) != NULL){ if(kbuf[0] != '\0') cbdatumprintf(datum, "%s\n", kbuf); } cbdatumprintf(datum, "\n"); cbmapiterinit(node->users); while((kbuf = cbmapiternext(node->users, NULL)) != NULL){ if(kbuf[0] != '\0') cbdatumprintf(datum, "%s\n", kbuf); } cbdatumprintf(datum, "\n"); cbmapiterinit(node->links); while((kbuf = cbmapiternext(node->links, NULL)) != NULL){ if(kbuf[0] != '\0') cbdatumprintf(datum, "%s\t%s\n", kbuf, cbmapiterval(kbuf, NULL)); } est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); free(dbuf); log_print(LL_DEBUG, "[%s:%d]: 200 OK (inform-helper)", req->claddr, req->clport); } else { log_print(LL_WARN, "[%s:%d]: the inform helper failed", req->claddr, req->clport); err = TRUE; } unlink(opath); if(pthread_mutex_unlock(&(node->mutex)) != 0) log_print(LL_ERROR, "unlocking failed"); return err ? FALSE : TRUE; } /* send the result of the cacheusage command */ static void sendnodecmdcacheusage(int clsock, REQUEST *req, NODE *node){ CBDATUM *datum; if(pthread_mutex_lock(&(node->mutex)) != 0){ log_print(LL_ERROR, "locking failed"); senderror(clsock, req, 500, "Internal Server Error (locking failed)"); return; } datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); cbdatumprintf(datum, "\r\n"); cbdatumprintf(datum, "%.6f\n", est_mtdb_cache_usage(node->db)); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (cacheusage)", req->claddr, req->clport); if(pthread_mutex_unlock(&(node->mutex)) != 0) log_print(LL_ERROR, "unlocking failed"); } /* send the result of the search command */ static void sendnodecmdsearch(int clsock, REQUEST *req, NODE *node){ pthread_t lth, *rths; TARGLCSRCH ltarg; TARGRMSRCH *rtargs; RESMAP *resmap; RESDOC **resdocs; ESTCOND *cond; CBMAP *hints; CBDATUM *datum; const char *tmp, *url, *label, *kbuf, *phrase, *order, *distinct; char *myurl, *dstr, name[NUMBUFSIZ], masks[LINKMASKMAX+1], *zbuf; int i, num, max, depth, wwidth, hwidth, awidth, skip, dnum, wnum, hnum, end, ksiz, rnum, zsiz; double curtime; time_t mtime; myurl = cbsprintf("%s%s%s", req->prefix, NODEPREFIX, node->name); if(islooproute(myurl, req)){ log_print(LL_DEBUG, "[%s:%d]: omitting request loop (self): %s", req->claddr, req->clport, myurl); free(myurl); senderror(clsock, req, 400, "Bad Request (the request loops)"); return; } if(est_mtdb_cache_usage(node->db) >= g_helpershift && (!(tmp = cbmapget(req->params, "depth", -1, NULL)) || atoi(tmp) < 1 || cbmaprnum(node->links) < 1) && sendnodecmdsearchhelper(clsock, req, node, myurl)){ free(myurl); return; } if(pthread_mutex_lock(&(node->mutex)) != 0){ log_print(LL_ERROR, "locking failed"); senderror(clsock, req, 500, "Internal Server Error (locking failed)"); return; } cond = est_cond_new(); max = DEFMAXSRCH; depth = 0; wwidth = g_snipwwidth; hwidth = g_sniphwidth; awidth = g_snipawidth; skip = 0; if((tmp = cbmapget(req->params, "skip", -1, NULL)) != NULL && (num = atoi(tmp)) > 0) skip = num; if((tmp = cbmapget(req->params, "phrase", -1, NULL)) != NULL && tmp[0] != '\0') est_cond_set_phrase(cond, tmp); if((tmp = cbmapget(req->params, "attr", -1, NULL)) != NULL && tmp[0] != '\0') est_cond_add_attr(cond, tmp); for(i = 0; i <= CONDATTRMAX; i++){ num = sprintf(name, "attr%d", i); if((tmp = cbmapget(req->params, name, num, NULL)) != NULL && tmp[0] != '\0') est_cond_add_attr(cond, tmp); } if((tmp = cbmapget(req->params, "order", -1, NULL)) != NULL && tmp[0] != '\0') est_cond_set_order(cond, tmp); if((tmp = cbmapget(req->params, "max", -1, NULL)) != NULL && (num = atoi(tmp)) >= 0) max = num; max = max > g_searchmax ? g_searchmax : max; if((tmp = cbmapget(req->params, "options", -1, NULL)) != NULL && (num = atoi(tmp)) > 0) est_cond_set_options(cond, num); if((tmp = cbmapget(req->params, "auxiliary", -1, NULL)) != NULL) est_cond_set_auxiliary(cond, atoi(tmp)); if((tmp = cbmapget(req->params, "distinct", -1, NULL)) != NULL && tmp[0] != '\0') est_cond_set_distinct(cond, tmp); if((tmp = cbmapget(req->params, "depth", -1, NULL)) != NULL && (num = atoi(tmp)) > 0) depth = num; if(depth >= g_searchdepth) depth = g_searchdepth; if((tmp = cbmapget(req->params, "wwidth", -1, NULL)) != NULL) wwidth = atoi(tmp); if((tmp = cbmapget(req->params, "hwidth", -1, NULL)) != NULL && (num = atoi(tmp)) >= 0) hwidth = num; if((tmp = cbmapget(req->params, "awidth", -1, NULL)) != NULL && (num = atoi(tmp)) >= 0) awidth = num; if((tmp = cbmapget(req->params, "mask", -1, NULL)) != NULL && tmp[0] != '\0'){ num = atoi(tmp); for(i = 0; i <= LINKMASKMAX; i++){ masks[i] = num & (1 << i); } } else if((tmp = cbmapget(req->params, "allmask", -1, NULL)) != NULL && tmp[0] != '\0'){ memset(masks, 1, LINKMASKMAX+1); for(i = 0; i <= LINKMASKMAX; i++){ num = sprintf(name, "nomask%d", i); if((tmp = cbmapget(req->params, name, num, NULL)) != NULL && tmp[0] != '\0') masks[i] = 0; } } else { memset(masks, 0, LINKMASKMAX+1); if(!(tmp = cbmapget(req->params, "nomask", -1, NULL)) || tmp[0] == '\0'){ for(i = 0; i <= LINKMASKMAX; i++){ num = sprintf(name, "mask%d", i); if((tmp = cbmapget(req->params, name, num, NULL)) != NULL && tmp[0] != '\0') masks[i] = 1; } } } resmap = resmap_new(); hints = cbmapopenex(MINIBNUM); curtime = est_gettimeofday(); ltarg.req = req; ltarg.alive = TRUE; ltarg.cond = est_cond_dup(cond); ltarg.hints = hints; ltarg.max = max + skip + 1; ltarg.node = node; ltarg.resmap = resmap; ltarg.words = cblistopen(); ltarg.hnum = 0; ltarg.mhnum = 0; ltarg.itime = 0.0; ltarg.etime = 0.0; if(masks[0]){ ltarg.alive = FALSE; } else if(g_stmode){ searchlocal(<arg); ltarg.alive = FALSE; } else if(pthread_create(<h, NULL, searchlocal, <arg) != 0){ log_print(LL_WARN, "creating thread failed"); ltarg.alive = FALSE; } rths = cbmalloc(cbmaprnum(node->links) * sizeof(pthread_t) + 1); rtargs = cbmalloc(cbmaprnum(node->links) * sizeof(TARGRMSRCH) + 1); cbmapiterinit(node->links); for(i = 0; (url = cbmapiternext(node->links, NULL)) != NULL; i++){ rtargs[i].req = req; rtargs[i].alive = TRUE; rtargs[i].myurl = myurl; rtargs[i].cond = est_cond_dup(cond); rtargs[i].hints = hints; rtargs[i].max = max + skip + 1; rtargs[i].node = node; rtargs[i].resmap = resmap; rtargs[i].url = url; label = cbmapiterval(url, NULL); if((tmp = strchr(label, '\t')) != NULL){ rtargs[i].label = cbmemdup(label, tmp - label); rtargs[i].credit = atoi(tmp + 1); } else { rtargs[i].label = cbmemdup(label, -1); rtargs[i].credit = 0; } rtargs[i].depth = depth; rtargs[i].wwidth = wwidth; rtargs[i].hwidth = hwidth; rtargs[i].awidth = awidth; rtargs[i].hnum = 0; rtargs[i].dnum = 0; rtargs[i].wnum = 0; rtargs[i].etime = 0.0; rtargs[i].size = 0.0; rtargs[i].mtime = 0; if(depth < 1 || (i < LINKMASKMAX && masks[i+1])){ rtargs[i].alive = FALSE; rtargs[i].hnum = -1; rtargs[i].dnum = -1; rtargs[i].wnum = -1; rtargs[i].etime = -1.0; rtargs[i].size = -1.0; } else if(g_stmode){ searchremote(&rtargs[i]); rtargs[i].alive = FALSE; } else if(pthread_create(rths + i, NULL, searchremote, rtargs + i) != 0){ log_print(LL_WARN, "creating thread failed"); rtargs[i].alive = FALSE; } } if(pthread_mutex_unlock(&(node->mutex)) != 0) log_print(LL_ERROR, "unlocking failed"); dnum = 0; wnum = 0; mtime = node->mtime; if(ltarg.alive){ if(pthread_join(lth, NULL) == 0){ dnum = est_mtdb_doc_num(node->db); wnum = est_mtdb_word_num(node->db); } else { log_print(LL_ERROR, "joining thread failed"); } } else if(g_stmode){ dnum = est_mtdb_doc_num(node->db); wnum = est_mtdb_word_num(node->db); } for(i = 0; i < cbmaprnum(node->links); i++){ if(rtargs[i].alive){ if(pthread_join(rths[i], NULL) == 0){ dnum += rtargs[i].dnum; wnum += rtargs[i].wnum; if(rtargs[i].mtime > mtime) mtime = rtargs[i].mtime; } else { log_print(LL_ERROR, "joining thread failed"); } } else if(g_stmode && depth > 0){ dnum += rtargs[i].dnum; wnum += rtargs[i].wnum; if(rtargs[i].mtime > mtime) mtime = rtargs[i].mtime; } } hnum = (tmp = cbmapget(hints, "", 0, NULL)) ? atoi(tmp) : 0; hnum -= ltarg.mhnum; if(hnum < 0) hnum = 0; end = max + skip; curtime = est_gettimeofday() - curtime; datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: %s; charset=UTF-8\r\n", ESTSEARCHTYPE); if(req->gzip){ cbdatumprintf(datum, "Content-Encoding: gzip\r\n"); } else if(req->deflate){ cbdatumprintf(datum, "Content-Encoding: deflate\r\n"); } dstr = cbdatestrhttp(mtime, 0); cbdatumprintf(datum, "Last-Modified: %s\r\n", dstr); free(dstr); cbdatumprintf(datum, "\r\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumsetsize(datum, 0); cbdatumprintf(datum, "%s\n", g_bordstr); cbdatumprintf(datum, "VERSION\t%s\n", _EST_PROTVER); cbdatumprintf(datum, "NODE\t%s\n", myurl); cbdatumprintf(datum, "HIT\t%d\n", hnum); cbmapiterinit(hints); num = 1; while((kbuf = cbmapiternext(hints, &ksiz)) != NULL){ if(ksiz < 1) continue; cbdatumprintf(datum, "HINT#%d\t%s\t%s\n", num, kbuf, cbmapiterval(kbuf, NULL)); num++; } cbdatumprintf(datum, "DOCNUM\t%d\n", dnum); cbdatumprintf(datum, "WORDNUM\t%d\n", wnum); cbdatumprintf(datum, "TIME\t%.6f\n", curtime / 1000.0); if(ltarg.itime >= 0.0) cbdatumprintf(datum, "TIME#i\t%.6f\n", ltarg.itime / 1000.0); if(ltarg.etime >= 0.0) cbdatumprintf(datum, "TIME#0\t%.6f\n", ltarg.etime / 1000.0); for(i = 0; i < cbmaprnum(node->links); i++){ if(rtargs[i].etime >= 0.0) cbdatumprintf(datum, "TIME#%d\t%.6f\n", i + 1, rtargs[i].etime / 1000.0); } cbdatumprintf(datum, "LINK#0\t%s\t", myurl); cbdatumprintf(datum, "%s\t%d\t%d\t%d\t%.0f\t%d\n", node->label, SELFCREDIT, est_mtdb_doc_num(node->db), est_mtdb_word_num(node->db), est_mtdb_size(node->db), ltarg.hnum - ltarg.mhnum); for(i = 0; i < cbmaprnum(node->links); i++){ cbdatumprintf(datum, "LINK#%d\t%s\t%s\t%d\t%d\t%d\t%.0f\t%d\n", i + 1, rtargs[i].url, rtargs[i].label, rtargs[i].credit, rtargs[i].dnum, rtargs[i].wnum, rtargs[i].size, rtargs[i].hnum); } cbdatumprintf(datum, "VIEW\tSNIPPET\n"); cbdatumprintf(datum, "\n"); phrase = est_cond_phrase(cond); order = est_cond_order(cond); if(!order && phrase && cbstrfwmatch(phrase, ESTOPSIMILAR)) order = DATTRNDSCORE " " ESTORDNUMD; distinct = depth > 0 && cbmaprnum(node->links) > 0 ? est_cond_distinct(cond) : NULL; resdocs = resmap_list(resmap, &rnum, order, distinct); for(i = skip; i < rnum && i < end; i++){ cbdatumprintf(datum, "%s\n", g_bordstr); catdocdata(datum, node, resdocs[i], ltarg.words, wwidth, hwidth, awidth); } free(resdocs); cbdatumprintf(datum, "%s:END\n", g_bordstr); if(req->gzip){ if((zbuf = est_deflate(cbdatumptr(datum), cbdatumsize(datum), &zsiz, 1)) != NULL){ est_sock_send_all(clsock, zbuf, zsiz); free(zbuf); } } else if(req->deflate){ if((zbuf = est_deflate(cbdatumptr(datum), cbdatumsize(datum), &zsiz, 0)) != NULL){ est_sock_send_all(clsock, zbuf, zsiz); free(zbuf); } } else { est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); } cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (search)", req->claddr, req->clport); cblistclose(ltarg.words); est_cond_delete(ltarg.cond); for(i = 0; i < cbmaprnum(node->links); i++){ free(rtargs[i].label); est_cond_delete(rtargs[i].cond); } free(rtargs); free(rths); cbmapclose(hints); resmap_delete(resmap); est_cond_delete(cond); free(myurl); } /* send the result of the search helper command */ static int sendnodecmdsearchhelper(int clsock, REQUEST *req, NODE *node, const char *myurl){ CBDATUM *datum; char *dbuf, *ebuf, dbpath[URIBUFSIZ], opath[URIBUFSIZ]; int i, dsiz, err; if(pthread_mutex_lock(&(node->mutex)) != 0){ log_print(LL_ERROR, "locking failed"); return FALSE; } datum = cbdatumopen(NULL, -1); sprintf(dbpath, "%s%c%s%c%s", g_rootdir, ESTPATHCHR, NODEDIR, ESTPATHCHR, node->name); cbdatumprintf(datum, "%s \"%s\"", SEARCHHELPER, dbpath); dbuf = cbmapdump(req->params, &dsiz); ebuf = cbbaseencode(dbuf, dsiz); sprintf(opath, "%s%chelper-%d", dbpath, ESTPATHCHR, g_accesscount); cbdatumprintf(datum, " \"%s\" \"%s\"", opath, ebuf); free(ebuf); free(dbuf); ebuf = cbbaseencode(myurl, -1); cbdatumprintf(datum, " \"%s\"", ebuf); free(ebuf); ebuf = cbbaseencode(node->label, -1); cbdatumprintf(datum, " \"%s\"", ebuf); free(ebuf); cbdatumprintf(datum, " \"%d\" \"%d\" \"%d\" \"%d\" \"%d\"", g_rateuri, g_mergemethod, g_scoreexpr, g_searchmax, g_wildmax); cbdatumprintf(datum, " \"%d\" \"%d\" \"%d\" \"%d\" \"%d\"", g_snipwwidth, g_sniphwidth, g_snipawidth, g_scancheck, g_smlrvnum); log_print(LL_DEBUG, "[%s:%d]: calling the search helper: %s", req->claddr, req->clport, cbdatumptr(datum)); for(i = 0; i < HELPERTRYNUM; i++){ system(cbdatumptr(datum)); if((dbuf = cbreadfile(opath, &dsiz)) != NULL) break; est_usleep(1000 * 100); } cbdatumclose(datum); err = FALSE; if(dbuf){ datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: %s; charset=UTF-8\r\n", ESTSEARCHTYPE); ebuf = cbdatestrhttp(node->mtime, 0); cbdatumprintf(datum, "Last-Modified: %s\r\n", ebuf); free(ebuf); cbdatumprintf(datum, "\r\n"); cbdatumcat(datum, dbuf, dsiz); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); free(dbuf); log_print(LL_DEBUG, "[%s:%d]: 200 OK (search-helper)", req->claddr, req->clport); } else { log_print(LL_WARN, "[%s:%d]: the search helper failed", req->claddr, req->clport); err = TRUE; } unlink(opath); if(pthread_mutex_unlock(&(node->mutex)) != 0) log_print(LL_ERROR, "unlocking failed"); return err ? FALSE : TRUE; } /* send the result of the list command */ static void sendnodecmdlist(int clsock, REQUEST *req, NODE *node){ ESTDOC *doc; CBDATUM *head, *body; const char *tmp, *prev; char *zbuf; int max, num, cnt, id, zsiz; max = DEFMAXSRCH; if((tmp = cbmapget(req->params, "max", -1, NULL)) != NULL && (num = atoi(tmp)) >= 0) max = num; max = max > g_searchmax ? g_searchmax : max; prev = cbmapget(req->params, "prev", -1, NULL); if(pthread_mutex_lock(&(node->mutex)) != 0){ log_print(LL_ERROR, "locking failed"); senderror(clsock, req, 500, "Internal Server Error (locking failed)"); return; } body = cbdatumopen(NULL, -1); est_mtdb_iter_init(node->db, prev); cnt = 0; while(cnt < max && (id = est_mtdb_iter_next(node->db)) > 0){ if(!(doc = est_mtdb_get_doc(node->db, id, ESTGDNOTEXT))) continue; tmp = est_doc_attr(doc, ESTDATTRID); cbdatumcat(body, tmp ? tmp : "", -1); cbdatumcat(body, "\t", 1); tmp = est_doc_attr(doc, ESTDATTRURI); cbdatumcat(body, tmp ? tmp : "", -1); cbdatumcat(body, "\t", 1); tmp = est_doc_attr(doc, ESTDATTRDIGEST); cbdatumcat(body, tmp ? tmp : "", -1); cbdatumcat(body, "\t", 1); tmp = est_doc_attr(doc, ESTDATTRCDATE); cbdatumcat(body, tmp ? tmp : "", -1); cbdatumcat(body, "\t", 1); tmp = est_doc_attr(doc, ESTDATTRMDATE); cbdatumcat(body, tmp ? tmp : "", -1); cbdatumcat(body, "\t", 1); tmp = est_doc_attr(doc, ESTDATTRADATE); cbdatumcat(body, tmp ? tmp : "", -1); cbdatumcat(body, "\t", 1); tmp = est_doc_attr(doc, ESTDATTRTITLE); cbdatumcat(body, tmp ? tmp : "", -1); cbdatumcat(body, "\t", 1); tmp = est_doc_attr(doc, ESTDATTRAUTHOR); cbdatumcat(body, tmp ? tmp : "", -1); cbdatumcat(body, "\t", 1); tmp = est_doc_attr(doc, ESTDATTRTYPE); cbdatumcat(body, tmp ? tmp : "", -1); cbdatumcat(body, "\t", 1); tmp = est_doc_attr(doc, ESTDATTRLANG); cbdatumcat(body, tmp ? tmp : "", -1); cbdatumcat(body, "\t", 1); tmp = est_doc_attr(doc, ESTDATTRGENRE); cbdatumcat(body, tmp ? tmp : "", -1); cbdatumcat(body, "\t", 1); tmp = est_doc_attr(doc, ESTDATTRSIZE); cbdatumcat(body, tmp ? tmp : "", -1); cbdatumcat(body, "\t", 1); tmp = est_doc_attr(doc, ESTDATTRWEIGHT); cbdatumcat(body, tmp ? tmp : "", -1); cbdatumcat(body, "\t", 1); tmp = est_doc_attr(doc, ESTDATTRMISC); cbdatumcat(body, tmp ? tmp : "", -1); cbdatumcat(body, "\n", 1); est_doc_delete(doc); cnt++; } if(pthread_mutex_unlock(&(node->mutex)) != 0) log_print(LL_ERROR, "unlocking failed"); head = cbdatumopen(NULL, -1); cbdatumprintf(head, "HTTP/1.0 200 OK\r\n"); addservinfo(head, req->now, 0); cbdatumprintf(head, "Content-Type: %s; charset=UTF-8\r\n", ESTLISTTYPE); if(req->gzip){ cbdatumprintf(head, "Content-Encoding: gzip\r\n"); } else if(req->deflate){ cbdatumprintf(head, "Content-Encoding: deflate\r\n"); } cbdatumprintf(head, "\r\n"); est_sock_send_all(clsock, cbdatumptr(head), cbdatumsize(head)); cbdatumclose(head); if(req->gzip){ if((zbuf = est_deflate(cbdatumptr(body), cbdatumsize(body), &zsiz, 1)) != NULL){ est_sock_send_all(clsock, zbuf, zsiz); free(zbuf); } } else if(req->deflate){ if((zbuf = est_deflate(cbdatumptr(body), cbdatumsize(body), &zsiz, 0)) != NULL){ est_sock_send_all(clsock, zbuf, zsiz); free(zbuf); } } else { est_sock_send_all(clsock, cbdatumptr(body), cbdatumsize(body)); } cbdatumclose(body); log_print(LL_DEBUG, "[%s:%d]: 200 OK (list)", req->claddr, req->clport); } /* send the result of the get_doc command */ static void sendnodecmdgetdoc(int clsock, REQUEST *req, NODE *node){ ESTDOC *doc; CBDATUM *datum; const char *tmp, *uri; char *draft, *zbuf; int id, zsiz; id = (tmp = cbmapget(req->params, "id", -1, NULL)) ? atoi(tmp) : 0; if(!(uri = cbmapget(req->params, "uri", -1, NULL))) uri = ""; if(id < 1 && uri[0] == '\0'){ senderror(clsock, req, 400, "Bad Request (the parameters lack)"); return; } if(est_mtdb_cache_usage(node->db) >= g_helpershift && sendnodecmdgetdochelper(clsock, req, node, id, uri)) return; if(id < 1) id = est_mtdb_uri_to_id(node->db, uri); if(id > 0 && (doc = est_mtdb_get_doc(node->db, id, 0)) != NULL){ setdocorigin(doc, req, node, -1); draft = est_doc_dump_draft(doc); datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: %s; charset=UTF-8\r\n", ESTDRAFTTYPE); if(req->gzip){ cbdatumprintf(datum, "Content-Encoding: gzip\r\n"); } else if(req->deflate){ cbdatumprintf(datum, "Content-Encoding: deflate\r\n"); } cbdatumprintf(datum, "\r\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumsetsize(datum, 0); cbdatumcat(datum, draft, -1); if(req->gzip){ if((zbuf = est_deflate(cbdatumptr(datum), cbdatumsize(datum), &zsiz, 1)) != NULL){ est_sock_send_all(clsock, zbuf, zsiz); free(zbuf); } } else if(req->deflate){ if((zbuf = est_deflate(cbdatumptr(datum), cbdatumsize(datum), &zsiz, 0)) != NULL){ est_sock_send_all(clsock, zbuf, zsiz); free(zbuf); } } else { est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); } cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (get_doc)", req->claddr, req->clport); free(draft); est_doc_delete(doc); } else { if(est_mtdb_fatal(node->db)) log_print(LL_ERROR, "DB-ERROR: %s", est_err_msg(est_mtdb_error(node->db))); senderror(clsock, req, 400, "Bad Request (maybe, the document does not exist)"); } } /* send the result of the get_doc helper command */ static int sendnodecmdgetdochelper(int clsock, REQUEST *req, NODE *node, int id, const char *uri){ ESTDOC *doc; CBDATUM *datum; char *dbuf, *ebuf, dbpath[URIBUFSIZ], opath[URIBUFSIZ], *draft; int i, dsiz, err; if(pthread_mutex_lock(&(node->mutex)) != 0){ log_print(LL_ERROR, "locking failed"); return FALSE; } datum = cbdatumopen(NULL, -1); sprintf(dbpath, "%s%c%s%c%s", g_rootdir, ESTPATHCHR, NODEDIR, ESTPATHCHR, node->name); cbdatumprintf(datum, "%s \"%s\"", GETDOCHELPER, dbpath); sprintf(opath, "%s%chelper-%d", dbpath, ESTPATHCHR, g_accesscount); cbdatumprintf(datum, " \"%s\"", opath); cbdatumprintf(datum, " \"%d\"", id); ebuf = cbbaseencode(uri, -1); cbdatumprintf(datum, " \"%s\"", ebuf); free(ebuf); log_print(LL_DEBUG, "[%s:%d]: calling the get_doc helper: %s", req->claddr, req->clport, cbdatumptr(datum)); for(i = 0; i < HELPERTRYNUM; i++){ system(cbdatumptr(datum)); if((dbuf = cbreadfile(opath, &dsiz)) != NULL) break; est_usleep(1000 * 100); } cbdatumclose(datum); err = FALSE; if(dbuf){ if(dsiz > 0){ datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: %s; charset=UTF-8\r\n", ESTDRAFTTYPE); ebuf = cbdatestrhttp(node->mtime, 0); cbdatumprintf(datum, "Last-Modified: %s\r\n", ebuf); free(ebuf); cbdatumprintf(datum, "\r\n"); doc = est_doc_new_from_draft(dbuf); setdocorigin(doc, req, node, -1); draft = est_doc_dump_draft(doc); cbdatumcat(datum, draft, -1); free(draft); free(doc); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (get_doc-helper)", req->claddr, req->clport); } else { senderror(clsock, req, 400, "Bad Request (maybe, the document does not exist)"); } free(dbuf); } else { log_print(LL_WARN, "[%s:%d]: the get_doc helper failed", req->claddr, req->clport); err = TRUE; } unlink(opath); if(pthread_mutex_unlock(&(node->mutex)) != 0) log_print(LL_ERROR, "unlocking failed"); return err ? FALSE : TRUE; } /* send the result of the get_doc_attr command */ static void sendnodecmdgetdocattr(int clsock, REQUEST *req, NODE *node){ CBDATUM *datum; const char *tmp, *uri, *attr; char *value; int id; id = (tmp = cbmapget(req->params, "id", -1, NULL)) ? atoi(tmp) : 0; if(!(uri = cbmapget(req->params, "uri", -1, NULL))) uri = ""; if(!(attr = cbmapget(req->params, "attr", -1, NULL))) attr = ""; if((id < 1 && uri[0] == '\0') || attr[0] == '\0'){ senderror(clsock, req, 400, "Bad Request (the parameters lack)"); return; } if(est_mtdb_cache_usage(node->db) >= g_helpershift && sendnodecmdgetdocattrhelper(clsock, req, node, id, uri, attr)) return; if(id < 1) id = est_mtdb_uri_to_id(node->db, uri); if(id > 0 && (value = est_mtdb_get_doc_attr(node->db, id, attr)) != NULL){ datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); cbdatumprintf(datum, "\r\n"); cbdatumprintf(datum, "%s\n", value); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (get_doc_attr)", req->claddr, req->clport); free(value); } else { if(est_mtdb_fatal(node->db)) log_print(LL_ERROR, "DB-ERROR: %s", est_err_msg(est_mtdb_error(node->db))); senderror(clsock, req, 400, "Bad Request (maybe, the document or the attribute does not exist)"); } } /* send the result of the get_doc_attr helper command */ static int sendnodecmdgetdocattrhelper(int clsock, REQUEST *req, NODE *node, int id, const char *uri, const char *attr){ CBDATUM *datum; char *dbuf, *ebuf, dbpath[URIBUFSIZ], opath[URIBUFSIZ]; int i, dsiz, err; if(pthread_mutex_lock(&(node->mutex)) != 0){ log_print(LL_ERROR, "locking failed"); return FALSE; } datum = cbdatumopen(NULL, -1); sprintf(dbpath, "%s%c%s%c%s", g_rootdir, ESTPATHCHR, NODEDIR, ESTPATHCHR, node->name); cbdatumprintf(datum, "%s \"%s\"", GETDOCATTRHELPER, dbpath); sprintf(opath, "%s%chelper-%d", dbpath, ESTPATHCHR, g_accesscount); cbdatumprintf(datum, " \"%s\"", opath); cbdatumprintf(datum, " \"%d\"", id); ebuf = cbbaseencode(uri, -1); cbdatumprintf(datum, " \"%s\"", ebuf); free(ebuf); ebuf = cbbaseencode(attr, -1); cbdatumprintf(datum, " \"%s\"", ebuf); free(ebuf); log_print(LL_DEBUG, "[%s:%d]: calling the get_doc_attr helper: %s", req->claddr, req->clport, cbdatumptr(datum)); for(i = 0; i < HELPERTRYNUM; i++){ system(cbdatumptr(datum)); if((dbuf = cbreadfile(opath, &dsiz)) != NULL) break; est_usleep(1000 * 100); } cbdatumclose(datum); err = FALSE; if(dbuf){ if(strchr(dbuf, '\n')){ datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); ebuf = cbdatestrhttp(node->mtime, 0); cbdatumprintf(datum, "Last-Modified: %s\r\n", ebuf); free(ebuf); cbdatumprintf(datum, "\r\n"); cbdatumcat(datum, dbuf, dsiz); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (get_doc_attr-helper)", req->claddr, req->clport); } else { senderror(clsock, req, 400, "Bad Request (maybe, the document or the attribute does not exist)"); } free(dbuf); } else { log_print(LL_WARN, "[%s:%d]: the get_doc_attr helper failed", req->claddr, req->clport); err = TRUE; } unlink(opath); if(pthread_mutex_unlock(&(node->mutex)) != 0) log_print(LL_ERROR, "unlocking failed"); return err ? FALSE : TRUE; } /* send the result of the etch_doc command */ static void sendnodecmdetchdoc(int clsock, REQUEST *req, NODE *node){ ESTDOC *doc; CBMAP *kwords; CBDATUM *datum; const char *tmp, *uri, *kbuf; int id, ksiz; id = (tmp = cbmapget(req->params, "id", -1, NULL)) ? atoi(tmp) : 0; if(!(uri = cbmapget(req->params, "uri", -1, NULL))) uri = ""; if(id < 1 && uri[0] == '\0'){ senderror(clsock, req, 400, "Bad Request (the parameters lack)"); return; } if(est_mtdb_cache_usage(node->db) >= g_helpershift && sendnodecmdetchdochelper(clsock, req, node, id, uri)) return; if(id < 1) id = est_mtdb_uri_to_id(node->db, uri); kwords = NULL; if(id > 0){ kwords = est_mtdb_get_keywords(node->db, id); if(!kwords && (doc = est_mtdb_get_doc(node->db, id, 0)) != NULL){ kwords = est_mtdb_etch_doc(node->db, doc, g_smlrvnum > 0 ? g_smlrvnum : KWORDNUM); if(g_runmode == RM_NORMAL) est_mtdb_put_keywords(node->db, id, kwords, 1.0); est_doc_delete(doc); } } if(kwords){ datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); cbdatumprintf(datum, "\r\n"); cbmapiterinit(kwords); while((kbuf = cbmapiternext(kwords, &ksiz)) != NULL){ cbdatumprintf(datum, "%s\t%s\n", kbuf, cbmapiterval(kbuf, NULL)); } est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (etch_doc)", req->claddr, req->clport); cbmapclose(kwords); } else { if(est_mtdb_fatal(node->db)) log_print(LL_ERROR, "DB-ERROR: %s", est_err_msg(est_mtdb_error(node->db))); senderror(clsock, req, 400, "Bad Request (maybe, the document does not exist)"); } } /* send the result of the etch_doc helper command */ static int sendnodecmdetchdochelper(int clsock, REQUEST *req, NODE *node, int id, const char *uri){ CBDATUM *datum; char *dbuf, *ebuf, dbpath[URIBUFSIZ], opath[URIBUFSIZ]; int i, dsiz, err; if(pthread_mutex_lock(&(node->mutex)) != 0){ log_print(LL_ERROR, "locking failed"); return FALSE; } datum = cbdatumopen(NULL, -1); sprintf(dbpath, "%s%c%s%c%s", g_rootdir, ESTPATHCHR, NODEDIR, ESTPATHCHR, node->name); cbdatumprintf(datum, "%s \"%s\"", ETCHDOCHELPER, dbpath); sprintf(opath, "%s%chelper-%d", dbpath, ESTPATHCHR, g_accesscount); cbdatumprintf(datum, " \"%s\"", opath); cbdatumprintf(datum, " \"%d\"", id); ebuf = cbbaseencode(uri, -1); cbdatumprintf(datum, " \"%s\"", ebuf); free(ebuf); cbdatumprintf(datum, " \"%d\"", g_smlrvnum > 0 ? g_smlrvnum : KWORDNUM); log_print(LL_DEBUG, "[%s:%d]: calling the etch_doc helper: %s", req->claddr, req->clport, cbdatumptr(datum)); for(i = 0; i < HELPERTRYNUM; i++){ system(cbdatumptr(datum)); if((dbuf = cbreadfile(opath, &dsiz)) != NULL) break; est_usleep(1000 * 100); } cbdatumclose(datum); err = FALSE; if(dbuf){ if(strchr(dbuf, '\n')){ datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: %s; charset=UTF-8\r\n", ESTDRAFTTYPE); ebuf = cbdatestrhttp(node->mtime, 0); cbdatumprintf(datum, "Last-Modified: %s\r\n", ebuf); free(ebuf); cbdatumprintf(datum, "\r\n"); cbdatumcat(datum, dbuf, dsiz); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (etch_doc-helper)", req->claddr, req->clport); } else { senderror(clsock, req, 400, "Bad Request (maybe, the document does not exist)"); } free(dbuf); } else { log_print(LL_WARN, "[%s:%d]: the etch_doc helper failed", req->claddr, req->clport); err = TRUE; } unlink(opath); if(pthread_mutex_unlock(&(node->mutex)) != 0) log_print(LL_ERROR, "unlocking failed"); return err ? FALSE : TRUE; } /* send the result of the uri_to_id command */ static void sendnodecmduritoid(int clsock, REQUEST *req, NODE *node){ CBDATUM *datum; const char *uri; int id; uri = cbmapget(req->params, "uri", -1, NULL); if(!uri){ senderror(clsock, req, 400, "Bad Request (the parameters lack)"); return; } if(est_mtdb_cache_usage(node->db) >= g_helpershift && sendnodecmduritoidhelper(clsock, req, node, uri)) return; if((id = est_mtdb_uri_to_id(node->db, uri)) > 0){ datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); cbdatumprintf(datum, "\r\n"); cbdatumprintf(datum, "%d\n", id); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (uri_to_id)", req->claddr, req->clport); } else { if(est_mtdb_fatal(node->db)) log_print(LL_ERROR, "DB-ERROR: %s", est_err_msg(est_mtdb_error(node->db))); senderror(clsock, req, 400, "Bad Request (maybe, the URI is not registered)"); } } /* send the result of the inform helper command */ static int sendnodecmduritoidhelper(int clsock, REQUEST *req, NODE *node, const char *uri){ CBDATUM *datum; char *dbuf, *ebuf, dbpath[URIBUFSIZ], opath[URIBUFSIZ]; int i, dsiz, err; if(pthread_mutex_lock(&(node->mutex)) != 0){ log_print(LL_ERROR, "locking failed"); return FALSE; } datum = cbdatumopen(NULL, -1); sprintf(dbpath, "%s%c%s%c%s", g_rootdir, ESTPATHCHR, NODEDIR, ESTPATHCHR, node->name); cbdatumprintf(datum, "%s \"%s\"", URITOIDHELPER, dbpath); sprintf(opath, "%s%chelper-%d", dbpath, ESTPATHCHR, g_accesscount); cbdatumprintf(datum, " \"%s\"", opath); ebuf = cbbaseencode(uri, -1); cbdatumprintf(datum, " \"%s\"", ebuf); free(ebuf); log_print(LL_DEBUG, "[%s:%d]: calling the uri_to_id helper: %s", req->claddr, req->clport, cbdatumptr(datum)); for(i = 0; i < HELPERTRYNUM; i++){ system(cbdatumptr(datum)); if((dbuf = cbreadfile(opath, &dsiz)) != NULL) break; est_usleep(1000 * 100); } cbdatumclose(datum); err = FALSE; if(dbuf){ if(dsiz > 0){ datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); ebuf = cbdatestrhttp(node->mtime, 0); cbdatumprintf(datum, "Last-Modified: %s\r\n", ebuf); free(ebuf); cbdatumprintf(datum, "\r\n"); cbdatumcat(datum, dbuf, dsiz); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (uri_to_id-helper)", req->claddr, req->clport); } else { senderror(clsock, req, 400, "Bad Request (maybe, the URI is not registered)"); } free(dbuf); } else { log_print(LL_WARN, "[%s:%d]: the uri_to_id helper failed", req->claddr, req->clport); err = TRUE; } unlink(opath); if(pthread_mutex_unlock(&(node->mutex)) != 0) log_print(LL_ERROR, "unlocking failed"); return err ? FALSE : TRUE; } /* send the result of the pub_doc command */ static void sendnodecmdputdoc(int clsock, REQUEST *req, NODE *node){ ESTDOC *doc; CBDATUM *datum; CBMAP *kwords; const char *draft; double ratio; if(req->ctype && cbstrfwimatch(req->ctype, ESTDRAFTTYPE)){ draft = req->body; } else { draft = cbmapget(req->params, "draft", -1, NULL); } if(!draft){ senderror(clsock, req, 400, "Bad Request (the parameters lack)"); return; } doc = est_doc_new_from_draft(draft); est_doc_slim(doc, g_limittextsize); if(est_mtdb_put_doc(node->db, doc, ESTPDCLEAN)){ if(!est_doc_keywords(doc) && g_smlrvnum > 0){ if(g_extdelay < 0){ kwords = est_morph_etch_doc(doc, g_smlrvnum); est_mtdb_put_keywords(node->db, est_doc_id(doc), kwords, 1.0); cbmapclose(kwords); } else if(est_mtdb_doc_num(node->db) > g_extdelay){ kwords = est_mtdb_etch_doc(node->db, doc, g_smlrvnum); est_mtdb_put_keywords(node->db, est_doc_id(doc), kwords, 1.0); cbmapclose(kwords); } } node->mtime = req->now; node->dirty = TRUE; ratio = est_mtdb_used_cache_size(node->db) / g_cachesize; g_cacheratio = ratio > g_cacheratio ? ratio : g_cacheratio; datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); cbdatumprintf(datum, "\r\n"); cbdatumprintf(datum, "OK\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (put_doc)", req->claddr, req->clport); } else { if(est_mtdb_fatal(node->db)) log_print(LL_ERROR, "DB-ERROR: %s", est_err_msg(est_mtdb_error(node->db))); if(!est_doc_attr(doc, ESTDATTRURI)){ senderror(clsock, req, 400, "Bad Request (maybe, the document is invalid)"); } else { senderror(clsock, req, 400, "Bad Request (maybe, the database has a fatal error)"); } } est_doc_delete(doc); } /* send the result of the out_doc command */ static void sendnodecmdoutdoc(int clsock, REQUEST *req, NODE *node){ CBDATUM *datum; const char *tmp, *uri; int id; id = (tmp = cbmapget(req->params, "id", -1, NULL)) ? atoi(tmp) : 0; if(!(uri = cbmapget(req->params, "uri", -1, NULL))) uri = ""; if(id < 1 && uri[0] == '\0'){ senderror(clsock, req, 400, "Bad Request (the parameters lack)"); return; } if(id < 1) id = est_mtdb_uri_to_id(node->db, uri); if(id > 0 && est_mtdb_out_doc(node->db, id, ESTODCLEAN)){ node->mtime = req->now; node->dirty = TRUE; datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); cbdatumprintf(datum, "\r\n"); cbdatumprintf(datum, "OK\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (out_doc)", req->claddr, req->clport); } else { if(est_mtdb_fatal(node->db)) log_print(LL_ERROR, "DB-ERROR: %s", est_err_msg(est_mtdb_error(node->db))); senderror(clsock, req, 400, "Bad Request (maybe, the document does not exist)"); } } /* send the result of the edit_doc command */ static void sendnodecmdeditdoc(int clsock, REQUEST *req, NODE *node){ ESTDOC *doc; CBDATUM *datum; const char *draft; if(req->ctype && cbstrfwimatch(req->ctype, ESTDRAFTTYPE)){ draft = req->body; } else { draft = cbmapget(req->params, "draft", -1, NULL); } if(!draft){ senderror(clsock, req, 400, "Bad Request (the parameters lack)"); return; } doc = est_doc_new_from_draft(draft); if(est_mtdb_edit_doc(node->db, doc)){ node->mtime = req->now; datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); cbdatumprintf(datum, "\r\n"); cbdatumprintf(datum, "OK\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (edit_doc)", req->claddr, req->clport); } else { if(est_mtdb_fatal(node->db)) log_print(LL_ERROR, "DB-ERROR: %s", est_err_msg(est_mtdb_error(node->db))); if(!est_doc_attr(doc, ESTDATTRID) || !est_doc_attr(doc, ESTDATTRURI)){ senderror(clsock, req, 400, "Bad Request (maybe, the document is invalid)"); } else { senderror(clsock, req, 400, "Bad Request (maybe, the database has a fatal error)"); } } est_doc_delete(doc); } /* send the result of the sync command */ static void sendnodecmdsync(int clsock, REQUEST *req, NODE *node){ CBDATUM *datum; if(est_mtdb_sync(node->db)){ node->mtime = req->now; datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); cbdatumprintf(datum, "\r\n"); cbdatumprintf(datum, "OK\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (sync)", req->claddr, req->clport); } else { if(est_mtdb_fatal(node->db)) log_print(LL_ERROR, "DB-ERROR: %s", est_err_msg(est_mtdb_error(node->db))); senderror(clsock, req, 400, "Bad Request (maybe, the database has a fatal error)"); } } /* send the result of the optimize command */ static void sendnodecmdoptimize(int clsock, REQUEST *req, NODE *node){ CBDATUM *datum; if(est_mtdb_optimize(node->db, ESTOPTNOPURGE)){ node->mtime = req->now; datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); cbdatumprintf(datum, "\r\n"); cbdatumprintf(datum, "OK\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (optimize)", req->claddr, req->clport); } else { if(est_mtdb_fatal(node->db)) log_print(LL_ERROR, "DB-ERROR: %s", est_err_msg(est_mtdb_error(node->db))); senderror(clsock, req, 400, "Bad Request (maybe, the database has a fatal error)"); } } /* send the result of the _set_user command */ static void sendnodecmdsetuser(int clsock, REQUEST *req, NODE *node){ CBDATUM *datum; const char *name, *tmp; int mode; name = cbmapget(req->params, "name", -1, NULL); mode = (tmp = cbmapget(req->params, "mode", -1, NULL)) != NULL && tmp[0] != '\0' ? atoi(tmp) : -1; if(!name || name[0] == '\0' || mode < 0 || mode > 2){ senderror(clsock, req, 400, "Bad Request (the parameters lack)"); return; } if(!check_alnum_name(name)){ senderror(clsock, req, 400, "Bad Request (the name is invalid)"); return; } switch(mode){ case 1: cbmapput(node->admins, name, -1, "", 0, FALSE); break; case 2: cbmapput(node->users, name, -1, "", 0, FALSE); break; default: cbmapout(node->admins, name, -1); cbmapout(node->users, name, -1); break; } datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); cbdatumprintf(datum, "\r\n"); cbdatumprintf(datum, "OK\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (_set_user)", req->claddr, req->clport); } /* send the result of the _set_link command */ static void sendnodecmdsetlink(int clsock, REQUEST *req, NODE *node){ CBDATUM *datum; const char *url, *label, *tmp; int credit; url = cbmapget(req->params, "url", -1, NULL); label = cbmapget(req->params, "label", -1, NULL); credit = (tmp = cbmapget(req->params, "credit", -1, NULL)) ? atoi(tmp) : -1; if(!url || url[0] == '\0'){ senderror(clsock, req, 400, "Bad Request (the parameters lack)"); return; } node_set_link(node, url, label, credit); datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: text/plain; charset=UTF-8\r\n"); cbdatumprintf(datum, "\r\n"); cbdatumprintf(datum, "OK\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (_set_link)", req->claddr, req->clport); } /* send the result of search user interface */ static void sendnodecmdsearchui(int clsock, REQUEST *req, NODE *node){ pthread_t lth, *rths; TARGLCSRCH ltarg; TARGRMSRCH *rtargs; RESMAP *resmap; RESDOC **resdocs; ESTCOND *cond; CBMAP *hints; const CBLIST *attrs; CBDATUM *condbuf, *simcondbuf, *datum; const char *tmp, *url, *label, *kbuf, *phrase, *order, *simnode; char *myurl, *dstr, name[NUMBUFSIZ], masks[LINKMASKMAX+1], *zbuf; int i, num, max, depth, simid, page, top, dnum, wnum, hnum, end, tab, axk, ksiz, rnum, zsiz; double curtime; time_t mtime; myurl = cbsprintf("%s%s%s", req->prefix, NODEPREFIX, node->name); if(islooproute(myurl, req)){ log_print(LL_DEBUG, "[%s:%d]: omitting request loop (self): %s", req->claddr, req->clport, myurl); free(myurl); senderror(clsock, req, 400, "Bad Request (the request loops)"); return; } if(pthread_mutex_lock(&(node->mutex)) != 0){ log_print(LL_ERROR, "locking failed"); senderror(clsock, req, 500, "Internal Server Error (locking failed)"); return; } cond = est_cond_new(); max = DEFMAXSRCH; depth = 0; page = 0; simid = 0; simnode = NULL; if((tmp = cbmapget(req->params, "page", -1, NULL)) != NULL && (num = atoi(tmp)) > 0) page = num; if((tmp = cbmapget(req->params, "pageone", -1, NULL)) != NULL && (num = atoi(tmp)) > 1) page = num - 1; if((tmp = cbmapget(req->params, "phrase", -1, NULL)) != NULL && tmp[0] != '\0') est_cond_set_phrase(cond, tmp); if((tmp = cbmapget(req->params, "attr", -1, NULL)) != NULL && tmp[0] != '\0') est_cond_add_attr(cond, tmp); for(i = 0; i <= CONDATTRMAX; i++){ num = sprintf(name, "attr%d", i); if((tmp = cbmapget(req->params, name, num, NULL)) != NULL && tmp[0] != '\0') est_cond_add_attr(cond, tmp); } if((tmp = cbmapget(req->params, "order", -1, NULL)) != NULL && tmp[0] != '\0') est_cond_set_order(cond, tmp); if((tmp = cbmapget(req->params, "max", -1, NULL)) != NULL && (num = atoi(tmp)) >= 0) max = num; max = max > g_searchmax ? g_searchmax : max; if((tmp = cbmapget(req->params, "depth", -1, NULL)) != NULL && (num = atoi(tmp)) > 0) depth = num; if((tmp = cbmapget(req->params, "simid", -1, NULL)) != NULL && (num = atoi(tmp)) > 0) simid = num; if((tmp = cbmapget(req->params, "simnode", -1, NULL)) != NULL && tmp[0] != '\0') simnode = tmp; if((tmp = cbmapget(req->params, "mask", -1, NULL)) != NULL && tmp[0] != '\0'){ num = atoi(tmp); for(i = 0; i <= LINKMASKMAX; i++){ masks[i] = num & (1 << i); } } else if((tmp = cbmapget(req->params, "allmask", -1, NULL)) != NULL && tmp[0] != '\0'){ memset(masks, 1, LINKMASKMAX+1); for(i = 0; i <= LINKMASKMAX; i++){ num = sprintf(name, "nomask%d", i); if((tmp = cbmapget(req->params, name, num, NULL)) != NULL && tmp[0] != '\0') masks[i] = 0; } } else { memset(masks, 0, LINKMASKMAX+1); if(!(tmp = cbmapget(req->params, "nomask", -1, NULL)) || tmp[0] == '\0'){ for(i = 0; i <= LINKMASKMAX; i++){ num = sprintf(name, "mask%d", i); if((tmp = cbmapget(req->params, name, num, NULL)) != NULL && tmp[0] != '\0') masks[i] = 1; } } } if(depth >= g_searchdepth) depth = g_searchdepth; switch(g_uiphraseform){ case PM_SIMPLE: est_cond_set_options(cond, ESTCONDSIMPLE); break; case PM_ROUGH: est_cond_set_options(cond, ESTCONDROUGH); break; case PM_UNION: est_cond_set_options(cond, ESTCONDUNION); break; case PM_ISECT: est_cond_set_options(cond, ESTCONDISECT); break; } top = !est_cond_phrase(cond) && !est_cond_attrs(cond) && simid < 1; phrase = est_cond_phrase(cond); attrs = est_cond_attrs(cond); order = est_cond_order(cond); condbuf = cbdatumopen(NULL, -1); simcondbuf = cbdatumopen(NULL, -1); cbdatumprintf(condbuf, "phrase=%?", phrase ? phrase : ""); cbdatumprintf(simcondbuf, "phrase="); if(attrs){ for(i = 0; i < cblistnum(attrs); i++){ tmp = cblistval(attrs, i, NULL); if(tmp[0] != '\0'){ cbdatumprintf(condbuf, "&attr%d=%?", i + 1, tmp); cbdatumprintf(simcondbuf, "&attr%d=%?", i + 1, tmp); } } } if(order && order[0] != '\0'){ cbdatumprintf(condbuf, "&order=%?", order); cbdatumprintf(simcondbuf, "&order=%?", order); } if(max != DEFMAXSRCH){ cbdatumprintf(condbuf, "&max=%d", max); cbdatumprintf(simcondbuf, "&max=%d", max); } if(depth > 0){ cbdatumprintf(condbuf, "&depth=%d", depth); cbdatumprintf(simcondbuf, "&depth=%d", depth); } if(simid > 0 && simnode && simnode[0] != '\0'){ cbdatumprintf(condbuf, "&simid=%d", simid); cbdatumprintf(condbuf, "&simnode=%?", simnode); setsimilarphrase(cond, simnode, simid); phrase = est_cond_phrase(cond); } for(i = 0; i <= LINKMASKMAX; i++){ if(masks[i]) cbdatumprintf(condbuf, "&mask%d=1", i); } resmap = resmap_new(); hints = cbmapopenex(MINIBNUM); curtime = est_gettimeofday(); ltarg.req = req; ltarg.alive = TRUE; ltarg.cond = est_cond_dup(cond); ltarg.hints = hints; ltarg.max = max + page * max + 1; ltarg.node = node; ltarg.resmap = resmap; ltarg.words = cblistopen(); ltarg.hnum = 0; ltarg.mhnum = 0; ltarg.itime = 0.0; ltarg.etime = 0.0; if(top || masks[0]){ ltarg.alive = FALSE; } else if(g_stmode){ searchlocal(<arg); ltarg.alive = FALSE; } else if(pthread_create(<h, NULL, searchlocal, <arg) != 0){ log_print(LL_WARN, "creating thread failed"); ltarg.alive = FALSE; } rths = cbmalloc(cbmaprnum(node->links) * sizeof(pthread_t) + 1); rtargs = cbmalloc(cbmaprnum(node->links) * sizeof(TARGRMSRCH) + 1); cbmapiterinit(node->links); for(i = 0; (url = cbmapiternext(node->links, NULL)) != NULL; i++){ rtargs[i].req = req; rtargs[i].alive = TRUE; rtargs[i].myurl = myurl; rtargs[i].cond = est_cond_dup(cond); rtargs[i].hints = hints; rtargs[i].max = max + page * max + 1; rtargs[i].node = node; rtargs[i].resmap = resmap; rtargs[i].url = url; label = cbmapiterval(url, NULL); if((tmp = strchr(label, '\t')) != NULL){ rtargs[i].label = cbmemdup(label, tmp - label); rtargs[i].credit = atoi(tmp + 1); } else { rtargs[i].label = cbmemdup(label, -1); rtargs[i].credit = 0; } rtargs[i].depth = depth; rtargs[i].wwidth = g_snipwwidth; rtargs[i].hwidth = g_sniphwidth; rtargs[i].awidth = g_snipawidth; rtargs[i].hnum = 0; rtargs[i].dnum = 0; rtargs[i].wnum = 0; rtargs[i].etime = 0.0; rtargs[i].size = 0.0; rtargs[i].mtime = 0; if(top || depth < 1 || (i < LINKMASKMAX && masks[i+1])){ rtargs[i].alive = FALSE; rtargs[i].hnum = -1; rtargs[i].dnum = -1; rtargs[i].wnum = -1; rtargs[i].etime = -1.0; rtargs[i].size = -1.0; } else if(g_stmode){ searchremote(&rtargs[i]); rtargs[i].alive = FALSE; } else if(pthread_create(rths + i, NULL, searchremote, rtargs + i) != 0){ log_print(LL_WARN, "creating thread failed"); rtargs[i].alive = FALSE; } } if(pthread_mutex_unlock(&(node->mutex)) != 0) log_print(LL_ERROR, "unlocking failed"); dnum = 0; wnum = 0; mtime = node->mtime; if(ltarg.alive){ if(pthread_join(lth, NULL) == 0){ dnum = est_mtdb_doc_num(node->db); wnum = est_mtdb_word_num(node->db); } else { log_print(LL_ERROR, "joining thread failed"); } } else if(!top && g_stmode){ dnum = est_mtdb_doc_num(node->db); wnum = est_mtdb_word_num(node->db); } for(i = 0; i < cbmaprnum(node->links); i++){ if(rtargs[i].alive){ if(pthread_join(rths[i], NULL) == 0){ dnum += rtargs[i].dnum; wnum += rtargs[i].wnum; if(rtargs[i].mtime > mtime) mtime = rtargs[i].mtime; } else { log_print(LL_ERROR, "joining thread failed"); } } else if(!top && g_stmode && depth > 0){ dnum += rtargs[i].dnum; wnum += rtargs[i].wnum; if(rtargs[i].mtime > mtime) mtime = rtargs[i].mtime; } } hnum = (tmp = cbmapget(hints, "", 0, NULL)) ? atoi(tmp) : 0; hnum -= ltarg.mhnum; if(hnum < 0) hnum = 0; end = page * max + max; curtime = est_gettimeofday() - curtime; tab = 1; axk = 'a'; datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, UICACHELIFE); cbdatumprintf(datum, "Content-Type: %s\r\n", UIMIMETYPE); if(req->gzip){ cbdatumprintf(datum, "Content-Encoding: gzip\r\n"); } else if(req->deflate){ cbdatumprintf(datum, "Content-Encoding: deflate\r\n"); } cbdatumprintf(datum, "Content-Disposition: inline; filename=%s.html\r\n", node->name); dstr = cbdatestrhttp(req->reload ? req->now : mtime, 0); cbdatumprintf(datum, "Last-Modified: %s\r\n", dstr); free(dstr); cbdatumprintf(datum, "\r\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumsetsize(datum, 0); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n", UIMIMETYPE); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); dstr = cbdatestrhttp(mtime, 0); cbdatumprintf(datum, "\n", dstr); free(dstr); cbdatumprintf(datum, "\n", SERVNAME, est_version); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n", UIMIMETYPE, req->prefix, NODEPREFIX, node->name, SEARCHUICMD); if(page > 0) cbdatumprintf(datum, "\n", UIMIMETYPE, req->prefix, NODEPREFIX, node->name, SEARCHUICMD, cbdatumptr(condbuf), page - 1); if(hnum > end) cbdatumprintf(datum, "\n", UIMIMETYPE, req->prefix, NODEPREFIX, node->name, SEARCHUICMD, cbdatumptr(condbuf), page + 1); cbdatumprintf(datum, "\n", OSRCHMIMETYPE, req->prefix, NODEPREFIX, node->name, OPENSEARCHCMD); if(!top){ cbdatumprintf(datum, "\n", ATOMMIMETYPE, req->prefix, NODEPREFIX, node->name, SEARCHATOMCMD, cbdatumptr(condbuf)); cbdatumprintf(datum, "\n", RSSMIMETYPE, req->prefix, NODEPREFIX, node->name, SEARCHRSSCMD, cbdatumptr(condbuf)); } cbdatumprintf(datum, "\n", req->prefix, FAVICONLOC); cbdatumprintf(datum, "\n", g_adminemail); if(phrase && phrase[0] != '\0'){ cbdatumprintf(datum, "Search %@: %@\n", node->label, simid > 0 ? ESTOPSIMILAR : phrase); } else { cbdatumprintf(datum, "Search Interface of %@\n", node->label); } cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "

Search Interface of %@

\n", node->label); cbdatumprintf(datum, "
\n", req->prefix, NODEPREFIX, node->name, SEARCHUICMD); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, ""); cbdatumprintf(datum, ""); cbdatumprintf(datum, ""); cbdatumprintf(datum, "\n"); for(i = 0; i < 3; i++){ cbdatumprintf(datum, ""); tmp = attrs ? cblistval(attrs, i, NULL) : NULL; cbdatumprintf(datum, "", i + 1); cbdatumprintf(datum, ""); cbdatumprintf(datum, "\n"); } cbdatumprintf(datum, ""); cbdatumprintf(datum, ""); cbdatumprintf(datum, ""); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, ""); cbdatumprintf(datum, ""); cbdatumprintf(datum, ""); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, ""); cbdatumprintf(datum, ""); cbdatumprintf(datum, ""); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, ""); cbdatumprintf(datum, ""); cbdatumprintf(datum, ""); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "
phrase:"); cbdatumprintf(datum, "", phrase ? phrase : "", tab++, axk++); cbdatumprintf(datum, "
attribute:"); cbdatumprintf(datum, "", i + 1, tmp ? tmp : "", i + 1, tab++, axk++); cbdatumprintf(datum, "
order:"); cbdatumprintf(datum, "", order ? order : "", tab++, axk++); cbdatumprintf(datum, "
max:"); cbdatumprintf(datum, ""); cbdatumprintf(datum, "
depth:"); cbdatumprintf(datum, ""); cbdatumprintf(datum, "
"); cbdatumprintf(datum, "", tab++, axk++); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, ""); cbdatumprintf(datum, ""); cbdatumprintf(datum, "", hnum); cbdatumprintf(datum, "\n"); cbmapiterinit(hints); num = 1; while((kbuf = cbmapiternext(hints, &ksiz)) != NULL){ if(ksiz < 1) continue; cbdatumprintf(datum, ""); cbdatumprintf(datum, "", num, num); cbdatumprintf(datum, "", num, kbuf, cbmapiterval(kbuf, NULL)); cbdatumprintf(datum, "\n"); num++; } cbdatumprintf(datum, ""); cbdatumprintf(datum, ""); cbdatumprintf(datum, "", dnum > 0 ? dnum : est_mtdb_doc_num(node->db)); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, ""); cbdatumprintf(datum, ""); cbdatumprintf(datum, "", wnum > 0 ? wnum : est_mtdb_word_num(node->db)); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, ""); cbdatumprintf(datum, ""); cbdatumprintf(datum, "", curtime / 1000.0); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, ""); cbdatumprintf(datum, ""); cbdatumprintf(datum, ""); cbdatumprintf(datum, "\n"); for(i = 0; i < cbmaprnum(node->links); i++){ cbdatumprintf(datum, ""); cbdatumprintf(datum, "", i + 1, i + 1); cbdatumprintf(datum, ""); cbdatumprintf(datum, "\n"); } cbdatumprintf(datum, ""); cbdatumprintf(datum, ""); if(top){ cbdatumprintf(datum, ""); } else { cbdatumprintf(datum, ""); } cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "
hit:%d
hint#%d:%@ (%@)
docnum:%d
wordnum:%d
time:%.6f
link#0:"); cbdatumprintf(datum, "", masks[0] ? "" : " checked=\"checked\""); cbdatumprintf(datum, " %@", node->label); cbdatumprintf(datum, " (%d)", ltarg.etime / 1000.0, ltarg.itime / 1000.0, ltarg.hnum - ltarg.mhnum); cbdatumprintf(datum, "
link#%d:", i + 1); cbdatumprintf(datum, "", i + 1, i < LINKMASKMAX && masks[i+1] ? "" : " checked=\"checked\"", i + 1); cbdatumprintf(datum, " %@", rtargs[i].url, SEARCHUICMD, cbdatumptr(condbuf), UIMIMETYPE, rtargs[i].label); if(rtargs[i].hnum >= 0){ cbdatumprintf(datum, " (%d)", rtargs[i].etime / 1000.0, rtargs[i].hnum); } cbdatumprintf(datum, "
feed:"); cbdatumprintf(datum, "OpenSearch\n", req->prefix, NODEPREFIX, node->name, OPENSEARCHCMD, OSRCHMIMETYPE); cbdatumprintf(datum, ""); cbdatumprintf(datum, "Atom", req->prefix, NODEPREFIX, node->name, SEARCHATOMCMD, cbdatumptr(condbuf), ATOMMIMETYPE); cbdatumprintf(datum, " RSS", req->prefix, NODEPREFIX, node->name, SEARCHRSSCMD, cbdatumptr(condbuf), RSSMIMETYPE); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); if(!top){ cbdatumprintf(datum, "
\n"); if(!order && phrase && cbstrfwmatch(phrase, ESTOPSIMILAR)) order = DATTRNDSCORE " " ESTORDNUMD; resdocs = resmap_list(resmap, &rnum, order, NULL); for(i = page * max; i < rnum && i < end; i++){ catdocdataui(datum, i + 1, resdocs[i], ltarg.words, req, node, cbdatumptr(condbuf), cbdatumptr(simcondbuf)); } free(resdocs); if(rnum < 1) cbdatumprintf(datum, "

The conditions did not match any documents.

\n"); cbdatumprintf(datum, "
"); if(page > 0){ cbdatumprintf(datum, "PREV", req->prefix, NODEPREFIX, node->name, SEARCHUICMD, cbdatumptr(condbuf), page - 1, UIMIMETYPE); } else { cbdatumprintf(datum, "PREV"); } if(hnum > end){ cbdatumprintf(datum, " NEXT", req->prefix, NODEPREFIX, node->name, SEARCHUICMD, cbdatumptr(condbuf), page + 1, UIMIMETYPE); } else { cbdatumprintf(datum, " NEXT"); } cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); } cbdatumprintf(datum, "
Powered by Hyper Estraier" " %@.
\n", _EST_PROJURL, est_version); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); if(req->gzip){ if((zbuf = est_deflate(cbdatumptr(datum), cbdatumsize(datum), &zsiz, 1)) != NULL){ est_sock_send_all(clsock, zbuf, zsiz); free(zbuf); } } else if(req->deflate){ if((zbuf = est_deflate(cbdatumptr(datum), cbdatumsize(datum), &zsiz, 0)) != NULL){ est_sock_send_all(clsock, zbuf, zsiz); free(zbuf); } } else { est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); } cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (%s)", req->claddr, req->clport, SEARCHUICMD); cblistclose(ltarg.words); est_cond_delete(ltarg.cond); for(i = 0; i < cbmaprnum(node->links); i++){ free(rtargs[i].label); est_cond_delete(rtargs[i].cond); } free(rtargs); free(rths); cbmapclose(hints); resmap_delete(resmap); cbdatumclose(simcondbuf); cbdatumclose(condbuf); est_cond_delete(cond); free(myurl); } /* send the result of Atom feed interface */ static void sendnodecmdsearchatom(int clsock, REQUEST *req, NODE *node){ pthread_t lth, *rths; TARGLCSRCH ltarg; TARGRMSRCH *rtargs; RESMAP *resmap; RESDOC **resdocs; ESTCOND *cond; CBMAP *hints; const CBLIST *attrs; CBDATUM *condbuf, *datum; const char *tmp, *url, *label, *kbuf, *phrase, *order, *simnode; char *myurl, *dstr, name[NUMBUFSIZ], masks[LINKMASKMAX+1], *zbuf; int i, num, max, depth, simid, page, top, dnum, wnum, hnum, end, ksiz, rnum, zsiz; double curtime; time_t mtime; myurl = cbsprintf("%s%s%s", req->prefix, NODEPREFIX, node->name); if(islooproute(myurl, req)){ log_print(LL_DEBUG, "[%s:%d]: omitting request loop (self): %s", req->claddr, req->clport, myurl); free(myurl); senderror(clsock, req, 400, "Bad Request (the request loops)"); return; } if(pthread_mutex_lock(&(node->mutex)) != 0){ log_print(LL_ERROR, "locking failed"); senderror(clsock, req, 500, "Internal Server Error (locking failed)"); return; } cond = est_cond_new(); max = DEFMAXSRCH; depth = 0; page = 0; simid = 0; simnode = NULL; if((tmp = cbmapget(req->params, "page", -1, NULL)) != NULL && (num = atoi(tmp)) > 0) page = num; if((tmp = cbmapget(req->params, "pageone", -1, NULL)) != NULL && (num = atoi(tmp)) > 1) page = num - 1; if((tmp = cbmapget(req->params, "phrase", -1, NULL)) != NULL && tmp[0] != '\0') est_cond_set_phrase(cond, tmp); if((tmp = cbmapget(req->params, "attr", -1, NULL)) != NULL && tmp[0] != '\0') est_cond_add_attr(cond, tmp); for(i = 0; i <= CONDATTRMAX; i++){ num = sprintf(name, "attr%d", i); if((tmp = cbmapget(req->params, name, num, NULL)) != NULL && tmp[0] != '\0') est_cond_add_attr(cond, tmp); } if((tmp = cbmapget(req->params, "order", -1, NULL)) != NULL && tmp[0] != '\0') est_cond_set_order(cond, tmp); if((tmp = cbmapget(req->params, "max", -1, NULL)) != NULL && (num = atoi(tmp)) >= 0) max = num; max = max > g_searchmax ? g_searchmax : max; if((tmp = cbmapget(req->params, "depth", -1, NULL)) != NULL && (num = atoi(tmp)) > 0) depth = num; if((tmp = cbmapget(req->params, "simid", -1, NULL)) != NULL && (num = atoi(tmp)) > 0) simid = num; if((tmp = cbmapget(req->params, "simnode", -1, NULL)) != NULL && tmp[0] != '\0') simnode = tmp; if((tmp = cbmapget(req->params, "mask", -1, NULL)) != NULL && tmp[0] != '\0'){ num = atoi(tmp); for(i = 0; i <= LINKMASKMAX; i++){ masks[i] = num & (1 << i); } } else if((tmp = cbmapget(req->params, "allmask", -1, NULL)) != NULL && tmp[0] != '\0'){ memset(masks, 1, LINKMASKMAX+1); for(i = 0; i <= LINKMASKMAX; i++){ num = sprintf(name, "nomask%d", i); if((tmp = cbmapget(req->params, name, num, NULL)) != NULL && tmp[0] != '\0') masks[i] = 0; } } else { memset(masks, 0, LINKMASKMAX+1); if(!(tmp = cbmapget(req->params, "nomask", -1, NULL)) || tmp[0] == '\0'){ for(i = 0; i <= LINKMASKMAX; i++){ num = sprintf(name, "mask%d", i); if((tmp = cbmapget(req->params, name, num, NULL)) != NULL && tmp[0] != '\0') masks[i] = 1; } } } if(depth >= g_searchdepth) depth = g_searchdepth; switch(g_uiphraseform){ case PM_SIMPLE: est_cond_set_options(cond, ESTCONDSIMPLE); break; case PM_ROUGH: est_cond_set_options(cond, ESTCONDROUGH); break; case PM_UNION: est_cond_set_options(cond, ESTCONDUNION); break; case PM_ISECT: est_cond_set_options(cond, ESTCONDISECT); break; } top = !est_cond_phrase(cond) && !est_cond_attrs(cond) && simid < 1; phrase = est_cond_phrase(cond); attrs = est_cond_attrs(cond); order = est_cond_order(cond); condbuf = cbdatumopen(NULL, -1); cbdatumprintf(condbuf, "phrase=%?", phrase ? phrase : ""); if(attrs){ for(i = 0; i < cblistnum(attrs); i++){ tmp = cblistval(attrs, i, NULL); if(tmp[0] != '\0') cbdatumprintf(condbuf, "&attr%d=%?", i + 1, tmp); } } if(order && order[0] != '\0') cbdatumprintf(condbuf, "&order=%?", order); if(max != DEFMAXSRCH) cbdatumprintf(condbuf, "&max=%d", max); if(depth > 0) cbdatumprintf(condbuf, "&depth=%d", depth); if(simid > 0 && simnode && simnode[0] != '\0'){ cbdatumprintf(condbuf, "&simid=%d", simid); cbdatumprintf(condbuf, "&simnode=%?", simnode); setsimilarphrase(cond, simnode, simid); phrase = est_cond_phrase(cond); } for(i = 0; i <= LINKMASKMAX; i++){ if(masks[i]) cbdatumprintf(condbuf, "&mask%d=1", i); } if(page > 0) cbdatumprintf(condbuf, "&page=%d", page); resmap = resmap_new(); hints = cbmapopenex(MINIBNUM); curtime = est_gettimeofday(); ltarg.req = req; ltarg.alive = TRUE; ltarg.cond = est_cond_dup(cond); ltarg.hints = hints; ltarg.max = max + page * max + 1; ltarg.node = node; ltarg.resmap = resmap; ltarg.words = cblistopen(); ltarg.hnum = 0; ltarg.mhnum = 0; ltarg.itime = 0.0; ltarg.etime = 0.0; if(top || masks[0]){ ltarg.alive = FALSE; } else if(g_stmode){ searchlocal(<arg); ltarg.alive = FALSE; } else if(pthread_create(<h, NULL, searchlocal, <arg) != 0){ log_print(LL_WARN, "creating thread failed"); ltarg.alive = FALSE; } rths = cbmalloc(cbmaprnum(node->links) * sizeof(pthread_t) + 1); rtargs = cbmalloc(cbmaprnum(node->links) * sizeof(TARGRMSRCH) + 1); cbmapiterinit(node->links); for(i = 0; (url = cbmapiternext(node->links, NULL)) != NULL; i++){ rtargs[i].req = req; rtargs[i].alive = TRUE; rtargs[i].myurl = myurl; rtargs[i].cond = est_cond_dup(cond); rtargs[i].hints = hints; rtargs[i].max = max + page * max + 1; rtargs[i].node = node; rtargs[i].resmap = resmap; rtargs[i].url = url; label = cbmapiterval(url, NULL); if((tmp = strchr(label, '\t')) != NULL){ rtargs[i].label = cbmemdup(label, tmp - label); rtargs[i].credit = atoi(tmp + 1); } else { rtargs[i].label = cbmemdup(label, -1); rtargs[i].credit = 0; } rtargs[i].depth = depth; rtargs[i].wwidth = g_snipwwidth; rtargs[i].hwidth = g_sniphwidth; rtargs[i].awidth = g_snipawidth; rtargs[i].hnum = 0; rtargs[i].dnum = 0; rtargs[i].wnum = 0; rtargs[i].etime = 0.0; rtargs[i].size = 0.0; rtargs[i].mtime = 0; if(top || depth < 1 || (i < LINKMASKMAX && masks[i+1])){ rtargs[i].alive = FALSE; rtargs[i].hnum = -1; rtargs[i].dnum = -1; rtargs[i].wnum = -1; rtargs[i].etime = -1.0; rtargs[i].size = -1.0; } else if(g_stmode){ searchremote(&rtargs[i]); rtargs[i].alive = FALSE; } else if(pthread_create(rths + i, NULL, searchremote, rtargs + i) != 0){ log_print(LL_WARN, "creating thread failed"); rtargs[i].alive = FALSE; } } if(pthread_mutex_unlock(&(node->mutex)) != 0) log_print(LL_ERROR, "unlocking failed"); dnum = 0; wnum = 0; mtime = node->mtime; if(ltarg.alive){ if(pthread_join(lth, NULL) == 0){ dnum = est_mtdb_doc_num(node->db); wnum = est_mtdb_word_num(node->db); } else { log_print(LL_ERROR, "joining thread failed"); } } else if(!top && g_stmode){ dnum = est_mtdb_doc_num(node->db); wnum = est_mtdb_word_num(node->db); } for(i = 0; i < cbmaprnum(node->links); i++){ if(rtargs[i].alive){ if(pthread_join(rths[i], NULL) == 0){ dnum += rtargs[i].dnum; wnum += rtargs[i].wnum; if(rtargs[i].mtime > mtime) mtime = rtargs[i].mtime; } else { log_print(LL_ERROR, "joining thread failed"); } } else if(!top && g_stmode && depth > 0){ dnum += rtargs[i].dnum; wnum += rtargs[i].wnum; if(rtargs[i].mtime > mtime) mtime = rtargs[i].mtime; } } hnum = (tmp = cbmapget(hints, "", 0, NULL)) ? atoi(tmp) : 0; hnum -= ltarg.mhnum; if(hnum < 0) hnum = 0; end = page * max + max; curtime = est_gettimeofday() - curtime; datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, UICACHELIFE); cbdatumprintf(datum, "Content-Type: %s\r\n", ATOMMIMETYPE); if(req->gzip){ cbdatumprintf(datum, "Content-Encoding: gzip\r\n"); } else if(req->deflate){ cbdatumprintf(datum, "Content-Encoding: deflate\r\n"); } cbdatumprintf(datum, "Content-Disposition: inline; filename=%s.atom\r\n", node->name); dstr = cbdatestrhttp(req->reload ? req->now : mtime, 0); cbdatumprintf(datum, "Last-Modified: %s\r\n", dstr); free(dstr); cbdatumprintf(datum, "\r\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumsetsize(datum, 0); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n", _EST_XNSEARCH); cbdatumprintf(datum, "%@%@%@/%@?%s\n", req->prefix, NODEPREFIX, node->name, SEARCHATOMCMD, cbdatumptr(condbuf)); if(phrase && phrase[0] != '\0'){ cbdatumprintf(datum, "Search %@: %@\n", node->label, simid > 0 ? ESTOPSIMILAR : phrase); } else { cbdatumprintf(datum, "Search Interface of %@\n", node->label); } cbdatumprintf(datum, "Search Result of Hyper Estraier %@\n", est_version); dstr = cbdatestrwww(mtime, 0); cbdatumprintf(datum, "%@\n", dstr); free(dstr); cbdatumprintf(datum, "\n", req->prefix, NODEPREFIX, node->name, SEARCHATOMCMD, cbdatumptr(condbuf), ATOMMIMETYPE); if(page > 0) cbdatumprintf(datum, "\n", req->prefix, NODEPREFIX, node->name, SEARCHATOMCMD, cbdatumptr(condbuf), page - 1, ATOMMIMETYPE); if(hnum > end) cbdatumprintf(datum, "\n", req->prefix, NODEPREFIX, node->name, SEARCHATOMCMD, cbdatumptr(condbuf), page + 1, ATOMMIMETYPE); cbdatumprintf(datum, "\n", req->prefix, NODEPREFIX, node->name, SEARCHUICMD, cbdatumptr(condbuf), UIMIMETYPE); cbdatumprintf(datum, "\n", req->prefix, NODEPREFIX, node->name, SEARCHRSSCMD, cbdatumptr(condbuf), RSSMIMETYPE); cbdatumprintf(datum, "" "Hyper Estraier (%@)\n", _EST_PROJURL, est_version, SERVNAME); cbdatumprintf(datum, "%@%@%@\n", req->prefix, IMAGEPREFIX, BIGICONNAME); cbdatumprintf(datum, "\n", hnum); cbmapiterinit(hints); while((kbuf = cbmapiternext(hints, &ksiz)) != NULL){ if(ksiz < 1) continue; cbdatumprintf(datum, "\n", kbuf, cbmapiterval(kbuf, NULL)); } cbdatumprintf(datum, "\n", curtime / 1000.0); cbdatumprintf(datum, "\n", dnum > 0 ? dnum : est_mtdb_doc_num(node->db), wnum > 0 ? wnum : est_mtdb_word_num(node->db)); cbdatumprintf(datum, "\n", req->prefix, NODEPREFIX, node->name, node->label, SELFCREDIT, est_mtdb_doc_num(node->db), est_mtdb_word_num(node->db), est_mtdb_size(node->db), ltarg.hnum - ltarg.mhnum, ltarg.etime >= 0.0 ? ltarg.etime / 1000.0 : 0.0); for(i = 0; i < cbmaprnum(node->links); i++){ cbdatumprintf(datum, "\n", rtargs[i].url, rtargs[i].label, rtargs[i].credit, rtargs[i].dnum, rtargs[i].wnum, rtargs[i].size, rtargs[i].hnum, rtargs[i].etime >= 0.0 ? rtargs[i].etime / 1000.0 : 0.0); } cbdatumprintf(datum, "\n", phrase ? phrase : ""); cbdatumprintf(datum, "%d\n", hnum); cbdatumprintf(datum, "%d\n", page * max + 1); cbdatumprintf(datum, "%d\n", max); cbdatumprintf(datum, "\n", req->prefix, NODEPREFIX, node->name, OPENSEARCHCMD, OSRCHMIMETYPE); if(!top){ if(!order && phrase && cbstrfwmatch(phrase, ESTOPSIMILAR)) order = DATTRNDSCORE " " ESTORDNUMD; resdocs = resmap_list(resmap, &rnum, order, NULL); for(i = page * max; i < rnum && i < end; i++){ catdocdataatom(datum, resdocs[i], ltarg.words, node); } free(resdocs); } cbdatumprintf(datum, "\n"); if(req->gzip){ if((zbuf = est_deflate(cbdatumptr(datum), cbdatumsize(datum), &zsiz, 1)) != NULL){ est_sock_send_all(clsock, zbuf, zsiz); free(zbuf); } } else if(req->deflate){ if((zbuf = est_deflate(cbdatumptr(datum), cbdatumsize(datum), &zsiz, 0)) != NULL){ est_sock_send_all(clsock, zbuf, zsiz); free(zbuf); } } else { est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); } cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (%s)", req->claddr, req->clport, SEARCHATOMCMD); cblistclose(ltarg.words); est_cond_delete(ltarg.cond); for(i = 0; i < cbmaprnum(node->links); i++){ free(rtargs[i].label); est_cond_delete(rtargs[i].cond); } free(rtargs); free(rths); cbmapclose(hints); resmap_delete(resmap); cbdatumclose(condbuf); est_cond_delete(cond); free(myurl); } /* send the result of RSS feed interface */ static void sendnodecmdsearchrss(int clsock, REQUEST *req, NODE *node){ pthread_t lth, *rths; TARGLCSRCH ltarg; TARGRMSRCH *rtargs; RESMAP *resmap; RESDOC **resdocs, *resdoc; ESTCOND *cond; CBMAP *hints; const CBLIST *attrs; CBDATUM *condbuf, *datum; const char *tmp, *url, *label, *kbuf, *phrase, *order, *simnode; char *myurl, name[NUMBUFSIZ], masks[LINKMASKMAX+1], *dstr, *turi, *zbuf; int i, num, max, depth, simid, page, top, dnum, wnum, hnum, end, ksiz, rnum, zsiz; double curtime; time_t mtime; myurl = cbsprintf("%s%s%s", req->prefix, NODEPREFIX, node->name); if(islooproute(myurl, req)){ log_print(LL_DEBUG, "[%s:%d]: omitting request loop (self): %s", req->claddr, req->clport, myurl); free(myurl); senderror(clsock, req, 400, "Bad Request (the request loops)"); return; } if(pthread_mutex_lock(&(node->mutex)) != 0){ log_print(LL_ERROR, "locking failed"); senderror(clsock, req, 500, "Internal Server Error (locking failed)"); return; } cond = est_cond_new(); max = DEFMAXSRCH; depth = 0; page = 0; simid = 0; simnode = NULL; if((tmp = cbmapget(req->params, "page", -1, NULL)) != NULL && (num = atoi(tmp)) > 0) page = num; if((tmp = cbmapget(req->params, "pageone", -1, NULL)) != NULL && (num = atoi(tmp)) > 1) page = num - 1; if((tmp = cbmapget(req->params, "phrase", -1, NULL)) != NULL && tmp[0] != '\0') est_cond_set_phrase(cond, tmp); if((tmp = cbmapget(req->params, "attr", -1, NULL)) != NULL && tmp[0] != '\0') est_cond_add_attr(cond, tmp); for(i = 0; i <= CONDATTRMAX; i++){ num = sprintf(name, "attr%d", i); if((tmp = cbmapget(req->params, name, num, NULL)) != NULL && tmp[0] != '\0') est_cond_add_attr(cond, tmp); } if((tmp = cbmapget(req->params, "order", -1, NULL)) != NULL && tmp[0] != '\0') est_cond_set_order(cond, tmp); if((tmp = cbmapget(req->params, "max", -1, NULL)) != NULL && (num = atoi(tmp)) >= 0) max = num; max = max > g_searchmax ? g_searchmax : max; if((tmp = cbmapget(req->params, "depth", -1, NULL)) != NULL && (num = atoi(tmp)) > 0) depth = num; if((tmp = cbmapget(req->params, "simid", -1, NULL)) != NULL && (num = atoi(tmp)) > 0) simid = num; if((tmp = cbmapget(req->params, "simnode", -1, NULL)) != NULL && tmp[0] != '\0') simnode = tmp; if((tmp = cbmapget(req->params, "mask", -1, NULL)) != NULL && tmp[0] != '\0'){ num = atoi(tmp); for(i = 0; i <= LINKMASKMAX; i++){ masks[i] = num & (1 << i); } } else if((tmp = cbmapget(req->params, "allmask", -1, NULL)) != NULL && tmp[0] != '\0'){ memset(masks, 1, LINKMASKMAX+1); for(i = 0; i <= LINKMASKMAX; i++){ num = sprintf(name, "nomask%d", i); if((tmp = cbmapget(req->params, name, num, NULL)) != NULL && tmp[0] != '\0') masks[i] = 0; } } else { memset(masks, 0, LINKMASKMAX+1); if(!(tmp = cbmapget(req->params, "nomask", -1, NULL)) || tmp[0] == '\0'){ for(i = 0; i <= LINKMASKMAX; i++){ num = sprintf(name, "mask%d", i); if((tmp = cbmapget(req->params, name, num, NULL)) != NULL && tmp[0] != '\0') masks[i] = 1; } } } if(depth >= g_searchdepth) depth = g_searchdepth; switch(g_uiphraseform){ case PM_SIMPLE: est_cond_set_options(cond, ESTCONDSIMPLE); break; case PM_ROUGH: est_cond_set_options(cond, ESTCONDROUGH); break; case PM_UNION: est_cond_set_options(cond, ESTCONDUNION); break; case PM_ISECT: est_cond_set_options(cond, ESTCONDISECT); break; } top = !est_cond_phrase(cond) && !est_cond_attrs(cond) && simid < 1; phrase = est_cond_phrase(cond); attrs = est_cond_attrs(cond); order = est_cond_order(cond); condbuf = cbdatumopen(NULL, -1); cbdatumprintf(condbuf, "phrase=%?", phrase ? phrase : ""); if(attrs){ for(i = 0; i < cblistnum(attrs); i++){ tmp = cblistval(attrs, i, NULL); if(tmp[0] != '\0') cbdatumprintf(condbuf, "&attr%d=%?", i + 1, tmp); } } if(order && order[0] != '\0') cbdatumprintf(condbuf, "&order=%?", order); if(max != DEFMAXSRCH) cbdatumprintf(condbuf, "&max=%d", max); if(depth > 0) cbdatumprintf(condbuf, "&depth=%d", depth); if(simid > 0 && simnode && simnode[0] != '\0'){ cbdatumprintf(condbuf, "&simid=%d", simid); cbdatumprintf(condbuf, "&simnode=%?", simnode); setsimilarphrase(cond, simnode, simid); phrase = est_cond_phrase(cond); } for(i = 0; i <= LINKMASKMAX; i++){ if(masks[i]) cbdatumprintf(condbuf, "&mask%d=1", i); } if(page > 0) cbdatumprintf(condbuf, "&page=%d", page); resmap = resmap_new(); hints = cbmapopenex(MINIBNUM); curtime = est_gettimeofday(); ltarg.req = req; ltarg.alive = TRUE; ltarg.cond = est_cond_dup(cond); ltarg.hints = hints; ltarg.max = max + page * max + 1; ltarg.node = node; ltarg.resmap = resmap; ltarg.words = cblistopen(); ltarg.hnum = 0; ltarg.mhnum = 0; ltarg.itime = 0.0; ltarg.etime = 0.0; if(top || masks[0]){ ltarg.alive = FALSE; } else if(g_stmode){ searchlocal(<arg); ltarg.alive = FALSE; } else if(pthread_create(<h, NULL, searchlocal, <arg) != 0){ log_print(LL_WARN, "creating thread failed"); ltarg.alive = FALSE; } rths = cbmalloc(cbmaprnum(node->links) * sizeof(pthread_t) + 1); rtargs = cbmalloc(cbmaprnum(node->links) * sizeof(TARGRMSRCH) + 1); cbmapiterinit(node->links); for(i = 0; (url = cbmapiternext(node->links, NULL)) != NULL; i++){ rtargs[i].req = req; rtargs[i].alive = TRUE; rtargs[i].myurl = myurl; rtargs[i].cond = est_cond_dup(cond); rtargs[i].hints = hints; rtargs[i].max = max + page * max + 1; rtargs[i].node = node; rtargs[i].resmap = resmap; rtargs[i].url = url; label = cbmapiterval(url, NULL); if((tmp = strchr(label, '\t')) != NULL){ rtargs[i].label = cbmemdup(label, tmp - label); rtargs[i].credit = atoi(tmp + 1); } else { rtargs[i].label = cbmemdup(label, -1); rtargs[i].credit = 0; } rtargs[i].depth = depth; rtargs[i].wwidth = g_snipwwidth; rtargs[i].hwidth = g_sniphwidth; rtargs[i].awidth = g_snipawidth; rtargs[i].hnum = 0; rtargs[i].dnum = 0; rtargs[i].wnum = 0; rtargs[i].etime = 0.0; rtargs[i].size = 0.0; rtargs[i].mtime = 0; if(top || depth < 1 || (i < LINKMASKMAX && masks[i+1])){ rtargs[i].alive = FALSE; rtargs[i].hnum = -1; rtargs[i].dnum = -1; rtargs[i].wnum = -1; rtargs[i].etime = -1.0; rtargs[i].size = -1.0; } else if(g_stmode){ searchremote(&rtargs[i]); rtargs[i].alive = FALSE; } else if(pthread_create(rths + i, NULL, searchremote, rtargs + i) != 0){ log_print(LL_WARN, "creating thread failed"); rtargs[i].alive = FALSE; } } if(pthread_mutex_unlock(&(node->mutex)) != 0) log_print(LL_ERROR, "unlocking failed"); dnum = 0; wnum = 0; mtime = node->mtime; if(ltarg.alive){ if(pthread_join(lth, NULL) == 0){ dnum = est_mtdb_doc_num(node->db); wnum = est_mtdb_word_num(node->db); } else { log_print(LL_ERROR, "joining thread failed"); } } else if(!top && g_stmode){ dnum = est_mtdb_doc_num(node->db); wnum = est_mtdb_word_num(node->db); } for(i = 0; i < cbmaprnum(node->links); i++){ if(rtargs[i].alive){ if(pthread_join(rths[i], NULL) == 0){ dnum += rtargs[i].dnum; wnum += rtargs[i].wnum; if(rtargs[i].mtime > mtime) mtime = rtargs[i].mtime; } else { log_print(LL_ERROR, "joining thread failed"); } } else if(!top && g_stmode && depth > 0){ dnum += rtargs[i].dnum; wnum += rtargs[i].wnum; if(rtargs[i].mtime > mtime) mtime = rtargs[i].mtime; } } hnum = (tmp = cbmapget(hints, "", 0, NULL)) ? atoi(tmp) : 0; hnum -= ltarg.mhnum; if(hnum < 0) hnum = 0; end = page * max + max; curtime = est_gettimeofday() - curtime; datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, UICACHELIFE); cbdatumprintf(datum, "Content-Type: %s\r\n", RSSMIMETYPE); if(req->gzip){ cbdatumprintf(datum, "Content-Encoding: gzip\r\n"); } else if(req->deflate){ cbdatumprintf(datum, "Content-Encoding: deflate\r\n"); } cbdatumprintf(datum, "Content-Disposition: inline; filename=%s.rdf\r\n", node->name); dstr = cbdatestrhttp(req->reload ? req->now : mtime, 0); cbdatumprintf(datum, "Last-Modified: %s\r\n", dstr); free(dstr); cbdatumprintf(datum, "\r\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumsetsize(datum, 0); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n", _EST_XNSEARCH); cbdatumprintf(datum, "\n", req->prefix, NODEPREFIX, node->name, SEARCHRSSCMD, cbdatumptr(condbuf)); if(phrase && phrase[0] != '\0'){ cbdatumprintf(datum, "Search %@: %@\n", node->label, simid > 0 ? ESTOPSIMILAR : phrase); } else { cbdatumprintf(datum, "Search Interface of %@\n", node->label); } cbdatumprintf(datum, "Search Result of Hyper Estraier %@\n", est_version); cbdatumprintf(datum, "%@%@%@/%@?%s\n", req->prefix, NODEPREFIX, node->name, SEARCHUICMD, cbdatumptr(condbuf)); if(!top){ if(!order && phrase && cbstrfwmatch(phrase, ESTOPSIMILAR)) order = DATTRNDSCORE " " ESTORDNUMD; resdocs = resmap_list(resmap, &rnum, order, NULL); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); for(i = page * max; i < rnum && i < end; i++){ resdoc = resdocs[i]; if(!(tmp = resdoc->doc ? est_doc_attr(resdoc->doc, ESTDATTRURI) : cbmapget(resdoc->attrs, ESTDATTRURI, -1, NULL))) tmp = "."; turi = makeshownuri(tmp); cbdatumprintf(datum, "\n", turi); free(turi); } cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); free(resdocs); } dstr = cbdatestrwww(mtime, 0); cbdatumprintf(datum, "%@\n", dstr); free(dstr); cbdatumprintf(datum, "\n", req->prefix, NODEPREFIX, node->name, SEARCHRSSCMD, cbdatumptr(condbuf), RSSMIMETYPE); if(page > 0) cbdatumprintf(datum, "\n", req->prefix, NODEPREFIX, node->name, SEARCHATOMCMD, cbdatumptr(condbuf), page - 1, ATOMMIMETYPE); if(hnum > end) cbdatumprintf(datum, "\n", req->prefix, NODEPREFIX, node->name, SEARCHATOMCMD, cbdatumptr(condbuf), page + 1, ATOMMIMETYPE); cbdatumprintf(datum, "\n", req->prefix, NODEPREFIX, node->name, SEARCHUICMD, cbdatumptr(condbuf), UIMIMETYPE); cbdatumprintf(datum, "\n", req->prefix, NODEPREFIX, node->name, SEARCHATOMCMD, cbdatumptr(condbuf), ATOMMIMETYPE); cbdatumprintf(datum, "\n", hnum); cbmapiterinit(hints); while((kbuf = cbmapiternext(hints, &ksiz)) != NULL){ if(ksiz < 1) continue; cbdatumprintf(datum, "\n", kbuf, cbmapiterval(kbuf, NULL)); } cbdatumprintf(datum, "\n", curtime / 1000.0); cbdatumprintf(datum, "\n", dnum > 0 ? dnum : est_mtdb_doc_num(node->db), wnum > 0 ? wnum : est_mtdb_word_num(node->db)); cbdatumprintf(datum, "\n", req->prefix, NODEPREFIX, node->name, node->label, SELFCREDIT, est_mtdb_doc_num(node->db), est_mtdb_word_num(node->db), est_mtdb_size(node->db), ltarg.hnum - ltarg.mhnum, ltarg.etime >= 0.0 ? ltarg.etime / 1000.0 : 0.0); for(i = 0; i < cbmaprnum(node->links); i++){ cbdatumprintf(datum, "\n", rtargs[i].url, rtargs[i].label, rtargs[i].credit, rtargs[i].dnum, rtargs[i].wnum, rtargs[i].size, rtargs[i].hnum, rtargs[i].etime >= 0.0 ? rtargs[i].etime / 1000.0 : 0.0); } cbdatumprintf(datum, "\n", phrase ? phrase : ""); cbdatumprintf(datum, "%d\n", hnum); cbdatumprintf(datum, "%d\n", page * max + 1); cbdatumprintf(datum, "%d\n", max); cbdatumprintf(datum, "\n", req->prefix, NODEPREFIX, node->name, OPENSEARCHCMD, OSRCHMIMETYPE); cbdatumprintf(datum, "\n"); if(!top){ if(!order && phrase && cbstrfwmatch(phrase, ESTOPSIMILAR)) order = DATTRNDSCORE " " ESTORDNUMD; resdocs = resmap_list(resmap, &rnum, order, NULL); for(i = page * max; i < rnum && i < end; i++){ catdocdatarss(datum, resdocs[i], ltarg.words, node); } free(resdocs); } cbdatumprintf(datum, "\n"); if(req->gzip){ if((zbuf = est_deflate(cbdatumptr(datum), cbdatumsize(datum), &zsiz, 1)) != NULL){ est_sock_send_all(clsock, zbuf, zsiz); free(zbuf); } } else if(req->deflate){ if((zbuf = est_deflate(cbdatumptr(datum), cbdatumsize(datum), &zsiz, 0)) != NULL){ est_sock_send_all(clsock, zbuf, zsiz); free(zbuf); } } else { est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); } cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (%s)", req->claddr, req->clport, SEARCHRSSCMD); cblistclose(ltarg.words); est_cond_delete(ltarg.cond); for(i = 0; i < cbmaprnum(node->links); i++){ free(rtargs[i].label); est_cond_delete(rtargs[i].cond); } free(rtargs); free(rths); cbmapclose(hints); resmap_delete(resmap); cbdatumclose(condbuf); est_cond_delete(cond); free(myurl); } /* send the result of OpenSearch description */ static void sendnodecmdopensearch(int clsock, REQUEST *req, NODE *node){ CBDATUM *datum; char *dstr, *zbuf; int zsiz; datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, UICACHELIFE); cbdatumprintf(datum, "Content-Type: %s\r\n", OSRCHMIMETYPE); if(req->gzip){ cbdatumprintf(datum, "Content-Encoding: gzip\r\n"); } else if(req->deflate){ cbdatumprintf(datum, "Content-Encoding: deflate\r\n"); } cbdatumprintf(datum, "Content-Disposition: inline; filename=%s.xml\r\n", node->name); dstr = cbdatestrhttp(node->mtime, 0); cbdatumprintf(datum, "Last-Modified: %s\r\n", dstr); free(dstr); cbdatumprintf(datum, "\r\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumsetsize(datum, 0); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n", _EST_XNNODE); cbdatumprintf(datum, "%@\n", node->name); cbdatumprintf(datum, "%@\n", node->label); cbdatumprintf(datum, "Hyper Estraier %@ %@ %@\n", SERVNAME, node->name, node->label); cbdatumprintf(datum, "%@\n", g_adminemail); cbdatumprintf(datum, "\n", ATOMMIMETYPE, req->prefix, NODEPREFIX, node->name, SEARCHATOMCMD); cbdatumprintf(datum, "\n", UIMIMETYPE, req->prefix, NODEPREFIX, node->name, SEARCHUICMD); cbdatumprintf(datum, "" "%@%@\n", req->prefix, FAVICONLOC); cbdatumprintf(datum, "" "%@%@%@\n", req->prefix, IMAGEPREFIX, BIGICONNAME); switch(g_uiphraseform){ default: cbdatumprintf(datum, "\n", "search AND hyperestraier OR hyperestraier"); break; case PM_SIMPLE: case PM_ROUGH: cbdatumprintf(datum, "\n", "search \"hyper estraier\" | hyperestraier"); break; case PM_UNION: case PM_ISECT: cbdatumprintf(datum, "\n", "hop step jump"); break; } cbdatumprintf(datum, "%d\n", est_mtdb_doc_num(node->db)); cbdatumprintf(datum, "%d\n", est_mtdb_word_num(node->db)); cbdatumprintf(datum, "%.0f\n", est_mtdb_size(node->db)); cbdatumprintf(datum, "\n"); if(req->gzip){ if((zbuf = est_deflate(cbdatumptr(datum), cbdatumsize(datum), &zsiz, 1)) != NULL){ est_sock_send_all(clsock, zbuf, zsiz); free(zbuf); } } else if(req->deflate){ if((zbuf = est_deflate(cbdatumptr(datum), cbdatumsize(datum), &zsiz, 0)) != NULL){ est_sock_send_all(clsock, zbuf, zsiz); free(zbuf); } } else { est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); } cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (opensearch)", req->claddr, req->clport); } /* send administration interface data */ static void sendmasteruidata(int clsock, REQUEST *req, USER *user){ CBDATUM *datum; CBLIST *list, *words; USER *tuser; NODE *tnode; const char *tmp, *name, *passwd, *flags, *fname, *misc, *label, *admins, *users, *links; char *pbuf, *pv, *nv; int i, action, sure, vml, vul, vnl, vne, vns, size, dnum, msiz; double fsiz; time_t rsec; action = UI_NONE; if((tmp = cbmapget(req->params, "action", -1, NULL)) != NULL) action = atoi(tmp); sure = FALSE; if((tmp = cbmapget(req->params, "sure", -1, NULL)) != NULL) sure = atoi(tmp) > 0; if(!(name = cbmapget(req->params, "name", -1, NULL))) name = ""; if(!(passwd = cbmapget(req->params, "passwd", -1, NULL))) passwd = ""; if(!(flags = cbmapget(req->params, "flags", -1, NULL))) flags = ""; if(!(fname = cbmapget(req->params, "fname", -1, NULL))) fname = ""; if(!(misc = cbmapget(req->params, "misc", -1, NULL))) misc = ""; if(!(label = cbmapget(req->params, "label", -1, NULL))) label = ""; admins = cbmapget(req->params, "admins", -1, NULL); users = cbmapget(req->params, "users", -1, NULL); links = cbmapget(req->params, "links", -1, NULL); if(!rwlock_lock(g_mgrlock, TRUE)){ log_print(LL_ERROR, "locking failed"); senderror(clsock, req, 500, "Internal Server Error (locking failed)"); return; } if(req->referer && !cbstrfwmatch(req->referer, req->prefix) && action != UI_VIEWMASTER && action != UI_VIEWUSERS && action != UI_VIEWNODES && action != UI_STATNODE && action != UI_NONE){ senderror(clsock, req, 403, "Forbidden (invalid referrer)"); if(!rwlock_unlock(g_mgrlock)) log_print(LL_ERROR, "unlocking failed"); return; } if(!ismasteradmin(req, user)){ sendautherror(clsock, req, "Super User"); if(!rwlock_unlock(g_mgrlock)) log_print(LL_ERROR, "unlocking failed"); return; } datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: %s\r\n", UIMIMETYPE); cbdatumprintf(datum, "\r\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n", UIMIMETYPE); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n", SERVNAME, est_version); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n", req->prefix, MASTERUILOC); cbdatumprintf(datum, "\n", req->prefix, FAVICONLOC); cbdatumprintf(datum, "\n", g_adminemail); cbdatumprintf(datum, "Administration Interface of Hyper Estraier\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "

Administration Interface

\n"); cbdatumprintf(datum, "
    \n"); vml = FALSE; vul = FALSE; vnl = FALSE; vne = FALSE; vns = FALSE; switch(action){ case UI_VIEWMASTER: case UI_SHUTDOWN: case UI_SYNCNODES: case UI_BACKUPDB: vml = TRUE; break; case UI_VIEWUSERS: case UI_NEWUSER: case UI_DELUSER: vul = TRUE; break; case UI_VIEWNODES: case UI_NEWNODE: case UI_DELENODE: vnl = TRUE; break; case UI_EDITNODE: vne = TRUE; break; case UI_STATNODE: vns = TRUE; break; default: break; } if(vml){ cbdatumprintf(datum, "
  • Manage Master" "
  • \n", req->prefix, MASTERUILOC, UI_VIEWMASTER); } else { cbdatumprintf(datum, "
  • Manage Master
  • \n", req->prefix, MASTERUILOC, UI_VIEWMASTER); } if(vul){ cbdatumprintf(datum, "
  • Manage Users" "
  • \n", req->prefix, MASTERUILOC, UI_VIEWUSERS); } else { cbdatumprintf(datum, "
  • Manage Users
  • \n", req->prefix, MASTERUILOC, UI_VIEWUSERS); } if(vnl || vne || vns){ cbdatumprintf(datum, "
  • Manage Nodes" "
  • \n", req->prefix, MASTERUILOC, UI_VIEWNODES); } else { cbdatumprintf(datum, "
  • Manage Nodes
  • \n", req->prefix, MASTERUILOC, UI_VIEWNODES); } cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); switch(action){ case UI_SHUTDOWN: if(sure){ g_sigterm = TRUE; cbdatumprintf(datum, "

Shutdown operation has been ordered.

\n"); } else { cbdatumprintf(datum, "
\n", req->prefix, MASTERUILOC); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "confirmation to shutdown the master server:\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n", UI_SHUTDOWN); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); } cbdatumprintf(datum, "
\n"); break; case UI_SYNCNODES: if(sure){ g_sigsync = TRUE; cbdatumprintf(datum, "

Synchronus operation has been ordered.

\n"); } else { cbdatumprintf(datum, "
\n", req->prefix, MASTERUILOC); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "confirmation to synchronize all nodes:\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n", UI_SYNCNODES); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); } cbdatumprintf(datum, "
\n"); break; case UI_BACKUPDB: if(sure){ g_sigback = TRUE; cbdatumprintf(datum, "

Backup operation has been ordered.

\n"); } else { cbdatumprintf(datum, "
\n", req->prefix, MASTERUILOC); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "confirmation to backup the database:\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n", UI_BACKUPDB); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); } cbdatumprintf(datum, "
\n"); break; case UI_NEWUSER: pbuf = est_make_crypt(passwd); if(g_runmode == RM_RDONLY){ cbdatumprintf(datum, "

The server is in read only mode.

\n"); } else if(name[0] == '\0' || passwd[0] == '\0'){ cbdatumprintf(datum, "

The name and the password sould not be empty.

\n"); } else if((tuser = umgr_get(g_umgr, name)) != NULL){ cbdatumprintf(datum, "

The name should not be duplicated.

\n"); } else if(!check_alnum_name(name)){ cbdatumprintf(datum, "

The name can include alphanumeric characters only.

\n"); } else if(umgr_put(g_umgr, name, pbuf, flags, fname, misc)){ cbdatumprintf(datum, "

%@ was created successfully.

\n", name); } else { cbdatumprintf(datum, "

Some errors occurred.

\n"); } free(pbuf); cbdatumprintf(datum, "
\n"); break; case UI_DELUSER: if(g_runmode == RM_RDONLY){ cbdatumprintf(datum, "

The server is in read only mode.

\n"); } else if(sure){ if(umgr_out(g_umgr, name)){ cbdatumprintf(datum, "

%@ was deleted successfully.

\n", name); } else { cbdatumprintf(datum, "

Some errors occurred.

\n"); } } else { cbdatumprintf(datum, "
\n", req->prefix, MASTERUILOC); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "confirmation to delete %@:\n", name); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n", UI_DELUSER); cbdatumprintf(datum, "\n", name); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); } cbdatumprintf(datum, "
\n"); break; case UI_NEWNODE: if(g_runmode == RM_RDONLY){ cbdatumprintf(datum, "

The server is in read only mode.

\n"); } else if(name[0] == '\0' || label[0] == '\0'){ cbdatumprintf(datum, "

The name and the label sould not be empty.

\n"); } else if((tnode = nmgr_get(g_nmgr, name)) != NULL){ cbdatumprintf(datum, "

The name should not be duplicated.

\n"); } else if(!check_alnum_name(name)){ cbdatumprintf(datum, "

The name can include alphanumeric characters only.

\n"); } else if(nmgr_put(g_nmgr, name, TRUE, getnodeoptions())){ if((tnode = nmgr_get(g_nmgr, name)) != NULL){ free(tnode->label); tnode->label = cbmemdup(label, -1); } nmgr_sync(g_nmgr, FALSE); cbdatumprintf(datum, "

%@ was created successfully.

\n", name); } else { cbdatumprintf(datum, "

Some errors occurred.

\n"); } cbdatumprintf(datum, "
\n"); break; case UI_DELENODE: if(g_runmode == RM_RDONLY){ cbdatumprintf(datum, "

The server is in read only mode.

\n"); } else if(sure){ if(nmgr_out(g_nmgr, name)){ cbdatumprintf(datum, "

%@ was deleted successfully.

\n", name); } else { cbdatumprintf(datum, "

Some errors occurred.

\n"); } } else { cbdatumprintf(datum, "
\n", req->prefix, MASTERUILOC); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "confirmation to delete %@:\n", name); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n", UI_DELENODE); cbdatumprintf(datum, "\n", name); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); } cbdatumprintf(datum, "
\n"); break; case UI_EDITNODE: if(!(tnode = nmgr_get(g_nmgr, name))){ cbdatumprintf(datum, "

Some errors occurred.

\n"); cbdatumprintf(datum, "
\n"); } else if(label[0] != '\0'){ free(tnode->label); tnode->label = cbmemdup(label, -1); cbdatumprintf(datum, "

The label was updated.

\n"); cbdatumprintf(datum, "
\n"); } else if(admins){ cbmapclose(tnode->admins); tnode->admins = cbmapopenex(MINIBNUM); list = cbsplit(admins, -1, "\r\n"); for(i = 0; i < cblistnum(list); i++){ pbuf = cbmemdup(cblistval(list, i, NULL), -1); cbstrtrim(pbuf); if(pbuf[0] != '\0' && check_alnum_name(pbuf)) cbmapput(tnode->admins, pbuf, -1, "", 0, TRUE); free(pbuf); } cblistclose(list); cbdatumprintf(datum, "

The list of administrators was updated.

\n"); cbdatumprintf(datum, "
\n"); } else if(users){ cbmapclose(tnode->users); tnode->users = cbmapopenex(MINIBNUM); list = cbsplit(users, -1, "\r\n"); for(i = 0; i < cblistnum(list); i++){ pbuf = cbmemdup(cblistval(list, i, NULL), -1); cbstrtrim(pbuf); if(pbuf[0] != '\0' && check_alnum_name(pbuf)) cbmapput(tnode->users, pbuf, -1, "", 0, TRUE); free(pbuf); } cblistclose(list); cbdatumprintf(datum, "

The list of normal users was updated.

\n"); cbdatumprintf(datum, "
\n"); } else if(links){ cbmapclose(tnode->links); tnode->links = cbmapopenex(MINIBNUM); list = cbsplit(links, -1, "\r\n"); for(i = 0; i < cblistnum(list); i++){ pbuf = cbmemdup(cblistval(list, i, NULL), -1); cbstrtrim(pbuf); if((pv = strstr(pbuf, DELIMSTR)) != NULL && (nv = strstr(pv + 1, DELIMSTR)) != NULL){ *pv = '\0'; pv += strlen(DELIMSTR); *nv = '\0'; nv += strlen(DELIMSTR); node_set_link(tnode, pbuf, pv, atoi(nv)); } free(pbuf); } cblistclose(list); cbdatumprintf(datum, "

The list of links was updated.

\n"); cbdatumprintf(datum, "
\n"); } break; default: break; } if(vml){ cbdatumprintf(datum, "
    \n"); cbdatumprintf(datum, "
  • "); cbdatumprintf(datum, "SHUTDOWN", req->prefix, MASTERUILOC, UI_SHUTDOWN); cbdatumprintf(datum, "
  • \n"); cbdatumprintf(datum, "
  • "); cbdatumprintf(datum, "SYNCHRONIZE", req->prefix, MASTERUILOC, UI_SYNCNODES); cbdatumprintf(datum, "
  • \n"); if(g_backupcmd[0] != '\0'){ cbdatumprintf(datum, "
  • "); cbdatumprintf(datum, "BACKUP", req->prefix, MASTERUILOC, UI_BACKUPDB); cbdatumprintf(datum, "
  • \n"); } cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); list = nmgr_names(g_nmgr); dnum = 0; fsiz = 0.0; msiz = 0; for(i = 0; i < cblistnum(list); i++){ if(!(tmp = cblistval(list, i, NULL)) || !(tnode = nmgr_get(g_nmgr, tmp))) continue; dnum += est_mtdb_doc_num(tnode->db); fsiz += est_mtdb_size(tnode->db); if((size = est_mtdb_used_cache_size(tnode->db)) > msiz) msiz = size; } g_cacheratio = msiz / g_cachesize; cblistclose(list); rsec = req->now - g_startdate; cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
total number of documents
\n"); cbdatumprintf(datum, "
%d
\n", dnum); cbdatumprintf(datum, "
total size of database files
\n"); cbdatumprintf(datum, "
%0.0fMB
\n", fsiz / 1024.0 / 1024.0); cbdatumprintf(datum, "
worst usage ratio of the writing cache
\n"); cbdatumprintf(datum, "
%0.1f%%
\n", g_cacheratio * 100.0); cbdatumprintf(datum, "
total access count
\n"); cbdatumprintf(datum, "
%d
\n", g_accesscount); cbdatumprintf(datum, "
running time
\n"); pbuf = cbdatestrwww(g_startdate, 0); cbdatumprintf(datum, "
%dh %dm %ds (since %@)
\n", (int)(rsec / 3600), (int)((rsec / 60) % 60), (int)(rsec % 60), pbuf); free(pbuf); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); } if(vul){ list = umgr_names(g_umgr); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); for(i = 0; i < cblistnum(list); i++){ if(!(tmp = cblistval(list, i, NULL)) || !(tuser = umgr_get(g_umgr, tmp))) continue; pbuf = cbmemdup(tuser->passwd, -1); if(strlen(pbuf) > 8) sprintf(pbuf + 4, "..."); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n", tuser->name); cbdatumprintf(datum, "\n", pbuf); cbdatumprintf(datum, "\n", tuser->flags); cbdatumprintf(datum, "\n", tuser->fname); cbdatumprintf(datum, "\n", tuser->misc); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); free(pbuf); } cbdatumprintf(datum, "
namepasswordflagsfull namemiscactions
%@%@%@%@%@"); cbdatumprintf(datum, "DELE", req->prefix, MASTERUILOC, UI_DELUSER, tuser->name); cbdatumprintf(datum, "
\n"); cblistclose(list); cbdatumprintf(datum, "
\n", req->prefix, MASTERUILOC); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n", UI_NEWUSER); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); } if(vnl){ list = nmgr_names(g_nmgr); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); for(i = 0; i < cblistnum(list); i++){ if(!(tmp = cblistval(list, i, NULL)) || !(tnode = nmgr_get(g_nmgr, tmp))) continue; cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n", req->prefix, NODEPREFIX, tnode->name, SEARCHUICMD, tnode->name); cbdatumprintf(datum, "\n", tnode->label); cbdatumprintf(datum, "\n", est_mtdb_doc_num(tnode->db)); cbdatumprintf(datum, "\n", est_mtdb_word_num(tnode->db)); cbdatumprintf(datum, "\n", est_mtdb_size(tnode->db) / 1024.0 / 1024.0); cbdatumprintf(datum, "\n", cbmaprnum(tnode->admins)); cbdatumprintf(datum, "\n", cbmaprnum(tnode->users)); cbdatumprintf(datum, "\n", cbmaprnum(tnode->links)); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); } cbdatumprintf(datum, "
namelabel#docs#wordssize#a#u#lactions
%@%@%d%d%.1f%d%d%d"); cbdatumprintf(datum, "STAT", req->prefix, MASTERUILOC, UI_STATNODE, tnode->name); cbdatumprintf(datum, " / EDIT", req->prefix, MASTERUILOC, UI_EDITNODE, tnode->name); cbdatumprintf(datum, " / DELE", req->prefix, MASTERUILOC, UI_DELENODE, tnode->name); cbdatumprintf(datum, "
\n"); cblistclose(list); cbdatumprintf(datum, "
\n", req->prefix, MASTERUILOC); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n", UI_NEWNODE); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); } if(vne){ if((tnode = nmgr_get(g_nmgr, name)) != NULL){ cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
name
\n"); cbdatumprintf(datum, "
%@
\n", tnode->name); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n", req->prefix, MASTERUILOC); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
label
\n"); cbdatumprintf(datum, "
"); cbdatumprintf(datum, "", tnode->label); cbdatumprintf(datum, " "); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "\n", UI_EDITNODE); cbdatumprintf(datum, "\n", name); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n", req->prefix, MASTERUILOC); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
list of administrators
\n"); cbdatumprintf(datum, "
"); cbdatumprintf(datum, ""); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
"); cbdatumprintf(datum, ""); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "\n", UI_EDITNODE); cbdatumprintf(datum, "\n", name); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n", req->prefix, MASTERUILOC); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
list of normal users
\n"); cbdatumprintf(datum, "
"); cbdatumprintf(datum, ""); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
"); cbdatumprintf(datum, ""); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "\n", UI_EDITNODE); cbdatumprintf(datum, "\n", name); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n", req->prefix, MASTERUILOC); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
list of links
\n"); cbdatumprintf(datum, "
"); cbdatumprintf(datum, ""); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
"); cbdatumprintf(datum, ""); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "\n", UI_EDITNODE); cbdatumprintf(datum, "\n", name); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); } else { cbdatumprintf(datum, "

Some errors occured.

\n"); } cbdatumprintf(datum, "
\n"); } if(vns){ if((tnode = nmgr_get(g_nmgr, name)) != NULL){ cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
name
\n"); cbdatumprintf(datum, "
%@
\n", tnode->name); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
label
\n"); cbdatumprintf(datum, "
%@
\n", tnode->label); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
number of documents
\n"); cbdatumprintf(datum, "
%d
\n", est_mtdb_doc_num(tnode->db)); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
number of unique words
\n"); cbdatumprintf(datum, "
%d
\n", est_mtdb_word_num(tnode->db)); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
size of the database
\n"); cbdatumprintf(datum, "
%.1fMB
\n", est_mtdb_size(tnode->db) / 1024.0 / 1024.0); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
words in the reading cache
\n"); cbdatumprintf(datum, "
"); words = est_mtdb_list_rescc(tnode->db); for(i = 0; i < cblistnum(words); i++){ tmp = cblistval(words, i, NULL); if(i > 0) cbdatumprintf(datum, ", "); cbdatumprintf(datum, "%@", req->prefix, NODEPREFIX, tnode->name, SEARCHUICMD, tmp, tmp); } cblistclose(words); cbdatumprintf(datum, "...
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
usage ratio of the writing cache
\n"); size = est_mtdb_used_cache_size(tnode->db); cbdatumprintf(datum, "
%.1f%% (%.1fMB / %.1fMB), %d unique words
", size / g_cachesize * 100, size / 1024.0 / 1024.0, g_cachesize / 1024 / 1024, est_mtdb_cache_num(tnode->db)); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
modification date
\n"); pbuf = cbdatestrhttp(tnode->mtime, 0); cbdatumprintf(datum, "
%@
", pbuf); free(pbuf); cbdatumprintf(datum, "
\n"); } else { cbdatumprintf(datum, "

Some errors occured.

\n"); } cbdatumprintf(datum, "
\n"); } cbdatumprintf(datum, "
Powered by Hyper Estraier" " %@.
\n", _EST_PROJURL, est_version); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (ui:%d)", req->claddr, req->clport, action); if(!rwlock_unlock(g_mgrlock)) log_print(LL_ERROR, "unlocking failed"); } /* send the favorite icon data */ static void sendfavicondata(int clsock, REQUEST *req){ CBDATUM *datum; if(req->ims > 0){ sendnotmoderror(clsock, req); return; } datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, IMGCACHELIFE); cbdatumprintf(datum, "Content-Length: %d\r\n", sizeof(g_favicon)); cbdatumprintf(datum, "Content-Type: image/png\r\n"); cbdatumprintf(datum, "Last-Modified: Sat, 01 Jan 2000 00:00:00 GMT\r\n"); cbdatumprintf(datum, "\r\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); est_sock_send_all(clsock, (char *)g_favicon, sizeof(g_favicon)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (favicon)", req->claddr, req->clport); } /* send a image data */ static void sendimagedata(int clsock, REQUEST *req, const char *name){ CBDATUM *datum; const char *ptr, *type; int size; if(req->ims > 0){ sendnotmoderror(clsock, req); return; } if(!strcmp(name, BIGICONNAME)){ ptr = (char *)g_bigicon; size = sizeof(g_bigicon); type = "image/png"; } else if(!strcmp(name, CANVASNAME)){ ptr = (char *)g_canvas; size = sizeof(g_canvas); type = "image/png"; } else { senderror(clsock, req, 404, "File Not Found"); return; } datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, IMGCACHELIFE); cbdatumprintf(datum, "Content-Length: %d\r\n", size); cbdatumprintf(datum, "Content-Type: %s\r\n", type); cbdatumprintf(datum, "Last-Modified: Sat, 01 Jan 2000 00:00:00 GMT\r\n"); cbdatumprintf(datum, "\r\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); est_sock_send_all(clsock, ptr, size); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (%s)", req->claddr, req->clport, name); } /* send a file data */ static void sendfiledata(int clsock, REQUEST *req){ struct stat sbuf; CBLIST *list; CBDATUM *datum; const char *elem; char *path, *index, pbuf[URIBUFSIZ], ibuf[IOBUFSIZ], *ext, *tmp; int i, fd, len, fdir; time_t ims; double size; path = makelocalpath(req->target); if(g_indexfile[0] != '\0'){ index = cbsprintf("%s%c%s", path, ESTPATHCHR, g_indexfile); if(stat(index, &sbuf) == 0 && cbstrbwmatch(req->target, "/")){ free(path); path = index; index = NULL; } free(index); } if((list = cbdirlist(path)) != NULL){ datum = cbdatumopen(NULL, -1); if(cbstrbwmatch(req->target, "/")){ cblistsort(list); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: %s\r\n", DIRMIMETYPE); cbdatumprintf(datum, "\r\n"); if(req->method != HM_HEAD){ cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n", DIRMIMETYPE); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n", SERVNAME, est_version); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n", req->prefix, FAVICONLOC); cbdatumprintf(datum, "\n", g_adminemail); cbdatumprintf(datum, "%@\n", req->target); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "

Index of %@

\n", req->target); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
    \n"); if(strcmp(req->target, "/")) cbdatumprintf(datum, "
  • ../
  • \n"); for(i = 0; i < cblistnum(list); i++){ elem = cblistval(list, i, NULL); if(!strcmp(elem, ESTCDIRSTR) || !strcmp(elem, ESTPDIRSTR) || elem[0] == ESTEXTCHR) continue; sprintf(pbuf, "%s%c%s", path, ESTPATHCHR, elem); if(!cbfilestat(pbuf, &fdir, NULL, NULL)) continue; cbdatumprintf(datum, "
  • %@%s
  • \n", elem, fdir ? "/" : "", elem, fdir ? "/" : ""); } cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
%@/%@ at %@/
\n", SERVNAME, est_version, req->prefix); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); } log_print(LL_DEBUG, "[%s:%d]: 200 OK (directory)", req->claddr, req->clport); } else { cblistsort(list); cbdatumprintf(datum, "HTTP/1.0 301 Moved Parmanently\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Location: %s%s/\r\n", req->prefix, req->target); cbdatumprintf(datum, "Content-Type: text/plain\r\n"); cbdatumprintf(datum, "\r\n"); if(req->method != HM_HEAD) cbdatumprintf(datum, "Go to %s/\n", req->target); log_print(LL_DEBUG, "[%s:%d]: 301 (moved parmanently)", req->claddr, req->clport); } est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); cblistclose(list); } else if(strstr(path, ESTPATHSTR ESTEXTSTR)){ senderror(clsock, req, 403, "Forbidden"); } else if((fd = open(path, O_RDONLY, 0)) != -1){ if(fstat(fd, &sbuf) == 0){ ims = sbuf.st_mtime; size = sbuf.st_size; } else { ims = -1; size = -1.0; } if(req->ims > 0 && ims <= req->ims){ sendnotmoderror(clsock, req); } else { datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); if(ims > 0){ tmp = cbdatestrhttp(ims, 0); cbdatumprintf(datum, "Last-Modified: %s\r\n", tmp); free(tmp); } if(size >= 0.0) cbdatumprintf(datum, "Content-Length: %.0f\r\n", size); ext = strrchr(path, ESTPATHCHR); ext = strrchr(ext ? ext : path, '.'); cbdatumprintf(datum, "Content-Type: %s\r\n", est_ext_type(ext ? ext : "")); cbdatumprintf(datum, "\r\n"); est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); if(req->method != HM_HEAD){ while((len = read(fd, ibuf, IOBUFSIZ)) > 0 && !g_sigterm){ send(clsock, ibuf, len, 0); } } cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (file)", req->claddr, req->clport); } close(fd); } else { senderror(clsock, req, 404, "Not Found"); } free(path); } /* make the local path of a target */ static char *makelocalpath(const char *target){ CBLIST *list; CBDATUM *datum; const char *elem; char *tmp; int i; datum = cbdatumopen(g_docroot, -1); list = cbsplit(target, -1, "/"); for(i = 0; i < cblistnum(list); i++){ elem = cblistval(list, i, NULL); if(elem[0] == '\0') continue; tmp = cburldecode(elem, NULL); cbdatumprintf(datum, "%c%s", ESTPATHCHR, tmp); free(tmp); } cblistclose(list); return cbdatumtomalloc(datum, NULL); } /* send the menu data */ static void sendmenudata(int clsock, REQUEST *req){ NODE *tnode; CBDATUM *datum; CBLIST *list; const char *tmp; int i; if(!rwlock_lock(g_mgrlock, FALSE)){ log_print(LL_ERROR, "locking failed"); senderror(clsock, req, 500, "Internal Server Error (locking failed)"); return; } datum = cbdatumopen(NULL, -1); cbdatumprintf(datum, "HTTP/1.0 200 OK\r\n"); addservinfo(datum, req->now, 0); cbdatumprintf(datum, "Content-Type: %s\r\n", DIRMIMETYPE); cbdatumprintf(datum, "\r\n"); if(req->method != HM_HEAD){ cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n", MENUMIMETYPE); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n", SERVNAME, est_version); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n", req->prefix, FAVICONLOC); cbdatumprintf(datum, "\n", g_adminemail); cbdatumprintf(datum, "%@\n", SERVNAME); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "

Hyper Estraier

\n"); list = nmgr_names(g_nmgr); if(cblistnum(list) > 0){ cbdatumprintf(datum, "
    \n"); for(i = 0; i < cblistnum(list); i++){ if(!(tmp = cblistval(list, i, NULL)) || !(tnode = nmgr_get(g_nmgr, tmp))) continue; cbdatumprintf(datum, "
  • %@
  • \n", req->prefix, NODEPREFIX, tnode->name, SEARCHUICMD, tnode->name); } cbdatumprintf(datum, "
\n"); } cblistclose(list); cbdatumprintf(datum, "
    \n"); cbdatumprintf(datum, "
  • administration
  • \n", req->prefix, MASTERUILOC); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
\n"); cbdatumprintf(datum, "
%@/%@ at %@/
\n", SERVNAME, est_version, req->prefix); cbdatumprintf(datum, "\n"); cbdatumprintf(datum, "\n"); } est_sock_send_all(clsock, cbdatumptr(datum), cbdatumsize(datum)); cbdatumclose(datum); log_print(LL_DEBUG, "[%s:%d]: 200 OK (menu)", req->claddr, req->clport); if(!rwlock_unlock(g_mgrlock)) log_print(LL_ERROR, "unlocking failed"); } /* END OF FILE */