source: trunk/macosx/Torrent.m @ 3202

Last change on this file since 3202 was 3202, checked in by livings124, 15 years ago

patch from beelsebob to fix crash with connected peers with no client name

  • Property svn:keywords set to Date Rev Author Id
File size: 53.0 KB
Line 
1/******************************************************************************
2 * $Id: Torrent.m 3202 2007-09-27 12:44:25Z livings124 $
3 *
4 * Copyright (c) 2006-2007 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#import "Torrent.h"
26#import "NSStringAdditions.h"
27
28static int static_lastid = 0;
29
30@interface Torrent (Private)
31
32- (id) initWithHash: (NSString *) hashString path: (NSString *) path lib: (tr_handle *) lib
33        publicTorrent: (NSNumber *) publicTorrent
34        downloadFolder: (NSString *) downloadFolder
35        useIncompleteFolder: (NSNumber *) useIncompleteFolder incompleteFolder: (NSString *) incompleteFolder
36        dateAdded: (NSDate *) dateAdded dateCompleted: (NSDate *) dateCompleted
37        dateActivity: (NSDate *) dateActivity
38        ratioSetting: (NSNumber *) ratioSetting ratioLimit: (NSNumber *) ratioLimit
39                pex: (NSNumber *) pex
40        waitToStart: (NSNumber *) waitToStart orderValue: (NSNumber *) orderValue;
41
42- (BOOL) shouldUseIncompleteFolderForName: (NSString *) name;
43- (void) updateDownloadFolder;
44
45- (void) createFileList;
46- (void) insertPath: (NSMutableArray *) components forSiblings: (NSMutableArray *) siblings
47            withParent: (NSMutableDictionary *) parent previousPath: (NSString *) previousPath
48            flatList: (NSMutableArray *) flatList fileSize: (uint64_t) size index: (int) index;
49
50- (void) quickPause;
51- (void) endQuickPause;
52
53- (void) trashFile: (NSString *) path;
54
55@end
56
57@implementation Torrent
58
59- (id) initWithPath: (NSString *) path location: (NSString *) location deleteTorrentFile: (torrentFileState) torrentDelete
60        lib: (tr_handle *) lib
61{
62    self = [self initWithHash: nil path: path lib: lib
63            publicTorrent: torrentDelete != TORRENT_FILE_DEFAULT
64                            ? [NSNumber numberWithBool: torrentDelete == TORRENT_FILE_SAVE] : nil
65            downloadFolder: location
66            useIncompleteFolder: nil incompleteFolder: nil
67            dateAdded: nil dateCompleted: nil
68            dateActivity: nil
69            ratioSetting: nil ratioLimit: nil
70                        pex: nil
71            waitToStart: nil orderValue: nil];
72   
73    if (self)
74    {
75        if (!fPublicTorrent)
76            [self trashFile: path];
77    }
78    return self;
79}
80
81- (id) initWithHistory: (NSDictionary *) history lib: (tr_handle *) lib
82{
83    self = [self initWithHash: [history objectForKey: @"TorrentHash"]
84                path: [history objectForKey: @"TorrentPath"] lib: lib
85                publicTorrent: [history objectForKey: @"PublicCopy"]
86                downloadFolder: [history objectForKey: @"DownloadFolder"]
87                useIncompleteFolder: [history objectForKey: @"UseIncompleteFolder"]
88                incompleteFolder: [history objectForKey: @"IncompleteFolder"]
89                dateAdded: [history objectForKey: @"Date"]
90                                dateCompleted: [history objectForKey: @"DateCompleted"]
91                dateActivity: [history objectForKey: @"DateActivity"]
92                ratioSetting: [history objectForKey: @"RatioSetting"]
93                ratioLimit: [history objectForKey: @"RatioLimit"]
94                                pex: [history objectForKey: @"Pex"]
95                waitToStart: [history objectForKey: @"WaitToStart"]
96                orderValue: [history objectForKey: @"OrderValue"]];
97   
98    if (self)
99    {
100        //start transfer
101        NSNumber * active;
102        if ((active = [history objectForKey: @"Active"]) && [active boolValue])
103        {
104            fStat = tr_torrentStat(fHandle);
105            [self startTransfer];
106        }
107    }
108    return self;
109}
110
111- (NSDictionary *) history
112{
113    NSMutableDictionary * history = [NSMutableDictionary dictionaryWithObjectsAndKeys:
114                    [NSNumber numberWithBool: fPublicTorrent], @"PublicCopy",
115                    [self hashString], @"TorrentHash",
116                    fDownloadFolder, @"DownloadFolder",
117                    [NSNumber numberWithBool: fUseIncompleteFolder], @"UseIncompleteFolder",
118                    [NSNumber numberWithBool: [self isActive]], @"Active",
119                    fDateAdded, @"Date",
120                    [NSNumber numberWithInt: fRatioSetting], @"RatioSetting",
121                    [NSNumber numberWithFloat: fRatioLimit], @"RatioLimit",
122                    [NSNumber numberWithBool: fWaitToStart], @"WaitToStart",
123                    [self orderValue], @"OrderValue", nil];
124   
125    if (fIncompleteFolder)
126        [history setObject: fIncompleteFolder forKey: @"IncompleteFolder"];
127
128    if (fPublicTorrent)
129        [history setObject: [self publicTorrentLocation] forKey: @"TorrentPath"];
130       
131        if (![self privateTorrent])
132                [history setObject: [NSNumber numberWithBool: fPex] forKey: @"Pex"];
133       
134        if (fDateCompleted)
135                [history setObject: fDateCompleted forKey: @"DateCompleted"];
136   
137    NSDate * dateActivity = [self dateActivity];
138    if (dateActivity)
139                [history setObject: dateActivity forKey: @"DateActivity"];
140       
141    return history;
142}
143
144- (void) dealloc
145{
146    if (fileStat)
147        tr_torrentFilesFree(fileStat, [self fileCount]);
148   
149    [fDownloadFolder release];
150    [fIncompleteFolder release];
151   
152    [fPublicTorrentLocation release];
153   
154    [fDateAdded release];
155    [fDateCompleted release];
156    [fDateActivity release];
157   
158    [fIcon release];
159   
160    [fProgressString release];
161    [fStatusString release];
162    [fShortStatusString release];
163    [fRemainingTimeString release];
164   
165    [fFileList release];
166    [fFlatFileList release];
167   
168    [fFileMenu release];
169   
170    [fQuickPauseDict release];
171   
172    [super dealloc];
173}
174
175- (void) closeTorrent
176{
177    tr_torrentClose(fHandle);
178}
179
180- (void) closeRemoveTorrent
181{
182    tr_torrentRemoveSaved(fHandle);
183    [self closeTorrent];
184}
185
186- (void) changeIncompleteDownloadFolder: (NSString *) folder
187{
188    fUseIncompleteFolder = folder != nil;
189   
190    [fIncompleteFolder release];
191    fIncompleteFolder = fUseIncompleteFolder ? [folder retain] : nil;
192   
193    [self updateDownloadFolder];
194}
195
196- (void) changeDownloadFolder: (NSString *) folder
197{
198    [fDownloadFolder release];
199    fDownloadFolder = [folder retain];
200   
201    [self updateDownloadFolder];
202}
203
204- (NSString *) downloadFolder
205{
206    return [NSString stringWithUTF8String: tr_torrentGetFolder(fHandle)];
207}
208
209- (void) getAvailability: (int8_t *) tab size: (int) size
210{
211    tr_torrentAvailability(fHandle, tab, size);
212}
213
214- (void) getAmountFinished: (float *) tab size: (int) size
215{
216    tr_torrentAmountFinished(fHandle, tab, size);
217}
218
219- (void) update
220{
221    fStat = tr_torrentStat(fHandle);
222   
223    //notification when downloading finished
224    if (tr_getComplete(fHandle) || tr_getDone(fHandle))
225    {
226        BOOL canMove = YES;
227       
228        //move file from incomplete folder to download folder
229        if (fUseIncompleteFolder && ![[self downloadFolder] isEqualToString: fDownloadFolder]
230            && (canMove = [self alertForMoveFolderAvailable]))
231        {
232            [self quickPause];
233           
234            if ([[NSFileManager defaultManager] movePath: [[self downloadFolder] stringByAppendingPathComponent: [self name]]
235                                    toPath: [fDownloadFolder stringByAppendingPathComponent: [self name]] handler: nil])
236                [self updateDownloadFolder];
237            else
238                canMove = NO;
239           
240            [self endQuickPause];
241        }
242       
243        if (!canMove)
244        {
245            fUseIncompleteFolder = NO;
246           
247            [fDownloadFolder release];
248            fDownloadFolder = fIncompleteFolder;
249            fIncompleteFolder = nil;
250        }
251               
252                [fDateCompleted release];
253                fDateCompleted = [[NSDate alloc] init];
254       
255        fStat = tr_torrentStat(fHandle);
256        [[NSNotificationCenter defaultCenter] postNotificationName: @"TorrentFinishedDownloading" object: self];
257    }
258    else if (tr_getIncomplete(fHandle))
259        [[NSNotificationCenter defaultCenter] postNotificationName: @"TorrentRestartedDownloading" object: self];
260    else;
261   
262    //check to stop for ratio
263    float stopRatio;
264    if ([self isSeeding] && (stopRatio = [self actualStopRatio]) != INVALID && [self ratio] >= stopRatio)
265    {
266        [self stopTransfer];
267        fStat = tr_torrentStat(fHandle);
268       
269        fFinishedSeeding = YES;
270       
271        [self setRatioSetting: NSOffState];
272        [[NSNotificationCenter defaultCenter] postNotificationName: @"TorrentStoppedForRatio" object: self];
273    }
274       
275        NSMutableString * progressString = [[NSMutableString alloc] init],
276                                        * remainingTimeString = [[NSMutableString alloc] init],
277                                        * statusString = [[NSMutableString alloc] init],
278                                        * shortStatusString = [[NSMutableString alloc] init];
279
280    if (![self allDownloaded])
281    {
282        if ([fDefaults boolForKey: @"DisplayStatusProgressSelected"])
283        {
284            uint64_t have = [self haveTotal];
285            [progressString appendFormat: NSLocalizedString(@"%@ of %@ selected (%.2f%%)", "Torrent -> progress string"),
286                            [NSString stringForFileSize: have], [NSString stringForFileSize: have + fStat->leftUntilDone],
287                            100.0 * [self progressDone]];
288        }
289        else
290            [progressString appendFormat: NSLocalizedString(@"%@ of %@ (%.2f%%)", "Torrent -> progress string"),
291                            [NSString stringForFileSize: [self haveTotal]],
292                            [NSString stringForFileSize: [self size]], 100.0 * [self progress]];
293    }
294    else if (![self isComplete])
295    {
296        if ([fDefaults boolForKey: @"DisplayStatusProgressSelected"])
297            [progressString appendFormat: NSLocalizedString(@"%@ selected, uploaded %@ (Ratio: %@)",
298                "Torrent -> progress string"), [NSString stringForFileSize: [self haveTotal]],
299                [NSString stringForFileSize: [self uploadedTotal]], [NSString stringForRatio: [self ratio]]];
300        else
301            [progressString appendFormat: NSLocalizedString(@"%@ of %@ (%.2f%%), uploaded %@ (Ratio: %@)",
302                "Torrent -> progress string"), [NSString stringForFileSize: [self haveTotal]],
303                [NSString stringForFileSize: [self size]], 100.0 * [self progress],
304                [NSString stringForFileSize: [self uploadedTotal]], [NSString stringForRatio: [self ratio]]];
305    }
306    else
307        [progressString appendFormat: NSLocalizedString(@"%@, uploaded %@ (Ratio: %@)", "Torrent -> progress string"),
308                [NSString stringForFileSize: [self size]], [NSString stringForFileSize: [self uploadedTotal]],
309                [NSString stringForRatio: [self ratio]]];
310
311    BOOL wasChecking = fChecking;
312    fChecking = NO;
313    switch (fStat->status)
314    {
315        NSString * tempString;
316       
317        case TR_STATUS_STOPPED:
318            if (fWaitToStart)
319            {
320                tempString = ![self allDownloaded]
321                        ? [NSLocalizedString(@"Waiting to download", "Torrent -> status string") stringByAppendingEllipsis]
322                        : [NSLocalizedString(@"Waiting to seed", "Torrent -> status string") stringByAppendingEllipsis];
323            }
324            else if (fFinishedSeeding)
325                tempString = NSLocalizedString(@"Seeding complete", "Torrent -> status string");
326            else
327                tempString = NSLocalizedString(@"Paused", "Torrent -> status string");
328           
329            [statusString setString: tempString];
330            [shortStatusString setString: tempString];
331           
332            break;
333
334        case TR_STATUS_CHECK_WAIT:
335            tempString = [NSLocalizedString(@"Waiting to check existing data", "Torrent -> status string") stringByAppendingEllipsis];
336           
337            [statusString setString: tempString];
338            [shortStatusString setString: tempString];
339            [remainingTimeString setString: tempString];
340           
341            fChecking = YES;
342           
343            break;
344
345        case TR_STATUS_CHECK:
346            tempString = [NSString stringWithFormat: NSLocalizedString(@"Checking existing data (%.2f%%)",
347                                    "Torrent -> status string"), 100.0 * fStat->recheckProgress];
348           
349            [statusString setString: tempString];
350            [shortStatusString setString: tempString];
351            [remainingTimeString setString: tempString];
352           
353            fChecking = YES;
354           
355            break;
356
357        case TR_STATUS_DOWNLOAD:
358            [statusString setString: @""];
359            if ([self totalPeersConnected] != 1)
360                [statusString appendFormat: NSLocalizedString(@"Downloading from %d of %d peers",
361                                                "Torrent -> status string"), [self peersSendingToUs], [self totalPeersConnected]];
362            else
363                [statusString appendFormat: NSLocalizedString(@"Downloading from %d of 1 peer",
364                                                "Torrent -> status string"), [self peersSendingToUs]];
365           
366            int eta = [self eta];
367            if (eta < 0)
368            {
369                [remainingTimeString setString: NSLocalizedString(@"Unknown", "Torrent -> remaining time")];
370                [progressString appendString: NSLocalizedString(@" - remaining time unknown", "Torrent -> progress string")];
371            }
372            else
373            {
374                if (eta < 60)
375                    [remainingTimeString appendFormat: NSLocalizedString(@"%d sec", "Torrent -> remaining time"), eta];
376                else if (eta < 3600) //60 * 60
377                    [remainingTimeString appendFormat: NSLocalizedString(@"%d min %d sec", "Torrent -> remaining time"),
378                                                            eta / 60, eta % 60];
379                else if (eta < 86400) //24 * 60 * 60
380                    [remainingTimeString appendFormat: NSLocalizedString(@"%d hr %d min", "Torrent -> remaining time"),
381                                                            eta / 3600, (eta / 60) % 60];
382                else
383                {
384                                        int days = eta / 86400;
385                    if (days > 1)
386                        [remainingTimeString appendFormat: NSLocalizedString(@"%d days %d hr", "Torrent -> remaining time"),
387                                                                days, (eta / 3600) % 24];
388                    else
389                        [remainingTimeString appendFormat: NSLocalizedString(@"1 day %d hr", "Torrent -> remaining time"),
390                                                                (eta / 3600) % 24];
391                }
392               
393                [progressString appendFormat: NSLocalizedString(@" - %@ remaining", "Torrent -> progress string"),
394                                                                    remainingTimeString];
395            }
396           
397            break;
398
399        case TR_STATUS_SEED:
400        case TR_STATUS_DONE:
401            [statusString setString: @""];
402            if ([self totalPeersConnected] != 1)
403                [statusString appendFormat: NSLocalizedString(@"Seeding to %d of %d peers", "Torrent -> status string"),
404                                                [self peersGettingFromUs], [self totalPeersConnected]];
405            else
406                [statusString appendFormat: NSLocalizedString(@"Seeding to %d of 1 peer", "Torrent -> status string"),
407                                                [self peersGettingFromUs]];
408           
409            break;
410
411        case TR_STATUS_STOPPING:
412            tempString = [NSLocalizedString(@"Stopping", "Torrent -> status string") stringByAppendingEllipsis];
413       
414            [statusString setString: tempString];
415            [shortStatusString setString: tempString];
416           
417            break;
418    }
419   
420    //check for error
421    BOOL wasError = fError;
422    fError = [self isError];
423   
424    //check if stalled
425    BOOL wasStalled = fStalled;
426    fStalled = [self isActive] && [fDefaults boolForKey: @"CheckStalled"]
427                && [fDefaults integerForKey: @"StalledMinutes"] < [self stalledMinutes];
428   
429    //create strings for error or stalled
430    if (fError)
431    {
432        NSString * errorString = [self errorMessage];
433        if (!errorString || [errorString isEqualToString: @""])
434            [statusString setString: NSLocalizedString(@"Error", "Torrent -> status string")];
435        else
436            [statusString setString: [NSLocalizedString(@"Error: ", "Torrent -> status string")
437                                    stringByAppendingString: errorString]];
438    }
439    else if (fStalled)
440        [statusString insertString: NSLocalizedString(@"Stalled, ", "Torrent -> status string") atIndex: 0];
441    else;
442   
443    //update queue for checking (from downloading to seeding), stalled, or error
444    if ((wasChecking && !fChecking) || (!wasStalled && fStalled) || (!wasError && fError && [self isActive]))
445        [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateQueue" object: self];
446
447    if ([self isActive] && ![self isChecking])
448    {
449        NSString * stringToAppend = @"";
450        if (![self allDownloaded])
451        {
452            stringToAppend = [NSString stringWithFormat: NSLocalizedString(@"DL: %@, ", "Torrent -> status string"),
453                                [NSString stringForSpeed: [self downloadRate]]];
454            [shortStatusString setString: @""];
455        }
456        else
457        {
458            NSString * ratioString = [NSString stringForRatio: [self ratio]];
459       
460            [shortStatusString setString: [NSString stringWithFormat: NSLocalizedString(@"Ratio: %@, ",
461                                            "Torrent -> status string"), ratioString]];
462            [remainingTimeString setString: [NSLocalizedString(@"Ratio: ", "Torrent -> status string")
463                                                stringByAppendingString: ratioString]];
464        }
465       
466        stringToAppend = [stringToAppend stringByAppendingString: [NSLocalizedString(@"UL: ", "Torrent -> status string")
467                                            stringByAppendingString: [NSString stringForSpeed: [self uploadRate]]]];
468
469        [statusString appendFormat: @" - %@", stringToAppend];
470        [shortStatusString appendString: stringToAppend];
471    }
472       
473    //retain the strings
474    [fProgressString release];
475    fProgressString = progressString;
476   
477    [fStatusString release];
478    fStatusString = statusString;
479   
480    [fShortStatusString release];
481    fShortStatusString = shortStatusString;
482   
483    [fRemainingTimeString release];
484    fRemainingTimeString = remainingTimeString;
485}
486
487- (void) startTransfer
488{
489    fWaitToStart = NO;
490    fFinishedSeeding = NO;
491   
492    if (![self isActive] && [self alertForFolderAvailable] && [self alertForRemainingDiskSpace])
493    {
494        tr_torrentStart(fHandle);
495        [self update];
496    }
497}
498
499- (void) stopTransfer
500{
501    fError = NO;
502    fWaitToStart = NO;
503   
504    if ([self isActive])
505    {
506        tr_torrentStop(fHandle);
507        [self update];
508       
509        [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateQueue" object: self];
510    }
511}
512
513- (void) sleep
514{
515    if ((fResumeOnWake = [self isActive]))
516        tr_torrentStop(fHandle);
517}
518
519- (void) wakeUp
520{
521    if (fResumeOnWake)
522        tr_torrentStart(fHandle);
523}
524
525- (void) manualAnnounce
526{
527    tr_manualUpdate(fHandle);
528}
529
530- (BOOL) canManualAnnounce
531{
532    return tr_torrentCanManualUpdate(fHandle);
533}
534
535- (void) resetCache
536{
537    tr_torrentRecheck(fHandle);
538    [self update];
539}
540
541- (float) ratio
542{
543    return fStat->ratio;
544}
545
546- (int) ratioSetting
547{
548    return fRatioSetting;
549}
550
551- (void) setRatioSetting: (int) setting
552{
553    fRatioSetting = setting;
554}
555
556- (float) ratioLimit
557{
558    return fRatioLimit;
559}
560
561- (void) setRatioLimit: (float) limit
562{
563    if (limit >= 0)
564        fRatioLimit = limit;
565}
566
567- (float) actualStopRatio
568{
569    if (fRatioSetting == NSOnState)
570        return fRatioLimit;
571    else if (fRatioSetting == NSMixedState && [fDefaults boolForKey: @"RatioCheck"])
572        return [fDefaults floatForKey: @"RatioLimit"];
573    else
574        return INVALID;
575}
576
577- (float) progressStopRatio
578{
579    float stopRatio, ratio;
580    if ((stopRatio = [self actualStopRatio]) == INVALID || (ratio = [self ratio]) >= stopRatio)
581        return 1.0;
582    else if (ratio > 0 && stopRatio > 0)
583        return ratio / stopRatio;
584    else
585        return 0;
586}
587
588- (tr_speedlimit) speedMode: (BOOL) upload
589{
590    return tr_torrentGetSpeedMode(fHandle, upload ? TR_UP : TR_DOWN);
591}
592
593- (void) setSpeedMode: (tr_speedlimit) mode upload: (BOOL) upload
594{
595    tr_torrentSetSpeedMode(fHandle, upload ? TR_UP : TR_DOWN, mode);
596}
597
598- (int) speedLimit: (BOOL) upload
599{
600    return tr_torrentGetSpeedLimit(fHandle, upload ? TR_UP : TR_DOWN);
601}
602
603- (void) setSpeedLimit: (int) limit upload: (BOOL) upload
604{
605    tr_torrentSetSpeedLimit(fHandle, upload ? TR_UP : TR_DOWN, limit);
606}
607
608- (void) setWaitToStart: (BOOL) wait
609{
610    fWaitToStart = wait;
611}
612
613- (BOOL) waitingToStart
614{
615    return fWaitToStart;
616}
617
618- (void) revealData
619{
620    [[NSWorkspace sharedWorkspace] selectFile: [self dataLocation] inFileViewerRootedAtPath: nil];
621}
622
623- (void) revealPublicTorrent
624{
625    if (fPublicTorrent)
626        [[NSWorkspace sharedWorkspace] selectFile: fPublicTorrentLocation inFileViewerRootedAtPath: nil];
627}
628
629- (void) trashData
630{
631    [self trashFile: [self dataLocation]];
632}
633
634- (void) trashTorrent
635{
636    if (fPublicTorrent)
637        [self trashFile: [self publicTorrentLocation]];
638}
639
640- (void) moveTorrentDataFileTo: (NSString *) folder
641{
642    NSString * oldFolder = [self downloadFolder];
643    if (![oldFolder isEqualToString: folder] || ![fDownloadFolder isEqualToString: folder])
644    {
645        //check if moving inside itself
646        NSArray * oldComponents = [oldFolder pathComponents],
647                * newComponents = [folder pathComponents];
648        int count;
649       
650        if ((count = [oldComponents count]) < [newComponents count]
651                && [[newComponents objectAtIndex: count] isEqualToString: [self name]]
652                && [oldComponents isEqualToArray:
653                        [newComponents objectsAtIndexes: [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, count)]]])
654        {
655            NSAlert * alert = [[NSAlert alloc] init];
656            [alert setMessageText: NSLocalizedString(@"A folder cannot be moved to inside itself.",
657                                                        "Move inside itself alert -> title")];
658            [alert setInformativeText: [NSString stringWithFormat:
659                            NSLocalizedString(@"The move operation of \"%@\" cannot be done.",
660                                                "Move inside itself alert -> message"), [self name]]];
661            [alert addButtonWithTitle: NSLocalizedString(@"OK", "Move inside itself alert -> button")];
662           
663            [alert runModal];
664            [alert release];
665           
666            return;
667        }
668       
669        [self quickPause];
670       
671        if ([[NSFileManager defaultManager] movePath: [oldFolder stringByAppendingPathComponent: [self name]]
672                            toPath: [folder stringByAppendingPathComponent: [self name]] handler: nil])
673        {
674            //get rid of both incomplete folder and old download folder, even if move failed
675            fUseIncompleteFolder = NO;
676            if (fIncompleteFolder)
677            {
678                [fIncompleteFolder release];
679                fIncompleteFolder = nil;
680            }
681            [self changeDownloadFolder: folder];
682           
683            [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateInfoSettings" object: nil];
684           
685            [self endQuickPause];
686        }
687        else
688        {
689            [self endQuickPause];
690       
691            NSAlert * alert = [[NSAlert alloc] init];
692            [alert setMessageText: NSLocalizedString(@"There was an error moving the data file.", "Move error alert -> title")];
693            [alert setInformativeText: [NSString stringWithFormat:
694                            NSLocalizedString(@"The move operation of \"%@\" cannot be done.",
695                                                "Move error alert -> message"), [self name]]];
696            [alert addButtonWithTitle: NSLocalizedString(@"OK", "Move error alert -> button")];
697           
698            [alert runModal];
699            [alert release];
700        }
701    }
702}
703
704- (void) copyTorrentFileTo: (NSString *) path
705{
706    [[NSFileManager defaultManager] copyPath: [self torrentLocation] toPath: path handler: nil];
707}
708
709- (BOOL) alertForRemainingDiskSpace
710{
711    if ([self allDownloaded] || ![fDefaults boolForKey: @"WarningRemainingSpace"])
712        return YES;
713   
714    NSString * volumeName;
715    if ((volumeName = [[[NSFileManager defaultManager] componentsToDisplayForPath: [self downloadFolder]] objectAtIndex: 0]))
716    {
717        NSDictionary * systemAttributes = [[NSFileManager defaultManager] fileSystemAttributesAtPath: [self downloadFolder]];
718        uint64_t remainingSpace = [[systemAttributes objectForKey: NSFileSystemFreeSize] unsignedLongLongValue];
719       
720        uint64_t existingSize = 0;
721        NSDirectoryEnumerator * enumerator;
722        if ((enumerator = [[NSFileManager defaultManager] enumeratorAtPath:
723                    [[self downloadFolder] stringByAppendingPathComponent: [self name]]]))
724        {
725            NSDictionary * fileAttributes;
726            while ([enumerator nextObject])
727            {
728                fileAttributes = [enumerator fileAttributes];
729                if (![[fileAttributes objectForKey: NSFileType] isEqualTo: NSFileTypeDirectory])
730                    existingSize += [[fileAttributes objectForKey: NSFileSize] unsignedLongLongValue];
731            }
732        }
733       
734        #warning factor in checked files
735        if (remainingSpace + existingSize <= [self size])
736        {
737            NSAlert * alert = [[NSAlert alloc] init];
738            [alert setMessageText: [NSString stringWithFormat:
739                                    NSLocalizedString(@"Not enough remaining disk space to download \"%@\" completely.",
740                                        "Torrent file disk space alert -> title"), [self name]]];
741            [alert setInformativeText: [NSString stringWithFormat: NSLocalizedString(@"The transfer will be paused."
742                                        " Clear up space on %@ or deselect files in the torrent inspector to continue.",
743                                        "Torrent file disk space alert -> message"), volumeName]];
744            [alert addButtonWithTitle: NSLocalizedString(@"OK", "Torrent file disk space alert -> button")];
745            [alert addButtonWithTitle: NSLocalizedString(@"Download Anyway", "Torrent file disk space alert -> button")];
746            [alert addButtonWithTitle: NSLocalizedString(@"Always Download", "Torrent file disk space alert -> button")];
747           
748            int result = [alert runModal];
749            [alert release];
750           
751            if (result == NSAlertThirdButtonReturn)
752                [fDefaults setBool: NO forKey: @"WarningRemainingSpace"];
753           
754            return result != NSAlertFirstButtonReturn;
755        }
756    }
757    return YES;
758}
759
760- (BOOL) alertForFolderAvailable
761{
762    if (access(tr_torrentGetFolder(fHandle), 0))
763    {
764        NSAlert * alert = [[NSAlert alloc] init];
765        [alert setMessageText: [NSString stringWithFormat:
766                                NSLocalizedString(@"The folder for downloading \"%@\" cannot be used.",
767                                    "Folder cannot be used alert -> title"), [self name]]];
768        [alert setInformativeText: [NSString stringWithFormat:
769                        NSLocalizedString(@"\"%@\" cannot be used. The transfer will be paused.",
770                                            "Folder cannot be used alert -> message"), [self downloadFolder]]];
771        [alert addButtonWithTitle: NSLocalizedString(@"OK", "Folder cannot be used alert -> button")];
772        [alert addButtonWithTitle: [NSLocalizedString(@"Choose New Location",
773                                    "Folder cannot be used alert -> location button") stringByAppendingEllipsis]];
774       
775        if ([alert runModal] != NSAlertFirstButtonReturn)
776        {
777            NSOpenPanel * panel = [NSOpenPanel openPanel];
778           
779            [panel setPrompt: NSLocalizedString(@"Select", "Folder cannot be used alert -> prompt")];
780            [panel setAllowsMultipleSelection: NO];
781            [panel setCanChooseFiles: NO];
782            [panel setCanChooseDirectories: YES];
783            [panel setCanCreateDirectories: YES];
784
785            [panel setMessage: [NSString stringWithFormat: NSLocalizedString(@"Select the download folder for \"%@\"",
786                                "Folder cannot be used alert -> select destination folder"), [self name]]];
787           
788            [[NSNotificationCenter defaultCenter] postNotificationName: @"MakeWindowKey" object: nil];
789            [panel beginSheetForDirectory: nil file: nil types: nil modalForWindow: [NSApp keyWindow] modalDelegate: self
790                    didEndSelector: @selector(destinationChoiceClosed:returnCode:contextInfo:) contextInfo: nil];
791        }
792       
793        [alert release];
794       
795        return NO;
796    }
797   
798    return YES;
799}
800
801- (void) destinationChoiceClosed: (NSOpenPanel *) openPanel returnCode: (int) code contextInfo: (void *) context
802{
803    if (code != NSOKButton)
804        return;
805   
806    NSString * folder = [[openPanel filenames] objectAtIndex: 0];
807    if (fUseIncompleteFolder)
808        [self changeDownloadFolder: folder];
809    else
810        [self changeDownloadFolder: folder];
811   
812    [self startTransfer];
813    [self update];
814   
815    [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateInfoSettings" object: nil];
816}
817
818- (BOOL) alertForMoveFolderAvailable
819{
820    if (access([fDownloadFolder UTF8String], 0))
821    {
822        NSAlert * alert = [[NSAlert alloc] init];
823        [alert setMessageText: [NSString stringWithFormat:
824                                NSLocalizedString(@"The folder for moving the completed \"%@\" cannot be used.",
825                                    "Move folder cannot be used alert -> title"), [self name]]];
826        [alert setInformativeText: [NSString stringWithFormat:
827                                NSLocalizedString(@"\"%@\" cannot be used. The file will remain in its current location.",
828                                    "Move folder cannot be used alert -> message"), fDownloadFolder]];
829        [alert addButtonWithTitle: NSLocalizedString(@"OK", "Move folder cannot be used alert -> button")];
830       
831        [alert runModal];
832        [alert release];
833       
834        return NO;
835    }
836   
837    return YES;
838}
839
840- (NSImage *) icon
841{
842    if (!fIcon)
843    {
844        fIcon = [[[NSWorkspace sharedWorkspace] iconForFileType: fInfo->multifile ? NSFileTypeForHFSTypeCode('fldr')
845                                                : [[self name] pathExtension]] retain];
846        [fIcon setFlipped: YES];
847    }
848    return fIcon;
849}
850
851- (NSString *) name
852{
853    return [NSString stringWithUTF8String: fInfo->name];
854}
855
856- (uint64_t) size
857{
858    return fInfo->totalSize;
859}
860
861- (NSString *) trackerAddress
862{
863    return [NSString stringWithFormat: @"http://%s:%d", fStat->tracker->address, fStat->tracker->port];
864}
865
866- (NSString *) trackerAddressAnnounce
867{
868    return [NSString stringWithUTF8String: fStat->tracker->announce];
869}
870
871- (NSString *) comment
872{
873    return [NSString stringWithUTF8String: fInfo->comment];
874}
875
876- (NSString *) creator
877{
878    return [NSString stringWithUTF8String: fInfo->creator];
879}
880
881- (NSDate *) dateCreated
882{
883    int date = fInfo->dateCreated;
884    return date > 0 ? [NSDate dateWithTimeIntervalSince1970: date] : nil;
885}
886
887- (int) pieceSize
888{
889    return fInfo->pieceSize;
890}
891
892- (int) pieceCount
893{
894    return fInfo->pieceCount;
895}
896
897- (NSString *) hashString
898{
899    return [NSString stringWithUTF8String: fInfo->hashString];
900}
901
902- (BOOL) privateTorrent
903{
904    return TR_FLAG_PRIVATE & fInfo->flags;
905}
906
907- (NSString *) torrentLocation
908{
909    return [NSString stringWithUTF8String: fInfo->torrent];
910}
911
912- (NSString *) publicTorrentLocation
913{
914    return fPublicTorrentLocation;
915}
916
917- (NSString *) dataLocation
918{
919    return [[self downloadFolder] stringByAppendingPathComponent: [self name]];
920}
921
922- (BOOL) publicTorrent
923{
924    return fPublicTorrent;
925}
926
927- (NSString *) stateString
928{
929    switch( fStat->status )
930    {
931        case TR_STATUS_STOPPED:
932            return NSLocalizedString(@"Paused", "Torrent -> status string");
933            break;
934
935        case TR_STATUS_CHECK:
936            return [NSString stringWithFormat: NSLocalizedString(@"Checking existing data (%.2f%%)",
937                                    "Torrent -> status string"), 100.0 * fStat->recheckProgress];
938            break;
939       
940        case TR_STATUS_CHECK_WAIT:
941            return [NSLocalizedString(@"Waiting to check existing data", "Torrent -> status string") stringByAppendingEllipsis];
942            break;
943
944        case TR_STATUS_DOWNLOAD:
945            return NSLocalizedString(@"Downloading", "Torrent -> status string");
946            break;
947
948        case TR_STATUS_SEED:
949        case TR_STATUS_DONE:
950            return NSLocalizedString(@"Seeding", "Torrent -> status string");
951            break;
952
953        case TR_STATUS_STOPPING:
954            return [NSLocalizedString(@"Stopping", "Torrent -> status string") stringByAppendingEllipsis];
955            break;
956       
957        default:
958            return NSLocalizedString(@"N/A", "Torrent -> status string");
959    }
960}
961
962- (float) progress
963{
964    return fStat->percentComplete;
965}
966
967- (float) progressDone
968{
969    return fStat->percentDone;
970}
971
972- (float) progressLeft
973{//NSLog(@"left %f",(float)fStat->leftUntilDone / [self size]);
974    return (float)fStat->leftUntilDone / [self size];
975}
976
977- (int) eta
978{
979    return fStat->eta;
980}
981
982/*- (float) notAvailableDesired
983{NSLog(@"not available %f", (float)(fStat->desiredSize - fStat->desiredAvailable) / [self size]);
984    return (float)(fStat->desiredSize - fStat->desiredAvailable) / [self size];
985}*/
986
987- (BOOL) isActive
988{
989    return fStat->status & TR_STATUS_ACTIVE;
990}
991
992- (BOOL) isSeeding
993{
994    return fStat->status == TR_STATUS_SEED || fStat->status == TR_STATUS_DONE;
995}
996
997- (BOOL) isPaused
998{
999    return fStat->status == TR_STATUS_STOPPED;
1000}
1001
1002- (BOOL) isChecking
1003{
1004    return fStat->status == TR_STATUS_CHECK || fStat->status == TR_STATUS_CHECK_WAIT;
1005}
1006
1007- (BOOL) allDownloaded
1008{
1009    return fStat->cpStatus != TR_CP_INCOMPLETE;
1010}
1011
1012- (BOOL) isComplete
1013{
1014    return fStat->cpStatus == TR_CP_COMPLETE;
1015}
1016
1017- (BOOL) isError
1018{
1019    return fStat->error != 0;
1020}
1021
1022- (NSString *) errorMessage
1023{
1024    if (![self isError])
1025        return @"";
1026   
1027    NSString * error;
1028    if (!(error = [NSString stringWithUTF8String: fStat->errorString])
1029        && !(error = [NSString stringWithCString: fStat->errorString encoding: NSISOLatin1StringEncoding]))
1030        error = NSLocalizedString(@"(unreadable error)", "Torrent -> error string unreadable");
1031   
1032    return error;
1033}
1034
1035- (NSArray *) peers
1036{
1037    int totalPeers, i;
1038    tr_peer_stat * peers = tr_torrentPeers(fHandle, &totalPeers);
1039   
1040    NSMutableArray * peerDics = [NSMutableArray arrayWithCapacity: totalPeers];
1041    NSMutableDictionary * dic;
1042   
1043    tr_peer_stat * peer;
1044    for (i = 0; i < totalPeers; i++)
1045    {
1046        peer = &peers[i];
1047       
1048        dic = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1049            [NSNumber numberWithBool: peer->isConnected], @"Connected",
1050            [NSNumber numberWithInt: peer->from], @"From",
1051            [NSString stringWithCString: (char *) peer->addr encoding: NSUTF8StringEncoding], @"IP",
1052            [NSNumber numberWithInt: peer->port], @"Port", nil];
1053       
1054        if (peer->isConnected)
1055        {
1056            [dic setObject: [NSNumber numberWithFloat: peer->progress] forKey: @"Progress"];
1057           
1058            if (peer->isDownloading)
1059                [dic setObject: [NSNumber numberWithFloat: peer->uploadToRate] forKey: @"UL To Rate"];
1060            if (peer->isUploading)
1061                [dic setObject: [NSNumber numberWithFloat: peer->downloadFromRate] forKey: @"DL From Rate"];
1062           
1063            [dic setObject: [NSNumber numberWithBool: peer->isEncrypted] forKey: @"Encryption"];
1064        }
1065       
1066        #warning why would connected clients have null value?
1067        if (peer->client)
1068            [dic setObject: [NSString stringWithCString: (char *) peer->client encoding: NSUTF8StringEncoding] forKey: @"Client"];
1069        else
1070            [dic setObject: @"" forKey: @"Client"]; //needed to be set here for client sort
1071       
1072        [peerDics addObject: dic];
1073    }
1074   
1075    tr_torrentPeersFree(peers, totalPeers);
1076   
1077    return peerDics;
1078}
1079
1080- (NSString *) progressString
1081{
1082    return fProgressString;
1083}
1084
1085- (NSString *) statusString
1086{
1087    return fStatusString;
1088}
1089
1090- (NSString *) shortStatusString
1091{
1092    return fShortStatusString;
1093}
1094
1095- (NSString *) remainingTimeString
1096{
1097    return fRemainingTimeString;
1098}
1099
1100- (int) seeders
1101{
1102    return fStat->seeders;
1103}
1104
1105- (int) leechers
1106{
1107    return fStat->leechers;
1108}
1109
1110- (int) completedFromTracker
1111{
1112    return fStat->completedFromTracker;
1113}
1114
1115- (int) totalPeersConnected
1116{
1117    return fStat->peersConnected;
1118}
1119
1120- (int) totalPeersTracker
1121{
1122    return fStat->peersFrom[TR_PEER_FROM_TRACKER];
1123}
1124
1125- (int) totalPeersIncoming
1126{
1127    return fStat->peersFrom[TR_PEER_FROM_INCOMING];
1128}
1129
1130- (int) totalPeersCache
1131{
1132    return fStat->peersFrom[TR_PEER_FROM_CACHE];
1133}
1134
1135- (int) totalPeersPex
1136{
1137    return fStat->peersFrom[TR_PEER_FROM_PEX];
1138}
1139
1140- (int) peersSendingToUs
1141{
1142    return fStat->peersSendingToUs;
1143}
1144
1145- (int) peersGettingFromUs
1146{
1147    return fStat->peersGettingFromUs;
1148}
1149
1150- (float) downloadRate
1151{
1152    return fStat->rateDownload;
1153}
1154
1155- (float) uploadRate
1156{
1157    return fStat->rateUpload;
1158}
1159
1160- (uint64_t) haveVerified
1161{
1162    return fStat->haveValid;
1163}
1164
1165- (uint64_t) haveTotal
1166{
1167    return [self haveVerified] + fStat->haveUnchecked;
1168}
1169
1170- (uint64_t) downloadedTotal
1171{
1172    return fStat->downloadedEver;
1173}
1174
1175- (uint64_t) uploadedTotal
1176{
1177    return fStat->uploadedEver;
1178}
1179
1180- (uint64_t) failedHash
1181{
1182    return fStat->corruptEver;
1183}
1184
1185- (float) swarmSpeed
1186{
1187    return fStat->swarmspeed;
1188}
1189
1190- (BOOL) pex
1191{
1192        return fPex;
1193}
1194
1195- (void) setPex: (BOOL) setting
1196{
1197        if (![self privateTorrent])
1198        {
1199                fPex = setting;
1200                tr_torrentDisablePex(fHandle, !setting);
1201        }
1202}
1203
1204- (NSNumber *) orderValue
1205{
1206    return [NSNumber numberWithInt: fOrderValue];
1207}
1208
1209- (void) setOrderValue: (int) orderValue
1210{
1211    fOrderValue = orderValue;
1212}
1213
1214- (NSArray *) fileList
1215{
1216    return fFileList;
1217}
1218
1219- (int) fileCount
1220{
1221    return fInfo->fileCount;
1222}
1223
1224- (void) updateFileStat
1225{
1226    if (fileStat)
1227        tr_torrentFilesFree(fileStat, [self fileCount]);
1228   
1229    int count;
1230    fileStat = tr_torrentFiles(fHandle, &count);
1231}
1232
1233- (float) fileProgress: (int) index
1234{
1235    if (!fileStat)
1236        [self updateFileStat];
1237       
1238    return fileStat[index].progress;
1239}
1240
1241- (BOOL) canChangeDownloadCheckForFile: (int) index
1242{
1243    if (!fileStat)
1244        [self updateFileStat];
1245   
1246    return [self fileCount] > 1 && fileStat[index].completionStatus != TR_CP_COMPLETE;
1247}
1248
1249- (BOOL) canChangeDownloadCheckForFiles: (NSIndexSet *) indexSet
1250{
1251    if ([self fileCount] <= 1 || [self isComplete])
1252        return NO;
1253   
1254    if (!fileStat)
1255        [self updateFileStat];
1256   
1257    int index;
1258    for (index = [indexSet firstIndex]; index != NSNotFound; index = [indexSet indexGreaterThanIndex: index])
1259        if (fileStat[index].completionStatus != TR_CP_COMPLETE)
1260            return YES;
1261    return NO;
1262}
1263
1264- (int) checkForFiles: (NSIndexSet *) indexSet
1265{
1266    BOOL onState = NO, offState = NO;
1267    int index;
1268    for (index = [indexSet firstIndex]; index != NSNotFound; index = [indexSet indexGreaterThanIndex: index])
1269    {
1270        if (tr_torrentGetFileDL(fHandle, index) || ![self canChangeDownloadCheckForFile: index])
1271            onState = YES;
1272        else
1273            offState = YES;
1274       
1275        if (onState && offState)
1276            return NSMixedState;
1277    }
1278    return onState ? NSOnState : NSOffState;
1279}
1280
1281- (void) setFileCheckState: (int) state forIndexes: (NSIndexSet *) indexSet
1282{
1283    int count = [indexSet count], i = 0, index;
1284    int * files = malloc(count * sizeof(int));
1285    for (index = [indexSet firstIndex]; index != NSNotFound; index = [indexSet indexGreaterThanIndex: index])
1286    {
1287        files[i] = index;
1288        i++;
1289    }
1290   
1291    tr_torrentSetFileDLs(fHandle, files, count, state != NSOffState);
1292    free(files);
1293   
1294    [self update];
1295}
1296
1297- (void) setFilePriority: (int) priority forIndexes: (NSIndexSet *) indexSet
1298{
1299    int count = [indexSet count], i = 0, index;
1300    int * files = malloc(count * sizeof(int));
1301    for (index = [indexSet firstIndex]; index != NSNotFound; index = [indexSet indexGreaterThanIndex: index])
1302    {
1303        files[i] = index;
1304        i++;
1305    }
1306
1307    tr_torrentSetFilePriorities(fHandle, files, count, priority);
1308    free(files);
1309}
1310
1311- (BOOL) hasFilePriority: (int) priority forIndexes: (NSIndexSet *) indexSet
1312{
1313    int index;
1314    for (index = [indexSet firstIndex]; index != NSNotFound; index = [indexSet indexGreaterThanIndex: index])
1315        if (priority == tr_torrentGetFilePriority(fHandle, index) && [self canChangeDownloadCheckForFile: index])
1316            return YES;
1317    return NO;
1318}
1319
1320- (NSSet *) filePrioritiesForIndexes: (NSIndexSet *) indexSet
1321{
1322    BOOL low = NO, normal = NO, high = NO;
1323    NSMutableSet * priorities = [NSMutableSet setWithCapacity: 3];
1324   
1325    int index, priority;
1326    for (index = [indexSet firstIndex]; index != NSNotFound; index = [indexSet indexGreaterThanIndex: index])
1327    {
1328        if (![self canChangeDownloadCheckForFile: index])
1329            continue;
1330       
1331        priority = tr_torrentGetFilePriority(fHandle, index);
1332        if (priority == TR_PRI_LOW)
1333        {
1334            if (low)
1335                continue;
1336            low = YES;
1337        }
1338        else if (priority == TR_PRI_HIGH)
1339        {
1340            if (high)
1341                continue;
1342            high = YES;
1343        }
1344        else
1345        {
1346            if (normal)
1347                continue;
1348            normal = YES;
1349        }
1350       
1351        [priorities addObject: [NSNumber numberWithInt: priority]];
1352        if (low && normal && high)
1353            break;
1354    }
1355    return priorities;
1356}
1357
1358- (NSMenu *) fileMenu
1359{
1360    if (!fFileMenu)
1361    {
1362        fFileMenu = [[NSMenu alloc] initWithTitle: [@"TorrentMenu:" stringByAppendingString: [self name]]];
1363        [fFileMenu setAutoenablesItems: NO];
1364    }
1365    return fFileMenu;
1366}
1367
1368- (NSDate *) dateAdded
1369{
1370    return fDateAdded;
1371}
1372
1373- (NSDate *) dateCompleted
1374{
1375    return fDateCompleted;
1376}
1377
1378- (NSDate *) dateActivity
1379{
1380    uint64_t date = fStat->activityDate;
1381    return date != 0 ? [NSDate dateWithTimeIntervalSince1970: date / 1000] : fDateActivity;
1382}
1383
1384- (int) stalledMinutes
1385{
1386    uint64_t start;
1387    if ((start = fStat->startDate) == 0)
1388        return -1;
1389   
1390    NSDate * started = [NSDate dateWithTimeIntervalSince1970: start / 1000],
1391            * activity = [self dateActivity];
1392   
1393    NSDate * laterDate = (!activity || [started compare: activity] == NSOrderedDescending) ? started : activity;
1394    return -1 * [laterDate timeIntervalSinceNow] / 60;
1395}
1396
1397- (BOOL) isStalled
1398{
1399    return fStalled;
1400}
1401
1402- (NSNumber *) stateSortKey
1403{
1404    if (![self isActive])
1405        return [NSNumber numberWithInt: 0];
1406    else if ([self isSeeding])
1407        return [NSNumber numberWithInt: 1];
1408    else
1409        return [NSNumber numberWithInt: 2];
1410}
1411
1412- (NSNumber *) progressSortKey
1413{
1414    return [NSNumber numberWithFloat: [self progress]];
1415}
1416
1417- (NSNumber *) ratioSortKey
1418{
1419    return [NSNumber numberWithFloat: [self ratio]];
1420}
1421
1422- (NSNumber *) ratioProgressSortKey
1423{
1424    return [NSNumber numberWithFloat: [self progressStopRatio]];
1425}
1426
1427- (int) torrentID
1428{
1429    return fID;
1430}
1431
1432- (const tr_info *) torrentInfo
1433{
1434    return fInfo;
1435}
1436
1437- (const tr_stat *) torrentStat
1438{
1439    return fStat;
1440}
1441
1442@end
1443
1444@implementation Torrent (Private)
1445
1446//if a hash is given, attempt to load that; otherwise, attempt to open file at path
1447- (id) initWithHash: (NSString *) hashString path: (NSString *) path lib: (tr_handle *) lib
1448        publicTorrent: (NSNumber *) publicTorrent
1449        downloadFolder: (NSString *) downloadFolder
1450        useIncompleteFolder: (NSNumber *) useIncompleteFolder incompleteFolder: (NSString *) incompleteFolder
1451        dateAdded: (NSDate *) dateAdded dateCompleted: (NSDate *) dateCompleted
1452        dateActivity: (NSDate *) dateActivity
1453        ratioSetting: (NSNumber *) ratioSetting ratioLimit: (NSNumber *) ratioLimit
1454                pex: (NSNumber *) pex
1455        waitToStart: (NSNumber *) waitToStart orderValue: (NSNumber *) orderValue;
1456{
1457    if (!(self = [super init]))
1458        return nil;
1459   
1460    static_lastid++;
1461    fID = static_lastid;
1462   
1463    fLib = lib;
1464    fDefaults = [NSUserDefaults standardUserDefaults];
1465
1466    fPublicTorrent = path && (publicTorrent ? [publicTorrent boolValue] : ![fDefaults boolForKey: @"DeleteOriginalTorrent"]);
1467    if (fPublicTorrent)
1468        fPublicTorrentLocation = [path retain];
1469   
1470    fDownloadFolder = downloadFolder ? downloadFolder : [fDefaults stringForKey: @"DownloadFolder"];
1471    fDownloadFolder = [[fDownloadFolder stringByExpandingTildeInPath] retain];
1472   
1473    fUseIncompleteFolder = useIncompleteFolder ? [useIncompleteFolder boolValue]
1474                                : [fDefaults boolForKey: @"UseIncompleteDownloadFolder"];
1475    if (fUseIncompleteFolder)
1476    {
1477        fIncompleteFolder = incompleteFolder ? incompleteFolder : [fDefaults stringForKey: @"IncompleteDownloadFolder"];
1478        fIncompleteFolder = [[fIncompleteFolder stringByExpandingTildeInPath] retain];
1479    }
1480   
1481    NSString * currentDownloadFolder;
1482    tr_info info;
1483    int error;
1484    if (hashString)
1485    {
1486        if (tr_torrentParseHash(fLib, [hashString UTF8String], NULL, &info) == TR_OK)
1487        {
1488            currentDownloadFolder = [self shouldUseIncompleteFolderForName: [NSString stringWithUTF8String: info.name]]
1489                                        ? fIncompleteFolder : fDownloadFolder;
1490            fHandle = tr_torrentInitSaved(fLib, [hashString UTF8String], [currentDownloadFolder UTF8String],
1491                                            TR_FLAG_SAVE | TR_FLAG_PAUSED, &error);
1492        }
1493        tr_metainfoFree(&info);
1494    }
1495    if (!fHandle && path)
1496    {
1497        if (tr_torrentParse(fLib, [path UTF8String], NULL, &info) == TR_OK)
1498        {
1499            currentDownloadFolder = [self shouldUseIncompleteFolderForName: [NSString stringWithUTF8String: info.name]]
1500                                        ? fIncompleteFolder : fDownloadFolder;
1501            fHandle = tr_torrentInit(fLib, [path UTF8String], [currentDownloadFolder UTF8String],
1502                                        TR_FLAG_SAVE | TR_FLAG_PAUSED, &error);
1503        }
1504        tr_metainfoFree(&info);
1505    }
1506    if (!fHandle)
1507    {
1508        [self release];
1509        return nil;
1510    }
1511   
1512    fInfo = tr_torrentInfo(fHandle);
1513
1514    fDateAdded = dateAdded ? [dateAdded retain] : [[NSDate alloc] init];
1515        if (dateCompleted)
1516                fDateCompleted = [dateCompleted retain];
1517    if (dateActivity)
1518                fDateActivity = [dateActivity retain];
1519       
1520    fRatioSetting = ratioSetting ? [ratioSetting intValue] : NSMixedState;
1521    fRatioLimit = ratioLimit ? [ratioLimit floatValue] : [fDefaults floatForKey: @"RatioLimit"];
1522    fFinishedSeeding = NO;
1523       
1524        if ([self privateTorrent])
1525                fPex = NO;
1526        else
1527                fPex = pex ? [pex boolValue] : YES;
1528        tr_torrentDisablePex(fHandle, !fPex);
1529   
1530    fWaitToStart = waitToStart ? [waitToStart boolValue] : [fDefaults boolForKey: @"AutoStartDownload"];
1531    fOrderValue = orderValue ? [orderValue intValue] : tr_torrentCount(fLib) - 1;
1532    fError = NO;
1533   
1534    [self createFileList];
1535   
1536    [self update];
1537    return self;
1538}
1539
1540- (void) createFileList
1541{
1542    int count = [self fileCount], i;
1543    tr_file * file;
1544    NSMutableArray * pathComponents;
1545    NSString * path;
1546   
1547    NSMutableArray * fileList = [[NSMutableArray alloc] init],
1548                    * flatFileList = [[NSMutableArray alloc] initWithCapacity: count];
1549   
1550    for (i = 0; i < count; i++)
1551    {
1552        file = &fInfo->files[i];
1553       
1554        pathComponents = [[[NSString stringWithUTF8String: file->name] pathComponents] mutableCopy];
1555        if (fInfo->multifile)
1556        {
1557            path = [pathComponents objectAtIndex: 0];
1558            [pathComponents removeObjectAtIndex: 0];
1559        }
1560        else
1561            path = @"";
1562       
1563        [self insertPath: pathComponents forSiblings: fileList withParent: nil previousPath: path
1564                flatList: flatFileList fileSize: file->length index: i];
1565        [pathComponents autorelease];
1566    }
1567   
1568    fFileList = [[NSArray alloc] initWithArray: fileList];
1569    [fileList release];
1570    fFlatFileList = [[NSArray alloc] initWithArray: flatFileList];
1571    [flatFileList release];
1572}
1573
1574- (void) insertPath: (NSMutableArray *) components forSiblings: (NSMutableArray *) siblings
1575        withParent: (NSMutableDictionary *) parent previousPath: (NSString *) previousPath
1576        flatList: (NSMutableArray *) flatList fileSize: (uint64_t) size index: (int) index
1577{
1578    NSString * name = [components objectAtIndex: 0];
1579    BOOL isFolder = [components count] > 1;
1580   
1581    NSMutableDictionary * dict = nil;
1582    if (isFolder)
1583    {
1584        NSEnumerator * enumerator = [siblings objectEnumerator];
1585        while ((dict = [enumerator nextObject]))
1586            if ([[dict objectForKey: @"IsFolder"] boolValue] && [[dict objectForKey: @"Name"] isEqualToString: name])
1587                break;
1588    }
1589   
1590    NSString * currentPath = [previousPath stringByAppendingPathComponent: name];
1591   
1592    //create new folder or item if it doesn't already exist
1593    if (!dict)
1594    {
1595        dict = [NSMutableDictionary dictionaryWithObjectsAndKeys: name, @"Name",
1596                [NSNumber numberWithBool: isFolder], @"IsFolder", currentPath, @"Path", nil];
1597        [siblings addObject: dict];
1598       
1599        if (isFolder)
1600        {
1601            [dict setObject: [NSMutableArray array] forKey: @"Children"];
1602            [dict setObject: [NSMutableIndexSet indexSetWithIndex: index] forKey: @"Indexes"];
1603        }
1604        else
1605        {
1606            [dict setObject: [NSIndexSet indexSetWithIndex: index] forKey: @"Indexes"];
1607            [dict setObject: [NSNumber numberWithUnsignedLongLong: size] forKey: @"Size"];
1608           
1609            NSImage * icon = [[NSWorkspace sharedWorkspace] iconForFileType: [name pathExtension]];
1610            [icon setFlipped: YES];
1611            [dict setObject: icon forKey: @"Icon"];
1612           
1613            [flatList addObject: dict];
1614        }
1615       
1616        if (parent)
1617            [dict setObject: parent forKey: @"Parent"];
1618    }
1619    else
1620        [[dict objectForKey: @"Indexes"] addIndex: index];
1621   
1622    if (isFolder)
1623    {
1624        [components removeObjectAtIndex: 0];
1625        [self insertPath: components forSiblings: [dict objectForKey: @"Children"]
1626            withParent: dict previousPath: currentPath flatList: flatList fileSize: size index: index];
1627    }
1628}
1629
1630- (BOOL) shouldUseIncompleteFolderForName: (NSString *) name
1631{
1632    return fUseIncompleteFolder &&
1633        ![[NSFileManager defaultManager] fileExistsAtPath: [fDownloadFolder stringByAppendingPathComponent: name]];
1634}
1635
1636- (void) updateDownloadFolder
1637{
1638    NSString * folder = [self shouldUseIncompleteFolderForName: [self name]] ? fIncompleteFolder : fDownloadFolder;
1639    tr_torrentSetFolder(fHandle, [folder UTF8String]);
1640}
1641
1642- (void) quickPause
1643{
1644    if (fQuickPauseDict)
1645        return;
1646
1647    fQuickPauseDict = [[NSDictionary alloc] initWithObjectsAndKeys:
1648                    [NSNumber numberWithInt: [self speedMode: YES]], @"UploadSpeedMode",
1649                    [NSNumber numberWithInt: [self speedLimit: YES]], @"UploadSpeedLimit",
1650                    [NSNumber numberWithInt: [self speedMode: NO]], @"DownloadSpeedMode",
1651                    [NSNumber numberWithInt: [self speedLimit: NO]], @"DownloadSpeedLimit", nil];
1652   
1653    [self setSpeedMode: TR_SPEEDLIMIT_SINGLE upload: YES];
1654    [self setSpeedLimit: 0 upload: YES];
1655    [self setSpeedMode: TR_SPEEDLIMIT_SINGLE upload: NO];
1656    [self setSpeedLimit: 0 upload: NO];
1657}
1658
1659- (void) endQuickPause
1660{
1661    if (!fQuickPauseDict)
1662        return;
1663   
1664    [self setSpeedMode: [[fQuickPauseDict objectForKey: @"UploadSpeedMode"] intValue] upload: YES];
1665    [self setSpeedLimit: [[fQuickPauseDict objectForKey: @"UploadSpeedLimit"] intValue] upload: YES];
1666    [self setSpeedMode: [[fQuickPauseDict objectForKey: @"DownloadSpeedMode"] intValue] upload: NO];
1667    [self setSpeedLimit: [[fQuickPauseDict objectForKey: @"DownloadSpeedLimit"] intValue] upload: NO];
1668   
1669    [fQuickPauseDict release];
1670    fQuickPauseDict = nil;
1671}
1672
1673- (void) trashFile: (NSString *) path
1674{
1675    //attempt to move to trash
1676    if (![[NSWorkspace sharedWorkspace] performFileOperation: NSWorkspaceRecycleOperation
1677            source: [path stringByDeletingLastPathComponent] destination: @""
1678            files: [NSArray arrayWithObject: [path lastPathComponent]] tag: nil])
1679    {
1680        //if cannot trash, just delete it (will work if it is on a remote volume)
1681        if (![[NSFileManager defaultManager] removeFileAtPath: path handler: nil])
1682            NSLog(@"Could not trash %@", path);
1683    }
1684}
1685
1686@end
Note: See TracBrowser for help on using the repository browser.