- various "cosmetics" cleanups to shut up gcc complaints on higher warning
[apt.git] / methods / http.cc
1 // -*- mode: c++; mode: fold -*-
2 // Description                                                          /*{{{*/
3 // $Id: http.cc,v 1.56 2003/02/12 15:33:36 doogie Exp $
4 /* ######################################################################
5
6    HTTP Aquire Method - This is the HTTP aquire method for APT.
7    
8    It uses HTTP/1.1 and many of the fancy options there-in, such as
9    pipelining, range, if-range and so on. 
10
11    It is based on a doubly buffered select loop. A groupe of requests are 
12    fed into a single output buffer that is constantly fed out the 
13    socket. This provides ideal pipelining as in many cases all of the
14    requests will fit into a single packet. The input socket is buffered 
15    the same way and fed into the fd for the file (may be a pipe in future).
16    
17    This double buffering provides fairly substantial transfer rates,
18    compared to wget the http method is about 4% faster. Most importantly,
19    when HTTP is compared with FTP as a protocol the speed difference is
20    huge. In tests over the internet from two sites to llug (via ATM) this
21    program got 230k/s sustained http transfer rates. FTP on the other 
22    hand topped out at 170k/s. That combined with the time to setup the
23    FTP connection makes HTTP a vastly superior protocol.
24       
25    ##################################################################### */
26                                                                         /*}}}*/
27 // Include Files                                                        /*{{{*/
28 #include <apt-pkg/fileutl.h>
29 #include <apt-pkg/acquire-method.h>
30 #include <apt-pkg/error.h>
31 #include <apt-pkg/hashes.h>
32
33 #include <sys/stat.h>
34 #include <sys/time.h>
35 #include <utime.h>
36 #include <unistd.h>
37 #include <signal.h>
38 #include <stdio.h>
39 #include <errno.h>
40 #include <string.h>
41 #include <iostream>
42 #include <map>
43
44 // Internet stuff
45 #include <netdb.h>
46
47 // CNC:2003-02-20 - Moved header to fix compilation error when
48 //                  --disable-nls is used.
49 #include <apti18n.h>
50
51 #include "connect.h"
52 #include "rfc2553emu.h"
53 #include "http.h"
54
55                                                                         /*}}}*/
56 using namespace std;
57
58 string HttpMethod::FailFile;
59 int HttpMethod::FailFd = -1;
60 time_t HttpMethod::FailTime = 0;
61 unsigned long PipelineDepth = 10;
62 unsigned long TimeOut = 120;
63 bool ChokePipe = true;
64 bool Debug = false;
65
66 // CircleBuf::CircleBuf - Circular input buffer                         /*{{{*/
67 // ---------------------------------------------------------------------
68 /* */
69 CircleBuf::CircleBuf(unsigned long Size) : Size(Size), Hash(0)
70 {
71    Buf = new unsigned char[Size];
72    Reset();
73 }
74                                                                         /*}}}*/
75 // CircleBuf::Reset - Reset to the default state                        /*{{{*/
76 // ---------------------------------------------------------------------
77 /* */
78 void CircleBuf::Reset()
79 {
80    InP = 0;
81    OutP = 0;
82    StrPos = 0;
83    MaxGet = (unsigned int)-1;
84    OutQueue = string();
85    if (Hash != 0)
86    {
87       delete Hash;
88       Hash = new Hashes;
89    }   
90 }
91                                                                         /*}}}*/
92 // CircleBuf::Read - Read from a FD into the circular buffer            /*{{{*/
93 // ---------------------------------------------------------------------
94 /* This fills up the buffer with as much data as is in the FD, assuming it
95    is non-blocking.. */
96 bool CircleBuf::Read(int Fd)
97 {
98    while (1)
99    {
100       // Woops, buffer is full
101       if (InP - OutP == Size)
102          return true;
103       
104       // Write the buffer segment
105       int Res;
106       Res = read(Fd,Buf + (InP%Size),LeftRead());
107       
108       if (Res == 0)
109          return false;
110       if (Res < 0)
111       {
112          if (errno == EAGAIN)
113             return true;
114          return false;
115       }
116
117       if (InP == 0)
118          gettimeofday(&Start,0);
119       InP += Res;
120    }
121 }
122                                                                         /*}}}*/
123 // CircleBuf::Read - Put the string into the buffer                     /*{{{*/
124 // ---------------------------------------------------------------------
125 /* This will hold the string in and fill the buffer with it as it empties */
126 bool CircleBuf::Read(string Data)
127 {
128    OutQueue += Data;
129    FillOut();
130    return true;
131 }
132                                                                         /*}}}*/
133 // CircleBuf::FillOut - Fill the buffer from the output queue           /*{{{*/
134 // ---------------------------------------------------------------------
135 /* */
136 void CircleBuf::FillOut()
137 {
138    if (OutQueue.empty() == true)
139       return;
140    while (1)
141    {
142       // Woops, buffer is full
143       if (InP - OutP == Size)
144          return;
145       
146       // Write the buffer segment
147       unsigned long Sz = LeftRead();
148       if (OutQueue.length() - StrPos < Sz)
149          Sz = OutQueue.length() - StrPos;
150       memcpy(Buf + (InP%Size),OutQueue.c_str() + StrPos,Sz);
151       
152       // Advance
153       StrPos += Sz;
154       InP += Sz;
155       if (OutQueue.length() == StrPos)
156       {
157          StrPos = 0;
158          OutQueue = "";
159          return;
160       }
161    }
162 }
163                                                                         /*}}}*/
164 // CircleBuf::Write - Write from the buffer into a FD                   /*{{{*/
165 // ---------------------------------------------------------------------
166 /* This empties the buffer into the FD. */
167 bool CircleBuf::Write(int Fd)
168 {
169    while (1)
170    {
171       FillOut();
172       
173       // Woops, buffer is empty
174       if (OutP == InP)
175          return true;
176       
177       if (OutP == MaxGet)
178          return true;
179       
180       // Write the buffer segment
181       int Res;
182       Res = write(Fd,Buf + (OutP%Size),LeftWrite());
183
184       if (Res == 0)
185          return false;
186       if (Res < 0)
187       {
188          if (errno == EAGAIN)
189             return true;
190          
191          return false;
192       }
193       
194       if (Hash != 0)
195          Hash->Add(Buf + (OutP%Size),Res);
196       
197       OutP += Res;
198    }
199 }
200                                                                         /*}}}*/
201 // CircleBuf::WriteTillEl - Write from the buffer to a string           /*{{{*/
202 // ---------------------------------------------------------------------
203 /* This copies till the first empty line */
204 bool CircleBuf::WriteTillEl(string &Data,bool Single)
205 {
206    // We cheat and assume it is unneeded to have more than one buffer load
207    for (unsigned long I = OutP; I < InP; I++)
208    {      
209       if (Buf[I%Size] != '\n')
210          continue;
211       for (I++; I < InP && Buf[I%Size] == '\r'; I++);
212       
213       if (Single == false)
214       {
215          if (Buf[I%Size] != '\n')
216             continue;
217          for (I++; I < InP && Buf[I%Size] == '\r'; I++);
218       }
219       
220       if (I > InP)
221          I = InP;
222       
223       Data = "";
224       while (OutP < I)
225       {
226          unsigned long Sz = LeftWrite();
227          if (Sz == 0)
228             return false;
229          if (I - OutP < LeftWrite())
230             Sz = I - OutP;
231          Data += string((char *)(Buf + (OutP%Size)),Sz);
232          OutP += Sz;
233       }
234       return true;
235    }      
236    return false;
237 }
238                                                                         /*}}}*/
239 // CircleBuf::Stats - Print out stats information                       /*{{{*/
240 // ---------------------------------------------------------------------
241 /* */
242 void CircleBuf::Stats()
243 {
244    if (InP == 0)
245       return;
246    
247    struct timeval Stop;
248    gettimeofday(&Stop,0);
249 /*   float Diff = Stop.tv_sec - Start.tv_sec + 
250              (float)(Stop.tv_usec - Start.tv_usec)/1000000;
251    clog << "Got " << InP << " in " << Diff << " at " << InP/Diff << endl;*/
252 }
253                                                                         /*}}}*/
254
255 // ServerState::ServerState - Constructor                               /*{{{*/
256 // ---------------------------------------------------------------------
257 /* */
258 ServerState::ServerState(URI Srv,HttpMethod *Owner) : Owner(Owner),
259                         In(64*1024), Out(4*1024),
260                         ServerName(Srv)
261 {
262    Reset();
263 }
264                                                                         /*}}}*/
265 // ServerState::Open - Open a connection to the server                  /*{{{*/
266 // ---------------------------------------------------------------------
267 /* This opens a connection to the server. */
268 bool ServerState::Open()
269 {
270    // Use the already open connection if possible.
271    if (ServerFd != -1)
272       return true;
273    
274    Close();
275    In.Reset();
276    Out.Reset();
277    Persistent = true;
278    
279    // Determine the proxy setting
280    if (getenv("http_proxy") == 0)
281    {
282       string DefProxy = _config->Find("Acquire::http::Proxy");
283       string SpecificProxy = _config->Find("Acquire::http::Proxy::" + ServerName.Host);
284       if (SpecificProxy.empty() == false)
285       {
286          if (SpecificProxy == "DIRECT")
287             Proxy = "";
288          else
289             Proxy = SpecificProxy;
290       }   
291       else
292          Proxy = DefProxy;
293    }
294    else
295       Proxy = getenv("http_proxy");
296    
297    // Parse no_proxy, a , separated list of domains
298    if (getenv("no_proxy") != 0)
299    {
300       if (CheckDomainList(ServerName.Host,getenv("no_proxy")) == true)
301          Proxy = "";
302    }
303    
304    // Determine what host and port to use based on the proxy settings
305    int Port = 0;
306    string Host;   
307    if (Proxy.empty() == true || Proxy.Host.empty() == true)
308    {
309       if (ServerName.Port != 0)
310          Port = ServerName.Port;
311       Host = ServerName.Host;
312    }
313    else
314    {
315       if (Proxy.Port != 0)
316          Port = Proxy.Port;
317       Host = Proxy.Host;
318    }
319    
320    // Connect to the remote server
321    if (Connect(Host,Port,"http",80,ServerFd,TimeOut,Owner) == false)
322       return false;
323    
324    return true;
325 }
326                                                                         /*}}}*/
327 // ServerState::Close - Close a connection to the server                /*{{{*/
328 // ---------------------------------------------------------------------
329 /* */
330 bool ServerState::Close()
331 {
332    close(ServerFd);
333    ServerFd = -1;
334    return true;
335 }
336                                                                         /*}}}*/
337 // ServerState::RunHeaders - Get the headers before the data            /*{{{*/
338 // ---------------------------------------------------------------------
339 /* Returns 0 if things are OK, 1 if an IO error occursed and 2 if a header
340    parse error occured */
341 int ServerState::RunHeaders()
342 {
343    State = Header;
344    
345    Owner->Status(_("Waiting for headers"));
346
347    Major = 0; 
348    Minor = 0; 
349    Result = 0; 
350    Size = 0; 
351    StartPos = 0;
352    Encoding = Closes;
353    HaveContent = false;
354    time(&Date);
355
356    do
357    {
358       string Data;
359       if (In.WriteTillEl(Data) == false)
360          continue;
361
362       if (Debug == true)
363          clog << Data;
364       
365       for (string::const_iterator I = Data.begin(); I < Data.end(); I++)
366       {
367          string::const_iterator J = I;
368          for (; J != Data.end() && *J != '\n' && *J != '\r';J++);
369          if (HeaderLine(string(I,J)) == false)
370             return 2;
371          I = J;
372       }
373
374       // 100 Continue is a Nop...
375       if (Result == 100)
376          continue;
377       
378       // Tidy up the connection persistance state.
379       if (Encoding == Closes && HaveContent == true)
380          Persistent = false;
381       
382       return 0;
383    }
384    while (Owner->Go(false,this) == true);
385    
386    return 1;
387 }
388                                                                         /*}}}*/
389 // ServerState::RunData - Transfer the data from the socket             /*{{{*/
390 // ---------------------------------------------------------------------
391 /* */
392 bool ServerState::RunData()
393 {
394    State = Data;
395    
396    // Chunked transfer encoding is fun..
397    if (Encoding == Chunked)
398    {
399       while (1)
400       {
401          // Grab the block size
402          bool Last = true;
403          string Data;
404          In.Limit(-1);
405          do
406          {
407             if (In.WriteTillEl(Data,true) == true)
408                break;
409          }
410          while ((Last = Owner->Go(false,this)) == true);
411
412          if (Last == false)
413             return false;
414                  
415          // See if we are done
416          unsigned long Len = strtol(Data.c_str(),0,16);
417          if (Len == 0)
418          {
419             In.Limit(-1);
420             
421             // We have to remove the entity trailer
422             Last = true;
423             do
424             {
425                if (In.WriteTillEl(Data,true) == true && Data.length() <= 2)
426                   break;
427             }
428             while ((Last = Owner->Go(false,this)) == true);
429             if (Last == false)
430                return false;
431             return !_error->PendingError();
432          }
433          
434          // Transfer the block
435          In.Limit(Len);
436          while (Owner->Go(true,this) == true)
437             if (In.IsLimit() == true)
438                break;
439          
440          // Error
441          if (In.IsLimit() == false)
442             return false;
443          
444          // The server sends an extra new line before the next block specifier..
445          In.Limit(-1);
446          Last = true;
447          do
448          {
449             if (In.WriteTillEl(Data,true) == true)
450                break;
451          }
452          while ((Last = Owner->Go(false,this)) == true);
453          if (Last == false)
454             return false;
455       }
456    }
457    else
458    {
459       /* Closes encoding is used when the server did not specify a size, the
460          loss of the connection means we are done */
461       if (Encoding == Closes)
462          In.Limit(-1);
463       else
464          In.Limit(Size - StartPos);
465       
466       // Just transfer the whole block.
467       do
468       {
469          if (In.IsLimit() == false)
470             continue;
471          
472          In.Limit(-1);
473          return !_error->PendingError();
474       }
475       while (Owner->Go(true,this) == true);
476    }
477
478    return Owner->Flush(this) && !_error->PendingError();
479 }
480                                                                         /*}}}*/
481 // ServerState::HeaderLine - Process a header line                      /*{{{*/
482 // ---------------------------------------------------------------------
483 /* */
484 bool ServerState::HeaderLine(string Line)
485 {
486    if (Line.empty() == true)
487       return true;
488
489    // The http server might be trying to do something evil.
490    if (Line.length() >= MAXLEN)
491       return _error->Error(_("Got a single header line over %u chars"),MAXLEN);
492
493    string::size_type Pos = Line.find(' ');
494    if (Pos == string::npos || Pos+1 > Line.length())
495    {
496       // Blah, some servers use "connection:closes", evil.
497       Pos = Line.find(':');
498       if (Pos == string::npos || Pos + 2 > Line.length())
499          return _error->Error(_("Bad header line"));
500       Pos++;
501    }
502
503    // Parse off any trailing spaces between the : and the next word.
504    string::size_type Pos2 = Pos;
505    while (Pos2 < Line.length() && isspace(Line[Pos2]) != 0)
506       Pos2++;
507       
508    string Tag = string(Line,0,Pos);
509    string Val = string(Line,Pos2);
510    
511    if (stringcasecmp(Tag.c_str(),Tag.c_str()+4,"HTTP") == 0)
512    {
513       // Evil servers return no version
514       if (Line[4] == '/')
515       {
516          if (sscanf(Line.c_str(),"HTTP/%u.%u %u %[^\n]",&Major,&Minor,
517                     &Result,Code) != 4)
518             return _error->Error(_("The http server sent an invalid reply header"));
519       }
520       else
521       {
522          Major = 0;
523          Minor = 9;
524          if (sscanf(Line.c_str(),"HTTP %u %[^\n]",&Result,Code) != 2)
525             return _error->Error(_("The http server sent an invalid reply header"));
526       }
527
528       /* Check the HTTP response header to get the default persistance
529          state. */
530       if (Major < 1)
531          Persistent = false;
532       else
533       {
534          if (Major == 1 && Minor <= 0)
535             Persistent = false;
536          else
537             Persistent = true;
538       }
539
540       return true;
541    }      
542       
543    if (stringcasecmp(Tag,"Content-Length:") == 0)
544    {
545       if (Encoding == Closes)
546          Encoding = Stream;
547       HaveContent = true;
548       
549       // The length is already set from the Content-Range header
550       if (StartPos != 0)
551          return true;
552       
553       if (sscanf(Val.c_str(),"%lu",&Size) != 1)
554          return _error->Error(_("The http server sent an invalid Content-Length header"));
555       return true;
556    }
557
558    if (stringcasecmp(Tag,"Content-Type:") == 0)
559    {
560       HaveContent = true;
561       return true;
562    }
563    
564    if (stringcasecmp(Tag,"Content-Range:") == 0)
565    {
566       HaveContent = true;
567       
568       if (sscanf(Val.c_str(),"bytes %lu-%*u/%lu",&StartPos,&Size) != 2)
569          return _error->Error(_("The http server sent an invalid Content-Range header"));
570       if ((unsigned)StartPos > Size)
571          return _error->Error(_("This http server has broken range support"));
572       return true;
573    }
574    
575    if (stringcasecmp(Tag,"Transfer-Encoding:") == 0)
576    {
577       HaveContent = true;
578       if (stringcasecmp(Val,"chunked") == 0)
579          Encoding = Chunked;      
580       return true;
581    }
582
583    if (stringcasecmp(Tag,"Connection:") == 0)
584    {
585       if (stringcasecmp(Val,"close") == 0)
586          Persistent = false;
587       if (stringcasecmp(Val,"keep-alive") == 0)
588          Persistent = true;
589       return true;
590    }
591    
592    if (stringcasecmp(Tag,"Last-Modified:") == 0)
593    {
594       if (StrToTime(Val,Date) == false)
595          return _error->Error(_("Unknown date format"));
596       return true;
597    }
598
599    if (stringcasecmp(Tag,"Location:") == 0)
600    {
601       Location = Val;
602       return true;
603    }
604
605    if (stringcasecmp(Tag,"WWW-Authenticate:") == 0 ||
606        stringcasecmp(Tag,"Proxy-Authenticate:") == 0)
607    {
608       string::size_type SplitPoint = Val.find(' ');
609       string AuthType = Val.substr(0, SplitPoint);
610       string RealmStr = Val.substr(SplitPoint + 1, 
611                                    Val.length() - SplitPoint - 1);
612       SplitPoint = RealmStr.find('=');
613       string FoundRealm = RealmStr.substr(SplitPoint, 
614                                           RealmStr.length() - SplitPoint);
615
616       if (stringcasecmp(Tag,"WWW-Authenticate:") == 0)
617          Realm = FoundRealm;
618       else
619          ProxyRealm = FoundRealm;
620
621       return true;
622    }
623
624    return true;
625 }
626                                                                         /*}}}*/
627
628 // HttpMethod::SendReq - Send the HTTP request                          /*{{{*/
629 // ---------------------------------------------------------------------
630 /* This places the http request in the outbound buffer */
631 void HttpMethod::SendReq(FetchItem *Itm,CircleBuf &Out)
632 {
633    URI Uri = Itm->Uri;
634
635    // The HTTP server expects a hostname with a trailing :port
636    char Buf[1000];
637    string ProperHost = Uri.Host;
638    if (Uri.Port != 0)
639    {
640       sprintf(Buf,":%u",Uri.Port);
641       ProperHost += Buf;
642    }   
643       
644    // Just in case.
645    if (Itm->Uri.length() >= sizeof(Buf))
646        abort();
647        
648    /* Build the request. We include a keep-alive header only for non-proxy
649       requests. This is to tweak old http/1.0 servers that do support keep-alive
650       but not HTTP/1.1 automatic keep-alive. Doing this with a proxy server 
651       will glitch HTTP/1.0 proxies because they do not filter it out and 
652       pass it on, HTTP/1.1 says the connection should default to keep alive
653       and we expect the proxy to do this */
654    if (Proxy.empty() == true)
655       sprintf(Buf,"GET %s HTTP/1.1\r\nHost: %s\r\nConnection: keep-alive\r\n",
656               QuoteString(Uri.Path,"~").c_str(),ProperHost.c_str());
657    else
658    {
659       /* Generate a cache control header if necessary. We place a max
660          cache age on index files, optionally set a no-cache directive
661          and a no-store directive for archives. */
662       sprintf(Buf,"GET %s HTTP/1.1\r\nHost: %s\r\n",
663               Itm->Uri.c_str(),ProperHost.c_str());
664       if (_config->FindB("Acquire::http::No-Cache",false) == true)
665          strcat(Buf,"Cache-Control: no-cache\r\nPragma: no-cache\r\n");
666       else
667       {
668          if (Itm->IndexFile == true)
669             sprintf(Buf+strlen(Buf),"Cache-Control: max-age=%u\r\n",
670                     _config->FindI("Acquire::http::Max-Age",60*60*24));
671          else
672          {
673             if (_config->FindB("Acquire::http::No-Store",false) == true)
674                strcat(Buf,"Cache-Control: no-store\r\n");
675          }       
676       }
677    }
678    
679    string Req = Buf;
680
681    // Check for a partial file
682    struct stat SBuf;
683    if (stat(Itm->DestFile.c_str(),&SBuf) >= 0 && SBuf.st_size > 0)
684    {
685       // In this case we send an if-range query with a range header
686       sprintf(Buf,"Range: bytes=%li-\r\nIf-Range: %s\r\n",(long)SBuf.st_size - 1,
687               TimeRFC1123(SBuf.st_mtime).c_str());
688       Req += Buf;
689    }
690    else
691    {
692       if (Itm->LastModified != 0)
693       {
694          sprintf(Buf,"If-Modified-Since: %s\r\n",TimeRFC1123(Itm->LastModified).c_str());
695          Req += Buf;
696       }
697    }
698
699    if (Proxy.User.empty() == false || Proxy.Password.empty() == false)
700       Req += string("Proxy-Authorization: Basic ") + 
701           Base64Encode(Proxy.User + ":" + Proxy.Password) + "\r\n";
702
703    if (Uri.User.empty() == false || Uri.Password.empty() == false)
704       Req += string("Authorization: Basic ") + 
705           Base64Encode(Uri.User + ":" + Uri.Password) + "\r\n";
706
707    // CNC:2003-01-29
708    string UserAgent = _config->Find("Acquire::http::User-Agent");
709    if (UserAgent.empty() == true) 
710           UserAgent = "RPM APT-HTTP/1.3";
711    Req += string("User-Agent: ") + UserAgent + "\r\n\r\n";
712    
713    if (Debug == true)
714       cerr << Req << endl;
715
716    Out.Read(Req);
717 }
718                                                                         /*}}}*/
719 // HttpMethod::Go - Run a single loop                                   /*{{{*/
720 // ---------------------------------------------------------------------
721 /* This runs the select loop over the server FDs, Output file FDs and
722    stdin. */
723 bool HttpMethod::Go(bool ToFile,ServerState *Srv)
724 {
725    // Server has closed the connection
726    if (Srv->ServerFd == -1 && (Srv->In.WriteSpace() == false || 
727                                ToFile == false))
728       return false;
729    
730    fd_set rfds,wfds;
731    FD_ZERO(&rfds);
732    FD_ZERO(&wfds);
733    
734    /* Add the server. We only send more requests if the connection will 
735       be persisting */
736    if (Srv->Out.WriteSpace() == true && Srv->ServerFd != -1 
737        && Srv->Persistent == true)
738       FD_SET(Srv->ServerFd,&wfds);
739    if (Srv->In.ReadSpace() == true && Srv->ServerFd != -1)
740       FD_SET(Srv->ServerFd,&rfds);
741    
742    // Add the file
743    int FileFD = -1;
744    if (File != 0)
745       FileFD = File->Fd();
746    
747    if (Srv->In.WriteSpace() == true && ToFile == true && FileFD != -1)
748       FD_SET(FileFD,&wfds);
749    
750    // Add stdin
751    FD_SET(STDIN_FILENO,&rfds);
752           
753    // Figure out the max fd
754    int MaxFd = FileFD;
755    if (MaxFd < Srv->ServerFd)
756       MaxFd = Srv->ServerFd;
757
758    // Select
759    struct timeval tv;
760    tv.tv_sec = TimeOut;
761    tv.tv_usec = 0;
762    int Res = 0;
763    if ((Res = select(MaxFd+1,&rfds,&wfds,0,&tv)) < 0)
764    {
765       if (errno == EINTR)
766          return true;
767       return _error->Errno("select",_("Select failed"));
768    }
769    
770    if (Res == 0)
771    {
772       _error->Error(_("Connection timed out"));
773       return ServerDie(Srv);
774    }
775    
776    // Handle server IO
777    if (Srv->ServerFd != -1 && FD_ISSET(Srv->ServerFd,&rfds))
778    {
779       errno = 0;
780       if (Srv->In.Read(Srv->ServerFd) == false)
781          return ServerDie(Srv);
782    }
783          
784    if (Srv->ServerFd != -1 && FD_ISSET(Srv->ServerFd,&wfds))
785    {
786       errno = 0;
787       if (Srv->Out.Write(Srv->ServerFd) == false)
788          return ServerDie(Srv);
789    }
790
791    // Send data to the file
792    if (FileFD != -1 && FD_ISSET(FileFD,&wfds))
793    {
794       if (Srv->In.Write(FileFD) == false)
795          return _error->Errno("write",_("Error writing to output file"));
796    }
797
798    // Handle commands from APT
799    if (FD_ISSET(STDIN_FILENO,&rfds))
800    {
801       if (Run(true) != -1)
802          exit(100);
803    }   
804        
805    return true;
806 }
807                                                                         /*}}}*/
808 // HttpMethod::Flush - Dump the buffer into the file                    /*{{{*/
809 // ---------------------------------------------------------------------
810 /* This takes the current input buffer from the Server FD and writes it
811    into the file */
812 bool HttpMethod::Flush(ServerState *Srv)
813 {
814    if (File != 0)
815    {
816       SetNonBlock(File->Fd(),false);
817       if (Srv->In.WriteSpace() == false)
818          return true;
819       
820       while (Srv->In.WriteSpace() == true)
821       {
822          if (Srv->In.Write(File->Fd()) == false)
823             return _error->Errno("write",_("Error writing to file"));
824          if (Srv->In.IsLimit() == true)
825             return true;
826       }
827
828       if (Srv->In.IsLimit() == true || Srv->Encoding == ServerState::Closes)
829          return true;
830    }
831    return false;
832 }
833                                                                         /*}}}*/
834 // HttpMethod::ServerDie - The server has closed the connection.        /*{{{*/
835 // ---------------------------------------------------------------------
836 /* */
837 bool HttpMethod::ServerDie(ServerState *Srv)
838 {
839    unsigned int LErrno = errno;
840    
841    // Dump the buffer to the file
842    if (Srv->State == ServerState::Data)
843    {
844       SetNonBlock(File->Fd(),false);
845       while (Srv->In.WriteSpace() == true)
846       {
847          if (Srv->In.Write(File->Fd()) == false)
848             return _error->Errno("write",_("Error writing to the file"));
849
850          // Done
851          if (Srv->In.IsLimit() == true)
852             return true;
853       }
854    }
855    
856    // See if this is because the server finished the data stream
857    if (Srv->In.IsLimit() == false && Srv->State != ServerState::Header && 
858        Srv->Encoding != ServerState::Closes)
859    {
860       Srv->Close();
861       if (LErrno == 0)
862          return _error->Error(_("Error reading from server Remote end closed connection"));
863       errno = LErrno;
864       return _error->Errno("read",_("Error reading from server"));
865    }
866    else
867    {
868       Srv->In.Limit(-1);
869
870       // Nothing left in the buffer
871       if (Srv->In.WriteSpace() == false)
872          return false;
873       
874       // We may have got multiple responses back in one packet..
875       Srv->Close();
876       return true;
877    }
878    
879    return false;
880 }
881                                                                         /*}}}*/
882 // HttpMethod::DealWithHeaders - Handle the retrieved header data       /*{{{*/
883 // ---------------------------------------------------------------------
884 /* We look at the header data we got back from the server and decide what
885    to do. Returns 
886      0 - File is open,
887      1 - IMS hit
888      3 - Unrecoverable error 
889      4 - Error with error content page
890      5 - Unrecoverable non-server error (close the connection)
891      6 - Try again with a new or changed URI  */
892 int HttpMethod::DealWithHeaders(FetchResult &Res,ServerState *Srv)
893 {
894    // Not Modified
895    if (Srv->Result == 304)
896    {
897       unlink(Queue->DestFile.c_str());
898       Res.IMSHit = true;
899       Res.LastModified = Queue->LastModified;
900       return 1;
901    }
902
903    /* Redirect
904     *
905     * Note that it is only OK for us to treat all redirection the same
906     * because we *always* use GET, not other HTTP methods.  There are
907     * three redirection codes for which it is not appropriate that we
908     * redirect.  Pass on those codes so the error handling kicks in.
909     */
910    if ((Srv->Result > 300 && Srv->Result < 400)
911        && (Srv->Result != 300       // Multiple Choices
912            && Srv->Result != 304    // Not Modified
913            && Srv->Result != 306))  // (Not part of HTTP/1.1, reserved)
914    {
915       if (!Srv->Location.empty())
916       {
917          NextURI = Srv->Location;
918          return 6;
919       }
920       else
921          return 3;
922    }
923
924    // Authentication
925    if (Srv->Result == 401)
926    {
927       string Description;
928       string AuthUser, AuthPass;
929       vector<AuthRec>::iterator CurrentAuth;
930       URI ParsedURI(Queue->Uri);
931
932       // Have we had to log in to this site before?
933       if (ParsedURI.User.empty())
934       {
935          for (CurrentAuth = AuthList.begin(); CurrentAuth != AuthList.end();
936               CurrentAuth++)
937             if (CurrentAuth->Host == Srv->ServerName.Host &&
938                 CurrentAuth->Realm == Srv->Realm)
939             {
940                AuthUser = CurrentAuth->User;
941                AuthPass = CurrentAuth->Password;
942                break;
943             }
944       }
945       else
946          CurrentAuth = AuthList.end();
947
948       // Nope - get username and password
949       if (CurrentAuth == AuthList.end())
950       {
951          Description = ParsedURI.Host + ":" + Srv->Realm;
952
953 #ifdef WITH_SSL
954          if (ParsedURI.Access == "https")
955             Description += string(" (secure)");
956 #endif
957
958          if (NeedAuth(Description, AuthUser, AuthPass) == true)
959          {
960             // Got new credentials; save them
961             AuthRec NewAuthInfo;
962
963             NewAuthInfo.Host = Srv->ServerName.Host;
964             NewAuthInfo.Realm = Srv->Realm;
965             NewAuthInfo.User = AuthUser;
966             NewAuthInfo.Password = AuthPass;
967
968             for (CurrentAuth = AuthList.begin(); CurrentAuth != AuthList.end();
969                  CurrentAuth++)
970                if (CurrentAuth->Host == Srv->ServerName.Host &&
971                    CurrentAuth->Realm == Srv->Realm)
972                {
973                   *CurrentAuth = NewAuthInfo;
974                   break;
975                }
976             
977             if (CurrentAuth == AuthList.end())
978                AuthList.push_back(NewAuthInfo);
979          }
980          else
981             // Interactive auth failed
982             return 4;
983       }
984
985       // Try the same URI again, with credentials this time
986       ParsedURI.User = AuthUser;
987       ParsedURI.Password = AuthPass;
988       NextURI = ParsedURI;
989       return 6;
990    }
991
992    /* We have a reply we dont handle. This should indicate a perm server
993       failure */
994    if (Srv->Result < 200 || Srv->Result >= 300)
995    {
996       _error->Error("%u %s",Srv->Result,Srv->Code);
997       if (Srv->HaveContent == true)
998          return 4;
999       return 3;
1000    }
1001
1002    // This is some sort of 2xx 'data follows' reply
1003    Res.LastModified = Srv->Date;
1004    Res.Size = Srv->Size;
1005    
1006    // Open the file
1007    delete File;
1008    File = new FileFd(Queue->DestFile,FileFd::WriteAny);
1009    if (_error->PendingError() == true)
1010       return 5;
1011
1012    FailFile = Queue->DestFile;
1013    FailFile.c_str();   // Make sure we dont do a malloc in the signal handler
1014    FailFd = File->Fd();
1015    FailTime = Srv->Date;
1016       
1017    // Set the expected size
1018    if (Srv->StartPos >= 0)
1019    {
1020       Res.ResumePoint = Srv->StartPos;
1021       if (ftruncate(File->Fd(),Srv->StartPos) != 0)
1022          return 5;
1023    }
1024       
1025    // Set the start point
1026    lseek(File->Fd(),0,SEEK_END);
1027
1028    delete Srv->In.Hash;
1029    Srv->In.Hash = new Hashes;
1030    
1031    // Fill the Hash if the file is non-empty (resume)
1032    if (Srv->StartPos > 0)
1033    {
1034       lseek(File->Fd(),0,SEEK_SET);
1035       if (Srv->In.Hash->AddFD(File->Fd(),Srv->StartPos) == false)
1036       {
1037          _error->Errno("read",_("Problem hashing file"));
1038          return 5;
1039       }
1040       lseek(File->Fd(),0,SEEK_END);
1041    }
1042    
1043    SetNonBlock(File->Fd(),true);
1044    return 0;
1045 }
1046                                                                         /*}}}*/
1047 // HttpMethod::SigTerm - Handle a fatal signal                          /*{{{*/
1048 // ---------------------------------------------------------------------
1049 /* This closes and timestamps the open file. This is neccessary to get 
1050    resume behavoir on user abort */
1051 void HttpMethod::SigTerm(int)
1052 {
1053    if (FailFd == -1)
1054       _exit(100);
1055    close(FailFd);
1056    
1057    // Timestamp
1058    struct utimbuf UBuf;
1059    UBuf.actime = FailTime;
1060    UBuf.modtime = FailTime;
1061    utime(FailFile.c_str(),&UBuf);
1062    
1063    _exit(100);
1064 }
1065                                                                         /*}}}*/
1066 // HttpMethod::Fetch - Fetch an item                                    /*{{{*/
1067 // ---------------------------------------------------------------------
1068 /* This adds an item to the pipeline. We keep the pipeline at a fixed
1069    depth. */
1070 bool HttpMethod::Fetch(FetchItem *)
1071 {
1072    if (Server == 0)
1073       return true;
1074
1075    // Queue the requests
1076    int Depth = -1;
1077    bool Tail = false;
1078    for (FetchItem *I = Queue; I != 0 && Depth < (signed)PipelineDepth; 
1079         I = I->Next, Depth++)
1080    {
1081       // If pipelining is disabled, we only queue 1 request
1082       if (Server->Pipeline == false && Depth >= 0)
1083          break;
1084
1085       // If we're choking the pipeline, we only queue 1 request
1086       if (ChokePipe == true && Depth >= 0)
1087       {
1088          ChokePipe = false;
1089          break;
1090       }
1091       
1092       // Make sure we stick with the same server
1093       if (Server->Comp(I->Uri) == false)
1094       {
1095          ChokePipe = true;
1096          break;
1097       }
1098
1099       if (QueueBack == I)
1100          Tail = true;
1101       if (Tail == true)
1102       {
1103          QueueBack = I->Next;
1104          SendReq(I,Server->Out);
1105          continue;
1106       }
1107    }
1108    
1109    return true;
1110 }
1111                                                                         /*}}}*/
1112 // HttpMethod::Configuration - Handle a configuration message           /*{{{*/
1113 // ---------------------------------------------------------------------
1114 /* We stash the desired pipeline depth */
1115 bool HttpMethod::Configuration(string Message)
1116 {
1117    if (pkgAcqMethod::Configuration(Message) == false)
1118       return false;
1119    
1120    TimeOut = _config->FindI("Acquire::http::Timeout",TimeOut);
1121    PipelineDepth = _config->FindI("Acquire::http::Pipeline-Depth",
1122                                   PipelineDepth);
1123    Debug = _config->FindB("Debug::Acquire::http",false);
1124    
1125    return true;
1126 }
1127                                                                         /*}}}*/
1128 // HttpMethod::Loop - Main loop                                         /*{{{*/
1129 // ---------------------------------------------------------------------
1130 /* */
1131 int HttpMethod::Loop()
1132 {
1133    typedef vector<string> StringVector;
1134    typedef vector<string>::iterator StringVectorIterator;
1135    map<string, StringVector> Redirected;
1136
1137    signal(SIGTERM,SigTerm);
1138    signal(SIGINT,SigTerm);
1139    
1140    Server = 0;
1141    
1142    int FailCounter = 0;
1143
1144    while (1)
1145    {      
1146       // We have no commands, wait for some to arrive
1147       if (Queue == 0)
1148       {
1149          if (WaitFd(STDIN_FILENO) == false)
1150             return 0;
1151       }
1152       
1153       /* Run messages, we can accept 0 (no message) if we didn't
1154          do a WaitFd above.. Otherwise the FD is closed. */
1155       int Result = Run(true);
1156       if (Result != -1 && (Result != 0 || Queue == 0))
1157          return 100;
1158
1159       if (Queue == 0)
1160          continue;
1161       
1162       // Connect to the server
1163       if (Server == 0 || Server->Comp(Queue->Uri) == false)
1164       {
1165          delete Server;
1166          Server = new ServerState(Queue->Uri,this);
1167       }
1168       
1169       /* If the server has explicitly said this is the last connection
1170          then we pre-emptively shut down the pipeline and tear down 
1171          the connection. This will speed up HTTP/1.0 servers a tad
1172          since we don't have to wait for the close sequence to
1173          complete */
1174       if (Server->Persistent == false)
1175          Server->Close();
1176       
1177       // Reset the pipeline
1178       if (Server->ServerFd == -1)
1179          QueueBack = Queue;      
1180          
1181       // Connnect to the host
1182       if (Server->Open() == false)
1183       {
1184          Fail(true);
1185          delete Server;
1186          Server = 0;
1187          continue;
1188       }
1189
1190       // Fill the pipeline.
1191       Fetch(0);
1192       
1193       // Fetch the next URL header data from the server.
1194       switch (Server->RunHeaders())
1195       {
1196          case 0:
1197          break;
1198          
1199          // The header data is bad
1200          case 2:
1201          {
1202             _error->Error(_("Bad header Data"));
1203             Fail(true);
1204             RotateDNS();
1205             continue;
1206          }
1207          
1208          // The server closed a connection during the header get..
1209          default:
1210          case 1:
1211          {
1212             FailCounter++;
1213             _error->Discard();
1214             Server->Close();
1215             Server->Pipeline = false;
1216             
1217             if (FailCounter >= 2)
1218             {
1219                Fail(_("Connection failed"),true);
1220                FailCounter = 0;
1221             }
1222             
1223             RotateDNS();
1224             continue;
1225          }
1226       };
1227
1228       // Decide what to do.
1229       FetchResult Res;
1230       Res.Filename = Queue->DestFile;
1231       switch (DealWithHeaders(Res,Server))
1232       {
1233          // Ok, the file is Open
1234          case 0:
1235          {
1236             URIStart(Res);
1237
1238             // Run the data
1239             bool Result =  Server->RunData();
1240
1241             /* If the server is sending back sizeless responses then fill in
1242                the size now */
1243             if (Res.Size == 0)
1244                Res.Size = File->Size();
1245             
1246             // Close the file, destroy the FD object and timestamp it
1247             FailFd = -1;
1248             delete File;
1249             File = 0;
1250             
1251             // Timestamp
1252             struct utimbuf UBuf;
1253             time(&UBuf.actime);
1254             UBuf.actime = Server->Date;
1255             UBuf.modtime = Server->Date;
1256             utime(Queue->DestFile.c_str(),&UBuf);
1257
1258             // Send status to APT
1259             if (Result == true)
1260             {
1261                Res.TakeHashes(*Server->In.Hash);
1262                URIDone(Res);
1263             }
1264             else
1265                Fail(true);
1266             
1267             break;
1268          }
1269          
1270          // IMS hit
1271          case 1:
1272          {
1273             URIDone(Res);
1274             break;
1275          }
1276          
1277          // Hard server error, not found or something
1278          case 3:
1279          {
1280             Fail();
1281             break;
1282          }
1283           
1284          // Hard internal error, kill the connection and fail
1285          case 5:
1286          {
1287             delete File;
1288             File = 0;
1289
1290             Fail();
1291             RotateDNS();
1292             Server->Close();
1293             break;
1294          }
1295
1296          // We need to flush the data, the header is like a 404 w/ error text
1297          case 4:
1298          {
1299             Fail();
1300             
1301             // Send to content to dev/null
1302             File = new FileFd("/dev/null",FileFd::WriteExists);
1303             Server->RunData();
1304             delete File;
1305             File = 0;
1306             break;
1307          }
1308
1309          // Try again with a new URL
1310          case 6:
1311          {
1312             // Clear rest of response if there is content
1313             if (Server->HaveContent)
1314             {
1315                File = new FileFd("/dev/null",FileFd::WriteExists);
1316                Server->RunData();
1317                delete File;
1318                File = 0;
1319             }
1320
1321             /* Detect redirect loops.  No more redirects are allowed
1322                after the same URI is seen twice in a queue item. */
1323             StringVector &R = Redirected[Queue->DestFile];
1324             bool StopRedirects = false;
1325             if (R.size() == 0)
1326                R.push_back(Queue->Uri);
1327             else if (R[0] == "STOP")
1328                StopRedirects = true;
1329             else
1330             {
1331                for (StringVectorIterator I = R.begin(); I != R.end(); I++)
1332                   if (Queue->Uri == *I)
1333                   {
1334                      R[0] = "STOP";
1335                      break;
1336                   }
1337
1338                R.push_back(Queue->Uri);
1339             }
1340
1341             if (StopRedirects == false)
1342                Redirect(NextURI);
1343             else
1344                Fail();
1345
1346             break;
1347          }
1348          
1349          default:
1350          Fail(_("Internal error"));
1351          break;
1352       }
1353       
1354       FailCounter = 0;
1355    }
1356    
1357    return 0;
1358 }
1359                                                                         /*}}}*/
1360
1361 int main()
1362 {
1363    HttpMethod Mth;
1364    
1365    return Mth.Loop();
1366 }
1367
1368