2 * Thrasher Bird - XMPP transport via libpurple
3 * Copyright (C) 2008 Barracuda Networks, Inc.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with Thrasher Bird; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
23 #include <sys/socket.h>
28 #include "thconnection.h"
31 #include "threquest.h"
32 #include "thconversations.h"
34 GHashTable
*account_to_connection
= NULL
;
35 GHashTable
*jid_to_account
= NULL
;
37 void debug_print(PurpleDebugLevel level
, const char *category
, const char *arg_s
);
38 gboolean
is_enabled(PurpleDebugLevel level
, const char *category
);
40 static PurpleEventLoopUiOps glib_eventloops
=
42 thrasher_wrapper_set_timeout_add
,
43 thrasher_wrapper_call_source_remove
,
44 thrasher_wrapper_set_input_add
,
45 thrasher_wrapper_call_source_remove
,
54 static PurpleCoreUiOps thrasher_core_uiops
=
69 static PurpleConnectionUiOps thrasher_conn_uiops
=
77 // connection-specific notices, effectively unused
81 // computer network connected
83 // computer network disconnected
85 // report disconnect reason
97 purple_core_set_ui_ops(&thrasher_core_uiops
);
98 purple_eventloop_set_ui_ops(&glib_eventloops
);
99 purple_connections_set_ui_ops(&thrasher_conn_uiops
);
101 if (!purple_core_init(UI_ID
))
102 error(EXIT_FAILURE
, 0, "libpurple init failed");
104 thrasher_blist_init();
105 thrasher_connection_init();
106 thrasher_xfer_init();
107 thrasher_perl_init();
108 thrasher_request_init();
109 thrasher_conversations_init();
111 // This hash table has no ownership on the pointers stored in it
112 account_to_connection
= g_hash_table_new(g_direct_hash
,
114 // Automatically cleans up keys, does not own stored pointers
115 jid_to_account
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
118 purple_set_blist(purple_blist_new());
120 purple_signal_connect(purple_connections_get_handle(),
122 thrasher_connection_get_handle(),
123 PURPLE_CALLBACK(thrasher_signing_off_cb
),
127 void thrasher_whoami (PurpleAccount
* pa
)
130 purple_debug_info("thrasher",
131 "You are user [%s]\n",
135 void thrasher_signing_off_cb(PurpleConnection
*gc
) {
136 g_return_if_fail(gc
);
138 PurpleAccount
* pa
= purple_connection_get_account(gc
);
139 g_return_if_fail(pa
);
141 if (g_hash_table_lookup(account_to_connection
, pa
)) {
142 char *jid
= thrasher_account_get_jid(pa
);
143 g_hash_table_remove(jid_to_account
, jid
);
144 g_hash_table_remove(account_to_connection
, pa
);
149 thrasher_logout (PurpleAccount
*pa
)
151 g_return_if_fail(pa
);
153 purple_account_set_enabled(pa
, UI_ID
, FALSE
);
155 // Only disconnect if we haven't already
156 if (g_hash_table_lookup(account_to_connection
, pa
)) {
157 purple_account_disconnect(pa
);
159 purple_debug_info("thrasher",
160 "Declined to log out unconnected account\n");
164 Thrasher_PurpleAccount_UI_Data
* ui_data
= pa
->ui_data
;
166 g_hash_table_destroy(ui_data
->pending_send_file
);
171 PurpleAccount
* thrasher_get_account_by_jid(const char* jid
) {
172 g_return_val_if_fail(jid
, NULL
);
173 return g_hash_table_lookup(jid_to_account
, jid
);
176 PurpleConnection
* thrasher_get_connection_by_account
177 (const PurpleAccount
* account
) {
178 g_return_val_if_fail(account
, NULL
);
179 return g_hash_table_lookup(account_to_connection
, account
);
182 // An error can occur during the login, when the protocol decides
183 // there's going to be a connection error before it even returns the
184 // connection to us. In that case, our user data won't exist on the
185 // connection, but the connection error still needs to propagate back
186 // to the user. In that case, we use this value. This rather ties
187 // us to a single thread.
189 static char *current_login_jid
= NULL
;
190 static uint got_error
= 0;
192 char * get_current_login_jid () {
193 return current_login_jid
;
196 void set_got_error (uint new_val
) {
202 thrasher_prefs_init() {
205 /* Disable all logging to disk because it might leave files under
206 * USER_ROOT_DIR open.
208 purple_prefs_set_bool("/purple/logging/log_ims", FALSE
);
209 purple_prefs_set_bool("/purple/logging/log_chats", FALSE
);
210 purple_prefs_set_bool("/purple/logging/log_system", FALSE
);
214 thrasher_login (char *service
, char *username
, char *password
, char *jid
,
215 GHashTable
*other_args
)
218 current_login_jid
= jid
;
221 if (purple_get_core() == 0)
222 error(EXIT_FAILURE
, 0, "Purple core is uninitialized");
224 purple_debug_info("thrasher",
225 "Thrasher login initiated\n");
227 /* Test service for validity */
229 /* Test user for validity */
230 /* We need to verify there is NO path redirection in the user name! */
231 /* We also need to verify username is NOT longer than MAX_NAME_LEN */
233 /* Test password for validity */
236 /* Validate the root_dir and build us a filename */
237 user_dir
= g_build_filename(USER_ROOT_DIR
, service
, username
, NULL
);
239 purple_util_set_user_dir(user_dir
);
243 PurpleAccount
*account
;
244 PurpleSavedStatus
*status
;
246 /* Need to research these... */
248 thrasher_prefs_init();
249 purple_pounces_load(); /* Don't believe we need this */
251 /* Setup the account and throw it back */
252 account
= purple_account_new(username
, service
);
253 g_hash_table_insert(jid_to_account
, g_strdup(jid
), account
);
254 purple_account_set_password(account
, password
);
256 Thrasher_PurpleAccount_UI_Data
* ui_data
257 = g_new(Thrasher_PurpleAccount_UI_Data
, 1);
258 ui_data
->jid
= g_strdup(jid
); /* Store the JID with the account */
259 ui_data
->pending_send_file
= g_hash_table_new(g_str_hash
,
261 account
->ui_data
= ui_data
;
266 char *char_key
, *char_value
;
268 g_hash_table_iter_init(&iter
, other_args
);
269 while (g_hash_table_iter_next(&iter
, &key
, &value
)) {
270 char_value
= (char *)value
;
271 char_key
= (char *)key
;
272 if (!strncmp(key
, "int_", 4))
275 int int_value
= atoi(char_value
);
276 purple_debug_info("thrasher",
277 "Setting account #: %s -> %d\n",
278 char_key
, int_value
);
279 purple_account_set_int(account
, char_key
, atoi(char_value
));
280 } else if (!strncmp(key
, "bool_", 5)) {
282 int value
= (*char_value
== '0' ||
283 *char_value
== 0) ? FALSE
: TRUE
;
284 purple_debug_info("thrasher",
285 "Setting account bool: %s -> %d\n",
287 purple_account_set_bool(account
, char_key
, value
);
289 purple_debug_info("thrasher",
290 "Setting account string: %s -> %s\n",
291 char_key
, char_value
);
292 purple_account_set_string(account
, char_key
,
298 purple_account_set_enabled(account
, UI_ID
, TRUE
);
299 status
= purple_savedstatus_new(NULL
, PURPLE_STATUS_AVAILABLE
);
300 purple_savedstatus_activate(status
);
302 current_login_jid
= NULL
;
305 purple_debug_info("thrasher",
306 "got an error during initial login\n");
311 purple_debug_info("thrasher",
312 "Supported protocols\n");
314 iter
= purple_plugins_get_protocols();
315 for (i
= 0; iter
; iter
= iter
->next
) {
316 PurplePlugin
*plugin
= iter
->data
;
317 PurplePluginInfo
*info
= plugin
->info
;
318 if (info
&& info
->name
) {
319 purple_debug_info("thrasher",
328 /* Not sure how I got in this state, but write a catch nonetheless */
329 if (!purple_plugins_get_protocols())
330 error(EXIT_FAILURE
, 0, "libpurple reports NO supported protocols, this is a BAD thing");
336 * How to send message to remote user
338 * pa = PurpleAccount ptr
339 * name = recipient name char*
340 * message = message char*
342 * If the conversation data can be created this returns TRUE, else it returns FALSE
344 * Get the conversation if it already exists
345 * (Per the code flow, we may be able to set type to NULL and let it figure it out.)
349 thrasher_send_msg (PurpleAccount
*pa
, const char *name
, const char *message
)
351 PurpleConversation
*conv
= thrasher_find_conversation_with_account(pa
,
355 /* Get the IM specific data */
356 im
= purple_conversation_get_im_data(conv
);
359 purple_conv_im_send(im
, message
);
363 purple_debug_info("thrasher",
364 "Failed in purple_conv_im_send\n");
369 void thrasher_connection (PurpleConnection
*conn
)
371 PurpleAccount
*account
= purple_connection_get_account(conn
);
373 g_hash_table_insert(account_to_connection
, account
, conn
);
375 thrasher_wrapper_connection(thrasher_account_get_jid(account
));
378 PurpleConnection
* thrasher_connection_for_account(PurpleAccount
380 g_return_val_if_fail(account
, NULL
);
382 return g_hash_table_lookup(account_to_connection
, account
);
386 * thrasher_account_get_jid
389 thrasher_account_get_jid(PurpleAccount
*account
)
391 g_return_val_if_fail(account
, NULL
);
393 Thrasher_PurpleAccount_UI_Data
* ui_data
= account
->ui_data
;
398 * thrasher_account_get_pending_send_file
401 thrasher_account_get_pending_send_file(PurpleAccount
*account
) {
402 g_return_val_if_fail(account
, NULL
);
404 Thrasher_PurpleAccount_UI_Data
* ui_data
= account
->ui_data
;
405 g_return_val_if_fail(ui_data
, NULL
);
406 return ui_data
->pending_send_file
;
410 thrasher_account_buddy_is_authorized(PurpleAccount
*account
,
411 PurpleBuddy
*buddy
) {
412 /* FIXME: There's no cross-prpl call for this? Get tricky. */
413 g_return_val_if_fail(account
, 1);
414 g_return_val_if_fail(buddy
, 1);
417 = purple_find_prpl(purple_account_get_protocol_id(buddy
->account
));;
418 g_return_val_if_fail(prpl
, 1);
420 PurplePluginProtocolInfo
*prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(prpl
);
421 g_return_val_if_fail(prpl_info
, 1);
423 if (prpl_info
->list_emblem
) {
424 const char *emblem_name
= prpl_info
->list_emblem(buddy
);
425 if (emblem_name
&& 0 == strcmp(emblem_name
, "not-authorized")) {