adium: Adding fix per bug #196 comments
[siplcs.git] / src / adium / ESPurpleSIPEAccount.m
blob1a48fa39f555426b5a11933c4dbe89258a0dad36
1 //
2 //  ESSIPEAccount.m
3 //  SIPEAdiumPlugin
4 //
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.
8 //
10 #import <AISharedAdium.h>
11 #import <AIAdium.h>
12 #import <Adium/AIStatus.h>
13 #import <Adium/AIStatusControllerProtocol.h>
14 #import <AIUtilities/AIHostReachabilityMonitor.h>
16 #import "ESPurpleSIPEAccount.h"
17 #import "ESSIPEService.h"
19 #include "sipe-core.h"
20 #include "sipe-backend.h"
21 #include "purple-private.h"
23 // C Declarations
24 extern void AILog(NSString *fmt, ...);
26 @class AICoreComponentLoader;
28 @implementation ESPurpleSIPEAccount
30 - (const char*)protocolPlugin
32         return "prpl-sipe";
35 - (NSString *)hostForPurple
37     NSString *server = [self preferenceForKey:KEY_SIPE_CONNECT_HOST group:GROUP_ACCOUNT_STATUS];
38     if (!server || [server length] == 0)
39     {
40         // set the KEY_CONNECT_HOST to "autodetect" just to make sure we don't lose it from the defaults
41         [self setPreference:@"autodetect" forKey:KEY_CONNECT_HOST group:GROUP_ACCOUNT_STATUS];
42         return @"autodetect";
43     } else {
44         return server;
45     }
48 -(void) didConnect
50     PurpleConnection *gc = purple_account_get_connection(account);
51     struct sipe_core_public *sipe_public = PURPLE_GC_TO_SIPE_CORE_PUBLIC;
53     // get the resolved hostname from sipe_private
54     NSString *host = [NSString stringWithUTF8String:sipe_core_transport_sip_server_name(sipe_public)];
55     AILog(@"(didConnect) server: %@",host);
57     // if we autodetected, the KEY_CONNECT_HOST will be "autodetect", we need to replace it with the real hostname
58     if([[self preferenceForKey:KEY_CONNECT_HOST group:GROUP_ACCOUNT_STATUS] compare:@"autodetect" options:NSCaseInsensitiveSearch] == NSOrderedSame )
59     {
60         [self setPreference:host forKey:KEY_CONNECT_HOST group:GROUP_ACCOUNT_STATUS];
61     }
64     // Adium Host reachability depends on having KEY_CONNECT_HOST filled in earlier, so we have to hack ourselves into the reachabilityMonitor
65     // TODO: Check with Adium team to see if there is a more elegant way to do this.
66     AIHostReachabilityMonitor   *monitor = [AIHostReachabilityMonitor defaultMonitor];
67     AICoreComponentLoader       *theComponentLoader = adium.componentLoader;
68     [monitor addObserver:[theComponentLoader pluginWithClassName:@"ESAccountNetworkConnectivityPlugin"] forHost:host];
70     [super didConnect];
73 #pragma mark Account Configuration
74 - (void)configurePurpleAccount
76         [super configurePurpleAccount];
78     // Account preferences
79     AILog(@"Configuring account: %s\n", self.purpleAccountName);
81     // !!! ------  HACK/Kludge alert!  ------
82     /*
83      * Adium's CBPurpleAccount class's implementation of configurePurpleAccount (called above)
84      * has the following line:
85      *
86      *         if (hostName && [hostName length]) {
87      *
88      * Which doesn't allow us to leave the KEY_CONNECT_HOST preference empty (Adium prompts for the user to fill it out)
89      * sipe-core is expecting the server account setting to be empty to engage the auto-detection piece.  The only way
90      * to fix this is to fake out Adium by storing the servername in a different key (KEY_SIPE_CONNECT_HOST) and setting a
91      * default KEY_CONNECT_HOST to something.  We then need to detect that we have an empty servername here, and
92      * "overwrite" the default placeholder with an empty string.
93      */
95     // if there is no host specified, we're looking to auto-detect
96     // TODO: change this to a checkbox, and enable/disable based on that.  Leaving a field blank is bad UI design.
97     NSString *server = [self preferenceForKey:KEY_SIPE_CONNECT_HOST group:GROUP_ACCOUNT_STATUS];
99     // 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
100     if([server isEqualToString:@""])
101     {
102         // force preference back to "autodetect" so that we can replace it when we get a hostname resolved
103         [self setPreference:@"autodetect" forKey:KEY_CONNECT_HOST group:GROUP_ACCOUNT_STATUS];
104         purple_account_set_string(account,"server", "");
105     } else {
106         // NOP.  Superclass already set this via the [self hostForPurple] response.
107     }
109     NSString *winLogin  = [self preferenceForKey:KEY_SIPE_WINDOWS_LOGIN group:GROUP_ACCOUNT_STATUS];
110     NSString *completeUserName = [NSString stringWithUTF8String:[self purpleAccountName]];
112     if (winLogin && [winLogin length]) {
113         // Configure the complete username ("user@domain.com,DOMAIN\user")
114         completeUserName = [NSString stringWithFormat:@"%@,%@",completeUserName, winLogin];
115         purple_account_set_username(account, [completeUserName UTF8String]);
116     } else {
117         purple_account_set_username(account, self.purpleAccountName);
118     }
120     NSString *thePassword = [self preferenceForKey:KEY_SIPE_PASSWORD group:GROUP_ACCOUNT_STATUS];
121     if (thePassword && [thePassword length])
122         purple_account_set_password(account, [thePassword UTF8String]);
124         BOOL sso = [[self preferenceForKey:KEY_SIPE_SINGLE_SIGN_ON group:GROUP_ACCOUNT_STATUS] boolValue];
125         purple_account_set_bool(account, "sso", sso);
127     if (sso) {
128             // 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
129             if (!thePassword)
130                 [self setPasswordTemporarily:@"placeholder"];
131     }
133     // Connection preferences
134     id connType = [self preferenceForKey:KEY_SIPE_CONNECTION_TYPE group:GROUP_ACCOUNT_STATUS];
135     if([connType isKindOfClass:[NSNumber class]])
136         {
137         // For backwards compatibility, we pick from an array if the preference is a NSNumber
138         NSMutableArray *myArray = [[NSMutableArray alloc] initWithObjects:@"auto", @"tls", @"tcp", nil];
139         connType = (NSString *)[myArray objectAtIndex:(NSUInteger)[self preferenceForKey:KEY_SIPE_CONNECTION_TYPE group:GROUP_ACCOUNT_STATUS]];
140         }
141     purple_account_set_string(account, "transport", [connType UTF8String]);
143     NSString *authScheme = [self preferenceForKey:KEY_SIPE_AUTH_SCHEME group:GROUP_ACCOUNT_STATUS];
144     purple_account_set_string(account, "authentication", [authScheme UTF8String]);
146         NSString *userAgent = [self preferenceForKey:KEY_SIPE_USER_AGENT group:GROUP_ACCOUNT_STATUS];
147     purple_account_set_string(account, "useragent", (userAgent.length != 0) ?  [userAgent UTF8String] : "" );
149     // Email preferences
150     NSString *emailURL = [self preferenceForKey:KEY_SIPE_EMAIL_URL group:GROUP_ACCOUNT_STATUS];
151     purple_account_set_string(account, "email_usr", (!emailURL.length) ?  [emailURL UTF8String] : "" );
153     // TODO: Use account name (user@domain) as default for this
154     NSString *email = [self preferenceForKey:KEY_SIPE_EMAIL group:GROUP_ACCOUNT_STATUS];
155     purple_account_set_string(account, "email", (!email.length) ?  [email UTF8String] : "" );
157     // TODO: Use Windows Login (DOMAIN\user) as default for this
158     NSString *emailLogin = [self preferenceForKey:KEY_SIPE_EMAIL_LOGIN group:GROUP_ACCOUNT_STATUS];
159     purple_account_set_string(account, "email_login", (!emailLogin.length) ?  [emailLogin UTF8String] : "" );
161     // TODO: Use default password as default for this
162     NSString *emailPassword = [self preferenceForKey:KEY_SIPE_EMAIL_PASSWORD group:GROUP_ACCOUNT_STATUS];
163     purple_account_set_string(account, "email_password", (!emailPassword.length) ?  [emailPassword UTF8String] : "" );
165     // Group chat preferences
166     NSString *groupchatUser = [self preferenceForKey:KEY_SIPE_GROUP_CHAT_PROXY group:GROUP_ACCOUNT_STATUS];
167     purple_account_set_string(account, "groupchat_user", (!groupchatUser.length) ? [groupchatUser UTF8String] : "" );
171 #pragma mark File transfer
173 - (BOOL)canSendFolders
175         return NO;
178 - (void)beginSendOfFileTransfer:(ESFileTransfer *)fileTransfer
180         [super _beginSendOfFileTransfer:fileTransfer];
183 - (void)acceptFileTransferRequest:(ESFileTransfer *)fileTransfer
185     [super acceptFileTransferRequest:fileTransfer];
188 - (void)rejectFileReceiveRequest:(ESFileTransfer *)fileTransfer
190     [super rejectFileReceiveRequest:fileTransfer];
193 - (void)cancelFileTransfer:(ESFileTransfer *)fileTransfer
195         [super cancelFileTransfer:fileTransfer];
198 #pragma mark Status Messages
200  * @brief Status name to use for a Purple buddy
201  */
202 - (NSString *)statusNameForPurpleBuddy:(PurpleBuddy *)buddy
204     NSString *statusName = [super statusNameForPurpleBuddy:buddy];
205     PurplePresence  *presence = purple_buddy_get_presence(buddy);
206     PurpleStatus    *status = purple_presence_get_active_status(presence);
207     const char      *purpleStatusID = purple_status_get_id(status);
209     if (!purpleStatusID) return nil;
211     switch (sipe_purple_token_to_activity(purpleStatusID))
212     {
213         case SIPE_ACTIVITY_AVAILABLE:
214         case SIPE_ACTIVITY_ONLINE:
215             statusName = STATUS_NAME_AVAILABLE;
216             break;
217         case SIPE_ACTIVITY_AWAY:
218         case SIPE_ACTIVITY_INACTIVE:
219             statusName = STATUS_NAME_AWAY;
220             break;
221         case SIPE_ACTIVITY_BRB:
222             statusName = STATUS_NAME_BRB;
223             break;
224         case SIPE_ACTIVITY_BUSY:
225         case SIPE_ACTIVITY_BUSYIDLE:
226             statusName = STATUS_NAME_BUSY;
227             break;
228         case SIPE_ACTIVITY_DND:
229             statusName = STATUS_NAME_DND;
230             break;
231         case SIPE_ACTIVITY_LUNCH:
232             statusName = STATUS_NAME_LUNCH;
233             break;
234         case SIPE_ACTIVITY_INVISIBLE:
235             statusName = STATUS_NAME_INVISIBLE;
236             break;
237         case SIPE_ACTIVITY_OFFLINE:
238             statusName = STATUS_NAME_OFFLINE;
239             break;
240         case SIPE_ACTIVITY_ON_PHONE:
241             statusName = STATUS_NAME_PHONE;
242             break;
243         case SIPE_ACTIVITY_IN_CONF:
244         case SIPE_ACTIVITY_IN_MEETING:
245             statusName = STATUS_NAME_NOT_AT_DESK;
246             break;
247         case SIPE_ACTIVITY_OOF:
248             statusName = STATUS_NAME_NOT_IN_OFFICE;
249             break;
250         case SIPE_ACTIVITY_URGENT_ONLY:
251             statusName = STATUS_NAME_AWAY_FRIENDS_ONLY;
252             break;
253         default:
254             statusName = STATUS_NAME_OFFLINE;
255     }
257     return statusName;
262  * @brief Maps purple status IDs to Adium statuses
263  */
264  - (const char *)purpleStatusIDForStatus:(AIStatus *)statusState arguments:(NSMutableDictionary *)arguments
266      const gchar    *statusID;
267      NSString           *statusName = statusState.statusName;
268      NSString           *statusMessageString = [statusState statusMessageString];
270      if (!statusMessageString) statusMessageString = @"";
272      // TODO: figure out why sipe_status_activity_to_token calls return junk, instead of a gchar*
273      switch (statusState.statusType) {
274          case AIAvailableStatusType:
275              statusID = sipe_activity_map[SIPE_ACTIVITY_AVAILABLE].status_id;
276              //statusID = sipe_status_activity_to_token(SIPE_ACTIVITY_AVAILABLE);
277              break;
279          case AIAwayStatusType:
280              if (([statusName isEqualToString:STATUS_NAME_AWAY]) ||
281                  ([statusMessageString caseInsensitiveCompare:[adium.statusController localizedDescriptionForCoreStatusName:STATUS_NAME_AWAY]] == NSOrderedSame))
282              {
283                  //statusID = sipe_status_activity_to_token(SIPE_ACTIVITY_AWAY);
284                  statusID = sipe_activity_map[SIPE_ACTIVITY_AWAY].status_id;
285              } else if (([statusName isEqualToString:STATUS_NAME_BRB]) ||
286                         ([statusMessageString caseInsensitiveCompare:[adium.statusController localizedDescriptionForCoreStatusName:STATUS_NAME_BRB]] == NSOrderedSame))
287              {
288                  //statusID = sipe_status_activity_to_token(SIPE_ACTIVITY_BRB);
289                  statusID = sipe_activity_map[SIPE_ACTIVITY_BRB].status_id;
290              } else if (([statusName isEqualToString:STATUS_NAME_BUSY]) ||
291                         ([statusMessageString caseInsensitiveCompare:[adium.statusController localizedDescriptionForCoreStatusName:STATUS_NAME_BUSY]] == NSOrderedSame))
292              {
293                  // TODO: Figure out how to determine if they should be "busy" or "busyidle"
294                  //statusID = sipe_status_activity_to_token(SIPE_ACTIVITY_BUSY);
295                  statusID = sipe_activity_map[SIPE_ACTIVITY_BUSY].status_id;
296              } else if (([statusName isEqualToString:STATUS_NAME_DND]) ||
297                       ([statusMessageString caseInsensitiveCompare:[adium.statusController localizedDescriptionForCoreStatusName:STATUS_NAME_DND]] == NSOrderedSame))
298              {
299                  //statusID = sipe_status_activity_to_token(SIPE_ACTIVITY_DND);
300                  statusID = sipe_activity_map[SIPE_ACTIVITY_DND].status_id;
301              } else if (([statusName isEqualToString:STATUS_NAME_LUNCH]) ||
302                       ([statusMessageString caseInsensitiveCompare:[adium.statusController localizedDescriptionForCoreStatusName:STATUS_NAME_LUNCH]] == NSOrderedSame))
303              {
304                  //statusID = sipe_status_activity_to_token(SIPE_ACTIVITY_LUNCH);
305                  statusID = sipe_activity_map[SIPE_ACTIVITY_LUNCH].status_id;
306              } else if (([statusName isEqualToString:STATUS_NAME_PHONE]) ||
307                         ([statusMessageString caseInsensitiveCompare:[adium.statusController localizedDescriptionForCoreStatusName:STATUS_NAME_PHONE]] == NSOrderedSame))
308              {
309                  //statusID = sipe_status_activity_to_token(SIPE_ACTIVITY_ON_PHONE);
310                  statusID = sipe_activity_map[SIPE_ACTIVITY_ON_PHONE].status_id;
311              } else if (([statusName isEqualToString:STATUS_NAME_NOT_AT_DESK]) ||
312                         ([statusMessageString caseInsensitiveCompare:[adium.statusController localizedDescriptionForCoreStatusName:STATUS_NAME_NOT_AT_DESK]] == NSOrderedSame))
313              {
314                  // TODO: Figure out how to determine if they should be "In a meeting" or "In a conference"
315                  //statusID = sipe_status_activity_to_token(SIPE_ACTIVITY_IN_MEETING);
316                  statusID = sipe_activity_map[SIPE_ACTIVITY_IN_MEETING].status_id;
317              } else if (([statusName isEqualToString:STATUS_NAME_NOT_IN_OFFICE]) ||
318                         ([statusMessageString caseInsensitiveCompare:[adium.statusController localizedDescriptionForCoreStatusName:STATUS_NAME_NOT_IN_OFFICE]] == NSOrderedSame))
319              {
320                  //statusID = sipe_status_activity_to_token(SIPE_ACTIVITY_OOF);
321                  statusID = sipe_activity_map[SIPE_ACTIVITY_OOF].status_id;
322              } else if (([statusName isEqualToString:STATUS_NAME_AWAY_FRIENDS_ONLY]) ||
323                         ([statusMessageString caseInsensitiveCompare:[adium.statusController localizedDescriptionForCoreStatusName:STATUS_NAME_AWAY_FRIENDS_ONLY]] == NSOrderedSame))
324              {
325                  //statusID = sipe_status_activity_to_token(SIPE_ACTIVITY_URGENT_ONLY);
326                  statusID = sipe_activity_map[SIPE_ACTIVITY_URGENT_ONLY].status_id;
327              }
329          case AIInvisibleStatusType:
330              //statusID = sipe_status_activity_to_token(SIPE_ACTIVITY_INVISIBLE);
331              statusID = sipe_activity_map[SIPE_ACTIVITY_INVISIBLE].status_id;
332              break;
334          case AIOfflineStatusType:
335              //statusID = sipe_status_activity_to_token(SIPE_ACTIVITY_OFFLINE);
336              statusID = sipe_activity_map[SIPE_ACTIVITY_OFFLINE].status_id;
337              break;
338      }
341      //If we didn't get a purple status type, request one from super
342      if (statusID == NULL) statusID = [super purpleStatusIDForStatus:statusState arguments:arguments];
344      return statusID;
348 @end