]>
Commit | Line | Data |
---|---|---|
da6ee469 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 | ||
00ec24d0 | 6 | HTTP Acquire Method - This is the HTTP aquire method for APT. |
da6ee469 JF |
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> | |
0e5943eb | 32 | #include <apt-pkg/netrc.h> |
da6ee469 | 33 | |
ca652fea | 34 | #include <sys/sysctl.h> |
da6ee469 JF |
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> | |
0e5943eb | 44 | #include <map> |
788c5b99 | 45 | #include <set> |
0e5943eb JF |
46 | #include <apti18n.h> |
47 | ||
da6ee469 JF |
48 | |
49 | // Internet stuff | |
50 | #include <netdb.h> | |
fe23a696 | 51 | #include <arpa/inet.h> |
da6ee469 | 52 | |
acc2d4c9 | 53 | #include <dlfcn.h> |
4db93271 | 54 | #include <lockdown.h> |
253ba34a | 55 | #include <CoreFoundation/CoreFoundation.h> |
25d34b29 | 56 | #include <CFNetwork/CFNetwork.h> |
985ed269 | 57 | #include <SystemConfiguration/SystemConfiguration.h> |
253ba34a | 58 | |
00ec24d0 | 59 | #include "config.h" |
da6ee469 | 60 | #include "http.h" |
da6ee469 JF |
61 | /*}}}*/ |
62 | using namespace std; | |
63 | ||
9439ca0e JF |
64 | static CFStringRef Firmware_; |
65 | static const char *Machine_; | |
66 | static CFStringRef UniqueID_; | |
ca652fea | 67 | |
fe23a696 | 68 | void CfrsError(const char *name, CFReadStreamRef rs) { |
985ed269 JF |
69 | CFStreamError se = CFReadStreamGetError(rs); |
70 | ||
71 | if (se.domain == kCFStreamErrorDomainCustom) { | |
72 | } else if (se.domain == kCFStreamErrorDomainPOSIX) { | |
73 | _error->Error("POSIX: %s", strerror(se.error)); | |
74 | } else if (se.domain == kCFStreamErrorDomainMacOSStatus) { | |
75 | _error->Error("MacOSStatus: %ld", se.error); | |
76 | } else if (se.domain == kCFStreamErrorDomainNetDB) { | |
fe23a696 | 77 | _error->Error("NetDB: %s %s", name, gai_strerror(se.error)); |
985ed269 JF |
78 | } else if (se.domain == kCFStreamErrorDomainMach) { |
79 | _error->Error("Mach: %ld", se.error); | |
80 | } else if (se.domain == kCFStreamErrorDomainHTTP) { | |
81 | switch (se.error) { | |
82 | case kCFStreamErrorHTTPParseFailure: | |
83 | _error->Error("Parse failure"); | |
84 | break; | |
85 | ||
86 | case kCFStreamErrorHTTPRedirectionLoop: | |
87 | _error->Error("Redirection loop"); | |
88 | break; | |
89 | ||
90 | case kCFStreamErrorHTTPBadURL: | |
91 | _error->Error("Bad URL"); | |
92 | break; | |
93 | ||
94 | default: | |
95 | _error->Error("Unknown HTTP error: %ld", se.error); | |
96 | break; | |
97 | } | |
98 | } else if (se.domain == kCFStreamErrorDomainSOCKS) { | |
99 | _error->Error("SOCKS: %ld", se.error); | |
100 | } else if (se.domain == kCFStreamErrorDomainSystemConfiguration) { | |
101 | _error->Error("SystemConfiguration: %ld", se.error); | |
102 | } else if (se.domain == kCFStreamErrorDomainSSL) { | |
103 | _error->Error("SSL: %ld", se.error); | |
104 | } else { | |
b7a89731 | 105 | _error->Error("Domain #%ld: %ld", se.domain, se.error); |
985ed269 JF |
106 | } |
107 | } | |
108 | ||
da6ee469 JF |
109 | string HttpMethod::FailFile; |
110 | int HttpMethod::FailFd = -1; | |
111 | time_t HttpMethod::FailTime = 0; | |
112 | unsigned long PipelineDepth = 10; | |
113 | unsigned long TimeOut = 120; | |
0e5943eb | 114 | bool AllowRedirect = false; |
da6ee469 | 115 | bool Debug = false; |
00ec24d0 | 116 | URI Proxy; |
da6ee469 | 117 | |
788c5b99 JF |
118 | static const CFOptionFlags kNetworkEvents = |
119 | kCFStreamEventOpenCompleted | | |
120 | kCFStreamEventHasBytesAvailable | | |
121 | kCFStreamEventEndEncountered | | |
122 | kCFStreamEventErrorOccurred | | |
123 | 0; | |
124 | ||
125 | static void CFReadStreamCallback(CFReadStreamRef stream, CFStreamEventType event, void *arg) { | |
126 | switch (event) { | |
127 | case kCFStreamEventOpenCompleted: | |
128 | break; | |
129 | ||
130 | case kCFStreamEventHasBytesAvailable: | |
131 | case kCFStreamEventEndEncountered: | |
132 | *reinterpret_cast<int *>(arg) = 1; | |
133 | CFRunLoopStop(CFRunLoopGetCurrent()); | |
134 | break; | |
135 | ||
136 | case kCFStreamEventErrorOccurred: | |
137 | *reinterpret_cast<int *>(arg) = -1; | |
138 | CFRunLoopStop(CFRunLoopGetCurrent()); | |
139 | break; | |
140 | } | |
141 | } | |
142 | ||
143 | /* http://lists.apple.com/archives/Macnetworkprog/2006/Apr/msg00014.html */ | |
144 | int CFReadStreamOpen(CFReadStreamRef stream, double timeout) { | |
145 | CFStreamClientContext context; | |
146 | int value(0); | |
147 | ||
148 | memset(&context, 0, sizeof(context)); | |
149 | context.info = &value; | |
150 | ||
151 | if (CFReadStreamSetClient(stream, kNetworkEvents, CFReadStreamCallback, &context)) { | |
152 | CFReadStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); | |
153 | if (CFReadStreamOpen(stream)) | |
154 | CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeout, false); | |
155 | else | |
156 | value = -1; | |
157 | CFReadStreamSetClient(stream, kCFStreamEventNone, NULL, NULL); | |
158 | } | |
159 | ||
160 | return value; | |
161 | } | |
162 | ||
da6ee469 JF |
163 | // HttpMethod::SigTerm - Handle a fatal signal /*{{{*/ |
164 | // --------------------------------------------------------------------- | |
165 | /* This closes and timestamps the open file. This is neccessary to get | |
166 | resume behavoir on user abort */ | |
167 | void HttpMethod::SigTerm(int) | |
168 | { | |
169 | if (FailFd == -1) | |
170 | _exit(100); | |
171 | close(FailFd); | |
172 | ||
173 | // Timestamp | |
174 | struct utimbuf UBuf; | |
175 | UBuf.actime = FailTime; | |
176 | UBuf.modtime = FailTime; | |
177 | utime(FailFile.c_str(),&UBuf); | |
178 | ||
179 | _exit(100); | |
180 | } | |
181 | /*}}}*/ | |
da6ee469 JF |
182 | // HttpMethod::Configuration - Handle a configuration message /*{{{*/ |
183 | // --------------------------------------------------------------------- | |
184 | /* We stash the desired pipeline depth */ | |
185 | bool HttpMethod::Configuration(string Message) | |
186 | { | |
187 | if (pkgAcqMethod::Configuration(Message) == false) | |
188 | return false; | |
189 | ||
0e5943eb | 190 | AllowRedirect = _config->FindB("Acquire::http::AllowRedirect",true); |
da6ee469 JF |
191 | TimeOut = _config->FindI("Acquire::http::Timeout",TimeOut); |
192 | PipelineDepth = _config->FindI("Acquire::http::Pipeline-Depth", | |
193 | PipelineDepth); | |
194 | Debug = _config->FindB("Debug::Acquire::http",false); | |
195 | ||
196 | return true; | |
197 | } | |
198 | /*}}}*/ | |
199 | // HttpMethod::Loop - Main loop /*{{{*/ | |
200 | // --------------------------------------------------------------------- | |
201 | /* */ | |
202 | int HttpMethod::Loop() | |
203 | { | |
0e5943eb JF |
204 | typedef vector<string> StringVector; |
205 | typedef vector<string>::iterator StringVectorIterator; | |
206 | map<string, StringVector> Redirected; | |
207 | ||
da6ee469 JF |
208 | signal(SIGTERM,SigTerm); |
209 | signal(SIGINT,SigTerm); | |
210 | ||
788c5b99 | 211 | std::set<std::string> cached; |
da6ee469 JF |
212 | |
213 | int FailCounter = 0; | |
214 | while (1) | |
215 | { | |
216 | // We have no commands, wait for some to arrive | |
217 | if (Queue == 0) | |
218 | { | |
219 | if (WaitFd(STDIN_FILENO) == false) | |
220 | return 0; | |
221 | } | |
222 | ||
223 | /* Run messages, we can accept 0 (no message) if we didn't | |
224 | do a WaitFd above.. Otherwise the FD is closed. */ | |
225 | int Result = Run(true); | |
226 | if (Result != -1 && (Result != 0 || Queue == 0)) | |
227 | return 100; | |
228 | ||
229 | if (Queue == 0) | |
230 | continue; | |
0e5943eb | 231 | |
253ba34a JF |
232 | CFStringEncoding se = kCFStringEncodingUTF8; |
233 | ||
b7a89731 JF |
234 | char *url = strdup(Queue->Uri.c_str()); |
235 | url: | |
ca652fea | 236 | URI uri = std::string(url); |
fe23a696 JF |
237 | std::string hs = uri.Host; |
238 | ||
788c5b99 JF |
239 | if (cached.find(hs) != cached.end()) { |
240 | _error->Error("Cached Failure"); | |
241 | Fail(true); | |
242 | free(url); | |
243 | FailCounter = 0; | |
244 | continue; | |
245 | } | |
246 | ||
fe23a696 JF |
247 | std::string urs = uri; |
248 | ||
bdccc0e5 JF |
249 | for (;;) { |
250 | size_t bad = urs.find_first_of("+"); | |
251 | if (bad == std::string::npos) | |
252 | break; | |
253 | // XXX: generalize | |
254 | urs = urs.substr(0, bad) + "%2b" + urs.substr(bad + 1); | |
255 | } | |
256 | ||
fe23a696 | 257 | CFStringRef sr = CFStringCreateWithCString(kCFAllocatorDefault, urs.c_str(), se); |
253ba34a JF |
258 | CFURLRef ur = CFURLCreateWithString(kCFAllocatorDefault, sr, NULL); |
259 | CFRelease(sr); | |
260 | CFHTTPMessageRef hm = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("GET"), ur, kCFHTTPVersion1_1); | |
261 | CFRelease(ur); | |
262 | ||
263 | struct stat SBuf; | |
264 | if (stat(Queue->DestFile.c_str(), &SBuf) >= 0 && SBuf.st_size > 0) { | |
265 | sr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("bytes=%li-"), (long) SBuf.st_size - 1); | |
266 | CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("Range"), sr); | |
267 | CFRelease(sr); | |
268 | ||
269 | sr = CFStringCreateWithCString(kCFAllocatorDefault, TimeRFC1123(SBuf.st_mtime).c_str(), se); | |
270 | CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("If-Range"), sr); | |
271 | CFRelease(sr); | |
78741bff JF |
272 | |
273 | CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("Cache-Control"), CFSTR("no-cache")); | |
253ba34a | 274 | } else if (Queue->LastModified != 0) { |
fc9b22c7 | 275 | sr = CFStringCreateWithCString(kCFAllocatorDefault, TimeRFC1123(Queue->LastModified).c_str(), se); |
253ba34a JF |
276 | CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("If-Modified-Since"), sr); |
277 | CFRelease(sr); | |
78741bff JF |
278 | |
279 | CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("Cache-Control"), CFSTR("no-cache")); | |
280 | } else | |
281 | CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("Cache-Control"), CFSTR("max-age=0")); | |
253ba34a | 282 | |
032f992c JF |
283 | if (Firmware_ != NULL) |
284 | CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("X-Firmware"), Firmware_); | |
285 | ||
ca652fea JF |
286 | sr = CFStringCreateWithCString(kCFAllocatorDefault, Machine_, se); |
287 | CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("X-Machine"), sr); | |
288 | CFRelease(sr); | |
289 | ||
4db93271 JF |
290 | if (UniqueID_ != NULL) |
291 | CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("X-Unique-ID"), UniqueID_); | |
ca652fea | 292 | |
acdafb44 | 293 | CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("User-Agent"), CFSTR("Telesphoreo APT-HTTP/1.0.592")); |
fe23a696 | 294 | |
253ba34a JF |
295 | CFReadStreamRef rs = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, hm); |
296 | CFRelease(hm); | |
297 | ||
788c5b99 JF |
298 | #define _kCFStreamPropertyReadTimeout CFSTR("_kCFStreamPropertyReadTimeout") |
299 | #define _kCFStreamPropertyWriteTimeout CFSTR("_kCFStreamPropertyWriteTimeout") | |
300 | #define _kCFStreamPropertySocketImmediateBufferTimeOut CFSTR("_kCFStreamPropertySocketImmediateBufferTimeOut") | |
301 | ||
302 | /*SInt32 to(TimeOut); | |
303 | CFNumberRef nm(CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &to));*/ | |
304 | double to(TimeOut); | |
305 | CFNumberRef nm(CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &to)); | |
306 | ||
307 | CFReadStreamSetProperty(rs, _kCFStreamPropertyReadTimeout, nm); | |
308 | CFReadStreamSetProperty(rs, _kCFStreamPropertyWriteTimeout, nm); | |
309 | CFReadStreamSetProperty(rs, _kCFStreamPropertySocketImmediateBufferTimeOut, nm); | |
310 | CFRelease(nm); | |
311 | ||
985ed269 JF |
312 | CFDictionaryRef dr = SCDynamicStoreCopyProxies(NULL); |
313 | CFReadStreamSetProperty(rs, kCFStreamPropertyHTTPProxy, dr); | |
314 | CFRelease(dr); | |
315 | ||
b7a89731 | 316 | //CFReadStreamSetProperty(rs, kCFStreamPropertyHTTPShouldAutoredirect, kCFBooleanTrue); |
253ba34a JF |
317 | CFReadStreamSetProperty(rs, kCFStreamPropertyHTTPAttemptPersistentConnection, kCFBooleanTrue); |
318 | ||
910f26ec | 319 | FetchResult Res; |
b7a89731 JF |
320 | CFIndex rd; |
321 | UInt32 sc; | |
910f26ec JF |
322 | |
323 | uint8_t data[10240]; | |
324 | size_t offset = 0; | |
325 | ||
fe23a696 | 326 | Status("Connecting to %s", hs.c_str()); |
253ba34a | 327 | |
788c5b99 JF |
328 | switch (CFReadStreamOpen(rs, to)) { |
329 | case -1: | |
330 | CfrsError("Open", rs); | |
331 | goto fail; | |
332 | ||
333 | case 0: | |
334 | _error->Error("Host Unreachable"); | |
335 | cached.insert(hs); | |
336 | goto fail; | |
337 | ||
338 | case 1: | |
339 | /* success */ | |
340 | break; | |
341 | ||
342 | fail: | |
343 | Fail(true); | |
910f26ec | 344 | goto done; |
da6ee469 JF |
345 | } |
346 | ||
b7a89731 | 347 | rd = CFReadStreamRead(rs, data, sizeof(data)); |
da6ee469 | 348 | |
910f26ec | 349 | if (rd == -1) { |
fe23a696 | 350 | CfrsError(uri.Host.c_str(), rs); |
788c5b99 | 351 | cached.insert(hs); |
910f26ec JF |
352 | Fail(true); |
353 | goto done; | |
354 | } | |
355 | ||
da6ee469 | 356 | Res.Filename = Queue->DestFile; |
da6ee469 | 357 | |
253ba34a | 358 | hm = (CFHTTPMessageRef) CFReadStreamCopyProperty(rs, kCFStreamPropertyHTTPResponseHeader); |
b7a89731 JF |
359 | sc = CFHTTPMessageGetResponseStatusCode(hm); |
360 | ||
415938a5 | 361 | if (sc == 301 || sc == 302) { |
b7a89731 JF |
362 | sr = CFHTTPMessageCopyHeaderFieldValue(hm, CFSTR("Location")); |
363 | if (sr == NULL) { | |
364 | Fail(); | |
365 | goto done_; | |
366 | } else { | |
367 | size_t ln = CFStringGetLength(sr) + 1; | |
368 | free(url); | |
369 | url = static_cast<char *>(malloc(ln)); | |
370 | ||
371 | if (!CFStringGetCString(sr, url, ln, se)) { | |
372 | Fail(); | |
373 | goto done_; | |
374 | } | |
375 | ||
376 | CFRelease(sr); | |
377 | goto url; | |
378 | } | |
379 | } | |
253ba34a | 380 | |
253ba34a JF |
381 | sr = CFHTTPMessageCopyHeaderFieldValue(hm, CFSTR("Content-Range")); |
382 | if (sr != NULL) { | |
383 | size_t ln = CFStringGetLength(sr) + 1; | |
384 | char cr[ln]; | |
385 | ||
386 | if (!CFStringGetCString(sr, cr, ln, se)) { | |
387 | Fail(); | |
985ed269 | 388 | goto done_; |
253ba34a JF |
389 | } |
390 | ||
391 | CFRelease(sr); | |
392 | ||
393 | if (sscanf(cr, "bytes %lu-%*u/%lu", &offset, &Res.Size) != 2) { | |
394 | _error->Error(_("The HTTP server sent an invalid Content-Range header")); | |
395 | Fail(); | |
985ed269 | 396 | goto done_; |
253ba34a JF |
397 | } |
398 | ||
399 | if (offset > Res.Size) { | |
400 | _error->Error(_("This HTTP server has broken range support")); | |
401 | Fail(); | |
985ed269 | 402 | goto done_; |
253ba34a JF |
403 | } |
404 | } else { | |
405 | sr = CFHTTPMessageCopyHeaderFieldValue(hm, CFSTR("Content-Length")); | |
406 | if (sr != NULL) { | |
407 | Res.Size = CFStringGetIntValue(sr); | |
408 | CFRelease(sr); | |
409 | } | |
410 | } | |
da6ee469 | 411 | |
253ba34a JF |
412 | time(&Res.LastModified); |
413 | ||
414 | sr = CFHTTPMessageCopyHeaderFieldValue(hm, CFSTR("Last-Modified")); | |
415 | if (sr != NULL) { | |
416 | size_t ln = CFStringGetLength(sr) + 1; | |
417 | char cr[ln]; | |
418 | ||
419 | if (!CFStringGetCString(sr, cr, ln, se)) { | |
420 | Fail(); | |
985ed269 | 421 | goto done_; |
253ba34a JF |
422 | } |
423 | ||
424 | CFRelease(sr); | |
425 | ||
426 | if (!StrToTime(cr, Res.LastModified)) { | |
427 | _error->Error(_("Unknown date format")); | |
428 | Fail(); | |
985ed269 | 429 | goto done_; |
253ba34a JF |
430 | } |
431 | } | |
432 | ||
788c5b99 JF |
433 | if (sc < 200 || sc >= 300 && sc != 304) { |
434 | sr = CFHTTPMessageCopyResponseStatusLine(hm); | |
435 | ||
436 | size_t ln = CFStringGetLength(sr) + 1; | |
437 | char cr[ln]; | |
438 | ||
439 | if (!CFStringGetCString(sr, cr, ln, se)) { | |
440 | Fail(); | |
441 | goto done; | |
442 | } | |
443 | ||
444 | CFRelease(sr); | |
445 | ||
446 | _error->Error("%s", cr); | |
447 | ||
448 | Fail(); | |
449 | goto done_; | |
450 | } | |
451 | ||
253ba34a JF |
452 | CFRelease(hm); |
453 | ||
454 | if (sc == 304) { | |
455 | unlink(Queue->DestFile.c_str()); | |
456 | Res.IMSHit = true; | |
457 | Res.LastModified = Queue->LastModified; | |
458 | URIDone(Res); | |
788c5b99 | 459 | } else { |
253ba34a JF |
460 | Hashes hash; |
461 | ||
462 | File = new FileFd(Queue->DestFile, FileFd::WriteAny); | |
463 | if (_error->PendingError() == true) { | |
464 | delete File; | |
465 | File = NULL; | |
466 | Fail(); | |
467 | goto done; | |
468 | } | |
469 | ||
470 | FailFile = Queue->DestFile; | |
471 | FailFile.c_str(); // Make sure we dont do a malloc in the signal handler | |
472 | FailFd = File->Fd(); | |
473 | FailTime = Res.LastModified; | |
474 | ||
475 | Res.ResumePoint = offset; | |
476 | ftruncate(File->Fd(), offset); | |
477 | ||
478 | if (offset != 0) { | |
479 | lseek(File->Fd(), 0, SEEK_SET); | |
480 | if (!hash.AddFD(File->Fd(), offset)) { | |
481 | _error->Errno("read", _("Problem hashing file")); | |
482 | delete File; | |
483 | File = NULL; | |
484 | Fail(); | |
485 | goto done; | |
486 | } | |
487 | } | |
488 | ||
489 | lseek(File->Fd(), 0, SEEK_END); | |
490 | ||
491 | URIStart(Res); | |
492 | ||
910f26ec | 493 | read: if (rd == -1) { |
fe23a696 | 494 | CfrsError("rd", rs); |
253ba34a | 495 | Fail(true); |
910f26ec | 496 | } else if (rd == 0) { |
da6ee469 JF |
497 | if (Res.Size == 0) |
498 | Res.Size = File->Size(); | |
253ba34a | 499 | |
da6ee469 JF |
500 | struct utimbuf UBuf; |
501 | time(&UBuf.actime); | |
253ba34a JF |
502 | UBuf.actime = Res.LastModified; |
503 | UBuf.modtime = Res.LastModified; | |
504 | utime(Queue->DestFile.c_str(), &UBuf); | |
da6ee469 | 505 | |
253ba34a | 506 | Res.TakeHashes(hash); |
da6ee469 | 507 | URIDone(Res); |
253ba34a JF |
508 | } else { |
509 | hash.Add(data, rd); | |
da6ee469 | 510 | |
253ba34a JF |
511 | uint8_t *dt = data; |
512 | while (rd != 0) { | |
513 | int sz = write(File->Fd(), dt, rd); | |
da6ee469 | 514 | |
253ba34a JF |
515 | if (sz == -1) { |
516 | delete File; | |
517 | File = NULL; | |
518 | Fail(); | |
519 | goto done; | |
520 | } | |
521 | ||
522 | dt += sz; | |
523 | rd -= sz; | |
524 | } | |
525 | ||
526 | rd = CFReadStreamRead(rs, data, sizeof(data)); | |
527 | goto read; | |
528 | } | |
da6ee469 | 529 | } |
253ba34a | 530 | |
985ed269 JF |
531 | goto done; |
532 | done_: | |
533 | CFRelease(hm); | |
253ba34a | 534 | done: |
985ed269 | 535 | CFReadStreamClose(rs); |
253ba34a | 536 | CFRelease(rs); |
b7a89731 | 537 | free(url); |
253ba34a | 538 | |
da6ee469 JF |
539 | FailCounter = 0; |
540 | } | |
541 | ||
542 | return 0; | |
543 | } | |
544 | /*}}}*/ | |
545 | ||
4c8eb365 | 546 | int main() |
da6ee469 JF |
547 | { |
548 | setlocale(LC_ALL, ""); | |
0e5943eb JF |
549 | // ignore SIGPIPE, this can happen on write() if the socket |
550 | // closes the connection (this is dealt with via ServerDie()) | |
551 | signal(SIGPIPE, SIG_IGN); | |
da6ee469 JF |
552 | |
553 | HttpMethod Mth; | |
ca652fea JF |
554 | |
555 | size_t size; | |
556 | sysctlbyname("hw.machine", NULL, &size, NULL, 0); | |
557 | char *machine = new char[size]; | |
558 | sysctlbyname("hw.machine", machine, &size, NULL, 0); | |
559 | Machine_ = machine; | |
560 | ||
032f992c JF |
561 | const char *path = "/System/Library/CoreServices/SystemVersion.plist"; |
562 | CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (uint8_t *) path, strlen(path), false); | |
563 | ||
564 | CFPropertyListRef plist; { | |
565 | CFReadStreamRef stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, url); | |
566 | CFReadStreamOpen(stream); | |
567 | plist = CFPropertyListCreateFromStream(kCFAllocatorDefault, stream, 0, kCFPropertyListImmutable, NULL, NULL); | |
568 | CFReadStreamClose(stream); | |
569 | } | |
570 | ||
571 | CFRelease(url); | |
572 | ||
573 | if (plist != NULL) { | |
574 | Firmware_ = (CFStringRef) CFRetain(CFDictionaryGetValue((CFDictionaryRef) plist, CFSTR("ProductVersion"))); | |
575 | CFRelease(plist); | |
576 | } | |
577 | ||
acc2d4c9 JF |
578 | if (UniqueID_ == NULL) |
579 | if (void *libMobileGestalt = dlopen("/usr/lib/libMobileGestalt.dylib", RTLD_GLOBAL | RTLD_LAZY)) | |
580 | if (CFStringRef (*$MGCopyAnswer)(CFStringRef) = (CFStringRef (*)(CFStringRef)) dlsym(libMobileGestalt, "MGCopyAnswer")) | |
581 | UniqueID_ = $MGCopyAnswer(CFSTR("UniqueDeviceID")); | |
582 | ||
583 | if (UniqueID_ == NULL) | |
4db93271 JF |
584 | if (void *lockdown = lockdown_connect()) { |
585 | UniqueID_ = lockdown_copy_value(lockdown, NULL, kLockdownUniqueDeviceIDKey); | |
586 | lockdown_disconnect(lockdown); | |
587 | } | |
0e5943eb | 588 | |
da6ee469 JF |
589 | return Mth.Loop(); |
590 | } |