comparison pidgin-audacious.c @ 0:8d4d17a528ef

initial import
author Yoshiki Yazawa <yaz@cc.rim.or.jp>
date Thu, 28 Jun 2007 19:48:21 +0900
parents
children 46071692f191
comparison
equal deleted inserted replaced
-1:000000000000 0:8d4d17a528ef
1 /*
2 * Pidgin-Audacious plugin.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17 * 02111-1307, USA.
18 */
19 #define PURPLE_PLUGINS 1
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <glib.h>
25
26 #include "gtkplugin.h"
27 #include "util.h"
28 #include "debug.h"
29 #include "connection.h"
30 #include "version.h"
31 #include <audacious/audctrl.h>
32 #include <audacious/dbus.h>
33
34 extern guchar *botch_utf(const void *msg, size_t len, size_t *newlen) __attribute__ ((weak));
35
36 #define PIDGINAUD_PLUGIN_ID "pidgin_audacious"
37
38 #define OPT_PIDGINAUD "/plugins/pidgin_audacious"
39 #define OPT_PROCESS_STATUS OPT_PIDGINAUD "/process_status"
40 #define OPT_PROCESS_USERINFO OPT_PIDGINAUD "/process_userinfo"
41 #define OPT_PROCESS_ALIAS OPT_PIDGINAUD "/process_alias"
42
43 #define SONG_TOKEN "%song"
44 #define NO_SONG_MESSAGE "No song being played."
45
46 #define BUDDY_ALIAS_MAXLEN 387
47
48 #define aud_debug(fmt, ...) purple_debug(PURPLE_DEBUG_INFO, "Pidgin-Audacious", \
49 fmt, ## __VA_ARGS__);
50 #define aud_error(fmt, ...) purple_debug(PURPLE_DEBUG_ERROR, "Pidgin-Audacious", \
51 fmt, ## __VA_ARGS__);
52
53 static gint timeout_tag = 0;
54
55 GHashTable *stored_status;
56 GHashTable *stored_userinfo;
57 GHashTable *stored_alias;
58
59 GHashTable *pushed_status;
60 GHashTable *pushed_userinfo;
61 GHashTable *pushed_alias;
62
63 DBusGProxy *session = NULL;
64
65 static void aud_process(gchar *aud_info);
66
67 static DBusGProxy *get_dbus_proxy(void)
68 {
69 DBusGConnection *connection = NULL;
70 DBusGProxy *session = NULL;
71 GError *error = NULL;
72 connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
73 g_clear_error(&error);
74
75 session = dbus_g_proxy_new_for_name(connection, AUDACIOUS_DBUS_SERVICE,
76 AUDACIOUS_DBUS_PATH,
77 AUDACIOUS_DBUS_INTERFACE);
78
79 g_clear_error(&error);
80 return session;
81 #if 0
82 if (audacious_remote_is_running(session)) {
83 return session;
84 }
85 else {
86 return NULL;
87 }
88 #endif
89 }
90
91 static gboolean
92 watchdog_func(void)
93 {
94 gint playpos = 0;
95 gchar *song = NULL, *tmp = NULL;
96 // DBusGProxy *session = get_dbus_proxy();
97
98 gboolean rv = TRUE;
99 size_t dummy;
100
101 aud_debug("session = %p\n", session);
102
103 aud_debug("is_playing = %d\n", audacious_remote_is_playing(session));
104
105 if(!audacious_remote_is_playing(session)) { /* audacious isn't playing */
106 aud_process(NULL);
107 return rv;
108 }
109
110 playpos = audacious_remote_get_playlist_pos(session);
111 tmp = audacious_remote_get_playlist_title(session, playpos);
112 if(tmp) {
113 if(botch_utf) // function exists
114 song = (gchar *) botch_utf(tmp, strlen(tmp), &dummy);
115 else
116 song = g_strdup(tmp);
117 }
118 g_free(tmp);
119 tmp = NULL;
120
121 aud_process(song);
122 g_free(song);
123 song = NULL;
124 return rv;
125 }
126
127 static void
128 aud_process_status(PurpleConnection *gc, gchar *aud_info)
129 {
130 gchar *new;
131 const gchar *old, *proto;
132 PurpleAccount *account;
133 PurplePresence *presence;
134 PurplePlugin *prpl;
135 PurplePluginProtocolInfo *prpl_info;
136 PurpleStatus *status;
137
138 gpointer val; // for hash
139 gchar *key;
140
141 account = purple_connection_get_account(gc);
142 presence = purple_account_get_presence(account);
143
144 proto = purple_account_get_protocol_id(account);
145 prpl = purple_find_prpl(proto);
146 g_return_if_fail(prpl != NULL);
147
148 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
149 g_return_if_fail(prpl_info != NULL && prpl_info->set_status != NULL);
150
151 status = purple_presence_get_active_status(presence);
152 g_return_if_fail(status != NULL);
153
154 old = purple_status_get_attr_string(status, "message");
155 aud_debug("status current = %s\n", old);
156 if(old == NULL || strlen(old) == 0) { // auto away etc.
157 /* invalidate pushded status */
158 /* generate key for hash table */
159 key = g_strdup_printf("%s %s", account->username, account->protocol_id);
160 g_hash_table_replace(pushed_status, g_strdup(key), g_strdup(""));
161 return;
162 }
163
164 /* generate key for hash table */
165 key = g_strdup_printf("%s %s", account->username, account->protocol_id);
166
167 val = g_hash_table_lookup(pushed_status, key);
168
169 /* if current alias differs from pushed_alias or contains token, replace seed with this. */
170 if( (val && g_ascii_strcasecmp(old, val)) || strstr(old, SONG_TOKEN) ) {
171 g_hash_table_replace(stored_status, g_strdup(key), g_strdup(old));
172 }
173
174 /* construct new status message */
175 val = g_hash_table_lookup(stored_status, key);
176 g_return_if_fail(val != NULL);
177 aud_debug("status stored = %s\n", (gchar *)val);
178
179 if(aud_info){
180 new = purple_strreplace(val, SONG_TOKEN, aud_info);
181 }
182 else {
183 new = g_strdup(NO_SONG_MESSAGE);
184 }
185
186 g_return_if_fail(new != NULL);
187
188 /* set status message only if text has been changed */
189 val = g_hash_table_lookup(pushed_status, key);
190 aud_debug("status pushed = %s\n", (gchar *)val);
191
192 if (!val || g_ascii_strcasecmp(val, new) != 0) {
193 g_hash_table_replace(pushed_status, g_strdup(key), g_strdup(new));
194 purple_status_set_attr_string(status, "message", new);
195 prpl_info->set_status(account, status);
196 }
197 g_free(key);
198 g_free(new);
199 }
200
201
202 static void
203 aud_process_userinfo(PurpleConnection *gc, gchar *aud_info)
204 {
205 gchar *new;
206 const gchar *old, *proto;
207 PurpleAccount *account;
208 PurplePlugin *prpl;
209 PurplePluginProtocolInfo *prpl_info;
210
211 gpointer val; // for hash
212 gchar *key;
213
214 account = purple_connection_get_account(gc);
215
216 proto = purple_account_get_protocol_id(account);
217 prpl = purple_find_prpl(proto);
218 g_return_if_fail(prpl != NULL);
219
220 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
221 g_return_if_fail(prpl_info != NULL && prpl_info->set_info != NULL);
222
223 /* retrieve the old user info */
224 old = purple_account_get_user_info(account); /* it's always from account.xml! */
225 if(old == NULL || strlen(old) == 0)
226 return;
227
228 /* generate key for hash table*/
229 key = g_strdup_printf("%s %s", account->username, account->protocol_id);
230
231 val = g_hash_table_lookup(pushed_userinfo, key);
232
233 /* if current alias differs from pushed_alias or contains token, replace seed with this. */
234 if( (val && g_ascii_strcasecmp(old, val)) || strstr(old, SONG_TOKEN) ) {
235 g_hash_table_replace(stored_userinfo, g_strdup(key), g_strdup(old));
236 }
237
238 /* construct new status message */
239 val = g_hash_table_lookup(stored_userinfo, key);
240 g_return_if_fail(val != NULL);
241
242 aud_debug("userinfo stored = %s\n", (gchar *)val);
243
244 if(aud_info){
245 new = purple_strreplace(val, SONG_TOKEN, aud_info);
246 }
247 else {
248 new = g_strdup(NO_SONG_MESSAGE);
249 }
250
251 g_return_if_fail(new != NULL);
252
253 /* set user info only if text has been changed */
254 val = g_hash_table_lookup(pushed_userinfo, key);
255 aud_debug("userinfo pushed = %s\n", (gchar *)val);
256
257 if (!val || g_ascii_strcasecmp(val, new) != 0) {
258 g_hash_table_replace(pushed_userinfo, g_strdup(key), g_strdup(new));
259 prpl_info->set_info(gc, new);
260 }
261 g_free(key);
262 g_free(new);
263 }
264
265 static void
266 aud_process_alias(PurpleConnection *gc, gchar *aud_info)
267 {
268 gchar *new;
269 const gchar *old, *proto;
270 PurpleAccount *account;
271 PurplePlugin *prpl;
272 PurplePluginProtocolInfo *prpl_info;
273
274 gpointer val; // for hash
275 glong bytes;
276 gchar *key;
277
278 account = purple_connection_get_account(gc);
279
280 proto = purple_account_get_protocol_id(account);
281 prpl = purple_find_prpl(proto);
282 g_return_if_fail(prpl != NULL);
283
284 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
285 g_return_if_fail(prpl_info != NULL);
286
287 /* retrieve the old alias */
288 // old = purple_url_decode(purple_account_get_alias(account));
289 old = purple_account_get_alias(account);
290 if(old == NULL || strlen(old) == 0) {
291 aud_error("couldn't get old alias\n");
292 return;
293 }
294 aud_debug("old alias = %s\n", old);
295
296 /* generate key for hash table*/
297 key = g_strdup_printf("%s %s", account->username, account->protocol_id);
298 // aud_debug("alias key = %s\n", key);
299
300 val = g_hash_table_lookup(pushed_alias, key);
301
302 /* if current alias differs from pushed_alias or contains token, replace seed with this. */
303 if( (val && g_ascii_strcasecmp(old, val)) || strstr(old, SONG_TOKEN) ) {
304 g_hash_table_replace(stored_alias, g_strdup(key), g_strdup(old));
305 }
306
307 /* construct new status message */
308 val = g_hash_table_lookup(stored_alias, key);
309 g_return_if_fail(val != NULL);
310
311 bytes = strlen(val);
312 bytes -= strlen(SONG_TOKEN);
313 aud_debug("alias: bytes = %ld", bytes);
314
315 aud_debug("alias: stored = %s\n", (gchar *)val);
316
317 if(aud_info){
318 gchar *tmp = g_malloc0(BUDDY_ALIAS_MAXLEN);
319 glong utflen = g_utf8_strlen(aud_info, BUDDY_ALIAS_MAXLEN/3 - bytes - 1);
320 g_utf8_strncpy(tmp, aud_info, utflen);
321 // aud_debug("alias: utflen = %ld tmp = %s\n", utflen, tmp);
322 new = purple_strreplace(val, SONG_TOKEN, tmp);
323 // aud_debug("alias: new bytes = %ld new = %s\n", strlen(new), new);
324 g_free(tmp);
325 }
326 else {
327 new = purple_strreplace(val, SONG_TOKEN, NO_SONG_MESSAGE);
328 }
329
330 g_return_if_fail(new != NULL);
331
332 /* set user info only if text has been changed */
333 val = g_hash_table_lookup(pushed_alias, key);
334 aud_debug("alias pushed = %s\n", (gchar *)val);
335
336 if (!val || g_ascii_strcasecmp(val, new) != 0) {
337 //gint result;
338 gboolean ok = FALSE;
339 PurplePlugin *msn_plugin = NULL;
340 msn_plugin = purple_plugins_find_with_id("prpl-msn");
341 aud_debug("msn_plugin = %p\n", msn_plugin);
342
343 g_hash_table_replace(pushed_alias, g_strdup(key), g_strdup(new));
344 purple_plugin_ipc_call(msn_plugin, "msn_set_friendly_name", &ok, gc, new);
345 aud_debug("ipc %d\n", ok);
346 }
347 g_free(key);
348 g_free(new);
349 }
350
351 static void
352 aud_process(gchar *aud_info)
353 {
354 GList *l;
355 PurpleConnection *gc;
356
357 for (l = purple_connections_get_all(); l != NULL; l = l->next) {
358 gc = (PurpleConnection *) l->data;
359
360 /* make sure we're connected */
361 if (purple_connection_get_state(gc) != PURPLE_CONNECTED) {
362 continue;
363 }
364
365 if (purple_prefs_get_bool(OPT_PROCESS_USERINFO)) {
366 aud_process_userinfo(gc, aud_info);
367 }
368
369 if (purple_prefs_get_bool(OPT_PROCESS_STATUS)) {
370 aud_process_status(gc, aud_info);
371 }
372
373 if (purple_prefs_get_bool(OPT_PROCESS_ALIAS)) {
374 aud_process_alias(gc, aud_info);
375 }
376
377 }
378 }
379 static void
380 removekey(gpointer data)
381 {
382 g_free(data);
383 }
384
385 static void
386 removeval(gpointer data)
387 {
388 g_free(data);
389 }
390
391 static gboolean
392 restore_alias(PurpleConnection *gc, gpointer data)
393 {
394 PurpleAccount *account;
395 gpointer val = NULL;
396 gchar *key = NULL;
397
398 aud_debug("********** restore_alias called **********\n");
399 account = purple_connection_get_account(gc);
400
401 key = g_strdup_printf("%s %s", account->username, account->protocol_id);
402 val = g_hash_table_lookup(stored_alias, key);
403 g_return_val_if_fail(val != NULL, FALSE);
404
405 aud_debug("write back alias %s\n", val);
406 purple_account_set_alias(account, val); //oct16
407
408 g_free(key);
409
410 return TRUE;
411 }
412
413
414 static gboolean
415 load_plugin(PurplePlugin *plugin)
416 {
417 stored_status = g_hash_table_new_full(g_str_hash, g_str_equal, removekey, removeval);
418 stored_alias = g_hash_table_new_full(g_str_hash, g_str_equal, removekey, removeval);
419 stored_userinfo = g_hash_table_new_full(g_str_hash, g_str_equal, removekey, removeval);
420
421 pushed_status = g_hash_table_new_full(g_str_hash, g_str_equal, removekey, removeval);
422 pushed_alias = g_hash_table_new_full(g_str_hash, g_str_equal, removekey, removeval);
423 pushed_userinfo = g_hash_table_new_full(g_str_hash, g_str_equal, removekey, removeval);
424
425 timeout_tag = g_timeout_add(15*1000, (gpointer)watchdog_func, NULL);
426
427 /* connect to signing-off signal */
428 purple_signal_connect(purple_connections_get_handle(), "signing-off", plugin,
429 PURPLE_CALLBACK(restore_alias), NULL);
430
431
432 return TRUE;
433 }
434
435 static gboolean
436 unload_plugin(PurplePlugin *plugin)
437 {
438 aud_debug("pidgin-audacious unload called\n");
439
440 g_hash_table_destroy(stored_status);
441 g_hash_table_destroy(stored_alias);
442 g_hash_table_destroy(stored_userinfo);
443
444 g_hash_table_destroy(pushed_status);
445 g_hash_table_destroy(pushed_alias);
446 g_hash_table_destroy(pushed_userinfo);
447
448 return TRUE;
449 }
450
451 static PurplePluginPrefFrame *
452 get_plugin_pref_frame(PurplePlugin *plugin)
453 {
454 PurplePluginPref *pref;
455 PurplePluginPrefFrame *frame = purple_plugin_pref_frame_new();
456
457 /* create gtk elements for the plugin preferences */
458 pref = purple_plugin_pref_new_with_label("Pidgin-Audacious Configuration");
459 purple_plugin_pref_frame_add(frame, pref);
460
461 pref = purple_plugin_pref_new_with_name_and_label(OPT_PROCESS_STATUS,
462 "Expand " SONG_TOKEN " to song info in the status message");
463 purple_plugin_pref_frame_add(frame, pref);
464
465 pref = purple_plugin_pref_new_with_name_and_label(OPT_PROCESS_USERINFO,
466 "Expand " SONG_TOKEN " to song info in the user info");
467 purple_plugin_pref_frame_add(frame, pref);
468
469 pref = purple_plugin_pref_new_with_name_and_label(OPT_PROCESS_ALIAS,
470 "Expand " SONG_TOKEN " to song info in the alias");
471 purple_plugin_pref_frame_add(frame, pref);
472
473 return frame;
474 }
475
476 static PurplePluginUiInfo pref_info =
477 {
478 get_plugin_pref_frame
479 };
480
481 static PurplePluginInfo info =
482 {
483 PURPLE_PLUGIN_MAGIC,
484 PURPLE_MAJOR_VERSION,
485 PURPLE_MINOR_VERSION,
486 PURPLE_PLUGIN_STANDARD, /**< type */
487 NULL, /**< ui_req */
488 0, /**< flags */
489 NULL, /**< deps */
490 PURPLE_PRIORITY_DEFAULT, /**< priority */
491 PIDGINAUD_PLUGIN_ID, /**< id */
492 "Pidgin-Audacious", /**< name */
493 "2.0.0d4", /**< version */
494 "Automatically updates your Pidgin status info with the currently "
495 "playing music in Audacious.", /** summary */
496 "Automatically updates your Pidgin status info with the currently "
497 "playing music in Audacious.", /** desc */
498 "Yoshiki Yazawa (yaz@honeyplanet.jp)", /**< author */
499 "http://www.honeyplanet.jp", /**< homepage */
500 load_plugin, /**< load */
501 unload_plugin, /**< unload */
502 NULL, /**< destroy */
503 NULL, /**< ui_info */
504 NULL, /**< extra_info */
505 &pref_info, /**< pref info */
506 NULL
507 };
508
509 static void
510 init_plugin(PurplePlugin *plugin)
511 {
512 g_type_init();
513
514 /* add plugin preferences */
515 purple_prefs_add_none(OPT_PIDGINAUD);
516 purple_prefs_add_bool(OPT_PROCESS_STATUS, TRUE);
517 purple_prefs_add_bool(OPT_PROCESS_USERINFO, TRUE);
518 purple_prefs_add_bool(OPT_PROCESS_ALIAS, TRUE);
519
520 session = get_dbus_proxy();
521 }
522
523 PURPLE_INIT_PLUGIN(pidgin_audacious, init_plugin, info)