2009-11-10 16:48:42 +01:00
//
// QuietUnrarAppDelegate . m
// QuietUnrar
//
// Created by Robert McGovern on 2009 / 09 / 06.
// Copyright 2009 Tarasis . All rights reserved .
//
# import < Carbon / Carbon . h >
# import "QuietUnrarAppDelegate.h"
# import "libunrar/dll.hpp"
2009-11-16 15:00:11 +01:00
# pragma mark Callbacks
// Declartions that are not to be part of the public interface .
// The two methods are for callbacks passed to the RAR library
2009-11-15 18:50:12 +01:00
QuietUnrarAppDelegate * quietUnrar ;
2009-11-15 15:43:54 +01:00
2009-11-15 00:18:58 +01:00
int changeVolume ( char * volumeName , int mode ) ;
int callbackFunction ( UINT message , LPARAM userData , LPARAM parameterOne , LPARAM parameterTwo ) ;
2009-11-16 15:00:11 +01:00
// Called everytime a new volume ( part ) of the RAR is needed .
// mode will either be
// RAR_VOL _NOTIFY that just notifies us that the volume has changed
// RAR_VOL _ASK indicates that a volume is needed and the library is asking for it .
//
// in both case volumeName is that name of the volume ( for instance . r00 )
//
// Note in the event of a volume being missing , there is no way to indicate to the
// library that you have found it . You would need to block the copy , let the user find the
// volume , copy it to where the other volumes are and unblock to let the library
// continue processing
2009-11-15 00:18:58 +01:00
int changeVolume ( char * volumeName , int mode ) {
2009-11-15 15:43:54 +01:00
if ( mode = = RAR_VOL _ASK )
[ ( QuietUnrarAppDelegate * ) quietUnrar alertUserOfMissing : volumeName ] ;
2009-11-15 00:18:58 +01:00
}
2009-11-16 15:00:11 +01:00
// Multipurpose callback function that is called un changing a volume , when data is being processed
// and when a password is required . This is indicated by the message parameter
//
// UCM_CHANGEVOLUME sent when changing volumes
// UCM_PROCESSDATA sent as each file in the archive is being extracted in chunks , useful for progress bars
// UCM_NEEDPASSWORD sent when the library discovers a password is needed .
//
// The userData param is a pointer to something we supplied when the callback was registered . In my
// case I am passing in the pointer to the archive data so that the requestArchivePassword method
// can supply the password to the RAR library via RARSetPassword
//
// parameterOne & parameterTwo have different meanings depending on what message is passed .
2009-11-15 00:18:58 +01:00
int callbackFunction ( UINT message , LPARAM userData , LPARAM parameterOne , LPARAM parameterTwo ) {
2009-11-15 18:50:12 +01:00
if ( message = = UCM_NEEDPASSWORD ) {
2009-11-16 15:00:11 +01:00
NSString * password = [ ( QuietUnrarAppDelegate * ) quietUnrar requestArchivePassword ] ;
if ( password )
RARSetPassword ( ( HANDLE ) userData , ( char * ) [ password cStringUsingEncoding : NSISOLatin1StringEncoding ] ) ;
2009-11-15 18:50:12 +01:00
}
2009-11-15 00:18:58 +01:00
}
2009-11-16 15:00:11 +01:00
# pragma mark
2009-11-10 16:48:42 +01:00
@ implementation QuietUnrarAppDelegate
2009-11-15 18:50:12 +01:00
@ synthesize window , passwordView , passwordField ;
2009-11-10 16:48:42 +01:00
2009-11-14 23:12:16 +01:00
- ( void ) applicationWillFinishLaunching : ( NSNotification * ) notification {
2009-11-16 15:00:11 +01:00
// The following is used to determine is the left or right shift keys were depressed
// as the application was launched . Could be used to display a gui on Application start .
2009-11-10 16:48:42 +01:00
KeyMap map ;
GetKeys ( map ) ;
2009-11-16 15:00:11 +01:00
if ( KEYMAP_GET ( map , kVKC_Shift ) || KEYMAP_GET ( map , kVKC_rShift ) )
NSLog ( @ "Shift or Right Shift" ) ;
2009-11-10 16:48:42 +01:00
}
2009-11-14 23:12:16 +01:00
- ( void ) applicationDidFinishLaunching : ( NSNotification * ) aNotification {
2009-11-15 13:54:07 +01:00
// Having extracted our file or not , quit . Though should not if error is displayed .
[ [ NSApplication sharedApplication ] terminate : self ] ;
2009-11-10 16:48:42 +01:00
}
2009-11-16 15:00:11 +01:00
// Call one at a time for each file selected when app is run
2009-11-10 16:48:42 +01:00
- ( BOOL ) application : ( NSApplication * ) theApplication openFile : ( NSString * ) filename {
2009-11-15 18:50:12 +01:00
// NSLog ( @ "openFile: %@" , filename ) ;
2009-11-14 23:12:16 +01:00
2009-11-15 15:43:54 +01:00
[ self extractRarWith : filename ] ;
// Always return YES even if there is an error to avoid dialog indicating unable to
// handle files of type RAR if the archive is corrupt or part of it is missing
return YES ;
2009-11-10 16:48:42 +01:00
}
2009-11-16 15:00:11 +01:00
# pragma mark "Main"
2009-11-14 23:12:16 +01:00
- ( BOOL ) extractRarWith : ( NSString * ) filename {
2009-11-15 15:43:54 +01:00
quietUnrar = ( void * ) self ;
2009-11-14 23:12:16 +01:00
char commentBuffer [ BUF_LEN ] ;
BOOL extractionSuccessful = YES ;
// Determine the folder we should extract the archive to . This by default
// is the < folderContainingTheArchive > / < archiveNameWithPathExtension >
NSString * defaultFolderToExtractTo = [ filename stringByDeletingPathExtension ] ;
2009-11-15 00:18:58 +01:00
char * filenameCString = ( char * ) [ filename cStringUsingEncoding : NSISOLatin1StringEncoding ] ;
2009-11-14 23:12:16 +01:00
// Open the Archive for extraction , we set the open result to 3 so we can see it has changed
2009-11-15 00:18:58 +01:00
struct RAROpenArchiveData arcData = { filenameCString , RAR_OM _EXTRACT , 3 , & commentBuffer [ 0 ] , BUF_LEN , 0 , 0 } ;
2009-11-14 23:12:16 +01:00
HANDLE archive = RAROpenArchive ( & arcData ) ;
2009-11-15 18:50:12 +01:00
// NSLog ( @ "Opening Archive %s with result %d" , filenameCString , arcData . OpenResult ) ;
2009-11-14 23:12:16 +01:00
// set call backs for if password needed or need to change volume
2009-11-15 15:43:54 +01:00
RARSetChangeVolProc ( archive , & changeVolume ) ;
2009-11-15 18:50:12 +01:00
RARSetCallback ( archive , & callbackFunction , ( LPARAM ) archive ) ;
2009-11-14 23:12:16 +01:00
2009-11-15 00:18:58 +01:00
//
2009-11-15 13:54:07 +01:00
struct RARHeaderData headerData ;
NSString * lastExtractedFilename = @ "" ;
NSString * currentFilename ;
2009-11-14 23:12:16 +01:00
while ( RARReadHeader ( archive , & headerData ) ! = ERAR_END _ARCHIVE ) {
2009-11-15 18:50:12 +01:00
// NSLog ( @ "Attempting to extract %s to %@" , headerData . FileName , defaultFolderToExtractTo ) ;
2009-11-14 23:12:16 +01:00
2009-11-15 13:54:07 +01:00
int processResult = 0 ;
BOOL extractFile = YES ;
currentFilename = [ NSString stringWithCString : ( const char * ) headerData . FileName encoding : NSISOLatin1StringEncoding ] ;
NSFileManager * fileManager = [ NSFileManager defaultManager ] ;
if ( [ fileManager fileExistsAtPath : [ NSString stringWithFormat : @ "%@/%s" , defaultFolderToExtractTo , headerData . FileName ] ] ) {
// If we have already processed the file once and the user has told us to skip
// don ' t ask them again , even though we ' ve changed volumes . Otherwise
// ask the user what to do .
if ( [ lastExtractedFilename isEqualToString : currentFilename ] || ! [ self shouldFileBeReplaced : currentFilename ] ) {
extractFile = NO ;
}
}
// NSLog ( @ "Last filename %@, currentFilename %@, equality %d" , lastExtractedFilename , currentFilename , [ lastExtractedFilename isEqualToString : currentFilename ] ) ;
if ( extractFile ) {
2009-11-15 18:50:12 +01:00
// NSLog ( @ "...Extracting" ) ;
2009-11-15 13:54:07 +01:00
processResult = RARProcessFile ( archive , RAR_EXTRACT , ( char * ) [ defaultFolderToExtractTo cStringUsingEncoding : NSISOLatin1StringEncoding ] , NULL ) ;
} else {
2009-11-15 18:50:12 +01:00
// NSLog ( @ "...Skipping as already exists" ) ;
2009-11-15 13:54:07 +01:00
processResult = RARProcessFile ( archive , RAR_SKIP , NULL , NULL ) ;
// Curious behavior by the lib , you have SKIP a file number of times ( 4 in my test example ) before
// it is skipped . However if you extract it is only processed once .
}
2009-11-14 23:12:16 +01:00
2009-11-15 00:18:58 +01:00
if ( processResult ! = 0 ) {
2009-11-15 13:54:07 +01:00
NSLog ( @ "Error: Process Result was %d" , processResult ) ;
2009-11-14 23:12:16 +01:00
extractionSuccessful = NO ;
2009-11-15 13:54:07 +01:00
break ;
2009-11-14 23:12:16 +01:00
// DISPLAY ERROR DIALOG , ALERT THE USER
}
2009-11-15 13:54:07 +01:00
lastExtractedFilename = currentFilename ;
2009-11-14 23:12:16 +01:00
}
2009-11-15 00:18:58 +01:00
int closeResult = RARCloseArchive ( archive ) ;
2009-11-15 18:50:12 +01:00
// NSLog ( @ "Closing Archive %s with result %d" , filenameCString , closeResult ) ;
2009-11-15 00:18:58 +01:00
2009-11-14 23:12:16 +01:00
return extractionSuccessful ;
}
2009-11-16 15:00:11 +01:00
// Presents a dialog to the user allowing them to Skip a file or overwrite an existing version
// returns YES or NO
2009-11-15 13:54:07 +01:00
- ( BOOL ) shouldFileBeReplaced : ( NSString * ) filename {
BOOL result = NO ;
NSAlert * alert = [ [ NSAlert alloc ] init ] ;
[ alert addButtonWithTitle : @ "Overwrite" ] ;
2009-11-15 15:54:55 +01:00
NSButton * skipButton = [ alert addButtonWithTitle : @ "Skip" ] ;
[ skipButton setKeyEquivalent : @ "\e" ] ;
2009-11-15 13:54:07 +01:00
[ alert setMessageText : [ NSString stringWithFormat : @ "Overwrite %@?" , filename ] ] ;
[ alert setInformativeText : [ NSString stringWithFormat : @ "The file already exists. Do you wish to extract it again, overwriting the original file?" , filename ] ] ;
[ alert setAlertStyle : NSWarningAlertStyle ] ;
if ( [ alert runModal ] = = NSAlertFirstButtonReturn ) {
result = YES ;
}
[ alert release ] ;
return result ;
}
2009-11-16 15:00:11 +01:00
// Indicate to the user that part of the RAR volume is missing .
2009-11-15 15:43:54 +01:00
- ( void ) alertUserOfMissing : ( const char * ) volume {
NSAlert * alert = [ [ NSAlert alloc ] init ] ;
[ alert addButtonWithTitle : @ "OK" ] ;
[ alert setMessageText : [ NSString stringWithFormat : @ "Archive part %s is missing." , volume ] ] ;
[ alert setInformativeText : @ "Unable to extract all files from RAR archive as part of it is missing" ] ;
[ alert setAlertStyle : NSCriticalAlertStyle ] ;
[ alert runModal ] ;
[ alert release ] ;
}
2009-11-16 15:00:11 +01:00
// Creates a dialog with a custom view with a NSSecureTextField which is displayed
// to the user so they can provide a password . Returns the entered password or nil
- ( NSString * ) requestArchivePassword {
2009-11-15 18:50:12 +01:00
if ( ! passwordView ) {
[ NSBundle loadNibNamed : @ "PasswordView" owner : self ] ;
}
2009-11-16 15:00:11 +01:00
NSString * password = nil ;
2009-11-15 18:50:12 +01:00
NSAlert * alert = [ [ NSAlert alloc ] init ] ;
[ alert addButtonWithTitle : @ "OK" ] ;
[ alert addButtonWithTitle : @ "Cancel" ] ;
[ alert setMessageText : @ "Archive Requires a password" ] ;
[ alert setInformativeText : @ "To extract the contents of this archive a password is required." ] ;
[ alert setAccessoryView : passwordView ] ;
[ alert setAlertStyle : NSWarningAlertStyle ] ;
if ( [ alert runModal ] = = NSAlertFirstButtonReturn ) {
2009-11-16 15:00:11 +01:00
password = [ passwordField stringValue ] ;
[ password autorelease ] ;
2009-11-15 18:50:12 +01:00
}
[ alert release ] ;
2009-11-16 15:00:11 +01:00
return password ;
2009-11-15 18:50:12 +01:00
}
2009-11-10 16:48:42 +01:00
@ end