Release 1.25.0 -- Buddy Idle Time, RTF
[siplcs.git] / src / adium / ESPurpleSIPEAccount.m
blob3a0677717b25e55fb2eeedbd045d0bd6824ca99f
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 <Adium/AISharedAdium.h>
13 #import <Adium/AIStatus.h>
14 #import <Adium/AIStatusControllerProtocol.h>
15 #import <Adium/ESDebugAILog.h>
16 #import <Adium/AIListContact.h>
17 #import "AIContactController.h"
19 #import "ESPurpleSIPEAccount.h"
20 #import "ESSIPEService.h"
22 #include "sipe-core.h"
23 #include "sipe-backend.h"
24 #include "purple-private.h"
26 @class AICoreComponentLoader;
28 @implementation ESPurpleSIPEAccount
30 - (void)initAccount
32     [super initAccount];
34     sipe_to_adium_status =
35     [[NSDictionary alloc] initWithObjectsAndKeys:
36      STATUS_NAME_AVAILABLE,         @"available",                 //SIPE_ACTIVITY_AVAILABLE
37      STATUS_NAME_AVAILABLE,         @"online",                    //SIPE_ACTIVITY_ONLINE
38      STATUS_NAME_AWAY,              @"idle",                      //SIPE_ACTIVITY_INACTIVE
39      STATUS_NAME_BUSY,              @"busy",                      //SIPE_ACTIVITY_BUSY
40      STATUS_NAME_BUSY,              @"busyidle",                  //SIPE_ACTIVITY_BUSYIDLE
41      STATUS_NAME_DND,               @"do-not-disturb",            //SIPE_ACTIVITY_DND
42      STATUS_NAME_BRB,               @"be-right-back",             //SIPE_ACTIVITY_BRB
43      STATUS_NAME_AWAY,              @"away",                      //SIPE_ACTIVITY_AWAY
44      STATUS_NAME_LUNCH,             @"out-to-lunch",              //SIPE_ACTIVITY_LUNCH
45      STATUS_NAME_INVISIBLE,         @"invisible",                 //SIPE_ACTIVITY_INVISIBLE
46      STATUS_NAME_OFFLINE,           @"offline",                   //SIPE_ACTIVITY_OFFLINE
47      STATUS_NAME_PHONE,             @"on-the-phone",              //SIPE_ACTIVITY_ON_PHONE
48      STATUS_NAME_NOT_AT_DESK,       @"in-a-conference",           //SIPE_ACTIVITY_IN_CONF
49      STATUS_NAME_NOT_AT_DESK,       @"in-a-meeting",              //SIPE_ACTIVITY_IN_MEETING
50      STATUS_NAME_NOT_IN_OFFICE,     @"out-of-office",             //SIPE_ACTIVITY_OOF
51      STATUS_NAME_AWAY_FRIENDS_ONLY, @"urgent-interruptions-only", //SIPE_ACTIVITY_URGENT_ONLY
52      nil
53      ];
55     adium_to_sipe_status =
56     [[NSDictionary alloc] initWithObjectsAndKeys:
57      @"available",                 STATUS_NAME_AVAILABLE,         //SIPE_ACTIVITY_AVAILABLE
58      @"busy",                      STATUS_NAME_BUSY,              //SIPE_ACTIVITY_BUSY
59      @"do-not-disturb",            STATUS_NAME_DND,               //SIPE_ACTIVITY_DND
60      @"be-right-back",             STATUS_NAME_BRB,               //SIPE_ACTIVITY_BRB
61      @"away",                      STATUS_NAME_AWAY,              //SIPE_ACTIVITY_AWAY
62      @"out-to-lunch",              STATUS_NAME_LUNCH,             //SIPE_ACTIVITY_LUNCH
63      @"invisible",                 STATUS_NAME_INVISIBLE,         //SIPE_ACTIVITY_INVISIBLE
64      @"offline",                   STATUS_NAME_OFFLINE,           //SIPE_ACTIVITY_OFFLINE
65      @"on-the-phone",              STATUS_NAME_PHONE,             //SIPE_ACTIVITY_ON_PHONE
66      @"in-a-meeting",              STATUS_NAME_NOT_AT_DESK,       //SIPE_ACTIVITY_IN_MEETING
67      @"out-of-office",             STATUS_NAME_NOT_IN_OFFICE,     //SIPE_ACTIVITY_OOF
68      @"urgent-interruptions-only", STATUS_NAME_AWAY_FRIENDS_ONLY, //SIPE_ACTIVITY_URGENT_ONLY
69      nil
70      ];
73 - (void)dealloc
75     [adium_to_sipe_status release];
76     [sipe_to_adium_status release];
77     [super dealloc];
80 - (const char*)protocolPlugin
82         return "prpl-sipe";
85 - (const char *)purpleAccountName
87     NSString *completeUserName = [NSString stringWithUTF8String:[super purpleAccountName]];
88     NSString *windowsLogin =[self preferenceForKey:KEY_SIPE_WINDOWS_LOGIN group:GROUP_ACCOUNT_STATUS];
90     if ( ![windowsLogin isEqualToString:@""] ) {
91         completeUserName = [NSString stringWithFormat:@"%@,%@", completeUserName, windowsLogin];
92     }
94         return [completeUserName UTF8String];
97 #pragma mark Account Configuration
98 - (void)configurePurpleAccount
100     // Account preferences
101     AILog(@"(ESPurpleSIPEAccount) Configuring account: %s\n", self.purpleAccountName);
103     NSArray *myArray = [NSArray arrayWithObjects:@"auto", @"tls", @"tcp", nil];
105     NSDictionary *keys_to_account =
106     [NSDictionary dictionaryWithObjectsAndKeys:
107      @"server",                        KEY_SIPE_CONNECT_HOST,
108      @"password",                      KEY_SIPE_PASSWORD,
109      @"transport",                     KEY_SIPE_CONNECTION_TYPE,
110      @"email",                         KEY_SIPE_EMAIL,
111      @"email_login",                   KEY_SIPE_EMAIL_LOGIN,
112      @"email_url",                     KEY_SIPE_EMAIL_URL,
113      @"email_password",                KEY_SIPE_EMAIL_PASSWORD,
114      @"groupchat_user",                KEY_SIPE_GROUP_CHAT_PROXY,
115      @"useragent",                     KEY_SIPE_USER_AGENT,
116      @"sso",                           KEY_SIPE_SINGLE_SIGN_ON,
117      @"dont-publish",                  KEY_SIPE_DONT_PUBLISH,
118      @"authentication",                KEY_SIPE_AUTH_SCHEME,
119      @"ssl_cdsa_beast_tls_workaround", KEY_SIPE_BEAST_DISABLE,
120      @"allow-web-photo",               KEY_SIPE_ALLOW_WEB_PHOTO,
121      nil
122      ];
124     for (NSString* key in keys_to_account) {
125         NSString *prpl_key = [keys_to_account objectForKey:key];
126         id value = [self preferenceForKey:key group:GROUP_ACCOUNT_STATUS];
128         if ([value isKindOfClass:[NSString class]]) {
129             if ([key isEqualToString:KEY_SIPE_CONNECT_HOST]) {
130                 if ([value isEqualToString:@""]) {
131                     /*
132                      * We're using auto-discover, i.e. we can only
133                      * determine the real server name when we have a
134                      * valid network connection.
135                      *
136                      * Unfortunately Adiums' reachability feature
137                      * requires us to specify a host even when no
138                      * network is available:
139                      *
140                      *   * must be a valid DNS name
141                      *     (can't use [[NSHost currentHost] ...])
142                      *   * must only be reachable via network
143                      *     (can't use @"localhost")
144                      *
145                      * Hard-code a well-known host name instead. As
146                      * this is for Adium we use the obvious choice.
147                      *
148                      * NOTE: this will fail for Intranet-only users.
149                      *
150                      * See also: https://sourceforge.net/p/sipe/bugs/262
151                      */
152                     [self setPreference:@"adium.im" forKey:KEY_CONNECT_HOST group:GROUP_ACCOUNT_STATUS];
153                 } else {
154                     // If the user entered server:port only give the server portion to adium
155                     // otherwise the DNS lookup will fail the reachability test
156                     NSArray *server = [value componentsSeparatedByString:@":"];
157                     [self setPreference:[server objectAtIndex:0] forKey:KEY_CONNECT_HOST group:GROUP_ACCOUNT_STATUS];
158                 }
159             }
161             purple_account_set_string(account, [prpl_key UTF8String], [value UTF8String]);
162         } else if ([value isKindOfClass:[NSNumber class]]) {
163             if ([key isEqualToString:KEY_SIPE_CONNECTION_TYPE]) {
164                 NSString *tmp = [myArray objectAtIndex:(NSUInteger)value];
165                 purple_account_set_string(account, [prpl_key UTF8String], [tmp UTF8String]);
166             } else {
167                 purple_account_set_bool(account, [prpl_key UTF8String], [value boolValue]);
168             }
169         } else {
170             AILog(@"(ESPurpleSIPEAccount) Unknown class %@ for key %@", [value class], key);
171         }
172     }
174     // 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
175     if ( [[self preferenceForKey:KEY_SIPE_SINGLE_SIGN_ON group:GROUP_ACCOUNT_STATUS] boolValue] &&
176         [[self preferenceForKey:KEY_SIPE_PASSWORD group:GROUP_ACCOUNT_STATUS] isEqualToString:@""] )
177     {
178         [self setPasswordTemporarily:@"placeholder"];
179     }
182 #pragma mark File transfer
184 - (BOOL)canSendFolders
186         return NO;
189 - (void)beginSendOfFileTransfer:(ESFileTransfer *)fileTransfer
191         [super _beginSendOfFileTransfer:fileTransfer];
194 - (void)acceptFileTransferRequest:(ESFileTransfer *)fileTransfer
196     [super acceptFileTransferRequest:fileTransfer];
199 - (void)rejectFileReceiveRequest:(ESFileTransfer *)fileTransfer
201     [super rejectFileReceiveRequest:fileTransfer];
204 - (void)cancelFileTransfer:(ESFileTransfer *)fileTransfer
206         [super cancelFileTransfer:fileTransfer];
209 #pragma mark Status Messages
211  * @brief Status name to use for a Purple buddy
212  */
213 - (NSString *)statusNameForPurpleBuddy:(PurpleBuddy *)buddy
215     NSString *statusName;
216     PurplePresence  *presence = purple_buddy_get_presence(buddy);
217     PurpleStatus    *status = purple_presence_get_active_status(presence);
218     NSString        *purpleStatusID = [NSString stringWithUTF8String:purple_status_get_id(status)];
220     if (!purpleStatusID) return nil;
222     if (sipe_to_adium_status[purpleStatusID])
223         statusName = sipe_to_adium_status[purpleStatusID];
224     else {
225         AILog(@"(ESPurpleSIPEAccount) Unknown purpleStatusID in statusNameForPurpleBuddy: %@", purpleStatusID);
226         statusName = STATUS_NAME_OFFLINE;
227     }
229     return statusName;
233  * @brief Maps purple status IDs to Adium statuses
234  */
235  - (const char *)purpleStatusIDForStatus:(AIStatus *)statusState arguments:(NSMutableDictionary *)arguments
237      const gchar    *statusID;
238      NSString           *statusName = statusState.statusName;
240      if ( adium_to_sipe_status[statusName] )
241          statusID = [adium_to_sipe_status[statusName] UTF8String];
242      else {
243          AILog(@"(ESPurpleSIPEAccount): Unknown statusName in purpleStatusIDForStatus: %@", statusName);
244          statusID = [super purpleStatusIDForStatus:statusState arguments:arguments];
245      }
247      return statusID;
251 // Improve the formatting of displayed names
252  - (AIListContact *)contactWithUID:(NSString *)sourceUID
254     // give the inherited implementation a whack at finding a contact
255     AIListContact *contact = [super contactWithUID:sourceUID];
257     NSRange sipURI = [sourceUID rangeOfString:@"sip:"];
258     if (sipURI.location != NSNotFound) {
259         // sourceUID is of the form "sip:<username>@<domain>".
260         // strip out the sip: part, and try find a contact with the nice display name
261         NSString *displayName = [sourceUID substringFromIndex:sipURI.location + sipURI.length];
263         // check to see if we have a contact already with the non-sip'ified username
264         if([adium.contactController existingContactWithService:service account:self UID:displayName])
265         {
266             // if we do, lets return it!
267             contact = [adium.contactController existingContactWithService:service account:self UID:displayName];
268         }
269         else
270         {
271             // otherwise, return the contact from super, setting formattedUID with "sip:" chopped
272             [contact setFormattedUID:displayName notify:NotifyNow];
273         }
276     }
278     return contact;
282 // generate group chat's creation dictionary from purple conversation, e.g. for bookmarking
283 - (NSDictionary *)extractChatCreationDictionaryFromConversation:(PurpleConversation *)conv
285     NSDictionary *dict = nil;
286     struct sipe_core_public *sipe_public = PURPLE_ACCOUNT_TO_SIPE_CORE_PUBLIC;
287     // called during purple_serv_got_joined_chat()?
288     struct sipe_chat_session *session = sipe_public->backend_private->adium_chat_session;
290     // Adium might never call this method after creating the chat. Just in case...
291     if (!session)
292         session = sipe_purple_chat_get_session(conv);
294     if (session) {
295         const gchar *uri = sipe_core_chat_id(sipe_public, session);
296         dict = [NSDictionary dictionaryWithObjectsAndKeys:
297                 [NSString stringWithUTF8String:uri], @"uri",
298                 nil
299                ];
300     } else {
301         AILog(@"(ESPurpleSIPEAccount) Can't determine chat session in extractChatCreationDictionaryFromConversation:");
302     }
304     return dict;
307 @end