Skip to content

Commit ab1debe

Browse files
committed
Improving file type detection for attachments
Changing the extension source of truth order to filename query param, MIME type, url.pathExtension, and then a new fallback of doing a reverse order query param check for any valid extension. Updated the unit tests for this area NSURL+OneSignal has the logic for query param file extension extraction
1 parent 8029bd4 commit ab1debe

File tree

5 files changed

+80
-39
lines changed

5 files changed

+80
-39
lines changed

iOS_SDK/OneSignalSDK/Source/NSURL+OneSignal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,6 @@
3131

3232
- (NSString *)valueFromQueryParameter:(NSString *)parameter;
3333

34+
- (NSString*)supportedFileExtension;
35+
3436
@end

iOS_SDK/OneSignalSDK/Source/NSURL+OneSignal.m

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
*/
2727

2828
#import "NSURL+OneSignal.h"
29+
#import "OneSignalCommonDefines.h"
2930

3031
@implementation NSURL (OneSignal)
3132
- (NSString *)valueFromQueryParameter:(NSString *)parameter {
@@ -37,4 +38,28 @@ - (NSString *)valueFromQueryParameter:(NSString *)parameter {
3738

3839
return nil;
3940
}
41+
42+
- (NSString*)supportedFileExtension {
43+
NSURLComponents *components = [NSURLComponents componentsWithURL:self resolvingAgainstBaseURL:false];
44+
45+
for(int i = (int)components.queryItems.count-1; i > -1; i--) {
46+
NSURLQueryItem *item = components.queryItems[i];
47+
NSString *value = item.value;
48+
NSString *extension = [self findExtensionInParam:value];
49+
if (extension)
50+
return extension;
51+
}
52+
return nil;
53+
}
54+
55+
- (NSString*)findExtensionInParam:(NSString *)parameter {
56+
NSArray *paramComponents = [parameter componentsSeparatedByString:@"."];
57+
for (int i = (int)paramComponents.count-1; i > -1; i--) {
58+
NSString *component = paramComponents[i];
59+
if ([ONESIGNAL_SUPPORTED_ATTACHMENT_TYPES containsObject:component])
60+
return component;
61+
}
62+
return nil;
63+
}
64+
4065
@end

iOS_SDK/OneSignalSDK/Source/OneSignalHelper.m

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ -(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataT
8181

8282
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)aResponse completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
8383
response = aResponse;
84+
long long expectedLength = response.expectedContentLength;
85+
if (expectedLength > 50000000) { //Enforcing 50 mb limit on media before downloading
86+
completionHandler(NSURLSessionResponseCancel);
87+
return;
88+
}
8489
completionHandler(NSURLSessionResponseAllow);
8590
}
8691

@@ -785,21 +790,16 @@ + (void)addNotificationRequest:(OSNotificationPayload*)payload
785790
+ (NSString*)downloadMediaAndSaveInBundle:(NSString*)urlString {
786791

787792
let url = [NSURL URLWithString:urlString];
788-
789-
NSString* extension = url.pathExtension;
790-
791-
if ([extension isEqualToString:@""])
792-
extension = nil;
793-
794-
// Unrecognized extention
795-
if (extension != nil && ![ONESIGNAL_SUPPORTED_ATTACHMENT_TYPES containsObject:extension])
796-
return nil;
797-
793+
794+
//Try to get extension from the filname parameter
795+
NSString* extension = [[[NSURL URLWithString:urlString] valueFromQueryParameter:@"filename"]
796+
supportedFileExtension];
797+
798+
//Download the file
798799
var name = [self randomStringWithLength:10];
799-
800+
800801
if (extension)
801802
name = [name stringByAppendingString:[NSString stringWithFormat:@".%@", extension]];
802-
803803
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
804804
NSString* filePath = [paths[0] stringByAppendingPathComponent:name];
805805

@@ -813,18 +813,24 @@ + (NSString*)downloadMediaAndSaveInBundle:(NSString*)urlString {
813813
[OneSignal onesignal_Log:ONE_S_LL_ERROR message:[NSString stringWithFormat:@"Encountered an error while attempting to download file with URL: %@", error]];
814814
return nil;
815815
}
816-
817816
if (!extension) {
818817
NSString *newExtension;
819-
818+
//Use the MIME type for the extension if one wasn't provided in the filename parameter
820819
if (mimeType != nil && ![mimeType isEqualToString:@""]) {
821820
newExtension = mimeType.fileExtensionForMimeType;
822-
} else {
823-
newExtension = [[[NSURL URLWithString:urlString] valueFromQueryParameter:@"filename"] supportedFileExtension];
824821
}
825822

826-
if (!newExtension || ![ONESIGNAL_SUPPORTED_ATTACHMENT_TYPES containsObject:newExtension])
827-
return nil;
823+
//Try using url.pathExtension
824+
if (!newExtension || ![ONESIGNAL_SUPPORTED_ATTACHMENT_TYPES containsObject:newExtension]) {
825+
newExtension = url.pathExtension;
826+
}
827+
828+
//Try getting an extension from the query
829+
if (!newExtension || ![ONESIGNAL_SUPPORTED_ATTACHMENT_TYPES containsObject:newExtension]) {
830+
newExtension = url.supportedFileExtension;
831+
if (!newExtension || [newExtension isEqualToString:@""])
832+
return nil;
833+
}
828834

829835
name = [NSString stringWithFormat:@"%@.%@", name, newExtension];
830836

@@ -834,12 +840,12 @@ + (NSString*)downloadMediaAndSaveInBundle:(NSString*)urlString {
834840
}
835841

836842
if (error) {
837-
[OneSignal onesignal_Log:ONE_S_LL_ERROR message:[NSString stringWithFormat:@"Encountered an error while attempting to download file with URL: %@", error]];
843+
[OneSignal onesignal_Log:ONE_S_LL_ERROR message:[NSString stringWithFormat:@"Encountered an error while attempting to download file with URL: %@", error]];
838844
return nil;
839845
}
840-
846+
841847
let standardUserDefaults = OneSignalUserDefaults.initStandard;
842-
848+
843849
NSArray* cachedFiles = [standardUserDefaults getSavedObjectForKey:OSUD_TEMP_CACHED_NOTIFICATION_MEDIA defaultValue:nil];
844850
NSMutableArray* appendedCache;
845851
if (cachedFiles) {
@@ -848,15 +854,14 @@ + (NSString*)downloadMediaAndSaveInBundle:(NSString*)urlString {
848854
}
849855
else
850856
appendedCache = [[NSMutableArray alloc] initWithObjects:name, nil];
851-
857+
852858
[standardUserDefaults saveObjectForKey:OSUD_TEMP_CACHED_NOTIFICATION_MEDIA withValue:appendedCache];
853859
return name;
854860
} @catch (NSException *exception) {
855861
[OneSignal onesignal_Log:ONE_S_LL_ERROR message:[NSString stringWithFormat:@"OneSignal encountered an exception while downloading file (%@), exception: %@", url, exception.description]];
856862

857863
return nil;
858864
}
859-
860865
}
861866

862867
// TODO: Add back after testing

iOS_SDK/OneSignalSDK/UnitTests/Shadows/NSURLSessionOverrider.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ + (NSString *)overrideDownloadItemAtURL:(NSURL*)url toFile:(NSString*)localPath
5050

5151
if ([url.absoluteString isEqualToString:@"http://domain.com/file"])
5252
return @"image/png";
53-
else if ([url.absoluteString isEqualToString:@"http://domain.com/secondFile"])
54-
return nil;
53+
else if ([url.path isEqualToString:@"/secondFile"])
54+
return @"image/heic";
5555
else
5656
return @"image/jpg";
5757
}

iOS_SDK/OneSignalSDK/UnitTests/UnitTests.m

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2136,16 +2136,7 @@ - (id)exampleNotificationJSONWithMediaURL:(NSString *)urlString {
21362136
}};
21372137
}
21382138

2139-
- (void)testExtractFileExtensionFromMimeType {
2140-
//test to make sure the MIME type parsing works correctly
2141-
//NSURLSessionOverrider returns image/png for this URL
2142-
id pngFormat = [self exampleNotificationJSONWithMediaURL:@"http://domain.com/file"];
2143-
2144-
let downloadedPngFilename = [self deliverNotificationWithJSON:pngFormat].URL.lastPathComponent;
2145-
XCTAssertTrue([downloadedPngFilename.supportedFileExtension isEqualToString:@"png"]);
2146-
}
2147-
2148-
- (void)testExtractFileExtensionFromQueryParameter {
2139+
- (void)testExtractFileExtensionFromFileNameQueryParameter {
21492140
// we allow developers to add ?filename=test.jpg (for example) to attachment URL's in cases where there is no extension & no mime type
21502141
// tests to make sure the SDK correctly extracts the file extension from the `filename` URL query parameter
21512142
// NSURLSessionOverrider returns nil for this URL
@@ -2155,14 +2146,32 @@ - (void)testExtractFileExtensionFromQueryParameter {
21552146
XCTAssertTrue([downloadedJpgFilename.supportedFileExtension isEqualToString:@"jpg"]);
21562147
}
21572148

2158-
- (void)testFileExtensionPrioritizesURLFileExtension {
2159-
//tests to make sure that the URL's file extension is prioritized above the MIME type and URL query param
2160-
//this attachment URL will have a file extension, a MIME type, and a filename query parameter. It should prioritize the URL file extension (gif)
2149+
- (void)testExtractFileExtensionFromMimeType {
2150+
//test to make sure the MIME type parsing works correctly
2151+
//NSURLSessionOverrider returns image/png for this URL
2152+
id pngFormat = [self exampleNotificationJSONWithMediaURL:@"http://domain.com/file"];
2153+
2154+
let downloadedPngFilename = [self deliverNotificationWithJSON:pngFormat].URL.lastPathComponent;
2155+
XCTAssertTrue([downloadedPngFilename.supportedFileExtension isEqualToString:@"png"]);
2156+
}
2157+
2158+
- (void)testFileExtensionPrioritizesFileNameParameter {
2159+
//tests to make sure that the filename query parameter is prioritized above the MIME type and URL extension
2160+
//this attachment URL will have a file extension, a MIME type, and a filename query parameter. It should prioritize the filename query parameter (png)
21612161
//NSURLSessionOverrider returns image/png for this URL
21622162
id gifFormat = [self exampleNotificationJSONWithMediaURL:@"http://domain.com/file.gif?filename=test.png"];
21632163

21642164
let downloadedGifFilename = [self deliverNotificationWithJSON:gifFormat].URL.lastPathComponent;
2165-
XCTAssertTrue([downloadedGifFilename.supportedFileExtension isEqualToString:@"gif"]);
2165+
XCTAssertTrue([downloadedGifFilename.supportedFileExtension isEqualToString:@"png"]);
2166+
}
2167+
2168+
- (void)testExtractFileExtensionFromAnyParamter {
2169+
//test to make sure the fallback of parsing all parameters for a file type works correctly
2170+
//NSURLSessionOverrider returns an unallowed extension (heic) for this URL to test the fallback
2171+
id pngFormat = [self exampleNotificationJSONWithMediaURL:@"http://domain.com/secondFile?file=test.png&media=image&type=.fakeextension"];
2172+
2173+
let downloadedPngFilename = [self deliverNotificationWithJSON:pngFormat].URL.lastPathComponent;
2174+
XCTAssertTrue([downloadedPngFilename.supportedFileExtension isEqualToString:@"png"]);
21662175
}
21672176

21682177
/*

0 commit comments

Comments
 (0)