diff -ru apt-0.7.25.3/methods/http.cc apt-0.7.25.3+iPhone/methods/http.cc --- apt-0.7.25.3/methods/http.cc 2010-02-01 19:44:41.000000000 +0000 +++ apt-0.7.25.3+iPhone/methods/http.cc 2010-02-22 20:20:42.000000000 +0000 @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -41,11 +42,19 @@ #include #include #include +#include #include // Internet stuff #include +#include + +#include +#include +#include +#include +#include #include "config.h" #include "connect.h" @@ -54,6 +62,51 @@ /*}}}*/ using namespace std; +CFStringRef Firmware_; +const char *Machine_; +CFStringRef UniqueID_; + +void CfrsError(const char *name, CFReadStreamRef rs) { + CFStreamError se = CFReadStreamGetError(rs); + + if (se.domain == kCFStreamErrorDomainCustom) { + } else if (se.domain == kCFStreamErrorDomainPOSIX) { + _error->Error("POSIX: %s", strerror(se.error)); + } else if (se.domain == kCFStreamErrorDomainMacOSStatus) { + _error->Error("MacOSStatus: %ld", se.error); + } else if (se.domain == kCFStreamErrorDomainNetDB) { + _error->Error("NetDB: %s %s", name, gai_strerror(se.error)); + } else if (se.domain == kCFStreamErrorDomainMach) { + _error->Error("Mach: %ld", se.error); + } else if (se.domain == kCFStreamErrorDomainHTTP) { + switch (se.error) { + case kCFStreamErrorHTTPParseFailure: + _error->Error("Parse failure"); + break; + + case kCFStreamErrorHTTPRedirectionLoop: + _error->Error("Redirection loop"); + break; + + case kCFStreamErrorHTTPBadURL: + _error->Error("Bad URL"); + break; + + default: + _error->Error("Unknown HTTP error: %ld", se.error); + break; + } + } else if (se.domain == kCFStreamErrorDomainSOCKS) { + _error->Error("SOCKS: %ld", se.error); + } else if (se.domain == kCFStreamErrorDomainSystemConfiguration) { + _error->Error("SystemConfiguration: %ld", se.error); + } else if (se.domain == kCFStreamErrorDomainSSL) { + _error->Error("SSL: %ld", se.error); + } else { + _error->Error("Domain #%ld: %ld", se.domain, se.error); + } +} + string HttpMethod::FailFile; int HttpMethod::FailFd = -1; time_t HttpMethod::FailTime = 0; @@ -646,6 +699,51 @@ } /*}}}*/ +static const CFOptionFlags kNetworkEvents = + kCFStreamEventOpenCompleted | + kCFStreamEventHasBytesAvailable | + kCFStreamEventEndEncountered | + kCFStreamEventErrorOccurred | +0; + +static void CFReadStreamCallback(CFReadStreamRef stream, CFStreamEventType event, void *arg) { + switch (event) { + case kCFStreamEventOpenCompleted: + break; + + case kCFStreamEventHasBytesAvailable: + case kCFStreamEventEndEncountered: + *reinterpret_cast(arg) = 1; + CFRunLoopStop(CFRunLoopGetCurrent()); + break; + + case kCFStreamEventErrorOccurred: + *reinterpret_cast(arg) = -1; + CFRunLoopStop(CFRunLoopGetCurrent()); + break; + } +} + +/* http://lists.apple.com/archives/Macnetworkprog/2006/Apr/msg00014.html */ +int CFReadStreamOpen(CFReadStreamRef stream, double timeout) { + CFStreamClientContext context; + int value(0); + + memset(&context, 0, sizeof(context)); + context.info = &value; + + if (CFReadStreamSetClient(stream, kNetworkEvents, CFReadStreamCallback, &context)) { + CFReadStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); + if (CFReadStreamOpen(stream)) + CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeout, false); + else + value = -1; + CFReadStreamSetClient(stream, kCFStreamEventNone, NULL, NULL); + } + + return value; +} + // HttpMethod::SendReq - Send the HTTP request /*{{{*/ // --------------------------------------------------------------------- /* This places the http request in the outbound buffer */ @@ -1088,6 +1186,8 @@ signal(SIGINT,SigTerm); Server = 0; + + std::set cached; int FailCounter = 0; while (1) @@ -1107,214 +1207,314 @@ if (Queue == 0) continue; - - // Connect to the server - if (Server == 0 || Server->Comp(Queue->Uri) == false) - { - delete Server; - Server = new ServerState(Queue->Uri,this); - } - /* If the server has explicitly said this is the last connection - then we pre-emptively shut down the pipeline and tear down - the connection. This will speed up HTTP/1.0 servers a tad - since we don't have to wait for the close sequence to - complete */ - if (Server->Persistent == false) - Server->Close(); - - // Reset the pipeline - if (Server->ServerFd == -1) - QueueBack = Queue; - - // Connnect to the host - if (Server->Open() == false) - { - Fail(true); - delete Server; - Server = 0; - continue; + + CFStringEncoding se = kCFStringEncodingUTF8; + + char *url = strdup(Queue->Uri.c_str()); + url: + URI uri = std::string(url); + std::string hs = uri.Host; + + if (cached.find(hs) != cached.end()) { + _error->Error("Cached Failure"); + Fail(true); + free(url); + FailCounter = 0; + continue; + } + + std::string urs = uri; + + for (;;) { + size_t bad = urs.find_first_of("+"); + if (bad == std::string::npos) + break; + // XXX: generalize + urs = urs.substr(0, bad) + "%2b" + urs.substr(bad + 1); } - // Fill the pipeline. - Fetch(0); - - // Fetch the next URL header data from the server. - switch (Server->RunHeaders()) - { - case 0: - break; - - // The header data is bad - case 2: - { - _error->Error(_("Bad header data")); - Fail(true); - RotateDNS(); - continue; - } - - // The server closed a connection during the header get.. - default: - case 1: - { - FailCounter++; - _error->Discard(); - Server->Close(); - Server->Pipeline = false; - - if (FailCounter >= 2) - { - Fail(_("Connection failed"),true); - FailCounter = 0; - } - - RotateDNS(); - continue; - } - }; + CFStringRef sr = CFStringCreateWithCString(kCFAllocatorDefault, urs.c_str(), se); + CFURLRef ur = CFURLCreateWithString(kCFAllocatorDefault, sr, NULL); + CFRelease(sr); + CFHTTPMessageRef hm = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("GET"), ur, kCFHTTPVersion1_1); + CFRelease(ur); + + struct stat SBuf; + if (stat(Queue->DestFile.c_str(), &SBuf) >= 0 && SBuf.st_size > 0) { + sr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("bytes=%li-"), (long) SBuf.st_size - 1); + CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("Range"), sr); + CFRelease(sr); + + sr = CFStringCreateWithCString(kCFAllocatorDefault, TimeRFC1123(SBuf.st_mtime).c_str(), se); + CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("If-Range"), sr); + CFRelease(sr); + + CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("Cache-Control"), CFSTR("no-cache")); + } else if (Queue->LastModified != 0) { + sr = CFStringCreateWithCString(kCFAllocatorDefault, TimeRFC1123(Queue->LastModified).c_str(), se); + CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("If-Modified-Since"), sr); + CFRelease(sr); + + CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("Cache-Control"), CFSTR("no-cache")); + } else + CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("Cache-Control"), CFSTR("max-age=0")); + + if (Firmware_ != NULL) + CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("X-Firmware"), Firmware_); + + sr = CFStringCreateWithCString(kCFAllocatorDefault, Machine_, se); + CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("X-Machine"), sr); + CFRelease(sr); + + if (UniqueID_ != NULL) + CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("X-Unique-ID"), UniqueID_); + + CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("User-Agent"), CFSTR("Telesphoreo APT-HTTP/1.0.592")); + + CFReadStreamRef rs = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, hm); + CFRelease(hm); + +#define _kCFStreamPropertyReadTimeout CFSTR("_kCFStreamPropertyReadTimeout") +#define _kCFStreamPropertyWriteTimeout CFSTR("_kCFStreamPropertyWriteTimeout") +#define _kCFStreamPropertySocketImmediateBufferTimeOut CFSTR("_kCFStreamPropertySocketImmediateBufferTimeOut") + + /*SInt32 to(TimeOut); + CFNumberRef nm(CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &to));*/ + double to(TimeOut); + CFNumberRef nm(CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &to)); + + CFReadStreamSetProperty(rs, _kCFStreamPropertyReadTimeout, nm); + CFReadStreamSetProperty(rs, _kCFStreamPropertyWriteTimeout, nm); + CFReadStreamSetProperty(rs, _kCFStreamPropertySocketImmediateBufferTimeOut, nm); + CFRelease(nm); + + CFDictionaryRef dr = SCDynamicStoreCopyProxies(NULL); + CFReadStreamSetProperty(rs, kCFStreamPropertyHTTPProxy, dr); + CFRelease(dr); + + //CFReadStreamSetProperty(rs, kCFStreamPropertyHTTPShouldAutoredirect, kCFBooleanTrue); + CFReadStreamSetProperty(rs, kCFStreamPropertyHTTPAttemptPersistentConnection, kCFBooleanTrue); - // Decide what to do. FetchResult Res; + CFIndex rd; + UInt32 sc; + + uint8_t data[10240]; + size_t offset = 0; + + Status("Connecting to %s", hs.c_str()); + + switch (CFReadStreamOpen(rs, to)) { + case -1: + CfrsError("Open", rs); + goto fail; + + case 0: + _error->Error("Host Unreachable"); + cached.insert(hs); + goto fail; + + case 1: + /* success */ + break; + + fail: + Fail(true); + goto done; + } + + rd = CFReadStreamRead(rs, data, sizeof(data)); + + if (rd == -1) { + CfrsError(uri.Host.c_str(), rs); + cached.insert(hs); + Fail(true); + goto done; + } + Res.Filename = Queue->DestFile; - switch (DealWithHeaders(Res,Server)) - { - // Ok, the file is Open - case 0: - { - URIStart(Res); - // Run the data - bool Result = Server->RunData(); + hm = (CFHTTPMessageRef) CFReadStreamCopyProperty(rs, kCFStreamPropertyHTTPResponseHeader); + sc = CFHTTPMessageGetResponseStatusCode(hm); + + if (sc == 301 || sc == 302) { + sr = CFHTTPMessageCopyHeaderFieldValue(hm, CFSTR("Location")); + if (sr == NULL) { + Fail(); + goto done_; + } else { + size_t ln = CFStringGetLength(sr) + 1; + free(url); + url = static_cast(malloc(ln)); + + if (!CFStringGetCString(sr, url, ln, se)) { + Fail(); + goto done_; + } + + CFRelease(sr); + goto url; + } + } + + sr = CFHTTPMessageCopyHeaderFieldValue(hm, CFSTR("Content-Range")); + if (sr != NULL) { + size_t ln = CFStringGetLength(sr) + 1; + char cr[ln]; + + if (!CFStringGetCString(sr, cr, ln, se)) { + Fail(); + goto done_; + } + + CFRelease(sr); + + if (sscanf(cr, "bytes %lu-%*u/%lu", &offset, &Res.Size) != 2) { + _error->Error(_("The HTTP server sent an invalid Content-Range header")); + Fail(); + goto done_; + } + + if (offset > Res.Size) { + _error->Error(_("This HTTP server has broken range support")); + Fail(); + goto done_; + } + } else { + sr = CFHTTPMessageCopyHeaderFieldValue(hm, CFSTR("Content-Length")); + if (sr != NULL) { + Res.Size = CFStringGetIntValue(sr); + CFRelease(sr); + } + } + + time(&Res.LastModified); + + sr = CFHTTPMessageCopyHeaderFieldValue(hm, CFSTR("Last-Modified")); + if (sr != NULL) { + size_t ln = CFStringGetLength(sr) + 1; + char cr[ln]; + + if (!CFStringGetCString(sr, cr, ln, se)) { + Fail(); + goto done_; + } + + CFRelease(sr); + + if (!StrToTime(cr, Res.LastModified)) { + _error->Error(_("Unknown date format")); + Fail(); + goto done_; + } + } + + if (sc < 200 || sc >= 300 && sc != 304) { + sr = CFHTTPMessageCopyResponseStatusLine(hm); - /* If the server is sending back sizeless responses then fill in - the size now */ + size_t ln = CFStringGetLength(sr) + 1; + char cr[ln]; + + if (!CFStringGetCString(sr, cr, ln, se)) { + Fail(); + goto done; + } + + CFRelease(sr); + + _error->Error("%s", cr); + + Fail(); + goto done_; + } + + CFRelease(hm); + + if (sc == 304) { + unlink(Queue->DestFile.c_str()); + Res.IMSHit = true; + Res.LastModified = Queue->LastModified; + URIDone(Res); + } else { + Hashes hash; + + File = new FileFd(Queue->DestFile, FileFd::WriteAny); + if (_error->PendingError() == true) { + delete File; + File = NULL; + Fail(); + goto done; + } + + FailFile = Queue->DestFile; + FailFile.c_str(); // Make sure we dont do a malloc in the signal handler + FailFd = File->Fd(); + FailTime = Res.LastModified; + + Res.ResumePoint = offset; + ftruncate(File->Fd(), offset); + + if (offset != 0) { + lseek(File->Fd(), 0, SEEK_SET); + if (!hash.AddFD(File->Fd(), offset)) { + _error->Errno("read", _("Problem hashing file")); + delete File; + File = NULL; + Fail(); + goto done; + } + } + + lseek(File->Fd(), 0, SEEK_END); + + URIStart(Res); + + read: if (rd == -1) { + CfrsError("rd", rs); + Fail(true); + } else if (rd == 0) { if (Res.Size == 0) Res.Size = File->Size(); - - // Close the file, destroy the FD object and timestamp it - FailFd = -1; - delete File; - File = 0; - - // Timestamp + struct utimbuf UBuf; time(&UBuf.actime); - UBuf.actime = Server->Date; - UBuf.modtime = Server->Date; - utime(Queue->DestFile.c_str(),&UBuf); + UBuf.actime = Res.LastModified; + UBuf.modtime = Res.LastModified; + utime(Queue->DestFile.c_str(), &UBuf); - // Send status to APT - if (Result == true) - { - Res.TakeHashes(*Server->In.Hash); - URIDone(Res); - } - else - { - if (Server->ServerFd == -1) - { - FailCounter++; - _error->Discard(); - Server->Close(); - - if (FailCounter >= 2) - { - Fail(_("Connection failed"),true); - FailCounter = 0; - } - - QueueBack = Queue; - } - else - Fail(true); - } - break; - } - - // IMS hit - case 1: - { + Res.TakeHashes(hash); URIDone(Res); - break; - } - - // Hard server error, not found or something - case 3: - { - Fail(); - break; - } - - // Hard internal error, kill the connection and fail - case 5: - { - delete File; - File = 0; + } else { + hash.Add(data, rd); - Fail(); - RotateDNS(); - Server->Close(); - break; - } + uint8_t *dt = data; + while (rd != 0) { + int sz = write(File->Fd(), dt, rd); + + if (sz == -1) { + delete File; + File = NULL; + Fail(); + goto done; + } - // We need to flush the data, the header is like a 404 w/ error text - case 4: - { - Fail(); - - // Send to content to dev/null - File = new FileFd("/dev/null",FileFd::WriteExists); - Server->RunData(); - delete File; - File = 0; - break; - } - - // Try again with a new URL - case 6: - { - // Clear rest of response if there is content - if (Server->HaveContent) - { - File = new FileFd("/dev/null",FileFd::WriteExists); - Server->RunData(); - delete File; - File = 0; + dt += sz; + rd -= sz; } - /* Detect redirect loops. No more redirects are allowed - after the same URI is seen twice in a queue item. */ - StringVector &R = Redirected[Queue->DestFile]; - bool StopRedirects = false; - if (R.size() == 0) - R.push_back(Queue->Uri); - else if (R[0] == "STOP" || R.size() > 10) - StopRedirects = true; - else - { - for (StringVectorIterator I = R.begin(); I != R.end(); I++) - if (Queue->Uri == *I) - { - R[0] = "STOP"; - break; - } - - R.push_back(Queue->Uri); - } - - if (StopRedirects == false) - Redirect(NextURI); - else - Fail(); - - break; + rd = CFReadStreamRead(rs, data, sizeof(data)); + goto read; } - - default: - Fail(_("Internal error")); - break; } - + + goto done; + done_: + CFRelease(hm); + done: + CFReadStreamClose(rs); + CFRelease(rs); + free(url); + FailCounter = 0; } @@ -1330,6 +1530,41 @@ signal(SIGPIPE, SIG_IGN); HttpMethod Mth; + + size_t size; + sysctlbyname("hw.machine", NULL, &size, NULL, 0); + char *machine = new char[size]; + sysctlbyname("hw.machine", machine, &size, NULL, 0); + Machine_ = machine; + + const char *path = "/System/Library/CoreServices/SystemVersion.plist"; + CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (uint8_t *) path, strlen(path), false); + + CFPropertyListRef plist; { + CFReadStreamRef stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, url); + CFReadStreamOpen(stream); + plist = CFPropertyListCreateFromStream(kCFAllocatorDefault, stream, 0, kCFPropertyListImmutable, NULL, NULL); + CFReadStreamClose(stream); + } + + CFRelease(url); + + if (plist != NULL) { + Firmware_ = (CFStringRef) CFRetain(CFDictionaryGetValue((CFDictionaryRef) plist, CFSTR("ProductVersion"))); + CFRelease(plist); + } + + if (UniqueID_ == NULL) + if (void *libMobileGestalt = dlopen("/usr/lib/libMobileGestalt.dylib", RTLD_GLOBAL | RTLD_LAZY)) + if (CFStringRef (*$MGCopyAnswer)(CFStringRef) = (CFStringRef (*)(CFStringRef)) dlsym(libMobileGestalt, "MGCopyAnswer")) + UniqueID_ = $MGCopyAnswer(CFSTR("UniqueDeviceID")); + + if (UniqueID_ == NULL) + if (void *lockdown = lockdown_connect()) { + UniqueID_ = lockdown_copy_value(lockdown, NULL, kLockdownUniqueDeviceIDKey); + lockdown_disconnect(lockdown); + } + return Mth.Loop(); } diff -ru apt-0.7.25.3/methods/makefile apt-0.7.25.3+iPhone/methods/makefile --- apt-0.7.25.3/methods/makefile 2010-02-01 19:44:41.000000000 +0000 +++ apt-0.7.25.3+iPhone/methods/makefile 2010-02-22 20:14:29.000000000 +0000 @@ -46,7 +46,7 @@ # The http method PROGRAM=http -SLIBS = -lapt-pkg $(SOCKETLIBS) $(INTLLIBS) +SLIBS = -lapt-pkg $(SOCKETLIBS) $(INTLLIBS) -framework CoreFoundation -framework CFNetwork -framework SystemConfiguration -framework IOKit -llockdown LIB_MAKES = apt-pkg/makefile SOURCE = http.cc rfc2553emu.cc connect.cc include $(PROGRAM_H)