]> git.saurik.com Git - apt.git/blobdiff - methods/server.cc
Fix endless loop in apt-get update that can cause disk fillup
[apt.git] / methods / server.cc
index a2128441cb78a005c108ceddd23c36142872515b..ba0a8864b46c18c774f7d2fc7e7864d7944e593d 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>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/strutl.h>
 
+#include <ctype.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
 #include <sys/stat.h>
 #include <sys/time.h>
-#include <utime.h>
+#include <time.h>
 #include <unistd.h>
-#include <signal.h>
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include <climits>
 #include <iostream>
+#include <limits>
 #include <map>
+#include <string>
+#include <vector>
 
-// Internet stuff
-#include <netdb.h>
-
-#include "config.h"
-#include "connect.h"
-#include "rfc2553emu.h"
-#include "http.h"
+#include "server.h"
 
 #include <apti18n.h>
                                                                        /*}}}*/
@@ -49,7 +44,8 @@ time_t ServerMethod::FailTime = 0;
 // ---------------------------------------------------------------------
 /* Returns 0 if things are OK, 1 if an IO error occurred and 2 if a header
    parse error occurred */
-ServerState::RunHeadersResult ServerState::RunHeaders(FileFd * const File)
+ServerState::RunHeadersResult ServerState::RunHeaders(FileFd * const File,
+                                                      const std::string &Uri)
 {
    State = Header;
    
@@ -59,6 +55,7 @@ ServerState::RunHeadersResult ServerState::RunHeaders(FileFd * const File)
    Minor = 0; 
    Result = 0; 
    Size = 0; 
+   JunkSize = 0;
    StartPos = 0;
    Encoding = Closes;
    HaveContent = false;
@@ -71,7 +68,7 @@ ServerState::RunHeadersResult ServerState::RunHeaders(FileFd * const File)
         continue;
 
       if (Owner->Debug == true)
-        clog << Data;
+        clog << "Answer for: " << Uri << endl << Data;
       
       for (string::const_iterator I = Data.begin(); I < Data.end(); ++I)
       {
@@ -86,7 +83,7 @@ ServerState::RunHeadersResult ServerState::RunHeaders(FileFd * const File)
       if (Result == 100)
         continue;
       
-      // Tidy up the connection persistance state.
+      // Tidy up the connection persistence state.
       if (Encoding == Closes && HaveContent == true)
         Persistent = false;
       
@@ -119,10 +116,10 @@ bool ServerState::HeaderLine(string Line)
    string::size_type Pos2 = Pos;
    while (Pos2 < Line.length() && isspace(Line[Pos2]) != 0)
       Pos2++;
-      
+
    string Tag = string(Line,0,Pos);
    string Val = string(Line,Pos2);
-   
+
    if (stringcasecmp(Tag.c_str(),Tag.c_str()+4,"HTTP") == 0)
    {
       // Evil servers return no version
@@ -132,7 +129,7 @@ bool ServerState::HeaderLine(string Line)
         if (elements == 3)
         {
            Code[0] = '\0';
-           if (Owner->Debug == true)
+           if (Owner != NULL && Owner->Debug == true)
               clog << "HTTP server doesn't give Reason-Phrase for " << Result << std::endl;
         }
         else if (elements != 4)
@@ -146,7 +143,7 @@ bool ServerState::HeaderLine(string Line)
            return _error->Error(_("The HTTP server sent an invalid reply header"));
       }
 
-      /* Check the HTTP response header to get the default persistance
+      /* Check the HTTP response header to get the default persistence
          state. */
       if (Major < 1)
         Persistent = false;
@@ -159,23 +156,30 @@ bool ServerState::HeaderLine(string Line)
       }
 
       return true;
-   }      
-      
+   }
+
    if (stringcasecmp(Tag,"Content-Length:") == 0)
    {
       if (Encoding == Closes)
         Encoding = Stream;
       HaveContent = true;
-      
-      // The length is already set from the Content-Range header
-      if (StartPos != 0)
-        return true;
 
-      Size = strtoull(Val.c_str(), NULL, 10);
-      if (Size >= std::numeric_limits<unsigned long long>::max())
+      unsigned long long * DownloadSizePtr = &DownloadSize;
+      if (Result == 416)
+        DownloadSizePtr = &JunkSize;
+
+      *DownloadSizePtr = strtoull(Val.c_str(), NULL, 10);
+      if (*DownloadSizePtr >= std::numeric_limits<unsigned long long>::max())
         return _error->Errno("HeaderLine", _("The HTTP server sent an invalid Content-Length header"));
-      else if (Size == 0)
+      else if (*DownloadSizePtr == 0)
         HaveContent = false;
+
+      // On partial content (206) the Content-Length less than the real
+      // size, so do not set it here but leave that to the Content-Range
+      // header instead
+      if(Result != 206 && Size == 0)
+         Size = DownloadSize;
+
       return true;
    }
 
@@ -184,29 +188,29 @@ bool ServerState::HeaderLine(string Line)
       HaveContent = true;
       return true;
    }
-   
+
    if (stringcasecmp(Tag,"Content-Range:") == 0)
    {
       HaveContent = true;
 
       // ยง14.16 says 'byte-range-resp-spec' should be a '*' in case of 416
       if (Result == 416 && sscanf(Val.c_str(), "bytes */%llu",&Size) == 1)
-      {
-        StartPos = 1; // ignore Content-Length, it would override Size
-        HaveContent = false;
-      }
+        ; // we got the expected filesize which is all we wanted
       else 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 long long)StartPos > Size)
         return _error->Error(_("This HTTP server has broken range support"));
+
+      // figure out what we will download
+      DownloadSize = Size - StartPos;
       return true;
    }
-   
+
    if (stringcasecmp(Tag,"Transfer-Encoding:") == 0)
    {
       HaveContent = true;
       if (stringcasecmp(Val,"chunked") == 0)
-        Encoding = Chunked;      
+        Encoding = Chunked;
       return true;
    }
 
@@ -218,7 +222,7 @@ bool ServerState::HeaderLine(string Line)
         Persistent = true;
       return true;
    }
-   
+
    if (stringcasecmp(Tag,"Last-Modified:") == 0)
    {
       if (RFC1123StrToTime(Val.c_str(), Date) == false)
@@ -291,11 +295,15 @@ ServerMethod::DealWithHeaders(FetchResult &Res)
       }
       else
       {
-         NextURI = DeQuoteString(Server->Location);
-         URI tmpURI = NextURI;
-         // Do not allow a redirection to switch protocol
-         if (tmpURI.Access == "http")
-            return TRY_AGAIN_OR_REDIRECT;
+        NextURI = DeQuoteString(Server->Location);
+        URI tmpURI = NextURI;
+        URI Uri = Queue->Uri;
+        // same protocol redirects are okay
+        if (tmpURI.Access == Uri.Access)
+           return TRY_AGAIN_OR_REDIRECT;
+        // as well as http to https
+        else if (Uri.Access == "http" && tmpURI.Access == "https")
+           return TRY_AGAIN_OR_REDIRECT;
       }
       /* else pass through for error message */
    }
@@ -308,9 +316,15 @@ ServerMethod::DealWithHeaders(FetchResult &Res)
         if ((unsigned long long)SBuf.st_size == Server->Size)
         {
            // the file is completely downloaded, but was not moved
+           if (Server->HaveContent == true)
+           {
+              // Send to error page to dev/null
+              FileFd DevNull("/dev/null",FileFd::WriteExists);
+              Server->RunData(&DevNull);
+           }
+           Server->HaveContent = false;
            Server->StartPos = Server->Size;
            Server->Result = 200;
-           Server->HaveContent = false;
         }
         else if (unlink(Queue->DestFile.c_str()) == 0)
         {
@@ -362,20 +376,20 @@ ServerMethod::DealWithHeaders(FetchResult &Res)
                                                                        /*}}}*/
 // ServerMethod::SigTerm - Handle a fatal signal                       /*{{{*/
 // ---------------------------------------------------------------------
-/* This closes and timestamps the open file. This is neccessary to get
+/* This closes and timestamps the open file. This is necessary to get
    resume behavoir on user abort */
 void ServerMethod::SigTerm(int)
 {
    if (FailFd == -1)
       _exit(100);
+
+   struct timeval times[2];
+   times[0].tv_sec = FailTime;
+   times[1].tv_sec = FailTime;
+   times[0].tv_usec = times[1].tv_usec = 0;
+   utimes(FailFile.c_str(), times);
    close(FailFd);
-   
-   // Timestamp
-   struct utimbuf UBuf;
-   UBuf.actime = FailTime;
-   UBuf.modtime = FailTime;
-   utime(FailFile.c_str(),&UBuf);
-   
+
    _exit(100);
 }
                                                                        /*}}}*/
@@ -409,7 +423,7 @@ bool ServerMethod::Fetch(FetchItem *)
    }
    
    return true;
-};
+}
                                                                        /*}}}*/
 // ServerMethod::Loop - Main loop                                      /*{{{*/
 int ServerMethod::Loop()
@@ -479,7 +493,7 @@ int ServerMethod::Loop()
       Fetch(0);
       
       // Fetch the next URL header data from the server.
-      switch (Server->RunHeaders(File))
+      switch (Server->RunHeaders(File, Queue->Uri))
       {
         case ServerState::RUN_HEADERS_OK:
         break;
@@ -539,11 +553,10 @@ int ServerMethod::Loop()
            File = 0;
            
            // Timestamp
-           struct utimbuf UBuf;
-           time(&UBuf.actime);
-           UBuf.actime = Server->Date;
-           UBuf.modtime = Server->Date;
-           utime(Queue->DestFile.c_str(),&UBuf);
+           struct timeval times[2];
+           times[0].tv_sec = times[1].tv_sec = Server->Date;
+           times[0].tv_usec = times[1].tv_usec = 0;
+           utimes(Queue->DestFile.c_str(), times);
 
            // Send status to APT
            if (Result == true)