Changeset 3099

Show
Ignore:
Timestamp:
12/29/05 02:34:02 (3 years ago)
Author:
timothy
Message:
  • DCC receive support with resume, IPv6 and partial passive support.
Files:

Legend:

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

    r3072 r3099  
    5959- (unsigned short) port; 
    6060 
    61 - (void) setFinalSize:(unsigned long long) finalSize; 
    62 - (void) setTransfered:(unsigned long long) transfered; 
    63 - (void) setStartDate:(NSDate *) startDate; 
    64  
    6561- (MVChatUser *) user; 
    6662 
  • branches/cocoa-networking/Chat Core/MVFileTransfer.m

    r3075 r3099  
    5858        if( ( self = [super init] ) ) { 
    5959                _status = MVFileTransferHoldingStatus; 
    60                 _port = 0; 
    61                 _startOffset = 0; 
    62                 _finalSize = 0; 
    63                 _transfered = 0; 
    64                 _startDate = nil; 
    65                 _host = nil; 
    6660                _user = [user retain]; 
    6761        } 
     
    9589 
    9690- (BOOL) isPassive { 
    97 // subclass if needed 
    98         return NO; 
     91        return _passive; 
    9992} 
    10093 
     
    135128- (unsigned short) port { 
    136129        return _port; 
    137 } 
    138  
    139 #pragma mark - 
    140  
    141 - (void) setFinalSize:(unsigned long long) finalSize { 
    142         @synchronized( self ) { 
    143                 _finalSize = finalSize; 
    144         } 
    145 } 
    146  
    147 - (void) setTransfered:(unsigned long long) transfered { 
    148         @synchronized( self ) { 
    149                 _transfered = transfered; 
    150         } 
    151 } 
    152  
    153 - (void) setStartDate:(NSDate *) startDate { 
    154         @synchronized( self ) { 
    155                 id old = _startDate; 
    156                 _startDate = [startDate retain]; 
    157                 [old release]; 
    158         } 
    159130} 
    160131 
     
    177148@implementation MVFileTransfer (MVFileTransferPrivate) 
    178149- (void) _setStatus:(MVFileTransferStatus) status { 
    179         @synchronized( self ) { 
    180                 _status = status; 
    181         } 
     150        _status = status; 
     151
     152 
     153- (void) _setFinalSize:(unsigned long long) finalSize { 
     154        _finalSize = finalSize; 
     155
     156 
     157- (void) _setTransfered:(unsigned long long) transfered { 
     158        _transfered = transfered; 
     159
     160 
     161- (void) _setStartOffset:(unsigned long long) startOffset { 
     162        _startOffset = startOffset; 
     163
     164 
     165- (void) _setStartDate:(NSDate *) startDate { 
     166        id old = _startDate; 
     167        _startDate = [startDate retain]; 
     168        [old release]; 
     169
     170 
     171- (void) _setHost:(NSHost *) host { 
     172        id old = _host; 
     173        _host = [host retain]; 
     174        [old release]; 
     175
     176 
     177- (void) _setPort:(unsigned short) port { 
     178        _port = port; 
     179
     180 
     181- (void) _setPassive:(BOOL) passive { 
     182        _passive = passive; 
    182183} 
    183184 
     
    185186        [self _setStatus:MVFileTransferErrorStatus]; 
    186187 
    187         @synchronized( self ) { 
    188                 id old = _lastError; 
    189                 _lastError = [error retain]; 
    190                 [old release]; 
    191         } 
     188        id old = _lastError; 
     189        _lastError = [error retain]; 
     190        [old release]; 
    192191 
    193192        NSDictionary *info = [[NSDictionary allocWithZone:nil] initWithObjectsAndKeys:error, @"error", nil]; 
    194         [[NSNotificationCenter defaultCenter] postNotificationName:MVFileTransferErrorOccurredNotification object:self userInfo:info]; 
     193        [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:MVFileTransferErrorOccurredNotification object:self userInfo:info]; 
    195194        [info release]; 
    196195} 
     
    212211#pragma mark - 
    213212 
    214 - (id) initWithUser:(MVChatUser *) user { 
    215         if( ( self = [super initWithUser:user] ) ) 
    216                 _source = nil; 
    217  
    218         return self; 
    219 } 
    220  
    221213- (void) dealloc { 
    222214        [_source release]; 
     
    240232#pragma mark - 
    241233 
     234@implementation MVUploadFileTransfer (MVUploadFileTransferPrivate) 
     235- (void) _setSource:(NSString *) source { 
     236        id old = _source; 
     237        _source = [[source stringByStandardizingPath] retain]; 
     238        [old release]; 
     239} 
     240@end 
     241 
     242#pragma mark - 
     243 
    242244@implementation MVDownloadFileTransfer 
    243 - (id) initWithUser:(MVChatUser *) user { 
    244         if( ( self = [super initWithUser:user] ) ) { 
    245                 _destination = nil; 
    246                 _originalFileName = nil; 
    247         } 
    248  
    249         return self; 
    250 } 
    251  
    252245- (void) dealloc { 
    253246        [_destination release]; 
     
    264257- (void) setDestination:(NSString *) path renameIfFileExists:(BOOL) rename { 
    265258        // subclass if needed, call super 
    266         @synchronized( self ) { 
    267                 id old = _destination; 
    268                 _destination = [[path stringByStandardizingPath] copyWithZone:nil]; 
    269                 [old release]; 
    270         } 
     259        id old = _destination; 
     260        _destination = [[path stringByStandardizingPath] copyWithZone:nil]; 
     261        [old release]; 
    271262} 
    272263 
     
    305296} 
    306297@end 
     298 
     299#pragma mark - 
     300 
     301@implementation MVDownloadFileTransfer (MVDownloadFileTransferPrivate) 
     302- (void) _setOriginalFileName:(NSString *) originalFileName { 
     303        id old = _originalFileName; 
     304        _originalFileName = [originalFileName copyWithZone:nil]; 
     305        [old release]; 
     306} 
     307@end 
  • branches/cocoa-networking/Chat Core/MVFileTransferPrivate.h

    r3081 r3099  
    33@interface MVFileTransfer (MVFileTransferPrivate) 
    44- (void) _setStatus:(MVFileTransferStatus) status; 
     5- (void) _setFinalSize:(unsigned long long) finalSize; 
     6- (void) _setTransfered:(unsigned long long) transfered; 
     7- (void) _setStartOffset:(unsigned long long) startOffset; 
     8- (void) _setStartDate:(NSDate *) startDate; 
     9- (void) _setHost:(NSHost *) host; 
     10- (void) _setPort:(unsigned short) port; 
     11- (void) _setPassive:(BOOL) passive; 
    512- (void) _postError:(NSError *) error; 
    613@end 
     14 
     15@interface MVUploadFileTransfer (MVUploadFileTransferPrivate) 
     16- (void) _setSource:(NSString *) source; 
     17@end 
     18 
     19@interface MVDownloadFileTransfer (MVDownloadFileTransferPrivate) 
     20- (void) _setOriginalFileName:(NSString *) originalFileName; 
     21@end 
  • branches/cocoa-networking/Chat Core/MVIRCChatConnection.h

    r3093 r3099  
    33 
    44@class AsyncSocket; 
     5@class MVFileTransfer; 
    56 
    67@interface MVIRCChatConnection : MVChatConnection { 
     
    910        NSThread *_connectionThread; 
    1011        NSMutableDictionary *_knownUsers; 
     12        NSMutableSet *_fileTransfers; 
    1113        NSString *_server; 
    1214        NSString *_currentNickname; 
     
    3840 
    3941- (void) _updateKnownUser:(MVChatUser *) user withNewNickname:(NSString *) nickname; 
     42 
     43- (void) _addFileTransfer:(MVFileTransfer *) transfer; 
     44- (void) _removeFileTransfer:(MVFileTransfer *) transfer; 
    4045@end 
  • branches/cocoa-networking/Chat Core/MVIRCChatConnection.m

    r3098 r3099  
    507507 
    508508                _knownUsers = [[NSMutableDictionary allocWithZone:nil] initWithCapacity:200]; 
     509                _fileTransfers = [[NSMutableSet allocWithZone:nil] initWithCapacity:5]; 
    509510        } 
    510511 
     
    517518        [_chatConnection release]; 
    518519        [_knownUsers release]; 
     520        [_fileTransfers release]; 
     521        [_server release]; 
     522        [_currentNickname release]; 
     523        [_nickname release]; 
     524        [_username release]; 
     525        [_password release]; 
     526        [_realName release]; 
     527        [_proxyServer release]; 
    519528        [_proxyUsername release]; 
    520529        [_proxyPassword release]; 
     
    523532        _connectionThread = nil; 
    524533        _knownUsers = nil; 
     534        _fileTransfers = nil; 
     535        _server = nil; 
     536        _currentNickname = nil; 
     537        _nickname = nil; 
     538        _username = nil; 
     539        _password = nil; 
     540        _realName = nil; 
     541        _proxyServer = nil; 
    525542        _proxyUsername = nil; 
    526543        _proxyPassword = nil; 
     
    12791296                [self sendRawMessageWithComponents:prefix, msg, nil]; 
    12801297                [prefix release]; 
     1298        } 
     1299} 
     1300 
     1301- (void) _addFileTransfer:(MVFileTransfer *) transfer { 
     1302        @synchronized( _fileTransfers ) { 
     1303                if( transfer ) [_fileTransfers addObject:transfer]; 
     1304        } 
     1305} 
     1306 
     1307- (void) _removeFileTransfer:(MVFileTransfer *) transfer { 
     1308        @synchronized( _fileTransfers ) { 
     1309                if( transfer ) [_fileTransfers removeObject:transfer]; 
    12811310        } 
    12821311} 
     
    14271456                        // only reply with packets less than 100 bytes, anything over that is bad karma 
    14281457                        if( [arguments length] < 100 ) [sender sendSubcodeReply:command withArguments:arguments]; 
     1458                } else if( [command caseInsensitiveCompare:@"DCC"] == NSOrderedSame ) { 
     1459                        NSString *msg = [[NSString allocWithZone:nil] initWithData:arguments encoding:[self encoding]]; 
     1460                        NSArray *parameters = [msg componentsSeparatedByString:@" "]; 
     1461                        [msg release]; 
     1462 
     1463                        if( [parameters count] >= 5 && [[parameters objectAtIndex:0] caseInsensitiveCompare:@"SEND"] == NSOrderedSame ) { 
     1464                                long long size = 0; 
     1465                                unsigned int port = [[parameters objectAtIndex:3] intValue]; 
     1466                                NSScanner *scanner = [NSScanner scannerWithString:[parameters objectAtIndex:4]]; 
     1467                                [scanner scanLongLong:&size]; 
     1468 
     1469                                NSString *address = [parameters objectAtIndex:2]; 
     1470                                if( [address rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@".:"]].location == NSNotFound ) { 
     1471                                        unsigned int ip4 = [address intValue]; 
     1472                                        address = [NSString stringWithFormat:@"%lu.%lu.%lu.%lu", (ip4 & 0xff000000) >> 24, (ip4 & 0x00ff0000) >> 16, (ip4 & 0x0000ff00) >> 8, (ip4 & 0x000000ff)]; 
     1473                                } 
     1474 
     1475                                NSHost *host = [NSHost hostWithAddress:address]; 
     1476 
     1477                                MVIRCDownloadFileTransfer *transfer = [[MVIRCDownloadFileTransfer allocWithZone:nil] initWithUser:sender]; 
     1478                                [transfer _setOriginalFileName:[parameters objectAtIndex:1]]; 
     1479                                [transfer _setFinalSize:(unsigned long long)size]; 
     1480                                [transfer _setHost:host]; 
     1481                                [transfer _setPort:port]; 
     1482                                [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:MVDownloadFileTransferOfferNotification object:transfer]; 
     1483                                [self _addFileTransfer:transfer]; 
     1484                                [transfer release]; 
     1485                        } else if( [parameters count] >= 4 && [[parameters objectAtIndex:0] caseInsensitiveCompare:@"ACCEPT"] == NSOrderedSame ) { 
     1486                                long long size = 0; 
     1487                                unsigned int port = [[parameters objectAtIndex:2] intValue]; 
     1488                                NSScanner *scanner = [NSScanner scannerWithString:[parameters objectAtIndex:3]]; 
     1489                                [scanner scanLongLong:&size]; 
     1490 
     1491                                @synchronized( _fileTransfers ) { 
     1492                                        NSEnumerator *enumerator = [_fileTransfers objectEnumerator]; 
     1493                                        MVFileTransfer *transfer = nil; 
     1494                                        while( ( transfer = [enumerator nextObject] ) ) { 
     1495                                                if( [transfer isDownload] && [[transfer user] isEqualToChatUser:sender] && [transfer port] == port ) { 
     1496                                                        [transfer _setTransfered:(unsigned long long)size]; 
     1497                                                        [transfer _setStartOffset:(unsigned long long)size]; 
     1498                                                        [(MVIRCDownloadFileTransfer *)transfer _setupAndStart]; 
     1499                                                } 
     1500                                        } 
     1501                                } 
     1502                        } 
    14291503                } else if( [command caseInsensitiveCompare:@"CLIENTINFO"] == NSOrderedSame ) { 
    14301504                        // make this extnesible later with a plugin registration method 
  • branches/cocoa-networking/Chat Core/MVIRCFileTransfer.h

    r3081 r3099  
    22#import "MVFileTransferPrivate.h" 
    33 
    4 @interface MVIRCUploadFileTransfer : MVUploadFileTransfer {} 
     4@class AsyncSocket; 
     5@class NSThread; 
     6 
     7@interface MVIRCUploadFileTransfer : MVUploadFileTransfer { 
     8        AsyncSocket *_connection; 
     9        NSThread *_connectionThread; 
     10
    511@end 
    612 
    713#pragma mark - 
    814 
    9 @interface MVIRCDownloadFileTransfer : MVDownloadFileTransfer {} 
     15@interface MVIRCDownloadFileTransfer : MVDownloadFileTransfer { 
     16        AsyncSocket *_connection; 
     17        NSThread *_connectionThread; 
     18        NSFileHandle *_fileHandle; 
     19        BOOL _fileNameQuoted; 
     20        unsigned int _passiveId; 
     21
     22- (void) _setupAndStart; 
    1023@end 
  • branches/cocoa-networking/Chat Core/MVIRCFileTransfer.m

    r3075 r3099  
     1#import <sched.h> 
    12 
    23#import "MVIRCFileTransfer.h" 
     
    45#import "MVChatUser.h" 
    56#import "NSNotificationAdditions.h" 
    6  
    7 /*static void MVFileTransferConnected( FILE_DCC_REC *dcc ) { 
    8         MVFileTransfer *self = [MVFileTransfer _transferForDCCFileRecord:dcc]; 
    9         if( ! self ) return; 
    10  
    11         [self _setStatus:MVFileTransferNormalStatus]; 
    12  
    13         NSNotification *note = [NSNotification notificationWithName:MVFileTransferStartedNotification object:self]; 
    14         [[NSNotificationCenter defaultCenter] postNotificationOnMainThread:note]; 
    15 
    16  
    17 static void MVFileTransferUpdate( FILE_DCC_REC *dcc ) { 
    18         MVFileTransfer *self = [MVFileTransfer _transferForDCCFileRecord:dcc]; 
    19         if( ! self ) return; 
    20  
    21         if( [self status] == MVFileTransferStoppedStatus ) 
    22                 dcc_close( (DCC_REC *) dcc ); 
    23 
    24  
    25 static void MVFileTransferClosed( FILE_DCC_REC *dcc ) { 
     7#import "AsyncSocket.h" 
     8#import "InterThreadMessaging.h" 
     9 
     10#define DCCPacketSize 4096 
     11 
     12/*static void MVFileTransferClosed( FILE_DCC_REC *dcc ) { 
    2613        MVFileTransfer *self = [MVFileTransfer _transferForDCCFileRecord:dcc]; 
    2714        if( ! self ) return; 
     
    4229} 
    4330 
    44 static void MVFileTransferDestroyed( FILE_DCC_REC *dcc ) { 
    45         MVFileTransfer *self = [MVFileTransfer _transferForDCCFileRecord:dcc]; 
    46         if( ! self ) return; 
    47  
    48         [self performSelector:@selector( _destroying )]; 
    49 } 
    50  
    5131static void MVFileTransferErrorConnect( FILE_DCC_REC *dcc ) { 
    5232        MVFileTransfer *self = [MVFileTransfer _transferForDCCFileRecord:dcc]; 
     
    10181        [error release]; 
    10282        [info release]; 
    103 
    104  
    105 #pragma mark - 
    106  
    107 static BOOL fileTransferSignalsRegistered = NO; 
    108  
    109 @implementation MVFileTransfer (MVIRCFileTransferPrivate) 
    110 + (id) _transferForDCCFileRecord:(FILE_DCC_REC *) record { 
    111         if( ! record ) return nil; 
    112  
    113         MVFileTransferModuleData *data = MODULE_DATA( record ); 
    114         if( data ) return [[(data -> transfer) retain] autorelease]; 
    115  
    116         return nil; 
    117 
    118 @end 
    119  
    120 #pragma mark - */ 
     83} */ 
     84 
     85#pragma mark - 
    12186 
    12287@implementation MVIRCUploadFileTransfer 
    123 /*+ (void) initialize { 
    124         [super initialize]; 
    125         if( ! fileTransferSignalsRegistered ) { 
    126                 IrssiLock(); 
    127                 signal_add_last( "dcc connected", (SIGNAL_FUNC) MVFileTransferConnected ); 
    128                 signal_add_last( "dcc transfer update", (SIGNAL_FUNC) MVFileTransferUpdate ); 
    129                 signal_add_last( "dcc closed", (SIGNAL_FUNC) MVFileTransferClosed ); 
    130                 signal_add_last( "dcc destroyed", (SIGNAL_FUNC) MVFileTransferDestroyed ); 
    131                 signal_add_last( "dcc error connect", (SIGNAL_FUNC) MVFileTransferErrorConnect ); 
    132                 signal_add_last( "dcc error file create", (SIGNAL_FUNC) MVFileTransferErrorFileCreate ); 
    133                 signal_add_last( "dcc error file open", (SIGNAL_FUNC) MVFileTransferErrorFileOpen ); 
    134                 signal_add_last( "dcc error send exists", (SIGNAL_FUNC) MVFileTransferErrorSendExists ); 
    135                 IrssiUnlock(); 
    136                 fileTransferSignalsRegistered = YES; 
    137         } 
    138 } 
    139  
    14088+ (id) transferWithSourceFile:(NSString *) path toUser:(MVChatUser *) user passively:(BOOL) passive { 
    14189        NSURL *url = [NSURL URLWithString:@"http://colloquy.info/ip.php"]; 
    14290        NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:3.]; 
    143         NSMutableData *result = [[[NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:NULL] mutableCopyWithZone:nil] autorelease]; 
     91        NSMutableData *result = [[NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:NULL] mutableCopyWithZone:nil]; 
    14492        [result appendBytes:"\0" length:1]; 
    145  
    146         if( [result length] > 1 ) { 
    147                 IrssiLock(); 
    148                 settings_set_str( "dcc_own_ip", [result bytes] ); 
    149                 IrssiUnlock(); 
    150         } 
    151  
    152         IrssiLock(); 
    153  
    154         int queue = dcc_queue_new(); 
    155         NSString *source = [[path stringByStandardizingPath] copyWithZone:nil]; 
    156  
    157         char *tag = [(MVIRCChatConnection *)[user connection] _irssiConnection] -> tag; 
    158  
    159         if( ! passive ) dcc_queue_add( queue, DCC_QUEUE_NORMAL, [[user connection] encodedBytesWithString:[user nickname]], [source fileSystemRepresentation], tag, NULL ); 
    160         else dcc_queue_add_passive( queue, DCC_QUEUE_NORMAL, [[user connection] encodedBytesWithString:[user nickname]], [source fileSystemRepresentation], tag, NULL ); 
    161  
    162         dcc_queue_send_next( queue ); 
    163  
    164         DCC_REC *dcc = dcc_find_request( DCC_SEND_TYPE, [[user connection] encodedBytesWithString:[user nickname]], [[source lastPathComponent] fileSystemRepresentation] ); 
    165  
    166         MVIRCUploadFileTransfer *ret = [[[MVIRCUploadFileTransfer allocWithZone:nil] initWithDCCFileRecord:dcc toUser:user] autorelease]; 
    167         ret -> _source = [[source stringByStandardizingPath] copyWithZone:nil]; 
    168         ret -> _transferQueue = queue; 
    169  
    170         IrssiUnlock(); 
    171  
    172         return ret; 
    173 
    174  
    175 #pragma mark - 
    176  
    177 - (id) initWithDCCFileRecord:(void *) record toUser:(MVChatUser *) user { 
    178         if( ( self = [self initWithUser:user] ) ) 
    179                 [self _setDCCFileRecord:record]; 
    180         return self; 
    181 
    182  
     93         
     94        [result release]; 
     95 
     96        MVIRCUploadFileTransfer *ret = [[MVIRCUploadFileTransfer allocWithZone:nil] initWithUser:user]; 
     97        [ret _setSource:path]; 
     98 
     99        return [ret autorelease]; 
     100
     101 
     102- (void) cancel { 
     103        [self _setStatus:MVFileTransferStoppedStatus]; 
     104        [_connection release]; 
     105        _connection = nil; 
     106
     107@end 
     108 
     109#pragma mark - 
     110 
     111@implementation MVIRCDownloadFileTransfer 
    183112- (void) dealloc { 
    184         [self _setDCCFileRecord:NULL]; 
     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; 
     122        _connectionThread = nil; 
     123        _fileHandle = nil; 
     124 
    185125        [super dealloc]; 
    186126} 
    187127 
    188 #pragma mark - 
    189  
    190 - (BOOL) isPassive { 
    191         if( _passive || ! [self _DCCFileRecord] ) 
    192                 return _passive; 
    193  
    194         @synchronized( self ) { 
    195                 IrssiLock(); 
    196                 if( [self _DCCFileRecord] ) 
    197                         _passive = dcc_is_passive( [self _DCCFileRecord] ); 
    198                 IrssiUnlock(); 
    199  
    200                 return _passive; 
    201         } 
    202 
    203  
    204 #pragma mark - 
    205  
    206 - (unsigned long long) finalSize { 
    207         if( _finalSize || ! [self _DCCFileRecord] ) 
    208                 return _finalSize; 
    209  
    210         @synchronized( self ) { 
    211                 IrssiLock(); 
    212                 if( [self _DCCFileRecord] ) 
    213                         _finalSize = [self _DCCFileRecord] -> size; 
    214                 IrssiUnlock(); 
    215  
    216                 return _finalSize; 
    217         } 
    218 
    219  
    220 - (unsigned long long) transfered { 
    221         if( ! [self _DCCFileRecord] ) 
    222                 return _transfered; 
    223  
    224         @synchronized( self ) { 
    225                 IrssiLock(); 
    226                 if( [self _DCCFileRecord] ) 
    227                         _transfered = [self _DCCFileRecord] -> transfd; 
    228                 IrssiUnlock(); 
    229  
    230                 return _transfered; 
    231         } 
    232 
    233  
    234 #pragma mark - 
    235  
    236 - (NSDate *) startDate { 
    237         @synchronized( self ) { 
    238                 if( _startDate || ! [self _DCCFileRecord] ) 
    239                         return [[_startDate retain] autorelease]; 
    240  
    241                 IrssiLock(); 
    242                 if( [self _DCCFileRecord] && [self _DCCFileRecord] -> starttime ) 
    243                         _startDate = [[NSDate dateWithTimeIntervalSince1970:[self _DCCFileRecord] -> starttime] retain]; 
    244                 IrssiUnlock(); 
    245  
    246                 return _startDate; 
    247         } 
    248 
    249  
    250 - (unsigned long long) startOffset { 
    251         if( _startOffset || ! [self _DCCFileRecord] ) 
    252                 return _startOffset; 
    253  
    254         @synchronized( self ) { 
    255                 IrssiLock(); 
    256                 if( [self _DCCFileRecord] ) 
    257                         _startOffset = [self _DCCFileRecord] -> skipped; 
    258                 IrssiUnlock(); 
    259  
    260                 return _startOffset; 
    261         } 
    262 
    263  
    264 #pragma mark - 
    265  
    266 - (NSHost *) host { 
    267         @synchronized( self ) { 
    268                 if( _host || ! [self _DCCFileRecord] ) 
    269                         return [[_host retain] autorelease]; 
    270  
    271                 IrssiLock(); 
    272                 if( [self _DCCFileRecord] ) 
    273                         _host = [[NSHost hostWithAddress:[NSString stringWithUTF8String:[self _DCCFileRecord] -> addrstr]] retain]; 
    274                 IrssiUnlock(); 
    275  
    276                 return _host; 
    277         } 
    278 
    279  
    280 - (unsigned short) port { 
    281         if( _port || ! [self _DCCFileRecord] ) 
    282                 return _port; 
    283  
    284         @synchronized( self ) { 
    285                 IrssiLock(); 
    286                 if( [self _DCCFileRecord] ) 
    287                         _port = [self _DCCFileRecord] -> port; 
    288                 IrssiUnlock(); 
    289  
    290                 return _port; 
    291         } 
    292 
    293  
    294 #pragma mark - 
     128- (void) reject { 
     129        if( _fileNameQuoted ) [[self user] sendSubcodeRequest:@"DCC" withArguments:[NSString stringWithFormat:@"REJECT \"%@\"", [self originalFileName]]]; 
     130        else [[self user] sendSubcodeRequest:@"DCC" withArguments:[NSString stringWithFormat:@"REJECT %@", [self originalFileName]]]; 
     131
    295132 
    296133- (void) cancel { 
    297         @synchronized( self ) { 
    298                 // the Irssi thread callbacks check for this and call dcc_close then 
    299                 [self _setStatus:MVFileTransferStoppedStatus]; 
    300         } 
    301 } */ 
     134        [self _setStatus:MVFileTransferStoppedStatus]; 
     135        [_connection release]; 
     136        _connection = nil; 
     137
     138 
     139- (void) acceptByResumingIfPossible:(BOOL) resume { 
     140        if( resume ) { 
     141                NSNumber *size = [[[NSFileManager defaultManager] fileAttributesAtPath:[self destination] traverseLink:YES] objectForKey:NSFileSize]; 
     142                BOOL fileExists = [[NSFileManager defaultManager] isWritableFileAtPath:[self destination]]; 
     143 
     144                if( fileExists && [size unsignedLongLongValue] < [self finalSize] ) { 
     145                        if( [self isPassive] ) { 
     146                                if( _fileNameQuoted ) [[self user] sendSubcodeRequest:@"DCC" withArguments:[NSString stringWithFormat:@"RESUME \"%@\" 0 %llu %lu", [self originalFileName], [size unsignedLongLongValue], _passiveId]]; 
     147                                else [[self user] sendSubcodeRequest:@"DCC" withArguments:[NSString stringWithFormat:@"RESUME %@ 0 %llu %lu", [self originalFileName], [size unsignedLongLongValue], _passiveId]]; 
     148                        } 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]]]; 
     151                        } 
     152                        return; // we need to wait until we get an ACCEPT reply 
     153                } 
     154        } 
     155 
     156        [self _setupAndStart]; 
     157
     158 
     159#pragma mark - 
     160 
     161- (void) socket:(AsyncSocket *) sock willDisconnectWithError:(NSError *) error { 
     162        NSLog(@"DCC willDisconnectWithError: %@", error ); 
     163        [self _setStatus:MVFileTransferErrorStatus]; 
     164
     165 
     166- (void) socketDidDisconnect:(AsyncSocket *) sock { 
     167        if( [self status] != MVFileTransferDoneStatus ) 
     168                [self _setStatus:MVFileTransferErrorStatus]; 
     169 
     170        [_fileHandle synchronizeFile]; 
     171        [_fileHandle closeFile]; 
     172        [_fileHandle release]; 
     173        _fileHandle = nil; 
     174
     175 
     176- (void) socket:(AsyncSocket *) sock didConnectToHost:(NSString *) host port:(UInt16) port { 
     177        [self _setStatus:MVFileTransferNormalStatus]; 
     178 
     179        [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:MVFileTransferStartedNotification object:self]; 
     180 
     181        unsigned long long progress = [self transfered]; 
     182        unsigned long long packet = DCCPacketSize; 
     183        if( ( progress + packet ) > [self finalSize] ) 
     184                packet = [self finalSize] - progress; 
     185        [_connection readDataToLength:packet withTimeout:-1. tag:0]; 
     186
     187 
     188- (void) socket:(AsyncSocket *) sock didReadData:(NSData *) data withTag:(long) tag { 
     189        unsigned long long progress = [self transfered] + [data length]; 
     190        [self _setTransfered:progress]; 
     191 
     192        // dcc only supports a 2 GB limit with these acknowledgment packets, we will acknowledge 
     193        // that we have all the bytes but keep reading if the file is over 2 GB 
     194        unsigned long progressToSend = htonl( progress & 0xffffffff ); 
     195        NSData *length = [[NSData allocWithZone:nil] initWithBytes:&progressToSend length:4]; 
     196        [_connection writeData:length withTimeout:-1 tag:0]; 
     197        [length release]; 
     198 
     199        [_fileHandle writeData:data]; 
     200 
     201        if( progress < [self finalSize] ) { 
     202                unsigned long long packet = DCCPacketSize; 
     203                if( ( progress + packet ) > [self finalSize] ) 
     204                        packet = [self finalSize] - progress; 
     205                [_connection readDataToLength:packet withTimeout:-1. tag:0]; 
     206        } else { 
     207                [self _setStatus:MVFileTransferDoneStatus]; 
     208                [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:MVFileTransferFinishedNotification object:self]; 
     209                [_connection disconnect]; 
     210        } 
     211
     212 
     213#pragma mark - 
     214 
     215- (void) _setupAndStart { 
     216        BOOL directory = NO; 
     217        BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:[self destination] isDirectory:&directory]; 
     218        if( directory ) return; 
     219        if( ! fileExists ) [[NSData data] writeToFile:[self destination] atomically:NO]; 
     220        fileExists = [[NSFileManager defaultManager] isWritableFileAtPath:[self destination]]; 
     221        if( ! fileExists ) return; 
     222 
     223        [_fileHandle release]; 
     224        _fileHandle = [[NSFileHandle fileHandleForWritingAtPath:[self destination]] retain]; 
     225        if( ! _fileHandle ) return; 
     226        [_fileHandle truncateFileAtOffset:[self startOffset]]; 
     227 
     228        [_connection release]; 
     229        _connection = [[AsyncSocket allocWithZone:nil] initWithDelegate:self]; 
     230 
     231        if( ! _connectionThread ) { 
     232                [NSThread prepareForInterThreadMessages]; 
     233                [NSThread detachNewThreadSelector:@selector( _dccRunloop ) toTarget:self withObject:nil]; 
     234                while( ! _connectionThread ) sched_yield(); 
     235        } 
     236 
     237        [self performSelector:@selector( _connect ) inThread:_connectionThread];         
     238
     239 
     240- (void) _connect { 
     241        if( ! [_connection connectToHost:[[self host] address] onPort:[self port] error:NULL] ) { 
     242                NSLog(@"can't connect to DCC" ); 
     243                return; 
     244        } 
     245
     246 
     247- (oneway void) _dccRunloop { 
     248        NSAutoreleasePool *pool = [[NSAutoreleasePool allocWithZone:nil] init]; 
     249 
     250        _connectionThread = [NSThread currentThread]; 
     251        [NSThread prepareForInterThreadMessages]; 
     252        [NSThread setThreadPriority:0.75]; 
     253 
     254        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; 
     260 
     261        [pool release]; 
     262
    302263@end 
    303  
    304 /* #pragma mark - 
    305  
    306 @implementation MVIRCUploadFileTransfer (MVIRCUploadFileTransferPrivate) 
    307 - (SEND_DCC_REC *) _DCCFileRecord { 
    308         return _dcc; 
    309 } 
    310  
    311 - (void) _setDCCFileRecord:(FILE_DCC_REC *) record { 
    312         @synchronized( self ) { 
    313                 IrssiLock(); 
    314  
    315                 if( _dcc ) { 
    316                         MVFileTransferModuleData *data = MODULE_DATA( (DCC_REC *) _dcc ); 
    317                         if( data ) data -> transfer = nil; 
    318                         MODULE_DATA_UNSET( (DCC_REC *) _dcc ); 
    319                 } 
    320  
    321                 _dcc = record; 
    322  
    323                 if( record ) { 
    324                         MVFileTransferModuleData *data = g_new0( MVFileTransferModuleData, 1 ); 
    325                         data -> transfer = self; 
    326                         MODULE_DATA_SET( (DCC_REC *) record, data ); 
    327                 } 
    328  
    329                 IrssiUnlock(); 
    330         } 
    331 } 
    332  
    333 - (void) _destroying { 
    334         @synchronized( self ) { 
    335                 // load the variables simply by calling the accessor 
    336                 [self isPassive]; 
    337                 [self finalSize]; 
    338                 [self transfered]; 
    339                 [self port]; 
    340                 [self startOffset]; 
    341                 [self startDate]; 
    342                 [self host]; 
    343  
    344                 [self _setDCCFileRecord:NULL]; 
    345         } 
    346 } 
    347 @end 
    348  
    349 #pragma mark - 
    350  
    351 static void MVIRCDownloadFileTransferSpecifyPath( GET_DCC_REC *dcc ) { 
    352         MVIRCDownloadFileTransfer *self = [MVFileTransfer _transferForDCCFileRecord:(FILE_DCC_REC *)dcc]; 
    353         if( ! self ) return; 
    354  
    355         @synchronized( self ) { 
    356                 g_free_not_null( dcc -> file ); 
    357                 dcc -> file = g_strdup( [[self destination] fileSystemRepresentation] ); 
    358         } 
    359 } 
    360  
    361 #pragma mark - */ 
    362  
    363 @implementation MVIRCDownloadFileTransfer 
    364 /* + (void) initialize { 
    365         [super initialize]; 
    366         static BOOL tooLate = NO; 
    367         if( ! tooLate ) { 
    368                 IrssiLock(); 
    369                 signal_add_last( "dcc get receive", (SIGNAL_FUNC) MVIRCDownloadFileTransferSpecifyPath ); 
    370                 IrssiUnlock(); 
    371                 tooLate = YES; 
    372         } 
    373  
    374         if( ! fileTransferSignalsRegistered ) { 
    375                 IrssiLock(); 
    376                 signal_add_last( "dcc connected", (SIGNAL_FUNC) MVFileTransferConnected ); 
    377                 signal_add_last( "dcc transfer update", (SIGNAL_FUNC) MVFileTransferUpdate ); 
    378                 signal_add_last( "dcc closed", (SIGNAL_FUNC) MVFileTransferClosed ); 
    379                 signal_add_last( "dcc destroyed", (SIGNAL_FUNC) MVFileTransferDestroyed ); 
    380                 signal_add_last( "dcc error connect", (SIGNAL_FUNC) MVFileTransferErrorConnect ); 
    381                 signal_add_last( "dcc error file create", (SIGNAL_FUNC) MVFileTransferErrorFileCreate ); 
    382                 signal_add_last( "dcc error file open", (SIGNAL_FUNC) MVFileTransferErrorFileOpen ); 
    383                 signal_add_last( "dcc error send exists", (SIGNAL_FUNC) MVFileTransferErrorSendExists ); 
    384                 IrssiUnlock(); 
    385                 fileTransferSignalsRegistered = YES; 
    386         } 
    387 } 
    388  
    389 #pragma mark - 
    390  
    391 - (id) initWithDCCFileRecord:(void *) record fromUser:(MVChatUser *) user { 
    392         if( ( self = [self initWithUser:user] ) ) 
    393                 [self _setDCCFileRecord:record]; 
    394         return self; 
    395 } 
    396  
    397 - (void) dealloc { 
    398         [self _setDCCFileRecord:NULL]; 
    399         [super dealloc]; 
    400 } 
    401  
    402 #pragma mark - 
    403  
    404 - (BOOL) isPassive { 
    405         if( _passive || ! [self _DCCFileRecord] ) 
    406                 return _passive; 
    407  
    408         @synchronized( self ) { 
    409                 IrssiLock(); 
    410                 if( [self _DCCFileRecord] ) 
    411                         _passive = dcc_is_passive( [self _DCCFileRecord] ); 
    412                 IrssiUnlock(); 
    413  
    414                 return _passive; 
    415         } 
    416 } 
    417  
    418 #pragma mark - 
    419  
    420 - (unsigned long long) finalSize { 
    421         if( _finalSize || ! [self _DCCFileRecord] ) 
    422                 return _finalSize; 
    423  
    424         @synchronized( self ) { 
    425                 IrssiLock(); 
    426                 if( [self _DCCFileRecord] ) 
    427                         _finalSize = [self _DCCFileRecord] -> size; 
    428                 IrssiUnlock(); 
    429  
    430                 return _finalSize; 
    431         } 
    432 } 
    433  
    434 - (unsigned long long) transfered { 
    435         if( ! [self _DCCFileRecord] ) return _transfered; 
    436  
    437         @synchronized( self ) { 
    438                 IrssiLock(); 
    439                 if( [self _DCCFileRecord] ) 
    440                         _transfered = [self _DCCFileRecord] -> transfd; 
    441                 IrssiUnlock(); 
    442  
    443                 return _transfered; 
    444         } 
    445 } 
    446  
    447 #pragma mark - 
    448  
    449 - (NSDate *) startDate { 
    450         @synchronized( self ) { 
    451                 if( _startDate || ! [self _DCCFileRecord] ) 
    452                         return [[_startDate retain] autorelease]; 
    453  
    454                 IrssiLock(); 
    455                 if( [self _DCCFileRecord] && [self _DCCFileRecord] -> starttime ) 
    456                         _startDate = [[NSDate dateWithTimeIntervalSince1970:[self _DCCFileRecord] -> starttime] retain]; 
    457                 IrssiUnlock(); 
    458  
    459                 return _startDate; 
    460         } 
    461 } 
    462  
    463 - (unsigned long long) startOffset { 
    464         if( _startOffset || ! [self _DCCFileRecord] ) 
    465                 return _startOffset; 
    466  
    467         @synchronized( self ) { 
    468                 IrssiLock(); 
    469                 if( [self _DCCFileRecord] ) 
    470                         _startOffset = [self _DCCFileRecord] -> skipped; 
    471                 IrssiUnlock(); 
    472  
    473                 return _startOffset; 
    474         } 
    475 } 
    476  
    477 #pragma mark - 
    478  
    479 - (NSHost *) host { 
    480         @synchronized( self ) { 
    481                 if( _host || ! [self _DCCFileRecord] ) 
    482                         return [[_host retain] autorelease]; 
    483  
    484                 IrssiLock(); 
    485                 if( [self _DCCFileRecord] ) 
    486                         _host = [[NSHost hostWithAddress:[NSString stringWithUTF8String:[self _DCCFileRecord] -> addrstr]] retain]; 
    487                 IrssiUnlock(); 
    488  
    489                 return _host; 
    490         } 
    491 } 
    492  
    493 - (unsigned short) port { 
    494         if( _port || ! [self _DCCFileRecord] ) 
    495                 return _port; 
    496