source: trunk/macosx/Torrent.m @ 770

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

Fix for when the returned client string is "unknown client ("

  • Property svn:keywords set to Date Rev Author Id
File size: 20.3 KB
Line 
1/******************************************************************************
2 * $Id: Torrent.m 770 2006-08-13 21:56:20Z livings124 $
3 *
4 * Copyright (c) 2006 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 "StringAdditions.h"
27
28@interface Torrent (Private)
29
30- (id) initWithHash: (NSString *) hashString path: (NSString *) path lib: (tr_handle_t *) lib
31        privateTorrent: (NSNumber *) privateTorrent publicTorrent: (NSNumber *) publicTorrent
32        date: (NSDate *) date stopRatioSetting: (NSNumber *) stopRatioSetting
33        ratioLimit: (NSNumber *) ratioLimit waitToStart: (NSNumber *) waitToStart
34        orderValue: (NSNumber *) orderValue;
35
36- (void) trashFile: (NSString *) path;
37
38@end
39
40
41@implementation Torrent
42
43- (id) initWithPath: (NSString *) path lib: (tr_handle_t *) lib
44{
45    self = [self initWithHash: nil path: path lib: lib privateTorrent: nil publicTorrent: nil
46            date: nil stopRatioSetting: nil ratioLimit: nil waitToStart: nil orderValue: nil];
47   
48    if (self)
49    {
50        if (!fPublicTorrent)
51            [self trashFile: path];
52    }
53    return self;
54}
55
56- (id) initWithHistory: (NSDictionary *) history lib: (tr_handle_t *) lib
57{
58    self = [self initWithHash: [history objectForKey: @"TorrentHash"]
59                path: [history objectForKey: @"TorrentPath"] lib: lib
60                privateTorrent: [history objectForKey: @"PrivateCopy"]
61                publicTorrent: [history objectForKey: @"PublicCopy"]
62                date: [history objectForKey: @"Date"]
63                stopRatioSetting: [history objectForKey: @"StopRatioSetting"]
64                ratioLimit: [history objectForKey: @"RatioLimit"]
65                waitToStart: [history objectForKey: @"WaitToStart"]
66                orderValue: [history objectForKey: @"OrderValue"]];
67   
68    if (self)
69    {
70        NSString * downloadFolder;
71        if (!(downloadFolder = [history objectForKey: @"DownloadFolder"]))
72            downloadFolder = [[fDefaults stringForKey: @"DownloadFolder"] stringByExpandingTildeInPath];
73        [self setDownloadFolder: downloadFolder];
74
75        NSString * paused;
76        if (!(paused = [history objectForKey: @"Paused"]) || [paused isEqualToString: @"NO"])
77            tr_torrentStart(fHandle);
78    }
79    return self;
80}
81
82- (NSDictionary *) history
83{
84    NSMutableDictionary * history = [NSMutableDictionary dictionaryWithObjectsAndKeys:
85                    [NSNumber numberWithBool: fPrivateTorrent], @"PrivateCopy",
86                    [NSNumber numberWithBool: fPublicTorrent], @"PublicCopy",
87                    [self downloadFolder], @"DownloadFolder",
88                    [self isActive] ? @"NO" : @"YES", @"Paused",
89                    [self date], @"Date",
90                    [NSNumber numberWithInt: fStopRatioSetting], @"StopRatioSetting",
91                    [NSNumber numberWithFloat: fRatioLimit], @"RatioLimit",
92                    [NSNumber numberWithBool: fWaitToStart], @"WaitToStart",
93                    [self orderValue], @"OrderValue", nil];
94           
95    if (fPrivateTorrent)
96        [history setObject: [self hashString] forKey: @"TorrentHash"];
97
98    if (fPublicTorrent)
99        [history setObject: [self publicTorrentLocation] forKey: @"TorrentPath"];
100   
101    return history;
102}
103
104- (void) dealloc
105{
106    if (fHandle)
107    {
108        tr_torrentClose(fLib, fHandle);
109       
110        if (fPublicTorrentLocation)
111            [fPublicTorrentLocation release];
112       
113        [fDate release];
114       
115        [fIcon release];
116        [fIconFlipped release];
117        [fIconSmall release];
118       
119        [fProgressString release];
120        [fStatusString release];
121        [fShortStatusString release];
122        [fRemainingTimeString release];
123    }
124    [super dealloc];
125}
126
127- (void) setDownloadFolder: (NSString *) path
128{
129    tr_torrentSetFolder(fHandle, [path UTF8String]);
130}
131
132- (NSString *) downloadFolder
133{
134    return [NSString stringWithUTF8String: tr_torrentGetFolder(fHandle)];
135}
136
137- (void) getAvailability: (int8_t *) tab size: (int) size
138{
139    tr_torrentAvailability(fHandle, tab, size);
140}
141
142- (void) update
143{
144    fStat = tr_torrentStat(fHandle);
145   
146    //notification when downloading finished
147    if ([self justFinished])
148        [[NSNotificationCenter defaultCenter] postNotificationName: @"TorrentFinishedDownloading" object: self];
149   
150    //check to stop for ratio
151    if ([self isSeeding] && ((fStopRatioSetting == RATIO_CHECK && [self ratio] >= fRatioLimit)
152            || (fStopRatioSetting == RATIO_GLOBAL && [fDefaults boolForKey: @"RatioCheck"]
153            && [self ratio] >= [fDefaults floatForKey: @"RatioLimit"])))
154    {
155        [self stopTransfer];
156        [self setStopRatioSetting: RATIO_NO_CHECK];
157        fFinishedSeeding = YES;
158       
159        fStat = tr_torrentStat(fHandle);
160       
161        [[NSNotificationCenter defaultCenter] postNotificationName: @"TorrentStoppedForRatio" object: self];
162    }
163
164    [fProgressString setString: @""];
165    if ([self progress] < 1.0)
166        [fProgressString appendFormat: @"%@ of %@ (%.2f%%)", [NSString stringForFileSize:
167                [self downloadedValid]], [NSString stringForFileSize: [self size]], 100.0 * [self progress]];
168    else
169        [fProgressString appendFormat: @"%@, uploaded %@ (Ratio: %@)", [NSString stringForFileSize:
170                [self size]], [NSString stringForFileSize: [self uploadedTotal]],
171                [NSString stringForRatioWithDownload: [self downloadedTotal] upload: [self uploadedTotal]]];
172
173    switch (fStat->status)
174    {
175        NSString * tempString;
176   
177        case TR_STATUS_PAUSE:
178            if (fFinishedSeeding)
179                tempString = @"Seeding complete";
180            else if (fWaitToStart && [[fDefaults stringForKey: @"StartSetting"] isEqualToString: @"Wait"])
181                tempString = [@"Waiting to start" stringByAppendingEllipsis];
182            else
183                tempString = @"Paused";
184           
185            [fStatusString setString: tempString];
186            [fShortStatusString setString: tempString];
187           
188            break;
189
190        case TR_STATUS_CHECK:
191            tempString = [@"Checking existing files" stringByAppendingEllipsis];
192           
193            [fStatusString setString: tempString];
194            [fShortStatusString setString: tempString];
195            [fRemainingTimeString setString: tempString];
196           
197            break;
198
199        case TR_STATUS_DOWNLOAD:
200            [fStatusString setString: @""];
201            [fStatusString appendFormat:
202                @"Downloading from %d of %d peer%s", [self peersUploading], [self totalPeers],
203                [self totalPeers] == 1 ? "" : "s"];
204           
205            [fRemainingTimeString setString: @""];
206            int eta = [self eta];
207            if (eta < 0)
208            {
209                [fRemainingTimeString setString: @"Unknown"];
210                [fProgressString appendString: @" - remaining time unknown"];
211            }
212            else
213            {
214                if (eta < 60)
215                    [fRemainingTimeString appendFormat: @"%d sec", eta];
216                else if (eta < 3600) //60 * 60
217                    [fRemainingTimeString appendFormat: @"%d min %02d sec", eta / 60, eta % 60];
218                else if (eta < 86400) //24 * 60 * 60
219                    [fRemainingTimeString appendFormat: @"%d hr %02d min", eta / 3600, (eta / 60) % 60];
220                else
221                    [fRemainingTimeString appendFormat: @"%d day%s %d hr",
222                                                eta / 86400, eta / 86400 == 1 ? "" : "s", (eta / 3600) % 24];
223               
224                [fProgressString appendFormat: @" - %@ remaining", fRemainingTimeString];
225            }
226           
227            break;
228
229        case TR_STATUS_SEED:
230            [fStatusString setString: @""];
231            [fStatusString appendFormat:
232                @"Seeding to %d of %d peer%s",
233                [self peersDownloading], [self totalPeers], [self totalPeers] == 1 ? "" : "s"];
234           
235            break;
236
237        case TR_STATUS_STOPPING:
238            tempString = [@"Stopping" stringByAppendingEllipsis];
239       
240            [fStatusString setString: tempString];
241            [fShortStatusString setString: tempString];
242           
243            break;
244    }
245   
246    if( fStat->error & TR_ETRACKER )
247        [fStatusString setString: [@"Error: " stringByAppendingString:
248                        [NSString stringWithUTF8String: fStat->trackerError]]];
249
250    if ([self isActive])
251    {
252        NSString * stringToAppend = @"";
253        if ([self progress] < 1.0)
254        {
255            stringToAppend = [NSString stringWithFormat: @"DL: %@, ", [NSString stringForSpeed: [self downloadRate]]];
256            [fShortStatusString setString: @""];
257        }
258        else
259        {
260            NSString * ratioString = [NSString stringForRatioWithDownload: [self downloadedTotal]
261                                                upload: [self uploadedTotal]];
262       
263            [fShortStatusString setString: [NSString stringWithFormat: @"Ratio: %@, ", ratioString]];
264            [fRemainingTimeString setString: [@"Ratio: " stringByAppendingString: ratioString]];
265        }
266       
267        stringToAppend = [stringToAppend stringByAppendingString: [@"UL: " stringByAppendingString:
268                                                [NSString stringForSpeed: [self uploadRate]]]];
269
270        [fStatusString appendFormat: @" - %@", stringToAppend];
271        [fShortStatusString appendString: stringToAppend];
272    }
273}
274
275- (void) startTransfer
276{
277    if (![self isActive])
278    {
279        tr_torrentStart(fHandle);
280
281        fFinishedSeeding = NO;
282        fWaitToStart = NO;
283       
284        [[NSNotificationCenter defaultCenter] postNotificationName: @"TorrentSettingChange" object: self];
285    }
286}
287
288- (void) stopTransfer
289{
290    if ([self isActive])
291    {
292        BOOL wasSeeding = [self isSeeding];
293   
294        tr_torrentStop(fHandle);
295
296        if (!wasSeeding)
297            [[NSNotificationCenter defaultCenter] postNotificationName: @"StoppedDownloading" object: self];
298    }
299}
300
301- (void) stopTransferForQuit
302{
303    if ([self isActive])
304        tr_torrentStop(fHandle);
305}
306
307- (void) removeForever
308{
309    if (fPrivateTorrent)
310        tr_torrentRemoveSaved(fHandle);
311}
312
313- (void) sleep
314{
315    if ((fResumeOnWake = [self isActive]))
316        tr_torrentStop(fHandle);
317}
318
319- (void) wakeUp
320{
321    if (fResumeOnWake)
322        tr_torrentStart(fHandle);
323}
324
325- (float) ratio
326{
327    float downloaded = [self downloadedTotal];
328    return downloaded > 0 ? (float)[self uploadedTotal] / downloaded : -1;
329}
330
331- (int) stopRatioSetting
332{
333        return fStopRatioSetting;
334}
335
336- (void) setStopRatioSetting: (int) setting
337{
338    fStopRatioSetting = setting;
339}
340
341- (float) ratioLimit
342{
343    return fRatioLimit;
344}
345
346- (void) setRatioLimit: (float) limit
347{
348    if (limit >= 0)
349        fRatioLimit = limit;
350}
351
352- (void) setWaitToStart: (BOOL) wait
353{
354    fWaitToStart = wait;
355}
356
357- (BOOL) waitingToStart
358{
359    return fWaitToStart;
360}
361
362- (void) revealData
363{
364    [[NSWorkspace sharedWorkspace] selectFile: [self dataLocation] inFileViewerRootedAtPath: nil];
365}
366
367- (void) trashData
368{
369    [self trashFile: [self dataLocation]];
370}
371
372- (void) trashTorrent
373{
374    if (fPublicTorrent)
375        [self trashFile: [self publicTorrentLocation]];
376}
377
378- (NSImage *) icon
379{
380    return fIcon;
381}
382
383- (NSImage *) iconFlipped
384{
385    return fIconFlipped;
386}
387
388- (NSImage *) iconSmall
389{
390    return fIconSmall;
391}
392
393- (NSString *) name
394{
395    return [NSString stringWithUTF8String: fInfo->name];
396}
397
398- (uint64_t) size
399{
400    return fInfo->totalSize;
401}
402
403- (NSString *) tracker
404{
405    return [NSString stringWithFormat: @"%s:%d", fInfo->trackerAddress, fInfo->trackerPort];
406}
407
408- (NSString *) announce
409{
410    return [NSString stringWithUTF8String: fInfo->trackerAnnounce];
411}
412
413- (int) pieceSize
414{
415    return fInfo->pieceSize;
416}
417
418- (int) pieceCount
419{
420    return fInfo->pieceCount;
421}
422
423- (NSString *) hashString
424{
425    return [NSString stringWithUTF8String: fInfo->hashString];
426}
427
428- (NSString *) torrentLocation
429{
430    return [NSString stringWithUTF8String: fInfo->torrent];
431}
432
433- (NSString *) publicTorrentLocation
434{
435    return fPublicTorrentLocation;
436}
437
438- (NSString *) torrentLocationString
439{
440    return fPrivateTorrent ? @"Transmission Support Folder"
441            : [fPublicTorrentLocation stringByAbbreviatingWithTildeInPath];
442}
443
444- (NSString *) dataLocation
445{
446    return [[self downloadFolder] stringByAppendingPathComponent: [self name]];
447}
448
449- (BOOL) publicTorrent
450{
451    return fPublicTorrent;
452}
453
454- (BOOL) privateTorrent
455{
456    return fPrivateTorrent;
457}
458
459- (NSString *) stateString
460{
461    switch( fStat->status )
462    {
463        case TR_STATUS_PAUSE:
464            return @"Paused";
465            break;
466
467        case TR_STATUS_CHECK:
468            return [@"Checking existing files" stringByAppendingEllipsis];
469            break;
470
471        case TR_STATUS_DOWNLOAD:
472            return @"Downloading";
473            break;
474
475        case TR_STATUS_SEED:
476            return @"Seeding";
477            break;
478
479        case TR_STATUS_STOPPING:
480            return [@"Stopping" stringByAppendingEllipsis];
481            break;
482       
483        default:
484            return @"N/A";
485    }
486}
487
488- (float) progress
489{
490    return fStat->progress;
491}
492
493- (int) eta
494{
495    return fStat->eta;
496}
497
498- (BOOL) isActive
499{
500    return fStat->status & TR_STATUS_ACTIVE;
501}
502
503- (BOOL) isSeeding
504{
505    return fStat->status == TR_STATUS_SEED;
506}
507
508- (BOOL) isPaused
509{
510    return fStat->status == TR_STATUS_PAUSE;
511}
512
513- (BOOL) justFinished
514{
515    return tr_getFinished(fHandle);
516}
517
518- (NSArray *) peers
519{
520    int totalPeers, i;
521   
522    tr_peer_stat_t * peers = tr_torrentPeers(fHandle, & totalPeers);
523    tr_peer_stat_t peer;
524   
525    NSMutableArray * peerDics = [NSMutableArray arrayWithCapacity: totalPeers];
526    NSString * client;
527    for (i = 0; i < totalPeers; i++)
528    {
529        peer = peers[i];
530   
531        client = [NSString stringWithCString: (char *) peer.client encoding: NSUTF8StringEncoding];
532        //get rid of strange returned client strings
533        if ([client hasPrefix: @"unknown client ("] && ![client hasSuffix: @")"])
534            client = @"unknown client";
535   
536        [peerDics addObject: [NSDictionary dictionaryWithObjectsAndKeys:
537            client, @"Client",
538            [NSNumber numberWithBool: peer.isDownloading], @"UL To",
539            [NSNumber numberWithBool: peer.isUploading], @"DL From", nil]];
540    }
541    //NSLog(@"%d", tr_peerId(peer));
542   
543    tr_torrentPeersFree(peers, totalPeers);
544   
545    return peerDics;
546}
547
548- (NSString *) progressString
549{
550    return fProgressString;
551}
552
553- (NSString *) statusString
554{
555    return fStatusString;
556}
557
558- (NSString *) shortStatusString
559{
560    return fShortStatusString;
561}
562
563- (NSString *) remainingTimeString
564{
565    return fRemainingTimeString;
566}
567
568- (int) seeders
569{
570    return fStat->seeders;
571}
572
573- (int) leechers
574{
575    return fStat->leechers;
576}
577
578- (int) totalPeers
579{
580    return fStat->peersTotal;
581}
582
583//peers uploading to you
584- (int) peersUploading
585{
586    return fStat->peersUploading;
587}
588
589//peers downloading from you
590- (int) peersDownloading
591{
592    return fStat->peersDownloading;
593}
594
595- (float) downloadRate
596{
597    return fStat->rateDownload;
598}
599
600- (float) uploadRate
601{
602    return fStat->rateUpload;
603}
604
605- (float) downloadedValid
606{
607    return [self progress] * [self size];
608}
609
610- (uint64_t) downloadedTotal
611{
612    return fStat->downloaded;
613}
614
615- (uint64_t) uploadedTotal
616{
617    return fStat->uploaded;
618}
619
620- (NSNumber *) orderValue
621{
622    return [NSNumber numberWithInt: fOrderValue];
623}
624
625- (void) setOrderValue: (int) orderValue
626{
627    fOrderValue = orderValue;
628}
629
630- (NSArray *) fileList
631{
632    int count = fInfo->fileCount, i;
633    NSMutableArray * files = [NSMutableArray arrayWithCapacity: count];
634    for (i = 0; i < count; i++)
635        [files addObject: [[self downloadFolder] stringByAppendingPathComponent:
636            [NSString stringWithUTF8String: fInfo->files[i].name]]];
637    return files;
638}
639
640- (NSDate *) date
641{
642    return fDate;
643}
644
645- (NSNumber *) stateSortKey
646{
647    if (![self isActive])
648        return [NSNumber numberWithInt: 0];
649    else if ([self isSeeding])
650        return [NSNumber numberWithInt: 1];
651    else
652        return [NSNumber numberWithInt: 2];
653}
654
655- (NSNumber *) progressSortKey
656{
657    //if finished downloading sort by ratio instead of progress
658    float progress = [self progress];
659    return [NSNumber numberWithFloat: progress < 1.0 ? progress : 2.0 + [self ratio]];
660}
661
662@end
663
664
665@implementation Torrent (Private)
666
667//if a hash is given, attempt to load that; otherwise, attempt to open file at path
668- (id) initWithHash: (NSString *) hashString path: (NSString *) path lib: (tr_handle_t *) lib
669        privateTorrent: (NSNumber *) privateTorrent publicTorrent: (NSNumber *) publicTorrent
670        date: (NSDate *) date stopRatioSetting: (NSNumber *) stopRatioSetting
671        ratioLimit: (NSNumber *) ratioLimit waitToStart: (NSNumber *) waitToStart
672        orderValue: (NSNumber *) orderValue
673{
674    if (!(self = [super init]))
675        return nil;
676
677    fLib = lib;
678    fDefaults = [NSUserDefaults standardUserDefaults];
679
680    fPrivateTorrent = privateTorrent ? [privateTorrent boolValue] : [fDefaults boolForKey: @"SavePrivateTorrent"];
681    fPublicTorrent = !fPrivateTorrent || (publicTorrent ? [publicTorrent boolValue]
682                                            : ![fDefaults boolForKey: @"DeleteOriginalTorrent"]);
683
684    int error;
685    if (fPrivateTorrent && hashString)
686        fHandle = tr_torrentInitSaved(fLib, [hashString UTF8String], TR_FSAVEPRIVATE, & error);
687   
688    if (!fHandle && path)
689        fHandle = tr_torrentInit(fLib, [path UTF8String], fPrivateTorrent ? TR_FSAVEPRIVATE : 0, & error);
690
691    if (!fHandle)
692    {
693        [self release];
694        return nil;
695    }
696   
697    fInfo = tr_torrentInfo( fHandle );
698
699    if (fPublicTorrent)
700        fPublicTorrentLocation = [path retain];
701
702    fDate = date ? [date retain] : [[NSDate alloc] init];
703   
704    fStopRatioSetting = stopRatioSetting ? [stopRatioSetting intValue] : -1;
705    fRatioLimit = ratioLimit ? [ratioLimit floatValue] : [fDefaults floatForKey: @"RatioLimit"];
706    fFinishedSeeding = NO;
707   
708    fWaitToStart = waitToStart ? [waitToStart boolValue]
709                    : ![[fDefaults stringForKey: @"StartSetting"] isEqualToString: @"Manual"];
710    fOrderValue = orderValue ? [orderValue intValue] : tr_torrentCount(fLib) - 1;
711   
712    NSString * fileType = fInfo->multifile ? NSFileTypeForHFSTypeCode('fldr') : [[self name] pathExtension];
713    fIcon = [[NSWorkspace sharedWorkspace] iconForFileType: fileType];
714    [fIcon retain];
715   
716    fIconFlipped = [fIcon copy];
717    [fIconFlipped setFlipped: YES];
718   
719    fIconSmall = [fIconFlipped copy];
720    [fIconSmall setScalesWhenResized: YES];
721    [fIconSmall setSize: NSMakeSize(16.0, 16.0)];
722
723    fProgressString = [[NSMutableString alloc] initWithCapacity: 50];
724    fStatusString = [[NSMutableString alloc] initWithCapacity: 75];
725    fShortStatusString = [[NSMutableString alloc] initWithCapacity: 30];
726    fRemainingTimeString = [[NSMutableString alloc] initWithCapacity: 30];
727
728    [self update];
729    return self;
730}
731
732- (void) trashFile: (NSString *) path
733{
734    //attempt to move to trash
735    if (![[NSWorkspace sharedWorkspace] performFileOperation: NSWorkspaceRecycleOperation
736            source: [path stringByDeletingLastPathComponent] destination: @""
737            files: [NSArray arrayWithObject: [path lastPathComponent]] tag: nil])
738    {
739        //if cannot trash, just delete it (will work if it is on a remote volume)
740        if (![[NSFileManager defaultManager] removeFileAtPath: path handler: nil])
741            NSLog([@"Could not trash " stringByAppendingString: path]);
742    }
743}
744
745@end
Note: See TracBrowser for help on using the repository browser.