VLCHTTPConnection.m 27.7 KB
Newer Older
1 2 3 4
/*****************************************************************************
 * VLCHTTPConnection.m
 * VLC for iOS
 *****************************************************************************
5
 * Copyright (c) 2013-2015 VideoLAN. All rights reserved.
6 7 8 9 10 11 12
 * $Id$
 *
 * Authors: Felix Paul Kühne <fkuehne # videolan.org>
 *          Jean-Baptiste Kempf <jb # videolan.org>
 *
 * Refer to the COPYING file of the official project for license.
 *****************************************************************************/
13 14 15 16 17 18 19 20 21

#import "VLCAppDelegate.h"
#import "VLCHTTPConnection.h"
#import "HTTPConnection.h"
#import "MultipartFormDataParser.h"
#import "HTTPMessage.h"
#import "HTTPDataResponse.h"
#import "HTTPFileResponse.h"
#import "MultipartMessageHeaderField.h"
22
#import "VLCHTTPUploaderController.h"
23
#import "HTTPDynamicFileResponse.h"
24
#import "VLCThumbnailsCache.h"
25
#import "NSString+SupportedMedia.h"
26
#import "UIDevice+VLC.h"
27 28 29

@interface VLCHTTPConnection()
{
30 31 32
    MultipartFormDataParser *_parser;
    NSFileHandle *_storeFile;
    NSString *_filepath;
33 34
    UInt64 _contentLength;
    UInt64 _receivedContent;
35
}
36 37 38

@property (nonatomic) VLCHTTPUploaderController *uploadController;

39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
@end

@implementation VLCHTTPConnection

- (BOOL)supportsMethod:(NSString *)method atPath:(NSString *)path
{
    // Add support for POST
    if ([method isEqualToString:@"POST"]) {
        if ([path isEqualToString:@"/upload.json"])
            return YES;
    }

    return [super supportsMethod:method atPath:path];
}

- (BOOL)expectsRequestBodyFromMethod:(NSString *)method atPath:(NSString *)path
{
    // Inform HTTP server that we expect a body to accompany a POST request
    if ([method isEqualToString:@"POST"] && [path isEqualToString:@"/upload.json"]) {
        // here we need to make sure, boundary is set in header
        NSString* contentType = [request headerField:@"Content-Type"];
        NSUInteger paramsSeparator = [contentType rangeOfString:@";"].location;
        if (NSNotFound == paramsSeparator)
            return NO;

        if (paramsSeparator >= contentType.length - 1)
            return NO;

        NSString* type = [contentType substringToIndex:paramsSeparator];
        if (![type isEqualToString:@"multipart/form-data"]) {
            // we expect multipart/form-data content type
            return NO;
        }

        // enumerate all params in content-type, and find boundary there
        NSArray* params = [[contentType substringFromIndex:paramsSeparator + 1] componentsSeparatedByString:@";"];
        for (NSString* param in params) {
            paramsSeparator = [param rangeOfString:@"="].location;
            if ((NSNotFound == paramsSeparator) || paramsSeparator >= param.length - 1)
                continue;

            NSString* paramName = [param substringWithRange:NSMakeRange(1, paramsSeparator-1)];
            NSString* paramValue = [param substringFromIndex:paramsSeparator+1];

            if ([paramName isEqualToString: @"boundary"])
                // let's separate the boundary from content-type, to make it more handy to handle
                [request setHeaderField:@"boundary" value:paramValue];
        }
        // check if boundary specified
        if (nil == [request headerField:@"boundary"])
            return NO;

        return YES;
    }
    return [super expectsRequestBodyFromMethod:method atPath:path];
}

- (NSObject<HTTPResponse> *)httpResponseForMethod:(NSString *)method URI:(NSString *)path
{
    if ([method isEqualToString:@"POST"] && [path isEqualToString:@"/upload.json"]) {
        return [[HTTPDataResponse alloc] initWithData:[@"\"OK\"" dataUsingEncoding:NSUTF8StringEncoding]];
    }
101 102
    if ([path hasPrefix:@"/download/"]) {
        NSString *filePath = [[path stringByReplacingOccurrencesOfString:@"/download/" withString:@""]stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
103 104 105
        HTTPFileResponse *fileResponse = [[HTTPFileResponse alloc] initWithFilePath:filePath forConnection:self];
        fileResponse.contentType = @"application/octet-stream";
        return fileResponse;
106
    }
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
    if ([path hasPrefix:@"/thumbnail"]) {
        NSString *filePath = [[path stringByReplacingOccurrencesOfString:@"/thumbnail/" withString:@""]stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
        filePath = [filePath stringByReplacingOccurrencesOfString:@".png" withString:@""];

        NSManagedObjectContext *moc = [[MLMediaLibrary sharedMediaLibrary] managedObjectContext];
        NSPersistentStoreCoordinator *psc = [moc persistentStoreCoordinator];
        NSManagedObject *mo = [moc existingObjectWithID:[psc managedObjectIDForURIRepresentation:[NSURL URLWithString:filePath]] error:nil];

        NSData *theData;
        if ([mo isKindOfClass:[MLFile class]])
            theData = UIImagePNGRepresentation([VLCThumbnailsCache thumbnailForMediaFile:(MLFile *)mo]);
        else if ([mo isKindOfClass:[MLShow class]])
            theData = UIImagePNGRepresentation([VLCThumbnailsCache thumbnailForShow:(MLShow *)mo]);
        else if ([mo isKindOfClass:[MLLabel class]])
            theData = UIImagePNGRepresentation([VLCThumbnailsCache thumbnailForLabel:(MLLabel *)mo]);
        else if ([mo isKindOfClass:[MLAlbum class]])
            theData = UIImagePNGRepresentation([VLCThumbnailsCache thumbnailForMediaFile:[[(MLAlbum *)mo tracks].anyObject files].anyObject]);
124 125 126 127
        else if ([mo isKindOfClass:[MLAlbumTrack class]])
            theData = UIImagePNGRepresentation([VLCThumbnailsCache thumbnailForMediaFile:[(MLAlbumTrack *)mo files].anyObject]);
        else if ([mo isKindOfClass:[MLShowEpisode class]])
            theData = UIImagePNGRepresentation([VLCThumbnailsCache thumbnailForMediaFile:[(MLShowEpisode *)mo files].anyObject]);
128

129 130 131 132 133
        if (theData) {
            HTTPDataResponse *dataResponse = [[HTTPDataResponse alloc] initWithData:theData];
            dataResponse.contentType = @"image/png";
            return dataResponse;
        }
134
    }
135 136 137 138
    NSString *filePath = [self filePathForURI:path];
    NSString *documentRoot = [config documentRoot];
    NSString *relativePath = [filePath substringFromIndex:[documentRoot length]];

139
    if (([relativePath isEqualToString:@"/index.html"]) || ([relativePath isEqualToString:@"/libMediaVLC.xml"])) {
140 141 142
        NSMutableArray *allMedia = [[NSMutableArray alloc] init];

        /* add all albums */
143 144
        NSArray *allAlbums = [MLAlbum allAlbums];
        for (MLAlbum *album in allAlbums) {
145 146
            if (album.name.length > 0 && album.tracks.count > 1)
                [allMedia addObject:album];
147
        }
148 149

        /* add all shows */
150 151
        NSArray *allShows = [MLShow allShows];
        for (MLShow *show in allShows) {
152 153
            if (show.name.length > 0 && show.episodes.count > 1)
                [allMedia addObject:show];
154
        }
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177

        /* add all folders*/
        NSArray *allFolders = [MLLabel allLabels];
        for (MLLabel *folder in allFolders)
            [allMedia addObject:folder];

        /* add all remaining files */
        NSArray *allFiles = [MLFile allFiles];
        for (MLFile *file in allFiles) {
            if (file.labels.count > 0) continue;

            if (!file.isShowEpisode && !file.isAlbumTrack)
                [allMedia addObject:file];
            else if (file.isShowEpisode) {
                if (file.showEpisode.show.episodes.count < 2)
                    [allMedia addObject:file];
            } else if (file.isAlbumTrack) {
                if (file.albumTrack.album.tracks.count < 2)
                    [allMedia addObject:file];
            }
        }

        NSMutableArray *mediaInHtml = [[NSMutableArray alloc] initWithCapacity:allMedia.count];
178 179 180
        NSMutableArray *mediaInXml = [[NSMutableArray alloc] initWithCapacity:allMedia.count];
        self.uploadController = [[VLCHTTPUploaderController alloc] init];
        NSString *hostName = [self.uploadController hostname];
181
        NSString *pathLibrary = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
182
        NSString *duration;
183 184

        for (NSManagedObject *mo in allMedia) {
185
            if ([mo isKindOfClass:[MLFile class]]) {
186
                duration = [[VLCTime timeWithNumber:[(MLFile *)mo duration]] stringValue];
187 188
                [mediaInHtml addObject:[NSString stringWithFormat:
                                        @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
189
                                        <a href=\"download/%@\" class=\"inner\"> \
190 191 192
                                        <div class=\"down icon\"></div> \
                                        <div class=\"infos\"> \
                                        <span class=\"first-line\">%@</span> \
193
                                        <span class=\"second-line\">%@ - %0.2f MB</span> \
194 195 196 197 198
                                        </div> \
                                        </a> \
                                        </div>",
                                        mo.objectID.URIRepresentation,
                                        [[(MLFile *)mo url] stringByReplacingOccurrencesOfString:@"file://"withString:@""],
199 200
                                        [(MLFile *)mo title],
                                        duration, (float)([(MLFile *)mo fileSizeInBytes] / 1e6)]];
201 202 203 204 205 206
                if ([relativePath isEqualToString:@"/libMediaVLC.xml"]) {
                    NSString *pathSub = [self _checkSubtitleFound:[(MLFile *)mo url]];
                    if (![pathSub isEqualToString:@""])
                        pathSub = [NSString stringWithFormat:@"http://%@/download/%@", hostName, pathSub];
                    [mediaInXml addObject:[NSString stringWithFormat:@"<Media title=\"%@\" thumb=\"http://%@/download/%@/Thumbnails/File/%@.png\" duration=\"%@\" size=\"%li\" pathfile=\"http://%@/download/%@\" pathSubtitle=\"%@\"/>", [(MLFile *)mo title], hostName, pathLibrary, [[NSString stringWithFormat:@"%@", mo.objectID.URIRepresentation] lastPathComponent], duration, [(MLFile *)mo fileSizeInBytes], hostName, [[(MLFile *)mo url] stringByReplacingOccurrencesOfString:@"file://"withString:@""], pathSub]];
                }
207
            }
208 209
            else if ([mo isKindOfClass:[MLShow class]]) {
                NSArray *episodes = [(MLShow *)mo sortedEpisodes];
210 211
                [mediaInHtml addObject:[NSString stringWithFormat:
                                        @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
212
                                        <a href=\"#\" class=\"inner folder\"> \
213 214 215
                                        <div class=\"open icon\"></div> \
                                        <div class=\"infos\"> \
                                        <span class=\"first-line\">%@</span> \
Felix Paul Kühne's avatar
Felix Paul Kühne committed
216
                                        <span class=\"second-line\">%lu items</span> \
217 218 219 220
                                        </div> \
                                        </a> \
                                        <div class=\"content\">",
                                        mo.objectID.URIRepresentation,
221
                                        [(MLShow *)mo name],
Felix Paul Kühne's avatar
Felix Paul Kühne committed
222
                                        (unsigned long)[episodes count]]];
223
                for (MLShowEpisode *showEp in episodes) {
224
                    duration = [[VLCTime timeWithNumber:[(MLFile *)[[showEp files] anyObject] duration]] stringValue];
225 226
                    [mediaInHtml addObject:[NSString stringWithFormat:
                                            @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
227
                                            <a href=\"download/%@\" class=\"inner\"> \
228 229 230
                                            <div class=\"down icon\"></div> \
                                            <div class=\"infos\"> \
                                            <span class=\"first-line\">S%@E%@ - %@</span> \
231
                                            <span class=\"second-line\">%@ - %0.2f MB</span> \
232 233 234 235 236 237 238
                                            </div> \
                                            </a> \
                                            </div>",
                                            showEp.objectID.URIRepresentation,
                                            [[(MLFile *)[[showEp files] anyObject] url] stringByReplacingOccurrencesOfString:@"file://"withString:@""],
                                            showEp.seasonNumber,
                                            showEp.episodeNumber,
239 240
                                            showEp.name,
                                            duration, (float)([(MLFile *)[[showEp files] anyObject] fileSizeInBytes] / 1e6)]];
241 242 243 244 245 246
                    if ([relativePath isEqualToString:@"/libMediaVLC.xml"]) {
                        NSString *pathSub = [self _checkSubtitleFound:[(MLFile *)[[showEp files] anyObject] url]];
                        if (![pathSub isEqualToString:@""])
                            pathSub = [NSString stringWithFormat:@"http://%@/download/%@", hostName, pathSub];
                        [mediaInXml addObject:[NSString stringWithFormat:@"<Media title=\"%@ - S%@E%@\" thumb=\"http://%@/download/%@/Thumbnails/File/%@.png\" duration=\"%@\" size=\"%li\" pathfile=\"http://%@/download/%@\" pathSubtitle=\"%@\"/>", [(MLShow *)mo name], showEp.seasonNumber, showEp.episodeNumber, hostName, pathLibrary, [[NSString stringWithFormat:@"%@", showEp.objectID.URIRepresentation] lastPathComponent], duration, [(MLFile *)[[showEp files] anyObject] fileSizeInBytes], hostName, [[(MLFile *)[[showEp files] anyObject] url] stringByReplacingOccurrencesOfString:@"file://"withString:@""], pathSub]];
                    }
247
                }
248
                [mediaInHtml addObject:@"</div></div>"];
249 250
            } else if ([mo isKindOfClass:[MLLabel class]]) {
                NSArray *folderItems = [(MLLabel *)mo sortedFolderItems];
251 252
                [mediaInHtml addObject:[NSString stringWithFormat:
                                        @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
253
                                        <a href=\"#\" class=\"inner folder\"> \
254 255 256
                                        <div class=\"open icon\"></div> \
                                        <div class=\"infos\"> \
                                        <span class=\"first-line\">%@</span> \
Felix Paul Kühne's avatar
Felix Paul Kühne committed
257
                                        <span class=\"second-line\">%lu items</span> \
258 259 260 261
                                        </div> \
                                        </a> \
                                        <div class=\"content\">",
                                        mo.objectID.URIRepresentation,
262
                                        [(MLLabel *)mo name],
Felix Paul Kühne's avatar
Felix Paul Kühne committed
263
                                        (unsigned long)[folderItems count]]];
264
                for (MLFile *file in folderItems) {
265
                    duration = [[VLCTime timeWithNumber:[file duration]] stringValue];
266 267
                    [mediaInHtml addObject:[NSString stringWithFormat:
                                            @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
268
                                            <a href=\"download/%@\" class=\"inner\"> \
269 270 271
                                            <div class=\"down icon\"></div> \
                                            <div class=\"infos\"> \
                                            <span class=\"first-line\">%@</span> \
272
                                            <span class=\"second-line\">%@ - %0.2f MB</span> \
273 274 275 276 277
                                            </div> \
                                            </a> \
                                            </div>",
                                            file.objectID.URIRepresentation,
                                            [[file url] stringByReplacingOccurrencesOfString:@"file://"withString:@""],
278 279
                                            file.title,
                                            duration, (float)([file fileSizeInBytes] / 1e6)]];
280 281 282 283 284 285
                    if ([relativePath isEqualToString:@"/libMediaVLC.xml"]) {
                        NSString *pathSub = [self _checkSubtitleFound:[file url]];
                        if (![pathSub isEqualToString:@""])
                            pathSub = [NSString stringWithFormat:@"http://%@/download/%@", hostName, pathSub];
                        [mediaInXml addObject:[NSString stringWithFormat:@"<Media title=\"%@\" thumb=\"http://%@/download/%@/Thumbnails/File/%@.png\" duration=\"%@\" size=\"%li\" pathfile=\"http://%@/download/%@\" pathSubtitle=\"%@\"/>", file.title, hostName, pathLibrary, [[NSString stringWithFormat:@"%@", file.objectID.URIRepresentation] lastPathComponent], duration, [file fileSizeInBytes], hostName, [[file url] stringByReplacingOccurrencesOfString:@"file://"withString:@""], pathSub]];
                    }
286
                }
287
                [mediaInHtml addObject:@"</div></div>"];
288 289
            } else if ([mo isKindOfClass:[MLAlbum class]]) {
                NSArray *albumTracks = [(MLAlbum *)mo sortedTracks];
290 291
                [mediaInHtml addObject:[NSString stringWithFormat:
                                        @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
292
                                        <a href=\"#\" class=\"inner folder\"> \
293 294 295
                                        <div class=\"open icon\"></div> \
                                        <div class=\"infos\"> \
                                        <span class=\"first-line\">%@</span> \
Felix Paul Kühne's avatar
Felix Paul Kühne committed
296
                                        <span class=\"second-line\">%lu items</span> \
297 298 299 300
                                        </div> \
                                        </a> \
                                        <div class=\"content\">",
                                        mo.objectID.URIRepresentation,
301
                                        [(MLAlbum *)mo name],
Felix Paul Kühne's avatar
Felix Paul Kühne committed
302
                                        (unsigned long)[albumTracks count]]];
303
                for (MLAlbumTrack *track in albumTracks) {
304
                    duration = [[VLCTime timeWithNumber:[(MLFile *)[[track files] anyObject] duration]] stringValue];
305 306
                    [mediaInHtml addObject:[NSString stringWithFormat:
                                            @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
307
                                            <a href=\"download/%@\" class=\"inner\"> \
308 309 310
                                            <div class=\"down icon\"></div> \
                                            <div class=\"infos\"> \
                                            <span class=\"first-line\">%@</span> \
311
                                            <span class=\"second-line\">%@ - %0.2f MB</span> \
312 313 314 315 316
                                            </div> \
                                            </a> \
                                            </div>",
                                            track.objectID.URIRepresentation,
                                            [[(MLFile *)[[track files] anyObject] url] stringByReplacingOccurrencesOfString:@"file://"withString:@""],
317 318
                                            track.title,
                                            duration, (float)([(MLFile *)[[track files] anyObject] fileSizeInBytes] / 1e6)]];
319 320
                    if ([relativePath isEqualToString:@"/libMediaVLC.xml"])
                        [mediaInXml addObject:[NSString stringWithFormat:@"<Media title=\"%@\" thumb=\"http://%@/download/%@/Thumbnails/File/%@.png\" duration=\"%@\" size=\"%li\" pathfile=\"http://%@/download/%@\" pathSubtitle=\"\"/>", track.title, hostName, pathLibrary, [[NSString stringWithFormat:@"%@", track.objectID.URIRepresentation] lastPathComponent], duration, [(MLFile *)[[track files] anyObject] fileSizeInBytes], hostName, [[(MLFile *)[[track files] anyObject] url] stringByReplacingOccurrencesOfString:@"file://"withString:@""]]];
321
                }
322
                [mediaInHtml addObject:@"</div></div>"];
323
            }
324
        }
325

326
        NSString *deviceModel = [[UIDevice currentDevice] model];
327 328 329 330 331 332 333 334 335 336 337 338 339
        NSDictionary *replacementDict;

        if ([relativePath isEqualToString:@"/libMediaVLC.xml"]) {
            replacementDict = @{@"FILES" : [mediaInXml componentsJoinedByString:@" "],
                                @"NB_FILE" : [NSString stringWithFormat:@"%li", (unsigned long)mediaInXml.count],
                                @"LIB_TITLE" : [[UIDevice currentDevice] name]};
        } else
            replacementDict = @{@"FILES" : [mediaInHtml componentsJoinedByString:@" "],
                                @"WEBINTF_TITLE" : NSLocalizedString(@"WEBINTF_TITLE", nil),
                                @"WEBINTF_DROPFILES" : NSLocalizedString(@"WEBINTF_DROPFILES", nil),
                                @"WEBINTF_DROPFILES_LONG" : [NSString stringWithFormat:NSLocalizedString(@"WEBINTF_DROPFILES_LONG", nil), deviceModel],
                                @"WEBINTF_DOWNLOADFILES" : NSLocalizedString(@"WEBINTF_DOWNLOADFILES", nil),
                                @"WEBINTF_DOWNLOADFILES_LONG" : [NSString stringWithFormat: NSLocalizedString(@"WEBINTF_DOWNLOADFILES_LONG", nil), deviceModel]};
340 341 342 343 344 345

        return [[HTTPDynamicFileResponse alloc] initWithFilePath:[self filePathForURI:path]
                                                   forConnection:self
                                                       separator:@"%%"
                                           replacementDictionary:replacementDict];
    } else if ([relativePath isEqualToString:@"/style.css"]) {
346
        NSDictionary *replacementDict = @{@"WEBINTF_TITLE" : NSLocalizedString(@"WEBINTF_TITLE", nil)};
347 348 349 350 351
        return [[HTTPDynamicFileResponse alloc] initWithFilePath:[self filePathForURI:path]
                                                   forConnection:self
                                                       separator:@"%%"
                                           replacementDictionary:replacementDict];
    }
352

353 354 355 356 357 358 359 360 361 362
    return [super httpResponseForMethod:method URI:path];
}

- (void)prepareForBodyWithSize:(UInt64)contentLength
{
    // set up mime parser
    NSString* boundary = [request headerField:@"boundary"];
    _parser = [[MultipartFormDataParser alloc] initWithBoundary:boundary formEncoding:NSUTF8StringEncoding];
    _parser.delegate = self;

363 364
    APLog(@"expecting file of size %lli kB", contentLength / 1024);
    _contentLength = contentLength;
365 366 367 368 369 370 371
}

- (void)processBodyData:(NSData *)postDataChunk
{
    /* append data to the parser. It will invoke callbacks to let us handle
     * parsed data. */
    [_parser appendData:postDataChunk];
372

373
    _receivedContent += postDataChunk.length;
374

375
    APLog(@"received %lli kB (%lli %%)", _receivedContent / 1024, ((_receivedContent * 100) / _contentLength));
376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
}

//-----------------------------------------------------------------
#pragma mark multipart form data parser delegate


- (void)processStartOfPartWithHeader:(MultipartMessageHeader*) header
{
    /* in this sample, we are not interested in parts, other then file parts.
     * check content disposition to find out filename */

    MultipartMessageHeaderField* disposition = (header.fields)[@"Content-Disposition"];
    NSString* filename = [(disposition.params)[@"filename"] lastPathComponent];

    if ((nil == filename) || [filename isEqualToString: @""]) {
        // it's either not a file part, or
        // an empty form sent. we won't handle it.
        return;
    }

396
    // create the path where to store the media temporarily
397
    NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
398
    NSString* uploadDirPath = [searchPaths[0] stringByAppendingPathComponent:@"Upload"];
399
    NSFileManager *fileManager = [NSFileManager defaultManager];
400 401

    BOOL isDir = YES;
402
    if (![fileManager fileExistsAtPath:uploadDirPath isDirectory:&isDir ])
403
        [fileManager createDirectoryAtPath:uploadDirPath withIntermediateDirectories:YES attributes:nil error:nil];
404

405
    _filepath = [uploadDirPath stringByAppendingPathComponent: filename];
406

407 408 409 410 411 412 413 414 415
    NSNumber *freeSpace = [[UIDevice currentDevice] freeDiskspace];
    if (_contentLength >= freeSpace.longLongValue) {
        /* avoid deadlock since we are on a background thread */
        [self performSelectorOnMainThread:@selector(notifyUserAboutEndOfFreeStorage:) withObject:filename waitUntilDone:NO];
        [self handleResourceNotFound];
        [self stop];
        return;
    }

416
    APLog(@"Saving file to %@", _filepath);
417
    if (![fileManager createDirectoryAtPath:uploadDirPath withIntermediateDirectories:true attributes:nil error:nil])
418
        APLog(@"Could not create directory at path: %@", _filepath);
419

420
    if (![fileManager createFileAtPath:_filepath contents:nil attributes:nil])
421
        APLog(@"Could not create file at path: %@", _filepath);
422

423
    _storeFile = [NSFileHandle fileHandleForWritingAtPath:_filepath];
424
    [(VLCAppDelegate*)[UIApplication sharedApplication].delegate networkActivityStarted];
425
    [(VLCAppDelegate*)[UIApplication sharedApplication].delegate disableIdleTimer];
426 427
}

428 429 430 431 432 433 434 435 436 437 438 439 440
- (void)notifyUserAboutEndOfFreeStorage:(NSString *)filename
{
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"DISK_FULL", nil)
                                                    message:[NSString stringWithFormat:
                                                             NSLocalizedString(@"DISK_FULL_FORMAT", nil),
                                                             filename,
                                                             [[UIDevice currentDevice] model]]
                                                   delegate:self
                                          cancelButtonTitle:NSLocalizedString(@"BUTTON_OK", nil)
                                          otherButtonTitles:nil];
    [alert show];
}

441 442 443
- (void)processContent:(NSData*)data WithHeader:(MultipartMessageHeader*) header
{
    // here we just write the output from parser to the file.
444 445 446 447 448 449 450 451 452 453 454 455 456
    if (_storeFile) {
        @try {
            [_storeFile writeData:data];
        }
        @catch (NSException *exception) {
            APLog(@"File to write further data because storage is full.");
            [_storeFile closeFile];
            _storeFile = nil;
            /* don't block */
            [self performSelector:@selector(stop) withObject:nil afterDelay:0.1];
        }
    }

457 458 459 460 461
}

- (void)processEndOfPartWithHeader:(MultipartMessageHeader*)header
{
    // as the file part is over, we close the file.
462
    APLog(@"closing file");
463 464 465 466
    [_storeFile closeFile];
    _storeFile = nil;
}

467
- (BOOL)shouldDie
468
{
469 470 471
    if (_filepath) {
        if (_filepath.length > 0)
            [[(VLCAppDelegate*)[UIApplication sharedApplication].delegate uploadController] moveFileFrom:_filepath];
472
    }
473
    return [super shouldDie];
474 475
}

476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
#pragma mark subtitle

- (NSMutableArray *)_listOfSubtitle
{
    NSMutableArray *listOfSubtitle = [[NSMutableArray alloc] init];
    NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    NSArray *allfiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:documentsDirectory error:nil];
    NSString *filePath;
    for (int count = 0; count < allfiles.count; count++) {
        filePath = [[NSString stringWithFormat:@"%@/%@", documentsDirectory, allfiles[count]] stringByReplacingOccurrencesOfString:@"file://"withString:@""];
        if ([filePath isSupportedSubtitleFormat])
            [listOfSubtitle addObject:filePath];
    }
    return listOfSubtitle;
}

- (NSString *)_checkSubtitleFound:(NSString *)fileURL
{
    NSString *subtitlePath = @"";
    NSString *fileName = [[fileURL lastPathComponent] stringByDeletingPathExtension];
    NSMutableArray *listOfSubtitle = [[NSMutableArray alloc] init];
    listOfSubtitle = [self _listOfSubtitle];
    NSString *fileSub;
    for (int count = 0; count < listOfSubtitle.count; count++) {
        fileSub = [NSString stringWithFormat:@"%@", listOfSubtitle[count]];
        if ([fileSub rangeOfString:fileName].location != NSNotFound)
            subtitlePath = [listOfSubtitle objectAtIndex:count];
    }
    return subtitlePath;
}

507
@end