root / trunk / src / dot / proxy / local.c

Revision 7795, 30.5 kB (checked in by BradNeuberg, 22 months ago)

Created way to have JavaScript? layer 'query' to see if the PAC file has been updated yet -- if not, we tell the user to restart their browser

Line 
1/*
2Copyright (c) 2003-2006 by Juliusz Chroboczek
3
4Permission is hereby granted, free of charge, to any person obtaining a copy
5of this software and associated documentation files (the "Software"), to deal
6in the Software without restriction, including without limitation the rights
7to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8copies of the Software, and to permit persons to whom the Software is
9furnished to do so, subject to the following conditions:
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20THE SOFTWARE.
21*/
22
23#include "polipo.h"
24
25int disableLocalInterface = 0;
26int disableConfiguration = 0;
27int disableIndexing = 1;
28int disableServersList = 1;
29int disableOfflineSupport = 1;
30
31AtomPtr atomInitForbidden;
32AtomPtr atomReopenLog;
33AtomPtr atomDiscardObjects;
34AtomPtr atomWriteoutObjects;
35AtomPtr atomFreeChunkArenas;
36
37void
38preinitLocal()
39{
40    atomInitForbidden = internAtom("init-forbidden");
41    atomReopenLog = internAtom("reopen-log");
42    atomDiscardObjects = internAtom("discard-objects");
43    atomWriteoutObjects = internAtom("writeout-objects");
44    atomFreeChunkArenas = internAtom("free-chunk-arenas");
45
46    /* These should not be settable for obvious reasons */
47    CONFIG_VARIABLE(disableLocalInterface, CONFIG_BOOLEAN,
48                    "Disable the local configuration pages.");
49    CONFIG_VARIABLE(disableConfiguration, CONFIG_BOOLEAN,
50                    "Disable reconfiguring Polipo at runtime.");
51    CONFIG_VARIABLE(disableIndexing, CONFIG_BOOLEAN,
52                    "Disable indexing of the local cache.");
53    CONFIG_VARIABLE(disableServersList, CONFIG_BOOLEAN,
54                    "Disable the list of known servers.");
55    CONFIG_VARIABLE(disableOfflineSupport, CONFIG_BOOLEAN,
56                    "Disable support for offline web applications.");
57}
58
59static void fillSpecialObject(ObjectPtr, void (*)(FILE*, char*), void*);
60
61int 
62httpLocalRequest(ObjectPtr object, int method, int from, int to,
63                 HTTPRequestPtr requestor, void *closure)
64{
65    if(object->requestor == NULL)
66        object->requestor = requestor;
67
68    if(!disableLocalInterface && urlIsSpecial(object->key, object->key_size))
69        return httpSpecialRequest(object, method, from, to,
70                                  requestor, closure);
71
72    if(method >= METHOD_POST) {
73        httpClientError(requestor, 405, internAtom("Method not allowed"));
74        requestor->connection->flags &= ~CONN_READER;
75        return 1;
76    }
77
78    /* objectFillFromDisk already did the real work but we have to
79       make sure we don't get into an infinite loop. */
80    if(object->flags & OBJECT_INITIAL) {
81        abortObject(object, 404, internAtom("Not found"));
82    }
83    object->age = current_time.tv_sec;
84    object->date = current_time.tv_sec;
85
86    object->flags &= ~OBJECT_VALIDATING;
87    notifyObject(object);
88    return 1;
89}
90
91void
92alternatingHttpStyle(FILE *out, char *id)
93{
94    fprintf(out,
95            "<style type=\"text/css\">\n"
96            "#%s tbody tr.even td { background-color: #eee; }\n"
97            "#%s tbody tr.odd  td { background-color: #fff; }\n"
98            "</style>\n", id, id);
99}
100
101static void
102printConfig(FILE *out, char *dummy)
103{
104    fprintf(out,
105            "<!DOCTYPE HTML PUBLIC "
106            "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
107            "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
108            "<html><head>\n"
109            "<title>Polipo configuration</title>\n"
110            "</head><body>\n"
111            "<h1>Polipo configuration</h1>\n");
112    printConfigVariables(out, 1);
113    fprintf(out, "<p><a href=\"/polipo/\">back</a></p>");
114    fprintf(out, "</body></html>\n");
115}
116
117static int
118matchUrl(char *base, ObjectPtr object)
119{
120    int n = strlen(base);
121    if(object->key_size < n)
122        return 0;
123    if(memcmp(base, object->key, n) != 0)
124        return 0;
125    return (object->key_size == n) || (((char*)object->key)[n] == '?');
126}
127
128#ifndef NO_OFFLINE_SUPPORT
129
130static int getHost(AtomPtr referer, char **host_results){
131        int cut_start = -1, cut_end = -1, cut_length;
132        int index;
133        char *cut_ptr;
134        char *host_ptr;
135       
136        /* get the host name */
137        /* find where the host name begins */
138        for(index = 0; index < strlen(referer->string); index++){
139       if(referer->string[index] == '/'
140          && (index + 1) < strlen(referer->string)
141          && referer->string[index + 1] == '/'){
142              index = index + 2;
143          break;
144       }
145        }
146       
147        if(index == strlen(referer->string)){
148       /* nothing left to process */
149       return -1;       
150    }
151
152    /* find the end cut for where the host name is */
153    cut_start = index;
154    while(index < strlen(referer->string)){
155       if(referer->string[index] == '/' 
156            || referer->string[index] == '?'
157            || referer->string[index] == ':'
158            || (index + 1) == strlen(referer->string)){
159          cut_end = index - 1; 
160          break;
161       }
162       index++;
163    }
164
165    if(cut_end == -1){
166      return -1;
167    }
168
169    /* 
170        +1 for index arrays starting at 0,
171        another +1 for end of string null character
172    */
173    cut_length = (cut_end - cut_start) + 2;
174    host_ptr = (char *)malloc((unsigned) cut_length);
175    if(host_ptr == NULL)
176        return -1;
177    cut_ptr = host_ptr;
178    for(index = cut_start; index <= cut_end
179                             && index < strlen(referer->string); index++){
180          *cut_ptr = referer->string[index];
181          cut_ptr++;
182    }
183    *cut_ptr = '\0';
184
185    if(strlen(host_ptr) == 0){
186      return -1;
187    }
188
189    *host_results = host_ptr;
190   
191    return 1;
192}
193
194void
195handleOfflineAPI(ObjectPtr object, HTTPRequestPtr requestor)
196{
197        AtomPtr referer;
198        char *host_ptr = NULL;
199        int safe_scheme = 0;
200        int status;
201        printf("handleOfflineAPI, key=%s\n", (char *)object->key);
202        if(requestor->referer == NULL
203          || requestor->referer->string == NULL
204          || strlen(requestor->referer->string) == 0) {
205       goto fail;
206        }
207       
208        referer = requestor->referer;
209       
210        /* make sure we have a scheme we know how to work with */
211        if(strlen(referer->string) >= 4
212       && lwrcmp(referer->string, "http", 4) == 0) {
213      safe_scheme = 1;
214    }
215        if(safe_scheme == 0){
216                goto fail;
217        }
218       
219        /* get the host inside of the referer header */
220        status = getHost(referer, &host_ptr);
221        if(status == -1){
222                goto fail;
223        }
224        printf("host=%s\n", host_ptr);
225       
226        /*
227       get the type of API request desired:
228           addOfflineHost, removeOfflineHost, isHostAvailableOffline,
229           isRunning, getVersion, goOnline, goOffline
230    */
231        if(matchUrl("/polipo/offline?addOfflineHost", object)){
232      status = addOfflineHost(host_ptr);
233      objectPrintf(object, 0,
234                 "%s('addOfflineHost', %s);",
235                 OFF_JAVASCRIPT_CALLBACK,
236                 (status == 1) ? "true" : "false");
237      object->length = object->size;
238    }else if(matchUrl("/polipo/offline?removeOfflineHost", object)){
239      status = removeOfflineHost(host_ptr);
240      objectPrintf(object, 0,
241                 "%s('removeOfflineHost', %s);",
242                 OFF_JAVASCRIPT_CALLBACK,
243                 (status == 1) ? "true" : "false");
244      object->length = object->size;   
245    }else if(matchUrl("/polipo/offline?isHostAvailableOffline", object)){
246      status = isHostAvailableOffline(host_ptr);
247      objectPrintf(object, 0,
248                 "%s('isHostAvailableOffline', %s);",
249                 OFF_JAVASCRIPT_CALLBACK,
250                 (status == 1) ? "true" : "false");
251      object->length = object->size;   
252    }else if(matchUrl("/polipo/offline?isRunning", object)){
253      objectPrintf(object, 0,
254                 "%s('isRunning', true);",
255                 OFF_JAVASCRIPT_CALLBACK);
256      object->length = object->size;   
257    }else if(matchUrl("/polipo/offline?getVersion", object)){
258      objectPrintf(object, 0,
259                 "%s('getVersion', '%s');",
260                 OFF_JAVASCRIPT_CALLBACK,
261                 OFF_OFFLINE_VERSION);
262      object->length = object->size;
263    }else if(matchUrl("/polipo/offline?goOnline", object)){
264          goOnline();
265      objectPrintf(object, 0,
266                 "%s('goOnline', null);",
267                 OFF_JAVASCRIPT_CALLBACK);
268      object->length = object->size;
269    }else if(matchUrl("/polipo/offline?goOffline", object)){
270          goOffline();
271      objectPrintf(object, 0,
272                 "%s('goOffline', null);",
273                 OFF_JAVASCRIPT_CALLBACK);
274      object->length = object->size;
275    }else{
276      goto fail;
277    }
278
279    /* make sure the browser doesn't cache our JavaScript response */
280    object->cache_control = CACHE_NO;
281
282    if(host_ptr){
283      free(host_ptr);   
284    }
285
286    return;
287
288    fail:
289       objectPrintf(object, 0,
290                  "%s('UnknownMethod', null);",
291                  OFF_JAVASCRIPT_CALLBACK);
292       object->length = object->size;
293
294       if(host_ptr){
295         free(host_ptr);       
296       }
297}
298
299#endif
300
301#ifndef NO_DISK_CACHE
302
303static void
304recursiveIndexDiskObjects(FILE *out, char *root)
305{
306    indexDiskObjects(out, root, 1);
307}
308
309static void
310plainIndexDiskObjects(FILE *out, char *root)
311{
312    indexDiskObjects(out, root, 0);
313}
314#endif
315
316static void
317serversList(FILE *out, char *dummy)
318{
319    listServers(out);
320}
321   
322int 
323httpSpecialRequest(ObjectPtr object, int method, int from, int to,
324                   HTTPRequestPtr requestor, void *closure)
325{
326    char buffer[1024];
327    int hlen = 0;
328    if(method >= METHOD_POST) {
329        return httpSpecialSideRequest(object, method, from, to,
330                                      requestor, closure);
331    }
332
333    if(!(object->flags & OBJECT_INITIAL)) {
334        privatiseObject(object, 0);
335        supersedeObject(object);
336        object->flags &= ~(OBJECT_VALIDATING | OBJECT_INPROGRESS);
337        notifyObject(object);
338        return 1;
339    }
340
341    object->date = current_time.tv_sec;
342    object->age = current_time.tv_sec;
343    object->headers = internAtomN(buffer, hlen);
344    object->code = 200;
345    object->message = internAtom("Okay");
346    object->flags &= ~OBJECT_INITIAL;
347    object->flags |= OBJECT_DYNAMIC;
348
349        if(disableOfflineSupport == 0 && matchUrl("/polipo/offline", object)){
350                hlen = snnprintf(buffer, 0, 1024,
351                     "\r\nServer: polipo"
352                     "\r\nContent-Type: text/javascript");
353        }else if(isPACCheck(object) == 1){
354                hlen = snnprintf(buffer, 0, 1024,
355                     "\r\nServer: polipo"
356                     "\r\nContent-Type: text/plain");
357        }else{
358        hlen = snnprintf(buffer, 0, 1024,
359                     "\r\nServer: polipo"
360                     "\r\nContent-Type: text/html");
361    }
362
363    if(object->key_size == 8 && memcmp(object->key, "/polipo/", 8) == 0) {
364        objectPrintf(object, 0,
365                     "<!DOCTYPE HTML PUBLIC "
366                     "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
367                     "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
368                     "<html><head>\n"
369                     "<title>Polipo</title>\n"
370                     "</head><body>\n"
371                     "<h1>Polipo</h1>\n"
372                     "<p><a href=\"status?\">Status report</a>.</p>\n"
373                     "<p><a href=\"config?\">Current configuration</a>.</p>\n"
374                     "<p><a href=\"servers?\">Known servers</a>.</p>\n"
375#ifndef NO_DISK_CACHE
376                     "<p><a href=\"index?\">Disk cache index</a>.</p>\n"
377#endif
378                     "</body></html>\n");
379        object->length = object->size;
380#ifndef NO_OFFLINE_SUPPORT
381    } else if(disableOfflineSupport == 0 &&
382            matchUrl("/polipo/offline", object)) {
383        handleOfflineAPI(object, requestor);
384#endif
385    } else if(isPACCheck(object) == 1) {
386        printf("pac_check.txt was requested");
387        objectPrintf(object, 0,
388                    "the web application is inside the PAC file");
389        object->length = object->size;       
390    } else if(matchUrl("/polipo/status", object)) {
391        objectPrintf(object, 0,
392                     "<!DOCTYPE HTML PUBLIC "
393                     "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
394                     "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
395                     "<html><head>\n"
396                     "<title>Polipo status report</title>\n"
397                     "</head><body>\n"
398                     "<h1>Polipo proxy on %s:%d: status report</h1>\n"
399                     "<p>The %s proxy on %s:%d is %s.</p>\n"
400                     "<p>There are %d public and %d private objects "
401                     "currently in memory using %d KB in %d chunks "
402                     "(%d KB allocated).</p>\n"
403                     "<p>There are %d atoms.</p>"
404                     "<p><form method=POST action=\"/polipo/status?\">"
405                     "<input type=submit name=\"init-forbidden\" "
406                     "value=\"Read forbidden file\"></form>\n"
407                     "<form method=POST action=\"/polipo/status?\">"
408                     "<input type=submit name=\"writeout-objects\" "
409                     "value=\"Write out in-memory cache\"></form>\n"
410                     "<form method=POST action=\"/polipo/status?\">"
411                     "<input type=submit name=\"discard-objects\" "
412                     "value=\"Discard in-memory cache\"></form>\n"
413                     "<form method=POST action=\"/polipo/status?\">"
414                     "<input type=submit name=\"reopen-log\" "
415                     "value=\"Reopen log file\"></form>\n"
416                     "<form method=POST action=\"/polipo/status?\">"
417                     "<input type=submit name=\"free-chunk-arenas\" "
418                     "value=\"Free chunk arenas\"></form></p>\n"
419                     "<p><a href=\"/polipo/\">back</a></p>"
420                     "</body></html>\n",
421                     proxyName->string, proxyPort,
422                     cacheIsShared ? "shared" : "private",
423                     proxyName->string, proxyPort,
424                     proxyOffline ? "off line" :
425                     (relaxTransparency ?
426                      "on line (transparency relaxed)" :
427                      "on line"),
428                     publicObjectCount, privateObjectCount,
429                     used_chunks * CHUNK_SIZE / 1024, used_chunks,
430                     totalChunkArenaSize() / 1024,
431                     used_atoms);
432        object->expires = current_time.tv_sec;
433        object->length = object->size;
434    } else if(matchUrl("/polipo/config", object)) {
435        fillSpecialObject(object, printConfig, NULL);
436        object->expires = current_time.tv_sec + 5;
437#ifndef NO_DISK_CACHE
438    } else if(matchUrl("/polipo/index", object)) {
439        int len;
440        char *root;
441        if(disableIndexing) {
442            abortObject(object, 403, internAtom("Action not allowed"));
443            notifyObject(object);
444            return 1;
445        }
446        len = MAX(0, object->key_size - 14);
447        root = strdup_n((char*)object->key + 14, len);
448        if(root == NULL) {
449            abortObject(object, 503, internAtom("Couldn't allocate root"));
450            notifyObject(object);
451            return 1;
452        }
453        writeoutObjects(1);
454        fillSpecialObject(object, plainIndexDiskObjects, root);
455        free(root);
456        object->expires = current_time.tv_sec + 5;
457    } else if(matchUrl("/polipo/recursive-index", object)) {
458        int len;
459        char *root;
460        if(disableIndexing) {
461            abortObject(object, 403, internAtom("Action not allowed"));
462            notifyObject(object);
463            return 1;
464        }
465        len = MAX(0, object->key_size - 24);
466        root = strdup_n((char*)object->key + 24, len);
467        if(root == NULL) {
468            abortObject(object, 503, internAtom("Couldn't allocate root"));
469            notifyObject(object);
470            return 1;
471        }
472        writeoutObjects(1);
473        fillSpecialObject(object, recursiveIndexDiskObjects, root);
474        free(root);
475        object->expires = current_time.tv_sec + 20;
476#endif
477    } else if(matchUrl("/polipo/servers", object)) {
478        if(disableServersList) {
479            abortObject(object, 403, internAtom("Action not allowed"));
480            notifyObject(object);
481            return 1;
482        }
483        fillSpecialObject(object, serversList, NULL);
484        object->expires = current_time.tv_sec + 2;
485    } else {
486        abortObject(object, 404, internAtom("Not found"));
487    }
488
489    object->flags &= ~OBJECT_VALIDATING;
490    notifyObject(object);
491    return 1;
492}
493
494int 
495httpSpecialSideRequest(ObjectPtr object, int method, int from, int to,
496                       HTTPRequestPtr requestor, void *closure)
497{
498    HTTPConnectionPtr client = requestor->connection;
499
500    assert(client->request == requestor);
501
502    if(method != METHOD_POST) {
503        httpClientError(requestor, 405, internAtom("Method not allowed"));
504        requestor->connection->flags &= ~CONN_READER;
505        return 1;
506    }
507
508    return httpSpecialDoSide(requestor);
509}
510
511int
512httpSpecialDoSide(HTTPRequestPtr requestor)
513{
514    HTTPConnectionPtr client = requestor->connection;
515
516    if(client->reqlen - client->reqbegin >= client->bodylen) {
517        AtomPtr data;
518        data = internAtomN(client->reqbuf + client->reqbegin,
519                           client->reqlen - client->reqbegin);
520        client->reqbegin = 0;
521        client->reqlen = 0;
522        if(data == NULL) {
523            do_log(L_ERROR, "Couldn't allocate data.\n");
524            httpClientError(requestor, 500,
525                            internAtom("Couldn't allocate data"));
526            return 1;
527        }
528        httpSpecialDoSideFinish(data, requestor);
529        return 1;
530    }
531
532    if(client->reqlen - client->reqbegin >= CHUNK_SIZE) {
533        httpClientError(requestor, 500, internAtom("POST too large"));
534        return 1;
535    }
536
537    if(client->reqbegin > 0 && client->reqlen > client->reqbegin) {
538        memmove(client->reqbuf, client->reqbuf + client->reqbegin,
539                client->reqlen - client->reqbegin);
540    }
541    client->reqlen -= client->reqbegin;
542    client->reqbegin = 0;
543
544    do_stream(IO_READ | IO_NOTNOW, client->fd,
545              client->reqlen, client->reqbuf, CHUNK_SIZE,
546              httpSpecialClientSideHandler, client);
547    return 1;
548}
549
550int
551httpSpecialClientSideHandler(int status,
552                             FdEventHandlerPtr event,
553                             StreamRequestPtr srequest)
554{
555    HTTPConnectionPtr connection = srequest->data;
556    HTTPRequestPtr request = connection->request;
557    int push;
558
559    if((request->object->flags & OBJECT_ABORTED) ||
560       !(request->object->flags & OBJECT_INPROGRESS)) {
561        httpClientDiscardBody(connection);
562        httpClientError(request, 503, internAtom("Post aborted"));
563        return 1;
564    }
565       
566    if(status < 0) {
567        do_log_error(L_ERROR, -status, "Reading from client");
568        if(status == -EDOGRACEFUL)
569            httpClientFinish(connection, 1);
570        else
571            httpClientFinish(connection, 2);
572        return 1;
573    }
574
575    push = MIN(srequest->offset - connection->reqlen,
576               connection->bodylen - connection->reqoffset);
577    if(push > 0) {
578        connection->reqlen += push;
579        httpSpecialDoSide(request);
580    }
581
582    do_log(L_ERROR, "Incomplete client request.\n");
583    connection->flags &= ~CONN_READER;
584    httpClientRawError(connection, 502,
585                       internAtom("Incomplete client request"), 1);
586    return 1;
587}
588
589int
590httpSpecialDoSideFinish(AtomPtr data, HTTPRequestPtr requestor)
591{
592    ObjectPtr object = requestor->object;
593
594    if(matchUrl("/polipo/config", object)) {
595        AtomListPtr list = NULL;
596        int i, rc;
597
598        if(disableConfiguration) {
599            abortObject(object, 403, internAtom("Action not allowed"));
600            goto out;
601        }
602
603        list = urlDecode(data->string, data->length);
604        if(list == NULL) {
605            abortObject(object, 400,
606                        internAtom("Couldn't parse variable to set"));
607            goto out;
608        }
609        for(i = 0; i < list->length; i++) {
610            rc = parseConfigLine(list->list[i]->string, NULL, 0, 1);
611            if(rc < 0) {
612                abortObject(object, 400,
613                            rc == -1 ?
614                            internAtom("Couldn't parse variable to set") :
615                            internAtom("Variable is not settable"));
616                destroyAtomList(list);
617                goto out;
618            }
619        }
620        destroyAtomList(list);
621        object->date = current_time.tv_sec;
622        object->age = current_time.tv_sec;
623        object->headers = internAtom("\r\nLocation: /polipo/config?");
624        object->code = 303;
625        object->message = internAtom("Done");
626        object->flags &= ~OBJECT_INITIAL;
627        object->length = 0;
628    } else if(matchUrl("/polipo/status", object)) {
629        AtomListPtr list = NULL;
630        int i;
631
632        if(disableConfiguration) {
633            abortObject(object, 403, internAtom("Action not allowed"));
634            goto out;
635        }
636
637        list = urlDecode(data->string, data->length);
638        if(list == NULL) {
639            abortObject(object, 400,
640                        internAtom("Couldn't parse action"));
641            goto out;
642        }
643        for(i = 0; i < list->length; i++) {
644            char *equals =
645                memchr(list->list[i]->string, '=', list->list[i]->length);
646            AtomPtr name =
647                equals ?
648                internAtomN(list->list[i]->string,
649                            equals - list->list[i]->string) :
650                retainAtom(list->list[i]);
651            if(name == atomInitForbidden)
652                initForbidden();
653            else if(name == atomReopenLog)
654                reopenLog();
655            else if(name == atomDiscardObjects)
656                discardObjects(1, 0);
657            else if(name == atomWriteoutObjects)
658                writeoutObjects(1);
659            else if(name == atomFreeChunkArenas)
660                free_chunk_arenas();
661            else {
662                abortObject(object, 400, internAtomF("Unknown action %s",
663                                                     name->string));
664                releaseAtom(name);
665                destroyAtomList(list);
666                goto out;
667            }
668            releaseAtom(name);
669        }
670        destroyAtomList(list);
671        object->date = current_time.tv_sec;
672        object->age = current_time.tv_sec;
673        object->headers = internAtom("\r\nLocation: /polipo/status?");
674        object->code = 303;
675        object->message = internAtom("Done");
676        object->flags &= ~OBJECT_INITIAL;
677        object->length = 0;
678    } else {
679        abortObject(object, 405, internAtom("Method not allowed"));
680    }
681
682 out:
683    releaseAtom(data);
684    notifyObject(object);
685    requestor->connection->flags &= ~CONN_READER;
686    return 1;
687}
688
689int
690isPACCheck(ObjectPtr object)
691{
692    char *testUrl;
693    printf("isPACCheck\n");
694   
695    if(disableOfflineSupport == 1)
696        return 0;
697       
698    /* object->key doesn't use a null terminator, which
699       strstr requires; copy it over and add a null
700       terminator. */
701    testUrl = (char *)malloc((unsigned)(object->key_size + 1));
702    memcpy(testUrl, object->key, object->key_size);
703    testUrl[object->key_size] = '\0';
704    printf("testUrl=%s\n", testUrl);
705    if(strstr(testUrl, "pac_check.txt") != NULL)
706        return 1;
707    else
708        return 0;
709}
710
711#ifdef HAVE_FORK
712static void
713fillSpecialObject(ObjectPtr object, void (*fn)(FILE*, char*), void* closure)
714{
715    int rc;
716    int filedes[2];
717    pid_t pid;
718    sigset_t ss, old_mask;
719
720    if(object->flags & OBJECT_INPROGRESS)
721        return;
722
723    rc = pipe(filedes);
724    if(rc < 0) {
725        do_log_error(L_ERROR, errno, "Couldn't create pipe");
726        abortObject(object, 503,
727                    internAtomError(errno, "Couldn't create pipe"));
728        return;
729    }
730
731    fflush(stdout);
732    fflush(stderr);
733    fflush(logF);
734
735    /* Block signals that we handle specially until the child can
736       disable the handlers. */
737    interestingSignals(&ss);
738    /* I'm a little confused.  POSIX doesn't allow EINTR here, but I
739       think that both Linux and SVR4 do. */
740    do {
741        rc = sigprocmask(SIG_BLOCK, &ss, &old_mask);
742    } while (rc < 0 && errno == EINTR);
743    if(rc < 0) {
744        do_log_error(L_ERROR, errno, "Sigprocmask failed");
745        abortObject(object, 503, internAtomError(errno, "Sigprocmask failed"));
746        close(filedes[0]);
747        close(filedes[1]);
748        return;
749    }
750   
751    pid = fork();
752    if(pid < 0) {
753        do_log_error(L_ERROR, errno, "Couldn't fork");
754        abortObject(object, 503, internAtomError(errno, "Couldn't fork"));
755        close(filedes[0]);
756        close(filedes[1]);
757        do {
758            rc = sigprocmask(SIG_SETMASK, &old_mask, NULL);
759        } while (rc < 0 && errno == EINTR);
760        if(rc < 0) {
761            do_log_error(L_ERROR, errno, "Couldn't restore signal mask");
762            polipoExit();
763        }
764        return;
765    }
766
767    if(pid > 0) {
768        SpecialRequestPtr request;
769        close(filedes[1]);
770        do {
771            rc = sigprocmask(SIG_SETMASK, &old_mask, NULL);
772        } while (rc < 0 && errno == EINTR);
773        if(rc < 0) {
774            do_log_error(L_ERROR, errno, "Couldn't restore signal mask");
775            polipoExit();
776            return;
777        }
778
779        request = malloc(sizeof(SpecialRequestRec));
780        if(request == NULL) {
781            kill(pid, SIGTERM);
782            close(filedes[0]);
783            abortObject(object, 503,
784                        internAtom("Couldn't allocate request\n"));
785            notifyObject(object);
786            /* specialRequestHandler will take care of the rest. */
787        } else {
788            request->buf = get_chunk();
789            if(request->buf == NULL) {
790                kill(pid, SIGTERM);
791                close(filedes[0]);
792                free(request);
793                abortObject(object, 503,
794                            internAtom("Couldn't allocate request\n"));
795                notifyObject(object);
796            }
797        }
798        object->flags |= OBJECT_INPROGRESS;
799        retainObject(object);
800        request->object = object;
801        request->fd = filedes[0];
802        request->pid = pid;
803        request->offset = 0;
804        /* Under any sensible scheduler, the child will run before the
805           parent.  So no need for IO_NOTNOW. */
806        do_stream(IO_READ, filedes[0], 0, request->buf, CHUNK_SIZE,
807                  specialRequestHandler, request);
808    } else {
809        /* child */
810        close(filedes[0]);
811        uninitEvents();
812        do {
813            rc = sigprocmask(SIG_SETMASK, &old_mask, NULL);
814        } while (rc < 0 && errno == EINTR);
815        if(rc < 0)
816            exit(1);
817
818        if(filedes[1] != 1)
819            dup2(filedes[1], 1);
820
821        (*fn)(stdout, closure);
822        exit(0);
823    }
824}
825
826int
827specialRequestHandler(int status,
828                      FdEventHandlerPtr event, StreamRequestPtr srequest)
829{
830    SpecialRequestPtr request = srequest->data;
831    int rc;
832    int killed = 0;
833
834    if(status < 0) {
835        kill(request->pid, SIGTERM);
836        killed = 1;
837        request->object->flags &= ~OBJECT_INPROGRESS;
838        abortObject(request->object, 502,
839                    internAtomError(-status, "Couldn't read from client"));
840        goto done;
841    }
842
843    if(srequest->offset > 0) {
844        rc = objectAddData(request->object, request->buf,
845                           request->offset, srequest->offset);
846        if(rc < 0) {
847            kill(request->pid, SIGTERM);
848            killed = 1;
849            request->object->flags &= ~OBJECT_INPROGRESS;
850            abortObject(request->object, 503,
851                        internAtom("Couldn't add data to connection"));
852            goto done;
853        }
854        request->offset += srequest->offset;
855    }
856    if(status) {
857        request->object->flags &= ~OBJECT_INPROGRESS;
858        request->object->length = request->object->size;
859        goto done;
860    }
861
862    /* If we're the only person interested in this object, let's abort
863       it now. */
864    if(request->object->refcount <= 1) {
865        kill(request->pid, SIGTERM);
866        killed = 1;
867        request->object->flags &= ~OBJECT_INPROGRESS;
868        abortObject(request->object, 500, internAtom("Aborted"));
869        goto done;
870    }
871    notifyObject(request->object);
872    do_stream(IO_READ | IO_NOTNOW, request->fd, 0, request->buf, CHUNK_SIZE,
873              specialRequestHandler, request);
874    return 1;
875
876 done:
877    close(request->fd);
878    dispose_chunk(request->buf);
879    releaseNotifyObject(request->object);
880    /* That's a blocking wait.  It shouldn't block for long, as we've
881       either already killed the child, or else we got EOF from it. */
882    do {
883        rc = waitpid(request->pid, &status, 0);
884    } while(rc < 0 && errno == EINTR);
885    if(rc < 0) {
886        do_log(L_ERROR, "Wait for %d: %d\n", (int)request->pid, errno);
887    } else {
888        int normal =
889            (WIFEXITED(status) && WEXITSTATUS(status) == 0) ||
890            (killed && WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM);
891        char *reason =
892            WIFEXITED(status) ? "with status" :
893            WIFSIGNALED(status) ? "on signal" :
894            "with unknown status";
895        int value =
896            WIFEXITED(status) ? WEXITSTATUS(status) :
897            WIFSIGNALED(status) ? WTERMSIG(status) :
898            status;
899        do_log(normal ? D_CHILD : L_ERROR,
900               "Child %d exited %s %d.\n",
901               (int)request->pid, reason, value);
902    }
903    free(request);
904    return 1;
905}
906#else
907static void
908fillSpecialObject(ObjectPtr object, void (*fn)(FILE*, char*), void* closure)
909{
910    FILE *tmp = NULL;
911    char *buf = NULL;
912    int rc, len, offset;
913
914    if(object->flags & OBJECT_INPROGRESS)
915        return;
916
917    buf = get_chunk();
918    if(buf == NULL) {
919        abortObject(object, 503, internAtom("Couldn't allocate chunk"));
920        goto done;
921    }
922
923    tmp = tmpfile();
924    if(tmp == NULL) {
925        abortObject(object, 503, internAtom(pstrerror(errno)));
926        goto done;
927    }
928
929    (*fn)(tmp, closure);
930    fflush(tmp);
931
932    rewind(tmp);
933    offset = 0;
934    while(1) {
935        len = fread(buf, 1, CHUNK_SIZE, tmp);
936        if(len <= 0 && ferror(tmp)) {
937            abortObject(object, 503, internAtom(pstrerror(errno)));
938            goto done;
939        }
940        if(len <= 0)
941            break;
942
943        rc = objectAddData(object, buf, offset, len);
944        if(rc < 0) {
945            abortObject(object, 503, internAtom("Couldn't add data to object"));
946            goto done;
947        }
948
949        offset += len;
950    }
951
952    object->length = offset;
953
954 done:
955    if(buf)
956        dispose_chunk(buf);
957    if(tmp)
958        fclose(tmp);
959    notifyObject(object);
960}
961#endif
Note: See TracBrowser for help on using the browser.