5 // Created by Matt Meissner on 10/30/09.
6 // Modified by Michael Lamb on 2/27/13
7 // Copyright 2013 Michael Lamb/Harris Kauffman. All rights reserved.
10 #import <AISharedAdium.h>
12 #import <Adium/AIStatus.h>
13 #import <Adium/AIStatusControllerProtocol.h>
14 #import <AIUtilities/AIHostReachabilityMonitor.h>
15 #import <ESDebugAILog.h>
17 #import "ESPurpleSIPEAccount.h"
18 #import "ESSIPEService.h"
20 #include "sipe-core.h"
21 #include "sipe-backend.h"
22 #include "purple-private.h"
24 @class AICoreComponentLoader;
26 @implementation ESPurpleSIPEAccount
32 sipe_to_adium_status =
33 [[NSDictionary alloc] initWithObjectsAndKeys:
34 STATUS_NAME_AVAILABLE, @"available", //SIPE_ACTIVITY_AVAILABLE
35 STATUS_NAME_AVAILABLE, @"online", //SIPE_ACTIVITY_ONLINE
36 STATUS_NAME_AWAY, @"idle", //SIPE_ACTIVITY_INACTIVE
37 STATUS_NAME_BUSY, @"busy", //SIPE_ACTIVITY_BUSY
38 STATUS_NAME_BUSY, @"busyidle", //SIPE_ACTIVITY_BUSYIDLE
39 STATUS_NAME_DND, @"do-not-disturb", //SIPE_ACTIVITY_DND
40 STATUS_NAME_BRB, @"be-right-back", //SIPE_ACTIVITY_BRB
41 STATUS_NAME_AWAY, @"away", //SIPE_ACTIVITY_AWAY
42 STATUS_NAME_LUNCH, @"out-to-lunch", //SIPE_ACTIVITY_LUNCH
43 STATUS_NAME_INVISIBLE, @"invisible", //SIPE_ACTIVITY_INVISIBLE
44 STATUS_NAME_OFFLINE, @"offline", //SIPE_ACTIVITY_OFFLINE
45 STATUS_NAME_PHONE, @"on-the-phone", //SIPE_ACTIVITY_ON_PHONE
46 STATUS_NAME_NOT_AT_DESK, @"in-a-conference", //SIPE_ACTIVITY_IN_CONF
47 STATUS_NAME_NOT_AT_DESK, @"in-a-meeting", //SIPE_ACTIVITY_IN_MEETING
48 STATUS_NAME_NOT_IN_OFFICE, @"out-of-office", //SIPE_ACTIVITY_OOF
49 STATUS_NAME_AWAY_FRIENDS_ONLY, @"urgent-interruptions-only", //SIPE_ACTIVITY_URGENT_ONLY
53 adium_to_sipe_status =
54 [[NSDictionary alloc] initWithObjectsAndKeys:
55 @"available", STATUS_NAME_AVAILABLE, //SIPE_ACTIVITY_AVAILABLE
56 @"busy", STATUS_NAME_BUSY, //SIPE_ACTIVITY_BUSY
57 @"do-not-disturb", STATUS_NAME_DND, //SIPE_ACTIVITY_DND
58 @"be-right-back", STATUS_NAME_BRB, //SIPE_ACTIVITY_BRB
59 @"away", STATUS_NAME_AWAY, //SIPE_ACTIVITY_AWAY
60 @"out-to-lunch", STATUS_NAME_LUNCH, //SIPE_ACTIVITY_LUNCH
61 @"invisible", STATUS_NAME_INVISIBLE, //SIPE_ACTIVITY_INVISIBLE
62 @"offline", STATUS_NAME_OFFLINE, //SIPE_ACTIVITY_OFFLINE
63 @"on-the-phone", STATUS_NAME_PHONE, //SIPE_ACTIVITY_ON_PHONE
64 @"in-a-meeting", STATUS_NAME_NOT_AT_DESK, //SIPE_ACTIVITY_IN_MEETING
65 @"out-of-office", STATUS_NAME_NOT_IN_OFFICE, //SIPE_ACTIVITY_OOF
66 @"urgent-interruptions-only", STATUS_NAME_AWAY_FRIENDS_ONLY, //SIPE_ACTIVITY_URGENT_ONLY
73 [adium_to_sipe_status release];
74 [sipe_to_adium_status release];
78 - (const char*)protocolPlugin
83 - (const char *)purpleAccountName
85 NSString *completeUserName = [NSString stringWithUTF8String:[super purpleAccountName]];
86 NSString *windowsLogin =[self preferenceForKey:KEY_SIPE_WINDOWS_LOGIN group:GROUP_ACCOUNT_STATUS];
88 if ( ![windowsLogin isEqualToString:@""] ) {
89 completeUserName = [NSString stringWithFormat:@"%@,%@", completeUserName, windowsLogin];
92 return [completeUserName UTF8String];
95 - (NSString *)hostForPurple
97 NSString *server = [self preferenceForKey:KEY_SIPE_CONNECT_HOST group:GROUP_ACCOUNT_STATUS];
98 if (!server || [server length] == 0)
100 // set the KEY_CONNECT_HOST to "autodetect" just to make sure we don't lose it from the defaults
101 [self setPreference:@"autodetect" forKey:KEY_CONNECT_HOST group:GROUP_ACCOUNT_STATUS];
102 return @"autodetect";
110 PurpleConnection *gc = purple_account_get_connection(account);
111 struct sipe_core_public *sipe_public = PURPLE_GC_TO_SIPE_CORE_PUBLIC;
113 // get the resolved hostname from sipe_private
114 NSString *host = [NSString stringWithUTF8String:sipe_core_transport_sip_server_name(sipe_public)];
115 AILog(@"(didConnect) server: %@",host);
117 // if we autodetected, the KEY_CONNECT_HOST will be "autodetect", we need to replace it with the real hostname
118 if([[self preferenceForKey:KEY_CONNECT_HOST group:GROUP_ACCOUNT_STATUS] compare:@"autodetect" options:NSCaseInsensitiveSearch] == NSOrderedSame )
120 [self setPreference:host forKey:KEY_CONNECT_HOST group:GROUP_ACCOUNT_STATUS];
124 // Adium Host reachability depends on having KEY_CONNECT_HOST filled in earlier, so we have to hack ourselves into the reachabilityMonitor
125 // TODO: Check with Adium team to see if there is a more elegant way to do this.
126 AIHostReachabilityMonitor *monitor = [AIHostReachabilityMonitor defaultMonitor];
127 AICoreComponentLoader *theComponentLoader = adium.componentLoader;
128 [monitor addObserver:[theComponentLoader pluginWithClassName:@"ESAccountNetworkConnectivityPlugin"] forHost:host];
133 #pragma mark Account Configuration
134 - (void)configurePurpleAccount
136 [super configurePurpleAccount];
138 // Account preferences
139 AILog(@"Configuring account: %s\n", self.purpleAccountName);
141 // !!! ------ HACK/Kludge alert! ------
143 * Adium's CBPurpleAccount class's implementation of configurePurpleAccount (called above)
144 * has the following line:
146 * if (hostName && [hostName length]) {
148 * Which doesn't allow us to leave the KEY_CONNECT_HOST preference empty (Adium prompts for the user to fill it out)
149 * sipe-core is expecting the server account setting to be empty to engage the auto-detection piece. The only way
150 * to fix this is to fake out Adium by storing the servername in a different key (KEY_SIPE_CONNECT_HOST) and setting a
151 * default KEY_CONNECT_HOST to something. We then need to detect that we have an empty servername here, and
152 * "overwrite" the default placeholder with an empty string.
155 // if there is no host specified, we're looking to auto-detect
156 // TODO: change this to a checkbox, and enable/disable based on that. Leaving a field blank is bad UI design.
157 NSString *server = [self preferenceForKey:KEY_SIPE_CONNECT_HOST group:GROUP_ACCOUNT_STATUS];
159 // if the server is empty, clear it in purple account (because the defaults hack for the superclass set it to "autodetect"). Otherwise, use the one provided
160 if([server isEqualToString:@""])
162 // force preference back to "autodetect" so that we can replace it when we get a hostname resolved
163 [self setPreference:@"autodetect" forKey:KEY_CONNECT_HOST group:GROUP_ACCOUNT_STATUS];
164 purple_account_set_string(account,"server", "");
166 // NOP. Superclass already set this via the [self hostForPurple] response.
169 NSString *thePassword = [self preferenceForKey:KEY_SIPE_PASSWORD group:GROUP_ACCOUNT_STATUS];
170 if (thePassword && [thePassword length])
171 purple_account_set_password(account, [thePassword UTF8String]);
173 BOOL sso = [[self preferenceForKey:KEY_SIPE_SINGLE_SIGN_ON group:GROUP_ACCOUNT_STATUS] boolValue];
174 purple_account_set_bool(account, "sso", sso);
177 // Adium doesn't honor our "optional" password on account creation and will prompt if the password field is left blank, so we must force it to think there is one, but only if there isn't already a password saved
179 [self setPasswordTemporarily:@"placeholder"];
182 BOOL dontPublish = [[self preferenceForKey:KEY_SIPE_DONT_PUBLISH group:GROUP_ACCOUNT_STATUS] boolValue];
183 purple_account_set_bool(account, "dont-publish", dontPublish);
185 // Disable BEAST mitigations that apple added
186 BOOL beastDisable = [[self preferenceForKey:KEY_SIPE_BEAST_DISABLE group:GROUP_ACCOUNT_STATUS] boolValue];
187 purple_account_set_bool(account, PURPLE_SSL_CDSA_BEAST_TLS_WORKAROUND, beastDisable);
190 // Connection preferences
191 id connType = [self preferenceForKey:KEY_SIPE_CONNECTION_TYPE group:GROUP_ACCOUNT_STATUS];
192 if([connType isKindOfClass:[NSNumber class]])
194 // For backwards compatibility, we pick from an array if the preference is a NSNumber
195 NSMutableArray *myArray = [[NSMutableArray alloc] initWithObjects:@"auto", @"tls", @"tcp", nil];
196 connType = (NSString *)[myArray objectAtIndex:(NSUInteger)[self preferenceForKey:KEY_SIPE_CONNECTION_TYPE group:GROUP_ACCOUNT_STATUS]];
198 purple_account_set_string(account, "transport", [connType UTF8String]);
200 NSString *authScheme = [self preferenceForKey:KEY_SIPE_AUTH_SCHEME group:GROUP_ACCOUNT_STATUS];
201 purple_account_set_string(account, "authentication", [authScheme UTF8String]);
203 NSString *userAgent = [self preferenceForKey:KEY_SIPE_USER_AGENT group:GROUP_ACCOUNT_STATUS];
204 purple_account_set_string(account, "useragent", (userAgent.length != 0) ? [userAgent UTF8String] : "" );
207 NSString *emailURL = [self preferenceForKey:KEY_SIPE_EMAIL_URL group:GROUP_ACCOUNT_STATUS];
208 purple_account_set_string(account, "email_url", (!emailURL.length) ? [emailURL UTF8String] : "" );
210 // TODO: Use account name (user@domain) as default for this
211 NSString *email = [self preferenceForKey:KEY_SIPE_EMAIL group:GROUP_ACCOUNT_STATUS];
212 purple_account_set_string(account, "email", (!email.length) ? [email UTF8String] : "" );
214 // TODO: Use Windows Login (DOMAIN\user) as default for this
215 NSString *emailLogin = [self preferenceForKey:KEY_SIPE_EMAIL_LOGIN group:GROUP_ACCOUNT_STATUS];
216 purple_account_set_string(account, "email_login", (!emailLogin.length) ? [emailLogin UTF8String] : "" );
218 // TODO: Use default password as default for this
219 NSString *emailPassword = [self preferenceForKey:KEY_SIPE_EMAIL_PASSWORD group:GROUP_ACCOUNT_STATUS];
220 purple_account_set_string(account, "email_password", (!emailPassword.length) ? [emailPassword UTF8String] : "" );
222 // Group chat preferences
223 NSString *groupchatUser = [self preferenceForKey:KEY_SIPE_GROUP_CHAT_PROXY group:GROUP_ACCOUNT_STATUS];
224 purple_account_set_string(account, "groupchat_user", (!groupchatUser.length) ? [groupchatUser UTF8String] : "" );
228 #pragma mark File transfer
230 - (BOOL)canSendFolders
235 - (void)beginSendOfFileTransfer:(ESFileTransfer *)fileTransfer
237 [super _beginSendOfFileTransfer:fileTransfer];
240 - (void)acceptFileTransferRequest:(ESFileTransfer *)fileTransfer
242 [super acceptFileTransferRequest:fileTransfer];
245 - (void)rejectFileReceiveRequest:(ESFileTransfer *)fileTransfer
247 [super rejectFileReceiveRequest:fileTransfer];
250 - (void)cancelFileTransfer:(ESFileTransfer *)fileTransfer
252 [super cancelFileTransfer:fileTransfer];
255 #pragma mark Status Messages
257 * @brief Status name to use for a Purple buddy
259 - (NSString *)statusNameForPurpleBuddy:(PurpleBuddy *)buddy
261 NSString *statusName = [super statusNameForPurpleBuddy:buddy];
262 PurplePresence *presence = purple_buddy_get_presence(buddy);
263 PurpleStatus *status = purple_presence_get_active_status(presence);
264 NSString *purpleStatusID = [NSString stringWithUTF8String:purple_status_get_id(status)];
266 if (!purpleStatusID) return nil;
268 if (sipe_to_adium_status[purpleStatusID])
269 statusName = sipe_to_adium_status[purpleStatusID];
271 AILog(@"(ESPurpleSIPEAccount) Unknown purpleStatusID in statusNameForPurpleBuddy: %@", purpleStatusID);
272 statusName = STATUS_NAME_OFFLINE;
279 * @brief Maps purple status IDs to Adium statuses
281 - (const char *)purpleStatusIDForStatus:(AIStatus *)statusState arguments:(NSMutableDictionary *)arguments
283 const gchar *statusID;
284 NSString *statusName = statusState.statusName;
285 NSString *statusMessageString = [statusState statusMessageString];
287 if (!statusMessageString) statusMessageString = @"";
289 if ( adium_to_sipe_status[statusName] )
290 statusID = [adium_to_sipe_status[statusName] UTF8String];
292 AILog(@"(ESPurpleSIPEAccount): Unknown statusName in purpleStatusIDForStatus: %@", statusName);
293 statusID = [super purpleStatusIDForStatus:statusState arguments:arguments];