Feature #101: Mac OS X 10.13 OpenSSL support (I)
[siplcs.git] / src / adium / ESPurpleSIPEAccount.m
blob2042f44fcb7a5f5e90d48c108b67d93c96fed695
1 //
2 //  ESSIPEAccount.m
3 //  SIPEAdiumPlugin
4 //
5 //  Copyright (C) 2015 SIPE Project <http://sipe.sourceforge.net/>
6 //
7 //  Created by Matt Meissner on 10/30/09.
8 //  Modified by Michael Lamb on 2/27/13
9 //  Copyright 2013 Michael Lamb/Harris Kauffman. All rights reserved.
12 #import <AISharedAdium.h>
13 #import <AIAdium.h>
14 #import <Adium/AIStatus.h>
15 #import <Adium/AIStatusControllerProtocol.h>
16 #import <ESDebugAILog.h>
17 #import <Adium/AIListContact.h>
18 #import "AIContactController.h"
20 #import "ESPurpleSIPEAccount.h"
21 #import "ESSIPEService.h"
23 #include "sipe-core.h"
24 #include "sipe-backend.h"
25 #include "purple-private.h"
27 @class AICoreComponentLoader;
29 @implementation ESPurpleSIPEAccount
31 - (void)initAccount
33     [super initAccount];
35     sipe_to_adium_status =
36     [[NSDictionary alloc] initWithObjectsAndKeys:
37      STATUS_NAME_AVAILABLE,         @"available",                 //SIPE_ACTIVITY_AVAILABLE
38      STATUS_NAME_AVAILABLE,         @"online",                    //SIPE_ACTIVITY_ONLINE
39      STATUS_NAME_AWAY,              @"idle",                      //SIPE_ACTIVITY_INACTIVE
40      STATUS_NAME_BUSY,              @"busy",                      //SIPE_ACTIVITY_BUSY
41      STATUS_NAME_BUSY,              @"busyidle",                  //SIPE_ACTIVITY_BUSYIDLE
42      STATUS_NAME_DND,               @"do-not-disturb",            //SIPE_ACTIVITY_DND
43      STATUS_NAME_BRB,               @"be-right-back",             //SIPE_ACTIVITY_BRB
44      STATUS_NAME_AWAY,              @"away",                      //SIPE_ACTIVITY_AWAY
45      STATUS_NAME_LUNCH,             @"out-to-lunch",              //SIPE_ACTIVITY_LUNCH
46      STATUS_NAME_INVISIBLE,         @"invisible",                 //SIPE_ACTIVITY_INVISIBLE
47      STATUS_NAME_OFFLINE,           @"offline",                   //SIPE_ACTIVITY_OFFLINE
48      STATUS_NAME_PHONE,             @"on-the-phone",              //SIPE_ACTIVITY_ON_PHONE
49      STATUS_NAME_NOT_AT_DESK,       @"in-a-conference",           //SIPE_ACTIVITY_IN_CONF
50      STATUS_NAME_NOT_AT_DESK,       @"in-a-meeting",              //SIPE_ACTIVITY_IN_MEETING
51      STATUS_NAME_NOT_IN_OFFICE,     @"out-of-office",             //SIPE_ACTIVITY_OOF
52      STATUS_NAME_AWAY_FRIENDS_ONLY, @"urgent-interruptions-only", //SIPE_ACTIVITY_URGENT_ONLY
53      nil
54      ];
56     adium_to_sipe_status =
57     [[NSDictionary alloc] initWithObjectsAndKeys:
58      @"available",                 STATUS_NAME_AVAILABLE,         //SIPE_ACTIVITY_AVAILABLE
59      @"busy",                      STATUS_NAME_BUSY,              //SIPE_ACTIVITY_BUSY
60      @"do-not-disturb",            STATUS_NAME_DND,               //SIPE_ACTIVITY_DND
61      @"be-right-back",             STATUS_NAME_BRB,               //SIPE_ACTIVITY_BRB
62      @"away",                      STATUS_NAME_AWAY,              //SIPE_ACTIVITY_AWAY
63      @"out-to-lunch",              STATUS_NAME_LUNCH,             //SIPE_ACTIVITY_LUNCH
64      @"invisible",                 STATUS_NAME_INVISIBLE,         //SIPE_ACTIVITY_INVISIBLE
65      @"offline",                   STATUS_NAME_OFFLINE,           //SIPE_ACTIVITY_OFFLINE
66      @"on-the-phone",              STATUS_NAME_PHONE,             //SIPE_ACTIVITY_ON_PHONE
67      @"in-a-meeting",              STATUS_NAME_NOT_AT_DESK,       //SIPE_ACTIVITY_IN_MEETING
68      @"out-of-office",             STATUS_NAME_NOT_IN_OFFICE,     //SIPE_ACTIVITY_OOF
69      @"urgent-interruptions-only", STATUS_NAME_AWAY_FRIENDS_ONLY, //SIPE_ACTIVITY_URGENT_ONLY
70      nil
71      ];
74 - (void)dealloc
76     [adium_to_sipe_status release];
77     [sipe_to_adium_status release];
78     [super dealloc];
81 - (const char*)protocolPlugin
83         return "prpl-sipe";
86 - (const char *)purpleAccountName
88     NSString *completeUserName = [NSString stringWithUTF8String:[super purpleAccountName]];
89     NSString *windowsLogin =[self preferenceForKey:KEY_SIPE_WINDOWS_LOGIN group:GROUP_ACCOUNT_STATUS];
91     if ( ![windowsLogin isEqualToString:@""] ) {
92         completeUserName = [NSString stringWithFormat:@"%@,%@", completeUserName, windowsLogin];
93     }
95         return [completeUserName UTF8String];
98 #pragma mark Account Configuration
99 - (void)configurePurpleAccount
101     // Account preferences
102     AILog(@"(ESPurpleSIPEAccount) Configuring account: %s\n", self.purpleAccountName);
104     NSArray *myArray = [NSArray arrayWithObjects:@"auto", @"tls", @"tcp", nil];
106     NSDictionary *keys_to_account =
107     [NSDictionary dictionaryWithObjectsAndKeys:
108      @"server",                        KEY_SIPE_CONNECT_HOST,
109      @"password",                      KEY_SIPE_PASSWORD,
110      @"transport",                     KEY_SIPE_CONNECTION_TYPE,
111      @"email",                         KEY_SIPE_EMAIL,
112      @"email_login",                   KEY_SIPE_EMAIL_LOGIN,
113      @"email_url",                     KEY_SIPE_EMAIL_URL,
114      @"email_password",                KEY_SIPE_EMAIL_PASSWORD,
115      @"groupchat_user",                KEY_SIPE_GROUP_CHAT_PROXY,
116      @"useragent",                     KEY_SIPE_USER_AGENT,
117      @"sso",                           KEY_SIPE_SINGLE_SIGN_ON,
118      @"dont-publish",                  KEY_SIPE_DONT_PUBLISH,
119      @"authentication",                KEY_SIPE_AUTH_SCHEME,
120      @"ssl_cdsa_beast_tls_workaround", KEY_SIPE_BEAST_DISABLE,
121      @"allow-web-photo",               KEY_SIPE_ALLOW_WEB_PHOTO,
122      nil
123      ];
125     for (NSString* key in keys_to_account) {
126         NSString *prpl_key = [keys_to_account objectForKey:key];
127         id value = [self preferenceForKey:key group:GROUP_ACCOUNT_STATUS];
129         if ([value isKindOfClass:[NSString class]]) {
130             if ([key isEqualToString:KEY_SIPE_CONNECT_HOST]) {
131                 if ([value isEqualToString:@""]) {
132                     /*
133                      * We're using auto-discover, i.e. we can only
134                      * determine the real server name when we have a
135                      * valid network connection.
136                      *
137                      * Unfortunately Adiums' reachability feature
138                      * requires us to specify a host even when no
139                      * network is available:
140                      *
141                      *   * must be a valid DNS name
142                      *     (can't use [[NSHost currentHost] ...])
143                      *   * must only be reachable via network
144                      *     (can't use @"localhost")
145                      *
146                      * Hard-code a well-known host name instead. As
147                      * this is for Adium we use the obvious choice.
148                      *
149                      * NOTE: this will fail for Intranet-only users.
150                      *
151                      * See also: https://sourceforge.net/p/sipe/bugs/262
152                      */
153                     [self setPreference:@"adium.im" forKey:KEY_CONNECT_HOST group:GROUP_ACCOUNT_STATUS];
154                 } else {
155                     // If the user entered server:port only give the server portion to adium
156                     // otherwise the DNS lookup will fail the reachability test
157                     NSArray *server = [value componentsSeparatedByString:@":"];
158                     [self setPreference:[server objectAtIndex:0] forKey:KEY_CONNECT_HOST group:GROUP_ACCOUNT_STATUS];
159                 }
160             }
162             purple_account_set_string(account, [prpl_key UTF8String], [value UTF8String]);
163         } else if ([value isKindOfClass:[NSNumber class]]) {
164             if ([key isEqualToString:KEY_SIPE_CONNECTION_TYPE]) {
165                 NSString *tmp = [myArray objectAtIndex:(NSUInteger)value];
166                 purple_account_set_string(account, [prpl_key UTF8String], [tmp UTF8String]);
167             } else {
168                 purple_account_set_bool(account, [prpl_key UTF8String], [value boolValue]);
169             }
170         } else {
171             AILog(@"(ESPurpleSIPEAccount) Unknown class %@ for key %@", [value class], key);
172         }
173     }
175     // 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
176     if ( [[self preferenceForKey:KEY_SIPE_SINGLE_SIGN_ON group:GROUP_ACCOUNT_STATUS] boolValue] &&
177         [[self preferenceForKey:KEY_SIPE_PASSWORD group:GROUP_ACCOUNT_STATUS] isEqualToString:@""] )
178     {
179         [self setPasswordTemporarily:@"placeholder"];
180     }
183 #pragma mark File transfer
185 - (BOOL)canSendFolders
187         return NO;
190 - (void)beginSendOfFileTransfer:(ESFileTransfer *)fileTransfer
192         [super _beginSendOfFileTransfer:fileTransfer];
195 - (void)acceptFileTransferRequest:(ESFileTransfer *)fileTransfer
197     [super acceptFileTransferRequest:fileTransfer];
200 - (void)rejectFileReceiveRequest:(ESFileTransfer *)fileTransfer
202     [super rejectFileReceiveRequest:fileTransfer];
205 - (void)cancelFileTransfer:(ESFileTransfer *)fileTransfer
207         [super cancelFileTransfer:fileTransfer];
210 #pragma mark Status Messages
212  * @brief Status name to use for a Purple buddy
213  */
214 - (NSString *)statusNameForPurpleBuddy:(PurpleBuddy *)buddy
216     NSString *statusName;
217     PurplePresence  *presence = purple_buddy_get_presence(buddy);
218     PurpleStatus    *status = purple_presence_get_active_status(presence);
219     NSString        *purpleStatusID = [NSString stringWithUTF8String:purple_status_get_id(status)];
221     if (!purpleStatusID) return nil;
223     if (sipe_to_adium_status[purpleStatusID])
224         statusName = sipe_to_adium_status[purpleStatusID];
225     else {
226         AILog(@"(ESPurpleSIPEAccount) Unknown purpleStatusID in statusNameForPurpleBuddy: %@", purpleStatusID);
227         statusName = STATUS_NAME_OFFLINE;
228     }
230     return statusName;
234  * @brief Maps purple status IDs to Adium statuses
235  */
236  - (const char *)purpleStatusIDForStatus:(AIStatus *)statusState arguments:(NSMutableDictionary *)arguments
238      const gchar    *statusID;
239      NSString           *statusName = statusState.statusName;
241      if ( adium_to_sipe_status[statusName] )
242          statusID = [adium_to_sipe_status[statusName] UTF8String];
243      else {
244          AILog(@"(ESPurpleSIPEAccount): Unknown statusName in purpleStatusIDForStatus: %@", statusName);
245          statusID = [super purpleStatusIDForStatus:statusState arguments:arguments];
246      }
248      return statusID;
252 // Improve the formatting of displayed names
253  - (AIListContact *)contactWithUID:(NSString *)sourceUID
255     // give the inherited implementation a whack at finding a contact
256     AIListContact *contact = [super contactWithUID:sourceUID];
258     NSRange sipURI = [sourceUID rangeOfString:@"sip:"];
259     if (sipURI.location != NSNotFound) {
260         // sourceUID is of the form "sip:<username>@<domain>".
261         // strip out the sip: part, and try find a contact with the nice display name
262         NSString *displayName = [sourceUID substringFromIndex:sipURI.location + sipURI.length];
264         // check to see if we have a contact already with the non-sip'ified username
265         if([adium.contactController existingContactWithService:service account:self UID:displayName])
266         {
267             // if we do, lets return it!
268             contact = [adium.contactController existingContactWithService:service account:self UID:displayName];
269         }
270         else
271         {
272             // otherwise, return the contact from super, setting formattedUID with "sip:" chopped
273             [contact setFormattedUID:displayName notify:NotifyNow];
274         }
277     }
279     return contact;
283 // generate group chat's creation dictionary from purple conversation, e.g. for bookmarking
284 - (NSDictionary *)extractChatCreationDictionaryFromConversation:(PurpleConversation *)conv
286     NSDictionary *dict = nil;
287     struct sipe_core_public *sipe_public = PURPLE_ACCOUNT_TO_SIPE_CORE_PUBLIC;
288     // called during purple_serv_got_joined_chat()?
289     struct sipe_chat_session *session = sipe_public->backend_private->adium_chat_session;
291     // Adium might never call this method after creating the chat. Just in case...
292     if (!session)
293         session = sipe_purple_chat_get_session(conv);
295     if (session) {
296         const gchar *uri = sipe_core_chat_id(sipe_public, session);
297         dict = [NSDictionary dictionaryWithObjectsAndKeys:
298                 [NSString stringWithUTF8String:uri], @"uri",
299                 nil
300                ];
301     } else {
302         AILog(@"(ESPurpleSIPEAccount) Can't determine chat session in extractChatCreationDictionaryFromConversation:");
303     }
305     return dict;
308 @end