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