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