// // MBIMFetchAttachmentOperation.m // kordophoned // // Created by James Magahern on 11/20/18. // Copyright © 2018 James Magahern. All rights reserved. // #import "MBIMFetchAttachmentOperation.h" #import "MBIMDataResponse.h" #import "MBIMImageUtils.h" #import "IMCore_ClassDump.h" #import "IMSharedUtilities_ClassDump.h" #import @implementation MBIMFetchAttachmentOperation + (void)load { [super load]; } + (NSString *)endpointName { return @"attachment"; } - (void)main { NSObject *response = nil; do { BOOL preview = [[self valueForQueryItemWithName:@"preview"] boolValue]; NSString *guid = [self valueForQueryItemWithName:@"guid"]; if (!guid) { MBIMLogInfo(@"No query item provided"); response = [[HTTPErrorResponse alloc] initWithErrorCode:500]; break; } IMFileTransfer *transfer = [[IMFileTransferCenter sharedInstance] transferForGUID:guid]; if (!transfer) { MBIMLogInfo(@"No transfer found for guid: %@", guid); response = [[HTTPErrorResponse alloc] initWithErrorCode:404]; break; } if (![transfer existsAtLocalPath]) { MBIMLogInfo(@"We don't have the file for this yet (still downloading to server?)"); response = [[HTTPErrorResponse alloc] initWithErrorCode:404]; break; } NSData *responseData = nil; NSURL *localURL = [transfer localURL]; NSString *extension = [[localURL pathExtension] lowercaseString]; if (preview) { IMPreviewConstraints constraints = IMPreviewConstraintsZero(); // Fetch preview constraints from transfer NSDictionary *previewConstraintsDict = [[transfer attributionInfo] objectForKey:@"pgenszc"]; if (previewConstraintsDict) { constraints = IMPreviewConstraintsFromDictionary(previewConstraintsDict); } else { // Or, make a guess. constraints.maxPxWidth = 500.0; constraints.scale = 1.0; } NSURL *previewURL = IMAttachmentPreviewFileURL(localURL, extension, YES); if (!previewURL) { // I'm not sure why this sometimes returns nil... MBIMLogInfo(@"Unable to generate attachment preview cache URL for %@, making one up.", localURL); NSURL *temporaryAttachmentCache = [[[NSFileManager defaultManager] temporaryDirectory] URLByAppendingPathComponent:@"kordophone_attachment_cache"]; temporaryAttachmentCache = [temporaryAttachmentCache URLByAppendingPathComponent:guid]; [[NSFileManager defaultManager] createDirectoryAtURL:temporaryAttachmentCache withIntermediateDirectories:YES attributes:nil error:nil]; previewURL = [temporaryAttachmentCache URLByAppendingPathComponent:[localURL lastPathComponent]]; } if (![[NSFileManager defaultManager] fileExistsAtPath:[previewURL path]]) { MBIMLogInfo(@"Generating preview image for guid: %@ at %@", guid, [previewURL path]); // Generate preview using preview generator manager NSError *error = nil; IMPreviewGeneratorManager *generator = [IMPreviewGeneratorManager sharedInstance]; CGImageRef previewImage = [generator newPreviewFromSourceURL:localURL withPreviewConstraints:constraints error:&error]; if (error) { MBIMLogInfo(@"Unable to generate preview for attachment guid: %@", guid); response = [[HTTPErrorResponse alloc] initWithErrorCode:500]; break; } // Convert to JPEG. responseData = MBIMCGImageJPEGRepresentation(previewImage); // Persist JPEG preview to disk if (previewURL) { [responseData writeToURL:previewURL atomically:YES]; } } else { // File exists MBIMLogInfo(@"Using cached preview image for guid: %@ at %@", guid, [previewURL path]); responseData = [NSData dataWithContentsOfURL:previewURL]; } } else { responseData = [NSData dataWithContentsOfURL:localURL]; } if (!responseData) { MBIMLogInfo(@"Wasn't able to load data for guid: %@", guid); response = [[HTTPErrorResponse alloc] initWithErrorCode:404]; break; } NSString *mimeType = [transfer mimeType]; if ([mimeType isEqualToString:@"image/heic"]) { // TODO: We should convert this to JPEG here. I don't want clients to have to deal with HEIC. MBIMLogInfo(@"WARNING: Returning HEIC data for attachment %@", guid); } // It's unusual, but if this is nil, try to guess the MIME type based on the filename if (!mimeType) { // XXX: REALLY hacky mimeType = [NSString stringWithFormat:@"image/%@", extension]; } response = [[MBIMDataResponse alloc] initWithData:responseData contentType:mimeType]; } while (0); self.serverCompletionBlock(response); } @end