purple: update purple-chat for 3.x.x API (II)
[siplcs.git] / src / purple / purple-chat.c
blob9b49c2b932ab17426912b68f86744498a395f309
1 /**
2 * @file purple-chat.c
4 * pidgin-sipe
6 * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
7 * Copyright (C) 2009 pier11 <pier11@operamail.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
29 #include <time.h>
31 #include <glib.h>
33 #include "conversation.h"
34 #include "server.h"
35 /* for ENOTCONN */
36 #ifdef _WIN32
37 #include "win32/win32dep.h"
38 #else
39 #include <errno.h>
40 #endif
42 #include "version.h"
43 #if PURPLE_VERSION_CHECK(3,0,0)
44 #include "buddylist.h"
45 #include "conversations.h"
46 #define BACKEND_SESSION_TO_PURPLE_CONV_CHAT(s) ((PurpleChatConversation *) s)
47 #define PURPLE_CONV_CHAT(c) c
48 #define PURPLE_CONV_TO_SIPE_CORE_PUBLIC ((struct sipe_core_public *) purple_connection_get_protocol_data(purple_conversation_get_connection(conv)))
49 #else
50 #include "blist.h"
51 #define purple_chat_conversation_add_user(c, n, m, f, b) purple_conv_chat_add_user(c, n, m, f, b)
52 #define purple_chat_conversation_clear_users(c) purple_conv_chat_clear_users(c)
53 #define purple_chat_conversation_get_id(c) purple_conv_chat_get_id(c)
54 #define purple_chat_conversation_remove_user(c, n, s) purple_conv_chat_remove_user(c, n, s)
55 #define purple_chat_conversation_set_nick(c, n) purple_conv_chat_set_nick(c, n)
56 #define purple_chat_conversation_set_topic(c, n, s) purple_conv_chat_set_topic(c, n, s)
57 #define purple_chat_get_components(chat) chat->components
58 #define purple_conversations_find_chat(g, n) purple_find_chat(g, n)
59 #define purple_conversations_get_chats purple_get_chats
60 #define purple_conversation_get_connection(c) purple_conversation_get_gc(c)
61 #define BACKEND_SESSION_TO_PURPLE_CONV_CHAT(s) (PURPLE_CONV_CHAT(((PurpleConversation *)s)))
62 #define PURPLE_CHAT_USER_NONE PURPLE_CBFLAGS_NONE
63 #define PURPLE_CONV_TO_SIPE_CORE_PUBLIC ((struct sipe_core_public *) conv->account->gc->proto_data)
64 #define PURPLE_CONVERSATION_UPDATE_TOPIC PURPLE_CONV_UPDATE_TOPIC
65 #endif
67 #include "sipe-common.h"
68 #include "sipe-backend.h"
69 #include "sipe-core.h"
70 #include "sipe-nls.h"
72 #define _PurpleMessageFlags PurpleMessageFlags
73 #include "purple-private.h"
75 /**
76 * Mapping between chat sessions in SIPE core and libpurple backend
78 * PurpleAccount
79 * This data structure is created when the user creates the account or at
80 * startup. It lives as long as the account exists, i.e. until the user
81 * deletes it or shutdown.
83 * Value does not change when connection is dropped & re-created.
84 * HAS: gc (PurpleConnection *)
86 * PurpleConversation / PurpleConvChat (sub-type)
87 * This data structure is created by serv_got_join_chat(). It lives as long
88 * as the user doesn't leave the chat or until shutdown.
90 * Value does not change when connection is dropped & re-created.
91 * HAS: account (PurpleAccount *)
92 * HAS: chat ID (int), must be unique
93 * HAS: name (char *), must be unique
94 * HAS: data (GHashTable *)
96 * PurpleConnection
97 * This data structure is created when the connection to the service is
98 * set up. It lives as long as the connection stays open, the user disables
99 * the account or until shutdown.
101 * Value *DOES NOT* survive a connection drop & re-creation.
102 * ASSOCIATED TO: account
104 * SIPE -> libpurple API
105 * add user: purple_conv_chat_add_user(conv, ...)
106 * create: serv_got_joined_chat(gc, chat ID, name)
107 * find user: purple_conv_chat_find_user(conv, ...)
108 * message: serv_got_chat_in(gc, chat ID, ...)
109 * remove user: purple_conv_chat_remove_user(conv, ...)
110 * topic: purple_conv_chat_set_topic(conv, ...)
112 * libpurple -> SIPE API
113 * join_chat(gc, params (GHashTable *))
114 * request to join a channel (again) [only Group Chat]
115 * SIPE must call serv_got_joined_chat() on join response
117 * reject_chat(gc, params (GHashTable *)) NOT IMPLEMENTED
118 * get_chat_name(params (GHashTable *)) NOT IMPLEMENTED
120 * chat_invite(gc, chat ID,...)
121 * invite a user to a join a chat
123 * chat_leave(gc, chat ID)
124 * request to leave a channel, also called on conversation destroy
125 * SIPE must call serv_got_chat_left() immediately!
127 * chat_whisper(gc, chat ID, ...) NOT IMPLEMENTED
129 * chat_send(gc, chat ID, ...)
130 * send a message to the channel
132 * set_chat_topic(gc, chat ID, ...) NOT IMPLEMENTED
133 * set channel topic [@TODO: for Group Chat]
136 * struct sipe_chat_session
137 * Same life span as PurpleConversation
138 * Pointer stored under key "sipe" in PurpleConversation->data
139 * Contains information private to core to identify chat session on server
141 * If connection is closed and THEN the conversation, then libpurple will
142 * not call chat_leave() and this will be a dangling data structure! Core
143 * must take care to release them at unload.
145 * HAS: backend_session (gpointer) -> PurpleConversation
147 * struct sipe_backend_private
149 * HAS: rejoin_chats (GList *)
150 * created on login() for existing chats
151 * initiate re-join calls to core (sipe_backend_chat_rejoin_all)
154 #define SIPE_PURPLE_KEY_CHAT_SESSION "sipe"
156 static struct sipe_chat_session *sipe_purple_chat_get_session(PurpleConversation *conv)
158 return(
159 #if PURPLE_VERSION_CHECK(3,0,0)
160 g_object_get_data(G_OBJECT(conv),
161 #else
162 purple_conversation_get_data(conv,
163 #endif
164 SIPE_PURPLE_KEY_CHAT_SESSION));
167 static struct sipe_chat_session *sipe_purple_chat_find(PurpleConnection *gc,
168 int id)
170 PurpleConversation *conv = (PurpleConversation *) purple_conversations_find_chat(gc, id);
172 if (!conv) {
173 SIPE_DEBUG_ERROR("sipe_purple_chat_find: can't find chat with ID %d?!?",
174 id);
175 return NULL;
178 return sipe_purple_chat_get_session(conv);
181 void sipe_purple_chat_setup_rejoin(struct sipe_backend_private *purple_private)
183 GList *entry = purple_conversations_get_chats();
185 while (entry) {
186 PurpleConversation *conv = entry->data;
187 if (purple_conversation_get_connection(conv) == purple_private->gc)
188 purple_private->rejoin_chats = g_list_prepend(purple_private->rejoin_chats,
189 sipe_purple_chat_get_session(conv));
190 entry = entry->next;
194 void sipe_purple_chat_destroy_rejoin(struct sipe_backend_private *purple_private)
196 g_list_free(purple_private->rejoin_chats);
197 purple_private->rejoin_chats = NULL;
200 void sipe_purple_chat_invite(PurpleConnection *gc, int id,
201 SIPE_UNUSED_PARAMETER const char *message,
202 const char *name)
204 struct sipe_chat_session *session = sipe_purple_chat_find(gc, id);
205 if (!session) return;
207 sipe_core_chat_invite(PURPLE_GC_TO_SIPE_CORE_PUBLIC, session, name);
210 void sipe_purple_chat_leave(PurpleConnection *gc, int id)
212 struct sipe_chat_session *session = sipe_purple_chat_find(gc, id);
213 if (!session) return;
215 sipe_core_chat_leave(PURPLE_GC_TO_SIPE_CORE_PUBLIC, session);
218 int sipe_purple_chat_send(PurpleConnection *gc,
219 int id,
220 const char *what,
221 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags)
223 struct sipe_chat_session *session = sipe_purple_chat_find(gc, id);
224 if (!session) return -ENOTCONN;
225 sipe_core_chat_send(PURPLE_GC_TO_SIPE_CORE_PUBLIC, session, what);
226 return 1;
229 static void sipe_purple_chat_menu_unlock_cb(SIPE_UNUSED_PARAMETER PurpleChat *chat,
230 PurpleConversation *conv)
232 struct sipe_core_public *sipe_public = PURPLE_CONV_TO_SIPE_CORE_PUBLIC;
233 struct sipe_chat_session *chat_session = sipe_purple_chat_get_session(conv);
234 SIPE_DEBUG_INFO("sipe_purple_chat_menu_lock_cb: %p %p", conv, chat_session);
235 sipe_core_chat_modify_lock(sipe_public, chat_session, FALSE);
238 static void sipe_purple_chat_menu_lock_cb(SIPE_UNUSED_PARAMETER PurpleChat *chat,
239 PurpleConversation *conv)
241 struct sipe_core_public *sipe_public = PURPLE_CONV_TO_SIPE_CORE_PUBLIC;
242 struct sipe_chat_session *chat_session = sipe_purple_chat_get_session(conv);
243 SIPE_DEBUG_INFO("sipe_purple_chat_menu_lock_cb: %p %p", conv, chat_session);
244 sipe_core_chat_modify_lock(sipe_public, chat_session, TRUE);
247 #ifdef HAVE_VV
249 static void sipe_purple_chat_menu_join_call_cb(SIPE_UNUSED_PARAMETER PurpleChat *chat,
250 PurpleConversation *conv)
252 struct sipe_core_public *sipe_public = PURPLE_CONV_TO_SIPE_CORE_PUBLIC;
253 struct sipe_chat_session *chat_session = sipe_purple_chat_get_session(conv);
254 SIPE_DEBUG_INFO("sipe_purple_chat_join_call_cb: %p %p", conv, chat_session);
255 sipe_core_media_connect_conference(sipe_public, chat_session);
258 #endif
260 GList *
261 sipe_purple_chat_menu(PurpleChat *chat)
263 PurpleConversation *conv = g_hash_table_lookup(purple_chat_get_components(chat),
264 SIPE_PURPLE_COMPONENT_KEY_CONVERSATION);
265 GList *menu = NULL;
267 if (conv) {
268 PurpleMenuAction *act = NULL;
270 SIPE_DEBUG_INFO("sipe_purple_chat_menu: %p", conv);
272 switch (sipe_core_chat_lock_status(PURPLE_CONV_TO_SIPE_CORE_PUBLIC,
273 sipe_purple_chat_get_session(conv))) {
274 case SIPE_CHAT_LOCK_STATUS_UNLOCKED:
275 act = purple_menu_action_new(_("Lock"),
276 PURPLE_CALLBACK(sipe_purple_chat_menu_lock_cb),
277 conv, NULL);
278 break;
279 case SIPE_CHAT_LOCK_STATUS_LOCKED:
280 act = purple_menu_action_new(_("Unlock"),
281 PURPLE_CALLBACK(sipe_purple_chat_menu_unlock_cb),
282 conv, NULL);
283 break;
284 default:
285 /* Not allowed */
286 break;
289 if (act)
290 menu = g_list_prepend(menu, act);
291 #ifdef HAVE_VV
292 if (!sipe_core_media_in_call(PURPLE_CONV_TO_SIPE_CORE_PUBLIC)) {
293 act = NULL;
294 act = purple_menu_action_new(_("Join conference call"),
295 PURPLE_CALLBACK(sipe_purple_chat_menu_join_call_cb),
296 conv, NULL);
297 if (act)
298 menu = g_list_prepend(menu, act);
300 #endif
303 return menu;
306 void sipe_backend_chat_session_destroy(SIPE_UNUSED_PARAMETER struct sipe_backend_chat_session *session)
308 /* Nothing to do here */
311 void sipe_backend_chat_add(struct sipe_backend_chat_session *backend_session,
312 const gchar *uri,
313 gboolean is_new)
315 purple_chat_conversation_add_user(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
316 uri,
317 NULL,
318 PURPLE_CHAT_USER_NONE,
319 is_new);
322 void sipe_backend_chat_close(struct sipe_backend_chat_session *backend_session)
324 purple_chat_conversation_clear_users(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session));
327 static int sipe_purple_chat_id(PurpleConnection *gc)
330 * A non-volatile ID counter.
331 * Should survive connection drop & reconnect.
333 static int chat_id = 0;
335 /* Find next free ID */
336 do {
337 if (++chat_id < 0) chat_id = 0;
338 } while (purple_conversations_find_chat(gc, chat_id) != NULL)
340 return chat_id;
343 struct sipe_backend_chat_session *sipe_backend_chat_create(struct sipe_core_public *sipe_public,
344 struct sipe_chat_session *session,
345 const gchar *title,
346 const gchar *nick)
348 struct sipe_backend_private *purple_private = sipe_public->backend_private;
349 #if PURPLE_VERSION_CHECK(3,0,0)
350 PurpleChatConversation *conv =
351 #else
352 PurpleConversation *conv =
353 #endif
354 serv_got_joined_chat(purple_private->gc,
355 sipe_purple_chat_id(purple_private->gc),
356 title);
357 #if PURPLE_VERSION_CHECK(3,0,0)
358 g_object_set_data(G_OBJECT(conv),
359 #else
360 purple_conversation_set_data(conv,
361 #endif
362 SIPE_PURPLE_KEY_CHAT_SESSION,
363 session);
364 purple_chat_conversation_set_nick(PURPLE_CONV_CHAT(conv), nick);
365 return((struct sipe_backend_chat_session *) conv);
368 gboolean sipe_backend_chat_find(struct sipe_backend_chat_session *backend_session,
369 const gchar *uri)
371 #if PURPLE_VERSION_CHECK(3,0,0)
372 return(purple_chat_conversation_find_user(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
373 uri) != NULL);
374 #else
375 return purple_conv_chat_find_user(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
376 uri);
377 #endif
380 gboolean sipe_backend_chat_is_operator(struct sipe_backend_chat_session *backend_session,
381 const gchar *uri)
383 #if PURPLE_VERSION_CHECK(3,0,0)
384 return((purple_chat_user_get_flags(
385 purple_chat_conversation_find_user(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
386 uri))
387 & PURPLE_CHAT_USER_OP)
388 == PURPLE_CHAT_USER_OP);
389 #else
390 return (purple_conv_chat_user_get_flags(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
391 uri) & PURPLE_CBFLAGS_OP)
392 == PURPLE_CBFLAGS_OP;
393 #endif
396 void sipe_backend_chat_message(struct sipe_core_public *sipe_public,
397 struct sipe_backend_chat_session *backend_session,
398 const gchar *from,
399 time_t when,
400 const gchar *html)
402 struct sipe_backend_private *purple_private = sipe_public->backend_private;
403 serv_got_chat_in(purple_private->gc,
404 purple_chat_conversation_get_id(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session)),
405 from,
406 PURPLE_MESSAGE_RECV,
407 html,
408 when ? when : time(NULL));
411 void sipe_backend_chat_operator(struct sipe_backend_chat_session *backend_session,
412 const gchar *uri)
414 #if PURPLE_VERSION_CHECK(3,0,0)
415 purple_chat_user_set_flags(
416 purple_chat_conversation_find_user(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
417 uri),
418 PURPLE_CHAT_USER_NONE | PURPLE_CHAT_USER_OP);
419 #else
420 purple_conv_chat_user_set_flags(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
421 uri,
422 PURPLE_CBFLAGS_NONE | PURPLE_CBFLAGS_OP);
423 #endif
426 void sipe_backend_chat_rejoin(struct sipe_core_public *sipe_public,
427 struct sipe_backend_chat_session *backend_session,
428 const gchar *nick,
429 const gchar *title)
431 struct sipe_backend_private *purple_private = sipe_public->backend_private;
432 #if PURPLE_VERSION_CHECK(3,0,0)
433 PurpleChatConversation *chat = BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session);
434 PurpleChatConversation *new;
435 #else
436 PurpleConvChat *chat = BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session);
437 PurpleConversation *new;
438 #endif
441 * As the chat is marked as "left", serv_got_joined_chat() will
442 * do a "rejoin cleanup" and return the same conversation.
444 new = serv_got_joined_chat(purple_private->gc,
445 purple_chat_conversation_get_id(chat),
446 title);
447 SIPE_DEBUG_INFO("sipe_backend_chat_rejoin: old %p (%p) == new %p (%p)",
448 backend_session, chat,
449 new, PURPLE_CONV_CHAT(new));
450 purple_chat_conversation_set_nick(chat, nick);
454 * Connection re-established: tell core what chats need to be rejoined
456 void sipe_backend_chat_rejoin_all(struct sipe_core_public *sipe_public)
458 struct sipe_backend_private *purple_private = sipe_public->backend_private;
459 GList *entry = purple_private->rejoin_chats;
461 while (entry) {
462 sipe_core_chat_rejoin(sipe_public, entry->data);
463 entry = entry->next;
465 sipe_purple_chat_destroy_rejoin(purple_private);
468 void sipe_backend_chat_remove(struct sipe_backend_chat_session *backend_session,
469 const gchar *uri)
471 purple_chat_conversation_remove_user(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
472 uri,
473 NULL /* reason */);
476 void sipe_backend_chat_show(struct sipe_backend_chat_session *backend_session)
478 /* Bring existing purple chat to the front */
479 /* @TODO: This seems to the trick, but is it the correct way? */
480 purple_conversation_update((PurpleConversation *) backend_session,
481 PURPLE_CONVERSATION_UPDATE_TOPIC);
484 void sipe_backend_chat_topic(struct sipe_backend_chat_session *backend_session,
485 const gchar *topic)
487 purple_chat_conversation_set_topic(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
488 NULL,
489 topic);
493 Local Variables:
494 mode: c
495 c-file-style: "bsd"
496 indent-tabs-mode: t
497 tab-width: 8
498 End: