]> git.saurik.com Git - apt.git/blobdiff - methods/http.cc
merged from lp:~donkult/apt/experimental/
[apt.git] / methods / http.cc
index b054446910ae8004cbd85bdd3e1306f5f40621d5..b8ed43cd256cb99d07827adb0bff7dc761c553b8 100644 (file)
    ##################################################################### */
                                                                        /*}}}*/
 // Include Files                                                       /*{{{*/
+#include <config.h>
+
 #include <apt-pkg/fileutl.h>
 #include <apt-pkg/acquire-method.h>
+#include <apt-pkg/configuration.h>
 #include <apt-pkg/error.h>
 #include <apt-pkg/hashes.h>
 #include <apt-pkg/netrc.h>
@@ -41,8 +44,6 @@
 #include <string.h>
 #include <iostream>
 #include <map>
-#include <apti18n.h>
-
 
 // Internet stuff
 #include <netdb.h>
@@ -51,6 +52,8 @@
 #include "connect.h"
 #include "rfc2553emu.h"
 #include "http.h"
+
+#include <apti18n.h>
                                                                        /*}}}*/
 using namespace std;
 
@@ -63,15 +66,15 @@ bool AllowRedirect = false;
 bool Debug = false;
 URI Proxy;
 
-unsigned long CircleBuf::BwReadLimit=0;
-unsigned long CircleBuf::BwTickReadData=0;
+unsigned long long CircleBuf::BwReadLimit=0;
+unsigned long long CircleBuf::BwTickReadData=0;
 struct timeval CircleBuf::BwReadTick={0,0};
 const unsigned int CircleBuf::BW_HZ=10;
-  
 // CircleBuf::CircleBuf - Circular input buffer                                /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-CircleBuf::CircleBuf(unsigned long Size) : Size(Size), Hash(0)
+CircleBuf::CircleBuf(unsigned long long Size) : Size(Size), Hash(0)
 {
    Buf = new unsigned char[Size];
    Reset();
@@ -87,7 +90,7 @@ void CircleBuf::Reset()
    InP = 0;
    OutP = 0;
    StrPos = 0;
-   MaxGet = (unsigned int)-1;
+   MaxGet = (unsigned long long)-1;
    OutQueue = string();
    if (Hash != 0)
    {
@@ -102,7 +105,7 @@ void CircleBuf::Reset()
    is non-blocking.. */
 bool CircleBuf::Read(int Fd)
 {
-   unsigned long BwReadMax;
+   unsigned long long BwReadMax;
 
    while (1)
    {
@@ -117,7 +120,7 @@ bool CircleBuf::Read(int Fd)
         struct timeval now;
         gettimeofday(&now,0);
 
-        unsigned long d = (now.tv_sec-CircleBuf::BwReadTick.tv_sec)*1000000 +
+        unsigned long long d = (now.tv_sec-CircleBuf::BwReadTick.tv_sec)*1000000 +
            now.tv_usec-CircleBuf::BwReadTick.tv_usec;
         if(d > 1000000/BW_HZ) {
            CircleBuf::BwReadTick = now;
@@ -131,7 +134,7 @@ bool CircleBuf::Read(int Fd)
       }
 
       // Write the buffer segment
-      int Res;
+      ssize_t Res;
       if(CircleBuf::BwReadLimit) {
         Res = read(Fd,Buf + (InP%Size), 
                    BwReadMax > LeftRead() ? LeftRead() : BwReadMax);
@@ -180,7 +183,7 @@ void CircleBuf::FillOut()
         return;
       
       // Write the buffer segment
-      unsigned long Sz = LeftRead();
+      unsigned long long Sz = LeftRead();
       if (OutQueue.length() - StrPos < Sz)
         Sz = OutQueue.length() - StrPos;
       memcpy(Buf + (InP%Size),OutQueue.c_str() + StrPos,Sz);
@@ -214,7 +217,7 @@ bool CircleBuf::Write(int Fd)
         return true;
       
       // Write the buffer segment
-      int Res;
+      ssize_t Res;
       Res = write(Fd,Buf + (OutP%Size),LeftWrite());
 
       if (Res == 0)
@@ -240,7 +243,7 @@ bool CircleBuf::Write(int Fd)
 bool CircleBuf::WriteTillEl(string &Data,bool Single)
 {
    // We cheat and assume it is unneeded to have more than one buffer load
-   for (unsigned long I = OutP; I < InP; I++)
+   for (unsigned long long I = OutP; I < InP; I++)
    {      
       if (Buf[I%Size] != '\n')
         continue;
@@ -258,7 +261,7 @@ bool CircleBuf::WriteTillEl(string &Data,bool Single)
       Data = "";
       while (OutP < I)
       {
-        unsigned long Sz = LeftWrite();
+        unsigned long long Sz = LeftWrite();
         if (Sz == 0)
            return false;
         if (I - OutP < Sz)
@@ -286,6 +289,11 @@ void CircleBuf::Stats()
    clog << "Got " << InP << " in " << Diff << " at " << InP/Diff << endl;*/
 }
                                                                        /*}}}*/
+CircleBuf::~CircleBuf()
+{
+   delete [] Buf;
+   delete Hash;
+}
 
 // ServerState::ServerState - Constructor                              /*{{{*/
 // ---------------------------------------------------------------------
@@ -378,7 +386,7 @@ bool ServerState::Close()
 // ---------------------------------------------------------------------
 /* Returns 0 if things are OK, 1 if an IO error occurred and 2 if a header
    parse error occurred */
-int ServerState::RunHeaders()
+ServerState::RunHeadersResult ServerState::RunHeaders()
 {
    State = Header;
    
@@ -402,12 +410,12 @@ int ServerState::RunHeaders()
       if (Debug == true)
         clog << Data;
       
-      for (string::const_iterator I = Data.begin(); I < Data.end(); I++)
+      for (string::const_iterator I = Data.begin(); I < Data.end(); ++I)
       {
         string::const_iterator J = I;
-        for (; J != Data.end() && *J != '\n' && *J != '\r';J++);
+        for (; J != Data.end() && *J != '\n' && *J != '\r'; ++J);
         if (HeaderLine(string(I,J)) == false)
-           return 2;
+           return RUN_HEADERS_PARSE_ERROR;
         I = J;
       }
 
@@ -419,11 +427,11 @@ int ServerState::RunHeaders()
       if (Encoding == Closes && HaveContent == true)
         Persistent = false;
       
-      return 0;
+      return RUN_HEADERS_OK;
    }
    while (Owner->Go(false,this) == true);
    
-   return 1;
+   return RUN_HEADERS_IO_ERROR;
 }
                                                                        /*}}}*/
 // ServerState::RunData - Transfer the data from the socket            /*{{{*/
@@ -453,7 +461,7 @@ bool ServerState::RunData()
            return false;
                 
         // See if we are done
-        unsigned long Len = strtol(Data.c_str(),0,16);
+        unsigned long long Len = strtoull(Data.c_str(),0,16);
         if (Len == 0)
         {
            In.Limit(-1);
@@ -553,8 +561,14 @@ bool ServerState::HeaderLine(string Line)
       // Evil servers return no version
       if (Line[4] == '/')
       {
-        if (sscanf(Line.c_str(),"HTTP/%u.%u %u%[^\n]",&Major,&Minor,
-                   &Result,Code) != 4)
+        int const elements = sscanf(Line.c_str(),"HTTP/%u.%u %u%[^\n]",&Major,&Minor,&Result,Code);
+        if (elements == 3)
+        {
+           Code[0] = '\0';
+           if (Debug == true)
+              clog << "HTTP server doesn't give Reason-Phrase for " << Result << std::endl;
+        }
+        else if (elements != 4)
            return _error->Error(_("The HTTP server sent an invalid reply header"));
       }
       else
@@ -590,7 +604,7 @@ bool ServerState::HeaderLine(string Line)
       if (StartPos != 0)
         return true;
       
-      if (sscanf(Val.c_str(),"%lu",&Size) != 1)
+      if (sscanf(Val.c_str(),"%llu",&Size) != 1)
         return _error->Error(_("The HTTP server sent an invalid Content-Length header"));
       return true;
    }
@@ -605,9 +619,9 @@ bool ServerState::HeaderLine(string Line)
    {
       HaveContent = true;
       
-      if (sscanf(Val.c_str(),"bytes %lu-%*u/%lu",&StartPos,&Size) != 2)
+      if (sscanf(Val.c_str(),"bytes %llu-%*u/%llu",&StartPos,&Size) != 2)
         return _error->Error(_("The HTTP server sent an invalid Content-Range header"));
-      if ((unsigned)StartPos > Size)
+      if ((unsigned long long)StartPos > Size)
         return _error->Error(_("This HTTP server has broken range support"));
       return true;
    }
@@ -631,7 +645,7 @@ bool ServerState::HeaderLine(string Line)
    
    if (stringcasecmp(Tag,"Last-Modified:") == 0)
    {
-      if (StrToTime(Val,Date) == false)
+      if (RFC1123StrToTime(Val.c_str(), Date) == false)
         return _error->Error(_("Unknown date format"));
       return true;
    }
@@ -710,7 +724,7 @@ void HttpMethod::SendReq(FetchItem *Itm,CircleBuf &Out)
    if (stat(Itm->DestFile.c_str(),&SBuf) >= 0 && SBuf.st_size > 0)
    {
       // In this case we send an if-range query with a range header
-      sprintf(Buf,"Range: bytes=%li-\r\nIf-Range: %s\r\n",(long)SBuf.st_size - 1,
+      sprintf(Buf,"Range: bytes=%lli-\r\nIf-Range: %s\r\n",(long long)SBuf.st_size - 1,
              TimeRFC1123(SBuf.st_mtime).c_str());
       Req += Buf;
    }
@@ -772,9 +786,10 @@ bool HttpMethod::Go(bool ToFile,ServerState *Srv)
    
    if (Srv->In.WriteSpace() == true && ToFile == true && FileFD != -1)
       FD_SET(FileFD,&wfds);
-   
+
    // Add stdin
-   FD_SET(STDIN_FILENO,&rfds);
+   if (_config->FindB("Acquire::http::DependOnSTDIN", true) == true)
+      FD_SET(STDIN_FILENO,&rfds);
          
    // Figure out the max fd
    int MaxFd = FileFD;
@@ -914,15 +929,10 @@ bool HttpMethod::ServerDie(ServerState *Srv)
 // HttpMethod::DealWithHeaders - Handle the retrieved header data      /*{{{*/
 // ---------------------------------------------------------------------
 /* We look at the header data we got back from the server and decide what
-   to do. Returns 
-     0 - File is open,
-     1 - IMS hit
-     3 - Unrecoverable error 
-     4 - Error with error content page
-     5 - Unrecoverable non-server error (close the connection) 
-     6 - Try again with a new or changed URI
+   to do. Returns DealWithHeadersResult (see http.h for details).
  */
-int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv)
+HttpMethod::DealWithHeadersResult
+HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv)
 {
    // Not Modified
    if (Srv->Result == 304)
@@ -930,7 +940,7 @@ int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv)
       unlink(Queue->DestFile.c_str());
       Res.IMSHit = true;
       Res.LastModified = Queue->LastModified;
-      return 1;
+      return IMS_HIT;
    }
    
    /* Redirect
@@ -946,10 +956,26 @@ int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv)
            && Srv->Result != 304    // Not Modified
            && Srv->Result != 306))  // (Not part of HTTP/1.1, reserved)
    {
-      if (!Srv->Location.empty())
+      if (Srv->Location.empty() == true);
+      else if (Srv->Location[0] == '/' && Queue->Uri.empty() == false)
       {
-         NextURI = Srv->Location;
-         return 6;
+        URI Uri = Queue->Uri;
+        if (Uri.Host.empty() == false)
+        {
+           if (Uri.Port != 0)
+              strprintf(NextURI, "http://%s:%u", Uri.Host.c_str(), Uri.Port);
+           else
+              NextURI = "http://" + Uri.Host;
+        }
+        else
+           NextURI.clear();
+        NextURI.append(DeQuoteString(Srv->Location));
+        return TRY_AGAIN_OR_REDIRECT;
+      }
+      else
+      {
+         NextURI = DeQuoteString(Srv->Location);
+         return TRY_AGAIN_OR_REDIRECT;
       }
       /* else pass through for error message */
    }
@@ -958,10 +984,13 @@ int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv)
       failure */
    if (Srv->Result < 200 || Srv->Result >= 300)
    {
+      char err[255];
+      snprintf(err,sizeof(err)-1,"HttpError%i",Srv->Result);
+      SetFailReason(err);
       _error->Error("%u %s",Srv->Result,Srv->Code);
       if (Srv->HaveContent == true)
-        return 4;
-      return 3;
+        return ERROR_WITH_CONTENT_PAGE;
+      return ERROR_UNRECOVERABLE;
    }
 
    // This is some sort of 2xx 'data follows' reply
@@ -972,41 +1001,31 @@ int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv)
    delete File;
    File = new FileFd(Queue->DestFile,FileFd::WriteAny);
    if (_error->PendingError() == true)
-      return 5;
+      return ERROR_NOT_FROM_SERVER;
 
    FailFile = Queue->DestFile;
    FailFile.c_str();   // Make sure we dont do a malloc in the signal handler
    FailFd = File->Fd();
    FailTime = Srv->Date;
-      
-   // Set the expected size
-   if (Srv->StartPos >= 0)
-   {
-      Res.ResumePoint = Srv->StartPos;
-      if (ftruncate(File->Fd(),Srv->StartPos) < 0)
-        _error->Errno("ftruncate", _("Failed to truncate file"));
-   }
-      
-   // Set the start point
-   lseek(File->Fd(),0,SEEK_END);
 
    delete Srv->In.Hash;
    Srv->In.Hash = new Hashes;
-   
-   // Fill the Hash if the file is non-empty (resume)
-   if (Srv->StartPos > 0)
+
+   // Set the expected size and read file for the hashes
+   if (Srv->StartPos >= 0)
    {
-      lseek(File->Fd(),0,SEEK_SET);
-      if (Srv->In.Hash->AddFD(File->Fd(),Srv->StartPos) == false)
+      Res.ResumePoint = Srv->StartPos;
+      File->Truncate(Srv->StartPos);
+
+      if (Srv->In.Hash->AddFD(*File,Srv->StartPos) == false)
       {
         _error->Errno("read",_("Problem hashing file"));
-        return 5;
+        return ERROR_NOT_FROM_SERVER;
       }
-      lseek(File->Fd(),0,SEEK_END);
    }
    
    SetNonBlock(File->Fd(),true);
-   return 0;
+   return FILE_IS_OPEN;
 }
                                                                        /*}}}*/
 // HttpMethod::SigTerm - Handle a fatal signal                         /*{{{*/
@@ -1109,7 +1128,13 @@ int HttpMethod::Loop()
          do a WaitFd above.. Otherwise the FD is closed. */
       int Result = Run(true);
       if (Result != -1 && (Result != 0 || Queue == 0))
-        return 100;
+      {
+        if(FailReason.empty() == false ||
+           _config->FindB("Acquire::http::DependOnSTDIN", true) == true)
+           return 100;
+        else
+           return 0;
+      }
 
       if (Queue == 0)
         continue;
@@ -1147,11 +1172,11 @@ int HttpMethod::Loop()
       // Fetch the next URL header data from the server.
       switch (Server->RunHeaders())
       {
-        case 0:
+        case ServerState::RUN_HEADERS_OK:
         break;
         
         // The header data is bad
-        case 2:
+        case ServerState::RUN_HEADERS_PARSE_ERROR:
         {
            _error->Error(_("Bad header data"));
            Fail(true);
@@ -1161,7 +1186,7 @@ int HttpMethod::Loop()
         
         // The server closed a connection during the header get..
         default:
-        case 1:
+        case ServerState::RUN_HEADERS_IO_ERROR:
         {
            FailCounter++;
            _error->Discard();
@@ -1185,7 +1210,7 @@ int HttpMethod::Loop()
       switch (DealWithHeaders(Res,Server))
       {
         // Ok, the file is Open
-        case 0:
+        case FILE_IS_OPEN:
         {
            URIStart(Res);
 
@@ -1238,21 +1263,21 @@ int HttpMethod::Loop()
         }
         
         // IMS hit
-        case 1:
+        case IMS_HIT:
         {
            URIDone(Res);
            break;
         }
         
         // Hard server error, not found or something
-        case 3:
+        case ERROR_UNRECOVERABLE:
         {
            Fail();
            break;
         }
          
         // Hard internal error, kill the connection and fail
-        case 5:
+        case ERROR_NOT_FROM_SERVER:
         {
            delete File;
            File = 0;
@@ -1264,7 +1289,7 @@ int HttpMethod::Loop()
         }
 
         // We need to flush the data, the header is like a 404 w/ error text
-        case 4:
+        case ERROR_WITH_CONTENT_PAGE:
         {
            Fail();
            
@@ -1277,7 +1302,7 @@ int HttpMethod::Loop()
         }
         
          // Try again with a new URL
-         case 6:
+         case TRY_AGAIN_OR_REDIRECT:
          {
             // Clear rest of response if there is content
             if (Server->HaveContent)
@@ -1298,7 +1323,7 @@ int HttpMethod::Loop()
                StopRedirects = true;
             else
             {
-               for (StringVectorIterator I = R.begin(); I != R.end(); I++)
+               for (StringVectorIterator I = R.begin(); I != R.end(); ++I)
                   if (Queue->Uri == *I)
                   {
                      R[0] = "STOP";
@@ -1345,9 +1370,10 @@ bool HttpMethod::AutoDetectProxy()
    pid_t Process = ExecFork();
    if (Process == 0)
    {
+      close(Pipes[0]);
       dup2(Pipes[1],STDOUT_FILENO);
       SetCloseExec(STDOUT_FILENO,false);
-      
+
       const char *Args[2];
       Args[0] = AutoDetectProxyCmd.c_str();
       Args[1] = 0;
@@ -1357,10 +1383,18 @@ bool HttpMethod::AutoDetectProxy()
    }
    char buf[512];
    int InFd = Pipes[0];
-   if (read(InFd, buf, sizeof(buf)) < 0)
+   close(Pipes[1]);
+   int res = read(InFd, buf, sizeof(buf));
+   ExecWait(Process, "ProxyAutoDetect", true);
+
+   if (res < 0)
       return _error->Errno("read", "Failed to read");
-   ExecWait(Process, "ProxyAutoDetect");
-   
+   if (res == 0)
+      return _error->Warning("ProxyAutoDetect returned no data");
+
+   // add trailing \0
+   buf[res] = 0;
+
    if (Debug)
       clog << "auto detect command returned: '" << buf << "'" << endl;
 
@@ -1371,15 +1405,4 @@ bool HttpMethod::AutoDetectProxy()
 }
                                                                        /*}}}*/
 
-int main()
-{
-   setlocale(LC_ALL, "");
-   // ignore SIGPIPE, this can happen on write() if the socket
-   // closes the connection (this is dealt with via ServerDie())
-   signal(SIGPIPE, SIG_IGN);
-
-   HttpMethod Mth;
-   return Mth.Loop();
-}
-