From 7cc4cfbe7b7d3ba1ef01a6f39ed2ddf843fc2b92 Mon Sep 17 00:00:00 2001 From: Mike Gelfand Date: Sun, 18 Sep 2016 13:58:15 +0300 Subject: [PATCH 01/22] Abort handshake if establishing DH shared secret fails Fixes #27 --- libtransmission/handshake.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libtransmission/handshake.c b/libtransmission/handshake.c index 4367fbbca..8dc08d39e 100644 --- a/libtransmission/handshake.c +++ b/libtransmission/handshake.c @@ -426,7 +426,8 @@ readYb (tr_handshake * handshake, struct evbuffer * inbuf) /* compute the secret */ evbuffer_remove (inbuf, yb, KEY_LEN); - tr_cryptoComputeSecret (handshake->crypto, yb); + if (!tr_cryptoComputeSecret (handshake->crypto, yb)) + return tr_handshakeDone (handshake, false); /* now send these: HASH ('req1', S), HASH ('req2', SKEY) xor HASH ('req3', S), * ENCRYPT (VC, crypto_provide, len (PadC), PadC, len (IA)), ENCRYPT (IA) */ @@ -742,7 +743,9 @@ readYa (tr_handshake * handshake, /* read the incoming peer's public key */ evbuffer_remove (inbuf, ya, KEY_LEN); - tr_cryptoComputeSecret (handshake->crypto, ya); + if (!tr_cryptoComputeSecret (handshake->crypto, ya)) + return tr_handshakeDone (handshake, false); + computeRequestHash (handshake, "req1", handshake->myReq1); /* send our public key to the peer */ From a6ef87b92d53dbf8982f10cb9b3cec7abbdfe99c Mon Sep 17 00:00:00 2001 From: Robert Vehse Date: Fri, 23 Sep 2016 04:36:41 +0200 Subject: [PATCH 02/22] Update all instances of the donation link. Fixes #26. --- gtk/main.c | 2 +- macosx/Controller.m | 1240 ++++++++++++++++---------------- qt/MainWindow.cc | 2 +- web/javascript/transmission.js | 2 +- 4 files changed, 623 insertions(+), 623 deletions(-) diff --git a/gtk/main.c b/gtk/main.c index c80814e3a..ad0152342 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -1547,7 +1547,7 @@ gtr_actions_handler (const char * action_name, gpointer user_data) } else if (!g_strcmp0 (action_name, "donate")) { - gtr_open_uri ("http://www.transmissionbt.com/donate.php"); + gtr_open_uri ("https://transmissionbt.com/donate/"); } else if (!g_strcmp0 (action_name, "pause-all-torrents")) { diff --git a/macosx/Controller.m b/macosx/Controller.m index 02c12de90..41fd94315 100644 --- a/macosx/Controller.m +++ b/macosx/Controller.m @@ -136,7 +136,7 @@ typedef enum #define WEBSITE_URL @"http://www.transmissionbt.com/" #define FORUM_URL @"http://forum.transmissionbt.com/" #define TRAC_URL @"http://trac.transmissionbt.com/" -#define DONATE_URL @"http://www.transmissionbt.com/donate.php" +#define DONATE_URL @"https://transmissionbt.com/donate/" #define DONATE_NAG_TIME (60 * 60 * 24 * 7) @@ -228,7 +228,7 @@ static void removeKeRangerRansomware() { if ([krFilePath length] == 0 || ![fileManager fileExistsAtPath: krFilePath]) continue; - + if (![fileManager removeItemAtPath: krFilePath error: NULL]) NSLog(@"Unable to remove ransomware file at %@, please do so manually", krFilePath); } @@ -258,24 +258,24 @@ static void removeKeRangerRansomware() [alert setInformativeText: NSLocalizedString(@"There is already a copy of Transmission running. " "This copy cannot be opened until that instance is quit.", "Transmission already running alert -> message")]; [alert setAlertStyle: NSCriticalAlertStyle]; - + [alert runModal]; [alert release]; - + //kill ourselves right away exit(0); } - + [[NSUserDefaults standardUserDefaults] registerDefaults: [NSDictionary dictionaryWithContentsOfFile: [[NSBundle mainBundle] pathForResource: @"Defaults" ofType: @"plist"]]]; - + //set custom value transformers ExpandedPathToPathTransformer * pathTransformer = [[[ExpandedPathToPathTransformer alloc] init] autorelease]; [NSValueTransformer setValueTransformer: pathTransformer forName: @"ExpandedPathToPathTransformer"]; - + ExpandedPathToIconTransformer * iconTransformer = [[[ExpandedPathToIconTransformer alloc] init] autorelease]; [NSValueTransformer setValueTransformer: iconTransformer forName: @"ExpandedPathToIconTransformer"]; - + //cover our asses if ([[NSUserDefaults standardUserDefaults] boolForKey: @"WarningLegal"]) { @@ -288,11 +288,11 @@ static void removeKeRangerRansomware() " You and you alone are fully responsible for exercising proper judgement and abiding by your local laws.", "Legal alert -> message")]; [alert setAlertStyle: NSInformationalAlertStyle]; - + if ([alert runModal] == NSAlertSecondButtonReturn) exit(0); [alert release]; - + [[NSUserDefaults standardUserDefaults] setBool: NO forKey: @"WarningLegal"]; } } @@ -302,7 +302,7 @@ static void removeKeRangerRansomware() if ((self = [super init])) { fDefaults = [NSUserDefaults standardUserDefaults]; - + //checks for old version speeds of -1 if ([fDefaults integerForKey: @"UploadLimit"] < 0) { @@ -314,39 +314,39 @@ static void removeKeRangerRansomware() [fDefaults removeObjectForKey: @"DownloadLimit"]; [fDefaults setBool: NO forKey: @"CheckDownload"]; } - + //upgrading from versions < 2.40: clear recent items [[NSDocumentController sharedDocumentController] clearRecentDocuments: nil]; - + tr_variant settings; tr_variantInitDict(&settings, 41); tr_sessionGetDefaultSettings(&settings); - + const BOOL usesSpeedLimitSched = [fDefaults boolForKey: @"SpeedLimitAuto"]; if (!usesSpeedLimitSched) tr_variantDictAddBool(&settings, TR_KEY_alt_speed_enabled, [fDefaults boolForKey: @"SpeedLimit"]); - + tr_variantDictAddInt(&settings, TR_KEY_alt_speed_up, [fDefaults integerForKey: @"SpeedLimitUploadLimit"]); tr_variantDictAddInt(&settings, TR_KEY_alt_speed_down, [fDefaults integerForKey: @"SpeedLimitDownloadLimit"]); - + tr_variantDictAddBool(&settings, TR_KEY_alt_speed_time_enabled, [fDefaults boolForKey: @"SpeedLimitAuto"]); tr_variantDictAddInt(&settings, TR_KEY_alt_speed_time_begin, [PrefsController dateToTimeSum: [fDefaults objectForKey: @"SpeedLimitAutoOnDate"]]); tr_variantDictAddInt(&settings, TR_KEY_alt_speed_time_end, [PrefsController dateToTimeSum: [fDefaults objectForKey: @"SpeedLimitAutoOffDate"]]); tr_variantDictAddInt(&settings, TR_KEY_alt_speed_time_day, [fDefaults integerForKey: @"SpeedLimitAutoDay"]); - + tr_variantDictAddInt(&settings, TR_KEY_speed_limit_down, [fDefaults integerForKey: @"DownloadLimit"]); tr_variantDictAddBool(&settings, TR_KEY_speed_limit_down_enabled, [fDefaults boolForKey: @"CheckDownload"]); tr_variantDictAddInt(&settings, TR_KEY_speed_limit_up, [fDefaults integerForKey: @"UploadLimit"]); tr_variantDictAddBool(&settings, TR_KEY_speed_limit_up_enabled, [fDefaults boolForKey: @"CheckUpload"]); - + //hidden prefs if ([fDefaults objectForKey: @"BindAddressIPv4"]) tr_variantDictAddStr(&settings, TR_KEY_bind_address_ipv4, [[fDefaults stringForKey: @"BindAddressIPv4"] UTF8String]); if ([fDefaults objectForKey: @"BindAddressIPv6"]) tr_variantDictAddStr(&settings, TR_KEY_bind_address_ipv6, [[fDefaults stringForKey: @"BindAddressIPv6"] UTF8String]); - + tr_variantDictAddBool(&settings, TR_KEY_blocklist_enabled, [fDefaults boolForKey: @"BlocklistNew"]); if ([fDefaults objectForKey: @"BlocklistURL"]) tr_variantDictAddStr(&settings, TR_KEY_blocklist_url, [[fDefaults stringForKey: @"BlocklistURL"] UTF8String]); @@ -364,16 +364,16 @@ static void removeKeRangerRansomware() tr_variantDictAddInt(&settings, TR_KEY_message_level, TR_LOG_DEBUG); tr_variantDictAddInt(&settings, TR_KEY_peer_limit_global, [fDefaults integerForKey: @"PeersTotal"]); tr_variantDictAddInt(&settings, TR_KEY_peer_limit_per_torrent, [fDefaults integerForKey: @"PeersTorrent"]); - + const BOOL randomPort = [fDefaults boolForKey: @"RandomPort"]; tr_variantDictAddBool(&settings, TR_KEY_peer_port_random_on_start, randomPort); if (!randomPort) tr_variantDictAddInt(&settings, TR_KEY_peer_port, [fDefaults integerForKey: @"BindPort"]); - + //hidden pref if ([fDefaults objectForKey: @"PeerSocketTOS"]) tr_variantDictAddStr(&settings, TR_KEY_peer_socket_tos, [[fDefaults stringForKey: @"PeerSocketTOS"] UTF8String]); - + tr_variantDictAddBool(&settings, TR_KEY_pex_enabled, [fDefaults boolForKey: @"PEXGlobal"]); tr_variantDictAddBool(&settings, TR_KEY_port_forwarding_enabled, [fDefaults boolForKey: @"NatTraversal"]); tr_variantDictAddBool(&settings, TR_KEY_queue_stalled_enabled, [fDefaults boolForKey: @"CheckStalled"]); @@ -392,27 +392,27 @@ static void removeKeRangerRansomware() tr_variantDictAddBool(&settings, TR_KEY_script_torrent_done_enabled, [fDefaults boolForKey: @"DoneScriptEnabled"]); tr_variantDictAddStr(&settings, TR_KEY_script_torrent_done_filename, [[fDefaults stringForKey: @"DoneScriptPath"] UTF8String]); tr_variantDictAddBool(&settings, TR_KEY_utp_enabled, [fDefaults boolForKey: @"UTPGlobal"]); - - + + NSString * kbString, * mbString, * gbString, * tbString; if ([NSApp isOnMountainLionOrBetter]) { NSByteCountFormatter * unitFormatter = [[NSByteCountFormatterMtLion alloc] init]; [unitFormatter setIncludesCount: NO]; [unitFormatter setAllowsNonnumericFormatting: NO]; - + [unitFormatter setAllowedUnits: NSByteCountFormatterUseKB]; kbString = [unitFormatter stringFromByteCount: 17]; //use a random value to avoid possible pluralization issues with 1 or 0 (an example is if we use 1 for bytes, we'd get "byte" when we'd want "bytes" for the generic libtransmission value at least) - + [unitFormatter setAllowedUnits: NSByteCountFormatterUseMB]; mbString = [unitFormatter stringFromByteCount: 17]; - + [unitFormatter setAllowedUnits: NSByteCountFormatterUseGB]; gbString = [unitFormatter stringFromByteCount: 17]; - + [unitFormatter setAllowedUnits: NSByteCountFormatterUseTB]; tbString = [unitFormatter stringFromByteCount: 17]; - + [unitFormatter release]; } else @@ -422,7 +422,7 @@ static void removeKeRangerRansomware() gbString = NSLocalizedString(@"GB", "file/memory size - gigabytes"); tbString = NSLocalizedString(@"TB", "file/memory size - terabytes"); } - + tr_formatter_size_init(1000, [kbString UTF8String], [mbString UTF8String], [gbString UTF8String], @@ -437,45 +437,45 @@ static void removeKeRangerRansomware() [mbString UTF8String], [gbString UTF8String], [tbString UTF8String]); - + const char * configDir = tr_getDefaultConfigDir("Transmission"); fLib = tr_sessionInit(configDir, YES, &settings); tr_variantFree(&settings); - + fConfigDirectory = [[NSString alloc] initWithUTF8String: configDir]; - + [NSApp setDelegate: self]; - + //register for magnet URLs (has to be in init) [[NSAppleEventManager sharedAppleEventManager] setEventHandler: self andSelector: @selector(handleOpenContentsEvent:replyEvent:) forEventClass: kInternetEventClass andEventID: kAEGetURL]; - + fTorrents = [[NSMutableArray alloc] init]; fDisplayedTorrents = [[NSMutableArray alloc] init]; - + fInfoController = [[InfoWindowController alloc] init]; - + //needs to be done before init-ing the prefs controller fFileWatcherQueue = [[VDKQueue alloc] init]; [fFileWatcherQueue setDelegate: self]; - + fPrefsController = [[PrefsController alloc] initWithHandle: fLib]; - + fQuitting = NO; fGlobalPopoverShown = NO; fSoundPlaying = NO; - + tr_sessionSetAltSpeedFunc(fLib, altSpeedToggledCallback, self); if (usesSpeedLimitSched) [fDefaults setBool: tr_sessionUsesAltSpeed(fLib) forKey: @"SpeedLimit"]; - + tr_sessionSetRPCCallback(fLib, rpcCallback, self); - + [GrowlApplicationBridge setGrowlDelegate: self]; - + [[SUUpdater sharedUpdater] setDelegate: self]; fQuitRequested = NO; - + fPauseOnLaunch = (GetCurrentKeyModifiers() & (optionKey | rightOptionKey)) != 0; } return self; @@ -490,39 +490,39 @@ static void removeKeRangerRansomware() [toolbar setDisplayMode: NSToolbarDisplayModeIconOnly]; [fWindow setToolbar: toolbar]; [toolbar release]; - + [fWindow setDelegate: self]; //do manually to avoid placement issue - + [fWindow makeFirstResponder: fTableView]; [fWindow setExcludedFromWindowsMenu: YES]; - + //set table size const BOOL small = [fDefaults boolForKey: @"SmallView"]; if (small) [fTableView setRowHeight: ROW_HEIGHT_SMALL]; [fTableView setUsesAlternatingRowBackgroundColors: !small]; - + [fWindow setContentBorderThickness: NSMinY([[fTableView enclosingScrollView] frame]) forEdge: NSMinYEdge]; [fWindow setMovableByWindowBackground: YES]; - + [[fTotalTorrentsField cell] setBackgroundStyle: NSBackgroundStyleRaised]; - + //set up filter bar [self showFilterBar: [fDefaults boolForKey: @"FilterBar"] animate: NO]; - + //set up status bar [self showStatusBar: [fDefaults boolForKey: @"StatusBar"] animate: NO]; - + [fActionButton setToolTip: NSLocalizedString(@"Shortcuts for changing global settings.", "Main window -> 1st bottom left button (action) tooltip")]; [fSpeedLimitButton setToolTip: NSLocalizedString(@"Speed Limit overrides the total bandwidth limits with its own limits.", "Main window -> 2nd bottom left button (turtle) tooltip")]; [fClearCompletedButton setToolTip: NSLocalizedString(@"Remove all transfers that have completed seeding.", "Main window -> 3rd bottom left button (remove all) tooltip")]; - + [fTableView registerForDraggedTypes: [NSArray arrayWithObject: TORRENT_TABLE_VIEW_DATA_TYPE]]; [fWindow registerForDraggedTypes: [NSArray arrayWithObjects: NSFilenamesPboardType, NSURLPboardType, nil]]; - + //sort the sort menu items (localization is from strings file) NSMutableArray * sortMenuItems = [NSMutableArray arrayWithCapacity: 7]; NSUInteger sortMenuIndex = 0; @@ -543,19 +543,19 @@ static void removeKeRangerRansomware() ++sortMenuIndex; } } - + [sortMenuItems sortUsingDescriptors: [NSArray arrayWithObject: [NSSortDescriptor sortDescriptorWithKey: @"title" ascending: YES selector: @selector(localizedCompare:)]]]; - + for (NSMenuItem * item in sortMenuItems) [fSortMenu insertItem: item atIndex: sortMenuIndex++]; - + //you would think this would be called later in this method from updateUI, but it's not reached in awakeFromNib //this must be called after showStatusBar: [fStatusBar updateWithDownload: 0.0 upload: 0.0]; //this should also be after the rest of the setup [self updateForAutoSize]; - + //register for sleep notifications IONotificationPortRef notify; io_object_t iterator; @@ -563,7 +563,7 @@ static void removeKeRangerRansomware() CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notify), kCFRunLoopCommonModes); else NSLog(@"Could not IORegisterForSystemPower"); - + //load previous transfers NSString * historyFile = [fConfigDirectory stringByAppendingPathComponent: TRANSFER_PLIST]; NSArray * history = [NSArray arrayWithContentsOfFile: historyFile]; @@ -573,91 +573,91 @@ static void removeKeRangerRansomware() if ((history = [fDefaults arrayForKey: @"History"])) [fDefaults removeObjectForKey: @"History"]; } - + if (history) { NSMutableArray * waitToStartTorrents = [NSMutableArray arrayWithCapacity: (([history count] > 0 && !fPauseOnLaunch) ? [history count]-1 : 0)]; //theoretical max without doing a lot of work - + for (NSDictionary * historyItem in history) { Torrent * torrent; if ((torrent = [[Torrent alloc] initWithHistory: historyItem lib: fLib forcePause: fPauseOnLaunch])) { [fTorrents addObject: torrent]; - + NSNumber * waitToStart; if (!fPauseOnLaunch && (waitToStart = [historyItem objectForKey: @"WaitToStart"]) && [waitToStart boolValue]) [waitToStartTorrents addObject: torrent]; - + [torrent release]; } } - + //now that all are loaded, let's set those in the queue to waiting for (Torrent * torrent in waitToStartTorrents) [torrent startTransfer]; } - + fBadger = [[Badger alloc] initWithLib: fLib]; - + if ([NSApp isOnMountainLionOrBetter]) [[NSUserNotificationCenterMtLion defaultUserNotificationCenter] setDelegate: self]; - + // remove Share menu items if (![NSApp isOnMountainLionOrBetter]) { [[fShareMenuItem menu] removeItem:fShareMenuItem]; [[fShareContextMenuItem menu] removeItem:fShareContextMenuItem]; } - + //observe notifications NSNotificationCenter * nc = [NSNotificationCenter defaultCenter]; - + [nc addObserver: self selector: @selector(updateUI) name: @"UpdateUI" object: nil]; - + [nc addObserver: self selector: @selector(torrentFinishedDownloading:) name: @"TorrentFinishedDownloading" object: nil]; - + [nc addObserver: self selector: @selector(torrentRestartedDownloading:) name: @"TorrentRestartedDownloading" object: nil]; - + [nc addObserver: self selector: @selector(torrentFinishedSeeding:) name: @"TorrentFinishedSeeding" object: nil]; - + [nc addObserver: self selector: @selector(applyFilter) name: kTorrentDidChangeGroupNotification object: nil]; - + //avoids need of setting delegate [nc addObserver: self selector: @selector(torrentTableViewSelectionDidChange:) name: NSOutlineViewSelectionDidChangeNotification object: fTableView]; - + [nc addObserver: self selector: @selector(changeAutoImport) name: @"AutoImportSettingChange" object: nil]; - + [nc addObserver: self selector: @selector(updateForAutoSize) name: @"AutoSizeSettingChange" object: nil]; - + [nc addObserver: self selector: @selector(updateForExpandCollape) name: @"OutlineExpandCollapse" object: nil]; - + [nc addObserver: fWindow selector: @selector(makeKeyWindow) name: @"MakeWindowKey" object: nil]; - + #warning rename [nc addObserver: self selector: @selector(fullUpdateUI) name: @"UpdateQueue" object: nil]; - + [nc addObserver: self selector: @selector(applyFilter) name: @"ApplyFilter" object: nil]; - + //open newly created torrent file [nc addObserver: self selector: @selector(beginCreateFile:) name: @"BeginCreateTorrentFile" object: nil]; - + //open newly created torrent file [nc addObserver: self selector: @selector(openCreatedFile:) name: @"OpenCreatedTorrentFile" object: nil]; - + [nc addObserver: self selector: @selector(applyFilter) name: @"UpdateGroups" object: nil]; @@ -667,11 +667,11 @@ static void removeKeRangerRansomware() selector: @selector(updateUI) userInfo: nil repeats: YES] retain]; [[NSRunLoop currentRunLoop] addTimer: fTimer forMode: NSModalPanelRunLoopMode]; [[NSRunLoop currentRunLoop] addTimer: fTimer forMode: NSEventTrackingRunLoopMode]; - + [self applyFilter]; - + [fWindow makeKeyAndOrderFront: nil]; - + if ([fDefaults boolForKey: @"InfoVisible"]) [self showInfo: nil]; } @@ -679,11 +679,11 @@ static void removeKeRangerRansomware() - (void) applicationDidFinishLaunching: (NSNotification *) notification { [NSApp setServicesProvider: self]; - + //register for dock icon drags (has to be in applicationDidFinishLaunching: to work) [[NSAppleEventManager sharedAppleEventManager] setEventHandler: self andSelector: @selector(handleOpenContentsEvent:replyEvent:) forEventClass: kCoreEventClass andEventID: kAEOpenContents]; - + //if we were opened from a user notification, do the corresponding action if ([NSApp isOnMountainLionOrBetter]) { @@ -691,56 +691,56 @@ static void removeKeRangerRansomware() if (launchNotification) [self userNotificationCenter: nil didActivateNotification: launchNotification]; } - + //auto importing [self checkAutoImportDirectory]; - + //registering the Web UI to Bonjour if ([fDefaults boolForKey: @"RPC"] && [fDefaults boolForKey: @"RPCWebDiscovery"]) [[BonjourController defaultController] startWithPort: [fDefaults integerForKey: @"RPCPort"]]; - + //shamelessly ask for donations if ([fDefaults boolForKey: @"WarningDonate"]) { tr_session_stats stats; tr_sessionGetCumulativeStats(fLib, &stats); const BOOL firstLaunch = stats.sessionCount <= 1; - + NSDate * lastDonateDate = [fDefaults objectForKey: @"DonateAskDate"]; const BOOL timePassed = !lastDonateDate || (-1 * [lastDonateDate timeIntervalSinceNow]) >= DONATE_NAG_TIME; - + if (!firstLaunch && timePassed) { [fDefaults setObject: [NSDate date] forKey: @"DonateAskDate"]; - + NSAlert * alert = [[NSAlert alloc] init]; [alert setMessageText: NSLocalizedString(@"Support open-source indie software", "Donation beg -> title")]; - + NSString * donateMessage = [NSString stringWithFormat: @"%@\n\n%@", NSLocalizedString(@"Transmission is a full-featured torrent application." " A lot of time and effort have gone into development, coding, and refinement." " If you enjoy using it, please consider showing your love with a donation.", "Donation beg -> message"), NSLocalizedString(@"Donate or not, there will be no difference to your torrenting experience.", "Donation beg -> message")]; - + [alert setInformativeText: donateMessage]; [alert setAlertStyle: NSInformationalAlertStyle]; - + [alert addButtonWithTitle: [NSLocalizedString(@"Donate", "Donation beg -> button") stringByAppendingEllipsis]]; NSButton * noDonateButton = [alert addButtonWithTitle: NSLocalizedString(@"Nope", "Donation beg -> button")]; [noDonateButton setKeyEquivalent: @"\e"]; //escape key - + const BOOL allowNeverAgain = lastDonateDate != nil; //hide the "don't show again" check the first time - give them time to try the app [alert setShowsSuppressionButton: allowNeverAgain]; if (allowNeverAgain) [[alert suppressionButton] setTitle: NSLocalizedString(@"Don't bug me about this ever again.", "Donation beg -> button")]; - + const NSInteger donateResult = [alert runModal]; if (donateResult == NSAlertFirstButtonReturn) [self linkDonate: self]; - + if (allowNeverAgain) [fDefaults setBool: ([[alert suppressionButton] state] != NSOnState) forKey: @"WarningDonate"]; - + [alert release]; } } @@ -751,7 +751,7 @@ static void removeKeRangerRansomware() NSWindow * mainWindow = [NSApp mainWindow]; if (!mainWindow || ![mainWindow isVisible]) [fWindow makeKeyAndOrderFront: nil]; - + return NO; } @@ -767,7 +767,7 @@ static void removeKeRangerRansomware() if (![torrent allDownloaded]) downloading++; } - + if ([fDefaults boolForKey: @"CheckQuitDownloading"] ? downloading > 0 : active > 0) { NSString * message = active == 1 @@ -783,7 +783,7 @@ static void removeKeRangerRansomware() return NSTerminateLater; } } - + return NSTerminateNow; } @@ -795,7 +795,7 @@ static void removeKeRangerRansomware() - (void) applicationWillTerminate: (NSNotification *) notification { fQuitting = YES; - + //stop the Bonjour service if ([BonjourController defaultControllerExists]) [[BonjourController defaultController] stop]; @@ -803,22 +803,22 @@ static void removeKeRangerRansomware() //stop blocklist download if ([BlocklistDownloader isRunning]) [[BlocklistDownloader downloader] cancelDownload]; - + //stop timers and notification checking [[NSNotificationCenter defaultCenter] removeObserver: self]; - + [fTimer invalidate]; [fTimer release]; - + if (fAutoImportTimer) - { + { if ([fAutoImportTimer isValid]) [fAutoImportTimer invalidate]; [fAutoImportTimer release]; } - + [fBadger setQuitting]; - + //remove all torrent downloads if (fPendingTorrentDownloads) { @@ -830,48 +830,48 @@ static void removeKeRangerRansomware() } [fPendingTorrentDownloads release]; } - + //remember window states and close all windows [fDefaults setBool: [[fInfoController window] isVisible] forKey: @"InfoVisible"]; - + if ([QLPreviewPanel sharedPreviewPanelExists] && [[QLPreviewPanel sharedPreviewPanel] isVisible]) [[QLPreviewPanel sharedPreviewPanel] updateController]; - + for (NSWindow * window in [NSApp windows]) [window close]; - + [self showStatusBar: NO animate: NO]; [self showFilterBar: NO animate: NO]; - + //save history [self updateTorrentHistory]; [fTableView saveCollapsedGroups]; - - //remaining calls the same as dealloc + + //remaining calls the same as dealloc [fInfoController release]; [fMessageController release]; [fPrefsController release]; - + [fStatusBar release]; [fFilterBar release]; - + [fTorrents release]; [fDisplayedTorrents release]; - + [fAddWindows release]; [fAddingTransfers release]; - + [fOverlayWindow release]; [fBadger release]; - + [fAutoImportedNames release]; - + [fPreviewPanel release]; - + [fConfigDirectory release]; - + [fFileWatcherQueue release]; - + //complete cleanup tr_sessionClose(fLib); } @@ -894,7 +894,7 @@ static void removeKeRangerRansomware() } else urlString = [directObject stringValue]; - + if (urlString) [self openURL: urlString]; } @@ -904,20 +904,20 @@ static void removeKeRangerRansomware() if ([[suggestedName pathExtension] caseInsensitiveCompare: @"torrent"] != NSOrderedSame) { [download cancel]; - + [fPendingTorrentDownloads removeObjectForKey: [[download request] URL]]; if ([fPendingTorrentDownloads count] == 0) { [fPendingTorrentDownloads release]; fPendingTorrentDownloads = nil; } - + NSRunAlertPanel(NSLocalizedString(@"Torrent download failed", "Download not a torrent -> title"), [NSString stringWithFormat: NSLocalizedString(@"It appears that the file \"%@\" from %@ is not a torrent file.", "Download not a torrent -> message"), suggestedName, [[[[download request] URL] absoluteString] stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding]], NSLocalizedString(@"OK", "Download not a torrent -> button"), nil, nil); - + [download release]; } else @@ -937,33 +937,33 @@ static void removeKeRangerRansomware() "Torrent download failed -> message"), [[[[download request] URL] absoluteString] stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding], [error localizedDescription]], NSLocalizedString(@"OK", "Torrent download failed -> button"), nil, nil); - + [fPendingTorrentDownloads removeObjectForKey: [[download request] URL]]; if ([fPendingTorrentDownloads count] == 0) { [fPendingTorrentDownloads release]; fPendingTorrentDownloads = nil; } - + [download release]; } - (void) downloadDidFinish: (NSURLDownload *) download { NSString * path = [[fPendingTorrentDownloads objectForKey: [[download request] URL]] objectForKey: @"Path"]; - + [self openFiles: [NSArray arrayWithObject: path] addType: ADD_URL forcePath: nil]; - + //delete the torrent file after opening [[NSFileManager defaultManager] removeItemAtPath: path error: NULL]; - + [fPendingTorrentDownloads removeObjectForKey: [[download request] URL]]; if ([fPendingTorrentDownloads count] == 0) { [fPendingTorrentDownloads release]; fPendingTorrentDownloads = nil; } - + [download release]; } @@ -987,17 +987,17 @@ static void removeKeRangerRansomware() deleteTorrentFile = [fDefaults boolForKey: @"DeleteOriginalTorrent"]; canToggleDelete = YES; } - + for (NSString * torrentPath in filenames) { //ensure torrent doesn't already exist tr_ctor * ctor = tr_ctorNew(fLib); tr_ctorSetMetainfoFromFile(ctor, [torrentPath UTF8String]); - + tr_info info; const tr_parse_result result = tr_torrentParse(ctor, &info); tr_ctorFree(ctor); - + if (result != TR_PARSE_OK) { if (result == TR_PARSE_DUPLICATE) @@ -1009,11 +1009,11 @@ static void removeKeRangerRansomware() } else NSAssert2(NO, @"Unknown error code (%d) when attempting to open \"%@\"", result, torrentPath); - + tr_metainfoFree(&info); continue; } - + //determine download location NSString * location; BOOL lockDestination = NO; //don't override the location with a group location if it has a hardcoded path @@ -1028,29 +1028,29 @@ static void removeKeRangerRansomware() location = [torrentPath stringByDeletingLastPathComponent]; else location = nil; - + //determine to show the options window const BOOL showWindow = type == ADD_SHOW_OPTIONS || ([fDefaults boolForKey: @"DownloadAsk"] && (info.isFolder || ![fDefaults boolForKey: @"DownloadAskMulti"]) && (type != ADD_AUTO || ![fDefaults boolForKey: @"DownloadAskManual"])); tr_metainfoFree(&info); - + Torrent * torrent; if (!(torrent = [[Torrent alloc] initWithPath: torrentPath location: location deleteTorrentFile: showWindow ? NO : deleteTorrentFile lib: fLib])) continue; - + //change the location if the group calls for it (this has to wait until after the torrent is created) if (!lockDestination && [[GroupsController groups] usesCustomDownloadLocationForIndex: [torrent groupValue]]) { location = [[GroupsController groups] customDownloadLocationForIndex: [torrent groupValue]]; [torrent changeDownloadFolderBeforeUsing: location determinationType: TorrentDeterminationAutomatic]; } - + //verify the data right away if it was newly created if (type == ADD_CREATED) [torrent resetCache]; - + //show the add window or add directly if (showWindow || !location) { @@ -1058,7 +1058,7 @@ static void removeKeRangerRansomware() lockDestination: lockDestination controller: self torrentFile: torrentPath deleteTorrentCheckEnableInitially: deleteTorrentFile canToggleDelete: canToggleDelete]; [addController showWindow: self]; - + if (!fAddWindows) fAddWindows = [[NSMutableSet alloc] init]; [fAddWindows addObject: addController]; @@ -1068,11 +1068,11 @@ static void removeKeRangerRansomware() { if ([fDefaults boolForKey: @"AutoStartDownload"]) [torrent startTransfer]; - + [torrent update]; [fTorrents addObject: torrent]; [torrent release]; - + if (!fAddingTransfers) fAddingTransfers = [[NSMutableSet alloc] init]; [fAddingTransfers addObject: torrent]; @@ -1085,19 +1085,19 @@ static void removeKeRangerRansomware() - (void) askOpenConfirmed: (AddWindowController *) addController add: (BOOL) add { Torrent * torrent = [addController torrent]; - + if (add) { [torrent setQueuePosition: [fTorrents count]]; - + [torrent update]; [fTorrents addObject: torrent]; [torrent release]; - + if (!fAddingTransfers) fAddingTransfers = [[NSMutableSet alloc] init]; [fAddingTransfers addObject: torrent]; - + [self fullUpdateUI]; } else @@ -1105,7 +1105,7 @@ static void removeKeRangerRansomware() [torrent closeRemoveTorrent: NO]; [torrent release]; } - + [fAddWindows removeObject: addController]; if ([fAddWindows count] == 0) { @@ -1124,32 +1124,32 @@ static void removeKeRangerRansomware() [self duplicateOpenMagnetAlert: address transferName: name]; return; } - + //determine download location NSString * location = nil; if ([fDefaults boolForKey: @"DownloadLocationConstant"]) location = [[fDefaults stringForKey: @"DownloadFolder"] stringByExpandingTildeInPath]; - + Torrent * torrent; if (!(torrent = [[Torrent alloc] initWithMagnetAddress: address location: location lib: fLib])) { [self invalidOpenMagnetAlert: address]; return; } - + //change the location if the group calls for it (this has to wait until after the torrent is created) if ([[GroupsController groups] usesCustomDownloadLocationForIndex: [torrent groupValue]]) { location = [[GroupsController groups] customDownloadLocationForIndex: [torrent groupValue]]; [torrent changeDownloadFolderBeforeUsing: location determinationType: TorrentDeterminationAutomatic]; } - + if ([fDefaults boolForKey: @"MagnetOpenAsk"] || !location) { AddMagnetWindowController * addController = [[AddMagnetWindowController alloc] initWithTorrent: torrent destination: location controller: self]; [addController showWindow: self]; - + if (!fAddWindows) fAddWindows = [[NSMutableSet alloc] init]; [fAddWindows addObject: addController]; @@ -1159,11 +1159,11 @@ static void removeKeRangerRansomware() { if ([fDefaults boolForKey: @"AutoStartDownload"]) [torrent startTransfer]; - + [torrent update]; [fTorrents addObject: torrent]; [torrent release]; - + if (!fAddingTransfers) fAddingTransfers = [[NSMutableSet alloc] init]; [fAddingTransfers addObject: torrent]; @@ -1175,19 +1175,19 @@ static void removeKeRangerRansomware() - (void) askOpenMagnetConfirmed: (AddMagnetWindowController *) addController add: (BOOL) add { Torrent * torrent = [addController torrent]; - + if (add) { [torrent setQueuePosition: [fTorrents count]]; - + [torrent update]; [fTorrents addObject: torrent]; [torrent release]; - + if (!fAddingTransfers) fAddingTransfers = [[NSMutableSet alloc] init]; [fAddingTransfers addObject: torrent]; - + [self fullUpdateUI]; } else @@ -1195,7 +1195,7 @@ static void removeKeRangerRansomware() [torrent closeRemoveTorrent: NO]; [torrent release]; } - + [fAddWindows removeObject: addController]; if ([fAddWindows count] == 0) { @@ -1214,7 +1214,7 @@ static void removeKeRangerRansomware() - (void) openFilesWithDict: (NSDictionary *) dictionary { [self openFiles: [dictionary objectForKey: @"Filenames"] addType: [[dictionary objectForKey: @"AddType"] intValue] forcePath: nil]; - + [dictionary release]; } @@ -1229,20 +1229,20 @@ static void removeKeRangerRansomware() - (void) openShowSheet: (id) sender { NSOpenPanel * panel = [NSOpenPanel openPanel]; - + [panel setAllowsMultipleSelection: YES]; [panel setCanChooseFiles: YES]; [panel setCanChooseDirectories: NO]; - + [panel setAllowedFileTypes: [NSArray arrayWithObjects: @"org.bittorrent.torrent", @"torrent", nil]]; - + [panel beginSheetModalForWindow: fWindow completionHandler: ^(NSInteger result) { if (result == NSFileHandlingPanelOKButton) { NSMutableArray * filenames = [NSMutableArray arrayWithCapacity: [[panel URLs] count]]; for (NSURL * url in [panel URLs]) [filenames addObject: [url path]]; - + NSDictionary * dictionary = [[NSDictionary alloc] initWithObjectsAndKeys: filenames, @"Filenames", [NSNumber numberWithInt: sender == fOpenIgnoreDownloadFolder ? ADD_SHOW_OPTIONS : ADD_MANUAL], @"AddType", nil]; [self performSelectorOnMainThread: @selector(openFilesWithDict:) withObject: dictionary waitUntilDone: NO]; @@ -1254,7 +1254,7 @@ static void removeKeRangerRansomware() { if (![fDefaults boolForKey: @"WarningInvalidOpen"]) return; - + NSAlert * alert = [[NSAlert alloc] init]; [alert setMessageText: [NSString stringWithFormat: NSLocalizedString(@"\"%@\" is not a valid torrent file.", "Open invalid alert -> title"), filename]]; @@ -1263,7 +1263,7 @@ static void removeKeRangerRansomware() "Open invalid alert -> message")]; [alert setAlertStyle: NSWarningAlertStyle]; [alert addButtonWithTitle: NSLocalizedString(@"OK", "Open invalid alert -> button")]; - + [alert runModal]; if ([[alert suppressionButton] state] == NSOnState) [fDefaults setBool: NO forKey: @"WarningInvalidOpen"]; @@ -1274,14 +1274,14 @@ static void removeKeRangerRansomware() { if (![fDefaults boolForKey: @"WarningInvalidOpen"]) return; - + NSAlert * alert = [[NSAlert alloc] init]; [alert setMessageText: NSLocalizedString(@"Adding magnetized transfer failed.", "Magnet link failed -> title")]; [alert setInformativeText: [NSString stringWithFormat: NSLocalizedString(@"There was an error when adding the magnet link \"%@\"." " The transfer will not occur.", "Magnet link failed -> message"), address]]; [alert setAlertStyle: NSWarningAlertStyle]; [alert addButtonWithTitle: NSLocalizedString(@"OK", "Magnet link failed -> button")]; - + [alert runModal]; if ([[alert suppressionButton] state] == NSOnState) [fDefaults setBool: NO forKey: @"WarningInvalidOpen"]; @@ -1292,7 +1292,7 @@ static void removeKeRangerRansomware() { if (![fDefaults boolForKey: @"WarningDuplicate"]) return; - + NSAlert * alert = [[NSAlert alloc] init]; [alert setMessageText: [NSString stringWithFormat: NSLocalizedString(@"A transfer of \"%@\" already exists.", "Open duplicate alert -> title"), name]]; @@ -1302,7 +1302,7 @@ static void removeKeRangerRansomware() [alert setAlertStyle: NSWarningAlertStyle]; [alert addButtonWithTitle: NSLocalizedString(@"OK", "Open duplicate alert -> button")]; [alert setShowsSuppressionButton: YES]; - + [alert runModal]; if ([[alert suppressionButton] state]) [fDefaults setBool: NO forKey: @"WarningDuplicate"]; @@ -1313,7 +1313,7 @@ static void removeKeRangerRansomware() { if (![fDefaults boolForKey: @"WarningDuplicate"]) return; - + NSAlert * alert = [[NSAlert alloc] init]; if (name) [alert setMessageText: [NSString stringWithFormat: NSLocalizedString(@"A transfer of \"%@\" already exists.", @@ -1327,7 +1327,7 @@ static void removeKeRangerRansomware() [alert setAlertStyle: NSWarningAlertStyle]; [alert addButtonWithTitle: NSLocalizedString(@"OK", "Open duplicate magnet alert -> button")]; [alert setShowsSuppressionButton: YES]; - + [alert runModal]; if ([[alert suppressionButton] state]) [fDefaults setBool: NO forKey: @"WarningDuplicate"]; @@ -1355,18 +1355,18 @@ static void removeKeRangerRansomware() else urlString = [@"http://" stringByAppendingString: urlString]; } - + NSURLRequest * request = [NSURLRequest requestWithURL: [NSURL URLWithString: urlString] cachePolicy: NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval: 60]; - + if ([fPendingTorrentDownloads objectForKey: [request URL]]) { NSLog(@"Already downloading %@", [request URL]); return; } - + NSURLDownload * download = [[NSURLDownload alloc] initWithRequest: request delegate: self]; - + if (!fPendingTorrentDownloads) fPendingTorrentDownloads = [[NSMutableDictionary alloc] init]; [fPendingTorrentDownloads setObject: [NSMutableDictionary dictionaryWithObject: download forKey: @"Download"] forKey: [request URL]]; @@ -1378,7 +1378,7 @@ static void removeKeRangerRansomware() if (!fUrlSheetController) { fUrlSheetController = [[URLSheetWindowController alloc] initWithController: self]; - + [NSApp beginSheet: [fUrlSheetController window] modalForWindow: fWindow modalDelegate: self didEndSelector: @selector(urlSheetDidEnd:returnCode:contextInfo:) contextInfo: nil]; } } @@ -1390,7 +1390,7 @@ static void removeKeRangerRansomware() NSString * urlString = [fUrlSheetController urlString]; [self performSelectorOnMainThread: @selector(openURL:) withObject: urlString waitUntilDone: NO]; } - + [fUrlSheetController release]; fUrlSheetController = nil; } @@ -1408,11 +1408,11 @@ static void removeKeRangerRansomware() - (void) resumeAllTorrents: (id) sender { NSMutableArray * torrents = [NSMutableArray arrayWithCapacity: [fTorrents count]]; - + for (Torrent * torrent in fTorrents) if (![torrent isFinishedSeeding]) [torrents addObject: torrent]; - + [self resumeTorrents: torrents]; } @@ -1420,7 +1420,7 @@ static void removeKeRangerRansomware() { for (Torrent * torrent in torrents) [torrent startTransfer]; - + [self fullUpdateUI]; } @@ -1432,11 +1432,11 @@ static void removeKeRangerRansomware() - (void) resumeWaitingTorrents: (id) sender { NSMutableArray * torrents = [NSMutableArray arrayWithCapacity: [fTorrents count]]; - + for (Torrent * torrent in fTorrents) if ([torrent waitingToStart]) [torrents addObject: torrent]; - + [self resumeTorrentsNoWait: torrents]; } @@ -1445,7 +1445,7 @@ static void removeKeRangerRansomware() //iterate through instead of all at once to ensure no conflicts for (Torrent * torrent in torrents) [torrent startTransferNoQueue]; - + [self fullUpdateUI]; } @@ -1465,10 +1465,10 @@ static void removeKeRangerRansomware() for (Torrent * torrent in torrents) if ([torrent waitingToStart]) [torrent stopTransfer]; - + for (Torrent * torrent in torrents) [torrent stopTransfer]; - + [self fullUpdateUI]; } @@ -1489,14 +1489,14 @@ static void removeKeRangerRansomware() { NSDictionary * dict = @{ @"Torrents" : torrents, @"DeleteData" : @(deleteData) }; - + NSString * title, * message; - + const NSInteger selected = [torrents count]; if (selected == 1) { NSString * torrentName = [(Torrent *)[torrents objectAtIndex: 0] name]; - + if (deleteData) title = [NSString stringWithFormat: NSLocalizedString(@"Are you sure you want to remove \"%@\" from the transfer list" @@ -1505,7 +1505,7 @@ static void removeKeRangerRansomware() title = [NSString stringWithFormat: NSLocalizedString(@"Are you sure you want to remove \"%@\" from the transfer list?", "Removal confirm panel -> title"), torrentName]; - + message = NSLocalizedString(@"This transfer is active." " Once removed, continuing the transfer will require the torrent file or magnet link.", "Removal confirm panel -> message"); @@ -1520,7 +1520,7 @@ static void removeKeRangerRansomware() title = [NSString stringWithFormat: NSLocalizedString(@"Are you sure you want to remove %@ transfers from the transfer list?", "Removal confirm panel -> title"), [NSString formattedUInteger: selected]]; - + if (selected == active) message = [NSString stringWithFormat: NSLocalizedString(@"There are %@ active transfers.", "Removal confirm panel -> message part 1"), [NSString formattedUInteger: active]]; @@ -1531,14 +1531,14 @@ static void removeKeRangerRansomware() NSLocalizedString(@"Once removed, continuing the transfers will require the torrent files or magnet links.", "Removal confirm panel -> message part 2")]; } - + NSBeginAlertSheet(title, NSLocalizedString(@"Remove", "Removal confirm panel -> button"), NSLocalizedString(@"Cancel", "Removal confirm panel -> button"), nil, fWindow, self, nil, @selector(removeSheetDidEnd:returnCode:contextInfo:), [dict retain], @"%@", message); return; } } - + [self confirmRemoveTorrents: torrents deleteData: deleteData]; } @@ -1558,14 +1558,14 @@ static void removeKeRangerRansomware() //don't want any of these starting then stopping if ([torrent waitingToStart]) [torrent stopTransfer]; - + //let's expand all groups that have removed items - they either don't exist anymore, are already expanded, or are collapsed (rpc) [fTableView removeCollapsedGroup: [torrent groupValue]]; - + //we can't assume the window is active - RPC removal, for example [fBadger removeTorrent: torrent]; } - + //#5106 - don't try to remove torrents that have already been removed (fix for a bug, but better safe than crash anyway) NSIndexSet * indexesToRemove = [torrents indexesOfObjectsWithOptions: NSEnumerationConcurrent passingTest: ^BOOL(Torrent * torrent, NSUInteger idx, BOOL * stop) { return [fTorrents indexOfObjectIdenticalTo: torrent] != NSNotFound; @@ -1574,46 +1574,46 @@ static void removeKeRangerRansomware() { NSLog(@"trying to remove %ld transfers, but %ld have already been removed", [torrents count], [torrents count] - [indexesToRemove count]); torrents = [torrents objectsAtIndexes: indexesToRemove]; - + if ([indexesToRemove count] == 0) { [self fullUpdateUI]; return; } } - + [fTorrents removeObjectsInArray: torrents]; - + //set up helpers to remove from the table __block BOOL beganUpdate = NO; - + void (^doTableRemoval)(NSMutableArray *, id) = ^(NSMutableArray * displayedTorrents, id parent) { NSIndexSet * indexes = [displayedTorrents indexesOfObjectsWithOptions: NSEnumerationConcurrent passingTest: ^(id obj, NSUInteger idx, BOOL * stop) { return [torrents containsObject: obj]; }]; - + if ([indexes count] > 0) { if (!beganUpdate) { [NSAnimationContext beginGrouping]; //this has to be before we set the completion handler (#4874) - + //we can't closeRemoveTorrent: until it's no longer in the GUI at all [[NSAnimationContext currentContext] setCompletionHandler: ^{ for (Torrent * torrent in torrents) [torrent closeRemoveTorrent: deleteData]; }]; - + [fTableView beginUpdates]; beganUpdate = YES; } - + [fTableView removeItemsAtIndexes: indexes inParent: parent withAnimation: NSTableViewAnimationSlideLeft]; [displayedTorrents removeObjectsAtIndexes: indexes]; } }; - + //if not removed from the displayed torrents here, fullUpdateUI might cause a crash if ([fDisplayedTorrents count] > 0) { @@ -1624,21 +1624,21 @@ static void removeKeRangerRansomware() } else doTableRemoval(fDisplayedTorrents, nil); - + if (beganUpdate) { [fTableView endUpdates]; [NSAnimationContext endGrouping]; } } - + if (!beganUpdate) { //do here if we're not doing it at the end of the animation for (Torrent * torrent in torrents) [torrent closeRemoveTorrent: deleteData]; } - + [self fullUpdateUI]; } @@ -1655,11 +1655,11 @@ static void removeKeRangerRansomware() - (void) clearCompleted: (id) sender { NSMutableArray * torrents = [NSMutableArray array]; - + for (Torrent * torrent in fTorrents) if ([torrent isFinishedSeeding]) [torrents addObject: torrent]; - + if ([fDefaults boolForKey: @"WarningRemoveCompleted"]) { NSString * message, * info; @@ -1668,7 +1668,7 @@ static void removeKeRangerRansomware() NSString * torrentName = [(Torrent *)[torrents objectAtIndex: 0] name]; message = [NSString stringWithFormat: NSLocalizedString(@"Are you sure you want to remove \"%@\" from the transfer list?", "Remove completed confirm panel -> title"), torrentName]; - + info = NSLocalizedString(@"Once removed, continuing the transfer will require the torrent file or magnet link.", "Remove completed confirm panel -> message"); } @@ -1676,11 +1676,11 @@ static void removeKeRangerRansomware() { message = [NSString stringWithFormat: NSLocalizedString(@"Are you sure you want to remove %@ completed transfers from the transfer list?", "Remove completed confirm panel -> title"), [NSString formattedUInteger: [torrents count]]]; - + info = NSLocalizedString(@"Once removed, continuing the transfers will require the torrent files or magnet links.", "Remove completed confirm panel -> message"); } - + NSAlert * alert = [[[NSAlert alloc] init] autorelease]; [alert setMessageText: message]; [alert setInformativeText: info]; @@ -1688,15 +1688,15 @@ static void removeKeRangerRansomware() [alert addButtonWithTitle: NSLocalizedString(@"Remove", "Remove completed confirm panel -> button")]; [alert addButtonWithTitle: NSLocalizedString(@"Cancel", "Remove completed confirm panel -> button")]; [alert setShowsSuppressionButton: YES]; - + const NSInteger returnCode = [alert runModal]; if ([[alert suppressionButton] state]) [fDefaults setBool: NO forKey: @"WarningRemoveCompleted"]; - + if (returnCode != NSAlertFirstButtonReturn) return; } - + [self confirmRemoveTorrents: torrents deleteData: NO]; } @@ -1713,7 +1713,7 @@ static void removeKeRangerRansomware() [panel setCanChooseFiles: NO]; [panel setCanChooseDirectories: YES]; [panel setCanCreateDirectories: YES]; - + NSInteger count = [torrents count]; if (count == 1) [panel setMessage: [NSString stringWithFormat: NSLocalizedString(@"Select the new folder for \"%@\".", @@ -1721,7 +1721,7 @@ static void removeKeRangerRansomware() else [panel setMessage: [NSString stringWithFormat: NSLocalizedString(@"Select the new folder for %d data files.", "Move torrent -> select destination folder"), count]]; - + [panel beginSheetModalForWindow: fWindow completionHandler: ^(NSInteger result) { if (result == NSFileHandlingPanelOKButton) { @@ -1743,22 +1743,22 @@ static void removeKeRangerRansomware() [torrents release]; return; } - + Torrent * torrent = [torrents objectAtIndex: 0]; - + if (![torrent isMagnet] && [[NSFileManager defaultManager] fileExistsAtPath: [torrent torrentLocation]]) { NSSavePanel * panel = [NSSavePanel savePanel]; [panel setAllowedFileTypes: [NSArray arrayWithObjects: @"org.bittorrent.torrent", @"torrent", nil]]; [panel setExtensionHidden: NO]; - + [panel setNameFieldStringValue: [torrent name]]; - + [panel beginSheetModalForWindow: fWindow completionHandler: ^(NSInteger result) { //copy torrent to new location with name of data file if (result == NSFileHandlingPanelOKButton) [torrent copyTorrentFileTo: [[panel URL] path]]; - + [torrents removeObjectAtIndex: 0]; [self performSelectorOnMainThread: @selector(copyTorrentFileForTorrents:) withObject: torrents waitUntilDone: NO]; }]; @@ -1771,15 +1771,15 @@ static void removeKeRangerRansomware() [alert addButtonWithTitle: NSLocalizedString(@"OK", "Torrent file copy alert -> button")]; [alert setMessageText: [NSString stringWithFormat: NSLocalizedString(@"Copy of \"%@\" Cannot Be Created", "Torrent file copy alert -> title"), [torrent name]]]; - [alert setInformativeText: [NSString stringWithFormat: + [alert setInformativeText: [NSString stringWithFormat: NSLocalizedString(@"The torrent file (%@) cannot be found.", "Torrent file copy alert -> message"), [torrent torrentLocation]]]; [alert setAlertStyle: NSWarningAlertStyle]; - + [alert runModal]; [alert release]; } - + [torrents removeObjectAtIndex: 0]; [self copyTorrentFileForTorrents: torrents]; } @@ -1788,16 +1788,16 @@ static void removeKeRangerRansomware() - (void) copyMagnetLinks: (id) sender { NSArray * torrents = [fTableView selectedTorrents]; - + if ([torrents count] <= 0) return; - + NSMutableArray * links = [NSMutableArray arrayWithCapacity: [torrents count]]; for (Torrent * torrent in torrents) [links addObject: [torrent magnetLink]]; - + NSString * text = [links componentsJoinedByString: @"\n"]; - + NSPasteboard * pb = [NSPasteboard generalPasteboard]; [pb clearContents]; [pb writeObjects: [NSArray arrayWithObject: text]]; @@ -1813,7 +1813,7 @@ static void removeKeRangerRansomware() if (location) [paths addObject: [NSURL fileURLWithPath: location]]; } - + if ([paths count] > 0) [[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs: paths]; } @@ -1823,13 +1823,13 @@ static void removeKeRangerRansomware() NSArray * selected = [fTableView selectedTorrents]; NSAssert([selected count] == 1, @"1 transfer needs to be selected to rename, but %ld are selected", [selected count]); Torrent * torrent = [selected objectAtIndex:0]; - + [FileRenameSheetController presentSheetForTorrent:torrent modalForWindow: fWindow completionHandler: ^(BOOL didRename) { if (didRename) { dispatch_async(dispatch_get_main_queue(), ^{ [self fullUpdateUI]; - + [[NSNotificationCenter defaultCenter] postNotificationName: @"ResetInspector" object: self userInfo: @{ @"Torrent" : torrent }]; }); } @@ -1854,7 +1854,7 @@ static void removeKeRangerRansomware() { for (Torrent * torrent in torrents) [torrent resetCache]; - + [self applyFilter]; } @@ -1885,18 +1885,18 @@ static void removeKeRangerRansomware() { [fInfoController updateInfoStats]; [[fInfoController window] orderFront: nil]; - + if ([fInfoController canQuickLook] && [QLPreviewPanel sharedPreviewPanelExists] && [[QLPreviewPanel sharedPreviewPanel] isVisible]) [[QLPreviewPanel sharedPreviewPanel] reloadData]; } - + [[fWindow toolbar] validateVisibleItems]; } - (void) resetInfo { [fInfoController setInfoForTorrents: [fTableView selectedTorrents]]; - + if ([QLPreviewPanel sharedPreviewPanelExists] && [[QLPreviewPanel sharedPreviewPanel] isVisible]) [[QLPreviewPanel sharedPreviewPanel] reloadData]; } @@ -1913,7 +1913,7 @@ static void removeKeRangerRansomware() { if (!fMessageController) fMessageController = [[MessageWindowController alloc] init]; - + return fMessageController; } @@ -1934,22 +1934,22 @@ static void removeKeRangerRansomware() for (Torrent * torrent in fTorrents) { [torrent update]; - + //pull the upload and download speeds - most consistent by using current stats dlRate += [torrent downloadRate]; ulRate += [torrent uploadRate]; - + anyCompleted |= [torrent isFinishedSeeding]; } - + if (![NSApp isHidden]) { if ([fWindow isVisible]) { [self sortTorrents: NO]; - + [fStatusBar updateWithDownload: dlRate upload: ulRate]; - + [fClearCompletedButton setHidden: !anyCompleted]; } @@ -1957,7 +1957,7 @@ static void removeKeRangerRansomware() if ([[fInfoController window] isVisible]) [fInfoController updateInfoStats]; } - + //badge dock [fBadger updateBadgeWithDownload: dlRate upload: ulRate]; } @@ -1980,17 +1980,17 @@ static void removeKeRangerRansomware() [NSString formattedUInteger: totalCount]]; else totalTorrentsString = NSLocalizedString(@"1 transfer", "Status bar transfer count"); - + if (filtering) { NSUInteger count = [fTableView numberOfRows]; //have to factor in collapsed rows if (count > 0 && ![[fDisplayedTorrents objectAtIndex: 0] isKindOfClass: [Torrent class]]) count -= [fDisplayedTorrents count]; - + totalTorrentsString = [NSString stringWithFormat: NSLocalizedString(@"%@ of %@", "Status bar transfer count"), [NSString formattedUInteger: count], totalTorrentsString]; } - + [fTotalTorrentsField setStringValue: totalTorrentsString]; } @@ -2003,7 +2003,7 @@ static void removeKeRangerRansomware() { if (![notification userInfo]) return; - + if ([notification activationType] == NSUserNotificationActivationTypeActionButtonClicked) //reveal { Torrent * torrent = [self torrentForHash: [[notification userInfo] objectForKey: @"Hash"]]; @@ -2039,15 +2039,15 @@ static void removeKeRangerRansomware() row = [fTableView rowForItem: torrent]; } } - + if (row == -1) { //not found - must be filtering NSAssert([fDefaults boolForKey: @"FilterBar"], @"expected the filter to be enabled"); [fFilterBar reset: YES]; - + row = [fTableView rowForItem: torrent]; - + //if it's not shown, it has to be in a collapsed row...again if ([fDefaults boolForKey: @"SortByGroup"]) { @@ -2067,7 +2067,7 @@ static void removeKeRangerRansomware() } } } - + NSAssert1(row != -1, @"expected a row to be found for torrent %@", torrent); [self showMainWindow: nil]; @@ -2079,7 +2079,7 @@ static void removeKeRangerRansomware() - (Torrent *) torrentForHash: (NSString *) hash { NSParameterAssert(hash != nil); - + __block Torrent * torrent = nil; [fTorrents enumerateObjectsWithOptions: NSEnumerationConcurrent usingBlock: ^(id obj, NSUInteger idx, BOOL * stop) { if ([[(Torrent *)obj hashString] isEqualToString: hash]) @@ -2094,7 +2094,7 @@ static void removeKeRangerRansomware() - (void) torrentFinishedDownloading: (NSNotification *) notification { Torrent * torrent = [notification object]; - + if ([[[notification userInfo] objectForKey: @"WasRunning"] boolValue]) { if (!fSoundPlaying && [fDefaults boolForKey: @"PlayDownloadSound"]) @@ -2107,48 +2107,48 @@ static void removeKeRangerRansomware() [sound play]; } } - + NSString * location = [torrent dataLocation]; - + NSString * notificationTitle = NSLocalizedString(@"Download Complete", "notification title"); if ([NSApp isOnMountainLionOrBetter]) { NSUserNotification * notification = [[NSUserNotificationMtLion alloc] init]; [notification setTitle: notificationTitle]; [notification setInformativeText: [torrent name]]; - + [notification setHasActionButton: YES]; [notification setActionButtonTitle: NSLocalizedString(@"Show", "notification button")]; - + NSMutableDictionary * userInfo = [NSMutableDictionary dictionaryWithObject: [torrent hashString] forKey: @"Hash"]; if (location) [userInfo setObject: location forKey: @"Location"]; [notification setUserInfo: userInfo]; - + [[NSUserNotificationCenterMtLion defaultUserNotificationCenter] deliverNotification: notification]; [notification release]; } - + NSMutableDictionary * clickContext = [NSMutableDictionary dictionaryWithObjectsAndKeys: GROWL_DOWNLOAD_COMPLETE, @"Type", nil]; - + if (location) [clickContext setObject: location forKey: @"Location"]; - + [GrowlApplicationBridge notifyWithTitle: notificationTitle description: [torrent name] notificationName: GROWL_DOWNLOAD_COMPLETE iconData: nil priority: 0 isSticky: NO clickContext: clickContext]; - + //NSLog(@"delegate: %@", [[NSUserNotificationCenterMtLion defaultUserNotificationCenter] delegate]); - + if (![fWindow isMainWindow]) [fBadger addCompletedTorrent: torrent]; - + //bounce download stack [[NSDistributedNotificationCenter defaultCenter] postNotificationName: @"com.apple.DownloadFileFinished" object: [torrent dataLocation]]; } - + [self fullUpdateUI]; } @@ -2160,7 +2160,7 @@ static void removeKeRangerRansomware() - (void) torrentFinishedSeeding: (NSNotification *) notification { Torrent * torrent = [notification object]; - + if (!fSoundPlaying && [fDefaults boolForKey: @"PlaySeedingSound"]) { NSSound * sound; @@ -2171,37 +2171,37 @@ static void removeKeRangerRansomware() [sound play]; } } - + NSString * location = [torrent dataLocation]; - + NSString * notificationTitle = NSLocalizedString(@"Seeding Complete", "notification title"); if ([NSApp isOnMountainLionOrBetter]) { NSUserNotification * notification = [[NSUserNotificationMtLion alloc] init]; [notification setTitle: notificationTitle]; [notification setInformativeText: [torrent name]]; - + [notification setHasActionButton: YES]; [notification setActionButtonTitle: NSLocalizedString(@"Show", "notification button")]; - + NSMutableDictionary * userInfo = [NSMutableDictionary dictionaryWithObject: [torrent hashString] forKey: @"Hash"]; if (location) [userInfo setObject: location forKey: @"Location"]; [notification setUserInfo: userInfo]; - + [[NSUserNotificationCenterMtLion defaultUserNotificationCenter] deliverNotification: notification]; [notification release]; } - + NSMutableDictionary * clickContext = [NSMutableDictionary dictionaryWithObject: GROWL_SEEDING_COMPLETE forKey: @"Type"]; - + if (location) [clickContext setObject: location forKey: @"Location"]; - + [GrowlApplicationBridge notifyWithTitle: notificationTitle description: [torrent name] notificationName: GROWL_SEEDING_COMPLETE iconData: nil priority: 0 isSticky: NO clickContext: clickContext]; - + //removing from the list calls fullUpdateUI if ([torrent removeWhenFinishSeeding]) [self confirmRemoveTorrents: @[ torrent ] deleteData: NO]; @@ -2209,9 +2209,9 @@ static void removeKeRangerRansomware() { if (![fWindow isMainWindow]) [fBadger addCompletedTorrent: torrent]; - + [self fullUpdateUI]; - + if ([[fTableView selectedTorrents] containsObject: torrent]) { [fInfoController updateInfoStats]; @@ -2223,10 +2223,10 @@ static void removeKeRangerRansomware() - (void) updateTorrentHistory { NSMutableArray * history = [NSMutableArray arrayWithCapacity: [fTorrents count]]; - + for (Torrent * torrent in fTorrents) [history addObject: [torrent history]]; - + NSString * historyFile = [fConfigDirectory stringByAppendingPathComponent: TRANSFER_PLIST]; [history writeToFile: historyFile atomically: YES]; } @@ -2265,9 +2265,9 @@ static void removeKeRangerRansomware() NSAssert1(NO, @"Unknown sort tag received: %ld", [(NSMenuItem *)sender tag]); return; } - + [fDefaults setObject: sortType forKey: @"Sort"]; - + [self sortTorrents: YES]; } @@ -2275,7 +2275,7 @@ static void removeKeRangerRansomware() { BOOL sortByGroup = ![fDefaults boolForKey: @"SortByGroup"]; [fDefaults setBool: sortByGroup forKey: @"SortByGroup"]; - + [self applyFilter]; } @@ -2299,17 +2299,17 @@ static void removeKeRangerRansomware() - (void) sortTorrentsCallUpdates: (BOOL) callUpdates includeQueueOrder: (BOOL) includeQueueOrder { const BOOL asc = ![fDefaults boolForKey: @"SortReverse"]; - + NSArray * descriptors; NSSortDescriptor * nameDescriptor = [NSSortDescriptor sortDescriptorWithKey: @"name" ascending: asc selector: @selector(localizedStandardCompare:)]; - + NSString * sortType = [fDefaults stringForKey: @"Sort"]; if ([sortType isEqualToString: SORT_STATE]) { NSSortDescriptor * stateDescriptor = [NSSortDescriptor sortDescriptorWithKey: @"stateSortKey" ascending: !asc], * progressDescriptor = [NSSortDescriptor sortDescriptorWithKey: @"progress" ascending: !asc], * ratioDescriptor = [NSSortDescriptor sortDescriptorWithKey: @"ratio" ascending: !asc]; - + descriptors = [NSArray arrayWithObjects: stateDescriptor, progressDescriptor, ratioDescriptor, nameDescriptor, nil]; } else if ([sortType isEqualToString: SORT_PROGRESS]) @@ -2317,32 +2317,32 @@ static void removeKeRangerRansomware() NSSortDescriptor * progressDescriptor = [NSSortDescriptor sortDescriptorWithKey: @"progress" ascending: asc], * ratioProgressDescriptor = [NSSortDescriptor sortDescriptorWithKey: @"progressStopRatio" ascending: asc], * ratioDescriptor = [NSSortDescriptor sortDescriptorWithKey: @"ratio" ascending: asc]; - + descriptors = [NSArray arrayWithObjects: progressDescriptor, ratioProgressDescriptor, ratioDescriptor, nameDescriptor, nil]; } else if ([sortType isEqualToString: SORT_TRACKER]) { NSSortDescriptor * trackerDescriptor = [NSSortDescriptor sortDescriptorWithKey: @"trackerSortKey" ascending: asc selector: @selector(localizedCaseInsensitiveCompare:)]; - + descriptors = [NSArray arrayWithObjects: trackerDescriptor, nameDescriptor, nil]; } else if ([sortType isEqualToString: SORT_ACTIVITY]) { NSSortDescriptor * rateDescriptor = [NSSortDescriptor sortDescriptorWithKey: @"totalRate" ascending: !asc]; NSSortDescriptor * activityDescriptor = [NSSortDescriptor sortDescriptorWithKey: @"dateActivityOrAdd" ascending: !asc]; - + descriptors = [NSArray arrayWithObjects: rateDescriptor, activityDescriptor, nameDescriptor, nil]; } else if ([sortType isEqualToString: SORT_DATE]) { NSSortDescriptor * dateDescriptor = [NSSortDescriptor sortDescriptorWithKey: @"dateAdded" ascending: asc]; - + descriptors = [NSArray arrayWithObjects: dateDescriptor, nameDescriptor, nil]; } else if ([sortType isEqualToString: SORT_SIZE]) { NSSortDescriptor * sizeDescriptor = [NSSortDescriptor sortDescriptorWithKey: @"size" ascending: asc]; - + descriptors = [NSArray arrayWithObjects: sizeDescriptor, nameDescriptor, nil]; } else if ([sortType isEqualToString: SORT_NAME]) @@ -2352,17 +2352,17 @@ static void removeKeRangerRansomware() else { NSAssert1([sortType isEqualToString: SORT_ORDER], @"Unknown sort type received: %@", sortType); - + if (!includeQueueOrder) return; - + NSSortDescriptor * orderDescriptor = [NSSortDescriptor sortDescriptorWithKey: @"queuePosition" ascending: asc]; - + descriptors = [NSArray arrayWithObject: orderDescriptor]; } - + BOOL beganTableUpdate = !callUpdates; - + //actually sort if ([fDefaults boolForKey: @"SortByGroup"]) { @@ -2371,7 +2371,7 @@ static void removeKeRangerRansomware() } else [self rearrangeTorrentTableArray: fDisplayedTorrents forParent: nil withSortDescriptors: descriptors beganTableUpdate: &beganTableUpdate]; - + if (beganTableUpdate && callUpdates) { [fTableView endUpdates]; @@ -2391,10 +2391,10 @@ static void removeKeRangerRansomware() if (result != NSOrderedSame) return result; } - + return NSOrderedSame; }]; - + if (insertIndex != currentIndex) { if (!*beganTableUpdate) @@ -2402,12 +2402,12 @@ static void removeKeRangerRansomware() *beganTableUpdate = YES; [fTableView beginUpdates]; } - + [rearrangeArray moveObjectAtIndex: currentIndex toIndex: insertIndex]; [fTableView moveItemAtIndex: currentIndex inParent: parent toIndex: insertIndex inParent: parent]; } } - + NSAssert2([rearrangeArray isEqualToArray: [rearrangeArray sortedArrayUsingDescriptors: descriptors]], @"Torrent rearranging didn't work! %@ %@", rearrangeArray, [rearrangeArray sortedArrayUsingDescriptors: descriptors]); } @@ -2426,15 +2426,15 @@ static void removeKeRangerRansomware() filterPause = YES; else filterStatus = NO; - + const NSInteger groupFilterValue = [fDefaults integerForKey: @"FilterGroup"]; const BOOL filterGroup = groupFilterValue != GROUP_FILTER_ALL_TAG; - + NSArray * searchStrings = [fFilterBar searchStrings]; if (searchStrings && [searchStrings count] == 0) searchStrings = nil; const BOOL filterTracker = searchStrings && [[fDefaults stringForKey: @"FilterSearchType"] isEqualToString: FILTER_TYPE_TRACKER]; - + //filter & get counts of each type NSIndexSet * indexesOfNonFilteredTorrents = [fTorrents indexesOfObjectsWithOptions: NSEnumerationConcurrent passingTest: ^BOOL(Torrent * torrent, NSUInteger idx, BOOL * stop) { //check status @@ -2443,7 +2443,7 @@ static void removeKeRangerRansomware() const BOOL isActive = ![torrent isStalled]; if (isActive) OSAtomicIncrement32(&active); - + if ([torrent isSeeding]) { OSAtomicIncrement32(&seeding); @@ -2463,12 +2463,12 @@ static void removeKeRangerRansomware() if (filterStatus && !filterPause) return NO; } - + //checkGroup if (filterGroup) if ([torrent groupValue] != groupFilterValue) return NO; - + //check text field if (searchStrings) { @@ -2476,7 +2476,7 @@ static void removeKeRangerRansomware() if (filterTracker) { NSArray * trackers = [torrent allTrackersFlat]; - + //to count, we need each string in at least 1 tracker [searchStrings enumerateObjectsWithOptions: NSEnumerationConcurrent usingBlock: ^(id searchString, NSUInteger idx, BOOL * stop) { __block BOOL found = NO; @@ -2504,24 +2504,24 @@ static void removeKeRangerRansomware() } }]; } - + if (removeTextField) return NO; } - + return YES; }]; - + NSArray * allTorrents = [fTorrents objectsAtIndexes: indexesOfNonFilteredTorrents]; - + //set button tooltips if (fFilterBar) [fFilterBar setCountAll: [fTorrents count] active: active downloading: downloading seeding: seeding paused: paused]; - + //if either the previous or current lists are blank, set its value to the other const BOOL groupRows = [allTorrents count] > 0 ? [fDefaults boolForKey: @"SortByGroup"] : ([fDisplayedTorrents count] > 0 && [[fDisplayedTorrents objectAtIndex: 0] isKindOfClass: [TorrentGroup class]]); const BOOL wasGroupRows = [fDisplayedTorrents count] > 0 ? [[fDisplayedTorrents objectAtIndex: 0] isKindOfClass: [TorrentGroup class]] : groupRows; - + #warning could probably be merged with later code somehow //clear display cache for not-shown torrents if ([fDisplayedTorrents count] > 0) @@ -2532,7 +2532,7 @@ static void removeKeRangerRansomware() if (![allTorrents containsObject: torrent]) [torrent setPreviousFinishedPieces: nil]; }; - + if (wasGroupRows) [fDisplayedTorrents enumerateObjectsWithOptions: NSEnumerationConcurrent usingBlock: ^(id obj, NSUInteger idx, BOOL * stop) { [[(TorrentGroup *)obj torrents] enumerateObjectsWithOptions: NSEnumerationConcurrent usingBlock: removePreviousFinishedPieces]; @@ -2540,7 +2540,7 @@ static void removeKeRangerRansomware() else [fDisplayedTorrents enumerateObjectsWithOptions: NSEnumerationConcurrent usingBlock: removePreviousFinishedPieces]; } - + BOOL beganUpdates = NO; //don't animate torrents when first launching @@ -2549,13 +2549,13 @@ static void removeKeRangerRansomware() [[NSAnimationContext currentContext] setDuration: 0]; }); [NSAnimationContext beginGrouping]; - + //add/remove torrents (and rearrange for groups), one by one if (!groupRows && !wasGroupRows) { NSMutableIndexSet * addIndexes = [NSMutableIndexSet indexSet], * removePreviousIndexes = [NSMutableIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [fDisplayedTorrents count])]; - + //for each of the torrents to add, find if it already exists (and keep track of those we've already added & those we need to remove) [allTorrents enumerateObjectsWithOptions: 0 usingBlock: ^(id objAll, NSUInteger previousIndex, BOOL * stop) { const NSUInteger currentIndex = [fDisplayedTorrents indexOfObjectAtIndexes: removePreviousIndexes options: NSEnumerationConcurrent passingTest: ^(id objDisplay, NSUInteger idx, BOOL *stop) { @@ -2566,19 +2566,19 @@ static void removeKeRangerRansomware() else [removePreviousIndexes removeIndex: currentIndex]; }]; - + if ([addIndexes count] > 0 || [removePreviousIndexes count] > 0) { beganUpdates = YES; [fTableView beginUpdates]; - + //remove torrents we didn't find if ([removePreviousIndexes count] > 0) { [fDisplayedTorrents removeObjectsAtIndexes: removePreviousIndexes]; [fTableView removeItemsAtIndexes: removePreviousIndexes inParent: nil withAnimation: NSTableViewAnimationSlideDown]; } - + //add new torrents if ([addIndexes count] > 0) { @@ -2588,13 +2588,13 @@ static void removeKeRangerRansomware() NSIndexSet * newAddIndexes = [allTorrents indexesOfObjectsAtIndexes: addIndexes options: NSEnumerationConcurrent passingTest: ^BOOL(id obj, NSUInteger idx, BOOL * stop) { return [fAddingTransfers containsObject: obj]; }]; - + [addIndexes removeIndexes: newAddIndexes]; - + [fDisplayedTorrents addObjectsFromArray: [allTorrents objectsAtIndexes: newAddIndexes]]; [fTableView insertItemsAtIndexes: [NSIndexSet indexSetWithIndexesInRange: NSMakeRange([fDisplayedTorrents count] - [newAddIndexes count], [newAddIndexes count])] inParent: nil withAnimation: NSTableViewAnimationSlideLeft]; } - + [fDisplayedTorrents addObjectsFromArray: [allTorrents objectsAtIndexes: addIndexes]]; [fTableView insertItemsAtIndexes: [NSIndexSet indexSetWithIndexesInRange: NSMakeRange([fDisplayedTorrents count] - [addIndexes count], [addIndexes count])] inParent: nil withAnimation: NSTableViewAnimationSlideDown]; } @@ -2603,24 +2603,24 @@ static void removeKeRangerRansomware() else if (groupRows && wasGroupRows) { NSAssert(groupRows && wasGroupRows, @"Should have had group rows and should remain with group rows"); - + #warning don't always do? beganUpdates = YES; [fTableView beginUpdates]; - + NSMutableIndexSet * unusedAllTorrentsIndexes = [NSMutableIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [allTorrents count])]; - + NSMutableDictionary * groupsByIndex = [NSMutableDictionary dictionaryWithCapacity: [fDisplayedTorrents count]]; for (TorrentGroup * group in fDisplayedTorrents) [groupsByIndex setObject: group forKey: [NSNumber numberWithInteger: [group groupIndex]]]; - + const NSUInteger originalGroupCount = [fDisplayedTorrents count]; for (NSUInteger index = 0; index < originalGroupCount; ++index) { TorrentGroup * group = [fDisplayedTorrents objectAtIndex: index]; - + NSMutableIndexSet * removeIndexes = [NSMutableIndexSet indexSet]; - + //needs to be a signed integer for (NSInteger indexInGroup = 0; indexInGroup < [[group torrents] count]; ++indexInGroup) { @@ -2633,7 +2633,7 @@ static void removeKeRangerRansomware() else { BOOL markTorrentAsUsed = YES; - + const NSInteger groupValue = [torrent groupValue]; if (groupValue != [group groupIndex]) { @@ -2653,27 +2653,27 @@ static void removeKeRangerRansomware() if ([fDisplayedTorrents indexOfObject: newGroup inRange: NSMakeRange(index+1, originalGroupCount-(index+1))] != NSNotFound) markTorrentAsUsed = NO; } - + [[group torrents] removeObjectAtIndex: indexInGroup]; [[newGroup torrents] addObject: torrent]; [fTableView moveItemAtIndex: indexInGroup inParent: group toIndex: [[newGroup torrents] count]-1 inParent: newGroup]; - + --indexInGroup; } - + if (markTorrentAsUsed) [unusedAllTorrentsIndexes removeIndex: allIndex]; } } - + if ([removeIndexes count] > 0) { [[group torrents] removeObjectsAtIndexes: removeIndexes]; [fTableView removeItemsAtIndexes: removeIndexes inParent: group withAnimation: NSTableViewAnimationEffectFade]; } } - + //add remaining new torrents for (Torrent * torrent in [allTorrents objectsAtIndexes: unusedAllTorrentsIndexes]) { @@ -2688,24 +2688,24 @@ static void removeKeRangerRansomware() [fTableView insertItemsAtIndexes: [NSIndexSet indexSetWithIndex: [fDisplayedTorrents count]-1] inParent: nil withAnimation: NSTableViewAnimationEffectFade]; [fTableView isGroupCollapsed: groupValue] ? [fTableView collapseItem: group] : [fTableView expandItem: group]; } - + [[group torrents] addObject: torrent]; const BOOL newTorrent = fAddingTransfers && [fAddingTransfers containsObject: torrent]; [fTableView insertItemsAtIndexes: [NSIndexSet indexSetWithIndex: [[group torrents] count]-1] inParent: group withAnimation: newTorrent ? NSTableViewAnimationSlideLeft : NSTableViewAnimationSlideDown]; } - + //remove empty groups NSIndexSet * removeGroupIndexes = [fDisplayedTorrents indexesOfObjectsAtIndexes: [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, originalGroupCount)] options: NSEnumerationConcurrent passingTest: ^BOOL(id obj, NSUInteger idx, BOOL * stop) { return [[(TorrentGroup *)obj torrents] count] == 0; }]; - + if ([removeGroupIndexes count] > 0) { [fDisplayedTorrents removeObjectsAtIndexes: removeGroupIndexes]; [fTableView removeItemsAtIndexes: removeGroupIndexes inParent: nil withAnimation: NSTableViewAnimationEffectFade]; } - + //now that all groups are there, sort them - don't insert on the fly in case groups were reordered in prefs NSSortDescriptor * groupDescriptor = [NSSortDescriptor sortDescriptorWithKey: @"groupOrderValue" ascending: YES]; [self rearrangeTorrentTableArray: fDisplayedTorrents forParent: nil withSortDescriptors: [NSArray arrayWithObject: groupDescriptor] beganTableUpdate: &beganUpdates]; @@ -2713,19 +2713,19 @@ static void removeKeRangerRansomware() else { NSAssert(groupRows != wasGroupRows, @"Trying toggling group-torrent reordering when we weren't expecting to."); - + //set all groups as expanded [fTableView removeAllCollapsedGroups]; - + //since we're not doing this the right way (boo buggy animation), we need to remember selected values #warning when Lion-only and using views instead of cells, this likely won't be needed NSArray * selectedValues = [fTableView selectedValues]; - + beganUpdates = YES; [fTableView beginUpdates]; [fTableView removeItemsAtIndexes: [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [fDisplayedTorrents count])] inParent: nil withAnimation: NSTableViewAnimationSlideDown]; - + if (groupRows) { //a map for quickly finding groups @@ -2739,12 +2739,12 @@ static void removeKeRangerRansomware() group = [[[TorrentGroup alloc] initWithGroup: groupValue] autorelease]; [groupsByIndex setObject: group forKey: [NSNumber numberWithInteger: groupValue]]; } - + [[group torrents] addObject: torrent]; } - + [fDisplayedTorrents setArray: [groupsByIndex allValues]]; - + //we need the groups to be sorted, and we can do it without moving items in the table, too! NSSortDescriptor * groupDescriptor = [NSSortDescriptor sortDescriptorWithKey: @"groupOrderValue" ascending: YES]; [fDisplayedTorrents sortUsingDescriptors: [NSArray arrayWithObject: groupDescriptor]]; @@ -2753,33 +2753,33 @@ static void removeKeRangerRansomware() [fDisplayedTorrents setArray: allTorrents]; [fTableView insertItemsAtIndexes: [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [fDisplayedTorrents count])] inParent: nil withAnimation: NSTableViewAnimationEffectFade]; - - if (groupRows) - { - //actually expand group rows - for (TorrentGroup * group in fDisplayedTorrents) - [fTableView expandItem: group]; - } - + + if (groupRows) + { + //actually expand group rows + for (TorrentGroup * group in fDisplayedTorrents) + [fTableView expandItem: group]; + } + if (selectedValues) [fTableView selectValues: selectedValues]; } - + //sort the torrents (won't sort the groups, though) [self sortTorrentsCallUpdates: !beganUpdates includeQueueOrder: YES]; if (beganUpdates) [fTableView endUpdates]; [fTableView setNeedsDisplay: YES]; - + [NSAnimationContext endGrouping]; [self resetInfo]; //if group is already selected, but the torrents in it change - + [self setBottomCountText: groupRows || filterStatus || filterGroup || searchStrings]; - + [self setWindowSizeToFit]; - + if (fAddingTransfers) { [fAddingTransfers release]; @@ -2796,15 +2796,15 @@ static void removeKeRangerRansomware() { if (fGlobalPopoverShown) return; - + NSPopover * popover = [[NSPopover alloc] init]; [popover setBehavior: NSPopoverBehaviorTransient]; GlobalOptionsPopoverViewController * viewController = [[GlobalOptionsPopoverViewController alloc] initWithHandle: fLib]; [popover setContentViewController: viewController]; [popover setDelegate: self]; - + [popover showRelativeToRect: [sender frame] ofView: sender preferredEdge: NSMaxYEdge]; - + [viewController release]; [popover release]; } @@ -2826,9 +2826,9 @@ static void removeKeRangerRansomware() { for (NSInteger i = [menu numberOfItems]-1; i >= 0; i--) [menu removeItemAtIndex: i]; - + NSMenu * groupMenu = [[GroupsController groups] groupMenuWithTarget: self action: @selector(setGroup:) isSmall: NO]; - + const NSInteger groupMenuCount = [groupMenu numberOfItems]; for (NSInteger i = 0; i < groupMenuCount; i++) { @@ -2840,7 +2840,7 @@ static void removeKeRangerRansomware() } else if (menu == fShareMenu || menu == fShareContextMenu) { [menu removeAllItems]; - + for (NSMenuItem * item in [[ShareTorrentFileHelper sharedHelper] menuItems]) { [menu addItem:item]; @@ -2854,10 +2854,10 @@ static void removeKeRangerRansomware() for (Torrent * torrent in [fTableView selectedTorrents]) { [fTableView removeCollapsedGroup: [torrent groupValue]]; //remove old collapsed group - + [torrent setGroupValue: [(NSMenuItem *)sender tag] determinationType: TorrentDeterminationUserSpecified]; } - + [self applyFilter]; [self updateUI]; [self updateTorrentHistory]; @@ -2882,7 +2882,7 @@ static void removeKeRangerRansomware() [fDefaults setBool: isLimited forKey: @"SpeedLimit"]; [fStatusBar updateSpeedFieldsToolTips]; - + if (![[dict objectForKey: @"ByUser"] boolValue]) [GrowlApplicationBridge notifyWithTitle: isLimited ? NSLocalizedString(@"Speed Limit Auto Enabled", "Growl notification title") : NSLocalizedString(@"Speed Limit Auto Disabled", "Growl notification title") description: NSLocalizedString(@"Bandwidth settings changed", "Growl notification description") @@ -2892,7 +2892,7 @@ static void removeKeRangerRansomware() isSticky: NO clickContext: nil identifier: GROWL_AUTO_SPEED_LIMIT]; - + [dict release]; } @@ -2904,18 +2904,18 @@ static void removeKeRangerRansomware() -(void) VDKQueue: (VDKQueue *) queue receivedNotification: (NSString*) notification forPath: (NSString*) fpath { //don't assume that just because we're watching for write notification, we'll only receive write notifications - + if (![fDefaults boolForKey: @"AutoImport"] || ![fDefaults stringForKey: @"AutoImportDirectory"]) return; - + if ([fAutoImportTimer isValid]) [fAutoImportTimer invalidate]; [fAutoImportTimer release]; - + //check again in 10 seconds in case torrent file wasn't complete - fAutoImportTimer = [[NSTimer scheduledTimerWithTimeInterval: 10.0 target: self + fAutoImportTimer = [[NSTimer scheduledTimerWithTimeInterval: 10.0 target: self selector: @selector(checkAutoImportDirectory) userInfo: nil repeats: NO] retain]; - + [self checkAutoImportDirectory]; } @@ -2925,10 +2925,10 @@ static void removeKeRangerRansomware() [fAutoImportTimer invalidate]; [fAutoImportTimer release]; fAutoImportTimer = nil; - + [fAutoImportedNames release]; fAutoImportedNames = nil; - + [self checkAutoImportDirectory]; } @@ -2937,70 +2937,70 @@ static void removeKeRangerRansomware() NSString * path; if (![fDefaults boolForKey: @"AutoImport"] || !(path = [fDefaults stringForKey: @"AutoImportDirectory"])) return; - + path = [path stringByExpandingTildeInPath]; - + NSArray * importedNames; if (!(importedNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath: path error: NULL])) return; - + //only check files that have not been checked yet NSMutableArray * newNames = [importedNames mutableCopy]; - + if (fAutoImportedNames) [newNames removeObjectsInArray: fAutoImportedNames]; else fAutoImportedNames = [[NSMutableArray alloc] init]; [fAutoImportedNames setArray: importedNames]; - + for (NSString * file in newNames) { if ([file hasPrefix: @"."]) continue; - + NSString * fullFile = [path stringByAppendingPathComponent: file]; - + if (!([[[NSWorkspace sharedWorkspace] typeOfFile: fullFile error: NULL] isEqualToString: @"org.bittorrent.torrent"] || [[fullFile pathExtension] caseInsensitiveCompare: @"torrent"] == NSOrderedSame)) continue; - + tr_ctor * ctor = tr_ctorNew(fLib); tr_ctorSetMetainfoFromFile(ctor, [fullFile UTF8String]); - + switch (tr_torrentParse(ctor, NULL)) { case TR_PARSE_OK: [self openFiles: [NSArray arrayWithObject: fullFile] addType: ADD_AUTO forcePath: nil]; - + NSString * notificationTitle = NSLocalizedString(@"Torrent File Auto Added", "notification title"); if ([NSApp isOnMountainLionOrBetter]) { NSUserNotification* notification = [[NSUserNotificationMtLion alloc] init]; [notification setTitle: notificationTitle]; [notification setInformativeText: file]; - + [notification setHasActionButton: NO]; - + [[NSUserNotificationCenterMtLion defaultUserNotificationCenter] deliverNotification: notification]; [notification release]; } - + [GrowlApplicationBridge notifyWithTitle: notificationTitle description: file notificationName: GROWL_AUTO_ADD iconData: nil priority: 0 isSticky: NO clickContext: nil]; break; - + case TR_PARSE_ERR: [fAutoImportedNames removeObject: file]; break; - + case TR_PARSE_DUPLICATE: //let's ignore this (but silence a warning) break; } - + tr_ctorFree(ctor); } - + [newNames release]; } @@ -3008,10 +3008,10 @@ static void removeKeRangerRansomware() { if (![fDefaults boolForKey: @"AutoImport"]) return; - + NSString * location = [(NSURL *)[notification object] path], * path = [fDefaults stringForKey: @"AutoImportDirectory"]; - + if (location && path && [[[location stringByDeletingLastPathComponent] stringByExpandingTildeInPath] isEqualToString: [path stringByExpandingTildeInPath]]) [fAutoImportedNames addObject: [location lastPathComponent]]; @@ -3067,7 +3067,7 @@ static void removeKeRangerRansomware() else { TorrentGroup * group = (TorrentGroup *)item; - + if ([fDefaults boolForKey: @"DisplayGroupRowRatio"]) return [NSString stringForRatio: [group ratio]]; else @@ -3089,10 +3089,10 @@ static void removeKeRangerRansomware() { if (![torrent isKindOfClass: [Torrent class]]) return NO; - + [indexSet addIndex: [fTableView rowForItem: torrent]]; } - + [pasteboard declareTypes: [NSArray arrayWithObject: TORRENT_TABLE_VIEW_DATA_TYPE] owner: self]; [pasteboard setData: [NSKeyedArchiver archivedDataWithRootObject: indexSet] forType: TORRENT_TABLE_VIEW_DATA_TYPE]; return YES; @@ -3110,7 +3110,7 @@ static void removeKeRangerRansomware() { if (!item) return NSDragOperationNone; - + if ([[fDefaults stringForKey: @"Sort"] isEqualToString: SORT_ORDER]) { if ([item isKindOfClass: [Torrent class]]) @@ -3131,18 +3131,18 @@ static void removeKeRangerRansomware() { if (index == NSOutlineViewDropOnItemIndex) return NSDragOperationNone; - + if (item) { index = [fTableView rowForItem: item] + 1; item = nil; } } - + [fTableView setDropItem: item dropChildIndex: index]; return NSDragOperationGeneric; } - + return NSDragOperationNone; } @@ -3152,19 +3152,19 @@ static void removeKeRangerRansomware() if ([[pasteboard types] containsObject: TORRENT_TABLE_VIEW_DATA_TYPE]) { NSIndexSet * indexes = [NSKeyedUnarchiver unarchiveObjectWithData: [pasteboard dataForType: TORRENT_TABLE_VIEW_DATA_TYPE]]; - + //get the torrents to move NSMutableArray * movingTorrents = [NSMutableArray arrayWithCapacity: [indexes count]]; for (NSUInteger i = [indexes firstIndex]; i != NSNotFound; i = [indexes indexGreaterThanIndex: i]) { Torrent * torrent = [fTableView itemAtRow: i]; [movingTorrents addObject: torrent]; - + //change groups if (item) [torrent setGroupValue: [item groupIndex] determinationType: TorrentDeterminationUserSpecified]; } - + //reorder queue order if (newRow != NSOutlineViewDropOnItemIndex) { @@ -3180,15 +3180,15 @@ static void removeKeRangerRansomware() break; } } - + //remove objects to reinsert [fTorrents removeObjectsInArray: movingTorrents]; - + //insert objects at new location const NSUInteger insertIndex = topTorrent ? [fTorrents indexOfObject: topTorrent] + 1 : 0; NSIndexSet * insertIndexes = [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(insertIndex, [movingTorrents count])]; [fTorrents insertObjects: movingTorrents atIndexes: insertIndexes]; - + //we need to make sure the queue order is updated in the Torrent object before we sort - safest to just reset all queue positions NSUInteger i = 0; for (Torrent * torrent in fTorrents) @@ -3196,18 +3196,18 @@ static void removeKeRangerRansomware() [torrent setQueuePosition: i++]; [torrent update]; } - + //do the drag animation here so that the dragged torrents are the ones that are animated as moving, and not the torrents around them [fTableView beginUpdates]; - + NSUInteger insertDisplayIndex = topTorrent ? [groupTorrents indexOfObject: topTorrent] + 1 : 0; - + for (Torrent * torrent in movingTorrents) { TorrentGroup * oldParent = item ? [fTableView parentForItem: torrent] : nil; NSMutableArray * oldTorrents = oldParent ? [oldParent torrents] : fDisplayedTorrents; const NSUInteger oldIndex = [oldTorrents indexOfObject: torrent]; - + if (item == oldParent) { if (oldIndex < insertDisplayIndex) @@ -3217,23 +3217,23 @@ static void removeKeRangerRansomware() else { NSAssert(item && oldParent, @"Expected to be dragging between group rows"); - + NSMutableArray * newTorrents = [(TorrentGroup *)item torrents]; [newTorrents insertObject: torrent atIndex: insertDisplayIndex]; [oldTorrents removeObjectAtIndex: oldIndex]; } [fTableView moveItemAtIndex: oldIndex inParent: oldParent toIndex: insertDisplayIndex inParent: item]; - + ++insertDisplayIndex; } [fTableView endUpdates]; } - + [self applyFilter]; } - + return YES; } @@ -3264,20 +3264,20 @@ static void removeKeRangerRansomware() if (!fOverlayWindow) fOverlayWindow = [[DragOverlayWindow alloc] initWithLib: fLib forWindow: fWindow]; [fOverlayWindow setTorrents: files]; - + return NSDragOperationCopy; } tr_ctorFree(ctor); } } - + //create a torrent file if a single file if (!torrent && [files count] == 1) { if (!fOverlayWindow) fOverlayWindow = [[DragOverlayWindow alloc] initWithLib: fLib forWindow: fWindow]; [fOverlayWindow setFile: [[files objectAtIndex: 0] lastPathComponent]]; - + return NSDragOperationCopy; } } @@ -3286,11 +3286,11 @@ static void removeKeRangerRansomware() if (!fOverlayWindow) fOverlayWindow = [[DragOverlayWindow alloc] initWithLib: fLib forWindow: fWindow]; [fOverlayWindow setURL: [[NSURL URLFromPasteboard: pasteboard] relativeString]]; - + return NSDragOperationCopy; } else; - + return NSDragOperationNone; } @@ -3304,12 +3304,12 @@ static void removeKeRangerRansomware() { if (fOverlayWindow) [fOverlayWindow fadeOut]; - + NSPasteboard * pasteboard = [info draggingPasteboard]; if ([[pasteboard types] containsObject: NSFilenamesPboardType]) { BOOL torrent = NO, accept = YES; - + //create an array of files that can be opened NSArray * files = [pasteboard propertyListForType: NSFilenamesPboardType]; NSMutableArray * filesToOpen = [NSMutableArray arrayWithCapacity: [files count]]; @@ -3326,7 +3326,7 @@ static void removeKeRangerRansomware() tr_ctorFree(ctor); } } - + if ([filesToOpen count] > 0) [self application: NSApp openFiles: filesToOpen]; else @@ -3336,7 +3336,7 @@ static void removeKeRangerRansomware() else accept = NO; } - + return accept; } else if ([[pasteboard types] containsObject: NSURLPboardType]) @@ -3349,7 +3349,7 @@ static void removeKeRangerRansomware() } } else; - + return NO; } @@ -3357,24 +3357,24 @@ static void removeKeRangerRansomware() { BOOL makeSmall = ![fDefaults boolForKey: @"SmallView"]; [fDefaults setBool: makeSmall forKey: @"SmallView"]; - + [fTableView setUsesAlternatingRowBackgroundColors: !makeSmall]; - + [fTableView setRowHeight: makeSmall ? ROW_HEIGHT_SMALL : ROW_HEIGHT_REGULAR]; [fTableView beginUpdates]; [fTableView noteHeightOfRowsWithIndexesChanged: [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [fTableView numberOfRows])]]; [fTableView endUpdates]; - + //resize for larger min height if not set to auto size if (![fDefaults boolForKey: @"AutoSize"]) { const NSSize contentSize = [[fWindow contentView] frame].size; - + NSSize contentMinSize = [fWindow contentMinSize]; contentMinSize.height = [self minWindowContentSizeAllowed]; [fWindow setContentMinSize: contentMinSize]; - + //make sure the window already isn't too small if (!makeSmall && contentSize.height < contentMinSize.height) { @@ -3382,7 +3382,7 @@ static void removeKeRangerRansomware() CGFloat heightChange = contentMinSize.height - contentSize.height; frame.size.height += heightChange; frame.origin.y -= heightChange; - + [fWindow setFrame: frame display: YES]; } } @@ -3405,18 +3405,18 @@ static void removeKeRangerRansomware() - (NSRect) windowFrameByAddingHeight: (CGFloat) height checkLimits: (BOOL) check { NSScrollView * scrollView = [fTableView enclosingScrollView]; - + //convert pixels to points NSRect windowFrame = [fWindow frame]; NSSize windowSize = [scrollView convertSize: windowFrame.size fromView: nil]; windowSize.height += height; - + if (check) { //we can't call minSize, since it might be set to the current size (auto size) const CGFloat minHeight = [self minWindowContentSizeAllowed] + (NSHeight([fWindow frame]) - NSHeight([[fWindow contentView] frame])); //contentView to window - + if (windowSize.height <= minHeight) windowSize.height = minHeight; else @@ -3456,31 +3456,31 @@ static void removeKeRangerRansomware() const BOOL prevShown = fStatusBar != nil; if (show == prevShown) return; - + if (show) { fStatusBar = [[StatusBarController alloc] initWithLib: fLib]; - + NSView * contentView = [fWindow contentView]; const NSSize windowSize = [contentView convertSize: [fWindow frame].size fromView: nil]; - + NSRect statusBarFrame = [[fStatusBar view] frame]; statusBarFrame.size.width = windowSize.width; [[fStatusBar view] setFrame: statusBarFrame]; - + [contentView addSubview: [fStatusBar view]]; [[fStatusBar view] setFrameOrigin: NSMakePoint(0.0, NSMaxY([contentView frame]))]; } - + CGFloat heightChange = [[fStatusBar view] frame].size.height; if (!show) heightChange *= -1; - + //allow bar to show even if not enough room if (show && ![fDefaults boolForKey: @"AutoSize"]) { NSRect frame = [self windowFrameByAddingHeight: heightChange checkLimits: NO]; - + NSScreen * screen = [fWindow screen]; if (screen) { @@ -3494,11 +3494,11 @@ static void removeKeRangerRansomware() } } } - + [self updateUI]; - + NSScrollView * scrollView = [fTableView enclosingScrollView]; - + //set views to not autoresize const NSUInteger statsMask = [[fStatusBar view] autoresizingMask]; [[fStatusBar view] setAutoresizingMask: NSViewNotSizable]; @@ -3510,23 +3510,23 @@ static void removeKeRangerRansomware() } const NSUInteger scrollMask = [scrollView autoresizingMask]; [scrollView setAutoresizingMask: NSViewNotSizable]; - + NSRect frame = [self windowFrameByAddingHeight: heightChange checkLimits: NO]; - [fWindow setFrame: frame display: YES animate: animate]; - + [fWindow setFrame: frame display: YES animate: animate]; + //re-enable autoresize [[fStatusBar view] setAutoresizingMask: statsMask]; if (fFilterBar) [[fFilterBar view] setAutoresizingMask: filterMask]; [scrollView setAutoresizingMask: scrollMask]; - + if (!show) { [[fStatusBar view] removeFromSuperviewWithoutNeedingDisplay]; [fStatusBar release]; fStatusBar = nil; } - + if ([fDefaults boolForKey: @"AutoSize"]) [self setWindowMinMaxToCurrent]; else @@ -3541,15 +3541,15 @@ static void removeKeRangerRansomware() - (void) toggleFilterBar: (id) sender { const BOOL show = fFilterBar == nil; - + //disable filtering when hiding (have to do before showFilterBar:animate:) if (!show) [fFilterBar reset: NO]; - + [self showFilterBar: show animate: YES]; [fDefaults setBool: show forKey: @"FilterBar"]; [[fWindow toolbar] validateVisibleItems]; - + [self applyFilter]; //do even if showing to ensure tooltips are updated } @@ -3559,18 +3559,18 @@ static void removeKeRangerRansomware() const BOOL prevShown = fFilterBar != nil; if (show == prevShown) return; - + if (show) { fFilterBar = [[FilterBarController alloc] init]; - + NSView * contentView = [fWindow contentView]; const NSSize windowSize = [contentView convertSize: [fWindow frame].size fromView: nil]; - + NSRect filterBarFrame = [[fFilterBar view] frame]; filterBarFrame.size.width = windowSize.width; [[fFilterBar view] setFrame: filterBarFrame]; - + if (fStatusBar) [contentView addSubview: [fFilterBar view] positioned: NSWindowBelow relativeTo: [fStatusBar view]]; else @@ -3580,16 +3580,16 @@ static void removeKeRangerRansomware() } else [fWindow makeFirstResponder: fTableView]; - + CGFloat heightChange = NSHeight([[fFilterBar view] frame]); if (!show) heightChange *= -1; - + //allow bar to show even if not enough room if (show && ![fDefaults boolForKey: @"AutoSize"]) { NSRect frame = [self windowFrameByAddingHeight: heightChange checkLimits: NO]; - + NSScreen * screen = [fWindow screen]; if (screen) { @@ -3603,7 +3603,7 @@ static void removeKeRangerRansomware() } } } - + NSScrollView * scrollView = [fTableView enclosingScrollView]; //set views to not autoresize @@ -3611,21 +3611,21 @@ static void removeKeRangerRansomware() const NSUInteger scrollMask = [scrollView autoresizingMask]; [[fFilterBar view] setAutoresizingMask: NSViewNotSizable]; [scrollView setAutoresizingMask: NSViewNotSizable]; - + const NSRect frame = [self windowFrameByAddingHeight: heightChange checkLimits: NO]; [fWindow setFrame: frame display: YES animate: animate]; - + //re-enable autoresize [[fFilterBar view] setAutoresizingMask: filterMask]; [scrollView setAutoresizingMask: scrollMask]; - + if (!show) { [[fFilterBar view] removeFromSuperviewWithoutNeedingDisplay]; [fFilterBar release]; fFilterBar = nil; } - + if ([fDefaults boolForKey: @"AutoSize"]) [self setWindowMinMaxToCurrent]; else @@ -3666,11 +3666,11 @@ static void removeKeRangerRansomware() { NSArray * selectedTorrents = [fTableView selectedTorrents]; NSMutableArray * qlArray = [NSMutableArray arrayWithCapacity: [selectedTorrents count]]; - + for (Torrent * torrent in selectedTorrents) if (([torrent isFolder] || [torrent isComplete]) && [torrent dataLocation]) [qlArray addObject: torrent]; - + return qlArray; } @@ -3697,7 +3697,7 @@ static void removeKeRangerRansomware() [super keyDown: event]; return YES; }*/ - + return NO; } @@ -3709,16 +3709,16 @@ static void removeKeRangerRansomware() { if (![fWindow isVisible]) return NSZeroRect; - + const NSInteger row = [fTableView rowForItem: item]; if (row == -1) return NSZeroRect; - + NSRect frame = [fTableView iconRectForRow: row]; - + if (!NSIntersectsRect([fTableView visibleRect], frame)) return NSZeroRect; - + frame.origin = [fTableView convertPoint: frame.origin toView: nil]; frame = [fWindow convertRectToScreen: frame]; frame.origin.y -= frame.size.height; @@ -3729,10 +3729,10 @@ static void removeKeRangerRansomware() - (void) showToolbarShare: (id) sender { NSParameterAssert([sender isKindOfClass:[NSButton class]]); - + NSSharingServicePicker * picker = [[NSSharingServicePicker alloc] initWithItems: [[ShareTorrentFileHelper sharedHelper] shareTorrentURLs]]; picker.delegate = self; - + [picker showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMinYEdge]; } @@ -3754,18 +3754,18 @@ static void removeKeRangerRansomware() - (id) toolbarButtonWithIdentifier: (NSString *) ident forToolbarButtonClass:(Class)class { ButtonToolbarItem * item = [[class alloc] initWithItemIdentifier: ident]; - + NSButton * button = [[NSButton alloc] init]; [button setBezelStyle: NSTexturedRoundedBezelStyle]; [button setStringValue: @""]; - + [item setView: button]; [button release]; - + const NSSize buttonSize = NSMakeSize(36.0, 25.0); [item setMinSize: buttonSize]; [item setMaxSize: buttonSize]; - + return [item autorelease]; } @@ -3774,7 +3774,7 @@ static void removeKeRangerRansomware() if ([ident isEqualToString: TOOLBAR_CREATE]) { ButtonToolbarItem * item = [self standardToolbarButtonWithIdentifier: ident]; - + [item setLabel: NSLocalizedString(@"Create", "Create toolbar item -> label")]; [item setPaletteLabel: NSLocalizedString(@"Create Torrent File", "Create toolbar item -> palette label")]; [item setToolTip: NSLocalizedString(@"Create torrent file", "Create toolbar item -> tooltip")]; @@ -3782,13 +3782,13 @@ static void removeKeRangerRansomware() [item setTarget: self]; [item setAction: @selector(createFile:)]; [item setAutovalidates: NO]; - + return item; } else if ([ident isEqualToString: TOOLBAR_OPEN_FILE]) { ButtonToolbarItem * item = [self standardToolbarButtonWithIdentifier: ident]; - + [item setLabel: NSLocalizedString(@"Open", "Open toolbar item -> label")]; [item setPaletteLabel: NSLocalizedString(@"Open Torrent Files", "Open toolbar item -> palette label")]; [item setToolTip: NSLocalizedString(@"Open torrent files", "Open toolbar item -> tooltip")]; @@ -3796,13 +3796,13 @@ static void removeKeRangerRansomware() [item setTarget: self]; [item setAction: @selector(openShowSheet:)]; [item setAutovalidates: NO]; - + return item; } else if ([ident isEqualToString: TOOLBAR_OPEN_WEB]) { ButtonToolbarItem * item = [self standardToolbarButtonWithIdentifier: ident]; - + [item setLabel: NSLocalizedString(@"Open Address", "Open address toolbar item -> label")]; [item setPaletteLabel: NSLocalizedString(@"Open Torrent Address", "Open address toolbar item -> palette label")]; [item setToolTip: NSLocalizedString(@"Open torrent web address", "Open address toolbar item -> tooltip")]; @@ -3810,13 +3810,13 @@ static void removeKeRangerRansomware() [item setTarget: self]; [item setAction: @selector(openURLShowSheet:)]; [item setAutovalidates: NO]; - + return item; } else if ([ident isEqualToString: TOOLBAR_REMOVE]) { ButtonToolbarItem * item = [self standardToolbarButtonWithIdentifier: ident]; - + [item setLabel: NSLocalizedString(@"Remove", "Remove toolbar item -> label")]; [item setPaletteLabel: NSLocalizedString(@"Remove Selected", "Remove toolbar item -> palette label")]; [item setToolTip: NSLocalizedString(@"Remove selected transfers", "Remove toolbar item -> tooltip")]; @@ -3824,134 +3824,134 @@ static void removeKeRangerRansomware() [item setTarget: self]; [item setAction: @selector(removeNoDelete:)]; [item setVisibilityPriority: NSToolbarItemVisibilityPriorityHigh]; - + return item; } else if ([ident isEqualToString: TOOLBAR_INFO]) { ButtonToolbarItem * item = [self standardToolbarButtonWithIdentifier: ident]; [[(NSButton *)[item view] cell] setShowsStateBy: NSContentsCellMask]; //blue when enabled - + [item setLabel: NSLocalizedString(@"Inspector", "Inspector toolbar item -> label")]; [item setPaletteLabel: NSLocalizedString(@"Toggle Inspector", "Inspector toolbar item -> palette label")]; [item setToolTip: NSLocalizedString(@"Toggle the torrent inspector", "Inspector toolbar item -> tooltip")]; [item setImage: [NSImage imageNamed: @"ToolbarInfoTemplate"]]; [item setTarget: self]; [item setAction: @selector(showInfo:)]; - + return item; } else if ([ident isEqualToString: TOOLBAR_PAUSE_RESUME_ALL]) { GroupToolbarItem * groupItem = [[GroupToolbarItem alloc] initWithItemIdentifier: ident]; - + NSSegmentedControl * segmentedControl = [[NSSegmentedControl alloc] initWithFrame: NSZeroRect]; [segmentedControl setCell: [[[ToolbarSegmentedCell alloc] init] autorelease]]; [groupItem setView: segmentedControl]; NSSegmentedCell * segmentedCell = (NSSegmentedCell *)[segmentedControl cell]; - + if ([NSApp isOnYosemiteOrBetter]) { segmentedControl.segmentStyle = NSSegmentStyleSeparated; } - + [segmentedControl setSegmentCount: 2]; [segmentedCell setTrackingMode: NSSegmentSwitchTrackingMomentary]; - + const NSSize groupSize = NSMakeSize(72.0, 25.0); [groupItem setMinSize: groupSize]; [groupItem setMaxSize: groupSize]; - + [groupItem setLabel: NSLocalizedString(@"Apply All", "All toolbar item -> label")]; [groupItem setPaletteLabel: NSLocalizedString(@"Pause / Resume All", "All toolbar item -> palette label")]; [groupItem setTarget: self]; [groupItem setAction: @selector(allToolbarClicked:)]; - + [groupItem setIdentifiers: [NSArray arrayWithObjects: TOOLBAR_PAUSE_ALL, TOOLBAR_RESUME_ALL, nil]]; - + [segmentedCell setTag: TOOLBAR_PAUSE_TAG forSegment: TOOLBAR_PAUSE_TAG]; [segmentedControl setImage: [NSImage imageNamed: @"ToolbarPauseAllTemplate"] forSegment: TOOLBAR_PAUSE_TAG]; [segmentedCell setToolTip: NSLocalizedString(@"Pause all transfers", "All toolbar item -> tooltip") forSegment: TOOLBAR_PAUSE_TAG]; - + [segmentedCell setTag: TOOLBAR_RESUME_TAG forSegment: TOOLBAR_RESUME_TAG]; [segmentedControl setImage: [NSImage imageNamed: @"ToolbarResumeAllTemplate"] forSegment: TOOLBAR_RESUME_TAG]; [segmentedCell setToolTip: NSLocalizedString(@"Resume all transfers", "All toolbar item -> tooltip") forSegment: TOOLBAR_RESUME_TAG]; - + [groupItem createMenu: [NSArray arrayWithObjects: NSLocalizedString(@"Pause All", "All toolbar item -> label"), NSLocalizedString(@"Resume All", "All toolbar item -> label"), nil]]; - + [segmentedControl release]; - + [groupItem setVisibilityPriority: NSToolbarItemVisibilityPriorityHigh]; - + return [groupItem autorelease]; } else if ([ident isEqualToString: TOOLBAR_PAUSE_RESUME_SELECTED]) { GroupToolbarItem * groupItem = [[GroupToolbarItem alloc] initWithItemIdentifier: ident]; - + NSSegmentedControl * segmentedControl = [[NSSegmentedControl alloc] initWithFrame: NSZeroRect]; [segmentedControl setCell: [[[ToolbarSegmentedCell alloc] init] autorelease]]; [groupItem setView: segmentedControl]; NSSegmentedCell * segmentedCell = (NSSegmentedCell *)[segmentedControl cell]; - + if ([NSApp isOnYosemiteOrBetter]) { segmentedControl.segmentStyle = NSSegmentStyleSeparated; } - + [segmentedControl setSegmentCount: 2]; [segmentedCell setTrackingMode: NSSegmentSwitchTrackingMomentary]; - + const NSSize groupSize = NSMakeSize(72.0, 25.0); [groupItem setMinSize: groupSize]; [groupItem setMaxSize: groupSize]; - + [groupItem setLabel: NSLocalizedString(@"Apply Selected", "Selected toolbar item -> label")]; [groupItem setPaletteLabel: NSLocalizedString(@"Pause / Resume Selected", "Selected toolbar item -> palette label")]; [groupItem setTarget: self]; [groupItem setAction: @selector(selectedToolbarClicked:)]; - + [groupItem setIdentifiers: [NSArray arrayWithObjects: TOOLBAR_PAUSE_SELECTED, TOOLBAR_RESUME_SELECTED, nil]]; - + [segmentedCell setTag: TOOLBAR_PAUSE_TAG forSegment: TOOLBAR_PAUSE_TAG]; [segmentedControl setImage: [NSImage imageNamed: @"ToolbarPauseSelectedTemplate"] forSegment: TOOLBAR_PAUSE_TAG]; [segmentedCell setToolTip: NSLocalizedString(@"Pause selected transfers", "Selected toolbar item -> tooltip") forSegment: TOOLBAR_PAUSE_TAG]; - + [segmentedCell setTag: TOOLBAR_RESUME_TAG forSegment: TOOLBAR_RESUME_TAG]; [segmentedControl setImage: [NSImage imageNamed: @"ToolbarResumeSelectedTemplate"] forSegment: TOOLBAR_RESUME_TAG]; [segmentedCell setToolTip: NSLocalizedString(@"Resume selected transfers", "Selected toolbar item -> tooltip") forSegment: TOOLBAR_RESUME_TAG]; - + [groupItem createMenu: [NSArray arrayWithObjects: NSLocalizedString(@"Pause Selected", "Selected toolbar item -> label"), NSLocalizedString(@"Resume Selected", "Selected toolbar item -> label"), nil]]; - + [segmentedControl release]; - + [groupItem setVisibilityPriority: NSToolbarItemVisibilityPriorityHigh]; - + return [groupItem autorelease]; } else if ([ident isEqualToString: TOOLBAR_FILTER]) { ButtonToolbarItem * item = [self standardToolbarButtonWithIdentifier: ident]; [[(NSButton *)[item view] cell] setShowsStateBy: NSContentsCellMask]; //blue when enabled - + [item setLabel: NSLocalizedString(@"Filter", "Filter toolbar item -> label")]; [item setPaletteLabel: NSLocalizedString(@"Toggle Filter", "Filter toolbar item -> palette label")]; [item setToolTip: NSLocalizedString(@"Toggle the filter bar", "Filter toolbar item -> tooltip")]; [item setImage: [NSImage imageNamed: @"ToolbarFilterTemplate"]]; [item setTarget: self]; [item setAction: @selector(toggleFilterBar:)]; - + return item; } else if ([ident isEqualToString: TOOLBAR_QUICKLOOK]) { ButtonToolbarItem * item = [self standardToolbarButtonWithIdentifier: ident]; [[(NSButton *)[item view] cell] setShowsStateBy: NSContentsCellMask]; //blue when enabled - + [item setLabel: NSLocalizedString(@"Quick Look", "QuickLook toolbar item -> label")]; [item setPaletteLabel: NSLocalizedString(@"Quick Look", "QuickLook toolbar item -> palette label")]; [item setToolTip: NSLocalizedString(@"Quick Look", "QuickLook toolbar item -> tooltip")]; @@ -3959,24 +3959,24 @@ static void removeKeRangerRansomware() [item setTarget: self]; [item setAction: @selector(toggleQuickLook:)]; [item setVisibilityPriority: NSToolbarItemVisibilityPriorityLow]; - + return item; } else if ([ident isEqualToString: TOOLBAR_SHARE]) { ShareToolbarItem * item = [self toolbarButtonWithIdentifier: ident forToolbarButtonClass: [ShareToolbarItem class]]; - + [item setLabel: NSLocalizedString(@"Share", "Share toolbar item -> label")]; [item setPaletteLabel: NSLocalizedString(@"Share", "Share toolbar item -> palette label")]; [item setToolTip: NSLocalizedString(@"Share torrent file", "Share toolbar item -> tooltip")]; [item setImage: [NSImage imageNamed: NSImageNameShareTemplate]]; [item setVisibilityPriority: NSToolbarItemVisibilityPriorityLow]; - + NSButton *itemButton = (NSButton *)[item view]; [itemButton setTarget: self]; [itemButton setAction: @selector(showToolbarShare:)]; [itemButton sendActionOn:NSLeftMouseDownMask]; - + return item; } else @@ -4044,7 +4044,7 @@ static void removeKeRangerRansomware() - (BOOL) validateToolbarItem: (NSToolbarItem *) toolbarItem { NSString * ident = [toolbarItem itemIdentifier]; - + //enable remove item if ([ident isEqualToString: TOOLBAR_REMOVE]) return [fTableView numberOfSelectedRows] > 0; @@ -4075,7 +4075,7 @@ static void removeKeRangerRansomware() return YES; return NO; } - + //enable resume item if ([ident isEqualToString: TOOLBAR_RESUME_SELECTED]) { @@ -4084,52 +4084,52 @@ static void removeKeRangerRansomware() return YES; return NO; } - + //set info item if ([ident isEqualToString: TOOLBAR_INFO]) { [(NSButton *)[toolbarItem view] setState: [[fInfoController window] isVisible]]; return YES; } - + //set filter item if ([ident isEqualToString: TOOLBAR_FILTER]) { [(NSButton *)[toolbarItem view] setState: fFilterBar != nil]; return YES; } - + //set quick look item if ([ident isEqualToString: TOOLBAR_QUICKLOOK]) { [(NSButton *)[toolbarItem view] setState: [QLPreviewPanel sharedPreviewPanelExists] && [[QLPreviewPanel sharedPreviewPanel] isVisible]]; return YES; } - + //enable share item if ([ident isEqualToString: TOOLBAR_SHARE]) return [fTableView numberOfSelectedRows] > 0; - + return YES; } - (BOOL) validateMenuItem: (NSMenuItem *) menuItem { SEL action = [menuItem action]; - + if (action == @selector(toggleSpeedLimit:)) { [menuItem setState: [fDefaults boolForKey: @"SpeedLimit"] ? NSOnState : NSOffState]; return YES; } - + //only enable some items if it is in a context menu or the window is useable BOOL canUseTable = [fWindow isKeyWindow] || [[menuItem menu] supermenu] != [NSApp mainMenu]; //enable open items if (action == @selector(openShowSheet:) || action == @selector(openURLShowSheet:)) return [fWindow attachedSheet] == nil; - + //enable sort options if (action == @selector(setSort:)) { @@ -4163,15 +4163,15 @@ static void removeKeRangerRansomware() default: NSAssert1(NO, @"Unknown sort tag received: %ld", [menuItem tag]); } - + [menuItem setState: [sortType isEqualToString: [fDefaults stringForKey: @"Sort"]] ? NSOnState : NSOffState]; return [fWindow isVisible]; } - + if (action == @selector(setGroup:)) { BOOL checked = NO; - + NSInteger index = [menuItem tag]; for (Torrent * torrent in [fTableView selectedTorrents]) if (index == [torrent groupValue]) @@ -4179,29 +4179,29 @@ static void removeKeRangerRansomware() checked = YES; break; } - + [menuItem setState: checked ? NSOnState : NSOffState]; return canUseTable && [fTableView numberOfSelectedRows] > 0; } - + if (action == @selector(toggleSmallView:)) { [menuItem setState: [fDefaults boolForKey: @"SmallView"] ? NSOnState : NSOffState]; return [fWindow isVisible]; } - + if (action == @selector(togglePiecesBar:)) { [menuItem setState: [fDefaults boolForKey: @"PiecesBar"] ? NSOnState : NSOffState]; return [fWindow isVisible]; } - + if (action == @selector(toggleAvailabilityBar:)) { [menuItem setState: [fDefaults boolForKey: @"DisplayProgressBarAvailable"] ? NSOnState : NSOffState]; return [fWindow isVisible]; } - + //enable show info if (action == @selector(showInfo:)) { @@ -4211,11 +4211,11 @@ static void removeKeRangerRansomware() return YES; } - + //enable prev/next inspector tab if (action == @selector(setInfoTab:)) return [[fInfoController window] isVisible]; - + //enable toggle status bar if (action == @selector(toggleStatusBar:)) { @@ -4225,7 +4225,7 @@ static void removeKeRangerRansomware() return [fWindow isVisible]; } - + //enable toggle filter bar if (action == @selector(toggleFilterBar:)) { @@ -4235,15 +4235,15 @@ static void removeKeRangerRansomware() return [fWindow isVisible]; } - + //enable prev/next filter button if (action == @selector(switchFilter:)) return [fWindow isVisible] && fFilterBar; - + //enable reveal in finder if (action == @selector(revealFile:)) return canUseTable && [fTableView numberOfSelectedRows] > 0; - + //enable renaming file/folder if (action == @selector(renameSelected:)) return canUseTable && [fTableView numberOfSelectedRows] == 1; @@ -4252,7 +4252,7 @@ static void removeKeRangerRansomware() if (action == @selector(removeNoDelete:) || action == @selector(removeDeleteData:)) { BOOL warning = NO; - + for (Torrent * torrent in [fTableView selectedTorrents]) { if ([torrent isActive]) @@ -4264,7 +4264,7 @@ static void removeKeRangerRansomware() } } } - + //append or remove ellipsis when needed NSString * title = [menuItem title], * ellipsis = [NSString ellipsis]; if (warning && [fDefaults boolForKey: @"CheckRemove"]) @@ -4277,10 +4277,10 @@ static void removeKeRangerRansomware() if ([title hasSuffix: ellipsis]) [menuItem setTitle: [title substringToIndex: [title rangeOfString: ellipsis].location]]; } - + return canUseTable && [fTableView numberOfSelectedRows] > 0; } - + //remove all completed transfers item if (action == @selector(clearCompleted:)) { @@ -4296,7 +4296,7 @@ static void removeKeRangerRansomware() if ([title hasSuffix: ellipsis]) [menuItem setTitle: [title substringToIndex: [title rangeOfString: ellipsis].location]]; } - + for (Torrent * torrent in fTorrents) if ([torrent isFinishedSeeding]) return YES; @@ -4311,7 +4311,7 @@ static void removeKeRangerRansomware() return YES; return NO; } - + //enable resume all item if (action == @selector(resumeAllTorrents:)) { @@ -4320,25 +4320,25 @@ static void removeKeRangerRansomware() return YES; return NO; } - + //enable resume all waiting item if (action == @selector(resumeWaitingTorrents:)) { if (![fDefaults boolForKey: @"Queue"] && ![fDefaults boolForKey: @"QueueSeed"]) return NO; - + for (Torrent * torrent in fTorrents) if ([torrent waitingToStart]) return YES; return NO; } - + //enable resume selected waiting item if (action == @selector(resumeSelectedTorrentsNoWait:)) { if (!canUseTable) return NO; - + for (Torrent * torrent in [fTableView selectedTorrents]) if (![torrent isActive]) return YES; @@ -4350,69 +4350,69 @@ static void removeKeRangerRansomware() { if (!canUseTable) return NO; - + for (Torrent * torrent in [fTableView selectedTorrents]) if ([torrent isActive] || [torrent waitingToStart]) return YES; return NO; } - + //enable resume item if (action == @selector(resumeSelectedTorrents:)) { if (!canUseTable) return NO; - + for (Torrent * torrent in [fTableView selectedTorrents]) if (![torrent isActive] && ![torrent waitingToStart]) return YES; return NO; } - + //enable manual announce item if (action == @selector(announceSelectedTorrents:)) { if (!canUseTable) return NO; - + for (Torrent * torrent in [fTableView selectedTorrents]) if ([torrent canManualAnnounce]) return YES; return NO; } - + //enable reset cache item if (action == @selector(verifySelectedTorrents:)) { if (!canUseTable) return NO; - + for (Torrent * torrent in [fTableView selectedTorrents]) if (![torrent isMagnet]) return YES; return NO; } - + //enable move torrent file item if (action == @selector(moveDataFilesSelected:)) return canUseTable && [fTableView numberOfSelectedRows] > 0; - + //enable copy torrent file item if (action == @selector(copyTorrentFiles:)) { if (!canUseTable) return NO; - + for (Torrent * torrent in [fTableView selectedTorrents]) if (![torrent isMagnet]) return YES; return NO; } - + //enable copy torrent file item if (action == @selector(copyMagnetLinks:)) return canUseTable && [fTableView numberOfSelectedRows] > 0; - + //enable reverse sort item if (action == @selector(setSortReverse:)) { @@ -4420,14 +4420,14 @@ static void removeKeRangerRansomware() [menuItem setState: (isReverse == [fDefaults boolForKey: @"SortReverse"]) ? NSOnState : NSOffState]; return ![[fDefaults stringForKey: @"Sort"] isEqualToString: SORT_ORDER]; } - + //enable group sort item if (action == @selector(setSortByGroup:)) { [menuItem setState: [fDefaults boolForKey: @"SortByGroup"] ? NSOnState : NSOffState]; return YES; } - + if (action == @selector(toggleQuickLook:)) { const BOOL visible =[QLPreviewPanel sharedPreviewPanelExists] && [[QLPreviewPanel sharedPreviewPanel] isVisible]; @@ -4435,10 +4435,10 @@ static void removeKeRangerRansomware() NSString * title = !visible ? NSLocalizedString(@"Quick Look", "View menu -> Quick Look") : NSLocalizedString(@"Close Quick Look", "View menu -> Quick Look"); [menuItem setTitle: title]; - + return YES; } - + return YES; } @@ -4456,13 +4456,13 @@ static void removeKeRangerRansomware() anyActive = YES; [torrent sleep]; //have to call on all, regardless if they are active } - + //if there are any running transfers, wait 15 seconds for them to stop if (anyActive) { sleep(15); } - + IOAllowPowerChange(fRootPort, (long) messageArgument); break; } @@ -4478,7 +4478,7 @@ static void removeKeRangerRansomware() return; } } - + IOAllowPowerChange(fRootPort, (long) messageArgument); break; @@ -4494,7 +4494,7 @@ static void removeKeRangerRansomware() { if (fQuitting) return nil; - + NSUInteger seeding = 0, downloading = 0; for (Torrent * torrent in fTorrents) { @@ -4504,29 +4504,29 @@ static void removeKeRangerRansomware() downloading++; else; } - + NSMenu * menu = [[NSMenu alloc] init]; - + if (seeding > 0) { NSString * title = [NSString stringWithFormat: NSLocalizedString(@"%d Seeding", "Dock item - Seeding"), seeding]; [menu addItemWithTitle: title action: nil keyEquivalent: @""]; } - + if (downloading > 0) { NSString * title = [NSString stringWithFormat: NSLocalizedString(@"%d Downloading", "Dock item - Downloading"), downloading]; [menu addItemWithTitle: title action: nil keyEquivalent: @""]; } - + if (seeding > 0 || downloading > 0) [menu addItem: [NSMenuItem separatorItem]]; - + [menu addItemWithTitle: NSLocalizedString(@"Pause All", "Dock item") action: @selector(stopAllTorrents:) keyEquivalent: @""]; [menu addItemWithTitle: NSLocalizedString(@"Resume All", "Dock item") action: @selector(resumeAllTorrents:) keyEquivalent: @""]; [menu addItem: [NSMenuItem separatorItem]]; [menu addItemWithTitle: NSLocalizedString(@"Speed Limit", "Dock item") action: @selector(toggleSpeedLimit:) keyEquivalent: @""]; - + return [menu autorelease]; } @@ -4534,7 +4534,7 @@ static void removeKeRangerRansomware() { //if auto size is enabled, the current frame shouldn't need to change NSRect frame = [fDefaults boolForKey: @"AutoSize"] ? [window frame] : [self sizedWindowFrame]; - + frame.size.width = [fDefaults boolForKey: @"SmallView"] ? [fWindow minSize].width : WINDOW_REGULAR_WIDTH; return frame; } @@ -4544,11 +4544,11 @@ static void removeKeRangerRansomware() if ([fDefaults boolForKey: @"AutoSize"]) { NSScrollView * scrollView = [fTableView enclosingScrollView]; - + [scrollView setHasVerticalScroller: NO]; [fWindow setFrame: [self sizedWindowFrame] display: YES animate: YES]; [scrollView setHasVerticalScroller: YES]; - + [self setWindowMinMaxToCurrent]; } } @@ -4557,11 +4557,11 @@ static void removeKeRangerRansomware() { NSUInteger groups = ([fDisplayedTorrents count] > 0 && ![[fDisplayedTorrents objectAtIndex: 0] isKindOfClass: [Torrent class]]) ? [fDisplayedTorrents count] : 0; - + CGFloat heightChange = (GROUP_SEPARATOR_HEIGHT + [fTableView intercellSpacing].height) * groups + ([fTableView rowHeight] + [fTableView intercellSpacing].height) * ([fTableView numberOfRows] - groups) - NSHeight([[fTableView enclosingScrollView] frame]); - + return [self windowFrameByAddingHeight: heightChange checkLimits: YES]; } @@ -4573,9 +4573,9 @@ static void removeKeRangerRansomware() { NSSize contentMinSize = [fWindow contentMinSize]; contentMinSize.height = [self minWindowContentSizeAllowed]; - + [fWindow setContentMinSize: contentMinSize]; - + NSSize contentMaxSize = [fWindow contentMaxSize]; contentMaxSize.height = FLT_MAX; [fWindow setContentMaxSize: contentMaxSize]; @@ -4585,12 +4585,12 @@ static void removeKeRangerRansomware() - (void) setWindowMinMaxToCurrent { const CGFloat height = NSHeight([[fWindow contentView] frame]); - + NSSize minSize = [fWindow contentMinSize], maxSize = [fWindow contentMaxSize]; minSize.height = height; maxSize.height = height; - + [fWindow setContentMinSize: minSize]; [fWindow setContentMaxSize: maxSize]; } @@ -4660,7 +4660,7 @@ static void removeKeRangerRansomware() - (NSDictionary *) registrationDictionaryForGrowl { NSArray * notifications = @[GROWL_DOWNLOAD_COMPLETE, GROWL_SEEDING_COMPLETE, GROWL_AUTO_ADD, GROWL_AUTO_SPEED_LIMIT]; - + return @{GROWL_NOTIFICATIONS_ALL : notifications, GROWL_NOTIFICATIONS_DEFAULT : notifications }; } @@ -4669,7 +4669,7 @@ static void removeKeRangerRansomware() { if (![clickContext isKindOfClass: [NSDictionary class]]) return; - + NSString * type = [clickContext objectForKey: @"Type"], * location; if (([type isEqualToString: GROWL_DOWNLOAD_COMPLETE] || [type isEqualToString: GROWL_SEEDING_COMPLETE]) && (location = [clickContext objectForKey: @"Location"])) @@ -4694,55 +4694,55 @@ static void removeKeRangerRansomware() *stop = YES; } }]; - + if (!torrent) { NSLog(@"No torrent found matching the given torrent struct from the RPC callback!"); return; } } - + dispatch_async(dispatch_get_main_queue(), ^{ switch (type) { case TR_RPC_TORRENT_ADDED: [self rpcAddTorrentStruct: torrentStruct]; break; - + case TR_RPC_TORRENT_STARTED: case TR_RPC_TORRENT_STOPPED: [self rpcStartedStoppedTorrent: torrent]; break; - + case TR_RPC_TORRENT_REMOVING: [self rpcRemoveTorrent: torrent deleteData: NO]; break; - + case TR_RPC_TORRENT_TRASHING: [self rpcRemoveTorrent: torrent deleteData: YES]; break; - + case TR_RPC_TORRENT_CHANGED: [self rpcChangedTorrent: torrent]; break; - + case TR_RPC_TORRENT_MOVED: [self rpcMovedTorrent: torrent]; break; - + case TR_RPC_SESSION_QUEUE_POSITIONS_CHANGED: [self rpcUpdateQueue]; break; - + case TR_RPC_SESSION_CHANGED: [fPrefsController rpcUpdatePrefs]; break; - + case TR_RPC_SESSION_CLOSE: fQuitRequested = YES; [NSApp terminate: self]; break; - + default: NSAssert1(NO, @"Unknown RPC command received: %d", type); } @@ -4755,24 +4755,24 @@ static void removeKeRangerRansomware() NSString * location = nil; if (tr_torrentGetDownloadDir(torrentStruct) != NULL) location = [NSString stringWithUTF8String: tr_torrentGetDownloadDir(torrentStruct)]; - + Torrent * torrent = [[Torrent alloc] initWithTorrentStruct: torrentStruct location: location lib: fLib]; - + //change the location if the group calls for it (this has to wait until after the torrent is created) if ([[GroupsController groups] usesCustomDownloadLocationForIndex: [torrent groupValue]]) { location = [[GroupsController groups] customDownloadLocationForIndex: [torrent groupValue]]; [torrent changeDownloadFolderBeforeUsing: location determinationType: TorrentDeterminationAutomatic]; } - + [torrent update]; [fTorrents addObject: torrent]; [torrent release]; - + if (!fAddingTransfers) fAddingTransfers = [[NSMutableSet alloc] init]; [fAddingTransfers addObject: torrent]; - + [self fullUpdateUI]; } @@ -4784,7 +4784,7 @@ static void removeKeRangerRansomware() - (void) rpcStartedStoppedTorrent: (Torrent *) torrent { [torrent update]; - + [self updateUI]; [self applyFilter]; [self updateTorrentHistory]; @@ -4793,7 +4793,7 @@ static void removeKeRangerRansomware() - (void) rpcChangedTorrent: (Torrent *) torrent { [torrent update]; - + if ([[fTableView selectedTorrents] containsObject: torrent]) { [fInfoController updateInfoStats]; //this will reload the file table @@ -4805,7 +4805,7 @@ static void removeKeRangerRansomware() { [torrent update]; [torrent updateTimeMachineExclude]; - + if ([[fTableView selectedTorrents] containsObject: torrent]) [fInfoController updateInfoStats]; } @@ -4814,11 +4814,11 @@ static void removeKeRangerRansomware() { for (Torrent * torrent in fTorrents) [torrent update]; - + NSSortDescriptor * descriptor = [NSSortDescriptor sortDescriptorWithKey: @"queuePosition" ascending: YES]; NSArray * descriptors = [NSArray arrayWithObject: descriptor]; [fTorrents sortUsingDescriptors: descriptors]; - + [self sortTorrents: YES]; } diff --git a/qt/MainWindow.cc b/qt/MainWindow.cc index aa65e2d4e..ec11841ff 100644 --- a/qt/MainWindow.cc +++ b/qt/MainWindow.cc @@ -623,7 +623,7 @@ MainWindow::openStats () void MainWindow::openDonate () { - QDesktopServices::openUrl (QUrl (QLatin1String ("http://www.transmissionbt.com/donate.php"))); + QDesktopServices::openUrl (QUrl (QLatin1String ("https://transmissionbt.com/donate/"))); } void diff --git a/web/javascript/transmission.js b/web/javascript/transmission.js index 82fa1176c..2fdc9a1e5 100644 --- a/web/javascript/transmission.js +++ b/web/javascript/transmission.js @@ -703,7 +703,7 @@ Transmission.prototype = break; case 'tipjar': - window.open('http://www.transmissionbt.com/donate.php'); + window.open('https://transmissionbt.com/donate/'); break; case 'unlimited_download_rate': From 440f03e9ad5ed3efc9e7d943befe33e5cbb65a20 Mon Sep 17 00:00:00 2001 From: Tavis Ormandy Date: Thu, 11 Jan 2018 10:00:41 -0800 Subject: [PATCH 03/22] mitigate dns rebinding attacks against daemon --- libtransmission/quark.c | 2 + libtransmission/quark.h | 2 + libtransmission/rpc-server.c | 109 ++++++++++++++++++++++++++++++--- libtransmission/rpc-server.h | 6 ++ libtransmission/session.c | 2 + libtransmission/transmission.h | 1 + libtransmission/web.c | 1 + 7 files changed, 113 insertions(+), 10 deletions(-) diff --git a/libtransmission/quark.c b/libtransmission/quark.c index 30cc2bca4..b4fd7aabd 100644 --- a/libtransmission/quark.c +++ b/libtransmission/quark.c @@ -289,6 +289,8 @@ static const struct tr_key_struct my_static[] = { "rpc-authentication-required", 27 }, { "rpc-bind-address", 16 }, { "rpc-enabled", 11 }, + { "rpc-host-whitelist", 18 }, + { "rpc-host-whitelist-enabled", 26 }, { "rpc-password", 12 }, { "rpc-port", 8 }, { "rpc-url", 7 }, diff --git a/libtransmission/quark.h b/libtransmission/quark.h index 7f5212733..17464be8f 100644 --- a/libtransmission/quark.h +++ b/libtransmission/quark.h @@ -291,6 +291,8 @@ enum TR_KEY_rpc_authentication_required, TR_KEY_rpc_bind_address, TR_KEY_rpc_enabled, + TR_KEY_rpc_host_whitelist, + TR_KEY_rpc_host_whitelist_enabled, TR_KEY_rpc_password, TR_KEY_rpc_port, TR_KEY_rpc_url, diff --git a/libtransmission/rpc-server.c b/libtransmission/rpc-server.c index a3485f3fa..c80a629ca 100644 --- a/libtransmission/rpc-server.c +++ b/libtransmission/rpc-server.c @@ -52,6 +52,7 @@ struct tr_rpc_server bool isEnabled; bool isPasswordEnabled; bool isWhitelistEnabled; + bool isHostWhitelistEnabled; tr_port port; char * url; struct in_addr bindAddress; @@ -63,6 +64,7 @@ struct tr_rpc_server char * password; char * whitelistStr; tr_list * whitelist; + tr_list * hostWhitelist; char * sessionId; time_t sessionIdExpiresAt; @@ -588,6 +590,47 @@ isAddressAllowed (const tr_rpc_server * server, const char * address) return false; } +static bool +isHostnameAllowed (const tr_rpc_server * server, struct evhttp_request * req) +{ + /* If password auth is enabled, any hostname is permitted. */ + if (server->isPasswordEnabled) + return true; + + /* If whitelist is disabled, no restrictions. */ + if (!server->isHostWhitelistEnabled) + return true; + + const char * const host = evhttp_find_header (req->input_headers, "Host"); + + /* No host header, invalid request. */ + if (host == NULL) + return false; + + /* Host header might include the port. */ + char * const hostname = tr_strndup (host, strcspn (host, ":")); + + /* localhost or ipaddress is always acceptable. */ + if (strcmp (hostname, "localhost") == 0 || strcmp (hostname, "localhost.") == 0 || tr_addressIsIP (hostname)) + { + tr_free (hostname); + return true; + } + + /* Otherwise, hostname must be whitelisted. */ + for (tr_list * l = server->hostWhitelist; l != NULL; l = l->next) + { + if (tr_wildmat (hostname, l->data)) + { + tr_free (hostname); + return true; + } + } + + tr_free(hostname); + return false; +} + static bool test_session_id (struct tr_rpc_server * server, struct evhttp_request * req) { @@ -663,6 +706,22 @@ handle_request (struct evhttp_request * req, void * arg) handle_upload (req, server); } #ifdef REQUIRE_SESSION_ID + else if (!isHostnameAllowed (server, req)) + { + char * const tmp = tr_strdup_printf ( + "

Transmission received your request, but the hostname was unrecognized.

" + "

To fix this, choose one of the following options:" + "

    " + "
  • Enable password authentication, then any hostname is allowed.
  • " + "
  • Add the hostname you want to use to the whitelist in settings.
  • " + "

" + "

If you're editing settings.json, see the 'rpc-host-whitelist' and 'rpc-host-whitelist-enabled' entries.

" + "

This requirement has been added to help prevent " + "DNS Rebinding " + "attacks.

"); + send_simple_response (req, 421, tmp); + tr_free (tmp); + } else if (!test_session_id (server, req)) { const char * sessionId = get_current_session_id (server); @@ -674,7 +733,7 @@ handle_request (struct evhttp_request * req, void * arg) "
  • When you get this 409 error message, resend your request with the updated header" "

    " "

    This requirement has been added to help prevent " - "CSRF " + "CSRF " "attacks.

    " "

    %s: %s

    ", TR_RPC_SESSION_ID_HEADER, sessionId); @@ -875,19 +934,14 @@ tr_rpcGetUrl (const tr_rpc_server * server) return server->url ? server->url : ""; } -void -tr_rpcSetWhitelist (tr_rpc_server * server, const char * whitelistStr) +static void +tr_rpcSetList (const char * whitelistStr, tr_list ** list) { void * tmp; const char * walk; - /* keep the string */ - tmp = server->whitelistStr; - server->whitelistStr = tr_strdup (whitelistStr); - tr_free (tmp); - /* clear out the old whitelist entries */ - while ((tmp = tr_list_pop_front (&server->whitelist))) + while ((tmp = tr_list_pop_front (list))) tr_free (tmp); /* build the new whitelist entries */ @@ -896,7 +950,7 @@ tr_rpcSetWhitelist (tr_rpc_server * server, const char * whitelistStr) const char * delimiters = " ,;"; const size_t len = strcspn (walk, delimiters); char * token = tr_strndup (walk, len); - tr_list_append (&server->whitelist, token); + tr_list_append (list, token); if (strcspn (token, "+-") < len) tr_logAddNamedInfo (MY_NAME, "Adding address to whitelist: %s (And it has a '+' or '-'! Are you using an old ACL by mistake?)", token); else @@ -909,6 +963,23 @@ tr_rpcSetWhitelist (tr_rpc_server * server, const char * whitelistStr) } } +void +tr_rpcSetHostWhitelist (tr_rpc_server* server, const char * whitelistStr) +{ + tr_rpcSetList (whitelistStr, &server->hostWhitelist); +} + +void +tr_rpcSetWhitelist (tr_rpc_server * server, const char * whitelistStr) +{ + /* keep the string */ + char* const tmp = server->whitelistStr; + server->whitelistStr = tr_strdup (whitelistStr); + tr_free (tmp); + + tr_rpcSetList (whitelistStr, &server->whitelist); +} + const char* tr_rpcGetWhitelist (const tr_rpc_server * server) { @@ -930,6 +1001,12 @@ tr_rpcGetWhitelistEnabled (const tr_rpc_server * server) return server->isWhitelistEnabled; } +void +tr_rpcSetHostWhitelistEnabled(tr_rpc_server * server, bool isEnabled) +{ + server->isHostWhitelistEnabled = isEnabled; +} + /**** ***** PASSWORD ****/ @@ -1063,6 +1140,18 @@ tr_rpcInit (tr_session * session, tr_variant * settings) else tr_rpcSetWhitelistEnabled (s, boolVal); + key = TR_KEY_rpc_host_whitelist_enabled; + if (!tr_variantDictFindBool (settings, key, &boolVal)) + missing_settings_key (key); + else + tr_rpcSetHostWhitelistEnabled (s, boolVal); + + key = TR_KEY_rpc_host_whitelist; + if (!tr_variantDictFindStr (settings, key, &str, NULL) && str) + missing_settings_key (key); + else + tr_rpcSetHostWhitelist (s, str); + key = TR_KEY_rpc_authentication_required; if (!tr_variantDictFindBool (settings, key, &boolVal)) missing_settings_key (key); diff --git a/libtransmission/rpc-server.h b/libtransmission/rpc-server.h index e0302c5ea..c91d160f4 100644 --- a/libtransmission/rpc-server.h +++ b/libtransmission/rpc-server.h @@ -49,6 +49,12 @@ void tr_rpcSetWhitelist (tr_rpc_server * server, const char* tr_rpcGetWhitelist (const tr_rpc_server * server); +void tr_rpcSetHostWhitelistEnabled (tr_rpc_server * server, + bool isEnabled); + +void tr_rpcSetHostWhitelist (tr_rpc_server * server, + const char * whitelist); + void tr_rpcSetPassword (tr_rpc_server * server, const char * password); diff --git a/libtransmission/session.c b/libtransmission/session.c index 844cadba8..bb60c23b5 100644 --- a/libtransmission/session.c +++ b/libtransmission/session.c @@ -359,6 +359,8 @@ tr_sessionGetDefaultSettings (tr_variant * d) tr_variantDictAddStr (d, TR_KEY_rpc_username, ""); tr_variantDictAddStr (d, TR_KEY_rpc_whitelist, TR_DEFAULT_RPC_WHITELIST); tr_variantDictAddBool (d, TR_KEY_rpc_whitelist_enabled, true); + tr_variantDictAddStr (d, TR_KEY_rpc_host_whitelist, TR_DEFAULT_RPC_HOST_WHITELIST); + tr_variantDictAddBool (d, TR_KEY_rpc_host_whitelist_enabled, true); tr_variantDictAddInt (d, TR_KEY_rpc_port, atoi (TR_DEFAULT_RPC_PORT_STR)); tr_variantDictAddStr (d, TR_KEY_rpc_url, TR_DEFAULT_RPC_URL_STR); tr_variantDictAddBool (d, TR_KEY_scrape_paused_torrents_enabled, true); diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h index 4f76adfd6..e213a8f4e 100644 --- a/libtransmission/transmission.h +++ b/libtransmission/transmission.h @@ -123,6 +123,7 @@ const char* tr_getDefaultDownloadDir (void); #define TR_DEFAULT_BIND_ADDRESS_IPV4 "0.0.0.0" #define TR_DEFAULT_BIND_ADDRESS_IPV6 "::" #define TR_DEFAULT_RPC_WHITELIST "127.0.0.1" +#define TR_DEFAULT_RPC_HOST_WHITELIST "" #define TR_DEFAULT_RPC_PORT_STR "9091" #define TR_DEFAULT_RPC_URL_STR "/transmission/" #define TR_DEFAULT_PEER_PORT_STR "51413" diff --git a/libtransmission/web.c b/libtransmission/web.c index ee495e9fc..c7f062730 100644 --- a/libtransmission/web.c +++ b/libtransmission/web.c @@ -594,6 +594,7 @@ tr_webGetResponseStr (long code) case 415: return "Unsupported Media Type"; case 416: return "Requested Range Not Satisfiable"; case 417: return "Expectation Failed"; + case 421: return "Misdirected Request"; case 500: return "Internal Server Error"; case 501: return "Not Implemented"; case 502: return "Bad Gateway"; From aa2730d47ca72a93d695c8199aac7de0790e83dd Mon Sep 17 00:00:00 2001 From: Mike Gelfand Date: Tue, 16 Jan 2018 00:57:25 +0300 Subject: [PATCH 04/22] Switch to HTTPS links, adjust wiki links where possible Fixes #39 --- AUTHORS | 2 +- README | 6 +++--- cli/transmission-cli.1 | 4 ++-- configure.ac | 2 +- daemon/daemon.c | 2 +- daemon/remote.c | 2 +- daemon/transmission-daemon.1 | 6 +++--- daemon/transmission-remote.1 | 2 +- gtk/main.c | 2 +- gtk/transmission-gtk.1 | 4 ++-- gtk/util.c | 2 +- libtransmission/fdlimit.c | 2 +- macosx/Controller.h | 2 +- macosx/Controller.m | 10 +++++----- macosx/Credits.rtf | 4 ++-- macosx/TransmissionHelp/html/FAQ.html | 4 ++-- macosx/TransmissionHelp/html/scripts.html | 2 +- macosx/TransmissionHelp/html/troubleshoot.html | 2 +- macosx/TransmissionHelp/index.html | 6 +++--- macosx/da.lproj/MainMenu.xib | 2 +- macosx/de.lproj/MainMenu.xib | 2 +- macosx/en.lproj/MainMenu.xib | 2 +- macosx/es.lproj/MainMenu.xib | 2 +- macosx/fr.lproj/MainMenu.xib | 2 +- macosx/it.lproj/MainMenu.xib | 2 +- macosx/nl.lproj/MainMenu.xib | 2 +- macosx/pt_PT.lproj/MainMenu.xib | 2 +- macosx/ru.lproj/MainMenu.xib | 2 +- macosx/tr.lproj/MainMenu.xib | 2 +- qt/AboutDialog.ui | 2 +- qt/MainWindow.cc | 2 +- qt/README.txt | 2 +- qt/transmission-qt.1 | 4 ++-- transmission-gtk.spec.in | 2 +- utils/transmission-create.1 | 2 +- utils/transmission-edit.1 | 2 +- utils/transmission-show.1 | 2 +- web/javascript/transmission.js | 2 +- 38 files changed, 53 insertions(+), 53 deletions(-) diff --git a/AUTHORS b/AUTHORS index 21a71b7df..45ff733ac 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,5 +1,5 @@ The Transmission Project -http://www.transmissionbt.com/ +https://transmissionbt.com/ Lead Developers Jordan Lee, Mnemosyne LLC (Daemon, Backend, GTK+ client) diff --git a/README b/README index 395136fb3..df035ef00 100644 --- a/README +++ b/README @@ -8,7 +8,7 @@ ABOUT * A headless daemon for servers and routers * A web UI for remote controlling any of the above - Visit http://www.transmissionbt.com/ for more information. + Visit https://transmissionbt.com/ for more information. BUILDING @@ -16,7 +16,7 @@ BUILDING for building in Xcode. For a more detailed description, and dependancies, visit: - http://trac.transmissionbt.com/wiki/ + https://github.com/transmission/transmission/wiki Building a Transmission release from the command line: @@ -28,7 +28,7 @@ BUILDING Building Transmission from the nightly builds: - Download a tarball from http://build.transmissionbt.com/job/trunk-linux-inc/ + Download a tarball from https://build.transmissionbt.com/job/trunk-linux-inc/ and follow the steps from the previous section. If you're new to building programs from source code, this is typically diff --git a/cli/transmission-cli.1 b/cli/transmission-cli.1 index 677ffc0e6..5acd2c6a9 100644 --- a/cli/transmission-cli.1 +++ b/cli/transmission-cli.1 @@ -65,7 +65,7 @@ Prefer unencrypted peer connections. Set a script to run when the torrent finishes .It Fl g, Fl -config-dir Ar directory Where to look for configuration files. This can be used to swap between using the cli, daemon, gtk, and qt clients. -See http://trac.transmissionbt.com/wiki/ConfigFiles for more information. +See https://github.com/transmission/transmission/wiki/Configuration-Files for more information. .It Fl h, Fl -help Prints a short usage summary. .It Fl m, Fl -portmap @@ -133,4 +133,4 @@ and .Xr transmission-remote 1 , .Xr transmission-show 1 .Pp -http://www.transmissionbt.com/ +https://transmissionbt.com/ diff --git a/configure.ac b/configure.ac index 8f9d0c875..339bdd911 100644 --- a/configure.ac +++ b/configure.ac @@ -6,7 +6,7 @@ dnl these should be the only two lines you need to change m4_define([user_agent_prefix],[2.92]) m4_define([peer_id_prefix],[-TR2920-]) -AC_INIT([transmission],[user_agent_prefix],[http://trac.transmissionbt.com/newticket]) +AC_INIT([transmission],[user_agent_prefix],[https://github.com/transmission/transmission]) AC_SUBST(USERAGENT_PREFIX,[user_agent_prefix]) AC_SUBST(PEERID_PREFIX,[peer_id_prefix]) diff --git a/daemon/daemon.c b/daemon/daemon.c index 40ac5a67f..d4b763354 100644 --- a/daemon/daemon.c +++ b/daemon/daemon.c @@ -80,7 +80,7 @@ static const char * getUsage (void) { return "Transmission " LONG_VERSION_STRING - " http://www.transmissionbt.com/\n" + " https://transmissionbt.com/\n" "A fast and easy BitTorrent client\n" "\n" MY_NAME " is a headless Transmission session\n" diff --git a/daemon/remote.c b/daemon/remote.c index 5e21afdcb..2b17d9293 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -198,7 +198,7 @@ getUsage (void) return MY_NAME" "LONG_VERSION_STRING"\n" "A fast and easy BitTorrent client\n" - "http://www.transmissionbt.com/\n" + "https://transmissionbt.com/\n" "\n" "Usage: " MY_NAME " [host] [options]\n" diff --git a/daemon/transmission-daemon.1 b/daemon/transmission-daemon.1 index ce44c087b..849290d25 100644 --- a/daemon/transmission-daemon.1 +++ b/daemon/transmission-daemon.1 @@ -59,7 +59,7 @@ Dump transmission-daemon's settings to stderr. Run in the foreground and print errors to stderr. .It Fl g Fl -config-dir Ar directory Where to look for configuration files. This can be used to swap between using the cli, daemon, gtk, and qt clients. -See http://trac.transmissionbt.com/wiki/ConfigFiles for more information. +See https://github.com/transmission/transmission/wiki/Configuration-Files for more information. .It Fl er Fl -encryption-required Encrypt all peer connections. .It Fl ep Fl -encryption-preferred @@ -148,7 +148,7 @@ The config-dir used when neither nor .Op Fl g is specified. -See http://trac.transmissionbt.com/wiki/ConfigFiles for more information. +See https://github.com/transmission/transmission/wiki/Configuration-Files for more information. .El .Sh AUTHORS .An -nosplit @@ -166,4 +166,4 @@ and .Xr transmission-remote 1 , .Xr transmission-show 1 .Pp -http://www.transmissionbt.com/ +https://transmissionbt.com/ diff --git a/daemon/transmission-remote.1 b/daemon/transmission-remote.1 index 8e05de164..b4c94f5cc 100644 --- a/daemon/transmission-remote.1 +++ b/daemon/transmission-remote.1 @@ -408,4 +408,4 @@ and .Xr transmission-remote 1 , .Xr transmission-show 1 .Pp -http://www.transmissionbt.com/ +https://transmissionbt.com/ diff --git a/gtk/main.c b/gtk/main.c index ad0152342..1aa835757 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -1363,7 +1363,7 @@ update_model_loop (gpointer gdata) static void show_about_dialog (GtkWindow * parent) { - const char * uri = "http://www.transmissionbt.com/"; + const char * uri = "https://transmissionbt.com/"; const char * authors[] = { "Jordan Lee (Backend; GTK+)", "Mitchell Livingston (Backend; OS X)", NULL }; diff --git a/gtk/transmission-gtk.1 b/gtk/transmission-gtk.1 index a03f5411f..feba9ba45 100644 --- a/gtk/transmission-gtk.1 +++ b/gtk/transmission-gtk.1 @@ -49,7 +49,7 @@ Start with all torrents paused Start minimized in notification area .It Fl g, Fl -config-dir Ar directory Where to look for configuration files. This can be used to swap between using the cli, daemon, gtk, and qt clients. -See http://trac.transmissionbt.com/wiki/ConfigFiles for more information. +See https://github.com/transmission/transmission/wiki/Configuration-Files for more information. .El .Pp Multiple .torrent files may be added at startup @@ -91,4 +91,4 @@ and .Xr transmission-remote 1 , .Xr transmission-show 1 .Pp -http://www.transmissionbt.com/ +https://transmissionbt.com/ diff --git a/gtk/util.c b/gtk/util.c index d1ed11e0b..e32d6330a 100644 --- a/gtk/util.c +++ b/gtk/util.c @@ -353,7 +353,7 @@ gtr_get_help_uri (void) if (!uri) { - const char * fmt = "http://www.transmissionbt.com/help/gtk/%d.%dx"; + const char * fmt = "https://transmissionbt.com/help/gtk/%d.%dx"; uri = g_strdup_printf (fmt, MAJOR_VERSION, MINOR_VERSION / 10); } diff --git a/libtransmission/fdlimit.c b/libtransmission/fdlimit.c index a271f207c..e181e5304 100644 --- a/libtransmission/fdlimit.c +++ b/libtransmission/fdlimit.c @@ -223,7 +223,7 @@ cached_file_open (struct tr_cached_file * o, /* If the file already exists and it's too large, truncate it. * This is a fringe case that happens if a torrent's been updated * and one of the updated torrent's files is smaller. - * http://trac.transmissionbt.com/ticket/2228 + * https://trac.transmissionbt.com/ticket/2228 * https://bugs.launchpad.net/ubuntu/+source/transmission/+bug/318249 */ if (resize_needed && !tr_sys_file_truncate (fd, file_size, &error)) diff --git a/macosx/Controller.h b/macosx/Controller.h index 18616944b..4a09d12e3 100644 --- a/macosx/Controller.h +++ b/macosx/Controller.h @@ -259,7 +259,7 @@ typedef enum - (void) linkHomepage: (id) sender; - (void) linkForums: (id) sender; -- (void) linkTrac: (id) sender; +- (void) linkGitHub: (id) sender; - (void) linkDonate: (id) sender; - (void) rpcCallback: (tr_rpc_callback_type) type forTorrentStruct: (struct tr_torrent *) torrentStruct; diff --git a/macosx/Controller.m b/macosx/Controller.m index 41fd94315..38ccfe728 100644 --- a/macosx/Controller.m +++ b/macosx/Controller.m @@ -133,9 +133,9 @@ typedef enum #define TRANSFER_PLIST @"Transfers.plist" -#define WEBSITE_URL @"http://www.transmissionbt.com/" -#define FORUM_URL @"http://forum.transmissionbt.com/" -#define TRAC_URL @"http://trac.transmissionbt.com/" +#define WEBSITE_URL @"https://transmissionbt.com/" +#define FORUM_URL @"https://forum.transmissionbt.com/" +#define GITHUB_URL @"https://github.com/transmission/transmission" #define DONATE_URL @"https://transmissionbt.com/donate/" #define DONATE_NAG_TIME (60 * 60 * 24 * 7) @@ -4642,9 +4642,9 @@ static void removeKeRangerRansomware() [[NSWorkspace sharedWorkspace] openURL: [NSURL URLWithString: FORUM_URL]]; } -- (void) linkTrac: (id) sender +- (void) linkGitHub: (id) sender { - [[NSWorkspace sharedWorkspace] openURL: [NSURL URLWithString: TRAC_URL]]; + [[NSWorkspace sharedWorkspace] openURL: [NSURL URLWithString: GITHUB_URL]]; } - (void) linkDonate: (id) sender diff --git a/macosx/Credits.rtf b/macosx/Credits.rtf index 7b2e15a28..520cead25 100644 --- a/macosx/Credits.rtf +++ b/macosx/Credits.rtf @@ -7,8 +7,8 @@ \f0\b\fs28 \cf0 The Transmission Project \fs24 \ \pard\tx440\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li100\slleading40\sb40\qc -{\field{\*\fldinst{HYPERLINK "http://www.transmissionbt.com/"}}{\fldrslt -\b0 \cf0 http://www.transmissionbt.com/}}\ +{\field{\*\fldinst{HYPERLINK "https://transmissionbt.com/"}}{\fldrslt +\b0 \cf0 https://transmissionbt.com/}}\ \pard\tx440\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\li100\slleading40\sb40 \cf0 \ Lead Developers diff --git a/macosx/TransmissionHelp/html/FAQ.html b/macosx/TransmissionHelp/html/FAQ.html index 01220e85a..d8a32b01b 100644 --- a/macosx/TransmissionHelp/html/FAQ.html +++ b/macosx/TransmissionHelp/html/FAQ.html @@ -37,7 +37,7 @@

    Transmission crashed, what should I do?

    -

    Post the crash log on the support forums so that the issue can be fixed as quickly as possible. Crash logs are held in ~/Library/Logs/CrashReporter/. +

    Post the crash log on the support forums so that the issue can be fixed as quickly as possible. Crash logs are held in ~/Library/Logs/CrashReporter/.

    If your torrents' progress are incorrect when you reopen Transmission (e.g. they are starting from 0%) then you should manually recheck them. Click here for instructions.

    @@ -84,7 +84,7 @@

    What are 'nightlies'?

    -

    Nightlies are releases on the bleeding edge of development. They normally contain new features and bugfixes, but are not officially supported (although you are more than welcome to discuss them on the Transmission forums). You can try one out here. +

    Nightlies are releases on the bleeding edge of development. They normally contain new features and bugfixes, but are not officially supported (although you are more than welcome to discuss them on the Transmission forums). You can try one out here.

    diff --git a/macosx/TransmissionHelp/html/scripts.html b/macosx/TransmissionHelp/html/scripts.html index 5ffd2cb7a..20aa5eee6 100644 --- a/macosx/TransmissionHelp/html/scripts.html +++ b/macosx/TransmissionHelp/html/scripts.html @@ -33,7 +33,7 @@ -

    More information is available on the Transmission wiki. +

    More information is available on the Transmission wiki. diff --git a/macosx/TransmissionHelp/html/troubleshoot.html b/macosx/TransmissionHelp/html/troubleshoot.html index 73260d7b3..4e024edf6 100644 --- a/macosx/TransmissionHelp/html/troubleshoot.html +++ b/macosx/TransmissionHelp/html/troubleshoot.html @@ -23,7 +23,7 @@

    If your port is still not open, even after you have enabled automatic port forwarding, here are some tips you can use which may get it working.

    -

    If you are still having problems, open the Message Log (in the Window menu) and post the debug output on the support forums. +

    If you are still having problems, open the Message Log (in the Window menu) and post the debug output on the support forums.

    1. Pause your torrents
    2. Clear the log and set it to 'Debug'
    3. diff --git a/macosx/TransmissionHelp/index.html b/macosx/TransmissionHelp/index.html index 472e2f5a7..3827b4fa7 100644 --- a/macosx/TransmissionHelp/index.html +++ b/macosx/TransmissionHelp/index.html @@ -15,7 +15,7 @@ Image of Transmission window

      Transmission icon Transmission Help

      -

      www.transmissionbt.com

      +

      transmissionbt.com

    @@ -33,8 +33,8 @@

    Look up topics easily

    More Information

    -

    Transmission Forums

    -

    Trac - Wiki and Development

    +

    Transmission Forums

    +

    GitHub - Wiki and Development

    diff --git a/macosx/da.lproj/MainMenu.xib b/macosx/da.lproj/MainMenu.xib index 0d30e31a4..d60927579 100644 --- a/macosx/da.lproj/MainMenu.xib +++ b/macosx/da.lproj/MainMenu.xib @@ -696,7 +696,7 @@ CA - + diff --git a/macosx/de.lproj/MainMenu.xib b/macosx/de.lproj/MainMenu.xib index 2e16779f9..6d5e404ef 100644 --- a/macosx/de.lproj/MainMenu.xib +++ b/macosx/de.lproj/MainMenu.xib @@ -697,7 +697,7 @@ CA - + diff --git a/macosx/en.lproj/MainMenu.xib b/macosx/en.lproj/MainMenu.xib index 6e34678f3..4084bab15 100644 --- a/macosx/en.lproj/MainMenu.xib +++ b/macosx/en.lproj/MainMenu.xib @@ -696,7 +696,7 @@ CA - + diff --git a/macosx/es.lproj/MainMenu.xib b/macosx/es.lproj/MainMenu.xib index 11936a3a8..8319e092e 100644 --- a/macosx/es.lproj/MainMenu.xib +++ b/macosx/es.lproj/MainMenu.xib @@ -696,7 +696,7 @@ CA - + diff --git a/macosx/fr.lproj/MainMenu.xib b/macosx/fr.lproj/MainMenu.xib index 83a09089a..ed45b42cc 100644 --- a/macosx/fr.lproj/MainMenu.xib +++ b/macosx/fr.lproj/MainMenu.xib @@ -695,7 +695,7 @@ CA - + diff --git a/macosx/it.lproj/MainMenu.xib b/macosx/it.lproj/MainMenu.xib index b2aae0856..0725daaff 100644 --- a/macosx/it.lproj/MainMenu.xib +++ b/macosx/it.lproj/MainMenu.xib @@ -696,7 +696,7 @@ CA - + diff --git a/macosx/nl.lproj/MainMenu.xib b/macosx/nl.lproj/MainMenu.xib index 246eba9c4..7eff9bbe1 100644 --- a/macosx/nl.lproj/MainMenu.xib +++ b/macosx/nl.lproj/MainMenu.xib @@ -695,7 +695,7 @@ CA - + diff --git a/macosx/pt_PT.lproj/MainMenu.xib b/macosx/pt_PT.lproj/MainMenu.xib index 30d737ea4..64027e5d7 100644 --- a/macosx/pt_PT.lproj/MainMenu.xib +++ b/macosx/pt_PT.lproj/MainMenu.xib @@ -696,7 +696,7 @@ CA - + diff --git a/macosx/ru.lproj/MainMenu.xib b/macosx/ru.lproj/MainMenu.xib index 0a1d3544c..0b7c232ef 100644 --- a/macosx/ru.lproj/MainMenu.xib +++ b/macosx/ru.lproj/MainMenu.xib @@ -696,7 +696,7 @@ CA - + diff --git a/macosx/tr.lproj/MainMenu.xib b/macosx/tr.lproj/MainMenu.xib index b22bdc344..c935f99fe 100644 --- a/macosx/tr.lproj/MainMenu.xib +++ b/macosx/tr.lproj/MainMenu.xib @@ -696,7 +696,7 @@ CA - + diff --git a/qt/AboutDialog.ui b/qt/AboutDialog.ui index dd118c758..38dbbc471 100644 --- a/qt/AboutDialog.ui +++ b/qt/AboutDialog.ui @@ -57,7 +57,7 @@ - <a href="http://www.transmissionbt.com/">http://www.transmissionbt.com/</a> + <a href="https://transmissionbt.com/">https://transmissionbt.com/</a> Qt::AlignCenter diff --git a/qt/MainWindow.cc b/qt/MainWindow.cc index ec11841ff..8383d0331 100644 --- a/qt/MainWindow.cc +++ b/qt/MainWindow.cc @@ -635,7 +635,7 @@ MainWindow::openAbout () void MainWindow::openHelp () { - QDesktopServices::openUrl (QUrl (QString::fromLatin1 ("http://www.transmissionbt.com/help/gtk/%1.%2x"). + QDesktopServices::openUrl (QUrl (QString::fromLatin1 ("https://transmissionbt.com/help/gtk/%1.%2x"). arg (MAJOR_VERSION).arg (MINOR_VERSION / 10))); } diff --git a/qt/README.txt b/qt/README.txt index 4b6800d9a..4d9bf9f37 100644 --- a/qt/README.txt +++ b/qt/README.txt @@ -1,7 +1,7 @@ VOLUNTEERS WANTED - Qt developers and translators are needed - - If you find a bug, please report it at http://trac.transmissionbt.com/ + - If you find a bug, please report it at https://github.com/transmission/transmission ABOUT TRANSMISSION-QT diff --git a/qt/transmission-qt.1 b/qt/transmission-qt.1 index f183c4b73..616f3c0f6 100644 --- a/qt/transmission-qt.1 +++ b/qt/transmission-qt.1 @@ -23,7 +23,7 @@ information on the BitTorrent protocol see http://www.bittorrent.org/ Show help options .It Fl g, Fl -config-dir Ar directory Where to look for configuration files. This can be used to swap between using the cli, daemon, gtk, and qt clients. -See http://trac.transmissionbt.com/wiki/ConfigFiles for more information. +See https://github.com/transmission/transmission/wiki/Configuration-Files for more information. .It Fl m Fl -minimized Start minimized in notification area .It Fl p, Fl -port Ar port @@ -74,4 +74,4 @@ and .Xr transmission-remote 1 , .Xr transmission-show 1 .Pp -http://www.transmissionbt.com/ +https://transmissionbt.com/ diff --git a/transmission-gtk.spec.in b/transmission-gtk.spec.in index 6e75e8a9c..8c3f149dd 100644 --- a/transmission-gtk.spec.in +++ b/transmission-gtk.spec.in @@ -8,7 +8,7 @@ Version: %{version} Release: %{release} License: MIT Group: Applications/Internet -URL: http://www.transmissionbt.com/ +URL: https://transmissionbt.com/ Epoch: 1 Source0: %{name}-%{version}.tar.bz2 diff --git a/utils/transmission-create.1 b/utils/transmission-create.1 index 2a2de017d..6fb15b60a 100644 --- a/utils/transmission-create.1 +++ b/utils/transmission-create.1 @@ -49,4 +49,4 @@ To add more than one, use this option multiple times. .Xr transmission-remote 1 , .Xr transmission-show 1 .Pp -http://www.transmissionbt.com/ +https://transmissionbt.com/ diff --git a/utils/transmission-edit.1 b/utils/transmission-edit.1 index 91ff7a507..9395ad978 100644 --- a/utils/transmission-edit.1 +++ b/utils/transmission-edit.1 @@ -44,4 +44,4 @@ $ transmission-edit -r old-passcode new-passcode ~/.config/transmission/torrents .Xr transmission-remote 1 , .Xr transmission-show 1 .Pp -http://www.transmissionbt.com/ +https://transmissionbt.com/ diff --git a/utils/transmission-show.1 b/utils/transmission-show.1 index b1b70c5b0..6961f0aa5 100644 --- a/utils/transmission-show.1 +++ b/utils/transmission-show.1 @@ -36,4 +36,4 @@ Ask the torrent's trackers how many peers are in the torrent's swarm .Xr transmission-remote 1 , .Xr transmission-show 1 .Pp -http://www.transmissionbt.com/ +https://transmissionbt.com/ diff --git a/web/javascript/transmission.js b/web/javascript/transmission.js index 2fdc9a1e5..d3827114e 100644 --- a/web/javascript/transmission.js +++ b/web/javascript/transmission.js @@ -699,7 +699,7 @@ Transmission.prototype = break; case 'homepage': - window.open('http://www.transmissionbt.com/'); + window.open('https://transmissionbt.com/'); break; case 'tipjar': From 15c91ec46efb78d4fd9b26ba73bdec87949e0530 Mon Sep 17 00:00:00 2001 From: Mike Gelfand Date: Sun, 25 Sep 2016 00:22:44 +0300 Subject: [PATCH 05/22] Add libevent submodule (should hopefully fix Mac build) --- .gitmodules | 3 +++ third-party/libevent | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 third-party/libevent diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..3d32a04a2 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "third-party/libevent"] + path = third-party/libevent + url = https://github.com/transmission/libevent diff --git a/third-party/libevent b/third-party/libevent new file mode 160000 index 000000000..c51b159cf --- /dev/null +++ b/third-party/libevent @@ -0,0 +1 @@ +Subproject commit c51b159cff9f5e86696f5b9a4c6f517276056258 From 95109af45918b540c53f5456970a44d3513058fa Mon Sep 17 00:00:00 2001 From: Mike Gelfand Date: Wed, 17 Jan 2018 00:46:54 +0300 Subject: [PATCH 06/22] Allow host whitelist control via plist on Mac Add RPCUseHostWhitelist (maps to rpc-host-whitelist-enabled) and RPCHostWhitelist (maps to rpc-host-whitelist) keys loading from the plist to allow adjustment via `defaults` command. --- macosx/Controller.m | 4 ++++ macosx/Defaults.plist | 2 ++ 2 files changed, 6 insertions(+) diff --git a/macosx/Controller.m b/macosx/Controller.m index 38ccfe728..25d8aea3a 100644 --- a/macosx/Controller.m +++ b/macosx/Controller.m @@ -386,6 +386,7 @@ static void removeKeRangerRansomware() tr_variantDictAddInt(&settings, TR_KEY_rpc_port, [fDefaults integerForKey: @"RPCPort"]); tr_variantDictAddStr(&settings, TR_KEY_rpc_username, [[fDefaults stringForKey: @"RPCUsername"] UTF8String]); tr_variantDictAddBool(&settings, TR_KEY_rpc_whitelist_enabled, [fDefaults boolForKey: @"RPCUseWhitelist"]); + tr_variantDictAddBool(&settings, TR_KEY_rpc_host_whitelist_enabled, [fDefaults boolForKey: @"RPCUseHostWhitelist"]); tr_variantDictAddBool(&settings, TR_KEY_seed_queue_enabled, [fDefaults boolForKey: @"QueueSeed"]); tr_variantDictAddInt(&settings, TR_KEY_seed_queue_size, [fDefaults integerForKey: @"QueueSeedNumber"]); tr_variantDictAddBool(&settings, TR_KEY_start_added_torrents, [fDefaults boolForKey: @"AutoStartDownload"]); @@ -393,6 +394,9 @@ static void removeKeRangerRansomware() tr_variantDictAddStr(&settings, TR_KEY_script_torrent_done_filename, [[fDefaults stringForKey: @"DoneScriptPath"] UTF8String]); tr_variantDictAddBool(&settings, TR_KEY_utp_enabled, [fDefaults boolForKey: @"UTPGlobal"]); + // TODO: Add to GUI + if ([fDefaults objectForKey: @"RPCHostWhitelist"]) + tr_variantDictAddStr(&settings, TR_KEY_rpc_host_whitelist, [[fDefaults stringForKey: @"RPCHostWhitelist"] UTF8String]); NSString * kbString, * mbString, * gbString, * tbString; if ([NSApp isOnMountainLionOrBetter]) diff --git a/macosx/Defaults.plist b/macosx/Defaults.plist index bb95ad603..c3ed9bdb3 100644 --- a/macosx/Defaults.plist +++ b/macosx/Defaults.plist @@ -138,6 +138,8 @@ RPCPort 9091 + RPCUseHostWhitelist + RPCUsername admin RPCUseWhitelist From 709ca6a4b22575b59299cc1b899cabe0069498c8 Mon Sep 17 00:00:00 2001 From: Eric Petit Date: Sun, 12 Mar 2017 06:20:14 +0100 Subject: [PATCH 07/22] Work around "error: m4_copy: won't overwrite defined macro: glib_DEFUN" on Xenial, copied from https://github.com/Alexpux/MINGW-packages/issues/1351 --- m4/glib-gettext.m4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/m4/glib-gettext.m4 b/m4/glib-gettext.m4 index 01db6de13..cb0ddfad0 100644 --- a/m4/glib-gettext.m4 +++ b/m4/glib-gettext.m4 @@ -36,8 +36,8 @@ dnl We go to great lengths to make sure that aclocal won't dnl try to pull in the installed version of these macros dnl when running aclocal in the glib directory. dnl -m4_copy([AC_DEFUN],[glib_DEFUN]) -m4_copy([AC_REQUIRE],[glib_REQUIRE]) +m4_copy_force([AC_DEFUN],[glib_DEFUN]) +m4_copy_force([AC_REQUIRE],[glib_REQUIRE]) dnl dnl At the end, if we're not within glib, we'll define the public dnl definitions in terms of our private definitions. From 0994e96f3136eb8d384af52e1aa0fcc4a2c921c9 Mon Sep 17 00:00:00 2001 From: Mike Gelfand Date: Tue, 4 Jul 2017 22:59:55 +0300 Subject: [PATCH 08/22] Uncaught exception when dragging multiple items between groups (macOS) Change group after all the torrents are retrieved from drop data to avoid premature view item index changes. Fixes: #51 --- macosx/Controller.m | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/macosx/Controller.m b/macosx/Controller.m index 25d8aea3a..0a35cdb4d 100644 --- a/macosx/Controller.m +++ b/macosx/Controller.m @@ -3163,10 +3163,15 @@ static void removeKeRangerRansomware() { Torrent * torrent = [fTableView itemAtRow: i]; [movingTorrents addObject: torrent]; + } - //change groups - if (item) - [torrent setGroupValue: [item groupIndex] determinationType: TorrentDeterminationUserSpecified]; + //change groups + if (item) + { + const NSInteger groupIndex = [item groupIndex]; + + for (Torrent * torrent in movingTorrents) + [torrent setGroupValue: groupIndex determinationType: TorrentDeterminationUserSpecified]; } //reorder queue order From 1d04ea65d3e03c9319fdcf547003f501ab118a64 Mon Sep 17 00:00:00 2001 From: Mike Gelfand Date: Tue, 4 Jul 2017 21:22:26 +0300 Subject: [PATCH 09/22] Don't switch trackers while announcing Fixes: #297 --- libtransmission/announcer-udp.c | 16 ++++++++++++---- libtransmission/announcer.c | 2 ++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/libtransmission/announcer-udp.c b/libtransmission/announcer-udp.c index 3b7c41341..3e573c65a 100644 --- a/libtransmission/announcer-udp.c +++ b/libtransmission/announcer-udp.c @@ -659,7 +659,8 @@ tau_tracker_is_idle (const struct tau_tracker * tracker) } static void -tau_tracker_upkeep (struct tau_tracker * tracker) +tau_tracker_upkeep_ex (struct tau_tracker * tracker, + bool timeout_reqs) { const time_t now = tr_time (); const bool closing = tracker->close_at != 0; @@ -717,12 +718,19 @@ tau_tracker_upkeep (struct tau_tracker * tracker) return; } - tau_tracker_timeout_reqs (tracker); + if (timeout_reqs) + tau_tracker_timeout_reqs (tracker); if ((tracker->addr != NULL) && (tracker->connection_expiration_time > now)) tau_tracker_send_reqs (tracker); } +static void +tau_tracker_upkeep (struct tau_tracker * tracker) +{ + tau_tracker_upkeep_ex (tracker, true); +} + /**** ***** ***** SESSION @@ -957,7 +965,7 @@ tr_tracker_udp_announce (tr_session * session, response_func, user_data); tr_ptrArrayAppend (&tracker->announces, r); - tau_tracker_upkeep (tracker); + tau_tracker_upkeep_ex (tracker, false); } void @@ -972,5 +980,5 @@ tr_tracker_udp_scrape (tr_session * session, response_func, user_data); tr_ptrArrayAppend (&tracker->scrapes, r); - tau_tracker_upkeep (tracker); + tau_tracker_upkeep_ex (tracker, false); } diff --git a/libtransmission/announcer.c b/libtransmission/announcer.c index eeffbb81e..35e1f49ba 100644 --- a/libtransmission/announcer.c +++ b/libtransmission/announcer.c @@ -1425,6 +1425,8 @@ multiscrape (tr_announcer * announcer, tr_ptrArray * tiers) char * url = tier->currentTracker->scrape; const uint8_t * hash = tier->tor->info.hash; + assert (url != NULL); + /* if there's a request with this scrape URL and a free slot, use it */ for (j=0; j Date: Wed, 18 Jan 2017 21:52:56 +0300 Subject: [PATCH 10/22] When building with Xcode, link against 'libcrypto.dylib' instead of 'libcrypto.0.9.8.dylib' Fixes: #71 --- Transmission.xcodeproj/project.pbxproj | 24 ++++++++++++------------ third-party/openssl/lib/libcrypto.dylib | 1 + 2 files changed, 13 insertions(+), 12 deletions(-) create mode 120000 third-party/openssl/lib/libcrypto.dylib diff --git a/Transmission.xcodeproj/project.pbxproj b/Transmission.xcodeproj/project.pbxproj index 4827a68a6..93c8c17f3 100644 --- a/Transmission.xcodeproj/project.pbxproj +++ b/Transmission.xcodeproj/project.pbxproj @@ -129,10 +129,10 @@ A225A4C0187E369C00CDE823 /* ShareToolbarItem.m in Sources */ = {isa = PBXBuildFile; fileRef = A225A4BF187E369C00CDE823 /* ShareToolbarItem.m */; }; A2265F420B5EF5F40093DDA5 /* FileNameCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A2265F400B5EF5F40093DDA5 /* FileNameCell.m */; }; A226FDAC0D0CDF20005A7F71 /* libnatpmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3C7A118D0D0B2EB800B5701F /* libnatpmp.a */; }; - A2290D1E14421CC100B95A09 /* libcrypto.0.9.8.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A27653A714369C5C009D3CCF /* libcrypto.0.9.8.dylib */; }; - A2290D2014421CD000B95A09 /* libcrypto.0.9.8.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A27653A714369C5C009D3CCF /* libcrypto.0.9.8.dylib */; }; - A2290D2214421CD800B95A09 /* libcrypto.0.9.8.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A27653A714369C5C009D3CCF /* libcrypto.0.9.8.dylib */; }; - A2290D2514421D1A00B95A09 /* libcrypto.0.9.8.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A27653A714369C5C009D3CCF /* libcrypto.0.9.8.dylib */; }; + A2290D1E14421CC100B95A09 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A27653A714369C5C009D3CCF /* libcrypto.dylib */; }; + A2290D2014421CD000B95A09 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A27653A714369C5C009D3CCF /* libcrypto.dylib */; }; + A2290D2214421CD800B95A09 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A27653A714369C5C009D3CCF /* libcrypto.dylib */; }; + A2290D2514421D1A00B95A09 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A27653A714369C5C009D3CCF /* libcrypto.dylib */; }; A2290D2E1442B23200B95A09 /* libcurl.4.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A2290D2D1442B23200B95A09 /* libcurl.4.dylib */; }; A2290D2F1442B23200B95A09 /* libcurl.4.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A2290D2D1442B23200B95A09 /* libcurl.4.dylib */; }; A2290D301442B23200B95A09 /* libcurl.4.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A2290D2D1442B23200B95A09 /* libcurl.4.dylib */; }; @@ -393,7 +393,7 @@ A2F35BCA15C5A0A100EBF632 /* GenerateThumbnailForURL.m in Sources */ = {isa = PBXBuildFile; fileRef = A2F35BC915C5A0A100EBF632 /* GenerateThumbnailForURL.m */; }; A2F35BCC15C5A0A100EBF632 /* GeneratePreviewForURL.m in Sources */ = {isa = PBXBuildFile; fileRef = A2F35BCB15C5A0A100EBF632 /* GeneratePreviewForURL.m */; }; A2F35BD415C5A19A00EBF632 /* libtransmission.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D18389709DEC0030047D688 /* libtransmission.a */; }; - A2F35BD715C5A46D00EBF632 /* libcrypto.0.9.8.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A27653A714369C5C009D3CCF /* libcrypto.0.9.8.dylib */; }; + A2F35BD715C5A46D00EBF632 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A27653A714369C5C009D3CCF /* libcrypto.dylib */; }; A2F35BDA15C5A49200EBF632 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A2B6141B1395ADE9000E0975 /* libz.dylib */; }; A2F35BDB15C5A4A000EBF632 /* libiconv.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = A296EF3411E5605E004A2781 /* libiconv.dylib */; }; A2F35BE115C5A7ED00EBF632 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A2F35BE015C5A7ED00EBF632 /* Cocoa.framework */; }; @@ -920,7 +920,7 @@ A2725D5C0DE7507C003445E7 /* TrackerTableView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = TrackerTableView.m; path = macosx/TrackerTableView.m; sourceTree = ""; }; A27476FF0CC38EE6003CC76D /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = macosx/es.lproj/InfoPlist.strings; sourceTree = ""; }; A27477010CC38EE6003CC76D /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = macosx/es.lproj/Localizable.strings; sourceTree = ""; }; - A27653A714369C5C009D3CCF /* libcrypto.0.9.8.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.0.9.8.dylib; path = "third-party/openssl/lib/libcrypto.0.9.8.dylib"; sourceTree = ""; }; + A27653A714369C5C009D3CCF /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.dylib; path = "third-party/openssl/lib/libcrypto.dylib"; sourceTree = ""; }; A277DA090C693D9C00DA2CD4 /* ActionOn.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = ActionOn.png; path = macosx/Images/ActionOn.png; sourceTree = ""; }; A279E3D011C3BDC300D48B1F /* nl */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = nl; path = macosx/nl.lproj/AddMagnetWindow.xib; sourceTree = ""; }; A279E3D111C3BDC300D48B1F /* nl */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = nl; path = macosx/nl.lproj/InfoActivityView.xib; sourceTree = ""; }; @@ -1256,7 +1256,7 @@ files = ( 4D9A2BF009E16D21002D0FF9 /* libtransmission.a in Frameworks */, A296EF3C11E560BD004A2781 /* libiconv.dylib in Frameworks */, - A2290D1E14421CC100B95A09 /* libcrypto.0.9.8.dylib in Frameworks */, + A2290D1E14421CC100B95A09 /* libcrypto.dylib in Frameworks */, A2290D2F1442B23200B95A09 /* libcurl.4.dylib in Frameworks */, A2B6141E1395B0EC000E0975 /* libz.dylib in Frameworks */, A2B3FB4C0E59023000FF78FB /* Cocoa.framework in Frameworks */, @@ -1273,7 +1273,7 @@ A24F19080A3A790800C9C145 /* Sparkle.framework in Frameworks */, A261F1DC0A69A1610002815A /* Growl.framework in Frameworks */, A296EF3B11E560A7004A2781 /* libiconv.dylib in Frameworks */, - A2290D2514421D1A00B95A09 /* libcrypto.0.9.8.dylib in Frameworks */, + A2290D2514421D1A00B95A09 /* libcrypto.dylib in Frameworks */, A2290D2E1442B23200B95A09 /* libcurl.4.dylib in Frameworks */, A2B6141F1395B0F5000E0975 /* libz.dylib in Frameworks */, A2E669790F5B8E5A00B4251A /* Security.framework in Frameworks */, @@ -1307,7 +1307,7 @@ A2F35BD415C5A19A00EBF632 /* libtransmission.a in Frameworks */, A2F35BDB15C5A4A000EBF632 /* libiconv.dylib in Frameworks */, A2F35BDA15C5A49200EBF632 /* libz.dylib in Frameworks */, - A2F35BD715C5A46D00EBF632 /* libcrypto.0.9.8.dylib in Frameworks */, + A2F35BD715C5A46D00EBF632 /* libcrypto.dylib in Frameworks */, A2AB76EA15D8130B009EFC95 /* libcurl.4.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1345,7 +1345,7 @@ files = ( BEFC1C050C07753500B0BB3C /* libtransmission.a in Frameworks */, A296EF3D11E560C3004A2781 /* libiconv.dylib in Frameworks */, - A2290D2014421CD000B95A09 /* libcrypto.0.9.8.dylib in Frameworks */, + A2290D2014421CD000B95A09 /* libcrypto.dylib in Frameworks */, A2290D301442B23200B95A09 /* libcurl.4.dylib in Frameworks */, A2B6141D1395B0E3000E0975 /* libz.dylib in Frameworks */, A2B3FB530E59027100FF78FB /* Cocoa.framework in Frameworks */, @@ -1358,7 +1358,7 @@ files = ( BEFC1D2D0C0783D900B0BB3C /* libtransmission.a in Frameworks */, A296EF3E11E560D1004A2781 /* libiconv.dylib in Frameworks */, - A2290D2214421CD800B95A09 /* libcrypto.0.9.8.dylib in Frameworks */, + A2290D2214421CD800B95A09 /* libcrypto.dylib in Frameworks */, A2290D311442B23200B95A09 /* libcurl.4.dylib in Frameworks */, A2B6141C1395ADE9000E0975 /* libz.dylib in Frameworks */, A25E03D90E4015100086C225 /* Cocoa.framework in Frameworks */, @@ -1838,7 +1838,7 @@ 4DDBB71509E16B3F00284745 /* Libraries */ = { isa = PBXGroup; children = ( - A27653A714369C5C009D3CCF /* libcrypto.0.9.8.dylib */, + A27653A714369C5C009D3CCF /* libcrypto.dylib */, A2290D2D1442B23200B95A09 /* libcurl.4.dylib */, A296EF3411E5605E004A2781 /* libiconv.dylib */, A2B6141B1395ADE9000E0975 /* libz.dylib */, diff --git a/third-party/openssl/lib/libcrypto.dylib b/third-party/openssl/lib/libcrypto.dylib new file mode 120000 index 000000000..ec6897890 --- /dev/null +++ b/third-party/openssl/lib/libcrypto.dylib @@ -0,0 +1 @@ +libcrypto.0.9.8.dylib \ No newline at end of file From c22e687b5162cb26c2daa8a8f352b7eacff16660 Mon Sep 17 00:00:00 2001 From: Mike Gelfand Date: Mon, 9 Jan 2017 14:15:07 +0300 Subject: [PATCH 11/22] Simplify `tr_ssha1_matches` logic Gets rid of needless heap memory allocations and copying. Convert SHA1 hash to hex in-place. Fixes: #141 --- libtransmission/crypto-utils.c | 41 +++++++++++++++------------------- libtransmission/utils.c | 17 ++++++++------ 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/libtransmission/crypto-utils.c b/libtransmission/crypto-utils.c index 99279732f..1eaff9a49 100644 --- a/libtransmission/crypto-utils.c +++ b/libtransmission/crypto-utils.c @@ -133,6 +133,8 @@ tr_ssha1 (const char * plain_text) "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "./"; + assert (plain_text != NULL); + size_t i; unsigned char salt[saltval_len]; uint8_t sha[SHA_DIGEST_LENGTH]; @@ -155,34 +157,27 @@ bool tr_ssha1_matches (const char * ssha1, const char * plain_text) { - char * salt; - size_t saltlen; - char * my_ssha1; - uint8_t buf[SHA_DIGEST_LENGTH]; - bool result; - const size_t sourcelen = strlen (ssha1); + assert (ssha1 != NULL); + assert (plain_text != NULL); + + const size_t brace_len = 1; + const size_t brace_and_hash_len = brace_len + 2 * SHA_DIGEST_LENGTH; + + const size_t source_len = strlen (ssha1); + if (source_len < brace_and_hash_len || ssha1[0] != '{') + return false; /* extract the salt */ - if (sourcelen < 2 * SHA_DIGEST_LENGTH - 1) - return false; - saltlen = sourcelen - 2 * SHA_DIGEST_LENGTH - 1; - salt = tr_malloc (saltlen); - memcpy (salt, ssha1 + 2 * SHA_DIGEST_LENGTH + 1, saltlen); + const char * const salt = ssha1 + brace_and_hash_len; + const size_t salt_len = source_len - brace_and_hash_len; + + uint8_t buf[SHA_DIGEST_LENGTH * 2 + 1]; /* hash pass + salt */ - my_ssha1 = tr_malloc (2 * SHA_DIGEST_LENGTH + saltlen + 2); - tr_sha1 (buf, plain_text, strlen (plain_text), salt, saltlen, NULL); - tr_sha1_to_hex (&my_ssha1[1], buf); - memcpy (my_ssha1 + 1 + 2 * SHA_DIGEST_LENGTH, salt, saltlen); - my_ssha1[1 + 2 * SHA_DIGEST_LENGTH + saltlen] = '\0'; - my_ssha1[0] = '{'; + tr_sha1 (buf, plain_text, (int) strlen (plain_text), salt, (int) salt_len, NULL); + tr_sha1_to_hex ((char *) buf, buf); - result = strcmp (ssha1, my_ssha1) == 0; - - tr_free (my_ssha1); - tr_free (salt); - - return result; + return strncmp (ssha1 + brace_len, (const char *) buf, SHA_DIGEST_LENGTH * 2) == 0; } /*** diff --git a/libtransmission/utils.c b/libtransmission/utils.c index db9a4456f..52755aae5 100644 --- a/libtransmission/utils.c +++ b/libtransmission/utils.c @@ -636,16 +636,19 @@ tr_binary_to_hex (const void * input, { static const char hex[] = "0123456789abcdef"; const uint8_t * input_octets = input; - size_t i; - for (i = 0; i < byte_length; ++i) - { - const unsigned int val = *input_octets++; - *output++ = hex[val >> 4]; - *output++ = hex[val & 0xf]; - } + /* go from back to front to allow for in-place conversion */ + input_octets += byte_length; + output += byte_length * 2; *output = '\0'; + + while (byte_length-- > 0) + { + const unsigned int val = *(--input_octets); + *(--output) = hex[val & 0xf]; + *(--output) = hex[val >> 4]; + } } void From 0b047f7aa59c0e08d8083f36ddeaa46b05d27179 Mon Sep 17 00:00:00 2001 From: Mike Gelfand Date: Wed, 17 Jan 2018 20:52:37 +0300 Subject: [PATCH 12/22] Update NEWS, bump to 2.93 --- CMakeLists.txt | 4 ++-- NEWS | 12 ++++++++++++ configure.ac | 4 ++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ac0989b15..1d6394acd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,8 +41,8 @@ set(TR_NAME ${PROJECT_NAME}) # "Z" for unsupported trunk builds, # "0" for stable, supported releases # these should be the only two lines you need to change -set(TR_USER_AGENT_PREFIX "2.92") -set(TR_PEER_ID_PREFIX "-TR2920-") +set(TR_USER_AGENT_PREFIX "2.93") +set(TR_PEER_ID_PREFIX "-TR2930-") string(REGEX MATCH "^([0-9]+)\\.([0-9]+).*" TR_VERSION "${TR_USER_AGENT_PREFIX}") set(TR_VERSION_MAJOR "${CMAKE_MATCH_1}") diff --git a/NEWS b/NEWS index 5f77a6e92..97278fa7f 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,15 @@ +=== Transmission 2.93 (2018/01/xx) === +[https://github.com/transmission/transmission/releases/tag/2.93 All tickets closed by this release] +==== All Platforms ==== + * Fix CVE-2018-5702 (#468) + * Fix crash on handshake if establishing DH shared secret fails (#27) + * Fix crash when switching to next tracker during announcement (#297) + * Fix potential issue during password salt extraction in OOM situation (#141) + * Workaround `glib_DEFUN`- and `glib_REQUIRE`-related configuration issue (#215) +==== Mac Client ==== + * Fix uncaught exception when dragging multiple items between groups (#51) + * Don't hard-code libcrypto version to 0.9.8 in Xcode project (#71) + === Transmission 2.92 (2016/03/06) === [http://trac.transmissionbt.com/query?milestone=2.92&group=component&order=severity All tickets closed by this release] ==== Mac Client ==== diff --git a/configure.ac b/configure.ac index 339bdd911..aff673b52 100644 --- a/configure.ac +++ b/configure.ac @@ -3,8 +3,8 @@ dnl STATUS: "X" for prerelease beta builds, dnl "Z" for unsupported trunk builds, dnl "0" for stable, supported releases dnl these should be the only two lines you need to change -m4_define([user_agent_prefix],[2.92]) -m4_define([peer_id_prefix],[-TR2920-]) +m4_define([user_agent_prefix],[2.93]) +m4_define([peer_id_prefix],[-TR2930-]) AC_INIT([transmission],[user_agent_prefix],[https://github.com/transmission/transmission]) AC_SUBST(USERAGENT_PREFIX,[user_agent_prefix]) From 4b359a52b252beb37ba4268997b682a7a8627931 Mon Sep 17 00:00:00 2001 From: Mike Gelfand Date: Wed, 17 Jan 2018 21:19:59 +0300 Subject: [PATCH 13/22] Add DNS rebinding notes to RPC spec Fixes: #472 --- extras/rpc-spec.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/extras/rpc-spec.txt b/extras/rpc-spec.txt index abe491fb6..827ebf99c 100644 --- a/extras/rpc-spec.txt +++ b/extras/rpc-spec.txt @@ -66,6 +66,21 @@ So, the correct way to handle a 409 response is to update your X-Transmission-Session-Id and to resend the previous request. +2.3.2. DNS Rebinding Protection + + If CSRF protection is enabled, additional check is being made on each RPC + request to make sure that the client sending the request does so using + one of the allowed hostnames by which RPC server is meant to be available. + + If host whitelisting is enabled (which is true by default), Transmission + inspects the "Host:" HTTP header value (with port stripped, if any) and + matches it to one of the whitelisted names. Regardless of host whitelist + content, "localhost" and "localhost." domain names as well as all the IP + addresses are always implicitly allowed. + + For more information on configuration, see settings.json documentation for + "rpc-host-whitelist-enabled" and "rpc-host-whitelist" keys. + 3. Torrent Requests 3.1. Torrent Action Requests From 90686f30ce5231ff965a4d102885acae205e4f3c Mon Sep 17 00:00:00 2001 From: Mike Gelfand Date: Wed, 17 Jan 2018 23:32:14 +0300 Subject: [PATCH 14/22] Support IPv6 addresses when whitelisting hosts (see #468) --- libtransmission/rpc-server.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/libtransmission/rpc-server.c b/libtransmission/rpc-server.c index c80a629ca..60a8320f7 100644 --- a/libtransmission/rpc-server.c +++ b/libtransmission/rpc-server.c @@ -590,6 +590,16 @@ isAddressAllowed (const tr_rpc_server * server, const char * address) return false; } +static bool +isIPAddressWithOptionalPort (const char * host) +{ + struct sockaddr_storage address; + int address_len = sizeof (address); + + /* TODO: move to net.{c,h} */ + return evutil_parse_sockaddr_port (host, (struct sockaddr *) &address, &address_len) != -1; +} + static bool isHostnameAllowed (const tr_rpc_server * server, struct evhttp_request * req) { @@ -607,11 +617,15 @@ isHostnameAllowed (const tr_rpc_server * server, struct evhttp_request * req) if (host == NULL) return false; + /* IP address is always acceptable. */ + if (isIPAddressWithOptionalPort (host)) + return true; + /* Host header might include the port. */ char * const hostname = tr_strndup (host, strcspn (host, ":")); - /* localhost or ipaddress is always acceptable. */ - if (strcmp (hostname, "localhost") == 0 || strcmp (hostname, "localhost.") == 0 || tr_addressIsIP (hostname)) + /* localhost is always acceptable. */ + if (strcmp (hostname, "localhost") == 0 || strcmp (hostname, "localhost.") == 0) { tr_free (hostname); return true; From c0c2ba0b1382800a942e0f19272d937d3dc01b71 Mon Sep 17 00:00:00 2001 From: Mike Gelfand Date: Wed, 17 Jan 2018 23:34:40 +0300 Subject: [PATCH 15/22] Make host validation not depend on CSRF being enabled (see #468) --- extras/rpc-spec.txt | 6 +++--- libtransmission/rpc-server.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extras/rpc-spec.txt b/extras/rpc-spec.txt index 827ebf99c..f75dbed13 100644 --- a/extras/rpc-spec.txt +++ b/extras/rpc-spec.txt @@ -68,9 +68,9 @@ 2.3.2. DNS Rebinding Protection - If CSRF protection is enabled, additional check is being made on each RPC - request to make sure that the client sending the request does so using - one of the allowed hostnames by which RPC server is meant to be available. + Additional check is being made on each RPC request to make sure that the + client sending the request does so using one of the allowed hostnames by + which RPC server is meant to be available. If host whitelisting is enabled (which is true by default), Transmission inspects the "Host:" HTTP header value (with port stripped, if any) and diff --git a/libtransmission/rpc-server.c b/libtransmission/rpc-server.c index 60a8320f7..3ae724583 100644 --- a/libtransmission/rpc-server.c +++ b/libtransmission/rpc-server.c @@ -719,7 +719,6 @@ handle_request (struct evhttp_request * req, void * arg) { handle_upload (req, server); } -#ifdef REQUIRE_SESSION_ID else if (!isHostnameAllowed (server, req)) { char * const tmp = tr_strdup_printf ( @@ -736,6 +735,7 @@ handle_request (struct evhttp_request * req, void * arg) send_simple_response (req, 421, tmp); tr_free (tmp); } +#ifdef REQUIRE_SESSION_ID else if (!test_session_id (server, req)) { const char * sessionId = get_current_session_id (server); From 22bde650ea903249c154562263e09bbd97c99478 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 5 Sep 2016 21:49:07 +0000 Subject: [PATCH 16/22] transmission: build against openssl 1.1.0 Signed-off-by: Sebastian Andrzej Siewior --- libtransmission/crypto-utils-openssl.c | 73 ++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 4 deletions(-) diff --git a/libtransmission/crypto-utils-openssl.c b/libtransmission/crypto-utils-openssl.c index 77a628bea..16a37b205 100644 --- a/libtransmission/crypto-utils-openssl.c +++ b/libtransmission/crypto-utils-openssl.c @@ -230,6 +230,61 @@ tr_rc4_process (tr_rc4_ctx_t handle, **** ***/ +#if OPENSSL_VERSION_NUMBER < 0x10100000 +static inline int +DH_set0_pqg (DH * dh, + BIGNUM * p, + BIGNUM * q, + BIGNUM * g) +{ + /* If the fields p and g in d are NULL, the corresponding input + * parameters MUST be non-NULL. q may remain NULL. + */ + if ((dh->p == NULL && p == NULL) + || (dh->g == NULL && g == NULL)) + return 0; + + if (p != NULL) { + BN_free (dh->p); + dh->p = p; + } + if (q != NULL) { + BN_free (dh->q); + dh->q = q; + } + if (g != NULL) { + BN_free (dh->g); + dh->g = g; + } + + if (q != NULL) { + dh->length = BN_num_bits (q); + } + + return 1; +} + +static inline int +DH_set_length (DH * dh, + long length) +{ + dh->length = length; + return 1; +} + +static inline void +DH_get0_key(const DH * dh, + const BIGNUM ** pub_key, + const BIGNUM ** priv_key) +{ + if (pub_key != NULL) + *pub_key = dh->pub_key; + if (priv_key != NULL) + *priv_key = dh->priv_key; +} + +#endif + tr_dh_ctx_t tr_dh_new (const uint8_t * prime_num, size_t prime_num_length, @@ -237,13 +292,19 @@ tr_dh_new (const uint8_t * prime_num, size_t generator_num_length) { DH * handle = DH_new (); + BIGNUM * p, * g; assert (prime_num != NULL); assert (generator_num != NULL); + p = BN_bin2bn (prime_num, prime_num_length, NULL); + g = BN_bin2bn (generator_num, generator_num_length, NULL); - if (!check_pointer (handle->p = BN_bin2bn (prime_num, prime_num_length, NULL)) || - !check_pointer (handle->g = BN_bin2bn (generator_num, generator_num_length, NULL))) + if (!check_pointer (p) || + !check_pointer (g) || + !DH_set0_pqg (handle, p, NULL, g)) { + BN_free (p); + BN_free (g); DH_free (handle); handle = NULL; } @@ -268,16 +329,20 @@ tr_dh_make_key (tr_dh_ctx_t raw_handle, { DH * handle = raw_handle; int dh_size, my_public_key_length; + const BIGNUM * hand_pub_key; assert (handle != NULL); assert (public_key != NULL); - handle->length = private_key_length * 8; + + DH_set_length(handle, private_key_length * 8); if (!check_result (DH_generate_key (handle))) return false; - my_public_key_length = BN_bn2bin (handle->pub_key, public_key); + DH_get0_key (handle, &hand_pub_key, NULL); + + my_public_key_length = BN_bn2bin (hand_pub_key, public_key); dh_size = DH_size (handle); tr_dh_align_key (public_key, my_public_key_length, dh_size); From 01985bfd1bb0c8afebff8209cf4c1fac95b405c1 Mon Sep 17 00:00:00 2001 From: Mike Gelfand Date: Fri, 19 Jan 2018 02:52:01 +0300 Subject: [PATCH 17/22] Update NEWS --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index 97278fa7f..f0348abc0 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,7 @@ * Fix crash when switching to next tracker during announcement (#297) * Fix potential issue during password salt extraction in OOM situation (#141) * Workaround `glib_DEFUN`- and `glib_REQUIRE`-related configuration issue (#215) + * Fix building against OpenSSL 1.1.0+ (#24) ==== Mac Client ==== * Fix uncaught exception when dragging multiple items between groups (#51) * Don't hard-code libcrypto version to 0.9.8 in Xcode project (#71) From b237a260c55bf62f5cbc4d44ef0bac836a226905 Mon Sep 17 00:00:00 2001 From: Mike Gelfand Date: Sun, 3 Apr 2016 05:53:32 +0000 Subject: [PATCH 18/22] Try getting SVN revision from Jenkins environment when using CMake (copy logic from update-version-h.sh) --- CMakeLists.txt | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d6394acd..7056c9e32 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,10 +60,16 @@ set(TR_SCM_REVISION_FILE "${CMAKE_SOURCE_DIR}/REVISION") set(TR_SCM_REVISION_RELIABLE ON) if(IS_DIRECTORY ${CMAKE_SOURCE_DIR}/.svn) - find_package(Subversion) - if(Subversion_FOUND) - Subversion_WC_INFO(${CMAKE_SOURCE_DIR} TR_SVN) - set(TR_SCM_REVISION "${TR_SVN_WC_REVISION}") + if(NOT "$ENV{JENKINS_URL}" STREQUAL "" AND NOT "$ENV{SVN_REVISION}" STREQUAL "") + # Jenkins automated build, use the set environment variables to avoid + # version mismatches between java's svn and command line's svn + set(TR_SCM_REVISION "$ENV{SVN_REVISION}") + else() + find_package(Subversion) + if(Subversion_FOUND) + Subversion_WC_INFO(${CMAKE_SOURCE_DIR} TR_SVN) + set(TR_SCM_REVISION "${TR_SVN_WC_REVISION}") + endif() endif() elseif(IS_DIRECTORY ${CMAKE_SOURCE_DIR}/.git) find_package(Git) From b875e79f3d5164171f6a2553bc340b90e0163adf Mon Sep 17 00:00:00 2001 From: Mike Gelfand Date: Fri, 2 Sep 2016 22:02:51 +0300 Subject: [PATCH 19/22] Replace SVN and SCM with VCS everywhere --- CMakeLists.txt | 38 ++++++++++++++++++------------------ libtransmission/version.h.in | 6 +++--- macosx/Info.plist | 4 ++-- update-version-h.sh | 30 ++++++++++++++-------------- 4 files changed, 39 insertions(+), 39 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7056c9e32..cb08cd288 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,19 +56,19 @@ else() set(TR_STABLE_RELEASE 1) endif() -set(TR_SCM_REVISION_FILE "${CMAKE_SOURCE_DIR}/REVISION") -set(TR_SCM_REVISION_RELIABLE ON) +set(TR_VCS_REVISION_FILE "${CMAKE_SOURCE_DIR}/REVISION") +set(TR_VCS_REVISION_RELIABLE ON) if(IS_DIRECTORY ${CMAKE_SOURCE_DIR}/.svn) - if(NOT "$ENV{JENKINS_URL}" STREQUAL "" AND NOT "$ENV{SVN_REVISION}" STREQUAL "") + if(NOT "$ENV{JENKINS_URL}" STREQUAL "" AND NOT "$ENV{VCS_REVISION}" STREQUAL "") # Jenkins automated build, use the set environment variables to avoid # version mismatches between java's svn and command line's svn - set(TR_SCM_REVISION "$ENV{SVN_REVISION}") + set(TR_VCS_REVISION "$ENV{VCS_REVISION}") else() find_package(Subversion) if(Subversion_FOUND) Subversion_WC_INFO(${CMAKE_SOURCE_DIR} TR_SVN) - set(TR_SCM_REVISION "${TR_SVN_WC_REVISION}") + set(TR_VCS_REVISION "${TR_SVN_WC_REVISION}") endif() endif() elseif(IS_DIRECTORY ${CMAKE_SOURCE_DIR}/.git) @@ -80,37 +80,37 @@ elseif(IS_DIRECTORY ${CMAKE_SOURCE_DIR}/.git) WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE - TR_SCM_REVISION + TR_VCS_REVISION OUTPUT_STRIP_TRAILING_WHITESPACE ) endif() endif() -if("${TR_SCM_REVISION}" STREQUAL "" AND EXISTS "${TR_SCM_REVISION_FILE}") - file(READ "${TR_SCM_REVISION_FILE}" TR_SCM_REVISION) - string(STRIP "${TR_SCM_REVISION}" TR_SCM_REVISION) +if("${TR_VCS_REVISION}" STREQUAL "" AND EXISTS "${TR_VCS_REVISION_FILE}") + file(READ "${TR_VCS_REVISION_FILE}" TR_VCS_REVISION) + string(STRIP "${TR_VCS_REVISION}" TR_VCS_REVISION) endif() -if("${TR_SCM_REVISION}" STREQUAL "") +if("${TR_VCS_REVISION}" STREQUAL "") # Give up and check the source files - set(TR_SCM_REVISION 0) - set(TR_SCM_REVISION_RELIABLE OFF) + set(TR_VCS_REVISION 0) + set(TR_VCS_REVISION_RELIABLE OFF) file(GLOB_RECURSE TR_ALL_SOURCES RELATIVE ${CMAKE_SOURCE_DIR} *.cc *.[chm] *.po) foreach(F ${TR_ALL_SOURCES}) file(STRINGS ${F} F_ID REGEX "\\$Id:") - if(F_ID MATCHES "\\$Id: [^ ]+ ([0-9]+) " AND CMAKE_MATCH_1 GREATER TR_SCM_REVISION) - set(TR_SCM_REVISION ${CMAKE_MATCH_1}) + if(F_ID MATCHES "\\$Id: [^ ]+ ([0-9]+) " AND CMAKE_MATCH_1 GREATER TR_VCS_REVISION) + set(TR_VCS_REVISION ${CMAKE_MATCH_1}) endif() endforeach() endif() -if("${TR_SCM_REVISION}" STREQUAL "") - set(TR_SCM_REVISION 0) +if("${TR_VCS_REVISION}" STREQUAL "") + set(TR_VCS_REVISION 0) else() - if(TR_SCM_REVISION_RELIABLE) - file(WRITE "${TR_SCM_REVISION_FILE}" "${TR_SCM_REVISION}") + if(TR_VCS_REVISION_RELIABLE) + file(WRITE "${TR_VCS_REVISION_FILE}" "${TR_VCS_REVISION}") else() - file(REMOVE "${TR_SCM_REVISION_FILE}") + file(REMOVE "${TR_VCS_REVISION_FILE}") endif() endif() diff --git a/libtransmission/version.h.in b/libtransmission/version.h.in index 78d3e1832..ce74a3d96 100644 --- a/libtransmission/version.h.in +++ b/libtransmission/version.h.in @@ -3,10 +3,10 @@ #define PEERID_PREFIX "${TR_PEER_ID_PREFIX}" #define USERAGENT_PREFIX "${TR_USER_AGENT_PREFIX}" -#define SVN_REVISION "${TR_SCM_REVISION}" -#define SVN_REVISION_NUM ${TR_SCM_REVISION} +#define VCS_REVISION "${TR_VCS_REVISION}" +#define VCS_REVISION_NUM ${TR_VCS_REVISION} #define SHORT_VERSION_STRING "${TR_USER_AGENT_PREFIX}" -#define LONG_VERSION_STRING "${TR_USER_AGENT_PREFIX} (${TR_SCM_REVISION})" +#define LONG_VERSION_STRING "${TR_USER_AGENT_PREFIX} (${TR_VCS_REVISION})" #define VERSION_STRING_INFOPLIST ${TR_USER_AGENT_PREFIX} #define MAJOR_VERSION ${TR_VERSION_MAJOR} #define MINOR_VERSION ${TR_VERSION_MINOR} diff --git a/macosx/Info.plist b/macosx/Info.plist index 6c4327205..1f6e290f2 100644 --- a/macosx/Info.plist +++ b/macosx/Info.plist @@ -28,7 +28,7 @@ CFBundleExecutable Transmission CFBundleGetInfoString - VERSION_STRING_INFOPLIST (SVN_REVISION_NUM) + VERSION_STRING_INFOPLIST (VCS_REVISION_NUM) CFBundleHelpBookFolder TransmissionHelp CFBundleHelpBookName @@ -59,7 +59,7 @@ CFBundleVersion - SVN_REVISION_NUM + VCS_REVISION_NUM LSApplicationCategoryType public.app-category.utilities LSMinimumSystemVersion diff --git a/update-version-h.sh b/update-version-h.sh index a5af24658..b93ba9490 100755 --- a/update-version-h.sh +++ b/update-version-h.sh @@ -20,38 +20,38 @@ peer_id_prefix=`grep m4_define configure.ac | sed "s/[][)(]/,/g" | grep peer_id_ major_version=`echo ${user_agent_prefix} | awk -F . '{print $1}'` minor_version=`echo ${user_agent_prefix} | awk -F . '{print $2 + 0}'` -svn_revision_file=REVISION -svn_revision_reliable=true +vcs_revision_file=REVISION +vcs_revision_reliable=true -if [ -n "$JENKINS_URL" -a -n "$SVN_REVISION" ]; then +if [ -n "$JENKINS_URL" -a -n "$VCS_REVISION" ]; then # Jenkins automated build, use the set environment variables to avoid # version mismatches between java's svn and command line's svn - svn_revision=$SVN_REVISION + vcs_revision=$VCS_REVISION elif [ -d ".svn" ] && type svnversion >/dev/null 2>&1; then # If this is a svn tree, and svnversion is available in PATH, use it to # grab the version. - svn_revision=`svnversion -n . | cut -d: -f1 | cut -dM -f1 | cut -dS -f1` -elif [ -f "$svn_revision_file" ]; then - svn_revision=`cat "$svn_revision_file"` + vcs_revision=`svnversion -n . | cut -d: -f1 | cut -dM -f1 | cut -dS -f1` +elif [ -f "$vcs_revision_file" ]; then + vcs_revision=`cat "$vcs_revision_file"` else # Give up and check the source files - svn_revision=`awk '/\\$Id: /{ if ($4>i) i=$4 } END {print i}' */*.cc */*.[chm] */*.po` - svn_revision_reliable=false + vcs_revision=`awk '/\\$Id: /{ if ($4>i) i=$4 } END {print i}' */*.cc */*.[chm] */*.po` + vcs_revision_reliable=false fi -if $svn_revision_reliable; then - [ -f "$svn_revision_file" ] && [ "`cat "$svn_revision_file"`" -eq "$svn_revision" ] || echo "$svn_revision" > "$svn_revision_file" +if $vcs_revision_reliable; then + [ -f "$vcs_revision_file" ] && [ "`cat "$vcs_revision_file"`" = "$vcs_revision" ] || echo "$vcs_revision" > "$vcs_revision_file" else - rm -f "$svn_revision_file" + rm -f "$vcs_revision_file" fi cat > libtransmission/version.h.new << EOF #define PEERID_PREFIX "${peer_id_prefix}" #define USERAGENT_PREFIX "${user_agent_prefix}" -#define SVN_REVISION "${svn_revision}" -#define SVN_REVISION_NUM ${svn_revision} +#define VCS_REVISION "${vcs_revision}" +#define VCS_REVISION_NUM ${vcs_revision} #define SHORT_VERSION_STRING "${user_agent_prefix}" -#define LONG_VERSION_STRING "${user_agent_prefix} (${svn_revision})" +#define LONG_VERSION_STRING "${user_agent_prefix} (${vcs_revision})" #define VERSION_STRING_INFOPLIST ${user_agent_prefix} #define MAJOR_VERSION ${major_version} #define MINOR_VERSION ${minor_version} From f6b23c02f015ecaee863048d6daca2efc510ef38 Mon Sep 17 00:00:00 2001 From: Mike Gelfand Date: Fri, 2 Sep 2016 22:45:03 +0300 Subject: [PATCH 20/22] Don't search for SVN revision when calculating version --- CMakeLists.txt | 41 ++++++++--------------------------------- update-version-h.sh | 15 +++++---------- 2 files changed, 13 insertions(+), 43 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cb08cd288..0b8cbd29e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,20 +57,11 @@ else() endif() set(TR_VCS_REVISION_FILE "${CMAKE_SOURCE_DIR}/REVISION") -set(TR_VCS_REVISION_RELIABLE ON) -if(IS_DIRECTORY ${CMAKE_SOURCE_DIR}/.svn) - if(NOT "$ENV{JENKINS_URL}" STREQUAL "" AND NOT "$ENV{VCS_REVISION}" STREQUAL "") - # Jenkins automated build, use the set environment variables to avoid - # version mismatches between java's svn and command line's svn - set(TR_VCS_REVISION "$ENV{VCS_REVISION}") - else() - find_package(Subversion) - if(Subversion_FOUND) - Subversion_WC_INFO(${CMAKE_SOURCE_DIR} TR_SVN) - set(TR_VCS_REVISION "${TR_SVN_WC_REVISION}") - endif() - endif() +if(NOT "$ENV{JENKINS_URL}" STREQUAL "" AND NOT "$ENV{VCS_REVISION}" STREQUAL "") + # Jenkins automated build, use the set environment variables to avoid + # version mismatches between java's svn and command line's svn + set(TR_VCS_REVISION "$ENV{VCS_REVISION}") elseif(IS_DIRECTORY ${CMAKE_SOURCE_DIR}/.git) find_package(Git) if(GIT_FOUND) @@ -91,27 +82,11 @@ if("${TR_VCS_REVISION}" STREQUAL "" AND EXISTS "${TR_VCS_REVISION_FILE}") string(STRIP "${TR_VCS_REVISION}" TR_VCS_REVISION) endif() -if("${TR_VCS_REVISION}" STREQUAL "") - # Give up and check the source files - set(TR_VCS_REVISION 0) - set(TR_VCS_REVISION_RELIABLE OFF) - file(GLOB_RECURSE TR_ALL_SOURCES RELATIVE ${CMAKE_SOURCE_DIR} *.cc *.[chm] *.po) - foreach(F ${TR_ALL_SOURCES}) - file(STRINGS ${F} F_ID REGEX "\\$Id:") - if(F_ID MATCHES "\\$Id: [^ ]+ ([0-9]+) " AND CMAKE_MATCH_1 GREATER TR_VCS_REVISION) - set(TR_VCS_REVISION ${CMAKE_MATCH_1}) - endif() - endforeach() -endif() - -if("${TR_VCS_REVISION}" STREQUAL "") - set(TR_VCS_REVISION 0) +if(NOT "${TR_VCS_REVISION}" STREQUAL "") + file(WRITE "${TR_VCS_REVISION_FILE}" "${TR_VCS_REVISION}") else() - if(TR_VCS_REVISION_RELIABLE) - file(WRITE "${TR_VCS_REVISION_FILE}" "${TR_VCS_REVISION}") - else() - file(REMOVE "${TR_VCS_REVISION_FILE}") - endif() + set(TR_VCS_REVISION 0) + file(REMOVE "${TR_VCS_REVISION_FILE}") endif() set_property(GLOBAL PROPERTY USE_FOLDERS ON) diff --git a/update-version-h.sh b/update-version-h.sh index b93ba9490..78c9071e6 100755 --- a/update-version-h.sh +++ b/update-version-h.sh @@ -20,28 +20,23 @@ peer_id_prefix=`grep m4_define configure.ac | sed "s/[][)(]/,/g" | grep peer_id_ major_version=`echo ${user_agent_prefix} | awk -F . '{print $1}'` minor_version=`echo ${user_agent_prefix} | awk -F . '{print $2 + 0}'` +vcs_revision= vcs_revision_file=REVISION -vcs_revision_reliable=true if [ -n "$JENKINS_URL" -a -n "$VCS_REVISION" ]; then # Jenkins automated build, use the set environment variables to avoid # version mismatches between java's svn and command line's svn vcs_revision=$VCS_REVISION -elif [ -d ".svn" ] && type svnversion >/dev/null 2>&1; then - # If this is a svn tree, and svnversion is available in PATH, use it to - # grab the version. - vcs_revision=`svnversion -n . | cut -d: -f1 | cut -dM -f1 | cut -dS -f1` +elif [ -d ".git" ] && type git >/dev/null 2>&1; then + vcs_revision=`git rev-list --max-count=1 --abbrev-commit HEAD` elif [ -f "$vcs_revision_file" ]; then vcs_revision=`cat "$vcs_revision_file"` -else - # Give up and check the source files - vcs_revision=`awk '/\\$Id: /{ if ($4>i) i=$4 } END {print i}' */*.cc */*.[chm] */*.po` - vcs_revision_reliable=false fi -if $vcs_revision_reliable; then +if [ -n "$vcs_revision" ]; then [ -f "$vcs_revision_file" ] && [ "`cat "$vcs_revision_file"`" = "$vcs_revision" ] || echo "$vcs_revision" > "$vcs_revision_file" else + vcs_revision=0 rm -f "$vcs_revision_file" fi From 7f008edab67b3f263d98081baa72aef6572eca1f Mon Sep 17 00:00:00 2001 From: Mike Gelfand Date: Sun, 15 Jan 2017 00:54:56 +0300 Subject: [PATCH 21/22] Improve version.h file generation Store full revision hash in REVISION file. Write fixes 10 chars of revision hash to version.h. Use `#pragma once` in version.h. Support getting revision from TeamCity environment variable (similar to Jenkins). --- CMakeLists.txt | 17 ++++++++++------- libtransmission/version.h.in | 5 +---- update-version-h.sh | 16 +++++++++++----- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b8cbd29e..262ddfe77 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,16 +58,16 @@ endif() set(TR_VCS_REVISION_FILE "${CMAKE_SOURCE_DIR}/REVISION") -if(NOT "$ENV{JENKINS_URL}" STREQUAL "" AND NOT "$ENV{VCS_REVISION}" STREQUAL "") - # Jenkins automated build, use the set environment variables to avoid - # version mismatches between java's svn and command line's svn - set(TR_VCS_REVISION "$ENV{VCS_REVISION}") +if(NOT "$ENV{JENKINS_URL}" STREQUAL "" AND NOT "$ENV{GIT_COMMIT}" STREQUAL "") + set(TR_VCS_REVISION "$ENV{GIT_COMMIT}") +elseif(NOT "$ENV{TEAMCITY_PROJECT_NAME}" STREQUAL "" AND NOT "$ENV{BUILD_VCS_NUMBER}" STREQUAL "") + set(TR_VCS_REVISION "$ENV{BUILD_VCS_NUMBER}") elseif(IS_DIRECTORY ${CMAKE_SOURCE_DIR}/.git) find_package(Git) if(GIT_FOUND) execute_process( COMMAND - ${GIT_EXECUTABLE} rev-list --max-count=1 --abbrev-commit HEAD + ${GIT_EXECUTABLE} rev-list --max-count=1 HEAD WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE @@ -79,16 +79,19 @@ endif() if("${TR_VCS_REVISION}" STREQUAL "" AND EXISTS "${TR_VCS_REVISION_FILE}") file(READ "${TR_VCS_REVISION_FILE}" TR_VCS_REVISION) - string(STRIP "${TR_VCS_REVISION}" TR_VCS_REVISION) endif() +string(STRIP "${TR_VCS_REVISION}" TR_VCS_REVISION) + if(NOT "${TR_VCS_REVISION}" STREQUAL "") - file(WRITE "${TR_VCS_REVISION_FILE}" "${TR_VCS_REVISION}") + file(WRITE "${TR_VCS_REVISION_FILE}" "${TR_VCS_REVISION}\n") else() set(TR_VCS_REVISION 0) file(REMOVE "${TR_VCS_REVISION_FILE}") endif() +string(SUBSTRING "${TR_VCS_REVISION}" 0 10 TR_VCS_REVISION) + set_property(GLOBAL PROPERTY USE_FOLDERS ON) set(CURL_MINIMUM 7.15.4) diff --git a/libtransmission/version.h.in b/libtransmission/version.h.in index ce74a3d96..db4b5be23 100644 --- a/libtransmission/version.h.in +++ b/libtransmission/version.h.in @@ -1,5 +1,4 @@ -#ifndef TR_VERSION_H -#define TR_VERSION_H +#pragma once #define PEERID_PREFIX "${TR_PEER_ID_PREFIX}" #define USERAGENT_PREFIX "${TR_USER_AGENT_PREFIX}" @@ -14,5 +13,3 @@ #cmakedefine TR_BETA_RELEASE 1 #cmakedefine TR_NIGHTLY_RELEASE 1 #cmakedefine TR_STABLE_RELEASE 1 - -#endif /* TR_VERSION_H */ diff --git a/update-version-h.sh b/update-version-h.sh index 78c9071e6..80c06a4f6 100755 --- a/update-version-h.sh +++ b/update-version-h.sh @@ -23,16 +23,18 @@ minor_version=`echo ${user_agent_prefix} | awk -F . '{print $2 + 0}'` vcs_revision= vcs_revision_file=REVISION -if [ -n "$JENKINS_URL" -a -n "$VCS_REVISION" ]; then - # Jenkins automated build, use the set environment variables to avoid - # version mismatches between java's svn and command line's svn - vcs_revision=$VCS_REVISION +if [ -n "$JENKINS_URL" -a -n "$GIT_COMMIT" ]; then + vcs_revision=$GIT_COMMIT +elif [ -n "$TEAMCITY_PROJECT_NAME" -a -n "$BUILD_VCS_NUMBER" ]; then + vcs_revision=$BUILD_VCS_NUMBER elif [ -d ".git" ] && type git >/dev/null 2>&1; then - vcs_revision=`git rev-list --max-count=1 --abbrev-commit HEAD` + vcs_revision=`git rev-list --max-count=1 HEAD` elif [ -f "$vcs_revision_file" ]; then vcs_revision=`cat "$vcs_revision_file"` fi +vcs_revision=`echo $vcs_revision` + if [ -n "$vcs_revision" ]; then [ -f "$vcs_revision_file" ] && [ "`cat "$vcs_revision_file"`" = "$vcs_revision" ] || echo "$vcs_revision" > "$vcs_revision_file" else @@ -40,7 +42,11 @@ else rm -f "$vcs_revision_file" fi +vcs_revision=${vcs_revision:0:10} + cat > libtransmission/version.h.new << EOF +#pragma once + #define PEERID_PREFIX "${peer_id_prefix}" #define USERAGENT_PREFIX "${user_agent_prefix}" #define VCS_REVISION "${vcs_revision}" From 3c5870d4f52c91bf8f73846ef2b1da74bbb22693 Mon Sep 17 00:00:00 2001 From: Mike Gelfand Date: Tue, 23 Jan 2018 22:14:01 +0300 Subject: [PATCH 22/22] Use `head` instead of bash extension to get revision substring --- update-version-h.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/update-version-h.sh b/update-version-h.sh index 80c06a4f6..2679b466b 100755 --- a/update-version-h.sh +++ b/update-version-h.sh @@ -42,7 +42,7 @@ else rm -f "$vcs_revision_file" fi -vcs_revision=${vcs_revision:0:10} +vcs_revision=`echo ${vcs_revision} | head -c10` cat > libtransmission/version.h.new << EOF #pragma once