chat: make Invite... menu work again
[siplcs.git] / src / purple / purple-chat.c
blobe0072d4ee70530c5eeed7c9ea450938ba0248c0b
1 /**
2 * @file purple-chat.c
4 * pidgin-sipe
6 * Copyright (C) 2010 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>
30 #include <errno.h>
32 #include <glib.h>
34 #include "blist.h"
35 #include "conversation.h"
36 #include "server.h"
38 #include "sipe-common.h"
39 #include "sipe-backend.h"
40 #include "sipe-core.h"
41 #include "sipe-nls.h"
43 #define _PurpleMessageFlags PurpleMessageFlags
44 #include "purple-private.h"
46 /**
47 * Mapping between chat sessions in SIPE core and libpurple backend
49 * PurpleAccount
50 * This data structure is created when the user creates the account or at
51 * startup. It lives as long as the account exists, i.e. until the user
52 * deletes it or shutdown.
54 * Value does not change when connection is dropped & re-created.
55 * HAS: gc (PurpleConnection *)
57 * PurpleConversation / PurpleConvChat (sub-type)
58 * This data structure is created by serv_got_join_chat(). It lives as long
59 * as the user doesn't leave the chat or until shutdown.
61 * Value does not change when connection is dropped & re-created.
62 * HAS: account (PurpleAccount *)
63 * HAS: chat ID (int), must be unique
64 * HAS: name (char *), must be unique
65 * HAS: data (GHashTable *)
67 * PurpleConnection
68 * This data structure is created when the connection to the service is
69 * set up. It lives as long as the connection stays open, the user disables
70 * the account or until shutdown.
72 * Value *DOES NOT* survive a connection drop & re-creation.
73 * ASSOCIATED TO: account
75 * SIPE -> libpurple API
76 * add user: purple_conv_chat_add_user(conv, ...)
77 * create: serv_got_joined_chat(gc, chat ID, name)
78 * find user: purple_conv_chat_find_user(conv, ...)
79 * message: serv_got_chat_in(gc, chat ID, ...)
80 * remove user: purple_conv_chat_remove_user(conv, ...)
81 * topic: purple_conv_chat_set_topic(conv, ...)
83 * libpurple -> SIPE API
84 * join_chat(gc, params (GHashTable *))
85 * request to join a channel (again) [only Group Chat]
86 * SIPE must call serv_got_joined_chat() on join response
88 * reject_chat(gc, params (GHashTable *)) NOT IMPLEMENTED
89 * get_chat_name(params (GHashTable *)) NOT IMPLEMENTED
91 * chat_invite(gc, chat ID,...)
92 * invite a user to a join a chat
94 * chat_leave(gc, chat ID)
95 * request to leave a channel, also called on conversation destroy
96 * SIPE must call serv_got_chat_left() immediately!
98 * chat_whisper(gc, chat ID, ...) NOT IMPLEMENTED
100 * chat_send(gc, chat ID, ...)
101 * send a message to the channel
103 * set_chat_topic(gc, chat ID, ...) NOT IMPLEMENTED
104 * set channel topic [@TODO: for Group Chat]
107 * struct sipe_chat_session
108 * Same life span as PurpleConversation
109 * Pointer stored under key "sipe" in PurpleConversation->data
110 * Contains information private to core to identify chat session on server
112 * If connection is closed and THEN the conversation, then libpurple will
113 * not call chat_leave() and this will be a dangling data structure! Core
114 * must take care to release them at unload.
116 * HAS: backend_session (gpointer) -> PurpleConversation
118 * struct sipe_backend_private
120 * HAS: rejoin_chats (GList *)
121 * created on login() for existing chats
122 * initiate re-join calls to core (sipe_backend_chat_rejoin_all)
125 #define SIPE_PURPLE_KEY_CHAT_SESSION "sipe"
127 #define BACKEND_SESSION_TO_PURPLE_CONV_CHAT(s) \
128 (PURPLE_CONV_CHAT(((PurpleConversation *)s)))
130 #define PURPLE_CONV_TO_SIPE_CORE_PUBLIC ((struct sipe_core_public *) conv->account->gc->proto_data)
132 static struct sipe_chat_session *sipe_purple_chat_get_session(PurpleConversation *conv)
134 return purple_conversation_get_data(conv,
135 SIPE_PURPLE_KEY_CHAT_SESSION);
138 static struct sipe_chat_session *sipe_purple_chat_find(PurpleConnection *gc,
139 int id)
141 PurpleConversation *conv = purple_find_chat(gc, id);
143 if (!conv) {
144 SIPE_DEBUG_ERROR("sipe_purple_chat_find: can't find chat with ID %d?!?",
145 id);
146 return NULL;
149 return sipe_purple_chat_get_session(conv);
152 void sipe_purple_chat_setup_rejoin(struct sipe_backend_private *purple_private)
154 GList *entry = purple_get_chats();
156 while (entry) {
157 PurpleConversation *conv = entry->data;
158 if (purple_conversation_get_gc(conv) == purple_private->gc)
159 purple_private->rejoin_chats = g_list_prepend(purple_private->rejoin_chats,
160 sipe_purple_chat_get_session(conv));
161 entry = entry->next;
165 void sipe_purple_chat_destroy_rejoin(struct sipe_backend_private *purple_private)
167 g_list_free(purple_private->rejoin_chats);
168 purple_private->rejoin_chats = NULL;
171 void sipe_purple_chat_invite(PurpleConnection *gc, int id,
172 SIPE_UNUSED_PARAMETER const char *message,
173 const char *name)
175 struct sipe_chat_session *session = sipe_purple_chat_find(gc, id);
176 if (!session) return;
178 sipe_core_chat_invite(PURPLE_GC_TO_SIPE_CORE_PUBLIC, session, name);
181 void sipe_purple_chat_leave(PurpleConnection *gc, int id)
183 struct sipe_chat_session *session = sipe_purple_chat_find(gc, id);
184 if (!session) return;
186 sipe_core_chat_leave(PURPLE_GC_TO_SIPE_CORE_PUBLIC, session);
189 int sipe_purple_chat_send(PurpleConnection *gc,
190 int id,
191 const char *what,
192 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags)
194 struct sipe_chat_session *session = sipe_purple_chat_find(gc, id);
195 if (!session) return -ENOTCONN;
196 sipe_core_chat_send(PURPLE_GC_TO_SIPE_CORE_PUBLIC, session, what);
197 return 1;
200 static void sipe_purple_chat_menu_unlock_cb(SIPE_UNUSED_PARAMETER PurpleChat *chat,
201 PurpleConversation *conv)
203 struct sipe_core_public *sipe_public = PURPLE_CONV_TO_SIPE_CORE_PUBLIC;
204 struct sipe_chat_session *chat_session = sipe_purple_chat_get_session(conv);
205 SIPE_DEBUG_INFO("sipe_purple_chat_menu_lock_cb: %p %p", conv, chat_session);
206 sipe_core_chat_modify_lock(sipe_public, chat_session, FALSE);
209 static void sipe_purple_chat_menu_lock_cb(SIPE_UNUSED_PARAMETER PurpleChat *chat,
210 PurpleConversation *conv)
212 struct sipe_core_public *sipe_public = PURPLE_CONV_TO_SIPE_CORE_PUBLIC;
213 struct sipe_chat_session *chat_session = sipe_purple_chat_get_session(conv);
214 SIPE_DEBUG_INFO("sipe_purple_chat_menu_lock_cb: %p %p", conv, chat_session);
215 sipe_core_chat_modify_lock(sipe_public, chat_session, TRUE);
218 GList *
219 sipe_purple_chat_menu(PurpleChat *chat)
221 PurpleConversation *conv = g_hash_table_lookup(chat->components,
222 SIPE_PURPLE_COMPONENT_KEY_CONVERSATION);
223 GList *menu = NULL;
225 if (conv) {
226 PurpleMenuAction *act = NULL;
228 SIPE_DEBUG_INFO("sipe_purple_chat_menu: %p", conv);
230 switch (sipe_core_chat_lock_status(PURPLE_CONV_TO_SIPE_CORE_PUBLIC,
231 sipe_purple_chat_get_session(conv))) {
232 case SIPE_CHAT_LOCK_STATUS_UNLOCKED:
233 act = purple_menu_action_new(_("Lock"),
234 PURPLE_CALLBACK(sipe_purple_chat_menu_lock_cb),
235 conv, NULL);
236 break;
237 case SIPE_CHAT_LOCK_STATUS_LOCKED:
238 act = purple_menu_action_new(_("Unlock"),
239 PURPLE_CALLBACK(sipe_purple_chat_menu_unlock_cb),
240 conv, NULL);
241 break;
242 default:
243 /* Not allowed */
244 break;
247 if (act)
248 menu = g_list_prepend(menu, act);
252 return menu;
255 void sipe_backend_chat_session_destroy(SIPE_UNUSED_PARAMETER struct sipe_backend_chat_session *session)
257 /* Nothing to do here */
260 void sipe_backend_chat_add(struct sipe_backend_chat_session *backend_session,
261 const gchar *uri,
262 gboolean is_new)
264 purple_conv_chat_add_user(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
265 uri, NULL, PURPLE_CBFLAGS_NONE, is_new);
268 void sipe_backend_chat_close(struct sipe_backend_chat_session *backend_session)
270 purple_conv_chat_clear_users(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session));
273 static int sipe_purple_chat_id(PurpleConnection *gc)
276 * A non-volatile ID counter.
277 * Should survive connection drop & reconnect.
279 static int chat_id = 0;
281 /* Find next free ID */
282 do {
283 if (++chat_id < 0) chat_id = 0;
284 } while (purple_find_chat(gc, chat_id) != NULL)
286 return chat_id;
289 struct sipe_backend_chat_session *sipe_backend_chat_create(struct sipe_core_public *sipe_public,
290 struct sipe_chat_session *session,
291 const gchar *title,
292 const gchar *nick)
294 struct sipe_backend_private *purple_private = sipe_public->backend_private;
295 PurpleConversation *conv = serv_got_joined_chat(purple_private->gc,
296 sipe_purple_chat_id(purple_private->gc),
297 title);
298 purple_conversation_set_data(conv,
299 SIPE_PURPLE_KEY_CHAT_SESSION,
300 session);
301 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(conv), nick);
302 return((struct sipe_backend_chat_session *) conv);
305 gboolean sipe_backend_chat_find(struct sipe_backend_chat_session *backend_session,
306 const gchar *uri)
308 return purple_conv_chat_find_user(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
309 uri);
312 gboolean sipe_backend_chat_is_operator(struct sipe_backend_chat_session *backend_session,
313 const gchar *uri)
315 return (purple_conv_chat_user_get_flags(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
316 uri) & PURPLE_CBFLAGS_OP)
317 == PURPLE_CBFLAGS_OP;
320 void sipe_backend_chat_message(struct sipe_core_public *sipe_public,
321 struct sipe_backend_chat_session *backend_session,
322 const gchar *from,
323 const gchar *html)
325 struct sipe_backend_private *purple_private = sipe_public->backend_private;
326 serv_got_chat_in(purple_private->gc,
327 purple_conv_chat_get_id(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session)),
328 from,
329 PURPLE_MESSAGE_RECV,
330 html,
331 time(NULL));
334 void sipe_backend_chat_operator(struct sipe_backend_chat_session *backend_session,
335 const gchar *uri)
337 purple_conv_chat_user_set_flags(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
338 uri,
339 PURPLE_CBFLAGS_NONE | PURPLE_CBFLAGS_OP);
342 void sipe_backend_chat_rejoin(struct sipe_core_public *sipe_public,
343 struct sipe_backend_chat_session *backend_session,
344 const gchar *nick,
345 const gchar *title)
347 struct sipe_backend_private *purple_private = sipe_public->backend_private;
348 PurpleConvChat *chat = BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session);
349 PurpleConversation *new;
352 * As the chat is marked as "left", serv_got_joined_chat() will
353 * do a "rejoin cleanup" and return the same conversation.
355 new = serv_got_joined_chat(purple_private->gc,
356 purple_conv_chat_get_id(chat),
357 title);
358 SIPE_DEBUG_INFO("sipe_backend_chat_rejoin: old %p (%p) == new %p (%p)",
359 backend_session, chat,
360 new, PURPLE_CONV_CHAT(new));
361 purple_conv_chat_set_nick(chat, nick);
365 * Connection re-established: tell core what chats need to be rejoined
367 void sipe_backend_chat_rejoin_all(struct sipe_core_public *sipe_public)
369 struct sipe_backend_private *purple_private = sipe_public->backend_private;
370 GList *entry = purple_private->rejoin_chats;
372 while (entry) {
373 sipe_core_chat_rejoin(sipe_public, entry->data);
374 entry = entry->next;
376 sipe_purple_chat_destroy_rejoin(purple_private);
379 void sipe_backend_chat_remove(struct sipe_backend_chat_session *backend_session,
380 const gchar *uri)
382 purple_conv_chat_remove_user(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
383 uri,
384 NULL /* reason */);
387 void sipe_backend_chat_show(struct sipe_backend_chat_session *backend_session)
389 /* Bring existing purple chat to the front */
390 /* @TODO: This seems to the trick, but is it the correct way? */
391 purple_conversation_update((PurpleConversation *) backend_session,
392 PURPLE_CONV_UPDATE_TOPIC);
395 void sipe_backend_chat_topic(struct sipe_backend_chat_session *backend_session,
396 const gchar *topic)
398 purple_conv_chat_set_topic(BACKEND_SESSION_TO_PURPLE_CONV_CHAT(backend_session),
399 NULL,
400 topic);
404 Local Variables:
405 mode: c
406 c-file-style: "bsd"
407 indent-tabs-mode: t
408 tab-width: 8
409 End: