Map XMPP dnd to PURPLE_STATUS_UNAVAILABLE.
[thrasher.git] / thrasher.c
blob96c6c285ed247e111f23be4951f807df8a6226ed
1 /*
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
19 #include <glib.h>
20 #include <signal.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <sys/socket.h>
25 #include "purple.h"
26 #include "thrasher.h"
27 #include "thblist.h"
28 #include "thconnection.h"
29 #include "thperl.h"
30 #include "thft.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,
46 NULL,
47 NULL,
49 /* padding */
50 NULL,
51 NULL,
52 NULL
54 static PurpleCoreUiOps thrasher_core_uiops =
56 NULL,
57 NULL,
58 NULL,
59 NULL,
61 /* padding */
62 NULL,
63 NULL,
64 NULL,
65 NULL
69 static PurpleConnectionUiOps thrasher_conn_uiops =
71 // connect progress
72 NULL,
73 // connected,
74 thrasher_connection,
75 // disconnected
76 NULL,
77 // connection-specific notices, effectively unused
78 NULL,
79 // report disconnect
80 NULL,
81 // computer network connected
82 NULL,
83 // computer network disconnected
84 NULL,
85 // report disconnect reason
86 NULL,
87 // reserved 1
88 NULL,
89 // reserved 2
90 NULL,
91 // reserved 3
92 NULL
95 void thrasher_init()
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,
113 g_direct_equal);
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,
116 g_free, NULL);
118 purple_set_blist(purple_blist_new());
120 purple_signal_connect(purple_connections_get_handle(),
121 "signing-off",
122 thrasher_connection_get_handle(),
123 PURPLE_CALLBACK(thrasher_signing_off_cb),
124 NULL);
127 void thrasher_whoami (PurpleAccount * pa)
129 if (pa)
130 purple_debug_info("thrasher",
131 "You are user [%s]\n",
132 pa->username);
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);
148 void
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);
158 } else {
159 purple_debug_info("thrasher",
160 "Declined to log out unconnected account\n");
163 if (pa->ui_data) {
164 Thrasher_PurpleAccount_UI_Data* ui_data = pa->ui_data;
165 pa->ui_data = NULL;
166 g_hash_table_destroy(ui_data->pending_send_file);
167 g_free(ui_data);
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) {
197 got_error = new_val;
201 static void
202 thrasher_prefs_init() {
203 purple_prefs_load();
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);
213 PurpleAccount *
214 thrasher_login (char *service, char *username, char *password, char *jid,
215 GHashTable *other_args)
217 got_error = 0;
218 current_login_jid = jid;
219 gchar *user_dir;
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);
241 g_free(user_dir);
243 PurpleAccount *account;
244 PurpleSavedStatus *status;
246 /* Need to research these... */
247 purple_blist_load();
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,
260 g_str_equal);
261 account->ui_data = ui_data;
263 if (other_args) {
264 GHashTableIter iter;
265 gpointer key, value;
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))
274 char_key += 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)) {
281 char_key += 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",
286 char_key, value);
287 purple_account_set_bool(account, char_key, value);
288 } else {
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,
293 char_value);
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;
304 if (got_error) {
305 purple_debug_info("thrasher",
306 "got an error during initial login\n");
307 return NULL;
310 #ifdef TH_DEBUG
311 purple_debug_info("thrasher",
312 "Using libpurple v%s\n",
313 purple_core_get_version());
314 purple_debug_info("thrasher",
315 "Supported protocols\n");
316 GList *iter; int i;
317 iter = purple_plugins_get_protocols();
318 for (i = 0; iter; iter = iter->next) {
319 PurplePlugin *plugin = iter->data;
320 PurplePluginInfo *info = plugin->info;
321 if (info && info->name) {
322 purple_debug_info("thrasher",
323 "\t%d: %s\n",
324 i++,
325 info->name);
328 #endif
331 /* Not sure how I got in this state, but write a catch nonetheless */
332 if (!purple_plugins_get_protocols())
333 error(EXIT_FAILURE, 0, "libpurple reports NO supported protocols, this is a BAD thing");
335 return account;
339 * How to send message to remote user
341 * pa = PurpleAccount ptr
342 * name = recipient name char*
343 * message = message char*
345 * If the conversation data can be created this returns TRUE, else it returns FALSE
347 * Get the conversation if it already exists
348 * (Per the code flow, we may be able to set type to NULL and let it figure it out.)
351 gboolean
352 thrasher_send_msg (PurpleAccount *pa, const char *name, const char *message)
354 PurpleConversation *conv = thrasher_find_conversation_with_account(pa,
355 name);
356 PurpleConvIm *im;
358 /* Get the IM specific data */
359 im = purple_conversation_get_im_data(conv);
361 if (im) {
362 purple_conv_im_send(im, message);
363 return TRUE;
366 purple_debug_info("thrasher",
367 "Failed in purple_conv_im_send\n");
369 return FALSE;
372 void thrasher_connection (PurpleConnection *conn)
374 PurpleAccount *account = purple_connection_get_account(conn);
376 g_hash_table_insert(account_to_connection, account, conn);
378 thrasher_wrapper_connection(thrasher_account_get_jid(account));
381 PurpleConnection* thrasher_connection_for_account(PurpleAccount
382 *account) {
383 g_return_val_if_fail(account, NULL);
385 return g_hash_table_lookup(account_to_connection, account);
389 * thrasher_account_get_jid
391 gchar*
392 thrasher_account_get_jid(PurpleAccount *account)
394 g_return_val_if_fail(account, NULL);
396 Thrasher_PurpleAccount_UI_Data* ui_data = account->ui_data;
397 return ui_data->jid;
401 * thrasher_account_get_pending_send_file
403 GHashTable*
404 thrasher_account_get_pending_send_file(PurpleAccount *account) {
405 g_return_val_if_fail(account, NULL);
407 Thrasher_PurpleAccount_UI_Data* ui_data = account->ui_data;
408 g_return_val_if_fail(ui_data, NULL);
409 return ui_data->pending_send_file;
412 gboolean
413 thrasher_account_buddy_is_authorized(PurpleAccount *account,
414 PurpleBuddy *buddy) {
415 /* FIXME: There's no cross-prpl call for this? Get tricky. */
416 g_return_val_if_fail(account, 1);
417 g_return_val_if_fail(buddy, 1);
419 PurplePlugin *prpl
420 = purple_find_prpl(purple_account_get_protocol_id(buddy->account));;
421 g_return_val_if_fail(prpl, 1);
423 PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
424 g_return_val_if_fail(prpl_info, 1);
426 if (prpl_info->list_emblem) {
427 const char *emblem_name = prpl_info->list_emblem(buddy);
428 if (emblem_name && 0 == strcmp(emblem_name, "not-authorized")) {
429 return 0;
433 return 1;