- apply Progeny's redir and authentication patch
authorpmatilai <pmatilai>
Tue, 17 Jan 2006 18:42:37 +0000 (18:42 +0000)
committerpmatilai <pmatilai>
Tue, 17 Jan 2006 18:42:37 +0000 (18:42 +0000)
apt-pkg/acquire-method.cc
apt-pkg/acquire-method.h
apt-pkg/acquire-worker.cc
apt-pkg/acquire-worker.h
apt-pkg/acquire.cc
apt-pkg/acquire.h
cmdline/acqprogress.cc
cmdline/acqprogress.h
methods/http.cc
methods/http.h

index 0b205ba..1f2783a 100644 (file)
@@ -26,6 +26,7 @@
 #include <apt-pkg/hashes.h>
 
 #include <iostream>
+#include <sstream>
 #include <stdarg.h>
 #include <stdio.h>
 #include <unistd.h>
@@ -138,24 +139,22 @@ void pkgAcqMethod::URIStart(FetchResult &Res)
 {
    if (Queue == 0)
       abort();
-   
-   char S[1024] = "";
-   char *End = S;
-   
-   End += snprintf(S,sizeof(S),"200 URI Start\nURI: %s\n",Queue->Uri.c_str());
+
+   ostringstream s;
+
+   s << "200 URI Start\nURI: " << Queue->Uri << "\n";
    if (Res.Size != 0)
-      End += snprintf(End,sizeof(S)-4 - (End - S),"Size: %lu\n",Res.Size);
-   
+      s << "Size: " << Res.Size << "\n";
+
    if (Res.LastModified != 0)
-      End += snprintf(End,sizeof(S)-4 - (End - S),"Last-Modified: %s\n",
-                     TimeRFC1123(Res.LastModified).c_str());
-   
+      s << "Last-Modified: " << TimeRFC1123(Res.LastModified) << "\n";
+
    if (Res.ResumePoint != 0)
-      End += snprintf(End,sizeof(S)-4 - (End - S),"Resume-Point: %lu\n",
-                     Res.ResumePoint);
-      
-   strcat(End,"\n");
-   if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
+      s << "Resume-Point: " << Res.ResumePoint << "\n";
+
+   s << "\n";
+   string S = s.str();
+   if (write(STDOUT_FILENO,S.c_str(),S.size()) != (ssize_t)S.size())
       exit(100);
 }
                                                                        /*}}}*/
@@ -167,63 +166,57 @@ void pkgAcqMethod::URIDone(FetchResult &Res, FetchResult *Alt)
    if (Queue == 0)
       abort();
    
-   char S[1024] = "";
-   char *End = S;
-   
-   End += snprintf(S,sizeof(S),"201 URI Done\nURI: %s\n",Queue->Uri.c_str());
+   ostringstream s;
 
+   s << "201 URI Done\nURI: " << Queue->Uri << "\n";
+   
    if (Res.Filename.empty() == false)
-      End += snprintf(End,sizeof(S)-50 - (End - S),"Filename: %s\n",Res.Filename.c_str());
+      s << "Filename: " << Res.Filename << "\n";
    
    if (Res.Size != 0)
-      End += snprintf(End,sizeof(S)-50 - (End - S),"Size: %lu\n",Res.Size);
+      s << "Size: " << Res.Size << "\n";
    
    if (Res.LastModified != 0)
-      End += snprintf(End,sizeof(S)-50 - (End - S),"Last-Modified: %s\n",
-                     TimeRFC1123(Res.LastModified).c_str());
+      s << "Last-Modified: " << TimeRFC1123(Res.LastModified) << "\n";
 
    if (Res.MD5Sum.empty() == false)
-      End += snprintf(End,sizeof(S)-50 - (End - S),"MD5-Hash: %s\n",Res.MD5Sum.c_str());
+      s << "MD5-Hash: " << Res.MD5Sum << "\n";
    if (Res.SHA1Sum.empty() == false)
-      End += snprintf(End,sizeof(S)-50 - (End - S),"SHA1-Hash: %s\n",Res.SHA1Sum.c_str());
+      s << "SHA1-Hash: " << Res.SHA1Sum << "\n";
 
    // CNC:2002-07-04
    if (Res.SignatureFP.empty() == false)
-      End += snprintf(End,sizeof(S)-50 - (End - S),"Signature-Fingerprint: %s\n",Res.SignatureFP.c_str());
+      s << "Signature-Fingerprint: " << Res.SignatureFP << "\n";
 
    if (Res.ResumePoint != 0)
-      End += snprintf(End,sizeof(S)-50 - (End - S),"Resume-Point: %lu\n",
-                     Res.ResumePoint);
+      s << "Resume-Point: " << Res.ResumePoint << "\n";
 
    if (Res.IMSHit == true)
-      strcat(End,"IMS-Hit: true\n");
-   End = S + strlen(S);
-   
+      s << "IMS-Hit: true\n";
+      
    if (Alt != 0)
    {
       if (Alt->Filename.empty() == false)
-        End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-Filename: %s\n",Alt->Filename.c_str());
+        s << "Alt-Filename: " << Alt->Filename << "\n";
       
       if (Alt->Size != 0)
-        End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-Size: %lu\n",Alt->Size);
+        s << "Alt-Size: " << Alt->Size << "\n";
       
       if (Alt->LastModified != 0)
-        End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-Last-Modified: %s\n",
-                        TimeRFC1123(Alt->LastModified).c_str());
+        s << "Alt-Last-Modified: " << TimeRFC1123(Alt->LastModified) << "\n";
       
       if (Alt->MD5Sum.empty() == false)
-        End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-MD5-Hash: %s\n",
-                        Alt->MD5Sum.c_str());
+        s << "Alt-MD5-Hash: " << Alt->MD5Sum << "\n";
       if (Alt->SHA1Sum.empty() == false)
-        End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-SHA1-Hash: %s\n",
-                        Alt->SHA1Sum.c_str());
+        s << "Alt-SHA1-Hash: " << Alt->SHA1Sum << "\n";
       
       if (Alt->IMSHit == true)
-        strcat(End,"Alt-IMS-Hit: true\n");
+        s << "Alt-IMS-Hit: true\n";
    }
    
-   strcat(End,"\n");
-   if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
+   s << "\n";
+   string S = s.str();
+   if (write(STDOUT_FILENO,S.c_str(),S.size()) != (ssize_t)S.size())
       exit(100);
 
    // Dequeue
@@ -283,6 +276,66 @@ bool pkgAcqMethod::MediaFail(string Required,string Drive)
         return !StringToBool(LookupTag(Message,"Fail"),false);
       }
       
+      Messages.push_back(Message);
+   }   
+}
+                                                                       /*}}}*/
+// AcqMethod::NeedAuth - Request authentication                                /*{{{*/
+// ---------------------------------------------------------------------
+/* This sends a 404 Authenticate message to the APT and waits for it
+   to be ackd */
+bool pkgAcqMethod::NeedAuth(string Description,string &User,string &Pass)
+{
+   char S[1024];
+   snprintf(S,sizeof(S),"404 Authenticate\nDescription: %s\n\n",
+           Description.c_str());
+
+   if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
+      exit(100);
+   
+   vector<string> MyMessages;
+   
+   /* Here we read messages until we find a 604, each non 604 message is
+      appended to the main message list for later processing */
+   while (1)
+   {
+      if (WaitFd(STDIN_FILENO) == false)
+        return false;
+      
+      if (ReadMessages(STDIN_FILENO,MyMessages) == false)
+        return false;
+
+      string Message = MyMessages.front();
+      MyMessages.erase(MyMessages.begin());
+      
+      // Fetch the message number
+      char *End;
+      int Number = strtol(Message.c_str(),&End,10);
+      if (End == Message.c_str())
+      {         
+        cerr << "Malformed message!" << endl;
+        exit(100);
+      }
+
+      // Change ack
+      if (Number == 604)
+      {
+        while (MyMessages.empty() == false)
+        {
+           Messages.push_back(MyMessages.front());
+           MyMessages.erase(MyMessages.begin());
+        }
+
+        if (StringToBool(LookupTag(Message,"Fail"),false) == false)
+        {
+           User = LookupTag(Message,"User");
+           Pass = LookupTag(Message,"Password");
+           return true;
+        }
+        else
+           return false;
+      }
+      
       Messages.push_back(Message);
    }   
 }
@@ -449,18 +502,51 @@ void pkgAcqMethod::Status(const char *Format,...)
    va_list args;
    va_start(args,Format);
 
+   ostringstream s;
+   s << "102 Status\nURI: " << CurrentURI << "\nMessage: ";
+
    // sprintf the description
-   char S[1024];
-   unsigned int Len = snprintf(S,sizeof(S)-4,"102 Status\nURI: %s\n"
-                              "Message: ",CurrentURI.c_str());
+   char Buf[1024];
+   vsnprintf(Buf,sizeof(Buf)-4,Format,args);
+   s << Buf << "\n\n";
 
-   vsnprintf(S+Len,sizeof(S)-4-Len,Format,args);
-   strcat(S,"\n\n");
-   
-   if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
+   string S = s.str();
+   if (write(STDOUT_FILENO,S.c_str(),S.size()) != (ssize_t)S.size())
       exit(100);
 }
                                                                        /*}}}*/
+// AcqMethod::Redirect - Send a redirect message                       /*{{{*/
+// ---------------------------------------------------------------------
+/* This method sends the redirect message and also manipulates the queue
+   to keep the pipeline synchronized. */
+void pkgAcqMethod::Redirect(const string &NewURI)
+{
+   string CurrentURI = "<UNKNOWN>";
+   if (Queue != 0)
+      CurrentURI = Queue->Uri;
+
+   ostringstream s;
+   s << "103 Redirect\nURI: " << CurrentURI << "\nNew-URI: " << NewURI 
+     << "\n\n";
+
+   string S = s.str();
+   if (write(STDOUT_FILENO,S.c_str(),S.size()) != (ssize_t)S.size())
+      exit(100);
+
+   // Change the URI for the request.
+   Queue->Uri = NewURI;
+
+   /* To keep the pipeline synchronized, move the current request to
+      the end of the queue, past the end of the current pipeline. */
+   FetchItem *I;
+   for (I = Queue; I->Next != 0; I = I->Next) ;
+   I->Next = Queue;
+   Queue = Queue->Next;
+   I->Next->Next = 0;
+   if (QueueBack == 0)
+      QueueBack = I->Next;
+}
+                                                                       /*}}}*/
 
 // AcqMethod::FetchResult::FetchResult - Constructor                   /*{{{*/
 // ---------------------------------------------------------------------
index 045c62e..dcca197 100644 (file)
@@ -71,6 +71,7 @@ class pkgAcqMethod
    void URIStart(FetchResult &Res);
    void URIDone(FetchResult &Res,FetchResult *Alt = 0);
    bool MediaFail(string Required,string Drive);
+   bool NeedAuth(string Description,string &User,string &Pass);
    virtual void Exit() {};
    // CNC:2004-04-27
    virtual string PreferredURI() { return ""; };
@@ -85,7 +86,8 @@ class pkgAcqMethod
 
    void Log(const char *Format,...);
    void Status(const char *Format,...);
-   
+   void Redirect(const string &NewURI);
+
    int Run(bool Single = false);
    inline void SetFailExtraMsg(string Msg) {FailExtra = Msg;};
    
index 93ddde1..c2673a7 100644 (file)
@@ -241,6 +241,20 @@ bool pkgAcquire::Worker::RunMessages()
         case 179:
         Config->PreferredURI = LookupTag(Message, "PreferredURI");
         break;
+
+        // 103 Redirect
+        case 103:
+        {
+           if (Itm == 0)
+           {
+              _error->Error("Method gave invalid 103 Redirect message");
+              break;
+           }
+
+           string NewURI = LookupTag(Message,"New-URI",URI.c_str());
+           Itm->URI = NewURI;
+           break;
+        }
            
         // 200 URI Start
         case 200:
@@ -344,6 +358,11 @@ bool pkgAcquire::Worker::RunMessages()
         case 403:
         MediaChange(Message); 
         break;
+
+        // 404 Authenticate
+        case 404:
+        Authenticate(Message);
+        break;
       }      
    }
    return true;
@@ -412,6 +431,34 @@ bool pkgAcquire::Worker::MediaChange(string Message)
    return true;
 }
                                                                        /*}}}*/
+// Worker::Authenticate - Request authentication                               /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool pkgAcquire::Worker::Authenticate(string Message)
+{
+   string User, Pass;
+   if (Log == 0 || Log->Authenticate(LookupTag(Message,"Description"),
+                                    User,Pass) == false)
+   {
+      char S[300];
+      snprintf(S,sizeof(S),"604 Authenticated\nFailed: true\n\n");
+      if (Debug == true)
+        clog << " -> " << Access << ':' << QuoteString(S,"\n") << endl;
+      OutQueue += S;
+      OutReady = true;
+      return true;
+   }
+
+   char S[300];
+   snprintf(S,sizeof(S),"604 Authenticated\nUser: %s\nPassword: %s\n\n",
+           User.c_str(), Pass.c_str());
+   if (Debug == true)
+      clog << " -> " << Access << ':' << QuoteString(S,"\n") << endl;
+   OutQueue += S;
+   OutReady = true;
+   return true;
+}
+                                                                       /*}}}*/
 // Worker::SendConfiguration - Send the config to the method           /*{{{*/
 // ---------------------------------------------------------------------
 /* */
index 242d0eb..cdb2565 100644 (file)
@@ -62,6 +62,7 @@ class pkgAcquire::Worker
    bool Capabilities(string Message);
    bool SendConfiguration();
    bool MediaChange(string Message);
+   bool Authenticate(string Message);
    
    bool MethodFailure();
    void ItemDone();
index 30497f3..233247a 100644 (file)
@@ -871,5 +871,17 @@ void pkgAcquireStatus::Fetched(unsigned long Size,unsigned long Resume)
 {   
    FetchedBytes += Size - Resume;
 }
+
+
+                                                                       /*}}}*/
+// AcquireStatus::Authenticate - Called to authenticate                        /*{{{*/
+// ---------------------------------------------------------------------
+/* This is used to fetch a username and password from the user */
+bool pkgAcquireStatus::Authenticate(string Desc,string &User,string &Pass)
+{
+   /* The default behavior for all clients is to refuse to authenticate
+      interactively; this preserves backwards compatibility. */
+   return false;
+}
                                                                        /*}}}*/
 // vim:sts=3:sw=3
index e9b4956..b29c8f6 100644 (file)
@@ -273,6 +273,9 @@ class pkgAcquireStatus
    
    // Called to change media
    virtual bool MediaChange(string Media,string Drive) = 0;
+
+   // Called to authenticate
+   virtual bool Authenticate(string Desc,string &User,string &Pass);
    
    // Each of these is called by the workers when an event occures
    virtual void IMSHit(pkgAcquire::ItemDesc &/*Itm*/) {};
index dbc8ade..5a6198d 100644 (file)
@@ -18,6 +18,8 @@
     
 #include <stdio.h>
 #include <signal.h>
+#include <termios.h>
+#include <unistd.h>
 #include <iostream>
                                                                        /*}}}*/
 
@@ -277,6 +279,62 @@ bool AcqTextStatus::MediaChange(string Media,string Drive)
    while (C != '\n' && C != '\r')
       read(STDIN_FILENO,&C,1);
    
+   Update = true;
+   return true;
+}
+                                                                       /*}}}*/
+// AcqTextStatus::Authenticate - Authenticate the user                 /*{{{*/
+// ---------------------------------------------------------------------
+/* Prompt for a username and password */
+bool AcqTextStatus::Authenticate(string Desc,string &User,string &Pass)
+{
+   if (Quiet > 0)
+      return false;
+
+   cout << '\r' << BlankLine << '\r';
+
+   ioprintf(cout,_("Please login to %s\nUsername: "), Desc.c_str());
+   cout << flush;
+
+   char S[1024];
+   char C = 0;
+   size_t idx = 0;
+   while (C != '\n' && C != '\r' && idx < (sizeof(S) - 1))
+   {
+      read(STDIN_FILENO,&C,1);
+      S[idx++] = C;
+   }
+   S[--idx] = '\0';
+   User = S;
+
+   ioprintf(cout,_("Password: "));
+   cout << flush;
+
+   // Turn off echo for entering the password
+   struct termios TermIO;
+   tcgetattr(STDIN_FILENO, &TermIO);
+
+   struct termios TermIO_noecho;
+   TermIO_noecho = TermIO;
+   TermIO_noecho.c_lflag &= !ECHO;
+   tcsetattr(STDIN_FILENO, TCSANOW, &TermIO_noecho);
+
+   C = 0;
+   idx = 0;
+   while (C != '\n' && C != '\r' && idx < (sizeof(S) - 1))
+   {
+      read(STDIN_FILENO,&C,1);
+      S[idx++] = C;
+   }
+   S[--idx] = '\0';
+   Pass = S;
+
+   // Turn echo back on
+   tcsetattr(STDIN_FILENO, TCSANOW, &TermIO);
+
+   ioprintf(cout,_("\n"));
+   cout << flush;
+   
    Update = true;
    return true;
 }
index 48f90ae..ba77b44 100644 (file)
@@ -22,6 +22,7 @@ class AcqTextStatus : public pkgAcquireStatus
    public:
    
    virtual bool MediaChange(string Media,string Drive);
+   virtual bool Authenticate(string Desc,string &User,string &Pass);
    virtual void IMSHit(pkgAcquire::ItemDesc &Itm);
    virtual void Fetch(pkgAcquire::ItemDesc &Itm);
    virtual void Done(pkgAcquire::ItemDesc &Itm);
index 8e8e41d..e9a020e 100644 (file)
@@ -39,6 +39,7 @@
 #include <errno.h>
 #include <string.h>
 #include <iostream>
+#include <map>
 
 // Internet stuff
 #include <netdb.h>
@@ -59,6 +60,7 @@ int HttpMethod::FailFd = -1;
 time_t HttpMethod::FailTime = 0;
 unsigned long PipelineDepth = 10;
 unsigned long TimeOut = 120;
+bool ChokePipe = true;
 bool Debug = false;
 
 // CircleBuf::CircleBuf - Circular input buffer                                /*{{{*/
@@ -594,6 +596,31 @@ bool ServerState::HeaderLine(string Line)
       return true;
    }
 
+   if (stringcasecmp(Tag,"Location:") == 0)
+   {
+      Location = Val;
+      return true;
+   }
+
+   if (stringcasecmp(Tag,"WWW-Authenticate:") == 0 ||
+       stringcasecmp(Tag,"Proxy-Authenticate:") == 0)
+   {
+      string::size_type SplitPoint = Val.find(' ');
+      string AuthType = Val.substr(0, SplitPoint);
+      string RealmStr = Val.substr(SplitPoint + 1, 
+                                  Val.length() - SplitPoint - 1);
+      SplitPoint = RealmStr.find('=');
+      string FoundRealm = RealmStr.substr(SplitPoint, 
+                                         RealmStr.length() - SplitPoint);
+
+      if (stringcasecmp(Tag,"WWW-Authenticate:") == 0)
+        Realm = FoundRealm;
+      else
+        ProxyRealm = FoundRealm;
+
+      return true;
+   }
+
    return true;
 }
                                                                        /*}}}*/
@@ -860,7 +887,8 @@ bool HttpMethod::ServerDie(ServerState *Srv)
      1 - IMS hit
      3 - Unrecoverable error 
      4 - Error with error content page
-     5 - Unrecoverable non-server error (close the connection) */
+     5 - Unrecoverable non-server error (close the connection)
+     6 - Try again with a new or changed URI  */
 int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv)
 {
    // Not Modified
@@ -871,7 +899,96 @@ int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv)
       Res.LastModified = Queue->LastModified;
       return 1;
    }
-   
+
+   /* Redirect
+    *
+    * Note that it is only OK for us to treat all redirection the same
+    * because we *always* use GET, not other HTTP methods.  There are
+    * three redirection codes for which it is not appropriate that we
+    * redirect.  Pass on those codes so the error handling kicks in.
+    */
+   if ((Srv->Result > 300 && Srv->Result < 400)
+       && (Srv->Result != 300       // Multiple Choices
+           && Srv->Result != 304    // Not Modified
+           && Srv->Result != 306))  // (Not part of HTTP/1.1, reserved)
+   {
+      if (!Srv->Location.empty())
+      {
+         NextURI = Srv->Location;
+         return 6;
+      }
+      else
+        return 3;
+   }
+
+   // Authentication
+   if (Srv->Result == 401)
+   {
+      string Description;
+      string AuthUser, AuthPass;
+      vector<AuthRec>::iterator CurrentAuth;
+      URI ParsedURI(Queue->Uri);
+
+      // Have we had to log in to this site before?
+      if (ParsedURI.User.empty())
+      {
+        for (CurrentAuth = AuthList.begin(); CurrentAuth != AuthList.end();
+             CurrentAuth++)
+           if (CurrentAuth->Host == Srv->ServerName.Host &&
+               CurrentAuth->Realm == Srv->Realm)
+           {
+              AuthUser = CurrentAuth->User;
+              AuthPass = CurrentAuth->Password;
+              break;
+           }
+      }
+      else
+        CurrentAuth = AuthList.end();
+
+      // Nope - get username and password
+      if (CurrentAuth == AuthList.end())
+      {
+        Description = ParsedURI.Host + ":" + Srv->Realm;
+
+#ifdef WITH_SSL
+        if (ParsedURI.Access == "https")
+           Description += string(" (secure)");
+#endif
+
+        if (NeedAuth(Description, AuthUser, AuthPass) == true)
+        {
+           // Got new credentials; save them
+           AuthRec NewAuthInfo;
+
+           NewAuthInfo.Host = Srv->ServerName.Host;
+           NewAuthInfo.Realm = Srv->Realm;
+           NewAuthInfo.User = AuthUser;
+           NewAuthInfo.Password = AuthPass;
+
+           for (CurrentAuth = AuthList.begin(); CurrentAuth != AuthList.end();
+                CurrentAuth++)
+              if (CurrentAuth->Host == Srv->ServerName.Host &&
+                  CurrentAuth->Realm == Srv->Realm)
+              {
+                 *CurrentAuth = NewAuthInfo;
+                 break;
+              }
+           
+           if (CurrentAuth == AuthList.end())
+              AuthList.push_back(NewAuthInfo);
+        }
+        else
+           // Interactive auth failed
+           return 4;
+      }
+
+      // Try the same URI again, with credentials this time
+      ParsedURI.User = AuthUser;
+      ParsedURI.Password = AuthPass;
+      NextURI = ParsedURI;
+      return 6;
+   }
+
    /* We have a reply we dont handle. This should indicate a perm server
       failure */
    if (Srv->Result < 200 || Srv->Result >= 300)
@@ -963,10 +1080,21 @@ bool HttpMethod::Fetch(FetchItem *)
       // If pipelining is disabled, we only queue 1 request
       if (Server->Pipeline == false && Depth >= 0)
         break;
+
+      // If we're choking the pipeline, we only queue 1 request
+      if (ChokePipe == true && Depth >= 0)
+      {
+        ChokePipe = false;
+        break;
+      }
       
       // Make sure we stick with the same server
       if (Server->Comp(I->Uri) == false)
+      {
+        ChokePipe = true;
         break;
+      }
+
       if (QueueBack == I)
         Tail = true;
       if (Tail == true)
@@ -1001,12 +1129,17 @@ bool HttpMethod::Configuration(string Message)
 /* */
 int HttpMethod::Loop()
 {
+   typedef vector<string> StringVector;
+   typedef vector<string>::iterator StringVectorIterator;
+   map<string, StringVector> Redirected;
+
    signal(SIGTERM,SigTerm);
    signal(SIGINT,SigTerm);
    
    Server = 0;
    
    int FailCounter = 0;
+
    while (1)
    {      
       // We have no commands, wait for some to arrive
@@ -1171,6 +1304,46 @@ int HttpMethod::Loop()
            File = 0;
            break;
         }
+
+        // Try again with a new URL
+        case 6:
+        {
+           // Clear rest of response if there is content
+           if (Server->HaveContent)
+           {
+              File = new FileFd("/dev/null",FileFd::WriteExists);
+              Server->RunData();
+              delete File;
+              File = 0;
+           }
+
+           /* Detect redirect loops.  No more redirects are allowed
+              after the same URI is seen twice in a queue item. */
+           StringVector &R = Redirected[Queue->DestFile];
+           bool StopRedirects = false;
+           if (R.size() == 0)
+              R.push_back(Queue->Uri);
+           else if (R[0] == "STOP")
+              StopRedirects = true;
+           else
+           {
+              for (StringVectorIterator I = R.begin(); I != R.end(); I++)
+                 if (Queue->Uri == *I)
+                 {
+                    R[0] = "STOP";
+                    break;
+                 }
+
+              R.push_back(Queue->Uri);
+           }
+
+           if (StopRedirects == false)
+              Redirect(NextURI);
+           else
+              Fail();
+
+           break;
+        }
         
         default:
         Fail(_("Internal error"));
index cd028db..8ee29b7 100644 (file)
@@ -94,6 +94,8 @@ struct ServerState
    enum {Chunked,Stream,Closes} Encoding;
    enum {Header, Data} State;
    bool Persistent;
+   string Location;
+   string Realm, ProxyRealm;
    
    // This is a Persistent attribute of the server itself.
    bool Pipeline;
@@ -123,6 +125,14 @@ struct ServerState
 
 class HttpMethod : public pkgAcqMethod
 {
+   struct AuthRec
+   {
+      string Host;
+      string Realm;
+      string User;
+      string Password;
+   };
+
    void SendReq(FetchItem *Itm,CircleBuf &Out);
    bool Go(bool ToFile,ServerState *Srv);
    bool Flush(ServerState *Srv);
@@ -137,6 +147,9 @@ class HttpMethod : public pkgAcqMethod
    static int FailFd;
    static time_t FailTime;
    static void SigTerm(int);
+
+   string NextURI;
+   vector<AuthRec> AuthList;
    
    public:
    friend class ServerState;