| 1 | #import <CoreFoundation/CoreFoundation.h> |
| 2 | #import <CoreServices/CoreServices.h> |
| 3 | #import <Foundation/Foundation.h> |
| 4 | |
| 5 | #include <unistd.h> |
| 6 | #include <openssl/md5.h> |
| 7 | |
| 8 | #define FLICKR_UPLOAD_URL @"http://api.flickr.com/services/upload/" |
| 9 | #define FLICKR_FINISHED_URL @"http://www.flickr.com/tools/uploader_edit.gne" |
| 10 | #define MIME_BOUNDARY "----16c17a9ea1d7b327e7489190e394d411----" |
| 11 | #define CONTENT_TYPE "multipart/form-data; boundary=" MIME_BOUNDARY |
| 12 | |
| 13 | NSString *api_sig(NSDictionary *params) |
| 14 | { |
| 15 | NSMutableString *sig = [NSMutableString stringWithString: PUSHR_SHARED_SECRET]; |
| 16 | NSArray *sortedKeys = [[params allKeys] sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)]; |
| 17 | |
| 18 | id key = nil; |
| 19 | NSEnumerator *iterator = [sortedKeys objectEnumerator]; |
| 20 | while ((key = [iterator nextObject])) { |
| 21 | [sig appendString: key]; |
| 22 | [sig appendString: [params objectForKey: key]]; |
| 23 | } |
| 24 | |
| 25 | unsigned char digest[16]; |
| 26 | char finalDigest[32]; |
| 27 | |
| 28 | MD5((const unsigned char *)[sig UTF8String], (const unsigned long)[sig length], digest); |
| 29 | for (unsigned short int index = 0; index < 16; ++index) |
| 30 | sprintf(finalDigest + (index * 2), "%02x", digest[index]); |
| 31 | |
| 32 | return [NSString stringWithCString: finalDigest length: 32]; |
| 33 | } |
| 34 | |
| 35 | void uploadPictureToFlickr(NSString *pathToJPG) |
| 36 | { |
| 37 | NSUserDefaults *settings = [NSUserDefaults standardUserDefaults]; |
| 38 | NSString *token = [settings stringForKey: @"token"]; |
| 39 | NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys: PUSHR_API_KEY, @"api_key", token, @"auth_token", @"0", @"is_public", nil]; |
| 40 | [params setObject: api_sig(params) forKey: @"api_sig"]; |
| 41 | [params setObject: [NSData dataWithContentsOfFile: pathToJPG] forKey: @"photo"]; |
| 42 | |
| 43 | NSMutableData *body = [[NSMutableData alloc] initWithLength: 0]; |
| 44 | [body appendData: [[[[NSString alloc] initWithFormat: @"--%@\r\n", @MIME_BOUNDARY] autorelease] dataUsingEncoding: NSUTF8StringEncoding]]; |
| 45 | |
| 46 | id key = nil; |
| 47 | NSEnumerator *enumerator = [params keyEnumerator]; |
| 48 | while ((key = [enumerator nextObject])) { |
| 49 | id val = [params objectForKey: key]; |
| 50 | id keyHeader = nil; |
| 51 | if ([key isEqualToString: @"photo"]) { |
| 52 | // If this is the photo... |
| 53 | keyHeader = [[NSString stringWithFormat: @"Content-Disposition: form-data; name=\"photo\"; filename=\"%@\"\r\nContent-Type: image/jpeg\r\n\r\n", pathToJPG] dataUsingEncoding: NSUTF8StringEncoding]; |
| 54 | [body appendData: keyHeader]; |
| 55 | [body appendData: val]; |
| 56 | } else { |
| 57 | // Treat all other values as strings. |
| 58 | keyHeader = [NSString stringWithFormat: @"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key]; |
| 59 | [body appendData: [keyHeader dataUsingEncoding: NSUTF8StringEncoding]]; |
| 60 | [body appendData: [val dataUsingEncoding: NSUTF8StringEncoding]]; |
| 61 | } |
| 62 | [body appendData: [[NSString stringWithFormat: @"\r\n--%@\r\n", @MIME_BOUNDARY] dataUsingEncoding: NSUTF8StringEncoding]]; |
| 63 | } |
| 64 | |
| 65 | [body appendData: [[NSString stringWithString: @"--\r\n"] dataUsingEncoding: NSUTF8StringEncoding]]; |
| 66 | long bodyLength = [body length]; |
| 67 | |
| 68 | CFURLRef uploadURL = CFURLCreateWithString(kCFAllocatorDefault, (CFStringRef)FLICKR_UPLOAD_URL, NULL); |
| 69 | CFHTTPMessageRef _request = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("POST"), uploadURL, kCFHTTPVersion1_1); |
| 70 | CFRelease(uploadURL); |
| 71 | uploadURL = NULL; |
| 72 | |
| 73 | CFHTTPMessageSetHeaderFieldValue(_request, CFSTR("Content-Type"), CFSTR(CONTENT_TYPE)); |
| 74 | CFHTTPMessageSetHeaderFieldValue(_request, CFSTR("Host"), CFSTR("api.flickr.com")); |
| 75 | CFHTTPMessageSetHeaderFieldValue(_request, CFSTR("Content-Length"), (CFStringRef)[NSString stringWithFormat: @"%d", bodyLength]); |
| 76 | CFHTTPMessageSetBody(_request, (CFDataRef)body); |
| 77 | [body release]; |
| 78 | |
| 79 | CFReadStreamRef _readStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, _request); |
| 80 | CFReadStreamOpen(_readStream); |
| 81 | |
| 82 | CFIndex numBytesRead; |
| 83 | long bytesWritten, previousBytesWritten = 0; |
| 84 | UInt8 buf[1024]; |
| 85 | BOOL doneUploading = NO; |
| 86 | |
| 87 | while (!doneUploading) { |
| 88 | CFNumberRef cfSize = CFReadStreamCopyProperty(_readStream, kCFStreamPropertyHTTPRequestBytesWrittenCount); |
| 89 | CFNumberGetValue(cfSize, kCFNumberLongType, &bytesWritten); |
| 90 | CFRelease(cfSize); |
| 91 | cfSize = NULL; |
| 92 | if (bytesWritten > previousBytesWritten) { |
| 93 | previousBytesWritten = bytesWritten; |
| 94 | fprintf(stderr, "(%lu bytes written / %lu total bytes) = %f %%\r", bytesWritten, bodyLength, (100.0f * ((float)bytesWritten/(float)bodyLength))); |
| 95 | fflush(stderr); |
| 96 | } |
| 97 | |
| 98 | if (!CFReadStreamHasBytesAvailable(_readStream)) { |
| 99 | usleep(3200); |
| 100 | continue; |
| 101 | } else { |
| 102 | fprintf(stderr, "\n"); |
| 103 | } |
| 104 | |
| 105 | numBytesRead = CFReadStreamRead(_readStream, buf, 1024); |
| 106 | fprintf(stderr, "%s", buf); |
| 107 | fflush(stderr); |
| 108 | |
| 109 | if (CFReadStreamGetStatus(_readStream) == kCFStreamStatusAtEnd) doneUploading = YES; |
| 110 | } |
| 111 | CFHTTPMessageRef _responseHeaderRef = (CFHTTPMessageRef)CFReadStreamCopyProperty(_readStream, kCFStreamPropertyHTTPResponseHeader); |
| 112 | NSDictionary *_responseHeaders = (NSDictionary *)CFHTTPMessageCopyAllHeaderFields(_responseHeaderRef); |
| 113 | NSLog(@"Header data was: \n---\n%@\n---\n", _responseHeaders); |
| 114 | CFRelease(_responseHeaderRef); |
| 115 | _responseHeaderRef = NULL; |
| 116 | |
| 117 | CFReadStreamClose(_readStream); |
| 118 | CFRelease(_request); |
| 119 | _request = NULL; |
| 120 | CFRelease(_readStream); |
| 121 | _readStream = NULL; |
| 122 | } |
| 123 | |
| 124 | int main(int argc, char *argv[]) |
| 125 | { |
| 126 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
| 127 | |
| 128 | fprintf(stderr, "Pausing to give you time to catch me\n"); |
| 129 | sleep(8); |
| 130 | |
| 131 | for (unsigned int indexOfPicture = 1; indexOfPicture < argc; ++indexOfPicture) { |
| 132 | uploadPictureToFlickr([NSString stringWithUTF8String: argv[indexOfPicture]]); |
| 133 | fprintf(stderr, "Sleeping - check memory usage now!\n"); |
| 134 | sleep(8); |
| 135 | } |
| 136 | |
| 137 | [pool release]; |
| 138 | return 0; |
| 139 | } |
| 140 | |