VLCHTTPConnection.m 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. /*****************************************************************************
  2. * VLCHTTPConnection.m
  3. * VLC for iOS
  4. *****************************************************************************
  5. * Copyright (c) 2013-2015 VideoLAN. All rights reserved.
  6. * $Id$
  7. *
  8. * Authors: Felix Paul Kühne <fkuehne # videolan.org>
  9. * Pierre Sagaspe <pierre.sagaspe # me.com>
  10. * Carola Nitz <caro # videolan.org>
  11. * Jean-Baptiste Kempf <jb # videolan.org>
  12. *
  13. * Refer to the COPYING file of the official project for license.
  14. *****************************************************************************/
  15. #import "VLCActivityManager.h"
  16. #import "VLCHTTPConnection.h"
  17. #import "MultipartFormDataParser.h"
  18. #import "HTTPMessage.h"
  19. #import "HTTPDataResponse.h"
  20. #import "HTTPFileResponse.h"
  21. #import "MultipartMessageHeaderField.h"
  22. #import "HTTPDynamicFileResponse.h"
  23. #import "NSString+SupportedMedia.h"
  24. #import "UIDevice+VLC.h"
  25. #import "VLCHTTPUploaderController.h"
  26. #if TARGET_OS_IOS
  27. #import "VLCThumbnailsCache.h"
  28. #endif
  29. @interface VLCHTTPConnection()
  30. {
  31. MultipartFormDataParser *_parser;
  32. NSFileHandle *_storeFile;
  33. NSString *_filepath;
  34. UInt64 _contentLength;
  35. UInt64 _receivedContent;
  36. }
  37. @end
  38. @implementation VLCHTTPConnection
  39. - (BOOL)supportsMethod:(NSString *)method atPath:(NSString *)path
  40. {
  41. // Add support for POST
  42. if ([method isEqualToString:@"POST"] && [path isEqualToString:@"/upload.json"])
  43. return YES;
  44. return [super supportsMethod:method atPath:path];
  45. }
  46. - (BOOL)expectsRequestBodyFromMethod:(NSString *)method atPath:(NSString *)path
  47. {
  48. // Inform HTTP server that we expect a body to accompany a POST request
  49. if ([method isEqualToString:@"POST"] && [path isEqualToString:@"/upload.json"]) {
  50. // here we need to make sure, boundary is set in header
  51. NSString* contentType = [request headerField:@"Content-Type"];
  52. NSUInteger paramsSeparator = [contentType rangeOfString:@";"].location;
  53. if (NSNotFound == paramsSeparator)
  54. return NO;
  55. if (paramsSeparator >= contentType.length - 1)
  56. return NO;
  57. NSString* type = [contentType substringToIndex:paramsSeparator];
  58. if (![type isEqualToString:@"multipart/form-data"]) {
  59. // we expect multipart/form-data content type
  60. return NO;
  61. }
  62. // enumerate all params in content-type, and find boundary there
  63. NSArray* params = [[contentType substringFromIndex:paramsSeparator + 1] componentsSeparatedByString:@";"];
  64. NSUInteger count = params.count;
  65. for (NSUInteger i = 0; i < count; i++) {
  66. NSString *param = params[i];
  67. paramsSeparator = [param rangeOfString:@"="].location;
  68. if ((NSNotFound == paramsSeparator) || paramsSeparator >= param.length - 1)
  69. continue;
  70. NSString* paramName = [param substringWithRange:NSMakeRange(1, paramsSeparator-1)];
  71. NSString* paramValue = [param substringFromIndex:paramsSeparator+1];
  72. if ([paramName isEqualToString: @"boundary"])
  73. // let's separate the boundary from content-type, to make it more handy to handle
  74. [request setHeaderField:@"boundary" value:paramValue];
  75. }
  76. // check if boundary specified
  77. if (nil == [request headerField:@"boundary"])
  78. return NO;
  79. return YES;
  80. }
  81. return [super expectsRequestBodyFromMethod:method atPath:path];
  82. }
  83. - (NSObject<HTTPResponse> *)httpResponseForMethod:(NSString *)method URI:(NSString *)path
  84. {
  85. if ([method isEqualToString:@"POST"] && [path isEqualToString:@"/upload.json"])
  86. return [[HTTPDataResponse alloc] initWithData:[@"\"OK\"" dataUsingEncoding:NSUTF8StringEncoding]];
  87. #if TARGET_OS_IOS
  88. if ([path hasPrefix:@"/download/"]) {
  89. NSString *filePath = [[path stringByReplacingOccurrencesOfString:@"/download/" withString:@""]stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
  90. HTTPFileResponse *fileResponse = [[HTTPFileResponse alloc] initWithFilePath:filePath forConnection:self];
  91. fileResponse.contentType = @"application/octet-stream";
  92. return fileResponse;
  93. }
  94. if ([path hasPrefix:@"/thumbnail"]) {
  95. NSString *filePath = [[path stringByReplacingOccurrencesOfString:@"/thumbnail/" withString:@""]stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
  96. filePath = [filePath stringByReplacingOccurrencesOfString:@".png" withString:@""];
  97. NSManagedObjectContext *moc = [[MLMediaLibrary sharedMediaLibrary] managedObjectContext];
  98. if (moc) {
  99. NSPersistentStoreCoordinator *psc = [moc persistentStoreCoordinator];
  100. if (psc) {
  101. NSManagedObject *mo = nil;
  102. @try {
  103. mo = [moc existingObjectWithID:[psc managedObjectIDForURIRepresentation:[NSURL URLWithString:filePath]] error:nil];
  104. }@catch (NSException *exeption) {
  105. // somebody gave us a malformed or stale URIRepresentation
  106. }
  107. NSData *theData;
  108. NSString *contentType;
  109. /* devices category 3 and faster include HW accelerated JPEG encoding
  110. * so we can make our transfers faster by using waaay smaller images */
  111. if ([[UIDevice currentDevice] speedCategory] < 3) {
  112. theData = UIImagePNGRepresentation([VLCThumbnailsCache thumbnailForManagedObject:mo]);
  113. contentType = @"image/png";
  114. } else {
  115. theData = UIImageJPEGRepresentation([VLCThumbnailsCache thumbnailForManagedObject:mo], .9);
  116. contentType = @"image/jpg";
  117. }
  118. if (theData) {
  119. HTTPDataResponse *dataResponse = [[HTTPDataResponse alloc] initWithData:theData];
  120. dataResponse.contentType = contentType;
  121. return dataResponse;
  122. }
  123. }
  124. }
  125. }
  126. NSString *filePath = [self filePathForURI:path];
  127. NSString *documentRoot = [config documentRoot];
  128. NSString *relativePath = [filePath substringFromIndex:[documentRoot length]];
  129. BOOL shouldReturnLibVLCXML = [relativePath isEqualToString:@"/libMediaVLC.xml"];
  130. if ([relativePath isEqualToString:@"/index.html"] || shouldReturnLibVLCXML) {
  131. NSMutableArray *allMedia = [[NSMutableArray alloc] init];
  132. /* add all albums */
  133. NSArray *allAlbums = [MLAlbum allAlbums];
  134. for (MLAlbum *album in allAlbums) {
  135. if (album.name.length > 0 && album.tracks.count > 1)
  136. [allMedia addObject:album];
  137. }
  138. /* add all shows */
  139. NSArray *allShows = [MLShow allShows];
  140. for (MLShow *show in allShows) {
  141. if (show.name.length > 0 && show.episodes.count > 1)
  142. [allMedia addObject:show];
  143. }
  144. /* add all folders*/
  145. NSArray *allFolders = [MLLabel allLabels];
  146. for (MLLabel *folder in allFolders)
  147. [allMedia addObject:folder];
  148. /* add all remaining files */
  149. NSArray *allFiles = [MLFile allFiles];
  150. for (MLFile *file in allFiles) {
  151. if (file.labels.count > 0) continue;
  152. if (!file.isShowEpisode && !file.isAlbumTrack)
  153. [allMedia addObject:file];
  154. else if (file.isShowEpisode) {
  155. if (file.showEpisode.show.episodes.count < 2)
  156. [allMedia addObject:file];
  157. } else if (file.isAlbumTrack) {
  158. if (file.albumTrack.album.tracks.count < 2)
  159. [allMedia addObject:file];
  160. }
  161. }
  162. NSUInteger mediaCount = allMedia.count;
  163. NSMutableArray *mediaInHtml = [[NSMutableArray alloc] initWithCapacity:mediaCount];
  164. NSMutableArray *mediaInXml = [[NSMutableArray alloc] initWithCapacity:mediaCount];
  165. NSString *hostName = [[VLCHTTPUploaderController sharedInstance] hostname];
  166. NSString *duration;
  167. for (NSManagedObject *mo in allMedia) {
  168. if ([mo isKindOfClass:[MLFile class]]) {
  169. MLFile *file = (MLFile *)mo;
  170. duration = [[VLCTime timeWithNumber:file.duration] stringValue];
  171. [mediaInHtml addObject:[NSString stringWithFormat:
  172. @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
  173. <a href=\"download/%@\" class=\"inner\"> \
  174. <div class=\"down icon\"></div> \
  175. <div class=\"infos\"> \
  176. <span class=\"first-line\">%@</span> \
  177. <span class=\"second-line\">%@ - %0.2f MB</span> \
  178. </div> \
  179. </a> \
  180. </div>",
  181. file.objectID.URIRepresentation,
  182. [file.url.path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
  183. file.title,
  184. duration, (float)(file.fileSizeInBytes / 1e6)]];
  185. if (shouldReturnLibVLCXML) {
  186. NSString *pathSub = [self _checkIfSubtitleWasFound:file.path];
  187. if (pathSub)
  188. pathSub = [NSString stringWithFormat:@"http://%@/download/%@", hostName, pathSub];
  189. [mediaInXml addObject:[NSString stringWithFormat:@"<Media title=\"%@\" thumb=\"http://%@/thumbnail/%@.png\" duration=\"%@\" size=\"%li\" pathfile=\"http://%@/download/%@\" pathSubtitle=\"%@\"/>", file.title, hostName, file.objectID.URIRepresentation.absoluteString, duration, file.fileSizeInBytes, hostName, [file.url.path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding], pathSub]];
  190. }
  191. }
  192. else if ([mo isKindOfClass:[MLShow class]]) {
  193. MLShow *show = (MLShow *)mo;
  194. NSArray *episodes = [show sortedEpisodes];
  195. [mediaInHtml addObject:[NSString stringWithFormat:
  196. @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
  197. <a href=\"#\" class=\"inner folder\"> \
  198. <div class=\"open icon\"></div> \
  199. <div class=\"infos\"> \
  200. <span class=\"first-line\">%@</span> \
  201. <span class=\"second-line\">%lu items</span> \
  202. </div> \
  203. </a> \
  204. <div class=\"content\">",
  205. mo.objectID.URIRepresentation,
  206. show.name,
  207. (unsigned long)[episodes count]]];
  208. for (MLShowEpisode *showEp in episodes) {
  209. MLFile *anyFileFromEpisode = (MLFile *)[[showEp files] anyObject];
  210. duration = [[VLCTime timeWithNumber:[anyFileFromEpisode duration]] stringValue];
  211. [mediaInHtml addObject:[NSString stringWithFormat:
  212. @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
  213. <a href=\"download/%@\" class=\"inner\"> \
  214. <div class=\"down icon\"></div> \
  215. <div class=\"infos\"> \
  216. <span class=\"first-line\">S%@E%@ - %@</span> \
  217. <span class=\"second-line\">%@ - %0.2f MB</span> \
  218. </div> \
  219. </a> \
  220. </div>",
  221. showEp.objectID.URIRepresentation,
  222. [anyFileFromEpisode.url.path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
  223. showEp.seasonNumber,
  224. showEp.episodeNumber,
  225. showEp.name,
  226. duration, (float)([anyFileFromEpisode fileSizeInBytes] / 1e6)]];
  227. if (shouldReturnLibVLCXML) {
  228. NSString *pathSub = [self _checkIfSubtitleWasFound:[anyFileFromEpisode path]];
  229. if (![pathSub isEqualToString:@""])
  230. pathSub = [NSString stringWithFormat:@"http://%@/download/%@", hostName, pathSub];
  231. [mediaInXml addObject:[NSString stringWithFormat:@"<Media title=\"%@ - S%@E%@\" thumb=\"http://%@/thumbnail/%@.png\" duration=\"%@\" size=\"%li\" pathfile=\"http://%@/download/%@\" pathSubtitle=\"%@\"/>", show.name, showEp.seasonNumber, showEp.episodeNumber, hostName, showEp.objectID.URIRepresentation, duration, [anyFileFromEpisode fileSizeInBytes], hostName, [anyFileFromEpisode.url.path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding], pathSub]];
  232. }
  233. }
  234. [mediaInHtml addObject:@"</div></div>"];
  235. } else if ([mo isKindOfClass:[MLLabel class]]) {
  236. MLLabel *label = (MLLabel *)mo;
  237. NSArray *folderItems = [label sortedFolderItems];
  238. [mediaInHtml addObject:[NSString stringWithFormat:
  239. @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
  240. <a href=\"#\" class=\"inner folder\"> \
  241. <div class=\"open icon\"></div> \
  242. <div class=\"infos\"> \
  243. <span class=\"first-line\">%@</span> \
  244. <span class=\"second-line\">%lu items</span> \
  245. </div> \
  246. </a> \
  247. <div class=\"content\">",
  248. label.objectID.URIRepresentation,
  249. label.name,
  250. (unsigned long)folderItems.count]];
  251. for (MLFile *file in folderItems) {
  252. duration = [[VLCTime timeWithNumber:[file duration]] stringValue];
  253. [mediaInHtml addObject:[NSString stringWithFormat:
  254. @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
  255. <a href=\"download/%@\" class=\"inner\"> \
  256. <div class=\"down icon\"></div> \
  257. <div class=\"infos\"> \
  258. <span class=\"first-line\">%@</span> \
  259. <span class=\"second-line\">%@ - %0.2f MB</span> \
  260. </div> \
  261. </a> \
  262. </div>",
  263. file.objectID.URIRepresentation,
  264. [file.url.path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
  265. file.title,
  266. duration, (float)(file.fileSizeInBytes / 1e6)]];
  267. if (shouldReturnLibVLCXML) {
  268. NSString *pathSub = [self _checkIfSubtitleWasFound:file.path];
  269. if (pathSub)
  270. pathSub = [NSString stringWithFormat:@"http://%@/download/%@", hostName, pathSub];
  271. [mediaInXml addObject:[NSString stringWithFormat:@"<Media title=\"%@\" thumb=\"http://%@/thumbnail/%@.png\" duration=\"%@\" size=\"%li\" pathfile=\"http://%@/download/%@\" pathSubtitle=\"%@\"/>", file.title, hostName, file.objectID.URIRepresentation, duration, file.fileSizeInBytes, hostName, [file.url.path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding], pathSub]];
  272. }
  273. }
  274. [mediaInHtml addObject:@"</div></div>"];
  275. } else if ([mo isKindOfClass:[MLAlbum class]]) {
  276. MLAlbum *album = (MLAlbum *)mo;
  277. NSArray *albumTracks = [album sortedTracks];
  278. [mediaInHtml addObject:[NSString stringWithFormat:
  279. @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
  280. <a href=\"#\" class=\"inner folder\"> \
  281. <div class=\"open icon\"></div> \
  282. <div class=\"infos\"> \
  283. <span class=\"first-line\">%@</span> \
  284. <span class=\"second-line\">%lu items</span> \
  285. </div> \
  286. </a> \
  287. <div class=\"content\">",
  288. album.objectID.URIRepresentation,
  289. album.name,
  290. (unsigned long)albumTracks.count]];
  291. for (MLAlbumTrack *track in albumTracks) {
  292. MLFile *anyFileFromTrack = [track anyFileFromTrack];
  293. duration = [[VLCTime timeWithNumber:[anyFileFromTrack duration]] stringValue];
  294. [mediaInHtml addObject:[NSString stringWithFormat:
  295. @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
  296. <a href=\"download/%@\" class=\"inner\"> \
  297. <div class=\"down icon\"></div> \
  298. <div class=\"infos\"> \
  299. <span class=\"first-line\">%@</span> \
  300. <span class=\"second-line\">%@ - %0.2f MB</span> \
  301. </div> \
  302. </a> \
  303. </div>",
  304. track.objectID.URIRepresentation,
  305. [anyFileFromTrack.url.path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
  306. track.title,
  307. duration, (float)([anyFileFromTrack fileSizeInBytes] / 1e6)]];
  308. if (shouldReturnLibVLCXML)
  309. [mediaInXml addObject:[NSString stringWithFormat:@"<Media title=\"%@\" thumb=\"http://%@/thumbnail/%@.png\" duration=\"%@\" size=\"%li\" pathfile=\"http://%@/download/%@\" pathSubtitle=\"\"/>", track.title, hostName, track.objectID.URIRepresentation, duration, [anyFileFromTrack fileSizeInBytes], hostName, [anyFileFromTrack.url.path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
  310. }
  311. [mediaInHtml addObject:@"</div></div>"];
  312. }
  313. }
  314. UIDevice *currentDevice = [UIDevice currentDevice];
  315. NSString *deviceModel = [currentDevice model];
  316. NSDictionary *replacementDict;
  317. HTTPDynamicFileResponse *fileResponse;
  318. if (shouldReturnLibVLCXML) {
  319. replacementDict = @{@"FILES" : [mediaInXml componentsJoinedByString:@" "],
  320. @"NB_FILE" : [NSString stringWithFormat:@"%li", (unsigned long)mediaInXml.count],
  321. @"LIB_TITLE" : [currentDevice name]};
  322. fileResponse = [[HTTPDynamicFileResponse alloc] initWithFilePath:[self filePathForURI:path]
  323. forConnection:self
  324. separator:@"%%"
  325. replacementDictionary:replacementDict];
  326. fileResponse.contentType = @"application/xml";
  327. } else {
  328. replacementDict = @{@"FILES" : [mediaInHtml componentsJoinedByString:@" "],
  329. @"WEBINTF_TITLE" : NSLocalizedString(@"WEBINTF_TITLE", nil),
  330. @"WEBINTF_DROPFILES" : NSLocalizedString(@"WEBINTF_DROPFILES", nil),
  331. @"WEBINTF_DROPFILES_LONG" : [NSString stringWithFormat:NSLocalizedString(@"WEBINTF_DROPFILES_LONG", nil), deviceModel],
  332. @"WEBINTF_DOWNLOADFILES" : NSLocalizedString(@"WEBINTF_DOWNLOADFILES", nil),
  333. @"WEBINTF_DOWNLOADFILES_LONG" : [NSString stringWithFormat: NSLocalizedString(@"WEBINTF_DOWNLOADFILES_LONG", nil), deviceModel]};
  334. fileResponse = [[HTTPDynamicFileResponse alloc] initWithFilePath:[self filePathForURI:path]
  335. forConnection:self
  336. separator:@"%%"
  337. replacementDictionary:replacementDict];
  338. fileResponse.contentType = @"text/html";
  339. }
  340. #else
  341. #warning FIXME
  342. UIDevice *currentDevice = [UIDevice currentDevice];
  343. NSString *deviceModel = [currentDevice model];
  344. NSString *filePath = [self filePathForURI:path];
  345. NSString *documentRoot = [config documentRoot];
  346. NSString *relativePath = [filePath substringFromIndex:[documentRoot length]];
  347. NSDictionary *replacementDict = @{@"WEBINTF_TITLE" : NSLocalizedString(@"WEBINTF_TITLE", nil),
  348. @"WEBINTF_DROPFILES" : NSLocalizedString(@"WEBINTF_DROPFILES", nil),
  349. @"WEBINTF_DROPFILES_LONG" : [NSString stringWithFormat:NSLocalizedString(@"WEBINTF_DROPFILES_LONG", nil), deviceModel],
  350. @"WEBINTF_DOWNLOADFILES" : NSLocalizedString(@"WEBINTF_DOWNLOADFILES", nil),
  351. @"WEBINTF_DOWNLOADFILES_LONG" : [NSString stringWithFormat: NSLocalizedString(@"WEBINTF_DOWNLOADFILES_LONG", nil), deviceModel]};
  352. HTTPDynamicFileResponse *fileResponse;
  353. if ([relativePath isEqualToString:@"/index.html"]) {
  354. fileResponse = [[HTTPDynamicFileResponse alloc] initWithFilePath:[self filePathForURI:path]
  355. forConnection:self
  356. separator:@"%%"
  357. replacementDictionary:replacementDict];
  358. fileResponse.contentType = @"text/html";
  359. #endif
  360. return fileResponse;
  361. } else if ([relativePath isEqualToString:@"/style.css"]) {
  362. NSDictionary *replacementDict = @{@"WEBINTF_TITLE" : NSLocalizedString(@"WEBINTF_TITLE", nil)};
  363. HTTPDynamicFileResponse *fileResponse = [[HTTPDynamicFileResponse alloc] initWithFilePath:[self filePathForURI:path]
  364. forConnection:self
  365. separator:@"%%"
  366. replacementDictionary:replacementDict];
  367. fileResponse.contentType = @"text/css";
  368. return fileResponse;
  369. }
  370. return [super httpResponseForMethod:method URI:path];
  371. }
  372. - (void)prepareForBodyWithSize:(UInt64)contentLength
  373. {
  374. // set up mime parser
  375. NSString* boundary = [request headerField:@"boundary"];
  376. _parser = [[MultipartFormDataParser alloc] initWithBoundary:boundary formEncoding:NSUTF8StringEncoding];
  377. _parser.delegate = self;
  378. APLog(@"expecting file of size %lli kB", contentLength / 1024);
  379. _contentLength = contentLength;
  380. }
  381. - (void)processBodyData:(NSData *)postDataChunk
  382. {
  383. /* append data to the parser. It will invoke callbacks to let us handle
  384. * parsed data. */
  385. [_parser appendData:postDataChunk];
  386. _receivedContent += postDataChunk.length;
  387. APLog(@"received %lli kB (%lli %%)", _receivedContent / 1024, ((_receivedContent * 100) / _contentLength));
  388. }
  389. //-----------------------------------------------------------------
  390. #pragma mark multipart form data parser delegate
  391. - (void)processStartOfPartWithHeader:(MultipartMessageHeader*) header
  392. {
  393. /* in this sample, we are not interested in parts, other then file parts.
  394. * check content disposition to find out filename */
  395. MultipartMessageHeaderField* disposition = (header.fields)[@"Content-Disposition"];
  396. NSString* filename = [(disposition.params)[@"filename"] lastPathComponent];
  397. if ((nil == filename) || [filename isEqualToString: @""]) {
  398. // it's either not a file part, or
  399. // an empty form sent. we won't handle it.
  400. return;
  401. }
  402. // create the path where to store the media temporarily
  403. NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
  404. NSString *uploadDirPath = [searchPaths[0] stringByAppendingPathComponent:@"Upload"];
  405. NSFileManager *fileManager = [NSFileManager defaultManager];
  406. BOOL isDir = YES;
  407. if (![fileManager fileExistsAtPath:uploadDirPath isDirectory:&isDir])
  408. [fileManager createDirectoryAtPath:uploadDirPath withIntermediateDirectories:YES attributes:nil error:nil];
  409. _filepath = [uploadDirPath stringByAppendingPathComponent: filename];
  410. NSNumber *freeSpace = [[UIDevice currentDevice] freeDiskspace];
  411. if (_contentLength >= freeSpace.longLongValue) {
  412. /* avoid deadlock since we are on a background thread */
  413. [self performSelectorOnMainThread:@selector(notifyUserAboutEndOfFreeStorage:) withObject:filename waitUntilDone:NO];
  414. [self handleResourceNotFound];
  415. [self stop];
  416. return;
  417. }
  418. APLog(@"Saving file to %@", _filepath);
  419. if (![fileManager createDirectoryAtPath:uploadDirPath withIntermediateDirectories:true attributes:nil error:nil])
  420. APLog(@"Could not create directory at path: %@", _filepath);
  421. if (![fileManager createFileAtPath:_filepath contents:nil attributes:nil])
  422. APLog(@"Could not create file at path: %@", _filepath);
  423. _storeFile = [NSFileHandle fileHandleForWritingAtPath:_filepath];
  424. VLCActivityManager *activityManager = [VLCActivityManager defaultManager];
  425. [activityManager networkActivityStarted];
  426. [activityManager disableIdleTimer];
  427. }
  428. - (void)notifyUserAboutEndOfFreeStorage:(NSString *)filename
  429. {
  430. #if TARGET_OS_IOS
  431. VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"DISK_FULL", nil)
  432. message:[NSString stringWithFormat:
  433. NSLocalizedString(@"DISK_FULL_FORMAT", nil),
  434. filename,
  435. [[UIDevice currentDevice] model]]
  436. delegate:self
  437. cancelButtonTitle:NSLocalizedString(@"BUTTON_OK", nil)
  438. otherButtonTitles:nil];
  439. [alert show];
  440. #else
  441. UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"DISK_FULL", nil)
  442. message:[NSString stringWithFormat:
  443. NSLocalizedString(@"DISK_FULL_FORMAT", nil),
  444. filename,
  445. [[UIDevice currentDevice] model]]
  446. preferredStyle:UIAlertControllerStyleAlert];
  447. [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"BUTTON_CANCEL", nil)
  448. style:UIAlertActionStyleCancel
  449. handler:nil]];
  450. [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertController animated:YES completion:nil];
  451. #endif
  452. }
  453. - (void)processContent:(NSData*)data WithHeader:(MultipartMessageHeader*) header
  454. {
  455. // here we just write the output from parser to the file.
  456. if (_storeFile) {
  457. @try {
  458. [_storeFile writeData:data];
  459. }
  460. @catch (NSException *exception) {
  461. APLog(@"File to write further data because storage is full.");
  462. [_storeFile closeFile];
  463. _storeFile = nil;
  464. /* don't block */
  465. [self performSelector:@selector(stop) withObject:nil afterDelay:0.1];
  466. }
  467. }
  468. }
  469. - (void)processEndOfPartWithHeader:(MultipartMessageHeader*)header
  470. {
  471. // as the file part is over, we close the file.
  472. APLog(@"closing file");
  473. [_storeFile closeFile];
  474. _storeFile = nil;
  475. }
  476. - (BOOL)shouldDie
  477. {
  478. if (_filepath) {
  479. if (_filepath.length > 0)
  480. [[VLCHTTPUploaderController sharedInstance] moveFileFrom:_filepath];
  481. }
  482. return [super shouldDie];
  483. }
  484. #pragma mark subtitle
  485. - (NSMutableArray *)_listOfSubtitles
  486. {
  487. NSMutableArray *listOfSubtitles = [[NSMutableArray alloc] init];
  488. NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
  489. NSArray *allFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:documentsDirectory error:nil];
  490. NSString *filePath;
  491. NSUInteger count = allFiles.count;
  492. for (NSUInteger i = 0; i < count; i++) {
  493. filePath = [[NSString stringWithFormat:@"%@/%@", documentsDirectory, allFiles[i]] stringByReplacingOccurrencesOfString:@"file://"withString:@""];
  494. if ([filePath isSupportedSubtitleFormat])
  495. [listOfSubtitles addObject:filePath];
  496. }
  497. return listOfSubtitles;
  498. }
  499. - (NSString *)_checkIfSubtitleWasFound:(NSString *)filePath
  500. {
  501. NSString *subtitlePath;
  502. NSString *fileSub;
  503. NSString *currentPath;
  504. NSString *fileName = [[filePath lastPathComponent] stringByDeletingPathExtension];
  505. if (fileName == nil)
  506. return nil;
  507. NSMutableArray *listOfSubtitles = [self _listOfSubtitles];
  508. NSUInteger count = listOfSubtitles.count;
  509. for (NSUInteger i = 0; i < count; i++) {
  510. currentPath = listOfSubtitles[i];
  511. fileSub = [NSString stringWithFormat:@"%@", currentPath];
  512. if ([fileSub rangeOfString:fileName].location != NSNotFound)
  513. subtitlePath = currentPath;
  514. }
  515. return subtitlePath;
  516. }
  517. @end