Remove $Id$ tags from most of webcit
[citadel.git] / webcit / context_loop.c
1 /*
2  * This is the other half of the webserver.  It handles the task of hooking
3  * up HTTP requests with the sessions they belong to, using HTTP cookies to
4  * keep track of things.  If the HTTP request doesn't belong to any currently
5  * active session, a new session is started.
6  *
7  * Copyright (c) 1996-2010 by the citadel.org team
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include "webcit.h"
25 #include "webserver.h"
26 #include "modules_init.h"
27
28 /* Only one thread may manipulate SessionList at a time... */
29 pthread_mutex_t SessionListMutex;
30
31 wcsession *SessionList = NULL;  /* Linked list of all webcit sessions */
32
33 pthread_key_t MyConKey;         /* TSD key for MySession() */
34 HashList *HttpReqTypes = NULL;
35 HashList *HttpHeaderHandler = NULL;
36 extern HashList *HandlerHash;
37
38 /* the following two values start at 1 because the initial parent thread counts as one. */
39 int num_threads_existing = 1;           /* Number of worker threads which exist. */
40 int num_threads_executing = 1;          /* Number of worker threads currently executing. */
41
42 void DestroyHttpHeaderHandler(void *V)
43 {
44         OneHttpHeader *pHdr;
45         pHdr = (OneHttpHeader*) V;
46         FreeStrBuf(&pHdr->Val);
47         free(pHdr);
48 }
49
50 void shutdown_sessions(void)
51 {
52         wcsession *sptr;
53         
54         for (sptr = SessionList; sptr != NULL; sptr = sptr->next) {
55                         sptr->killthis = 1;
56         }
57 }
58
59 void do_housekeeping(void)
60 {
61         wcsession *sptr, *ss;
62         wcsession *sessions_to_kill = NULL;
63
64         /*
65          * Lock the session list, moving any candidates for euthanasia into
66          * a separate list.
67          */
68         pthread_mutex_lock(&SessionListMutex);
69         for (sptr = SessionList; sptr != NULL; sptr = sptr->next) {
70
71                 /* Kill idle sessions */
72                 if ((time(NULL) - (sptr->lastreq)) > (time_t) WEBCIT_TIMEOUT) {
73                         sptr->killthis = 1;
74                 }
75
76                 /* Remove sessions flagged for kill */
77                 if (sptr->killthis) {
78
79                         /** remove session from linked list */
80                         if (sptr == SessionList) {
81                                 SessionList = SessionList->next;
82                         }
83                         else for (ss=SessionList;ss!=NULL;ss=ss->next) {
84                                 if (ss->next == sptr) {
85                                         ss->next = ss->next->next;
86                                 }
87                         }
88
89                         sptr->next = sessions_to_kill;
90                         sessions_to_kill = sptr;
91                 }
92         }
93         pthread_mutex_unlock(&SessionListMutex);
94
95         /*
96          * Now free up and destroy the culled sessions.
97          */
98         while (sessions_to_kill != NULL) {
99                 lprintf(3, "Destroying session %d\n", sessions_to_kill->wc_session);
100                 pthread_mutex_lock(&sessions_to_kill->SessionMutex);
101                 pthread_mutex_unlock(&sessions_to_kill->SessionMutex);
102                 sptr = sessions_to_kill->next;
103
104                 session_destroy_modules(&sessions_to_kill);
105                 sessions_to_kill = sptr;
106         }
107 }
108
109 /*
110  * Check the size of our thread pool.  If all threads are executing, spawn another.
111  */
112 void check_thread_pool_size(void)
113 {
114         if (time_to_die) return;                /* don't expand the thread pool during shutdown */
115
116         begin_critical_section(S_SPAWNER);      /* only one of these should run at a time */
117         while (
118                 (num_threads_executing >= num_threads_existing)
119                 && (num_threads_existing < MAX_WORKER_THREADS)
120         ) {
121                 lprintf(3, "%d of %d threads are executing.  Adding another worker thread.\n",
122                         num_threads_executing,
123                         num_threads_existing
124                 );
125                 spawn_another_worker_thread();
126         }
127         end_critical_section(S_SPAWNER);
128 }
129
130
131 /*
132  * Wake up occasionally and clean house
133  */
134 void housekeeping_loop(void)
135 {
136         while (1) {
137                 sleeeeeeeeeep(HOUSEKEEPING);
138                 do_housekeeping();
139         }
140 }
141
142
143 /*
144  * Create a Session id
145  * Generate a unique WebCit session ID (which is not the same thing as the
146  * Citadel session ID).
147  */
148 int GenerateSessionID(void)
149 {
150         static int seq = (-1);
151
152         if (seq < 0) {
153                 seq = (int) time(NULL);
154         }
155                 
156         return ++seq;
157 }
158
159 wcsession *FindSession(wcsession **wclist, ParsedHttpHdrs *Hdr, pthread_mutex_t *ListMutex)
160 {
161         wcsession *sptr = NULL;
162         wcsession *TheSession = NULL;   
163         
164         if (Hdr->HR.got_auth == AUTH_BASIC) {
165                 GetAuthBasic(Hdr);
166         }
167
168         pthread_mutex_lock(ListMutex);
169         for (sptr = *wclist; ((sptr != NULL) && (TheSession == NULL)); sptr = sptr->next) {
170                 
171                 /* If HTTP-AUTH, look for a session with matching credentials */
172                 switch (Hdr->HR.got_auth)
173                 {
174                 case AUTH_BASIC:
175                         if ( (Hdr->HR.SessionKey != sptr->SessionKey))
176                                 continue;
177                         if ((!strcasecmp(ChrPtr(Hdr->c_username), ChrPtr(sptr->wc_username))) &&
178                             (!strcasecmp(ChrPtr(Hdr->c_password), ChrPtr(sptr->wc_password))) ) {
179                                 TheSession = sptr;
180                         }
181                         if (TheSession == NULL)
182                                 lprintf(1, "found sessionkey [%ld], but credentials for [%s|%s] didn't match\n",
183                                         Hdr->HR.SessionKey,ChrPtr(Hdr->c_username), ChrPtr(sptr->wc_username));
184                         break;
185                 case AUTH_COOKIE:
186                         /* If cookie-session, look for a session with matching session ID */
187                         if ( (Hdr->HR.desired_session != 0) && 
188                              (sptr->wc_session == Hdr->HR.desired_session)) {
189                                 TheSession = sptr;
190                         }
191                         break;                       
192                 case NO_AUTH:
193                         break;
194                 }
195         }
196         pthread_mutex_unlock(ListMutex);
197         if (TheSession == NULL)
198                 lprintf(1, "didn't find sessionkey [%ld] for user [%s]\n",
199                         Hdr->HR.SessionKey,ChrPtr(Hdr->c_username));
200         return TheSession;
201 }
202
203 wcsession *CreateSession(int Lockable, int Static, wcsession **wclist, ParsedHttpHdrs *Hdr, pthread_mutex_t *ListMutex)
204 {
205         wcsession *TheSession;
206         if (!Static)
207                 lprintf(3, "Creating a new session\n");
208         TheSession = (wcsession *) malloc(sizeof(wcsession));
209         memset(TheSession, 0, sizeof(wcsession));
210         TheSession->Hdr = Hdr;
211         TheSession->SessionKey = Hdr->HR.SessionKey;
212         TheSession->serv_sock = (-1);
213         TheSession->is_mobile = -1;
214
215         pthread_setspecific(MyConKey, (void *)TheSession);
216         
217         /* If we're recreating a session that expired, it's best to give it the same
218          * session number that it had before.  The client browser ought to pick up
219          * the new session number and start using it, but in some rare situations it
220          * doesn't, and that's a Bad Thing because it causes lots of spurious sessions
221          * to get created.
222          */     
223         if (Hdr->HR.desired_session == 0) {
224                 TheSession->wc_session = GenerateSessionID();
225         }
226         else {
227                 TheSession->wc_session = Hdr->HR.desired_session;
228         }
229         Hdr->HR.Static = Static;
230         session_new_modules(TheSession);
231
232         if (Lockable) {
233                 pthread_mutex_init(&TheSession->SessionMutex, NULL);
234
235                 if (ListMutex != NULL)
236                         pthread_mutex_lock(ListMutex);
237
238                 if (wclist != NULL) {
239                         TheSession->nonce = rand();
240                         TheSession->next = *wclist;
241                         *wclist = TheSession;
242                 }
243                 if (ListMutex != NULL)
244                         pthread_mutex_unlock(ListMutex);
245         }
246         return TheSession;
247 }
248
249
250 /*
251  * Detects a 'mobile' user agent 
252  */
253 int is_mobile_ua(char *user_agent) {
254       if (strstr(user_agent,"iPhone OS") != NULL) {
255         return 1;
256       } else if (strstr(user_agent,"Windows CE") != NULL) {
257         return 1;
258       } else if (strstr(user_agent,"SymbianOS") != NULL) {
259         return 1;
260       } else if (strstr(user_agent, "Opera Mobi") != NULL) {
261         return 1;
262       } else if (strstr(user_agent, "Firefox/2.0.0 Opera 9.51 Beta") != NULL) {
263               /*  For some reason a new install of Opera 9.51beta decided to spoof. */
264           return 1;
265           }
266       return 0;
267 }
268
269 /* If it's a "force 404" situation then display the error and bail. */
270 void do_404(void)
271 {
272         hprintf("HTTP/1.1 404 Not found\r\n");
273         hprintf("Content-Type: text/plain\r\n");
274         wc_printf("Not found\r\n");
275         end_burst();
276 }
277
278 int ReadHttpSubject(ParsedHttpHdrs *Hdr, StrBuf *Line, StrBuf *Buf)
279 {
280         const char *Args;
281         void *vLine, *vHandler;
282         const char *Pos = NULL;
283
284
285         Hdr->HR.ReqLine = Line;
286         /* The requesttype... GET, POST... */
287         StrBufExtract_token(Buf, Hdr->HR.ReqLine, 0, ' ');
288         if (GetHash(HttpReqTypes, SKEY(Buf), &vLine) &&
289             (vLine != NULL))
290         {
291                 Hdr->HR.eReqType = *(long*)vLine;
292         }
293         else {
294                 Hdr->HR.eReqType = eGET;
295                 return 1;
296         }
297         StrBufCutLeft(Hdr->HR.ReqLine, StrLength(Buf) + 1);
298
299         /* the HTTP Version... */
300         StrBufExtract_token(Buf, Hdr->HR.ReqLine, 1, ' ');
301         StrBufCutRight(Hdr->HR.ReqLine, StrLength(Buf) + 1);
302         
303         if (StrLength(Buf) == 0) {
304                 Hdr->HR.eReqType = eGET;
305                 return 1;
306         }
307
308         StrBufAppendBuf(Hdr->this_page, Hdr->HR.ReqLine, 0);
309         /* chop Filename / query arguments */
310         Args = strchr(ChrPtr(Hdr->HR.ReqLine), '?');
311         if (Args == NULL) /* whe're not that picky about params... TODO: this will spoil '&' in filenames.*/
312                 Args = strchr(ChrPtr(Hdr->HR.ReqLine), '&');
313         if (Args != NULL) {
314                 Args ++; /* skip the ? */
315                 StrBufPlain(Hdr->PlainArgs, 
316                             Args, 
317                             StrLength(Hdr->HR.ReqLine) -
318                             (Args - ChrPtr(Hdr->HR.ReqLine)));
319                 StrBufCutAt(Hdr->HR.ReqLine, 0, Args - 1);
320         } /* don't parse them yet, maybe we don't even care... */
321         
322         /* now lookup what we are going to do with this... */
323         /* skip first slash */
324         StrBufExtract_NextToken(Buf, Hdr->HR.ReqLine, &Pos, '/');
325         do {
326                 StrBufExtract_NextToken(Buf, Hdr->HR.ReqLine, &Pos, '/');
327
328                 GetHash(HandlerHash, SKEY(Buf), &vHandler),
329                 Hdr->HR.Handler = (WebcitHandler*) vHandler;
330                 if (Hdr->HR.Handler == NULL)
331                         break;
332                 /*
333                  * If the request is prefixed by "/webcit" then chop that off.  This
334                  * allows a front end web server to forward all /webcit requests to us
335                  * while still using the same web server port for other things.
336                  */
337                 if ((Hdr->HR.Handler->Flags & URLNAMESPACE) != 0)
338                         continue;
339                 break;
340         } while (1);
341         /* remove the handlername from the URL */
342         if ((Pos != NULL) && (Pos != StrBufNOTNULL)){
343                 StrBufCutLeft(Hdr->HR.ReqLine, 
344                               Pos - ChrPtr(Hdr->HR.ReqLine));
345         }
346
347         if (Hdr->HR.Handler != NULL) {
348                 if ((Hdr->HR.Handler->Flags & BOGUS) != 0)
349                         return 1;
350                 Hdr->HR.DontNeedAuth = (
351                         ((Hdr->HR.Handler->Flags & ISSTATIC) != 0) ||
352                         ((Hdr->HR.Handler->Flags & ANONYMOUS) != 0)
353                         );
354         }
355         else {
356                 Hdr->HR.DontNeedAuth = 1; /* Flat request? show him the login screen... */
357         }
358
359         return 0;
360 }
361
362 int AnalyseHeaders(ParsedHttpHdrs *Hdr)
363 {
364         OneHttpHeader *pHdr;
365         void *vHdr;
366         long HKLen;
367         const char *HashKey;
368         HashPos *at = GetNewHashPos(Hdr->HTTPHeaders, 0);
369         
370         while (GetNextHashPos(Hdr->HTTPHeaders, at, &HKLen, &HashKey, &vHdr) && 
371                (vHdr != NULL)) {
372                 pHdr = (OneHttpHeader *)vHdr;
373                 if (pHdr->HaveEvaluator)
374                         pHdr->H(pHdr->Val, Hdr);
375
376         }
377         DeleteHashPos(&at);
378         return 0;
379 }
380
381 /*const char *nix(void *vptr) {return ChrPtr( (StrBuf*)vptr);}*/
382
383 /*
384  * Read in the request
385  */
386 int ReadHTTPRequest (ParsedHttpHdrs *Hdr)
387 {
388         const char *pch, *pchs, *pche;
389         OneHttpHeader *pHdr;
390         StrBuf *Line, *LastLine, *HeaderName;
391         int nLine = 0;
392         void *vF;
393         int isbogus = 0;
394
395         HeaderName = NewStrBuf();
396         LastLine = NULL;
397         do {
398                 nLine ++;
399                 Line = NewStrBufPlain(NULL, SIZ / 4);
400
401                 if (ClientGetLine(Hdr, Line) < 0) return 1;
402
403                 if (StrLength(Line) == 0) {
404                         FreeStrBuf(&Line);
405                         continue;
406                 }
407                 if (nLine == 1) {
408                         Hdr->HTTPHeaders = NewHash(1, NULL);
409                         pHdr = (OneHttpHeader*) malloc(sizeof(OneHttpHeader));
410                         memset(pHdr, 0, sizeof(OneHttpHeader));
411                         pHdr->Val = Line;
412                         Put(Hdr->HTTPHeaders, HKEY("GET /"), pHdr, DestroyHttpHeaderHandler);
413                         lprintf(9, "%s\n", ChrPtr(Line));
414                         isbogus = ReadHttpSubject(Hdr, Line, HeaderName);
415                         if (isbogus) break;
416                         continue;
417                 }
418
419                 /* Do we need to Unfold? */
420                 if ((LastLine != NULL) && 
421                     (isspace(*ChrPtr(Line)))) {
422                         pch = pchs = ChrPtr(Line);
423                         pche = pchs + StrLength(Line);
424                         while (isspace(*pch) && (pch < pche))
425                                 pch ++;
426                         StrBufCutLeft(Line, pch - pchs);
427                         StrBufAppendBuf(LastLine, Line, 0);
428
429                         FreeStrBuf(&Line);
430                         continue;
431                 }
432
433                 StrBufSanitizeAscii(Line, '§');
434                 StrBufExtract_token(HeaderName, Line, 0, ':');
435
436                 pchs = ChrPtr(Line);
437                 pche = pchs + StrLength(Line);
438                 pch = pchs + StrLength(HeaderName) + 1;
439                 pche = pchs + StrLength(Line);
440                 while ((pch < pche) && isspace(*pch))
441                         pch ++;
442                 StrBufCutLeft(Line, pch - pchs);
443
444                 StrBufUpCase(HeaderName);
445
446                 pHdr = (OneHttpHeader*) malloc(sizeof(OneHttpHeader));
447                 memset(pHdr, 0, sizeof(OneHttpHeader));
448                 pHdr->Val = Line;
449
450                 if (GetHash(HttpHeaderHandler, SKEY(HeaderName), &vF) &&
451                     (vF != NULL))
452                 {
453                         OneHttpHeader *FHdr = (OneHttpHeader*) vF;
454                         pHdr->H = FHdr->H;
455                         pHdr->HaveEvaluator = 1;
456                 }
457                 Put(Hdr->HTTPHeaders, SKEY(HeaderName), pHdr, DestroyHttpHeaderHandler);
458                 LastLine = Line;
459         } while (Line != NULL);
460
461         FreeStrBuf(&HeaderName);
462
463         return isbogus;
464 }
465
466 void OverrideRequest(ParsedHttpHdrs *Hdr, const char *Line, long len)
467 {
468         StrBuf *Buf = NewStrBuf();
469
470         if (Hdr->HR.ReqLine != NULL) {
471                 FlushStrBuf(Hdr->HR.ReqLine);
472                 StrBufPlain(Hdr->HR.ReqLine, Line, len);
473         }
474         else {
475                 Hdr->HR.ReqLine = NewStrBufPlain(Line, len);
476         }
477         ReadHttpSubject(Hdr, Hdr->HR.ReqLine, Buf);
478
479         FreeStrBuf(&Buf);
480 }
481
482 /*
483  * handle one request
484  *
485  * This loop gets called once for every HTTP connection made to WebCit.  At
486  * this entry point we have an HTTP socket with a browser allegedly on the
487  * other end, but we have not yet bound to a WebCit session.
488  *
489  * The job of this function is to locate the correct session and bind to it,
490  * or create a session if necessary and bind to it, then run the WebCit
491  * transaction loop.  Afterwards, we unbind from the session.  When this
492  * function returns, the worker thread is then free to handle another
493  * transaction.
494  */
495 void context_loop(ParsedHttpHdrs *Hdr)
496 {
497         int isbogus = 0;
498         wcsession *TheSession;
499         struct timeval tx_start;
500         struct timeval tx_finish;
501         
502         gettimeofday(&tx_start, NULL);          /* start a stopwatch for performance timing */
503
504         /*
505          * Find out what it is that the web browser is asking for
506          */
507         isbogus = ReadHTTPRequest(Hdr);
508
509         Hdr->HR.dav_depth = 32767; /* TODO: find a general way to have non-0 defaults */
510         if (!isbogus)
511                 isbogus = AnalyseHeaders(Hdr);
512
513         if ((isbogus) ||
514             ((Hdr->HR.Handler != NULL) &&
515              ((Hdr->HR.Handler->Flags & BOGUS) != 0)))
516         {
517                 wcsession *Bogus;
518
519                 Bogus = CreateSession(0, 1, NULL, Hdr, NULL);
520
521                 do_404();
522
523                 lprintf(9, "HTTP: 404 [%ld.%06ld] %s %s \n",
524                         ((tx_finish.tv_sec*1000000 + tx_finish.tv_usec) - (tx_start.tv_sec*1000000 + tx_start.tv_usec)) / 1000000,
525                         ((tx_finish.tv_sec*1000000 + tx_finish.tv_usec) - (tx_start.tv_sec*1000000 + tx_start.tv_usec)) % 1000000,
526                         ReqStrs[Hdr->HR.eReqType],
527                         ChrPtr(Hdr->this_page)
528                         );
529                 session_detach_modules(Bogus);
530                 session_destroy_modules(&Bogus);
531                 return;
532         }
533
534         if ((Hdr->HR.Handler != NULL) && ((Hdr->HR.Handler->Flags & ISSTATIC) != 0))
535         {
536                 wcsession *Static;
537                 Static = CreateSession(0, 1, NULL, Hdr, NULL);
538                 
539                 Hdr->HR.Handler->F();
540
541                 /* How long did this transaction take? */
542                 gettimeofday(&tx_finish, NULL);
543                 
544 #ifdef TECH_PREVIEW
545                 if ((Hdr->HR.Handler != NULL) ||
546                     ((Hdr->HR.Handler->Flags & LOGCHATTY) == 0))
547 #endif
548                         lprintf(9, "HTTP: 200 [%ld.%06ld] %s %s \n",
549                                 ((tx_finish.tv_sec*1000000 + tx_finish.tv_usec) - (tx_start.tv_sec*1000000 + tx_start.tv_usec)) / 1000000,
550                                 ((tx_finish.tv_sec*1000000 + tx_finish.tv_usec) - (tx_start.tv_sec*1000000 + tx_start.tv_usec)) % 1000000,
551                                 ReqStrs[Hdr->HR.eReqType],
552                                 ChrPtr(Hdr->this_page)
553                                 );
554                 session_detach_modules(Static);
555                 session_destroy_modules(&Static);
556                 return;
557         }
558
559         if (Hdr->HR.got_auth == AUTH_BASIC) {
560                 CheckAuthBasic(Hdr);
561         }
562
563         /*
564          * See if there's an existing session open with the desired ID or user/pass
565          */
566         TheSession = FindSession(&SessionList, Hdr, &SessionListMutex);
567
568         /*
569          * Create a new session if we have to
570          */
571         if (TheSession == NULL) {
572                 TheSession = CreateSession(1, 0, &SessionList, Hdr, &SessionListMutex);
573
574                 if ((StrLength(Hdr->c_username) == 0) && (!Hdr->HR.DontNeedAuth)) {
575
576                         if ((Hdr->HR.Handler != NULL) && 
577                             (XHTTP_COMMANDS & Hdr->HR.Handler->Flags) == XHTTP_COMMANDS) {
578                                 OverrideRequest(Hdr, HKEY("GET /401 HTTP/1.0"));
579                                 Hdr->HR.prohibit_caching = 1;                           
580                         }
581                         else {
582                                 OverrideRequest(Hdr, HKEY("GET /static/nocookies.html?force_close_session=yes HTTP/1.0"));
583                                 Hdr->HR.prohibit_caching = 1;
584                         }
585                 }
586                 
587                 if (StrLength(Hdr->c_language) > 0) {
588                         lprintf(9, "Session cookie requests language '%s'\n", ChrPtr(Hdr->c_language));
589                         set_selected_language(ChrPtr(Hdr->c_language));
590                         go_selected_language();
591                 }
592         }
593
594         /*
595          * A future improvement might be to check the session integrity
596          * at this point before continuing.
597          */
598
599         /*
600          * Bind to the session and perform the transaction
601          */
602         pthread_mutex_lock(&TheSession->SessionMutex);          /* bind */
603         pthread_setspecific(MyConKey, (void *)TheSession);
604         
605         TheSession->lastreq = time(NULL);                       /* log */
606         TheSession->Hdr = Hdr;
607
608         session_attach_modules(TheSession);
609         session_loop();                         /* do transaction */
610
611
612         /* How long did this transaction take? */
613         gettimeofday(&tx_finish, NULL);
614         
615
616 #ifdef TECH_PREVIEW
617         if ((Hdr->HR.Handler != NULL) &&
618             ((Hdr->HR.Handler->Flags & LOGCHATTY) == 0))
619 #endif
620                 lprintf(9, "HTTP: 200 [%ld.%06ld] %s %s \n",
621                         ((tx_finish.tv_sec*1000000 + tx_finish.tv_usec) - (tx_start.tv_sec*1000000 + tx_start.tv_usec)) / 1000000,
622                         ((tx_finish.tv_sec*1000000 + tx_finish.tv_usec) - (tx_start.tv_sec*1000000 + tx_start.tv_usec)) % 1000000,
623                         ReqStrs[Hdr->HR.eReqType],
624                         ChrPtr(Hdr->this_page)
625                         );
626
627         session_detach_modules(TheSession);
628
629         TheSession->Hdr = NULL;
630         pthread_mutex_unlock(&TheSession->SessionMutex);        /* unbind */
631 }
632
633 void tmplput_nonce(StrBuf *Target, WCTemplputParams *TP)
634 {
635         wcsession *WCC = WC;
636         StrBufAppendPrintf(Target, "%ld",
637                            (WCC != NULL)? WCC->nonce:0);                   
638 }
639
640 void tmplput_current_user(StrBuf *Target, WCTemplputParams *TP)
641 {
642         StrBufAppendTemplate(Target, TP, WC->wc_fullname, 0);
643 }
644
645 void Header_HandleContentLength(StrBuf *Line, ParsedHttpHdrs *hdr)
646 {
647         hdr->HR.ContentLength = StrToi(Line);
648 }
649
650 void Header_HandleContentType(StrBuf *Line, ParsedHttpHdrs *hdr)
651 {
652         hdr->HR.ContentType = Line;
653 }
654
655 void Header_HandleUserAgent(StrBuf *Line, ParsedHttpHdrs *hdr)
656 {
657         hdr->HR.user_agent = Line;
658 #ifdef TECH_PREVIEW
659 /* TODO: do this later on session creating
660         if ((WCC->is_mobile < 0) && is_mobile_ua(&buf[12])) {                   
661                 WCC->is_mobile = 1;
662         }
663         else {
664                 WCC->is_mobile = 0;
665         }
666 */
667 #endif
668 }
669
670
671 void Header_HandleHost(StrBuf *Line, ParsedHttpHdrs *hdr)
672 {
673         if ((follow_xff) && (hdr->HR.http_host != NULL))
674                 return;
675         else
676                 hdr->HR.http_host = Line;
677 }
678
679 void Header_HandleXFFHost(StrBuf *Line, ParsedHttpHdrs *hdr)
680 {
681         if (follow_xff)
682                 hdr->HR.http_host = Line;
683 }
684
685
686 void Header_HandleXFF(StrBuf *Line, ParsedHttpHdrs *hdr)
687 {
688         hdr->HR.browser_host = Line;
689
690         while (StrBufNum_tokens(hdr->HR.browser_host, ',') > 1) {
691                 StrBufRemove_token(hdr->HR.browser_host, 0, ',');
692         }
693         StrBufTrim(hdr->HR.browser_host);
694 }
695
696 void Header_HandleIfModSince(StrBuf *Line, ParsedHttpHdrs *hdr)
697 {
698         hdr->HR.if_modified_since = httpdate_to_timestamp(Line);
699 }
700
701 void Header_HandleAcceptEncoding(StrBuf *Line, ParsedHttpHdrs *hdr)
702 {
703         /*
704          * Can we compress?
705          */
706         if (strstr(&ChrPtr(Line)[16], "gzip")) {
707                 hdr->HR.gzip_ok = 1;
708         }
709 }
710 const char *ReqStrs[eNONE] = {
711         "GET",
712         "POST",
713         "OPTIONS",
714         "PROPFIND",
715         "PUT",
716         "DELETE",
717         "HEAD",
718         "MOVE",
719         "COPY"
720 };
721
722 void
723 ServerStartModule_CONTEXT
724 (void)
725 {
726         long *v;
727         HttpReqTypes = NewHash(1, NULL);
728         HttpHeaderHandler = NewHash(1, NULL);
729
730         v = malloc(sizeof(long));
731         *v = eGET;
732         Put(HttpReqTypes, HKEY("GET"), v, NULL);
733
734         v = malloc(sizeof(long));
735         *v = ePOST;
736         Put(HttpReqTypes, HKEY("POST"), v, NULL);
737
738         v = malloc(sizeof(long));
739         *v = eOPTIONS;
740         Put(HttpReqTypes, HKEY("OPTIONS"), v, NULL);
741
742         v = malloc(sizeof(long));
743         *v = ePROPFIND;
744         Put(HttpReqTypes, HKEY("PROPFIND"), v, NULL);
745
746         v = malloc(sizeof(long));
747         *v = ePUT;
748         Put(HttpReqTypes, HKEY("PUT"), v, NULL);
749
750         v = malloc(sizeof(long));
751         *v = eDELETE;
752         Put(HttpReqTypes, HKEY("DELETE"), v, NULL);
753
754         v = malloc(sizeof(long));
755         *v = eHEAD;
756         Put(HttpReqTypes, HKEY("HEAD"), v, NULL);
757
758         v = malloc(sizeof(long));
759         *v = eMOVE;
760         Put(HttpReqTypes, HKEY("MOVE"), v, NULL);
761
762         v = malloc(sizeof(long));
763         *v = eCOPY;
764         Put(HttpReqTypes, HKEY("COPY"), v, NULL);
765 }
766
767 void 
768 ServerShutdownModule_CONTEXT
769 (void)
770 {
771         DeleteHash(&HttpReqTypes);
772         DeleteHash(&HttpHeaderHandler);
773 }
774
775 void RegisterHeaderHandler(const char *Name, long Len, Header_Evaluator F)
776 {
777         OneHttpHeader *pHdr;
778         pHdr = (OneHttpHeader*) malloc(sizeof(OneHttpHeader));
779         memset(pHdr, 0, sizeof(OneHttpHeader));
780         pHdr->H = F;
781         Put(HttpHeaderHandler, Name, Len, pHdr, DestroyHttpHeaderHandler);
782 }
783
784
785 void 
786 InitModule_CONTEXT
787 (void)
788 {
789         RegisterHeaderHandler(HKEY("CONTENT-LENGTH"), Header_HandleContentLength);
790         RegisterHeaderHandler(HKEY("CONTENT-TYPE"), Header_HandleContentType);
791         RegisterHeaderHandler(HKEY("USER-AGENT"), Header_HandleUserAgent);
792         RegisterHeaderHandler(HKEY("X-FORWARDED-HOST"), Header_HandleXFFHost); /* Apache way... */
793         RegisterHeaderHandler(HKEY("X-REAL-IP"), Header_HandleXFFHost);        /* NGinX way... */
794         RegisterHeaderHandler(HKEY("HOST"), Header_HandleHost);
795         RegisterHeaderHandler(HKEY("X-FORWARDED-FOR"), Header_HandleXFF);
796         RegisterHeaderHandler(HKEY("ACCEPT-ENCODING"), Header_HandleAcceptEncoding);
797         RegisterHeaderHandler(HKEY("IF-MODIFIED-SINCE"), Header_HandleIfModSince);
798
799         RegisterNamespace("CURRENT_USER", 0, 1, tmplput_current_user, NULL, CTX_NONE);
800         RegisterNamespace("NONCE", 0, 0, tmplput_nonce, NULL, 0);
801
802         WebcitAddUrlHandler(HKEY("404"), "", 0, do_404, ANONYMOUS|COOKIEUNNEEDED);
803 /*
804  * Look for commonly-found probes of malware such as worms, viruses, trojans, and Microsoft Office.
805  * Short-circuit these requests so we don't have to send them through the full processing loop.
806  */
807         WebcitAddUrlHandler(HKEY("scripts"), "", 0, do_404, ANONYMOUS|BOGUS);           /* /root.exe - Worms and trojans and viruses, oh my! */
808         WebcitAddUrlHandler(HKEY("c"), "", 0, do_404, ANONYMOUS|BOGUS);         /* /winnt */
809         WebcitAddUrlHandler(HKEY("MSADC"), "", 0, do_404, ANONYMOUS|BOGUS);
810         WebcitAddUrlHandler(HKEY("_vti"), "", 0, do_404, ANONYMOUS|BOGUS);              /* Broken Microsoft DAV implementation */
811         WebcitAddUrlHandler(HKEY("MSOffice"), "", 0, do_404, ANONYMOUS|BOGUS);          /* Stoopid MSOffice thinks everyone is IIS */
812         WebcitAddUrlHandler(HKEY("nonexistenshit"), "", 0, do_404, ANONYMOUS|BOGUS);    /* Exploit found in the wild January 2009 */
813 }
814         
815
816 void 
817 HttpNewModule_CONTEXT
818 (ParsedHttpHdrs *httpreq)
819 {
820         httpreq->PlainArgs = NewStrBufPlain(NULL, SIZ);
821         httpreq->this_page = NewStrBufPlain(NULL, SIZ);
822 }
823
824 void 
825 HttpDetachModule_CONTEXT
826 (ParsedHttpHdrs *httpreq)
827 {
828         FlushStrBuf(httpreq->PlainArgs);
829         FlushStrBuf(httpreq->this_page);
830         FlushStrBuf(httpreq->PlainArgs);
831         DeleteHash(&httpreq->HTTPHeaders);
832         memset(&httpreq->HR, 0, sizeof(HdrRefs));
833 }
834
835 void 
836 HttpDestroyModule_CONTEXT
837 (ParsedHttpHdrs *httpreq)
838 {
839         FreeStrBuf(&httpreq->this_page);
840         FreeStrBuf(&httpreq->PlainArgs);
841         FreeStrBuf(&httpreq->this_page);
842         FreeStrBuf(&httpreq->PlainArgs);
843         DeleteHash(&httpreq->HTTPHeaders);
844
845 }