1 /* Notification plugin for Claws Mail
2  * Copyright (C) 2005-2007 Holger Berndt
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU 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, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #ifdef HAVE_CONFIG_H
19 #  include "config.h"
20 #  include "claws-features.h"
21 #endif
22 
23 #include "folder.h"
24 #include "folderview.h"
25 #include "codeconv.h"
26 #include "gtk/gtkutils.h"
27 
28 #include "notification_core.h"
29 #include "notification_plugin.h"
30 #include "notification_prefs.h"
31 #include "notification_banner.h"
32 #include "notification_popup.h"
33 #include "notification_command.h"
34 #include "notification_lcdproc.h"
35 #include "notification_trayicon.h"
36 #include "notification_indicator.h"
37 
38 #ifdef HAVE_LIBCANBERRA_GTK
39 # include <canberra-gtk.h>
40 #endif
41 
42 typedef struct {
43   GSList *collected_msgs;
44   GSList *folder_items;
45   gboolean unread_also;
46   gint max_msgs;
47   gint num_msgs;
48 } TraverseCollect;
49 
50 static gboolean notification_traverse_collect(GNode*, gpointer);
51 static void     notification_new_unnotified_do_msg(MsgInfo*);
52 static gboolean notification_traverse_hash_startup(GNode*, gpointer);
53 
54 static GHashTable *msg_count_hash;
55 static NotificationMsgCount msg_count;
56 
57 #ifdef HAVE_LIBCANBERRA_GTK
58 static gboolean canberra_new_email_is_playing = FALSE;
59 #endif
60 
61 static void msg_count_hash_update_func(FolderItem*, gpointer);
62 static void msg_count_update_from_hash(gpointer, gpointer, gpointer);
63 static void msg_count_clear(NotificationMsgCount*);
64 static void msg_count_add(NotificationMsgCount*,NotificationMsgCount*);
65 static void msg_count_copy(NotificationMsgCount*,NotificationMsgCount*);
66 
notification_core_global_includes_changed(void)67 void notification_core_global_includes_changed(void)
68 {
69 #ifdef NOTIFICATION_BANNER
70   notification_update_banner();
71 #endif
72 
73   if(msg_count_hash) {
74     g_hash_table_destroy(msg_count_hash);
75     msg_count_hash = NULL;
76   }
77   notification_update_msg_counts(NULL);
78 }
79 
80 /* Hide/show main window */
notification_toggle_hide_show_window(void)81 void notification_toggle_hide_show_window(void)
82 {
83   MainWindow *mainwin;
84 	GdkWindow *gdkwin;
85 
86   if((mainwin = mainwindow_get_mainwindow()) == NULL)
87     return;
88 
89 	gdkwin = gtk_widget_get_window(GTK_WIDGET(mainwin->window));
90   if(gtk_widget_get_visible(GTK_WIDGET(mainwin->window))) {
91     if((gdk_window_get_state(gdkwin) & GDK_WINDOW_STATE_ICONIFIED)
92        || mainwindow_is_obscured()) {
93       notification_show_mainwindow(mainwin);
94     }
95     else {
96       main_window_hide(mainwin);
97     }
98   }
99   else {
100     notification_show_mainwindow(mainwin);
101   }
102 }
103 
notification_update_msg_counts(FolderItem * removed_item)104 void notification_update_msg_counts(FolderItem *removed_item)
105 {
106   if(!msg_count_hash)
107     msg_count_hash = g_hash_table_new_full(g_str_hash,g_str_equal,
108 					   g_free,g_free);
109 
110   folder_func_to_all_folders(msg_count_hash_update_func, msg_count_hash);
111 
112   if(removed_item) {
113     gchar *identifier;
114     identifier = folder_item_get_identifier(removed_item);
115     if(identifier) {
116       g_hash_table_remove(msg_count_hash, identifier);
117       g_free(identifier);
118     }
119   }
120   msg_count_clear(&msg_count);
121   g_hash_table_foreach(msg_count_hash, msg_count_update_from_hash, NULL);
122 #ifdef NOTIFICATION_LCDPROC
123   notification_update_lcdproc();
124 #endif
125 #ifdef NOTIFICATION_TRAYICON
126   notification_update_trayicon();
127 #endif
128 #ifdef NOTIFICATION_INDICATOR
129   notification_update_indicator();
130 #endif
131   notification_update_urgency_hint();
132 }
133 
msg_count_clear(NotificationMsgCount * count)134 static void msg_count_clear(NotificationMsgCount *count)
135 {
136   count->new_msgs          = 0;
137   count->unread_msgs       = 0;
138   count->unreadmarked_msgs = 0;
139   count->marked_msgs       = 0;
140   count->total_msgs        = 0;
141 }
142 
143 /* c1 += c2 */
msg_count_add(NotificationMsgCount * c1,NotificationMsgCount * c2)144 static void msg_count_add(NotificationMsgCount *c1,NotificationMsgCount *c2)
145 {
146   c1->new_msgs          += c2->new_msgs;
147   c1->unread_msgs       += c2->unread_msgs;
148   c1->unreadmarked_msgs += c2->unreadmarked_msgs;
149   c1->marked_msgs       += c2->marked_msgs;
150   c1->total_msgs        += c2->total_msgs;
151 }
152 
153 /* c1 = c2 */
msg_count_copy(NotificationMsgCount * c1,NotificationMsgCount * c2)154 static void msg_count_copy(NotificationMsgCount *c1,NotificationMsgCount *c2)
155 {
156   c1->new_msgs          = c2->new_msgs;
157   c1->unread_msgs       = c2->unread_msgs;
158   c1->unreadmarked_msgs = c2->unreadmarked_msgs;
159   c1->marked_msgs       = c2->marked_msgs;
160   c1->total_msgs        = c2->total_msgs;
161 }
162 
get_flat_gslist_from_nodes_traverse_func(GNode * node,gpointer data)163 gboolean get_flat_gslist_from_nodes_traverse_func(GNode *node, gpointer data)
164 {
165   if(node->data) {
166     GSList **list = data;
167     *list = g_slist_prepend(*list, node->data);
168   }
169   return FALSE;
170 }
171 
get_flat_gslist_from_nodes(GNode * node)172 GSList* get_flat_gslist_from_nodes(GNode *node)
173 {
174   GSList *retval = NULL;
175 
176   g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, get_flat_gslist_from_nodes_traverse_func, &retval);
177   return retval;
178 }
179 
notification_core_get_msg_count_of_foldername(gchar * foldername,NotificationMsgCount * count)180 void notification_core_get_msg_count_of_foldername(gchar *foldername, NotificationMsgCount *count)
181 {
182   GList *list;
183   GSList *f_list;
184 
185   Folder *walk_folder;
186   Folder *folder = NULL;
187 
188   for(list = folder_get_list(); list != NULL; list = list->next) {
189     walk_folder = list->data;
190     if(g_strcmp0(foldername, walk_folder->name) == 0) {
191       folder = walk_folder;
192       break;
193     }
194   }
195   if(!folder) {
196     debug_print("Notification plugin: Error: Could not find folder %s\n", foldername);
197     return;
198   }
199 
200   msg_count_clear(count);
201   f_list = get_flat_gslist_from_nodes(folder->node);
202   notification_core_get_msg_count(f_list, count);
203   g_slist_free(f_list);
204 }
205 
notification_core_get_msg_count(GSList * folder_list,NotificationMsgCount * count)206 void notification_core_get_msg_count(GSList *folder_list,
207 				     NotificationMsgCount *count)
208 {
209   GSList *walk;
210 
211   if(!folder_list)
212     msg_count_copy(count,&msg_count);
213   else {
214     msg_count_clear(count);
215     for(walk = folder_list; walk; walk = walk->next) {
216       gchar *identifier;
217       NotificationMsgCount *item_count;
218       FolderItem *item = (FolderItem*) walk->data;
219       identifier = folder_item_get_identifier(item);
220       if(identifier) {
221 	item_count = g_hash_table_lookup(msg_count_hash,identifier);
222 	g_free(identifier);
223 	if(item_count)
224 	  msg_count_add(count, item_count);
225       }
226     }
227   }
228 }
229 
msg_count_hash_update_func(FolderItem * item,gpointer data)230 static void msg_count_hash_update_func(FolderItem *item, gpointer data)
231 {
232   gchar *identifier;
233   NotificationMsgCount *count;
234   GHashTable *hash = data;
235 
236   if(!notify_include_folder_type(item->folder->klass->type,
237 				 item->folder->klass->uistr))
238     return;
239 
240   identifier = folder_item_get_identifier(item);
241   if(!identifier)
242     return;
243 
244   count = g_hash_table_lookup(hash, identifier);
245 
246   if(!count) {
247     count = g_new0(NotificationMsgCount,1);
248     g_hash_table_insert(hash, identifier, count);
249   }
250   else
251     g_free(identifier);
252 
253   count->new_msgs          = item->new_msgs;
254   count->unread_msgs       = item->unread_msgs;
255   count->unreadmarked_msgs = item->unreadmarked_msgs;
256   count->marked_msgs       = item->marked_msgs;
257   count->total_msgs        = item->total_msgs;
258 }
259 
msg_count_update_from_hash(gpointer key,gpointer value,gpointer data)260 static void msg_count_update_from_hash(gpointer key, gpointer value,
261 				       gpointer data)
262 {
263   NotificationMsgCount *count = value;
264   msg_count_add(&msg_count,count);
265 }
266 
267 
268 /* Replacement for the post-filtering hook:
269    Pseudocode by Colin:
270 hook on FOLDER_ITEM_UPDATE_HOOKLIST
271  if hook flags & F_ITEM_UPDATE_MSGCOUNT
272   scan mails (folder_item_get_msg_list)
273    if MSG_IS_NEW(msginfo->flags) and not in hashtable
274     notify()
275     add to hashtable
276    procmsg_msg_list_free
277 
278 hook on MSGINFO_UPDATE_HOOKLIST
279  if hook flags & MSGINFO_UPDATE_FLAGS
280   if !MSG_IS_NEW(msginfo->flags)
281    remove from hashtable, it's now useless
282 */
283 
284 /* This hash table holds all mails that we already notified about,
285    and that still are marked as "new". The keys are the msgid's,
286    the values are just 1's stored in a pointer. */
287 static GHashTable *notified_hash = NULL;
288 
289 
290 /* Remove message from the notified_hash if
291  *  - the message flags changed
292  *  - the message is not new
293  *  - the message is in the hash
294 */
notification_notified_hash_msginfo_update(MsgInfoUpdate * msg_update)295 gboolean notification_notified_hash_msginfo_update(MsgInfoUpdate *msg_update)
296 {
297   g_return_val_if_fail(msg_update != NULL, FALSE);
298 
299   if((msg_update->flags & MSGINFO_UPDATE_FLAGS) &&
300      !MSG_IS_NEW(msg_update->msginfo->flags)) {
301 
302     MsgInfo *msg;
303     gchar *msgid;
304 
305     msg = msg_update->msginfo;
306     if(msg->msgid)
307       msgid = msg->msgid;
308     else {
309       debug_print("Notification Plugin: Message has no message ID!\n");
310       msgid = "";
311     }
312 
313     g_return_val_if_fail(msg != NULL, FALSE);
314 
315     if(g_hash_table_lookup(notified_hash, msgid) != NULL) {
316 
317       debug_print("Notification Plugin: Removing message id %s from hash "
318 		  "table\n", msgid);
319       g_hash_table_remove(notified_hash, msgid);
320     }
321   }
322   return FALSE;
323 }
324 
325 /* On startup, mark all new mails as already notified
326  * (by including them in the hash) */
notification_notified_hash_startup_init(void)327 void notification_notified_hash_startup_init(void)
328 {
329   GList *folder_list, *walk;
330   Folder *folder;
331 
332   if(!notified_hash) {
333     notified_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
334 					  g_free, NULL);
335     debug_print("Notification Plugin: Hash table created\n");
336   }
337 
338   folder_list = folder_get_list();
339   for(walk = folder_list; walk != NULL; walk = g_list_next(walk)) {
340     folder = walk->data;
341 
342     g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
343 		    notification_traverse_hash_startup, NULL);
344   }
345 }
346 
notification_traverse_hash_startup(GNode * node,gpointer data)347 static gboolean notification_traverse_hash_startup(GNode *node, gpointer data)
348 {
349   GSList *walk;
350   GSList *msg_list;
351   FolderItem *item = (FolderItem*) node->data;
352   gint new_msgs_left;
353 
354   if(!(item->new_msgs))
355     return FALSE;
356 
357   new_msgs_left = item->new_msgs;
358   msg_list = folder_item_get_msg_list(item);
359 
360   for(walk = msg_list; walk; walk = g_slist_next(walk)) {
361     MsgInfo *msg = (MsgInfo*) walk->data;
362     if(MSG_IS_NEW(msg->flags)) {
363       gchar *msgid;
364 
365       if(msg->msgid)
366 	msgid = msg->msgid;
367       else {
368 	debug_print("Notification Plugin: Message has no message ID!\n");
369 	msgid = "";
370       }
371 
372       /* If the message id is not yet in the hash, add it */
373       g_hash_table_insert(notified_hash, g_strdup(msgid),
374 			  GINT_TO_POINTER(1));
375       debug_print("Notification Plugin: Init: Added msg id %s to the hash\n",
376 		  msgid);
377       /* Decrement left count and check if we're already done */
378       new_msgs_left--;
379       if(new_msgs_left == 0)
380 	break;
381     }
382   }
383   procmsg_msg_list_free(msg_list);
384   return FALSE;
385 }
386 
notification_core_free(void)387 void notification_core_free(void)
388 {
389   if(notified_hash) {
390     g_hash_table_destroy(notified_hash);
391     notified_hash = NULL;
392   }
393   if(msg_count_hash) {
394     g_hash_table_destroy(msg_count_hash);
395     msg_count_hash = NULL;
396   }
397   debug_print("Notification Plugin: Freed internal data\n");
398 }
399 
notification_new_unnotified_msgs(FolderItemUpdateData * update_data)400 void notification_new_unnotified_msgs(FolderItemUpdateData *update_data)
401 {
402   GSList *msg_list, *walk;
403 
404   g_return_if_fail(notified_hash != NULL);
405 
406   msg_list = folder_item_get_msg_list(update_data->item);
407 
408   for(walk = msg_list; walk; walk = g_slist_next(walk)) {
409     MsgInfo *msg;
410     msg = (MsgInfo*) walk->data;
411 
412     if(MSG_IS_NEW(msg->flags)) {
413       gchar *msgid;
414 
415       if(msg->msgid)
416 	msgid = msg->msgid;
417       else {
418 	debug_print("Notification Plugin: Message has not message ID!\n");
419 	msgid = "";
420       }
421 
422       debug_print("Notification Plugin: Found msg %s, "
423 		  "checking if it is in hash...\n", msgid);
424       /* Check if message is in hash table */
425       if(g_hash_table_lookup(notified_hash, msgid) != NULL)
426 	debug_print("yes.\n");
427       else {
428 	/* Add to hashtable */
429 	g_hash_table_insert(notified_hash, g_strdup(msgid),
430 			    GINT_TO_POINTER(1));
431 	debug_print("no, added to table.\n");
432 
433 	/* Do the notification */
434 	notification_new_unnotified_do_msg(msg);
435       }
436 
437     } /* msg is 'new' */
438   } /* for all messages */
439   procmsg_msg_list_free(msg_list);
440 }
441 
442 #ifdef HAVE_LIBCANBERRA_GTK
canberra_finished_cb(ca_context * c,uint32_t id,int error,void * data)443 static void canberra_finished_cb(ca_context *c, uint32_t id, int error, void *data)
444 {
445   canberra_new_email_is_playing = FALSE;
446 }
447 #endif
448 
notification_new_unnotified_do_msg(MsgInfo * msg)449 static void notification_new_unnotified_do_msg(MsgInfo *msg)
450 {
451 #ifdef NOTIFICATION_POPUP
452   notification_popup_msg(msg);
453 #endif
454 #ifdef NOTIFICATION_COMMAND
455   notification_command_msg(msg);
456 #endif
457 #ifdef NOTIFICATION_TRAYICON
458   notification_trayicon_msg(msg);
459 #endif
460 
461 #ifdef HAVE_LIBCANBERRA_GTK
462   /* canberra */
463   if(notify_config.canberra_play_sounds && !canberra_new_email_is_playing) {
464     ca_proplist *proplist;
465     ca_proplist_create(&proplist);
466     ca_proplist_sets(proplist,CA_PROP_EVENT_ID ,"message-new-email");
467     canberra_new_email_is_playing = TRUE;
468     ca_context_play_full(ca_gtk_context_get(), 0, proplist, canberra_finished_cb, NULL);
469     ca_proplist_destroy(proplist);
470   }
471 #endif
472 }
473 
474 /* If folders is not NULL, then consider only those folder items
475  * If max_msgs is not 0, stop after collecting msg_msgs messages
476  */
notification_collect_msgs(gboolean unread_also,GSList * folder_items,gint max_msgs)477 GSList* notification_collect_msgs(gboolean unread_also, GSList *folder_items,
478 				  gint max_msgs)
479 {
480   GList *folder_list, *walk;
481   Folder *folder;
482   TraverseCollect collect_data;
483 
484   collect_data.unread_also = unread_also;
485   collect_data.collected_msgs = NULL;
486   collect_data.folder_items = folder_items;
487   collect_data.max_msgs = max_msgs;
488   collect_data.num_msgs = 0;
489 
490   folder_list = folder_get_list();
491   for(walk = folder_list; walk != NULL; walk = g_list_next(walk)) {
492     folder = walk->data;
493 
494     g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
495 		    notification_traverse_collect, &collect_data);
496   }
497   return collect_data.collected_msgs;
498 }
499 
notification_collected_msgs_free(GSList * collected_msgs)500 void notification_collected_msgs_free(GSList *collected_msgs)
501 {
502   if(collected_msgs) {
503     GSList *walk;
504     for(walk = collected_msgs; walk != NULL; walk = g_slist_next(walk)) {
505       CollectedMsg *msg = walk->data;
506       if(msg->from)
507 				g_free(msg->from);
508       if(msg->subject)
509 				g_free(msg->subject);
510       if(msg->folderitem_name)
511 				g_free(msg->folderitem_name);
512 			msg->msginfo = NULL;
513       g_free(msg);
514     }
515     g_slist_free(collected_msgs);
516   }
517 }
518 
notification_traverse_collect(GNode * node,gpointer data)519 static gboolean notification_traverse_collect(GNode *node, gpointer data)
520 {
521   TraverseCollect *cdata = data;
522   FolderItem *item = node->data;
523   gchar *folder_id_cur;
524 
525   /* Obey global folder type limitations */
526   if(!notify_include_folder_type(item->folder->klass->type,
527 				 item->folder->klass->uistr))
528     return FALSE;
529 
530   /* If a folder_items list was given, check it first */
531   if((cdata->folder_items) && (item->path != NULL) &&
532      ((folder_id_cur  = folder_item_get_identifier(item)) != NULL)) {
533     FolderItem *list_item;
534     GSList *walk;
535     gchar *folder_id_list;
536     gboolean eq;
537     gboolean folder_in_list = FALSE;
538 
539     for(walk = cdata->folder_items; walk != NULL; walk = g_slist_next(walk)) {
540       list_item = walk->data;
541       folder_id_list = folder_item_get_identifier(list_item);
542       eq = !g_strcmp0(folder_id_list,folder_id_cur);
543       g_free(folder_id_list);
544       if(eq) {
545 	folder_in_list = TRUE;
546 	break;
547       }
548     }
549     g_free(folder_id_cur);
550     if(!folder_in_list)
551       return FALSE;
552   }
553 
554   if(item->new_msgs || (cdata->unread_also && item->unread_msgs)) {
555     GSList *msg_list = folder_item_get_msg_list(item);
556     GSList *walk;
557     for(walk = msg_list; walk != NULL; walk = g_slist_next(walk)) {
558       MsgInfo *msg_info = walk->data;
559       CollectedMsg *cmsg;
560 
561       if((cdata->max_msgs != 0) && (cdata->num_msgs >= cdata->max_msgs))
562 	return FALSE;
563 
564       if(MSG_IS_NEW(msg_info->flags) ||
565 				 (MSG_IS_UNREAD(msg_info->flags) && cdata->unread_also)) {
566 
567 				cmsg = g_new(CollectedMsg, 1);
568 				cmsg->from = g_strdup(msg_info->from ? msg_info->from : "");
569 				cmsg->subject = g_strdup(msg_info->subject ? msg_info->subject : "");
570 				if(msg_info->folder && msg_info->folder->name)
571 					cmsg->folderitem_name = g_strdup(msg_info->folder->path);
572 				else
573 					cmsg->folderitem_name = g_strdup("");
574 
575 				cmsg->msginfo = msg_info;
576 
577 				cdata->collected_msgs = g_slist_prepend(cdata->collected_msgs, cmsg);
578 				cdata->num_msgs++;
579       }
580     }
581     procmsg_msg_list_free(msg_list);
582   }
583 
584   return FALSE;
585 }
586 
notify_include_folder_type(FolderType ftype,gchar * uistr)587 gboolean notify_include_folder_type(FolderType ftype, gchar *uistr)
588 {
589   gboolean retval;
590 
591   retval = FALSE;
592   switch(ftype) {
593   case F_MH:
594   case F_MBOX:
595   case F_MAILDIR:
596   case F_IMAP:
597     if(notify_config.include_mail)
598       retval = TRUE;
599     break;
600   case F_NEWS:
601     if(notify_config.include_news)
602       retval = TRUE;
603     break;
604   case F_UNKNOWN:
605     if(uistr == NULL)
606       retval = FALSE;
607     else if(!strcmp(uistr, "vCalendar")) {
608       if(notify_config.include_calendar)
609 	retval = TRUE;
610     }
611     else if(!strcmp(uistr, "RSSyl")) {
612       if(notify_config.include_rss)
613 	retval = TRUE;
614     }
615     else
616       debug_print("Notification Plugin: Unknown folder type %d\n",ftype);
617     break;
618   default:
619     debug_print("Notification Plugin: Unknown folder type %d\n",ftype);
620   }
621 
622   return retval;
623 }
624 
fix_folderview_scroll(MainWindow * mainwin)625 static void fix_folderview_scroll(MainWindow *mainwin)
626 {
627 	static gboolean fix_done = FALSE;
628 
629 	if (fix_done)
630 		return;
631 
632 	gtk_widget_queue_resize(mainwin->folderview->ctree);
633 
634 	fix_done = TRUE;
635 }
636 
notification_show_mainwindow(MainWindow * mainwin)637 void notification_show_mainwindow(MainWindow *mainwin)
638 {
639       gtk_window_deiconify(GTK_WINDOW(mainwin->window));
640       gtk_window_set_skip_taskbar_hint(GTK_WINDOW(mainwin->window), FALSE);
641       main_window_show(mainwin);
642       gtk_window_present(GTK_WINDOW(mainwin->window));
643       fix_folderview_scroll(mainwin);
644 }
645 
646 #ifdef HAVE_LIBNOTIFY
647 #define STR_MAX_LEN 511
648 /* Returns a newly allocated string which needs to be freed */
notification_libnotify_sanitize_str(gchar * in)649 gchar* notification_libnotify_sanitize_str(gchar *in)
650 {
651   gint out;
652   gchar tmp_str[STR_MAX_LEN+1];
653 
654   if(in == NULL) return NULL;
655 
656   out = 0;
657   while(*in) {
658     if(*in == '<') {
659       if(out+4 > STR_MAX_LEN) break;
660       memcpy(&(tmp_str[out]),"&lt;",4);
661       in++; out += 4;
662     }
663     else if(*in == '>') {
664       if(out+4 > STR_MAX_LEN) break;
665       memcpy(&(tmp_str[out]),"&gt;",4);
666       in++; out += 4;
667     }
668     else if(*in == '&') {
669       if(out+5 > STR_MAX_LEN) break;
670       memcpy(&(tmp_str[out]),"&amp;",5);
671       in++; out += 5;
672     }
673     else {
674       if(out+1 > STR_MAX_LEN) break;
675       tmp_str[out++] = *in++;
676     }
677   }
678   tmp_str[out] = '\0';
679   return strdup(tmp_str);
680 }
681 
notification_validate_utf8_str(gchar * text)682 gchar* notification_validate_utf8_str(gchar *text)
683 {
684   gchar *utf8_str = NULL;
685 
686   if(!g_utf8_validate(text, -1, NULL)) {
687     debug_print("Notification plugin: String is not valid utf8, "
688 		"trying to fix it...\n");
689     /* fix it */
690     utf8_str = conv_codeset_strdup(text,
691 				   conv_get_locale_charset_str_no_utf8(),
692 				   CS_INTERNAL);
693     /* check if the fix worked */
694     if(utf8_str == NULL || !g_utf8_validate(utf8_str, -1, NULL)) {
695       debug_print("Notification plugin: String is still not valid utf8, "
696 		  "sanitizing...\n");
697       utf8_str = g_malloc(strlen(text)*2+1);
698       conv_localetodisp(utf8_str, strlen(text)*2+1, text);
699     }
700   }
701   else {
702     debug_print("Notification plugin: String is valid utf8\n");
703     utf8_str = g_strdup(text);
704   }
705   return utf8_str;
706 }
707 #endif
708