Source/AIAdium.m
author Peter Hosey
Tue Aug 14 02:59:20 2007 +0000 (2007-08-14)
branchadium-1.0
changeset 18554 30d173574771
parent 18281 e45de3601283
child 18690 305588b612ab
permissions -rw-r--r--
The first generation of appcasts has ended.

The second generation of appcasts has begun.

The difference is that generation 1 appcasts cannot have the `minimumSystemVersion` element, whereas generation 2 appcasts should (and must have the `generation=2` key in their URL query).
adamiser@9164
     1
/* 
adamiser@9164
     2
 * Adium is the legal property of its developers, whose names are listed in the copyright file included
adamiser@9164
     3
 * with this source distribution.
adamiser@9164
     4
 * 
adamiser@9164
     5
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
adamiser@9164
     6
 * General Public License as published by the Free Software Foundation; either version 2 of the License,
adamiser@9164
     7
 * or (at your option) any later version.
adamiser@9164
     8
 * 
adamiser@9164
     9
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
adamiser@9164
    10
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
adamiser@9164
    11
 * Public License for more details.
adamiser@9164
    12
 * 
adamiser@9164
    13
 * You should have received a copy of the GNU General Public License along with this program; if not,
adamiser@9164
    14
 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
adamiser@9164
    15
 */
adamiser@3
    16
Evan@16125
    17
#import "AIAdium.h"
Colin@12580
    18
#import "AdiumURLHandling.h"
adamiser@3
    19
#import "AIAccountController.h"
Evan@11773
    20
#import "AIChatController.h"
adamiser@3
    21
#import "AIContactController.h"
adamiser@3
    22
#import "AIContentController.h"
adamiser@8782
    23
#import "AICoreComponentLoader.h"
adamiser@8782
    24
#import "AICorePluginLoader.h"
adamiser@8782
    25
#import "AIDockController.h"
adamiser@9967
    26
#import "AIEmoticonController.h"
adamiser@8782
    27
#import "AIInterfaceController.h"
adamiser@8782
    28
#import "AILoginController.h"
adamiser@8782
    29
#import "AIMenuController.h"
adamiser@3
    30
#import "AIPreferenceController.h"
adamiser@8782
    31
#import "AISoundController.h"
adamiser@8782
    32
#import "AIStatusController.h"
adamiser@8782
    33
#import "AIToolbarController.h"
adamiser@9164
    34
#import "ESApplescriptabilityController.h"
adamiser@8782
    35
#import "ESContactAlertsController.h"
adamiser@9164
    36
#import "ESDebugController.h"
Evan@1280
    37
#import "ESFileTransferController.h"
psiona@926
    38
#import "LNAboutBoxController.h"
David@12648
    39
#import "AIXtrasManager.h"
Evan@13082
    40
#import "AdiumSetupWizard.h"
gbooker@14441
    41
#import "ESTextAndButtonsWindowController.h"
Evan@16125
    42
#import "AIAppearancePreferences.h"
Evan@16125
    43
#import <Adium/AIAdiumProtocol.h>
Evan@16125
    44
#import <Adium/AIPathUtilities.h>
adamiser@9164
    45
#import <AIUtilities/AIFileManagerAdditions.h>
Evan@12088
    46
#import <AIUtilities/AIApplicationAdditions.h>
David@16311
    47
#import <AIUtilities/AICalendarDateAdditions.h>
David@16291
    48
#import <Sparkle/SUConstants.h>
David@16630
    49
#import <Sparkle/SUUtilities.h>
Evan@1719
    50
Evan@17170
    51
//For Apple Help
Evan@17170
    52
#import <Carbon/Carbon.h>
Evan@17170
    53
David@12633
    54
#define ADIUM_TRAC_PAGE						@"http://trac.adiumx.com/"
Evan@17180
    55
#define ADIUM_REPORT_BUG_PAGE				@"http://trac.adiumx.com/wiki/ReportingBugs"
David@12633
    56
#define ADIUM_FORUM_PAGE					AILocalizedString(@"http://forum.adiumx.com/","Adium forums page. Localized only if a translated version exists.")
David@12633
    57
#define ADIUM_FEEDBACK_PAGE					@"mailto:feedback@adiumx.com"
David@12633
    58
David@12633
    59
//Portable Adium prefs key
David@12633
    60
#define PORTABLE_ADIUM_KEY					@"Preference Folder Location"
David@12633
    61
Evan@13082
    62
#define ALWAYS_RUN_SETUP_WIZARD FALSE
Evan@13082
    63
Evan@9306
    64
static NSString	*prefsCategory;
adamiser@9164
    65
David@17097
    66
enum {
David@17097
    67
    kNumberType,
David@17097
    68
    kStringType,
David@17097
    69
    kPeriodType
David@17097
    70
};
David@17097
    71
David@17097
    72
// The version comparison code here is courtesy of Kevin Ballard, adapted from MacPAD. Thanks, Kevin!
David@17097
    73
Evan@17137
    74
int AIGetCharType(NSString *character)
David@17097
    75
{
David@17097
    76
    if ([character isEqualToString:@"."]) {
David@17097
    77
        return kPeriodType;
David@17097
    78
    } else if ([character isEqualToString:@"0"] || [character intValue] != 0) {
David@17097
    79
        return kNumberType;
David@17097
    80
    } else {
David@17097
    81
        return kStringType;
David@17097
    82
    }	
David@17097
    83
}
David@17097
    84
Evan@17137
    85
NSArray *AISplitVersionString(NSString *version)
David@17097
    86
{
David@17097
    87
    NSString *character;
David@17097
    88
    NSMutableString *s;
David@17097
    89
    int i, n, oldType, newType;
David@17097
    90
    NSMutableArray *parts = [NSMutableArray array];
David@17097
    91
    if ([version length] == 0) {
David@17097
    92
        // Nothing to do here
David@17097
    93
        return parts;
David@17097
    94
    }
David@17097
    95
    s = [[[version substringToIndex:1] mutableCopy] autorelease];
Evan@17137
    96
    oldType = AIGetCharType(s);
David@17097
    97
    n = [version length] - 1;
David@17097
    98
    for (i = 1; i <= n; ++i) {
David@17097
    99
        character = [version substringWithRange:NSMakeRange(i, 1)];
Evan@17137
   100
        newType = AIGetCharType(character);
David@17097
   101
        if (oldType != newType || oldType == kPeriodType) {
David@17097
   102
            // We've reached a new segment
David@17097
   103
			NSString *aPart = [[NSString alloc] initWithString:s];
David@17097
   104
            [parts addObject:aPart];
David@17097
   105
			[aPart release];
David@17097
   106
            [s setString:character];
David@17097
   107
        } else {
David@17097
   108
            // Add character to string and continue
David@17097
   109
            [s appendString:character];
David@17097
   110
        }
David@17097
   111
        oldType = newType;
David@17097
   112
    }
David@17097
   113
    
David@17097
   114
    // Add the last part onto the array
David@17097
   115
    [parts addObject:[NSString stringWithString:s]];
David@17097
   116
    return parts;
David@17097
   117
}
David@17097
   118
David@17313
   119
//newVersion, currentVersion
David@17097
   120
NSComparisonResult AICustomVersionComparison(NSString *versionA, NSString *versionB)
David@17097
   121
{
Evan@17137
   122
	NSArray *partsA = AISplitVersionString(versionA);
Evan@17137
   123
    NSArray *partsB = AISplitVersionString(versionB);
David@17097
   124
    
David@17097
   125
    NSString *partA, *partB;
David@17097
   126
    int i, n, typeA, typeB, intA, intB;
David@17097
   127
    
David@17097
   128
    n = MIN([partsA count], [partsB count]);
David@17097
   129
    for (i = 0; i < n; ++i) {
David@17097
   130
        partA = [partsA objectAtIndex:i];
David@17097
   131
        partB = [partsB objectAtIndex:i];
David@17097
   132
        
Evan@17137
   133
        typeA = AIGetCharType(partA);
Evan@17137
   134
        typeB = AIGetCharType(partB);
David@17097
   135
        
David@17097
   136
        // Compare types
David@17097
   137
        if (typeA == typeB) {
David@17097
   138
            // Same type; we can compare
David@17097
   139
            if (typeA == kNumberType) {
David@17097
   140
                intA = [partA intValue];
David@17097
   141
                intB = [partB intValue];
David@17097
   142
                if (intA > intB) {
David@17097
   143
                    return NSOrderedAscending;
David@17097
   144
                } else if (intA < intB) {
David@17097
   145
                    return NSOrderedDescending;
David@17097
   146
                }
David@17097
   147
            } else if (typeA == kStringType) {
David@17097
   148
                NSComparisonResult result = [partA compare:partB];
David@17097
   149
                if (result != NSOrderedSame) {
David@17313
   150
					if ([partB isEqualToString:@"rc"])
David@17313
   151
						return NSOrderedDescending;
David@17097
   152
					if ([partA isEqualToString:@"rc"])
David@17097
   153
						return NSOrderedAscending;
David@17313
   154
					if ([partB isEqualToString:@"b"])
David@17097
   155
						return NSOrderedDescending;
David@17097
   156
					if ([partA isEqualToString:@"b"])
David@17097
   157
						return NSOrderedAscending;
David@17313
   158
					if ([partB isEqualToString:@"a"])
David@17097
   159
						return NSOrderedDescending;
David@17097
   160
					if ([partA isEqualToString:@"a"])
David@17097
   161
						return NSOrderedAscending;
David@17097
   162
                }
David@17097
   163
            }
David@17097
   164
        } else {
David@17097
   165
            // Not the same type? Now we have to do some validity checking
David@17097
   166
            if (typeA != kStringType && typeB == kStringType) {
David@17097
   167
                // typeA wins
David@17097
   168
                return NSOrderedAscending;
David@17097
   169
            } else if (typeA == kStringType && typeB != kStringType) {
David@17097
   170
                // typeB wins
David@17097
   171
                return NSOrderedDescending;
David@17097
   172
            } else {
David@17097
   173
                // One is a number and the other is a period. The period is invalid
David@17097
   174
                if (typeA == kNumberType) {
David@17097
   175
                    return NSOrderedAscending;
David@17097
   176
                } else {
David@17097
   177
                    return NSOrderedDescending;
David@17097
   178
                }
David@17097
   179
            }
David@17097
   180
        }
David@17097
   181
    }
David@17097
   182
    // The versions are equal up to the point where they both still have parts
David@17097
   183
    // Lets check to see if one is larger than the other
David@17097
   184
    if ([partsA count] != [partsB count]) {
David@17097
   185
        // Yep. Lets get the next part of the larger
David@17097
   186
        // n holds the value we want
David@17097
   187
        NSString *missingPart;
David@17097
   188
        int missingType, shorterResult, largerResult;
David@17097
   189
        
David@17097
   190
        if ([partsA count] > [partsB count]) {
David@17097
   191
            missingPart = [partsA objectAtIndex:n];
David@17097
   192
            shorterResult = NSOrderedDescending;
David@17097
   193
            largerResult = NSOrderedAscending;
David@17097
   194
        } else {
David@17097
   195
            missingPart = [partsB objectAtIndex:n];
David@17097
   196
            shorterResult = NSOrderedAscending;
David@17097
   197
            largerResult = NSOrderedDescending;
David@17097
   198
        }
David@17097
   199
        
Evan@17137
   200
        missingType = AIGetCharType(missingPart);
David@17097
   201
        // Check the type
David@17097
   202
        if (missingType == kStringType) {
David@17097
   203
            // It's a string. Shorter version wins
David@17097
   204
            return shorterResult;
David@17097
   205
        } else {
David@17097
   206
            // It's a number/period. Larger version wins
David@17097
   207
            return largerResult;
David@17097
   208
        }
David@17097
   209
    }
David@17097
   210
    
David@17097
   211
    // The 2 strings are identical
David@17097
   212
    return NSOrderedSame;
David@17097
   213
}
David@17097
   214
adamiser@3
   215
@interface AIAdium (PRIVATE)
adamiser@3
   216
- (void)completeLogin;
Evan@5163
   217
- (void)openAppropriatePreferencesIfNeeded;
Evan@17170
   218
- (void)configureHelp;
Evan@6860
   219
- (void)deleteTemporaryFiles;
adamiser@3
   220
@end
adamiser@3
   221
adamiser@3
   222
@implementation AIAdium
adamiser@3
   223
adamiser@2892
   224
//Init
adamiser@7751
   225
- (id)init
adamiser@7751
   226
{
Evan@11752
   227
	if ((self = [super init])) {
Peter@9686
   228
		[AIObject _setSharedAdiumInstance:self];
Peter@9686
   229
	}
Evan@9106
   230
Peter@9686
   231
	return self;
adamiser@1714
   232
}
adamiser@1714
   233
adamiser@2892
   234
//Core Controllers -----------------------------------------------------------------------------------------------------
adamiser@2892
   235
#pragma mark Core Controllers
Evan@15541
   236
- (NSObject <AILoginController> *)loginController{
Catfish_Man@12140
   237
    return loginController;
adamiser@3
   238
}
Evan@15541
   239
- (NSObject <AIMenuController> *)menuController{
Catfish_Man@12140
   240
    return menuController;
adamiser@3
   241
}
Evan@15541
   242
- (NSObject <AIAccountController> *)accountController{
Catfish_Man@12140
   243
    return accountController;
adamiser@3
   244
}
Evan@15541
   245
- (NSObject <AIChatController> *)chatController{
Catfish_Man@12140
   246
	return chatController;
Evan@11773
   247
}
Evan@15541
   248
- (NSObject <AIContentController> *)contentController{
Catfish_Man@12140
   249
    return contentController;
adamiser@3
   250
}
Evan@15541
   251
- (NSObject <AIContactController> *)contactController{
Catfish_Man@12140
   252
    return contactController;
adamiser@3
   253
}
Evan@15541
   254
- (NSObject <AIEmoticonController> *)emoticonController{
Catfish_Man@12140
   255
    return emoticonController;
adamiser@9967
   256
}
Evan@15541
   257
- (NSObject <AISoundController> *)soundController{
Catfish_Man@12140
   258
    return soundController;
adamiser@3
   259
}
Evan@15541
   260
- (NSObject <AIInterfaceController> *)interfaceController{
Catfish_Man@12140
   261
    return interfaceController;
adamiser@3
   262
}
Evan@15541
   263
- (NSObject <AIPreferenceController> *)preferenceController{
Catfish_Man@12140
   264
    return preferenceController;
adamiser@3
   265
}
Evan@15541
   266
- (NSObject <AIToolbarController> *)toolbarController{
Catfish_Man@12140
   267
    return toolbarController;
adamiser@3
   268
}
Evan@15541
   269
- (NSObject <AIDockController> *)dockController{
Catfish_Man@12140
   270
    return dockController;
ramoth4@167
   271
}
Evan@15541
   272
- (NSObject <AIFileTransferController> *)fileTransferController{
Catfish_Man@12140
   273
    return fileTransferController;    
Evan@1280
   274
}
Evan@15541
   275
- (NSObject <AIContactAlertsController> *)contactAlertsController{
Catfish_Man@12140
   276
    return contactAlertsController;
Evan@1428
   277
}
Evan@15541
   278
- (NSObject <AIApplescriptabilityController> *)applescriptabilityController{
Catfish_Man@12140
   279
	return applescriptabilityController;
Evan@5707
   280
}
Evan@15541
   281
- (NSObject <AIDebugController> *)debugController{
Catfish_Man@12140
   282
	return debugController;
Evan@7094
   283
}
Evan@15541
   284
- (NSObject <AIStatusController> *)statusController{
Catfish_Man@12140
   285
    return statusController;
adamiser@8782
   286
}
adamiser@2892
   287
Peter@10945
   288
//Loaders --------------------------------------------------------------------------------------------------------
Peter@10945
   289
#pragma mark Loaders
Peter@10945
   290
Peter@10945
   291
- (AICoreComponentLoader *)componentLoader
Peter@10945
   292
{
Peter@10945
   293
	return componentLoader;
Peter@10945
   294
}
Peter@10945
   295
adamiser@2892
   296
//Notifications --------------------------------------------------------------------------------------------------------
adamiser@2892
   297
#pragma mark Notifications
adamiser@2892
   298
//Return the shared Adium notification center
adamiser@239
   299
- (NSNotificationCenter *)notificationCenter
adamiser@239
   300
{
Evan@11752
   301
    if (notificationCenter == nil) {
adamiser@239
   302
        notificationCenter = [[NSNotificationCenter alloc] init];
adamiser@239
   303
    }
adamiser@969
   304
            
Catfish_Man@12140
   305
    return notificationCenter;
adamiser@239
   306
}
adamiser@239
   307
adamiser@7751
   308
adamiser@2892
   309
//Startup and Shutdown -------------------------------------------------------------------------------------------------
adamiser@2892
   310
#pragma mark Startup and Shutdown
adamiser@2892
   311
//Adium is almost done launching, init
adamiser@2892
   312
- (void)applicationWillFinishLaunching:(NSNotification *)notification
adamiser@2892
   313
{
adamiser@2892
   314
    notificationCenter = nil;
adamiser@2892
   315
    completedApplicationLoad = NO;
Evan@5163
   316
	advancedPrefsName = nil;
Evan@9306
   317
	prefsCategory = nil;
Colin@12580
   318
	queuedURLEvents = nil;
Colin@12580
   319
	
Evan@3961
   320
    //Ignore SIGPIPE, which is a harmless error signal
Evan@3961
   321
    //sent when write() or similar function calls fail due to a broken pipe in the network connection
Evan@3961
   322
    signal(SIGPIPE, SIG_IGN);
Colin@12580
   323
	
Colin@12580
   324
	[[NSAppleEventManager sharedAppleEventManager] setEventHandler:self 
Colin@12580
   325
												   andSelector:@selector(handleURLEvent:withReplyEvent:)
Colin@12580
   326
												 forEventClass:kInternetEventClass
Colin@12580
   327
													andEventID:kAEGetURL];
Colin@12580
   328
}
Colin@12580
   329
Colin@12580
   330
- (void)handleURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
Colin@12580
   331
{
Colin@12580
   332
	if (!completedApplicationLoad) {
Colin@12580
   333
		if (!queuedURLEvents) {
Colin@12580
   334
			queuedURLEvents = [[NSMutableArray alloc] init];
Colin@12580
   335
		}
Colin@12583
   336
		[queuedURLEvents addObject:[[event descriptorAtIndex:1] stringValue]];
Colin@12580
   337
	} else {
Colin@12583
   338
		[AdiumURLHandling handleURLEvent:[[event descriptorAtIndex:1] stringValue]];
Colin@12580
   339
	}
Evan@5163
   340
}
Evan@5163
   341
Evan@5163
   342
//Adium has finished launching
Evan@5163
   343
- (void)applicationDidFinishLaunching:(NSNotification *)notification
Evan@5163
   344
{
Evan@5163
   345
	//Begin loading and initing the components
Evan@11773
   346
	loginController = [[AILoginController alloc] init];
adamiser@2892
   347
    
adamiser@2892
   348
    //Begin Login
adamiser@2892
   349
    [loginController requestUserNotifyingTarget:self selector:@selector(completeLogin)];
adamiser@2892
   350
}
adamiser@2892
   351
adamiser@2892
   352
//Forward a re-open message to the interface controller
Evan@1577
   353
- (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag
Evan@1577
   354
{
Catfish_Man@12140
   355
    return [interfaceController handleReopenWithVisibleWindows:flag];
Evan@1577
   356
}
Evan@1577
   357
adamiser@2892
   358
//Called by the login controller when a user has been selected, continue logging in
adamiser@3
   359
- (void)completeLogin
adamiser@3
   360
{
Evan@14152
   361
	NSAutoreleasePool *pool;
Evan@14152
   362
Evan@14152
   363
	pool = [[NSAutoreleasePool alloc] init];
Evan@14152
   364
Evan@11773
   365
	/* Init the controllers.
Evan@11773
   366
	 * Menu and interface controllers are created by MainMenu.nib when it loads.
Evan@11773
   367
	 */
Evan@11773
   368
	preferenceController = [[AIPreferenceController alloc] init];
Evan@11773
   369
	toolbarController = [[AIToolbarController alloc] init];
Evan@11787
   370
Evan@11787
   371
#ifdef DEBUG_BUILD
Evan@11773
   372
	debugController = [[ESDebugController alloc] init];
Evan@11787
   373
#else
Evan@11787
   374
	debugController = nil;
Evan@11787
   375
#endif
Evan@11787
   376
Evan@11773
   377
	contactAlertsController = [[ESContactAlertsController alloc] init];
Evan@11773
   378
	soundController = [[AISoundController alloc] init];
Evan@11773
   379
	emoticonController = [[AIEmoticonController alloc] init];
Evan@11773
   380
	accountController = [[AIAccountController alloc] init];
Evan@11773
   381
	contactController = [[AIContactController alloc] init];
Evan@11773
   382
	chatController = [[AIChatController alloc] init];
Evan@11773
   383
	contentController = [[AIContentController alloc] init];
Evan@11773
   384
	dockController = [[AIDockController alloc] init];
Evan@11773
   385
	fileTransferController = [[ESFileTransferController alloc] init];
Evan@11773
   386
	applescriptabilityController = [[ESApplescriptabilityController alloc] init];
Evan@11773
   387
	statusController = [[AIStatusController alloc] init];
Evan@11773
   388
Evan@11773
   389
	//Finish setting up the preference controller before the components and plugins load so they can read prefs 
adamiser@11846
   390
	[preferenceController controllerDidLoad];
adamiser@11846
   391
	[debugController controllerDidLoad];
David@15999
   392
	//Safety for when we remove previously included list xtras
David@15999
   393
	[AIAppearancePreferences migrateOldListSettingsIfNeeded];
Evan@14152
   394
	[pool release];
Peter@10535
   395
adamiser@11846
   396
	//Plugins and components should always init last, since they rely on everything else.
Evan@14152
   397
	pool = [[NSAutoreleasePool alloc] init];
Evan@11773
   398
	componentLoader = [[AICoreComponentLoader alloc] init];
Evan@11773
   399
	pluginLoader = [[AICorePluginLoader alloc] init];
Evan@14152
   400
	[pool release];
Evan@14152
   401
adamiser@11846
   402
	//Finish initing
Evan@14152
   403
	pool = [[NSAutoreleasePool alloc] init];
gbooker@14427
   404
	[AdiumURLHandling registerURLTypes];		//Asks the user questions so must load after components
adamiser@11846
   405
	[menuController controllerDidLoad];			//Loaded by nib
adamiser@11846
   406
	[accountController controllerDidLoad];		//** Before contactController so accounts and services are available for contact creation
adamiser@11846
   407
	[contactController controllerDidLoad];		//** Before interfaceController so the contact list is available to the interface
adamiser@11846
   408
	[interfaceController controllerDidLoad];	//Loaded by nib
Evan@14152
   409
	[pool release];
Evan@14152
   410
Evan@14152
   411
	pool = [[NSAutoreleasePool alloc] init];
adamiser@11846
   412
	[toolbarController controllerDidLoad];
adamiser@11846
   413
	[contactAlertsController controllerDidLoad];
adamiser@11846
   414
	[soundController controllerDidLoad];
adamiser@11846
   415
	[emoticonController controllerDidLoad];
adamiser@11846
   416
	[chatController controllerDidLoad];
adamiser@11846
   417
	[contentController controllerDidLoad];
adamiser@11846
   418
	[dockController controllerDidLoad];
adamiser@11846
   419
	[fileTransferController controllerDidLoad];
Evan@14152
   420
	[pool release];
Evan@14152
   421
Evan@14152
   422
	pool = [[NSAutoreleasePool alloc] init];
adamiser@11846
   423
	[applescriptabilityController controllerDidLoad];
adamiser@11846
   424
	[statusController controllerDidLoad];
toby@10776
   425
Evan@5163
   426
	//Open the preferences if we were unable to because application:openFile: was called before we got here
Evan@5163
   427
	[self openAppropriatePreferencesIfNeeded];
toby@10776
   428
Evan@13082
   429
	//If no accounts are setup, run the setup wizard
Evan@13082
   430
	if (([[accountController accounts] count] == 0) || ALWAYS_RUN_SETUP_WIZARD) {
Evan@13082
   431
		[AdiumSetupWizard runWizard];
adamiser@9819
   432
	}
toby@10776
   433
Colin@12580
   434
	//Process any delayed URL events 
Colin@12580
   435
	if (queuedURLEvents) {
Colin@12583
   436
		NSString *eventString = nil;
Colin@12580
   437
		NSEnumerator *e  = [queuedURLEvents objectEnumerator];
Colin@12583
   438
		while ((eventString = [e nextObject])) {
Colin@12583
   439
			[AdiumURLHandling handleURLEvent:eventString];
Colin@12580
   440
		}
Evan@12900
   441
		[queuedURLEvents release]; queuedURLEvents = nil;
Colin@12580
   442
	}
Colin@12580
   443
	
Evan@14046
   444
	//If we were asked to open a log at launch, do it now
Evan@14046
   445
	if (queuedLogPathToShow) {
Evan@17646
   446
		[[self notificationCenter] postNotificationName:AIShowLogAtPathNotification
Evan@14046
   447
												 object:queuedLogPathToShow];
Evan@14046
   448
		[queuedLogPathToShow release];
Evan@14046
   449
	}
Evan@14046
   450
	
toby@10776
   451
	completedApplicationLoad = YES;
Evan@8200
   452
Evan@17170
   453
	[self configureHelp];
Evan@17170
   454
	
Evan@18014
   455
	[[NSDistributedNotificationCenter defaultCenter] addObserver:self
Evan@18014
   456
														selector:@selector(systemTimeZoneDidChange:)
Evan@18014
   457
															name:@"NSSystemTimeZoneDidChangeDistributedNotification"
Evan@18014
   458
														  object:nil];
Evan@18014
   459
Evan@18014
   460
	//Broadcast our presence
Evan@18014
   461
	NSConnection *connection = [NSConnection defaultConnection];
Evan@18014
   462
	[connection setRootObject:self];
Evan@18025
   463
	[connection registerName:@"com.adiumX.adiumX"];
Evan@18014
   464
Evan@18016
   465
	[[self notificationCenter] postNotificationName:AIApplicationDidFinishLoadingNotification object:nil];
Evan@18016
   466
	[[NSDistributedNotificationCenter defaultCenter]  postNotificationName:AIApplicationDidFinishLoadingNotification object:nil];
Evan@18016
   467
Evan@14152
   468
	[pool release];
adamiser@3
   469
}
adamiser@3
   470
adamiser@2892
   471
//Give all the controllers a chance to close down
adamiser@158
   472
- (void)applicationWillTerminate:(NSNotification *)notification
adamiser@158
   473
{
Evan@13067
   474
	//Take no action if we didn't complete the application load
Evan@13067
   475
	if (!completedApplicationLoad) return;
Evan@13067
   476
Evan@17646
   477
	[[self notificationCenter] postNotificationName:AIAppWillTerminateNotification object:nil];
Evan@13356
   478
	
adamiser@11846
   479
	//Close the preference window before we shut down the plugins that compose it
adamiser@11846
   480
	[preferenceController closePreferenceWindow:nil];
adamiser@2819
   481
adamiser@158
   482
    //Close the controllers in reverse order
adamiser@11846
   483
	[pluginLoader controllerWillClose]; 				//** First because plugins rely on all the controllers
adamiser@11846
   484
	[componentLoader controllerWillClose];				//** First because components rely on all the controllers
adamiser@11846
   485
	[statusController controllerWillClose];				//** Before accountController so account states are saved before being set to offline
adamiser@11846
   486
    [chatController controllerWillClose];				//** Before interfaceController so chats can be correctly closed
adamiser@11846
   487
	[contactAlertsController controllerWillClose];
adamiser@11846
   488
    [fileTransferController controllerWillClose];
adamiser@11846
   489
    [dockController controllerWillClose];
adamiser@11846
   490
    [interfaceController controllerWillClose];
adamiser@11846
   491
    [contentController controllerWillClose];
adamiser@11846
   492
    [contactController controllerWillClose];
adamiser@11846
   493
    [accountController controllerWillClose];
adamiser@11846
   494
	[emoticonController controllerWillClose];
adamiser@11846
   495
    [soundController controllerWillClose];
adamiser@11846
   496
    [menuController controllerWillClose];
adamiser@11846
   497
    [applescriptabilityController controllerWillClose];
adamiser@11846
   498
	[debugController controllerWillClose];
adamiser@11846
   499
	[toolbarController controllerWillClose];
adamiser@11846
   500
    [preferenceController controllerWillClose];			//** Last since other controllers may want to write preferences as they close
Evan@6860
   501
	
Evan@6860
   502
	[self deleteTemporaryFiles];
Evan@6860
   503
}
Evan@6860
   504
Evan@6860
   505
- (void)deleteTemporaryFiles
Evan@6860
   506
{
Evan@8541
   507
	[[NSFileManager defaultManager] removeFilesInDirectory:[self cachesPath]
Evan@6913
   508
												withPrefix:@"TEMP"
Evan@6913
   509
											 movingToTrash:NO];
adamiser@158
   510
}
adamiser@158
   511
adamiser@2892
   512
adamiser@2892
   513
//Menu Item Hooks ------------------------------------------------------------------------------------------------------
adamiser@2892
   514
#pragma mark Menu Item Hooks
adamiser@2892
   515
//Show the about box
ramoth4@834
   516
- (IBAction)showAboutBox:(id)sender
ramoth4@834
   517
{
adamiser@1714
   518
    [[LNAboutBoxController aboutBoxController] showWindow:nil];
Evan@1981
   519
}
adamiser@2892
   520
adamiser@3321
   521
- (IBAction)reportABug:(id)sender{
Evan@17180
   522
    [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:ADIUM_REPORT_BUG_PAGE]];
adamiser@3321
   523
}
adamiser@3321
   524
- (IBAction)sendFeedback:(id)sender{
adamiser@3321
   525
    [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:ADIUM_FEEDBACK_PAGE]];
adamiser@3321
   526
}
adamiser@3321
   527
- (IBAction)showForums:(id)sender{
adamiser@3321
   528
    [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:ADIUM_FORUM_PAGE]];
adamiser@3321
   529
}
Evan@5507
   530
- (IBAction)showXtras:(id)sender{
David@12648
   531
	[[AIXtrasManager sharedManager] showXtras];
Evan@5507
   532
}
ramoth4@834
   533
Evan@14835
   534
- (IBAction)contibutingToAdium:(id)sender
Evan@14835
   535
{
Evan@14835
   536
	[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://trac.adiumx.com/wiki/ContributingToAdium"]];
Evan@14835
   537
}
Evan@14835
   538
- (IBAction)donate:(id)sender
Evan@14835
   539
{
Evan@14835
   540
	[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&submit.x=57&submit.y=8&encrypted=-----BEGIN+PKCS7-----%0D%0AMIIHFgYJKoZIhvcNAQcEoIIHBzCCBwMCAQExggEwMIIBLAIBADCBlDCBjjELMAkG%0D%0AA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQw%0D%0AEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UE%0D%0AAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwDQYJ%0D%0AKoZIhvcNAQEBBQAEgYAFR5tF%2BRKUV3BS49vJraDG%2BIoWDoZMieUT%2FJJ1Fzjsr511%0D%0Au7hS1F2piJuHuqmm%2F0r8Kf8oaycOo74K3zLmUQ6T6hUS6%2Bh6lZAoIlhI3A1YmqIP%0D%0AdrdY%2FtfKRbWfolDumJ9Mdv%2FzJxPnpdQiTN5K1PMrPYE6GgPWE9WC4V9lqstSmTEL%0D%0AMAkGBSsOAwIaBQAwgZMGCSqGSIb3DQEHATAUBggqhkiG9w0DBwQIjtd%2BN9o4ZB6A%0D%0AcIbH8ZjOLmE35xBQ%2F93chtzIcRXHhIQJVpBRCkyJkdTD3libP3F7TgkrLij1DBxg%0D%0AfFlE0V%2FGTk29Ys%2FwsPO7hNs3YSNuSz0HT5F6sa8aXwFtMCE%2FgB1Ha4qdtYY%2BNETJ%0D%0AEETwNMLefjhaBfI%2BnRxl2K2gggOHMIIDgzCCAuygAwIBAgIBADANBgkqhkiG9w0B%0D%0AAQUFADCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3Vu%0D%0AdGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9j%0D%0AZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBh%0D%0AbC5jb20wHhcNMDQwMjEzMTAxMzE1WhcNMzUwMjEzMTAxMzE1WjCBjjELMAkGA1UE%0D%0ABhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYD%0D%0AVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQI%0D%0AbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20wgZ8wDQYJKoZI%0D%0AhvcNAQEBBQADgY0AMIGJAoGBAMFHTt38RMxLXJyO2SmS%2BNdl72T7oKJ4u4uw%2B6aw%0D%0AntALWh03PewmIJuzbALScsTS4sZoS1fKciBGoh11gIfHzylvkdNe%2FhJl66%2FRGqrj%0D%0A5rFb08sAABNTzDTiqqNpJeBsYs%2Fc2aiGozptX2RlnBktH%2BSUNpAajW724Nv2Wvhi%0D%0Af6sFAgMBAAGjge4wgeswHQYDVR0OBBYEFJaffLvGbxe9WT9S1wob7BDWZJRrMIG7%0D%0ABgNVHSMEgbMwgbCAFJaffLvGbxe9WT9S1wob7BDWZJRroYGUpIGRMIGOMQswCQYD%0D%0AVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDAS%0D%0ABgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQD%0D%0AFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbYIBADAMBgNV%0D%0AHRMEBTADAQH%2FMA0GCSqGSIb3DQEBBQUAA4GBAIFfOlaagFrl71%2Bjq6OKidbWFSE%2B%0D%0AQ4FqROvdgIONth%2B8kSK%2F%2FY%2F4ihuE4Ymvzn5ceE3S%2FiBSQQMjyvb%2Bs2TWbQYDwcp1%0D%0A29OPIbD9epdr4tJOUNiSojw7BHwYRiPh58S1xGlFgHFXwrEBb3dgNbMUa%2Bu4qect%0D%0AsMAXpVHnD9wIyfmHMYIBmjCCAZYCAQEwgZQwgY4xCzAJBgNVBAYTAlVTMQswCQYD%0D%0AVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFs%0D%0AIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRww%0D%0AGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tAgEAMAkGBSsOAwIaBQCgXTAYBgkq%0D%0AhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0wNDAzMjUwNDQ0%0D%0AMzRaMCMGCSqGSIb3DQEJBDEWBBRzTAS6zk5cmMeC49IorY8CM%2BkX0TANBgkqhkiG%0D%0A9w0BAQEFAASBgBsyRfMv9mSyoYq00wIB7BmUHFGq5x%2Ffnr8M24XbKjhkyeULk2NC%0D%0As4jbCgaWNg6grvccJtjbvmDskMKt%2BdS%2BEAkeWwm1Zf%2F%2B5u1fMyb5vo1NNcRIs5oq%0D%0A7SvXiLTPRzVqzQdhVs7PoZG0i0RRIb0tMeo1IssZeB2GE5Nsg0D8PwpB%0D%0A-----END+PKCS7-----"]];
Evan@14835
   541
}
Evan@14835
   542
gbooker@14441
   543
- (void)unreadQuitQuestion:(NSNumber *)number userInfo:(id)info
gbooker@14441
   544
{
gbooker@14441
   545
	AITextAndButtonsReturnCode result = [number intValue];
gbooker@14441
   546
	switch(result)
gbooker@14441
   547
	{
gbooker@14441
   548
		case AITextAndButtonsDefaultReturn:
gbooker@14441
   549
			//Quit
gbooker@14441
   550
			//Should we ask about File Transfers here?????
gbooker@14441
   551
			[NSApp terminate:nil];
gbooker@14441
   552
			break;
gbooker@14441
   553
		case AITextAndButtonsOtherReturn:
gbooker@14441
   554
			//Don't Ask Again
gbooker@14441
   555
			[[self preferenceController] setPreference:[NSNumber numberWithBool:YES]
gbooker@14441
   556
												 forKey:@"Suppress Quit Confirmation for Unread Messages"
gbooker@14441
   557
												  group:@"Confirmations"];
gbooker@14441
   558
			[NSApp terminate:nil];
gbooker@14441
   559
			break;
gbooker@14441
   560
		default:
gbooker@14441
   561
			//Cancel
gbooker@14441
   562
			break;
gbooker@14441
   563
	}
gbooker@14441
   564
}
gbooker@14441
   565
gbooker@14441
   566
- (void)fileTransferQuitQuestion:(NSNumber *)number userInfo:(id)info
gbooker@14441
   567
{
gbooker@14441
   568
	AITextAndButtonsReturnCode result = [number intValue];
gbooker@14441
   569
	switch(result)
gbooker@14441
   570
	{
gbooker@14441
   571
		case AITextAndButtonsDefaultReturn:
gbooker@14441
   572
			//Quit
gbooker@14441
   573
			[NSApp terminate:nil];
gbooker@14441
   574
			break;
gbooker@14441
   575
		case AITextAndButtonsOtherReturn:
gbooker@14441
   576
			//Don't Ask Again
gbooker@14441
   577
			[[self preferenceController] setPreference:[NSNumber numberWithBool:YES]
gbooker@14441
   578
												 forKey:@"Suppress Quit Confirmation for File Transfers"
gbooker@14441
   579
												  group:@"Confirmations"];
gbooker@14441
   580
			[NSApp terminate:nil];
gbooker@14441
   581
			break;
gbooker@14441
   582
		default:
gbooker@14441
   583
			//Cancel
gbooker@14441
   584
			break;
gbooker@14441
   585
	}
gbooker@14441
   586
}
gbooker@14441
   587
Evan@4931
   588
//Last call to perform actions before the app shuffles off its mortal coil and joins the bleeding choir invisible
ramoth4@892
   589
- (IBAction)confirmQuit:(id)sender
ramoth4@892
   590
{
Evan@17202
   591
	/* We may have received a message or begun a file transfer while the menu was open, if this is reached via a menu item.
Evan@17202
   592
	 * Wait one last run loop before beginning to quit so that activity can be registered, since menus run in
Evan@17202
   593
	 * a different run loop mode, NSEventTrackingRunLoopMode.
Evan@17202
   594
	 */
Evan@17202
   595
	[NSObject cancelPreviousPerformRequestsWithTarget:self
Evan@17202
   596
											 selector:@selector(reallyConfirmQuit)
Evan@17202
   597
											   object:nil];
Evan@17202
   598
	[self performSelector:@selector(reallyConfirmQuit)
Evan@17202
   599
			   withObject:nil
Evan@17202
   600
			   afterDelay:0];
Evan@17202
   601
}
Evan@17202
   602
Evan@17202
   603
- (void)reallyConfirmQuit
Evan@17202
   604
{
gbooker@14440
   605
	BOOL allowQuit = YES;
Evan@13198
   606
	if (([chatController unviewedContentCount] > 0) &&
David@14068
   607
		(![[preferenceController preferenceForKey:@"Suppress Quit Confirmation for Unread Messages"
Evan@13198
   608
											group:@"Confirmations"] boolValue])) {
gbooker@14441
   609
		[[self interfaceController] displayQuestion:AILocalizedString(@"Confirm Quit", nil)
gbooker@14441
   610
									withDescription:AILocalizedString(@"You have unread messages.\nAre you sure you want to quit?", nil)
gbooker@14441
   611
									withWindowTitle:nil
gbooker@14441
   612
									  defaultButton:AILocalizedString(@"Quit", nil)
gbooker@14441
   613
									alternateButton:AILocalizedString(@"Cancel", nil)
gbooker@14441
   614
										otherButton:AILocalizedString(@"Don't ask again", nil)
gbooker@14441
   615
											 target:self
gbooker@14441
   616
										   selector:@selector(unreadQuitQuestion:userInfo:)
gbooker@14441
   617
										   userInfo:nil];
gbooker@14440
   618
		allowQuit = NO;
David@14068
   619
	} 
David@14068
   620
	
gbooker@14441
   621
	if (allowQuit &&
gbooker@14441
   622
		([fileTransferController activeTransferCount] > 0) &&
gbooker@14440
   623
		(![[preferenceController preferenceForKey:@"Suppress Quit Confirmation for File Transfers"
gbooker@14440
   624
											group:@"Confirmations"]  boolValue])) {
gbooker@14441
   625
		[[self interfaceController] displayQuestion:AILocalizedString(@"Confirm Quit", nil)
gbooker@14441
   626
									withDescription:AILocalizedString(@"You have file transfers in progress.\nAre you sure you want to quit?", nil)
gbooker@14441
   627
									withWindowTitle:nil
gbooker@14441
   628
									  defaultButton:AILocalizedString(@"Quit", nil)
gbooker@14441
   629
									alternateButton:AILocalizedString(@"Cancel", nil)
gbooker@14441
   630
										otherButton:AILocalizedString(@"Don't ask again", nil)
gbooker@14441
   631
											 target:self
gbooker@14441
   632
										   selector:@selector(fileTransferQuitQuestion:userInfo:)
gbooker@14441
   633
										   userInfo:nil];
gbooker@14440
   634
		allowQuit = NO;
David@14068
   635
	}
David@14068
   636
	
Evan@14816
   637
	if (allowQuit) {
Evan@13198
   638
		[NSApp terminate:nil];
Evan@13198
   639
	}
ramoth4@892
   640
}
ramoth4@892
   641
Peter@3867
   642
//Other -------------------------------------------------------------------------------------------------------
Peter@3867
   643
#pragma mark Other
adamiser@2892
   644
//If Adium was launched by double-clicking an associated file, we get this call after willFinishLaunching but before
adamiser@2892
   645
//didFinishLaunching
Evan@2022
   646
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
Evan@2022
   647
{
Evan@4867
   648
    NSString			*extension = [filename pathExtension];
Evan@4867
   649
    NSString			*destination = nil;
Evan@4867
   650
	NSString			*errorMessage = nil;
Evan@5163
   651
    NSString			*fileDescription = nil, *prefsButton = nil;
Evan@4867
   652
	BOOL				success = NO, requiresRestart = NO;
Evan@4867
   653
	int					buttonPressed;
Evan@5163
   654
	
Evan@14046
   655
	if (([extension caseInsensitiveCompare:@"AdiumLog"] == NSOrderedSame) ||
David@16339
   656
		([extension caseInsensitiveCompare:@"AdiumHtmlLog"] == NSOrderedSame) ||
David@16339
   657
		([extension caseInsensitiveCompare:@"chatlog"] == NSOrderedSame)) {
Evan@14046
   658
		if (completedApplicationLoad) {
Evan@14046
   659
			//Request display of the log immediately if Adium is ready
Evan@17646
   660
			[[self notificationCenter] postNotificationName:AIShowLogAtPathNotification
Evan@14046
   661
													 object:filename];
Evan@14046
   662
		} else {
Evan@14046
   663
			//Queue the request until Adium is done launching if Adium is not ready
Evan@14046
   664
			[queuedLogPathToShow release]; queuedLogPathToShow = [filename retain];
Evan@14046
   665
		}
Evan@14046
   666
		
Evan@14046
   667
		//Don't continue to the xtras installation code. Return YES because we handled the open.
Evan@14046
   668
		return YES;
Evan@14046
   669
	}	
Evan@14046
   670
	
Evan@14046
   671
	/* Installation of Xtras below this point */
Evan@14046
   672
Evan@9306
   673
	[prefsCategory release]; prefsCategory = nil;
Evan@5163
   674
    [advancedPrefsName release]; advancedPrefsName = nil;
Evan@14046
   675
Evan@2022
   676
    //Specify a file extension and a human-readable description of what the files of this type do
Evan@18185
   677
    if ([extension caseInsensitiveCompare:@"AdiumPlugin"] == NSOrderedSame) {
Peter@13081
   678
        destination = [AISearchPathForDirectoriesInDomains(AIPluginsDirectory, NSUserDomainMask, /*expandTilde*/ YES) objectAtIndex:0];
Evan@2022
   679
        //Plugins haven't been loaded yet if the application isn't done loading, so only request a restart if it has finished loading already 
Evan@2022
   680
        requiresRestart = completedApplicationLoad;
eevyl@6470
   681
        fileDescription = AILocalizedString(@"Adium plugin",nil);
Evan@10297
   682
Evan@18185
   683
    } else if ([extension caseInsensitiveCompare:@"AdiumLibpurplePlugin"] == NSOrderedSame) {
Evan@18185
   684
        destination = [AISearchPathForDirectoriesInDomains(AIPluginsDirectory, NSUserDomainMask, /*expandTilde*/ YES) objectAtIndex:0];
Evan@18185
   685
        //Plugins haven't been loaded yet if the application isn't done loading, so only request a restart if it has finished loading already 
Evan@18185
   686
        requiresRestart = completedApplicationLoad;
Evan@18185
   687
        fileDescription = AILocalizedString(@"Adium plugin",nil);
Evan@18185
   688
		extension = @"AdiumLibpurplePlugin";
Evan@18185
   689
Evan@18185
   690
	} else if ([extension caseInsensitiveCompare:@"AdiumIcon"] == NSOrderedSame) {
Peter@13081
   691
		destination = [AISearchPathForDirectoriesInDomains(AIDockIconsDirectory, NSUserDomainMask, /*expandTilde*/ YES) objectAtIndex:0];
eevyl@6470
   692
        fileDescription = AILocalizedString(@"dock icon set",nil);
adamiser@9964
   693
		prefsButton = AILocalizedString(@"Open Appearance Prefs",nil);
adamiser@9964
   694
		prefsCategory = @"appearance";
dchoby98@4680
   695
Evan@11752
   696
	} else if ([extension caseInsensitiveCompare:@"AdiumSoundset"] == NSOrderedSame) {
Peter@13081
   697
		destination = [AISearchPathForDirectoriesInDomains(AISoundsDirectory, NSUserDomainMask, /*expandTilde*/ YES) objectAtIndex:0];
eevyl@6470
   698
		fileDescription = AILocalizedString(@"sound set",nil);
adamiser@9964
   699
		prefsButton = AILocalizedString(@"Open Event Prefs",nil);
Evan@9306
   700
		prefsCategory = @"events";
dchoby98@4680
   701
Evan@11752
   702
	} else if ([extension caseInsensitiveCompare:@"AdiumEmoticonset"] == NSOrderedSame) {
Peter@13081
   703
		destination = [AISearchPathForDirectoriesInDomains(AIEmoticonsDirectory, NSUserDomainMask, /*expandTilde*/ YES) objectAtIndex:0];
eevyl@6470
   704
		fileDescription = AILocalizedString(@"emoticon set",nil);
adamiser@9964
   705
		prefsButton = AILocalizedString(@"Open Appearance Prefs",nil);
adamiser@9964
   706
		prefsCategory = @"appearance";
Evan@5163
   707
		
dchoby98@5117
   708
	} else if ([extension caseInsensitiveCompare:@"AdiumScripts"] == NSOrderedSame) {
Peter@13081
   709
		destination = [AISearchPathForDirectoriesInDomains(AIScriptsDirectory, NSUserDomainMask, /*expandTilde*/ YES) objectAtIndex:0];
eevyl@6470
   710
		fileDescription = AILocalizedString(@"AppleScript set",nil);
dchoby98@5117
   711
		
Evan@11752
   712
	} else if ([extension caseInsensitiveCompare:@"AdiumMessageStyle"] == NSOrderedSame) {
Peter@13081
   713
		destination = [AISearchPathForDirectoriesInDomains(AIMessageStylesDirectory, NSUserDomainMask, /*expandTilde*/ YES) objectAtIndex:0];
toby@11276
   714
		fileDescription = AILocalizedString(@"message style",nil);
toby@11276
   715
		prefsButton = AILocalizedString(@"Open Message Prefs",nil);
toby@11276
   716
		prefsCategory = @"messages";
Evan@11752
   717
	} else if ([extension caseInsensitiveCompare:@"ListLayout"] == NSOrderedSame) {
Peter@13081
   718
		destination = [AISearchPathForDirectoriesInDomains(AIContactListDirectory, NSUserDomainMask, /*expandTilde*/ YES) objectAtIndex:0];
eevyl@6856
   719
		fileDescription = AILocalizedString(@"contact list layout",nil);
adamiser@9964
   720
		prefsButton = AILocalizedString(@"Open Appearance Prefs",nil);
adamiser@9964
   721
		prefsCategory = @"appearance";
Evan@10297
   722
		
Evan@11752
   723
	} else if ([extension caseInsensitiveCompare:@"ListTheme"] == NSOrderedSame) {
Peter@13081
   724
		destination = [AISearchPathForDirectoriesInDomains(AIContactListDirectory, NSUserDomainMask, /*expandTilde*/ YES) objectAtIndex:0];
eevyl@6856
   725
		fileDescription = AILocalizedString(@"contact list theme",nil);
adamiser@9964
   726
		prefsButton = AILocalizedString(@"Open Appearance Prefs",nil);
adamiser@9964
   727
		prefsCategory = @"appearance";
Evan@10297
   728
		
Evan@11752
   729
	} else if ([extension caseInsensitiveCompare:@"AdiumServiceIcons"] == NSOrderedSame) {
Peter@13081
   730
		destination = [AISearchPathForDirectoriesInDomains(AIServiceIconsDirectory, NSUserDomainMask, /*expandTilde*/ YES) objectAtIndex:0];
Evan@10297
   731
		fileDescription = AILocalizedString(@"service icons",nil);
Evan@10297
   732
		prefsButton = AILocalizedString(@"Open Appearance Prefs",nil);
Evan@10297
   733
		prefsCategory = @"appearance";
Evan@10297
   734
		
Evan@11752
   735
	} else if ([extension caseInsensitiveCompare:@"AdiumStatusIcons"] == NSOrderedSame) {
Evan@11978
   736
		NSString	*packName = [[filename lastPathComponent] stringByDeletingPathExtension];
Evan@11979
   737
/*
Evan@11979
   738
 //Can't do this because the preferenceController isn't ready yet
Evan@11979
   739
 NSString	*defaultPackName = [[self preferenceController] defaultPreferenceForKey:@"Status Icon Pack"
Evan@11979
   740
																			  group:@"Appearance"
Evan@11979
   741
																			 object:nil];
Evan@11979
   742
*/
David@16968
   743
		NSString	*defaultPackName = @"iBubble Status";
Evan@11979
   744
Evan@11978
   745
		if (![packName isEqualToString:defaultPackName]) {
Peter@13081
   746
			destination = [AISearchPathForDirectoriesInDomains(AIStatusIconsDirectory, NSUserDomainMask, /*expandTilde*/ YES) objectAtIndex:0];
Evan@11978
   747
			fileDescription = AILocalizedString(@"status icons",nil);
Evan@11978
   748
			prefsButton = AILocalizedString(@"Open Appearance Prefs",nil);
Evan@11978
   749
			prefsCategory = @"appearance";
Evan@11978
   750
		} else {
Evan@11978
   751
			errorMessage = [NSString stringWithFormat:AILocalizedString(@"%@ is the name of the default status icon pack; this pack therefore can not be installed.",nil),
Evan@11978
   752
				packName];
Evan@11978
   753
		}
Evan@2448
   754
	}
Evan@2781
   755
Evan@11752
   756
    if (destination) {
Evan@2036
   757
        NSString    *destinationFilePath = [destination stringByAppendingPathComponent:[filename lastPathComponent]];
Evan@2036
   758
        
Evan@4867
   759
        NSString	*alertTitle = nil;
Evan@4867
   760
        NSString	*alertMsg = nil;
Evan@4867
   761
		NSString	*format;
dchoby98@4680
   762
		
Evan@11752
   763
		if ([filename isEqualToString:destinationFilePath]) {
dchoby98@2987
   764
			// Don't copy the file if it's already in the right place!!
eevyl@6470
   765
			alertTitle= AILocalizedString(@"Installation Successful","Title of installation successful window");
Evan@4867
   766
			
eevyl@6470
   767
			format = AILocalizedString(@"Installation of the %@ %@ was successful because the file was already in the correct location.",
Evan@4867
   768
									   "Installation introduction, like 'Installation of the message style Fiat was successful...'.");
Evan@4867
   769
			
Evan@4867
   770
			alertMsg = [NSString stringWithFormat:format,
Evan@4867
   771
				fileDescription,
Evan@4867
   772
				[[filename lastPathComponent] stringByDeletingPathExtension]];
Evan@4867
   773
			
dchoby98@2987
   774
		} else {
Evan@3858
   775
			//Trash the old file if one exists (since we know it isn't ourself)
dchoby98@2987
   776
			[[NSFileManager defaultManager] trashFileAtPath:destinationFilePath];
dchoby98@2987
   777
			
Evan@3858
   778
			//Ensure the directory exists
Evan@3858
   779
			[[NSFileManager defaultManager] createDirectoryAtPath:destination attributes:nil];
Evan@3858
   780
			
dchoby98@2987
   781
			//Perform the copy and display an alert informing the user of its success or failure
dchoby98@2987
   782
			if ([[NSFileManager defaultManager] copyPath:filename 
dchoby98@2987
   783
												  toPath:destinationFilePath 
Evan@11752
   784
												 handler:nil]) {
dchoby98@2987
   785
				
eevyl@6470
   786
				alertTitle = AILocalizedString(@"Installation Successful","Title of installation successful window");
eevyl@6470
   787
				alertMsg = [NSString stringWithFormat:AILocalizedString(@"Installation of the %@ %@ was successful.",
Evan@4867
   788
																		   "Installation sentence, like 'Installation of the message style Fiat was successful.'."),
Evan@4867
   789
					fileDescription,
Evan@4867
   790
					[[filename lastPathComponent] stringByDeletingPathExtension]];
Evan@4867
   791
				
Evan@11752
   792
				if (requiresRestart) {
eevyl@6470
   793
					alertMsg = [alertMsg stringByAppendingString:AILocalizedString(@" Please restart Adium.",nil)];
dchoby98@2987
   794
				}
dchoby98@2987
   795
				
dchoby98@2987
   796
				success = YES;
Evan@11752
   797
			} else {
eevyl@6470
   798
				alertTitle = AILocalizedString(@"Installation Failed","Title of installation failed window");
eevyl@6470
   799
				alertMsg = [NSString stringWithFormat:AILocalizedString(@"Installation of the %@ %@ was unsuccessful.",
Evan@4867
   800
																		"Installation failed sentence, like 'Installation of the message style Fiat was unsuccessful.'."),
Evan@4867
   801
					fileDescription,
Evan@4867
   802
					[[filename lastPathComponent] stringByDeletingPathExtension]];
dchoby98@2987
   803
			}
dchoby98@2987
   804
		}
Evan@4194
   805
		
Evan@17646
   806
		[[self notificationCenter] postNotificationName:AIXtrasDidChangeNotification
Evan@5186
   807
												 object:[[filename lastPathComponent] pathExtension]];
Evan@4867
   808
		
Evan@4867
   809
        buttonPressed = NSRunInformationalAlertPanel(alertTitle,alertMsg,nil,prefsButton,nil);
dchoby98@4680
   810
		
dchoby98@4680
   811
		// User clicked the "open prefs" button
Evan@11752
   812
		if (buttonPressed == NSAlertAlternateReturn) {
Evan@5780
   813
			//If we're done loading the app, open the prefs now; if not, it'll be done once the load is finished
Evan@5780
   814
			//so the controllers and plugins have had a chance to initialize
Evan@11752
   815
			if (completedApplicationLoad) {
Evan@5163
   816
				[self openAppropriatePreferencesIfNeeded];
Evan@5780
   817
			}
Evan@11752
   818
		} else {
Evan@5780
   819
			//If the user didn't press the "open prefs" button, clear the pref opening information
Evan@9306
   820
			[prefsCategory release]; prefsCategory = nil;
Evan@5780
   821
			[advancedPrefsName release]; advancedPrefsName = nil;
dchoby98@4680
   822
		}
Evan@5780
   823
		
Evan@11752
   824
    } else {
Evan@11752
   825
		if (!errorMessage) {
eevyl@6470
   826
			errorMessage = AILocalizedString(@"An error occurred while installing the X(tra).",nil);
Evan@4867
   827
		}
Evan@4867
   828
		
eevyl@6470
   829
		NSRunAlertPanel(AILocalizedString(@"Installation Failed","Title of installation failed window"),
Evan@4867
   830
						errorMessage,
Evan@4867
   831
						nil,nil,nil);
Evan@4867
   832
	}
Evan@2022
   833
Evan@2022
   834
    return success;
Evan@1719
   835
}
Evan@1719
   836
Evan@7571
   837
- (BOOL)application:(NSApplication *)theApplication openTempFile:(NSString *)filename
Evan@7571
   838
{
Evan@7572
   839
	BOOL success;
Evan@7572
   840
	
Evan@7572
   841
	success = [self application:theApplication openFile:filename];
Evan@7572
   842
	[[NSFileManager defaultManager] removeFileAtPath:filename handler:nil];
Evan@7572
   843
	
Catfish_Man@12140
   844
	return success;
Evan@7571
   845
}
Evan@7571
   846
Evan@5163
   847
- (void)openAppropriatePreferencesIfNeeded
Evan@5163
   848
{
Evan@11752
   849
	if (prefsCategory) {
Evan@11752
   850
		if ([prefsCategory isEqualToString:@"advanced"]) {
Evan@9306
   851
			[preferenceController openPreferencesToAdvancedPane:advancedPrefsName];
Evan@11752
   852
		} else {
Evan@9306
   853
			[preferenceController openPreferencesToCategoryWithIdentifier:prefsCategory];
Evan@5163
   854
		}
Evan@5163
   855
		
Evan@9306
   856
		[prefsCategory release]; prefsCategory = nil;
Evan@5163
   857
	}
Evan@5163
   858
}
Evan@5163
   859
Evan@9366
   860
/*!
Evan@13647
   861
 * @brief Returns the location of Adium's preference folder
Evan@13647
   862
 * 
Evan@13647
   863
 * This may be specified in our bundle's info dictionary keyed as PORTABLE_ADIUM_KEY
Evan@13647
   864
 * or, by default, be within the system's 'application support' directory.
Evan@13647
   865
 */
Evan@13647
   866
- (NSString *)applicationSupportDirectory
Evan@13647
   867
{
Evan@13647
   868
	//Path to the preferences folder
Evan@13647
   869
	static NSString *_preferencesFolderPath = nil;
Evan@13647
   870
	
Evan@13647
   871
    //Determine the preferences path if neccessary
Evan@13647
   872
	if (!_preferencesFolderPath) {
Evan@13647
   873
		_preferencesFolderPath = [[[[[NSBundle mainBundle] infoDictionary] objectForKey:PORTABLE_ADIUM_KEY] stringByExpandingTildeInPath] retain];
Evan@13647
   874
		if (!_preferencesFolderPath)
Evan@13647
   875
			_preferencesFolderPath = [[[[NSHomeDirectory() stringByAppendingPathComponent:@"Library"] stringByAppendingPathComponent:@"Application Support"] stringByAppendingPathComponent:@"Adium 2.0"] retain];
Evan@13647
   876
	}
Evan@13647
   877
	
Evan@13647
   878
	return _preferencesFolderPath;
Evan@13647
   879
}
Evan@13647
   880
Evan@13647
   881
/*!
Evan@9250
   882
 * @brief Create a resource folder in the Library/Application\ Support/Adium\ 2.0 folder.
Evan@9250
   883
 *
Evan@9250
   884
 * Pass it the name of the folder (e.g. @"Scripts").
Evan@9250
   885
 * If it is found to already in a library folder, return that pathname, using the same order of preference as
Evan@9250
   886
 * -[AIAdium resourcePathsForName:]. Otherwise, create it in the user library and return the pathname to it.
Evan@9250
   887
 */
Peter@3954
   888
- (NSString *)createResourcePathForName:(NSString *)name
Peter@3954
   889
{
Evan@7279
   890
    NSString		*targetPath;    //This is the subfolder for the user domain (i.e. ~/L/AS/Adium\ 2.0).
Evan@7279
   891
    NSFileManager	*defaultManager;
Evan@7279
   892
    NSArray			*existingResourcePaths;
Peter@3954
   893
Evan@7279
   894
	defaultManager = [NSFileManager defaultManager];
Evan@7279
   895
	existingResourcePaths = [self resourcePathsForName:name];
Evan@13647
   896
	targetPath = [[self applicationSupportDirectory] stringByAppendingPathComponent:name];	
Evan@7279
   897
	
Evan@7279
   898
    /*
Evan@7279
   899
	 If the targetPath doesn't exist, create it, as this method was called to ensure that it exists
Evan@7279
   900
	 for creating files in the user domain.
Evan@7279
   901
	 */
Evan@11752
   902
    if ([existingResourcePaths indexOfObject:targetPath] == NSNotFound) {
Evan@11752
   903
        if (![defaultManager createDirectoryAtPath:targetPath attributes:nil]) {
Evan@7279
   904
			BOOL error;
Evan@7279
   905
			
Evan@7279
   906
			//If the directory could not be created, there may be a file in the way. Death to file.
Evan@7279
   907
			error = ![defaultManager trashFileAtPath:targetPath];
Peter@3954
   908
Evan@7279
   909
			if (!error) error = ![defaultManager createDirectoryAtPath:targetPath attributes:nil];
Evan@7279
   910
Evan@11752
   911
			if (error) {
Evan@7279
   912
				targetPath = nil;
Evan@7279
   913
				
Evan@7352
   914
				int result;
Evan@7352
   915
				result = NSRunCriticalAlertPanel([NSString stringWithFormat:AILocalizedString(@"Could not create the %@ folder.",nil), name],
Evan@7352
   916
												 AILocalizedString(@"Try running Repair Permissions from Disk Utility.",nil),
Evan@7352
   917
												 AILocalizedString(@"OK",nil), 
Evan@7352
   918
												 AILocalizedString(@"Launch Disk Utility",nil), 
Evan@7352
   919
												 nil);
Evan@11752
   920
				if (result == NSAlertAlternateReturn) {
Evan@7352
   921
					[[NSWorkspace sharedWorkspace] launchApplication:@"Disk Utility"];
Evan@7352
   922
				}
Evan@7279
   923
			}
Peter@4107
   924
		}
Peter@3954
   925
    } else {
Evan@7279
   926
        targetPath = [existingResourcePaths objectAtIndex:0];
Peter@3954
   927
    }
Peter@3954
   928
Peter@3954
   929
    return targetPath;
Peter@3954
   930
}
Peter@3954
   931
Evan@9366
   932
/*!
Evan@9250
   933
 * @brief Return zero or more resource pathnames to an filename 
Evan@9250
   934
 *
Evan@9250
   935
 * Searches in the Application Support folders and the Resources/ folder of the Adium.app bundle.
Evan@9250
   936
 * Only those pathnames that exist are returned.  The Adium bundle's resource path will be the last item in the array,
Evan@9250
   937
 * so precedence is given to the user and system Application Support folders.
Evan@9250
   938
 * 
Evan@9250
   939
 * Pass nil to receive an array of paths to existing Adium Application Support folders (plus the Resouces folder).
Evan@9250
   940
 *
Evan@9250
   941
 * Example: If you call[adium resourcePathsForName:@"Scripts"], and there's a
Evan@9250
   942
 * Scripts folder in ~/Library/Application Support/Adium\ 2.0 and in /Library/Application Support/Adium\ 2.0, but not
Evan@9250
   943
 * in /System/Library/ApplicationSupport/Adium\ 2.0 or /Network/Library/Application Support/Adium\ 2.0.
Evan@9250
   944
 * The array you get back will be { @"/Users/username/Library/Application Support/Adium 2.0/Scripts",
Evan@9250
   945
 * @"/Library/Application Support/Adium 2.0/Scripts" }.
Evan@9250
   946
 *
Evan@9250
   947
 * @param name The full name (including extension as appropriate) of the resource for which to search
Evan@9250
   948
 */
hanji@3518
   949
- (NSArray *)resourcePathsForName:(NSString *)name
Peter@3104
   950
{
Evan@5128
   951
	NSArray			*librarySearchPaths;
Evan@5128
   952
	NSEnumerator	*searchPathEnumerator;
Evan@5128
   953
	NSString		*adiumFolderName, *path;
Evan@5128
   954
	NSMutableArray  *pathArray = [NSMutableArray arrayWithCapacity:4];
Evan@7279
   955
	NSFileManager	*defaultManager = [NSFileManager defaultManager];
Evan@7279
   956
	BOOL			isDir;
Evan@7279
   957
			
Evan@13644
   958
	adiumFolderName = (name ?
Evan@13644
   959
					   [[@"Application Support" stringByAppendingPathComponent:@"Adium 2.0"] stringByAppendingPathComponent:name] :
Evan@13644
   960
					   [@"Application Support" stringByAppendingPathComponent:@"Adium 2.0"]);
Evan@5128
   961
Evan@5128
   962
	//Find Library directories in all domains except /System (as of Panther, that's ~/Library, /Library, and /Network/Library)
Evan@5128
   963
	librarySearchPaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSAllDomainsMask - NSSystemDomainMask, YES);
Evan@5128
   964
	searchPathEnumerator = [librarySearchPaths objectEnumerator];
Evan@5128
   965
Evan@5128
   966
	//Copy each discovered path into the pathArray after adding our subfolder path
Evan@11752
   967
	while ((path = [searchPathEnumerator nextObject])) {
Evan@7279
   968
		NSString	*fullPath;
Evan@7279
   969
		
Evan@7279
   970
		fullPath = [path stringByAppendingPathComponent:adiumFolderName];
Evan@11752
   971
		if (([defaultManager fileExistsAtPath:fullPath isDirectory:&isDir]) &&
Evan@13644
   972
			(isDir)) {
Evan@7279
   973
			
adamiser@5942
   974
			[pathArray addObject:fullPath];
adamiser@5942
   975
		}
Peter@3104
   976
	}
hanji@3518
   977
	
Evan@13644
   978
	/* Check our application support directory directly. It may have been covered by the NSSearchPathForDirectoriesInDomains() search,
Evan@13644
   979
	 * or it may be distinct via the Portable Adium preference.
Evan@13644
   980
	 */
Evan@13644
   981
	path = (name ?
Evan@13647
   982
			[[self applicationSupportDirectory] stringByAppendingPathComponent:name] :
Evan@13647
   983
			[self applicationSupportDirectory]);
Evan@13656
   984
	if (![pathArray containsObject:path] &&
Evan@13644
   985
		([defaultManager fileExistsAtPath:path isDirectory:&isDir]) &&
Evan@13644
   986
		(isDir)) {
Evan@13647
   987
		//Our application support directory should always be first
Evan@13646
   988
		if ([pathArray count]) {
Evan@13646
   989
			[pathArray insertObject:path atIndex:0];
Evan@13646
   990
		} else {
Evan@13646
   991
			[pathArray addObject:path];			
Evan@13646
   992
		}
Evan@13644
   993
	}
Evan@13644
   994
Evan@5128
   995
	//Add the path to the resource in Adium's bundle
Evan@11752
   996
	if (name) {
Peter@3871
   997
		path = [[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:name] stringByExpandingTildeInPath];
Evan@11752
   998
		if (([defaultManager fileExistsAtPath:path isDirectory:&isDir]) &&
Evan@11752
   999
		   (isDir)) {
Peter@3871
  1000
			[pathArray addObject:path];
Peter@3871
  1001
		}
Peter@3871
  1002
	}
Peter@3871
  1003
    
Catfish_Man@12140
  1004
	return pathArray;
Peter@3104
  1005
}
Peter@4653
  1006
samsamoa@10651
  1007
samsamoa@10651
  1008
/*!
samsamoa@10651
  1009
 * @brief Returns an array of the paths to all of the resources for a given name, filtering out those without a certain extension
samsamoa@10651
  1010
 * @param name The full name (including extension as appropriate) of the resource for which to search
samsamoa@10651
  1011
 * @param extensions The extension(s) of the resources for which to search, either an NSString or an NSArray
samsamoa@10651
  1012
 */
samsamoa@10651
  1013
- (NSArray *)allResourcesForName:(NSString *)name withExtensions:(id)extensions {
Peter@10864
  1014
	NSMutableArray *resources = [NSMutableArray array];
samsamoa@10651
  1015
	NSEnumerator *pathEnumerator;
samsamoa@10651
  1016
	NSEnumerator *resourceEnumerator;
samsamoa@10651
  1017
	NSString *resourceDir;
samsamoa@10651
  1018
	NSString *resourcePath;
samsamoa@10651
  1019
	BOOL extensionsArray = [extensions isKindOfClass:[NSArray class]];
samsamoa@10651
  1020
	NSEnumerator *extensionsEnumerator;
samsamoa@10651
  1021
	NSString *extension;
samsamoa@10651
  1022
	
samsamoa@10651
  1023
	// Get every path that can contain these resources
samsamoa@10651
  1024
	pathEnumerator = [[self resourcePathsForName:name] objectEnumerator];
samsamoa@10651
  1025
	
samsamoa@11441
  1026
	while ((resourceDir = [pathEnumerator nextObject])) {
samsamoa@10651
  1027
		resourceEnumerator = [[[NSFileManager defaultManager] directoryContentsAtPath:resourceDir] objectEnumerator];
samsamoa@10651
  1028
		
samsamoa@11441
  1029
		while ((resourcePath = [resourceEnumerator nextObject])) {
samsamoa@10651
  1030
			// Add each resource to the array
samsamoa@10651
  1031
			if (extensionsArray) {
samsamoa@10651
  1032
				extensionsEnumerator = [extensions objectEnumerator];
samsamoa@11441
  1033
				while ((extension = [extensionsEnumerator nextObject])) {
samsamoa@10651
  1034
					if ([[resourcePath pathExtension] caseInsensitiveCompare:extension] == NSOrderedSame)
samsamoa@10651
  1035
						[resources addObject:[resourceDir stringByAppendingPathComponent:resourcePath]];
samsamoa@10651
  1036
				}
samsamoa@10651
  1037
			}
samsamoa@10651
  1038
			else {
samsamoa@10651
  1039
				if ([[resourcePath pathExtension] caseInsensitiveCompare:extensions] == NSOrderedSame)
samsamoa@10651
  1040
					[resources addObject:[resourceDir stringByAppendingPathComponent:resourcePath]];
samsamoa@10651
  1041
			}
samsamoa@10651
  1042
		}
samsamoa@10651
  1043
	}
samsamoa@10651
  1044
samsamoa@10651
  1045
	return resources;
samsamoa@10651
  1046
}
samsamoa@10651
  1047
Evan@9366
  1048
/*!
Evan@9250
  1049
 * @brief Return the path to be used for caching files for this user.
Evan@9250
  1050
 *
Evan@9250
  1051
 * @result A cached, tilde-expanded full path.
Evan@9250
  1052
 */
Evan@8541
  1053
- (NSString *)cachesPath
Evan@8541
  1054
{
Evan@8541
  1055
	static NSString *cachesPath = nil;
Evan@8557
  1056
Evan@11752
  1057
	if (!cachesPath) {
Evan@8557
  1058
		NSString		*generalAdiumCachesPath;
Evan@8557
  1059
		NSFileManager	*defaultManager = [NSFileManager defaultManager];
Evan@8557
  1060
Peter@11624
  1061
		generalAdiumCachesPath = [[[NSHomeDirectory() stringByAppendingPathComponent:@"Library"] stringByAppendingPathComponent:@"Caches"] stringByAppendingPathComponent:@"Adium"];
Evan@8557
  1062
		cachesPath = [[generalAdiumCachesPath stringByAppendingPathComponent:[[self loginController] currentUser]] retain];
Evan@8541
  1063
Evan@8541
  1064
		//Ensure our cache path exists
Evan@11752
  1065
		if ([defaultManager createDirectoriesForPath:cachesPath]) {
Evan@8557
  1066
			//If we have to make directories, try to move old cache files into the new directory
Evan@8557
  1067
			NSEnumerator	*enumerator;
Evan@8557
  1068
			NSString		*filename;
Evan@8557
  1069
			BOOL			isDir;
Evan@8557
  1070
Evan@8557
  1071
			enumerator = [[defaultManager directoryContentsAtPath:generalAdiumCachesPath] objectEnumerator];
Evan@11752
  1072
			while ((filename = [enumerator nextObject])) {
Evan@8557
  1073
				NSString	*fullPath = [generalAdiumCachesPath stringByAppendingPathComponent:filename];
Evan@8557
  1074
				
Evan@11752
  1075
				if (([defaultManager fileExistsAtPath:fullPath isDirectory:&isDir]) &&
Evan@11752
  1076
				   (!isDir)) {
Evan@8557
  1077
					[defaultManager movePath:fullPath
Evan@8557
  1078
									  toPath:[cachesPath stringByAppendingPathComponent:filename]
Evan@8557
  1079
									 handler:nil];
Evan@8557
  1080
				}
Evan@8557
  1081
			}
Evan@8557
  1082
		}
Evan@8541
  1083
	}
Evan@8557
  1084
	
Evan@8541
  1085
	return cachesPath;
Evan@8541
  1086
}
Evan@8541
  1087
Peter@8206
  1088
- (NSString *)pathOfPackWithName:(NSString *)name extension:(NSString *)extension resourceFolderName:(NSString *)folderName
Evan@8200
  1089
{
Evan@8200
  1090
	NSFileManager	*fileManager = [NSFileManager defaultManager];
Evan@8200
  1091
    NSString		*packFileName = [name stringByAppendingPathExtension:extension];
Evan@8200
  1092
    NSEnumerator	*enumerator = [[self resourcePathsForName:folderName] objectEnumerator];
Evan@8200
  1093
    NSString		*resourcePath;
Evan@8200
  1094
Evan@8200
  1095
	//Search all our resource paths for the requested pack
Evan@11752
  1096
    while ((resourcePath = [enumerator nextObject])) {
Evan@8200
  1097
		NSString *packPath = [resourcePath stringByAppendingPathComponent:packFileName];
Catfish_Man@12140
  1098
		if ([fileManager fileExistsAtPath:packPath]) return [packPath stringByExpandingTildeInPath];
Evan@8200
  1099
	}
Evan@8200
  1100
Catfish_Man@12140
  1101
    return nil;	
Evan@8200
  1102
}
Evan@8200
  1103
Evan@18014
  1104
- (void)systemTimeZoneDidChange:(NSNotification *)inNotification
Evan@18014
  1105
{
Evan@18014
  1106
	[NSTimeZone resetSystemTimeZone];
Evan@18014
  1107
}
Evan@18014
  1108
Evan@18014
  1109
- (NSApplication *)application
Evan@18014
  1110
{
Evan@18014
  1111
	return [NSApplication sharedApplication];
Evan@18014
  1112
}
Evan@18014
  1113
Evan@5707
  1114
#pragma mark Scripting
Evan@5707
  1115
- (BOOL)application:(NSApplication *)sender delegateHandlesKey:(NSString *)key {
Evan@5707
  1116
	BOOL handleKey = NO;
Evan@5780
  1117
	
Evan@11752
  1118
	if ([key isEqualToString:@"applescriptabilityController"] || 
Evan@11752
  1119
	   [key isEqualToString:@"interfaceController"] ) {
Evan@5707
  1120
		handleKey = YES;
Evan@5707
  1121
		
Evan@5707
  1122
	}
Evan@5707
  1123
	
Evan@5707
  1124
	return handleKey;
Evan@5707
  1125
}
Evan@5707
  1126
Evan@17170
  1127
#pragma mark Help
Evan@17170
  1128
- (void)configureHelp
Evan@17170
  1129
{
Evan@17170
  1130
	CFBundleRef myApplicationBundle;
Evan@17170
  1131
	CFURLRef myBundleURL;
Evan@17170
  1132
	FSRef myBundleRef;
Evan@17170
  1133
Evan@17170
  1134
	if ((myApplicationBundle = CFBundleGetMainBundle())) {
Evan@17170
  1135
		myBundleURL = CFBundleCopyBundleURL(myApplicationBundle);
Evan@17170
  1136
Evan@17170
  1137
		if (CFURLGetFSRef(myBundleURL, &myBundleRef)) {
Evan@17170
  1138
			AHRegisterHelpBook(&myBundleRef);
Evan@17170
  1139
		}
Evan@17170
  1140
	}
Evan@17170
  1141
}
Evan@17170
  1142
David@14622
  1143
#pragma mark Sparkle Delegate Methods
David@14622
  1144
David@17973
  1145
#define BETA_UPDATE_DICT [NSDictionary dictionaryWithObjectsAndKeys:@"type", @"key", @"Update Type", @"visibleKey", @"beta", @"value", @"Beta or Release Versions", @"visibleValue", nil]
David@17973
  1146
#define RELEASE_UPDATE_DICT [NSDictionary dictionaryWithObjectsAndKeys:@"type", @"key", @"Update Type", @"visibleKey", @"release", @"value", @"Release Versions Only", @"visibleValue", nil]
David@17973
  1147
Evan@18281
  1148
#ifdef BETA_RELEASE
Evan@18232
  1149
//For a beta release, always use the beta appcast
Evan@18232
  1150
#define UPDATE_TYPE_DICT BETA_UPDATE_DICT
David@17308
  1151
#else
Evan@18232
  1152
//For a release, use the beta appcast if AIAlwaysUpdateToBetas is enabled; otherwise, use the release appcast
Evan@18232
  1153
#define UPDATE_TYPE_DICT ([[NSUserDefaults standardUserDefaults] boolForKey:@"AIAlwaysUpdateToBetas"] ? BETA_UPDATE_DICT : RELEASE_UPDATE_DICT)
David@17308
  1154
#endif
David@17308
  1155
Peter@18554
  1156
//The first generation ended with 1.0.5 and 1.1. Our Sparkle Plus up to that point had a bug that left it unable to properly handle the sparkle:minimumSystemVersion element.
Peter@18554
  1157
//The second generation began with 1.0.6 and 1.1.1, with a Sparkle Plus that can handle that element.
Peter@18554
  1158
#define UPDATE_GENERATION_DICT [NSDictionary dictionaryWithObjectsAndKeys:@"generation", @"key", @"Appcast generation number", @"visibleKey", @"2", @"value", @"2", @"visibleValue", nil]
Peter@18554
  1159
David@14698
  1160
/* This method gives the delegate the opportunity to customize the information that will
David@14698
  1161
 * be included with update checks.  Add or remove items from the dictionary as desired.
David@14698
  1162
 * Each entry in profileInfo is an NSDictionary with the following keys:
Evan@16215
  1163
 *		key: 		The key to be used  when reporting data to the server
Evan@16215
  1164
 *		visibleKey:	Alternate version of key to be used in UI displays of profile information
Evan@16215
  1165
 *		value:		Value to be used when reporting data to the server
Evan@16215
  1166
 *		visibleValue:	Alternate version of value to be used in UI displays of profile information.
David@14698
  1167
 */
David@15946
  1168
- (NSMutableArray *)updaterCustomizeProfileInfo:(NSMutableArray *)profileInfo
David@16296
  1169
{
David@16296
  1170
	NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
Evan@18232
  1171
	
Evan@18232
  1172
	//If we're not sending profile information, return just the type of update we're looking for
Peter@18554
  1173
 	if ([[defaults objectForKey:SUSendProfileInfoKey] boolValue]) {
Peter@18554
  1174
 		int now = [[NSCalendarDate date] dayOfCommonEra];
Peter@18554
  1175
 
Peter@18554
  1176
 		if (abs([defaults integerForKey:@"AILastSubmittedProfileDate2"] - now) >= 7) {
Peter@18554
  1177
 			[defaults setInteger:now forKey:@"AILastSubmittedProfileDate2"];
Peter@18554
  1178
 			
Peter@18554
  1179
 			NSString *value = ([defaults boolForKey:@"AIHasSentSparkleProfileInfo"]) ? @"no" : @"yes";
Peter@18554
  1180
 			
Peter@18554
  1181
 			NSDictionary *entry = [NSDictionary dictionaryWithObjectsAndKeys:
Peter@18554
  1182
 				@"FirstSubmission", @"key", 
Peter@18554
  1183
 				@"First Time Submitting Profile Information", @"visibleKey",
Peter@18554
  1184
 				value, @"value",
Peter@18554
  1185
 				value, @"visibleValue",
Peter@18554
  1186
 				nil];
Peter@18554
  1187
 			
Peter@18554
  1188
 			[profileInfo addObject:entry];
Peter@18554
  1189
 			
Peter@18554
  1190
 			[defaults setBool:YES forKey:@"AIHasSentSparkleProfileInfo"];
Peter@18554
  1191
 			
Peter@18554
  1192
 			/*************** Include info about what IM services are used ************/
Peter@18554
  1193
 			NSMutableString *accountInfo = [NSMutableString string];
Peter@18554
  1194
 			NSCountedSet *condensedAccountInfo = [NSCountedSet set];
Peter@18554
  1195
 			NSEnumerator *accountEnu = [[[self accountController] accounts] objectEnumerator];
Peter@18554
  1196
 			AIAccount *account = nil;
Peter@18554
  1197
 			while ((account = [accountEnu nextObject])) {
Peter@18554
  1198
 				NSString *serviceID = [account serviceID];
Peter@18554
  1199
 				[accountInfo appendFormat:@"%@, ", serviceID];
Peter@18554
  1200
 				if([serviceID isEqualToString:@"Yahoo! Japan"]) serviceID = @"YJ";
Peter@18554
  1201
 				[condensedAccountInfo addObject:[NSString stringWithFormat:@"%@", [serviceID substringToIndex:2]]]; 
Peter@18554
  1202
 			}
Peter@18554
  1203
 			
Peter@18554
  1204
 			NSMutableString *accountInfoString = [NSMutableString string];
Peter@18554
  1205
 			NSEnumerator *infoEnu = [[[condensedAccountInfo allObjects] sortedArrayUsingSelector:@selector(compare:)] objectEnumerator];
Peter@18554
  1206
 			while ((value = [infoEnu nextObject]))
Peter@18554
  1207
 				[accountInfoString appendFormat:@"%@%d", value, [condensedAccountInfo countForObject:value]];
Peter@18554
  1208
 			
Peter@18554
  1209
 			entry = [NSDictionary dictionaryWithObjectsAndKeys:
Peter@18554
  1210
 				@"IMServices", @"key", 
Peter@18554
  1211
 				@"IM Services Used", @"visibleKey",
Peter@18554
  1212
 				accountInfoString, @"value",
Peter@18554
  1213
 				accountInfo, @"visibleValue",
Peter@18554
  1214
 				nil];
Peter@18554
  1215
 			[profileInfo addObject:entry];
Peter@18554
  1216
 		}
Peter@18554
  1217
 	}
Peter@18554
  1218
 
Peter@18554
  1219
 	[profileInfo addObject:UPDATE_GENERATION_DICT];
Peter@18554
  1220
 	[profileInfo addObject:UPDATE_TYPE_DICT];
Peter@18554
  1221
 	return profileInfo;
David@14622
  1222
}
David@14622
  1223
David@16630
  1224
- (NSComparisonResult) compareVersion:(NSString *)newVersion toVersion:(NSString *)currentVersion
David@16630
  1225
{
David@16630
  1226
	//Allow updating from betas to anything, and anything to non-betas
David@16630
  1227
	//Careful! a15 is fine, but A15 is not, because it would hit the A in Adium.
David@16630
  1228
	NSCharacterSet *guardCharacters = [NSCharacterSet characterSetWithCharactersInString:@"abBrcRC"];
David@16630
  1229
	if([currentVersion rangeOfCharacterFromSet:guardCharacters].location != NSNotFound || !([newVersion rangeOfCharacterFromSet:guardCharacters].location != NSNotFound))
David@17097
  1230
		return AICustomVersionComparison(newVersion, currentVersion); //handles rc > b > a properly
David@16630
  1231
	else 
David@16630
  1232
		return NSOrderedSame;
David@16630
  1233
}
David@16630
  1234
adamiser@3
  1235
@end