comparison main.c @ 254:c2620a99622b

- divided the source file into several parts.
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Sat, 22 Nov 2008 18:01:18 +0900
parents pidgin-twitter.c@a37ae6c8fa66
children 9fb8f597adf3
comparison
equal deleted inserted replaced
253:a37ae6c8fa66 254:c2620a99622b
1 /*
2 * Pidgin-Twitter 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 "pidgin-twitter.h"
22 #include "main.h"
23
24 /***********/
25 /* globals */
26 /***********/
27 GRegex *regp[NUM_REGPS];
28 static gboolean suppress_oops = FALSE;
29 GHashTable *icon_hash[NUM_SERVICES];
30 static GHashTable *conv_hash = NULL;
31 static GList *wassr_parrot_list = NULL;
32 static GList *identica_parrot_list = NULL;
33 #ifdef _WIN32
34 static gboolean blink_state = FALSE;
35 static gboolean blink_modified = FALSE;
36 #endif
37
38 source_t source;
39
40 #ifndef _WIN32
41 extern gchar *sanitize_utf(const gchar *msg, gsize len, gsize *newlen) __attribute__ ((weak));
42 #endif
43
44 /* prototypes */
45 static gboolean is_twitter_conv(PurpleConversation *conv);
46 static gboolean is_wassr_account(PurpleAccount *account, const char *name);
47 static gboolean is_wassr_conv(PurpleConversation *conv);
48 static gboolean is_identica_account(PurpleAccount *account, const char *name);
49 static gboolean is_identica_conv(PurpleConversation *conv);
50 static gboolean is_jisko_account(PurpleAccount *account, const char *name);
51 static gboolean is_jisko_conv(PurpleConversation *conv);
52 static void cleanup_hash_entry_func(gpointer key, gpointer value, gpointer user_data);
53
54 static void init_plugin(PurplePlugin *plugin);
55 static gboolean load_plugin(PurplePlugin *plugin);
56 static gboolean unload_plugin(PurplePlugin *plugin);
57 static gboolean sending_im_cb(PurpleAccount *account, char *recipient, char **buffer, void *data);
58 static gboolean eval(const GMatchInfo *match_info, GString *result, gpointer user_data);
59 static void translate(gchar **str, gint which, gint service);
60 static void playsound(gchar **str, gint which);
61 static gboolean writing_im_cb(PurpleAccount *account, char *sender, char **buffer, PurpleConversation *conv, int flags, void *data);
62 static void insert_text_cb(GtkTextBuffer *textbuffer, GtkTextIter *position, gchar *new_text, gint new_text_length, gpointer user_data);
63 static void delete_text_cb(GtkTextBuffer *textbuffer, GtkTextIter *start_pos, GtkTextIter *end_pos, gpointer user_data);
64 static void detach_from_conv(PurpleConversation *conv, gpointer null);
65 static void delete_requested_icon_marks(PidginConversation *gtkconv, GHashTable *table);
66 static void attach_to_conv(PurpleConversation *conv, gpointer null);
67
68
69 static void conv_created_cb(PurpleConversation *conv, gpointer null);
70 static void deleting_conv_cb(PurpleConversation *conv);
71 static gboolean receiving_im_cb(PurpleAccount *account, char **sender, char **buffer, PurpleConversation *conv, PurpleMessageFlags *flags, void *data);
72
73 static void remove_marks_func(gpointer key, gpointer value, gpointer user_data);
74 static void cancel_fetch_func(gpointer key, gpointer value, gpointer user_data);
75
76 static gboolean displaying_im_cb(PurpleAccount *account, const char *who, char **message, PurpleConversation *conv, PurpleMessageFlags flags, void *data);
77 static void displayed_im_cb(PurpleAccount *account, const char *who, char *message, PurpleConversation *conv, PurpleMessageFlags flags);
78
79
80
81 /*************/
82 /* functions */
83 /*************/
84
85
86 /***********************/
87 /* intrinsic functions */
88 /***********************/
89 static gboolean
90 sending_im_cb(PurpleAccount *account, char *recipient, char **buffer,
91 void *data)
92 {
93 int utflen, bytes;
94 gboolean twitter_ac = FALSE, wassr_ac = FALSE, identica_ac = FALSE;
95 twitter_debug("called\n");
96
97 twitter_ac = is_twitter_account(account, recipient);
98 wassr_ac = is_wassr_account(account, recipient);
99 identica_ac = is_identica_account(account, recipient);
100
101 /* strip all markups */
102 if(twitter_ac || wassr_ac || identica_ac) {
103 gchar *tmp, *plain;
104 gsize dummy;
105
106 tmp = strip_html_markup(*buffer);
107
108 #ifndef _WIN32
109 if(sanitize_utf) {
110 plain = sanitize_utf(tmp, -1, &dummy);
111 g_free(tmp);
112 }
113 else
114 #endif
115 plain = tmp;
116
117 if(wassr_ac) {
118 /* store sending message to address parrot problem */
119 wassr_parrot_list =
120 g_list_prepend(wassr_parrot_list, g_strdup(plain));
121 twitter_debug("wassr parrot pushed:%s\n", plain);
122 }
123 else if(identica_ac) {
124 /* store sending message to address parrot problem */
125 identica_parrot_list =
126 g_list_prepend(identica_parrot_list, g_strdup(plain));
127 twitter_debug("identica parrot pushed:%s\n", plain);
128 }
129
130 g_free(*buffer);
131 *buffer = g_markup_escape_text(plain, -1);
132 g_free(plain);
133 }
134
135 /* return here if the message is not to twitter */
136 if(!twitter_ac)
137 return FALSE;
138
139 /* escape pseudo command */
140 if(twitter_ac &&
141 purple_prefs_get_bool(OPT_ESCAPE_PSEUDO)) {
142 escape(buffer);
143 }
144
145 /* update status with Twitter API instead of IM protocol */
146 if (purple_prefs_get_bool(OPT_API_BASE_POST)) {
147 if (buffer && *buffer) {
148 post_status_with_api(account, buffer);
149 (*buffer)[0] = '\0';
150 }
151 return FALSE;
152 }
153
154 /* try to suppress oops message */
155 utflen = g_utf8_strlen(*buffer, -1);
156 bytes = strlen(*buffer);
157 twitter_debug("utflen = %d bytes = %d\n", utflen, bytes);
158 if(bytes > 140 && utflen <= 140)
159 suppress_oops = TRUE;
160
161 return FALSE;
162 }
163
164 static gboolean
165 eval(const GMatchInfo *match_info, GString *result, gpointer user_data)
166 {
167 eval_data *data = (eval_data *)user_data;
168 gint which = data->which;
169 gint service = data->service;
170 gchar sub[SUBST_BUF_SIZE];
171
172 twitter_debug("which = %d service = %d\n", which, service);
173
174 if(which == RECIPIENT) {
175 gchar *match1 = g_match_info_fetch(match_info, 1); /* preceding \s */
176 gchar *match2 = g_match_info_fetch(match_info, 2); /* recipient */
177 const gchar *format = NULL;
178
179 switch(service) {
180 case twitter_service:
181 format = RECIPIENT_FORMAT_TWITTER;
182 break;
183 case wassr_service:
184 format = RECIPIENT_FORMAT_WASSR;
185 break;
186 case identica_service:
187 format = RECIPIENT_FORMAT_IDENTICA;
188 break;
189 case jisko_service:
190 format = RECIPIENT_FORMAT_JISKO;
191 break;
192 default:
193 twitter_debug("unknown service\n");
194 break;
195 }
196 g_snprintf(sub, SUBST_BUF_SIZE, format, match1 ? match1: "", match2, match2);
197 g_free(match1);
198 g_free(match2);
199 }
200 else if(which == SENDER) {
201 gchar *match1 = g_match_info_fetch(match_info, 1); /*preceding CR|LF*/
202 gchar *match2 = g_match_info_fetch(match_info, 2); /* sender */
203 const gchar *format = NULL;
204
205 switch(service) {
206 case twitter_service:
207 format = SENDER_FORMAT_TWITTER;
208 break;
209 case wassr_service:
210 format = SENDER_FORMAT_WASSR;
211 break;
212 case identica_service:
213 format = SENDER_FORMAT_IDENTICA;
214 break;
215 case jisko_service:
216 format = SENDER_FORMAT_JISKO;
217 break;
218 default:
219 twitter_debug("unknown service\n");
220 break;
221 }
222
223 g_snprintf(sub, SUBST_BUF_SIZE, format, match1 ? match1: "", match2, match2);
224
225 g_free(match1);
226 g_free(match2);
227 }
228 else if(which == CHANNEL_WASSR && service == wassr_service) {
229 gchar *match1 = g_match_info_fetch(match_info, 1); /*before channel*/
230 gchar *match2 = g_match_info_fetch(match_info, 2); /* channel */
231 const gchar *format = CHANNEL_FORMAT_WASSR;
232
233 g_snprintf(sub, SUBST_BUF_SIZE, format, match1 ? match1: "", match2, match2);
234
235 g_free(match1);
236 g_free(match2);
237 }
238 else if(which == TAG_IDENTICA && service == identica_service) {
239 gchar *match = g_match_info_fetch(match_info, 1);
240 gchar *link = g_ascii_strdown(match, -1);
241 purple_str_strip_char(link, '-');
242 purple_str_strip_char(link, '_');
243 const gchar *format = TAG_FORMAT_IDENTICA;
244 g_snprintf(sub, SUBST_BUF_SIZE, format, link, match);
245 g_free(match);
246 g_free(link);
247 }
248 else if(which == EXCESS_LF) {
249 g_snprintf(sub, SUBST_BUF_SIZE, "%s", "\n");
250 }
251
252 g_string_append(result, sub);
253 twitter_debug("sub = %s\n", sub);
254
255 return FALSE;
256 }
257
258 static void
259 translate(gchar **str, gint regp_id, gint service)
260 {
261 gchar *newstr;
262 eval_data *data = g_new0(eval_data, 1);
263
264 data->which = regp_id;
265 data->service = service;
266
267 newstr = g_regex_replace_eval(regp[regp_id], /* compiled regex */
268 *str, /* subject string */
269 -1, /* length of the subject string */
270 0, /* start position */
271 0, /* match options */
272 eval, /* function to be called for each match */
273 data, /* user data */
274 NULL); /* error handler */
275
276 g_free(data); data = NULL;
277
278 twitter_debug("which = %d *str = %s newstr = %s\n", regp_id, *str, newstr);
279
280 g_free(*str);
281 *str = newstr;
282 }
283
284 static void
285 playsound(gchar **str, gint which)
286 {
287 GMatchInfo *match_info;
288 const gchar *list = NULL;
289 gchar **candidates = NULL, **candidate = NULL;
290
291 list = purple_prefs_get_string(which ? OPT_USERLIST_SENDER :
292 OPT_USERLIST_RECIPIENT);
293 g_return_if_fail(list != NULL);
294 if(strstr(list, DEFAULT_LIST))
295 return;
296
297 candidates = g_strsplit_set(list, " ,:;", 0);
298 g_return_if_fail(candidates != NULL);
299
300 g_regex_match(regp[which], *str, 0, &match_info);
301 while(g_match_info_matches(match_info)) {
302 gchar *user = NULL;
303 if(which == RECIPIENT)
304 user = g_match_info_fetch(match_info, 2);
305 else if(which == SENDER)
306 user = g_match_info_fetch(match_info, 2);
307 twitter_debug("user = %s\n", user);
308
309 for(candidate = candidates; *candidate; candidate++) {
310 if(!strcmp(*candidate, ""))
311 continue;
312 twitter_debug("candidate = %s\n", *candidate);
313 if(!strcmp(user, *candidate)) {
314 twitter_debug("match. play sound\n");
315 purple_sound_play_event(purple_prefs_get_int
316 (which ? OPT_SOUNDID_SENDER :
317 OPT_SOUNDID_RECIPIENT), NULL);
318 break;
319 }
320 }
321 g_free(user);
322 g_match_info_next(match_info, NULL);
323 }
324 g_strfreev(candidates);
325 g_match_info_free(match_info);
326 }
327
328 static gboolean
329 writing_im_cb(PurpleAccount *account, char *sender, char **buffer,
330 PurpleConversation *conv, int flags, void *data)
331 {
332 twitter_debug("called\n");
333
334 gint service = get_service_type(conv);
335
336 /* check if the conversation is between twitter */
337 if(service == unknown_service)
338 return FALSE;
339
340 /* add screen_name if the current message is posted by myself */
341 if (flags & PURPLE_MESSAGE_SEND) {
342 gchar *m = NULL;
343 const char *screen_name = NULL;
344
345 switch(service) {
346 case twitter_service:
347 screen_name = purple_prefs_get_string(OPT_SCREEN_NAME_TWITTER);
348 break;
349 case wassr_service:
350 screen_name = purple_prefs_get_string(OPT_SCREEN_NAME_WASSR);
351 break;
352 case identica_service:
353 screen_name = purple_prefs_get_string(OPT_SCREEN_NAME_IDENTICA);
354 break;
355 case jisko_service:
356 screen_name = purple_prefs_get_string(OPT_SCREEN_NAME_JISKO);
357 break;
358 }
359
360 if (screen_name) {
361 m = g_strdup_printf("%s: %s", screen_name, *buffer);
362 g_free(*buffer);
363 *buffer = m;
364 }
365 }
366
367 /* strip all markups */
368 strip_markup(buffer, TRUE);
369
370 /* playsound */
371 if(purple_prefs_get_bool(OPT_PLAYSOUND_SENDER)) {
372 playsound(buffer, SENDER);
373 }
374 if(purple_prefs_get_bool(OPT_PLAYSOUND_RECIPIENT)) {
375 playsound(buffer, RECIPIENT);
376 }
377
378 /* translate */
379 if(purple_prefs_get_bool(OPT_TRANSLATE_SENDER)) {
380 translate(buffer, SENDER, service);
381 }
382 if(purple_prefs_get_bool(OPT_TRANSLATE_RECIPIENT)) {
383 translate(buffer, RECIPIENT, service);
384 }
385 if(service == wassr_service &&
386 purple_prefs_get_bool(OPT_TRANSLATE_CHANNEL)) {
387 translate(buffer, CHANNEL_WASSR, service);
388 }
389 if(service == identica_service &&
390 purple_prefs_get_bool(OPT_TRANSLATE_CHANNEL)) {
391 translate(buffer, TAG_IDENTICA, service);
392 }
393
394 /* escape pseudo command (to show the same result as sending message) */
395 if(service == twitter_service &&
396 purple_prefs_get_bool(OPT_ESCAPE_PSEUDO)) {
397 escape(buffer);
398 }
399
400 if(purple_prefs_get_bool(OPT_STRIP_EXCESS_LF)) {
401 translate(buffer, EXCESS_LF, service);
402 }
403
404 return FALSE;
405 }
406
407 static void
408 insert_text_cb(GtkTextBuffer *textbuffer, GtkTextIter *position,
409 gchar *new_text, gint new_text_length, gpointer user_data)
410 {
411 PurpleConversation *conv = (PurpleConversation *)user_data;
412 PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
413
414 GtkWidget *box, *counter = NULL;
415 gchar *markup = NULL;
416 gint service = get_service_type(conv);
417 guint count;
418
419 g_return_if_fail(gtkconv != NULL);
420
421 switch(service) {
422 case twitter_service:
423 case identica_service:
424 case jisko_service:
425 count = gtk_text_buffer_get_char_count(textbuffer) +
426 (unsigned int)g_utf8_strlen(new_text, -1);
427 markup = g_markup_printf_escaped("<span color=\"%s\">%u</span>",
428 count <= 140 ? "black" : "red", count);
429 break;
430 case wassr_service:
431 count = gtk_text_buffer_get_char_count(textbuffer) +
432 (unsigned int)g_utf8_strlen(new_text, -1);
433 markup = g_markup_printf_escaped("<span color=\"%s\">%u</span>",
434 count <= 255 ? "black" : "red", count);
435 break;
436 default:
437 twitter_debug("unknown service\n");
438 break;
439 }
440
441 box = gtkconv->toolbar;
442 counter = g_object_get_data(G_OBJECT(box), PLUGIN_ID "-counter");
443 if(counter)
444 gtk_label_set_markup(GTK_LABEL(counter), markup);
445
446 g_free(markup);
447 }
448
449 static void
450 delete_text_cb(GtkTextBuffer *textbuffer, GtkTextIter *start_pos,
451 GtkTextIter *end_pos, gpointer user_data)
452 {
453 PurpleConversation *conv = (PurpleConversation *)user_data;
454 PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
455 GtkWidget *box, *counter = NULL;
456 gchar *markup = NULL;
457 gint service = get_service_type(conv);
458 guint count = 0;
459
460 g_return_if_fail(gtkconv != NULL);
461
462 switch(service) {
463 case twitter_service:
464 case identica_service:
465 case jisko_service:
466 count= gtk_text_buffer_get_char_count(textbuffer) -
467 (gtk_text_iter_get_offset(end_pos) -
468 gtk_text_iter_get_offset(start_pos));
469 markup = g_markup_printf_escaped("<span color=\"%s\">%u</span>",
470 count <= 140 ? "black" : "red", count);
471 break;
472 case wassr_service:
473 count= gtk_text_buffer_get_char_count(textbuffer) -
474 (gtk_text_iter_get_offset(end_pos) -
475 gtk_text_iter_get_offset(start_pos));
476 markup = g_markup_printf_escaped("<span color=\"%s\">%u</span>",
477 count <= 255 ? "black" : "red", count);
478 break;
479 default:
480 twitter_debug("unknown service\n");
481 break;
482 }
483
484 box = gtkconv->toolbar;
485 counter = g_object_get_data(G_OBJECT(box), PLUGIN_ID "-counter");
486 if(counter)
487 gtk_label_set_markup(GTK_LABEL(counter), markup);
488
489 g_free(markup);
490 }
491
492 void
493 detach_from_window(void)
494 {
495 GList *list;
496
497 /* find twitter conv window out and detach from that */
498 for(list = pidgin_conv_windows_get_list(); list; list = list->next) {
499 PidginWindow *win = list->data;
500 PurpleConversation *conv =
501 pidgin_conv_window_get_active_conversation(win);
502 gint service = get_service_type(conv);
503 switch(service) {
504 case twitter_service:
505 case wassr_service:
506 case identica_service:
507 case jisko_service:
508 detach_from_conv(conv, NULL);
509 break;
510 default:
511 twitter_debug("unknown service\n");
512 break;
513 }
514 }
515 }
516
517 static void
518 detach_from_conv(PurpleConversation *conv, gpointer null)
519 {
520 PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
521 GtkWidget *box, *counter = NULL, *sep = NULL;
522
523 g_signal_handlers_disconnect_by_func(G_OBJECT(gtkconv->entry_buffer),
524 (GFunc) insert_text_cb, conv);
525 g_signal_handlers_disconnect_by_func(G_OBJECT(gtkconv->entry_buffer),
526 (GFunc) delete_text_cb, conv);
527
528 box = gtkconv->toolbar;
529
530 /* remove counter */
531 counter = g_object_get_data(G_OBJECT(box), PLUGIN_ID "-counter");
532 if(counter) {
533 gtk_container_remove(GTK_CONTAINER(box), counter);
534 g_object_unref(counter);
535 g_object_set_data(G_OBJECT(box), PLUGIN_ID "-counter", NULL);
536 }
537
538 /* remove separator */
539 sep = g_object_get_data(G_OBJECT(box), PLUGIN_ID "-sep");
540 if(sep) {
541 gtk_container_remove(GTK_CONTAINER(box), sep);
542 g_object_unref(sep);
543 g_object_set_data(G_OBJECT(box), PLUGIN_ID "-sep", NULL);
544 }
545
546 gtk_widget_queue_draw(pidgin_conv_get_window(gtkconv)->window);
547 }
548
549 static void
550 remove_marks_func(gpointer key, gpointer value, gpointer user_data)
551 {
552 icon_data *data = (icon_data *)value;
553 GtkTextBuffer *text_buffer = (GtkTextBuffer *)user_data;
554 GList *mark_list = NULL;
555 GList *current;
556
557 if(!data)
558 return;
559
560 if(data->request_list)
561 mark_list = data->request_list;
562
563 /* remove the marks in its GtkTextBuffers */
564 current = g_list_first(mark_list);
565 while(current) {
566 GtkTextMark *current_mark = current->data;
567 GtkTextBuffer *current_text_buffer =
568 gtk_text_mark_get_buffer(current_mark);
569 GList *next;
570
571 next = g_list_next(current);
572
573 if(!current_text_buffer)
574 continue;
575
576 if(text_buffer) {
577 if(current_text_buffer == text_buffer) {
578 /* the mark will be freed in this function */
579 gtk_text_buffer_delete_mark(current_text_buffer,
580 current_mark);
581 current->data = NULL;
582 mark_list = g_list_delete_link(mark_list, current);
583 }
584 }
585 else {
586 gtk_text_buffer_delete_mark(current_text_buffer, current_mark);
587 current->data = NULL;
588 mark_list = g_list_delete_link(mark_list, current);
589 }
590
591 current = next;
592 }
593
594 data->request_list = mark_list;
595 }
596
597 static void
598 delete_requested_icon_marks(PidginConversation *conv, GHashTable *table) {
599 GtkTextBuffer *text_buffer = gtk_text_view_get_buffer(
600 GTK_TEXT_VIEW(conv->imhtml));
601
602 g_hash_table_foreach(table,
603 (GHFunc)remove_marks_func,
604 (gpointer)text_buffer);
605 }
606
607 void
608 attach_to_window(void)
609 {
610 GList *list;
611
612 twitter_debug("called\n");
613
614 /* find twitter conv window out and attach to that */
615 for(list = pidgin_conv_windows_get_list(); list; list = list->next) {
616 PidginWindow *win = list->data;
617 PurpleConversation *conv =
618 pidgin_conv_window_get_active_conversation(win);
619 gint service = get_service_type(conv);
620 /* only attach to twitter conversation window */
621 switch(service) {
622 case twitter_service:
623 case wassr_service:
624 case identica_service:
625 case jisko_service:
626 attach_to_conv(conv, NULL);
627 break;
628 default:
629 twitter_debug("unknown service\n");
630 break;
631 }
632 }
633 }
634
635 static void
636 attach_to_conv(PurpleConversation *conv, gpointer null)
637 {
638 PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
639 GtkWidget *box, *sep, *counter, *menus;
640 GtkIMHtml *imhtml;
641
642 box = gtkconv->toolbar;
643 imhtml = GTK_IMHTML(gtkconv->imhtml);
644
645 /* Disable widgets that decorate or add link to composing text
646 * because Twitter cannot receive marked up string. For lean-view
647 * and wide-view, see pidgin/gtkimhtmltoolbar.c.
648 */
649 menus = g_object_get_data(G_OBJECT(box), "lean-view");
650 if(menus) {
651 gtk_widget_set_sensitive(GTK_WIDGET(menus), FALSE);
652 }
653 menus = g_object_get_data(G_OBJECT(box), "wide-view");
654 if(menus) {
655 gtk_widget_set_sensitive(GTK_WIDGET(menus), FALSE);
656 }
657
658 purple_conversation_set_features(
659 gtkconv->active_conv,
660 purple_conversation_get_features(gtkconv->active_conv) &
661 ~PURPLE_CONNECTION_HTML);
662
663 /* check if the counter is enabled */
664 if(!purple_prefs_get_bool(OPT_COUNTER))
665 return;
666
667 /* get counter object */
668 counter = g_object_get_data(G_OBJECT(box), PLUGIN_ID "-counter");
669 g_return_if_fail(counter == NULL);
670
671 /* make counter object */
672 counter = gtk_label_new(NULL);
673 gtk_widget_set_name(counter, "counter_label");
674 gtk_label_set_text(GTK_LABEL(counter), "0");
675 gtk_box_pack_end(GTK_BOX(box), counter, FALSE, FALSE, 0);
676 gtk_widget_show_all(counter);
677 g_object_set_data(G_OBJECT(box), PLUGIN_ID "-counter", counter);
678
679 /* make separator object */
680 sep = gtk_vseparator_new();
681 gtk_box_pack_end(GTK_BOX(box), sep, FALSE, FALSE, 0);
682 gtk_widget_show_all(sep);
683 g_object_set_data(G_OBJECT(box), PLUGIN_ID "-sep", sep);
684
685 /* connect to signals */
686 g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "insert_text",
687 G_CALLBACK(insert_text_cb), conv);
688 g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "delete_range",
689 G_CALLBACK(delete_text_cb), conv);
690
691 /* redraw window */
692 gtk_widget_queue_draw(pidgin_conv_get_window(gtkconv)->window);
693 }
694
695 gboolean
696 is_twitter_account(PurpleAccount *account, const char *name)
697 {
698 const gchar *proto = purple_account_get_protocol_id(account);
699
700 if(g_strstr_len(name, 19, "twitter@twitter.com") &&
701 g_strstr_len(proto, 11, "prpl-jabber")) {
702 return TRUE;
703 }
704
705 return FALSE;
706 }
707
708 static gboolean
709 is_twitter_conv(PurpleConversation *conv)
710 {
711 g_return_val_if_fail(conv != NULL, FALSE);
712
713 const char *name = purple_conversation_get_name(conv);
714 PurpleAccount *account = purple_conversation_get_account(conv);
715
716 return is_twitter_account(account, name);
717 }
718
719 static gboolean
720 is_wassr_account(PurpleAccount *account, const char *name)
721 {
722 const gchar *proto = purple_account_get_protocol_id(account);
723
724 if(g_strstr_len(name, 18, "wassr-bot@wassr.jp") &&
725 g_strstr_len(proto, 11, "prpl-jabber")) {
726 return TRUE;
727 }
728
729 return FALSE;
730 }
731
732 static gboolean
733 is_wassr_conv(PurpleConversation *conv)
734 {
735 g_return_val_if_fail(conv != NULL, FALSE);
736
737 const char *name = purple_conversation_get_name(conv);
738 PurpleAccount *account = purple_conversation_get_account(conv);
739
740 return is_wassr_account(account, name);
741 }
742
743 static gboolean
744 is_identica_account(PurpleAccount *account, const char *name)
745 {
746 const gchar *proto = purple_account_get_protocol_id(account);
747
748 if(g_strstr_len(name, 16, "update@identi.ca") &&
749 g_strstr_len(proto, 11, "prpl-jabber")) {
750 return TRUE;
751 }
752
753 return FALSE;
754 }
755
756 static gboolean
757 is_identica_conv(PurpleConversation *conv)
758 {
759 g_return_val_if_fail(conv != NULL, FALSE);
760
761 const char *name = purple_conversation_get_name(conv);
762 PurpleAccount *account = purple_conversation_get_account(conv);
763
764 return is_identica_account(account, name);
765 }
766
767 static gboolean
768 is_jisko_account(PurpleAccount *account, const char *name)
769 {
770 const gchar *proto = purple_account_get_protocol_id(account);
771
772 if(g_strstr_len(name, 16, "bot@jisko.net") &&
773 g_strstr_len(proto, 11, "prpl-jabber")) {
774 return TRUE;
775 }
776
777 return FALSE;
778 }
779
780 static gboolean
781 is_jisko_conv(PurpleConversation *conv)
782 {
783 g_return_val_if_fail(conv != NULL, FALSE);
784
785 const char *name = purple_conversation_get_name(conv);
786 PurpleAccount *account = purple_conversation_get_account(conv);
787
788 return is_jisko_account(account, name);
789 }
790
791 gint
792 get_service_type_by_account(PurpleAccount *account, const char *sender)
793 {
794 gint service = unknown_service;
795
796 g_return_val_if_fail(account != NULL, unknown_service);
797 g_return_val_if_fail(sender != NULL, unknown_service);
798
799 if(is_twitter_account(account, sender))
800 service = twitter_service;
801 else if(is_wassr_account(account, sender))
802 service = wassr_service;
803 else if(is_identica_account(account, sender))
804 service = identica_service;
805 else if(is_jisko_account(account, sender))
806 service = jisko_service;
807
808 return service;
809 }
810
811 gint
812 get_service_type(PurpleConversation *conv)
813 {
814 gint service = unknown_service;
815
816 g_return_val_if_fail(conv != NULL, unknown_service);
817
818 if(is_twitter_conv(conv))
819 service = twitter_service;
820 else if(is_wassr_conv(conv))
821 service = wassr_service;
822 else if(is_identica_conv(conv))
823 service = identica_service;
824 else if(is_jisko_conv(conv))
825 service = jisko_service;
826
827 return service;
828 }
829
830 static void
831 conv_created_cb(PurpleConversation *conv, gpointer null)
832 {
833 PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
834
835 twitter_debug("called\n");
836
837 g_return_if_fail(gtkconv != NULL);
838
839 gint service = get_service_type(conv);
840 /* only attach to twitter conversation window */
841 switch(service) {
842 case twitter_service:
843 get_status_with_api((gpointer)conv);
844 source.id = g_timeout_add_seconds(
845 purple_prefs_get_int(OPT_API_BASE_GET_INTERVAL),
846 get_status_with_api, (gpointer)conv);
847 source.conv = conv;
848 attach_to_conv(conv, NULL);
849 break;
850 case wassr_service:
851 case identica_service:
852 case jisko_service:
853 attach_to_conv(conv, NULL);
854 break;
855 default:
856 twitter_debug("unknown service\n");
857 break;
858 }
859 }
860
861 static void
862 deleting_conv_cb(PurpleConversation *conv)
863 {
864 PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
865
866 twitter_debug("called\n");
867
868 g_return_if_fail(gtkconv != NULL);
869
870 gint service = get_service_type(conv);
871 GHashTable *hash = NULL;
872
873 /* only attach to twitter conversation window */
874 switch(service) {
875 case twitter_service:
876 if(purple_prefs_get_bool(OPT_API_BASE_POST)) {
877 g_source_remove_by_user_data((gpointer)conv);
878 source.id = 0;
879 source.conv = NULL;
880 }
881 hash = icon_hash[twitter_service];
882 break;
883 case wassr_service:
884 hash = icon_hash[wassr_service];
885 break;
886 case identica_service:
887 hash = icon_hash[identica_service];
888 break;
889 case jisko_service:
890 hash = icon_hash[jisko_service];
891 break;
892 default:
893 twitter_debug("unknown service\n");
894 break;
895 }
896
897 if(hash)
898 delete_requested_icon_marks(gtkconv, hash);
899 }
900
901 void
902 apply_filter(gchar **sender, gchar **buffer, PurpleMessageFlags *flags, int service)
903 {
904 GMatchInfo *match_info;
905 const gchar *list = NULL;
906 gchar *screen_name = NULL;
907 gchar **candidates = NULL, **candidate = NULL;
908
909 g_return_if_fail(*sender != NULL);
910 g_return_if_fail(*buffer != NULL);
911
912 gchar *plain = strip_html_markup(*buffer);
913
914 switch(service) {
915 case twitter_service:
916 default:
917 list = purple_prefs_get_string(OPT_FILTER_TWITTER);
918 screen_name = g_strdup_printf("@%s", purple_prefs_get_string(OPT_SCREEN_NAME_TWITTER));
919 break;
920 case wassr_service:
921 list = purple_prefs_get_string(OPT_FILTER_WASSR);
922 screen_name = g_strdup_printf("@%s", purple_prefs_get_string(OPT_SCREEN_NAME_WASSR));
923 break;
924 case identica_service:
925 list = purple_prefs_get_string(OPT_FILTER_IDENTICA);
926 screen_name = g_strdup_printf("@%s", purple_prefs_get_string(OPT_SCREEN_NAME_IDENTICA));
927 break;
928 case jisko_service:
929 list = purple_prefs_get_string(OPT_FILTER_JISKO);
930 screen_name = g_strdup_printf("@%s", purple_prefs_get_string(OPT_SCREEN_NAME_JISKO));
931 break;
932 }
933 g_return_if_fail(list != NULL);
934 if(strstr(list, DEFAULT_LIST))
935 return;
936
937 /* find @myself */
938 if(purple_prefs_get_bool(OPT_FILTER_EXCLUDE_REPLY) &&
939 strstr(plain, screen_name)) {
940 g_free(plain);
941 g_free(screen_name);
942 return;
943 }
944 else
945 g_free(screen_name);
946
947 candidates = g_strsplit_set(list, " ,:;", 0);
948 g_return_if_fail(candidates != NULL);
949
950 g_regex_match(regp[SENDER], plain, 0, &match_info);
951 while(g_match_info_matches(match_info)) {
952 gchar *user = NULL;
953 user = g_match_info_fetch(match_info, 2);
954 twitter_debug("user = %s\n", user);
955
956 for(candidate = candidates; *candidate; candidate++) {
957 if(!strcmp(*candidate, ""))
958 continue;
959 twitter_debug("candidate = %s\n", *candidate);
960 if(!strcmp(user, *candidate)) {
961 twitter_debug("match. filter %s\n", user);
962 /* pidgin should handle this flag properly --yaz */
963 // *flags |= PURPLE_MESSAGE_INVISIBLE;
964
965 /* temporal workaround */
966 g_free(*sender); *sender = NULL;
967 g_free(*buffer); *buffer = NULL;
968 break;
969 }
970 }
971
972 g_free(user);
973 g_match_info_next(match_info, NULL);
974 }
975
976 g_free(plain);
977 g_strfreev(candidates);
978 g_match_info_free(match_info);
979 }
980
981
982 static gboolean
983 receiving_im_cb(PurpleAccount *account, char **sender, char **buffer,
984 PurpleConversation *conv, PurpleMessageFlags *flags, void *data)
985 {
986 twitter_debug("called\n");
987 twitter_debug("buffer = %s suppress_oops = %d\n", *buffer, suppress_oops);
988
989 gint service;
990
991 service = get_service_type_by_account(account, *sender);
992 twitter_debug("service = %d\n", service);
993
994 #ifdef _WIN32
995 /* suppress notification of incoming messages. */
996 if(service != unknown_service &&
997 purple_prefs_get_bool(OPT_PREVENT_NOTIFICATION)) {
998 if(!blink_modified) {
999 blink_modified = TRUE;
1000 blink_state = purple_prefs_get_bool(OPT_PIDGIN_BLINK_IM);
1001 purple_prefs_set_bool(OPT_PIDGIN_BLINK_IM, FALSE);
1002 }
1003 }
1004 else {
1005 if(blink_modified) {
1006 purple_prefs_set_bool(OPT_PIDGIN_BLINK_IM, blink_state);
1007 blink_modified = FALSE;
1008 }
1009 }
1010 #endif
1011
1012 if(service == wassr_service) {
1013 gchar *stripped = strip_html_markup(*buffer);
1014 /* suppress annoying completion message from wassr */
1015 if(strstr(*buffer, "<body>チャンネル投稿完了:")) {
1016 twitter_debug("clearing channel parrot message\n");
1017 g_free(*sender); *sender = NULL;
1018 g_free(*buffer); *buffer = NULL;
1019 }
1020 /* discard parrot message */
1021 else {
1022 GList *current = g_list_first(wassr_parrot_list);
1023 while(current) {
1024 GList *next = g_list_next(current);
1025
1026 if(strstr(stripped, current->data)) {
1027 twitter_debug("parrot clearing: buf = %s post = %s\n",
1028 *buffer, (char *)current->data);
1029 g_free(*sender); *sender = NULL;
1030 g_free(*buffer); *buffer = NULL;
1031 g_free(current->data);
1032 current->data = NULL;
1033 wassr_parrot_list =
1034 g_list_delete_link(wassr_parrot_list, current);
1035 break;
1036 }
1037
1038 current = next;
1039 }
1040 }
1041 g_free(stripped);
1042 }
1043
1044 if(service == identica_service) {
1045 /* discard parrot message */
1046 gchar *stripped = strip_html_markup(*buffer);
1047 GList *current = g_list_first(identica_parrot_list);
1048 while(current) {
1049 GList *next = g_list_next(current);
1050
1051 if(strstr(stripped, current->data)) {
1052 twitter_debug("identica parrot clearing: buf = %s post = %s\n",
1053 *buffer, (char *)current->data);
1054 g_free(*sender); *sender = NULL;
1055 g_free(*buffer); *buffer = NULL;
1056 g_free(current->data);
1057 current->data = NULL;
1058 identica_parrot_list =
1059 g_list_delete_link(identica_parrot_list, current);
1060 break;
1061 }
1062
1063 current = next;
1064 }
1065 g_free(stripped);
1066 }
1067
1068 /* filtering */
1069 if(purple_prefs_get_bool(OPT_FILTER)) {
1070 apply_filter(sender, buffer, flags, service);
1071 }
1072
1073 /* return here if it is not twitter */
1074 if(service != twitter_service) {
1075 return FALSE;
1076 }
1077
1078 /* if we use api, discard all incoming IM messages. */
1079 if(purple_prefs_get_bool(OPT_API_BASE_POST)) {
1080 g_free(*sender); *sender = NULL;
1081 g_free(*buffer); *buffer = NULL;
1082 }
1083
1084 if(!suppress_oops || !purple_prefs_get_bool(OPT_SUPPRESS_OOPS))
1085 return FALSE;
1086
1087 if(strstr(*buffer, OOPS_MESSAGE)) {
1088 twitter_debug("clearing sender and buffer\n");
1089 g_free(*sender); *sender = NULL;
1090 g_free(*buffer); *buffer = NULL;
1091 suppress_oops = FALSE;
1092 }
1093 return FALSE;
1094 }
1095
1096
1097 static gboolean
1098 displaying_im_cb(PurpleAccount *account, const char *who, char **message,
1099 PurpleConversation *conv, PurpleMessageFlags flags,
1100 void *unused)
1101 {
1102 GtkIMHtml *imhtml;
1103 GtkTextBuffer *text_buffer;
1104 gint service = get_service_type(conv);
1105 gint linenumber = 0;
1106
1107 twitter_debug("called\n");
1108
1109 if(service == unknown_service) {
1110 twitter_debug("neither twitter nor wassr conv\n");
1111 return FALSE;
1112 }
1113
1114 /* get text buffer */
1115 imhtml = GTK_IMHTML(PIDGIN_CONVERSATION(conv)->imhtml);
1116 text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(imhtml));
1117
1118 /* store number of lines */
1119 linenumber = gtk_text_buffer_get_line_count(text_buffer);
1120 g_hash_table_insert(conv_hash, conv, GINT_TO_POINTER(linenumber));
1121 twitter_debug("conv = %p linenumber = %d\n", conv, linenumber);
1122
1123 return FALSE;
1124 }
1125
1126 static void
1127 displayed_im_cb(PurpleAccount *account, const char *who, char *message,
1128 PurpleConversation *conv, PurpleMessageFlags flags)
1129 {
1130 GMatchInfo *match_info = NULL;
1131 gchar *user_name = NULL;
1132 GtkIMHtml *imhtml;
1133 GtkTextBuffer *text_buffer;
1134 GtkTextIter insertion_point;
1135 gint service = get_service_type(conv);
1136 icon_data *data = NULL;
1137 gint linenumber;
1138 GHashTable *hash = NULL;
1139 gboolean renew = FALSE;
1140
1141 twitter_debug("called\n");
1142
1143 if(service == unknown_service) {
1144 twitter_debug("unknown service\n");
1145 return;
1146 }
1147
1148 /* get user's name */
1149 g_regex_match(regp[USER], message, 0, &match_info);
1150 if(!g_match_info_matches(match_info)) {
1151 twitter_debug("message was not matched : %s\n", message);
1152 g_match_info_free(match_info);
1153 return;
1154 }
1155
1156 user_name = g_match_info_fetch(match_info, 1);
1157 g_match_info_free(match_info);
1158
1159 /* insert icon */
1160 imhtml = GTK_IMHTML(PIDGIN_CONVERSATION(conv)->imhtml);
1161 text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(imhtml));
1162
1163 /* get GtkTextIter in the target line */
1164 linenumber = GPOINTER_TO_INT(g_hash_table_lookup(conv_hash, conv));
1165 gtk_text_buffer_get_iter_at_line(text_buffer,
1166 &insertion_point,
1167 linenumber);
1168
1169 switch(service) {
1170 case twitter_service:
1171 hash = icon_hash[twitter_service];
1172 break;
1173 case wassr_service:
1174 hash = icon_hash[wassr_service];
1175 break;
1176 case identica_service:
1177 hash = icon_hash[identica_service];
1178 break;
1179 case jisko_service:
1180 hash = icon_hash[jisko_service];
1181 break;
1182 default:
1183 twitter_debug("unknown service\n");
1184 break;
1185 }
1186
1187 if(hash)
1188 data = g_hash_table_lookup(hash, user_name);
1189
1190 if(data) {
1191 /* check validity of icon */
1192 int count_thres = purple_prefs_get_int(OPT_ICON_MAX_COUNT);
1193 int days_thres = DAYS_TO_SECONDS(
1194 purple_prefs_get_int(OPT_ICON_MAX_DAYS));
1195
1196 if(data->use_count > count_thres ||
1197 (data->mtime && ((time(NULL) - data->mtime)) > days_thres)) {
1198 twitter_debug("count=%d mtime=%d\n",
1199 data->use_count, (int)(data->mtime));
1200 renew = TRUE;
1201 request_icon(user_name, service, renew);
1202 }
1203 }
1204
1205 /* if we don't have the icon for this user, put a mark instead and
1206 * request the icon */
1207 if(!data || !data->pixbuf) {
1208 twitter_debug("%s's icon is not in memory.\n", user_name);
1209 mark_icon_for_user(gtk_text_buffer_create_mark(
1210 text_buffer, NULL, &insertion_point, FALSE),
1211 user_name, service);
1212 /* request to attach icon to the buffer */
1213 request_icon(user_name, service, renew);
1214 g_free(user_name); user_name = NULL;
1215 return;
1216 }
1217
1218 /* if we have icon for this user, insert icon immediately */
1219 if(purple_prefs_get_bool(OPT_SHOW_ICON)) {
1220 gtk_text_buffer_insert_pixbuf(text_buffer,
1221 &insertion_point,
1222 data->pixbuf);
1223 data->use_count++;
1224 }
1225 g_free(user_name); user_name = NULL;
1226
1227 twitter_debug("reach end of function\n");
1228 }
1229
1230 static gboolean
1231 load_plugin(PurplePlugin *plugin)
1232 {
1233 int i;
1234
1235 /* connect to signal */
1236 purple_signal_connect(purple_conversations_get_handle(), "writing-im-msg",
1237 plugin, PURPLE_CALLBACK(writing_im_cb), NULL);
1238 purple_signal_connect(purple_conversations_get_handle(), "sending-im-msg",
1239 plugin, PURPLE_CALLBACK(sending_im_cb), NULL);
1240 purple_signal_connect(purple_conversations_get_handle(),
1241 "conversation-created",
1242 plugin, PURPLE_CALLBACK(conv_created_cb), NULL);
1243 purple_signal_connect(purple_conversations_get_handle(), "receiving-im-msg",
1244 plugin, PURPLE_CALLBACK(receiving_im_cb), NULL);
1245 purple_signal_connect(pidgin_conversations_get_handle(), "displaying-im-msg",
1246 plugin, PURPLE_CALLBACK(displaying_im_cb), NULL);
1247
1248 purple_signal_connect(pidgin_conversations_get_handle(), "displayed-im-msg",
1249 plugin, PURPLE_CALLBACK(displayed_im_cb), NULL);
1250 purple_signal_connect(purple_conversations_get_handle(),
1251 "deleting-conversation",
1252 plugin, PURPLE_CALLBACK(deleting_conv_cb), NULL);
1253 purple_signal_connect(purple_connections_get_handle(), "signed-on",
1254 plugin, PURPLE_CALLBACK(signed_on_cb), NULL);
1255
1256
1257 /* compile regex */
1258 regp[RECIPIENT] = g_regex_new(P_RECIPIENT, 0, 0, NULL);
1259 regp[SENDER] = g_regex_new(P_SENDER, 0, 0, NULL);
1260 regp[COMMAND] = g_regex_new(P_COMMAND, G_REGEX_RAW, 0, NULL);
1261 regp[PSEUDO] = g_regex_new(P_PSEUDO, G_REGEX_RAW, 0, NULL);
1262 regp[USER] = g_regex_new(P_USER, 0, 0, NULL);
1263 regp[CHANNEL_WASSR] = g_regex_new(P_CHANNEL, 0, 0, NULL);
1264 regp[TAG_IDENTICA] = g_regex_new(P_TAG_IDENTICA, 0, 0, NULL);
1265 regp[IMAGE_TWITTER] = g_regex_new(P_IMAGE_TWITTER, 0, 0, NULL);
1266 regp[IMAGE_WASSR] = g_regex_new(P_IMAGE_WASSR, 0, 0, NULL);
1267 regp[IMAGE_IDENTICA] = g_regex_new(P_IMAGE_IDENTICA, 0, 0, NULL);
1268 regp[IMAGE_JISKO] = g_regex_new(P_IMAGE_JISKO, 0, 0, NULL);
1269 regp[SIZE_128_WASSR] = g_regex_new(P_SIZE_128_WASSR, 0, 0, NULL);
1270 regp[EXCESS_LF] = g_regex_new(P_EXCESS_LF, 0, 0, NULL);
1271
1272 for(i = twitter_service; i < NUM_SERVICES; i++) {
1273 icon_hash[i] = g_hash_table_new_full(g_str_hash, g_str_equal,
1274 g_free, NULL);
1275 }
1276
1277 conv_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
1278 NULL, NULL);
1279
1280
1281 /* attach counter to the existing twitter window */
1282 if(purple_prefs_get_bool(OPT_COUNTER)) {
1283 attach_to_window();
1284 }
1285
1286 return TRUE;
1287 }
1288
1289 static void
1290 cancel_fetch_func(gpointer key, gpointer value, gpointer user_data)
1291 {
1292 icon_data *data = (icon_data *)value;
1293
1294 if(!data)
1295 return;
1296
1297 if(data->requested) {
1298 purple_util_fetch_url_cancel(data->fetch_data);
1299 data->fetch_data = NULL;
1300 data->requested = FALSE;
1301 }
1302
1303 if(data->request_list) {
1304 twitter_debug("somehow, request_list != NULL\n");
1305 }
1306 }
1307
1308 static void
1309 cleanup_hash_entry_func(gpointer key, gpointer value, gpointer user_data)
1310 {
1311 remove_marks_func(key, value, user_data);
1312 cancel_fetch_func(key, value, user_data);
1313 }
1314
1315 static gboolean
1316 unload_plugin(PurplePlugin *plugin)
1317 {
1318 int i;
1319 GList *current;
1320
1321 twitter_debug("called\n");
1322
1323 /* disconnect from signal */
1324 purple_signal_disconnect(purple_conversations_get_handle(),
1325 "writing-im-msg",
1326 plugin, PURPLE_CALLBACK(writing_im_cb));
1327 purple_signal_disconnect(purple_conversations_get_handle(),
1328 "sending-im-msg",
1329 plugin, PURPLE_CALLBACK(sending_im_cb));
1330 purple_signal_disconnect(purple_conversations_get_handle(),
1331 "conversation-created",
1332 plugin, PURPLE_CALLBACK(conv_created_cb));
1333 purple_signal_disconnect(pidgin_conversations_get_handle(),
1334 "displaying-im-msg",
1335 plugin, PURPLE_CALLBACK(displaying_im_cb));
1336 purple_signal_disconnect(pidgin_conversations_get_handle(),
1337 "displayed-im-msg",
1338 plugin, PURPLE_CALLBACK(displayed_im_cb));
1339 purple_signal_disconnect(purple_conversations_get_handle(),
1340 "receiving-im-msg",
1341 plugin, PURPLE_CALLBACK(receiving_im_cb));
1342 purple_signal_disconnect(purple_conversations_get_handle(),
1343 "deleting-conversation",
1344 plugin, PURPLE_CALLBACK(deleting_conv_cb));
1345 purple_signal_disconnect(purple_connections_get_handle(),
1346 "signed-on",
1347 plugin, PURPLE_CALLBACK(signed_on_cb));
1348
1349 /* unreference regp */
1350 for(i = 0; i < NUM_REGPS; i++) {
1351 g_regex_unref(regp[i]);
1352 }
1353
1354 /* remove mark list in each hash entry */
1355 /* cancel request that has not been finished yet */
1356 for(i = twitter_service; i < NUM_SERVICES; i++) {
1357 /* delete mark list and stop requeset for each hash table */
1358 g_hash_table_foreach(icon_hash[i],
1359 (GHFunc)cleanup_hash_entry_func, NULL);
1360 /* destroy hash table for icon_data */
1361 g_hash_table_destroy(icon_hash[i]);
1362 }
1363
1364 g_hash_table_destroy(conv_hash);
1365
1366 /* detach from twitter window */
1367 detach_from_window();
1368
1369 /* free wassr_parrot_list */
1370 current = g_list_first(wassr_parrot_list);
1371 while(current) {
1372 GList *next;
1373
1374 next = g_list_next(current);
1375 g_free(current->data);
1376 wassr_parrot_list =
1377 g_list_delete_link(wassr_parrot_list, current);
1378
1379 current = next;
1380 }
1381 g_list_free(wassr_parrot_list);
1382 wassr_parrot_list = NULL;
1383
1384 /* free identica_parot_list */
1385 current = g_list_first(identica_parrot_list);
1386 while(current) {
1387 GList *next;
1388
1389 next = g_list_next(current);
1390 g_free(current->data);
1391 identica_parrot_list =
1392 g_list_delete_link(identica_parrot_list, current);
1393
1394 current = next;
1395 }
1396 g_list_free(identica_parrot_list);
1397 identica_parrot_list = NULL;
1398
1399 return TRUE;
1400 }
1401
1402 static PidginPluginUiInfo ui_info = {
1403 prefs_get_frame,
1404 0, /* page number - reserved */
1405 NULL, /* reserved 1 */
1406 NULL, /* reserved 2 */
1407 NULL, /* reserved 3 */
1408 NULL /* reserved 4 */
1409 };
1410
1411 static PurplePluginInfo info = {
1412 PURPLE_PLUGIN_MAGIC,
1413 PURPLE_MAJOR_VERSION,
1414 PURPLE_MINOR_VERSION,
1415 PURPLE_PLUGIN_STANDARD, /**< type */
1416 PIDGIN_PLUGIN_TYPE, /**< ui_req */
1417 0, /**< flags */
1418 NULL, /**< deps */
1419 PURPLE_PRIORITY_DEFAULT, /**< priority */
1420 PLUGIN_ID, /**< id */
1421 "Pidgin-Twitter", /**< name */
1422 "0.9.0d1", /**< version */
1423 "provides useful features for twitter", /** summary */
1424 "provides useful features for twitter", /** desc */
1425 "Yoshiki Yazawa, mikanbako, \nKonosuke Watanabe, IWATA Ray, \nmojin, umq, \nthe pidging-twitter team", /**< author */
1426 "http://www.honeyplanet.jp/pidgin-twitter/", /**< homepage */
1427 load_plugin, /**< load */
1428 unload_plugin, /**< unload */
1429 NULL, /**< destroy */
1430 &ui_info, /**< ui_info */
1431 NULL, /**< extra_info */
1432 NULL, /**< pref info */
1433 NULL
1434 };
1435
1436 static void
1437 init_plugin(PurplePlugin *plugin)
1438 {
1439 char *dirname = NULL;
1440
1441 g_type_init();
1442 dirname = g_build_filename(purple_user_dir(), "pidgin-twitter", "icons", NULL);
1443 if(dirname)
1444 purple_prefs_add_string(OPT_ICON_DIR, dirname);
1445 g_free(dirname);
1446
1447 /* add plugin preferences */
1448 purple_prefs_add_none(OPT_PIDGINTWITTER);
1449 purple_prefs_add_bool(OPT_TRANSLATE_RECIPIENT, TRUE);
1450 purple_prefs_add_bool(OPT_TRANSLATE_SENDER, TRUE);
1451 purple_prefs_add_bool(OPT_TRANSLATE_CHANNEL, TRUE);
1452 purple_prefs_add_bool(OPT_ESCAPE_PSEUDO, TRUE);
1453 purple_prefs_add_bool(OPT_STRIP_EXCESS_LF, TRUE);
1454
1455 purple_prefs_add_bool(OPT_PLAYSOUND_RECIPIENT, TRUE);
1456 purple_prefs_add_bool(OPT_PLAYSOUND_SENDER, TRUE);
1457 purple_prefs_add_int(OPT_SOUNDID_RECIPIENT, PURPLE_SOUND_POUNCE_DEFAULT);
1458 purple_prefs_add_string(OPT_USERLIST_RECIPIENT, DEFAULT_LIST);
1459 purple_prefs_add_int(OPT_SOUNDID_SENDER, PURPLE_SOUND_POUNCE_DEFAULT);
1460 purple_prefs_add_string(OPT_USERLIST_SENDER, DEFAULT_LIST);
1461
1462 purple_prefs_add_bool(OPT_COUNTER, TRUE);
1463 purple_prefs_add_bool(OPT_SUPPRESS_OOPS, TRUE);
1464 purple_prefs_add_bool(OPT_PREVENT_NOTIFICATION, FALSE);
1465
1466 purple_prefs_add_bool(OPT_API_BASE_POST, FALSE);
1467 purple_prefs_add_int(OPT_API_BASE_GET_INTERVAL, TWITTER_DEFAULT_INTERVAL);
1468 purple_prefs_add_string(OPT_SCREEN_NAME_TWITTER, EMPTY);
1469 purple_prefs_add_string(OPT_PASSWORD_TWITTER, EMPTY);
1470 purple_prefs_add_string(OPT_SCREEN_NAME_WASSR, EMPTY);
1471 purple_prefs_add_string(OPT_SCREEN_NAME_IDENTICA, EMPTY);
1472 purple_prefs_add_string(OPT_SCREEN_NAME_JISKO, EMPTY);
1473
1474 purple_prefs_add_bool(OPT_SHOW_ICON, TRUE);
1475 purple_prefs_add_int(OPT_ICON_SIZE, DEFAULT_ICON_SIZE);
1476 purple_prefs_add_bool(OPT_UPDATE_ICON, TRUE);
1477 purple_prefs_add_int(OPT_ICON_MAX_COUNT, DEFAULT_ICON_MAX_COUNT);
1478 purple_prefs_add_int(OPT_ICON_MAX_DAYS, DEFAULT_ICON_MAX_DAYS);
1479 purple_prefs_add_bool(OPT_LOG_OUTPUT, FALSE);
1480
1481 purple_prefs_add_bool(OPT_FILTER, TRUE);
1482 purple_prefs_add_bool(OPT_FILTER_EXCLUDE_REPLY, TRUE);
1483 purple_prefs_add_string(OPT_FILTER_TWITTER, DEFAULT_LIST);
1484 purple_prefs_add_string(OPT_FILTER_WASSR, DEFAULT_LIST);
1485 purple_prefs_add_string(OPT_FILTER_IDENTICA, DEFAULT_LIST);
1486 purple_prefs_add_string(OPT_FILTER_JISKO, DEFAULT_LIST);
1487 }
1488
1489 PURPLE_INIT_PLUGIN(pidgin_twitter, init_plugin, info)