Changeset 3101

Show
Ignore:
Timestamp:
12/30/05 06:05:58 (3 years ago)
Author:
timothy
Message:
  • Added DCC send and improved DCC receive.
Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/cocoa-networking/Chat Core/MVIRCChatConnection.h

    r3099 r3101  
    3030 
    3131@interface MVChatConnection (MVIRCChatConnectionPrivate) 
     32- (AsyncSocket *) _chatConnection; 
    3233- (void) _readNextMessageFromServer; 
    3334 
  • branches/cocoa-networking/Chat Core/MVIRCChatConnection.m

    r3099 r3101  
    516516        [self disconnect]; 
    517517 
     518        [_chatConnection setDelegate:nil]; 
     519 
    518520        [_chatConnection release]; 
    519521        [_knownUsers release]; 
     
    948950 
    949951@implementation MVIRCChatConnection (MVIRCChatConnectionPrivate) 
     952- (AsyncSocket *) _chatConnection { 
     953        return _chatConnection; 
     954} 
     955 
    950956- (void) _connect { 
    951957        [_chatConnection disconnect]; 
     
    14751481                                NSHost *host = [NSHost hostWithAddress:address]; 
    14761482 
    1477                                 MVIRCDownloadFileTransfer *transfer = [[MVIRCDownloadFileTransfer allocWithZone:nil] initWithUser:sender]; 
     1483                                MVIRCDownloadFileTransfer *transfer = [(MVIRCDownloadFileTransfer *)[MVIRCDownloadFileTransfer allocWithZone:nil] initWithUser:sender]; 
    14781484                                [transfer _setOriginalFileName:[parameters objectAtIndex:1]]; 
    14791485                                [transfer _setFinalSize:(unsigned long long)size]; 
  • branches/cocoa-networking/Chat Core/MVIRCFileTransfer.h

    r3099 r3101  
    77@interface MVIRCUploadFileTransfer : MVUploadFileTransfer { 
    88        AsyncSocket *_connection; 
     9        AsyncSocket *_clientConnection; 
    910        NSThread *_connectionThread; 
     11        NSFileHandle *_fileHandle; 
     12        BOOL _fileNameQuoted; 
     13        BOOL _done; 
     14        unsigned int _passiveId; 
    1015} 
     16- (void) _setupAndStart; 
     17- (void) _sendNextPacket; 
     18- (void) _finish; 
    1119@end 
    1220 
     
    1826        NSFileHandle *_fileHandle; 
    1927        BOOL _fileNameQuoted; 
     28        BOOL _done; 
    2029        unsigned int _passiveId; 
    2130} 
    2231- (void) _setupAndStart; 
     32- (void) _finish; 
    2333@end 
  • branches/cocoa-networking/Chat Core/MVIRCFileTransfer.m

    r3099 r3101  
    11#import <sched.h> 
     2#import <arpa/inet.h> 
    23 
    34#import "MVIRCFileTransfer.h" 
     
    8384} */ 
    8485 
    85 #pragma mark - 
     86static NSRange portRange; 
    8687 
    8788@implementation MVIRCUploadFileTransfer 
     89+ (void) initialize { 
     90        portRange = NSMakeRange( 1024, 20 ); 
     91} 
     92 
    8893+ (id) transferWithSourceFile:(NSString *) path toUser:(MVChatUser *) user passively:(BOOL) passive { 
    89         NSURL *url = [NSURL URLWithString:@"http://colloquy.info/ip.php"]; 
    90         NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:3.]; 
    91         NSMutableData *result = [[NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:NULL] mutableCopyWithZone:nil]; 
    92         [result appendBytes:"\0" length:1]; 
    93          
    94         [result release]; 
    95  
    96         MVIRCUploadFileTransfer *ret = [[MVIRCUploadFileTransfer allocWithZone:nil] initWithUser:user]; 
     94        static unsigned passiveId = 0; 
     95 
     96        MVIRCUploadFileTransfer *ret = [(MVIRCUploadFileTransfer *)[MVIRCUploadFileTransfer allocWithZone:nil] initWithUser:user]; 
     97        [(MVIRCChatConnection *)[user connection] _addFileTransfer:ret]; 
    9798        [ret _setSource:path]; 
     99        [ret _setPassive:passive]; 
     100 
     101        NSNumber *size = [[[NSFileManager defaultManager] fileAttributesAtPath:[ret source] traverseLink:YES] objectForKey:NSFileSize]; 
     102        [ret _setFinalSize:[size unsignedLongLongValue]]; 
     103 
     104        NSString *fileName = [[ret source] lastPathComponent];   
     105        ret->_fileNameQuoted = ( [fileName rangeOfString:@" "].location != NSNotFound ); 
     106 
     107        if( passive ) { 
     108                passiveId++; 
     109                if( passiveId > 1000 ) 
     110                        passiveId = 1; 
     111                ret->_passiveId = passiveId; 
     112        } else { 
     113                [ret _setupAndStart]; 
     114        } 
    98115 
    99116        return [ret autorelease]; 
     117} 
     118 
     119- (void) release { 
     120        if( ( [self retainCount] - 1 ) == 1 ) 
     121                [(MVIRCChatConnection *)[[self user] connection] _removeFileTransfer:self]; 
     122        [super release]; 
     123} 
     124 
     125- (void) dealloc { 
     126        id old = _fileHandle; 
     127        _fileHandle = nil; 
     128        [old closeFile]; 
     129        [old release]; 
     130 
     131        _connectionThread = nil; 
     132 
     133        [super dealloc]; 
    100134} 
    101135 
    102136- (void) cancel { 
    103137        [self _setStatus:MVFileTransferStoppedStatus]; 
    104         [_connection release]; 
     138 
     139        [self performSelector:@selector( _finish ) inThread:_connectionThread]; 
     140 
     141        id old = _fileHandle; 
     142        _fileHandle = nil; 
     143        [old closeFile]; 
     144        [old release]; 
     145 
     146        [(MVIRCChatConnection *)[[self user] connection] _removeFileTransfer:self]; 
     147
     148 
     149#pragma mark - 
     150 
     151- (void) socket:(AsyncSocket *) sock didAcceptNewSocket:(AsyncSocket *) newSocket { 
     152        if( ! _clientConnection ) _clientConnection = [newSocket retain]; 
     153        else [newSocket disconnect]; 
     154
     155 
     156- (void) socket:(AsyncSocket *) sock willDisconnectWithError:(NSError *) error { 
     157        NSLog(@"upload DCC willDisconnectWithError: %@", error ); 
     158        [self _setStatus:MVFileTransferErrorStatus]; 
     159
     160 
     161- (void) socketDidDisconnect:(AsyncSocket *) sock { 
     162        if( [self status] != MVFileTransferDoneStatus && [self status] != MVFileTransferStoppedStatus ) 
     163                [self _setStatus:MVFileTransferErrorStatus]; 
     164 
     165        id old = _fileHandle; 
     166        _fileHandle = nil; 
     167        [old closeFile]; 
     168        [old release]; 
     169 
     170        [self _finish]; 
     171
     172 
     173- (void) socket:(AsyncSocket *) sock didConnectToHost:(NSString *) host port:(UInt16) port { 
     174        [self _setStatus:MVFileTransferNormalStatus]; 
     175        [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:MVFileTransferStartedNotification object:self]; 
     176        [self _sendNextPacket]; 
     177 
     178        // now that we are connected deregister with the connection 
     179        // do this last incase the connection is the last thing retaining us 
     180        [(MVIRCChatConnection *)[[self user] connection] _removeFileTransfer:self]; 
     181
     182 
     183- (void) socket:(AsyncSocket *) sock didWriteDataWithTag:(long) tag { 
     184        unsigned long long progress = [self transfered] + tag; 
     185        [self _setTransfered:progress]; 
     186 
     187        [_clientConnection readDataToLength:4 withTimeout:-1. tag:( progress == [self finalSize] )]; 
     188        if( progress < [self finalSize] ) [self _sendNextPacket]; 
     189
     190 
     191- (void) socket:(AsyncSocket *) sock didReadData:(NSData *) data withTag:(long) tag { 
     192        if( tag ) { 
     193                [self _setStatus:MVFileTransferDoneStatus]; 
     194                [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:MVFileTransferFinishedNotification object:self]; 
     195                [self _finish]; 
     196        } 
     197
     198 
     199#pragma mark - 
     200 
     201- (void) _setupAndStart { 
     202        BOOL directory = NO; 
     203        BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:[self source] isDirectory:&directory]; 
     204        if( directory || ! fileExists ) return; 
     205 
     206        id old = _fileHandle; 
     207        _fileHandle = [[NSFileHandle fileHandleForReadingAtPath:[self source]] retain]; 
     208        [old release]; 
     209 
     210        if( ! _fileHandle ) return; 
     211        [_fileHandle seekToFileOffset:[self startOffset]]; 
     212 
     213        old = _connection; 
     214        _connection = [[AsyncSocket allocWithZone:nil] initWithDelegate:self]; 
     215        [old release]; 
     216 
     217        if( ! _connectionThread ) { 
     218                [NSThread prepareForInterThreadMessages]; 
     219                [NSThread detachNewThreadSelector:@selector( _dccRunloop ) toTarget:self withObject:nil]; 
     220                while( ! _connectionThread ) sched_yield(); 
     221        } 
     222 
     223        if( ! [self isPassive] ) [self performSelector:@selector( _waitForConnection ) inThread:_connectionThread];      
     224
     225 
     226- (void) _waitForConnection { 
     227        unsigned int port = portRange.location; 
     228        BOOL success = NO; 
     229        while( ! success ) { 
     230                if( [_connection acceptOnPort:port error:NULL] ) { 
     231                        success = YES; 
     232                        break; 
     233                } else { 
     234                        [_connection disconnect]; 
     235                        if( ++port > NSMaxRange( portRange ) ) 
     236                                port = 0; // just use a random port since the user defined range is in use 
     237                } 
     238        } 
     239 
     240        if( success ) { 
     241                id address = [[(MVIRCChatConnection *)[[self user] connection] _chatConnection] localHost]; 
     242                if( [address rangeOfString:@"."].location != NSNotFound ) 
     243                        address = [NSNumber numberWithUnsignedLong:ntohl( inet_addr( [address UTF8String] ) )]; 
     244                [self _setPort:[_connection localPort]]; 
     245 
     246                NSString *fileName = [[self source] lastPathComponent]; 
     247                if( _fileNameQuoted ) [[self user] sendSubcodeRequest:@"DCC" withArguments:[NSString stringWithFormat:@"SEND \"%@\" %@ %hu %llu", fileName, address, [self port], [self finalSize]]]; 
     248                else [[self user] sendSubcodeRequest:@"DCC" withArguments:[NSString stringWithFormat:@"SEND %@ %@ %hu %llu", fileName, address, [self port], [self finalSize]]]; 
     249        } 
     250
     251 
     252- (void) _sendNextPacket { 
     253        NSData *data = [_fileHandle readDataOfLength:DCCPacketSize]; 
     254 
     255        if( [data length] > 0 ) { 
     256                [_clientConnection writeData:data withTimeout:-1 tag:[data length]]; 
     257        } else { 
     258                [self _setStatus:MVFileTransferErrorStatus]; 
     259                [self _finish]; 
     260        } 
     261
     262 
     263- (void) _finish { 
     264        id old = _connection; 
    105265        _connection = nil; 
     266        [old setDelegate:nil]; 
     267        [old release]; 
     268 
     269        old = _clientConnection; 
     270        _clientConnection = nil; 
     271        [old setDelegate:nil]; 
     272        [old release]; 
     273 
     274        _done = YES; 
     275} 
     276 
     277- (oneway void) _dccRunloop { 
     278        NSAutoreleasePool *pool = [[NSAutoreleasePool allocWithZone:nil] init]; 
     279        [self retain]; 
     280 
     281        _connectionThread = [NSThread currentThread]; 
     282        [NSThread prepareForInterThreadMessages]; 
     283        [NSThread setThreadPriority:0.75]; 
     284 
     285        BOOL active = YES; 
     286        while( active && ! _done ) { 
     287                NSDate *timeout = [[NSDate allocWithZone:nil] initWithTimeIntervalSinceNow:5.]; 
     288                active = [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeout]; 
     289                [timeout release]; 
     290        } 
     291 
     292        _connectionThread = nil; 
     293 
     294        [self _finish]; 
     295        [self release]; 
     296 
     297        [pool release]; 
    106298} 
    107299@end 
     
    110302 
    111303@implementation MVIRCDownloadFileTransfer 
     304- (void) release { 
     305        if( ( [self retainCount] - 1 ) == 1 ) 
     306                [(MVIRCChatConnection *)[[self user] connection] _removeFileTransfer:self]; 
     307        [super release]; 
     308} 
     309 
    112310- (void) dealloc { 
    113         [(MVIRCChatConnection *)[[self user] connection] _removeFileTransfer:self]; 
    114  
    115         [_fileHandle synchronizeFile]; 
    116         [_fileHandle closeFile]; 
    117  
    118         [_connection release]; 
    119         [_fileHandle release]; 
    120  
    121         _connection = nil; 
     311        id old = _fileHandle; 
     312        _fileHandle = nil; 
     313        [old synchronizeFile]; 
     314        [old closeFile]; 
     315        [old release]; 
     316 
    122317        _connectionThread = nil; 
    123         _fileHandle = nil; 
    124318 
    125319        [super dealloc]; 
     
    129323        if( _fileNameQuoted ) [[self user] sendSubcodeRequest:@"DCC" withArguments:[NSString stringWithFormat:@"REJECT \"%@\"", [self originalFileName]]]; 
    130324        else [[self user] sendSubcodeRequest:@"DCC" withArguments:[NSString stringWithFormat:@"REJECT %@", [self originalFileName]]]; 
     325        [self cancel]; 
    131326} 
    132327 
    133328- (void) cancel { 
    134329        [self _setStatus:MVFileTransferStoppedStatus]; 
    135         [_connection release]; 
    136         _connection = nil; 
     330 
     331        [self performSelector:@selector( _finish ) inThread:_connectionThread]; 
     332 
     333        id old = _fileHandle; 
     334        _fileHandle = nil; 
     335        [old closeFile]; 
     336        [old release]; 
     337 
     338        [(MVIRCChatConnection *)[[self user] connection] _removeFileTransfer:self]; 
    137339} 
    138340 
     
    147349                                else [[self user] sendSubcodeRequest:@"DCC" withArguments:[NSString stringWithFormat:@"RESUME %@ 0 %llu %lu", [self originalFileName], [size unsignedLongLongValue], _passiveId]]; 
    148350                        } else { 
    149                                 if( _fileNameQuoted ) [[self user] sendSubcodeRequest:@"DCC" withArguments:[NSString stringWithFormat:@"RESUME \"%@\" %lu %llu", [self originalFileName], [self port], [size unsignedLongLongValue]]]; 
    150                                 else [[self user] sendSubcodeRequest:@"DCC" withArguments:[NSString stringWithFormat:@"RESUME %@ %lu %llu", [self originalFileName], [self port], [size unsignedLongLongValue]]]; 
     351                                if( _fileNameQuoted ) [[self user] sendSubcodeRequest:@"DCC" withArguments:[NSString stringWithFormat:@"RESUME \"%@\" %hu %llu", [self originalFileName], [self port], [size unsignedLongLongValue]]]; 
     352                                else [[self user] sendSubcodeRequest:@"DCC" withArguments:[NSString stringWithFormat:@"RESUME %@ %hu %llu", [self originalFileName], [self port], [size unsignedLongLongValue]]]; 
    151353                        } 
    152354                        return; // we need to wait until we get an ACCEPT reply 
     
    160362 
    161363- (void) socket:(AsyncSocket *) sock willDisconnectWithError:(NSError *) error { 
    162         NSLog(@"DCC willDisconnectWithError: %@", error ); 
     364        NSLog(@"download DCC willDisconnectWithError: %@", error ); 
    163365        [self _setStatus:MVFileTransferErrorStatus]; 
    164366} 
    165367 
    166368- (void) socketDidDisconnect:(AsyncSocket *) sock { 
    167         if( [self status] != MVFileTransferDoneStatus
     369        if( [self status] != MVFileTransferDoneStatus && [self status] != MVFileTransferStoppedStatus
    168370                [self _setStatus:MVFileTransferErrorStatus]; 
    169371 
    170         [_fileHandle synchronizeFile]; 
    171         [_fileHandle closeFile]; 
    172         [_fileHandle release]; 
    173         _fileHandle = nil; 
     372        id old = _fileHandle; 
     373        _fileHandle = nil; 
     374        [old synchronizeFile]; 
     375        [old closeFile]; 
     376        [old release]; 
     377 
     378        _done = YES; 
    174379} 
    175380 
     
    184389                packet = [self finalSize] - progress; 
    185390        [_connection readDataToLength:packet withTimeout:-1. tag:0]; 
     391 
     392        // now that we are connected deregister with the connection 
     393        // do this last incase the connection is the last thing retaining us 
     394        [(MVIRCChatConnection *)[[self user] connection] _removeFileTransfer:self]; 
    186395} 
    187396 
     
    208417                [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:MVFileTransferFinishedNotification object:self]; 
    209418                [_connection disconnect]; 
     419                _done = YES; 
    210420        } 
    211421} 
     
    221431        if( ! fileExists ) return; 
    222432 
    223         [_fileHandle release]
     433        id old = _fileHandle
    224434        _fileHandle = [[NSFileHandle fileHandleForWritingAtPath:[self destination]] retain]; 
     435        [old release]; 
     436 
    225437        if( ! _fileHandle ) return; 
    226438        [_fileHandle truncateFileAtOffset:[self startOffset]]; 
    227439 
    228         [_connection release]
     440        old = _connection
    229441        _connection = [[AsyncSocket allocWithZone:nil] initWithDelegate:self]; 
     442        [old release]; 
    230443 
    231444        if( ! _connectionThread ) { 
     
    245458} 
    246459 
     460- (void) _finish { 
     461        id old = _connection; 
     462        _connection = nil; 
     463        [old setDelegate:nil]; 
     464        [old release]; 
     465 
     466        _done = YES; 
     467} 
     468 
    247469- (oneway void) _dccRunloop { 
    248470        NSAutoreleasePool *pool = [[NSAutoreleasePool allocWithZone:nil] init]; 
     471        [self retain]; 
    249472 
    250473        _connectionThread = [NSThread currentThread]; 
     
    253476 
    254477        BOOL active = YES; 
    255         while( active && ( [self status] == MVFileTransferNormalStatus || [self status] == MVFileTransferHoldingStatus ) ) 
    256                 active = [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; 
    257  
    258         if( [NSThread currentThread] == _connectionThread ) 
    259                 _connectionThread = nil; 
     478        while( active && ! _done ) { 
     479                NSDate *timeout = [[NSDate allocWithZone:nil] initWithTimeIntervalSinceNow:5.]; 
     480                active = [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeout]; 
     481                [timeout release]; 
     482        } 
     483 
     484        _connectionThread = nil; 
     485 
     486        [self _finish]; 
     487        [self release]; 
    260488 
    261489        [pool release];