mingw: fix compilation error
[siplcs.git] / src / purple / purple-chat.c
blobb6b2cc54cd4d5fabba47ae4d26b2f91261682dcf
1 /**
2 * @file purple-chat.c
4 * pidgin-sipe
6 * Copyright (C) 2010-12 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 "blist.h"
34 #include "conversation.h"
35 #include "server.h"
36 #include "version.h"
37 /* for ENOTCONN */
38 #ifdef _WIN32
39 #include "win32/win32dep.h"
40 #else
41 #include <errno.h>
42 #endif
44 #include "sipe-common.h"
45 #include "sipe-backend.h"
46 #include "sipe-core.h"
47 #include "sipe-nls.h"
49 #define _PurpleMessageFlags PurpleMessageFlags
50 #include "purple-private.h"
52 /**
53 * Mapping between chat sessions in SIPE core and libpurple backend
55 * PurpleAccount
56 * This data structure is created when the user creates the account or at
57 * startup. It lives as long as the account exists, i.e. until the user
58 * deletes it or shutdown.
60 * Value does not change when connection is dropped & re-created.
61 * HAS: gc (PurpleConnection *)
63 * PurpleConversation / PurpleConvChat (sub-type)
64 * This data structure is created by serv_got_join_chat(). It lives as long
65 * as the user doesn't leave the chat or until shutdown.
67 * Value does not change when connection is dropped & re-created.
68 * HAS: account (PurpleAccount *)
69 * HAS: chat ID (int), must be unique
70 * HAS: name (char *), must be unique
71 * HAS: data (GHashTable *)
73 * PurpleConnection
74 * This data structure is created when the connection to the service is
75 * set up. It lives as long as the connection stays open, the user disables
76 * the account or until shutdown.
78 * Value *DOES NOT* survive a connection drop & re-creation.
79 * ASSOCIATED TO: account
81 * SIPE -> libpurple API
82 * add user: purple_conv_chat_add_user(conv, ...)
83 * create: serv_got_joined_chat(gc, chat ID, name)
84 * find user: purple_conv_chat_find_user(conv, ...)
85 * message: serv_got_chat_in(gc, chat ID, ...)
86 * remove user: purple_conv_chat_remove_user(conv, ...)
87 * topic: purple_conv_chat_set_topic(conv, ...)
89 * libpurple -> SIPE API
90 * join_chat(gc, params (GHashTable *))
91 * request to join a channel (again) [only Group Chat]
92 * SIPE must call serv_got_joined_chat() on join response
94 * reject_chat(gc, params (GHashTable *)) NOT IMPLEMENTED
95 * get_chat_name(params (GHashTable *)) NOT IMPLEMENTED
97 * chat_invite(gc, chat ID,...)
98 * invite a user to a join a chat
100 * chat_leave(gc, chat ID)
101 * request to leave a channel, also called on conversation destroy
102 * SIPE must call serv_got_chat_left() immediately!
104 * chat_whisper(gc, chat ID, ...) NOT IMPLEMENTED
106 * chat_send(gc, chat ID, ...)
107 * send a message to the channel
109 * set_chat_topic(gc, chat ID, ...) NOT IMPLEMENTED
110 * set channel topic [@TODO: for Group Chat]
113 * struct sipe_chat_session
114 * Same life span as PurpleConversation
115 * Pointer stored under key "sipe" in PurpleConversation->data
116 * Contains information private to core to identify chat session on server
118 * If connection is closed and THEN the conversation, then libpurple will
119 * not call chat_leave() and this will be a dangling data structure! Core
120 * must take care to release them at unload.
122 * HAS: backend_session (gpointer) -> PurpleConversation
124 * struct sipe_backend_private
126 * HAS: rejoin_chats (GList *)
127 * created on login() for existing chats
128 * initiate re-join calls to core (sipe_backend_chat_rejoin_all)
131 #define SIPE_PURPLE_KEY_CHAT_SESSION "sipe"
133 #define BACKEND_SESSION_TO_PURPLE_CONV_CHAT(s) \
134 (PURPLE_CONV_CHAT(((PurpleConversation *)s)))
136 #if PURPLE_VERSION_CHECK(3,0,0)
137 #define PURPLE_CONV_TO_SIPE_CORE_PUBLIC ((struct sipe_core_public *) purple_conversation_get_connection(conv)->proto_data)
138 #else
139 #define PURPLE_CONV_TO_SIPE_CORE_PUBLIC ((struct sipe_core_public *) conv->account->gc->proto_data)
140 #endif
142 static struct sipe_chat_session *sipe_purple_chat_get_session(PurpleConversation *conv)
144 return purple_conversation_get_data(conv,
145 SIPE_PURPLE_KEY_CHAT_SESSION);
148 static struct sipe_chat_session *sipe_purple_chat_find(PurpleConnection *gc,
149 int id)
151 PurpleConversation *conv = purple_find_chat(gc, id);
153 if (!conv) {
154 SIPE_DEBUG_ERROR("sipe_purple_chat_find: can't find chat with ID %d?!?",
155 id);
156 return NULL;
159 return sipe_purple_chat_get_session(conv);
162 void sipe_purple_chat_setup_rejoin(struct sipe_backend_private *purple_private)
164 GList *entry = purple_get_chats();
166 while (entry) {
167 PurpleConversation *conv = entry->data;
168 if (
169 #if PURPLE_VERSION_CHECK(3,0,0)
170 purple_conversation_get_connection(conv)
171 #else
172 purple_conversation_get_gc(conv)
173 #endif
174 == purple_private->gc)
175 purple_private->rejoin_chats = g_list_prepend(purple_private->rejoin_chats,
176 sipe_purple_chat_get_session(conv));
177 entry = entry->next;
181 void sipe_purple_chat_destroy_rejoin(struct sipe_backend_private *purple_private)
183 g_list_free(purple_private->rejoin_chats);
184 purple_private->rejoin_chats = NULL;
187 void sipe_purple_chat_invite(PurpleConnection *gc, int id,
188 SIPE_UNUSED_PARAMETER const char *message,
189 const char *name)
191 struct sipe_chat_session *session = sipe_purple_chat_find(gc, id);
192 if (!session) return;
194 sipe_core_chat_invite(PURPLE_GC_TO_SIPE_CORE_PUBLIC, session, name);
197 void sipe_purple_chat_leave(PurpleConnection *gc, int id)
199 struct sipe_chat_session *session = sipe_purple_chat_find(gc, id);
200 if (!session) return;
202 sipe_core_chat_leave(PURPLE_GC_TO_SIPE_CORE_PUBLIC, session);
205 int sipe_purple_chat_send(PurpleConnection *gc,
206 int id,
207 const char *what,
208 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags)
210 struct sipe_chat_session *session = sipe_purple_chat_find(gc, id);
211 if (!session) return -ENOTCONN;
212 sipe_core_chat_send(PURPLE_GC_TO_SIPE_CORE_PUBLIC, session, what);
213 return 1;
216 static void sipe_purple_chat_menu_unlock_cb(SIPE_UNUSED_PARAMETER PurpleChat *chat,
217 PurpleConversation *conv)
219 struct sipe_core_public *sipe_public = PURPLE_CONV_TO_SIPE_CORE_PUBLIC;
220 struct sipe_chat_session *chat_session = sipe_purple_chat_get_session(conv);
221 SIPE_DEBUG_INFO("sipe_purple_chat_menu_lock_cb: %p %p", conv, chat_session);
222 sipe_core_chat_modify_lock(sipe_public, chat_session, FALSE);
225 static void sipe_purple_chat_menu_lock_cb(SIPE_UNUSED_PARAMETER PurpleChat *chat,
226 PurpleConversation *conv)
228 struct sipe_core_public *sipe_public = PURPLE_CONV_TO_SIPE_CORE_PUBLIC;
229 struct sipe_chat_session *chat_session = sipe_purple_chat_get_session(conv);
230 SIPE_DEBUG_INFO("sipe_purple_chat_menu_lock_cb: %p %p", conv, chat_session);
231 sipe_core_chat_modify_lock(sipe_public, chat_session, TRUE);
234 #ifdef HAVE_VV
236 static void sipe_purple_chat_menu_join_call_cb(SIPE_UNUSED_PARAMETER PurpleChat *chat,
237 PurpleConversation *conv)
239 struct sipe_core_public *sipe_public = PURPLE_CONV_TO_SIPE_CORE_PUBLIC;
240 struct sipe_chat_session *chat_session = sipe_purple_chat_get_session(conv);
241 SIPE_DEBUG_INFO("sipe_purple_chat_join_call_cb: %p %p", conv, chat_session);
242 sipe_core_media_connect_conference(sipe_public, chat_session);
245 #endif
247 GList *
248 sipe_purple_chat_menu(PurpleChat *chat)
250 PurpleConversation *conv = g_hash_table_lookup(chat->components,
251 SIPE_PURPLE_COMPONENT_KEY_CONVERSATION);
252 GList *menu = NULL;
254 if (conv) {
255 PurpleMenuAction *act = NULL;
257 SIPE_DEBUG_INFO("sipe_purple_chat_menu: %p", conv);
259 switch (sipe_core_chat_lock_status(PURPLE_CONV_TO_SIPE_CORE_PUBLIC,
260 sipe_purple_chat_get_session(conv))) {
261 case SIPE_CHAT_LOCK_STATUS_UNLOCKED:
262 act = purple_menu_action_new(_("Lock"),
263 PURPLE_CALLBACK(sipe_purple_chat_menu_lock_cb),
264 conv, NULL);
265 break;
266 case SIPE_CHAT_LOCK_STATUS_LOCKED:
267 act = purple_menu_action_new(_("Unlock"),
268 PURPLE_CALLBACK(sipe_purple_chat_menu_unlock_cb),
269 conv, NULL);
270 break;
271 default:
272 /* Not allowed */
273 break;
276 if (act)
277 menu = g_list_prepend(menu, act);
278 #ifdef HAVE_VV
279 if (!sipe_core_media_in_call(PURPLE_CONV_TO_SIPE_CORE_PUBLIC)) {
280 act = NULL;
281 act = purple_menu_action_new(_("Join conference call"),
282 PURPLE_CALLBACK(sipe_purple_chat_menu_join_call_cb),
283 conv, NULL);
284 if (act)
285 menu = g_list_prepend(menu, act);
287 #endif
290 return menu;
293 void sipe_backend_chat_session_destroy(SIPE_UNUSED_PARAMETER struct sipe_backend_chat_session *session)
295 /* Nothing to do here */
298 void sipe_backend_chat_add(struct sipe_backend_chat_session *backend_session,
299 const gchar *uri,
300 gboolean is_new)
302 purple_conv_chat_add_user(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
303 uri, NULL, PURPLE_CBFLAGS_NONE, is_new);
306 void sipe_backend_chat_close(struct sipe_backend_chat_session *backend_session)
308 purple_conv_chat_clear_users(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session));
311 static int sipe_purple_chat_id(PurpleConnection *gc)
314 * A non-volatile ID counter.
315 * Should survive connection drop & reconnect.
317 static int chat_id = 0;
319 /* Find next free ID */
320 do {
321 if (++chat_id < 0) chat_id = 0;
322 } while (purple_find_chat(gc, chat_id) != NULL)
324 return chat_id;
327 struct sipe_backend_chat_session *sipe_backend_chat_create(struct sipe_core_public *sipe_public,
328 struct sipe_chat_session *session,
329 const gchar *title,
330 const gchar *nick)
332 struct sipe_backend_private *purple_private = sipe_public->backend_private;
333 PurpleConversation *conv = serv_got_joined_chat(purple_private->gc,
334 sipe_purple_chat_id(purple_private->gc),
335 title);
336 purple_conversation_set_data(conv,
337 SIPE_PURPLE_KEY_CHAT_SESSION,
338 session);
339 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(conv), nick);
340 return((struct sipe_backend_chat_session *) conv);
343 gboolean sipe_backend_chat_find(struct sipe_backend_chat_session *backend_session,
344 const gchar *uri)
346 return purple_conv_chat_find_user(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
347 uri);
350 gboolean sipe_backend_chat_is_operator(struct sipe_backend_chat_session *backend_session,
351 const gchar *uri)
353 return (purple_conv_chat_user_get_flags(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
354 uri) & PURPLE_CBFLAGS_OP)
355 == PURPLE_CBFLAGS_OP;
358 void sipe_backend_chat_message(struct sipe_core_public *sipe_public,
359 struct sipe_backend_chat_session *backend_session,
360 const gchar *from,
361 const gchar *html)
363 struct sipe_backend_private *purple_private = sipe_public->backend_private;
364 serv_got_chat_in(purple_private->gc,
365 purple_conv_chat_get_id(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session)),
366 from,
367 PURPLE_MESSAGE_RECV,
368 html,
369 time(NULL));
372 void sipe_backend_chat_operator(struct sipe_backend_chat_session *backend_session,
373 const gchar *uri)
375 purple_conv_chat_user_set_flags(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
376 uri,
377 PURPLE_CBFLAGS_NONE | PURPLE_CBFLAGS_OP);
380 void sipe_backend_chat_rejoin(struct sipe_core_public *sipe_public,
381 struct sipe_backend_chat_session *backend_session,
382 const gchar *nick,
383 const gchar *title)
385 struct sipe_backend_private *purple_private = sipe_public->backend_private;
386 PurpleConvChat *chat = BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session);
387 PurpleConversation *new;
390 * As the chat is marked as "left", serv_got_joined_chat() will
391 * do a "rejoin cleanup" and return the same conversation.
393 new = serv_got_joined_chat(purple_private->gc,
394 purple_conv_chat_get_id(chat),
395 title);
396 SIPE_DEBUG_INFO("sipe_backend_chat_rejoin: old %p (%p) == new %p (%p)",
397 backend_session, chat,
398 new, PURPLE_CONV_CHAT(new));
399 purple_conv_chat_set_nick(chat, nick);
403 * Connection re-established: tell core what chats need to be rejoined
405 void sipe_backend_chat_rejoin_all(struct sipe_core_public *sipe_public)
407 struct sipe_backend_private *purple_private = sipe_public->backend_private;
408 GList *entry = purple_private->rejoin_chats;
410 while (entry) {
411 sipe_core_chat_rejoin(sipe_public, entry->data);
412 entry = entry->next;
414 sipe_purple_chat_destroy_rejoin(purple_private);
417 void sipe_backend_chat_remove(struct sipe_backend_chat_session *backend_session,
418 const gchar *uri)
420 purple_conv_chat_remove_user(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
421 uri,
422 NULL /* reason */);
425 void sipe_backend_chat_show(struct sipe_backend_chat_session *backend_session)
427 /* Bring existing purple chat to the front */
428 /* @TODO: This seems to the trick, but is it the correct way? */
429 purple_conversation_update((PurpleConversation *) backend_session,
430 PURPLE_CONV_UPDATE_TOPIC);
433 void sipe_backend_chat_topic(struct sipe_backend_chat_session *backend_session,
434 const gchar *topic)
436 purple_conv_chat_set_topic(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
437 NULL,
438 topic);
442 Local Variables:
443 mode: c
444 c-file-style: "bsd"
445 indent-tabs-mode: t
446 tab-width: 8
447 End: