Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Removed DTDownload and DTBonjour

They are now in their own GitHub repos.
  • Loading branch information...
commit ffd7fc5fead6dc8de1453e829d381abf46ca2419 1 parent daa93de
@Cocoanetics authored
Showing with 282 additions and 4,509 deletions.
  1. +0 −72 Core/Source/DTBonjour/DTBonjourDataChunk.h
  2. +0 −337 Core/Source/DTBonjour/DTBonjourDataChunk.m
  3. +0 −149 Core/Source/DTBonjour/DTBonjourDataConnection.h
  4. +0 −320 Core/Source/DTBonjour/DTBonjourDataConnection.m
  5. +0 −89 Core/Source/DTBonjour/DTBonjourServer.h
  6. +0 −433 Core/Source/DTBonjour/DTBonjourServer.m
  7. +0 −22 Core/Source/DTBonjour/NSScanner+DTBonjour.h
  8. +0 −47 Core/Source/DTBonjour/NSScanner+DTBonjour.m
  9. +0 −74 Core/Source/DTDownload/DTCachedFile.h
  10. +0 −26 Core/Source/DTDownload/DTCachedFile.m
  11. +0 −231 Core/Source/DTDownload/DTDownload.h
  12. +0 −677 Core/Source/DTDownload/DTDownload.m
  13. +0 −116 Core/Source/DTDownload/DTDownloadCache.h
  14. +0 −1,002 Core/Source/DTDownload/DTDownloadCache.m
  15. +0 −32 Core/Source/DTDownload/DTDownloadItem.h
  16. +0 −37 Core/Source/DTDownload/DTDownloadItem.m
  17. +0 −71 Core/Source/DTDownload/DTDownloadQueue.h
  18. +0 −257 Core/Source/DTDownload/DTDownloadQueue.m
  19. +249 −0 Core/Source/iOS/DTNetworking/DTNetworking.xcodeproj/project.pbxproj
  20. +7 −0 Core/Source/iOS/DTNetworking/DTNetworking/DTNetworking-Prefix.pch
  21. +13 −0 Core/Source/iOS/DTNetworking/DTNetworking/DTNetworking.h
  22. +13 −0 Core/Source/iOS/DTNetworking/DTNetworking/DTNetworking.m
  23. +0 −16 DTFoundation.podspec
  24. +0 −299 DTFoundation.xcodeproj/project.pbxproj
  25. +0 −25 Test/Resources/DTDownload/Test1.txt.download/Info.plist
  26. +0 −1  Test/Resources/DTDownload/Test1.txt.download/Test1.txt
  27. +0 −25 Test/Resources/DTDownload/Test2.txt.download/Info.plist
  28. 0  Test/Resources/Test
  29. +0 −16 Test/Source/DTDownloadTest.h
  30. +0 −135 Test/Source/DTDownloadTest.m
View
72 Core/Source/DTBonjour/DTBonjourDataChunk.h
@@ -1,72 +0,0 @@
-//
-// DTBonjourDataChunk.h
-// DTFoundation
-//
-// Created by Oliver Drobnik on 15.11.12.
-// Copyright (c) 2012 Cocoanetics. All rights reserved.
-//
-
-#import <Foundation/Foundation.h>
-
-#import "DTBonjourDataConnection.h"
-
-
-/**
- This class represents an object that is being transmitted as a chunk of data comprised of a header and the raw data.
- */
-
-@interface DTBonjourDataChunk : NSObject
-
-/**
- Creates a data chunk meant for sending.
- @param object An object to be encoded and transmitted
- @param encoding The transport encoding to be used for encoding the object
- @param error An optional error output parameter for when the object cannot be encoded
- */
-- (id)initWithObject:(id)object encoding:(DTBonjourDataConnectionContentType)encoding error:(NSError **)error;
-
-/**
- Creates a data chunk meant for receiving.
- */
-- (id)initForReading;
-
-/**
- Number of bytes of the receiver
- */
-@property (nonatomic, readonly) NSUInteger totalBytes;
-
-/**
- Number of bytes that have been transferred already
- */
-@property (nonatomic, readonly) NSUInteger numberOfTransferredBytes;
-
-/**
- Sequence number of this chunk on the connection
-*/
-@property (nonatomic, readonly) NSUInteger sequenceNumber;
-
-/**
- Writes the as many bytes to the output stream as it would accept
- @param stream The output stream to write to
- @returns The number of bytes written. -1 means that there was an error.
- */
-- (NSInteger)writeToOutputStream:(NSOutputStream *)stream;
-
-/**
- Reads from the input stream and initializes the header and payload as the necessary data becomes available.
- @param stream The input stream to read from
- @returns The number of bytes read. -1 means that there was an error.
- */
-- (NSInteger)readFromInputStream:(NSInputStream *)stream;
-
-/**
- Decodes the object contained in the received data
- */
-- (id)decodedObject;
-
-/**
- @returns `YES` if all bytes have been written/read
- */
-- (BOOL)isTransmissionComplete;
-
-@end
View
337 Core/Source/DTBonjour/DTBonjourDataChunk.m
@@ -1,337 +0,0 @@
-//
-// DTBonjourDataChunk.m
-// DTFoundation
-//
-// Created by Oliver Drobnik on 15.11.12.
-// Copyright (c) 2012 Cocoanetics. All rights reserved.
-//
-
-#import "DTBonjourDataChunk.h"
-#import "DTBonjourDataConnection.h"
-#import "NSScanner+DTBonjour.h"
-
-#import <Foundation/NSJSONSerialization.h>
-
-
-@interface DTBonjourDataChunk ()
-
-@property (nonatomic, assign) NSUInteger sequenceNumber;
-
-@end
-
-@implementation DTBonjourDataChunk
-{
- NSMutableData *_data;
-
- NSUInteger _numberOfTransferredBytes;
- NSUInteger _totalBytes;
- NSUInteger _sequenceNumber;
-
- DTBonjourDataConnectionContentType _encoding;
-
- NSRange _rangeOfHeader;
- NSUInteger _contentLength;
-}
-
-- (id)initWithObject:(id)object encoding:(DTBonjourDataConnectionContentType)encoding error:(NSError **)error
-{
- self = [super init];
-
- if (self)
- {
- _encoding = encoding;
-
- if (![self _encodeObject:object error:error])
- {
- return nil;
- }
- }
-
- return self;
-}
-
-- (id)initForReading
-{
- self = [super init];
-
- if (self)
- {
- _data = [[NSMutableData alloc] initWithCapacity:1000];
- }
-
- return self;
-}
-
-
-
-- (BOOL)_encodeObject:(id)object error:(NSError **)error
-{
- NSData *archivedData = nil;
- NSString *contentType = nil;
-
- switch (_encoding)
- {
- case DTBonjourDataConnectionContentTypeJSON:
- {
- // check if our sending encoding type permits this object
- if (![NSJSONSerialization isValidJSONObject:object])
- {
- if (error)
- {
- NSString *errorMsg = [NSString stringWithFormat:@"Object %@ is not a valid root object for JSON serialization", object];
- NSDictionary *userInfo = @{NSLocalizedDescriptionKey:errorMsg};
- *error = [NSError errorWithDomain:DTBonjourDataConnectionErrorDomain code:1 userInfo:userInfo];
- }
-
- return NO;
- }
-
- archivedData = [NSJSONSerialization dataWithJSONObject:object options:0 error:error];
-
- if (!archivedData)
- {
- return NO;
- }
-
- contentType = @"application/json";
-
- break;
- }
-
- case DTBonjourDataConnectionContentTypeNSCoding:
- {
- // check if our sending encoding type permits this object
- if (![object conformsToProtocol:@protocol(NSCoding)])
- {
- if (error)
- {
- NSString *errorMsg = [NSString stringWithFormat:@"Object %@ does not conform to NSCoding", object];
- NSDictionary *userInfo = @{NSLocalizedDescriptionKey:errorMsg};
- *error = [NSError errorWithDomain:DTBonjourDataConnectionErrorDomain code:1 userInfo:userInfo];
- }
-
- return NO;
- }
-
- archivedData = [NSKeyedArchiver archivedDataWithRootObject:object];
- contentType = @"application/octet-stream";
-
- break;
- }
-
- default:
- {
- if (error)
- {
- NSString *errorMsg = [NSString stringWithFormat:@"Unknown encoding type %d", _encoding];
- NSDictionary *userInfo = @{NSLocalizedDescriptionKey:errorMsg};
- *error = [NSError errorWithDomain:DTBonjourDataConnectionErrorDomain code:1 userInfo:userInfo];
- }
-
- return NO;
- }
- }
-
- NSString *type = NSStringFromClass([object class]);
- NSString *header = [NSString stringWithFormat:@"PUT\r\nClass: %@\r\nContent-Type: %@\r\nSequence-Number: %ld\r\nContent-Length:%ld\r\n\r\n", type, contentType, (unsigned long)_sequenceNumber, (long)[archivedData length]];
- NSData *headerData = [header dataUsingEncoding:NSUTF8StringEncoding];
-
- _contentLength = [archivedData length];
- _rangeOfHeader = NSMakeRange(0, [headerData length]);
-
- _totalBytes = _rangeOfHeader.length + _contentLength;
-
- NSMutableData *data = [[NSMutableData alloc] initWithCapacity:_totalBytes];
-
- [data appendData:headerData];
- [data appendData:archivedData];
-
- _data = data;
-
- return YES;
-}
-
-- (id)decodedObject
-{
- if (!_totalBytes || [_data length] < _totalBytes)
- {
- NSLog(@"Insufficient data received yet for decoding object");
- return nil;
- }
-
- NSInteger indexAfterHeader = NSMaxRange(_rangeOfHeader);
- NSRange payloadRange = NSMakeRange(indexAfterHeader, _contentLength);
- NSData *payloadData = [_data subdataWithRange:payloadRange];
-
- // decode data
- id object = nil;
-
- if (_encoding == DTBonjourDataConnectionContentTypeJSON)
- {
- object = [NSJSONSerialization JSONObjectWithData:payloadData options:0 error:NULL];
- }
- else if (_encoding == DTBonjourDataConnectionContentTypeNSCoding)
- {
- object = [NSKeyedUnarchiver unarchiveObjectWithData:payloadData];
- }
-
- if (!object)
- {
- NSLog(@"Unable to decode object");
- }
-
- return object;
-}
-
-
-- (BOOL)_hasCompleteHeader
-{
- // find end of header, \r\n\r\n
- NSData *headerEnd = [@"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding];
-
- NSRange headerEndRange = [_data rangeOfData:headerEnd options:0 range:NSMakeRange(0, [_data length])];
-
- if (headerEndRange.location == NSNotFound)
- {
- // we don't have a complete header
- return NO;
- }
-
- // update the range
- _rangeOfHeader = NSMakeRange(0, headerEndRange.location + headerEndRange.length);
-
- return YES;
-}
-
-- (void)_decodeHeader
-{
- NSAssert(_rangeOfHeader.length>0, @"Don't decode header if range is unknown yet");
-
- NSString *string = [[NSString alloc] initWithBytesNoCopy:(void *)[_data bytes] length:_rangeOfHeader.length encoding:NSUTF8StringEncoding freeWhenDone:NO];
-
- if (!string)
- {
- NSLog(@"Error decoding header, not a valid NSString");
- return;
- }
-
- NSScanner *scanner = [NSScanner scannerWithString:string];
- scanner.charactersToBeSkipped = [NSCharacterSet whitespaceAndNewlineCharacterSet];
-
- if (![scanner scanString:@"PUT" intoString:NULL])
- {
- return;
- }
-
- NSDictionary *headers;
- if (![scanner scanBonjourConnectionHeaders:&headers])
- {
- return;
- }
-
- NSString *contentType = headers[@"Content-Type"];
- if ([contentType isEqualToString:@"application/json"])
- {
- _encoding = DTBonjourDataConnectionContentTypeJSON;
- }
- else if ([contentType isEqualToString:@"application/octet-stream"])
- {
- _encoding = DTBonjourDataConnectionContentTypeNSCoding;
- }
- else
- {
- NSLog(@"Unknown transport type: %@", contentType);
- return;
- }
-
- /*
- // unused
- NSString *classString = headers[@"Class"];
- _receivingDataClass = NSClassFromString(classString);
- */
-
- _sequenceNumber = [headers[@"Sequence-Number:"] unsignedIntegerValue];
- _contentLength = [headers[@"Content-Length"] longLongValue];
- _totalBytes = _rangeOfHeader.length + _contentLength;
-}
-
-- (NSInteger)writeToOutputStream:(NSOutputStream *)stream
-{
- // how many bytes there are still untransmitted
- NSUInteger maxLength = [_data length] - _numberOfTransferredBytes;
-
- if (!maxLength)
- {
- return 0;
- }
-
- // current write position
- const uint8_t *position = [_data bytes]+_numberOfTransferredBytes;
-
- NSInteger actuallyWritten = [stream write:position maxLength:maxLength];
-
- if (actuallyWritten>0)
- {
- _numberOfTransferredBytes += actuallyWritten;
- }
-
- return actuallyWritten;
-}
-
-- (NSInteger)readFromInputStream:(NSInputStream *)stream
-{
- uint8_t buffer[2048*8];
-
- NSUInteger maxLength;
-
- BOOL readingHeader;
-
- if (!_rangeOfHeader.length)
- {
- maxLength = 1;
- readingHeader = YES;
- }
- else
- {
- maxLength = MIN(sizeof(buffer), (_totalBytes - _numberOfTransferredBytes));
- readingHeader = NO;
- }
-
- NSInteger actuallyRead = [stream read:(uint8_t *)buffer maxLength:maxLength];
-
- if (actuallyRead > 0)
- {
- [_data appendBytes:buffer length:actuallyRead];
-
- if (readingHeader)
- {
- if ([self _hasCompleteHeader])
- {
- [self _decodeHeader];
- }
- }
-
- _numberOfTransferredBytes += actuallyRead;
- }
-
- return actuallyRead;
-}
-
-- (BOOL)isTransmissionComplete
-{
- if (!_totalBytes)
- {
- return NO;
- }
-
- return (_numberOfTransferredBytes == _totalBytes);
-}
-
-#pragma mark - Properties
-
-@synthesize numberOfTransferredBytes = _numberOfTransferredBytes;
-@synthesize totalBytes = _totalBytes;
-@synthesize sequenceNumber = _sequenceNumber;
-
-
-@end
View
149 Core/Source/DTBonjour/DTBonjourDataConnection.h
@@ -1,149 +0,0 @@
-//
-// DTBonjourDataConnection.h
-// BonjourTest
-//
-// Created by Oliver Drobnik on 01.11.12.
-// Copyright (c) 2012 Oliver Drobnik. All rights reserved.
-//
-
-#import <Foundation/Foundation.h>
-#import <CoreFoundation/CoreFoundation.h>
-
-/**
- Type of encoding to use for sending objects
- */
-typedef enum
-{
- DTBonjourDataConnectionContentTypeNSCoding = 0,
- DTBonjourDataConnectionContentTypeJSON,
-} DTBonjourDataConnectionContentType;
-
-extern NSString * DTBonjourDataConnectionErrorDomain;
-
-@class DTBonjourDataConnection, DTBonjourDataChunk;
-
-@protocol DTBonjourDataConnectionDelegate <NSObject>
-@optional
-
-// sending
-
-/**
- Called before the connection will start sending a data chunk
- @param connection The connection
- @param chunk The chunk
- */
-- (void)connection:(DTBonjourDataConnection *)connection willStartSendingChunk:(DTBonjourDataChunk *)chunk;
-
-/**
- Called multiple times while the the connection is sending a data chunk
- @param connection The connection
- @param bytesSent The number of bytes sent so far
- @param chunk The chunk
- */
-- (void)connection:(DTBonjourDataConnection *)connection didSendBytes:(NSUInteger)bytesSent ofChunk:(DTBonjourDataChunk *)chunk;
-
-/**
- Called after the connection did finish sending a data chunk
- @param connection The connection
- @param chunk The chunk
- */
-- (void)connection:(DTBonjourDataConnection *)connection didFinishSendingChunk:(DTBonjourDataChunk *)chunk;
-
-// receiving
-
-/**
- Called before the connection will start receiving a data chunk
- @param connection The connection
- @param chunk The chunk
- */
-- (void)connection:(DTBonjourDataConnection *)connection willStartReceivingChunk:(DTBonjourDataChunk *)chunk;
-
-/**
- Called multiple times while the the connection is receiving a data chunk
- @param connection The connection
- @param bytesReceived The number of bytes received so far
- @param chunk The chunk
- */
-- (void)connection:(DTBonjourDataConnection *)connection didReceiveBytes:(NSUInteger)bytesReceived ofChunk:(DTBonjourDataChunk *)chunk;
-
-/**
- Called after the connection did finish receiving a data chunk
- @param connection The connection
- @param chunk The chunk
- */
-- (void)connection:(DTBonjourDataConnection *)connection didFinishReceivingChunk:(DTBonjourDataChunk *)chunk;
-
-/**
- Called when the connection did received and decode an object
- @param connection The connection
- @param object The decoded object that was received
- */
-- (void)connection:(DTBonjourDataConnection *)connection didReceiveObject:(id)object;
-
-// connection
-
-/**
- Called when the connection was closed
- @param connection The connection
- */
-- (void)connectionDidClose:(DTBonjourDataConnection *)connection;
-@end
-
-/**
- This class represents a data connection, established via file handle or Bonjour `NSNetService`
-
- It can be used by itself to establish a connection as a client to a remote server. In the context of a `DTBonjourServer` there might be any number of live connections. In the server context you should not modify the connection delegate as the connections are owned and maintained by the server.
- */
-@interface DTBonjourDataConnection : NSObject
-
-/**
- Initializes the receiver from a native Posix file handle representing a socket.
- @param nativeSocketHandle The native socket handle to create the connection for
- */
-- (id)initWithNativeSocketHandle:(CFSocketNativeHandle)nativeSocketHandle;
-
-/**
- Initialized the receiver from a Bonjour `NSNetService`
- @param service The `NSNetService` to create the connection for
- */
-- (id)initWithService:(NSNetService *)service;
-
-/**
- Opens the connection and establishes the input and output streams.
- @returns `YES` if the connection could be established.
- */
-- (BOOL)open;
-
-/**
- Closes the connection
- */
-- (void)close;
-
-/**
- @returns `YES` if the connection is open and can be used to send or receive data
- */
-- (BOOL)isOpen;
-
-/**
- Encodes the passed object via the current sendingContentType and adds it to the output stream.
-
- Note: The return parameter does not tell if the actual sending has already taken place, only if the object could be encoded and enqueued for sending.
- @param object Can be any object that is supported by the current sending content type.
- @param error An option error output parameter
- @returns `YES` if the object was successfully encoded and enqueued for sending
- */
-- (BOOL)sendObject:(id)object error:(NSError **)error;
-
-/**
- A delegate to be informed about activities of the connection. If the connection is owned by a `DTBonjourServer` you should not modify this property.
- */
-@property (nonatomic, weak) id <DTBonjourDataConnectionDelegate> delegate;
-
-/**
- The type of how objects are to be encoded for transit. The default is to encode with NSCoding, JSON is also available as an option.
-
- Note: JSON can only encode `NSArray` and `NSDictionary` root objects.
- */
-@property (nonatomic, assign) DTBonjourDataConnectionContentType sendingContentType;
-
-@end
View
320 Core/Source/DTBonjour/DTBonjourDataConnection.m
@@ -1,320 +0,0 @@
-//
-// DTBonjourDataConnection.m
-// BonjourTest
-//
-// Created by Oliver Drobnik on 01.11.12.
-// Copyright (c) 2012 Oliver Drobnik. All rights reserved.
-//
-
-#import "DTBonjourDataConnection.h"
-#import "DTBonjourDataChunk.h"
-#import "NSScanner+DTBonjour.h"
-
-#import <Foundation/NSJSONSerialization.h>
-
-NSString * DTBonjourDataConnectionErrorDomain = @"DTBonjourDataConnection";
-
-@interface DTBonjourDataConnection () <NSStreamDelegate>
-
-@end
-
-@interface DTBonjourDataChunk (private)
-
-// make read-only property assignable
-@property (nonatomic, assign) NSUInteger sequenceNumber;
-
-@end
-
-typedef enum
-{
- DTBonjourDataConnectionExpectedDataTypeNothing,
- DTBonjourDataConnectionExpectedDataTypeHeader,
- DTBonjourDataConnectionExpectedDataTypeData
-} DTBonjourDataConnectionExpectedDataType;
-
-@implementation DTBonjourDataConnection
-{
- NSInputStream *_inputStream;
- NSOutputStream *_outputStream;
-
- NSMutableArray *_outputQueue;
- DTBonjourDataChunk *_receivingChunk;
-
- NSUInteger _chunkSequenceNumber;
-
- __weak id <DTBonjourDataConnectionDelegate> _delegate;
-}
-
-- (id)initWithNativeSocketHandle:(CFSocketNativeHandle)nativeSocketHandle
-{
- self = [super init];
-
- if (self)
- {
- CFReadStreamRef readStream = NULL;
- CFWriteStreamRef writeStream = NULL;
- CFStreamCreatePairWithSocket(kCFAllocatorDefault, nativeSocketHandle, &readStream, &writeStream);
-
- if (readStream && writeStream)
- {
- CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
- CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
-
- _inputStream = (__bridge_transfer NSInputStream *)readStream;
- _outputStream = (__bridge_transfer NSOutputStream *)writeStream;
-
- _outputQueue = [[NSMutableArray alloc] init];
- }
- else
- {
- close(nativeSocketHandle);
-
- return nil;
- }
- }
-
- return self;
-}
-
-- (id)initWithService:(NSNetService *)service
-{
- self = [super init];
-
- if (self)
- {
- if (![service getInputStream:&_inputStream outputStream:&_outputStream])
- {
- return nil;
- }
-
- _outputQueue = [[NSMutableArray alloc] init];
- }
-
- return self;
-}
-
-- (void)dealloc
-{
- _delegate = nil;
- [self close];
-}
-
-- (BOOL)open
-{
- [_inputStream setDelegate:self];
- [_outputStream setDelegate:self];
- [_inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
- [_outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
- [_inputStream open];
- [_outputStream open];
-
- return YES;
-}
-
-- (void)close
-{
- if (!_inputStream&&!_outputStream)
- {
- return;
- }
-
- [_inputStream setDelegate:nil];
- [_outputStream setDelegate:nil];
- [_inputStream close];
- [_outputStream close];
- [_inputStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
- [_outputStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
- _inputStream = nil;
- _outputStream = nil;
-
- if ([_delegate respondsToSelector:@selector(connectionDidClose:)])
- {
- [_delegate connectionDidClose:self];
- }
-}
-
-- (BOOL)isOpen
-{
- return (_inputStream&&_outputStream);
-}
-
-- (void)_startOutput
-{
- if (![_outputQueue count])
- {
- return;
- }
-
- DTBonjourDataChunk *chunk = _outputQueue[0];
-
- if (chunk.numberOfTransferredBytes==0)
- {
- // nothing sent yet
- if ([_delegate respondsToSelector:@selector(connection:willStartSendingChunk:)])
- {
- [_delegate connection:self willStartSendingChunk:chunk];
- }
- }
-
- NSUInteger writtenBytes = [chunk writeToOutputStream:_outputStream];
-
- if (writtenBytes > 0)
- {
- if ([_delegate respondsToSelector:@selector(connection:didSendBytes:ofChunk:)])
- {
- [_delegate connection:self didSendBytes:writtenBytes ofChunk:chunk];
- }
-
- // If we didn't write all the bytes we'll continue writing them in response to the next
- // has-space-available event.
-
- if ([chunk isTransmissionComplete])
- {
- [_outputQueue removeObject:chunk];
-
- if ([_delegate respondsToSelector:@selector(connection:didFinishSendingChunk:)])
- {
- [_delegate connection:self didFinishSendingChunk:chunk];
- }
- }
- }
- else
- {
- // A non-positive result from -write:maxLength: indicates a failure of some form; in this
- // simple app we respond by simply closing down our connection.
- [self close];
- }
-}
-
-#pragma mark - Public Interface
-
-- (BOOL)sendObject:(id)object error:(NSError **)error
-{
- if (![self isOpen])
- {
- if (error)
- {
- NSDictionary *userInfo = @{NSLocalizedDescriptionKey:@"Connection is not open"};
- *error = [NSError errorWithDomain:@"DTBonjourDataConnection" code:1 userInfo:userInfo];
- }
-
- return NO;
- }
-
- DTBonjourDataChunk *newChunk = [[DTBonjourDataChunk alloc] initWithObject:object encoding:self.sendingContentType error:error];
-
- if (!newChunk)
- {
- return NO;
- }
-
- newChunk.sequenceNumber = _chunkSequenceNumber;
-
- BOOL queueWasEmpty = (![_outputQueue count]);
-
- [_outputQueue addObject:newChunk];
-
- if (queueWasEmpty && _outputStream.streamStatus == NSStreamStatusOpen)
- {
- [self _startOutput];
- }
-
- return YES;
-}
-
-#pragma mark - NSStream Delegate
-
-- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)streamEvent
-{
- switch(streamEvent)
- {
- case NSStreamEventOpenCompleted:
- {
- break;
- }
-
- case NSStreamEventHasBytesAvailable:
- {
- if (!_receivingChunk)
- {
- // start reading a new chunk
- _receivingChunk = [[DTBonjourDataChunk alloc] initForReading];
-
- // nothing received yet
- if ([_delegate respondsToSelector:@selector(connection:willStartReceivingChunk:)])
- {
- [_delegate connection:self willStartReceivingChunk:_receivingChunk];
- }
- }
-
- // continue reading
- NSInteger actuallyRead = [_receivingChunk readFromInputStream:_inputStream];
-
- if (actuallyRead<0)
- {
- [self close];
- break;
- }
-
- if ([_delegate respondsToSelector:@selector(connection:didReceiveBytes:ofChunk:)])
- {
- [_delegate connection:self didReceiveBytes:actuallyRead ofChunk:_receivingChunk];
- }
-
- if ([_receivingChunk isTransmissionComplete])
- {
- if ([_delegate respondsToSelector:@selector(connection:didFinishReceivingChunk:)])
- {
- [_delegate connection:self didFinishReceivingChunk:_receivingChunk];
- }
-
- if ([_delegate respondsToSelector:@selector(connection:didReceiveObject:)])
- {
- id decodedObject = [_receivingChunk decodedObject];
-
- [_delegate connection:self didReceiveObject:decodedObject];
- }
-
- // we're done with this chunk
- _receivingChunk = nil;
- }
-
- break;
- }
-
- case NSStreamEventErrorOccurred:
- {
- NSLog(@"Error occurred: %@", [aStream.streamError localizedDescription]);
- }
-
- case NSStreamEventEndEncountered:
- {
- [self close];
-
- break;
- }
-
- case NSStreamEventHasSpaceAvailable:
- {
- if ([_outputQueue count])
- {
- [self _startOutput];
- }
-
- break;
- }
-
- default:
- {
- // do nothing
- break;
- }
- }
-}
-
-
-#pragma mark - Properties
-
-@synthesize delegate = _delegate;
-
-@end
View
89 Core/Source/DTBonjour/DTBonjourServer.h
@@ -1,89 +0,0 @@
-//
-// DTBonjourServer.h
-// BonjourTest
-//
-// Created by Oliver Drobnik on 01.11.12.
-// Copyright (c) 2012 Oliver Drobnik. All rights reserved.
-//
-
-#import "DTBonjourDataConnection.h"
-
-@class DTBonjourServer;
-
-/**
- The delegate protocol by which the DTBonjourServer communicates about events
- */
-@protocol DTBonjourServerDelegate <NSObject>
-@optional
-
-/**
- Callend when a new incoming connection was accepted by the server.
- @param server The server
- @param connection The connection that was accepted
- */
-- (void)bonjourServer:(DTBonjourServer *)server didAcceptConnection:(DTBonjourDataConnection *)connection;
-
-/**
- Called when the server received a new object on a given connection
- @param server The server
- @param object The decoded object that was received
- @param connection The individual connection that it was received on
- */
-- (void)bonjourServer:(DTBonjourServer *)server didReceiveObject:(id)object onConnection:(DTBonjourDataConnection *)connection;
-@end
-
-/**
- This class represents a service that clients can connect to. It owns its inbound connections and thus you should never modify the delegate of the individual data connections.
-
- On iOS the server is automatically stopped if the app enters the background and restarted when the app comes back into the foreground.
- */
-@interface DTBonjourServer : NSObject <DTBonjourDataConnectionDelegate>
-
-/**
- Creates a server instances with the given bonjour type, e.g. "_servicename._tcp"
- @param bonjourType The full service type string
- */
-- (id)initWithBonjourType:(NSString *)bonjourType;
-
-/**
- Starts up the server, prepares it to be connected to and publishes the service via Bonjour
- @returns `YES` if the startup was successful
- */
-- (BOOL)start;
-
-/**
- Stops the service
- */
-- (void)stop;
-
-/**
- Sends the object to all currently connected clients.
-
- Note: any errors will be ignored. If you require finer-grained control then you should iterate over the individual connections.
- @param object The object that will be encoded and sent to all clients
- */
-- (void)broadcastObject:(id)object;
-
-/**
- The delegate that will be informed about activities happening on the server.
- */
-@property (nonatomic, weak) id <DTBonjourServerDelegate> delegate;
-
-/**
- The actual port bound to, valid after -start
- */
-@property (nonatomic, assign, readonly ) NSUInteger port;
-
-/**
- The currently connected inbound DTBonjourDataConnection instances.
- */
-@property (nonatomic, readonly) NSSet *connections;
-
-/**
- The TXT Record attached to the Bonjour service.
-
- Updating this property while the server is running will update the broadcast TXTRecord. The server has its own instance of the TXTRecord so that it can be set even before calling start.
- */
-@property (nonatomic, strong) NSDictionary *TXTRecord;
-
-@end
View
433 Core/Source/DTBonjour/DTBonjourServer.m
@@ -1,433 +0,0 @@
-//
-// DTBonjourServer.m
-// BonjourTest
-//
-// Created by Oliver Drobnik on 01.11.12.
-// Copyright (c) 2012 Oliver Drobnik. All rights reserved.
-//
-
-#import "DTBonjourServer.h"
-
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <unistd.h>
-#include <arpa/inet.h>
-
-#import <CoreFoundation/CoreFoundation.h>
-
-#import "DTBonjourDataConnection.h"
-#import "DTBonjourDataChunk.h"
-
-#if TARGET_OS_IPHONE
-#import <UIKit/UIKit.h>
-#endif
-
-@interface DTBonjourServer() <NSNetServiceDelegate, DTBonjourDataConnectionDelegate>
-
-- (void)_acceptConnection:(CFSocketNativeHandle)nativeSocketHandle;
-
-@end
-
-// call-back function for incoming connections
-static void ListeningSocketCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
-{
- DTBonjourServer *server = (__bridge DTBonjourServer *)info;
-
- const struct sockaddr *sa = (const struct sockaddr *)CFDataGetBytePtr(address);
-
- sa_family_t family = sa->sa_family;
-
- NSString *ipString = nil;
- NSString *familyString = nil;
- NSUInteger port = 0;
-
- if (family == AF_INET)
- {
- familyString = @"IPv4";
-
- struct sockaddr_in addr4;
- CFDataGetBytes(address, CFRangeMake(0, sizeof(addr4)), (void *)&addr4);
-
- char str[INET_ADDRSTRLEN];
- inet_ntop(AF_INET, &(addr4.sin_addr), str, INET_ADDRSTRLEN);
- ipString = [[NSString alloc] initWithBytes:str length:strlen(str) encoding:NSUTF8StringEncoding];
-
- port = ntohs(addr4.sin_port);
- }
- else if (family == AF_INET6)
- {
- familyString = @"IPv6";
-
- struct sockaddr_in6 addr6;
- CFDataGetBytes(address, CFRangeMake(0, sizeof(addr6)), (void *)&addr6);
-
- char str[INET6_ADDRSTRLEN];
- inet_ntop(AF_INET6, &(addr6.sin6_addr), str, INET6_ADDRSTRLEN);
- ipString = [[NSString alloc] initWithBytes:str length:strlen(str) encoding:NSUTF8StringEncoding];
-
- port = ntohs(addr6.sin6_port);
- }
-
- NSLog(@"Accepting %@ connection from %@ on port %d", familyString, ipString, (int)port);
-
- // For an accept callback, the data parameter is a pointer to a CFSocketNativeHandle.
- [server _acceptConnection:*(CFSocketNativeHandle *)data];
-}
-
-
-@implementation DTBonjourServer
-{
- NSNetService *_service;
- NSDictionary *_TXTRecord;
-
- CFSocketRef _ipv4socket;
- CFSocketRef _ipv6socket;
-
- NSUInteger _port; // used port, assigned during start
-
- NSMutableSet *_connections;
- NSString *_bonjourType;
-
- __weak id <DTBonjourServerDelegate> _delegate;
-}
-
-- (id)init
-{
- self = [super init];
-
- if (self)
- {
- if (![_bonjourType length])
- {
- return nil;
- }
-
- _connections = [[NSMutableSet alloc] init];
-
-#if TARGET_OS_IPHONE
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
-#endif
- }
-
- return self;
-}
-
-- (id)initWithBonjourType:(NSString *)bonjourType
-{
- if (!bonjourType)
- {
- return nil;
- }
-
- _bonjourType = bonjourType;
-
- self = [self init];
-
- if (self)
- {
-
- }
-
- return self;
-}
-
-- (void)dealloc
-{
- [[NSNotificationCenter defaultCenter] removeObserver:self];
-
- _delegate = nil;
-
- [self stop];
-}
-
-- (BOOL)start
-{
- assert(_ipv4socket == NULL && _ipv6socket == NULL); // don't call -start twice!
-
- CFSocketContext socketCtxt = {0, (__bridge void *) self, NULL, NULL, NULL};
- _ipv4socket = CFSocketCreate(kCFAllocatorDefault, AF_INET, SOCK_STREAM, 0, kCFSocketAcceptCallBack, &ListeningSocketCallback, &socketCtxt);
- _ipv6socket = CFSocketCreate(kCFAllocatorDefault, AF_INET6, SOCK_STREAM, 0, kCFSocketAcceptCallBack, &ListeningSocketCallback, &socketCtxt);
-
- if (NULL == _ipv4socket || NULL == _ipv6socket)
- {
- [self stop];
- return NO;
- }
-
- static const int yes = 1;
- (void) setsockopt(CFSocketGetNative(_ipv4socket), SOL_SOCKET, SO_REUSEADDR, (const void *) &yes, sizeof(yes));
- (void) setsockopt(CFSocketGetNative(_ipv6socket), SOL_SOCKET, SO_REUSEADDR, (const void *) &yes, sizeof(yes));
-
- // Set up the IPv4 listening socket; port is 0, which will cause the kernel to choose a port for us.
- struct sockaddr_in addr4;
- memset(&addr4, 0, sizeof(addr4));
- addr4.sin_len = sizeof(addr4);
- addr4.sin_family = AF_INET;
- addr4.sin_port = htons(0);
- addr4.sin_addr.s_addr = htonl(INADDR_ANY);
-
- if (kCFSocketSuccess != CFSocketSetAddress(_ipv4socket, (__bridge CFDataRef) [NSData dataWithBytes:&addr4 length:sizeof(addr4)]))
- {
- [self stop];
- return NO;
- }
-
- // Now that the IPv4 binding was successful, we get the port number
- // -- we will need it for the IPv6 listening socket and for the NSNetService.
- NSData *addr = (__bridge_transfer NSData *)CFSocketCopyAddress(_ipv4socket);
- assert([addr length] == sizeof(struct sockaddr_in));
- _port = ntohs(((const struct sockaddr_in *)[addr bytes])->sin_port);
-
- // Set up the IPv6 listening socket.
- struct sockaddr_in6 addr6;
- memset(&addr6, 0, sizeof(addr6));
- addr6.sin6_len = sizeof(addr6);
- addr6.sin6_family = AF_INET6;
- addr6.sin6_port = htons(self.port);
- memcpy(&(addr6.sin6_addr), &in6addr_any, sizeof(addr6.sin6_addr));
- if (kCFSocketSuccess != CFSocketSetAddress(_ipv6socket, (__bridge CFDataRef) [NSData dataWithBytes:&addr6 length:sizeof(addr6)]))
- {
- [self stop];
- return NO;
- }
-
- // Set up the run loop sources for the sockets.
- CFRunLoopSourceRef source4 = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _ipv4socket, 0);
- CFRunLoopAddSource(CFRunLoopGetCurrent(), source4, kCFRunLoopCommonModes);
- CFRelease(source4);
-
- CFRunLoopSourceRef source6 = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _ipv6socket, 0);
- CFRunLoopAddSource(CFRunLoopGetCurrent(), source6, kCFRunLoopCommonModes);
- CFRelease(source6);
-
- if (_ipv6socket)
- {
- CFSocketInvalidate(_ipv6socket);
- CFRelease(_ipv6socket);
- _ipv6socket = NULL;
- }
-
- assert(self.port > 0 && self.port < 65536);
- _service = [[NSNetService alloc] initWithDomain:@"" type:_bonjourType name:@"" port:(int)_port];
- _service.delegate = self;
-
- if (_TXTRecord)
- {
- [_service setTXTRecordData:[NSNetService dataFromTXTRecordDictionary:_TXTRecord]];
- }
-
- [_service publishWithOptions:0];
-
- return YES;
-}
-
-/*
- // this is the way to create the sockets on the Posix-level
-- (BOOL)start
-{
- CFSocketContext context = {0, (__bridge void *)self, NULL, NULL, NULL};
-
- // create IPv4 socket
- int fd4 = socket(AF_INET, SOCK_STREAM, 0);
-
- // allow for reuse of local address
- static const int yes = 1;
- int err = setsockopt(fd4, SOL_SOCKET, SO_REUSEADDR, (const void *) &yes, sizeof(yes));
-
- // a structure for the socket address
- struct sockaddr_in sin;
- memset(&sin, 0, sizeof(sin));
- sin.sin_family = AF_INET;
- sin.sin_len = sizeof(sin);
- sin.sin_port = htons(0); // asks kernel for arbitrary port number
-
- err = bind(fd4, (const struct sockaddr *) &sin, sin.sin_len);
-
- socklen_t addrLen = sizeof(sin);
- err = getsockname(fd4, (struct sockaddr *)&sin, &addrLen);
- err = listen(fd4, 5);
-
- // should have a port number now
- _port = sin.sin_port;
-
- if (!_port)
- {
- return NO;
- }
-
- // create a CFSocket for the file descriptor
- _ipv4socket = CFSocketCreateWithNative(NULL, fd4, kCFSocketAcceptCallBack, ListeningSocketCallback, &context);
-
- // create IPv6 socket
- int fd6 = socket(AF_INET6, SOCK_STREAM, 0);
-
- int one = 1;
- err = setsockopt(fd6, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
- err = setsockopt(fd6, SOL_SOCKET, SO_REUSEADDR, (const void *) &yes, sizeof(yes));
-
- struct sockaddr_in sin6;
- memset(&sin6, 0, sizeof(sin6));
- sin6.sin_family = AF_INET6;
- sin6.sin_len = sizeof(sin6);
- sin6.sin_port = sin.sin_port; // uses same port as IPv4
-
- err = bind(fd6, (const struct sockaddr *) &sin6, sin6.sin_len);
-
- err = listen(fd6, 5);
-
- // create a CFSocket for the file descriptor
- _ipv6socket = CFSocketCreateWithNative(NULL, fd6, kCFSocketAcceptCallBack, ListeningSocketCallback, &context);
-
- // Set up the run loop sources for the sockets.
- CFRunLoopSourceRef source4 = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _ipv4socket, 0);
- CFRunLoopAddSource(CFRunLoopGetCurrent(), source4, kCFRunLoopCommonModes);
- CFRelease(source4);
-
- CFRunLoopSourceRef source6 = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _ipv6socket, 0);
- CFRunLoopAddSource(CFRunLoopGetCurrent(), source6, kCFRunLoopCommonModes);
- CFRelease(source6);
- _service = [[NSNetService alloc] initWithDomain:@"" // use all available domains
- type:_bonjourType
- name:@"" // uses default name of system
- port:ntohs(_port)];
-
- [_service scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
- _service.delegate = self;
-
- [_service publish];
-
-#if TARGET_OS_IPHONE
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
-#endif
-
- return YES;
-}
- */
-
-- (void)stop
-{
- // stop the bonjour advertising
- [_service stop];
- _service = nil;
-
- // Closes all the open connections. The EchoConnectionDidCloseNotification notification will ensure
- // that the connection gets removed from the self.connections set. To avoid mututation under iteration
- // problems, we make a copy of that set and iterate over the copy.
- for (DTBonjourDataConnection *connection in [_connections copy])
- {
- [connection close];
- }
-
- if (_ipv4socket)
- {
- CFSocketInvalidate(_ipv4socket);
- CFRelease(_ipv4socket);
- _ipv4socket = NULL;
- }
-
- if (_ipv6socket)
- {
- CFSocketInvalidate(_ipv6socket);
- CFRelease(_ipv6socket);
- _ipv6socket = NULL;
- }
-}
-
-- (void)_acceptConnection:(CFSocketNativeHandle)nativeSocketHandle
-{
- DTBonjourDataConnection *newConnection = [[DTBonjourDataConnection alloc] initWithNativeSocketHandle:nativeSocketHandle];
-
- //DTBonjourDataConnection *newConnection = [[DTBonjourDataConnection alloc] initWithService:service];
-
- newConnection.delegate = self;
- [newConnection open];
- [_connections addObject:newConnection];
-
- if ([_delegate respondsToSelector:@selector(bonjourServer:didAcceptConnection:)])
- {
- [_delegate bonjourServer:self didAcceptConnection:newConnection];
- }
-}
-
-- (void)broadcastObject:(id)object
-{
- for (DTBonjourDataConnection *connection in _connections)
- {
- NSError *error;
-
- if (![connection sendObject:object error:&error])
- {
- NSLog(@"%@", [error localizedDescription]);
- }
- }
-}
-
-#pragma mark - NSNetService Delegate
-
-- (void)netService:(NSNetService *)sender didNotPublish:(NSDictionary *)errorDict
-{
- NSLog(@"Error publishing: %@", errorDict);
-}
-
-- (void)netServiceDidPublish:(NSNetService *)sender
-{
- NSLog(@"My name: %@ port: %d", [sender name], (int)sender.port);
-}
-
-- (void)netServiceDidStop:(NSNetService *)sender
-{
- NSLog(@"Bonjour Service shut down");
-}
-
-#pragma mark - DTBonjourDataConnection Delegate
-- (void)connection:(DTBonjourDataConnection *)connection didReceiveObject:(id)object
-{
- if ([_delegate respondsToSelector:@selector(bonjourServer:didReceiveObject:onConnection:)])
- {
- [_delegate bonjourServer:self didReceiveObject:object onConnection:connection];
- }
-}
-
-- (void)connectionDidClose:(DTBonjourDataConnection *)connection
-{
- [_connections removeObject:connection];
-}
-
-#pragma mark - Notifications
-
-- (void)appDidEnterBackground:(NSNotification *)notification
-{
- [self stop];
-}
-
-- (void)appWillEnterForeground:(NSNotification *)notification
-{
- [self start];
-}
-
-#pragma mark - Properties
-
-- (NSSet *)connections
-{
- // make a copy to be non-mutable
- return [_connections copy];
-}
-
-- (void)setTXTRecord:(NSDictionary *)TXTRecord
-{
- _TXTRecord = TXTRecord;
-
- // update service if it is running
- if (_service)
- {
- [_service setTXTRecordData:[NSNetService dataFromTXTRecordDictionary:_TXTRecord]];
- }
-}
-
-@synthesize TXTRecord = _TXTRecord;
-@synthesize delegate = _delegate;
-@synthesize port = _port;
-
-@end
View
22 Core/Source/DTBonjour/NSScanner+DTBonjour.h
@@ -1,22 +0,0 @@
-//
-// NSScanner+DTBonjour.h
-// DTFoundation
-//
-// Created by Oliver Drobnik on 15.11.12.
-// Copyright (c) 2012 Cocoanetics. All rights reserved.
-//
-
-/**
- Category extension for `NSScanner` to deal with scanning the headers used by DTBonjour
- */
-
-@interface NSScanner (DTBonjour)
-
-/**
- The receiver scans for DTBonjour headers.
- @param headers The output dictionary with the scanned headers
- @returns `YES` if successfully scanned the headers
- */
-- (BOOL)scanBonjourConnectionHeaders:(NSDictionary **)headers;
-
-@end
View
47 Core/Source/DTBonjour/NSScanner+DTBonjour.m
@@ -1,47 +0,0 @@
-//
-// NSScanner+DTBonjour.m
-// DTFoundation
-//
-// Created by Oliver Drobnik on 15.11.12.
-// Copyright (c) 2012 Cocoanetics. All rights reserved.
-//
-
-#import "NSScanner+DTBonjour.h"
-
-@implementation NSScanner (DTBonjour)
-
-- (BOOL)scanBonjourConnectionHeaders:(NSDictionary **)headers
-{
- NSString *headerName;
-
- NSUInteger positionBeforeScanning = [self scanLocation];
-
- NSMutableDictionary *tmpDict = [NSMutableDictionary dictionary];
-
- while (!self.isAtEnd)
- {
- if (![self scanUpToString:@":" intoString:&headerName])
- {
- self.scanLocation = positionBeforeScanning;
- return NO;
- }
-
- // skip colon
- [self scanString:@":" intoString:NULL];
-
- NSString *headerValue;
- if ([self scanUpToCharactersFromSet:[NSCharacterSet newlineCharacterSet] intoString:&headerValue])
- {
- tmpDict[headerName] = headerValue;
- }
- }
-
- if (headers)
- {
- *headers = [tmpDict copy];
- }
-
- return YES;
-}
-
-@end
View
74 Core/Source/DTDownload/DTCachedFile.h
@@ -1,74 +0,0 @@
-//
-// DTCachedFile.h
-// DTFoundation
-//
-// Created by Oliver Drobnik on 4/20/12.
-// Copyright (c) 2012 Cocoanetics. All rights reserved.
-//
-
-#import <Foundation/Foundation.h>
-#import <CoreData/CoreData.h>
-
-/**
- A managed object representing meta information on a cached file.
-
- You usually never access these directly, they are used internally by <DTDownloadCache>
- */
-@interface DTCachedFile : NSManagedObject
-
-/**
- Remote URL of the receiver
- */
-@property (nonatomic, retain) NSString *remoteURL;
-
-/**
- The data in the file represented by the receiver
- */
-@property (nonatomic, retain) NSData *fileData;
-
-/**
- The last time when the receiver was accessed
- */
-@property (nonatomic, retain) NSDate *lastAccessDate;
-
-/**
- The last time the receiver was modified
- */
-@property (nonatomic, retain) NSDate *lastModifiedDate;
-
-/**
- The time when the receiver is set to expire. Note: Not yet implemented.
- */
-@property (nonatomic, retain) NSDate *expirationDate;
-
-/**
- The content MIME type of the receiver
- */
-@property (nonatomic, retain) NSString *contentType;
-
-/**
- The file size in bytes of the receiver
- */
-@property (nonatomic, retain) NSNumber *fileSize;
-
-/**
- Flag to force the loading of the file, even if data is cached
- */
-@property (nonatomic, retain) NSNumber *forceLoad;
-
-/**
-Abort a download if the ETag and/or last modified are the same
- */
-@property (nonatomic, retain) NSNumber *abortDownloadIfNotChanged;
-
-/**
- Transient flag to show that this is already being downloaded
- */
-@property (nonatomic, retain) NSNumber *isLoading;
-
-/**
- The ETag of the receiver if the remote host did return one
- */
-@property (nonatomic, retain) NSString *entityTagIdentifier;
-
-@end
View
26 Core/Source/DTDownload/DTCachedFile.m
@@ -1,26 +0,0 @@
-//
-// DTCachedFile.m
-// DTFoundation
-//
-// Created by Oliver Drobnik on 4/20/12.
-// Copyright (c) 2012 Cocoanetics. All rights reserved.
-//
-
-#import "DTCachedFile.h"
-
-
-@implementation DTCachedFile
-
-@dynamic remoteURL;
-@dynamic fileData;
-@dynamic lastAccessDate;
-@dynamic lastModifiedDate;
-@dynamic expirationDate;
-@dynamic contentType;
-@dynamic fileSize;
-@dynamic forceLoad;
-@dynamic isLoading;
-@dynamic abortDownloadIfNotChanged;
-@dynamic entityTagIdentifier;
-
-@end
View
231 Core/Source/DTDownload/DTDownload.h
@@ -1,231 +0,0 @@
-//
-// DTDownload.h
-// DTFoundation
-//
-// Created by Oliver Drobnik on 8/6/10.
-// Copyright 2010 Drobnik.com. All rights reserved.
-//
-
-// notifications
-extern NSString * const DTDownloadDidStartNotification;
-extern NSString * const DTDownloadDidFinishNotification;
-extern NSString * const DTDownloadDidCancelNotification;
-extern NSString * const DTDownloadProgressNotification;
-
-
-@class DTDownload;
-
-// block-based response handler, called after headers were received. Returning YES via shouldCancel cancels the download
-typedef void (^DTDownloadResponseHandler)(NSDictionary *headers, BOOL *shouldCancel);
-
-// block-based completion handler, called once the download has finished, in case of error path is nil and error is set
-typedef void (^DTDownloadCompletionHandler)(NSString *path, NSError *error);
-
-
-/**
- Methods that a delegate of a download object is being queried with.
- */
-
-@protocol DTDownloadDelegate <NSObject>
-
-@optional
-
-/**
- Sent by the download object to the delegate to inquire if a download that can be resumed should continue. Return `NO` if the download should startNext from the beginning.
-
- @param download A download object.
- */
-- (BOOL)shouldResumeDownload:(DTDownload *)download;
-
-/**
- Sent by the download object to inform the delegate about its progress.
-
- @param download A download object.
- @param downloadedBytes The number of bytes that were downloaded so far.
- @param totalBytes The number of total bytes to be downloaded.
- @param speed A rough estimate of the current download speed.
- */
-- (void)download:(DTDownload *)download downloadedBytes:(long long)downloadedBytes ofTotalBytes:(long long)totalBytes withSpeed:(float)speed;
-
-/**
- Sent by the download object to the delegate when only a HEAD was requested and the request is done.
-
- @param download A download object.
- */
-- (void)downloadDidFinishHEAD:(DTDownload *)download;
-
-
-/**
- Sent by the download object to the delegate when the download was been aborted due to failure.
-
- @param download A download object.
- @param error An error object that contains information about what caused the failure.
- */
-- (void)download:(DTDownload *)download didFailWithError:(NSError *)error;
-
-/**
- Sent by the download object to the delegate when the download as completed successfully.
-
- @param download A download object.
- @param path The file path to the downloaded file
- */
-- (void)download:(DTDownload *)download didFinishWithFile:(NSString *)path;
-
-/**
- Sent by the download object to the delegate if the download was in progress but got cancelled
-
- @param download A download object.
- */
-- (void)downloadDidCancel:(DTDownload *)download;
-
-@end
-
-
-
-/**
- A Class that represents a download of a file from a remote server. It also supports only getting a HEAD on the given URL and optionally resume an interrupted download
- */
-
-@interface DTDownload : NSObject
-
-/**
- Returns the URL that is being downloaded by the receiver.
- */
-@property (nonatomic, strong, readonly) NSURL *URL;
-
-/**
- Returns the entity tag of the downloading file.
- */
-@property (nonatomic, strong, readonly) NSString *downloadEntityTag;
-
-/**
- Returns the MIME type of the downloading file.
- */
-@property (nonatomic, strong, readonly) NSString *contentType;
-
-/**
- Returns the number expected content bytes of the downloading file.
-
- If the headers did not specify a content length to expect then this value is -1
- */
-@property (nonatomic, assign, readonly) long long expectedContentLength;
-
-/**
- Returns the last modified date of the downloading file.
- */
-@property (nonatomic, strong, readonly) NSDate *lastModifiedDate;
-
-
-/**
- Use to set or retrieve an object that provides a context for the download.
- */
-@property (nonatomic, strong) id context;
-
-/**
- Returns the receiver’s delegate.
-
- @see delegate
- */
-@property (nonatomic, assign) id <DTDownloadDelegate> delegate;
-
-/**-------------------------------------------------------------------------------------
- @name Initializing a Download Object
- ---------------------------------------------------------------------------------------
- */
-
-/** Creates a download for a given URL.
-
- @param url A remote URL
- @returns An initialized download object
- */
-- (id)initWithURL:(NSURL *)url;
-
-
-/** Creates a download for a given URL and stores the result to the destination path
- with the filename that provided by the webserver. The the webserver does provide a filename then the
- last path component of the URL is used.
-
- @param url A remote URL
- @param destinationPath where the downloaded file should be stored
- @returns An initialized download object
- */
-- (id)initWithURL:(NSURL *)url withDestinationPath:(NSString *)destinationPath;
-
-
-/** Creates a download for a given URL and stores the result to the file
-
- @param url A remote URL
- @param destinationPath where the downloaded file should be stored
- @returns An initialized download object
- */
-- (id)initWithURL:(NSURL *)url withDestinationFile:(NSString *)destinationFile;
-
-/**
-* Creates a download for a given URL at the given path
-* If a previous uncompleted download at the destination location exists then the download is tried to resumed,
-* otherwise a new full download is performed.
-*
-* @return a new DTDownload instance
-*/
-+ (DTDownload *)downloadForURL:(NSURL *)URL atPath:(NSString *)path;
-
-
-/**-------------------------------------------------------------------------------------
- @name Starting the Download
- ---------------------------------------------------------------------------------------
- */
-
-/**
- Starts or Resumes a download for a given URL.
-
- @param shouldResume Specifies if the download should be resumed if possible
- @returns An initialized download object
- */
-- (void)start;
-
-/**
- Starts a HEAD request for the given URL. This retrieves the headers and not the body of the document.
- */
-- (void)startHEAD;
-
-/**
- Stops a download in progress
- */
-- (void)stop;
-
-/**
- Determines if the download is currently in progress
- */
-- (BOOL)isRunning;
-
-/**
- * Removes the downloaded file or the incomplete file if the download is currently running.
- * Note: Also the download is cancelled if necessary
- */
-- (void)cleanup;
-
-/**
-* @return YES if the download can be resumed, otherwise no
-*/
-- (BOOL)canResume;
-
-/**-------------------------------------------------------------------------------------
- @name Block Handlers
- ---------------------------------------------------------------------------------------
- */
-
-/**
- Sets the block to execute as soon as the HTTP response has been received and the headers are available.
- */
-@property (nonatomic, copy) DTDownloadResponseHandler responseHandler;
-
-
-/**
- Sets the block to execute as soon as the download has completed.
- */
-@property (nonatomic, copy) DTDownloadCompletionHandler completionHandler;
-
-
-
-
-@end
View
677 Core/Source/DTDownload/DTDownload.m
@@ -1,677 +0,0 @@
-//
-// DTDownload.m
-// DTFoundation
-//
-// Created by Oliver Drobnik on 8/6/10.
-// Copyright 2010 Drobnik.com. All rights reserved.
-//
-
-#import "DTDownload.h"
-
-NSString *const DTDownloadDidStartNotification = @"DTDownloadDidStartNotification";
-NSString *const DTDownloadDidFinishNotification = @"DTDownloadDidFinishNotification";
-NSString *const DTDownloadDidCancelNotification = @"DTDownloadDidCancelNotification";
-NSString *const DTDownloadProgressNotification = @"DTDownloadProgressNotification";
-
-static NSString *const DownloadEntryErrorCodeDictionaryKey = @"DownloadEntryErrorCodeDictionaryKey";
-static NSString *const DownloadEntryErrorDomainDictionaryKey = @"DownloadEntryErrorDomainDictionaryKey";
-static NSString *const DownloadEntryPath = @"DownloadEntryPath";
-static NSString *const DownloadEntryProgressBytesSoFar = @"DownloadEntryProgressBytesSoFar";
-static NSString *const DownloadEntryProgressTotalToLoad = @"DownloadEntryProgressTotalToLoad";
-static NSString *const DownloadEntryResumeInformation = @"DownloadEntryResumeInformation";
-static NSString *const DownloadEntryURL = @"DownloadEntryURL";
-static NSString *const NSURLDownloadBytesReceived = @"NSURLDownloadBytesReceived";
-static NSString *const NSURLDownloadEntityTag = @"NSURLDownloadEntityTag";
-
-@interface DTDownload () <NSURLConnectionDelegate>
-
-@property(nonatomic, retain) NSDate *lastPacketTimestamp;
-
-- (void)storeDownloadInfo;
-
-- (void)_completeWithSuccess;
-
-- (void)_completeWithError:(NSError *)error;
-
-@end
-
-@implementation DTDownload
-{
- NSURL *_URL;
- NSString *_downloadEntityTag;
- NSDate *_lastModifiedDate;
-
- NSString *_destinationPath;
- NSString *_destinationFileName;
-
- // downloading
- NSURLConnection *_urlConnection;
- NSMutableData *_receivedData;
-
- NSDate *_lastPacketTimestamp;
- float _previousSpeed;
-
- long long _receivedBytes;
- long long _expectedContentLength;
- long long _resumeFileOffset;
-
- NSString *_contentType;
-
- NSString *_destinationBundleFilePath;
- NSFileHandle *_destinationFileHandle;
-
- __unsafe_unretained id <DTDownloadDelegate> _delegate;
-
- BOOL _headOnly;
-
- // response handlers
- DTDownloadResponseHandler _responseHandler;
- DTDownloadCompletionHandler _completionHandler;
-}
-
-#pragma mark Downloading
-
-- (id)initWithURL:(NSURL *)URL {
- return [self initWithURL:URL withDestinationPath:nil];
-}
-
-- (id)initWithURL:(NSURL *)URL withDestinationPath:(NSString *)destinationPath;
-{
- NSAssert(![URL isFileURL], @"File URL is illegal parameter for DTDownload");
-
- self = [super init];
- if (self)
- {
- _URL = URL;
- _resumeFileOffset = 0;
- _destinationPath = destinationPath;
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillTerminate:) name:UIApplicationWillTerminateNotification object:nil];
- }
- return self;
-}
-
-- (id)initWithURL:(NSURL *)URL withDestinationFile:(NSString *)destinationFile {
- self = [self initWithURL:URL withDestinationPath:[destinationFile stringByDeletingLastPathComponent]];
- _destinationFileName = [destinationFile lastPathComponent];
- return self;
-}
-
-
-
-- (id)initWithDictionary:(NSDictionary *)dictionary atBundlePath:(NSString *)path;
-{
- self = [super init];
- if (self)
- {
- [self setInfoDictionary:dictionary];
-
- // update the destination path so that the path is correct also if the download bundle was moved
- _destinationBundleFilePath = [path stringByAppendingPathComponent:[_destinationBundleFilePath lastPathComponent]];
-
- NSDictionary *fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:_destinationBundleFilePath error:nil];
- NSNumber *fileSize = [fileAttributes objectForKey:NSFileSize];
- if ([fileSize longLongValue] < _resumeFileOffset) {
- _resumeFileOffset = 0;
- }
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillTerminate:) name:UIApplicationWillTerminateNotification object:nil];
- }
- return self;
-}
-
-
-+ (DTDownload *)downloadForURL:(NSURL *)URL atPath:(NSString *)path
-{
- NSFileManager *fileManager = [NSFileManager defaultManager];
- NSDirectoryEnumerator *enumerator = [fileManager enumeratorAtPath:path];
- NSString *file;
- while (file = [enumerator nextObject])
- {
- if ([[file pathExtension] isEqualToString:@"download"])
- {
- NSString *bundlePath = [path stringByAppendingPathComponent:file];
- NSString *infoFile = [bundlePath stringByAppendingPathComponent:@"Info.plist"];
- NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfFile:infoFile];
- NSString *infoFileURL = [dictionary objectForKey:DownloadEntryURL];
- if ([infoFileURL isEqualToString:[URL absoluteString]])
- {
- return [[DTDownload alloc] initWithDictionary:dictionary atBundlePath:bundlePath];
- }
-
- }
- }
- return [[DTDownload alloc] initWithURL:URL withDestinationPath:path];
-}
-
-
-- (void)dealloc
-{
- _urlConnection = nil;
- [[NSNotificationCenter defaultCenter] removeObserver:self];
-
- [self closeDestinationFile];
- // stop connection if still in flight
- [self stop];
-}
-
-- (void)startHEAD
-{
- NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:_URL
- cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
- timeoutInterval:60.0];
- [request setHTTPMethod:@"HEAD"];
-
- // startNext downloading
- _urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
-
- // without this special it would get paused during scrolling of scroll views
- [_urlConnection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
- [_urlConnection start];
-
- // getting only a HEAD
- _headOnly = YES;
-}
-
-
-- (void)start
-{
- if (_urlConnection)
- {
- return;
- }
-
-
- NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:_URL cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:60.0];
-
- if (_receivedBytes && _receivedBytes == _expectedContentLength)
- {
- // Already done!
- [self _completeWithSuccess];
- return;
- }
-
- if (_resumeFileOffset)
- {
- [request setValue:[NSString stringWithFormat:@"bytes=%lld-", _resumeFileOffset] forHTTPHeaderField:@"Range"];
- }
-
- [[NSNotificationCenter defaultCenter] postNotificationName:DTDownloadDidStartNotification object:self];
-
- _urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
-
- // without this special it would get paused during scrolling of scroll views
- [_urlConnection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
-
- // start urlConnection on the main queue, because when download lots of small file, we had a crash when this is done on a background thread
- dispatch_async(dispatch_get_main_queue(), ^
- {
- [_urlConnection start];
- });
-
- if (_urlConnection)
- {
- _receivedData = [NSMutableData data];
- }
-}
-
-- (void)stop
-{
- if (!_urlConnection)
- {
- return;
- }
-
- // update resume info on disk if necessary
- [self storeDownloadInfo];
- _resumeFileOffset = _receivedBytes;
-
- // only send cancel notification if it was loading
-
- // cancel the connection
- [_urlConnection cancel];
- _urlConnection = nil;
-
- // send notification
- [[NSNotificationCenter defaultCenter] postNotificationName:DTDownloadDidCancelNotification object:self];
-
- if ([_delegate respondsToSelector:@selector(downloadDidCancel:)])
- {
- [_delegate downloadDidCancel:self];
- }
- _receivedData = nil;
-}
-
-- (void)cleanup
-{
- [self stop];
-
- // remove cached file
- NSFileManager *fileManager = [[NSFileManager alloc] init];
- [fileManager removeItemAtPath:_destinationBundleFilePath error:nil];
- [fileManager removeItemAtPath:[[self downloadBundlePath] stringByAppendingPathComponent:@"Info.plist"] error:nil];
- [fileManager removeItemAtPath:[self downloadBundlePath] error:nil];
-
-}
-
-- (NSString *)description
-{
- return [NSString stringWithFormat:@"<%@ URL='%@'>", NSStringFromClass([self class]), self.URL];
-}
-
-#pragma mark - Internal Utilities
-
-- (void)_completeWithError:(NSError *)error
-{
- // notify delegate of error
- if ([_delegate respondsToSelector:@selector(download:didFailWithError:)])
- {
- [_delegate download:self didFailWithError:error];
- }
-
- // call completion handler
- if (_completionHandler)
- {
- _completionHandler(nil, error);
- }
-
- _urlConnection = nil;
- // send notification
- [[NSNotificationCenter defaultCenter] postNotificationName:DTDownloadDidFinishNotification object:self];
-}
-
-- (void)_completeWithSuccess
-{
-
- if (_headOnly)
- {
- // only a HEAD request
- if ([_delegate respondsToSelector:@selector(downloadDidFinishHEAD:)])
- {
- [_delegate downloadDidFinishHEAD:self];
- }
- }
- else
- {
- // normal GET request
- NSError *error = nil;
-
- NSFileManager *fileManager = [NSFileManager defaultManager];
-
- NSString *fileName = [_destinationBundleFilePath lastPathComponent];
- NSString *targetPath = [[[_destinationBundleFilePath stringByDeletingLastPathComponent] stringByDeletingLastPathComponent] stringByAppendingPathComponent:fileName];
-
- if (![fileManager moveItemAtPath:_destinationBundleFilePath toPath:targetPath error:&error])
- {
- NSLog(@"Cannot move item from %@ to %@, %@", _destinationBundleFilePath, targetPath, [error localizedDescription]);
- [self _completeWithError:error];
- return;
- }
-
- if (![fileManager removeItemAtPath:[_destinationBundleFilePath stringByDeletingLastPathComponent] error:&error]) {
- NSLog(@"Cannot remove item from %@, %@ ", [_destinationBundleFilePath stringByDeletingLastPathComponent], [error localizedDescription]);
-
- }
-
- // notify delegate
- if ([_delegate respondsToSelector:@selector(download:didFinishWithFile:)])
- {
- [_delegate download:self didFinishWithFile:targetPath];
- }
-
- // run completion handler
- if (_completionHandler)
- {
- _completionHandler(targetPath, nil);
- }
- }
-
- // nil the completion handlers in case they captured self
- _urlConnection = nil;
-
- // send notification
- [[NSNotificationCenter defaultCenter] postNotificationName:DTDownloadDidFinishNotification object:self];
-}
-
-
-- (void)setInfoDictionary:(NSDictionary *)infoDictionary
-{
- _URL = [NSURL URLWithString:[infoDictionary objectForKey:DownloadEntryURL]];
- _destinationBundleFilePath = [infoDictionary objectForKey:DownloadEntryPath];
- _expectedContentLength = [[infoDictionary objectForKey:DownloadEntryProgressTotalToLoad] longLongValue];
- NSDictionary *resumeInfo = [infoDictionary objectForKey:DownloadEntryResumeInformation];
- _resumeFileOffset = [[resumeInfo objectForKey:NSURLDownloadBytesReceived] longLongValue];
- _downloadEntityTag = [infoDictionary objectForKey:NSURLDownloadEntityTag];
- _expectedContentLength = [[infoDictionary objectForKey:DownloadEntryProgressTotalToLoad] longLongValue];
-}
-
-- (NSDictionary *)infoDictionary
-{
- NSMutableDictionary *resumeDictionary = [NSMutableDictionary dictionary];
- [resumeDictionary setObject:[NSNumber numberWithLongLong:_receivedBytes] forKey:NSURLDownloadBytesReceived];
- if (_downloadEntityTag)
- {
- [resumeDictionary setObject:_downloadEntityTag forKey:NSURLDownloadEntityTag];
- }
- [resumeDictionary setObject:[_URL absoluteString] forKey:DownloadEntryURL];
- NSDictionary *infoDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
- [NSNumber numberWithInt:-999], DownloadEntryErrorCodeDictionaryKey,
- NSURLErrorDomain, DownloadEntryErrorDomainDictionaryKey,
- _destinationBundleFilePath, DownloadEntryPath,
- [NSNumber numberWithLongLong:_receivedBytes], DownloadEntryProgressBytesSoFar,
- [NSNumber numberWithLongLong:_expectedContentLength], DownloadEntryProgressTotalToLoad,
- resumeDictionary, DownloadEntryResumeInformation,
- [_URL absoluteString], DownloadEntryURL
- , nil];
-
- return infoDictionary;
-}
-
-- (void)storeDownloadInfo
-{
- // no need to save resume info if we have not received any bytes yet, or download is complete
- if (_receivedBytes == 0 || (_receivedBytes >= _expectedContentLength) || _headOnly)
- {
- return;
- }
-
- NSString *infoPath = [[self downloadBundlePath] stringByAppendingPathComponent:@"Info.plist"];
- [[self infoDictionary] writeToFile:infoPath atomically:YES];
-}
-
-
-#pragma mark - NSURLConnection Delegate
-
-- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
-{
- _receivedData = nil;
- _urlConnection = nil;
-
- [self closeDestinationFile];
-
- // update resume info on disk
- [self storeDownloadInfo];
-
- [self _completeWithError:error];
-}
-
-- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
-{
- if ([response isKindOfClass:[NSHTTPURLResponse class]])
- {
- NSHTTPURLResponse *http = (NSHTTPURLResponse *) response;
- _contentType = http.MIMEType;
-
- if (http.statusCode >= 400)
- {
- NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSHTTPURLResponse localizedStringForStatusCode:http.statusCode] forKey:NSLocalizedDescriptionKey];
-
- NSError *error = [NSError errorWithDomain:@"iCatalog" code:http.statusCode userInfo:userInfo];
-
- [connection cancel];
-
- [self connection:connection didFailWithError:error];
- return;
- }
-
- if (_expectedContentLength <= 0)
- {
- _expectedContentLength = [response expectedContentLength];
-
- if (_expectedContentLength < 0)
- {
- NSLog(@"No expected content length for %@", _URL);
- }
- }
-
- NSString *currentEntityTag = [http.allHeaderFields objectForKey:@"Etag"];
- if (!_downloadEntityTag)
- {
- _downloadEntityTag = currentEntityTag;
- }
- else
- {
- // check if it's the same as from last time
- if (![self.downloadEntityTag isEqualToString:currentEntityTag])
- {
- // file was changed on server restart from beginning
- [_urlConnection cancel];
- _urlConnection = nil;
- // update loading flag to allow resume
- [self start];
- }
- }
-
- // get something to identify file
- NSString *modified = [http.allHeaderFields objectForKey:@"Last-Modified"];
- if (modified)
- {
- NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
- [dateFormatter setDateFormat:@"EEE, dd MMM yyyy HH:mm:ss zzz"];
- NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
- [dateFormatter setLocale:locale];
-
- _lastModifiedDate = [dateFormatter dateFromString:modified];
- }
-
- if (_responseHandler)
- {
- BOOL shouldCancel = NO;
- _responseHandler([http allHeaderFields], &shouldCancel);
-
- if (shouldCancel)
- {
- [self stop];
- }
- }
-
- // if _destinationBundleFilePath is not nil this means that it is a resumable download
- NSLog(@"_destinationBundleFilePath %@", _destinationBundleFilePath);
- if (!_destinationBundleFilePath) {
- _destinationBundleFilePath = [self createBundleFilePathForFilename:[self filenameFromHeader:http.allHeaderFields]];
- }
- NSLog(@"store result in %@", _destinationBundleFilePath);
-
- }
- else
- {
- [_urlConnection cancel];
- }
- // could be redirections, so we set the Length to 0 every time
- [_receivedData setLength:0];
-}
-
-- (NSString *)createBundleFilePathForFilename:(NSString *)fileName
-{
- if (_destinationFileName) {
- fileName = _destinationFileName;
- }
- else if (!fileName)
- {
- fileName = [[_URL path] lastPathComponent];
- }
- NSString *folderForDownloading = _destinationPath;
- if (!folderForDownloading)
- {
- folderForDownloading = NSTemporaryDirectory();
- }
-
- NSString * fullFileName = [self uniqueFileNameForFile:fileName atDestinationPath:folderForDownloading];
-
- NSString *downloadBundlePath = [folderForDownloading stringByAppendingPathComponent:[fullFileName stringByAppendingPathExtension:@"download"]];
- NSFileManager *fileManager = [NSFileManager defaultManager];
-
- if (![fileManager fileExistsAtPath:self.downloadBundlePath])
- {
- NSError *error;
- if (![fileManager createDirectoryAtPath:downloadBundlePath withIntermediateDirectories:YES attributes:nil error:&error])
- {
- NSLog(@"Cannot create download folder %@, %@", downloadBundlePath, [error localizedDescription]);
- [self _completeWithError:error];
- return nil;
- }
-
- }
- return [downloadBundlePath stringByAppendingPathComponent:fullFileName];
-}
-
-
-- (NSString *)uniqueFileNameForFile:(NSString *)fileName atDestinationPath:(NSString *)path {
-
- NSString *resultFileName = [path stringByAppendingPathComponent:fileName];
- NSFileManager *fileManager = [NSFileManager defaultManager];
-
- int i=1;
- while ([fileManager fileExistsAtPath:resultFileName] || [fileManager fileExistsAtPath:[resultFileName stringByAppendingPathExtension:@"download"]]) {
- NSString *extension = [fileName pathExtension];
- if ([extension length] > 0) {
- NSInteger endIndex = [fileName length]- [extension length] - 1;
- NSString *basename = [NSString stringWithFormat: @"%@-%d", [fileName substringToIndex:endIndex], i];
- resultFileName = [[path stringByAppendingPathComponent:basename] stringByAppendingPathExtension:extension];
- } else {
- resultFileName = [path stringByAppendingPathComponent:[NSString stringWithFormat: @"%@-%d", fileName, i]];
- }
-
- i++;
- }
- return [resultFileName lastPathComponent];
-
-}
-
-- (NSString *)filenameFromHeader:(NSDictionary *)headerDictionary
-{
- NSString *contentDisposition = [headerDictionary objectForKey:@"Content-disposition"];
-
- NSRange range = [contentDisposition rangeOfString:@"filename=\""];
- if (range.location != NSNotFound)
- {
- NSUInteger startIndex = range.location + range.length;
- NSUInteger length = contentDisposition.length - startIndex - 1;
- NSRange newRange = NSMakeRange(startIndex, length);
- return [contentDisposition substringWithRange:newRange];
- }
- return nil;
-}
-
-
-- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
-{
- [self writeToDestinationFile:data];
-
- // calculate a transfer speed
- float downloadSpeed = 0;
- NSDate *now = [NSDate date];
- if (self.lastPacketTimestamp)
- {
- NSTimeInterval downloadDurationForPacket = [now timeIntervalSinceDate:self.lastPacketTimestamp];
- float instantSpeed = [data length] / downloadDurationForPacket;
-
- downloadSpeed = (_previousSpeed * 0.9) + 0.1 * instantSpeed;
- }
- self.lastPacketTimestamp = now;
- // calculation speed done
-
-
- // send notification
- if (_expectedContentLength > 0)
- {
- // notify delegate
- if ([_delegate respondsToSelector:@selector(download:downloadedBytes:ofTotalBytes:withSpeed:)])
- {
- [_delegate download:self downloadedBytes:_receivedBytes ofTotalBytes:_expectedContentLength withSpeed:downloadSpeed];
- }
-
- NSDictionary *userInfo = @{@"ProgressPercent" : [NSNumber numberWithFloat:(float) _receivedBytes / (float) _expectedContentLength], @"TotalBytes" : [NSNumber numberWithLongLong:_expectedContentLength], @"ReceivedBytes" : [NSNumber numberWithLongLong:_receivedBytes]};
- [[NSNotificationCenter defaultCenter] postNotificationName:DTDownloadProgressNotification object:self userInfo:userInfo];
- }
-}
-
-
-- (void)connectionDidFinishLoading:(NSURLConnection *)connection
-{
- _receivedData = nil;
- _urlConnection = nil;
-
- [self closeDestinationFile];
-
- [self _completeWithSuccess];
-}
-
-#pragma mark Notifications
-- (void)appWillTerminate:(NSNotification *)notification
-{
- [self stop];
-}
-
-/**
-* Writes to the destination file the given data to the end of the file.
-* Also the destination file is opened lazy if needed.
-*/
-- (void)writeToDestinationFile:(NSData *)data
-{
- if (!_destinationBundleFilePath) {
- // should never happen because in didReceiveResponse the _destinationBundleFilePath is set
- NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"Cannot store the downloaded data"};
- NSError *error = [[NSError alloc] initWithDomain:@"DTDownload" code:100 userInfo:userInfo];
- [self _completeWithError:error];
- return;
- }
-
- if (!_destinationFileHandle)
- {
- NSFileManager *fileManager = [NSFileManager defaultManager];
- if (![fileManager fileExistsAtPath:_destinationBundleFilePath])
- {
- // if file does not exist then create it
- [fileManager createFileAtPath:_destinationBundleFilePath contents:data attributes:nil];
- _receivedBytes += [data length];
- _resumeFileOffset = 0;
- _destinationFileHandle = [NSFileHandle fileHandleForWritingAtPath:_destinationBundleFilePath];
- [_destinationFileHandle seekToEndOfFile];
- // we are done here, so exit
- return;
- } else {
- _destinationFileHandle = [NSFileHandle fileHandleForWritingAtPath:_destinationBundleFilePath];
- [_destinationFileHandle seekToFileOffset:_resumeFileOffset];
- _receivedBytes = _resumeFileOffset;
- }
- }
- //NSLog(@"write %d bytes for %@", [data length], _URL);
- [_destinationFileHandle writeData:data];
- _receivedBytes += [data length];
-}
-
-- (void)closeDestinationFile {
- [_destinationFileHandle closeFile];
- _destinationFileHandle = nil;
-}
-
-#pragma mark Properties
-
-- (BOOL)isRunning
-{
- return (_urlConnection != nil);
-}
-
-
-- (BOOL)canResume
-{
- return _resumeFileOffset > 0;
-}
-
-- (NSString *)downloadBundlePath {
- return [_destinationBundleFilePath stringByDeletingLastPathComponent];
-}
-
-
-@synthesize URL = _URL;
-@synthesize downloadEntityTag = _downloadEntityTag;
-//@synthesize folderForDownloading = _folderForDownloading;
-@synthesize lastPacketTimestamp = _lastPacketTimestamp;
-@synthesize delegate = _delegate;
-@synthesize lastModifiedDate = _lastModifiedDate;
-@synthesize contentType = _contentType;
-@synthesize expectedContentLength = _expectedContentLength;
-@synthesize context = _context;
-@synthesize responseHandler = _responseHandler;
-@synthesize completionHandler = _completionHandler;
-
-@end
View
116 Core/Source/DTDownload/DTDownloadCache.h
@@ -1,116 +0,0 @@
-//
-// DTDownloadCache.h
-// DTFoundation
-//
-// Created by Oliver Drobnik on 4/20/12.
-// Copyright (c) 2012 Cocoanetics. All rights reserved.
-//
-
-#import "DTDownload.h"
-
-extern NSString *DTDownloadCacheDidCacheFileNotification;
-
-
-enum {
- DTDownloadCacheOptionNeverLoad = 0,
- DTDownloadCacheOptionLoadIfNotCached,
- DTDownloadCacheOptionReturnCacheAndLoadAlways,
- DTDownloadCacheOptionReturnCacheAndLoadIfChanged,
-};
-typedef NSUInteger DTDownloadCacheOption;
-
-
-// when download succeedes or fails the blocks are called, passing URL. If there was an error then data/image is nil and the NSError holds the reason
-typedef void (^DTDownloadCacheDataCompletionBlock)(NSURL *URL, NSData *data, NSError *error);
-typedef void (^DTDownloadCacheImageCompletionBlock)(NSURL *URL, UIImage *image, NSError