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