Fix #197: Account stays in connecting stage
[siplcs.git] / src / adium / ESPurpleSIPEAccount.m
blobf10a9aa3116345434e600047f1a4c9bf381a71b5
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>
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
28 - (void)initAccount
30     [super initAccount];
31     
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
50      nil
51      ];
52     
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
67      nil
68      ];
71 - (void)dealloc
73     [adium_to_sipe_status release];
74     [sipe_to_adium_status release];
75     [super dealloc];
78 - (const char*)protocolPlugin
80         return "prpl-sipe";
83 - (const char *)purpleAccountName
85     NSString *completeUserName = [NSString stringWithUTF8String:[super purpleAccountName]];
86     NSString *windowsLogin =[self preferenceForKey:KEY_SIPE_WINDOWS_LOGIN group:GROUP_ACCOUNT_STATUS];
87     
88     if ( ![windowsLogin isEqualToString:@""] ) {
89         completeUserName = [NSString stringWithFormat:@"%@,%@", completeUserName, windowsLogin];
90     }
91     
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)
99     {
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";
103     } else {
104         return server;
105     }
108 -(void) didConnect
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 )
119     {
120         [self setPreference:host forKey:KEY_CONNECT_HOST group:GROUP_ACCOUNT_STATUS];
121     }
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];
130     [super didConnect];
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!  ------
142     /*
143      * Adium's CBPurpleAccount class's implementation of configurePurpleAccount (called above)
144      * has the following line:
145      *
146      *         if (hostName && [hostName length]) {
147      *
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.
153      */
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:@""])
161     {
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", "");
165     } else {
166         // NOP.  Superclass already set this via the [self hostForPurple] response.
167     }
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);
176     if (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
178             if (!thePassword)
179                 [self setPasswordTemporarily:@"placeholder"];
180     }
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);
189     
190     // Connection preferences
191     id connType = [self preferenceForKey:KEY_SIPE_CONNECTION_TYPE group:GROUP_ACCOUNT_STATUS];
192     if([connType isKindOfClass:[NSNumber class]])
193         {
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]];
197         }
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] : "" );
206     // Email preferences
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
232         return NO;
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
258  */
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)];
265     
266     if (!purpleStatusID) return nil;
267     
268     if (sipe_to_adium_status[purpleStatusID])
269         statusName = sipe_to_adium_status[purpleStatusID];
270     else {
271         AILog(@"(ESPurpleSIPEAccount) Unknown purpleStatusID in statusNameForPurpleBuddy: %@", purpleStatusID);
272         statusName = STATUS_NAME_OFFLINE;
273     }
274     
275     return statusName;
279  * @brief Maps purple status IDs to Adium statuses
280  */
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 = @"";
288      
289      if ( adium_to_sipe_status[statusName] )
290          statusID = [adium_to_sipe_status[statusName] UTF8String];
291      else {
292          AILog(@"(ESPurpleSIPEAccount): Unknown statusName in purpleStatusIDForStatus: %@", statusName);
293          statusID = [super purpleStatusIDForStatus:statusState arguments:arguments];
294      }
295      
296      return statusID;
300 @end