purple: fix build when appshare server is disabled
[siplcs.git] / src / purple / purple-chat.c
blobbfe66c482de7a91b3db2a9633c3a8f4582834fef
1 /**
2 * @file purple-chat.c
4 * pidgin-sipe
6 * Copyright (C) 2010-2018 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 purple_serv_got_chat_in(c, i, w, f, m, t) serv_got_chat_in(c, i, w, f, m, t)
62 #define purple_serv_got_joined_chat(c, i, n) serv_got_joined_chat(c, i, n)
63 #define BACKEND_SESSION_TO_PURPLE_CONV_CHAT(s) (PURPLE_CONV_CHAT(((PurpleConversation *)s)))
64 #define PURPLE_CHAT_USER_NONE PURPLE_CBFLAGS_NONE
65 #define PURPLE_CONV_TO_SIPE_CORE_PUBLIC ((struct sipe_core_public *) conv->account->gc->proto_data)
66 #define PURPLE_CONVERSATION_UPDATE_TOPIC PURPLE_CONV_UPDATE_TOPIC
67 #endif
69 #include "sipe-common.h"
70 #include "sipe-backend.h"
71 #include "sipe-core.h"
72 #include "sipe-nls.h"
74 #define _PurpleMessageFlags PurpleMessageFlags
75 #include "purple-private.h"
77 /**
78 * Mapping between chat sessions in SIPE core and libpurple backend
80 * PurpleAccount
81 * This data structure is created when the user creates the account or at
82 * startup. It lives as long as the account exists, i.e. until the user
83 * deletes it or shutdown.
85 * Value does not change when connection is dropped & re-created.
86 * HAS: gc (PurpleConnection *)
88 * PurpleConversation / PurpleConvChat (sub-type)
89 * This data structure is created by serv_got_join_chat(). It lives as long
90 * as the user doesn't leave the chat or until shutdown.
92 * Value does not change when connection is dropped & re-created.
93 * HAS: account (PurpleAccount *)
94 * HAS: chat ID (int), must be unique
95 * HAS: name (char *), must be unique
96 * HAS: data (GHashTable *)
98 * PurpleConnection
99 * This data structure is created when the connection to the service is
100 * set up. It lives as long as the connection stays open, the user disables
101 * the account or until shutdown.
103 * Value *DOES NOT* survive a connection drop & re-creation.
104 * ASSOCIATED TO: account
106 * SIPE -> libpurple API
107 * add user: purple_conv_chat_add_user(conv, ...)
108 * create: serv_got_joined_chat(gc, chat ID, name)
109 * find user: purple_conv_chat_find_user(conv, ...)
110 * message: serv_got_chat_in(gc, chat ID, ...)
111 * remove user: purple_conv_chat_remove_user(conv, ...)
112 * topic: purple_conv_chat_set_topic(conv, ...)
114 * libpurple -> SIPE API
115 * join_chat(gc, params (GHashTable *))
116 * request to join a channel (again) [only Group Chat]
117 * SIPE must call serv_got_joined_chat() on join response
119 * reject_chat(gc, params (GHashTable *)) NOT IMPLEMENTED
120 * get_chat_name(params (GHashTable *)) NOT IMPLEMENTED
122 * chat_invite(gc, chat ID,...)
123 * invite a user to a join a chat
125 * chat_leave(gc, chat ID)
126 * request to leave a channel, also called on conversation destroy
127 * SIPE must call serv_got_chat_left() immediately!
129 * chat_whisper(gc, chat ID, ...) NOT IMPLEMENTED
131 * chat_send(gc, chat ID, ...)
132 * send a message to the channel
134 * set_chat_topic(gc, chat ID, ...) NOT IMPLEMENTED
135 * set channel topic [@TODO: for Group Chat]
138 * struct sipe_chat_session
139 * Same life span as PurpleConversation
140 * Pointer stored under key "sipe" in PurpleConversation->data
141 * Contains information private to core to identify chat session on server
143 * If connection is closed and THEN the conversation, then libpurple will
144 * not call chat_leave() and this will be a dangling data structure! Core
145 * must take care to release them at unload.
147 * HAS: backend_session (gpointer) -> PurpleConversation
149 * struct sipe_backend_private
151 * HAS: rejoin_chats (GList *)
152 * created on login() for existing chats
153 * initiate re-join calls to core (sipe_backend_chat_rejoin_all)
156 #define SIPE_PURPLE_KEY_CHAT_SESSION "sipe"
158 struct sipe_chat_session *sipe_purple_chat_get_session(PurpleConversation *conv)
160 return(
161 #if PURPLE_VERSION_CHECK(3,0,0)
162 g_object_get_data(G_OBJECT(conv),
163 #else
164 purple_conversation_get_data(conv,
165 #endif
166 SIPE_PURPLE_KEY_CHAT_SESSION));
169 static struct sipe_chat_session *sipe_purple_chat_find(PurpleConnection *gc,
170 int id)
172 PurpleConversation *conv = (PurpleConversation *) purple_conversations_find_chat(gc, id);
174 if (!conv) {
175 SIPE_DEBUG_ERROR("sipe_purple_chat_find: can't find chat with ID %d?!?",
176 id);
177 return NULL;
180 return sipe_purple_chat_get_session(conv);
183 void sipe_purple_chat_setup_rejoin(struct sipe_backend_private *purple_private)
185 GList *entry = purple_conversations_get_chats();
187 while (entry) {
188 PurpleConversation *conv = entry->data;
189 if (purple_conversation_get_connection(conv) == purple_private->gc)
190 purple_private->rejoin_chats = g_list_prepend(purple_private->rejoin_chats,
191 sipe_purple_chat_get_session(conv));
192 entry = entry->next;
196 void sipe_purple_chat_destroy_rejoin(struct sipe_backend_private *purple_private)
198 g_list_free(purple_private->rejoin_chats);
199 purple_private->rejoin_chats = NULL;
202 void sipe_purple_chat_invite(PurpleConnection *gc, int id,
203 SIPE_UNUSED_PARAMETER const char *message,
204 const char *name)
206 struct sipe_chat_session *session = sipe_purple_chat_find(gc, id);
207 if (!session) return;
209 sipe_core_chat_invite(PURPLE_GC_TO_SIPE_CORE_PUBLIC, session, name);
212 void sipe_purple_chat_leave(PurpleConnection *gc, int id)
214 struct sipe_chat_session *session = sipe_purple_chat_find(gc, id);
215 if (!session) return;
217 sipe_core_chat_leave(PURPLE_GC_TO_SIPE_CORE_PUBLIC, session);
220 int sipe_purple_chat_send(PurpleConnection *gc,
221 int id,
222 #if PURPLE_VERSION_CHECK(3,0,0)
223 PurpleMessage *msg)
224 #else
225 const char *what,
226 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags)
227 #endif
229 struct sipe_chat_session *session = sipe_purple_chat_find(gc, id);
230 if (!session) return -ENOTCONN;
231 sipe_core_chat_send(PURPLE_GC_TO_SIPE_CORE_PUBLIC, session,
232 #if PURPLE_VERSION_CHECK(3,0,0)
233 purple_message_get_contents(msg));
234 #else
235 what);
236 #endif
237 return 1;
240 static void sipe_purple_chat_menu_unlock_cb(SIPE_UNUSED_PARAMETER PurpleChat *chat,
241 PurpleConversation *conv)
243 struct sipe_core_public *sipe_public = PURPLE_CONV_TO_SIPE_CORE_PUBLIC;
244 struct sipe_chat_session *chat_session = sipe_purple_chat_get_session(conv);
245 SIPE_DEBUG_INFO("sipe_purple_chat_menu_lock_cb: %p %p", conv, chat_session);
246 sipe_core_chat_modify_lock(sipe_public, chat_session, FALSE);
249 static void sipe_purple_chat_menu_lock_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_menu_lock_cb: %p %p", conv, chat_session);
255 sipe_core_chat_modify_lock(sipe_public, chat_session, TRUE);
258 #ifdef HAVE_VV
260 static void sipe_purple_chat_menu_join_call_cb(SIPE_UNUSED_PARAMETER PurpleChat *chat,
261 PurpleConversation *conv)
263 struct sipe_core_public *sipe_public = PURPLE_CONV_TO_SIPE_CORE_PUBLIC;
264 struct sipe_chat_session *chat_session = sipe_purple_chat_get_session(conv);
265 SIPE_DEBUG_INFO("sipe_purple_chat_join_call_cb: %p %p", conv, chat_session);
266 sipe_core_media_connect_conference(sipe_public, chat_session);
269 #ifdef HAVE_APPSHARE
270 static void
271 sipe_purple_chat_menu_show_presentation_cb(SIPE_UNUSED_PARAMETER PurpleChat *chat,
272 PurpleConversation *conv)
274 sipe_appshare_role role;
276 role = sipe_core_conf_get_appshare_role(PURPLE_CONV_TO_SIPE_CORE_PUBLIC,
277 sipe_purple_chat_get_session(conv));
279 if (role == SIPE_APPSHARE_ROLE_VIEWER) {
280 return;
283 sipe_core_appshare_connect_conference(PURPLE_CONV_TO_SIPE_CORE_PUBLIC,
284 sipe_purple_chat_get_session(conv),
285 FALSE);
288 #ifdef HAVE_APPSHARE_SERVER
289 static void
290 sipe_purple_chat_menu_share_desktop_cb(SIPE_UNUSED_PARAMETER PurpleChat *chat,
291 PurpleConversation *conv)
293 sipe_core_conf_share_desktop(PURPLE_CONV_TO_SIPE_CORE_PUBLIC,
294 sipe_purple_chat_get_session(conv));
296 #endif
297 #endif
298 #endif // HAVE_VV
300 static void sipe_purple_chat_menu_entry_info_cb(SIPE_UNUSED_PARAMETER PurpleChat *chat,
301 PurpleConversation *conv)
303 gchar *tmp = sipe_core_conf_entry_info(PURPLE_CONV_TO_SIPE_CORE_PUBLIC,
304 sipe_purple_chat_get_session(conv));
305 purple_notify_formatted(NULL, NULL, "", NULL, tmp, NULL, NULL);
306 g_free(tmp);
309 GList *
310 sipe_purple_chat_menu(PurpleChat *chat)
312 PurpleConversation *conv = g_hash_table_lookup(purple_chat_get_components(chat),
313 SIPE_PURPLE_COMPONENT_KEY_CONVERSATION);
314 GList *menu = NULL;
316 if (conv) {
317 PurpleMenuAction *act = NULL;
318 struct sipe_chat_session *chat_session;
319 #ifdef HAVE_APPSHARE
320 sipe_appshare_role role;
321 #endif
323 SIPE_DEBUG_INFO("sipe_purple_chat_menu: %p", conv);
325 chat_session = sipe_purple_chat_get_session(conv);
327 switch (sipe_core_chat_lock_status(PURPLE_CONV_TO_SIPE_CORE_PUBLIC,
328 chat_session)) {
329 case SIPE_CHAT_LOCK_STATUS_UNLOCKED:
330 act = purple_menu_action_new(_("Lock"),
331 PURPLE_CALLBACK(sipe_purple_chat_menu_lock_cb),
332 conv, NULL);
333 break;
334 case SIPE_CHAT_LOCK_STATUS_LOCKED:
335 act = purple_menu_action_new(_("Unlock"),
336 PURPLE_CALLBACK(sipe_purple_chat_menu_unlock_cb),
337 conv, NULL);
338 break;
339 default:
340 /* Not allowed */
341 break;
344 if (act)
345 menu = g_list_prepend(menu, act);
347 switch (sipe_core_chat_type(chat_session)) {
348 case SIPE_CHAT_TYPE_CONFERENCE:
349 case SIPE_CHAT_TYPE_MULTIPARTY:
350 #ifdef HAVE_VV
351 if (!sipe_core_media_get_call(PURPLE_CONV_TO_SIPE_CORE_PUBLIC)) {
352 act = NULL;
353 act = purple_menu_action_new(_("Join conference call"),
354 PURPLE_CALLBACK(sipe_purple_chat_menu_join_call_cb),
355 conv, NULL);
356 if (act)
357 menu = g_list_prepend(menu, act);
359 #ifdef HAVE_APPSHARE
360 role = sipe_core_conf_get_appshare_role(PURPLE_CONV_TO_SIPE_CORE_PUBLIC,
361 chat_session);
362 if (role == SIPE_APPSHARE_ROLE_NONE) {
363 act = purple_menu_action_new(_("Show presentation"),
364 PURPLE_CALLBACK(sipe_purple_chat_menu_show_presentation_cb),
365 conv, NULL);
366 menu = g_list_prepend(menu, act);
368 #ifdef HAVE_APPSHARE_SERVER
369 if (role != SIPE_APPSHARE_ROLE_PRESENTER) {
370 act = purple_menu_action_new(_("Share my desktop"),
371 PURPLE_CALLBACK(sipe_purple_chat_menu_share_desktop_cb),
372 conv, NULL);
373 menu = g_list_prepend(menu, act);
375 #endif
376 #endif
377 #endif // HAVE_VV
378 act = purple_menu_action_new(_("Meeting entry info"),
379 PURPLE_CALLBACK(sipe_purple_chat_menu_entry_info_cb),
380 conv, NULL);
381 menu = g_list_append(menu, act);
382 break;
383 default:
384 break;
388 return menu;
391 void sipe_backend_chat_session_destroy(SIPE_UNUSED_PARAMETER struct sipe_backend_chat_session *session)
393 /* Nothing to do here */
396 void sipe_backend_chat_add(struct sipe_backend_chat_session *backend_session,
397 const gchar *uri,
398 gboolean is_new)
400 purple_chat_conversation_add_user(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
401 uri,
402 NULL,
403 PURPLE_CHAT_USER_NONE,
404 is_new);
407 void sipe_backend_chat_close(struct sipe_backend_chat_session *backend_session)
409 purple_chat_conversation_clear_users(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session));
412 static int sipe_purple_chat_id(PurpleConnection *gc)
415 * A non-volatile ID counter.
416 * Should survive connection drop & reconnect.
418 static int chat_id = 0;
420 /* Find next free ID */
421 do {
422 if (++chat_id < 0) chat_id = 0;
423 } while (purple_conversations_find_chat(gc, chat_id) != NULL)
425 return chat_id;
428 struct sipe_backend_chat_session *sipe_backend_chat_create(struct sipe_core_public *sipe_public,
429 struct sipe_chat_session *session,
430 const gchar *title,
431 const gchar *nick)
433 struct sipe_backend_private *purple_private = sipe_public->backend_private;
434 #if PURPLE_VERSION_CHECK(3,0,0)
435 PurpleChatConversation *conv;
436 #else
437 PurpleConversation *conv;
438 #endif
441 * Adium calls back into SIPE code during execution of the following
442 * libpurple API. That code needs access to "session". As "conv" is
443 * still being initialized we can't use sipe_purple_chat_get_session().
445 purple_private->adium_chat_session = session;
446 conv = purple_serv_got_joined_chat(purple_private->gc,
447 sipe_purple_chat_id(purple_private->gc),
448 title);
449 purple_private->adium_chat_session = NULL;
450 #if PURPLE_VERSION_CHECK(3,0,0)
451 g_object_set_data(G_OBJECT(conv),
452 #else
453 purple_conversation_set_data(conv,
454 #endif
455 SIPE_PURPLE_KEY_CHAT_SESSION,
456 session);
457 purple_chat_conversation_set_nick(PURPLE_CONV_CHAT(conv), nick);
458 return((struct sipe_backend_chat_session *) conv);
461 gboolean sipe_backend_chat_find(struct sipe_backend_chat_session *backend_session,
462 const gchar *uri)
464 #if PURPLE_VERSION_CHECK(3,0,0)
465 return(purple_chat_conversation_find_user(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
466 uri) != NULL);
467 #else
468 return purple_conv_chat_find_user(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
469 uri);
470 #endif
473 gboolean sipe_backend_chat_is_operator(struct sipe_backend_chat_session *backend_session,
474 const gchar *uri)
476 #if PURPLE_VERSION_CHECK(3,0,0)
477 return((purple_chat_user_get_flags(
478 purple_chat_conversation_find_user(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
479 uri))
480 & PURPLE_CHAT_USER_OP)
481 == PURPLE_CHAT_USER_OP);
482 #else
483 return (purple_conv_chat_user_get_flags(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
484 uri) & PURPLE_CBFLAGS_OP)
485 == PURPLE_CBFLAGS_OP;
486 #endif
489 void sipe_backend_chat_message(struct sipe_core_public *sipe_public,
490 struct sipe_backend_chat_session *backend_session,
491 const gchar *from,
492 time_t when,
493 const gchar *html)
495 struct sipe_backend_private *purple_private = sipe_public->backend_private;
496 purple_serv_got_chat_in(purple_private->gc,
497 purple_chat_conversation_get_id(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session)),
498 from,
499 PURPLE_MESSAGE_RECV,
500 html,
501 when ? when : time(NULL));
504 void sipe_backend_chat_operator(struct sipe_backend_chat_session *backend_session,
505 const gchar *uri)
507 #if PURPLE_VERSION_CHECK(3,0,0)
508 purple_chat_user_set_flags(
509 purple_chat_conversation_find_user(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
510 uri),
511 PURPLE_CHAT_USER_NONE | PURPLE_CHAT_USER_OP);
512 #else
513 purple_conv_chat_user_set_flags(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
514 uri,
515 PURPLE_CBFLAGS_NONE | PURPLE_CBFLAGS_OP);
516 #endif
519 void sipe_backend_chat_rejoin(struct sipe_core_public *sipe_public,
520 struct sipe_backend_chat_session *backend_session,
521 const gchar *nick,
522 const gchar *title)
524 struct sipe_backend_private *purple_private = sipe_public->backend_private;
525 #if PURPLE_VERSION_CHECK(3,0,0)
526 PurpleChatConversation *chat = BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session);
527 PurpleChatConversation *new;
528 #else
529 PurpleConvChat *chat = BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session);
530 PurpleConversation *new;
531 #endif
534 * As the chat is marked as "left", serv_got_joined_chat() will
535 * do a "rejoin cleanup" and return the same conversation.
537 new = purple_serv_got_joined_chat(purple_private->gc,
538 purple_chat_conversation_get_id(chat),
539 title);
540 SIPE_DEBUG_INFO("sipe_backend_chat_rejoin: old %p (%p) == new %p (%p)",
541 backend_session, chat,
542 new, PURPLE_CONV_CHAT(new));
543 purple_chat_conversation_set_nick(chat, nick);
547 * Connection re-established: tell core what chats need to be rejoined
549 void sipe_backend_chat_rejoin_all(struct sipe_core_public *sipe_public)
551 struct sipe_backend_private *purple_private = sipe_public->backend_private;
552 GList *entry = purple_private->rejoin_chats;
554 while (entry) {
555 sipe_core_chat_rejoin(sipe_public, entry->data);
556 entry = entry->next;
558 sipe_purple_chat_destroy_rejoin(purple_private);
561 void sipe_backend_chat_remove(struct sipe_backend_chat_session *backend_session,
562 const gchar *uri)
564 purple_chat_conversation_remove_user(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
565 uri,
566 NULL /* reason */);
569 void sipe_backend_chat_show(struct sipe_backend_chat_session *backend_session)
571 /* Bring existing purple chat to the front */
572 /* @TODO: This seems to the trick, but is it the correct way? */
573 purple_conversation_update((PurpleConversation *) backend_session,
574 PURPLE_CONVERSATION_UPDATE_TOPIC);
577 void sipe_backend_chat_topic(struct sipe_backend_chat_session *backend_session,
578 const gchar *topic)
580 purple_chat_conversation_set_topic(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
581 NULL,
582 topic);
586 Local Variables:
587 mode: c
588 c-file-style: "bsd"
589 indent-tabs-mode: t
590 tab-width: 8
591 End: