* Adium is the legal property of its developers, whose names are listed in the copyright file included * with this source distribution. * This program is free software; you can redistribute it and/or modify it under the terms of the GNU * General Public License as published by the Free Software Foundation; either version 2 of the License, * or (at your option) any later version. * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. * You should have received a copy of the GNU General Public License along with this program; if not, * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #import "ESFileTransferController.h" #import <Adium/AIAccountControllerProtocol.h> #import <Adium/AIChatControllerProtocol.h> #import <Adium/AIContactControllerProtocol.h> #import <Adium/AIInterfaceControllerProtocol.h> #import <Adium/AIMenuControllerProtocol.h> #import <Adium/AIToolbarControllerProtocol.h> #import <Adium/AIContactAlertsControllerProtocol.h> #import "ESFileTransferPreferences.h" #import "ESFileTransferProgressWindowController.h" #import "ESFileTransferRequestPromptController.h" #import <AIUtilities/AIDictionaryAdditions.h> #import <AIUtilities/AIFileManagerAdditions.h> #import <AIUtilities/AIMenuAdditions.h> #import <AIUtilities/AIStringAdditions.h> #import <AIUtilities/AIToolbarUtilities.h> #import <AIUtilities/AIObjectAdditions.h> #import <AIUtilities/AIImageAdditions.h> #import <Adium/AIAccount.h> #import <Adium/AIListContact.h> #import <Adium/AIListObject.h> #import <Adium/AIListGroup.h> #import "ESFileTransfer.h" #import <Adium/AIWindowController.h> #define SEND_FILE AILocalizedString(@"Send File",nil) #define SEND_FILE_WITH_ELLIPSIS [SEND_FILE stringByAppendingEllipsis] #define CONTACT AILocalizedString(@"Contact",nil) #define SEND_FILE_IDENTIFIER @"SendFile" #define FILE_TRANSFER_DEFAULT_PREFS @"FileTransferPrefs" #define SAFE_FILE_EXTENSIONS_SET [NSSet setWithObjects:@"jpg",@"jpeg",@"gif",@"png",@"tif",@"tiff",@"psd",@"pdf",@"txt",@"rtf",@"html",@"htm",@"swf",@"mp3",@"wma",@"wmv",@"ogg",@"ogm",@"mov",@"mpg",@"mpeg",@"m1v",@"m2v",@"mp4",@"avi",@"vob",@"avi",@"asx",@"asf",@"pls",@"m3u",@"rmp",@"aif",@"aiff",@"aifc",@"wav",@"wave",@"m4a",@"m4p",@"m4b",@"dmg",@"udif",@"ndif",@"dart",@"sparseimage",@"cdr",@"dvdr",@"iso",@"img",@"toast",@"rar",@"sit",@"sitx",@"bin",@"hqx",@"zip",@"gz",@"tgz",@"tar",@"bz",@"bz2",@"tbz",@"z",@"taz",@"uu",@"uue",@"colloquytranscript",@"torrent",@"AdiumIcon",@"AdiumSoundset",@"AdiumEmoticon",@"AdiumMessageStyle",nil] static ESFileTransferPreferences * preferences ; @interface ESFileTransferController () - ( void ) requestForSendingFileToListContact : ( AIListContact * ) listContact forWindow : ( NSWindow * ) theWindow ; - ( void ) configureFileTransferProgressWindow ; - ( void ) showProgressWindow: ( id ) sender ; - ( void ) showProgressWindowIfNotOpen: ( id ) sender ; - ( void ) _finishReceiveRequestForFileTransfer: ( ESFileTransfer * ) fileTransfer localFilename: ( NSString * ) localFilename ; - ( BOOL ) shouldOpenCompleteFileTransfer: ( ESFileTransfer * ) fileTransfer ; - ( IBAction ) contextualMenuSendFile: ( id ) sender ; - ( IBAction ) sendFileToSelectedContact: ( id ) sender ; @implementation ESFileTransferController if (( self = [ super init ])) { fileTransferArray = [[ NSMutableArray alloc ] init ]; safeFileExtensions = nil ; - ( void ) controllerDidLoad //Add our get info contextual menu item menuItem_sendFileContext = [[ NSMenuItem alloc ] initWithTitle : SEND_FILE_WITH_ELLIPSIS target : self action : @selector ( contextualMenuSendFile : ) [ adium . menuController addContextualMenuItem : menuItem_sendFileContext toLocation : Context_Contact_Action ]; //Register the events we generate NSObject < AIContactAlertsController > * contactAlertsController = adium . contactAlertsController ; [ contactAlertsController registerEventID : FILE_TRANSFER_REQUEST withHandler : self inGroup : AIFileTransferEventHandlerGroup globalOnly : YES ]; [ contactAlertsController registerEventID : FILE_TRANSFER_WAITING_REMOTE withHandler : self inGroup : AIFileTransferEventHandlerGroup globalOnly : YES ]; [ contactAlertsController registerEventID : FILE_TRANSFER_BEGAN withHandler : self inGroup : AIFileTransferEventHandlerGroup globalOnly : YES ]; [ contactAlertsController registerEventID : FILE_TRANSFER_CANCELLED withHandler : self inGroup : AIFileTransferEventHandlerGroup globalOnly : YES ]; [ contactAlertsController registerEventID : FILE_TRANSFER_COMPLETE withHandler : self inGroup : AIFileTransferEventHandlerGroup globalOnly : YES ]; [ contactAlertsController registerEventID : FILE_TRANSFER_FAILED withHandler : self inGroup : AIFileTransferEventHandlerGroup globalOnly : NO ]; //Install the Send File menu item menuItem_sendFile = [[ NSMenuItem alloc ] initWithTitle : SEND_FILE_WITH_ELLIPSIS target : self action : @selector ( sendFileToSelectedContact : ) [ menuItem_sendFile setKeyEquivalentModifierMask : ( NSCommandKeyMask | NSShiftKeyMask )]; [ adium . menuController addMenuItem : menuItem_sendFile toLocation : LOC_Contact_Action ]; //Add our "Send File" toolbar item NSToolbarItem * toolbarItem ; toolbarItem = [ AIToolbarUtilities toolbarItemWithIdentifier : SEND_FILE_IDENTIFIER toolTip : AILocalizedString ( @"Send a file" , "Tooltip for the Send File toolbar item" ) settingSelector : @selector ( setImage : ) itemContent :[ NSImage imageNamed : @"msg-send-file" forClass : [ self class ] loadLazily : YES ] action : @selector ( sendFileToSelectedContact : ) [ adium . toolbarController registerToolbarItem : toolbarItem forToolbarType : @"ListObject" ]; //Register our default preferences [ adium . preferenceController registerDefaults : [ NSDictionary dictionaryNamed : FILE_TRANSFER_DEFAULT_PREFS forGroup : PREF_GROUP_FILE_TRANSFER ]; [ adium . preferenceController registerPreferenceObserver : self forGroup : PREF_GROUP_FILE_TRANSFER ]; preferences = [( ESFileTransferPreferences * )[ ESFileTransferPreferences preferencePane ] retain ]; //Set up the file transfer progress window [ self configureFileTransferProgressWindow ]; - ( void ) controllerWillClose [ adium . preferenceController unregisterPreferenceObserver : self ]; [ safeFileExtensions release ]; safeFileExtensions = nil ; [ fileTransferArray release ]; fileTransferArray = nil ; #pragma mark Access to file transfer objects - ( ESFileTransfer * ) newFileTransferWithContact: ( AIListContact * ) inContact forAccount: ( AIAccount * ) inAccount type: ( AIFileTransferType ) t ESFileTransfer * fileTransfer ; fileTransfer = [ ESFileTransfer fileTransferWithContact : inContact [ fileTransferArray addObject : fileTransfer ]; [ fileTransfer setStatus : Not_Started_FileTransfer ]; //Wait until the next run loop to inform observers of the new file transfer object; //this way the code which requested a new ESFileTransfer has time to configure it before we //dispaly information to the user [[ NSNotificationCenter defaultCenter ] performSelector : @selector ( postNotificationName : object : ) withObject : FileTransfer_NewFileTransfer - ( NSUInteger ) activeTransferCount for ( t in fileTransferArray ) { AIFileTransferStatus status = [ t status ]; if (( status == Unknown_Status_FileTransfer ) || ( status == Not_Started_FileTransfer ) || ( status == Checksumming_Filetransfer ) || ( status == Waiting_on_Remote_User_FileTransfer ) || ( status == Connecting_FileTransfer ) || ( status == Accepted_FileTransfer ) || ( status == In_Progress_FileTransfer )) - ( NSArray * ) fileTransferArray return fileTransferArray ; //Remove a file transfer from our array. - ( void ) _removeFileTransfer: ( ESFileTransfer * ) fileTransfer [ fileTransferArray removeObject : fileTransfer ]; #pragma mark Sending and receiving //Sent by an account when it gets a request for us to receive a file; prompt the user for a save location - ( void ) receiveRequestForFileTransfer: ( ESFileTransfer * ) fileTransfer AIListContact * listContact = [ fileTransfer contact ]; NSString * localFilename = nil ; [ fileTransfer setFileTransferType : Incoming_FileTransfer ]; [ adium . contactAlertsController generateEvent : FILE_TRANSFER_REQUEST forListObject : listContact previouslyPerformedActionIDs : nil ]; if (( autoAcceptType == AutoAccept_All ) || (( autoAcceptType == AutoAccept_FromContactList ) && [ listContact isIntentionallyNotAStranger ])) { NSString * preferredDownloadFolder = [ adium . preferenceController userPreferredDownloadFolder ]; NSString * remoteFilename = [ fileTransfer remoteFilename ]; //If the incoming file would become hidden, prefix it with an underscore so it is visible. if ([ remoteFilename hasPrefix : @"." ]) remoteFilename = [ @"_" stringByAppendingString : remoteFilename ]; //If we should autoaccept, determine the local filename and proceed to accept the request. localFilename = [ preferredDownloadFolder stringByAppendingPathComponent : remoteFilename ]; [ self _finishReceiveRequestForFileTransfer : fileTransfer localFilename :[[ NSFileManager defaultManager ] uniquePathForPath : localFilename ]]; [ ESFileTransferRequestPromptController displayPromptForFileTransfer : fileTransfer selector : @selector ( _finishReceiveRequestForFileTransfer : localFilename : )]; * @brief Finish the receive request process * Called by either ESFileTransferRequestPromptController or self, this method is the last step in accepting or * refusing a request to be sent a file. * @param fileTransfer The file transfer in question * @param localFilename Full path at which to save the file. If anything exists at this path it will be overwritten without further confirmation. Pass nil to deny the transfer. - ( void ) _finishReceiveRequestForFileTransfer: ( ESFileTransfer * ) fileTransfer localFilename: ( NSString * ) localFilename if ([ fileTransfer isStopped ]) //if it's been canceled while we were busy asking the user stuff, ignore it [ fileTransfer setLocalFilename : localFilename ]; [ fileTransfer setStatus : Accepted_FileTransfer ]; [( AIAccount < AIAccount_Files > * ) fileTransfer . account acceptFileTransferRequest : fileTransfer ]; if ( showProgressWindow ) { [ self showProgressWindowIfNotOpen : nil ]; [( AIAccount < AIAccount_Files > * ) fileTransfer . account rejectFileReceiveRequest : fileTransfer ]; [ fileTransfer setStatus : Cancelled_Local_FileTransfer ]; - ( NSOpenPanel * ) sendFilePanelForListContact: ( AIListContact * ) listContact NSOpenPanel * openPanel = [ NSOpenPanel openPanel ]; [ openPanel setTitle : [ NSString stringWithFormat : AILocalizedString ( @"Send File to %@" , nil ), listContact . displayName ]]; [ openPanel setCanChooseDirectories : YES ]; [ openPanel setResolvesAliases : YES ]; [ openPanel setAllowsMultipleSelection : YES ]; [ openPanel setPrompt : AILocalizedStringFromTable ( @"Send" , @"Buttons" , nil )]; //Prompt the user for the file to send via an Open File dialogue - ( void ) requestForSendingFileToListContact: ( AIListContact * ) listContact [ self requestForSendingFileToListContact : listContact forWindow : nil ]; - ( void ) requestForSendingFileToListContact: ( AIListContact * ) listContact forWindow: ( NSWindow * ) theWindow NSOpenPanel * openPanel = [ self sendFilePanelForListContact : listContact ]; id handler = ^ ( NSInteger result ) { if ( result == NSFileHandlingPanelOKButton ) { for ( NSURL * url in openPanel . URLs ) { [ self sendFile : url . path toListContact : listContact ]; [ openPanel beginSheetModalForWindow : theWindow completionHandler : handler ]; [ openPanel beginWithCompletionHandler : handler ]; - ( NSString * ) pathToArchiveOfFolder: ( NSString * ) inPath NSString * pathToArchive = nil ; NSFileManager * defaultManager = [ NSFileManager defaultManager ]; //Desired folder: /private/tmp/$UID/`uuidgen` tmpDir = [ NSTemporaryDirectory () stringByAppendingPathComponent : [[ NSProcessInfo processInfo ] globallyUniqueString ]]; NSString * launchPath = [[[ @"/" stringByAppendingPathComponent : @"usr" ] stringByAppendingPathComponent : @"bin" ] stringByAppendingPathComponent : @"zip" ]; //Proceed only if /usr/bin/zip exists if ([ defaultManager fileExistsAtPath : launchPath ]) { NSString * folderName = [ inPath lastPathComponent ]; //Ensure our temporary directory exists [it never will the first time this method is called] [ defaultManager createDirectoryAtPath : tmpDir withIntermediateDirectories : YES attributes : nil error : NULL ]; pathToArchive = [[ NSFileManager defaultManager ] uniquePathForPath : [ tmpDir stringByAppendingPathComponent : [ folderName stringByAppendingPathExtension : @"zip" ]]]; arguments = [ NSArray arrayWithObjects : @"-r" , //we'll want to store recursively @"-1" , //use the fastest level of compression that isn't storage; the user can compress manually to do better @"-y" , //store symbolic links as such instead of resolving the link pathToArchive , //output to our destination name folderName , //store the folder AILog ( @"-[ESFileTransferController pathToArchiveOfFolder:]: Will launch %@ with arguments %@ in directory %@" , launchPath , arguments , [ inPath stringByDeletingLastPathComponent ]); zipTask = [[ NSTask alloc ] init ]; [ zipTask setLaunchPath : launchPath ]; [ zipTask setArguments : arguments ]; [ zipTask setCurrentDirectoryPath : [ inPath stringByDeletingLastPathComponent ]]; success = (([ zipTask terminationStatus ] == -1 ) || ([ zipTask terminationStatus ] == 0 )); if ( ! success ) pathToArchive = nil ; AILog ( @"-[ESFileTransferController pathToArchiveOfFolder:]: Success %i (%i), so pathToArchive is %@" , success , [ zipTask terminationStatus ], pathToArchive ); //Initiate sending a file at a specified path to listContact - ( void ) sendFile: ( NSString * ) inPath toListContact: ( AIListContact * ) listContact if (( account = [ adium . accountController preferredAccountForSendingContentType : CONTENT_FILE_TRANSFER_TYPE toContact : listContact ]) && [ account conformsToProtocol : @ protocol ( AIAccount_Files )]) { NSFileManager * defaultManager = [ NSFileManager defaultManager ]; //Resolve any alias we're passed if necessary inPath = [ defaultManager pathByResolvingAlias : inPath ]; if ([ defaultManager fileExistsAtPath : inPath isDirectory :& isDir ]) { //If we get a directory and the account we're sending from doesn't support folder transfers ! [( AIAccount < AIAccount_Files > * ) account canSendFolders ]) { inPath = [ self pathToArchiveOfFolder : inPath ]; long fileSize = [[[ defaultManager attributesOfItemAtPath : inPath error : NULL ] objectForKey : NSFileSize ] longValue ]; ESFileTransfer * fileTransfer ; //Set up a fileTransfer object fileTransfer = [ self newFileTransferWithContact : listContact type : Outgoing_FileTransfer ]; [ fileTransfer setLocalFilename : inPath ]; [ fileTransfer setSize : fileSize ]; //The fileTransfer object should now have everything the account needs to begin transferring [( AIAccount < AIAccount_Files > * ) account beginSendOfFileTransfer : fileTransfer ]; if ( showProgressWindow ) { [ self showProgressWindowIfNotOpen : nil ]; //XXX Show a warning message rather than just beeping //Menu or context menu item for sending a file was selected - possible only when a listContact is selected - ( IBAction ) sendFileToSelectedContact: ( id ) sender //Get the "selected" list object (contact list or message window) AIListObject * selectedObject ; AIListContact * listContact = nil ; selectedObject = adium . interfaceController . selectedListObject ; if ([ selectedObject isKindOfClass : [ AIListContact class ]]) { listContact = [ adium . contactController preferredContactForContentType : CONTENT_FILE_TRANSFER_TYPE forListContact :( AIListContact * ) selectedObject ]; if ( ! [ sender isKindOfClass : [ NSToolbarItem class ]] || ! listContact ) { [ self requestForSendingFileToListContact : listContact ]; AIChat * theChat = [ adium . chatController existingChatWithContact : listContact ]; NSWindow * theWindow = [ adium . interfaceController windowForChat : theChat ]; [ self requestForSendingFileToListContact : listContact forWindow : theWindow ]; //Prompt for a new contact with the current tab's name - ( IBAction ) contextualMenuSendFile: ( id ) sender AIListObject * selectedObject = adium . menuController . currentContextMenuObject ; AIListContact * listContact = [ adium . contactController preferredContactForContentType : CONTENT_FILE_TRANSFER_TYPE forListContact :( AIListContact * ) selectedObject ]; [ NSApp activateIgnoringOtherApps : YES ]; [ self requestForSendingFileToListContact : listContact ]; #pragma mark Status updates - ( void ) fileTransfer: ( ESFileTransfer * ) fileTransfer didSetStatus: ( AIFileTransferStatus ) status case Checksumming_Filetransfer : [ adium . contactAlertsController generateEvent : FILE_TRANSFER_CHECKSUMMING forListObject :[ fileTransfer contact ] previouslyPerformedActionIDs : nil ]; if ( showProgressWindow ) { [ self showProgressWindowIfNotOpen : nil ]; case Waiting_on_Remote_User_FileTransfer : [ adium . contactAlertsController generateEvent : FILE_TRANSFER_WAITING_REMOTE forListObject :[ fileTransfer contact ] previouslyPerformedActionIDs : nil ]; if ( showProgressWindow ) { [ self showProgressWindowIfNotOpen : nil ]; case Accepted_FileTransfer : [ adium . contactAlertsController generateEvent : FILE_TRANSFER_BEGAN forListObject :[ fileTransfer contact ] previouslyPerformedActionIDs : nil ]; if ( showProgressWindow ) { [ self showProgressWindowIfNotOpen : nil ]; case Complete_FileTransfer : [ adium . contactAlertsController generateEvent : FILE_TRANSFER_COMPLETE forListObject :[ fileTransfer contact ] previouslyPerformedActionIDs : nil ]; //The file is complete; if we are supposed to automatically open safe files and this is one, open it if ([ self shouldOpenCompleteFileTransfer : fileTransfer ]) { [[ NSDistributedNotificationCenter defaultCenter ] postNotificationName : @"com.apple.DownloadFileFinished" object : fileTransfer . localFilename ]; case Cancelled_Remote_FileTransfer : [ adium . contactAlertsController generateEvent : FILE_TRANSFER_CANCELLED forListObject :[ fileTransfer contact ] previouslyPerformedActionIDs : nil ]; case Failed_FileTransfer : [ adium . contactAlertsController generateEvent : FILE_TRANSFER_FAILED forListObject :[ fileTransfer contact ] previouslyPerformedActionIDs : nil ]; - ( BOOL ) shouldOpenCompleteFileTransfer: ( ESFileTransfer * ) fileTransfer ([ fileTransfer fileTransferType ] == Incoming_FileTransfer )) { if ( ! safeFileExtensions ) safeFileExtensions = [ SAFE_FILE_EXTENSIONS_SET retain ]; shouldOpen = [ safeFileExtensions containsObject : [[[ fileTransfer localFilename ] pathExtension ] lowercaseString ]]; - ( BOOL ) validateMenuItem: ( NSMenuItem * ) menuItem AIListContact * listContact = nil ; if ( menuItem == menuItem_sendFile ) { AIListObject * selectedObject = adium . interfaceController . selectedListObject ; if ( selectedObject && [ selectedObject isKindOfClass : [ AIListContact class ]]) { listContact = [ adium . contactController preferredContactForContentType : CONTENT_FILE_TRANSFER_TYPE forListContact :( AIListContact * ) selectedObject ]; return listContact != nil ; } else if ( menuItem == menuItem_sendFileContext ) { AIListObject * selectedObject = adium . menuController . currentContextMenuObject ; if ( selectedObject && [ selectedObject isKindOfClass : [ AIListContact class ]]) { listContact = [ adium . contactController preferredContactForContentType : CONTENT_FILE_TRANSFER_TYPE forListContact :( AIListContact * ) selectedObject ]; return listContact != nil ; } else if ( menuItem == menuItem_showFileTransferProgress ) { - (BOOL)validateToolbarItem:(NSToolbarItem *)theItem AIListContact *listContact = nil; AIListObject *selectedObject = adium.interfaceController.selectedListObject; if (selectedObject && [selectedObject isKindOfClass:[AIListContact class]]) { listContact = [adium.contactController preferredContactForContentType:CONTENT_FILE_TRANSFER_TYPE forListContact:(AIListContact *)selectedObject]; return listContact != nil; #pragma mark File transfer progress window - ( void ) configureFileTransferProgressWindow //Add the File Transfer Progress window menuItem menuItem_showFileTransferProgress = [[ NSMenuItem alloc ] initWithTitle : AILocalizedString ( @"File Transfers" , nil ) action : @selector ( showProgressWindow : ) [ menuItem_showFileTransferProgress setKeyEquivalentModifierMask : ( NSCommandKeyMask | NSAlternateKeyMask )]; [ adium . menuController addMenuItem : menuItem_showFileTransferProgress toLocation : LOC_Window_Auxiliary ]; //Show the file transfer progress window - ( void ) showProgressWindow: ( id ) sender [ ESFileTransferProgressWindowController showFileTransferProgressWindow ]; - ( void ) showProgressWindowIfNotOpen: ( id ) sender [ ESFileTransferProgressWindowController showFileTransferProgressWindowIfNotOpen ]; - ( void ) preferencesChangedForGroup: ( NSString * ) group key: ( NSString * ) key object :( AIListObject * ) object preferenceDict: ( NSDictionary * ) prefDict firstTime: ( BOOL ) firstTime autoAcceptType = [[ prefDict objectForKey : KEY_FT_AUTO_ACCEPT ] intValue ]; autoOpenSafe = [[ prefDict objectForKey : KEY_FT_AUTO_OPEN_SAFE ] boolValue ]; //If we created a safe file extensions set and no longer need it, desroy it if ( ! autoOpenSafe && safeFileExtensions ) { [ safeFileExtensions release ]; safeFileExtensions = nil ; showProgressWindow = [[ prefDict objectForKey : KEY_FT_SHOW_PROGRESS_WINDOW ] boolValue ]; #pragma mark AIEventHandler - ( NSString * ) shortDescriptionForEventID: ( NSString * ) eventID if ([ eventID isEqualToString : FILE_TRANSFER_FAILED ]) { description = AILocalizedString ( @"File transfer fails" , nil ); - ( NSString * ) globalShortDescriptionForEventID: ( NSString * ) eventID if ([ eventID isEqualToString : FILE_TRANSFER_REQUEST ]) { description = AILocalizedString ( @"File transfer requested" , nil ); } else if ([ eventID isEqualToString : FILE_TRANSFER_CHECKSUMMING ]) { description = AILocalizedString ( @"File is checksummed before sending" , nil ); } else if ([ eventID isEqualToString : FILE_TRANSFER_WAITING_REMOTE ]) { description = AILocalizedString ( @"File transfer being offered to other side" , nil ); } else if ([ eventID isEqualToString : FILE_TRANSFER_BEGAN ]) { description = AILocalizedString ( @"File transfer begins" , nil ); } else if ([ eventID isEqualToString : FILE_TRANSFER_CANCELLED ]) { description = AILocalizedString ( @"File transfer cancelled by the other side" , nil ); } else if ([ eventID isEqualToString : FILE_TRANSFER_COMPLETE ]) { description = AILocalizedString ( @"File transfer completed successfully" , nil ); } else if ([ eventID isEqualToString : FILE_TRANSFER_FAILED ]) { description = AILocalizedString ( @"File transfer failed" , nil ); //Evan: This exists because old X(tras) relied upon matching the description of event IDs, and I don't feel like making //a converter for old packs. If anyone wants to fix this situation, please feel free :) //XXX-fix this for the above comment. - ( NSString * ) englishGlobalShortDescriptionForEventID: ( NSString * ) eventID if ([ eventID isEqualToString : FILE_TRANSFER_REQUEST ]) { description = @"File Transfer Request" ; } else if ([ eventID isEqualToString : FILE_TRANSFER_CHECKSUMMING ]) { description = @"File Checksumming for Sending" ; } else if ([ eventID isEqualToString : FILE_TRANSFER_WAITING_REMOTE ]) { description = @"File Transfer Being Offered to Remote User" ; } else if ([ eventID isEqualToString : FILE_TRANSFER_BEGAN ]) { description = @"File Transfer Began" ; } else if ([ eventID isEqualToString : FILE_TRANSFER_CANCELLED ]) { //Canceled, not Cancelled as we use elsewhere in Adium, for historical reasons. Both are valid spellings. description = @"File Transfer Canceled Remotely" ; } else if ([ eventID isEqualToString : FILE_TRANSFER_COMPLETE ]) { description = @"File Transfer Complete" ; } else if ([ eventID isEqualToString : FILE_TRANSFER_FAILED ]) { description = @"File transfer failed" ; - ( NSString * ) longDescriptionForEventID: ( NSString * ) eventID forListObject: ( AIListObject * ) listObject if ([ eventID isEqualToString : FILE_TRANSFER_FAILED ]) { format = AILocalizedString ( @"When a file transfer with %@ fails" , nil ); name = ([ listObject isKindOfClass : [ AIListGroup class ]] ? [ NSString stringWithFormat : AILocalizedString ( @"a member of %@" , nil ), listObject . displayName ] : description = [ NSString stringWithFormat : format , name ]; if ([ eventID isEqualToString : FILE_TRANSFER_REQUEST ]) { description = AILocalizedString ( @"When a file transfer is requested" , nil ); } else if ([ eventID isEqualToString : FILE_TRANSFER_CHECKSUMMING ]) { description = AILocalizedString ( @"When a file is checksummed prior to sending" , nil ); } else if ([ eventID isEqualToString : FILE_TRANSFER_WAITING_REMOTE ]) { description = AILocalizedString ( @"When a file transfer is offered to a remote user" , nil ); } else if ([ eventID isEqualToString : FILE_TRANSFER_BEGAN ]) { description = AILocalizedString ( @"When a file transfer begins" , nil ); } else if ([ eventID isEqualToString : FILE_TRANSFER_CANCELLED ]) { description = AILocalizedString ( @"When a file transfer is cancelled remotely" , nil ); } else if ([ eventID isEqualToString : FILE_TRANSFER_COMPLETE ]) { description = AILocalizedString ( @"When a file transfer is completed successfully" , nil ); } else if ([ eventID isEqualToString : FILE_TRANSFER_FAILED ]) { description = AILocalizedString ( @"When a file transfer fails" , nil ); - ( NSString * ) naturalLanguageDescriptionForEventID: ( NSString * ) eventID listObject :( AIListObject * ) listObject includeSubject :( BOOL ) includeSubject NSString * description = nil ; NSString * displayName , * displayFilename ; ESFileTransfer * fileTransfer ; NSParameterAssert ([ userInfo isKindOfClass : [ ESFileTransfer class ]]); fileTransfer = ( ESFileTransfer * ) userInfo ; displayName = listObject . displayName ; displayFilename = [ fileTransfer displayFilename ]; if ([ eventID isEqualToString : FILE_TRANSFER_REQUEST ]) { //Should only happen for an incoming transfer format = AILocalizedString ( @"%@ requests to send you %@" , "A person is wanting to send you a file. The first %@ is a name; the second %@ is the filename of the file being sent." ); } else if ([ eventID isEqualToString : FILE_TRANSFER_WAITING_REMOTE ]) { //Should only happen for outgoing file transfers format = AILocalizedString ( @"Offering to send %@ to %@" , "You are offering to send a file to a remote user. The first %@ is the filename of the file being sent; the second %@ is the recipient of the file being sent." ); } else if ([ eventID isEqualToString : FILE_TRANSFER_BEGAN ]) { if ([ fileTransfer fileTransferType ] == Incoming_FileTransfer ) { format = AILocalizedString ( @"%@ began sending you %@" , "A person began sending you a file. The first %@ is a name; the second %@ is the filename of the file being sent." ); format = AILocalizedString ( @"%@ began receiving %@" , "A person began receiving a file from you. The first %@ is the recipient of the file; the second %@ is the filename of the file being sent." ); } else if ([ eventID isEqualToString : FILE_TRANSFER_CANCELLED ]) { format = AILocalizedString ( @"%@ cancelled the transfer of %@" , "The other contact cancelled a file transfer in progress. The first %@ is the recipient of the file; the second %@ is the filename of the file being sent." ); } else if ([ eventID isEqualToString : FILE_TRANSFER_COMPLETE ]) { if ([ fileTransfer fileTransferType ] == Incoming_FileTransfer ) { format = AILocalizedString ( @"%@ sent you %@" , "First placeholder is a name; second is a filename" ); format = AILocalizedString ( @"%@ received %@" , "First placeholder is a name; second is a filename" ); } else if ([ eventID isEqualToString : FILE_TRANSFER_FAILED ]) { if ([ fileTransfer fileTransferType ] == Incoming_FileTransfer ) { format = AILocalizedString ( @"%@'s transfer of %@ failed" , "First placeholder is a name; second is a filename" ); format = AILocalizedString ( @"Your transfer to %@ of %@ failed" , "First placeholder is a name; second is a filename" ); description = [ NSString stringWithFormat : format , displayName , displayFilename ]; if ([ eventID isEqualToString : FILE_TRANSFER_REQUEST ]) { //Should only happen for an incoming transfer format = AILocalizedString ( @"requests to send you %@" , "%@ is a filename of a file being sent" ); } else if ([ eventID isEqualToString : FILE_TRANSFER_WAITING_REMOTE ]) { //Should only happen for an outgoing transfer format = AILocalizedString ( @"offers to send %@" , "%@ is a filename of a file being sent" ); } else if ([ eventID isEqualToString : FILE_TRANSFER_BEGAN ]) { if ([ fileTransfer fileTransferType ] == Incoming_FileTransfer ) { format = AILocalizedString ( @"began sending you %@" , "%@ is a filename of a file being sent" ); format = AILocalizedString ( @"began receiving %@" , "%@ is a filename of a file being sent" ); } else if ([ eventID isEqualToString : FILE_TRANSFER_CANCELLED ]) { format = AILocalizedString ( @"cancelled the transfer of %@" , "%@ is a filename of a file being sent" ); } else if ([ eventID isEqualToString : FILE_TRANSFER_COMPLETE ]) { if ([ fileTransfer fileTransferType ] == Incoming_FileTransfer ) { format = AILocalizedString ( @"sent you %@" , "%@ is a filename of a file being sent" ); format = AILocalizedString ( @"received %@" , "%@ is a filename of a file being sent" ); } else if ([ eventID isEqualToString : FILE_TRANSFER_FAILED ]) { if ([ fileTransfer fileTransferType ] == Incoming_FileTransfer ) { format = AILocalizedString ( @"failed to send you %@" , "%@ is a filename of a file being sent" ); format = AILocalizedString ( @"failed to receive %@" , "%@ is a filename of a file being sent" ); description = [ NSString stringWithFormat : format , displayFilename ]; - ( NSImage * ) imageForEventID: ( NSString * ) eventID static NSImage * eventImage = nil ; if ( ! eventImage ) eventImage = [[ NSImage imageNamed : @"pref-file-transfer" forClass : [ self class ]] retain ]; - ( NSString * ) descriptionForCombinedEventID: ( NSString * ) eventID forListObject :( AIListObject * ) listObject withCount :( NSUInteger ) count if ([ eventID isEqualToString : FILE_TRANSFER_REQUEST ]) { format = AILocalizedString ( @"%u incoming file transfers" , nil ); } else if ([ eventID isEqualToString : FILE_TRANSFER_CHECKSUMMING ]) { format = AILocalizedString ( @"%u files being checksummed prior to sending" , nil ); } else if ([ eventID isEqualToString : FILE_TRANSFER_WAITING_REMOTE ]) { format = AILocalizedString ( @"%u files offered to send" , nil ); } else if ([ eventID isEqualToString : FILE_TRANSFER_BEGAN ]) { format = AILocalizedString ( @"%u files began transferring" , nil ); } else if ([ eventID isEqualToString : FILE_TRANSFER_CANCELLED ]) { format = AILocalizedString ( @"%u files cancelled remotely" , nil ); } else if ([ eventID isEqualToString : FILE_TRANSFER_COMPLETE ]) { format = AILocalizedString ( @"%u files completed successfully" , nil ); } else if ([ eventID isEqualToString : FILE_TRANSFER_FAILED ]) { format = AILocalizedString ( @"%u file transfers failed" , nil ); return format ? [ NSString stringWithFormat : format , count ] : @"" ; #pragma mark Strings for sizes #define ZERO_BYTES AILocalizedString(@"Zero bytes", "no file size") - ( NSString * ) stringForSize: ( unsigned long long ) inSize if ( inSize == 0. ) ret = ZERO_BYTES ; else if ( inSize > 0. && inSize < 1024. ) ret = [ NSString stringWithFormat : AILocalizedString ( @"%llu bytes" , "file size measured in bytes" ), inSize ]; else if ( inSize >= 1024. && inSize < pow ( 1024. , 2. ) ) ret = [ NSString stringWithFormat : AILocalizedString ( @"%.1f KB" , "file size measured in kilobytes" ), ( inSize / 1024. )]; else if ( inSize >= pow ( 1024. , 2. ) && inSize < pow ( 1024. , 3. ) ) ret = [ NSString stringWithFormat : AILocalizedString ( @"%.2f MB" , "file size measured in megabytes" ), ( inSize / pow ( 1024. , 2. ) )]; else if ( inSize >= pow ( 1024. , 3. ) && inSize < pow ( 1024. , 4. ) ) ret = [ NSString stringWithFormat : AILocalizedString ( @"%.3f GB" , "file size measured in gigabytes" ), ( inSize / pow ( 1024. , 3. ) )]; else if ( inSize >= pow ( 1024. , 4. ) ) ret = [ NSString stringWithFormat : AILocalizedString ( @"%.4f TB" , "file size measured in terabytes" ), ( inSize / pow ( 1024. , 4. ) )]; if ( ! ret ) ret = ZERO_BYTES ; - ( NSString * ) stringForSize: ( unsigned long long ) inSize of: ( unsigned long long ) totalSize ofString: ( NSString * ) totalSizeString } else if ( inSize > 0. && inSize < 1024. ) { if ( totalSize > 0. && totalSize < 1024. ) { ret = [ NSString stringWithFormat : AILocalizedString ( @"%llu of %llu bytes" , "file sizes both measured in bytes" ), inSize , totalSize ]; ret = [ NSString stringWithFormat : AILocalizedString ( @"%llu bytes of %@" , "file size measured in bytes out of some other measurement" ), inSize , totalSizeString ]; } else if ( inSize >= 1024. && inSize < pow ( 1024. , 2. ) ) { if ( totalSize >= 1024. && totalSize < pow ( 1024. , 2. ) ) { ret = [ NSString stringWithFormat : AILocalizedString ( @"%.1f of %.1f KB" , "file sizes both measured in kilobytes" ), ( inSize / 1024. ), ( totalSize / 1024. )]; ret = [ NSString stringWithFormat : AILocalizedString ( @"%.1f KB of %@" , "file size measured in kilobytes out of some other measurement" ), ( inSize / 1024. ), totalSizeString ]; else if ( inSize >= pow ( 1024. , 2. ) && inSize < pow ( 1024. , 3. ) ) { if ( totalSize >= pow ( 1024. , 2. ) && totalSize < pow ( 1024. , 3. ) ) { ret = [ NSString stringWithFormat : AILocalizedString ( @"%.2f of %.2f MB" , "file sizes both measured in megabytes" ), ( inSize / pow ( 1024. , 2. ) ), ( totalSize / pow ( 1024. , 2. ) )]; ret = [ NSString stringWithFormat : AILocalizedString ( @"%.2f MB of %@" , "file size measured in megabytes out of some other measurement" ), ( inSize / pow ( 1024. , 2. ) ), totalSizeString ]; else if ( inSize >= pow ( 1024. , 3. ) && inSize < pow ( 1024. , 4. ) ) { if ( totalSize >= pow ( 1024. , 3. ) && totalSize < pow ( 1024. , 4. ) ) { ret = [ NSString stringWithFormat : AILocalizedString ( @"%.3f of %.3f GB" , "file sizes both measured in gigabytes" ), ( inSize / pow ( 1024. , 3. ) ), ( totalSize / pow ( 1024. , 3. ) )]; ret = [ NSString stringWithFormat : AILocalizedString ( @"%.3f GB of %@" , "file size measured in gigabytes out of some other measurement" ), ( inSize / pow ( 1024. , 3. ) ), totalSizeString ]; else if ( inSize >= pow ( 1024. , 4. ) ) { if ( totalSize >= pow ( 1024. , 4. ) ) { ret = [ NSString stringWithFormat : AILocalizedString ( @"%.4f of %.4f TB" , "file sizes both measured in terabytes" ), ( inSize / pow ( 1024. , 4. ) ), ( totalSize / pow ( 1024. , 4. ) )]; ret = [ NSString stringWithFormat : AILocalizedString ( @"%.4f TB of %@" , "file size measured in terabytes out of some other measurement" ), ( inSize / pow ( 1024. , 4. ) ), totalSizeString ]; if ( ! ret ) ret = ZERO_BYTES ;