Plugins/Purple Service/ESPurpleJabberAccountViewController.m
author Zachary West <zacw@adium.im>
Fri Oct 16 10:54:20 2009 -0400 (2009-10-16)
changeset 2615 bcbb03bada1a
parent 2466 fc5fc792c0aa
child 2634 160b373a9e46
permissions -rw-r--r--
Add a "BOSH Server" option for Jabber accounts.
David@0
     1
/* 
David@0
     2
 * Adium is the legal property of its developers, whose names are listed in the copyright file included
David@0
     3
 * with this source distribution.
David@0
     4
 * 
David@0
     5
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
David@0
     6
 * General Public License as published by the Free Software Foundation; either version 2 of the License,
David@0
     7
 * or (at your option) any later version.
David@0
     8
 * 
David@0
     9
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
David@0
    10
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
David@0
    11
 * Public License for more details.
David@0
    12
 * 
David@0
    13
 * You should have received a copy of the GNU General Public License along with this program; if not,
David@0
    14
 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
David@0
    15
 */
David@0
    16
David@0
    17
#import "ESPurpleJabberAccountViewController.h"
David@0
    18
#import <Adium/AIAccount.h>
David@0
    19
#import <Adium/AIContactControllerProtocol.h>
David@0
    20
#import <Adium/AIService.h>
David@194
    21
#import <Adium/AIContactList.h>
catfish@2093
    22
#import <SystemConfiguration/SystemConfiguration.h>
David@0
    23
am@2459
    24
#define SERVERFEEDRSSURL @"http://xmpp.org/services/services-full.xml"
David@0
    25
David@0
    26
@implementation ESPurpleJabberAccountViewController
David@0
    27
David@0
    28
- (NSString *)nibName{
David@0
    29
    return @"ESPurpleJabberAccountView";
David@0
    30
}
David@0
    31
David@0
    32
- (void)awakeFromNib
David@0
    33
{
David@0
    34
	[super awakeFromNib];
David@0
    35
	
David@0
    36
	[checkBox_checkMail setEnabled:NO];
David@0
    37
	
David@1109
    38
	[[NSNotificationCenter defaultCenter] addObserver:self
David@0
    39
								   selector:@selector(contactListChanged:)
David@0
    40
									   name:Contact_ListChanged
David@0
    41
									 object:nil];
David@0
    42
}
David@0
    43
David@0
    44
//Configure our controls
David@0
    45
- (void)configureForAccount:(AIAccount *)inAccount
David@0
    46
{
David@0
    47
    [super configureForAccount:inAccount];
David@0
    48
	
David@0
    49
	//Connection security
David@0
    50
	[checkBox_forceOldSSL setState:[[account preferenceForKey:KEY_JABBER_FORCE_OLD_SSL group:GROUP_ACCOUNT_STATUS] boolValue]];
David@0
    51
	[checkBox_requireTLS setState:[[account preferenceForKey:KEY_JABBER_REQUIRE_TLS group:GROUP_ACCOUNT_STATUS] boolValue]];
David@0
    52
	[checkBox_checkCertificates setState:[account preferenceForKey:KEY_JABBER_VERIFY_CERTS group:GROUP_ACCOUNT_STATUS]?[[account preferenceForKey:KEY_JABBER_VERIFY_CERTS group:GROUP_ACCOUNT_STATUS] boolValue]:YES];
David@0
    53
	[checkBox_allowPlaintext setState:[[account preferenceForKey:KEY_JABBER_ALLOW_PLAINTEXT group:GROUP_ACCOUNT_STATUS] boolValue]];
David@0
    54
	
David@0
    55
	//Resource
Evan@775
    56
	NSString *resource = [account preferenceForKey:KEY_JABBER_RESOURCE group:GROUP_ACCOUNT_STATUS];
Evan@775
    57
	if (!resource)
Evan@775
    58
		resource = [(NSString*)SCDynamicStoreCopyLocalHostName(NULL) autorelease];
Evan@775
    59
	if (!resource)
Evan@775
    60
		resource = @"";	
Evan@775
    61
	[textField_resource setStringValue:resource];
David@0
    62
	
David@0
    63
	//Connect server
David@0
    64
	NSString *connectServer = [account preferenceForKey:KEY_JABBER_CONNECT_SERVER group:GROUP_ACCOUNT_STATUS];
David@0
    65
	[textField_connectServer setStringValue:(connectServer ? connectServer : @"")];
David@0
    66
	
zacw@2615
    67
	// BOSH server
zacw@2615
    68
	NSString *boshServer = [account preferenceForKey:KEY_JABBER_BOSH_SERVER group:GROUP_ACCOUNT_STATUS];
zacw@2615
    69
	[textField_connectServer setStringValue:(boshServer ?: @"")];	
zacw@2615
    70
	
David@0
    71
	//Priority
David@0
    72
	NSNumber *priority = [account preferenceForKey:KEY_JABBER_PRIORITY_AVAILABLE group:GROUP_ACCOUNT_STATUS];
David@0
    73
	[textField_priorityAvailable setStringValue:(priority ? [priority stringValue] : @"")];
David@0
    74
	priority = [account preferenceForKey:KEY_JABBER_PRIORITY_AWAY group:GROUP_ACCOUNT_STATUS];
David@0
    75
	[textField_priorityAway setStringValue:(priority ? [priority stringValue] : @"")];
zacw@2202
    76
	
zacw@2202
    77
	//File transfer proxies
zacw@2202
    78
	NSString *ftProxies = [account preferenceForKey:KEY_JABBER_FT_PROXIES group:GROUP_ACCOUNT_STATUS];
zacw@2205
    79
	[textField_ftProxies setStringValue:ftProxies ?: @""];
David@0
    80
		
David@0
    81
	//Subscription behavior
David@0
    82
	int subbeh = [[account preferenceForKey:KEY_JABBER_SUBSCRIPTION_BEHAVIOR group:GROUP_ACCOUNT_STATUS] intValue];
David@0
    83
	[popup_subscriptionBehavior selectItemWithTag:subbeh];
David@0
    84
	NSString *defaultGroup = [account preferenceForKey:KEY_JABBER_SUBSCRIPTION_GROUP group:GROUP_ACCOUNT_STATUS];
David@0
    85
	[comboBox_subscriptionGroup setStringValue:(defaultGroup ? defaultGroup : @"")];
David@0
    86
	
David@0
    87
	//Hide the register button if the account can't register new accounts
David@837
    88
	[button_register setHidden:![account.service canRegisterNewAccounts]];
David@0
    89
	
David@0
    90
	//Set hidden flag of the default group combobox
David@0
    91
	[self subscriptionModeDidChange:nil];
David@0
    92
}
David@0
    93
David@0
    94
//Save controls
David@0
    95
- (void)saveConfiguration
David@0
    96
{
David@0
    97
    [super saveConfiguration];
David@0
    98
	
David@0
    99
	//Connection security
David@0
   100
	[account setPreference:[NSNumber numberWithBool:[checkBox_forceOldSSL state]]
David@0
   101
					forKey:KEY_JABBER_FORCE_OLD_SSL group:GROUP_ACCOUNT_STATUS];
David@0
   102
	[account setPreference:[NSNumber numberWithBool:[checkBox_requireTLS state]]
David@0
   103
								   forKey:KEY_JABBER_REQUIRE_TLS group:GROUP_ACCOUNT_STATUS];
David@0
   104
	[account setPreference:[NSNumber numberWithBool:[checkBox_checkCertificates state]]
David@0
   105
					forKey:KEY_JABBER_VERIFY_CERTS group:GROUP_ACCOUNT_STATUS];
David@0
   106
	[account setPreference:[NSNumber numberWithBool:[checkBox_allowPlaintext state]]
David@0
   107
					forKey:KEY_JABBER_ALLOW_PLAINTEXT group:GROUP_ACCOUNT_STATUS];
David@0
   108
David@0
   109
	//Resource
David@0
   110
	[account setPreference:([[textField_resource stringValue] length] ? [textField_resource stringValue] : nil)
David@0
   111
					forKey:KEY_JABBER_RESOURCE group:GROUP_ACCOUNT_STATUS];
David@0
   112
	
David@0
   113
	//Connect server
David@0
   114
	[account setPreference:([[textField_connectServer stringValue] length] ? [textField_connectServer stringValue] : nil)
David@0
   115
					forKey:KEY_JABBER_CONNECT_SERVER group:GROUP_ACCOUNT_STATUS];
zacw@2202
   116
	
zacw@2615
   117
	//BOSH server
zacw@2615
   118
	[account setPreference:([[textField_BOSHserver stringValue] length] ? [textField_BOSHserver stringValue] : nil)
zacw@2615
   119
					forKey:KEY_JABBER_BOSH_SERVER group:GROUP_ACCOUNT_STATUS];	
zacw@2615
   120
	
zacw@2202
   121
	//FT proxies
zacw@2232
   122
	[account setPreference:[textField_ftProxies stringValue]
zacw@2202
   123
					forKey:KEY_JABBER_FT_PROXIES group:GROUP_ACCOUNT_STATUS];
zacw@2202
   124
	
David@0
   125
	//Priority
David@0
   126
	[account setPreference:([textField_priorityAvailable intValue] ? [NSNumber numberWithInt:[textField_priorityAvailable intValue]] : nil)
David@0
   127
					forKey:KEY_JABBER_PRIORITY_AVAILABLE
David@0
   128
					 group:GROUP_ACCOUNT_STATUS];
David@0
   129
	[account setPreference:([textField_priorityAway intValue] ? [NSNumber numberWithInt:[textField_priorityAway intValue]] : nil)
David@0
   130
					forKey:KEY_JABBER_PRIORITY_AWAY
David@0
   131
					 group:GROUP_ACCOUNT_STATUS];
David@0
   132
David@0
   133
	//Subscription Behavior
David@0
   134
	[account setPreference:([[popup_subscriptionBehavior selectedItem] tag] ? [NSNumber numberWithInt:[[popup_subscriptionBehavior selectedItem] tag]] : nil)
David@0
   135
					forKey:KEY_JABBER_SUBSCRIPTION_BEHAVIOR
David@0
   136
					 group:GROUP_ACCOUNT_STATUS];
David@0
   137
	[account setPreference:([[comboBox_subscriptionGroup stringValue] length] ? [comboBox_subscriptionGroup stringValue] : nil)
David@0
   138
					forKey:KEY_JABBER_SUBSCRIPTION_GROUP group:GROUP_ACCOUNT_STATUS];
David@0
   139
}
David@0
   140
David@0
   141
- (IBAction)subscriptionModeDidChange:(id)sender {
David@0
   142
	// only show these two when "accept and add to contact list" is selected
David@0
   143
	int tag = [[popup_subscriptionBehavior selectedItem] tag];
David@0
   144
	[textField_subscriptionModeLabel setHidden:tag != 2];
David@0
   145
	[comboBox_subscriptionGroup setHidden:tag != 2];
David@0
   146
}
David@0
   147
David@0
   148
- (void)dealloc {
David@1109
   149
	[[NSNotificationCenter defaultCenter] removeObserver:self];
David@0
   150
	[window_registerServer release];
David@0
   151
	[servers release];
David@0
   152
David@0
   153
	[super dealloc];
David@0
   154
}
David@0
   155
David@0
   156
#pragma mark group combobox datasource
David@0
   157
David@0
   158
- (void)contactListChanged:(NSNotification*)n {
David@0
   159
	[comboBox_subscriptionGroup reloadData];
David@0
   160
}
David@0
   161
David@0
   162
- (int)numberOfItemsInComboBox:(NSComboBox *)aComboBox {
Peter@1061
   163
	return [adium.contactController.contactList countOfContainedObjects];
David@0
   164
}
David@0
   165
David@0
   166
- (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(int)index {
David@194
   167
	return [[adium.contactController.contactList.containedObjects objectAtIndex:index] formattedUID];
David@0
   168
}
David@0
   169
David@3
   170
- (NSUInteger)comboBox:(NSComboBox *)aComboBox indexOfItemWithStringValue:(NSString *)string {
David@194
   171
	NSArray *groups = adium.contactController.contactList.containedObjects;
David@3
   172
	NSUInteger i;
David@3
   173
	for(i = 0;i < [groups count];++i) {
David@0
   174
		AIListGroup *group = [groups objectAtIndex:i];
David@837
   175
		if([group.formattedUID isEqualToString:string])
David@0
   176
			return i;
David@0
   177
	}
David@0
   178
	return NSNotFound;
David@0
   179
}
David@0
   180
David@0
   181
- (NSString *)comboBox:(NSComboBox *)aComboBox completedString:(NSString *)string {
David@191
   182
	for(AIListObject *obj in adium.contactController.contactList) {
David@837
   183
		if([obj isKindOfClass:[AIListGroup class]] && [obj.formattedUID hasPrefix:string])
David@837
   184
			return obj.formattedUID;
David@0
   185
	}
David@0
   186
	return string;
David@0
   187
}
David@0
   188
David@0
   189
#pragma mark account creation
David@0
   190
David@3
   191
static NSComparisonResult compareByDistance(id one, id two, void*context) {
David@0
   192
	NSNumber *dist1obj = [one objectForKey:@"distance"];
David@0
   193
	NSNumber *dist2obj = [two objectForKey:@"distance"];
David@0
   194
	
David@0
   195
	if((id)dist2obj == [NSNull null]) {
David@0
   196
		if((id)dist1obj == [NSNull null])
David@0
   197
			return NSOrderedSame;
David@0
   198
		return NSOrderedAscending;
David@0
   199
	}
David@0
   200
	if((id)dist1obj == [NSNull null])
David@0
   201
		return NSOrderedDescending;
David@0
   202
	
David@0
   203
	float dist1 = [dist1obj floatValue];
David@0
   204
	float dist2 = [dist2obj floatValue];
David@0
   205
	
David@0
   206
	if(fabs(dist1 - dist2) < 0.000001)
David@0
   207
		return NSOrderedSame;
David@0
   208
	
David@0
   209
	if(dist1 > dist2)
David@0
   210
		return NSOrderedDescending;
David@0
   211
	return NSOrderedAscending;
David@0
   212
}
David@0
   213
David@0
   214
- (IBAction)registerNewAccount:(id)sender {
David@0
   215
	if(!servers) {
David@0
   216
		NSError *err = NULL;
David@748
   217
		NSXMLDocument *serverfeed = [[[NSXMLDocument alloc] initWithContentsOfURL:[NSURL URLWithString:SERVERFEEDRSSURL]
David@0
   218
																		 options:0
David@748
   219
																		   error:&err] autorelease];
David@0
   220
		if(err) {
David@0
   221
			[[NSAlert alertWithError:err] runModal];
David@0
   222
		} else {
David@0
   223
			NSXMLElement *root = [serverfeed rootElement];
Andreas@305
   224
			NSArray *items = [root elementsForName:@"item"];
David@0
   225
			
Andreas@305
   226
			if(!root || !items || ![[root name] isEqualToString:@"query"]) {
David@0
   227
				
David@0
   228
				[[NSAlert alertWithMessageText:AILocalizedString(@"Parse Error.",nil)
David@0
   229
								 defaultButton:AILocalizedString(@"OK",nil)
David@0
   230
							   alternateButton:nil
David@0
   231
								   otherButton:nil
David@0
   232
					 informativeTextWithFormat:[NSString stringWithFormat:
David@0
   233
												AILocalizedString(@"Unable to parse the server list at %@. Please try again later.",nil), SERVERFEEDRSSURL]] runModal];
catfish@2104
   234
			} else {				
David@0
   235
				MachineLocation loc;
David@0
   236
				ReadLocation(&loc);
David@0
   237
				
catfish@2104
   238
				float latitude = FractToFloat(loc.latitude)*(M_PI/2.0f);
catfish@2104
   239
				float longitude = FractToFloat(loc.longitude)*(M_PI/2.0f);
David@0
   240
				
David@0
   241
				servers = [[NSMutableArray alloc] init];
David@0
   242
				
catfish@2104
   243
				for (NSXMLElement *item in items) {
am@2459
   244
					NSXMLElement *title = [[item elementsForName:@"domain"] lastObject];
David@0
   245
					if(!title)
David@0
   246
						continue;
David@0
   247
					NSXMLElement *description = [[item elementsForName:@"description"] lastObject];
Andreas@305
   248
					NSXMLElement *latitudeNode  = [[item elementsForName:@"latitude"] lastObject];
Andreas@305
   249
					NSXMLElement *longitudeNode = [[item elementsForName:@"longitude"] lastObject];
Andreas@305
   250
					NSString *domain = [[item attributeForName:@"jid"] stringValue];
Andreas@305
   251
					NSString *homepageStr = [[[item elementsForName:@"homepage"] lastObject] stringValue];
Andreas@305
   252
					NSURL *homepage = homepageStr?[NSURL URLWithString:homepageStr]:nil;
David@0
   253
					
David@0
   254
					id distance = [NSNull null];
David@0
   255
					if (latitudeNode && longitudeNode) {
David@0
   256
						/* Calculate the distance between the computer and the xmpp server in km
David@0
   257
						 * Note that this assumes that the earth is a perfect sphere
David@0
   258
						 * If it turns out to be flat or doughnut-shaped, this will not work!
David@0
   259
						 */
David@0
   260
						
David@0
   261
						float latitude2 = [[latitudeNode stringValue] floatValue] * (M_PI/180.0f);
David@0
   262
						float longitude2 = [[longitudeNode stringValue] floatValue] * (M_PI/180.0f);
David@0
   263
						
David@0
   264
						float d_lat = sinf((latitude2 - latitude)/2.0);
David@0
   265
						float d_long = sinf((longitude2 - longitude)/2.0);
David@0
   266
						float a = d_lat*d_lat + cosf(latitude)*cosf(latitude2)*d_long*d_long;
David@0
   267
						float c = 2*atan2f(sqrtf(a),sqrtf(1.0-a));
David@0
   268
						float d = 6372.797*c; // mean earth radius
David@0
   269
						
David@0
   270
						distance = [NSNumber numberWithFloat:d];
David@0
   271
					}
David@0
   272
					
David@0
   273
					[(NSMutableArray*)servers addObject:[NSDictionary dictionaryWithObjectsAndKeys:
David@0
   274
						[title stringValue], @"servername",
David@0
   275
						(description ? (id)[description stringValue] : (id)[NSNull null]), @"description",
David@0
   276
						distance, @"distance",
Andreas@305
   277
						domain, @"domain",
Andreas@305
   278
						homepage, @"homepage", // might be nil
David@0
   279
						nil]];
David@0
   280
				}
David@0
   281
				
David@0
   282
				[(NSMutableArray*)servers sortUsingFunction:compareByDistance context:nil];
David@0
   283
				
David@0
   284
				[tableview_servers reloadData];
David@0
   285
			}
David@0
   286
		}
David@0
   287
	}
David@0
   288
	
David@0
   289
	[NSApp beginSheet:window_registerServer
David@0
   290
	   modalForWindow:[sender window]
David@0
   291
		modalDelegate:self
David@0
   292
	   didEndSelector:@selector(registrationSheetDidEnd:returnCode:contextInfo:)
David@0
   293
		  contextInfo:NULL];
David@0
   294
}
David@0
   295
David@0
   296
- (void)registrationSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
David@0
   297
{
David@0
   298
	
David@0
   299
}
David@0
   300
David@0
   301
- (int)numberOfRowsInTableView:(NSTableView *)tableView {
David@0
   302
	return [servers count];
David@0
   303
}
David@0
   304
David@0
   305
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row {
David@0
   306
	id objectValue = [[servers objectAtIndex:row] objectForKey:[tableColumn identifier]];
David@0
   307
	return ((objectValue && ![objectValue isKindOfClass:[NSNull class]]) ? objectValue : @"");
David@0
   308
}
David@0
   309
David@0
   310
- (void)tableViewSelectionDidChange:(NSNotification *)notification {
Andreas@305
   311
	NSDictionary *serverInfo = [servers objectAtIndex:[tableview_servers selectedRow]];
Andreas@305
   312
	NSString *servername = [serverInfo objectForKey:@"domain"];
David@0
   313
	[textField_registerServerName setStringValue:servername];
David@0
   314
	[textField_registerServerPort setStringValue:@""];
Andreas@305
   315
	[textView_serverDescription setString:[serverInfo objectForKey:@"description"]];
Andreas@305
   316
	
Andreas@305
   317
	[button_serverHomepage setEnabled:[serverInfo objectForKey:@"homepage"] != nil];
Andreas@305
   318
}
Andreas@305
   319
Andreas@305
   320
- (IBAction)visitServerHomepage:(id)sender {
Andreas@305
   321
	NSDictionary *serverInfo = [servers objectAtIndex:[tableview_servers selectedRow]];
Andreas@305
   322
	
Andreas@305
   323
	[[NSWorkspace sharedWorkspace] openURL:[serverInfo objectForKey:@"homepage"]];
David@0
   324
}
David@0
   325
David@0
   326
- (IBAction)registerCancel:(id)sender {
David@0
   327
	[window_registerServer orderOut:nil];
David@0
   328
	[NSApp endSheet:window_registerServer];
David@0
   329
}
David@0
   330
David@0
   331
- (IBAction)registerRequestAccount:(id)sender {
David@0
   332
	[[sender window] makeFirstResponder:nil]; // apply all changes
zacw@2466
   333
	
David@0
   334
	if([[textField_registerServerName stringValue] length] == 0) {
David@0
   335
		NSBeep();
David@0
   336
		return;
David@0
   337
	}
zacw@2466
   338
	
David@0
   339
	[account setPreference:[NSNumber numberWithInt:[textField_registerServerPort intValue]]
David@0
   340
					forKey:KEY_CONNECT_PORT group:GROUP_ACCOUNT_STATUS];
David@0
   341
David@0
   342
	NSString *newUID;
David@0
   343
	if ([[textField_accountUID stringValue] length]) {
David@0
   344
		NSRange atLocation = [[textField_accountUID stringValue] rangeOfString:@"@" options:NSLiteralSearch];
David@0
   345
		if (atLocation.location == NSNotFound)
David@0
   346
			newUID = [NSString stringWithFormat:@"%@@%@",[textField_accountUID stringValue], [textField_registerServerName stringValue]];
David@0
   347
		else
David@0
   348
			newUID = [NSString stringWithFormat:@"%@@%@",[[textField_accountUID stringValue] substringToIndex:atLocation.location], [textField_registerServerName stringValue]];
David@0
   349
	} else {
David@0
   350
		newUID = [NSString stringWithFormat:@"nobody@%@",[textField_registerServerName stringValue]];
David@0
   351
	}
David@0
   352
David@0
   353
	[account filterAndSetUID:newUID];
David@0
   354
	
David@0
   355
	[window_registerServer orderOut:nil];
David@0
   356
	[NSApp endSheet:window_registerServer];
David@0
   357
	
David@0
   358
	[account performRegisterWithPassword:[textField_password stringValue]];
David@0
   359
	[self didBeginRegistration];
David@0
   360
}
David@0
   361
David@0
   362
@end