+extern "C" {
+ #include <mach-o/nlist.h>
+}
+
// -*- mode: cpp; mode: fold -*-
// Description /*{{{*/
// $Id: http.cc,v 1.59 2004/05/08 19:42:35 mdz Exp $
#include <apt-pkg/error.h>
#include <apt-pkg/hashes.h>
+#include <sys/sysctl.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <utime.h>
// Internet stuff
#include <netdb.h>
+#include <arpa/inet.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreServices/CoreServices.h>
+#include <SystemConfiguration/SystemConfiguration.h>
#include "connect.h"
#include "rfc2553emu.h"
/*}}}*/
using namespace std;
+CFStringRef Firmware_;
+const char *Machine_;
+const char *SerialNumber_;
+
+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;
if (Queue == 0)
continue;
-
- // Connect to the server
- if (Server == 0 || Server->Comp(Queue->Uri) == false)
- {
- delete Server;
- Server = new ServerState(Queue->Uri,this);
+
+ CFStringEncoding se = kCFStringEncodingUTF8;
+
+ char *url = strdup(Queue->Uri.c_str());
+ url:
+ URI uri = std::string(url);
+ std::string hs = uri.Host;
+
+ 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);
}
-
- /* 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;
+
+ 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);
+ } else if (Queue->LastModified != 0) {
+ sr = CFStringCreateWithCString(kCFAllocatorDefault, TimeRFC1123(Queue->LastModified).c_str(), se);
+ CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("If-Modified-Since"), sr);
+ CFRelease(sr);
}
- // 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;
- }
- };
+ if (Firmware_ != NULL)
+ CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("X-Firmware"), Firmware_);
+
+ sr = CFStringCreateWithCString(kCFAllocatorDefault, Machine_, se);
+ CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("X-Machine"), sr);
+ CFRelease(sr);
+
+ sr = CFStringCreateWithCString(kCFAllocatorDefault, SerialNumber_, se);
+ CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("X-Serial-Number"), sr);
+ CFRelease(sr);
+
+ CFHTTPMessageSetHeaderFieldValue(hm, CFSTR("User-Agent"), CFSTR("Telesphoreo APT-HTTP/1.0.98"));
+
+ CFReadStreamRef rs = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, hm);
+ CFRelease(hm);
+
+ 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());
+
+ if (!CFReadStreamOpen(rs)) {
+ CfrsError("Open", rs);
+ Fail(true);
+ goto done;
+ }
+
+ rd = CFReadStreamRead(rs, data, sizeof(data));
+
+ if (rd == -1) {
+ CfrsError(uri.Host.c_str(), rs);
+ 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<char *>(malloc(ln));
+
+ if (!CFStringGetCString(sr, url, ln, se)) {
+ Fail();
+ goto done_;
+ }
- /* If the server is sending back sizeless responses then fill in
- the size now */
+ 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_;
+ }
+ }
+
+ CFRelease(hm);
+
+ if (sc == 304) {
+ unlink(Queue->DestFile.c_str());
+ Res.IMSHit = true;
+ Res.LastModified = Queue->LastModified;
+ URIDone(Res);
+ } else if (sc < 200 || sc >= 300)
+ Fail();
+ 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
- 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);
- // 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;
- }
-
- default:
- Fail(_("Internal error"));
- break;
+ if (sz == -1) {
+ delete File;
+ File = NULL;
+ Fail();
+ goto done;
+ }
+
+ dt += sz;
+ rd -= sz;
+ }
+
+ rd = CFReadStreamRead(rs, data, sizeof(data));
+ goto read;
+ }
}
-
+
+ goto done;
+ done_:
+ CFRelease(hm);
+ done:
+ CFReadStreamClose(rs);
+ CFRelease(rs);
+ free(url);
+
FailCounter = 0;
}
}
/*}}}*/
-int main_();
-int main() {
-_exit(main_());
-}
-int main_()
+int main()
{
+#if !defined(__ENVIRONMENT_ASPEN_VERSION_MIN_REQUIRED__) || __ENVIRONMENT_ASPEN_VERSION_MIN_REQUIRED__ < 10200
+ struct nlist nl[2];
+ memset(nl, 0, sizeof(nl));
+ nl[0].n_un.n_name = (char *) "_useMDNSResponder";
+ nlist("/usr/lib/libc.dylib", nl);
+ if (nl[0].n_type != N_UNDF)
+ *(int *) nl[0].n_value = 0;
+#endif
+
setlocale(LC_ALL, "");
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 (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
+ if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
+ if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
+ SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
+ CFRelease(serial);
+ }
+
+ IOObjectRelease(service);
+ }
return Mth.Loop();
}