[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

APT HTTP Digest proxy



Hi,
I have a little patch that enable digest authorization scheme with HTTP proxies. It allows to use proxies with only the digest authorization(I had some problems with apt and the university LAN, they use squid with the digest scheme). 
I patched against apt version 0.5.28.6, files http.cc and http.h are in the methods subdirectory.

Best Regards,
Giuseppe Scrivano



--- http.cc	2005-05-17 21:04:25.000000000 +0200
+++ ../../../http.cc	2005-05-17 21:14:34.000000000 +0200
@@ -1,6 +1,7 @@
 // -*- mode: cpp; mode: fold -*-
 // Description								/*{{{*/
 // $Id: http.cc,v 1.59 2004/05/08 19:42:35 mdz Exp $
+// Added Digest Support for HTTP proxies by Giuseppe Scrivano<gscrivano@gmail.com> 2005/05/17
 /* ######################################################################
 
    HTTP Aquire Method - This is the HTTP aquire method for APT.
@@ -37,10 +38,10 @@
 #include <signal.h>
 #include <stdio.h>
 #include <errno.h>
-#include <string.h>
 #include <iostream>
 #include <apti18n.h>
-
+#include <string>
+#include <sstream>
 // Internet stuff
 #include <netdb.h>
 
@@ -198,41 +199,41 @@
 /* This copies till the first empty line */
 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++)
-   {      
+  // We cheat and assume it is unneeded to have more than one buffer load
+  for (unsigned long I = OutP; I < InP; I++)
+    {      
       if (Buf[I%Size] != '\n')
-	 continue;
+        continue;
       ++I;
       if (I < InP  && Buf[I%Size] == '\r')
-         ++I;
+        ++I;
       
       if (Single == false)
-      {
-	 if (Buf[I%Size] != '\n')
-	    continue;
-         ++I;
-         if (I < InP  && Buf[I%Size] == '\r')
+        {
+          if (Buf[I%Size] != '\n')
+            continue;
+          ++I;
+          if (I < InP  && Buf[I%Size] == '\r')
             ++I;
-      }
+        }
       
       if (I > InP)
-	 I = InP;
+        I = InP;
       
       Data = "";
       while (OutP < I)
-      {
-	 unsigned long Sz = LeftWrite();
-	 if (Sz == 0)
-	    return false;
-	 if (I - OutP < LeftWrite())
-	    Sz = I - OutP;
-	 Data += string((char *)(Buf + (OutP%Size)),Sz);
-	 OutP += Sz;
-      }
+        {
+          unsigned long Sz = LeftWrite();
+          if (Sz == 0)
+            return false;
+          if (I - OutP < LeftWrite())
+            Sz = I - OutP;
+          Data += string((char *)(Buf + (OutP%Size)),Sz);
+          OutP += Sz;
+        }
       return true;
-   }      
-   return false;
+    }      
+  return false;
 }
 									/*}}}*/
 // CircleBuf::Stats - Print out stats information			/*{{{*/
@@ -306,16 +307,15 @@
    if (Proxy.empty() == true || Proxy.Host.empty() == true)
    {
       if (ServerName.Port != 0)
-	 Port = ServerName.Port;
+        Port = ServerName.Port;
       Host = ServerName.Host;
    }
    else
    {
       if (Proxy.Port != 0)
-	 Port = Proxy.Port;
+        Port = Proxy.Port;
       Host = Proxy.Host;
    }
-   
    // Connect to the remote server
    if (Connect(Host,Port,"http",80,ServerFd,TimeOut,Owner) == false)
       return false;
@@ -330,19 +330,20 @@
 {
    close(ServerFd);
    ServerFd = -1;
+   Reset();
    return true;
 }
 									/*}}}*/
 // ServerState::RunHeaders - Get the headers before the data		/*{{{*/
 // ---------------------------------------------------------------------
-/* Returns 0 if things are OK, 1 if an IO error occursed and 2 if a header
-   parse error occured */
+/* Returns 0 if things are OK, 1 if an IO error occursed, 2 if a header
+   parse error occured, 3 to send again last request(used by the digest scheme) */
 int ServerState::RunHeaders()
 {
    State = Header;
    
    Owner->Status(_("Waiting for headers"));
-
+   Pipeline = true;
    Major = 0; 
    Minor = 0; 
    Result = 0; 
@@ -354,127 +355,131 @@
 
    do
    {
-      string Data;
-      if (In.WriteTillEl(Data) == false)
-	 continue;
-
-      if (Debug == true)
-	 clog << Data;
-      
-      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++);
-	 if (HeaderLine(string(I,J)) == false)
-	    return 2;
-	 I = J;
-      }
-
+     string Data;
+     if (In.WriteTillEl(Data) == false)
+        continue;
+     
+     if (Debug == true)
+       clog << Data;
+     
+     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++);
+         if (HeaderLine(string(I,J)) == false)
+           return 2;
+         
+         I = J;
+       }
       // 100 Continue is a Nop...
-      if (Result == 100)
-	 continue;
-      
-      // Tidy up the connection persistance state.
-      if (Encoding == Closes && HaveContent == true)
-	 Persistent = false;
-      
-      return 0;
+     if (Result == 100)
+       continue;
+
+     // Resend the request if the server wants digest.
+     if(ProxyAuth == Digest && Result == 407)
+       return 3;
+     
+     // Tidy up the connection persistance state.
+     if (Encoding == Closes && HaveContent == true)
+       Persistent = false;
+            
+     return 0;
    }
    while (Owner->Go(false,this) == true);
    
    return 1;
 }
-									/*}}}*/
+/*}}}*/
 // ServerState::RunData - Transfer the data from the socket		/*{{{*/
 // ---------------------------------------------------------------------
 /* */
 bool ServerState::RunData()
 {
-   State = Data;
-   
-   // Chunked transfer encoding is fun..
-   if (Encoding == Chunked)
-   {
+  State = Data;
+  
+  // Chunked transfer encoding is fun..
+  if (Encoding == Chunked)
+    {
       while (1)
-      {
-	 // Grab the block size
-	 bool Last = true;
-	 string Data;
-	 In.Limit(-1);
-	 do
-	 {
-	    if (In.WriteTillEl(Data,true) == true)
-	       break;
-	 }
-	 while ((Last = Owner->Go(false,this)) == true);
-
-	 if (Last == false)
-	    return false;
-	 	 
-	 // See if we are done
-	 unsigned long Len = strtol(Data.c_str(),0,16);
-	 if (Len == 0)
-	 {
-	    In.Limit(-1);
-	    
-	    // We have to remove the entity trailer
-	    Last = true;
-	    do
-	    {
-	       if (In.WriteTillEl(Data,true) == true && Data.length() <= 2)
-		  break;
-	    }
-	    while ((Last = Owner->Go(false,this)) == true);
-	    if (Last == false)
-	       return false;
-	    return !_error->PendingError();
-	 }
-	 
-	 // Transfer the block
-	 In.Limit(Len);
-	 while (Owner->Go(true,this) == true)
-	    if (In.IsLimit() == true)
-	       break;
-	 
-	 // Error
-	 if (In.IsLimit() == false)
-	    return false;
-	 
-	 // The server sends an extra new line before the next block specifier..
-	 In.Limit(-1);
-	 Last = true;
-	 do
-	 {
-	    if (In.WriteTillEl(Data,true) == true)
-	       break;
-	 }
-	 while ((Last = Owner->Go(false,this)) == true);
-	 if (Last == false)
-	    return false;
-      }
-   }
-   else
-   {
+        {
+          // Grab the block size
+          bool Last = true;
+          string Data;
+          In.Limit(-1);
+          do
+            {
+              if (In.WriteTillEl(Data,true) == true)
+                break;
+            }
+          while ((Last = Owner->Go(false,this)) == true);
+          
+          if (Last == false)
+            return false;
+          
+          // See if we are done
+          unsigned long Len = strtol(Data.c_str(),0,16);
+          if (Len == 0)
+            {
+              In.Limit(-1);
+              
+              // We have to remove the entity trailer
+              Last = true;
+              do
+                {
+                  if (In.WriteTillEl(Data,true) == true && Data.length() <= 2)
+                    break;
+                }
+              while ((Last = Owner->Go(false,this)) == true);
+              if (Last == false)
+                return false;
+              return !_error->PendingError();
+            }
+          
+          // Transfer the block
+          In.Limit(Len);
+          while (Owner->Go(true,this) == true)
+            if (In.IsLimit() == true)
+              break;
+          
+          // Error
+          if (In.IsLimit() == false)
+            return false;
+          
+          // The server sends an extra new line before the next block specifier..
+          In.Limit(-1);
+          Last = true;
+          do
+            {
+              if (In.WriteTillEl(Data,true) == true)
+                break;
+            }
+          while ((Last = Owner->Go(false,this)) == true);
+          if (Last == false)
+            return false;
+        }
+    }
+  else
+    {
       /* Closes encoding is used when the server did not specify a size, the
          loss of the connection means we are done */
       if (Encoding == Closes)
-	 In.Limit(-1);
+        In.Limit(-1);
       else
-	 In.Limit(Size - StartPos);
+        In.Limit(Size - StartPos);
       
       // Just transfer the whole block.
       do
-      {
-	 if (In.IsLimit() == false)
-	    continue;
-	 
-	 In.Limit(-1);
-	 return !_error->PendingError();
-      }
+        {
+          if (In.IsLimit() == false)
+            continue;
+          
+          In.Limit(-1);
+        return !_error->PendingError();
+        }
       while (Owner->Go(true,this) == true);
-   }
-
-   return Owner->Flush(this) && !_error->PendingError();
+    }
+  
+  return Owner->Flush(this) && !_error->PendingError();
 }
 									/*}}}*/
 // ServerState::HeaderLine - Process a header line			/*{{{*/
@@ -485,9 +490,9 @@
    if (Line.empty() == true)
       return true;
 
-   // The http server might be trying to do something evil.
+    // The http server might be trying to do something evil.
    if (Line.length() >= MAXLEN)
-      return _error->Error(_("Got a single header line over %u chars"),MAXLEN);
+     return _error->Error(_("Got a single header line over %u chars"),MAXLEN);
 
    string::size_type Pos = Line.find(' ');
    if (Pos == string::npos || Pos+1 > Line.length())
@@ -495,7 +500,7 @@
       // Blah, some servers use "connection:closes", evil.
       Pos = Line.find(':');
       if (Pos == string::npos || Pos + 2 > Line.length())
-	 return _error->Error(_("Bad header line"));
+        return _error->Error(_("Bad header line"));
       Pos++;
    }
 
@@ -506,36 +511,33 @@
       
    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
       if (Line[4] == '/')
       {
-	 if (sscanf(Line.c_str(),"HTTP/%u.%u %u %[^\n]",&Major,&Minor,
-		    &Result,Code) != 4)
-	    return _error->Error(_("The http server sent an invalid reply header"));
+        if (sscanf(Line.c_str(),"HTTP/%u.%u %u %[^\n]",&Major,&Minor,
+                   &Result,Code) != 4)
+          return _error->Error(_("The http server sent an invalid reply header"));
+         
       }
       else
       {
-	 Major = 0;
-	 Minor = 9;
-	 if (sscanf(Line.c_str(),"HTTP %u %[^\n]",&Result,Code) != 2)
-	    return _error->Error(_("The http server sent an invalid reply header"));
+        Major = 0;
+        Minor = 9;
+        if (sscanf(Line.c_str(),"HTTP %u %[^\n]",&Result,Code) != 2)
+          return _error->Error(_("The http server sent an invalid reply header"));
       }
 
       /* Check the HTTP response header to get the default persistance
          state. */
       if (Major < 1)
-	 Persistent = false;
+        Persistent = false;
       else
       {
-	 if (Major == 1 && Minor <= 0)
-	    Persistent = false;
-	 else
-	    Persistent = true;
+        Persistent = true;
       }
-
+      
       return true;
    }      
       
@@ -547,10 +549,10 @@
       
       // The length is already set from the Content-Range header
       if (StartPos != 0)
-	 return true;
+        return true;
       
       if (sscanf(Val.c_str(),"%lu",&Size) != 1)
-	 return _error->Error(_("The http server sent an invalid Content-Length header"));
+        return _error->Error(_("The http server sent an invalid Content-Length header"));
       return true;
    }
 
@@ -559,6 +561,19 @@
       HaveContent = true;
       return true;
    }
+   if (stringcasecmp(Tag,"Proxy-Authenticate:") == 0)
+   {
+      string::size_type Pos = Line.find("Digest");
+      if(Pos != (-1))
+      {
+        ProxyAuth=Digest;
+        Pipeline = false;
+        return ParseDigest(Line);
+      }
+      
+      ProxyAuth=Basic;
+      return true;
+   }
    
    if (stringcasecmp(Tag,"Content-Range:") == 0)
    {
@@ -575,28 +590,77 @@
    {
       HaveContent = true;
       if (stringcasecmp(Val,"chunked") == 0)
-	 Encoding = Chunked;      
+        Encoding = Chunked;      
       return true;
    }
 
    if (stringcasecmp(Tag,"Connection:") == 0)
    {
-      if (stringcasecmp(Val,"close") == 0)
-	 Persistent = false;
-      if (stringcasecmp(Val,"keep-alive") == 0)
-	 Persistent = true;
-      return true;
+     if (stringcasecmp(Val,"close") == 0)
+       Persistent = false;
+     if (stringcasecmp(Val,"keep-alive") == 0)
+       Persistent = true;
+     return true;
    }
    
    if (stringcasecmp(Tag,"Last-Modified:") == 0)
    {
       if (StrToTime(Val,Date) == false)
-	 return _error->Error(_("Unknown date format"));
+          return _error->Error(_("Unknown date format"));
       return true;
    }
 
    return true;
 }
+
+/* Copy the digest data in the buffers. */
+bool ServerState::ParseDigest(string Line)
+{
+  int Len;
+  int Pos = Line.find("realm");
+  string TmpNonce;
+  if(Pos == (-1))
+    return false;
+    
+   
+  for(Len=Pos+7; Line[Len] && Line[Len]!='\"'; Len++);
+  if(!Line[Len])
+    return false;
+    
+  ProxyRealm.assign(Line, Pos+7, Len-Pos-7);
+  Pos = Line.find("opaque");
+  if(Pos != (-1))
+  {
+    for(Len=Pos+8; Line[Len] && Line[Len]!='\"'; Len++);
+    if(!Line[Len])
+      return false;
+
+    ProxyOpaque.assign(Line, Pos+8, Len-Pos-8);
+  }
+  else
+    ProxyOpaque.assign("");
+
+  Pos = Line.find("nonce");
+  if(Pos == (-1))
+    return false;
+  
+   
+  for(Len=Pos+7; Line[Len] && Line[Len]!='\"'; Len++);
+  if(!Line[Pos])
+    return false;
+  
+  TmpNonce.assign(Line, Pos+7, Len-Pos-7);
+  
+  if(TmpNonce.compare(ProxyNonce) == 0)
+    ProxyNc = 0;
+  
+  ProxyNonce.assign(TmpNonce);
+
+  ProxyCnonce.assign("stupidCnonce");
+
+  return true;
+  
+}
 									/*}}}*/
 
 // HttpMethod::SendReq - Send the HTTP request				/*{{{*/
@@ -636,7 +700,7 @@
       sprintf(Buf,"GET %s HTTP/1.1\r\nHost: %s\r\n",
 	      Itm->Uri.c_str(),ProperHost.c_str());
       if (_config->FindB("Acquire::http::No-Cache",false) == true)
-	 strcat(Buf,"Cache-Control: no-cache\r\nPragma: no-cache\r\n");
+	 strcat(Buf,"Proxy-Connection: keep-alive\r\nCache-Control: no-cache\r\nPragma: no-cache\r\n");
       else
       {
 	 if (Itm->IndexFile == true)
@@ -665,199 +729,250 @@
    {
       if (Itm->LastModified != 0)
       {
-	 sprintf(Buf,"If-Modified-Since: %s\r\n",TimeRFC1123(Itm->LastModified).c_str());
-	 Req += Buf;
+        sprintf(Buf,"If-Modified-Since: %s\r\n",TimeRFC1123(Itm->LastModified).c_str());
+        Req += Buf;
       }
    }
-
-   if (Proxy.User.empty() == false || Proxy.Password.empty() == false)
-      Req += string("Proxy-Authorization: Basic ") + 
-          Base64Encode(Proxy.User + ":" + Proxy.Password) + "\r\n";
-
-   if (Uri.User.empty() == false || Uri.Password.empty() == false)
-      Req += string("Authorization: Basic ") + 
-          Base64Encode(Uri.User + ":" + Uri.Password) + "\r\n";
    
+   if(Server->ProxyAuth == ServerState::Digest)
+     {
+       /* Digest auth scheme for proxy. */
+       MD5Summation A1;
+       MD5Summation A2;
+       char Nc[9];
+       string Tmp("");
+       int i;
+       int slashesCount;
+       string DigUri;
+       MD5Summation Response;
+
+       for(i=0, slashesCount=0; Itm->Uri[i] && slashesCount < 3; i++)
+         if(Itm->Uri[i] == '/')
+           slashesCount++;
+       DigUri = Itm->Uri.substr(i);
+       
+       for(i=0; i< 8; i++)
+         Nc[i]='0';
+       Nc[i] ='\0';
+       Tmp = Proxy.User + string(":") + Server->ProxyRealm + string(":") + Proxy.Password;
+       A1.Add(Tmp.c_str());
+       Tmp = string("GET:") + DigUri;
+       A2.Add(Tmp.c_str());
+
+       {
+         ostringstream TmpStream;
+         Server->ProxyNc++;
+         TmpStream << hex <<  Server->ProxyNc;
+         strcpy(&Nc[8-TmpStream.str().length()], TmpStream.str().c_str());
+         Nc[8] ='\0';
+
+         TmpStream.str(string(""));
+         TmpStream << (A1.Result().Value()) << (":") << Server->ProxyNonce 
+                   << (":") << hex << string(Nc)  << (":") << Server->ProxyCnonce 
+                   << (":auth:") <<  (A2.Result().Value());
+    
+         Response.Add(TmpStream.str().c_str());
+
+       }
+      
+       Req += string("Proxy-Connection: keep-alive\r\nProxy-Authorization: Digest username=\"") + 
+         Proxy.User + string("\", nonce=\"") + Server->ProxyNonce + string("\", uri=\"") +  
+         DigUri + string("\", qop=auth, realm=\"")+ Server->ProxyRealm + string("\", cnonce=\"") + 
+         Server->ProxyCnonce +  string("\", nc=") + string(Nc) + string(", ") +
+         (Server->ProxyOpaque.length() 
+          ?  (string("opaque=\"") + Server->ProxyOpaque + string("\", ")) 
+          : string ("") ) + string("response=\"")+ string(Response.Result().Value()) 
+                                              + string("\"\r\n");
+     }
+   else
+     {
+       if (Proxy.User.empty() == false || Proxy.Password.empty() == false)
+         Req += string("Proxy-Connection: keep-alive\r\nProxy-Authorization: Basic ") + 
+           Base64Encode(Proxy.User + ":" + Proxy.Password) + "\r\n";
+       
+       if (Uri.User.empty() == false || Uri.Password.empty() == false)
+         Req += string("Authorization: Basic ") + 
+           Base64Encode(Uri.User + ":" + Uri.Password) + "\r\n";
+     }
    Req += "User-Agent: Debian APT-HTTP/1.3\r\n\r\n";
    
    if (Debug == true)
-      cerr << Req << endl;
-
+     cerr << Req << endl;
    Out.Read(Req);
 }
-									/*}}}*/
+/*}}}*/
 // HttpMethod::Go - Run a single loop					/*{{{*/
 // ---------------------------------------------------------------------
 /* This runs the select loop over the server FDs, Output file FDs and
    stdin. */
 bool HttpMethod::Go(bool ToFile,ServerState *Srv)
 {
-   // Server has closed the connection
-   if (Srv->ServerFd == -1 && (Srv->In.WriteSpace() == false || 
-			       ToFile == false))
-      return false;
-   
-   fd_set rfds,wfds;
-   FD_ZERO(&rfds);
-   FD_ZERO(&wfds);
-   
-   /* Add the server. We only send more requests if the connection will 
-      be persisting */
-   if (Srv->Out.WriteSpace() == true && Srv->ServerFd != -1 
-       && Srv->Persistent == true)
-      FD_SET(Srv->ServerFd,&wfds);
-   if (Srv->In.ReadSpace() == true && Srv->ServerFd != -1)
-      FD_SET(Srv->ServerFd,&rfds);
-   
-   // Add the file
-   int FileFD = -1;
-   if (File != 0)
-      FileFD = File->Fd();
-   
-   if (Srv->In.WriteSpace() == true && ToFile == true && FileFD != -1)
-      FD_SET(FileFD,&wfds);
-   
-   // Add stdin
-   FD_SET(STDIN_FILENO,&rfds);
-	  
-   // Figure out the max fd
-   int MaxFd = FileFD;
-   if (MaxFd < Srv->ServerFd)
-      MaxFd = Srv->ServerFd;
-
-   // Select
-   struct timeval tv;
-   tv.tv_sec = TimeOut;
-   tv.tv_usec = 0;
-   int Res = 0;
-   if ((Res = select(MaxFd+1,&rfds,&wfds,0,&tv)) < 0)
-   {
+  // Server has closed the connection
+  if (Srv->ServerFd == -1 && (Srv->In.WriteSpace() == false || 
+                              ToFile == false))
+    return false;
+  
+  fd_set rfds,wfds;
+  FD_ZERO(&rfds);
+  FD_ZERO(&wfds);
+  
+  /* Add the server. We only send more requests if the connection will 
+     be persisting */
+  if (Srv->Out.WriteSpace() == true && Srv->ServerFd != -1 
+      && Srv->Persistent == true)
+    FD_SET(Srv->ServerFd,&wfds);
+  if (Srv->In.ReadSpace() == true && Srv->ServerFd != -1)
+    FD_SET(Srv->ServerFd,&rfds);
+  
+  // Add the file
+  int FileFD = -1;
+  if (File != 0)
+    FileFD = File->Fd();
+  
+  if (Srv->In.WriteSpace() == true && ToFile == true && FileFD != -1)
+    FD_SET(FileFD,&wfds);
+  
+  // Add stdin
+  FD_SET(STDIN_FILENO,&rfds);
+  
+  // Figure out the max fd
+  int MaxFd = FileFD;
+  if (MaxFd < Srv->ServerFd)
+    MaxFd = Srv->ServerFd;
+  
+  // Select
+  struct timeval tv;
+  tv.tv_sec = TimeOut;
+  tv.tv_usec = 0;
+  int Res = 0;
+  if ((Res = select(MaxFd+1,&rfds,&wfds,0,&tv)) < 0)
+    {
       if (errno == EINTR)
-	 return true;
+        return true;
       return _error->Errno("select",_("Select failed"));
-   }
-   
-   if (Res == 0)
-   {
+    }
+  
+  if (Res == 0)
+    {
       _error->Error(_("Connection timed out"));
       return ServerDie(Srv);
-   }
-   
-   // Handle server IO
-   if (Srv->ServerFd != -1 && FD_ISSET(Srv->ServerFd,&rfds))
-   {
+    }
+  
+  // Handle server IO
+  if (Srv->ServerFd != -1 && FD_ISSET(Srv->ServerFd,&rfds))
+    {
       errno = 0;
       if (Srv->In.Read(Srv->ServerFd) == false)
-	 return ServerDie(Srv);
-   }
-	 
-   if (Srv->ServerFd != -1 && FD_ISSET(Srv->ServerFd,&wfds))
-   {
+        return ServerDie(Srv);
+    }
+  
+  if (Srv->ServerFd != -1 && FD_ISSET(Srv->ServerFd,&wfds))
+    {
       errno = 0;
       if (Srv->Out.Write(Srv->ServerFd) == false)
-	 return ServerDie(Srv);
-   }
-
-   // Send data to the file
-   if (FileFD != -1 && FD_ISSET(FileFD,&wfds))
-   {
+        return ServerDie(Srv);
+    }
+  
+  // Send data to the file
+  if (FileFD != -1 && FD_ISSET(FileFD,&wfds))
+    {
       if (Srv->In.Write(FileFD) == false)
-	 return _error->Errno("write",_("Error writing to output file"));
-   }
-
-   // Handle commands from APT
-   if (FD_ISSET(STDIN_FILENO,&rfds))
-   {
+        return _error->Errno("write",_("Error writing to output file"));
+    }
+  
+  // Handle commands from APT
+  if (FD_ISSET(STDIN_FILENO,&rfds))
+    {
       if (Run(true) != -1)
-	 exit(100);
-   }   
-       
-   return true;
+        exit(100);
+    }   
+  
+  return true;
 }
-									/*}}}*/
+/*}}}*/
 // HttpMethod::Flush - Dump the buffer into the file			/*{{{*/
 // ---------------------------------------------------------------------
 /* This takes the current input buffer from the Server FD and writes it
    into the file */
 bool HttpMethod::Flush(ServerState *Srv)
 {
-   if (File != 0)
-   {
+  if (File != 0)
+    {
       SetNonBlock(File->Fd(),false);
       if (Srv->In.WriteSpace() == false)
-	 return true;
+        return true;
       
       while (Srv->In.WriteSpace() == true)
-      {
-	 if (Srv->In.Write(File->Fd()) == false)
-	    return _error->Errno("write",_("Error writing to file"));
-	 if (Srv->In.IsLimit() == true)
-	    return true;
-      }
-
+        {
+          if (Srv->In.Write(File->Fd()) == false)
+            return _error->Errno("write",_("Error writing to file"));
+          if (Srv->In.IsLimit() == true)
+            return true;
+        }
+      
       if (Srv->In.IsLimit() == true || Srv->Encoding == ServerState::Closes)
-	 return true;
-   }
-   return false;
+        return true;
+    }
+  return false;
 }
-									/*}}}*/
+/*}}}*/
 // HttpMethod::ServerDie - The server has closed the connection.	/*{{{*/
 // ---------------------------------------------------------------------
 /* */
 bool HttpMethod::ServerDie(ServerState *Srv)
 {
-   unsigned int LErrno = errno;
-   
-   // Dump the buffer to the file
-   if (Srv->State == ServerState::Data)
-   {
+  unsigned int LErrno = errno;
+  
+  // Dump the buffer to the file
+  if (Srv->State == ServerState::Data)
+    {
       SetNonBlock(File->Fd(),false);
       while (Srv->In.WriteSpace() == true)
-      {
-	 if (Srv->In.Write(File->Fd()) == false)
-	    return _error->Errno("write",_("Error writing to the file"));
-
-	 // Done
-	 if (Srv->In.IsLimit() == true)
-	    return true;
-      }
-   }
-   
-   // See if this is because the server finished the data stream
-   if (Srv->In.IsLimit() == false && Srv->State != ServerState::Header && 
-       Srv->Encoding != ServerState::Closes)
-   {
+        {
+          if (Srv->In.Write(File->Fd()) == false)
+            return _error->Errno("write",_("Error writing to the file"));
+          
+          // Done
+          if (Srv->In.IsLimit() == true)
+            return true;
+        }
+    }
+  
+  // See if this is because the server finished the data stream
+  if (Srv->In.IsLimit() == false && Srv->State != ServerState::Header && 
+      Srv->Encoding != ServerState::Closes)
+    {
       Srv->Close();
       if (LErrno == 0)
-	 return _error->Error(_("Error reading from server Remote end closed connection"));
+        return _error->Error(_("Error reading from server Remote end closed connection"));
       errno = LErrno;
       return _error->Errno("read",_("Error reading from server"));
-   }
-   else
-   {
+    }
+  else
+    {
       Srv->In.Limit(-1);
-
+      
       // Nothing left in the buffer
       if (Srv->In.WriteSpace() == false)
-	 return false;
+        return false;
       
       // We may have got multiple responses back in one packet..
       Srv->Close();
       return true;
-   }
-   
-   return false;
+    }
+  
+  return false;
 }
-									/*}}}*/
+/*}}}*/
 // 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) */
+   0 - File is open,
+   1 - IMS hit
+   3 - Unrecoverable error 
+   4 - Error with error content page
+   5 - Unrecoverable non-server error (close the connection) */
 int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv)
 {
    // Not Modified
@@ -875,7 +990,7 @@
    {
       _error->Error("%u %s",Srv->Result,Srv->Code);
       if (Srv->HaveContent == true)
-	 return 4;
+        return 4;
       return 3;
    }
 
@@ -913,7 +1028,7 @@
       lseek(File->Fd(),0,SEEK_SET);
       if (Srv->In.Hash->AddFD(File->Fd(),Srv->StartPos) == false)
       {
-	 _error->Errno("read",_("Problem hashing file"));
+	 _error->Errno("read", _("Problem hashing file"));
 	 return 5;
       }
       lseek(File->Fd(),0,SEEK_END);
@@ -955,23 +1070,25 @@
    int Depth = -1;
    bool Tail = false;
    for (FetchItem *I = Queue; I != 0 && Depth < (signed)PipelineDepth; 
-	I = I->Next, Depth++)
+        I = I->Next, Depth++)
    {
       // If pipelining is disabled, we only queue 1 request
-      if (Server->Pipeline == false && Depth >= 0)
-	 break;
-      
-      // Make sure we stick with the same server
-      if (Server->Comp(I->Uri) == false)
-	 break;
-      if (QueueBack == I)
-	 Tail = true;
-      if (Tail == true)
-      {
-	 QueueBack = I->Next;
-	 SendReq(I,Server->Out);
-	 continue;
-      }
+     if (( (Server->ProxyAuth==ServerState::Digest) || (Server->Pipeline==false)) && (Depth>=0))
+       break;
+
+     // Make sure we stick with the same server
+     if (Server->Comp(I->Uri) == false)
+       break;
+
+     if (QueueBack == I)
+       Tail = true;
+
+     if (Tail == true)
+     {
+         QueueBack = I->Next;
+         SendReq(I,Server->Out);
+         continue;
+     }
    }
    
    return true;
@@ -998,196 +1115,215 @@
 /* */
 int HttpMethod::Loop()
 {
-   signal(SIGTERM,SigTerm);
-   signal(SIGINT,SigTerm);
-   
-   Server = 0;
+  signal(SIGTERM,SigTerm);
+  signal(SIGINT,SigTerm);
+  
+  Server = 0;
+  int reFetch = 0;
+  int FailCounter = 0;
+  while (1)
+    {      
+      FetchResult Res;
+      if(reFetch == 0)
+        {
+          // We have no commands, wait for some to arrive
+          if (Queue == 0)
+            {
+              if (WaitFd(STDIN_FILENO) == false)
+                return 0;
+            }
+          
+          /* Run messages, we can accept 0 (no message) if we didn't
+             do a WaitFd above.. Otherwise the FD is closed. */
+          int Result = Run(true);
+          if (Result != -1 && (Result != 0 || Queue == 0))
+            return 100;
+          
+          if (Queue == 0)
+            continue;
+                    
+          // Connect to the server
+          if (Server == 0 || Server->Comp(Queue->Uri) == false)
+            {
+              delete Server;
+              Server = new ServerState(Queue->Uri,this);
+            }
+          
+          /* If the server has explicitly said this is the last connection
+             then we pre-emptively shut down the pipeline and tear down 
+             the connection. This will speed up HTTP/1.0 servers a tad
+             since we don't have to wait for the close sequence to
+             complete */
+          if (Server->Persistent == false)
+            Server->Close();
+          // Reset the pipeline
+          if (Server->ServerFd == -1)
+            QueueBack = Queue;	 
+          
+          // Connect to the host
+          if (Server->Open() == false)
+            {
+              Fail(true);
+              delete Server;
+              Server = 0;
+              continue;
+            }
+          Server->Pipeline = false;
+          Server->Persistent= true;
+          // Fill the pipeline.
+          Fetch(0);
+        }
+      else
+        {
+          SendReq(Queue,Server->Out);  
+        } 
    
-   int FailCounter = 0;
-   while (1)
-   {      
-      // We have no commands, wait for some to arrive
-      if (Queue == 0)
-      {
-	 if (WaitFd(STDIN_FILENO) == false)
-	    return 0;
-      }
-      
-      /* Run messages, we can accept 0 (no message) if we didn't
-         do a WaitFd above.. Otherwise the FD is closed. */
-      int Result = Run(true);
-      if (Result != -1 && (Result != 0 || Queue == 0))
-	 return 100;
-
-      if (Queue == 0)
-	 continue;
-      
-      // Connect to the server
-      if (Server == 0 || Server->Comp(Queue->Uri) == false)
-      {
-	 delete Server;
-	 Server = new ServerState(Queue->Uri,this);
-      }
-      
-      /* If the server has explicitly said this is the last connection
-         then we pre-emptively shut down the pipeline and tear down 
-	 the connection. This will speed up HTTP/1.0 servers a tad
-	 since we don't have to wait for the close sequence to
-         complete */
-      if (Server->Persistent == false)
-	 Server->Close();
-      
-      // Reset the pipeline
-      if (Server->ServerFd == -1)
-	 QueueBack = Queue;	 
-	 
-      // Connnect to the host
-      if (Server->Open() == false)
-      {
-	 Fail(true);
-	 delete Server;
-	 Server = 0;
-	 continue;
-      }
-
-      // Fill the pipeline.
-      Fetch(0);
-      
+      reFetch = 0;
       // Fetch the next URL header data from the server.
       switch (Server->RunHeaders())
-      {
-	 case 0:
-	 break;
-	 
-	 // The header data is bad
-	 case 2:
-	 {
-	    _error->Error(_("Bad header Data"));
-	    Fail(true);
-	    RotateDNS();
-	    continue;
-	 }
-	 
-	 // The server closed a connection during the header get..
-	 default:
-	 case 1:
-	 {
-	    FailCounter++;
-	    _error->Discard();
-	    Server->Close();
-	    Server->Pipeline = false;
-	    
-	    if (FailCounter >= 2)
-	    {
-	       Fail(_("Connection failed"),true);
-	       FailCounter = 0;
-	    }
-	    
-	    RotateDNS();
-	    continue;
-	 }
-      };
-
+        {
+        case 0:
+          break;
+          
+          // The header data is bad
+        case 2:
+          {
+            _error->Error(_("Bad header Data"));
+            Fail(true);
+            RotateDNS();
+            continue;
+          }
+          // Repeat last request
+        case 3:
+          Res.Filename = Queue->DestFile;
+          /* Flush the data before send a new request. */
+          File = new FileFd("/dev/null",FileFd::WriteExists);
+          Server->RunData();
+          delete File;
+          File = 0;
+          Server->Out.Reset();
+      
+          reFetch = 1;
+          continue;
+          // The server closed a connection during the header get..
+        default:
+        case 1:
+          {
+             FailCounter++;
+            _error->Discard();
+            Server->Close();
+            Server->Pipeline = false;
+            
+            if (FailCounter >= 2)
+              {
+                Fail(_("Connection failed"),true);
+                FailCounter = 0;
+              }
+            
+            RotateDNS();
+            continue;
+          }
+          
+        };
+      
       // Decide what to do.
-      FetchResult Res;
       Res.Filename = Queue->DestFile;
       switch (DealWithHeaders(Res,Server))
-      {
-	 // Ok, the file is Open
-	 case 0:
-	 {
-	    URIStart(Res);
-
-	    // Run the data
-	    bool Result =  Server->RunData();
-
-	    /* If the server is sending back sizeless responses then fill in
-	       the size now */
-	    if (Res.Size == 0)
-	       Res.Size = File->Size();
-	    
-	    // Close the file, destroy the FD object and timestamp it
-	    FailFd = -1;
-	    delete File;
-	    File = 0;
-	    
-	    // Timestamp
-	    struct utimbuf UBuf;
-	    time(&UBuf.actime);
-	    UBuf.actime = Server->Date;
-	    UBuf.modtime = Server->Date;
-	    utime(Queue->DestFile.c_str(),&UBuf);
-
-	    // Send status to APT
-	    if (Result == true)
-	    {
-	       Res.TakeHashes(*Server->In.Hash);
-	       URIDone(Res);
-	    }
-	    else
-	       Fail(true);
-	    
-	    break;
-	 }
-	 
-	 // IMS hit
-	 case 1:
-	 {
-	    URIDone(Res);
-	    break;
-	 }
-	 
-	 // Hard server error, not found or something
-	 case 3:
-	 {
-	    Fail();
-	    break;
-	 }
-	  
-	 // Hard internal error, kill the connection and fail
-	 case 5:
-	 {
-	    delete File;
-	    File = 0;
-
-	    Fail();
-	    RotateDNS();
-	    Server->Close();
-	    break;
-	 }
-
-	 // We need to flush the data, the header is like a 404 w/ error text
-	 case 4:
-	 {
-	    Fail();
-	    
-	    // Send to content to dev/null
-	    File = new FileFd("/dev/null",FileFd::WriteExists);
-	    Server->RunData();
-	    delete File;
-	    File = 0;
-	    break;
-	 }
-	 
-	 default:
-	 Fail(_("Internal error"));
-	 break;
-      }
+        {
+          // Ok, the file is Open
+        case 0:
+          {
+            URIStart(Res);
+            
+            // Run the data
+            bool Result =  Server->RunData();
+            
+            /* If the server is sending back sizeless responses then fill in
+               the size now */
+            if (Res.Size == 0)
+              Res.Size = File->Size();
+            
+            // Close the file, destroy the FD object and timestamp it
+            FailFd = -1;
+            delete File;
+            File = 0;
+            
+            // Timestamp
+            struct utimbuf UBuf;
+            time(&UBuf.actime);
+            UBuf.actime = Server->Date;
+            UBuf.modtime = Server->Date;
+            utime(Queue->DestFile.c_str(),&UBuf);
+            
+            // Send status to APT
+            if (Result == true)
+              {
+                Res.TakeHashes(*Server->In.Hash);
+                URIDone(Res);
+              }
+            else
+              Fail(true);
+            
+            break;
+          }
+          
+          // IMS hit
+        case 1:
+          {
+            URIDone(Res);
+            break;
+          }
+          
+          // Hard server error, not found or something
+        case 3:
+          {
+            Fail();
+            break;
+          }
+          
+          // Hard internal error, kill the connection and fail
+        case 5:
+          {
+            delete File;
+            File = 0;
+            
+            Fail();
+            RotateDNS();
+            Server->Close();
+            break;
+          }
+          
+          // We need to flush the data, the header is like a 404 w/ error text
+        case 4:
+          {
+            Fail();
+            // Send to content to dev/null
+            File = new FileFd("/dev/null",FileFd::WriteExists);
+            Server->RunData();
+            delete File;
+            File = 0;
+            break;
+          }
+          
+        default:
+          Fail(_("Internal error"));
+          break;
+        }
       
       FailCounter = 0;
-   }
-   
-   return 0;
+    }
+  
+  return 0;
 }
-									/*}}}*/
+                /*}}}*/
 
 int main()
 {
-   setlocale(LC_ALL, "");
-
-   HttpMethod Mth;
-   
-   return Mth.Loop();
+  setlocale(LC_ALL, "");
+  
+  HttpMethod Mth;
+  
+  return Mth.Loop();
 }
 
 
--- http.h	2005-05-17 21:04:28.000000000 +0200
+++ ../../../http.h	2005-05-17 21:14:34.000000000 +0200
@@ -85,7 +85,16 @@
    unsigned int Minor;
    unsigned int Result;
    char Code[MAXLEN];
-   
+
+   /*! Digest stuff. */
+	 string ProxyRealm;
+	 string ProxyOpaque;
+	 string ProxyNonce;
+	 string ProxyCnonce;
+   unsigned int ProxyNc;
+
+   enum{Basic, Digest} ProxyAuth;
+
    // These are some statistics from the last parsed header lines
    unsigned long Size;
    signed long StartPos;
@@ -110,13 +119,17 @@
    bool Comp(URI Other) {return Other.Host == ServerName.Host && Other.Port == ServerName.Port;};
    void Reset() {Major = 0; Minor = 0; Result = 0; Size = 0; StartPos = 0;
                  Encoding = Closes; time(&Date); ServerFd = -1; 
-                 Pipeline = true;};
+                 Pipeline = true; ProxyRealm.assign(""); ProxyOpaque.assign("");
+                 ProxyNonce.assign(""); ProxyCnonce.assign("");
+   	             ProxyNc=0; ProxyAuth=Basic;};
    int RunHeaders();
    bool RunData();
    
    bool Open();
    bool Close();
    
+  bool ParseDigest(string line);
+
    ServerState(URI Srv,HttpMethod *Owner);
    ~ServerState() {Close();};
 };



Reply to: