home *** CD-ROM | disk | FTP | other *** search
/ Dream 52 / Amiga_Dream_52.iso / Linux / Magazine / wwwoffle-2.1.tar.gz / wwwoffle-2.1 / wwwoffles.c < prev   
C/C++ Source or Header  |  1998-03-02  |  27KB  |  953 lines

  1. /***************************************
  2.   $Header: /home/amb/wwwoffle/RCS/wwwoffles.c 2.48 1998/03/02 15:06:00 amb Exp $
  3.  
  4.   WWWOFFLE - World Wide Web Offline Explorer - Version 2.1.
  5.   A server to fetch the required pages.
  6.   ******************/ /******************
  7.   Written by Andrew M. Bishop
  8.  
  9.   This file Copyright 1996,97,98 Andrew M. Bishop
  10.   It may be distributed under the GNU Public License, version 2, or
  11.   any higher version.  See section COPYING of the GNU Public license
  12.   for conditions under which this file may be redistributed.
  13.   ***************************************/
  14.  
  15.  
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19.  
  20. #include <sys/types.h>
  21. #include <sys/stat.h>
  22. #include <time.h>
  23. #include <signal.h>
  24. #include <unistd.h>
  25. #include <fcntl.h>
  26.  
  27. #include "wwwoffle.h"
  28. #include "misc.h"
  29. #include "proto.h"
  30. #include "config.h"
  31. #include "sockets.h"
  32. #include "errors.h"
  33.  
  34.  
  35. static void uninstall_sighandlers(void);
  36.  
  37.  
  38. /*+ The mode of operation of the server. +*/
  39. typedef enum _Mode
  40. {
  41.  None,                          /*+ Undecided. +*/
  42.  Real,                          /*+ From server host to cache and client. +*/
  43.  RealNoCache,                   /*+ From server host to client. +*/
  44.  RealRefresh,                   /*+ Refresh the page, forced from index. +*/
  45.  SpoolOrReal,                   /*+ From server host to cache and client but only if not already cached. +*/
  46.  Fetch,                         /*+ From server host to cache. +*/
  47.  Spool,                         /*+ From cache to client. +*/
  48.  SpoolGet,                      /*+ Not in cache so record request in outgoing. +*/
  49.  SpoolWillGet,                  /*+ Not in cache but is in outgoing. +*/
  50.  SpoolRefresh,                  /*+ Refresh the page, forced from index. +*/
  51.  SpoolPragma                    /*+ Refresh the page, forced from browser by 'Pragma: no-cache'. +*/
  52. }
  53. Mode;
  54.  
  55.  
  56. /*++++++++++++++++++++++++++++++++++++++
  57.   The main server program.
  58.  
  59.   int wwwoffles Returns the exit status.
  60.  
  61.   int online Whether the demon is online or not.
  62.  
  63.   int browser Set to true if there is a browser.
  64.  
  65.   int client The file descriptor of the client.
  66.   ++++++++++++++++++++++++++++++++++++++*/
  67.  
  68. int wwwoffles(int online,int browser,int client)
  69. {
  70.  int outgoing=-1,spool=-1;
  71.  int fetch_again=0;
  72.  char *mirrorProto=NULL,*mirrorHost=NULL;
  73.  char *request_head,*request_body;
  74.  char *reply_head,*reply_body;
  75.  int reply_status=-1;
  76.  char *url;
  77.  URL *Url;
  78.  Mode mode=None;
  79.  int lasttime_exists=0,spool_exists=0;
  80.  
  81.  /* Initialise things. */
  82.  
  83.  uninstall_sighandlers();
  84.  
  85.  InitErrorHandler("wwwoffles",-1,-1); /* change name nothing else */
  86.  
  87.  if(online==1 && browser)
  88.     mode=Real;
  89.  else if(online==1 && !browser)
  90.     mode=Fetch;
  91.  else if(online==-1 && browser)
  92.     mode=SpoolOrReal;
  93.  else if(!online && browser)
  94.     mode=Spool;
  95.  else
  96.    {
  97.     if(client!=-1)
  98.        ServerError(client,"Started server in illegal mode, seek help.");
  99.     PrintMessage(Fatal,"Started in a mode that is not allowed (online=%d, browser=%d).",online,browser);
  100.    }
  101.  
  102.  /* Set up the client file. */
  103.  
  104.  if(client<0 && mode!=Fetch)
  105.    {PrintMessage(Warning,"Cannot use client file descriptor %d.",client);exit(1);}
  106.  
  107.  /* Set up the outgoing file. */
  108.  
  109.  if(mode==Fetch)
  110.    {
  111.     outgoing=OpenOutgoingSpoolFile(1);
  112.     init_buffer(outgoing);
  113.  
  114.     if(outgoing==-1)
  115.       {PrintMessage(Inform,"No more outgoing requests.");exit(3);}
  116.    }
  117.  
  118.  /* Get the URL from the request. */
  119.  
  120.  if(mode==Real || mode==Spool || mode==SpoolOrReal)
  121.     url=ParseRequest(client,&request_head,&request_body);
  122.  else /* mode==Fetch */
  123.     url=ParseRequest(outgoing,&request_head,&request_body);
  124.  
  125.  if(!url)
  126.    {
  127.     if(mode!=Fetch)
  128.        ServerError(client,request_head?"Cannot parse the HTTP request":"The HTTP request was empty");
  129.     PrintMessage(Warning,"Could not parse HTTP request (%s).",request_head?"Parse error":"Empty request");
  130.     exit(1);
  131.    }
  132.  
  133.  PrintMessage(Inform,"URL='%s'.",url);
  134.  
  135.  Url=SplitURL(url);
  136.  
  137.  PrintMessage(Debug,"proto='%s'; host='%s'; path='%s'; args='%s'.",Url->proto,Url->host,Url->path,Url->args);
  138.  
  139.  if(!Url->Protocol)
  140.    {
  141.     if(mode==Real || mode==Spool || mode==SpoolOrReal)
  142.        IllegalProto(client,Url->name,Url->proto);
  143.     PrintMessage(Inform,"The protocol '%s' is not available.",Url);
  144.     exit(1);
  145.    }
  146.  
  147.  if(Url->user)
  148.    {
  149.     if(mode==Real || mode==Spool || mode==SpoolOrReal)
  150.        PasswordDisallowed(client,Url);
  151.     PrintMessage(Inform,"URLs with username and password are not allowed.",Url->proto);
  152.     exit(1);
  153.    }
  154.  
  155.  /* Change the mode based on the URL as required. */
  156.  /* (At this point mode is Spool, Real, SpoolOrReal or Fetch only.) */
  157.  
  158.  if(IsNotGot(Url->proto,Url->host,Url->path))
  159.    {
  160.     char* colon=strchr(Url->host,':');
  161.     if(colon) *colon=0;
  162.     if(mode==Real || mode==Spool || mode==SpoolOrReal)
  163.        RemoteHostError(client,Url,"is on the list of hosts and paths not to get.",0,0);
  164.     PrintMessage(Inform,"The server '%s://%s' and/or path '%s' is on the list not to get.",Url->proto,Url->host,Url->path);
  165.     exit(0);
  166.    }
  167.  
  168.  if(Url->local)
  169.    {
  170.     int newurl=0;
  171.  
  172.     if(!strncmp("/refresh",Url->path,8))
  173.       {
  174.        url=RefreshPage(client,Url->path,Url->args,Url->name,request_body,online);
  175.  
  176.        if(url==(char*)1)
  177.           newurl=-1;
  178.        else if(url)
  179.          {
  180.           FreeURL(Url);
  181.           Url=SplitURL(url);
  182.  
  183.           if(Url->args && *Url->args=='!')
  184.             {
  185.              PrintMessage(Inform,"It is not possible to refresh a URL that was posted.");
  186.              if(mode!=Fetch)
  187.                 CantRefreshPosted(client,Url);
  188.              exit(0);
  189.             }
  190.           else if(!*Url->host || IsLocalNetHost(Url->host))
  191.              mode=RealNoCache;
  192.           else if(mode==Real || mode==SpoolOrReal)
  193.             {
  194.              mode=RealRefresh;
  195.              DeleteWebpageSpoolFile(Url,0);
  196.             }
  197.           else if(mode==Spool)
  198.             {
  199.              if(ExistsWebpageSpoolFile(Url))
  200.                 mode=SpoolRefresh;
  201.              else
  202.                 if(ExistsOutgoingSpoolFile(Url))
  203.                    mode=SpoolWillGet;
  204.                 else
  205.                    mode=SpoolGet;
  206.             }
  207.           newurl=1;
  208.          }
  209.       }
  210.     else if(mode==Fetch)
  211.        PrintMessage(Inform,"The request to fetch a page from the local host is ignored.");
  212.     else if(!strcmp(Url->path,"/") && !Url->args)
  213.        WelcomePage(client);
  214.     else if(!strncmp("/index/",Url->path,7))
  215.        IndexPage(client,&Url->path[7],Url->args);
  216.     else if(!strncmp("/control/",Url->path,9))
  217.        ControlPage(client,&Url->path[9],Url->args,request_head,request_body);
  218.     else if(!strncmp("/monitor",Url->path,8))
  219.        MonitorPage(client,Url->path,Url->args,request_body);
  220.     else
  221.       {
  222.        int i;
  223.  
  224.        for(i=0;i<NProtocols;i++)
  225.           if(!strncmp(Protocols[i].name,Url->pathp+1,strlen(Protocols[i].name)) &&
  226.              Url->pathp[strlen(Protocols[i].name)+1]=='/')
  227.             {
  228.              url=(char*)malloc(strlen(Url->pathp)+4);
  229.              Url->pathp[strlen(Protocols[i].name)+1]=0;
  230.              sprintf(url,"%s://%s",Url->pathp+1,&Url->pathp[strlen(Protocols[i].name)+2]);
  231.              Url->pathp[strlen(Protocols[i].name)+1]='/';
  232.              newurl=1;
  233.              break;
  234.             }
  235.  
  236.        if(!newurl)
  237.           IllegalPage(client,Url->pathp);
  238.  
  239.        FreeURL(Url);
  240.        Url=SplitURL(url);
  241.       }
  242.  
  243.     if(!newurl)
  244.        exit(0);
  245.     else if(newurl==-1)
  246.        exit(4);
  247.  
  248.     PrintMessage(Inform,"new URL='%s'.",url);
  249.     PrintMessage(Debug,"new proto='%s'; host='%s'; path='%s'; args='%s'.",Url->proto,Url->host,Url->path,Url->args);
  250.  
  251.     if(!Url->Protocol)
  252.       {
  253.        if(mode==Real || mode==Spool || mode==SpoolOrReal)
  254.           IllegalProto(client,Url->name,Url->proto);
  255.        PrintMessage(Inform,"The protocol '%s' is not available.",Url->proto);
  256.        exit(1);
  257.       }
  258.    }
  259.  
  260.  if(IsMirrored(Url->proto,Url->host,&mirrorProto,&mirrorHost))
  261.    {
  262.     char *newurl=(char*)malloc(strlen(mirrorProto)+strlen(mirrorHost)+strlen(Url->pathp)+8);
  263.  
  264.     sprintf(newurl,"%s://%s%s",mirrorProto,mirrorHost,Url->pathp);
  265.  
  266.     url=newurl;
  267.  
  268.     FreeURL(Url);
  269.     Url=SplitURL(url);
  270.  
  271.     PrintMessage(Inform,"mirrored URL='%s'.",url);
  272.     PrintMessage(Debug,"mirrored proto='%s'; host='%s'; path='%s'; args='%s'.",Url->proto,Url->host,Url->path,Url->args);
  273.  
  274.     if(!Url->Protocol)
  275.       {
  276.        if(mode==Real || mode==Spool || mode==SpoolOrReal)
  277.           IllegalProto(client,Url->name,Url->proto);
  278.        PrintMessage(Inform,"The protocol '%s' is not available.",Url->proto);
  279.        exit(1);
  280.       }
  281.    }
  282.  
  283.  if(IsLocalNetHost(Url->host))
  284.    {
  285.     if(mode==Real || mode==Spool || mode==SpoolOrReal)
  286.        mode=RealNoCache;
  287.     else
  288.       {
  289.        PrintMessage(Inform,"The request to fetch a page from the local network host '%s' is ignored.",Url->host);
  290.        exit(0);
  291.       }
  292.    }
  293.  else if((mode==Real || mode==SpoolOrReal) && IsNotCached(Url->proto,Url->host,Url->path))
  294.    {
  295.     mode=RealNoCache;
  296.    }
  297.  
  298.  if(!strncasecmp(request_head,"POST",4))
  299.    {
  300.     if(mode==Spool)
  301.        mode=SpoolGet;
  302.    }
  303.  else if(Url->args && *Url->args=='!')
  304.    {
  305.     if(mode==Real || mode==SpoolOrReal)
  306.        mode=Spool;
  307.     else if(mode==Fetch)
  308.       {
  309.        PrintMessage(Inform,"It is not possible to fetch a URL that was posted.");
  310.        exit(0);
  311.       }
  312.    }
  313.  
  314.  if(strstr(request_head,"\nIf-Modified-Since:"))
  315.    {
  316.     char *bol=strstr(request_head,"\nIf-Modified-Since:")+1,*eol=strchr(bol,'\n');
  317.     *eol++=0;
  318.  
  319.     if(mode==Spool || mode==SpoolOrReal)
  320.       {
  321.        spool=OpenWebpageSpoolFile(1,Url);
  322.  
  323.        if(spool!=-1)
  324.          {
  325.           struct stat buf;
  326.           long since;
  327.  
  328.           buf.st_mtime=time(NULL)+1;
  329.           fstat(spool,&buf);
  330.           since=DateToTimeT(bol+19);
  331.  
  332.           close(spool);
  333.  
  334.           if(since>buf.st_mtime)
  335.             {NotModified(client);exit(0);}
  336.          }
  337.       }
  338.  
  339.     while(*eol)
  340.        *bol++=*eol++;
  341.     *bol=0;
  342.    }
  343.  
  344.  spool_exists=ExistsWebpageSpoolFile(Url);
  345.  
  346.  if(PragmaNoCache && strstr(request_head,"\nPragma: no-cache"))
  347.    {
  348.     if(mode==Spool || mode==SpoolGet)
  349.       {
  350.        if(spool_exists)
  351.           mode=SpoolPragma;
  352.        else
  353.           if(ExistsOutgoingSpoolFile(Url))
  354.              mode=SpoolWillGet;
  355.           else
  356.              mode=SpoolGet;
  357.       }
  358.     else if(mode==SpoolOrReal)
  359.        mode=Real;
  360.  
  361.     /* (mode==Fetch || mode==Real) are left unchanged, not modified as below. */
  362.    }
  363.  else if(mode==Spool && !spool_exists)
  364.    {
  365.     if(ExistsOutgoingSpoolFile(Url))
  366.        mode=SpoolWillGet;
  367.     else
  368.        mode=SpoolGet;
  369.    }
  370.  else if(mode==Fetch && spool_exists)
  371.    {
  372.     spool=OpenWebpageSpoolFile(1,Url);
  373.     init_buffer(spool);
  374.  
  375.     if(RequestChanges(spool,&request_head)==1)
  376.        TouchWebpageSpoolFile(Url);
  377.     else
  378.        exit(0);
  379.  
  380.     close(spool);
  381.    }
  382.  else if(mode==Real && spool_exists)
  383.    {
  384.     spool=OpenWebpageSpoolFile(1,Url);
  385.     init_buffer(spool);
  386.  
  387.     if(RequestChanged>=0 && RequestChanges(spool,&request_head)==1)
  388.        TouchWebpageSpoolFile(Url);
  389.     else
  390.        mode=Spool;
  391.  
  392.     close(spool);
  393.    }
  394.  else if(mode==SpoolOrReal)
  395.    {
  396.     if(spool_exists)
  397.        mode=Spool;
  398.     else
  399.        mode=Real;
  400.    }
  401.  
  402.  /* Create the last time file. */
  403.  
  404.  if(mode==Real || mode==Fetch)
  405.     lasttime_exists=CreateLastTimeSpoolFile(Url);
  406.  
  407.  /* Set up the file descriptor for the spool file. */
  408.  
  409.  if(mode==Real || mode==Fetch)
  410.    {
  411.     spool=OpenWebpageSpoolFile(0,Url);
  412.     init_buffer(spool);         /* may be read if Real changes to Spool later. */
  413.  
  414.     if(spool==-1)
  415.       {
  416.        if(mode==Real)
  417.           ServerError(client,"Cannot open the spooled web page to write.");
  418.        PrintMessage(Warning,"Cannot open the spooled web page to write.");
  419.        exit(1);
  420.       }
  421.  
  422.     if(spool_exists)
  423.        CreateBackupWebpageSpoolFile(Url,spool);
  424.    }
  425.  else if(mode==Spool || mode==SpoolPragma)
  426.    {
  427.     spool=OpenWebpageSpoolFile(1,Url);
  428.     init_buffer(spool);
  429.  
  430.     if(spool==-1)
  431.       {
  432.        ServerError(client,"Cannot open the spooled web page to read.");
  433.        PrintMessage(Warning,"Cannot open the spooled web page to read.");
  434.        exit(1);
  435.       }
  436.    }
  437.  
  438.  /* Set up the outgoing file. */
  439.  
  440.  if((mode==SpoolGet || mode==SpoolRefresh || mode==SpoolPragma) && OfflineRequests)
  441.    {
  442.     outgoing=OpenOutgoingSpoolFile(0);
  443.  
  444.     if(outgoing==-1)
  445.       {
  446.        ServerError(client,"Cannot open the outgoing request to write.");
  447.        PrintMessage(Warning,"Cannot open the outgoing request to write.");
  448.        exit(1);
  449.       }
  450.    }
  451.  
  452.  /* Open the connection to the server host. */
  453.  
  454.  if(mode==Real || mode==RealNoCache || mode==Fetch)
  455.    {
  456.     char *err=(Url->Protocol->open)(Url);
  457.  
  458.     if(err)
  459.       {
  460.        if(mode==Fetch || mode==Real)
  461.          {
  462.           lseek(spool,0,SEEK_SET);
  463.           ftruncate(spool,0);
  464.           RemoteHostError(spool,Url,err,1,spool_exists);
  465.          }
  466.        if(mode==Real || mode==RealNoCache)
  467.           RemoteHostError(client,Url,err,0,spool_exists);
  468.        exit(1);
  469.       }
  470.    }
  471.  
  472.  /* Modify the header (Censor / Cannonicalise URL / POST / HTTP-1.1 etc). */
  473.  
  474.  if(mode==Real || mode==RealNoCache || mode==Fetch || mode==SpoolGet || mode==SpoolRefresh || mode==SpoolPragma)
  475.     request_head=ModifyRequest(Url,request_head);
  476.  
  477.  /* Write request to remote server or outgoing file. */
  478.  
  479.  if(mode==Real || mode==RealNoCache || mode==Fetch)
  480.    {
  481.     char *err=(Url->Protocol->request)(Url,request_head,request_body);
  482.  
  483.     if(err)
  484.       {
  485.        if(mode==Real || mode==Fetch)
  486.          {
  487.           lseek(spool,0,SEEK_SET);
  488.           ftruncate(spool,0);
  489.           RemoteHostError(spool,Url,err,1,spool_exists);
  490.          }
  491.        if(mode==Real || mode==RealNoCache)
  492.           RemoteHostError(client,Url,err,0,spool_exists);
  493.        else if(mode==Fetch && client!=-1)
  494.           write_formatted(client,"Fetching %s [Server Connection Error]\n",Url->name);
  495.        exit(1);
  496.       }
  497.    }
  498.  else if((mode==SpoolGet || mode==SpoolRefresh || mode==SpoolPragma) && OfflineRequests)
  499.    {
  500.     int err=write_string(outgoing,request_head);
  501.  
  502.     if(err==-1)
  503.       {
  504.        ServerError(client,"Cannot write the outgoing request.");
  505.        PrintMessage(Warning,"Cannot write the outgoing request.");
  506.        exit(1);
  507.       }
  508.  
  509.     if(request_body)
  510.        write_string(outgoing,request_body);
  511.    }
  512.  
  513.  /* Parse the reply */
  514.  
  515.  if(mode==Real || mode==RealNoCache || mode==Fetch)
  516.    {
  517.     reply_status=ParseReply(Url,&reply_head);
  518.  
  519.     if(!reply_head)
  520.       {
  521.        if(mode==Real || mode==Fetch)
  522.          {
  523.           lseek(spool,0,SEEK_SET);
  524.           ftruncate(spool,0);
  525.           RemoteHostError(spool,Url,"the server timed out before sending the reply.",1,spool_exists);
  526.          }
  527.        if(mode==Real || mode==RealNoCache)
  528.           RemoteHostError(client,Url,"the server timed out before sending the reply.",0,spool_exists);
  529.        else if(mode==Fetch && client!=-1)
  530.           write_formatted(client,"Fetching %s [Server Reply Error]\n",Url->name);
  531.        PrintMessage(Warning,"Timed out reading the reply.");
  532.        exit(1);
  533.       }
  534.  
  535.     if(mode==Fetch && (reply_status==301 || reply_status==302))
  536.       {
  537.        char *new_url=MovedLocation(Url,reply_head);
  538.  
  539.        if(client!=-1)
  540.           write_formatted(client,"Fetching %s [Page Moved]\n",Url->name);
  541.  
  542.        if(!new_url)
  543.           PrintMessage(Warning,"Cannot parse the reply for the new location.");
  544.        else
  545.          {
  546.           char *new_request=RequestURL(new_url,Url->name);
  547.           int new_outgoing=OpenOutgoingSpoolFile(0);
  548.  
  549.           PrintMessage(Inform,"This URL has been moved to %s.",new_url);
  550.  
  551.           if(new_outgoing==-1)
  552.              PrintMessage(Warning,"Cannot open the new outgoing request to write.");
  553.           else
  554.             {
  555.              URL *new_Url=SplitURL(new_url);
  556.              write_string(new_outgoing,new_request);
  557.              CloseOutgoingSpoolFile(new_outgoing,new_Url);
  558.              FreeURL(new_Url);
  559.              fetch_again++;
  560.             }
  561.  
  562.           free(new_request);
  563.           free(new_url);
  564.          }
  565.       }
  566.     else if(reply_status==304)
  567.       {
  568.        PrintMessage(Inform,"Server page is not newer than the one in cache.");
  569.  
  570.        if((mode==Fetch || mode==Real) && !lasttime_exists)
  571.           DeleteLastTimeSpoolFile(Url);
  572.        if(mode==Fetch)
  573.          {
  574.           if(client!=-1)
  575.              write_formatted(client,"Fetching %s [Page Unchanged]\n",Url->name);
  576.           if(spool_exists)
  577.              DeleteBackupWebpageSpoolFile(Url);
  578.           exit(0);
  579.          }
  580.        else if(mode==Real)
  581.          {
  582.           mode=Spool;
  583.           if(spool_exists)
  584.              DeleteBackupWebpageSpoolFile(Url);
  585.          }
  586.       }
  587.     else if(mode==Fetch && client!=-1)
  588.        write_formatted(client,"Fetching %s\n",Url->name);
  589.    }
  590.  else
  591.     reply_head=NULL;
  592.  
  593.  reply_body=(char*)malloc(257);
  594.  
  595.  /* Close the outgoing file if any. */
  596.  
  597.  if(outgoing>=0)
  598.    {
  599.     if(mode==Fetch)
  600.        close(outgoing);
  601.     if(mode==SpoolGet || mode==SpoolRefresh || mode==SpoolPragma)
  602.        CloseOutgoingSpoolFile(outgoing,Url);
  603.    }
  604.  
  605.  /* Process the request. */
  606.  
  607.  if(mode==Real || mode==RealNoCache)
  608.    {
  609.     int n=strlen(reply_head),err=0;
  610.  
  611.     if(mode==Real)
  612.        write_data(spool,reply_head,n);
  613.     err=write_data(client,reply_head,n);
  614.  
  615.     while(err!=-1 && (n=(Url->Protocol->readbody)(reply_body,256))>0)
  616.       {
  617.        if(mode==Real)
  618.           write_data(spool,reply_body,n);
  619.        err=write_data(client,reply_body,n);
  620.       }
  621.  
  622.     if(mode==Real)
  623.        if(err==-1 || n<0)
  624.          {
  625.           lseek(spool,0,SEEK_SET);
  626.           ftruncate(spool,0);
  627.           if(err==-1)
  628.             {
  629.              RemoteHostError(spool,Url,"the connection was closed by the client during the transfer.",1,spool_exists);
  630.              PrintMessage(Warning,"Error writing to client [%!s]; client disconnected?.");
  631.              exit(1);
  632.             }
  633.           else
  634.             {
  635.              RemoteHostError(spool,Url,"the server timed out during the transfer.",1,spool_exists);
  636.              PrintMessage(Warning,"Timed out while reading from remote host.");
  637.              exit(1);
  638.             }
  639.          }
  640.        else
  641.          {
  642.           int tell=lseek(spool,0,SEEK_CUR);
  643.           if(tell!=-1)
  644.              ftruncate(spool,tell);
  645.  
  646.           if(spool_exists)
  647.              DeleteBackupWebpageSpoolFile(Url);
  648.          }
  649.    }
  650.  else if(mode==Fetch)
  651.    {
  652.     int n=strlen(reply_head);
  653.  
  654.     write_data(spool,reply_head,n);
  655.  
  656.     while((n=(Url->Protocol->readbody)(reply_body,256))>0)
  657.        write_data(spool,reply_body,n);
  658.  
  659.     if(n<0)
  660.       {
  661.        lseek(spool,0,SEEK_SET);
  662.        ftruncate(spool,0);
  663.        RemoteHostError(spool,Url,"the server timed out during the transfer.",1,spool_exists);
  664.        PrintMessage(Warning,"Timed out while reading from remote host.");
  665.        exit(1);
  666.       }
  667.     else
  668.       {
  669.        int tell=lseek(spool,0,SEEK_CUR);
  670.        if(tell!=-1)
  671.           ftruncate(spool,tell);
  672.  
  673.        if(spool_exists)
  674.           DeleteBackupWebpageSpoolFile(Url);
  675.       }
  676.  
  677.     if(reply_status>=200 && reply_status<400)
  678.       {
  679.        lseek(spool,0,SEEK_SET);
  680.        init_buffer(spool);
  681.  
  682.        if(ParseHTML(spool,Url,0))
  683.          {
  684.           char **list,*refresh;
  685.  
  686.           if((refresh=MetaRefresh()))
  687.             {
  688.              char *new_request=RequestURL(refresh,Url->name);
  689.              int new_outgoing=OpenOutgoingSpoolFile(0);
  690.  
  691.              PrintMessage(Debug,"Meta-Refresh=%s",refresh);
  692.  
  693.              if(new_outgoing==-1)
  694.                 PrintMessage(Warning,"Cannot open the new outgoing request to write.");
  695.              else
  696.                {
  697.                 URL *ref_Url=SplitURL(refresh);
  698.                 write_string(new_outgoing,new_request);
  699.                 CloseOutgoingSpoolFile(new_outgoing,ref_Url);
  700.                 FreeURL(ref_Url);
  701.                 fetch_again++;
  702.                }
  703.  
  704.              free(new_request);
  705.              free(refresh);
  706.             }
  707.  
  708.           if(FetchImages && (list=ListImages()))
  709.              for(n=0;list[n];n++)
  710.                {
  711.                 char *new_request=RequestURL(list[n],Url->name);
  712.                 int new_outgoing=OpenOutgoingSpoolFile(0);
  713.  
  714.                 PrintMessage(Debug,"Image=%s",list[n]);
  715.  
  716.                 if(new_outgoing==-1)
  717.                    PrintMessage(Warning,"Cannot open the new outgoing request to write.");
  718.                 else
  719.                   {
  720.                    URL *new_Url=SplitURL(list[n]);
  721.                    write_string(new_outgoing,new_request);
  722.                    CloseOutgoingSpoolFile(new_outgoing,new_Url);
  723.                    FreeURL(new_Url);
  724.                    fetch_again++;
  725.                   }
  726.  
  727.                 free(new_request);
  728.                }
  729.  
  730.           if(FetchFrames && (list=ListFrames()))
  731.              for(n=0;list[n];n++)
  732.                {
  733.                 char *new_request=RequestURL(list[n],Url->name);
  734.                 int new_outgoing=OpenOutgoingSpoolFile(0);
  735.  
  736.                 PrintMessage(Debug,"Frame=%s",list[n]);
  737.  
  738.                 if(new_outgoing==-1)
  739.                    PrintMessage(Warning,"Cannot open the new outgoing request to write.");
  740.                 else
  741.                   {
  742.                    URL *new_Url=SplitURL(list[n]);
  743.                    write_string(new_outgoing,new_request);
  744.                    CloseOutgoingSpoolFile(new_outgoing,new_Url);
  745.                    FreeURL(new_Url);
  746.                    fetch_again++;
  747.                   }
  748.  
  749.                 free(new_request);
  750.                }
  751.          }
  752.       }
  753.    }
  754.  else if(mode==Spool || mode==SpoolPragma)
  755.    {
  756.     struct stat buf;
  757.     char *head="HTTP/1.0 503 WWWOFFLE Remote Host Error\r\n"; /* This line must not be changed (see messages.c). */
  758.     int n=read_data(spool,reply_body,64);
  759.  
  760.     if(!strncmp(reply_body,head,strlen(head)) || (!fstat(spool,&buf) && buf.st_size==0))
  761.       {
  762.        DeleteWebpageSpoolFile(Url,0);
  763.        RestoreBackupWebpageSpoolFile(Url);
  764.       }
  765.  
  766.     if(AddInfoRefresh)
  767.       {
  768.        struct stat buf;
  769.  
  770.        lseek(spool,0,SEEK_SET);
  771.        init_buffer(spool);
  772.  
  773.        if(ParseHTML(spool,Url,0) && !fstat(spool,&buf))
  774.          {
  775.           time_t t_ago=time(NULL)-buf.st_mtime;
  776.           int position=GetHTMLEnd();
  777.           char *date=RFC822Date(buf.st_mtime,0),ago[24],*localhost=GetLocalHost(1);
  778.           char *refresh=(char*)malloc(4*strlen(Url->name)+4*strlen(localhost)+256);
  779.           char *index=(char*)malloc(strlen(Url->name)+strlen(localhost)+64);
  780.           char *msg=(char*)malloc(strlen(date)+5*strlen(Url->name)+5*strlen(localhost)+320);
  781.  
  782.           if(t_ago<0)
  783.              sprintf(ago,"in the future");
  784.           else if(t_ago<3600)
  785.              sprintf(ago,"%ld min%s ago",t_ago/60,t_ago/60==1?"":"s");
  786.           else if(t_ago<(24*3600))
  787.              sprintf(ago,"%ld hour%s ago",t_ago/3600,t_ago/3600==1?"":"s");
  788.           else if(t_ago<(14*24*3600))
  789.              sprintf(ago,"%ld day%s ago",t_ago/(24*3600),t_ago/(24*3600)==1?"":"s");
  790.           else if(t_ago<(30*24*3600))
  791.              sprintf(ago,"%ld week%s ago",t_ago/(7*24*3600),t_ago/(7*24*3600)==1?"":"s");
  792.           else
  793.              sprintf(ago,"%ld month%s ago",t_ago/(30*24*3600),t_ago/(30*24*3600)==1?"":"s");
  794.  
  795.           if(Url->args && *Url->args=='!')
  796.              sprintf(refresh,"[<a href=\"http://%s/control/delete?url=%s\">Delete</a>"
  797.                              "|Refresh:"
  798.                              "Options|"
  799.                              "Monitor]",
  800.                      localhost,Url->name);
  801.           else
  802.              sprintf(refresh,"[<a href=\"http://%s/control/delete?url=%s\">Delete</a>"
  803.                              "|<a href=\"http://%s/refresh/%s/%s\">Refresh</a>:"
  804.                              "<a href=\"http://%s/refresh/?%s\">Options</a>|"
  805.                              "<a href=\"http://%s/monitor/?%s\">Monitor</a>]",
  806.                      localhost,Url->name,
  807.                      localhost,Url->proto,Url->hostp,
  808.                      localhost,Url->name,
  809.                      localhost,Url->name);
  810.  
  811.           sprintf(index,"[<a href=\"http://%s/index/%s/%s/?none\">Index</a>]",
  812.                   localhost,Url->proto,Url->host);
  813.  
  814.           sprintf(msg,"\n"
  815.                       "<hr>\n"
  816.                       "<p align=center>\n"
  817.                       "WWWOFFLE - %s (%s) - %s - %s - WWWOFFLE\n"
  818.                       "</p>\n"
  819.                       "<hr>\n",
  820.                   date,ago,refresh,index);
  821.  
  822.           lseek(spool,0,SEEK_SET);
  823.           init_buffer(spool);
  824.  
  825.           while((reply_head=read_line(spool,reply_head)))
  826.             {
  827.              if(!strncmp(reply_head,"Content-Length:",15))
  828.                 sprintf(reply_head,"Content-Length: %d\r\n",atoi(&reply_head[16])+strlen(msg));
  829.              write_string(client,reply_head);
  830.              if(reply_head[0]=='\r' || reply_head[0]=='\n')
  831.                 break;
  832.             }
  833.  
  834.           while(position>0 && (n=read_data(spool,reply_body,position>256?256:position))>0)
  835.             {
  836.              write_data(client,reply_body,n);
  837.              position-=n;
  838.             }
  839.  
  840.           write_string(client,msg);
  841.  
  842.           while((n=read_data(spool,reply_body,256))>0)
  843.              write_data(client,reply_body,n);
  844.  
  845.           free(localhost);
  846.           free(refresh);
  847.           free(index);
  848.           free(msg);
  849.          }
  850.        else
  851.          {
  852.           lseek(spool,0,SEEK_SET);
  853.           init_buffer(spool);
  854.  
  855.           while((n=read_data(spool,reply_body,256))>0)
  856.              write_data(client,reply_body,n);
  857.          }
  858.       }
  859.     else
  860.        do
  861.          {
  862.           write_data(client,reply_body,n);
  863.          }
  864.        while((n=read_data(spool,reply_body,256))>0);
  865.    }
  866.  else if(mode==SpoolGet)
  867.    {
  868.     if(OfflineRequests)
  869.        WillGetURL(client,Url,0);
  870.     else
  871.        RefusedRequest(client,Url);
  872.    }
  873.  else if(mode==SpoolWillGet)
  874.    {
  875.     WillGetURL(client,Url,1);
  876.    }
  877.  else if(mode==RealRefresh || mode==SpoolRefresh)
  878.    {
  879.     RefreshRedirect(client,Url);
  880.    }
  881.  
  882.  /* Close down and exit. */
  883.  
  884.  if(mode==Real && ExistsOutgoingSpoolFile(Url))
  885.     DeleteOutgoingSpoolFile(Url);
  886.  
  887.  if(mode==Real || mode==RealNoCache || mode==Fetch)
  888.     (Url->Protocol->close)();
  889.  
  890.  if(spool>=0)
  891.     close(spool);
  892.  
  893.  if(request_head)
  894.     free(request_head);
  895.  if(request_body)
  896.     free(request_body);
  897.  
  898.  if(reply_head)
  899.     free(reply_head);
  900.  if(reply_body)
  901.     free(reply_body);
  902.  
  903.  if(client>=0)
  904.     CloseSocket(client);
  905.  
  906.  if(fetch_again)
  907.     return(4);
  908.  else
  909.     return(0);
  910. }
  911.  
  912.  
  913. /*++++++++++++++++++++++++++++++++++++++
  914.   Uninstall the signal handlers.
  915.   ++++++++++++++++++++++++++++++++++++++*/
  916.  
  917. static void uninstall_sighandlers(void)
  918. {
  919.  struct sigaction action;
  920.  
  921.  /* SIGCHLD */
  922.  action.sa_handler = SIG_DFL;
  923.  sigemptyset (&action.sa_mask);
  924.  action.sa_flags = 0;
  925.  if(sigaction(SIGCHLD, &action, NULL) != 0)
  926.     PrintMessage(Warning, "Cannot uninstall SIGCHLD handler.");
  927.  
  928.  /* SIGINT, SIGQUIT, SIGTERM */
  929.  action.sa_handler = SIG_DFL;
  930.  sigemptyset(&action.sa_mask);
  931.  action.sa_flags = 0;
  932.  if(sigaction(SIGINT, &action, NULL) != 0)
  933.     PrintMessage(Warning, "Cannot uninstall SIGINT handler.");
  934.  if(sigaction(SIGQUIT, &action, NULL) != 0)
  935.     PrintMessage(Warning, "Cannot uninstall SIGQUIT handler.");
  936.  if(sigaction(SIGTERM, &action, NULL) != 0)
  937.     PrintMessage(Warning, "Cannot uninstall SIGTERM handler.");
  938.  
  939.  /* SIGHUP */
  940.  action.sa_handler = SIG_DFL;
  941.  sigemptyset(&action.sa_mask);
  942.  action.sa_flags = 0;
  943.  if(sigaction(SIGHUP, &action, NULL) != 0)
  944.     PrintMessage(Warning, "Cannot uninstall SIGHUP handler.");
  945.  
  946.  /* SIGPIPE */
  947.  action.sa_handler = SIG_IGN;
  948.  sigemptyset (&action.sa_mask);
  949.  action.sa_flags = 0;
  950.  if(sigaction(SIGPIPE, &action, NULL) != 0)
  951.     PrintMessage(Warning, "Cannot ignore SIGPIPE.");
  952. }
  953.