wwwoffles.c
上传用户:seven77cht
上传日期:2007-01-04
资源大小:486k
文件大小:48k
源码类别:

浏览器

开发平台:

Unix_Linux

  1. /***************************************
  2.   $Header: /home/amb/wwwoffle/RCS/wwwoffles.c 2.141 1999/12/04 11:38:21 amb Exp $
  3.   WWWOFFLE - World Wide Web Offline Explorer - Version 2.5c.
  4.   A server to fetch the required pages.
  5.   ******************/ /******************
  6.   Written by Andrew M. Bishop
  7.   This file Copyright 1996,97,98,99 Andrew M. Bishop
  8.   It may be distributed under the GNU Public License, version 2, or
  9.   any higher version.  See section COPYING of the GNU Public license
  10.   for conditions under which this file may be redistributed.
  11.   ***************************************/
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <sys/types.h>
  16. #include <sys/stat.h>
  17. #include <time.h>
  18. #include <signal.h>
  19. #include <unistd.h>
  20. #include <fcntl.h>
  21. #include "wwwoffle.h"
  22. #include "document.h"
  23. #include "misc.h"
  24. #include "proto.h"
  25. #include "config.h"
  26. #include "sockets.h"
  27. #include "errors.h"
  28. static int ssl_tunnel(int client,URL *Url,Header *request_head);
  29. static void uninstall_sighandlers(void);
  30. /*+ The mode of operation of the server. +*/
  31. typedef enum _Mode
  32. {
  33.  None,                          /*+ Undecided. +*/
  34.  Real,                          /*+ From server host to cache and client. +*/
  35.  RealNoCache,                   /*+ From server host to client. +*/
  36.  RealRefresh,                   /*+ Refresh the page, forced from index. +*/
  37.  RealNoPassword,                /*+ From server host to cache, not using supplied password. +*/
  38.  SpoolOrReal,                   /*+ Spool if already cached else Real. +*/
  39.  Fetch,                         /*+ From server host to cache. +*/
  40.  FetchNoPassword,               /*+ From server host to cache, not using supplied password. +*/
  41.  Spool,                         /*+ From cache to client. +*/
  42.  SpoolGet,                      /*+ Not in cache so record request in outgoing. +*/
  43.  SpoolWillGet,                  /*+ Not in cache but is in outgoing. +*/
  44.  SpoolRefresh,                  /*+ Refresh the page, forced from refresh page. +*/
  45.  SpoolPragma,                   /*+ Refresh the page, forced from browser by 'Pragma: no-cache'. +*/
  46.  SpoolInternal                  /*+ The page is one that has been internally generated by WWWOFFLE. +*/
  47. }
  48. Mode;
  49. /*++++++++++++++++++++++++++++++++++++++
  50.   The main server program.
  51.   int wwwoffles Returns the exit status.
  52.   int online Whether the demon is online or not.
  53.   int browser Set to true if there is a browser.
  54.   int client The file descriptor of the client.
  55.   ++++++++++++++++++++++++++++++++++++++*/
  56. int wwwoffles(int online,int browser,int client)
  57. {
  58.  int outgoing=-1,spool=-1,tmpclient=-1;
  59.  int fetch_again=0;
  60.  char *proxy_auth,*proxy_user;
  61.  char *aliasProto=NULL,*aliasHost=NULL,*aliasPath=NULL;
  62.  Header *request_head=NULL,*reply_head=NULL;
  63.  Body *request_body=NULL,*reply_body=NULL;
  64.  int reply_status=-1,head_only=0;
  65.  char *url;
  66.  URL *Url=NULL,*Urlpw=NULL;
  67.  Mode mode=None;
  68.  int outgoing_exists=0,lasttime_exists=0;
  69.  time_t spool_exists=0,spool_exists_pw=0;
  70.  int offline_request=1;
  71.  char *is_client_wwwoffle=NULL,*is_client_htdig=NULL;
  72.  /* Initialise things. */
  73.  uninstall_sighandlers();
  74.  InitErrorHandler("wwwoffles",-1,-1); /* change name nothing else */
  75.  if(online==1 && browser)
  76.     mode=Real;
  77.  else if(online==1 && !browser)
  78.     mode=Fetch;
  79.  else if(online==-1 && browser)
  80.     mode=SpoolOrReal;
  81.  else if(!online && browser)
  82.     mode=Spool;
  83.  else
  84.     PrintMessage(Fatal,"Started in a mode that is not allowed (online=%d, browser=%d).",online,browser);
  85.  /* mode = Spool, Real, SpoolOrReal or Fetch */
  86.  /* Set up the client file. */
  87.  if(client==-1 && mode!=Fetch)
  88.     PrintMessage(Fatal,"Cannot use client file descriptor %d.",client);
  89.  if(mode!=Fetch)
  90.    {
  91.     tmpclient=CreateTempSpoolFile();
  92.     if(tmpclient==-1)
  93.       {
  94.        PrintMessage(Warning,"Cannot open temporary spool file [%!s].");
  95.        HTMLMessage(client,500,"WWWOFFLE Server Error",NULL,"ServerError",
  96.                    "error","Cannot open temporary file.",
  97.                    NULL);
  98.        exit(1);
  99.       }
  100.    }
  101.  /* Set up the outgoing file. */
  102.  if(mode==Fetch)
  103.    {
  104.     outgoing=OpenOutgoingSpoolFile(1);
  105.     init_buffer(outgoing);
  106.     if(outgoing==-1)
  107.       {PrintMessage(Inform,"No more outgoing requests.");exit(3);}
  108.    }
  109.  /* Get the URL from the request. */
  110.  if(mode==Real || mode==Spool || mode==SpoolOrReal)
  111.     url=ParseRequest(client,&request_head,&request_body);
  112.  else /* mode==Fetch */
  113.     url=ParseRequest(outgoing,&request_head,&request_body);
  114.  if(DebugLevel==ExtraDebug)
  115.    {
  116.     if(request_head)
  117.        PrintMessage(ExtraDebug,"Incoming Request Head (from browser)n%s",HeaderString(request_head));
  118.     else
  119.        PrintMessage(ExtraDebug,"Incoming Request Head (from browser) is empty");
  120.     if(request_body)
  121.        if(strcmp(request_head->method,"POST")) /* only POST is guaranteed to be ASCII. */
  122.           PrintMessage(ExtraDebug,"Incoming Request Body (from browser) is %d bytes of binary datan",request_body->length);
  123.        else
  124.           PrintMessage(ExtraDebug,"Incoming Request Body (from browser)n%s",request_body->content);
  125.    }
  126.  if(!url)
  127.    {
  128.     PrintMessage(Warning,"Could not parse HTTP request (%s).",request_head?"Parse error":"Empty request");
  129.     if(mode!=Fetch)
  130.        HTMLMessage(client,500,"WWWOFFLE Server Error",NULL,"ServerError",
  131.                    "error",request_head?"Cannot parse the HTTP request":"The HTTP request was empty",
  132.                    NULL);
  133.     exit(1);
  134.    }
  135.  Url=SplitURL(url);
  136.  /* Authenticate the user with the proxy */
  137.  proxy_auth=GetHeader(request_head,"Proxy-Authorization",NULL);
  138.  proxy_user=IsAllowedConnectUser(proxy_auth);
  139.  if(!proxy_user)
  140.    {
  141.     PrintMessage(Inform,"HTTP Proxy connection rejected from unauthenticated user."); /* Used in audit-usage.pl */
  142.     HTMLMessageHead(tmpclient,407,"WWWOFFLE Proxy Authentication Required",
  143.                     "Proxy-Authenticate","Basic realm="wwwoffle-proxy"",
  144.                     NULL);
  145.     HTMLMessageBody(tmpclient,"ProxyAuthFail",
  146.                     NULL);
  147.     mode=SpoolInternal; goto spoolinternal;
  148.    }
  149.  else if(proxy_auth)
  150.     PrintMessage(Inform,"HTTP Proxy connection from user '%s'.",proxy_user); /* Used in audit-usage.pl */
  151.  /* Check the HTTP method that is requested */
  152.  if(!strcasecmp(request_head->method,"CONNECT"))
  153.    {
  154.     PrintMessage(Inform,"SSL='%s'.",Url->host); /* Used in audit-usage.pl */
  155.     if(mode==Real || mode==SpoolOrReal || (mode==Spool && IsLocalNetHost(Url->host)))
  156.       {
  157.        if(IsSSLAllowedPort(Url->host))
  158.          {
  159.           int ret=ssl_tunnel(client,Url,request_head);
  160.           exit(ret);
  161.          }
  162.        else
  163.          {
  164.           PrintMessage(Warning,"A SSL proxy connection for %s was received but is not allowed.",Url->host);
  165.           HTMLMessage(tmpclient,500,"WWWOFFLE Server Error",NULL,"ServerError",
  166.                       "error","SSL proxy connection to specified port is not allowed.",
  167.                       NULL);
  168.           mode=SpoolInternal; goto spoolinternal;
  169.          }
  170.       }
  171.     else /* mode==Fetch || mode==Spool */
  172.       {
  173.        PrintMessage(Warning,"A SSL proxy connection for %s was received but wwwoffles is in wrong mode.",Url->host);
  174.        if(mode==Fetch)
  175.          {
  176.           if(client!=-1)
  177.              write_formatted(client,"Cannot fetch %s [HTTP method 'CONNECT' not supported in this mode]n",Url->name);
  178.           exit(1);
  179.          }
  180.        else
  181.          {
  182.           HTMLMessage(tmpclient,500,"WWWOFFLE Server Error",NULL,"ServerError",
  183.                       "error","SSL proxy connection while offline is not allowed.",
  184.                       NULL);
  185.           mode=SpoolInternal; goto spoolinternal;
  186.          }
  187.       }
  188.    }
  189.  else if(strcmp(request_head->method,"GET") &&
  190.          strcmp(request_head->method,"HEAD") &&
  191.          strcmp(request_head->method,"POST") &&
  192.          strcmp(request_head->method,"PUT"))
  193.    {
  194.     PrintMessage(Warning,"The requested method '%s' is not supported.",request_head->method);
  195.     if(mode==Fetch)
  196.       {
  197.        if(client!=-1)
  198.           write_formatted(client,"Cannot fetch %s [The %s method is not supported]n",Url->name,request_head->method);
  199.        exit(1);
  200.       }
  201.     else
  202.       {
  203.        HTMLMessage(tmpclient,501,"WWWOFFLE Method Unsupported",NULL,"MethodUnsupported",
  204.                    "method",request_head->method,
  205.                    "protocol","all",
  206.                    NULL);
  207.        mode=SpoolInternal; goto spoolinternal;
  208.       }
  209.    }
  210.  else if(!strcmp(request_head->method,"HEAD"))
  211.    {
  212.     strcpy(request_head->method,"GET");
  213.     request_head->size-=1;
  214.     head_only=1;
  215.    }
  216.  PrintMessage(Inform,"URL='%s'%s.",Url->name,Url->user?" (With username/password)":""); /* Used in audit-usage.pl */
  217.  PrintMessage(Debug,"proto='%s'; host='%s'; path='%s'; args='%s'; user:pass='%s:%s'.",
  218.               Url->proto,Url->host,Url->path,Url->args,Url->user,Url->pass);
  219.  /* Check for an alias. */
  220.  if(IsAliased(Url->proto,Url->host,Url->path,&aliasProto,&aliasHost,&aliasPath))
  221.    {
  222.     URL *newUrl;
  223.     char *newurl=(char*)malloc(strlen(aliasProto)+strlen(aliasHost)+strlen(aliasPath)-
  224.                                strlen(Url->path)+strlen(Url->pathp)+8);
  225.     sprintf(newurl,"%s://%s%s%s",aliasProto,aliasHost,aliasPath,Url->pathp+strlen(Url->path));
  226.     newUrl=SplitURL(newurl);
  227.     if(Url->user)
  228.        AddURLPassword(newUrl,Url->user,Url->pass);
  229.     url=newurl;
  230.     FreeURL(Url);
  231.     Url=newUrl;
  232.     PrintMessage(Inform,"Aliased URL='%s'%s.",newUrl->name,newUrl->user?" (With username/password)":"");
  233.     PrintMessage(Debug,"Aliased proto='%s'; host='%s'; path='%s'; args='%s'; user:pass='%s:%s'.",
  234.                  newUrl->proto,newUrl->host,newUrl->path,newUrl->args,newUrl->user,newUrl->pass);
  235.    }
  236.  /* Initial sanity checks. */
  237.  if(IsNotGot(Url->proto,Url->host,Url->path,Url->args))
  238.    {
  239.     char *replace = NotGotReplacement(Url->proto,Url->host,Url->path,Url->args);
  240.     if(!replace)
  241.       {
  242.        PrintMessage(Inform,"The server '%s://%s' and/or path '%s' is on the list not to get.",Url->proto,Url->host,Url->path);
  243.        if(mode==Fetch)
  244.           exit(0);
  245.        else
  246.          {
  247.           HTMLMessage(tmpclient,404,"WWWOFFLE Host Not Got",NULL,"HostNotGot",
  248.                       "url",Url->name,
  249.                       NULL);
  250.           mode=SpoolInternal; goto spoolinternal;
  251.          }
  252.       }
  253.     else
  254.       {
  255.        char *newurl=(char*)malloc(strlen(replace)+1);
  256.        URL *newUrl=SplitURL(replace);
  257.        strcpy(newurl,replace);
  258.        url=newurl;
  259.        FreeURL(Url);
  260.        Url=newUrl;
  261.        PrintMessage(Inform,"Replaced URL='%s'%s.",newUrl->name,newUrl->user?" (With username/password)":"");
  262.        PrintMessage(Debug,"Replaced proto='%s'; host='%s'; path='%s'; args='%s'; user:pass='%s:%s'.",
  263.                     newUrl->proto,newUrl->host,newUrl->path,newUrl->args,newUrl->user,newUrl->pass);
  264.       }
  265.    }
  266.  if(!Url->Protocol)
  267.    {
  268.     PrintMessage(Inform,"The protocol '%s' is not available.",Url->proto);
  269.     if(mode==Fetch)
  270.       {
  271.        if(client!=-1)
  272.           write_formatted(client,"Cannot fetch %s [Protocol not available]n",Url->name);
  273.        exit(1);
  274.       }
  275.     else
  276.       {
  277.        HTMLMessage(tmpclient,404,"WWWOFFLE Illegal Protocol",NULL,"IllegalProtocol",
  278.                    "url",Url->name,
  279.                    "protocol",Url->proto,
  280.                    NULL);
  281.        mode=SpoolInternal; goto spoolinternal;
  282.       }
  283.    }
  284.  if((!strcmp(request_head->method,"POST") && !Url->Protocol->postable) ||
  285.     (!strcmp(request_head->method,"PUT") && !Url->Protocol->putable))
  286.    {
  287.     PrintMessage(Warning,"The requested method '%s' is not supported for the %s protocol.",request_head->method,Url->Protocol->name);
  288.     if(mode==Fetch)
  289.       {
  290.        if(client!=-1)
  291.           write_formatted(client,"Cannot fetch %s [The %s method is not supported for %s]n",Url->name,request_head->method,Url->Protocol->name);
  292.        exit(1);
  293.       }
  294.     else
  295.       {
  296.        HTMLMessage(tmpclient,501,"WWWOFFLE Method Unsupported",NULL,"MethodUnsupported",
  297.                    "method",request_head->method,
  298.                    "protocol",Url->Protocol->name,
  299.                    NULL);
  300.        mode=SpoolInternal; goto spoolinternal;
  301.       }
  302.    }
  303.  /* Change the mode based on the URL as required. */
  304.  /* mode = Spool, Real, SpoolOrReal or Fetch */
  305.  /* A local URL. */
  306.  if(Url->local)
  307.    {
  308.     if(!strncmp("/refresh",Url->path,8))
  309.       {
  310.        int recurse=0;
  311.        char *newurl=RefreshPage(tmpclient,Url,request_body,&recurse);
  312.        if(!newurl)
  313.          {mode=SpoolInternal; goto spoolinternal;}
  314.        else
  315.          {
  316.           URL *newUrl=SplitURL(newurl);
  317.           if(Url->user)
  318.              AddURLPassword(newUrl,Url->user,Url->pass);
  319.           if(newUrl->args && *newUrl->args=='!')
  320.             {
  321.              PrintMessage(Inform,"It is not possible to refresh a URL that used the POST/PUT method.");
  322.              if(mode==Fetch)
  323.                {
  324.                 if(client!=-1)
  325.                    write_formatted(client,"Cannot fetch %s [Reply from a POST/PUT method]n",Url->name);
  326.                 exit(1);
  327.                }
  328.              else
  329.                {
  330.                 HTMLMessage(tmpclient,404,"WWWOFFLE Cant Refresh POST/PUT",NULL,"CantRefreshPosted",
  331.                             "url",newUrl->name,
  332.                             NULL);
  333.                 mode=SpoolInternal; goto spoolinternal;
  334.                }
  335.             }
  336.           else if(recurse)
  337.             {
  338.              fetch_again=1;
  339.              if(mode!=Fetch)
  340.                {
  341.                 mode=SpoolGet;
  342.                 if(!Url->args || *Url->args!='!')
  343.                   {
  344.                    free(newurl);
  345.                    FreeURL(newUrl);
  346.                    newurl=(char*)malloc(strlen(url)+1);
  347.                    strcpy(newurl,url);
  348.                    newUrl=SplitURL(newurl);
  349.                    if(Url->user)
  350.                       AddURLPassword(newUrl,Url->user,Url->pass);
  351.                   }
  352.                 FreeHeader(request_head);
  353.                 request_head=RequestURL(newUrl,NULL);
  354.                 if(request_body)
  355.                    FreeBody(request_body);
  356.                 request_body=NULL;
  357.                }
  358.             }
  359.           else
  360.             {
  361.              if(mode==Spool)
  362.                 mode=SpoolRefresh;
  363.              else
  364.                {
  365.                 DeleteWebpageSpoolFile(newUrl,0);
  366.                 mode=RealRefresh;
  367.                }
  368.             }
  369.           url=newurl;
  370.           FreeURL(Url);
  371.           Url=newUrl;
  372.           PrintMessage(Inform,"Refresh URL='%s'%s.",newUrl->name,newUrl->user?" (With username/password)":"");
  373.           PrintMessage(Debug,"Refresh proto='%s'; host='%s'; path='%s'; args='%s'; user:pass='%s:%s'.",
  374.                        newUrl->proto,newUrl->host,newUrl->path,newUrl->args,newUrl->user,newUrl->pass);
  375.          }
  376.       }
  377.     else if(mode==Fetch)
  378.       {
  379.        PrintMessage(Inform,"The request to fetch a page from the local host is ignored.");
  380.        if(client!=-1)
  381.           write_formatted(client,"Cannot fetch %s [On local host]n",Url->name);
  382.        exit(1);
  383.       }
  384.     else if(!strncmp("/index/",Url->path,7))
  385.       {
  386.        IndexPage(tmpclient,Url);
  387.        mode=SpoolInternal; goto spoolinternal;
  388.       }
  389.     else if(!strncmp("/control/",Url->path,9))
  390.       {
  391.        ControlPage(tmpclient,Url,request_head,request_body);
  392.        mode=SpoolInternal; goto spoolinternal;
  393.       }
  394.     else if(!strncmp("/monitor",Url->path,8))
  395.       {
  396.        MonitorPage(tmpclient,Url,request_body);
  397.        mode=SpoolInternal; goto spoolinternal;
  398.       }
  399.     else if((!strchr(Url->path+1,'/') || !strncmp("/local/",Url->path,7)) && !Url->args)
  400.       {
  401.        LocalPage(tmpclient,Url->path,request_head);
  402.        mode=SpoolInternal; goto spoolinternal;
  403.       }
  404.     else if(!strncmp("/htdig/",Url->path,7))
  405.       {
  406.        HTDigPage(tmpclient,Url,request_head);
  407.        mode=SpoolInternal; goto spoolinternal;
  408.       }
  409.     else
  410.       {
  411.        int i;
  412.        for(i=0;i<NProtocols;i++)
  413.           if(!strncmp(Protocols[i].name,Url->pathp+1,strlen(Protocols[i].name)) &&
  414.              Url->pathp[strlen(Protocols[i].name)+1]=='/')
  415.             {
  416.              url=(char*)malloc(strlen(Url->pathp)+4);
  417.              Url->pathp[strlen(Protocols[i].name)+1]=0;
  418.              sprintf(url,"%s://%s",Url->pathp+1,&Url->pathp[strlen(Protocols[i].name)+2]);
  419.              Url->pathp[strlen(Protocols[i].name)+1]='/';
  420.              break;
  421.             }
  422.        if(i==NProtocols)
  423.          {
  424.           PrintMessage(Inform,"The requested URL '%s' does not exist on the local server.",Url->pathp);
  425.           HTMLMessage(tmpclient,404,"WWWOFFLE Page Not Found",NULL,"PageNotFound",
  426.                       "url",Url->name,
  427.                       NULL);
  428.           mode=SpoolInternal; goto spoolinternal;
  429.          }
  430.        else
  431.          {
  432.           FreeURL(Url);
  433.           Url=SplitURL(url);
  434.          }
  435.       }
  436.     /* Repeat initial sanity checks. */
  437.     if(IsNotGot(Url->proto,Url->host,Url->path,Url->args))
  438.       {
  439.        char *replace = NotGotReplacement(Url->proto,Url->host,Url->path,Url->args);
  440.        if(!replace)
  441.          {
  442.           PrintMessage(Inform,"The server '%s://%s' and/or path '%s' is on the list not to get.",Url->proto,Url->host,Url->path);
  443.           if(mode==Fetch)
  444.              exit(0);
  445.           else
  446.             {
  447.              HTMLMessage(tmpclient,404,"WWWOFFLE Host Not Got",NULL,"HostNotGot",
  448.                          "url",Url->name,
  449.                          NULL);
  450.              mode=SpoolInternal; goto spoolinternal;
  451.             }
  452.          }
  453.        else
  454.          {
  455.           char *newurl=(char*)malloc(strlen(replace)+1);
  456.           URL *newUrl=SplitURL(replace);
  457.           strcpy(newurl,replace);
  458.           url=newurl;
  459.           FreeURL(Url);
  460.           Url=newUrl;
  461.           PrintMessage(Inform,"Replaced URL='%s'%s.",newUrl->name,newUrl->user?" (With username/password)":"");
  462.           PrintMessage(Debug,"Replaced proto='%s'; host='%s'; path='%s'; args='%s'; user:pass='%s:%s'.",
  463.                        newUrl->proto,newUrl->host,newUrl->path,newUrl->args,newUrl->user,newUrl->pass);
  464.          }
  465.       }
  466.     if(!Url->Protocol)
  467.       {
  468.        PrintMessage(Inform,"The protocol '%s' is not available.",Url->proto);
  469.        if(mode==Fetch)
  470.          {
  471.           if(client!=-1)
  472.              write_formatted(client,"Cannot fetch %s [Protocol not available]n",Url->name);
  473.           exit(1);
  474.          }
  475.        else
  476.          {
  477.           HTMLMessage(tmpclient,404,"WWWOFFLE Illegal Protocol",NULL,"IllegalProtocol",
  478.                       "url",Url->name,
  479.                       "protocol",Url->proto,
  480.                       NULL);
  481.           mode=SpoolInternal; goto spoolinternal;
  482.          }
  483.       }
  484.     if((!strcmp(request_head->method,"POST") && !Url->Protocol->postable) ||
  485.        (!strcmp(request_head->method,"PUT") && !Url->Protocol->putable))
  486.       {
  487.        PrintMessage(Warning,"The requested method '%s' is not supported for the %s protocol.",request_head->method,Url->Protocol->name);
  488.        if(mode==Fetch)
  489.          {
  490.           if(client!=-1)
  491.              write_formatted(client,"Cannot fetch %s [The %s method is not supported for %s]n",Url->name,request_head->method,Url->Protocol->name);
  492.           exit(1);
  493.          }
  494.        else
  495.          {
  496.           HTMLMessage(tmpclient,501,"WWWOFFLE Method Unsupported",NULL,"MethodUnsupported",
  497.                       "method",request_head->method,
  498.                       "protocol",Url->Protocol->name,
  499.                       NULL);
  500.           mode=SpoolInternal; goto spoolinternal;
  501.          }
  502.       }
  503.    }
  504.  else if(mode==Fetch)
  505.     ParseRecurseOptions(NULL);
  506.  /* mode = Spool, SpoolGet, SpoolRefresh, Real, RealRefresh, SpoolOrReal or Fetch */
  507.  /* Check for a username / password */
  508.  if(Url->user)
  509.    {
  510.     URL *new=SplitURL(Url->name);
  511.     Urlpw=Url;
  512.     Url=new;
  513.     if(!Urlpw->pass)
  514.       {
  515.        if(mode==Fetch)
  516.          {
  517.           FreeURL(Urlpw);
  518.           Urlpw=NULL;
  519.          }
  520.        else
  521.          {
  522.           HTMLMessage(tmpclient,403,"WWWOFFLE Username Needs Password",NULL,"UserNeedsPass",
  523.                       "url",Urlpw->name,
  524.                       "user",Urlpw->user,
  525.                       NULL);
  526.           mode=SpoolInternal; goto spoolinternal;
  527.          }
  528.       }
  529.    }
  530.  /* Check for an existing cached version */
  531.  outgoing_exists=ExistsOutgoingSpoolFile(Url);
  532.  spool_exists=ExistsWebpageSpoolFile(Url);
  533.  if(Urlpw)
  534.    {
  535.     spool_exists_pw=ExistsWebpageSpoolFile(Urlpw);
  536.     if((mode==Spool || mode==SpoolGet || mode==SpoolOrReal) && spool_exists)
  537.       {
  538.        if(SpooledPageStatus(Url)==401)
  539.          {FreeURL(Url);Url=Urlpw;Urlpw=NULL;
  540.          spool_exists=spool_exists_pw;spool_exists_pw=0;}
  541.        else
  542.          {FreeURL(Urlpw);Urlpw=NULL;
  543.          spool_exists_pw=0;}
  544.       }
  545.     else if(mode==Spool || mode==SpoolGet || mode==SpoolOrReal) /* && ! spool_exists */
  546.       {
  547.        if(spool_exists_pw)
  548.          {
  549.           int new_outgoing=OpenOutgoingSpoolFile(0);
  550.           if(new_outgoing==-1)
  551.              PrintMessage(Warning,"Cannot open the new outgoing request to write.");
  552.           else
  553.             {
  554.              char *head=HeaderString(request_head);
  555.              write_string(new_outgoing,head);
  556.              if(request_body)
  557.                 write_data(new_outgoing,request_body->content,request_body->length);
  558.              CloseOutgoingSpoolFile(new_outgoing,Url);
  559.              free(head);
  560.             }
  561.          }
  562.        FreeURL(Url);Url=Urlpw;Urlpw=NULL;
  563.        spool_exists=spool_exists_pw;spool_exists_pw=0;
  564.       }
  565.     else if((mode==Fetch || mode==Real) && spool_exists)
  566.       {
  567.        if(SpooledPageStatus(Url)==401)
  568.          {FreeURL(Url);Url=Urlpw;Urlpw=NULL;
  569.          spool_exists=spool_exists_pw;spool_exists_pw=0;}
  570.        else
  571.          {FreeURL(Urlpw);Urlpw=NULL;
  572.          spool_exists_pw=0;}
  573.       }
  574.    }
  575.  /* If mode is Real or Fetch and a password is set then get page with
  576.     no password then come here and try again with a password. */
  577. passwordagain:
  578.  /* Check if it needs to be cached. */
  579.  if(IsLocalNetHost(Url->host))
  580.    {
  581.     if(mode==Real || mode==Spool || mode==SpoolOrReal)
  582.        mode=RealNoCache;
  583.     else if(mode==Fetch)
  584.       {
  585.        PrintMessage(Inform,"The request to fetch a page from the local network host '%s' is ignored.",Url->host);
  586.        if(client!=-1)
  587.           write_formatted(client,"Cannot fetch %s [On local network]n",Url->name);
  588.        exit(1);
  589.       }
  590.    }
  591.  else if((mode==Real || mode==SpoolOrReal) && IsNotCached(Url->proto,Url->host,Url->path,Url->args))
  592.    {
  593.     mode=RealNoCache;
  594.    }
  595.  if(mode==RealNoCache && Urlpw)
  596.    {FreeURL(Url);Url=Urlpw;Urlpw=NULL;
  597.    spool_exists=spool_exists_pw;spool_exists_pw=0;}
  598.  /* mode = Spool, SpoolGet, SpoolRefresh, Real, RealRefresh, RealNoCache, SpoolOrReal or Fetch */
  599.  /* Check if it was a POST/PUT method. */
  600.  if(!strcmp(request_head->method,"POST") ||
  601.     !strcmp(request_head->method,"PUT"))
  602.    {
  603.     if(mode==Spool)
  604.        mode=SpoolGet;
  605.    }
  606.  else if(Url->args && *Url->args=='!')
  607.    {
  608.     if(mode==Fetch)
  609.       {
  610.        PrintMessage(Inform,"It is not possible to fetch a URL that used the POST/PUT method.");
  611.        if(client!=-1)
  612.           write_formatted(client,"Cannot fetch %s [Reply from a POST/PUT method]n",Url->name);
  613.        exit(1);
  614.       }
  615.     else if(!spool_exists && !outgoing_exists)
  616.       {
  617.        PrintMessage(Inform,"It is not possible to request a URL that used the POST/PUT method.");
  618.        HTMLMessage(tmpclient,404,"WWWOFFLE Cant Refresh POST/PUT",NULL,"CantRefreshPosted",
  619.                    "url",Url->name,
  620.                    NULL);
  621.        mode=SpoolInternal; goto spoolinternal;
  622.       }
  623.     else
  624.        mode=Spool;
  625.    }
  626.  /* Check if it is a conditional request. */
  627.  if(mode!=RealNoCache && GetHeader(request_head,"If-Modified-Since",NULL))
  628.    {
  629.     char *val=GetHeader(request_head,"If-Modified-Since",NULL);
  630.     if(mode==Spool || mode==SpoolOrReal)
  631.       {
  632.        spool=OpenWebpageSpoolFile(1,Url);
  633.        if(spool!=-1)
  634.          {
  635.           Header *spooled_head=NULL;
  636.           char *modified;
  637.           time_t modtime;
  638.           time_t since=DateToTimeT(val);
  639.           init_buffer(spool);
  640.           ParseReply(spool,NULL,&spooled_head);
  641.           if(spooled_head && (modified=GetHeader(spooled_head,"Last-Modified",NULL)))
  642.              modtime=DateToTimeT(modified);
  643.           else
  644.             {
  645.              struct stat buf;
  646.              if(fstat(spool,&buf))
  647.                 modtime=time(NULL)+1;
  648.              else
  649.                 modtime=buf.st_mtime;
  650.             }
  651.           close(spool);
  652.           spool=-1;
  653.           if(spooled_head)
  654.              FreeHeader(spooled_head);
  655.           if(since>=modtime)
  656.             {
  657.              HTMLMessageHead(tmpclient,304,"WWWOFFLE Not Modified",
  658.                              NULL);
  659.              mode=SpoolInternal; goto spoolinternal;
  660.             }
  661.          }
  662.       }
  663.     RemoveFromHeader(request_head,"If-Modified-Since",NULL);
  664.    }
  665.  /* Check if a refresh is needed based on pragma, request changes, autodial. */
  666.  if(mode==Real && ExistsLockWebpageSpoolFile(Url))
  667.    {
  668.     mode=Spool;
  669.    }
  670.  else if(mode==Fetch && ExistsLockWebpageSpoolFile(Url))
  671.    {
  672.     PrintMessage(Debug,"Already fetching URL.");
  673.     if(client!=-1)
  674.        write_formatted(client,"Cannot fetch %s [Already fetching]n",Url->name);
  675.     exit(fetch_again?4:0);
  676.    }
  677.  else if(RefreshForced() || (PragmaNoCache && GetHeader(request_head,"Pragma","no-cache")))
  678.    {
  679.     if(mode==Spool || mode==SpoolGet)
  680.       {
  681.        if(spool_exists)
  682.           mode=SpoolPragma;
  683.        else
  684.           if(outgoing_exists)
  685.              mode=SpoolWillGet;
  686.           else
  687.              mode=SpoolGet;
  688.       }
  689.     else if(mode==SpoolOrReal)
  690.        mode=Real;
  691.     /* (mode==Fetch || mode==Real) are left unchanged, not modified as below. */
  692.    }
  693.  else if(mode==Spool && !spool_exists)
  694.    {
  695.     if(outgoing_exists)
  696.        mode=SpoolWillGet;
  697.     else
  698.        mode=SpoolGet;
  699.    }
  700.  else if(mode==Fetch && spool_exists)
  701.    {
  702.     spool=OpenWebpageSpoolFile(1,Url);
  703.     init_buffer(spool);
  704.     if((RequestChanged>=0 || RequestChangedOnce) && RequestChanges(spool,request_head)==1)
  705.        TouchWebpageSpoolFile(Url,0);
  706.     else if(fetch_again)
  707.       {
  708.        lseek(spool,0,SEEK_SET);
  709.        init_buffer(spool);
  710.        if(ParseDocument(spool,Url))
  711.           RecurseFetch(Url,0);
  712.        exit(4);
  713.       }
  714.     else
  715.        exit(0);
  716.     close(spool);
  717.    }
  718.  else if(mode==Real && spool_exists)
  719.    {
  720.     spool=OpenWebpageSpoolFile(1,Url);
  721.     init_buffer(spool);
  722.     if((RequestChanged>=0 || RequestChangedOnce) && RequestChanges(spool,request_head)==1)
  723.        TouchWebpageSpoolFile(Url,0);
  724.     else
  725.        mode=Spool;
  726.     close(spool);
  727.    }
  728.  else if(mode==SpoolOrReal)
  729.    {
  730.     if(spool_exists)
  731.        mode=Spool;
  732.     else
  733.        mode=Real;
  734.    }
  735.  /* Don't let htdig request any URLs. */
  736.  is_client_htdig=GetHeader(request_head,"User-Agent","htdig/");
  737.  if((mode==SpoolGet || mode==Real) && is_client_htdig)
  738.    {
  739.     PrintMessage(Inform,"URL unavailable to be searched.");
  740.     HTMLMessageHead(tmpclient,404,"WWWOFFLE Not Searched",
  741.                     NULL);
  742.     mode=SpoolInternal; goto spoolinternal;
  743.    }
  744.  /* mode = Spool, SpoolGet, SpoolWillGet, SpoolPragma, SpoolRefresh, Real, RealRefresh, RealNoCache or Fetch */
  745.  /* Set up the file descriptor for the spool file. */
  746.  if(mode==Real || mode==Fetch)
  747.    {
  748.     if(spool_exists)
  749.        CreateBackupWebpageSpoolFile(Url);
  750.     spool=OpenWebpageSpoolFile(0,Url);
  751.     CreateLockWebpageSpoolFile(Url);
  752.     if(spool==-1)
  753.       {
  754.        DeleteLockWebpageSpoolFile(Url);
  755.        PrintMessage(Warning,"Cannot open the spooled web page to write.");
  756.        if(mode==Real)
  757.          {
  758.           HTMLMessage(tmpclient,500,"WWWOFFLE Server Error",NULL,"ServerError",
  759.                       "error","Cannot open the spooled web page to write.",
  760.                       NULL);
  761.           mode=SpoolInternal; goto spoolinternal;
  762.          }
  763.        else /* mode==Fetch */
  764.          {
  765.           if(client!=-1)
  766.              write_formatted(client,"Internal Error %s [Cannot open spool file]n",Url->name);
  767.           exit(1);
  768.          }
  769.       }
  770.     lasttime_exists=CreateLastTimeSpoolFile(Url);
  771.    }
  772.  else if(mode==Spool || mode==SpoolPragma)
  773.    {
  774.     spool=OpenWebpageSpoolFile(1,Url);
  775.     init_buffer(spool);
  776.     if(spool==-1)
  777.       {
  778.        PrintMessage(Warning,"Cannot open the spooled web page to read.");
  779.        HTMLMessage(tmpclient,500,"WWWOFFLE Server Error",NULL,"ServerError",
  780.                    "error","Cannot open the spooled web page to read.",
  781.                    NULL);
  782.        mode=SpoolInternal; goto spoolinternal;
  783.       }
  784.    }
  785.  /* Set up the outgoing file. */
  786.  offline_request=!IsNotRequestedOffline(Url->proto,Url->host,Url->path,Url->args);
  787.  is_client_wwwoffle=GetHeader(request_head,"Pragma","wwwoffle");
  788.  if((offline_request || online) &&
  789.     ((mode==SpoolGet && (!ConfirmRequests || is_client_wwwoffle || Url->local || (Url->args && *Url->args=='!'))) ||
  790.      mode==SpoolRefresh ||
  791.      mode==SpoolPragma))
  792.    {
  793.     outgoing=OpenOutgoingSpoolFile(0);
  794.     if(outgoing==-1)
  795.       {
  796.        PrintMessage(Warning,"Cannot open the outgoing request to write.");
  797.        HTMLMessage(tmpclient,500,"WWWOFFLE Server Error",NULL,"ServerError",
  798.                    "error","Cannot open the outgoing request to write.",
  799.                    NULL);
  800.        mode=SpoolInternal; goto spoolinternal;
  801.       }
  802.    }
  803.  /* Open the connection to the server host. */
  804.  if(mode==Real || mode==RealNoCache || mode==Fetch)
  805.    {
  806.     char *err=(Url->Protocol->open)(Url);
  807.     if(err && ConnectRetry)
  808.       {
  809.        PrintMessage(Inform,"Waiting to try connection again.");
  810.        sleep(10);
  811.        err=(Url->Protocol->open)(Url);
  812.       }
  813.     if(err)
  814.       {
  815.        if(mode==Fetch || mode==Real)
  816.          {
  817.           lseek(spool,0,SEEK_SET);
  818.           ftruncate(spool,0);
  819.           HTMLMessage(spool,503,"WWWOFFLE Remote Host Error",NULL,"RemoteHostError",
  820.                       "url",Url->name,
  821.                       "reason",err,
  822.                       "cache","yes",
  823.                       "backup",spool_exists?"yes":NULL,
  824.                       NULL);
  825.           DeleteLockWebpageSpoolFile(Url);
  826.           close(spool);
  827.          }
  828.        if(mode==Fetch)
  829.          {
  830.           if(client!=-1)
  831.              write_formatted(client,"Fetch Failure %s [Server Connection Failed]n",Url->name);
  832.           exit(1);
  833.          }
  834.        else
  835.          {
  836.           HTMLMessage(tmpclient,503,"WWWOFFLE Remote Host Error",NULL,"RemoteHostError",
  837.                       "url",Url->name,
  838.                       "reason",err,
  839.                       "cache","",
  840.                       "backup",spool_exists?"yes":NULL,
  841.                       NULL);
  842.           mode=SpoolInternal; goto spoolinternal;
  843.          }
  844.       }
  845.    }
  846.  /* Modify the request header (Censor / Cannonicalise URL / POST/PUT / HTTP-1.1 etc). */
  847.  if(mode==Real || mode==RealNoCache || mode==Fetch || mode==SpoolGet || mode==SpoolRefresh || mode==SpoolPragma)
  848.     ModifyRequest(Url,request_head);
  849.  /* Write request to remote server or outgoing file. */
  850.  if(mode==Fetch && client!=-1)
  851.     write_formatted(client,"Fetching %s ...n",Url->name);
  852.  if(mode==Real || mode==RealNoCache || mode==Fetch)
  853.    {
  854.     char *err=(Url->Protocol->request)(Url,request_head,request_body);
  855.     if(err)
  856.       {
  857.        if(mode==Real || mode==Fetch)
  858.          {
  859.           lseek(spool,0,SEEK_SET);
  860.           ftruncate(spool,0);
  861.           HTMLMessage(spool,503,"WWWOFFLE Remote Host Error",NULL,"RemoteHostError",
  862.                       "url",Url->name,
  863.                       "reason",err,
  864.                       "cache","yes",
  865.                       "backup",spool_exists?"yes":NULL,
  866.                       NULL);
  867.           DeleteLockWebpageSpoolFile(Url);
  868.          }
  869.        if(mode==Real || mode==RealNoCache)
  870.          {
  871.           HTMLMessage(tmpclient,503,"WWWOFFLE Remote Host Error",NULL,"RemoteHostError",
  872.                       "url",Url->name,
  873.                       "reason",err,
  874.                       "cache",NULL,
  875.                       "backup",spool_exists?"yes":NULL,
  876.                       NULL);
  877.           mode=SpoolInternal; goto spoolinternal;
  878.          }
  879.        else /* mode==Fetch */
  880.          {
  881.           if(client!=-1)
  882.              write_formatted(client,"Fetch Failure %s [Server Connection Error]n",Url->name);
  883.           exit(1);
  884.          }
  885.       }
  886.    }
  887.  else if((offline_request || online) &&
  888.          ((mode==SpoolGet && (!ConfirmRequests || Url->local || (Url->args && *Url->args=='!'))) ||
  889.           mode==SpoolRefresh ||
  890.           mode==SpoolPragma))
  891.    {
  892.     char *head=HeaderString(request_head);
  893.     int err=write_string(outgoing,head);
  894.     if(err==-1)
  895.       {
  896.        PrintMessage(Warning,"Cannot write the outgoing request.");
  897.        HTMLMessage(tmpclient,500,"WWWOFFLE Server Error",NULL,"ServerError",
  898.                    "error","Cannot write the outgoing request.",
  899.                    NULL);
  900.        mode=SpoolInternal; goto spoolinternal;
  901.       }
  902.     if(request_body)
  903.        write_data(outgoing,request_body->content,request_body->length);
  904.     free(head);
  905.    }
  906.  /* Parse the reply */
  907.  if(mode==Real || mode==RealNoCache || mode==Fetch)
  908.    {
  909.     reply_status=ParseReply(-1,Url,&reply_head);
  910.     if(!reply_head)
  911.       {
  912.        PrintMessage(Warning,"Timed out reading the reply.");
  913.        if(mode==Real || mode==Fetch)
  914.          {
  915.           lseek(spool,0,SEEK_SET);
  916.           ftruncate(spool,0);
  917.           HTMLMessage(spool,503,"WWWOFFLE Remote Host Error",NULL,"RemoteHostError",
  918.                       "url",Url->name,
  919.                       "reason","TimeoutReply",
  920.                       "cache","yes",
  921.                       "backup",spool_exists?"yes":NULL,
  922.                       NULL);
  923.           DeleteLockWebpageSpoolFile(Url);
  924.          }
  925.        if(mode==Real || mode==RealNoCache)
  926.           HTMLMessage(client,503,"WWWOFFLE Remote Host Error",NULL,"RemoteHostError",
  927.                       "url",Url->name,
  928.                       "reason","TimeoutReply",
  929.                       "cache",NULL,
  930.                       "backup",spool_exists?"yes":NULL,
  931.                       NULL);
  932.        else if(mode==Fetch && client!=-1)
  933.           write_formatted(client,"Fetch Failure %s [Server Reply Error]n",Url->name);
  934.        exit(1);
  935.       }
  936.     if(DebugLevel==ExtraDebug)
  937.        PrintMessage(ExtraDebug,"Incoming Reply Head (from server/proxy)n%s",HeaderString(reply_head));
  938.     if(mode==Fetch && (reply_status==301 || reply_status==302))
  939.       {
  940.        char *new_url=MovedLocation(Url,reply_head);
  941.        if(client!=-1)
  942.           write_formatted(client,"Fetching More %s [Page Moved]n",Url->name);
  943.        if(!new_url)
  944.           PrintMessage(Warning,"Cannot parse the reply for the new location.");
  945.        else
  946.           fetch_again+=RecurseFetchRelocation(Url,new_url);
  947.       }
  948.     else if(reply_status==304)
  949.       {
  950.        PrintMessage(Inform,"Server page is not newer than the one in cache.");
  951.        if(mode==Fetch || mode==Real)
  952.          {
  953.           close(spool);
  954.           DeleteLockWebpageSpoolFile(Url);
  955.           RestoreBackupWebpageSpoolFile(Url);
  956.           if(!lasttime_exists)
  957.              DeleteLastTimeSpoolFile(Url);
  958.          }
  959.        if(mode==Fetch)
  960.          {
  961.           if(client!=-1)
  962.              write_formatted(client,"Not fetching %s [Page Unchanged]n",Url->name);
  963.           if(fetch_again)
  964.             {
  965.              spool=OpenWebpageSpoolFile(1,Url);
  966.              init_buffer(spool);
  967.              if(ParseDocument(spool,Url))
  968.                 RecurseFetch(Url,1);
  969.              close(spool);
  970.             }
  971.           exit(fetch_again?4:0);
  972.          }
  973.        else if(mode==Real)
  974.          {
  975.           spool=OpenWebpageSpoolFile(1,Url);
  976.           init_buffer(spool);
  977.           mode=Spool;
  978.          }
  979.       }
  980.     else if(mode==Fetch && reply_status==401 && Urlpw)
  981.       {
  982.        mode=FetchNoPassword;
  983.        if(client!=-1)
  984.           write_formatted(client,"Fetching More %s [URL with password]n",Url->name);
  985.       }
  986.     else if(mode==Real && reply_status==401 && Urlpw)
  987.       {
  988.        mode=RealNoPassword;
  989.       }
  990.    }
  991.  else
  992.     reply_head=NULL;
  993.  if(mode==Spool || mode==SpoolPragma)
  994.    {
  995.     reply_status=ParseReply(spool,Url,&reply_head);
  996.     if(!reply_head)
  997.        PrintMessage(Warning,"Spooled Reply Head (from cache) is empty.");
  998.     else
  999.       {
  1000.        char *head=HeaderString(reply_head);
  1001.        if(DebugLevel==ExtraDebug)
  1002.           PrintMessage(ExtraDebug,"Spooled Reply Head (from cache)n%s",head);
  1003.        free(head);
  1004.       }
  1005.    }
  1006.  reply_body=CreateBody(READ_BUFFER_SIZE);
  1007.  /* mode = Spool, SpoolGet, SpoolWillGet, SpoolPragma, SpoolRefresh, Real, RealRefresh, RealNoCache, RealNoPassword, Fetch or FetchNoPassword */
  1008.  /* Close the outgoing file if any. */
  1009.  if(outgoing>=0)
  1010.    {
  1011.     if(mode==Fetch)
  1012.        close(outgoing);
  1013.     if(mode==SpoolGet || mode==SpoolRefresh || mode==SpoolPragma)
  1014.        CloseOutgoingSpoolFile(outgoing,Url);
  1015.    }
  1016.  /* The main body of the handling of the data. */
  1017.  if(mode==Real || mode==RealNoCache || mode==RealNoPassword)
  1018.    {
  1019.     char *head;
  1020.     int n=0,err=0,bytes=0;
  1021.     ModifyReply(reply_head);
  1022.     head=HeaderString(reply_head);
  1023.     if(DebugLevel==ExtraDebug)
  1024.        PrintMessage(ExtraDebug,"Outgoing Reply Head (to browser)n%s",head);
  1025.     if(mode!=RealNoCache)
  1026.        write_string(spool,head);
  1027.     if(mode!=RealNoPassword)
  1028.        err=write_string(client,head);
  1029.     free(head);
  1030.     while(err!=-1 && (n=(Url->Protocol->readbody)(reply_body->content,READ_BUFFER_SIZE))>0)
  1031.       {
  1032.        bytes+=n;
  1033.        if(mode!=RealNoCache)
  1034.           write_data(spool,reply_body->content,n);
  1035.        if(mode!=RealNoPassword && !head_only)
  1036.           err=write_data(client,reply_body->content,n);
  1037.        if(err==-1)
  1038.          {
  1039.           char *length=GetHeader(reply_head,"Content-Length",NULL);
  1040.           if(length)
  1041.             {
  1042.              int size=atoi(length);
  1043.              if(size<(IntrDownloadSize<<10) || (100*(double)bytes/(double)size)>IntrDownloadPercent)
  1044.                 err=0;
  1045.              head_only=1; /* cheat to stop writing to client */
  1046.             }
  1047.          }
  1048.       }
  1049.     if(mode!=RealNoCache)
  1050.       {
  1051.        if(err==-1 && !IntrDownloadKeep)
  1052.          {
  1053.           PrintMessage(Warning,"Error writing to client [%!s]; client disconnected?");
  1054.           lseek(spool,0,SEEK_SET);
  1055.           ftruncate(spool,0);
  1056.           HTMLMessage(spool,503,"WWWOFFLE Remote Host Error",NULL,"RemoteHostError",
  1057.                       "url",Url->name,
  1058.                       "reason","ClientClose",
  1059.                       "cache","yes",
  1060.                       "backup",spool_exists?"yes":NULL,
  1061.                       NULL);
  1062.          }
  1063.        else if(n<0 && !TimeoutDownloadKeep)
  1064.          {
  1065.           PrintMessage(Warning,"Timed out while reading from remote host.");
  1066.           lseek(spool,0,SEEK_SET);
  1067.           ftruncate(spool,0);
  1068.           HTMLMessage(spool,503,"WWWOFFLE Remote Host Error",NULL,"RemoteHostError",
  1069.                       "url",Url->name,
  1070.                       "reason","TimeoutTransfer",
  1071.                       "cache","yes",
  1072.                       "backup",spool_exists?"yes":NULL,
  1073.                       NULL);
  1074.          }
  1075.        else
  1076.          {
  1077.           if(n<0 || err==-1)
  1078.             {
  1079.              reply_head->status=503;
  1080.              head=HeaderString(reply_head);
  1081.              lseek(spool,0,SEEK_SET);
  1082.              write_string(spool,head);
  1083.              free(head);
  1084.             }
  1085.           if(spool_exists)
  1086.              DeleteBackupWebpageSpoolFile(Url);
  1087.          }
  1088.        DeleteLockWebpageSpoolFile(Url);
  1089.       }
  1090.    }
  1091.  else if(mode==Fetch || mode==FetchNoPassword)
  1092.    {
  1093.     char *head=HeaderString(reply_head);
  1094.     int n;
  1095.     write_string(spool,head);
  1096.     free(head);
  1097.     while((n=(Url->Protocol->readbody)(reply_body->content,READ_BUFFER_SIZE))>0)
  1098.        write_data(spool,reply_body->content,n);
  1099.     if(n<0 && !TimeoutDownloadKeep)
  1100.       {
  1101.        PrintMessage(Warning,"Timed out while reading from remote host.");
  1102.        lseek(spool,0,SEEK_SET);
  1103.        ftruncate(spool,0);
  1104.        HTMLMessage(spool,503,"WWWOFFLE Remote Host Error",NULL,"RemoteHostError",
  1105.                    "url",Url->name,
  1106.                    "reason","TimeoutTransfer",
  1107.                    "cache","yes",
  1108.                    "backup",spool_exists?"yes":NULL,
  1109.                    NULL);
  1110.        if(client!=-1)
  1111.           write_formatted(client,"Fetch Failure %s [Timeout]n",Url->name);
  1112.       }
  1113.     else
  1114.       {
  1115.        if(spool_exists)
  1116.           DeleteBackupWebpageSpoolFile(Url);
  1117.        if(client!=-1)
  1118.           write_formatted(client,"Fetch Success %sn",Url->name);
  1119.       }
  1120.     DeleteLockWebpageSpoolFile(Url);
  1121.     if(n>=0 && reply_status>=200 && reply_status<400)
  1122.       {
  1123.        lseek(spool,0,SEEK_SET);
  1124.        init_buffer(spool);
  1125.        if(ParseDocument(spool,Url))
  1126.          {
  1127.           int links=RecurseFetch(Url,1);
  1128.           if(client!=-1 && links)
  1129.              write_formatted(client,"Fetching More %s [%d Extra URLs]n",Url->name,links);
  1130.           fetch_again+=links;
  1131.          }
  1132.       }
  1133.    }
  1134.  else if(mode==Spool || mode==SpoolPragma)
  1135.    {
  1136.     struct stat buf;
  1137.     char *remote_error_note="WWWOFFLE Remote Host Error";
  1138.     if(ExistsLockWebpageSpoolFile(Url))
  1139.       {
  1140.        int t=0;
  1141.        if(online)
  1142.          {
  1143.           t=SocketTimeout/6;
  1144.           PrintMessage(Inform,"Waiting for the page to be unlocked.");
  1145.           while(--t>0 && ExistsLockWebpageSpoolFile(Url))
  1146.              sleep(1);
  1147.           if(t<=0)
  1148.              PrintMessage(Inform,"Timed out waiting for the page to be unlocked.");
  1149.          }
  1150.        if(t<=0)
  1151.          {
  1152.           HTMLMessage(tmpclient,503,"WWWOFFLE File Locked",NULL,"FileLocked",
  1153.                       "url",Url->name,
  1154.                       NULL);
  1155.           mode=SpoolInternal; goto spoolinternal;
  1156.          }
  1157.       }
  1158.     if((reply_head && !strcmp(reply_head->note,remote_error_note)) || (!fstat(spool,&buf) && buf.st_size==0))
  1159.       {
  1160.        DeleteWebpageSpoolFile(Url,0);
  1161.        RestoreBackupWebpageSpoolFile(Url);
  1162.       }
  1163.     if(reply_head)
  1164.       {
  1165.        char *head=HeaderString(reply_head);
  1166.        write_string(tmpclient,head);
  1167.        free(head);
  1168.       }
  1169.     if(EnableHTMLModifications &&
  1170.        reply_status>=200 && reply_status<400 &&
  1171.        GetHeader(reply_head,"Content-Type","text/html") &&
  1172.        !is_client_wwwoffle &&
  1173.        !is_client_htdig)
  1174.        OutputHTMLWithModifications(tmpclient,spool,Url);
  1175.     else if(DisableAnimatedGIF &&
  1176.        reply_status>=200 && reply_status<400 &&
  1177.        GetHeader(reply_head,"Content-Type","image/gif") &&
  1178.        !is_client_wwwoffle &&
  1179.        !is_client_htdig)
  1180.        OutputGIFWithModifications(tmpclient,spool,Url);
  1181.     else
  1182.       {
  1183.        int n;
  1184.        while((n=read_data(spool,reply_body->content,READ_BUFFER_SIZE))>0)
  1185.           write_data(tmpclient,reply_body->content,n);
  1186.       }
  1187.     mode=SpoolInternal;
  1188.    }
  1189.  else if(mode==SpoolGet)
  1190.    {
  1191.     if((offline_request || online))
  1192.       {
  1193.        if(ConfirmRequests && !Url->local && (!Url->args || *Url->args!='!'))
  1194.           HTMLMessage(tmpclient,404,"WWWOFFLE Confirm Request",NULL,"ConfirmRequest",
  1195.                       "url",Url->name,
  1196.                       NULL);
  1197.        else if(fetch_again)
  1198.           HTMLMessage(tmpclient,404,"WWWOFFLE Refresh Will Get",NULL,"RefreshWillGet",
  1199.                       "url",Url->name,
  1200.                       "password",HashOutgoingSpoolFile(Url),
  1201.                       NULL);
  1202.        else
  1203.           HTMLMessage(tmpclient,404,"WWWOFFLE Will Get",NULL,"WillGet",
  1204.                       "already",NULL,
  1205.                       "url",Url->name,
  1206.                       "password",HashOutgoingSpoolFile(Url),
  1207.                       NULL);
  1208.       }
  1209.     else
  1210.        HTMLMessage(tmpclient,404,"WWWOFFLE Refused Request",NULL,"RefusedRequest",
  1211.                    "url",Url->name,
  1212.                    NULL);
  1213.     mode=SpoolInternal;
  1214.    }
  1215.  else if(mode==SpoolWillGet)
  1216.    {
  1217.     HTMLMessage(tmpclient,404,"WWWOFFLE Will Get",NULL,"WillGet",
  1218.                 "already","yes",
  1219.                 "url",Url->name,
  1220.                 "password",HashOutgoingSpoolFile(Url),
  1221.                 NULL);
  1222.     mode=SpoolInternal;
  1223.    }
  1224.  else if(mode==RealRefresh || mode==SpoolRefresh)
  1225.    {
  1226.     HTMLMessage(tmpclient,301,"WWWOFFLE Refresh Redirect",Url->link,"RefreshRedirect",
  1227.                 "url",Url->name,
  1228.                 "link",Url->link,
  1229.                 NULL);
  1230.     mode=SpoolInternal;
  1231.    }
  1232. spoolinternal:                  /* Jump here if mode is SpoolInternal. */
  1233.  if(mode==SpoolInternal)
  1234.    {
  1235.     int n;
  1236.     unsigned long size;
  1237.     if(!reply_body)
  1238.        reply_body=CreateBody(READ_BUFFER_SIZE);
  1239.     size=lseek(tmpclient,0,SEEK_CUR);
  1240.     lseek(tmpclient,0,SEEK_SET);
  1241.     init_buffer(tmpclient);
  1242.     reply_status=ParseReply(tmpclient,Url,&reply_head);
  1243.     if(!reply_head)
  1244.        PrintMessage(Warning,"Outgoing Reply Head (to browser) is empty.");
  1245.     else
  1246.       {
  1247.        char *head,length[10];
  1248.        size-=reply_head->size;
  1249.        RemoveFromHeader(reply_head,"Content-Length",NULL);
  1250.        sprintf(length,"%ld",size);
  1251.        AddToHeader(reply_head,"Content-Length",length);
  1252.        ModifyReply(reply_head);
  1253.        head=HeaderString(reply_head);
  1254.        if(DebugLevel==ExtraDebug)
  1255.           PrintMessage(ExtraDebug,"Outgoing Reply Head (to browser)n%s",head);
  1256.        write_string(client,head);
  1257.        free(head);
  1258.       }
  1259.     if(!head_only)
  1260.        while((n=read_data(tmpclient,reply_body->content,READ_BUFFER_SIZE))>0)
  1261.           write_data(client,reply_body->content,n);
  1262.    }
  1263.  /* Close down and exit. */
  1264.  if((mode==Real || mode==RealNoPassword) && outgoing_exists)
  1265.     DeleteOutgoingSpoolFile(Url);
  1266.  if(mode==Real || mode==RealNoCache || mode==RealNoPassword ||
  1267.     mode==Fetch || mode==FetchNoPassword)
  1268.     (Url->Protocol->close)();
  1269.  if(tmpclient>=0)
  1270.     CloseTempSpoolFile(tmpclient);
  1271.  if(spool>=0)
  1272.     close(spool);
  1273.  if(mode==SpoolInternal && request_head && spool_exists && is_client_htdig)
  1274.     TouchWebpageSpoolFile(Url,spool_exists);
  1275.  if(reply_head)
  1276.     FreeHeader(reply_head);
  1277.  if(reply_body)
  1278.     FreeBody(reply_body);
  1279.  if(mode==RealNoPassword || mode==FetchNoPassword)
  1280.    {
  1281.     FreeURL(Url);Url=Urlpw;Urlpw=NULL;
  1282.     spool_exists=spool_exists_pw;spool_exists_pw=0;
  1283.     reply_head=NULL;
  1284.     reply_body=NULL;
  1285.     if(mode==RealNoPassword)
  1286.        mode=Real;
  1287.     else
  1288.        mode=Fetch;
  1289.     goto passwordagain;
  1290.    }
  1291.  if(request_head)
  1292.     FreeHeader(request_head);
  1293.  if(request_body)
  1294.     FreeBody(request_body);
  1295.  if(client>=0)
  1296.     CloseSocket(client);
  1297.  if(fetch_again)
  1298.     return(4);
  1299.  else
  1300.     return(0);
  1301. }
  1302. /*++++++++++++++++++++++++++++++++++++++
  1303.   Make an SSL proxy connection.
  1304.   int ssl_tunnel Return 1 in case of failure.
  1305.   int client The client socket.
  1306.   URL *Url The URL to get (used for host only).
  1307.   Header *request_head The head of the request.
  1308.   ++++++++++++++++++++++++++++++++++++++*/
  1309. static int ssl_tunnel(int client,URL *Url,Header *request_head)
  1310. {
  1311.  char *err=SSL_Open(Url);
  1312.  if(err && ConnectRetry)
  1313.    {
  1314.     PrintMessage(Inform,"Waiting to try connection again.");
  1315.     sleep(10);
  1316.     err=SSL_Open(Url);
  1317.    }
  1318.  if(err)
  1319.    {
  1320.     HTMLMessage(client,503,"WWWOFFLE Remote Host Error",NULL,"RemoteHostError",
  1321.                 "url",Url->host,
  1322.                 "reason",err,
  1323.                 "cache",NULL,
  1324.                 "backup",NULL,
  1325.                 NULL);
  1326.     return(1);
  1327.    }
  1328.  ModifyRequest(Url,request_head);
  1329.  err=SSL_Request(client,Url,request_head);
  1330.  if(err)
  1331.    {
  1332.     HTMLMessage(client,503,"WWWOFFLE Remote Host Error",NULL,"RemoteHostError",
  1333.                 "url",Url->host,
  1334.                 "reason",err,
  1335.                 "cache",NULL,
  1336.                 "backup",NULL,
  1337.                 NULL);
  1338.     return(1);
  1339.    }
  1340.  else if(!SSLProxy)
  1341.     HTMLMessageHead(client,200,"WWWOFFLE SSL OK",
  1342.                     "Content-Type",NULL,
  1343.                     NULL);
  1344.  SSL_Transfer(client);
  1345.  SSL_Close();
  1346.  return(0);
  1347. }
  1348. /*++++++++++++++++++++++++++++++++++++++
  1349.   Uninstall the signal handlers.
  1350.   ++++++++++++++++++++++++++++++++++++++*/
  1351. static void uninstall_sighandlers(void)
  1352. {
  1353.  struct sigaction action;
  1354.  /* SIGCHLD */
  1355.  action.sa_handler = SIG_DFL;
  1356.  sigemptyset (&action.sa_mask);
  1357.  action.sa_flags = 0;
  1358.  if(sigaction(SIGCHLD, &action, NULL) != 0)
  1359.     PrintMessage(Warning, "Cannot uninstall SIGCHLD handler.");
  1360.  /* SIGINT, SIGQUIT, SIGTERM */
  1361.  action.sa_handler = SIG_DFL;
  1362.  sigemptyset(&action.sa_mask);
  1363.  action.sa_flags = 0;
  1364.  if(sigaction(SIGINT, &action, NULL) != 0)
  1365.     PrintMessage(Warning, "Cannot uninstall SIGINT handler.");
  1366.  if(sigaction(SIGQUIT, &action, NULL) != 0)
  1367.     PrintMessage(Warning, "Cannot uninstall SIGQUIT handler.");
  1368.  if(sigaction(SIGTERM, &action, NULL) != 0)
  1369.     PrintMessage(Warning, "Cannot uninstall SIGTERM handler.");
  1370.  /* SIGHUP */
  1371.  action.sa_handler = SIG_DFL;
  1372.  sigemptyset(&action.sa_mask);
  1373.  action.sa_flags = 0;
  1374.  if(sigaction(SIGHUP, &action, NULL) != 0)
  1375.     PrintMessage(Warning, "Cannot uninstall SIGHUP handler.");
  1376.  /* SIGPIPE */
  1377.  action.sa_handler = SIG_IGN;
  1378.  sigemptyset (&action.sa_mask);
  1379.  action.sa_flags = 0;
  1380.  if(sigaction(SIGPIPE, &action, NULL) != 0)
  1381.     PrintMessage(Warning, "Cannot ignore SIGPIPE.");
  1382. }