commit abf3a16d4009378957934790e69619e292642dfa
parent 2c79a205e4b3c1134b237adc1e6460b309381ba7
Author: Colin Leroy <colin@colino.net>
Date: Sun, 17 Feb 2013 21:22:03 +0000
2013-02-17 [colin] 3.9.0cvs75
* src/plugins/Makefile.am
* src/plugins/archive/Makefile.am
* src/plugins/clamd/Makefile.am
* src/plugins/clamd/clamav_plugin.c
* src/plugins/clamd/clamav_plugin.h
* src/plugins/clamd/clamav_plugin_gtk.c
* src/plugins/clamd/placeholder.txt
* src/plugins/clamd/libclamd/Makefile.am
* src/plugins/clamd/libclamd/clamd-plugin.c
* src/plugins/clamd/libclamd/clamd-plugin.h
* src/plugins/fetchinfo/Makefile.am
* src/plugins/fetchinfo/fetchinfo_plugin.c
* src/plugins/fetchinfo/fetchinfo_plugin.h
* src/plugins/fetchinfo/fetchinfo_plugin_gtk.c
* src/plugins/fetchinfo/placeholder.txt
* src/plugins/gdata/Makefile.am
* src/plugins/gdata/cm_gdata_contacts.c
* src/plugins/gdata/cm_gdata_contacts.h
* src/plugins/gdata/cm_gdata_prefs.c
* src/plugins/gdata/cm_gdata_prefs.h
* src/plugins/gdata/gdata_plugin.c
* src/plugins/gdata/gdata_plugin.h
* src/plugins/gdata/placeholder.txt
* src/plugins/geolocation/placeholder.txt
* src/plugins/gtkhtml2_viewer/placeholder.txt
* src/plugins/mailmbox/Makefile.am
* src/plugins/mailmbox/carray.c
* src/plugins/mailmbox/carray.h
* src/plugins/mailmbox/chash.c
* src/plugins/mailmbox/chash.h
* src/plugins/mailmbox/clist.c
* src/plugins/mailmbox/clist.h
* src/plugins/mailmbox/mailimf.c
* src/plugins/mailmbox/mailimf.h
* src/plugins/mailmbox/mailimf_types.c
* src/plugins/mailmbox/mailimf_types.h
* src/plugins/mailmbox/mailimf_types_helper.c
* src/plugins/mailmbox/mailimf_types_helper.h
* src/plugins/mailmbox/mailimf_write.c
* src/plugins/mailmbox/mailimf_write.h
* src/plugins/mailmbox/maillock.c
* src/plugins/mailmbox/maillock.h
* src/plugins/mailmbox/mailmbox.c
* src/plugins/mailmbox/mailmbox.h
* src/plugins/mailmbox/mailmbox_folder.c
* src/plugins/mailmbox/mailmbox_folder.h
* src/plugins/mailmbox/mailmbox_gtk.deps
* src/plugins/mailmbox/mailmbox_parse.c
* src/plugins/mailmbox/mailmbox_parse.h
* src/plugins/mailmbox/mailmbox_types.c
* src/plugins/mailmbox/mailmbox_types.h
* src/plugins/mailmbox/mmapstring.c
* src/plugins/mailmbox/mmapstring.h
* src/plugins/mailmbox/placeholder.txt
* src/plugins/mailmbox/plugin.c
* src/plugins/mailmbox/plugin_gtk.c
* src/plugins/mailmbox/plugin_gtk.h
* src/plugins/newmail/Makefile.am
* src/plugins/newmail/newmail.c
* src/plugins/newmail/placeholder.txt
* src/plugins/notification/Makefile.am
* src/plugins/notification/claws.def
* src/plugins/notification/notification_banner.c
* src/plugins/notification/notification_banner.h
* src/plugins/notification/notification_command.c
* src/plugins/notification/notification_command.h
* src/plugins/notification/notification_core.c
* src/plugins/notification/notification_core.h
* src/plugins/notification/notification_foldercheck.c
* src/plugins/notification/notification_foldercheck.h
* src/plugins/notification/notification_hotkeys.c
* src/plugins/notification/notification_hotkeys.h
* src/plugins/notification/notification_indicator.c
* src/plugins/notification/notification_indicator.h
* src/plugins/notification/notification_lcdproc.c
* src/plugins/notification/notification_lcdproc.h
* src/plugins/notification/notification_pixbuf.c
* src/plugins/notification/notification_pixbuf.h
* src/plugins/notification/notification_plugin.c
* src/plugins/notification/notification_plugin.h
* src/plugins/notification/notification_popup.c
* src/plugins/notification/notification_popup.h
* src/plugins/notification/notification_prefs.c
* src/plugins/notification/notification_prefs.h
* src/plugins/notification/notification_trayicon.c
* src/plugins/notification/notification_trayicon.h
* src/plugins/notification/placeholder.txt
* src/plugins/notification/plugin.def
* src/plugins/notification/raw_claws_mail_logo_64x64.h
* src/plugins/notification/version.rc
* src/plugins/pdf_viewer/Makefile.am
* src/plugins/pdf_viewer/doc_index.xpm
* src/plugins/pdf_viewer/doc_index_close.xpm
* src/plugins/pdf_viewer/doc_info.xpm
* src/plugins/pdf_viewer/first_arrow.xpm
* src/plugins/pdf_viewer/last_arrow.xpm
* src/plugins/pdf_viewer/left_arrow.xpm
* src/plugins/pdf_viewer/placeholder.txt
* src/plugins/pdf_viewer/poppler_viewer.c
* src/plugins/pdf_viewer/poppler_viewer.h
* src/plugins/pdf_viewer/right_arrow.xpm
* src/plugins/pdf_viewer/rotate_left.xpm
* src/plugins/pdf_viewer/rotate_right.xpm
* src/plugins/pdf_viewer/zoom_fit.xpm
* src/plugins/pdf_viewer/zoom_in.xpm
* src/plugins/pdf_viewer/zoom_out.xpm
* src/plugins/pdf_viewer/zoom_width.xpm
* src/plugins/perl/Makefile.am
* src/plugins/perl/perl_gtk.c
* src/plugins/perl/perl_gtk.h
* src/plugins/perl/perl_plugin.c
* src/plugins/perl/perl_plugin.h
* src/plugins/perl/placeholder.txt
* src/plugins/python/Makefile.am
* src/plugins/python/clawsmailmodule.c
* src/plugins/python/clawsmailmodule.h
* src/plugins/python/composewindowtype.c
* src/plugins/python/composewindowtype.h
* src/plugins/python/foldertype.c
* src/plugins/python/foldertype.h
* src/plugins/python/messageinfotype.c
* src/plugins/python/messageinfotype.h
* src/plugins/python/nodetype.c
* src/plugins/python/nodetype.h
* src/plugins/python/placeholder.txt
* src/plugins/python/python-hooks.c
* src/plugins/python/python-hooks.h
* src/plugins/python/python-shell.c
* src/plugins/python/python-shell.h
* src/plugins/python/python_plugin.c
* src/plugins/vcalendar/Makefile.in
Add some plugins (clamd, fetchinfo, gdata, mailmbox, newmail,
notification, pdf_viewer, perl, python). Notification not yet
enabled because it has too much autoconf switches for my taste.
Diffstat:
134 files changed, 42232 insertions(+), 15 deletions(-)
diff --git a/ChangeLog b/ChangeLog
@@ -1,3 +1,141 @@
+2013-02-17 [colin] 3.9.0cvs75
+
+ * src/plugins/Makefile.am
+ * src/plugins/archive/Makefile.am
+ * src/plugins/clamd/Makefile.am
+ * src/plugins/clamd/clamav_plugin.c
+ * src/plugins/clamd/clamav_plugin.h
+ * src/plugins/clamd/clamav_plugin_gtk.c
+ * src/plugins/clamd/placeholder.txt
+ * src/plugins/clamd/libclamd/Makefile.am
+ * src/plugins/clamd/libclamd/clamd-plugin.c
+ * src/plugins/clamd/libclamd/clamd-plugin.h
+ * src/plugins/fetchinfo/Makefile.am
+ * src/plugins/fetchinfo/fetchinfo_plugin.c
+ * src/plugins/fetchinfo/fetchinfo_plugin.h
+ * src/plugins/fetchinfo/fetchinfo_plugin_gtk.c
+ * src/plugins/fetchinfo/placeholder.txt
+ * src/plugins/gdata/Makefile.am
+ * src/plugins/gdata/cm_gdata_contacts.c
+ * src/plugins/gdata/cm_gdata_contacts.h
+ * src/plugins/gdata/cm_gdata_prefs.c
+ * src/plugins/gdata/cm_gdata_prefs.h
+ * src/plugins/gdata/gdata_plugin.c
+ * src/plugins/gdata/gdata_plugin.h
+ * src/plugins/gdata/placeholder.txt
+ * src/plugins/geolocation/placeholder.txt
+ * src/plugins/gtkhtml2_viewer/placeholder.txt
+ * src/plugins/mailmbox/Makefile.am
+ * src/plugins/mailmbox/carray.c
+ * src/plugins/mailmbox/carray.h
+ * src/plugins/mailmbox/chash.c
+ * src/plugins/mailmbox/chash.h
+ * src/plugins/mailmbox/clist.c
+ * src/plugins/mailmbox/clist.h
+ * src/plugins/mailmbox/mailimf.c
+ * src/plugins/mailmbox/mailimf.h
+ * src/plugins/mailmbox/mailimf_types.c
+ * src/plugins/mailmbox/mailimf_types.h
+ * src/plugins/mailmbox/mailimf_types_helper.c
+ * src/plugins/mailmbox/mailimf_types_helper.h
+ * src/plugins/mailmbox/mailimf_write.c
+ * src/plugins/mailmbox/mailimf_write.h
+ * src/plugins/mailmbox/maillock.c
+ * src/plugins/mailmbox/maillock.h
+ * src/plugins/mailmbox/mailmbox.c
+ * src/plugins/mailmbox/mailmbox.h
+ * src/plugins/mailmbox/mailmbox_folder.c
+ * src/plugins/mailmbox/mailmbox_folder.h
+ * src/plugins/mailmbox/mailmbox_gtk.deps
+ * src/plugins/mailmbox/mailmbox_parse.c
+ * src/plugins/mailmbox/mailmbox_parse.h
+ * src/plugins/mailmbox/mailmbox_types.c
+ * src/plugins/mailmbox/mailmbox_types.h
+ * src/plugins/mailmbox/mmapstring.c
+ * src/plugins/mailmbox/mmapstring.h
+ * src/plugins/mailmbox/placeholder.txt
+ * src/plugins/mailmbox/plugin.c
+ * src/plugins/mailmbox/plugin_gtk.c
+ * src/plugins/mailmbox/plugin_gtk.h
+ * src/plugins/newmail/Makefile.am
+ * src/plugins/newmail/newmail.c
+ * src/plugins/newmail/placeholder.txt
+ * src/plugins/notification/Makefile.am
+ * src/plugins/notification/claws.def
+ * src/plugins/notification/notification_banner.c
+ * src/plugins/notification/notification_banner.h
+ * src/plugins/notification/notification_command.c
+ * src/plugins/notification/notification_command.h
+ * src/plugins/notification/notification_core.c
+ * src/plugins/notification/notification_core.h
+ * src/plugins/notification/notification_foldercheck.c
+ * src/plugins/notification/notification_foldercheck.h
+ * src/plugins/notification/notification_hotkeys.c
+ * src/plugins/notification/notification_hotkeys.h
+ * src/plugins/notification/notification_indicator.c
+ * src/plugins/notification/notification_indicator.h
+ * src/plugins/notification/notification_lcdproc.c
+ * src/plugins/notification/notification_lcdproc.h
+ * src/plugins/notification/notification_pixbuf.c
+ * src/plugins/notification/notification_pixbuf.h
+ * src/plugins/notification/notification_plugin.c
+ * src/plugins/notification/notification_plugin.h
+ * src/plugins/notification/notification_popup.c
+ * src/plugins/notification/notification_popup.h
+ * src/plugins/notification/notification_prefs.c
+ * src/plugins/notification/notification_prefs.h
+ * src/plugins/notification/notification_trayicon.c
+ * src/plugins/notification/notification_trayicon.h
+ * src/plugins/notification/placeholder.txt
+ * src/plugins/notification/plugin.def
+ * src/plugins/notification/raw_claws_mail_logo_64x64.h
+ * src/plugins/notification/version.rc
+ * src/plugins/pdf_viewer/Makefile.am
+ * src/plugins/pdf_viewer/doc_index.xpm
+ * src/plugins/pdf_viewer/doc_index_close.xpm
+ * src/plugins/pdf_viewer/doc_info.xpm
+ * src/plugins/pdf_viewer/first_arrow.xpm
+ * src/plugins/pdf_viewer/last_arrow.xpm
+ * src/plugins/pdf_viewer/left_arrow.xpm
+ * src/plugins/pdf_viewer/placeholder.txt
+ * src/plugins/pdf_viewer/poppler_viewer.c
+ * src/plugins/pdf_viewer/poppler_viewer.h
+ * src/plugins/pdf_viewer/right_arrow.xpm
+ * src/plugins/pdf_viewer/rotate_left.xpm
+ * src/plugins/pdf_viewer/rotate_right.xpm
+ * src/plugins/pdf_viewer/zoom_fit.xpm
+ * src/plugins/pdf_viewer/zoom_in.xpm
+ * src/plugins/pdf_viewer/zoom_out.xpm
+ * src/plugins/pdf_viewer/zoom_width.xpm
+ * src/plugins/perl/Makefile.am
+ * src/plugins/perl/perl_gtk.c
+ * src/plugins/perl/perl_gtk.h
+ * src/plugins/perl/perl_plugin.c
+ * src/plugins/perl/perl_plugin.h
+ * src/plugins/perl/placeholder.txt
+ * src/plugins/python/Makefile.am
+ * src/plugins/python/clawsmailmodule.c
+ * src/plugins/python/clawsmailmodule.h
+ * src/plugins/python/composewindowtype.c
+ * src/plugins/python/composewindowtype.h
+ * src/plugins/python/foldertype.c
+ * src/plugins/python/foldertype.h
+ * src/plugins/python/messageinfotype.c
+ * src/plugins/python/messageinfotype.h
+ * src/plugins/python/nodetype.c
+ * src/plugins/python/nodetype.h
+ * src/plugins/python/placeholder.txt
+ * src/plugins/python/python-hooks.c
+ * src/plugins/python/python-hooks.h
+ * src/plugins/python/python-shell.c
+ * src/plugins/python/python-shell.h
+ * src/plugins/python/python_plugin.c
+ * src/plugins/vcalendar/Makefile.in
+ Add some plugins (clamd, fetchinfo, gdata, mailmbox, newmail,
+ notification, pdf_viewer, perl, python). Notification not yet
+ enabled because it has too much autoconf switches for my taste.
+
+
2013-02-16 [colin] 3.9.0cvs74
* po/POTFILES.in
diff --git a/PATCHSETS b/PATCHSETS
@@ -4572,3 +4572,4 @@
( cvs diff -u -r 1.53.2.41 -r 1.53.2.42 po/POTFILES.in; cvs diff -u -r 1.21.2.11 -r 1.21.2.12 po/bg.po; cvs diff -u -r 1.1.2.21 -r 1.1.2.22 po/ca.po; cvs diff -u -r 1.9.2.28 -r 1.9.2.29 po/cs.po; cvs diff -u -r 1.58.2.54 -r 1.58.2.55 po/de.po; cvs diff -u -r 1.12.2.20 -r 1.12.2.21 po/en_GB.po; cvs diff -u -r 1.60.2.68 -r 1.60.2.69 po/es.po; cvs diff -u -r 1.1.2.31 -r 1.1.2.32 po/fi.po; cvs diff -u -r 1.42.2.61 -r 1.42.2.62 po/fr.po; cvs diff -u -r 1.5.2.25 -r 1.5.2.26 po/hu.po; cvs diff -u -r 1.1.2.5 -r 1.1.2.6 po/id_ID.po; cvs diff -u -r 1.34.2.32 -r 1.34.2.33 po/it.po; cvs diff -u -r 1.16.2.14 -r 1.16.2.15 po/ja.po; cvs diff -u -r 1.1.2.7 -r 1.1.2.8 po/lt.po; cvs diff -u -r 1.28.2.17 -r 1.28.2.18 po/nl.po; cvs diff -u -r 1.10.2.20 -r 1.10.2.21 po/pl.po; cvs diff -u -r 1.50.2.47 -r 1.50.2.48 po/pt_BR.po; cvs diff -u -r 1.1.2.6 -r 1.1.2.7 po/pt_PT.po; cvs diff -u -r 1.17.2.24 -r 1.17.2.25 po/ru.po; cvs diff -u -r 1.2.2.39 -r 1.2.2.40 po/sk.po; cvs diff -u -r 1.17.2.30 -r 1.17.2.31 po/sr.po; cvs diff -u -r 1.4.2.15 -r 1.4.2.16 po/sv.po; cvs diff -u -r 1.1.2.8 -r 1.1.2.9 po/uk.po; cvs diff -u -r 1.5.2.33 -r 1.5.2.34 po/zh_CN.po; cvs diff -u -r 1.1.2.7 -r 1.1.2.8 po/zh_TW.po; ) > 3.9.0cvs72.patchset
( cvs diff -u -r 1.8.2.11 -r 1.8.2.12 src/plugins/Makefile.am; diff -u /dev/null src/plugins/acpi_notifier/Makefile.am; diff -u /dev/null src/plugins/acpi_notifier/acpi_notifier.c; cvs diff -u -r -1.1.2.1 -r -1.1.2.2 src/plugins/acpi_notifier/placeholder.txt; diff -u /dev/null src/plugins/address_keeper/Makefile.am; diff -u /dev/null src/plugins/address_keeper/address_keeper.c; diff -u /dev/null src/plugins/address_keeper/address_keeper.h; diff -u /dev/null src/plugins/address_keeper/address_keeper_prefs.c; diff -u /dev/null src/plugins/address_keeper/address_keeper_prefs.h; diff -u /dev/null src/plugins/address_keeper/claws.def; cvs diff -u -r -1.1.2.1 -r -1.1.2.2 src/plugins/address_keeper/placeholder.txt; diff -u /dev/null src/plugins/address_keeper/plugin.def; diff -u /dev/null src/plugins/address_keeper/version.rc; diff -u /dev/null src/plugins/attachwarner/Makefile.am; diff -u /dev/null src/plugins/attachwarner/attachwarner.c; diff -u /dev/null src/plugins/attachwarner/attachwarner.h; diff -u /dev/null src/plugins/attachwarner/attachwarner_prefs.c; diff -u /dev/null src/plugins/attachwarner/attachwarner_prefs.h; diff -u /dev/null src/plugins/attachwarner/claws.def; cvs diff -u -r -1.1.2.1 -r -1.1.2.2 src/plugins/attachwarner/placeholder.txt; diff -u /dev/null src/plugins/attachwarner/plugin.def; diff -u /dev/null src/plugins/attachwarner/version.rc; cvs diff -u -r 1.1.2.2 -r 1.1.2.3 src/plugins/vcalendar/libical/libical/icalversion.h; cvs diff -u -r 1.53.2.42 -r 1.53.2.43 po/POTFILES.in; cvs diff -u -r 1.21.2.12 -r 1.21.2.13 po/bg.po; cvs diff -u -r 1.1.2.22 -r 1.1.2.23 po/ca.po; cvs diff -u -r 1.9.2.29 -r 1.9.2.30 po/cs.po; cvs diff -u -r 1.58.2.55 -r 1.58.2.56 po/de.po; cvs diff -u -r 1.12.2.21 -r 1.12.2.22 po/en_GB.po; cvs diff -u -r 1.60.2.69 -r 1.60.2.70 po/es.po; cvs diff -u -r 1.1.2.32 -r 1.1.2.33 po/fi.po; cvs diff -u -r 1.42.2.62 -r 1.42.2.63 po/fr.po; cvs diff -u -r 1.5.2.26 -r 1.5.2.27 po/hu.po; cvs diff -u -r 1.1.2.6 -r 1.1.2.7 po/id_ID.po; cvs diff -u -r 1.34.2.33 -r 1.34.2.34 po/it.po; cvs diff -u -r 1.16.2.15 -r 1.16.2.16 po/ja.po; cvs diff -u -r 1.1.2.8 -r 1.1.2.9 po/lt.po; cvs diff -u -r 1.28.2.18 -r 1.28.2.19 po/nl.po; cvs diff -u -r 1.10.2.21 -r 1.10.2.22 po/pl.po; cvs diff -u -r 1.50.2.48 -r 1.50.2.49 po/pt_BR.po; cvs diff -u -r 1.1.2.7 -r 1.1.2.8 po/pt_PT.po; cvs diff -u -r 1.17.2.25 -r 1.17.2.26 po/ru.po; cvs diff -u -r 1.2.2.40 -r 1.2.2.41 po/sk.po; cvs diff -u -r 1.17.2.31 -r 1.17.2.32 po/sr.po; cvs diff -u -r 1.4.2.16 -r 1.4.2.17 po/sv.po; cvs diff -u -r 1.1.2.9 -r 1.1.2.10 po/uk.po; cvs diff -u -r 1.5.2.34 -r 1.5.2.35 po/zh_CN.po; cvs diff -u -r 1.1.2.8 -r 1.1.2.9 po/zh_TW.po; ) > 3.9.0cvs73.patchset
( cvs diff -u -r 1.53.2.43 -r 1.53.2.44 po/POTFILES.in; cvs diff -u -r 1.21.2.13 -r 1.21.2.14 po/bg.po; cvs diff -u -r 1.1.2.23 -r 1.1.2.24 po/ca.po; cvs diff -u -r 1.9.2.30 -r 1.9.2.31 po/cs.po; cvs diff -u -r 1.58.2.56 -r 1.58.2.57 po/de.po; cvs diff -u -r 1.12.2.22 -r 1.12.2.23 po/en_GB.po; cvs diff -u -r 1.60.2.70 -r 1.60.2.71 po/es.po; cvs diff -u -r 1.1.2.33 -r 1.1.2.34 po/fi.po; cvs diff -u -r 1.42.2.63 -r 1.42.2.64 po/fr.po; cvs diff -u -r 1.5.2.27 -r 1.5.2.28 po/hu.po; cvs diff -u -r 1.1.2.7 -r 1.1.2.8 po/id_ID.po; cvs diff -u -r 1.34.2.34 -r 1.34.2.35 po/it.po; cvs diff -u -r 1.16.2.16 -r 1.16.2.17 po/ja.po; cvs diff -u -r 1.1.2.9 -r 1.1.2.10 po/lt.po; cvs diff -u -r 1.28.2.19 -r 1.28.2.20 po/nl.po; cvs diff -u -r 1.10.2.22 -r 1.10.2.23 po/pl.po; cvs diff -u -r 1.50.2.49 -r 1.50.2.50 po/pt_BR.po; cvs diff -u -r 1.1.2.8 -r 1.1.2.9 po/pt_PT.po; cvs diff -u -r 1.17.2.26 -r 1.17.2.27 po/ru.po; cvs diff -u -r 1.2.2.41 -r 1.2.2.42 po/sk.po; cvs diff -u -r 1.4.2.17 -r 1.4.2.18 po/sv.po; cvs diff -u -r 1.1.2.10 -r 1.1.2.11 po/uk.po; cvs diff -u -r 1.5.2.35 -r 1.5.2.36 po/zh_CN.po; cvs diff -u -r 1.1.2.9 -r 1.1.2.10 po/zh_TW.po; cvs diff -u -r 1.8.2.12 -r 1.8.2.13 src/plugins/Makefile.am; diff -u /dev/null src/plugins/archive/.cvsignore; diff -u /dev/null src/plugins/archive/Makefile.am; diff -u /dev/null src/plugins/archive/archiver.c; diff -u /dev/null src/plugins/archive/archiver.h; diff -u /dev/null src/plugins/archive/archiver_gtk.c; diff -u /dev/null src/plugins/archive/archiver_prefs.c; diff -u /dev/null src/plugins/archive/archiver_prefs.h; diff -u /dev/null src/plugins/archive/libarchive_archive.c; diff -u /dev/null src/plugins/archive/libarchive_archive.h; cvs diff -u -r -1.1.2.1 -r -1.1.2.2 src/plugins/archive/placeholder.txt; diff -u /dev/null src/plugins/att_remover/.cvsignore; diff -u /dev/null src/plugins/att_remover/Makefile.am; diff -u /dev/null src/plugins/att_remover/att_remover.c; cvs diff -u -r -1.1.2.1 -r -1.1.2.2 src/plugins/att_remover/placeholder.txt; diff -u /dev/null src/plugins/bsfilter/.cvsignore; diff -u /dev/null src/plugins/bsfilter/Makefile.am; diff -u /dev/null src/plugins/bsfilter/bsfilter.c; diff -u /dev/null src/plugins/bsfilter/bsfilter.h; diff -u /dev/null src/plugins/bsfilter/bsfilter_gtk.c; diff -u /dev/null src/plugins/bsfilter/claws.def; cvs diff -u -r -1.1.2.1 -r -1.1.2.2 src/plugins/bsfilter/placeholder.txt; diff -u /dev/null src/plugins/bsfilter/plugin.def; diff -u /dev/null src/plugins/bsfilter/version.rc; cvs diff -u -r 1.1.2.2 -r 1.1.2.3 src/plugins/vcalendar/Makefile.in; cvs diff -u -r -1.1.2.3 -r -1.1.2.4 src/plugins/vcalendar/libical/libical/icalversion.h; ) > 3.9.0cvs74.patchset
+( cvs diff -u -r 1.8.2.13 -r 1.8.2.14 src/plugins/Makefile.am; cvs diff -u -r 1.1.2.1 -r 1.1.2.2 src/plugins/archive/Makefile.am; diff -u /dev/null src/plugins/clamd/Makefile.am; diff -u /dev/null src/plugins/clamd/clamav_plugin.c; diff -u /dev/null src/plugins/clamd/clamav_plugin.h; diff -u /dev/null src/plugins/clamd/clamav_plugin_gtk.c; cvs diff -u -r -1.1.2.1 -r -1.1.2.2 src/plugins/clamd/placeholder.txt; diff -u /dev/null src/plugins/clamd/libclamd/Makefile.am; diff -u /dev/null src/plugins/clamd/libclamd/clamd-plugin.c; diff -u /dev/null src/plugins/clamd/libclamd/clamd-plugin.h; diff -u /dev/null src/plugins/fetchinfo/Makefile.am; diff -u /dev/null src/plugins/fetchinfo/fetchinfo_plugin.c; diff -u /dev/null src/plugins/fetchinfo/fetchinfo_plugin.h; diff -u /dev/null src/plugins/fetchinfo/fetchinfo_plugin_gtk.c; cvs diff -u -r -1.1.2.1 -r -1.1.2.2 src/plugins/fetchinfo/placeholder.txt; diff -u /dev/null src/plugins/gdata/Makefile.am; diff -u /dev/null src/plugins/gdata/cm_gdata_contacts.c; diff -u /dev/null src/plugins/gdata/cm_gdata_contacts.h; diff -u /dev/null src/plugins/gdata/cm_gdata_prefs.c; diff -u /dev/null src/plugins/gdata/cm_gdata_prefs.h; diff -u /dev/null src/plugins/gdata/gdata_plugin.c; diff -u /dev/null src/plugins/gdata/gdata_plugin.h; cvs diff -u -r -1.1.2.1 -r -1.1.2.2 src/plugins/gdata/placeholder.txt; cvs diff -u -r -1.1.2.1 -r -1.1.2.2 src/plugins/geolocation/placeholder.txt; cvs diff -u -r -1.1.2.1 -r -1.1.2.2 src/plugins/gtkhtml2_viewer/placeholder.txt; diff -u /dev/null src/plugins/mailmbox/Makefile.am; diff -u /dev/null src/plugins/mailmbox/carray.c; diff -u /dev/null src/plugins/mailmbox/carray.h; diff -u /dev/null src/plugins/mailmbox/chash.c; diff -u /dev/null src/plugins/mailmbox/chash.h; diff -u /dev/null src/plugins/mailmbox/clist.c; diff -u /dev/null src/plugins/mailmbox/clist.h; diff -u /dev/null src/plugins/mailmbox/mailimf.c; diff -u /dev/null src/plugins/mailmbox/mailimf.h; diff -u /dev/null src/plugins/mailmbox/mailimf_types.c; diff -u /dev/null src/plugins/mailmbox/mailimf_types.h; diff -u /dev/null src/plugins/mailmbox/mailimf_types_helper.c; diff -u /dev/null src/plugins/mailmbox/mailimf_types_helper.h; diff -u /dev/null src/plugins/mailmbox/mailimf_write.c; diff -u /dev/null src/plugins/mailmbox/mailimf_write.h; diff -u /dev/null src/plugins/mailmbox/maillock.c; diff -u /dev/null src/plugins/mailmbox/maillock.h; diff -u /dev/null src/plugins/mailmbox/mailmbox.c; diff -u /dev/null src/plugins/mailmbox/mailmbox.h; diff -u /dev/null src/plugins/mailmbox/mailmbox_folder.c; diff -u /dev/null src/plugins/mailmbox/mailmbox_folder.h; diff -u /dev/null src/plugins/mailmbox/mailmbox_gtk.deps; diff -u /dev/null src/plugins/mailmbox/mailmbox_parse.c; diff -u /dev/null src/plugins/mailmbox/mailmbox_parse.h; diff -u /dev/null src/plugins/mailmbox/mailmbox_types.c; diff -u /dev/null src/plugins/mailmbox/mailmbox_types.h; diff -u /dev/null src/plugins/mailmbox/mmapstring.c; diff -u /dev/null src/plugins/mailmbox/mmapstring.h; cvs diff -u -r -1.1.2.1 -r -1.1.2.2 src/plugins/mailmbox/placeholder.txt; diff -u /dev/null src/plugins/mailmbox/plugin.c; diff -u /dev/null src/plugins/mailmbox/plugin_gtk.c; diff -u /dev/null src/plugins/mailmbox/plugin_gtk.h; diff -u /dev/null src/plugins/newmail/Makefile.am; diff -u /dev/null src/plugins/newmail/newmail.c; cvs diff -u -r -1.1.2.1 -r -1.1.2.2 src/plugins/newmail/placeholder.txt; diff -u /dev/null src/plugins/notification/Makefile.am; diff -u /dev/null src/plugins/notification/claws.def; diff -u /dev/null src/plugins/notification/notification_banner.c; diff -u /dev/null src/plugins/notification/notification_banner.h; diff -u /dev/null src/plugins/notification/notification_command.c; diff -u /dev/null src/plugins/notification/notification_command.h; diff -u /dev/null src/plugins/notification/notification_core.c; diff -u /dev/null src/plugins/notification/notification_core.h; diff -u /dev/null src/plugins/notification/notification_foldercheck.c; diff -u /dev/null src/plugins/notification/notification_foldercheck.h; diff -u /dev/null src/plugins/notification/notification_hotkeys.c; diff -u /dev/null src/plugins/notification/notification_hotkeys.h; diff -u /dev/null src/plugins/notification/notification_indicator.c; diff -u /dev/null src/plugins/notification/notification_indicator.h; diff -u /dev/null src/plugins/notification/notification_lcdproc.c; diff -u /dev/null src/plugins/notification/notification_lcdproc.h; diff -u /dev/null src/plugins/notification/notification_pixbuf.c; diff -u /dev/null src/plugins/notification/notification_pixbuf.h; diff -u /dev/null src/plugins/notification/notification_plugin.c; diff -u /dev/null src/plugins/notification/notification_plugin.h; diff -u /dev/null src/plugins/notification/notification_popup.c; diff -u /dev/null src/plugins/notification/notification_popup.h; diff -u /dev/null src/plugins/notification/notification_prefs.c; diff -u /dev/null src/plugins/notification/notification_prefs.h; diff -u /dev/null src/plugins/notification/notification_trayicon.c; diff -u /dev/null src/plugins/notification/notification_trayicon.h; cvs diff -u -r -1.1.2.1 -r -1.1.2.2 src/plugins/notification/placeholder.txt; diff -u /dev/null src/plugins/notification/plugin.def; diff -u /dev/null src/plugins/notification/raw_claws_mail_logo_64x64.h; diff -u /dev/null src/plugins/notification/version.rc; diff -u /dev/null src/plugins/pdf_viewer/Makefile.am; diff -u /dev/null src/plugins/pdf_viewer/doc_index.xpm; diff -u /dev/null src/plugins/pdf_viewer/doc_index_close.xpm; diff -u /dev/null src/plugins/pdf_viewer/doc_info.xpm; diff -u /dev/null src/plugins/pdf_viewer/first_arrow.xpm; diff -u /dev/null src/plugins/pdf_viewer/last_arrow.xpm; diff -u /dev/null src/plugins/pdf_viewer/left_arrow.xpm; cvs diff -u -r -1.1.2.1 -r -1.1.2.2 src/plugins/pdf_viewer/placeholder.txt; diff -u /dev/null src/plugins/pdf_viewer/poppler_viewer.c; diff -u /dev/null src/plugins/pdf_viewer/poppler_viewer.h; diff -u /dev/null src/plugins/pdf_viewer/right_arrow.xpm; diff -u /dev/null src/plugins/pdf_viewer/rotate_left.xpm; diff -u /dev/null src/plugins/pdf_viewer/rotate_right.xpm; diff -u /dev/null src/plugins/pdf_viewer/zoom_fit.xpm; diff -u /dev/null src/plugins/pdf_viewer/zoom_in.xpm; diff -u /dev/null src/plugins/pdf_viewer/zoom_out.xpm; diff -u /dev/null src/plugins/pdf_viewer/zoom_width.xpm; diff -u /dev/null src/plugins/perl/Makefile.am; diff -u /dev/null src/plugins/perl/perl_gtk.c; diff -u /dev/null src/plugins/perl/perl_gtk.h; diff -u /dev/null src/plugins/perl/perl_plugin.c; diff -u /dev/null src/plugins/perl/perl_plugin.h; cvs diff -u -r -1.1.2.1 -r -1.1.2.2 src/plugins/perl/placeholder.txt; diff -u /dev/null src/plugins/python/Makefile.am; diff -u /dev/null src/plugins/python/clawsmailmodule.c; diff -u /dev/null src/plugins/python/clawsmailmodule.h; diff -u /dev/null src/plugins/python/composewindowtype.c; diff -u /dev/null src/plugins/python/composewindowtype.h; diff -u /dev/null src/plugins/python/foldertype.c; diff -u /dev/null src/plugins/python/foldertype.h; diff -u /dev/null src/plugins/python/messageinfotype.c; diff -u /dev/null src/plugins/python/messageinfotype.h; diff -u /dev/null src/plugins/python/nodetype.c; diff -u /dev/null src/plugins/python/nodetype.h; cvs diff -u -r -1.1.2.1 -r -1.1.2.2 src/plugins/python/placeholder.txt; diff -u /dev/null src/plugins/python/python-hooks.c; diff -u /dev/null src/plugins/python/python-hooks.h; diff -u /dev/null src/plugins/python/python-shell.c; diff -u /dev/null src/plugins/python/python-shell.h; diff -u /dev/null src/plugins/python/python_plugin.c; cvs diff -u -r 1.1.2.3 -r 1.1.2.4 src/plugins/vcalendar/Makefile.in; ) > 3.9.0cvs75.patchset
diff --git a/configure.ac b/configure.ac
@@ -12,7 +12,7 @@ MINOR_VERSION=9
MICRO_VERSION=0
INTERFACE_AGE=0
BINARY_AGE=0
-EXTRA_VERSION=74
+EXTRA_VERSION=75
EXTRA_RELEASE=
EXTRA_GTK2_VERSION=
@@ -863,6 +863,9 @@ AC_ARG_ENABLE(acpi_notifier-plugin,
[ac_cv_enable_acpi_notifier_plugin=$enableval], [ac_cv_enable_acpi_notifier_plugin=yes])
if test x"$ac_cv_enable_acpi_notifier_plugin" = xyes; then
PLUGINS="acpi_notifier $PLUGINS"
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
fi
AM_CONDITIONAL(BUILD_ACPI_NOTIFIER_PLUGIN, test x"$ac_cv_enable_acpi_notifier_plugin" = xyes)
@@ -872,6 +875,9 @@ AC_ARG_ENABLE(address_keeper-plugin,
[ac_cv_enable_address_keeper_plugin=$enableval], [ac_cv_enable_address_keeper_plugin=yes])
if test x"$ac_cv_enable_address_keeper_plugin" = xyes; then
PLUGINS="address_keeper $PLUGINS"
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
fi
AM_CONDITIONAL(BUILD_ADDRESS_KEEPER_PLUGIN, test x"$ac_cv_enable_address_keeper_plugin" = xyes)
@@ -887,6 +893,9 @@ if test x"$ac_cv_enable_archive_plugin" = xyes; then
exit 1
)
PLUGINS="archive $PLUGINS"
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
fi
AM_CONDITIONAL(BUILD_ARCHIVE_PLUGIN, test x"$ac_cv_enable_archive_plugin" = xyes)
@@ -896,6 +905,9 @@ AC_ARG_ENABLE(att_remover-plugin,
[ac_cv_enable_att_remover_plugin=$enableval], [ac_cv_enable_att_remover_plugin=yes])
if test x"$ac_cv_enable_att_remover_plugin" = xyes; then
PLUGINS="att_remover $PLUGINS"
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
fi
AM_CONDITIONAL(BUILD_ATT_REMOVER_PLUGIN, test x"$ac_cv_enable_att_remover_plugin" = xyes)
@@ -905,6 +917,9 @@ AC_ARG_ENABLE(attachwarner-plugin,
[ac_cv_enable_attachwarner_plugin=$enableval], [ac_cv_enable_attachwarner_plugin=yes])
if test x"$ac_cv_enable_attachwarner_plugin" = xyes; then
PLUGINS="attachwarner $PLUGINS"
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
fi
AM_CONDITIONAL(BUILD_ATTACHWARNER_PLUGIN, test x"$ac_cv_enable_attachwarner_plugin" = xyes)
@@ -929,15 +944,34 @@ AC_ARG_ENABLE(bsfilter-plugin,
[ac_cv_enable_bsfilter_plugin=$enableval], [ac_cv_enable_bsfilter_plugin=yes])
if test x"$ac_cv_enable_bsfilter_plugin" = xyes; then
PLUGINS="bsfilter $PLUGINS"
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
fi
AM_CONDITIONAL(BUILD_BSFILTER_PLUGIN, test x"$ac_cv_enable_bsfilter_plugin" = xyes)
+dnl --- clamd ---
+AC_MSG_CHECKING([whether to build clamd plugin])
+AC_ARG_ENABLE(clamd-plugin,
+ [ --disable-clamd-plugin do not build clamd plugin],
+ [ac_cv_enable_clamd_plugin=$enableval], [ac_cv_enable_clamd_plugin=yes])
+if test x"$ac_cv_enable_clamd_plugin" = xyes; then
+ PLUGINS="clamd $PLUGINS"
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
+fi
+AM_CONDITIONAL(BUILD_CLAMD_PLUGIN, test x"$ac_cv_enable_clamd_plugin" = xyes)
+
dnl --- Demo ---
AC_ARG_ENABLE(demo-plugin,
[ --enable-demo-plugin build demo plugin],
[ac_cv_enable_demo_plugin=$enableval], [ac_cv_enable_demo_plugin=no])
if test x"$ac_cv_enable_demo_plugin" = xyes; then
PLUGINS="demo $PLUGINS"
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
fi
AM_CONDITIONAL(BUILD_DEMO_PLUGIN, test x"$ac_cv_enable_demo_plugin" = xyes)
@@ -993,6 +1027,209 @@ if test x"$ac_cv_enable_fancy_plugin" = xyes; then
fi
AM_CONDITIONAL(BUILD_FANCY_PLUGIN, test x"$ac_cv_enable_fancy_plugin" = xyes)
+dnl --- fetchinfo ---
+AC_MSG_CHECKING([whether to build fetchinfo plugin])
+AC_ARG_ENABLE(fetchinfo-plugin,
+ [ --disable-fetchinfo-plugin do not build fetchinfo plugin],
+ [ac_cv_enable_fetchinfo_plugin=$enableval], [ac_cv_enable_fetchinfo_plugin=yes])
+if test x"$ac_cv_enable_fetchinfo_plugin" = xyes; then
+ PLUGINS="fetchinfo $PLUGINS"
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
+fi
+AM_CONDITIONAL(BUILD_FETCHINFO_PLUGIN, test x"$ac_cv_enable_fetchinfo_plugin" = xyes)
+
+dnl --- gdata ---
+AC_MSG_CHECKING([whether to build gdata plugin])
+AC_ARG_ENABLE(gdata-plugin,
+ [ --disable-gdata-plugin do not build gdata plugin],
+ [ac_cv_enable_gdata_plugin=$enableval], [ac_cv_enable_gdata_plugin=yes])
+if test x"$ac_cv_enable_gdata_plugin" = xyes; then
+ PKG_CHECK_MODULES(GDATA, libgdata >= 0.9.1, [ac_cv_enable_gdata_plugin=yes], [ac_cv_enable_gdata_plugin=no])
+ if test x"$ac_cv_enable_gdata_plugin" = xyes; then
+ AC_DEFINE(HAVE_GDATA_VERSION_0_9_1, 1, [at least version 0.9.1 of libgdata is available])
+ AC_DEFINE(HAVE_GDATA_VERSION_0_9, 1, [at least version 0.9.0 of libgdata is available])
+ else
+ PKG_CHECK_MODULES(GDATA, libgdata >= 0.6.4, [ac_cv_enable_gdata_plugin=yes], [ac_cv_enable_gdata_plugin=no])
+ fi
+ if test x"$ac_cv_enable_gdata_plugin" = xyes; then
+ AC_SUBST(GDATA_CFLAGS)
+ AC_SUBST(GDATA_LIBS)
+ AC_DEFINE(CM_GDATA_CLIENT_ID, ["Claws Mail GData plugin"], [client id])
+
+ PLUGINS="gdata $PLUGINS"
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_ERROR(libgdata is not available)
+ fi
+else
+ AC_MSG_RESULT(no)
+fi
+AM_CONDITIONAL(BUILD_GDATA_PLUGIN, test x"$ac_cv_enable_gdata_plugin" = xyes)
+
+dnl --- mailmbox ---
+AC_MSG_CHECKING([whether to build mailmbox plugin])
+AC_ARG_ENABLE(mailmbox-plugin,
+ [ --disable-mailmbox-plugin do not build mailmbox plugin],
+ [ac_cv_enable_mailmbox_plugin=$enableval], [ac_cv_enable_mailmbox_plugin=yes])
+if test x"$ac_cv_enable_mailmbox_plugin" = xyes; then
+ PLUGINS="mailmbox $PLUGINS"
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
+fi
+AM_CONDITIONAL(BUILD_MAILMBOX_PLUGIN, test x"$ac_cv_enable_mailmbox_plugin" = xyes)
+
+dnl --- newmail ---
+AC_MSG_CHECKING([whether to build newmail plugin])
+AC_ARG_ENABLE(newmail-plugin,
+ [ --disable-newmail-plugin do not build newmail plugin],
+ [ac_cv_enable_newmail_plugin=$enableval], [ac_cv_enable_newmail_plugin=yes])
+if test x"$ac_cv_enable_newmail_plugin" = xyes; then
+ PLUGINS="newmail $PLUGINS"
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
+fi
+AM_CONDITIONAL(BUILD_NEWMAIL_PLUGIN, test x"$ac_cv_enable_newmail_plugin" = xyes)
+
+dnl --- notification - disabled - too many autofoo annoyances for today ---
+AC_MSG_CHECKING([whether to build notification plugin])
+AC_ARG_ENABLE(notification-plugin,
+ [ --disable-notification-plugin do not build notification plugin],
+ [ac_cv_enable_notification_plugin=$enableval], [ac_cv_enable_notification_plugin=no])
+if test x"$ac_cv_enable_notification_plugin" = xyes; then
+ PLUGINS="notification $PLUGINS"
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
+fi
+AM_CONDITIONAL(BUILD_NOTIFICATION_PLUGIN, test x"$ac_cv_enable_notification_plugin" = xyes)
+
+dnl --- pdf_viewer ---
+AC_MSG_CHECKING([whether to build pdf_viewer plugin])
+AC_ARG_ENABLE(pdf_viewer-plugin,
+ [ --disable-pdf_viewer-plugin do not build pdf_viewer plugin],
+ [ac_cv_enable_pdf_viewer_plugin=$enableval], [ac_cv_enable_pdf_viewer_plugin=yes])
+if test x"$ac_cv_enable_pdf_viewer_plugin" = xyes; then
+ PKG_CHECK_MODULES(POPPLER, poppler-glib >= 0.4.2, ,
+ AC_MSG_ERROR([Can't find Poppler >= 0.4.2 Glib wrapper]))
+ AC_SUBST(POPPLER_LIBS)
+ AC_SUBST(POPPLER_CFLAGS)
+
+ OLD_CFLAGS=$CFLAGS
+ CFLAGS="$POPPLER_CFLAGS $GTK_CFLAGS $GLIB_CFLAGS"
+ AC_CHECK_DECL(POPPLER_DEST_NAMED,
+ [AC_DEFINE([HAVE_POPPLER_DEST_NAMED], [], [Description])],
+ ,[#include <poppler-action.h>])
+ AC_CHECK_DECL(POPPLER_DEST_XYZ,
+ [AC_DEFINE([HAVE_POPPLER_DEST_XYZ], [], [Description])],
+ ,[#include <poppler-action.h>])
+ CFLAGS=$OLD_CFLAGS
+ PLUGINS="pdf_viewer $PLUGINS"
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
+fi
+AM_CONDITIONAL(BUILD_PDF_VIEWER_PLUGIN, test x"$ac_cv_enable_pdf_viewer_plugin" = xyes)
+
+dnl --- perl ---
+AC_MSG_CHECKING([whether to build perl plugin])
+AC_ARG_ENABLE(perl-plugin,
+ [ --disable-perl-plugin do not build perl plugin],
+ [ac_cv_enable_perl_plugin=$enableval], [ac_cv_enable_perl_plugin=yes])
+if test x"$ac_cv_enable_perl_plugin" = xyes; then
+ AC_PATH_PROG(sedpath, sed, no)
+ if test x$sedpath = xno ; then
+ AC_MSG_ERROR(Test for sed failed.)
+ fi
+
+ dnl Perl
+ AC_PATH_PROG(PERL_PATH, perl, no)
+ if test x$PERL_PATH = xno ; then
+ AC_MSG_ERROR(Test for Perl failed)
+ fi
+ AC_MSG_CHECKING(for perl >= 5.8.0)
+ PERL_VER=`$PERL_PATH -e 'print $] > 5.0079999?"yes":"no"'`
+ if test "$PERL_VER" = "yes"; then
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no)
+ AC_MSG_ERROR(Your Perl version is too old.)
+ fi
+ AC_MSG_CHECKING(for Perl compile flags)
+ PERL_CFLAGS=`$PERL_PATH -MExtUtils::Embed -e ccopts`
+ PERL_CFLAGS=`echo $PERL_CFLAGS | $sedpath 's/-D_FILE_OFFSET_BITS=[[0-9]]*//'`
+ PERL_LDFLAGS=`$PERL_PATH -MExtUtils::Embed -e ldopts |$sedpath 's/-lgdbm//'`
+ PERL_LDFLAGS=`echo $PERL_LDFLAGS |$sedpath 's/-ldb//'`
+ PERL_LDFLAGS=`echo $PERL_LDFLAGS |$sedpath 's/-lndbm//'`
+ PERL_LDFLAGS=`echo $PERL_LDFLAGS |$sedpath 's/-lc//'`
+ AC_MSG_RESULT(ok)
+ AC_SUBST(PERL_CFLAGS)
+ AC_SUBST(PERL_LDFLAGS)
+ PLUGINS="perl $PLUGINS"
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
+fi
+AM_CONDITIONAL(BUILD_PERL_PLUGIN, test x"$ac_cv_enable_perl_plugin" = xyes)
+
+dnl --- python ---
+AC_MSG_CHECKING([whether to build python plugin])
+AC_ARG_ENABLE(python-plugin,
+ [ --disable-python-plugin do not build python plugin],
+ [ac_cv_enable_python_plugin=$enableval], [ac_cv_enable_python_plugin=yes])
+if test x"$ac_cv_enable_python_plugin" = xyes; then
+ AM_PATH_PYTHON([2.5], [
+ AC_PATH_PROG(PYTHON_CONFIG, python$PYTHON_VERSION-config)
+ if test x"$PYTHON_CONFIG" = x"" ; then
+ AC_PATH_PROG(PYTHON_CONFIG, python-config)
+ fi
+ if test x"$PYTHON_CONFIG" != x""; then
+ PYTHON_CFLAGS=`$PYTHON_CONFIG --includes`
+ PYTHON_LIBS=`$PYTHON_CONFIG --libs`
+ PYTHON_PREFIX=`$PYTHON_CONFIG --prefix`
+ else
+ AC_MSG_ERROR(python-config not found. Maybe you need to install development packages for Python.)
+ fi
+
+ # libpython.so
+ PYTHON_SO_FILE="libpython${PYTHON_VERSION}.so"
+ found_libpython_so="no"
+ if test -f "$PYTHON_PREFIX/lib/$PYTHON_SO_FILE"; then
+ found_libpython_so="yes"
+ PYTHON_SHARED_LIB=`python -c "import os,sys; print os.path.basename(os.path.realpath(\"$PYTHON_PREFIX/lib/$PYTHON_SO_FILE\"))"`
+ fi
+ if test -f "$PYTHON_PREFIX/lib64/$PYTHON_SO_FILE"; then
+ found_libpython_so="yes"
+ PYTHON_SHARED_LIB=`python -c "import os,sys; print os.path.basename(os.path.realpath(\"$PYTHON_PREFIX/lib64/$PYTHON_SO_FILE\"))"`
+ fi
+ if test x"$found_libpython_so" != x"yes"; then
+ AC_MSG_ERROR(Could not find Python shared libary: $PYTHON_SO_FILE does not exist. Maybe you need to install development packages for Python.)
+ fi
+
+ # PyGTK
+ PKG_CHECK_MODULES(PYGTK,
+ [pygtk-2.0 >= 2.10.3],
+ [
+ AC_DEFINE(ENABLE_PYTHON, [1], [Enable Python support])
+ ])
+ ])
+
+ AC_SUBST(PYTHON_SHARED_LIB)
+ AC_SUBST(PYTHON_CFLAGS)
+ AC_SUBST(PYTHON_LIBS)
+ AC_SUBST(PYGTK_CFLAGS)
+ AC_SUBST(PYGTK_LIBS)
+
+ PLUGINS="python $PLUGINS"
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
+fi
+AM_CONDITIONAL(BUILD_PYTHON_PLUGIN, test x"$ac_cv_enable_python_plugin" = xyes)
+
dnl --- PGP/CORE ---
AC_MSG_CHECKING([whether to build PGP/CORE plugin])
AC_ARG_ENABLE(pgpcore-plugin,
@@ -1214,9 +1451,18 @@ src/plugins/att_remover/Makefile
src/plugins/attachwarner/Makefile
src/plugins/bogofilter/Makefile
src/plugins/bsfilter/Makefile
+src/plugins/clamd/Makefile
+src/plugins/clamd/libclamd/Makefile
src/plugins/demo/Makefile
src/plugins/dillo_viewer/Makefile
src/plugins/fancy/Makefile
+src/plugins/fetchinfo/Makefile
+src/plugins/gdata/Makefile
+src/plugins/mailmbox/Makefile
+src/plugins/newmail/Makefile
+src/plugins/pdf_viewer/Makefile
+src/plugins/perl/Makefile
+src/plugins/python/Makefile
src/plugins/pgpcore/Makefile
src/plugins/pgpmime/Makefile
src/plugins/pgpinline/Makefile
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
@@ -26,6 +26,10 @@ if BUILD_BSFILTER_PLUGIN
bsfilter_dir = bsfilter
endif
+if BUILD_CLAMD_PLUGIN
+clamd_dir = clamd
+endif
+
if BUILD_DEMO_PLUGIN
demo_dir = demo
endif
@@ -38,6 +42,38 @@ if BUILD_FANCY_PLUGIN
fancy_dir = fancy
endif
+if BUILD_FETCHINFO_PLUGIN
+fetchinfo_dir = fetchinfo
+endif
+
+if BUILD_GDATA_PLUGIN
+gdata_dir = gdata
+endif
+
+if BUILD_MAILMBOX_PLUGIN
+mailmbox_dir = mailmbox
+endif
+
+if BUILD_NEWMAIL_PLUGIN
+newmail_dir = newmail
+endif
+
+if BUILD_NOTIFICATION_PLUGIN
+notification_dir = notification
+endif
+
+if BUILD_PDF_VIEWER_PLUGIN
+pdf_viewer_dir = pdf_viewer
+endif
+
+if BUILD_PERL_PLUGIN
+perl_dir = perl
+endif
+
+if BUILD_PYTHON_PLUGIN
+python_dir = python
+endif
+
if BUILD_PGPCORE_PLUGIN
pgpcore_dir = pgpcore
if BUILD_PGPMIME_PLUGIN
@@ -75,9 +111,18 @@ SUBDIRS = \
$(attachwarner_dir) \
$(bogofilter_dir) \
$(bsfilter_dir) \
+ $(clamd_dir) \
$(demo_dir) \
$(dillo_viewer_dir) \
$(fancy_dir) \
+ $(fetchinfo_dir) \
+ $(gdata_dir) \
+ $(mailmbox_dir) \
+ $(newmail_dir) \
+ $(notification_dir) \
+ $(pdf_viewer_dir) \
+ $(perl_dir) \
+ $(python_dir) \
$(pgpcore_dir) \
$(pgpmime_dir) \
$(pgpinline_dir) \
diff --git a/src/plugins/archive/Makefile.am b/src/plugins/archive/Makefile.am
@@ -1,8 +1,5 @@
plugindir = $(pkglibdir)/plugins
-INCLUDES = @GLIB_CFLAGS@ \
- -I$(top_srcdir) -I$(top_builddir)
-
plugin_LTLIBRARIES = archive.la
archive_la_SOURCES = \
diff --git a/src/plugins/clamd/Makefile.am b/src/plugins/clamd/Makefile.am
@@ -0,0 +1,30 @@
+SUBDIRS = libclamd
+plugindir = $(pkglibdir)/plugins
+
+plugin_LTLIBRARIES = clamd.la
+
+clamd_la_SOURCES = \
+ clamav_plugin.c \
+ clamav_plugin_gtk.c \
+ clamav_plugin.h
+
+clamd_la_LDFLAGS = \
+ -avoid-version -module \
+ $(GTK_LIBS) \
+ -L$(top_builddir)/src/plugins/clamd/libclamd
+
+clamd_la_LIBADD = \
+ -lclamd-plugin
+
+INCLUDES = \
+ -I$(top_srcdir)/src/plugins/clamd/libclamd \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/src/common \
+ -I$(top_srcdir)/src/gtk
+
+AM_CPPFLAGS = \
+ -Wall \
+ $(CLAWS_MAIL_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(GTK_CFLAGS) \
+ -DLOCALEDIR=\""$(localedir)"\"
diff --git a/src/plugins/clamd/clamav_plugin.c b/src/plugins/clamd/clamav_plugin.c
@@ -0,0 +1,344 @@
+/*
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2003-2010 Michael Rasmussen and the Claws Mail Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+# include "claws-features.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "defs.h"
+
+#include "common/claws.h"
+#include "common/version.h"
+#include "plugin.h"
+#include "utils.h"
+#include "hooks.h"
+#include "inc.h"
+#include "mimeview.h"
+#include "folder.h"
+#include "prefs.h"
+#include "prefs_gtk.h"
+#include "alertpanel.h"
+
+#include "clamav_plugin.h"
+#include "clamd-plugin.h"
+
+#define PLUGIN_NAME (_("Clam AntiVirus"))
+
+static guint hook_id;
+static MessageCallback message_callback;
+
+static ClamAvConfig config;
+
+static PrefParam param[] = {
+ {"clamav_enable", "FALSE", &config.clamav_enable, P_BOOL,
+ NULL, NULL, NULL},
+/* {"clamav_enable_arc", "FALSE", &config.clamav_enable_arc, P_BOOL,
+ NULL, NULL, NULL},*/
+ {"clamav_max_size", "1", &config.clamav_max_size, P_USHORT,
+ NULL, NULL, NULL},
+ {"clamav_recv_infected", "TRUE", &config.clamav_recv_infected, P_BOOL,
+ NULL, NULL, NULL},
+ {"clamav_save_folder", NULL, &config.clamav_save_folder, P_STRING,
+ NULL, NULL, NULL},
+ {"clamad_config_type", "TRUE", &config.clamd_config_type, P_BOOL,
+ NULL, NULL, NULL},
+ {"clamd_config_folder", NULL, &config.clamd_config_folder, P_STRING,
+ NULL, NULL, NULL},
+ {"clamd_host", NULL, &config.clamd_host, P_STRING,
+ NULL, NULL, NULL},
+ {"clamd_port", NULL, &config.clamd_port, P_INT,
+ NULL, NULL, NULL},
+
+ {NULL, NULL, NULL, P_OTHER, NULL, NULL, NULL}
+};
+
+struct clamd_result {
+ Clamd_Stat status;
+};
+
+static gboolean scan_func(GNode *node, gpointer data)
+{
+ struct clamd_result *result = (struct clamd_result *) data;
+ MimeInfo *mimeinfo = (MimeInfo *) node->data;
+ gchar *outfile;
+ response buf;
+ int max;
+ struct stat info;
+
+ outfile = procmime_get_tmp_file_name(mimeinfo);
+ if (procmime_get_part(outfile, mimeinfo) < 0)
+ g_warning("Can't get the part of multipart message.");
+ else {
+ max = config.clamav_max_size * 1048576; /* maximum file size */
+ if (stat(outfile, &info) == -1)
+ g_warning("Can't determine file size");
+ else {
+ if (info.st_size <= max) {
+ debug_print("Scanning %s\n", outfile);
+ result->status = clamd_verify_email(outfile, &buf);
+ debug_print("status: %d\n", result->status);
+ switch (result->status) {
+ case NO_SOCKET:
+ g_warning("[scanning] No socket information");
+ alertpanel_error(_("Scanning\nNo socket information.\nAntivirus disabled."));
+ break;
+ case NO_CONNECTION:
+ g_warning("[scanning] Clamd does not respond to ping");
+ alertpanel_warning(_("Scanning\nClamd does not respond to ping.\nIs clamd running?"));
+ break;
+ case VIRUS:
+ g_warning("Detected %s virus.\n", clamd_get_virus_name(buf.msg));
+ alertpanel_warning(_("Detected %s virus."), clamd_get_virus_name(buf.msg));
+ break;
+ case SCAN_ERROR:
+ debug_print("Error: %s\n", buf.msg);
+ alertpanel_error(_("Scanning error:\n%s"), buf.msg);
+ break;
+ case OK:
+ debug_print("No virus detected.\n");
+ break;
+ }
+ }
+ else {
+ debug_print("File: %s. Size (%d) greater than limit (%d)\n",
+ outfile, (int) info.st_size, max);
+ }
+ }
+ g_unlink(outfile);
+ }
+
+ return (result->status == OK) ? FALSE : TRUE;
+}
+
+static gboolean mail_filtering_hook(gpointer source, gpointer data)
+{
+ MailFilteringData *mail_filtering_data = (MailFilteringData *) source;
+ MsgInfo *msginfo = mail_filtering_data->msginfo;
+ MimeInfo *mimeinfo;
+
+ struct clamd_result result;
+
+ if (!config.clamav_enable)
+ return FALSE;
+
+ mimeinfo = procmime_scan_message(msginfo);
+ if (!mimeinfo) return FALSE;
+
+ debug_print("Scanning message %d for viruses\n", msginfo->msgnum);
+ if (message_callback != NULL)
+ message_callback(_("ClamAV: scanning message..."));
+
+ debug_print("status: %d\n", result.status);
+ g_node_traverse(mimeinfo->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, scan_func, &result);
+
+ if (result.status == VIRUS) {
+ if (config.clamav_recv_infected) {
+ FolderItem *clamav_save_folder;
+
+ if ((!config.clamav_save_folder) ||
+ (config.clamav_save_folder[0] == '\0') ||
+ ((clamav_save_folder = folder_find_item_from_identifier(config.clamav_save_folder)) == NULL))
+ clamav_save_folder = folder_get_default_trash();
+
+ procmsg_msginfo_unset_flags(msginfo, ~0, 0);
+ msginfo->filter_op = IS_MOVE;
+ msginfo->to_filter_folder = clamav_save_folder;
+ } else {
+ folder_item_remove_msg(msginfo->folder, msginfo->msgnum);
+ }
+ }
+
+ procmime_mimeinfo_free_all(mimeinfo);
+
+ return (result.status == OK) ? FALSE : TRUE;
+}
+
+Clamd_Stat clamd_prepare(void) {
+ debug_print("Creating socket\n");
+ if (!config.clamd_config_type || (config.clamd_host != NULL && config.clamd_port > 0)) {
+ if (config.clamd_host == NULL || config.clamd_port < 1) {
+ /* error */
+ return NO_SOCKET;
+ }
+ /* Manual configuration has highest priority */
+ debug_print("Using user input: %s:%d\n",
+ config.clamd_host, config.clamd_port);
+ clamd_create_config_manual(config.clamd_host, config.clamd_port);
+ }
+ else if (config.clamd_config_type || config.clamd_config_folder != NULL) {
+ if (config.clamd_config_folder == NULL) {
+ /* error */
+ return NO_SOCKET;
+ }
+ debug_print("Using clamd.conf: %s\n", config.clamd_config_folder);
+ clamd_create_config_automatic(config.clamd_config_folder);
+ }
+ else {
+ /* Fall back. Try enable anyway */
+ if (! clamd_find_socket())
+ return NO_SOCKET;
+ }
+
+ return clamd_init(NULL);
+}
+
+ClamAvConfig *clamav_get_config(void)
+{
+ return &config;
+}
+
+void clamav_save_config(void)
+{
+ PrefFile *pfile;
+ gchar *rcpath;
+
+ debug_print("Saving Clamd Page\n");
+
+ rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
+ pfile = prefs_write_open(rcpath);
+ g_free(rcpath);
+ if (!pfile || (prefs_set_block_label(pfile, "ClamAV") < 0))
+ return;
+
+ if (prefs_write_param(param, pfile->fp) < 0) {
+ g_warning("failed to write Clamd configuration to file\n");
+ prefs_file_close_revert(pfile);
+ return;
+ }
+ if (fprintf(pfile->fp, "\n") < 0) {
+ FILE_OP_ERROR(rcpath, "fprintf");
+ prefs_file_close_revert(pfile);
+ } else
+ prefs_file_close(pfile);
+}
+
+void clamav_set_message_callback(MessageCallback callback)
+{
+ message_callback = callback;
+}
+
+gint plugin_init(gchar **error)
+{
+ gchar *rcpath;
+
+ if (!check_plugin_version(MAKE_NUMERIC_VERSION(2,9,2,72),
+ VERSION_NUMERIC, PLUGIN_NAME, error))
+ return -1;
+
+ hook_id = hooks_register_hook(MAIL_FILTERING_HOOKLIST, mail_filtering_hook, NULL);
+ if (hook_id == -1) {
+ *error = g_strdup(_("Failed to register mail filtering hook"));
+ return -1;
+ }
+
+ prefs_set_default(param);
+ rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
+ prefs_read_config(param, "ClamAV", rcpath, NULL);
+ g_free(rcpath);
+
+ clamav_gtk_init();
+
+ if (config.clamav_enable) {
+ debug_print("Creating socket\n");
+ Clamd_Stat status = clamd_prepare();
+ switch (status) {
+ case NO_SOCKET:
+ g_warning("[init] No socket information");
+ alertpanel_error(_("Init\nNo socket information.\nAntivirus disabled."));
+ break;
+ case NO_CONNECTION:
+ g_warning("[init] Clamd does not respond to ping");
+ alertpanel_warning(_("Init\nClamd does not respond to ping.\nIs clamd running?"));
+ break;
+ default:
+ break;
+ }
+ }
+
+ debug_print("Clamd plugin loaded\n");
+
+ return 0;
+
+}
+
+gboolean plugin_done(void)
+{
+ hooks_unregister_hook(MAIL_FILTERING_HOOKLIST, hook_id);
+ g_free(config.clamav_save_folder);
+ clamav_gtk_done();
+ clamd_free();
+
+ debug_print("Clamd plugin unloaded\n");
+ return TRUE;
+}
+
+const gchar *plugin_name(void)
+{
+ return PLUGIN_NAME;
+}
+
+const gchar *plugin_desc(void)
+{
+ return _("This plugin uses Clam AntiVirus to scan all messages that are "
+ "received from an IMAP, LOCAL or POP account.\n"
+ "\n"
+ "When a message attachment is found to contain a virus it can be "
+ "deleted or saved in a specially designated folder.\n"
+ "\n"
+ "Because this plugin communicates with clamd via a\n"
+ "socket then there are some minimum requirements to\n"
+ "the permissions for your home folder and the\n"
+ ".claws-mail folder provided the clamav-daemon is\n"
+ "configured to communicate via a unix socket. All\n"
+ "users at least need to be given execute permissions\n"
+ "on these folders.\n"
+ "\n"
+ "To avoid changing permissions you could configure\n"
+ "the clamav-daemon to communicate via a TCP socket\n"
+ "and choose manual configuration for clamd.\n"
+ "\n"
+ "Options can be found in /Configuration/Preferences/Plugins/Clam AntiVirus");
+}
+
+const gchar *plugin_type(void)
+{
+ return "GTK2";
+}
+
+const gchar *plugin_licence(void)
+{
+ return "GPL3+";
+}
+
+const gchar *plugin_version(void)
+{
+ return VERSION;
+}
+
+struct PluginFeature *plugin_provides(void)
+{
+ static struct PluginFeature features[] =
+ { {PLUGIN_FILTERING, N_("Virus detection")},
+ {PLUGIN_NOTHING, NULL}};
+ return features;
+}
diff --git a/src/plugins/clamd/clamav_plugin.h b/src/plugins/clamd/clamav_plugin.h
@@ -0,0 +1,50 @@
+/*
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2003-2010 Michael Rasmussen and the Claws Mail Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef CLAMAV_PLUGIN_H
+#define CLAMAV_PLUGIN_H 1
+
+#include <glib.h>
+#include "clamd-plugin.h"
+
+typedef struct _ClamAvConfig ClamAvConfig;
+
+typedef void (*MessageCallback) (gchar *);
+
+struct _ClamAvConfig
+{
+ gboolean clamav_enable;
+/* gboolean clamav_enable_arc;*/
+ guint clamav_max_size;
+ gboolean clamav_recv_infected;
+ gchar *clamav_save_folder;
+ gboolean clamd_config_type;
+ gchar *clamd_host;
+ int clamd_port;
+ gchar *clamd_config_folder;
+};
+
+ClamAvConfig *clamav_get_config (void);
+void clamav_save_config (void);
+void clamav_set_message_callback (MessageCallback callback);
+Clamd_Stat clamd_prepare(void);
+gint clamav_gtk_init(void);
+void clamav_gtk_done(void);
+
+#endif
diff --git a/src/plugins/clamd/clamav_plugin_gtk.c b/src/plugins/clamd/clamav_plugin_gtk.c
@@ -0,0 +1,569 @@
+/*
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 2003-2010 the Claws Mail Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+# include "claws-features.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "defs.h"
+
+#include <gtk/gtk.h>
+#include <gtk/gtkutils.h>
+
+#include "common/claws.h"
+#include "common/version.h"
+#include "plugin.h"
+#include "utils.h"
+#include "prefs.h"
+#include "folder.h"
+#include "prefs_gtk.h"
+#include "foldersel.h"
+#include "clamav_plugin.h"
+#include "statusbar.h"
+#include "alertpanel.h"
+#include "clamd-plugin.h"
+
+struct ClamAvPage
+{
+ PrefsPage page;
+
+ GtkWidget *enable_clamav;
+/* GtkWidget *enable_arc;*/
+ GtkWidget *max_size;
+ GtkWidget *recv_infected;
+ GtkWidget *save_folder;
+ GtkWidget *config_type;
+ GtkWidget *config_folder;
+ GtkWidget *config_host;
+ GtkWidget *config_port;
+};
+
+static GtkWidget *hbox_auto1, *hbox_auto2, *hbox_manual1, *hbox_manual2;
+
+static void foldersel_cb(GtkWidget *widget, gpointer data)
+{
+ struct ClamAvPage *page = (struct ClamAvPage *) data;
+ FolderItem *item;
+ gchar *item_id;
+ gint newpos = 0;
+
+ item = foldersel_folder_sel(NULL, FOLDER_SEL_MOVE, NULL, FALSE);
+ if (item && (item_id = folder_item_get_identifier(item)) != NULL) {
+ gtk_editable_delete_text(GTK_EDITABLE(page->save_folder), 0, -1);
+ gtk_editable_insert_text(GTK_EDITABLE(page->save_folder), item_id, strlen(item_id), &newpos);
+ g_free(item_id);
+ }
+}
+
+static void clamd_folder_cb(GtkWidget *widget, gpointer data)
+{
+ GtkWidget *dialog;
+ gchar* file;
+ gint newpos = 0;
+ struct ClamAvPage *page = (struct ClamAvPage *) data;
+
+ dialog = gtk_file_chooser_dialog_new(
+ "Select file with clamd configuration [clamd.conf]",
+ NULL,
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_APPLY, GTK_RESPONSE_APPLY,
+ NULL);
+ gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), "/etc");
+ if (gtk_dialog_run (GTK_DIALOG(dialog)) == GTK_RESPONSE_APPLY) {
+ file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+ debug_print("New clamd.conf: %s\n", file);
+ if (file) {
+ gtk_editable_delete_text(GTK_EDITABLE(page->config_folder), 0, -1);
+ gtk_editable_insert_text(GTK_EDITABLE(page->config_folder), file, strlen(file), &newpos);
+ g_free(file);
+ }
+ }
+ gtk_widget_destroy(dialog);
+}
+
+static void check_permission(gchar* folder) {
+ struct stat info;
+
+ g_stat(folder, &info);
+ mode_t perm = info.st_mode & ~(S_IFMT);
+ debug_print("%s: Old file permission: %05o\n", folder, perm);
+ if ((perm & S_IXOTH) != S_IXOTH) {
+ perm = perm | S_IXOTH;
+ g_chmod(folder, perm);
+ }
+ debug_print("%s: New file permission: %05o\n", folder, perm);
+}
+
+static void folder_permission_cb(GtkWidget *widget, gpointer data) {
+ static gchar* folders[] = {
+ ".claws-mail",
+ ".claws-mail/mimetmp",
+ ".claws-mail/tmp",
+ NULL};
+ const gchar* home = g_get_home_dir();
+ int i;
+
+ check_permission((gchar *) home);
+ for (i = 0; folders[i]; i++) {
+ gchar* file = g_strdup_printf("%s/%s", home, folders[i]);
+ check_permission(file);
+ g_free(file);
+ }
+}
+
+static void clamav_show_config(Config* config) {
+ if (config) {
+ if (config->ConfigType == MANUAL) {
+ gtk_widget_hide(hbox_auto1);
+ gtk_widget_hide(hbox_auto2);
+ gtk_widget_show(hbox_manual1);
+ gtk_widget_show(hbox_manual2);
+ }
+ else {
+ gtk_widget_hide(hbox_manual1);
+ gtk_widget_hide(hbox_manual2);
+ gtk_widget_show(hbox_auto1);
+ gtk_widget_show(hbox_auto2);
+ }
+ }
+}
+
+static void setting_type_cb(GtkWidget *widget, gpointer data) {
+ gboolean state = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+ struct ClamAvPage *page = (struct ClamAvPage *) data;
+ Config* c;
+ gint newpos = 0;
+ gboolean tmp_conf = FALSE;
+
+ if (page && page->page.widget) {
+ /* Reset configuration */
+ debug_print("Resetting configuration\n");
+ gtk_editable_delete_text(GTK_EDITABLE(page->config_folder), 0, -1);
+ gtk_editable_delete_text(GTK_EDITABLE(page->config_host), 0, -1);
+ gtk_editable_delete_text(GTK_EDITABLE(page->config_port), 0, -1);
+ clamav_save_config();
+
+ c = clamd_get_config();
+ if (!c) {
+ c = clamd_config_new();
+ tmp_conf = TRUE;
+ }
+ if (state) {
+ /* Automatic configuration */
+ debug_print("Setting clamd to automatic configuration\n");
+ if (clamd_find_socket()) {
+ if (tmp_conf) {
+ Config* clamd_conf = clamd_get_config();
+ if (clamd_conf->automatic.folder)
+ c->automatic.folder = g_strdup(clamd_conf->automatic.folder);
+ else
+ c->automatic.folder = g_strdup("");
+ }
+ if (c->ConfigType == AUTOMATIC) {
+ gtk_editable_insert_text(GTK_EDITABLE(page->config_folder),
+ c->automatic.folder, strlen(c->automatic.folder), &newpos);
+ clamav_save_config();
+ }
+ }
+ c->ConfigType = AUTOMATIC;
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(page->config_type), TRUE);
+ }
+ else {
+ /* Manual configuration */
+ debug_print("Setting clamd to manual configuration\n");
+ c->ConfigType = MANUAL;
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(page->config_type), FALSE);
+ }
+ clamav_show_config(c);
+ if (tmp_conf)
+ clamd_config_free(c);
+ }
+}
+
+static void clamav_create_widget_func(PrefsPage * _page, GtkWindow *window, gpointer data)
+{
+ struct ClamAvPage *page = (struct ClamAvPage *) _page;
+ ClamAvConfig *config;
+ Config *clamd_config;
+
+ GtkWidget *vbox1, *vbox2;
+ GtkWidget *enable_clamav;
+ GtkWidget *label1;
+/* GtkWidget *enable_arc;*/
+ GtkWidget *label2;
+ GtkObject *max_size_adj;
+ GtkWidget *max_size;
+ GtkWidget *hbox1;
+ GtkWidget *recv_infected;
+ GtkWidget *save_folder;
+ GtkWidget *save_folder_select;
+ GtkWidget *clamd_conf_label;
+ GtkWidget *config_folder;
+ GtkWidget *config_host;
+ GtkWidget *config_port;
+ GtkWidget *config_folder_select;
+ GtkWidget *blank;
+ GtkWidget *permission_label;
+ GtkWidget *permission_select;
+ GtkWidget *host_label;
+ GtkWidget *port_label;
+ GtkWidget *setting_type;
+ GtkTooltips *tooltips;
+
+ tooltips = gtk_tooltips_new();
+ enable_clamav = page->enable_clamav;
+
+ vbox1 = gtk_vbox_new (FALSE, VSPACING);
+ gtk_widget_show (vbox1);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox1), VBOX_BORDER);
+
+ vbox2 = gtk_vbox_new (FALSE, 4);
+ gtk_widget_show (vbox2);
+ gtk_box_pack_start (GTK_BOX (vbox1), vbox2, FALSE, FALSE, 0);
+
+ PACK_CHECK_BUTTON (vbox2, enable_clamav, _("Enable virus scanning"));
+/* PACK_CHECK_BUTTON (vbox2, enable_arc, _("Scan archive contents"));
+
+ SET_TOGGLE_SENSITIVITY (enable_clamav, enable_arc);*/
+
+ hbox1 = gtk_hbox_new (FALSE, 8);
+ gtk_widget_show (hbox1);
+ gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, FALSE, 0);
+/* SET_TOGGLE_SENSITIVITY (enable_arc, hbox1);*/
+
+ label1 = gtk_label_new(_("Maximum attachment size"));
+ gtk_widget_show (label1);
+ gtk_box_pack_start (GTK_BOX (hbox1), label1, FALSE, FALSE, 0);
+ SET_TOGGLE_SENSITIVITY (enable_clamav, label1);
+
+ max_size_adj = gtk_adjustment_new (1, 1, 1024, 1, 10, 0);
+ max_size = gtk_spin_button_new (GTK_ADJUSTMENT (max_size_adj), 1, 0);
+ gtk_widget_show (max_size);
+ gtk_box_pack_start (GTK_BOX (hbox1), max_size, FALSE, FALSE, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (max_size), TRUE);
+ gtk_tooltips_set_tip(tooltips, max_size,
+ _("Message attachments larger than this will not be scanned"),
+ NULL);
+ SET_TOGGLE_SENSITIVITY (enable_clamav, max_size);
+
+ label2 = gtk_label_new(_("MB"));
+ gtk_widget_show (label2);
+ gtk_box_pack_start (GTK_BOX (hbox1), label2, FALSE, FALSE, 0);
+ SET_TOGGLE_SENSITIVITY (enable_clamav, label2);
+
+ hbox1 = gtk_hbox_new (FALSE, 8);
+ gtk_widget_show (hbox1);
+ gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, FALSE, 0);
+
+ recv_infected = gtk_check_button_new_with_label(_("Save infected mail in"));
+ gtk_widget_show (recv_infected);
+ gtk_box_pack_start (GTK_BOX (hbox1), recv_infected, FALSE, FALSE, 0);
+ gtk_tooltips_set_tip(tooltips, recv_infected,
+ _("Save mail that contains viruses"),
+ NULL);
+ SET_TOGGLE_SENSITIVITY (enable_clamav, recv_infected);
+
+ save_folder = gtk_entry_new ();
+ gtk_widget_show (save_folder);
+ gtk_box_pack_start (GTK_BOX (hbox1), save_folder, TRUE, TRUE, 0);
+ gtk_tooltips_set_tip(tooltips, save_folder,
+ _("Folder for storing infected mail. Leave empty to use the default trash folder"),
+ NULL);
+ SET_TOGGLE_SENSITIVITY (enable_clamav, save_folder);
+
+ save_folder_select = gtkut_get_browse_directory_btn(_("_Browse"));
+ gtk_widget_show (save_folder_select);
+ gtk_box_pack_start (GTK_BOX (hbox1), save_folder_select, FALSE, FALSE, 0);
+ gtk_tooltips_set_tip(tooltips, save_folder_select,
+ _("Click this button to select a folder for storing infected mail"),
+ NULL);
+ SET_TOGGLE_SENSITIVITY (enable_clamav, save_folder_select);
+
+ hbox1 = gtk_hbox_new (FALSE, 8);
+ gtk_widget_show (hbox1);
+ gtk_box_pack_start (GTK_BOX (vbox2), hbox1, FALSE, FALSE, 0);
+
+ setting_type = gtk_check_button_new_with_label(_("Automatic configuration"));
+ /*gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(setting_type), TRUE);*/
+ gtk_widget_show (setting_type);
+ gtk_box_pack_start (GTK_BOX (hbox1), setting_type, FALSE, FALSE, 0);
+ gtk_tooltips_set_tip(tooltips, setting_type,
+ _("Should configuration be done automatic or manual"),
+ NULL);
+ SET_TOGGLE_SENSITIVITY (enable_clamav, setting_type);
+
+ hbox_auto1 = gtk_hbox_new (FALSE, 8);
+ gtk_widget_show (hbox_auto1);
+ gtk_box_pack_start (GTK_BOX (vbox2), hbox_auto1, FALSE, FALSE, 0);
+
+ clamd_conf_label = gtk_label_new(_("Where is clamd.conf"));
+ gtk_widget_show (clamd_conf_label);
+ gtk_box_pack_start (GTK_BOX (hbox_auto1), clamd_conf_label, FALSE, FALSE, 0);
+
+ config_folder = gtk_entry_new ();
+ gtk_widget_show (config_folder);
+ gtk_box_pack_start (GTK_BOX (hbox_auto1), config_folder, TRUE, TRUE, 0);
+ gtk_tooltips_set_tip(tooltips, config_folder,
+ _("Full path to clamd.conf. If this field is not empty then the plugin has been able to locate the file automatically"),
+ NULL);
+ SET_TOGGLE_SENSITIVITY (enable_clamav, config_folder);
+
+ config_folder_select = gtkut_get_browse_directory_btn(_("Br_owse"));
+ gtk_widget_show (config_folder_select);
+ gtk_box_pack_start (GTK_BOX (hbox_auto1), config_folder_select, FALSE, FALSE, 0);
+ gtk_tooltips_set_tip(tooltips, config_folder_select,
+ _("Click this button to select full path to clamd.conf"),
+ NULL);
+ SET_TOGGLE_SENSITIVITY (enable_clamav, config_folder_select);
+
+ hbox_auto2 = gtk_hbox_new (FALSE, 8);
+ gtk_widget_show (hbox_auto2);
+ gtk_box_pack_start (GTK_BOX (vbox2), hbox_auto2, FALSE, FALSE, 0);
+
+ permission_label = gtk_label_new(_("Check permission for folders and adjust if necessary"));
+ gtk_widget_show (permission_label);
+ gtk_box_pack_start (GTK_BOX (hbox_auto2), permission_label, FALSE, FALSE, 0);
+
+ blank = gtk_label_new("");
+ gtk_widget_show (blank);
+ gtk_box_pack_start (GTK_BOX (hbox_auto2), blank, TRUE, TRUE, 0);
+
+ permission_select = gtk_button_new_from_stock(GTK_STOCK_FIND_AND_REPLACE);
+ /*gtk_button_new_with_mnemonic(_("_Check Permission"));*/
+ gtk_widget_show (permission_select);
+ gtk_box_pack_start (GTK_BOX (hbox_auto2), permission_select, FALSE, FALSE, 0);
+ gtk_tooltips_set_tip(tooltips, permission_select,
+ _("Click this button to check and adjust folder permissions"),
+ NULL);
+ SET_TOGGLE_SENSITIVITY (enable_clamav, permission_select);
+
+ hbox_manual1 = gtk_hbox_new (FALSE, 8);
+ gtk_widget_show (hbox_manual1);
+ gtk_box_pack_start (GTK_BOX (vbox2), hbox_manual1, FALSE, FALSE, 0);
+
+ host_label = gtk_label_new(_("Remote Host"));
+ gtk_widget_show (host_label);
+ gtk_box_pack_start (GTK_BOX (hbox_manual1), host_label, FALSE, FALSE, 0);
+
+ config_host = gtk_entry_new ();
+ gtk_widget_show (config_host);
+ gtk_box_pack_start (GTK_BOX (hbox_manual1), config_host, FALSE, FALSE, 0);
+ gtk_tooltips_set_tip(tooltips, config_host,
+ _("Hostname or IP for remote host running clamav daemon"),
+ NULL);
+ SET_TOGGLE_SENSITIVITY (enable_clamav, config_host);
+
+ blank = gtk_label_new("");
+ gtk_widget_show (blank);
+ gtk_box_pack_start (GTK_BOX (hbox_manual1), blank, TRUE, TRUE, 0);
+
+ hbox_manual2 = gtk_hbox_new (FALSE, 8);
+ gtk_widget_show (hbox_manual2);
+ gtk_box_pack_start (GTK_BOX (vbox2), hbox_manual2, FALSE, FALSE, 0);
+
+ port_label = gtk_label_new(_("Port"));
+ gtk_widget_show (port_label);
+ gtk_box_pack_start (GTK_BOX (hbox_manual2), port_label, FALSE, FALSE, 0);
+
+ config_port = gtk_entry_new ();
+ gtk_entry_set_width_chars(GTK_ENTRY(config_port), 5);
+ gtk_entry_set_max_length(GTK_ENTRY(config_port), 5);
+ gtk_widget_show (config_port);
+ gtk_box_pack_start (GTK_BOX (hbox_manual2), config_port, FALSE, FALSE, 0);
+ gtk_tooltips_set_tip(tooltips, config_port,
+ _("Port number where clamav daemon is listening"),
+ NULL);
+
+ blank = gtk_label_new("");
+ gtk_widget_show (blank);
+ gtk_box_pack_start (GTK_BOX (hbox_manual2), blank, TRUE, TRUE, 0);
+
+ SET_TOGGLE_SENSITIVITY (enable_clamav, config_port);
+
+ g_signal_connect(G_OBJECT(save_folder_select), "clicked",
+ G_CALLBACK(foldersel_cb), page);
+ g_signal_connect(G_OBJECT(config_folder_select), "clicked",
+ G_CALLBACK(clamd_folder_cb), page);
+ g_signal_connect(G_OBJECT(permission_select), "clicked",
+ G_CALLBACK(folder_permission_cb), page);
+ g_signal_connect(G_OBJECT(setting_type), "clicked",
+ G_CALLBACK(setting_type_cb), page);
+
+ config = clamav_get_config();
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(enable_clamav), config->clamav_enable);
+/* gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(enable_arc), config->clamav_enable_arc);*/
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(max_size), (float) config->clamav_max_size);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(recv_infected), config->clamav_recv_infected);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(setting_type), config->clamd_config_type);
+
+ clamd_config = clamd_get_config();
+
+ if (config->clamav_save_folder != NULL)
+ gtk_entry_set_text(GTK_ENTRY(save_folder), config->clamav_save_folder);
+ if (!config->clamd_config_type) {
+ /*if (config->clamd_host && strlen(config->clamd_host) > 0 && config->clamd_port > 0) {*/
+ gtk_entry_set_text(GTK_ENTRY(config_host), config->clamd_host);
+ gchar* s = int2char(config->clamd_port);
+ gtk_entry_set_text(GTK_ENTRY(config_port), s);
+ g_free(s);
+ /* activate manual checkbox and blind folder */
+ debug_print("Showing manual configuration and hiding automatic configuration\n");
+ if (! clamd_config) {
+ clamd_config = clamd_config_new();
+ clamd_config->ConfigType = MANUAL;
+ clamav_show_config(clamd_config);
+ clamd_config_free(clamd_config);
+ }
+ else
+ clamav_show_config(clamd_config);
+ }
+ else {
+ //else if (config->clamd_config_folder == NULL || strlen(config->clamd_config_folder) == 0) {
+ if (clamd_find_socket()) {
+ Config* c = clamd_get_config();
+ if (c && c->ConfigType == AUTOMATIC) {
+ config->clamd_config_folder = g_strdup(c->automatic.folder);
+ /* deactivate manual checkbox and blind host and port */
+ debug_print("Showing automatic configuration and hiding manual configuration\n");
+ clamav_show_config(c);
+ gint newpos = 0;
+ gtk_editable_delete_text(GTK_EDITABLE(config_folder), 0, -1);
+ gtk_editable_insert_text(GTK_EDITABLE(config_folder),
+ config->clamd_config_folder, strlen(config->clamd_config_folder), &newpos);
+ }
+ else if (c && c->ConfigType == MANUAL) {
+ /* deactivate automatic automatic configuration */
+ debug_print("Showing manual configuration and hiding automatic configuration\n");
+ clamav_show_config(c);
+ }
+ }
+ }
+/* else {
+ gtk_entry_set_text(GTK_ENTRY(config_folder), config->clamd_config_folder);
+ // deactivate manual checkbox and blind host and port
+ debug_print("Showing automatic configuration and hiding manual configuration\n");
+ if (! clamd_config) {
+ clamd_config = clamd_config_new();
+ clamd_config->ConfigType = AUTOMATIC;
+ clamav_show_config(clamd_config);
+ clamd_config_free(clamd_config);
+ }
+ else
+ clamav_show_config(clamd_config);
+ }*/
+
+ page->enable_clamav = enable_clamav;
+/* page->enable_arc = enable_arc;*/
+ page->max_size = max_size;
+ page->recv_infected = recv_infected;
+ page->save_folder = save_folder;
+ page->config_type = setting_type;
+ page->config_folder = config_folder;
+ page->config_host = config_host;
+ page->config_port = config_port;
+ page->page.widget = vbox1;
+
+ clamav_save_config();
+}
+
+static void clamav_destroy_widget_func(PrefsPage *_page)
+{
+ debug_print("Destroying Clamd widget\n");
+}
+
+static void clamav_save_func(PrefsPage *_page)
+{
+ struct ClamAvPage *page = (struct ClamAvPage *) _page;
+ ClamAvConfig *config;
+
+ debug_print("Saving Clamd Page\n");
+
+ config = clamav_get_config();
+
+ config->clamav_enable = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->enable_clamav));
+/* config->clamav_enable_arc = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->enable_arc));*/
+
+ config->clamav_max_size = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(page->max_size));
+ config->clamav_recv_infected = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->recv_infected));
+ g_free(config->clamav_save_folder);
+ config->clamav_save_folder = gtk_editable_get_chars(GTK_EDITABLE(page->save_folder), 0, -1);
+ config->clamd_config_type = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->config_type));
+ g_free(config->clamd_config_folder);
+ config->clamd_config_folder = gtk_editable_get_chars(GTK_EDITABLE(page->config_folder), 0, -1);
+ g_free(config->clamd_host);
+ config->clamd_host = gtk_editable_get_chars(GTK_EDITABLE(page->config_host), 0, -1);
+ config->clamd_port = atoi(gtk_entry_get_text(GTK_ENTRY(page->config_port)));
+
+ if (config->clamav_enable) {
+ Clamd_Stat status = clamd_prepare();
+ switch (status) {
+ case NO_SOCKET:
+ g_warning("[New config] No socket information");
+ alertpanel_error(_("New config\nNo socket information.\nAntivirus disabled."));
+ break;
+ case NO_CONNECTION:
+ g_warning("[New config] Clamd does not respond to ping");
+ alertpanel_warning(_("New config\nClamd does not respond to ping.\nIs clamd running?"));
+ break;
+ default:
+ break;
+ }
+ }
+ clamav_save_config();
+}
+
+static struct ClamAvPage clamav_page;
+
+static void gtk_message_callback(gchar *message)
+{
+ statusbar_print_all("%s", message);
+}
+
+gint clamav_gtk_init(void)
+{
+ static gchar *path[3];
+
+ path[0] = _("Plugins");
+ path[1] = _("Clam AntiVirus");
+ path[2] = NULL;
+
+ clamav_page.page.path = path;
+ clamav_page.page.create_widget = clamav_create_widget_func;
+ clamav_page.page.destroy_widget = clamav_destroy_widget_func;
+ clamav_page.page.save_page = clamav_save_func;
+ clamav_page.page.weight = 35.0;
+
+ prefs_gtk_register_page((PrefsPage *) &clamav_page);
+ clamav_set_message_callback(gtk_message_callback);
+
+ debug_print("Clamd GTK plugin loaded\n");
+ return 0;
+}
+
+void clamav_gtk_done(void)
+{
+ prefs_gtk_unregister_page((PrefsPage *) &clamav_page);
+}
diff --git a/src/plugins/clamd/libclamd/Makefile.am b/src/plugins/clamd/libclamd/Makefile.am
@@ -0,0 +1,20 @@
+INCLUDES = @GLIB_CFLAGS@ \
+ @GTK_CFLAGS@ \
+ -I$(top_srcdir) \
+ -I$(top_builddir) \
+ $(CLAWS_MAIL_CFLAGS) \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/src/common \
+ -I$(top_srcdir)/src/gtk
+
+noinst_LTLIBRARIES = libclamd-plugin.la
+
+libclamd_plugin_la_SOURCES = \
+ clamd-plugin.h \
+ clamd-plugin.c
+
+noinst_HEADERS = clamd-plugin.h
+
+libclamd_plugin_la_LIBADD = \
+ @GLIB_LIBS@ \
+ @GTK_LIBS@
diff --git a/src/plugins/clamd/libclamd/clamd-plugin.c b/src/plugins/clamd/libclamd/clamd-plugin.c
@@ -0,0 +1,689 @@
+/* vim: set textwidth=80 tabstop=4: */
+
+/*
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2008 Michael Rasmussen and the Claws Mail Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "defs.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <gtk/gtkutils.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "common/claws.h"
+#include "common/version.h"
+#include "plugin.h"
+#include "utils.h"
+#include "prefs.h"
+#include "folder.h"
+#include "prefs_gtk.h"
+#include "foldersel.h"
+#include "statusbar.h"
+#include "alertpanel.h"
+#include "clamd-plugin.h"
+
+/* needs to be generic */
+static const gchar* config_dirs[] = {
+ "/etc",
+ "/usr/local/etc",
+ "/etc/clamav",
+ "/usr/local/etc/clamav",
+ NULL };
+
+static const gchar* clamd_tokens[] = {
+ "LocalSocket",
+ "TCPSocket",
+ "TCPAddr",
+ NULL };
+
+static Clamd_Socket* Socket = NULL;
+static int sock;
+static Config* config = NULL;
+
+/**
+ * clamd commands used
+ * prefixing with either z or n is recommended
+ * z <=> null terminated command
+ * n <=> newline terminated command
+ */
+static const gchar ping[] = "nPING\n";
+static const gchar version[] = "nVERSION\n";
+static const gchar scan[] = "nSCAN";
+static const gchar contscan[] = "nCONTSCAN";
+static const gchar instream[10] = "zINSTREAM\0";
+
+void clamd_create_config_automatic(const gchar* path) {
+ FILE* conf;
+ char buf[1024];
+ gchar* key = NULL;
+ gchar* value = NULL;
+
+ /*debug_set_mode(TRUE);*/
+ /*debug_print("%s : %s\n", folder, path);*/
+ if (! path) {
+ g_warning("Missing path");
+ return;
+ }
+ if (config && config->ConfigType == AUTOMATIC &&
+ config->automatic.folder &&
+ strcmp(config->automatic.folder, path) == 0) {
+ debug_print("%s : %s - Identical. No need to read again\n",
+ config->automatic.folder, path);
+ return;
+ }
+ if (config)
+ clamd_config_free(config);
+ config = clamd_config_new();
+
+ config->ConfigType = AUTOMATIC;
+ config->automatic.folder = g_strdup(path);
+ debug_print("Opening %s to parse config file\n", path);
+ conf = fopen(path, "r");
+ if (!conf) {
+ /*g_error("%s: Unable to open", path);*/
+ alertpanel_error(_("%s: Unable to open\nclamd will be disabled"), path);
+ return;
+ }
+ while (fgets(buf, sizeof(buf), conf)) {
+ g_strstrip(buf);
+ if (buf[0] == '#')
+ continue;
+ const gchar** tokens = clamd_tokens;
+ while (*tokens) {
+ const gchar* token = *tokens++;
+ if ((key = g_strstr_len(buf, strlen(buf), token)) != NULL) {
+ gchar* tmp = &(*(key + strlen(token)));
+ tmp = g_strchug(tmp);
+ gchar* end = index(tmp, '#');
+ if (end)
+ value = g_strndup(tmp, end - tmp);
+ else
+ value = g_strdup(g_strchomp(tmp));
+ if (strcmp(clamd_tokens[0], token) == 0) {
+ /* UNIX socket */
+ Socket = (Clamd_Socket *) malloc(sizeof(Clamd_Socket *));
+ if (Socket) {
+ Socket->socket.path = NULL;
+ Socket->socket.host = NULL;
+ Socket->socket.port = -1;
+ Socket->type = UNIX_SOCKET;
+ Socket->socket.path = g_strdup(value);
+ g_free(value);
+ value = NULL;
+ fclose(conf);
+ debug_print("clamctl: %s\n", Socket->socket.path);
+ return;
+ }
+ }
+ else if (strcmp(clamd_tokens[1], token) == 0) {
+ /* INET socket */
+ if (! Socket) {
+ Socket = (Clamd_Socket *) malloc(sizeof(Clamd_Socket *));
+ if (Socket) {
+ Socket->socket.path = NULL;
+ Socket->socket.host = NULL;
+ Socket->socket.port = -1;
+ Socket->type = INET_SOCKET;
+ Socket->socket.port = atoi(value);
+ Socket->socket.host = g_strdup("localhost");
+ g_free(value);
+ value = NULL;
+ debug_print("clamctl: %s:%d\n",
+ Socket->socket.host, Socket->socket.port);
+ }
+ }
+ else {
+ Socket->type = INET_SOCKET;
+ Socket->socket.port = atoi(value);
+ g_free(value);
+ value = NULL;
+ if (! Socket->socket.host)
+ Socket->socket.host = g_strdup("localhost");
+ debug_print("clamctl: %s:%d\n",
+ Socket->socket.host, Socket->socket.port);
+ }
+ /* We must continue since TCPAddr could also be configured */
+ }
+ else if (strcmp(clamd_tokens[2], token) == 0) {
+ if (! Socket) {
+ Socket = (Clamd_Socket *) malloc(sizeof(Clamd_Socket *));
+ if (Socket) {
+ Socket->socket.path = NULL;
+ Socket->socket.host = NULL;
+ Socket->socket.port = 3310; /* default port */
+ Socket->type = INET_SOCKET;
+ Socket->socket.host = g_strdup(value);
+ g_free(value);
+ value = NULL;
+ debug_print("clamctl: %s:%d\n",
+ Socket->socket.host, Socket->socket.port);
+ }
+ }
+ else {
+ Socket->type = INET_SOCKET;
+ if (Socket->socket.host)
+ g_free(Socket->socket.host);
+ Socket->socket.host = g_strdup(value);
+ g_free(value);
+ value = NULL;
+ if (Socket->socket.port == -1)
+ Socket->socket.port = 3310;
+ debug_print("clamctl: %s:%d\n",
+ Socket->socket.host, Socket->socket.port);
+ }
+ /* We must continue since TCPSocket could also be configured */
+ }
+ }
+ }
+ }
+ fclose(conf);
+ if (! (Socket && (Socket->socket.port || Socket->socket.path))) {
+ /*g_error("%s: Not able to find required information", path);*/
+ alertpanel_error(_("%s: Not able to find required information\nclamd will be disabled"), path);
+ }
+ /*debug_set_mode(FALSE);*/
+}
+
+void clamd_create_config_manual(const gchar* host, int port) {
+ if (! host || port < 1) {
+ g_warning("Missing host or port < 1");
+ return;
+ }
+ if (config && config->ConfigType == MANUAL &&
+ config->manual.host && config->manual.port == port &&
+ strcmp(config->manual.host, host) == 0) {
+ debug_print("%s : %s and %d : %d - Identical. No need to read again\n",
+ config->manual.host, host, config->manual.port, port);
+ return;
+ }
+
+ if (config)
+ clamd_config_free(config);
+ config = clamd_config_new();
+
+ config->ConfigType = MANUAL;
+ config->manual.host = g_strdup(host);
+ config->manual.port = port;
+ /* INET socket */
+ Socket = (Clamd_Socket *) malloc(sizeof(Clamd_Socket *));
+ if (Socket) {
+ Socket->type = INET_SOCKET;
+ Socket->socket.port = port;
+ Socket->socket.host = g_strdup(host);
+ }
+ else {
+ /*g_error("%s: Not able to find required information", path);*/
+ alertpanel_error(_("Could not create socket"));
+ }
+}
+
+gboolean clamd_find_socket() {
+ const gchar** config_dir = config_dirs;
+ gchar *clamd_conf = NULL;
+
+ while (*config_dir) {
+ clamd_conf = g_strdup_printf("%s/clamd.conf", *config_dir++);
+ debug_print("Looking for %s\n", clamd_conf);
+ if (g_file_test(clamd_conf, G_FILE_TEST_EXISTS))
+ break;
+ g_free(clamd_conf);
+ clamd_conf = NULL;
+ }
+ if (! clamd_conf)
+ return FALSE;
+
+ debug_print("Using %s to find configuration\n", clamd_conf);
+ clamd_create_config_automatic(clamd_conf);
+ g_free(clamd_conf);
+
+ return TRUE;
+}
+
+Config* clamd_get_config() {
+ return config;
+}
+
+Clamd_Socket* clamd_get_socket() {
+ return Socket;
+}
+
+static void close_socket() {
+ debug_print("Closing socket: %d\n", sock);
+ close(sock);
+}
+
+static void create_socket() {
+ struct sockaddr_un addr_u;
+ struct sockaddr_in addr_i;
+ struct hostent *hp;
+
+ /*debug_set_mode(TRUE);*/
+ if (! Socket) {
+ sock = -1;
+ return;
+ }
+ memset(&addr_u, 0, sizeof(addr_u));
+ memset(&addr_i, 0, sizeof(addr_i));
+ debug_print("socket->type: %d\n", Socket->type);
+ switch (Socket->type) {
+ case UNIX_SOCKET:
+ debug_print("socket path: %s\n", Socket->socket.path);
+ sock = socket(PF_UNIX, SOCK_STREAM, 0);
+ debug_print("socket file (create): %d\n", sock);
+ if (sock < 0)
+ return;
+ addr_u.sun_family = AF_UNIX;
+ memcpy(addr_u.sun_path, Socket->socket.path,
+ strlen(Socket->socket.path));
+ if (connect(sock, (struct sockaddr *) &addr_u, sizeof(addr_u)) < 0) {
+ perror("connect socket");
+ close_socket();
+ sock = -2;
+ }
+ debug_print("socket file (connect): %d\n", sock);
+ break;
+ case INET_SOCKET:
+ addr_i.sin_family = AF_INET;
+ addr_i.sin_port = htons(Socket->socket.port);
+ hp = gethostbyname(Socket->socket.host);
+ bcopy((void *)hp->h_addr, (void *)&addr_i.sin_addr, hp->h_length);
+ sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (sock < 0)
+ return;
+ if (connect(sock, (struct sockaddr *)&addr_i, sizeof(addr_i)) < 0) {
+ perror("connect socket");
+ close_socket();
+ sock = -2;
+ }
+ break;
+ }
+ /*debug_set_mode(FALSE);*/
+}
+
+static void copy_socket(Clamd_Socket* sock) {
+ Socket = (Clamd_Socket *) malloc(sizeof(Clamd_Socket *));
+ Socket->socket.path = NULL;
+ Socket->socket.host = NULL;
+ Socket->type = sock->type;
+ if (Socket->type == UNIX_SOCKET) {
+ Socket->socket.path = g_strdup(sock->socket.path);
+ }
+ else {
+ Socket->socket.host = g_strdup(sock->socket.host);
+ Socket->socket.port = sock->socket.port;
+ }
+}
+
+Clamd_Stat clamd_init(Clamd_Socket* config) {
+ gchar buf[BUFSIZ];
+ int n_read;
+ gboolean connect = FALSE;
+
+ /*debug_set_mode(TRUE);*/
+ if (config != NULL && Socket != NULL)
+ return NO_SOCKET;
+ if (config) {
+ debug_print("socket: %s\n", config->socket.path);
+ copy_socket(config);
+ }
+ create_socket();
+ if (sock < 0) {
+ debug_print("no connection\n");
+ return NO_CONNECTION;
+ }
+ if (write(sock, ping, strlen(ping)) == -1) {
+ debug_print("no connection\n");
+ return NO_CONNECTION;
+ }
+ memset(buf, '\0', sizeof(buf));
+ while ((n_read = read(sock, buf, BUFSIZ)) > 0) {
+ if (buf[strlen(buf) - 1] == '\n')
+ buf[strlen(buf) - 1] = '\0';
+ debug_print("Ping result: %s\n", buf);
+ if (strcmp("PONG", buf) == 0)
+ connect = TRUE;
+ }
+ close_socket();
+ create_socket();
+ if (sock < 0) {
+ debug_print("no connection\n");
+ return NO_CONNECTION;
+ }
+ if (write(sock, version, strlen(version)) == -1) {
+ debug_print("no connection\n");
+ return NO_CONNECTION;
+ }
+ memset(buf, '\0', sizeof(buf));
+ while ((n_read = read(sock, buf, BUFSIZ)) > 0) {
+ if (buf[strlen(buf) - 1] == '\n')
+ buf[strlen(buf) - 1] = '\0';
+ debug_print("Version: %s\n", buf);
+ }
+ close_socket();
+ /*debug_set_mode(FALSE);*/
+ return (connect) ? OK : NO_CONNECTION;
+}
+
+static Clamd_Stat clamd_stream_scan(
+ const gchar* path, gchar** res, ssize_t size) {
+ int fd;
+ ssize_t count;
+ gchar buf[BUFSIZ];
+ int n_read;
+ int32_t chunk;
+
+ debug_print("Scanning: %s\n", path);
+
+ memset(buf, '\0', sizeof(buf));
+
+ if (! res || size < 1) {
+ return SCAN_ERROR;
+ }
+ if (! *res)
+ *res = g_new(gchar, size);
+ memset(*res, '\0', size);
+
+ if (! g_file_test(path, G_FILE_TEST_EXISTS)) {
+ *res = g_strconcat("ERROR -> ", path, _(": File does not exist"), NULL);
+ debug_print("res: %s\n", *res);
+ return SCAN_ERROR;
+ }
+
+#ifdef _LARGE_FILES
+ fd = open(path, O_RDONLY, O_LARGEFILE);
+#else
+ fd = open(path, O_RDONLY);
+#endif
+
+ if (fd < 0) {
+ /*g_error("%s: Unable to open", path);*/
+ *res = g_strconcat("ERROR -> ", path, _(": Unable to open"), NULL);
+ return SCAN_ERROR;
+ }
+
+ debug_print("command: %s\n", instream);
+ if (write(sock, instream, strlen(instream) + 1) == -1) {
+ close(fd);
+ return NO_CONNECTION;
+ }
+
+ while ((count = read(fd, (void *) buf, sizeof(buf))) > 0) {
+ if (count == -1) {
+ close(fd);
+ *res = g_strconcat("ERROR -> ", path, _("%s: Error reading"), NULL);
+ return SCAN_ERROR;
+ }
+ if (buf[strlen(buf) - 1] == '\n')
+ buf[strlen(buf) - 1] = '\0';
+ debug_print("read: %ld bytes\n", count);
+
+ debug_print("chunk size: %ld\n", count);
+ chunk = htonl(count);
+ if (write(sock, &chunk, 4) == -1) {
+ close(fd);
+ *res = g_strconcat("ERROR -> ", _("Socket write error"), NULL);
+ return SCAN_ERROR;
+ }
+ if (write(sock, buf, count) == -1) {
+ close(fd);
+ *res = g_strconcat("ERROR -> ", _("Socket write error"), NULL);
+ return SCAN_ERROR;
+ }
+ memset(buf, '\0', sizeof(buf));
+ }
+ close(fd);
+
+ chunk = htonl(0);
+ if (write(sock, &chunk, 4) == -1) {
+ *res = g_strconcat("ERROR -> ", _("Socket write error"), NULL);
+ return SCAN_ERROR;
+ }
+
+ debug_print("reading from socket\n");
+ n_read = read(sock, *res, size);
+ if (n_read < 0) {
+ *res = g_strconcat("ERROR -> ", _("Socket read error"), NULL);
+ return SCAN_ERROR;
+ }
+ debug_print("received: %s\n", *res);
+ return OK;
+}
+
+Clamd_Stat clamd_verify_email(const gchar* path, response* result) {
+ gchar buf[BUFSIZ];
+ int n_read;
+ gchar* command;
+ Clamd_Stat stat;
+
+ /*debug_set_mode(TRUE);*/
+ if (!result) {
+ result = malloc(sizeof(response *));
+ memset(result, '\0', sizeof(response *));
+ }
+ create_socket();
+ if (sock < 0) {
+ debug_print("no connection\n");
+ return NO_CONNECTION;
+ }
+ memset(buf, '\0', sizeof(buf));
+ if (Socket->type == INET_SOCKET) {
+ gchar* tmp = g_new0(gchar, BUFSIZ);
+ stat = clamd_stream_scan(path, &tmp, BUFSIZ);
+ if (stat != OK) {
+ close_socket();
+ result->msg = g_strdup(tmp);
+ g_free(tmp);
+ debug_print("result: %s\n", result->msg);
+ /*debug_set_mode(FALSE);*/
+ return stat;
+ }
+ debug_print("copy to buf: %s\n", tmp);
+ memcpy(&buf, tmp, BUFSIZ);
+ g_free(tmp);
+ }
+ else {
+ command = g_strconcat(scan, " ", path, "\n", NULL);
+ debug_print("command: %s\n", command);
+ if (write(sock, command, strlen(command)) == -1) {
+ debug_print("no connection\n");
+ stat = NO_CONNECTION;
+ }
+ g_free(command);
+ memset(buf, '\0', sizeof(buf));
+ while ((n_read = read(sock, buf, BUFSIZ)) > 0) {
+ if (buf[strlen(buf) - 1] == '\n')
+ buf[strlen(buf) - 1] = '\0';
+ }
+ }
+ debug_print("response: %s\n", buf);
+ if (strstr(buf, "ERROR")) {
+ stat = SCAN_ERROR;
+ result->msg = g_strdup(buf);
+ }
+ else if (strstr(buf, "FOUND")) {
+ stat = VIRUS;
+ result->msg = g_strdup(buf);
+ }
+ else {
+ stat = OK;
+ result->msg = NULL;
+ }
+ close_socket();
+ /*debug_set_mode(FALSE);*/
+
+ return stat;
+}
+
+GSList* clamd_verify_dir(const gchar* path) {
+ gchar buf[BUFSIZ];
+ int n_read;
+ gchar* command;
+ GSList *list = NULL;
+
+ if (Socket->type == INET_SOCKET)
+ return list;
+
+ create_socket();
+ if (sock < 0) {
+ debug_print("No socket\n");
+ return list;
+ }
+ command = g_strconcat(contscan, path, "\n", NULL);
+ debug_print("command: %s\n", command);
+ if (write(sock, command, strlen(command)) == -1) {
+ debug_print("No socket\n");
+ return list;
+ }
+ g_free(command);
+ memset(buf, '\0', sizeof(buf));
+ while ((n_read = read(sock, buf, BUFSIZ)) > 0) {
+ gchar** tmp = g_strsplit(buf, "\n", 0);
+ gchar** head = tmp;
+ while (*tmp) {
+ gchar* file = *tmp++;
+ debug_print("%s\n", file);
+ if (strstr(file, "ERROR")) {
+ g_warning("%s", file);
+ /* dont report files with errors */
+ }
+ else if (strstr(file, "FOUND")) {
+ list = g_slist_append(list, g_strdup(file));
+ }
+ }
+ g_strfreev(head);
+ }
+ close_socket();
+ return list;
+}
+
+void clamd_free_gslist(GSList* list) {
+ GSList* tmp = list;
+ while(tmp) {
+ g_free(tmp->data);
+ tmp = g_slist_next(tmp);
+ }
+ g_slist_free(list);
+}
+
+gchar* clamd_get_virus_name(gchar* msg) {
+ gchar *head, *tail, *name;
+
+ tail = g_strrstr_len(msg, strlen(msg), "FOUND");
+ if (! tail)
+ return NULL;
+ head = g_strstr_len(msg, strlen(msg), ":");
+ ++head;
+ name = g_strndup(head, tail - head);
+ g_strstrip(name);
+ return name;
+}
+
+void clamd_free() {
+/*
+ * struct _Clamd_Socket {
+ * Type type;
+ * union {
+ * struct {
+ * gchar* path;
+ * };
+ * struct {
+ * gchar* host;
+ * int port;
+ * };
+ * } socket;
+ * };
+ */
+ if (sock > 0) {
+ close_socket();
+ sock = 0;
+ }
+ if (Socket) {
+ switch (Socket->type) {
+ case UNIX_SOCKET:
+ if (Socket->socket.path) {
+ g_free(Socket->socket.path);
+ Socket->socket.path = NULL;
+ }
+ break;
+ case INET_SOCKET:
+ if (Socket->socket.host) {
+ g_free(Socket->socket.host);
+ Socket->socket.host = NULL;
+ }
+ break;
+ }
+ g_free(Socket);
+ Socket = NULL;
+ }
+ if (config) {
+ clamd_config_free(config);
+ config = NULL;
+ }
+}
+
+Config* clamd_config_new() {
+ return g_new0(Config, 1);
+}
+
+void clamd_config_free(Config* c) {
+ if (c->ConfigType == AUTOMATIC) {
+ g_free(c->automatic.folder);
+ c->automatic.folder = NULL;
+ }
+ else {
+ g_free(c->manual.host);
+ c->manual.host = NULL;
+ }
+ g_free(c);
+}
+
+gchar* int2char(int i) {
+ gchar* s = g_new0(gchar, 5);
+
+ sprintf(s, "%d", i);
+
+ return s;
+}
+
+gchar* long2char(long l) {
+ gchar* s = g_new0(gchar, 5);
+
+ debug_print("l: %ld\n", l);
+ sprintf(s, "%ld", l);
+ debug_print("s: %s\n", s);
+
+ return s;
+}
diff --git a/src/plugins/clamd/libclamd/clamd-plugin.h b/src/plugins/clamd/libclamd/clamd-plugin.h
@@ -0,0 +1,144 @@
+/* vim: set textwidth=80 tabstop=4: */
+
+/*
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2008 Michael Rasmussen and the Claws Mail Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __CLAMD_PLUGIN_H__
+#define __CLAMD_PLUGIN_H__
+
+#include <glib.h>
+
+typedef enum _Type Type;
+enum _Type { UNIX_SOCKET, INET_SOCKET };
+
+typedef enum _Clamd_Stat Clamd_Stat;
+enum _Clamd_Stat { OK, VIRUS, NO_SOCKET, NO_CONNECTION, SCAN_ERROR };
+
+typedef struct _Clamd_Socket Clamd_Socket;
+struct _Clamd_Socket {
+ Type type;
+ union {
+ struct {
+ gchar* path;
+ };
+ struct {
+ gchar* host;
+ int port;
+ };
+ } socket;
+};
+
+typedef struct {
+ enum { AUTOMATIC, MANUAL } ConfigType;
+ union {
+ struct {
+ gchar* folder;
+ } automatic;
+ struct {
+ gchar* host;
+ int port;
+ } manual;
+ };
+} Config;
+
+typedef struct _response response;
+struct _response {
+ gchar* msg;
+};
+
+void clamd_create_config_automatic(const gchar* path);
+
+void clamd_create_config_manual(const gchar* host, int port);
+
+gchar* int2char(int i);
+
+gchar* long2char(long l);
+
+/**
+ * Function which looks for clamd.conf the default places
+ * and configures the plugin according to the information
+ * found.
+ * @return <b>TRUE</b> if clamd.conf found which means all
+ * information need to make a connection has been found.
+ * <b>FALSE</b> otherwise.
+ */
+gboolean clamd_find_socket();
+
+/**
+ * Function to get current configuration
+ * @return the current configuration for clamd or <b>NULL</b>
+ */
+Config* clamd_get_config();
+
+/**
+ * Function to retrieve virus name from msg
+ * @param msg Message returned from clamd
+ * @return virus name or <b>NULL</b> if no virus name found
+ */
+gchar* clamd_get_virus_name(gchar* msg);
+
+/**
+ * Function to initialize the connection to clamd.
+ * @param config A pointer to a struct _Clamd_Socket having
+ * the required information. If clamd_find_socket returned
+ * TRUE config should be <b>NULL</b> because all the needed
+ * information is already present @see clamd_find_socket.
+ * @return Clamd_Stat. @see _Clamd_Stat.
+ */
+Clamd_Stat clamd_init(Clamd_Socket* config);
+
+/**
+ * Function returning the current socket information.
+ * @return reference to the current Clamd_Socket. @see _Clamd_Socket.
+ */
+Clamd_Socket* clamd_get_socket();
+
+/**
+ * Function which is checks a specific email for known viruses
+ * @param path Absolut path to email to check.
+ * @param msg String to which result of scan will be copied. Will be
+ * <b>NULL</b> if no virus was found.
+ * @return Clamd_Stat. @see _Clamd_Stat.
+ */
+Clamd_Stat clamd_verify_email(const gchar* path, response* result);
+
+/**
+ * Function which is checks files in a specific directory for
+ * known viruses. Dont stop when a virus is found but keeps going
+ * @param path Absolut path to directory to check.
+ * @return list of list with virus or <b>NULL</b>.
+ */
+GSList* clamd_verify_dir(const gchar* path);
+
+/**
+ * Function to free all memory assigned to a GSList
+ * @param list The GSList to free
+ */
+void clamd_free_gslist(GSList* list);
+
+/**
+ * Function which frees all memory assigned to clamd_plugin
+ */
+void clamd_free();
+
+Config* clamd_config_new();
+
+void clamd_config_free(Config* c);
+
+#endif
diff --git a/src/plugins/clamd/placeholder.txt b/src/plugins/clamd/placeholder.txt
@@ -1 +0,0 @@
-Placeholder
diff --git a/src/plugins/fetchinfo/Makefile.am b/src/plugins/fetchinfo/Makefile.am
@@ -0,0 +1,23 @@
+plugindir = $(pkglibdir)/plugins
+
+plugin_LTLIBRARIES = fetchinfo_plugin.la
+
+fetchinfo_plugin_la_SOURCES = \
+ fetchinfo_plugin.c fetchinfo_plugin.h \
+ fetchinfo_plugin_gtk.c
+
+fetchinfo_plugin_la_LDFLAGS = \
+ -avoid-version -module \
+ $(GTK_LIBS)
+
+AM_CPPFLAGS = \
+ $(CLAWS_MAIL_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(GTK_CFLAGS)
+
+INCLUDES = \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/src/common \
+ -I$(top_srcdir)/src/gtk
+
+EXTRA_DIST=
diff --git a/src/plugins/fetchinfo/fetchinfo_plugin.c b/src/plugins/fetchinfo/fetchinfo_plugin.c
@@ -0,0 +1,239 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2003 Hiroyuki Yamamoto and the Claws Mail Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+# include "claws-features.h"
+#endif
+
+#include "defs.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+/* common */
+#include "version.h"
+#include "claws.h"
+#include "plugin.h"
+#include "utils.h"
+#include "hooks.h"
+#include "inc.h"
+#include "prefs.h"
+#include "prefs_gtk.h"
+#include "fetchinfo_plugin.h"
+/* add headers */
+#include "pop.h"
+#include "quoted-printable.h"
+/* parse headers */
+#include "procheader.h"
+#include "plugin.h"
+
+static guint mail_receive_hook_id;
+
+static FetchinfoConfig config;
+
+static PrefParam param[] = {
+ {"fetchinfo_enable", "FALSE", &config.fetchinfo_enable,
+ P_BOOL, NULL, NULL, NULL},
+ {"fetchinfo_uidl", "TRUE", &config.fetchinfo_uidl,
+ P_BOOL, NULL, NULL, NULL},
+ {"fetchinfo_account", "TRUE", &config.fetchinfo_account,
+ P_BOOL, NULL, NULL, NULL},
+ {"fetchinfo_server", "TRUE", &config.fetchinfo_server,
+ P_BOOL, NULL, NULL, NULL},
+ {"fetchinfo_userid", "TRUE", &config.fetchinfo_userid,
+ P_BOOL, NULL, NULL, NULL},
+ {"fetchinfo_time", "TRUE", &config.fetchinfo_time,
+ P_BOOL, NULL, NULL, NULL},
+
+ {NULL, NULL, NULL, P_OTHER, NULL, NULL, NULL}
+};
+
+gchar *fetchinfo_add_header(gchar **data, const gchar *header,const gchar *value)
+{
+ gchar *line;
+ gchar *qpline;
+ gchar *newdata;
+
+ line = g_strdup_printf("%s: %s", header, value);
+ qpline = g_malloc(strlen(line)*4);
+ qp_encode_line(qpline, line);
+ newdata = g_strconcat(*data, qpline, NULL);
+ g_free(line);
+ g_free(qpline);
+ g_free(*data);
+ *data = newdata;
+ return newdata;
+}
+
+static gboolean mail_receive_hook(gpointer source, gpointer data)
+{
+ MailReceiveData *mail_receive_data = (MailReceiveData *) source;
+ Pop3Session *session;
+ gchar *newheaders;
+ gchar *newdata;
+ gchar date[PREFSBUFSIZE];
+
+ if (!config.fetchinfo_enable) {
+ return FALSE;
+ }
+
+ g_return_val_if_fail(
+ mail_receive_data
+ && mail_receive_data->session
+ && mail_receive_data->data,
+ FALSE );
+
+ session = mail_receive_data->session;
+ get_rfc822_date(date, PREFSBUFSIZE);
+ newheaders = g_strdup("");
+
+ if (config.fetchinfo_uidl)
+ fetchinfo_add_header(&newheaders, "X-FETCH-UIDL",
+ session->msg[session->cur_msg].uidl);
+ if (config.fetchinfo_account)
+ fetchinfo_add_header(&newheaders, "X-FETCH-ACCOUNT",
+ session->ac_prefs->account_name);
+ if (config.fetchinfo_server)
+ fetchinfo_add_header(&newheaders, "X-FETCH-SERVER",
+ session->ac_prefs->recv_server);
+ if (config.fetchinfo_userid)
+ fetchinfo_add_header(&newheaders, "X-FETCH-USERID",
+ session->ac_prefs->userid);
+ if (config.fetchinfo_time)
+ fetchinfo_add_header(&newheaders, "X-FETCH-TIME",
+ date);
+
+ newdata = g_strconcat(newheaders, mail_receive_data->data, NULL);
+ g_free(newheaders);
+ g_free(mail_receive_data->data);
+ mail_receive_data->data = newdata;
+ mail_receive_data->data_len = strlen(newdata);
+ return FALSE;
+}
+
+FetchinfoConfig *fetchinfo_get_config(void)
+{
+ return &config;
+}
+
+void fetchinfo_save_config(void)
+{
+ PrefFile *pfile;
+ gchar *rcpath;
+
+ debug_print("Saving Fetchinfo Page\n");
+
+ rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
+ pfile = prefs_write_open(rcpath);
+ g_free(rcpath);
+ if (!pfile || (prefs_set_block_label(pfile, "Fetchinfo") < 0))
+ return;
+
+ if (prefs_write_param(param, pfile->fp) < 0) {
+ /* i18n: Possible error message during plugin load */
+ g_warning(_("failed to write Fetchinfo configuration to file\n"));
+ prefs_file_close_revert(pfile);
+ return;
+ }
+ if (fprintf(pfile->fp, "\n") < 0) {
+ FILE_OP_ERROR(rcpath, "fprintf");
+ prefs_file_close_revert(pfile);
+ } else
+ prefs_file_close(pfile);
+}
+
+gint plugin_init(gchar **error)
+{
+ gchar *rcpath;
+
+ if (!check_plugin_version(MAKE_NUMERIC_VERSION(2,9,2,72),
+ VERSION_NUMERIC, _("Fetchinfo"), error))
+ return -1;
+
+ mail_receive_hook_id = hooks_register_hook(MAIL_RECEIVE_HOOKLIST, mail_receive_hook, NULL);
+ if (mail_receive_hook_id == (guint)-1) {
+ /* i18n: Possible error message during plugin load */
+ *error = g_strdup(_("Failed to register mail receive hook"));
+ return -1;
+ }
+
+ prefs_set_default(param);
+ rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
+ prefs_read_config(param, "Fetchinfo", rcpath, NULL);
+ g_free(rcpath);
+
+ fetchinfo_gtk_init();
+
+ debug_print("Fetchinfo plugin loaded\n");
+
+ return 0;
+}
+
+gboolean plugin_done(void)
+{
+ hooks_unregister_hook(MAIL_RECEIVE_HOOKLIST, mail_receive_hook_id);
+ fetchinfo_gtk_done();
+
+ debug_print("Fetchinfo plugin unloaded\n");
+ return TRUE;
+}
+
+const gchar *plugin_name(void)
+{
+ return _("Fetchinfo");
+}
+
+const gchar *plugin_desc(void)
+{
+ /* i18n: Description seen in plugins dialog.
+ * Translation of "Plugins" part of preferences path should to be
+ * the same as translation of "Plugins" string in Claws Mail message
+ * catalog. */
+ return _("This plugin modifies the downloaded messages. "
+ "It inserts headers containing some download "
+ "information: UIDL, Claws Mail account name, "
+ "POP server, user ID and retrieval time.\n"
+ "\n"
+ "Options can be found in /Configuration/Preferences/Plugins/Fetchinfo");
+}
+
+const gchar *plugin_type(void)
+{
+ return "GTK2";
+}
+
+const gchar *plugin_licence(void)
+{
+ return "GPL3+";
+}
+
+const gchar *plugin_version(void)
+{
+ return VERSION;
+}
+
+struct PluginFeature *plugin_provides(void)
+{
+ static struct PluginFeature features[] =
+ /* i18n: Description of functionality added by this plugin */
+ { {PLUGIN_UTILITY, N_("Mail marking")},
+ {PLUGIN_NOTHING, NULL}};
+ return features;
+}
diff --git a/src/plugins/fetchinfo/fetchinfo_plugin.h b/src/plugins/fetchinfo/fetchinfo_plugin.h
@@ -0,0 +1,42 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2003 Hiroyuki Yamamoto and the Claws Mail Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FETCHINFO_PLUGIN_H
+#define FETCHINFO_PLUGIN_H 1
+
+#include <glib.h>
+
+typedef struct _FetchinfoConfig FetchinfoConfig;
+
+struct _FetchinfoConfig
+{
+ gboolean fetchinfo_enable;
+ gboolean fetchinfo_uidl;
+ gboolean fetchinfo_account;
+ gboolean fetchinfo_server;
+ gboolean fetchinfo_userid;
+ gboolean fetchinfo_time;
+};
+
+FetchinfoConfig *fetchinfo_get_config (void);
+void fetchinfo_save_config (void);
+gint fetchinfo_gtk_init(void);
+void fetchinfo_gtk_done(void);
+
+#endif
diff --git a/src/plugins/fetchinfo/fetchinfo_plugin_gtk.c b/src/plugins/fetchinfo/fetchinfo_plugin_gtk.c
@@ -0,0 +1,198 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2003 Hiroyuki Yamamoto and the Claws Mail Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+# include "claws-features.h"
+#endif
+
+#include "defs.h"
+#include "version.h"
+#include "claws.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "plugin.h"
+#include "utils.h"
+#include "prefs.h"
+#include "prefs_gtk.h"
+
+#include "fetchinfo_plugin.h"
+
+struct FetchinfoPage
+{
+ PrefsPage page;
+
+ GtkWidget *fetchinfo_enable;
+ GtkWidget *fetchinfo_uidl;
+ GtkWidget *fetchinfo_account;
+ GtkWidget *fetchinfo_server;
+ GtkWidget *fetchinfo_userid;
+ GtkWidget *fetchinfo_time;
+};
+
+static void fetchinfo_set_sensitive(struct FetchinfoPage *page, gboolean enable)
+{
+ gtk_widget_set_sensitive(GTK_WIDGET(page->fetchinfo_uidl), enable);
+ gtk_widget_set_sensitive(GTK_WIDGET(page->fetchinfo_account), enable);
+ gtk_widget_set_sensitive(GTK_WIDGET(page->fetchinfo_server), enable);
+ gtk_widget_set_sensitive(GTK_WIDGET(page->fetchinfo_userid), enable);
+ gtk_widget_set_sensitive(GTK_WIDGET(page->fetchinfo_time), enable);
+}
+
+static void fetchinfo_enable_cb(GtkWidget *widget, gpointer data)
+{
+ struct FetchinfoPage *page = (struct FetchinfoPage *) data;
+
+ fetchinfo_set_sensitive(page, gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(page->fetchinfo_enable)));
+}
+
+#define ADD_NEW_CHECKBOX(line, button, text) \
+ button = gtk_check_button_new_with_label (text); \
+ gtk_widget_show (button); \
+ gtk_table_attach (GTK_TABLE (table), button, 1, 2, line, line+1, \
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), \
+ (GtkAttachOptions) (0), 0, 0);
+
+static void fetchinfo_create_widget_func(PrefsPage * _page, GtkWindow *window, gpointer data)
+{
+ struct FetchinfoPage *page = (struct FetchinfoPage *) _page;
+ FetchinfoConfig *config;
+ GtkWidget *table;
+ GtkWidget *fetchinfo_enable;
+ GtkWidget *fetchinfo_uidl;
+ GtkWidget *fetchinfo_account;
+ GtkWidget *fetchinfo_server;
+ GtkWidget *fetchinfo_userid;
+ GtkWidget *fetchinfo_time;
+
+ table = gtk_table_new (6, 3, FALSE);
+ gtk_widget_show(table);
+ gtk_table_set_row_spacings(GTK_TABLE(table), 4);
+ gtk_table_set_col_spacings(GTK_TABLE(table), 8);
+
+ /* i18n: Heading of a preferences section determining which headers to add */
+ fetchinfo_enable = gtk_check_button_new_with_label (_("Add fetchinfo headers"));
+ gtk_widget_show (fetchinfo_enable);
+ gtk_table_attach (GTK_TABLE (table), fetchinfo_enable, 0, 2, 0, 1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+ /* i18n: Description of a header to be added */
+ ADD_NEW_CHECKBOX(1, fetchinfo_uidl, _("UIDL"));
+ /* i18n: Description of a header to be added */
+ ADD_NEW_CHECKBOX(2, fetchinfo_account, _("Account name"));
+ /* i18n: Description of a header to be added */
+ ADD_NEW_CHECKBOX(3, fetchinfo_server, _("Receive server"));
+ /* i18n: Description of a header to be added */
+ ADD_NEW_CHECKBOX(4, fetchinfo_userid, _("UserID"));
+ /* i18n: Description of a header to be added */
+ ADD_NEW_CHECKBOX(5, fetchinfo_time, _("Fetch time"));
+
+ config = fetchinfo_get_config();
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fetchinfo_enable),
+ config->fetchinfo_enable);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fetchinfo_uidl),
+ config->fetchinfo_uidl);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fetchinfo_account),
+ config->fetchinfo_account);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fetchinfo_server),
+ config->fetchinfo_server);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fetchinfo_userid),
+ config->fetchinfo_userid);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fetchinfo_time),
+ config->fetchinfo_time);
+
+ g_signal_connect(G_OBJECT(fetchinfo_enable), "released",
+ G_CALLBACK(fetchinfo_enable_cb), page);
+
+ page->fetchinfo_enable = fetchinfo_enable;
+ page->fetchinfo_uidl = fetchinfo_uidl;
+ page->fetchinfo_account = fetchinfo_account;
+ page->fetchinfo_server = fetchinfo_server;
+ page->fetchinfo_userid = fetchinfo_userid;
+ page->fetchinfo_time = fetchinfo_time;
+
+ page->page.widget = table;
+
+ fetchinfo_set_sensitive(page, config->fetchinfo_enable);
+}
+#undef ADD_NEW_CHECKBOX
+
+static void fetchinfo_destroy_widget_func(PrefsPage *_page)
+{
+ debug_print("Destroying Fetchinfo widget\n");
+}
+
+static void fetchinfo_save_func(PrefsPage *_page)
+{
+ struct FetchinfoPage *page = (struct FetchinfoPage *) _page;
+ FetchinfoConfig *config;
+
+ debug_print("Saving Fetchinfo Page\n");
+
+ config = fetchinfo_get_config();
+
+ config->fetchinfo_enable = gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(page->fetchinfo_enable) );
+ config->fetchinfo_uidl = gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(page->fetchinfo_uidl) );
+ config->fetchinfo_account = gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(page->fetchinfo_account));
+ config->fetchinfo_server = gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(page->fetchinfo_server) );
+ config->fetchinfo_userid = gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(page->fetchinfo_userid) );
+ config->fetchinfo_time = gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(page->fetchinfo_time) );
+
+ fetchinfo_save_config();
+}
+
+static struct FetchinfoPage fetchinfo_page;
+
+gint fetchinfo_gtk_init(void)
+{
+ static gchar *path[3];
+
+ path[0] = _("Plugins");
+ path[1] = _("Fetchinfo");
+ path[2] = NULL;
+
+ fetchinfo_page.page.path = path;
+ fetchinfo_page.page.create_widget = fetchinfo_create_widget_func;
+ fetchinfo_page.page.destroy_widget = fetchinfo_destroy_widget_func;
+ fetchinfo_page.page.save_page = fetchinfo_save_func;
+
+ prefs_gtk_register_page((PrefsPage *) &fetchinfo_page);
+
+ debug_print("Fetchinfo GTK plugin loaded\n");
+ return 0;
+}
+
+void fetchinfo_gtk_done(void)
+{
+ prefs_gtk_unregister_page((PrefsPage *) &fetchinfo_page);
+
+ debug_print("Fetchinfo GTK plugin unloaded\n");
+}
diff --git a/src/plugins/fetchinfo/placeholder.txt b/src/plugins/fetchinfo/placeholder.txt
@@ -1 +0,0 @@
-Placeholder
diff --git a/src/plugins/gdata/Makefile.am b/src/plugins/gdata/Makefile.am
@@ -0,0 +1,30 @@
+plugindir = $(pkglibdir)/plugins
+
+plugin_LTLIBRARIES = gdata_plugin.la
+
+gdata_plugin_la_SOURCES = \
+ gdata_plugin.c \
+ gdata_plugin.h \
+ cm_gdata_contacts.c \
+ cm_gdata_contacts.h \
+ cm_gdata_prefs.c \
+ cm_gdata_prefs.h
+
+gdata_plugin_la_LDFLAGS = \
+ -avoid-version -module \
+ $(GTK_LIBS) \
+ $(GDATA_LIBS)
+
+
+INCLUDES = \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/src/common \
+ -I$(top_srcdir)/src/gtk
+
+AM_CPPFLAGS = \
+ -Wall \
+ $(CLAWS_MAIL_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(GTK_CFLAGS) \
+ $(GDATA_CFLAGS) \
+ -DLOCALEDIR=\""$(localedir)"\"
diff --git a/src/plugins/gdata/cm_gdata_contacts.c b/src/plugins/gdata/cm_gdata_contacts.c
@@ -0,0 +1,508 @@
+/* GData plugin for Claws-Mail
+ * Copyright (C) 2011 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+# include "claws-features.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "cm_gdata_contacts.h"
+#include "cm_gdata_prefs.h"
+
+#include <gtk/gtk.h>
+#include "addr_compl.h"
+#include "main.h"
+#include "prefs_common.h"
+#include "common/log.h"
+#include "common/xml.h"
+
+#include <gdata/gdata.h>
+
+#define GDATA_CONTACTS_FILENAME "gdata_cache.xml"
+
+typedef struct
+{
+ const gchar *family_name;
+ const gchar *given_name;
+ const gchar *full_name;
+ const gchar *address;
+} Contact;
+
+typedef struct
+{
+ GSList *contacts;
+} CmGDataContactsCache;
+
+
+CmGDataContactsCache contacts_cache;
+gboolean cm_gdata_contacts_query_running = FALSE;
+gchar *contacts_group_id = NULL;
+
+static void write_cache_to_file(void)
+{
+ gchar *path;
+ PrefFile *pfile;
+ XMLTag *tag;
+ XMLNode *xmlnode;
+ GNode *rootnode;
+ GNode *contactsnode;
+ GSList *walk;
+
+ path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, GDATA_CONTACTS_FILENAME, NULL);
+ pfile = prefs_write_open(path);
+ g_free(path);
+ if(pfile == NULL) {
+ debug_print("GData plugin error: Cannot open file " GDATA_CONTACTS_FILENAME " for writing\n");
+ return;
+ }
+
+ /* XML declarations */
+ xml_file_put_xml_decl(pfile->fp);
+
+ /* Build up XML tree */
+
+ /* root node */
+ tag = xml_tag_new("gdata");
+ xmlnode = xml_node_new(tag, NULL);
+ rootnode = g_node_new(xmlnode);
+
+ /* contacts node */
+ tag = xml_tag_new("contacts");
+ xmlnode = xml_node_new(tag, NULL);
+ contactsnode = g_node_new(xmlnode);
+ g_node_append(rootnode, contactsnode);
+
+ /* walk contacts cache */
+ for(walk = contacts_cache.contacts; walk; walk = walk->next)
+ {
+ GNode *contactnode;
+ Contact *contact = walk->data;
+ tag = xml_tag_new("contact");
+ xml_tag_add_attr(tag, xml_attr_new("family_name",contact->family_name));
+ xml_tag_add_attr(tag, xml_attr_new("given_name",contact->given_name));
+ xml_tag_add_attr(tag, xml_attr_new("full_name",contact->full_name));
+ xml_tag_add_attr(tag, xml_attr_new("address",contact->address));
+ xmlnode = xml_node_new(tag, NULL);
+ contactnode = g_node_new(xmlnode);
+ g_node_append(contactsnode, contactnode);
+ }
+
+ /* Actual writing and cleanup */
+ xml_write_tree(rootnode, pfile->fp);
+ if(prefs_file_close(pfile) < 0)
+ debug_print("GData plugin error: Failed to write file " GDATA_CONTACTS_FILENAME "\n");
+
+ debug_print("GData plugin error: Wrote cache to file " GDATA_CONTACTS_FILENAME "\n");
+
+ /* Free XML tree */
+ xml_free_tree(rootnode);
+}
+
+static int add_gdata_contact_to_cache(GDataContactsContact *contact)
+{
+ GList *walk;
+ int retval;
+
+ retval = 0;
+ for(walk = gdata_contacts_contact_get_email_addresses(contact); walk; walk = walk->next) {
+ const gchar *email_address;
+ GDataGDEmailAddress *address = GDATA_GD_EMAIL_ADDRESS(walk->data);
+
+ email_address = gdata_gd_email_address_get_address(address);
+ if(email_address && (*email_address != '\0')) {
+ GDataGDName *name;
+ Contact *cached_contact;
+
+ name = gdata_contacts_contact_get_name(contact);
+
+ cached_contact = g_new0(Contact, 1);
+ cached_contact->full_name = g_strdup(gdata_gd_name_get_full_name(name));
+ cached_contact->given_name = g_strdup(gdata_gd_name_get_given_name(name));
+ cached_contact->family_name = g_strdup(gdata_gd_name_get_family_name(name));
+ cached_contact->address = g_strdup(email_address);
+
+ contacts_cache.contacts = g_slist_prepend(contacts_cache.contacts, cached_contact);
+
+ debug_print("GData plugin: Added %s <%s>\n", cached_contact->full_name, cached_contact->address);
+ retval = 1;
+ }
+ }
+ if(retval == 0)
+ {
+ debug_print("GData plugin: Skipped received contact \"%s\" because it doesn't have an email address\n",
+ gdata_gd_name_get_full_name(gdata_contacts_contact_get_name(contact)));
+ }
+ return retval;
+}
+
+static void free_contact(Contact *contact)
+{
+ g_free((gpointer)contact->full_name);
+ g_free((gpointer)contact->family_name);
+ g_free((gpointer)contact->given_name);
+ g_free((gpointer)contact->address);
+ g_free(contact);
+}
+
+static void clear_contacts_cache(void)
+{
+ GSList *walk;
+ for(walk = contacts_cache.contacts; walk; walk = walk->next)
+ free_contact(walk->data);
+ g_slist_free(contacts_cache.contacts);
+ contacts_cache.contacts = NULL;
+}
+
+static void cm_gdata_query_contacts_ready(GDataContactsService *service, GAsyncResult *res, gpointer data)
+{
+ GDataFeed *feed;
+ GList *walk;
+ GError *error = NULL;
+ guint num_contacts = 0;
+ guint num_contacts_added = 0;
+ gchar *tmpstr1, *tmpstr2;
+
+ feed = gdata_service_query_finish(GDATA_SERVICE(service), res, &error);
+ cm_gdata_contacts_query_running = FALSE;
+ if(error)
+ {
+ g_object_unref(feed);
+ log_error(LOG_PROTOCOL, _("GData plugin: Error querying for contacts: %s\n"), error->message);
+ g_error_free(error);
+ return;
+ }
+
+ /* clear cache */
+ clear_contacts_cache();
+
+ /* Iterate through the returned contacts and fill the cache */
+ for(walk = gdata_feed_get_entries(feed); walk; walk = walk->next) {
+ num_contacts_added += add_gdata_contact_to_cache(GDATA_CONTACTS_CONTACT(walk->data));
+ num_contacts++;
+ }
+ g_object_unref(feed);
+ contacts_cache.contacts = g_slist_reverse(contacts_cache.contacts);
+ /* i18n: First part of "Added X of Y contacts to cache" */
+ tmpstr1 = g_strdup_printf(ngettext("Added %d of", "Added %d of", num_contacts_added), num_contacts_added);
+ /* i18n: Second part of "Added X of Y contacts to cache" */
+ tmpstr2 = g_strdup_printf(ngettext("1 contact to the cache", "%d contacts to the cache", num_contacts), num_contacts);
+ log_message(LOG_PROTOCOL, "%s %s\n", tmpstr1, tmpstr2);
+ g_free(tmpstr1);
+ g_free(tmpstr2);
+}
+
+static void query_after_auth(GDataContactsService *service)
+{
+ GDataContactsQuery *query;
+
+ log_message(LOG_PROTOCOL, _("GData plugin: Starting async contacts query\n"));
+
+ query = gdata_contacts_query_new(NULL);
+ gdata_contacts_query_set_group(query, contacts_group_id);
+ gdata_query_set_max_results(GDATA_QUERY(query), cm_gdata_config.max_num_results);
+ gdata_contacts_service_query_contacts_async(service, GDATA_QUERY(query), NULL, NULL, NULL,
+#ifdef HAVE_GDATA_VERSION_0_9_1
+ NULL,
+#endif
+ (GAsyncReadyCallback)cm_gdata_query_contacts_ready, NULL);
+
+ g_object_unref(query);
+}
+
+#ifdef HAVE_GDATA_VERSION_0_9_1
+static void cm_gdata_query_groups_ready(GDataContactsService *service, GAsyncResult *res, gpointer data)
+{
+ GDataFeed *feed;
+ GList *walk;
+ GError *error = NULL;
+
+ feed = gdata_service_query_finish(GDATA_SERVICE(service), res, &error);
+ if(error)
+ {
+ g_object_unref(feed);
+ log_error(LOG_PROTOCOL, _("GData plugin: Error querying for groups: %s\n"), error->message);
+ g_error_free(error);
+ return;
+ }
+
+ /* Iterate through the returned groups and search for Contacts group id */
+ for(walk = gdata_feed_get_entries(feed); walk; walk = walk->next) {
+ const gchar *system_group_id;
+ GDataContactsGroup *group = GDATA_CONTACTS_GROUP(walk->data);
+
+ system_group_id = gdata_contacts_group_get_system_group_id(group);
+ if(system_group_id && !strcmp(system_group_id, GDATA_CONTACTS_GROUP_CONTACTS)) {
+ gchar *pos;
+ const gchar *id;
+
+ id = gdata_entry_get_id(GDATA_ENTRY(group));
+
+ /* possibly replace projection "full" by "base" */
+ pos = g_strrstr(id, "/full/");
+ if(pos) {
+ GString *str = g_string_new("\0");
+ int off = pos-id;
+
+ g_string_append_len(str, id, off);
+ g_string_append(str, "/base/");
+ g_string_append(str, id+off+strlen("/full/"));
+ g_string_append_c(str, '\0');
+ contacts_group_id = str->str;
+ g_string_free(str, FALSE);
+ }
+ else
+ contacts_group_id = g_strdup(id);
+ break;
+ }
+ }
+ g_object_unref(feed);
+
+ log_message(LOG_PROTOCOL, _("GData plugin: Groups received\n"));
+
+ query_after_auth(service);
+}
+#endif
+
+#ifdef HAVE_GDATA_VERSION_0_9
+static void query_for_contacts_group_id(GDataClientLoginAuthorizer *authorizer)
+{
+ GDataContactsService *service;
+#ifdef HAVE_GDATA_VERSION_0_9_1
+
+ log_message(LOG_PROTOCOL, _("GData plugin: Starting async groups query\n"));
+
+ service = gdata_contacts_service_new(GDATA_AUTHORIZER(authorizer));
+ gdata_contacts_service_query_groups_async(service, NULL, NULL, NULL, NULL, NULL,
+ (GAsyncReadyCallback)cm_gdata_query_groups_ready, NULL);
+#else
+ service = gdata_contacts_service_new(GDATA_AUTHORIZER(authorizer));
+ query_after_auth(service);
+#endif
+ g_object_unref(service);
+}
+
+static void cm_gdata_auth_ready(GDataClientLoginAuthorizer *authorizer, GAsyncResult *res, gpointer data)
+{
+ GError *error = NULL;
+
+ if(gdata_client_login_authorizer_authenticate_finish(authorizer, res, &error) == FALSE)
+ {
+ log_error(LOG_PROTOCOL, _("GData plugin: Authentication error: %s\n"), error->message);
+ g_error_free(error);
+ cm_gdata_contacts_query_running = FALSE;
+ return;
+ }
+
+ log_message(LOG_PROTOCOL, _("GData plugin: Authenticated\n"));
+
+ if(!contacts_group_id)
+ {
+ query_for_contacts_group_id(authorizer);
+ }
+ else {
+ GDataContactsService *service;
+ service = gdata_contacts_service_new(GDATA_AUTHORIZER(authorizer));
+ query_after_auth(service);
+ g_object_unref(service);
+ }
+}
+#else
+static void cm_gdata_auth_ready(GDataContactsService *service, GAsyncResult *res, gpointer data)
+{
+ GError *error = NULL;
+
+ if(!gdata_service_authenticate_finish(GDATA_SERVICE(service), res, &error))
+ {
+ log_error(LOG_PROTOCOL, _("GData plugin: Authentication error: %s\n"), error->message);
+ g_error_free(error);
+ cm_gdata_contacts_query_running = FALSE;
+ return;
+ }
+
+ log_message(LOG_PROTOCOL, _("GData plugin: Authenticated\n"));
+
+ query_after_auth(service);
+}
+#endif
+static void query()
+{
+
+#ifdef HAVE_GDATA_VERSION_0_9
+ GDataClientLoginAuthorizer *authorizer;
+#else
+ GDataContactsService *service;
+#endif
+
+ if(cm_gdata_contacts_query_running)
+ {
+ debug_print("GData plugin: Network query already in progress");
+ return;
+ }
+
+ log_message(LOG_PROTOCOL, _("GData plugin: Starting async authentication\n"));
+
+#ifdef HAVE_GDATA_VERSION_0_9
+ authorizer = gdata_client_login_authorizer_new(CM_GDATA_CLIENT_ID, GDATA_TYPE_CONTACTS_SERVICE);
+ gdata_client_login_authorizer_authenticate_async(authorizer, cm_gdata_config.username, cm_gdata_config.password, NULL, (GAsyncReadyCallback)cm_gdata_auth_ready, NULL);
+ cm_gdata_contacts_query_running = TRUE;
+#else
+ service = gdata_contacts_service_new(CM_GDATA_CLIENT_ID);
+ cm_gdata_contacts_query_running = TRUE;
+ gdata_service_authenticate_async(GDATA_SERVICE(service), cm_gdata_config.username, cm_gdata_config.password, NULL,
+ (GAsyncReadyCallback)cm_gdata_auth_ready, NULL);
+#endif
+
+
+#ifdef HAVE_GDATA_VERSION_0_9
+ g_object_unref(authorizer);
+#else
+ g_object_unref(service);
+#endif
+
+}
+
+
+static void add_contacts_to_list(GList **address_list, GSList *contacts)
+{
+ GSList *walk;
+
+ for(walk = contacts; walk; walk = walk->next)
+ {
+ address_entry *ae;
+ Contact *contact = walk->data;
+
+ ae = g_new0(address_entry, 1);
+ ae->name = g_strdup(contact->full_name);
+ ae->address = g_strdup(contact->address);
+ ae->grp_emails = NULL;
+
+ *address_list = g_list_prepend(*address_list, ae);
+ addr_compl_add_address1(ae->address, ae);
+
+ if(contact->given_name && *(contact->given_name) != '\0')
+ addr_compl_add_address1(contact->given_name, ae);
+
+ if(contact->family_name && *(contact->family_name) != '\0')
+ addr_compl_add_address1(contact->family_name, ae);
+ }
+}
+
+void cm_gdata_add_contacts(GList **address_list)
+{
+ add_contacts_to_list(address_list, contacts_cache.contacts);
+}
+
+gboolean cm_gdata_update_contacts_cache(void)
+{
+ if(prefs_common.work_offline)
+ {
+ debug_print("GData plugin: Offline mode\n");
+ }
+ else if(!cm_gdata_config.username || *(cm_gdata_config.username) == '\0' || !cm_gdata_config.password)
+ {
+ /* noop if no credentials are given */
+ debug_print("GData plugin: Empty username or password\n");
+ }
+ else
+ {
+ debug_print("GData plugin: Querying contacts");
+ query();
+ }
+ return TRUE;
+}
+
+void cm_gdata_contacts_done(void)
+{
+ g_free(contacts_group_id);
+ contacts_group_id = NULL;
+
+ write_cache_to_file();
+ if(contacts_cache.contacts && !claws_is_exiting())
+ clear_contacts_cache();
+}
+
+void cm_gdata_load_contacts_cache_from_file(void)
+{
+ gchar *path;
+ GNode *rootnode, *childnode, *contactnode;
+ XMLNode *xmlnode;
+
+ path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, GDATA_CONTACTS_FILENAME, NULL);
+ if(!is_file_exist(path)) {
+ g_free(path);
+ return;
+ }
+
+ /* no merging; make sure the cache is empty (this should be a noop, but just to be safe...) */
+ clear_contacts_cache();
+
+ rootnode = xml_parse_file(path);
+ g_free(path);
+ if(!rootnode)
+ return;
+ xmlnode = rootnode->data;
+
+ /* Check that root entry is "gdata" */
+ if(strcmp2(xmlnode->tag->tag, "gdata") != 0) {
+ g_warning("wrong gdata cache file\n");
+ xml_free_tree(rootnode);
+ return;
+ }
+
+ for(childnode = rootnode->children; childnode; childnode = childnode->next) {
+ GList *attributes;
+ xmlnode = childnode->data;
+
+ if(strcmp2(xmlnode->tag->tag, "contacts") != 0)
+ continue;
+
+ for(contactnode = childnode->children; contactnode; contactnode = contactnode->next)
+ {
+ Contact *cached_contact;
+
+ xmlnode = contactnode->data;
+
+ cached_contact = g_new0(Contact, 1);
+ /* Attributes of the branch nodes */
+ for(attributes = xmlnode->tag->attr; attributes; attributes = attributes->next) {
+ XMLAttr *attr = attributes->data;
+
+ if(attr && attr->name && attr->value) {
+ if(!strcmp2(attr->name, "full_name"))
+ cached_contact->full_name = g_strdup(attr->value);
+ else if(!strcmp2(attr->name, "given_name"))
+ cached_contact->given_name = g_strdup(attr->value);
+ else if(!strcmp2(attr->name, "family_name"))
+ cached_contact->family_name = g_strdup(attr->value);
+ else if(!strcmp2(attr->name, "address"))
+ cached_contact->address = g_strdup(attr->value);
+ }
+ }
+ contacts_cache.contacts = g_slist_prepend(contacts_cache.contacts, cached_contact);
+ debug_print("Read contact from cache: %s\n", cached_contact->full_name);
+ }
+ }
+
+ /* Free XML tree */
+ xml_free_tree(rootnode);
+
+ contacts_cache.contacts = g_slist_reverse(contacts_cache.contacts);
+}
diff --git a/src/plugins/gdata/cm_gdata_contacts.h b/src/plugins/gdata/cm_gdata_contacts.h
@@ -0,0 +1,28 @@
+/* GData plugin for Claws-Mail
+ * Copyright (C) 2011 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CM_GDATA_CONTACTS_H
+#define CM_GDATA_CONTACTS_H
+
+#include <glib.h>
+
+void cm_gdata_add_contacts(GList **address_list);
+void cm_gdata_contacts_done(void);
+gboolean cm_gdata_update_contacts_cache(void);
+void cm_gdata_load_contacts_cache_from_file(void);
+
+#endif /* CM_GDATA_CONTACTS_H */
diff --git a/src/plugins/gdata/cm_gdata_prefs.c b/src/plugins/gdata/cm_gdata_prefs.c
@@ -0,0 +1,161 @@
+/* GData plugin for Claws-Mail
+ * Copyright (C) 2011 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+# include "claws-features.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "cm_gdata_prefs.h"
+#include "gdata_plugin.h"
+#include "cm_gdata_contacts.h"
+
+#include "prefs_gtk.h"
+#include "main.h"
+
+#include <gtk/gtk.h>
+
+
+typedef struct
+{
+ PrefsPage page;
+ GtkWidget *entry_username;
+ GtkWidget *entry_password;
+ GtkWidget *spin_max_num_results;
+ GtkWidget *spin_max_cache_age;
+} CmGDataPage;
+
+CmGDataPrefs cm_gdata_config;
+CmGDataPage gdata_page;
+
+PrefParam cm_gdata_param[] =
+{
+ {"username", NULL, &cm_gdata_config.username, P_STRING,
+ &gdata_page.entry_username, prefs_set_data_from_entry, prefs_set_entry},
+
+ {"password", NULL, &cm_gdata_config.password, P_PASSWORD,
+ &gdata_page.entry_password, prefs_set_data_from_entry, prefs_set_entry},
+
+ { "max_num_results", "1000", &cm_gdata_config.max_num_results, P_INT,
+ &gdata_page.spin_max_num_results, prefs_set_data_from_spinbtn, prefs_set_spinbtn},
+
+ { "max_cache_age", "300", &cm_gdata_config.max_cache_age, P_INT,
+ &gdata_page.spin_max_cache_age, prefs_set_data_from_spinbtn, prefs_set_spinbtn},
+
+ {NULL, NULL, NULL, P_OTHER, NULL, NULL, NULL }
+};
+
+static void gdata_create_prefs_page(PrefsPage *page, GtkWindow *window, gpointer data)
+{
+ GtkWidget *vbox;
+ GtkWidget *frame;
+ GtkWidget *spinner;
+ GtkWidget *table;
+ GtkWidget *label;
+ GtkWidget *entry;
+
+ vbox = gtk_vbox_new(FALSE, 0);
+
+ /* auth frame */
+ frame = gtk_frame_new(_("Authentication"));
+ gtk_container_set_border_width(GTK_CONTAINER(frame), 5);
+ gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
+
+ /* username */
+ table = gtk_table_new(2, 2, FALSE);
+ label = gtk_label_new(_("Username:"));
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 4, 4);
+ entry = gtk_entry_new();
+ gtk_widget_set_size_request(entry, 250, -1);
+ gtk_table_attach(GTK_TABLE(table), entry, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 4, 4);
+ gdata_page.entry_username = entry;
+ label = gtk_label_new(_("Password:"));
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 4, 4);
+ entry = gtk_entry_new();
+ gtk_widget_set_size_request(entry, 250, -1);
+ gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
+ gdata_page.entry_password = entry;
+ gtk_table_attach(GTK_TABLE(table), entry, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 4, 4);
+ gtk_container_add(GTK_CONTAINER(frame), table);
+
+ table = gtk_table_new(2, 2, FALSE);
+ gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
+ label = gtk_label_new(_("Polling interval (seconds):"));
+ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 4, 4);
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+ spinner = gtk_spin_button_new_with_range(10, 10000, 10);
+ gtk_table_attach(GTK_TABLE(table), spinner, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 4, 4);
+ gdata_page.spin_max_cache_age = spinner;
+
+ label = gtk_label_new(_("Maximum number of results:"));
+ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 4, 4);
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+ spinner = gtk_spin_button_new_with_range(0, G_MAXINT, 50);
+ gtk_table_attach(GTK_TABLE(table), spinner, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 4, 4);
+ gdata_page.spin_max_num_results = spinner;
+
+ gtk_widget_show_all(vbox);
+ page->widget = vbox;
+
+ prefs_set_dialog(cm_gdata_param);
+}
+
+static void gdata_destroy_prefs_page(PrefsPage *page)
+{
+}
+
+static void gdata_save_prefs(PrefsPage *page)
+{
+ int old_max_cache_age = cm_gdata_config.max_cache_age;
+
+ if (!page->page_open)
+ return;
+
+ prefs_set_data_from_dialog(cm_gdata_param);
+
+ cm_gdata_update_contacts_cache();
+ if(old_max_cache_age != cm_gdata_config.max_cache_age)
+ cm_gdata_update_contacts_update_timer();
+}
+
+void cm_gdata_prefs_init(void)
+{
+ static gchar *path[3];
+
+ path[0] = _("Plugins");
+ path[1] = _("GData");
+ path[2] = NULL;
+
+ gdata_page.page.path = path;
+ gdata_page.page.create_widget = gdata_create_prefs_page;
+ gdata_page.page.destroy_widget = gdata_destroy_prefs_page;
+ gdata_page.page.save_page = gdata_save_prefs;
+ prefs_gtk_register_page((PrefsPage*) &gdata_page);
+}
+
+void cm_gdata_prefs_done(void)
+{
+ if(!claws_is_exiting()) {
+ prefs_gtk_unregister_page((PrefsPage*) &gdata_page);
+ }
+}
diff --git a/src/plugins/gdata/cm_gdata_prefs.h b/src/plugins/gdata/cm_gdata_prefs.h
@@ -0,0 +1,36 @@
+/* GData plugin for Claws-Mail
+ * Copyright (C) 2011 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CM_GDATA_PREFS_H_
+#define CM_GDATA_PREFS_H_
+
+#include "prefs_gtk.h"
+
+typedef struct {
+ char *username;
+ char *password;
+ int max_num_results;
+ int max_cache_age;
+} CmGDataPrefs;
+
+extern CmGDataPrefs cm_gdata_config;
+extern PrefParam cm_gdata_param[];
+
+void cm_gdata_prefs_init(void);
+void cm_gdata_prefs_done(void);
+
+#endif /* CM_GDATA_PREFS_H_ */
diff --git a/src/plugins/gdata/gdata_plugin.c b/src/plugins/gdata/gdata_plugin.c
@@ -0,0 +1,190 @@
+/* GData plugin for Claws-Mail
+ * Copyright (C) 2011 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+# include "claws-features.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#ifdef G_OS_UNIX
+# include <libintl.h>
+#endif
+
+#include "common/plugin.h"
+#include "common/version.h"
+#include "common/utils.h"
+#include "common/hooks.h"
+#include "common/defs.h"
+#include "common/prefs.h"
+#include "main.h"
+#include "mainwindow.h"
+#include "addr_compl.h"
+
+#include "cm_gdata_contacts.h"
+#include "cm_gdata_prefs.h"
+
+static guint hook_address_completion;
+static guint hook_offline_switch;
+static guint timer_query_contacts = 0;
+
+static gboolean my_address_completion_build_list_hook(gpointer source, gpointer data)
+{
+ cm_gdata_add_contacts(source);
+ return FALSE;
+}
+
+static gboolean my_offline_switch_hook(gpointer source, gpointer data)
+{
+ cm_gdata_update_contacts_cache();
+ return FALSE;
+}
+
+static void cm_gdata_save_config(void)
+{
+ PrefFile *pfile;
+ gchar *rcpath;
+
+ debug_print("Saving GData plugin configuration...\n");
+
+ rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
+ pfile = prefs_write_open(rcpath);
+ g_free(rcpath);
+ if (!pfile || (prefs_set_block_label(pfile, "GDataPlugin") < 0))
+ return;
+
+ if (prefs_write_param(cm_gdata_param, pfile->fp) < 0) {
+ debug_print("failed!\n");
+ g_warning(_("\nGData Plugin: Failed to write plugin configuration to file\n"));
+ prefs_file_close_revert(pfile);
+ return;
+ }
+ if (fprintf(pfile->fp, "\n") < 0) {
+ FILE_OP_ERROR(rcpath, "fprintf");
+ prefs_file_close_revert(pfile);
+ }
+ else
+ prefs_file_close(pfile);
+ debug_print("done.\n");
+}
+
+void cm_gdata_update_contacts_update_timer(void)
+{
+ if(timer_query_contacts != 0)
+ g_source_remove(timer_query_contacts);
+ timer_query_contacts = g_timeout_add_seconds(cm_gdata_config.max_cache_age, (GSourceFunc)cm_gdata_update_contacts_cache, NULL);
+}
+
+gint plugin_init(gchar **error)
+{
+ gchar *rcpath;
+
+ /* Version check */
+ if(!check_plugin_version(MAKE_NUMERIC_VERSION(3,7,1,55),
+ VERSION_NUMERIC, _("GData"), error))
+ return -1;
+
+ hook_address_completion = hooks_register_hook(ADDDRESS_COMPLETION_BUILD_ADDRESS_LIST_HOOKLIST,
+ my_address_completion_build_list_hook, NULL);
+ if(hook_address_completion == (guint) -1) {
+ *error = g_strdup(_("Failed to register address completion hook in the GData plugin"));
+ return -1;
+ }
+
+ hook_offline_switch = hooks_register_hook(OFFLINE_SWITCH_HOOKLIST, my_offline_switch_hook, NULL);
+ if(hook_offline_switch == (guint) -1) {
+ hooks_unregister_hook(ADDDRESS_COMPLETION_BUILD_ADDRESS_LIST_HOOKLIST, hook_address_completion);
+ *error = g_strdup(_("Failed to register offline switch hook in the GData plugin"));
+ return -1;
+ }
+
+ /* Configuration */
+ prefs_set_default(cm_gdata_param);
+ rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
+ prefs_read_config(cm_gdata_param, "GDataPlugin", rcpath, NULL);
+ g_free(rcpath);
+
+ cm_gdata_prefs_init();
+
+ /* contacts cache */
+ cm_gdata_load_contacts_cache_from_file();
+ cm_gdata_update_contacts_update_timer();
+ cm_gdata_update_contacts_cache();
+
+ debug_print("GData plugin loaded\n");
+
+ return 0;
+}
+
+gboolean plugin_done(void)
+{
+ if(!claws_is_exiting()) {
+ hooks_unregister_hook(ADDDRESS_COMPLETION_BUILD_ADDRESS_LIST_HOOKLIST, hook_address_completion);
+ hooks_unregister_hook(OFFLINE_SWITCH_HOOKLIST, hook_offline_switch);
+ g_source_remove(timer_query_contacts);
+ }
+ cm_gdata_prefs_done();
+ cm_gdata_contacts_done();
+
+ cm_gdata_save_config();
+
+ debug_print("GData plugin unloaded\n");
+
+ /* returning FALSE because dependant libraries may not be unload-safe. */
+ return FALSE;
+}
+
+const gchar *plugin_name(void)
+{
+ return _("GData");
+}
+
+const gchar *plugin_desc(void)
+{
+ return _("This plugin provides access to the GData protocol "
+ "for Claws Mail.\n\n"
+ "The GData protocol is an interface to Google services.\n"
+ "Currently, the only implemented functionality is to include "
+ "Google Contacts into the Tab-address completion.\n"
+ "\nFeedback to <berndth@gmx.de> is welcome.");
+}
+
+const gchar *plugin_type(void)
+{
+ return "GTK2";
+}
+
+const gchar *plugin_licence(void)
+{
+ return "GPL3+";
+}
+
+const gchar *plugin_version(void)
+{
+ return VERSION;
+}
+
+struct PluginFeature *plugin_provides(void)
+{
+ static struct PluginFeature features[] =
+ { {PLUGIN_UTILITY, N_("GData integration")},
+ {PLUGIN_NOTHING, NULL}};
+ return features;
+}
diff --git a/src/plugins/gdata/gdata_plugin.h b/src/plugins/gdata/gdata_plugin.h
@@ -0,0 +1,23 @@
+/* GData plugin for Claws-Mail
+ * Copyright (C) 2011 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GDATA_PLUGIN_H_
+#define GDATA_PLUGIN_H_
+
+void cm_gdata_update_contacts_update_timer(void);
+
+#endif /* GDATA_PLUGIN_H_ */
diff --git a/src/plugins/gdata/placeholder.txt b/src/plugins/gdata/placeholder.txt
@@ -1 +0,0 @@
-Placeholder
diff --git a/src/plugins/geolocation/placeholder.txt b/src/plugins/geolocation/placeholder.txt
@@ -1 +0,0 @@
-Placeholder
diff --git a/src/plugins/gtkhtml2_viewer/placeholder.txt b/src/plugins/gtkhtml2_viewer/placeholder.txt
@@ -1 +0,0 @@
-Placeholder
diff --git a/src/plugins/mailmbox/Makefile.am b/src/plugins/mailmbox/Makefile.am
@@ -0,0 +1,42 @@
+plugindir = $(pkglibdir)/plugins
+plugin_LTLIBRARIES = mailmbox.la
+
+## Mailmbox folder plugin
+
+mailmbox_la_SOURCES = \
+ plugin.c plugin_gtk.c \
+ carray.c carray.h \
+ chash.c chash.h \
+ clist.c clist.h \
+ mailimf.c mailimf.h \
+ mailimf_types.c mailimf_types.h \
+ mailimf_types_helper.c mailimf_types_helper.h \
+ mailimf_write.c mailimf_write.h \
+ maillock.c maillock.h \
+ mailmbox.c mailmbox.h \
+ mailmbox_folder.c mailmbox_folder.h \
+ mailmbox_parse.c mailmbox_parse.h \
+ mailmbox_types.c mailmbox_types.h \
+ mmapstring.c mmapstring.h \
+ plugin_gtk.h
+
+mailmbox_la_LDFLAGS = \
+ -avoid-version -module
+
+mailmbox_la_LIBADD = \
+ $(GTK_LIBS)
+
+mailmbox_la_CPPFLAGS = \
+ $(CLAWS_MAIL_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(GTK_CFLAGS)
+
+AM_CPPFLAGS = \
+ $(CLAWS_MAIL_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(GTK_CFLAGS)
+
+INCLUDES = \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/src/common \
+ -I$(top_srcdir)/src/gtk
diff --git a/src/plugins/mailmbox/carray.c b/src/plugins/mailmbox/carray.c
@@ -0,0 +1,143 @@
+
+/*
+ * libEtPan! -- a mail stuff library
+ *
+ * carray - Implements simple dynamic pointer arrays
+ *
+ * Copyright (c) 1999-2000, Gaël Roualland <gael.roualland@iname.com>
+ * interface changes - 2002 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "carray.h"
+
+carray * carray_new(unsigned int initsize) {
+ carray * array;
+
+ array = (carray *) malloc(sizeof(carray));
+ if (!array) return NULL;
+
+ array->len = 0;
+ array->max = initsize;
+ array->array = (void **) malloc(sizeof(void *) * initsize);
+ if (!array->array) {
+ free(array);
+ return NULL;
+ }
+ return array;
+}
+
+int carray_add(carray * array, void * data, unsigned int * index) {
+ int r;
+
+ r = carray_set_size(array, array->len + 1);
+ if (r < 0)
+ return r;
+
+ array->array[array->len - 1] = data;
+ if (index != NULL)
+ * index = array->len - 1;
+
+ return 0;
+}
+
+int carray_set_size(carray * array, unsigned int new_size)
+{
+ if (new_size > array->max) {
+ unsigned int n = array->max * 2;
+ void * new;
+
+ while (n <= new_size)
+ n *= 2;
+
+ new = (void **) realloc(array->array, sizeof(void *) * n);
+ if (!new)
+ return -1;
+ array->array = new;
+ array->max = n;
+ }
+ array->len = new_size;
+
+ return 0;
+}
+
+int carray_delete_fast(carray * array, unsigned int indx) {
+ if (indx >= array->len)
+ return -1;
+
+ array->array[indx] = NULL;
+
+ return 0;
+}
+
+int carray_delete(carray * array, unsigned int indx) {
+ if (indx >= array->len)
+ return -1;
+
+ if (indx != --array->len)
+ array->array[indx] = array->array[array->len];
+ return 0;
+}
+
+int carray_delete_slow(carray * array, unsigned int indx) {
+ if (indx >= array->len)
+ return -1;
+
+ if (indx != --array->len)
+ memmove(array->array + indx, array->array + indx + 1,
+ (array->len - indx) * sizeof(void *));
+ return 0;
+}
+
+#ifdef NO_MACROS
+void ** carray_data(carray * array) {
+ return array->array;
+}
+
+unsigned int carray_count(carray * array) {
+ return array->len;
+}
+
+void * carray_get(carray * array, unsigned int indx) {
+ return array->array[indx];
+}
+
+void carray_set(carray * array, unsigned int indx, void * value) {
+ array->array[indx] = value;
+}
+#endif
+
+void carray_free(carray * array) {
+ free(array->array);
+ free(array);
+}
diff --git a/src/plugins/mailmbox/carray.h b/src/plugins/mailmbox/carray.h
@@ -0,0 +1,126 @@
+
+/*
+ * libEtPan! -- a mail stuff library
+ *
+ * carray - Implements simple dynamic pointer arrays
+ *
+ * Copyright (c) 1999-2000, Gaël Roualland <gael.roualland@iname.com>
+ * interface changes - 2002 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef CARRAY_H
+#define CARRAY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <inttypes.h>
+
+struct carray_s {
+ void ** array;
+ unsigned int len;
+ unsigned int max;
+};
+
+typedef struct carray_s carray;
+
+/* Creates a new array of pointers, with initsize preallocated cells */
+carray * carray_new(unsigned int initsize);
+
+/* Adds the pointer to data in the array.
+ Returns the index of the pointer in the array or -1 on error */
+int carray_add(carray * array, void * data, unsigned int * index);
+
+int carray_set_size(carray * array, unsigned int new_size);
+
+/* Removes the cell at this index position. Returns TRUE on success.
+ Order of elements in the array IS changed. */
+int carray_delete(carray * array, unsigned int indx);
+
+/* Removes the cell at this index position. Returns TRUE on success.
+ Order of elements in the array IS not changed. */
+int carray_delete_slow(carray * array, unsigned int indx);
+
+/* remove without decreasing the size of the array */
+int carray_delete_fast(carray * array, unsigned int indx);
+
+/* Some of the following routines can be implemented as macros to
+ be faster. If you don't want it, define NO_MACROS */
+#ifdef NO_MACROS
+
+/* Returns the array itself */
+void ** carray_data(carray);
+
+/* Returns the number of elements in the array */
+int carray_count(carray);
+
+/* Returns the contents of one cell */
+void * carray_get(carray array, unsigned int indx);
+
+/* Sets the contents of one cell */
+void carray_set(carray array, unsigned int indx, void * value);
+
+#else
+
+#if 0
+#define carray_data(a) (a->array)
+#define carray_count(a) (a->len)
+#define carray_get(a, indx) (a->array[indx])
+#define carray_set(a, indx, v) do { a->array[indx]=v; } while(0)
+#endif
+
+static inline void ** carray_data(carray * array) {
+ return array->array;
+}
+
+static inline unsigned int carray_count(carray * array) {
+ return array->len;
+}
+
+static inline void * carray_get(carray * array, unsigned int indx) {
+ return array->array[indx];
+}
+
+static inline void carray_set(carray * array,
+ unsigned int indx, void * value) {
+ array->array[indx] = value;
+}
+#endif
+
+void carray_free(carray * array);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/plugins/mailmbox/chash.c b/src/plugins/mailmbox/chash.c
@@ -0,0 +1,395 @@
+
+/*
+ * libEtPan! -- a mail stuff library
+ *
+ * chash - Implements generic hash tables.
+ *
+ * Copyright (c) 1999-2000, Gaël Roualland <gael.roualland@iname.com>
+ * interface changes - 2002 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "chash.h"
+
+/* This defines the maximum (average) number of entries per bucket.
+ The hash is resized everytime inserting an entry makes the
+ average go over that value. */
+#define CHASH_MAXDEPTH 3
+
+static inline unsigned int chash_func(const char * key, unsigned int len) {
+#if 0
+ register unsigned int c = 0, t;
+ register const char * k = key;
+
+ while (len--) {
+ c += (c << 4) + *k++;
+ if ((t = c & 0xF0000000)) {
+ c ^= t >> 24;
+ c ^= t;
+ }
+ }
+ return c;
+#endif
+ register unsigned int c = 5381;
+ register const char * k = key;
+
+ while (len--) {
+ c = ((c << 5) + c) + *k++;
+ }
+
+ return c;
+}
+
+static inline char * chash_dup(const void * data, unsigned int len)
+{
+ void * r;
+
+ r = (char *) malloc(len);
+ if (!r)
+ return NULL;
+ memcpy(r, data, len);
+ return r;
+}
+
+chash * chash_new(unsigned int size, int flags)
+{
+ chash * h;
+
+ h = (chash *) malloc(sizeof(chash));
+ if (h == NULL)
+ return NULL;
+
+ h->count = 0;
+ h->cells = (struct chashcell **) calloc(size, sizeof(struct chashcell *));
+ if (h->cells == NULL) {
+ free(h);
+ return NULL;
+ }
+ h->size = size;
+ h->copykey = flags & CHASH_COPYKEY;
+ h->copyvalue = flags & CHASH_COPYVALUE;
+
+ return h;
+}
+
+int chash_get(chash * hash,
+ chashdatum * key, chashdatum * result)
+{
+ unsigned int func;
+ chashiter * iter;
+
+ func = chash_func(key->data, key->len);
+
+ /* look for the key in existing cells */
+ iter = hash->cells[func % hash->size];
+ while (iter) {
+ if (iter->key.len == key->len && iter->func == func
+ && !memcmp(iter->key.data, key->data, key->len)) {
+ * result = iter->value; /* found */
+
+ return 0;
+ }
+ iter = iter->next;
+ }
+
+ return -1;
+}
+
+int chash_set(chash * hash,
+ chashdatum * key,
+ chashdatum * value,
+ chashdatum * oldvalue)
+{
+ unsigned int func, indx;
+ chashiter * iter, * cell;
+ int r;
+
+ if (hash->count > hash->size * CHASH_MAXDEPTH) {
+ r = chash_resize(hash, (hash->count / CHASH_MAXDEPTH) * 2 + 1);
+ if (r < 0)
+ goto err;
+ }
+
+ func = chash_func(key->data, key->len);
+ indx = func % hash->size;
+
+ /* look for the key in existing cells */
+ iter = hash->cells[indx];
+ while (iter) {
+ if (iter->key.len == key->len && iter->func == func
+ && !memcmp(iter->key.data, key->data, key->len)) {
+ /* found, replacing entry */
+ if (hash->copyvalue) {
+ char * data;
+
+ data = chash_dup(value->data, value->len);
+ if (data == NULL)
+ goto err;
+
+ free(iter->value.data);
+ iter->value.data = data;
+ iter->value.len = value->len;
+ } else {
+ if (oldvalue != NULL) {
+ oldvalue->data = iter->value.data;
+ oldvalue->len = iter->value.len;
+ }
+ iter->value.data = value->data;
+ iter->value.len = value->len;
+ }
+ if (!hash->copykey)
+ iter->key.data = key->data;
+
+ if (oldvalue != NULL) {
+ oldvalue->data = value->data;
+ oldvalue->len = value->len;
+ }
+
+ return 0;
+ }
+ iter = iter->next;
+ }
+
+ if (oldvalue != NULL) {
+ oldvalue->data = NULL;
+ oldvalue->len = 0;
+ }
+
+ /* not found, adding entry */
+ cell = (struct chashcell *) malloc(sizeof(struct chashcell));
+ if (cell == NULL)
+ goto err;
+
+ if (hash->copykey) {
+ cell->key.data = chash_dup(key->data, key->len);
+ if (cell->key.data == NULL)
+ goto free;
+ }
+ else
+ cell->key.data = key->data;
+
+ cell->key.len = key->len;
+ if (hash->copyvalue) {
+ cell->value.data = chash_dup(value->data, value->len);
+ if (cell->value.data == NULL)
+ goto free_key_data;
+ }
+ else
+ cell->value.data = value->data;
+
+ cell->value.len = value->len;
+ cell->func = func;
+ cell->next = hash->cells[indx];
+ hash->cells[indx] = cell;
+ hash->count++;
+
+ return 0;
+
+ free_key_data:
+ if (hash->copykey)
+ free(cell->key.data);
+ free:
+ free(cell);
+ err:
+ return -1;
+}
+
+int chash_delete(chash * hash, chashdatum * key, chashdatum * oldvalue)
+{
+ /* chashdatum result = { NULL, TRUE }; */
+ unsigned int func, indx;
+ chashiter * iter, * old;
+
+ /*
+ if (!keylen)
+ keylen = strlen(key) + 1;
+ */
+
+ func = chash_func(key->data, key->len);
+ indx = func % hash->size;
+
+ /* look for the key in existing cells */
+ old = NULL;
+ iter = hash->cells[indx];
+ while (iter) {
+ if (iter->key.len == key->len && iter->func == func
+ && !memcmp(iter->key.data, key->data, key->len)) {
+ /* found, deleting */
+ if (old)
+ old->next = iter->next;
+ else
+ hash->cells[indx] = iter->next;
+ if (hash->copykey)
+ free(iter->key.data);
+ if (hash->copyvalue)
+ free(iter->value.data);
+ else {
+ if (oldvalue != NULL) {
+ oldvalue->data = iter->value.data;
+ oldvalue->len = iter->value.len;
+ }
+ }
+ free(iter);
+ hash->count--;
+ return 0;
+ }
+ old = iter;
+ iter = iter->next;
+ }
+
+ return -1; /* not found */
+}
+
+void chash_free(chash * hash) {
+ unsigned int indx;
+ chashiter * iter, * next;
+
+ /* browse the hash table */
+ for(indx = 0; indx < hash->size; indx++) {
+ iter = hash->cells[indx];
+ while (iter) {
+ next = iter->next;
+ if (hash->copykey)
+ free(iter->key.data);
+ if (hash->copyvalue)
+ free(iter->value.data);
+ free(iter);
+ iter = next;
+ }
+ }
+ free(hash->cells);
+ free(hash);
+}
+
+void chash_clear(chash * hash) {
+ unsigned int indx;
+ chashiter * iter, * next;
+
+ /* browse the hash table */
+ for(indx = 0; indx < hash->size; indx++) {
+ iter = hash->cells[indx];
+ while (iter) {
+ next = iter->next;
+ if (hash->copykey)
+ free(iter->key.data);
+ if (hash->copyvalue)
+ free(iter->value.data);
+ free(iter);
+ iter = next;
+ }
+ }
+ memset(hash->cells, 0, hash->size * sizeof(* hash->cells));
+ hash->count = 0;
+}
+
+chashiter * chash_begin(chash * hash) {
+ chashiter * iter;
+ unsigned int indx = 0;
+
+ iter = hash->cells[0];
+ while(!iter) {
+ indx++;
+ if (indx >= hash->size)
+ return NULL;
+ iter = hash->cells[indx];
+ }
+ return iter;
+}
+
+chashiter * chash_next(chash * hash, chashiter * iter) {
+ unsigned int indx;
+
+ if (!iter)
+ return NULL;
+
+ indx = iter->func % hash->size;
+ iter = iter->next;
+
+ while(!iter) {
+ indx++;
+ if (indx >= hash->size)
+ return NULL;
+ iter = hash->cells[indx];
+ }
+ return iter;
+}
+
+int chash_resize(chash * hash, unsigned int size)
+{
+ struct chashcell ** cells;
+ unsigned int indx, nindx;
+ chashiter * iter, * next;
+
+ if (hash->size == size)
+ return 0;
+
+ cells = (struct chashcell **) calloc(size, sizeof(struct chashcell *));
+ if (!cells)
+ return -1;
+
+ /* browse initial hash and copy items in second hash */
+ for(indx = 0; indx < hash->size; indx++) {
+ iter = hash->cells[indx];
+ while (iter) {
+ next = iter->next;
+ nindx = iter->func % size;
+ iter->next = cells[nindx];
+ cells[nindx] = iter;
+ iter = next;
+ }
+ }
+ free(hash->cells);
+ hash->size = size;
+ hash->cells = cells;
+
+ return 0;
+}
+
+#ifdef NO_MACROS
+int chash_count(chash * hash) {
+ return hash->count;
+}
+
+int chash_size(chash * hash) {
+ return hash->size;
+}
+
+void chash_value(chashiter * iter, chashdatum * result) {
+ * result = iter->value;
+}
+
+void chash_key(chashiter * iter, chashdatum * result) {
+ * result = iter->key;
+}
+#endif
diff --git a/src/plugins/mailmbox/chash.h b/src/plugins/mailmbox/chash.h
@@ -0,0 +1,166 @@
+
+/*
+ * libEtPan! -- a mail stuff library
+ *
+ * chash - Implements generic hash tables.
+ *
+ * Copyright (c) 1999-2000, Gaël Roualland <gael.roualland@iname.com>
+ * interface changes - 2002 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef CHASH_H
+#define CHASH_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ void * data;
+ unsigned int len;
+} chashdatum;
+
+struct chash {
+ unsigned int size;
+ unsigned int count;
+ int copyvalue;
+ int copykey;
+ struct chashcell ** cells;
+};
+
+typedef struct chash chash;
+
+struct chashcell {
+ unsigned int func;
+ chashdatum key;
+ chashdatum value;
+ struct chashcell * next;
+};
+
+typedef struct chashcell chashiter;
+
+#define CHASH_COPYNONE 0
+#define CHASH_COPYKEY 1
+#define CHASH_COPYVALUE 2
+#define CHASH_COPYALL (CHASH_COPYKEY | CHASH_COPYVALUE)
+
+#define CHASH_DEFAULTSIZE 13
+
+/* Allocates a new (empty) hash using this initial size and the given flags,
+ specifying which data should be copied in the hash.
+ CHASH_COPYNONE : Keys/Values are not copied.
+ CHASH_COPYKEY : Keys are dupped and freed as needed in the hash.
+ CHASH_COPYVALUE : Values are dupped and freed as needed in the hash.
+ CHASH_COPYALL : Both keys and values are dupped in the hash.
+ */
+chash * chash_new(unsigned int size, int flags);
+
+/* Frees a hash */
+void chash_free(chash * hash);
+
+/* Removes all elements from a hash */
+void chash_clear(chash * hash);
+
+/* Adds an entry in the hash table.
+ Length can be 0 if key/value are strings.
+ If an entry already exists for this key, it is replaced, and its value
+ is returned. Otherwise, the data pointer will be NULL and the length
+ field be set to TRUE or FALSe to indicate success or failure. */
+int chash_set(chash * hash,
+ chashdatum * key,
+ chashdatum * value,
+ chashdatum * oldvalue);
+
+/* Retrieves the data associated to the key if it is found in the hash table.
+ The data pointer and the length will be NULL if not found*/
+int chash_get(chash * hash,
+ chashdatum * key, chashdatum * result);
+
+/* Removes the entry associated to this key if it is found in the hash table,
+ and returns its contents if not dupped (otherwise, pointer will be NULL
+ and len TRUE). If entry is not found both pointer and len will be NULL. */
+int chash_delete(chash * hash,
+ chashdatum * key,
+ chashdatum * oldvalue);
+
+/* Resizes the hash table to the passed size. */
+int chash_resize(chash * hash, unsigned int size);
+
+/* Returns an iterator to the first non-empty entry of the hash table */
+chashiter * chash_begin(chash * hash);
+
+/* Returns the next non-empty entry of the hash table */
+chashiter * chash_next(chash * hash, chashiter * iter);
+
+/* Some of the following routines can be implemented as macros to
+ be faster. If you don't want it, define NO_MACROS */
+#ifdef NO_MACROS
+/* Returns the size of the hash table */
+unsigned int chash_size(chash * hash);
+
+/* Returns the number of entries in the hash table */
+unsigned int chash_count(chash * hash);
+
+/* Returns the key part of the entry pointed by the iterator */
+void chash_key(chashiter * iter, chashdatum * result);
+
+/* Returns the value part of the entry pointed by the iterator */
+void chash_value(chashiter * iter, chashdatum * result);
+
+#else
+static inline unsigned int chash_size(chash * hash)
+{
+ return hash->size;
+}
+
+static inline unsigned int chash_count(chash * hash)
+{
+ return hash->count;
+}
+
+static inline void chash_key(chashiter * iter, chashdatum * result)
+{
+ * result = iter->key;
+}
+
+static inline void chash_value(chashiter * iter, chashdatum * result)
+{
+ * result = iter->value;
+}
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/plugins/mailmbox/clist.c b/src/plugins/mailmbox/clist.c
@@ -0,0 +1,266 @@
+
+/*
+ * libEtPan! -- a mail stuff library
+ *
+ * clist - Implements simple generic double-linked pointer lists
+ *
+ * Copyright (c) 1999-2000, Gaël Roualland <gael.roualland@iname.com>
+ * interface changes - 2002 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#include <stdlib.h>
+#include "clist.h"
+
+clist * clist_new() {
+ clist * lst;
+
+ lst = (clist *) malloc(sizeof(clist));
+ if (!lst) return NULL;
+
+ lst->first = lst->last = NULL;
+ lst->count = 0;
+
+ return lst;
+}
+
+void clist_free(clist * lst) {
+ clistcell * l1, * l2;
+
+ l1 = lst->first;
+ while (l1) {
+ l2 = l1->next;
+ free(l1);
+ l1 = l2;
+ }
+
+ free(lst);
+}
+
+#ifdef NO_MACROS
+int clist_isempty(clist * lst) {
+ return ((lst->first==lst->last) && (lst->last==NULL));
+}
+
+clistiter * clist_begin(clist * lst) {
+ return lst->first;
+}
+
+clistiter * clist_end(clist * lst) {
+ return lst->last;
+}
+
+clistiter * clist_next(clistiter * iter) {
+ if (iter)
+ return iter->next;
+ else
+ return NULL;
+}
+
+clistiter * clist_previous(clistiter * iter) {
+ if (iter)
+ return iter->previous;
+ else
+ return NULL;
+}
+
+void * clist_content(clistiter * iter) {
+ if (iter)
+ return iter->data;
+ else
+ return NULL;
+}
+
+int clist_count(clist * lst) {
+ return lst->count;
+}
+
+int clist_prepend(clist * lst, void * data) {
+ return clist_insert_before(lst, lst->first, data);
+}
+
+int clist_append(clist * lst, void * data) {
+ return clist_insert_after(lst, lst->last, data);
+}
+#endif
+
+int clist_insert_before(clist * lst, clistiter * iter, void * data) {
+ clistcell * c;
+
+ c = (clistcell *) malloc(sizeof(clistcell));
+ if (!c) return -1;
+
+ c->data = data;
+ lst->count++;
+
+ if (clist_isempty(lst)) {
+ c->previous = c->next = NULL;
+ lst->first = lst->last = c;
+ return 0;
+ }
+
+ if (!iter) {
+ c->previous = lst->last;
+ c->previous->next = c;
+ c->next = NULL;
+ lst->last = c;
+ return 0;
+ }
+
+ c->previous = iter->previous;
+ c->next = iter;
+ c->next->previous = c;
+ if (c->previous)
+ c->previous->next = c;
+ else
+ lst->first = c;
+
+ return 0;
+}
+
+int clist_insert_after(clist * lst, clistiter * iter, void * data) {
+ clistcell * c;
+
+ c = (clistcell *) malloc(sizeof(clistcell));
+ if (!c) return -1;
+
+ c->data = data;
+ lst->count++;
+
+ if (clist_isempty(lst)) {
+ c->previous = c->next = NULL;
+ lst->first = lst->last = c;
+ return 0;
+ }
+
+ if (!iter) {
+ c->previous = lst->last;
+ c->previous->next = c;
+ c->next = NULL;
+ lst->last = c;
+ return 0;
+ }
+
+ c->previous = iter;
+ c->next = iter->next;
+ if (c->next)
+ c->next->previous = c;
+ else
+ lst->last = c;
+ c->previous->next = c;
+
+ return 0;
+}
+
+clistiter * clist_delete(clist * lst, clistiter * iter) {
+ clistiter * ret;
+
+ if (!iter) return NULL;
+
+ if (iter->previous)
+ iter->previous->next = iter->next;
+ else
+ lst->first = iter->next;
+
+ if (iter->next) {
+ iter->next->previous = iter->previous;
+ ret = iter->next;
+ } else {
+ lst->last = iter->previous;
+ ret = NULL;
+ }
+
+ free(iter);
+ lst->count--;
+
+ return ret;
+}
+
+
+
+void clist_foreach(clist * lst, clist_func func, void * data)
+{
+ clistiter * cur;
+
+ for(cur = clist_begin(lst) ; cur != NULL ; cur = cur->next)
+ func(cur->data, data);
+}
+
+void clist_concat(clist * dest, clist * src)
+{
+ if (src->first == NULL) {
+ /* do nothing */
+ }
+ else if (dest->last == NULL) {
+ dest->first = src->first;
+ dest->last = src->last;
+ }
+ else {
+ dest->last->next = src->first;
+ src->first->previous = dest->last;
+ dest->last = src->last;
+ }
+
+ dest->count += src->count;
+ src->last = src->first = NULL;
+}
+
+static inline clistiter * internal_clist_nth(clist * lst, int index)
+{
+ clistiter * cur;
+
+ cur = clist_begin(lst);
+ while ((index > 0) && (cur != NULL)) {
+ cur = cur->next;
+ index --;
+ }
+
+ if (cur == NULL)
+ return NULL;
+
+ return cur;
+}
+
+void * clist_nth_data(clist * lst, int index)
+{
+ clistiter * cur;
+
+ cur = internal_clist_nth(lst, index);
+ if (cur == NULL)
+ return NULL;
+
+ return cur->data;
+}
+
+clistiter * clist_nth(clist * lst, int index)
+{
+ return internal_clist_nth(lst, index);
+}
diff --git a/src/plugins/mailmbox/clist.h b/src/plugins/mailmbox/clist.h
@@ -0,0 +1,134 @@
+
+/*
+ * libEtPan! -- a mail stuff library
+ *
+ * clist - Implements simple generic double-linked pointer lists
+ *
+ * Copyright (c) 1999-2000, Gaël Roualland <gael.roualland@iname.com>
+ * interface changes - 2002 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef CLIST_H
+#define CLIST_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct clistcell_s {
+ void * data;
+ struct clistcell_s * previous;
+ struct clistcell_s * next;
+} clistcell;
+
+struct clist_s {
+ clistcell * first;
+ clistcell * last;
+ int count;
+};
+
+typedef struct clist_s clist;
+typedef clistcell clistiter;
+
+/* Allocate a new pointer list */
+clist * clist_new();
+
+/* Destroys a list. Data pointed by data pointers is NOT freed. */
+void clist_free(clist *);
+
+/* Some of the following routines can be implemented as macros to
+ be faster. If you don't want it, define NO_MACROS */
+#ifdef NO_MACROS
+
+/* Returns TRUE if list is empty */
+int clist_isempty(clist *);
+
+/* Returns the number of elements in the list */
+int clist_count(clist *);
+
+/* Returns an iterator to the first element of the list */
+clistiter * clist_begin(clist *);
+
+/* Returns an iterator to the last element of the list */
+clistiter * clist_end(clist *);
+
+/* Returns an iterator to the next element of the list */
+clistiter * clist_next(clistiter *);
+
+/* Returns an iterator to the previous element of the list */
+clistiter * clist_previous(clistiter *);
+
+/* Returns the data pointer of this element of the list */
+void* clist_content(clistiter *);
+
+/* Inserts this data pointer at the beginning of the list */
+int clist_prepend(clist *, void *);
+
+/* Inserts this data pointer at the end of the list */
+int clist_append(clist *, void *);
+#else
+#define clist_isempty(lst) ((lst->first==lst->last) && (lst->last==NULL))
+#define clist_count(lst) (lst->count)
+#define clist_begin(lst) (lst->first)
+#define clist_end(lst) (lst->last)
+#define clist_next(iter) (iter ? iter->next : NULL)
+#define clist_previous(iter) (iter ? iter->previous : NULL)
+#define clist_content(iter) (iter ? iter->data : NULL)
+#define clist_prepend(lst, data) (clist_insert_before(lst, lst->first, data))
+#define clist_append(lst, data) (clist_insert_after(lst, lst->last, data))
+#endif
+
+/* Inserts this data pointer before the element pointed by the iterator */
+int clist_insert_before(clist *, clistiter *, void *);
+
+/* Inserts this data pointer after the element pointed by the iterator */
+int clist_insert_after(clist *, clistiter *, void *);
+
+/* Deletes the element pointed by the iterator.
+ Returns an iterator to the next element. */
+clistiter * clist_delete(clist *, clistiter *);
+
+typedef void (* clist_func)(void *, void *);
+
+void clist_foreach(clist * lst, clist_func func, void * data);
+
+void clist_concat(clist * dest, clist * src);
+
+void * clist_nth_data(clist * lst, int index);
+
+clistiter * clist_nth(clist * lst, int index);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/plugins/mailmbox/mailimf.c b/src/plugins/mailmbox/mailimf.c
@@ -0,0 +1,7585 @@
+/*
+ * libEtPan! -- a mail stuff library
+ *
+ * Copyright (C) 2001, 2002 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#include "mailimf.h"
+
+/*
+ RFC 2822
+
+ RFC 2821 ...
+ A message-originating SMTP system SHOULD NOT send a message that
+ already contains a Return-path header. SMTP servers performing a
+ relay function MUST NOT inspect the message data, and especially not
+ to the extent needed to determine if Return-path headers are present.
+ SMTP servers making final delivery MAY remove Return-path headers
+ before adding their own.
+*/
+
+#include <ctype.h>
+#include <mmapstring.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+
+
+
+
+
+
+static inline int is_dtext(char ch);
+
+static int mailimf_quoted_pair_parse(const char * message, size_t length,
+ size_t * index, char * result);
+
+static int mailimf_ccontent_parse(const char * message, size_t length,
+ size_t * index);
+
+static int
+mailimf_comment_fws_ccontent_parse(const char * message, size_t length,
+ size_t * index);
+
+static inline int mailimf_comment_parse(const char * message, size_t length,
+ size_t * index);
+
+static int mailimf_qcontent_parse(const char * message, size_t length,
+ size_t * index, char * ch);
+
+static int mailimf_phrase_parse(const char * message, size_t length,
+ size_t * index, char ** result);
+
+static int mailimf_unstructured_parse(const char * message, size_t length,
+ size_t * index, char ** result);
+
+static int mailimf_ignore_unstructured_parse(const char * message, size_t length,
+ size_t * index);
+
+static int mailimf_day_of_week_parse(const char * message, size_t length,
+ size_t * index, int * result);
+
+static int mailimf_day_name_parse(const char * message, size_t length,
+ size_t * index, int * result);
+
+static int mailimf_date_parse(const char * message, size_t length,
+ size_t * index,
+ int * pday, int * pmonth, int * pyear);
+
+static int mailimf_year_parse(const char * message, size_t length,
+ size_t * index, int * result);
+
+static int mailimf_month_parse(const char * message, size_t length,
+ size_t * index, int * result);
+
+static int mailimf_month_name_parse(const char * message, size_t length,
+ size_t * index, int * result);
+
+static int mailimf_day_parse(const char * message, size_t length,
+ size_t * index, int * result);
+
+static int mailimf_time_parse(const char * message, size_t length,
+ size_t * index,
+ int * phour, int * pmin,
+ int * psec,
+ int * zone);
+static int mailimf_time_of_day_parse(const char * message, size_t length,
+ size_t * index,
+ int * phour, int * pmin,
+ int * psec);
+
+static int mailimf_hour_parse(const char * message, size_t length,
+ size_t * index, int * result);
+
+static int mailimf_minute_parse(const char * message, size_t length,
+ size_t * index, int * result);
+
+static int mailimf_second_parse(const char * message, size_t length,
+ size_t * index, int * result);
+
+static int mailimf_zone_parse(const char * message, size_t length,
+ size_t * index, int * result);
+
+static int mailimf_name_addr_parse(const char * message, size_t length,
+ size_t * index,
+ char ** pdisplay_name,
+ char ** pangle_addr);
+
+static int mailimf_angle_addr_parse(const char * message, size_t length,
+ size_t * index, char ** result);
+
+static int mailimf_group_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_group ** result);
+
+static int mailimf_display_name_parse(const char * message, size_t length,
+ size_t * index, char ** result);
+
+static int mailimf_addr_spec_parse(const char * message, size_t length,
+ size_t * index,
+ char ** address);
+
+#if 0
+static int mailimf_local_part_parse(const char * message, size_t length,
+ size_t * index,
+ char ** result);
+
+static int mailimf_domain_parse(const char * message, size_t length,
+ size_t * index,
+ char ** result);
+#endif
+
+#if 0
+static int mailimf_domain_literal_parse(const char * message, size_t length,
+ size_t * index, char ** result);
+#endif
+
+#if 0
+static int mailimf_dcontent_parse(const char * message, size_t length,
+ size_t * index, char * result);
+#endif
+
+static int
+mailimf_orig_date_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_orig_date ** result);
+
+static int
+mailimf_from_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_from ** result);
+
+static int
+mailimf_sender_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_sender ** result);
+
+static int
+mailimf_reply_to_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_reply_to ** result);
+
+static int
+mailimf_to_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_to ** result);
+
+static int
+mailimf_cc_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_cc ** result);
+
+static int
+mailimf_bcc_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_bcc ** result);
+
+static int mailimf_message_id_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_message_id ** result);
+
+static int
+mailimf_in_reply_to_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_in_reply_to ** result);
+
+#if 0
+static int mailimf_references_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_references **
+ result);
+#endif
+
+static int mailimf_unstrict_msg_id_parse(const char * message, size_t length,
+ size_t * index,
+ char ** result);
+
+#if 0
+static int mailimf_id_left_parse(const char * message, size_t length,
+ size_t * index, char ** result);
+
+static int mailimf_id_right_parse(const char * message, size_t length,
+ size_t * index, char ** result);
+#endif
+
+#if 0
+static int mailimf_no_fold_quote_parse(const char * message, size_t length,
+ size_t * index, char ** result);
+
+static int mailimf_no_fold_literal_parse(const char * message, size_t length,
+ size_t * index, char ** result);
+#endif
+
+static int mailimf_subject_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_subject ** result);
+
+static int mailimf_comments_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_comments ** result);
+
+static int mailimf_keywords_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_keywords ** result);
+
+static int
+mailimf_resent_date_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_orig_date ** result);
+
+static int
+mailimf_resent_from_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_from ** result);
+
+static int
+mailimf_resent_sender_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_sender ** result);
+
+static int
+mailimf_resent_to_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_to ** result);
+
+static int
+mailimf_resent_cc_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_cc ** result);
+
+static int
+mailimf_resent_bcc_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_bcc ** result);
+
+static int
+mailimf_resent_msg_id_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_message_id ** result);
+
+static int mailimf_return_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_return ** result);
+
+static int
+mailimf_path_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_path ** result);
+
+static int
+mailimf_optional_field_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_optional_field ** result);
+
+static int mailimf_field_name_parse(const char * message, size_t length,
+ size_t * index, char ** result);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/* *************************************************************** */
+
+static inline int is_digit(char ch)
+{
+ return (ch >= '0') && (ch <= '9');
+}
+
+static int mailimf_digit_parse(const char * message, size_t length,
+ size_t * index, int * result)
+{
+ size_t cur_token;
+
+ cur_token = * index;
+
+ if (cur_token >= length)
+ return MAILIMF_ERROR_PARSE;
+
+ if (is_digit(message[cur_token])) {
+ * result = message[cur_token] - '0';
+ cur_token ++;
+ * index = cur_token;
+ return MAILIMF_NO_ERROR;
+ }
+ else
+ return MAILIMF_ERROR_PARSE;
+}
+
+int
+mailimf_number_parse(const char * message, size_t length,
+ size_t * index, uint32_t * result)
+{
+ size_t cur_token;
+ int digit;
+ uint32_t number;
+ int parsed;
+ int r;
+
+ cur_token = * index;
+ parsed = FALSE;
+
+ number = 0;
+ while (1) {
+ r = mailimf_digit_parse(message, length, &cur_token, &digit);
+ if (r != MAILIMF_NO_ERROR) {
+ if (r == MAILIMF_ERROR_PARSE)
+ break;
+ else
+ return r;
+ }
+ number *= 10;
+ number += digit;
+ parsed = TRUE;
+ }
+
+ if (!parsed)
+ return MAILIMF_ERROR_PARSE;
+
+ * result = number;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+
+int mailimf_char_parse(const char * message, size_t length,
+ size_t * index, char token)
+{
+ size_t cur_token;
+
+ cur_token = * index;
+
+ if (cur_token >= length)
+ return MAILIMF_ERROR_PARSE;
+
+ if (message[cur_token] == token) {
+ cur_token ++;
+ * index = cur_token;
+ return MAILIMF_NO_ERROR;
+ }
+ else
+ return MAILIMF_ERROR_PARSE;
+}
+
+int mailimf_unstrict_char_parse(const char * message, size_t length,
+ size_t * index, char token)
+{
+ size_t cur_token;
+ int r;
+
+ cur_token = * index;
+
+ r = mailimf_cfws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
+ return r;
+
+ r = mailimf_char_parse(message, length, &cur_token, token);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+
+int
+mailimf_token_case_insensitive_len_parse(const char * message, size_t length,
+ size_t * index, char * token,
+ size_t token_length)
+{
+ size_t cur_token;
+
+ cur_token = * index;
+
+ if (cur_token + token_length - 1 >= length)
+ return MAILIMF_ERROR_PARSE;
+
+ if (strncasecmp(message + cur_token, token, token_length) == 0) {
+ cur_token += token_length;
+ * index = cur_token;
+ return MAILIMF_NO_ERROR;
+ }
+ else
+ return MAILIMF_ERROR_PARSE;
+}
+
+static int mailimf_oparenth_parse(const char * message, size_t length,
+ size_t * index)
+{
+ return mailimf_char_parse(message, length, index, '(');
+}
+
+static int mailimf_cparenth_parse(const char * message, size_t length,
+ size_t * index)
+{
+ return mailimf_char_parse(message, length, index, ')');
+}
+
+static int mailimf_comma_parse(const char * message, size_t length,
+ size_t * index)
+{
+ return mailimf_unstrict_char_parse(message, length, index, ',');
+}
+
+static int mailimf_dquote_parse(const char * message, size_t length,
+ size_t * index)
+{
+ return mailimf_char_parse(message, length, index, '\"');
+}
+
+static int mailimf_colon_parse(const char * message, size_t length,
+ size_t * index)
+{
+ return mailimf_unstrict_char_parse(message, length, index, ':');
+}
+
+static int mailimf_semi_colon_parse(const char * message, size_t length,
+ size_t * index)
+{
+ return mailimf_unstrict_char_parse(message, length, index, ';');
+}
+
+static int mailimf_plus_parse(const char * message, size_t length,
+ size_t * index)
+{
+ return mailimf_unstrict_char_parse(message, length, index, '+');
+}
+
+static int mailimf_minus_parse(const char * message, size_t length,
+ size_t * index)
+{
+ return mailimf_unstrict_char_parse(message, length, index, '-');
+}
+
+static int mailimf_lower_parse(const char * message, size_t length,
+ size_t * index)
+{
+ return mailimf_unstrict_char_parse(message, length, index, '<');
+}
+
+static int mailimf_greater_parse(const char * message, size_t length,
+ size_t * index)
+{
+ return mailimf_unstrict_char_parse(message, length, index, '>');
+}
+
+#if 0
+static int mailimf_obracket_parse(const char * message, size_t length,
+ size_t * index)
+{
+ return mailimf_unstrict_char_parse(message, length, index, '[');
+}
+
+static int mailimf_cbracket_parse(const char * message, size_t length,
+ size_t * index)
+{
+ return mailimf_unstrict_char_parse(message, length, index, ']');
+}
+#endif
+
+static int mailimf_at_sign_parse(const char * message, size_t length,
+ size_t * index)
+{
+ return mailimf_unstrict_char_parse(message, length, index, '@');
+}
+
+static int mailimf_point_parse(const char * message, size_t length,
+ size_t * index)
+{
+ return mailimf_unstrict_char_parse(message, length, index, '.');
+}
+
+int
+mailimf_custom_string_parse(const char * message, size_t length,
+ size_t * index, char ** result,
+ int (* is_custom_char)(char))
+{
+ size_t begin;
+ size_t end;
+ char * gstr;
+
+ begin = * index;
+
+ end = begin;
+
+ if (end >= length)
+ return MAILIMF_ERROR_PARSE;
+
+ while (is_custom_char(message[end])) {
+ end ++;
+ if (end >= length)
+ break;
+ }
+
+ if (end != begin) {
+ /*
+ gstr = strndup(message + begin, end - begin);
+ */
+ gstr = malloc(end - begin + 1);
+ if (gstr == NULL)
+ return MAILIMF_ERROR_MEMORY;
+ strncpy(gstr, message + begin, end - begin);
+ gstr[end - begin] = '\0';
+
+ * index = end;
+ * result = gstr;
+ return MAILIMF_NO_ERROR;
+ }
+ else
+ return MAILIMF_ERROR_PARSE;
+}
+
+
+
+
+
+
+
+typedef int mailimf_struct_parser(const char * message, size_t length,
+ size_t * index, void * result);
+
+typedef int mailimf_struct_destructor(void * result);
+
+
+static int
+mailimf_struct_multiple_parse(const char * message, size_t length,
+ size_t * index, clist ** result,
+ mailimf_struct_parser * parser,
+ mailimf_struct_destructor * destructor)
+{
+ clist * struct_list;
+ size_t cur_token;
+ void * value;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = parser(message, length, &cur_token, &value);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ struct_list = clist_new();
+ if (struct_list == NULL) {
+ destructor(value);
+ res = MAILIMF_ERROR_MEMORY;
+ goto err;
+ }
+
+ r = clist_append(struct_list, value);
+ if (r < 0) {
+ destructor(value);
+ res = MAILIMF_ERROR_MEMORY;
+ goto free;
+ }
+
+ while (1) {
+ r = parser(message, length, &cur_token, &value);
+ if (r != MAILIMF_NO_ERROR) {
+ if (r == MAILIMF_ERROR_PARSE)
+ break;
+ else {
+ res = r;
+ goto free;
+ }
+ }
+ r = clist_append(struct_list, value);
+ if (r < 0) {
+ (* destructor)(value);
+ res = MAILIMF_ERROR_MEMORY;
+ goto free;
+ }
+ }
+
+ * result = struct_list;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free:
+ clist_foreach(struct_list, (clist_func) destructor, NULL);
+ clist_free(struct_list);
+ err:
+ return res;
+}
+
+
+
+static int
+mailimf_struct_list_parse(const char * message, size_t length,
+ size_t * index, clist ** result,
+ char symbol,
+ mailimf_struct_parser * parser,
+ mailimf_struct_destructor * destructor)
+{
+ clist * struct_list;
+ size_t cur_token;
+ void * value;
+ size_t final_token;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = parser(message, length, &cur_token, &value);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ struct_list = clist_new();
+ if (struct_list == NULL) {
+ destructor(value);
+ res = MAILIMF_ERROR_MEMORY;
+ goto err;
+ }
+
+ r = clist_append(struct_list, value);
+ if (r < 0) {
+ destructor(value);
+ res = MAILIMF_ERROR_MEMORY;
+ goto free;
+ }
+
+ final_token = cur_token;
+
+ while (1) {
+ r = mailimf_unstrict_char_parse(message, length, &cur_token, symbol);
+ if (r != MAILIMF_NO_ERROR) {
+ if (r == MAILIMF_ERROR_PARSE)
+ break;
+ else {
+ res = r;
+ goto free;
+ }
+ }
+
+ r = parser(message, length, &cur_token, &value);
+ if (r != MAILIMF_NO_ERROR) {
+ if (r == MAILIMF_ERROR_PARSE)
+ break;
+ else {
+ res = r;
+ goto free;
+ }
+ }
+
+ r = clist_append(struct_list, value);
+ if (r < 0) {
+ destructor(value);
+ res = MAILIMF_ERROR_MEMORY;
+ goto free;
+ }
+
+ final_token = cur_token;
+ }
+
+ * result = struct_list;
+ * index = final_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free:
+ clist_foreach(struct_list, (clist_func) destructor, NULL);
+ clist_free(struct_list);
+ err:
+ return res;
+}
+
+static inline int mailimf_wsp_parse(const char * message, size_t length,
+ size_t * index)
+{
+ size_t cur_token;
+
+ cur_token = * index;
+
+ if (cur_token >= length)
+ return MAILIMF_ERROR_PARSE;
+
+ if ((message[cur_token] != ' ') && (message[cur_token] != '\t'))
+ return MAILIMF_ERROR_PARSE;
+
+ cur_token ++;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+int mailimf_crlf_parse(const char * message, size_t length, size_t * index)
+{
+ size_t cur_token;
+ int r;
+
+ cur_token = * index;
+
+ r = mailimf_char_parse(message, length, &cur_token, '\r');
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
+ return r;
+
+ r = mailimf_char_parse(message, length, &cur_token, '\n');
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ * index = cur_token;
+ return MAILIMF_NO_ERROR;
+}
+
+static int mailimf_unstrict_crlf_parse(const char * message,
+ size_t length, size_t * index)
+{
+ size_t cur_token;
+ int r;
+
+ cur_token = * index;
+
+ mailimf_cfws_parse(message, length, &cur_token);
+
+ r = mailimf_char_parse(message, length, &cur_token, '\r');
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
+ return r;
+
+ r = mailimf_char_parse(message, length, &cur_token, '\n');
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ * index = cur_token;
+ return MAILIMF_NO_ERROR;
+}
+
+/* ************************************************************************ */
+
+
+
+/* RFC 2822 grammar */
+
+/*
+NO-WS-CTL = %d1-8 / ; US-ASCII control characters
+ %d11 / ; that do not include the
+ %d12 / ; carriage return, line feed,
+ %d14-31 / ; and white space characters
+ %d127
+*/
+
+static inline int is_no_ws_ctl(char ch)
+{
+ if ((ch == 9) || (ch == 10) || (ch == 13))
+ return FALSE;
+
+ if (ch == 127)
+ return TRUE;
+
+ return (ch >= 1) && (ch <= 31);
+}
+
+/*
+text = %d1-9 / ; Characters excluding CR and LF
+ %d11 /
+ %d12 /
+ %d14-127 /
+ obs-text
+*/
+
+/*
+specials = "(" / ")" / ; Special characters used in
+ "<" / ">" / ; other parts of the syntax
+ "[" / "]" /
+ ":" / ";" /
+ "@" / "\" /
+ "," / "." /
+ DQUOTE
+*/
+
+/*
+quoted-pair = ("\" text) / obs-qp
+*/
+
+static inline int mailimf_quoted_pair_parse(const char * message, size_t length,
+ size_t * index, char * result)
+{
+ size_t cur_token;
+
+ cur_token = * index;
+
+ if (cur_token + 1 >= length)
+ return MAILIMF_ERROR_PARSE;
+
+ if (message[cur_token] != '\\')
+ return MAILIMF_ERROR_PARSE;
+
+ cur_token ++;
+ * result = message[cur_token];
+ cur_token ++;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+FWS = ([*WSP CRLF] 1*WSP) / ; Folding white space
+ obs-FWS
+*/
+
+int mailimf_fws_parse(const char * message, size_t length, size_t * index)
+{
+ size_t cur_token;
+ size_t final_token;
+ int fws_1;
+ int fws_2;
+ int fws_3;
+ int r;
+
+ cur_token = * index;
+
+ fws_1 = FALSE;
+ while (1) {
+ r = mailimf_wsp_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ if (r == MAILIMF_ERROR_PARSE)
+ break;
+ else
+ return r;
+ }
+ fws_1 = TRUE;
+ }
+ final_token = cur_token;
+
+ r = mailimf_crlf_parse(message, length, &cur_token);
+ switch (r) {
+ case MAILIMF_NO_ERROR:
+ fws_2 = TRUE;
+ break;
+ case MAILIMF_ERROR_PARSE:
+ fws_2 = FALSE;
+ break;
+ default:
+ return r;
+ }
+
+ fws_3 = FALSE;
+ if (fws_2) {
+ while (1) {
+ r = mailimf_wsp_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ if (r == MAILIMF_ERROR_PARSE)
+ break;
+ else
+ return r;
+ }
+ fws_3 = TRUE;
+ }
+ }
+
+ if ((!fws_1) && (!fws_3))
+ return MAILIMF_ERROR_PARSE;
+
+ if (!fws_3)
+ cur_token = final_token;
+
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+/*
+ctext = NO-WS-CTL / ; Non white space controls
+
+ %d33-39 / ; The rest of the US-ASCII
+ %d42-91 / ; characters not including "(",
+ %d93-126 ; ")", or "\"
+*/
+
+static inline int is_ctext(char ch)
+{
+ unsigned char uch = (unsigned char) ch;
+
+ if (is_no_ws_ctl(ch))
+ return TRUE;
+
+ if (uch < 33)
+ return FALSE;
+
+ if ((uch == 40) || (uch == 41))
+ return FALSE;
+
+ if (uch == 92)
+ return FALSE;
+
+ if (uch == 127)
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ccontent = ctext / quoted-pair / comment
+*/
+
+static inline int mailimf_ccontent_parse(const char * message, size_t length,
+ size_t * index)
+{
+ size_t cur_token;
+ char ch;
+ int r;
+
+ cur_token = * index;
+
+ if (cur_token >= length)
+ return MAILIMF_ERROR_PARSE;
+
+ if (is_ctext(message[cur_token])) {
+ cur_token ++;
+ }
+ else {
+ r = mailimf_quoted_pair_parse(message, length, &cur_token, &ch);
+
+ if (r == MAILIMF_ERROR_PARSE)
+ r = mailimf_comment_parse(message, length, &cur_token);
+
+ if (r == MAILIMF_ERROR_PARSE)
+ return r;
+ }
+
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+[FWS] ccontent
+*/
+
+static inline int
+mailimf_comment_fws_ccontent_parse(const char * message, size_t length,
+ size_t * index)
+{
+ size_t cur_token;
+ int r;
+
+ cur_token = * index;
+
+ r = mailimf_fws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
+ return r;
+
+ r = mailimf_ccontent_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+comment = "(" *([FWS] ccontent) [FWS] ")"
+*/
+
+static inline int mailimf_comment_parse(const char * message, size_t length,
+ size_t * index)
+{
+ size_t cur_token;
+ int r;
+
+ cur_token = * index;
+
+ r = mailimf_oparenth_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ while (1) {
+ r = mailimf_comment_fws_ccontent_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ if (r == MAILIMF_ERROR_PARSE)
+ break;
+ else
+ return r;
+ }
+ }
+
+ r = mailimf_fws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
+ return r;
+
+ r = mailimf_cparenth_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+[FWS] comment
+*/
+
+static inline int mailimf_cfws_fws_comment_parse(const char * message, size_t length,
+ size_t * index)
+{
+ size_t cur_token;
+ int r;
+
+ cur_token = * index;
+
+ r = mailimf_fws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
+ return r;
+
+ r = mailimf_comment_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+CFWS = *([FWS] comment) (([FWS] comment) / FWS)
+*/
+
+int mailimf_cfws_parse(const char * message, size_t length,
+ size_t * index)
+{
+ size_t cur_token;
+ int has_comment;
+ int r;
+
+ cur_token = * index;
+
+ has_comment = FALSE;
+ while (1) {
+ r = mailimf_cfws_fws_comment_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ if (r == MAILIMF_ERROR_PARSE)
+ break;
+ else
+ return r;
+ }
+ has_comment = TRUE;
+ }
+
+ if (!has_comment) {
+ r = mailimf_fws_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+atext = ALPHA / DIGIT / ; Any character except controls,
+ "!" / "#" / ; SP, and specials.
+ "$" / "%" / ; Used for atoms
+ "&" / "'" /
+ "*" / "+" /
+ "-" / "/" /
+ "=" / "?" /
+ "^" / "_" /
+ "`" / "{" /
+ "|" / "}" /
+ "~"
+*/
+
+static inline int is_atext(char ch)
+{
+ switch (ch) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+#if 0
+ case '(':
+ case ')':
+#endif
+ case '<':
+ case '>':
+#if 0
+ case '@':
+#endif
+ case ',':
+ case '"':
+ case ':':
+ case ';':
+ return FALSE;
+ default:
+ return TRUE;
+ }
+}
+
+/*
+atom = [CFWS] 1*atext [CFWS]
+*/
+
+int mailimf_atom_parse(const char * message, size_t length,
+ size_t * index, char ** result)
+{
+ size_t cur_token;
+ int r;
+ int res;
+ char * atom;
+ size_t end;
+
+ cur_token = * index;
+
+ r = mailimf_cfws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE)) {
+ res = r;
+ goto err;
+ }
+
+ end = cur_token;
+ if (end >= length) {
+ res = MAILIMF_ERROR_PARSE;
+ goto err;
+ }
+
+ while (is_atext(message[end])) {
+ end ++;
+ if (end >= length)
+ break;
+ }
+ if (end == cur_token) {
+ res = MAILIMF_ERROR_PARSE;
+ goto err;
+ }
+
+ atom = malloc(end - cur_token + 1);
+ if (atom == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto err;
+ }
+ strncpy(atom, message + cur_token, end - cur_token);
+ atom[end - cur_token] = '\0';
+
+ cur_token = end;
+
+ * index = cur_token;
+ * result = atom;
+
+ return MAILIMF_NO_ERROR;
+
+ err:
+ return res;
+}
+
+int mailimf_fws_atom_parse(const char * message, size_t length,
+ size_t * index, char ** result)
+{
+ size_t cur_token;
+ int r;
+ int res;
+ char * atom;
+ size_t end;
+
+ cur_token = * index;
+
+ r = mailimf_fws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE)) {
+ res = r;
+ goto err;
+ }
+
+ end = cur_token;
+ if (end >= length) {
+ res = MAILIMF_ERROR_PARSE;
+ goto err;
+ }
+
+ while (is_atext(message[end])) {
+ end ++;
+ if (end >= length)
+ break;
+ }
+ if (end == cur_token) {
+ res = MAILIMF_ERROR_PARSE;
+ goto err;
+ }
+
+ atom = malloc(end - cur_token + 1);
+ if (atom == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto err;
+ }
+ strncpy(atom, message + cur_token, end - cur_token);
+ atom[end - cur_token] = '\0';
+
+ cur_token = end;
+
+ * index = cur_token;
+ * result = atom;
+
+ return MAILIMF_NO_ERROR;
+
+ err:
+ return res;
+}
+
+/*
+dot-atom = [CFWS] dot-atom-text [CFWS]
+*/
+
+#if 0
+static int mailimf_dot_atom_parse(const char * message, size_t length,
+ size_t * index, char ** result)
+{
+ return mailimf_atom_parse(message, length, index, result);
+}
+#endif
+
+/*
+dot-atom-text = 1*atext *("." 1*atext)
+*/
+
+#if 0
+static int
+mailimf_dot_atom_text_parse(const char * message, size_t length,
+ size_t * index, char ** result)
+{
+ return mailimf_atom_parse(message, length, index, result);
+}
+#endif
+
+/*
+qtext = NO-WS-CTL / ; Non white space controls
+
+ %d33 / ; The rest of the US-ASCII
+ %d35-91 / ; characters not including "\"
+ %d93-126 ; or the quote character
+*/
+
+static inline int is_qtext(char ch)
+{
+ unsigned char uch = (unsigned char) ch;
+
+ if (is_no_ws_ctl(ch))
+ return TRUE;
+
+ if (uch < 33)
+ return FALSE;
+
+ if (uch == 34)
+ return FALSE;
+
+ if (uch == 92)
+ return FALSE;
+
+ if (uch == 127)
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+qcontent = qtext / quoted-pair
+*/
+
+static int mailimf_qcontent_parse(const char * message, size_t length,
+ size_t * index, char * result)
+{
+ size_t cur_token;
+ char ch;
+ int r;
+
+ cur_token = * index;
+
+ if (cur_token >= length)
+ return MAILIMF_ERROR_PARSE;
+
+ if (is_qtext(message[cur_token])) {
+ ch = message[cur_token];
+ cur_token ++;
+ }
+ else {
+ r = mailimf_quoted_pair_parse(message, length, &cur_token, &ch);
+
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+
+ * result = ch;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+quoted-string = [CFWS]
+ DQUOTE *([FWS] qcontent) [FWS] DQUOTE
+ [CFWS]
+*/
+
+int mailimf_quoted_string_parse(const char * message, size_t length,
+ size_t * index, char ** result)
+{
+ size_t cur_token;
+ MMAPString * gstr;
+ char ch;
+ char * str;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_cfws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE)) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_dquote_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ gstr = mmap_string_new("");
+ if (gstr == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto err;
+ }
+
+#if 0
+ if (mmap_string_append_c(gstr, '\"') == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_gstr;
+ }
+#endif
+
+ while (1) {
+ r = mailimf_fws_parse(message, length, &cur_token);
+ if (r == MAILIMF_NO_ERROR) {
+ if (mmap_string_append_c(gstr, ' ') == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_gstr;
+ }
+ }
+ else if (r != MAILIMF_ERROR_PARSE) {
+ res = r;
+ goto free_gstr;
+ }
+
+ r = mailimf_qcontent_parse(message, length, &cur_token, &ch);
+ if (r == MAILIMF_NO_ERROR) {
+ if (mmap_string_append_c(gstr, ch) == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_gstr;
+ }
+ }
+ else if (r == MAILIMF_ERROR_PARSE)
+ break;
+ else {
+ res = r;
+ goto free_gstr;
+ }
+ }
+
+ r = mailimf_dquote_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_gstr;
+ }
+
+#if 0
+ if (mmap_string_append_c(gstr, '\"') == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_gstr;
+ }
+#endif
+
+ str = strdup(gstr->str);
+ if (str == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_gstr;
+ }
+ mmap_string_free(gstr);
+
+ * index = cur_token;
+ * result = str;
+
+ return MAILIMF_NO_ERROR;
+
+ free_gstr:
+ mmap_string_free(gstr);
+ err:
+ return res;
+}
+
+int mailimf_fws_quoted_string_parse(const char * message, size_t length,
+ size_t * index, char ** result)
+{
+ size_t cur_token;
+ MMAPString * gstr;
+ char ch;
+ char * str;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_fws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE)) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_dquote_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ gstr = mmap_string_new("");
+ if (gstr == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto err;
+ }
+
+#if 0
+ if (mmap_string_append_c(gstr, '\"') == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_gstr;
+ }
+#endif
+
+ while (1) {
+ r = mailimf_fws_parse(message, length, &cur_token);
+ if (r == MAILIMF_NO_ERROR) {
+ if (mmap_string_append_c(gstr, ' ') == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_gstr;
+ }
+ }
+ else if (r != MAILIMF_ERROR_PARSE) {
+ res = r;
+ goto free_gstr;
+ }
+
+ r = mailimf_qcontent_parse(message, length, &cur_token, &ch);
+ if (r == MAILIMF_NO_ERROR) {
+ if (mmap_string_append_c(gstr, ch) == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_gstr;
+ }
+ }
+ else if (r == MAILIMF_ERROR_PARSE)
+ break;
+ else {
+ res = r;
+ goto free_gstr;
+ }
+ }
+
+ r = mailimf_dquote_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_gstr;
+ }
+
+#if 0
+ if (mmap_string_append_c(gstr, '\"') == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_gstr;
+ }
+#endif
+
+ str = strdup(gstr->str);
+ if (str == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_gstr;
+ }
+ mmap_string_free(gstr);
+
+ * index = cur_token;
+ * result = str;
+
+ return MAILIMF_NO_ERROR;
+
+ free_gstr:
+ mmap_string_free(gstr);
+ err:
+ return res;
+}
+
+/*
+word = atom / quoted-string
+*/
+
+int mailimf_word_parse(const char * message, size_t length,
+ size_t * index, char ** result)
+{
+ size_t cur_token;
+ char * word;
+ int r;
+
+ cur_token = * index;
+
+ r = mailimf_atom_parse(message, length, &cur_token, &word);
+
+ if (r == MAILIMF_ERROR_PARSE)
+ r = mailimf_quoted_string_parse(message, length, &cur_token, &word);
+
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ * result = word;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+
+int mailimf_fws_word_parse(const char * message, size_t length,
+ size_t * index, char ** result)
+{
+ size_t cur_token;
+ char * word;
+ int r;
+
+ cur_token = * index;
+
+ r = mailimf_fws_atom_parse(message, length, &cur_token, &word);
+
+ if (r == MAILIMF_ERROR_PARSE)
+ r = mailimf_fws_quoted_string_parse(message, length, &cur_token, &word);
+
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ * result = word;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+phrase = 1*word / obs-phrase
+*/
+
+static int mailimf_phrase_parse(const char * message, size_t length,
+ size_t * index, char ** result)
+{
+ MMAPString * gphrase;
+ char * word;
+ int first;
+ size_t cur_token;
+ int r;
+ int res;
+ char * str;
+
+ cur_token = * index;
+
+ gphrase = mmap_string_new("");
+ if (gphrase == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto err;
+ }
+
+ first = TRUE;
+
+ while (1) {
+ r = mailimf_fws_word_parse(message, length, &cur_token, &word);
+ if (r == MAILIMF_NO_ERROR) {
+ if (!first) {
+ if (mmap_string_append_c(gphrase, ' ') == NULL) {
+ mailimf_word_free(word);
+ res = MAILIMF_ERROR_MEMORY;
+ goto free;
+ }
+ }
+ if (mmap_string_append(gphrase, word) == NULL) {
+ mailimf_word_free(word);
+ res = MAILIMF_ERROR_MEMORY;
+ goto free;
+ }
+ mailimf_word_free(word);
+ first = FALSE;
+ }
+ else if (r == MAILIMF_ERROR_PARSE)
+ break;
+ else {
+ res = r;
+ goto free;
+ }
+ }
+
+ if (first) {
+ res = MAILIMF_ERROR_PARSE;
+ goto free;
+ }
+
+ str = strdup(gphrase->str);
+ if (str == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free;
+ }
+ mmap_string_free(gphrase);
+
+ * result = str;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free:
+ mmap_string_free(gphrase);
+ err:
+ return res;
+}
+
+/*
+utext = NO-WS-CTL / ; Non white space controls
+ %d33-126 / ; The rest of US-ASCII
+ obs-utext
+
+added : WSP
+*/
+
+enum {
+ UNSTRUCTURED_START,
+ UNSTRUCTURED_CR,
+ UNSTRUCTURED_LF,
+ UNSTRUCTURED_WSP,
+ UNSTRUCTURED_OUT
+};
+
+static int mailimf_unstructured_parse(const char * message, size_t length,
+ size_t * index, char ** result)
+{
+ size_t cur_token;
+ int state;
+ size_t begin;
+ size_t terminal;
+ char * str;
+
+ cur_token = * index;
+
+
+ while (1) {
+ int r;
+
+ r = mailimf_wsp_parse(message, length, &cur_token);
+ if (r == MAILIMF_NO_ERROR) {
+ /* do nothing */
+ }
+ else if (r == MAILIMF_ERROR_PARSE)
+ break;
+ else {
+ return r;
+ }
+ }
+
+ state = UNSTRUCTURED_START;
+ begin = cur_token;
+ terminal = cur_token;
+
+ while (state != UNSTRUCTURED_OUT) {
+
+ switch(state) {
+ case UNSTRUCTURED_START:
+ if (cur_token >= length)
+ return MAILIMF_ERROR_PARSE;
+
+ terminal = cur_token;
+ switch(message[cur_token]) {
+ case '\r':
+ state = UNSTRUCTURED_CR;
+ break;
+ case '\n':
+ state = UNSTRUCTURED_LF;
+ break;
+ default:
+ state = UNSTRUCTURED_START;
+ break;
+ }
+ break;
+ case UNSTRUCTURED_CR:
+ if (cur_token >= length)
+ return MAILIMF_ERROR_PARSE;
+
+ switch(message[cur_token]) {
+ case '\n':
+ state = UNSTRUCTURED_LF;
+ break;
+ default:
+ state = UNSTRUCTURED_START;
+ break;
+ }
+ break;
+
+ case UNSTRUCTURED_LF:
+ if (cur_token >= length) {
+ state = UNSTRUCTURED_OUT;
+ break;
+ }
+
+ switch(message[cur_token]) {
+ case '\t':
+ case ' ':
+ state = UNSTRUCTURED_WSP;
+ break;
+ default:
+ state = UNSTRUCTURED_OUT;
+ break;
+ }
+ break;
+ case UNSTRUCTURED_WSP:
+ if (cur_token >= length)
+ return MAILIMF_ERROR_PARSE;
+
+ switch(message[cur_token]) {
+ case '\r':
+ state = UNSTRUCTURED_CR;
+ break;
+ case '\n':
+ state = UNSTRUCTURED_LF;
+ break;
+ default:
+ state = UNSTRUCTURED_START;
+ break;
+ }
+ break;
+ }
+
+ cur_token ++;
+ }
+
+ str = malloc(terminal - begin + 1);
+ if (str == NULL)
+ return MAILIMF_ERROR_MEMORY;
+ strncpy(str, message + begin, terminal - begin);
+ str[terminal - begin] = '\0';
+
+ * index = terminal;
+ * result = str;
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+static int mailimf_ignore_unstructured_parse(const char * message, size_t length,
+ size_t * index)
+{
+ size_t cur_token;
+ int state;
+ size_t terminal;
+
+ cur_token = * index;
+
+ state = UNSTRUCTURED_START;
+ terminal = cur_token;
+
+ while (state != UNSTRUCTURED_OUT) {
+
+ switch(state) {
+ case UNSTRUCTURED_START:
+ if (cur_token >= length)
+ return MAILIMF_ERROR_PARSE;
+ terminal = cur_token;
+ switch(message[cur_token]) {
+ case '\r':
+ state = UNSTRUCTURED_CR;
+ break;
+ case '\n':
+ state = UNSTRUCTURED_LF;
+ break;
+ default:
+ state = UNSTRUCTURED_START;
+ break;
+ }
+ break;
+ case UNSTRUCTURED_CR:
+ if (cur_token >= length)
+ return MAILIMF_ERROR_PARSE;
+ switch(message[cur_token]) {
+ case '\n':
+ state = UNSTRUCTURED_LF;
+ break;
+ default:
+ state = UNSTRUCTURED_START;
+ break;
+ }
+ break;
+ case UNSTRUCTURED_LF:
+ if (cur_token >= length) {
+ state = UNSTRUCTURED_OUT;
+ break;
+ }
+ switch(message[cur_token]) {
+ case '\t':
+ case ' ':
+ state = UNSTRUCTURED_WSP;
+ break;
+ default:
+ state = UNSTRUCTURED_OUT;
+ break;
+ }
+ break;
+ case UNSTRUCTURED_WSP:
+ if (cur_token >= length)
+ return MAILIMF_ERROR_PARSE;
+ switch(message[cur_token]) {
+ case '\r':
+ state = UNSTRUCTURED_CR;
+ break;
+ case '\n':
+ state = UNSTRUCTURED_LF;
+ break;
+ default:
+ state = UNSTRUCTURED_START;
+ break;
+ }
+ break;
+ }
+
+ cur_token ++;
+ }
+
+ * index = terminal;
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+int mailimf_ignore_field_parse(const char * message, size_t length,
+ size_t * index)
+{
+ int has_field;
+ size_t cur_token;
+ int state;
+ size_t terminal;
+
+ has_field = FALSE;
+ cur_token = * index;
+
+ terminal = cur_token;
+ state = UNSTRUCTURED_START;
+
+ /* check if this is not a beginning CRLF */
+
+ if (cur_token >= length)
+ return MAILIMF_ERROR_PARSE;
+
+ switch (message[cur_token]) {
+ case '\r':
+ return MAILIMF_ERROR_PARSE;
+ case '\n':
+ return MAILIMF_ERROR_PARSE;
+ }
+
+ while (state != UNSTRUCTURED_OUT) {
+
+ switch(state) {
+ case UNSTRUCTURED_START:
+ if (cur_token >= length)
+ return MAILIMF_ERROR_PARSE;
+
+ switch(message[cur_token]) {
+ case '\r':
+ state = UNSTRUCTURED_CR;
+ break;
+ case '\n':
+ state = UNSTRUCTURED_LF;
+ break;
+ case ':':
+ has_field = TRUE;
+ state = UNSTRUCTURED_START;
+ break;
+ default:
+ state = UNSTRUCTURED_START;
+ break;
+ }
+ break;
+ case UNSTRUCTURED_CR:
+ if (cur_token >= length)
+ return MAILIMF_ERROR_PARSE;
+
+ switch(message[cur_token]) {
+ case '\n':
+ state = UNSTRUCTURED_LF;
+ break;
+ case ':':
+ has_field = TRUE;
+ state = UNSTRUCTURED_START;
+ break;
+ default:
+ state = UNSTRUCTURED_START;
+ break;
+ }
+ break;
+ case UNSTRUCTURED_LF:
+ if (cur_token >= length) {
+ terminal = cur_token;
+ state = UNSTRUCTURED_OUT;
+ break;
+ }
+
+ switch(message[cur_token]) {
+ case '\t':
+ case ' ':
+ state = UNSTRUCTURED_WSP;
+ break;
+ default:
+ terminal = cur_token;
+ state = UNSTRUCTURED_OUT;
+ break;
+ }
+ break;
+ case UNSTRUCTURED_WSP:
+ if (cur_token >= length)
+ return MAILIMF_ERROR_PARSE;
+
+ switch(message[cur_token]) {
+ case '\r':
+ state = UNSTRUCTURED_CR;
+ break;
+ case '\n':
+ state = UNSTRUCTURED_LF;
+ break;
+ case ':':
+ has_field = TRUE;
+ state = UNSTRUCTURED_START;
+ break;
+ default:
+ state = UNSTRUCTURED_START;
+ break;
+ }
+ break;
+ }
+
+ cur_token ++;
+ }
+
+ if (!has_field)
+ return MAILIMF_ERROR_PARSE;
+
+ * index = terminal;
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+/*
+date-time = [ day-of-week "," ] date FWS time [CFWS]
+*/
+
+int mailimf_date_time_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_date_time ** result)
+{
+ size_t cur_token;
+ int day_of_week;
+ struct mailimf_date_time * date_time;
+ int day;
+ int month;
+ int year;
+ int hour;
+ int min;
+ int sec;
+ int zone;
+ int r;
+
+ cur_token = * index;
+
+ day_of_week = -1;
+ r = mailimf_day_of_week_parse(message, length, &cur_token, &day_of_week);
+ if (r == MAILIMF_NO_ERROR) {
+ r = mailimf_comma_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+ else if (r != MAILIMF_ERROR_PARSE)
+ return r;
+
+ r = mailimf_date_parse(message, length, &cur_token, &day, &month, &year);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_fws_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_time_parse(message, length, &cur_token,
+ &hour, &min, &sec, &zone);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ date_time = mailimf_date_time_new(day, month, year, hour, min, sec, zone);
+ if (date_time == NULL)
+ return MAILIMF_ERROR_MEMORY;
+
+ * index = cur_token;
+ * result = date_time;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+day-of-week = ([FWS] day-name) / obs-day-of-week
+*/
+
+static int mailimf_day_of_week_parse(const char * message, size_t length,
+ size_t * index, int * result)
+{
+ size_t cur_token;
+ int day_of_week;
+ int r;
+
+ cur_token = * index;
+
+ r = mailimf_cfws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
+ return r;
+
+ r = mailimf_day_name_parse(message, length, &cur_token, &day_of_week);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ * index = cur_token;
+ * result = day_of_week;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+day-name = "Mon" / "Tue" / "Wed" / "Thu" /
+ "Fri" / "Sat" / "Sun"
+*/
+
+struct mailimf_token_value {
+ int value;
+ char * str;
+};
+
+static struct mailimf_token_value day_names[] = {
+ {1, "Mon"},
+ {2, "Tue"},
+ {3, "Wed"},
+ {4, "Thu"},
+ {5, "Fri"},
+ {6, "Sat"},
+ {7, "Sun"},
+};
+
+enum {
+ DAY_NAME_START,
+ DAY_NAME_T,
+ DAY_NAME_S
+};
+
+static int guess_day_name(const char * message, size_t length, size_t index)
+{
+ int state;
+
+ state = DAY_NAME_START;
+
+ while (1) {
+
+ if (index >= length)
+ return -1;
+
+ switch(state) {
+ case DAY_NAME_START:
+ switch((char) toupper((unsigned char) message[index])) {
+ case 'M': /* Mon */
+ return 1;
+ break;
+ case 'T': /* Tue Thu */
+ state = DAY_NAME_T;
+ break;
+ case 'W': /* Wed */
+ return 3;
+ case 'F':
+ return 5;
+ case 'S': /* Sat Sun */
+ state = DAY_NAME_S;
+ break;
+ default:
+ return -1;
+ }
+ break;
+ case DAY_NAME_T:
+ switch((char) toupper((unsigned char) message[index])) {
+ case 'U':
+ return 2;
+ case 'H':
+ return 4;
+ default:
+ return -1;
+ }
+ break;
+ case DAY_NAME_S:
+ switch((char) toupper((unsigned char) message[index])) {
+ case 'A':
+ return 6;
+ case 'U':
+ return 7;
+ default:
+ return -1;
+ }
+ break;
+ }
+
+ index ++;
+ }
+}
+
+static int mailimf_day_name_parse(const char * message, size_t length,
+ size_t * index, int * result)
+{
+ size_t cur_token;
+ int day_of_week;
+ int guessed_day;
+ int r;
+
+ cur_token = * index;
+
+ guessed_day = guess_day_name(message, length, cur_token);
+ if (guessed_day == -1)
+ return MAILIMF_ERROR_PARSE;
+
+ r = mailimf_token_case_insensitive_parse(message, length,
+ &cur_token,
+ day_names[guessed_day - 1].str);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ day_of_week = guessed_day;
+
+ * result = day_of_week;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+date = day month year
+*/
+
+static int mailimf_date_parse(const char * message, size_t length,
+ size_t * index,
+ int * pday, int * pmonth, int * pyear)
+{
+ size_t cur_token;
+ int day;
+ int month;
+ int year;
+ int r;
+
+ cur_token = * index;
+
+ r = mailimf_day_parse(message, length, &cur_token, &day);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_month_parse(message, length, &cur_token, &month);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_year_parse(message, length, &cur_token, &year);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ * pday = day;
+ * pmonth = month;
+ * pyear = year;
+
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+year = 4*DIGIT / obs-year
+*/
+
+static int mailimf_year_parse(const char * message, size_t length,
+ size_t * index, int * result)
+{
+ uint32_t number;
+ size_t cur_token;
+ int r;
+
+ cur_token = * index;
+
+ r = mailimf_cfws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
+ return r;
+
+ r = mailimf_number_parse(message, length, &cur_token, &number);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ * index = cur_token;
+ * result = number;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+month = (FWS month-name FWS) / obs-month
+*/
+
+static int mailimf_month_parse(const char * message, size_t length,
+ size_t * index, int * result)
+{
+ size_t cur_token;
+ int month;
+ int r;
+
+ cur_token = * index;
+
+ r = mailimf_cfws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
+ return r;
+
+ r = mailimf_month_name_parse(message, length, &cur_token, &month);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ * result = month;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+month-name = "Jan" / "Feb" / "Mar" / "Apr" /
+ "May" / "Jun" / "Jul" / "Aug" /
+ "Sep" / "Oct" / "Nov" / "Dec"
+*/
+
+static struct mailimf_token_value month_names[] = {
+ {1, "Jan"},
+ {2, "Feb"},
+ {3, "Mar"},
+ {4, "Apr"},
+ {5, "May"},
+ {6, "Jun"},
+ {7, "Jul"},
+ {8, "Aug"},
+ {9, "Sep"},
+ {10, "Oct"},
+ {11, "Nov"},
+ {12, "Dec"},
+};
+
+enum {
+ MONTH_START,
+ MONTH_J,
+ MONTH_JU,
+ MONTH_M,
+ MONTH_MA,
+ MONTH_A
+};
+
+static int guess_month(const char * message, size_t length, size_t index)
+{
+ int state;
+
+ state = MONTH_START;
+
+ while (1) {
+
+ if (index >= length)
+ return -1;
+
+ switch(state) {
+ case MONTH_START:
+ switch((char) toupper((unsigned char) message[index])) {
+ case 'J': /* Jan Jun Jul */
+ state = MONTH_J;
+ break;
+ case 'F': /* Feb */
+ return 2;
+ case 'M': /* Mar May */
+ state = MONTH_M;
+ break;
+ case 'A': /* Apr Aug */
+ state = MONTH_A;
+ break;
+ case 'S': /* Sep */
+ return 9;
+ case 'O': /* Oct */
+ return 10;
+ case 'N': /* Nov */
+ return 11;
+ case 'D': /* Dec */
+ return 12;
+ default:
+ return -1;
+ }
+ break;
+ case MONTH_J:
+ switch((char) toupper((unsigned char) message[index])) {
+ case 'A':
+ return 1;
+ case 'U':
+ state = MONTH_JU;
+ break;
+ default:
+ return -1;
+ }
+ break;
+ case MONTH_JU:
+ switch((char) toupper((unsigned char) message[index])) {
+ case 'N':
+ return 6;
+ case 'L':
+ return 7;
+ default:
+ return -1;
+ }
+ break;
+ case MONTH_M:
+ switch((char) toupper((unsigned char) message[index])) {
+ case 'A':
+ state = MONTH_MA;
+ break;
+ default:
+ return -1;
+ }
+ break;
+ case MONTH_MA:
+ switch((char) toupper((unsigned char) message[index])) {
+ case 'Y':
+ return 5;
+ case 'R':
+ return 3;
+ default:
+ return -1;
+ }
+ break;
+ case MONTH_A:
+ switch((char) toupper((unsigned char) message[index])) {
+ case 'P':
+ return 4;
+ case 'U':
+ return 8;
+ default:
+ return -1;
+ }
+ break;
+ }
+
+ index ++;
+ }
+}
+
+static int mailimf_month_name_parse(const char * message, size_t length,
+ size_t * index, int * result)
+{
+ size_t cur_token;
+ int month;
+ int guessed_month;
+ int r;
+
+ cur_token = * index;
+
+ guessed_month = guess_month(message, length, cur_token);
+ if (guessed_month == -1)
+ return MAILIMF_ERROR_PARSE;
+
+ r = mailimf_token_case_insensitive_parse(message, length,
+ &cur_token,
+ month_names[guessed_month - 1].str);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ month = guessed_month;
+
+ * result = month;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+day = ([FWS] 1*2DIGIT) / obs-day
+*/
+
+static int mailimf_day_parse(const char * message, size_t length,
+ size_t * index, int * result)
+{
+ size_t cur_token;
+ uint32_t day;
+ int r;
+
+ cur_token = * index;
+
+ r = mailimf_cfws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
+ return r;
+
+ r = mailimf_number_parse(message, length, &cur_token, &day);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ * result = day;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+time = time-of-day FWS zone
+*/
+
+static int mailimf_time_parse(const char * message, size_t length,
+ size_t * index,
+ int * phour, int * pmin,
+ int * psec,
+ int * pzone)
+{
+ size_t cur_token;
+ int hour;
+ int min;
+ int sec;
+ int zone;
+ int r;
+
+ cur_token = * index;
+
+ r = mailimf_cfws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
+ return r;
+
+ r = mailimf_time_of_day_parse(message, length, &cur_token,
+ &hour, &min, &sec);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_fws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
+ return r;
+
+ r = mailimf_zone_parse(message, length, &cur_token, &zone);
+ if (r == MAILIMF_NO_ERROR) {
+ /* do nothing */
+ }
+ else if (r == MAILIMF_ERROR_PARSE) {
+ zone = 0;
+ }
+ else {
+ return r;
+ }
+
+ * phour = hour;
+ * pmin = min;
+ * psec = sec;
+ * pzone = zone;
+
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+time-of-day = hour ":" minute [ ":" second ]
+*/
+
+static int mailimf_time_of_day_parse(const char * message, size_t length,
+ size_t * index,
+ int * phour, int * pmin,
+ int * psec)
+{
+ int hour;
+ int min;
+ int sec;
+ size_t cur_token;
+ int r;
+
+ cur_token = * index;
+
+ r = mailimf_hour_parse(message, length, &cur_token, &hour);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_minute_parse(message, length, &cur_token, &min);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r == MAILIMF_NO_ERROR) {
+ r = mailimf_second_parse(message, length, &cur_token, &sec);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+ else if (r == MAILIMF_ERROR_PARSE)
+ sec = 0;
+ else
+ return r;
+
+ * phour = hour;
+ * pmin = min;
+ * psec = sec;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+hour = 2DIGIT / obs-hour
+*/
+
+static int mailimf_hour_parse(const char * message, size_t length,
+ size_t * index, int * result)
+{
+ uint32_t hour;
+ int r;
+
+ r = mailimf_number_parse(message, length, index, &hour);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ * result = hour;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+minute = 2DIGIT / obs-minute
+*/
+
+static int mailimf_minute_parse(const char * message, size_t length,
+ size_t * index, int * result)
+{
+ uint32_t minute;
+ int r;
+
+ r = mailimf_number_parse(message, length, index, &minute);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ * result = minute;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+second = 2DIGIT / obs-second
+*/
+
+static int mailimf_second_parse(const char * message, size_t length,
+ size_t * index, int * result)
+{
+ uint32_t second;
+ int r;
+
+ r = mailimf_number_parse(message, length, index, &second);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ * result = second;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+zone = (( "+" / "-" ) 4DIGIT) / obs-zone
+*/
+
+/*
+obs-zone = "UT" / "GMT" / ; Universal Time
+ ; North American UT
+ ; offsets
+ "EST" / "EDT" / ; Eastern: - 5/ - 4
+ "CST" / "CDT" / ; Central: - 6/ - 5
+ "MST" / "MDT" / ; Mountain: - 7/ - 6
+ "PST" / "PDT" / ; Pacific: - 8/ - 7
+
+ %d65-73 / ; Military zones - "A"
+ %d75-90 / ; through "I" and "K"
+ %d97-105 / ; through "Z", both
+ %d107-122 ; upper and lower case
+*/
+
+enum {
+ STATE_ZONE_1 = 0,
+ STATE_ZONE_2 = 1,
+ STATE_ZONE_3 = 2,
+ STATE_ZONE_OK = 3,
+ STATE_ZONE_ERR = 4,
+ STATE_ZONE_CONT = 5,
+};
+
+static int mailimf_zone_parse(const char * message, size_t length,
+ size_t * index, int * result)
+{
+ uint32_t zone;
+ int sign;
+ size_t cur_token;
+ int r;
+
+ cur_token = * index;
+
+ if (cur_token + 1 < length) {
+ if ((message[cur_token] == 'U') && (message[cur_token] == 'T')) {
+ * result = TRUE;
+ * index = cur_token + 2;
+
+ return MAILIMF_NO_ERROR;
+ }
+ }
+
+ if (cur_token + 2 < length) {
+ int state;
+
+ state = STATE_ZONE_1;
+
+ while (state <= 2) {
+ switch (state) {
+ case STATE_ZONE_1:
+ switch (message[cur_token]) {
+ case 'G':
+ if (message[cur_token + 1] == 'M' && message[cur_token + 2] == 'T') {
+ zone = 0;
+ state = STATE_ZONE_OK;
+ }
+ else {
+ state = STATE_ZONE_ERR;
+ }
+ break;
+ case 'E':
+ zone = -5;
+ state = STATE_ZONE_2;
+ break;
+ case 'C':
+ zone = -6;
+ state = STATE_ZONE_2;
+ break;
+ case 'M':
+ zone = -7;
+ state = STATE_ZONE_2;
+ break;
+ case 'P':
+ zone = -8;
+ state = STATE_ZONE_2;
+ break;
+ default:
+ state = STATE_ZONE_CONT;
+ break;
+ }
+ break;
+ case STATE_ZONE_2:
+ switch (message[cur_token + 1]) {
+ case 'S':
+ state = STATE_ZONE_3;
+ break;
+ case 'D':
+ zone ++;
+ state = STATE_ZONE_3;
+ break;
+ default:
+ state = STATE_ZONE_ERR;
+ break;
+ }
+ break;
+ case STATE_ZONE_3:
+ if (message[cur_token + 2] == 'T') {
+ zone *= 100;
+ state = STATE_ZONE_OK;
+ }
+ else
+ state = STATE_ZONE_ERR;
+ break;
+ }
+ }
+
+ switch (state) {
+ case STATE_ZONE_OK:
+ * result = zone;
+ * index = cur_token + 3;
+ return MAILIMF_NO_ERROR;
+
+ case STATE_ZONE_ERR:
+ return MAILIMF_ERROR_PARSE;
+ }
+ }
+
+ sign = 1;
+ r = mailimf_plus_parse(message, length, &cur_token);
+ if (r == MAILIMF_NO_ERROR)
+ sign = 1;
+
+ if (r == MAILIMF_ERROR_PARSE) {
+ r = mailimf_minus_parse(message, length, &cur_token);
+ if (r == MAILIMF_NO_ERROR)
+ sign = -1;
+ }
+
+ if (r == MAILIMF_NO_ERROR) {
+ /* do nothing */
+ }
+ else if (r == MAILIMF_ERROR_PARSE)
+ sign = 1;
+ else
+ return r;
+
+ r = mailimf_number_parse(message, length, &cur_token, &zone);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ zone = zone * sign;
+
+ * index = cur_token;
+ * result = zone;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+address = mailbox / group
+*/
+
+int mailimf_address_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_address ** result)
+{
+ int type;
+ size_t cur_token;
+ struct mailimf_mailbox * mailbox;
+ struct mailimf_group * group;
+ struct mailimf_address * address;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ mailbox = NULL;
+ group = NULL;
+
+ type = MAILIMF_ADDRESS_ERROR; /* XXX - removes a gcc warning */
+ r = mailimf_group_parse(message, length, &cur_token, &group);
+ if (r == MAILIMF_NO_ERROR)
+ type = MAILIMF_ADDRESS_GROUP;
+
+ if (r == MAILIMF_ERROR_PARSE) {
+ r = mailimf_mailbox_parse(message, length, &cur_token, &mailbox);
+ if (r == MAILIMF_NO_ERROR)
+ type = MAILIMF_ADDRESS_MAILBOX;
+ }
+
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ address = mailimf_address_new(type, mailbox, group);
+ if (address == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free;
+ }
+
+ * result = address;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free:
+ if (mailbox != NULL)
+ mailimf_mailbox_free(mailbox);
+ if (group != NULL)
+ mailimf_group_free(group);
+ err:
+ return res;
+}
+
+
+/*
+mailbox = name-addr / addr-spec
+*/
+
+
+int mailimf_mailbox_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_mailbox ** result)
+{
+ size_t cur_token;
+ char * display_name;
+ struct mailimf_mailbox * mailbox;
+ char * addr_spec;
+ int r;
+ int res;
+
+ cur_token = * index;
+ display_name = NULL;
+ addr_spec = NULL;
+
+ r = mailimf_name_addr_parse(message, length, &cur_token,
+ &display_name, &addr_spec);
+ if (r == MAILIMF_ERROR_PARSE)
+ r = mailimf_addr_spec_parse(message, length, &cur_token, &addr_spec);
+
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ mailbox = mailimf_mailbox_new(display_name, addr_spec);
+ if (mailbox == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free;
+ }
+
+ * result = mailbox;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free:
+ if (display_name != NULL)
+ mailimf_display_name_free(display_name);
+ if (addr_spec != NULL)
+ mailimf_addr_spec_free(addr_spec);
+ err:
+ return res;
+}
+
+/*
+name-addr = [display-name] angle-addr
+*/
+
+static int mailimf_name_addr_parse(const char * message, size_t length,
+ size_t * index,
+ char ** pdisplay_name,
+ char ** pangle_addr)
+{
+ char * display_name;
+ char * angle_addr;
+ size_t cur_token;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ display_name = NULL;
+ angle_addr = NULL;
+
+ r = mailimf_display_name_parse(message, length, &cur_token, &display_name);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE)) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_angle_addr_parse(message, length, &cur_token, &angle_addr);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_display_name;
+ }
+
+ * pdisplay_name = display_name;
+ * pangle_addr = angle_addr;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_display_name:
+ if (display_name != NULL)
+ mailimf_display_name_free(display_name);
+ err:
+ return res;
+}
+
+/*
+angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr
+*/
+
+static int mailimf_angle_addr_parse(const char * message, size_t length,
+ size_t * index, char ** result)
+{
+ size_t cur_token;
+ char * addr_spec;
+ int r;
+
+ cur_token = * index;
+
+ r = mailimf_cfws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
+ return r;
+
+ r = mailimf_lower_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_addr_spec_parse(message, length, &cur_token, &addr_spec);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_greater_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ free(addr_spec);
+ return r;
+ }
+
+ * result = addr_spec;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+group = display-name ":" [mailbox-list / CFWS] ";"
+ [CFWS]
+*/
+
+static int mailimf_group_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_group ** result)
+{
+ size_t cur_token;
+ char * display_name;
+ struct mailimf_mailbox_list * mailbox_list;
+ struct mailimf_group * group;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ mailbox_list = NULL;
+
+ r = mailimf_display_name_parse(message, length, &cur_token, &display_name);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_display_name;
+ }
+
+ r = mailimf_mailbox_list_parse(message, length, &cur_token, &mailbox_list);
+ switch (r) {
+ case MAILIMF_NO_ERROR:
+ break;
+ case MAILIMF_ERROR_PARSE:
+ r = mailimf_cfws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
+ return r;
+ break;
+ default:
+ return r;
+ }
+
+ r = mailimf_semi_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_mailbox_list;
+ }
+
+ group = mailimf_group_new(display_name, mailbox_list);
+ if (group == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_mailbox_list;
+ }
+
+ * index = cur_token;
+ * result = group;
+
+ return MAILIMF_NO_ERROR;
+
+ free_mailbox_list:
+ mailimf_mailbox_list_free(mailbox_list);
+ free_display_name:
+ mailimf_display_name_free(display_name);
+ err:
+ return res;
+}
+
+/*
+display-name = phrase
+*/
+
+static int mailimf_display_name_parse(const char * message, size_t length,
+ size_t * index, char ** result)
+{
+ return mailimf_phrase_parse(message, length, index, result);
+}
+
+/*
+mailbox-list = (mailbox *("," mailbox)) / obs-mbox-list
+*/
+
+int
+mailimf_mailbox_list_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_mailbox_list ** result)
+{
+ size_t cur_token;
+ clist * list;
+ struct mailimf_mailbox_list * mailbox_list;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_struct_list_parse(message, length,
+ &cur_token, &list, ',',
+ (mailimf_struct_parser *)
+ mailimf_mailbox_parse,
+ (mailimf_struct_destructor *)
+ mailimf_mailbox_free);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ mailbox_list = mailimf_mailbox_list_new(list);
+ if (mailbox_list == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_list;
+ }
+
+ * result = mailbox_list;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_list:
+ clist_foreach(list, (clist_func) mailimf_mailbox_free, NULL);
+ clist_free(list);
+ err:
+ return res;
+}
+
+/*
+address-list = (address *("," address)) / obs-addr-list
+*/
+
+
+int
+mailimf_address_list_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_address_list ** result)
+{
+ size_t cur_token;
+ clist * list;
+ struct mailimf_address_list * address_list;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_struct_list_parse(message, length,
+ &cur_token, &list, ',',
+ (mailimf_struct_parser *)
+ mailimf_address_parse,
+ (mailimf_struct_destructor *)
+ mailimf_address_free);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ address_list = mailimf_address_list_new(list);
+ if (address_list == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_list;
+ }
+
+ * result = address_list;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_list:
+ clist_foreach(list, (clist_func) mailimf_address_free, NULL);
+ clist_free(list);
+ err:
+ return res;
+}
+
+/*
+addr-spec = local-part "@" domain
+*/
+
+
+static int mailimf_addr_spec_parse(const char * message, size_t length,
+ size_t * index,
+ char ** result)
+{
+ size_t cur_token;
+#if 0
+ char * local_part;
+ char * domain;
+#endif
+ char * addr_spec;
+ int r;
+ int res;
+ size_t begin;
+ size_t end;
+ int final;
+ size_t count;
+ const char * src;
+ char * dest;
+ size_t i;
+
+ cur_token = * index;
+
+ r = mailimf_cfws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE)) {
+ res = r;
+ goto err;
+ }
+
+ end = cur_token;
+ if (end >= length) {
+ res = MAILIMF_ERROR_PARSE;
+ goto err;
+ }
+
+ begin = cur_token;
+
+ final = FALSE;
+ while (1) {
+ switch (message[end]) {
+ case '>':
+ case ',':
+ case '\r':
+ case '\n':
+ case '(':
+ case ')':
+ case ':':
+ case ';':
+ final = TRUE;
+ break;
+ }
+
+ if (final)
+ break;
+
+ end ++;
+ if (end >= length)
+ break;
+ }
+
+ if (end == begin) {
+ res = MAILIMF_ERROR_PARSE;
+ goto err;
+ }
+
+ addr_spec = malloc(end - cur_token + 1);
+ if (addr_spec == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto err;
+ }
+
+ count = end - cur_token;
+ src = message + cur_token;
+ dest = addr_spec;
+ for(i = 0 ; i < count ; i ++) {
+ if ((* src != ' ') && (* src != '\t')) {
+ * dest = * src;
+ dest ++;
+ }
+ src ++;
+ }
+ * dest = '\0';
+
+#if 0
+ strncpy(addr_spec, message + cur_token, end - cur_token);
+ addr_spec[end - cur_token] = '\0';
+#endif
+
+ cur_token = end;
+
+#if 0
+ r = mailimf_local_part_parse(message, length, &cur_token, &local_part);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_at_sign_parse(message, length, &cur_token);
+ switch (r) {
+ case MAILIMF_NO_ERROR:
+ r = mailimf_domain_parse(message, length, &cur_token, &domain);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_local_part;
+ }
+ break;
+
+ case MAILIMF_ERROR_PARSE:
+ domain = NULL;
+ break;
+
+ default:
+ res = r;
+ goto free_local_part;
+ }
+
+ if (domain) {
+ addr_spec = malloc(strlen(local_part) + strlen(domain) + 2);
+ if (addr_spec == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_domain;
+ }
+
+ strcpy(addr_spec, local_part);
+ strcat(addr_spec, "@");
+ strcat(addr_spec, domain);
+
+ mailimf_domain_free(domain);
+ mailimf_local_part_free(local_part);
+ }
+ else {
+ addr_spec = local_part;
+ }
+#endif
+
+ * result = addr_spec;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+#if 0
+ free_domain:
+ mailimf_domain_free(domain);
+ free_local_part:
+ mailimf_local_part_free(local_part);
+#endif
+ err:
+ return res;
+}
+
+/*
+local-part = dot-atom / quoted-string / obs-local-part
+*/
+
+#if 0
+static int mailimf_local_part_parse(const char * message, size_t length,
+ size_t * index,
+ char ** result)
+{
+ int r;
+
+ r = mailimf_dot_atom_parse(message, length, index, result);
+ switch (r) {
+ case MAILIMF_NO_ERROR:
+ return r;
+ case MAILIMF_ERROR_PARSE:
+ break;
+ default:
+ return r;
+ }
+
+ r = mailimf_quoted_string_parse(message, length, index, result);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ return MAILIMF_NO_ERROR;
+}
+#endif
+
+/*
+domain = dot-atom / domain-literal / obs-domain
+*/
+
+#if 0
+static int mailimf_domain_parse(const char * message, size_t length,
+ size_t * index,
+ char ** result)
+{
+ int r;
+
+ r = mailimf_dot_atom_parse(message, length, index, result);
+ switch (r) {
+ case MAILIMF_NO_ERROR:
+ return r;
+ case MAILIMF_ERROR_PARSE:
+ break;
+ default:
+ return r;
+ }
+
+ r = mailimf_domain_literal_parse(message, length, index, result);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ return MAILIMF_NO_ERROR;
+}
+#endif
+
+/*
+[FWS] dcontent
+*/
+
+#if 0
+static int
+mailimf_domain_literal_fws_dcontent_parse(const char * message, size_t length,
+ size_t * index)
+{
+ size_t cur_token;
+ char ch;
+ int r;
+
+ cur_token = * index;
+
+ r = mailimf_cfws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
+ return r;
+
+ r = mailimf_dcontent_parse(message, length, &cur_token, &ch);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+#endif
+
+/*
+domain-literal = [CFWS] "[" *([FWS] dcontent) [FWS] "]" [CFWS]
+*/
+
+#if 0
+static int mailimf_domain_literal_parse(const char * message, size_t length,
+ size_t * index, char ** result)
+{
+ size_t cur_token;
+ int len;
+ int begin;
+ char * domain_literal;
+ int r;
+
+ cur_token = * index;
+
+ r = mailimf_cfws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
+ return r;
+
+ begin = cur_token;
+ r = mailimf_obracket_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ while (1) {
+ r = mailimf_domain_literal_fws_dcontent_parse(message, length,
+ &cur_token);
+ if (r == MAILIMF_NO_ERROR) {
+ /* do nothing */
+ }
+ else if (r == MAILIMF_ERROR_PARSE)
+ break;
+ else
+ return r;
+ }
+
+ r = mailimf_fws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
+ return r;
+
+ r = mailimf_cbracket_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ len = cur_token - begin;
+
+ domain_literal = malloc(len + 1);
+ if (domain_literal == NULL)
+ return MAILIMF_ERROR_MEMORY;
+ strncpy(domain_literal, message + begin, len);
+ domain_literal[len] = '\0';
+
+ * result = domain_literal;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+#endif
+
+/*
+dcontent = dtext / quoted-pair
+*/
+
+#if 0
+static int mailimf_dcontent_parse(const char * message, size_t length,
+ size_t * index, char * result)
+{
+ size_t cur_token;
+ char ch;
+ int r;
+
+ cur_token = * index;
+
+ if (cur_token >= length)
+ return MAILIMF_ERROR_PARSE;
+
+ if (is_dtext(message[cur_token])) {
+ ch = message[cur_token];
+ cur_token ++;
+ }
+ else {
+ r = mailimf_quoted_pair_parse(message, length, &cur_token, &ch);
+
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+
+ * index = cur_token;
+ * result = ch;
+
+ return MAILIMF_NO_ERROR;
+}
+#endif
+
+
+/*
+dtext = NO-WS-CTL / ; Non white space controls
+
+ %d33-90 / ; The rest of the US-ASCII
+ %d94-126 ; characters not including "[",
+ ; "]", or "\"
+*/
+
+static inline int is_dtext(char ch)
+{
+ unsigned char uch = (unsigned char) ch;
+
+ if (is_no_ws_ctl(ch))
+ return TRUE;
+
+ if (uch < 33)
+ return FALSE;
+
+ if ((uch >= 91) && (uch <= 93))
+ return FALSE;
+
+ if (uch == 127)
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+message = (fields / obs-fields)
+ [CRLF body]
+*/
+
+int mailimf_message_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_message ** result)
+{
+ struct mailimf_fields * fields;
+ struct mailimf_body * body;
+ struct mailimf_message * msg;
+ size_t cur_token;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_fields_parse(message, length, &cur_token, &fields);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_crlf_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE)) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_body_parse(message, length, &cur_token, &body);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_fields;
+ }
+
+ msg = mailimf_message_new(fields, body);
+ if (msg == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_body;
+ }
+
+ * index = cur_token;
+ * result = msg;
+
+ return MAILIMF_NO_ERROR;
+
+ free_body:
+ mailimf_body_free(body);
+ free_fields:
+ mailimf_fields_free(fields);
+ err:
+ return res;
+}
+
+/*
+body = *(*998text CRLF) *998text
+*/
+
+int mailimf_body_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_body ** result)
+{
+ size_t cur_token;
+ struct mailimf_body * body;
+
+ cur_token = * index;
+
+ body = mailimf_body_new(message + cur_token, length - cur_token);
+ if (body == NULL)
+ return MAILIMF_ERROR_MEMORY;
+
+ cur_token = length;
+
+ * result = body;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+CHANGE TO THE RFC 2822
+
+original :
+
+fields = *(trace
+ *(resent-date /
+ resent-from /
+ resent-sender /
+ resent-to /
+ resent-cc /
+ resent-bcc /
+ resent-msg-id))
+ *(orig-date /
+ from /
+ sender /
+ reply-to /
+ to /
+ cc /
+ bcc /
+ message-id /
+ in-reply-to /
+ references /
+ subject /
+ comments /
+ keywords /
+ optional-field)
+
+INTO THE FOLLOWING :
+*/
+
+/*
+resent-fields-list = *(resent-date /
+ resent-from /
+ resent-sender /
+ resent-to /
+ resent-cc /
+ resent-bcc /
+ resent-msg-id))
+*/
+
+#if 0
+enum {
+ RESENT_HEADER_START,
+};
+
+static int guess_resent_header_type(char * message,
+ size_t length, size_t index)
+{
+ int r;
+
+ r = mailimf_token_case_insensitive_parse(message,
+ length, &index, "Resent-");
+ if (r != MAILIMF_NO_ERROR)
+ return MAILIMF_RESENT_FIELD_NONE;
+
+ if (index >= length)
+ return MAILIMF_RESENT_FIELD_NONE;
+
+ switch(toupper(message[index])) {
+ case 'D':
+ return MAILIMF_RESENT_FIELD_DATE;
+ case 'F':
+ return MAILIMF_RESENT_FIELD_FROM;
+ case 'S':
+ return MAILIMF_RESENT_FIELD_SENDER;
+ case 'T':
+ return MAILIMF_RESENT_FIELD_TO;
+ case 'C':
+ return MAILIMF_RESENT_FIELD_CC;
+ case 'B':
+ return MAILIMF_RESENT_FIELD_BCC;
+ case 'M':
+ return MAILIMF_RESENT_FIELD_MSG_ID;
+ default:
+ return MAILIMF_RESENT_FIELD_NONE;
+ }
+}
+#endif
+
+#if 0
+static int
+mailimf_resent_field_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_resent_field ** result)
+{
+ struct mailimf_orig_date * resent_date;
+ struct mailimf_from * resent_from;
+ struct mailimf_sender * resent_sender;
+ struct mailimf_to* resent_to;
+ struct mailimf_cc * resent_cc;
+ struct mailimf_bcc * resent_bcc;
+ struct mailimf_message_id * resent_msg_id;
+ size_t cur_token;
+ int type;
+ struct mailimf_resent_field * resent_field;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ resent_date = NULL;
+ resent_from = NULL;
+ resent_sender = NULL;
+ resent_to = NULL;
+ resent_cc = NULL;
+ resent_bcc = NULL;
+ resent_msg_id = NULL;
+
+ type = guess_resent_header_type(message, length, cur_token);
+
+ switch(type) {
+ case MAILIMF_RESENT_FIELD_DATE:
+ r = mailimf_resent_date_parse(message, length, &cur_token,
+ &resent_date);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_RESENT_FIELD_FROM:
+ r = mailimf_resent_from_parse(message, length, &cur_token,
+ &resent_from);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_RESENT_FIELD_SENDER:
+ r = mailimf_resent_sender_parse(message, length, &cur_token,
+ &resent_sender);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_RESENT_FIELD_TO:
+ r = mailimf_resent_to_parse(message, length, &cur_token,
+ &resent_to);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_RESENT_FIELD_CC:
+ r= mailimf_resent_cc_parse(message, length, &cur_token,
+ &resent_cc);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_RESENT_FIELD_BCC:
+ r = mailimf_resent_bcc_parse(message, length, &cur_token,
+ &resent_bcc);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_RESENT_FIELD_MSG_ID:
+ r = mailimf_resent_msg_id_parse(message, length, &cur_token,
+ &resent_msg_id);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+ break;
+ default:
+ res = MAILIMF_ERROR_PARSE;
+ goto err;
+ }
+
+ resent_field = mailimf_resent_field_new(type, resent_date,
+ resent_from, resent_sender,
+ resent_to, resent_cc,
+ resent_bcc, resent_msg_id);
+ if (resent_field == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_resent;
+ }
+
+ * result = resent_field;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_resent:
+ if (resent_msg_id != NULL)
+ mailimf_message_id_free(resent_msg_id);
+ if (resent_bcc != NULL)
+ mailimf_bcc_free(resent_bcc);
+ if (resent_cc != NULL)
+ mailimf_cc_free(resent_cc);
+ if (resent_to != NULL)
+ mailimf_to_free(resent_to);
+ if (resent_sender != NULL)
+ mailimf_sender_free(resent_sender);
+ if (resent_from != NULL)
+ mailimf_from_free(resent_from);
+ if (resent_date != NULL)
+ mailimf_orig_date_free(resent_date);
+ err:
+ return res;
+}
+#endif
+
+#if 0
+static int
+mailimf_resent_fields_list_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_resent_fields_list ** result)
+{
+ clist * list;
+ size_t cur_token;
+ struct mailimf_resent_fields_list * resent_fields_list;
+ int r;
+ int res;
+
+ cur_token = * index;
+ list = NULL;
+
+ r = mailimf_struct_multiple_parse(message, length, &cur_token, &list,
+ (mailimf_struct_parser *)
+ mailimf_resent_field_parse,
+ (mailimf_struct_destructor *)
+ mailimf_resent_field_free);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ resent_fields_list = mailimf_resent_fields_list_new(list);
+ if (resent_fields_list == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_list;
+ }
+
+ * result = resent_fields_list;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_list:
+ clist_foreach(list, (clist_func) mailimf_resent_field_free, NULL);
+ clist_free(list);
+ err:
+ return res;
+}
+#endif
+
+/*
+ ([trace]
+ [resent-fields-list])
+*/
+
+#if 0
+static int
+mailimf_trace_resent_fields_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_trace_resent_fields ** result)
+{
+ size_t cur_token;
+ struct mailimf_return * return_path;
+ struct mailimf_resent_fields_list * resent_fields;
+ struct mailimf_trace_resent_fields * trace_resent_fields;
+ int res;
+ int r;
+
+ cur_token = * index;
+
+ return_path = NULL;
+ resent_fields = NULL;
+
+ r = mailimf_return_parse(message, length, &cur_token,
+ &return_path);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE)) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_resent_fields_list_parse(message, length, &cur_token,
+ &resent_fields);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE)) {
+ res = r;
+ goto err;
+ }
+
+ if ((return_path == NULL) && (resent_fields == NULL)) {
+ res = MAILIMF_ERROR_PARSE;
+ goto err;
+ }
+
+ trace_resent_fields = mailimf_trace_resent_fields_new(return_path,
+ resent_fields);
+ if (trace_resent_fields == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_resent_fields;
+ }
+
+ * result = trace_resent_fields;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_resent_fields:
+ if (resent_fields != NULL)
+ mailimf_resent_fields_list_free(resent_fields);
+ if (return_path != NULL)
+ mailimf_return_free(return_path);
+ err:
+ return res;
+}
+#endif
+
+/*
+delivering-info = *([trace]
+ [resent-fields-list])
+*/
+
+#if 0
+static int
+mailimf_delivering_info_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_delivering_info ** result)
+{
+ size_t cur_token;
+ clist * list;
+ struct mailimf_delivering_info * delivering_info;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_struct_multiple_parse(message, length, &cur_token,
+ &list,
+ (mailimf_struct_parser *)
+ mailimf_trace_resent_fields_parse,
+ (mailimf_struct_destructor *)
+ mailimf_trace_resent_fields_free);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ delivering_info = mailimf_delivering_info_new(list);
+ if (delivering_info == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_list;
+ }
+
+ * result = delivering_info;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_list:
+ clist_foreach(list, (clist_func) mailimf_trace_resent_fields_free, NULL);
+ clist_free(list);
+ err:
+ return res;
+}
+#endif
+
+/*
+field = delivering-info /
+ orig-date /
+ from /
+ sender /
+ reply-to /
+ to /
+ cc /
+ bcc /
+ message-id /
+ in-reply-to /
+ references /
+ subject /
+ comments /
+ keywords /
+ optional-field
+*/
+
+enum {
+ HEADER_START,
+ HEADER_C,
+ HEADER_R,
+ HEADER_RE,
+ HEADER_S,
+ HEADER_RES,
+};
+
+static int guess_header_type(const char * message, size_t length, size_t index)
+{
+ int state;
+ int r;
+
+ state = HEADER_START;
+
+ while (1) {
+
+ if (index >= length)
+ return MAILIMF_FIELD_NONE;
+
+ switch(state) {
+ case HEADER_START:
+ switch((char) toupper((unsigned char) message[index])) {
+ case 'B':
+ return MAILIMF_FIELD_BCC;
+ case 'C':
+ state = HEADER_C;
+ break;
+ case 'D':
+ return MAILIMF_FIELD_ORIG_DATE;
+ case 'F':
+ return MAILIMF_FIELD_FROM;
+ case 'I':
+ return MAILIMF_FIELD_IN_REPLY_TO;
+ case 'K':
+ return MAILIMF_FIELD_KEYWORDS;
+ case 'M':
+ return MAILIMF_FIELD_MESSAGE_ID;
+ case 'R':
+ state = HEADER_R;
+ break;
+ case 'T':
+ return MAILIMF_FIELD_TO;
+ break;
+ case 'S':
+ state = HEADER_S;
+ break;
+ default:
+ return MAILIMF_FIELD_NONE;
+ }
+ break;
+ case HEADER_C:
+ switch((char) toupper((unsigned char) message[index])) {
+ case 'O':
+ return MAILIMF_FIELD_COMMENTS;
+ case 'C':
+ return MAILIMF_FIELD_CC;
+ default:
+ return MAILIMF_FIELD_NONE;
+ }
+ break;
+ case HEADER_R:
+ switch((char) toupper((unsigned char) message[index])) {
+ case 'E':
+ state = HEADER_RE;
+ break;
+ default:
+ return MAILIMF_FIELD_NONE;
+ }
+ break;
+ case HEADER_RE:
+ switch((char) toupper((unsigned char) message[index])) {
+ case 'F':
+ return MAILIMF_FIELD_REFERENCES;
+ case 'P':
+ return MAILIMF_FIELD_REPLY_TO;
+ case 'S':
+ state = HEADER_RES;
+ break;
+ case 'T':
+ return MAILIMF_FIELD_RETURN_PATH;
+ default:
+ return MAILIMF_FIELD_NONE;
+ }
+ break;
+ case HEADER_S:
+ switch((char) toupper((unsigned char) message[index])) {
+ case 'E':
+ return MAILIMF_FIELD_SENDER;
+ case 'U':
+ return MAILIMF_FIELD_SUBJECT;
+ default:
+ return MAILIMF_FIELD_NONE;
+ }
+ break;
+
+ case HEADER_RES:
+ r = mailimf_token_case_insensitive_parse(message,
+ length, &index, "ent-");
+ if (r != MAILIMF_NO_ERROR)
+ return MAILIMF_FIELD_NONE;
+
+ if (index >= length)
+ return MAILIMF_FIELD_NONE;
+
+ switch((char) toupper((unsigned char) message[index])) {
+ case 'D':
+ return MAILIMF_FIELD_RESENT_DATE;
+ case 'F':
+ return MAILIMF_FIELD_RESENT_FROM;
+ case 'S':
+ return MAILIMF_FIELD_RESENT_SENDER;
+ case 'T':
+ return MAILIMF_FIELD_RESENT_TO;
+ case 'C':
+ return MAILIMF_FIELD_RESENT_CC;
+ case 'B':
+ return MAILIMF_FIELD_RESENT_BCC;
+ case 'M':
+ return MAILIMF_FIELD_RESENT_MSG_ID;
+ default:
+ return MAILIMF_FIELD_NONE;
+ }
+ break;
+ }
+ index ++;
+ }
+}
+
+static int mailimf_field_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_field ** result)
+{
+ size_t cur_token;
+ int type;
+ struct mailimf_return * return_path;
+ struct mailimf_orig_date * resent_date;
+ struct mailimf_from * resent_from;
+ struct mailimf_sender * resent_sender;
+ struct mailimf_to* resent_to;
+ struct mailimf_cc * resent_cc;
+ struct mailimf_bcc * resent_bcc;
+ struct mailimf_message_id * resent_msg_id;
+ struct mailimf_orig_date * orig_date;
+ struct mailimf_from * from;
+ struct mailimf_sender * sender;
+ struct mailimf_reply_to * reply_to;
+ struct mailimf_to * to;
+ struct mailimf_cc * cc;
+ struct mailimf_bcc * bcc;
+ struct mailimf_message_id * message_id;
+ struct mailimf_in_reply_to * in_reply_to;
+ struct mailimf_references * references;
+ struct mailimf_subject * subject;
+ struct mailimf_comments * comments;
+ struct mailimf_keywords * keywords;
+ struct mailimf_optional_field * optional_field;
+ struct mailimf_field * field;
+ int guessed_type;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ return_path = NULL;
+ resent_date = NULL;
+ resent_from = NULL;
+ resent_sender = NULL;
+ resent_to = NULL;
+ resent_cc = NULL;
+ resent_bcc = NULL;
+ resent_msg_id = NULL;
+ orig_date = NULL;
+ from = NULL;
+ sender = NULL;
+ reply_to = NULL;
+ to = NULL;
+ cc = NULL;
+ bcc = NULL;
+ message_id = NULL;
+ in_reply_to = NULL;
+ references = NULL;
+ subject = NULL;
+ comments = NULL;
+ keywords = NULL;
+ optional_field = NULL;
+
+ guessed_type = guess_header_type(message, length, cur_token);
+ type = MAILIMF_FIELD_NONE;
+
+ switch (guessed_type) {
+ case MAILIMF_FIELD_ORIG_DATE:
+ r = mailimf_orig_date_parse(message, length, &cur_token,
+ &orig_date);
+ if (r == MAILIMF_NO_ERROR)
+ type = MAILIMF_FIELD_ORIG_DATE;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_FROM:
+ r = mailimf_from_parse(message, length, &cur_token,
+ &from);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_SENDER:
+ r = mailimf_sender_parse(message, length, &cur_token,
+ &sender);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_REPLY_TO:
+ r = mailimf_reply_to_parse(message, length, &cur_token,
+ &reply_to);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_TO:
+ r = mailimf_to_parse(message, length, &cur_token,
+ &to);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_CC:
+ r = mailimf_cc_parse(message, length, &cur_token,
+ &cc);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_BCC:
+ r = mailimf_bcc_parse(message, length, &cur_token,
+ &bcc);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_MESSAGE_ID:
+ r = mailimf_message_id_parse(message, length, &cur_token,
+ &message_id);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_IN_REPLY_TO:
+ r = mailimf_in_reply_to_parse(message, length, &cur_token,
+ &in_reply_to);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_REFERENCES:
+ r = mailimf_references_parse(message, length, &cur_token,
+ &references);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_SUBJECT:
+ r = mailimf_subject_parse(message, length, &cur_token,
+ &subject);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_COMMENTS:
+ r = mailimf_comments_parse(message, length, &cur_token,
+ &comments);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_KEYWORDS:
+ r = mailimf_keywords_parse(message, length, &cur_token,
+ &keywords);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_RETURN_PATH:
+ r = mailimf_return_parse(message, length, &cur_token,
+ &return_path);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_RESENT_DATE:
+ r = mailimf_resent_date_parse(message, length, &cur_token,
+ &resent_date);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_RESENT_FROM:
+ r = mailimf_resent_from_parse(message, length, &cur_token,
+ &resent_from);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_RESENT_SENDER:
+ r = mailimf_resent_sender_parse(message, length, &cur_token,
+ &resent_sender);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_RESENT_TO:
+ r = mailimf_resent_to_parse(message, length, &cur_token,
+ &resent_to);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_RESENT_CC:
+ r= mailimf_resent_cc_parse(message, length, &cur_token,
+ &resent_cc);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_RESENT_BCC:
+ r = mailimf_resent_bcc_parse(message, length, &cur_token,
+ &resent_bcc);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_RESENT_MSG_ID:
+ r = mailimf_resent_msg_id_parse(message, length, &cur_token,
+ &resent_msg_id);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ }
+
+ if (type == MAILIMF_FIELD_NONE) {
+ r = mailimf_optional_field_parse(message, length, &cur_token,
+ &optional_field);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ type = MAILIMF_FIELD_OPTIONAL_FIELD;
+ }
+
+ field = mailimf_field_new(type, return_path, resent_date,
+ resent_from, resent_sender, resent_to, resent_cc, resent_bcc,
+ resent_msg_id, orig_date, from, sender, reply_to, to,
+ cc, bcc, message_id, in_reply_to, references,
+ subject, comments, keywords, optional_field);
+ if (field == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_field;
+ }
+
+ * result = field;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_field:
+ if (return_path != NULL)
+ mailimf_return_free(return_path);
+ if (resent_date != NULL)
+ mailimf_orig_date_free(resent_date);
+ if (resent_from != NULL)
+ mailimf_from_free(resent_from);
+ if (resent_sender != NULL)
+ mailimf_sender_free(resent_sender);
+ if (resent_to != NULL)
+ mailimf_to_free(resent_to);
+ if (resent_cc != NULL)
+ mailimf_cc_free(resent_cc);
+ if (resent_bcc != NULL)
+ mailimf_bcc_free(resent_bcc);
+ if (resent_msg_id != NULL)
+ mailimf_message_id_free(resent_msg_id);
+ if (orig_date != NULL)
+ mailimf_orig_date_free(orig_date);
+ if (from != NULL)
+ mailimf_from_free(from);
+ if (sender != NULL)
+ mailimf_sender_free(sender);
+ if (reply_to != NULL)
+ mailimf_reply_to_free(reply_to);
+ if (to != NULL)
+ mailimf_to_free(to);
+ if (cc != NULL)
+ mailimf_cc_free(cc);
+ if (bcc != NULL)
+ mailimf_bcc_free(bcc);
+ if (message_id != NULL)
+ mailimf_message_id_free(message_id);
+ if (in_reply_to != NULL)
+ mailimf_in_reply_to_free(in_reply_to);
+ if (references != NULL)
+ mailimf_references_free(references);
+ if (subject != NULL)
+ mailimf_subject_free(subject);
+ if (comments != NULL)
+ mailimf_comments_free(comments);
+ if (keywords != NULL)
+ mailimf_keywords_free(keywords);
+ if (optional_field != NULL)
+ mailimf_optional_field_free(optional_field);
+ err:
+ return res;
+}
+
+
+/*
+fields = *(delivering-info /
+ orig-date /
+ from /
+ sender /
+ reply-to /
+ to /
+ cc /
+ bcc /
+ message-id /
+ in-reply-to /
+ references /
+ subject /
+ comments /
+ keywords /
+ optional-field)
+*/
+
+#if 0
+int
+mailimf_unparsed_fields_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_unparsed_fields ** result)
+{
+ size_t cur_token;
+ clist * list;
+ struct mailimf_unparsed_fields * fields;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ list = NULL;
+
+ r = mailimf_struct_multiple_parse(message, length, &cur_token,
+ &list,
+ (mailimf_struct_parser *)
+ mailimf_optional_field_parse,
+ (mailimf_struct_destructor *)
+ mailimf_optional_field_free);
+ /*
+ if ((r = MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE)) {
+ res = r;
+ goto err;
+ }
+ */
+
+ switch (r) {
+ case MAILIMF_NO_ERROR:
+ /* do nothing */
+ break;
+
+ case MAILIMF_ERROR_PARSE:
+ list = clist_new();
+ if (list == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto err;
+ }
+ break;
+
+ default:
+ res = r;
+ goto err;
+ }
+
+ fields = mailimf_unparsed_fields_new(list);
+ if (fields == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free;
+ }
+
+ * result = fields;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free:
+ if (list != NULL) {
+ clist_foreach(list, (clist_func) mailimf_optional_field_free, NULL);
+ clist_free(list);
+ }
+ err:
+ return res;
+}
+#endif
+
+int mailimf_fields_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_fields ** result)
+{
+ size_t cur_token;
+ clist * list;
+ struct mailimf_fields * fields;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ list = NULL;
+
+ r = mailimf_struct_multiple_parse(message, length, &cur_token,
+ &list,
+ (mailimf_struct_parser *)
+ mailimf_field_parse,
+ (mailimf_struct_destructor *)
+ mailimf_field_free);
+ /*
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE)) {
+ res = r;
+ goto err;
+ }
+ */
+
+ switch (r) {
+ case MAILIMF_NO_ERROR:
+ /* do nothing */
+ break;
+
+ case MAILIMF_ERROR_PARSE:
+ list = clist_new();
+ if (list == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto err;
+ }
+ break;
+
+ default:
+ res = r;
+ goto err;
+ }
+
+ fields = mailimf_fields_new(list);
+ if (fields == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free;
+ }
+
+ * result = fields;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free:
+ if (list != NULL) {
+ clist_foreach(list, (clist_func) mailimf_field_free, NULL);
+ clist_free(list);
+ }
+ err:
+ return res;
+}
+
+/*
+orig-date = "Date:" date-time CRLF
+*/
+
+
+static int
+mailimf_orig_date_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_orig_date ** result)
+{
+ struct mailimf_date_time * date_time;
+ struct mailimf_orig_date * orig_date;
+ size_t cur_token;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_token_case_insensitive_parse(message, length,
+ &cur_token, "Date:");
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_date_time_parse(message, length, &cur_token, &date_time);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_ignore_unstructured_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_date_time;
+ }
+
+ r = mailimf_unstrict_crlf_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_date_time;
+ }
+
+ orig_date = mailimf_orig_date_new(date_time);
+ if (orig_date == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_date_time;
+ }
+
+ * result = orig_date;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_date_time:
+ mailimf_date_time_free(date_time);
+ err:
+ return res;
+}
+
+/*
+from = "From:" mailbox-list CRLF
+*/
+
+static int
+mailimf_from_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_from ** result)
+{
+ struct mailimf_mailbox_list * mb_list;
+ struct mailimf_from * from;
+ size_t cur_token;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_token_case_insensitive_parse(message, length,
+ &cur_token, "From");
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_mailbox_list_parse(message, length, &cur_token, &mb_list);
+
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_unstrict_crlf_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_mb_list;
+ }
+
+ from = mailimf_from_new(mb_list);
+ if (from == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_mb_list;
+ }
+
+ * result = from;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_mb_list:
+ mailimf_mailbox_list_free(mb_list);
+ err:
+ return res;
+}
+
+/*
+sender = "Sender:" mailbox CRLF
+*/
+
+static int
+mailimf_sender_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_sender ** result)
+{
+ struct mailimf_mailbox * mb;
+ struct mailimf_sender * sender;
+ size_t cur_token;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_token_case_insensitive_parse(message, length,
+ &cur_token, "Sender");
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_mailbox_parse(message, length, &cur_token, &mb);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_unstrict_crlf_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_mb;
+ }
+
+ sender = mailimf_sender_new(mb);
+ if (sender == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_mb;
+ }
+
+ * result = sender;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_mb:
+ mailimf_mailbox_free(mb);
+ err:
+ return res;
+}
+
+/*
+reply-to = "Reply-To:" address-list CRLF
+*/
+
+
+static int
+mailimf_reply_to_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_reply_to ** result)
+{
+ struct mailimf_address_list * addr_list;
+ struct mailimf_reply_to * reply_to;
+ size_t cur_token;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_token_case_insensitive_parse(message, length,
+ &cur_token, "Reply-To");
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_address_list_parse(message, length, &cur_token, &addr_list);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_unstrict_crlf_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_addr_list;
+ }
+
+ reply_to = mailimf_reply_to_new(addr_list);
+ if (reply_to == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_addr_list;
+ }
+
+ * result = reply_to;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_addr_list:
+ mailimf_address_list_free(addr_list);
+ err:
+ return res;
+}
+
+/*
+to = "To:" address-list CRLF
+*/
+
+static int
+mailimf_to_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_to ** result)
+{
+ struct mailimf_address_list * addr_list;
+ struct mailimf_to * to;
+ size_t cur_token;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_token_case_insensitive_parse(message, length,
+ &cur_token, "To");
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_address_list_parse(message, length, &cur_token, &addr_list);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_unstrict_crlf_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_addr_list;
+ }
+
+ to = mailimf_to_new(addr_list);
+ if (to == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_addr_list;
+ }
+
+ * result = to;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_addr_list:
+ mailimf_address_list_free(addr_list);
+ err:
+ return res;
+}
+
+/*
+cc = "Cc:" address-list CRLF
+*/
+
+
+static int
+mailimf_cc_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_cc ** result)
+{
+ struct mailimf_address_list * addr_list;
+ struct mailimf_cc * cc;
+ size_t cur_token;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_token_case_insensitive_parse(message, length,
+ &cur_token, "Cc");
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_address_list_parse(message, length, &cur_token, &addr_list);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_unstrict_crlf_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_addr_list;
+ }
+
+ cc = mailimf_cc_new(addr_list);
+ if (cc == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_addr_list;
+ }
+
+ * result = cc;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_addr_list:
+ mailimf_address_list_free(addr_list);
+ err:
+ return res;
+}
+
+/*
+bcc = "Bcc:" (address-list / [CFWS]) CRLF
+*/
+
+
+static int
+mailimf_bcc_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_bcc ** result)
+{
+ struct mailimf_address_list * addr_list;
+ struct mailimf_bcc * bcc;
+ size_t cur_token;
+ int r;
+ int res;
+
+ cur_token = * index;
+ addr_list = NULL;
+
+ r = mailimf_token_case_insensitive_parse(message, length,
+ &cur_token, "Bcc");
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_address_list_parse(message, length, &cur_token, &addr_list);
+ switch (r) {
+ case MAILIMF_NO_ERROR:
+ /* do nothing */
+ break;
+ case MAILIMF_ERROR_PARSE:
+ r = mailimf_cfws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE)) {
+ res = r;
+ goto err;
+ }
+ break;
+ default:
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_unstrict_crlf_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_addr_list;
+ }
+
+ bcc = mailimf_bcc_new(addr_list);
+ if (bcc == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_addr_list;
+ }
+
+ * result = bcc;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_addr_list:
+ mailimf_address_list_free(addr_list);
+ err:
+ return res;
+}
+
+/*
+message-id = "Message-ID:" msg-id CRLF
+*/
+
+static int mailimf_message_id_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_message_id ** result)
+{
+ char * value;
+ size_t cur_token;
+ struct mailimf_message_id * message_id;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_token_case_insensitive_parse(message, length,
+ &cur_token, "Message-ID");
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_msg_id_parse(message, length, &cur_token, &value);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_unstrict_crlf_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_value;
+ }
+
+ message_id = mailimf_message_id_new(value);
+ if (message_id == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_value;
+ }
+
+ * result = message_id;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_value:
+ mailimf_msg_id_free(value);
+ err:
+ return res;
+}
+
+/*
+in-reply-to = "In-Reply-To:" 1*msg-id CRLF
+*/
+
+int mailimf_msg_id_list_parse(const char * message, size_t length,
+ size_t * index, clist ** result)
+{
+ return mailimf_struct_multiple_parse(message, length, index,
+ result,
+ (mailimf_struct_parser *)
+ mailimf_unstrict_msg_id_parse,
+ (mailimf_struct_destructor *)
+ mailimf_msg_id_free);
+}
+
+static int mailimf_in_reply_to_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_in_reply_to ** result)
+{
+ struct mailimf_in_reply_to * in_reply_to;
+ size_t cur_token;
+ clist * msg_id_list;
+ int res;
+ int r;
+
+ cur_token = * index;
+
+ r = mailimf_token_case_insensitive_parse(message, length,
+ &cur_token, "In-Reply-To");
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_msg_id_list_parse(message, length, &cur_token, &msg_id_list);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_unstrict_crlf_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_list;
+ }
+
+ in_reply_to = mailimf_in_reply_to_new(msg_id_list);
+ if (in_reply_to == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_list;
+ }
+
+ * result = in_reply_to;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_list:
+ clist_foreach(msg_id_list, (clist_func) mailimf_msg_id_free, NULL);
+ clist_free(msg_id_list);
+ err:
+ return res;
+}
+
+/*
+references = "References:" 1*msg-id CRLF
+*/
+
+int mailimf_references_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_references ** result)
+{
+ struct mailimf_references * references;
+ size_t cur_token;
+ clist * msg_id_list;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_token_case_insensitive_parse(message, length,
+ &cur_token, "References");
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_msg_id_list_parse(message, length, &cur_token, &msg_id_list);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_unstrict_crlf_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_list;
+ }
+
+ references = mailimf_references_new(msg_id_list);
+ if (references == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_list;
+ }
+
+ * result = references;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_list:
+ clist_foreach(msg_id_list, (clist_func) mailimf_msg_id_free, NULL);
+ clist_free(msg_id_list);
+ err:
+ return res;
+}
+
+/*
+msg-id = [CFWS] "<" id-left "@" id-right ">" [CFWS]
+*/
+
+int mailimf_msg_id_parse(const char * message, size_t length,
+ size_t * index,
+ char ** result)
+{
+ size_t cur_token;
+#if 0
+ char * id_left;
+ char * id_right;
+#endif
+ char * msg_id;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_cfws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
+ return r;
+
+ r = mailimf_lower_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_addr_spec_parse(message, length, &cur_token, &msg_id);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_greater_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ free(msg_id);
+ res = r;
+ goto err;
+ }
+
+#if 0
+ r = mailimf_id_left_parse(message, length, &cur_token, &id_left);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_at_sign_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_id_left;
+ }
+
+ r = mailimf_id_right_parse(message, length, &cur_token, &id_right);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_id_left;
+ }
+
+ r = mailimf_greater_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_id_right;
+ }
+
+ msg_id = malloc(strlen(id_left) + strlen(id_right) + 2);
+ if (msg_id == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_id_right;
+ }
+ strcpy(msg_id, id_left);
+ strcat(msg_id, "@");
+ strcat(msg_id, id_right);
+
+ mailimf_id_left_free(id_left);
+ mailimf_id_right_free(id_right);
+#endif
+
+ * result = msg_id;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+#if 0
+ free_id_right:
+ mailimf_id_right_free(id_right);
+ free_id_left:
+ mailimf_id_left_free(id_left);
+#endif
+ /*
+ free:
+ mailimf_atom_free(msg_id);
+ */
+ err:
+ return res;
+}
+
+static int mailimf_parse_unwanted_msg_id(const char * message, size_t length,
+ size_t * index)
+{
+ size_t cur_token;
+ int r;
+ char * word;
+ int token_parsed;
+
+ cur_token = * index;
+
+ token_parsed = TRUE;
+ while (token_parsed) {
+ token_parsed = FALSE;
+ r = mailimf_word_parse(message, length, &cur_token, &word);
+ if (r == MAILIMF_NO_ERROR) {
+ mailimf_word_free(word);
+ token_parsed = TRUE;
+ }
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else
+ return r;
+ r = mailimf_semi_colon_parse(message, length, &cur_token);
+ if (r == MAILIMF_NO_ERROR)
+ token_parsed = TRUE;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else
+ return r;
+ r = mailimf_comma_parse(message, length, &cur_token);
+ if (r == MAILIMF_NO_ERROR)
+ token_parsed = TRUE;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else
+ return r;
+ r = mailimf_plus_parse(message, length, &cur_token);
+ if (r == MAILIMF_NO_ERROR)
+ token_parsed = TRUE;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else
+ return r;
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r == MAILIMF_NO_ERROR)
+ token_parsed = TRUE;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else
+ return r;
+ r = mailimf_point_parse(message, length, &cur_token);
+ if (r == MAILIMF_NO_ERROR)
+ token_parsed = TRUE;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else
+ return r;
+ r = mailimf_at_sign_parse(message, length, &cur_token);
+ if (r == MAILIMF_NO_ERROR)
+ token_parsed = TRUE;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else
+ return r;
+ }
+
+ return MAILIMF_NO_ERROR;
+}
+
+static int mailimf_unstrict_msg_id_parse(const char * message, size_t length,
+ size_t * index,
+ char ** result)
+{
+ char * msgid;
+ size_t cur_token;
+ int r;
+
+ cur_token = * index;
+
+ r = mailimf_cfws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
+ return r;
+
+ r = mailimf_parse_unwanted_msg_id(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_msg_id_parse(message, length, &cur_token, &msgid);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_parse_unwanted_msg_id(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ * result = msgid;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+id-left = dot-atom-text / no-fold-quote / obs-id-left
+*/
+
+#if 0
+static int mailimf_id_left_parse(const char * message, size_t length,
+ size_t * index, char ** result)
+{
+ int r;
+
+ r = mailimf_dot_atom_text_parse(message, length, index, result);
+ switch (r) {
+ case MAILIMF_NO_ERROR:
+ return MAILIMF_NO_ERROR;
+ case MAILIMF_ERROR_PARSE:
+ break;
+ default:
+ return r;
+ }
+
+ r = mailimf_no_fold_quote_parse(message, length, index, result);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ return MAILIMF_NO_ERROR;
+}
+#endif
+
+/*
+id-right = dot-atom-text / no-fold-literal / obs-id-right
+*/
+
+#if 0
+static int mailimf_id_right_parse(const char * message, size_t length,
+ size_t * index, char ** result)
+{
+ int r;
+
+ r = mailimf_dot_atom_text_parse(message, length, index, result);
+ switch (r) {
+ case MAILIMF_NO_ERROR:
+ return MAILIMF_NO_ERROR;
+ case MAILIMF_ERROR_PARSE:
+ break;
+ default:
+ return r;
+ }
+
+ r = mailimf_no_fold_literal_parse(message, length, index, result);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ return MAILIMF_NO_ERROR;
+}
+#endif
+
+/*
+no-fold-quote = DQUOTE *(qtext / quoted-pair) DQUOTE
+*/
+
+#if 0
+static int mailimf_no_fold_quote_char_parse(const char * message, size_t length,
+ size_t * index, char * result)
+{
+ char ch;
+ size_t cur_token;
+ int r;
+
+ cur_token = * index;
+
+#if 0
+ r = mailimf_qtext_parse(message, length, &cur_token, &ch);
+#endif
+
+ if (cur_token >= length)
+ return MAILIMF_ERROR_PARSE;
+
+ if (is_qtext(message[cur_token])) {
+ ch = message[cur_token];
+ cur_token ++;
+ }
+ else {
+ r = mailimf_quoted_pair_parse(message, length, &cur_token, &ch);
+
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+
+ * index = cur_token;
+ * result = ch;
+
+ return MAILIMF_NO_ERROR;
+}
+#endif
+
+#if 0
+static int mailimf_no_fold_quote_parse(const char * message, size_t length,
+ size_t * index, char ** result)
+{
+ size_t cur_token;
+ size_t begin;
+ char ch;
+ char * no_fold_quote;
+ int r;
+ int res;
+
+ begin = cur_token;
+ r = mailimf_dquote_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ while (1) {
+ r = mailimf_no_fold_quote_char_parse(message, length, &cur_token, &ch);
+ if (r == MAILIMF_NO_ERROR) {
+ /* do nothing */
+ }
+ else if (r == MAILIMF_ERROR_PARSE)
+ break;
+ else {
+ res = r;
+ goto err;
+ }
+ }
+
+ r = mailimf_dquote_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ /* no_fold_quote = strndup(message + begin, cur_token - begin); */
+ no_fold_quote = malloc(cur_token - begin + 1);
+ if (no_fold_quote == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto err;
+ }
+ strncpy(no_fold_quote, message + begin, cur_token - begin);
+ no_fold_quote[cur_token - begin] = '\0';
+
+ * result = no_fold_quote;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ err:
+ return res;
+}
+#endif
+
+/*
+no-fold-literal = "[" *(dtext / quoted-pair) "]"
+*/
+
+#if 0
+static inline int
+mailimf_no_fold_literal_char_parse(const char * message, size_t length,
+ size_t * index, char * result)
+{
+ char ch;
+ size_t cur_token;
+ int r;
+
+ cur_token = * index;
+
+#if 0
+ r = mailimf_dtext_parse(message, length, &cur_token, &ch);
+#endif
+ if (cur_token >= length)
+ return MAILIMF_ERROR_PARSE;
+
+ if (is_dtext(message[cur_token])) {
+ ch = message[cur_token];
+ cur_token ++;
+ }
+ else {
+ r = mailimf_quoted_pair_parse(message, length, &cur_token, &ch);
+
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+
+ * index = cur_token;
+ * result = ch;
+
+ return MAILIMF_NO_ERROR;
+}
+#endif
+
+#if 0
+static int mailimf_no_fold_literal_parse(const char * message, size_t length,
+ size_t * index, char ** result)
+{
+ size_t cur_token;
+ size_t begin;
+ char ch;
+ char * no_fold_literal;
+ int r;
+ int res;
+
+ begin = cur_token;
+ r = mailimf_obracket_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ while (1) {
+ r = mailimf_no_fold_literal_char_parse(message, length,
+ &cur_token, &ch);
+ if (r == MAILIMF_NO_ERROR) {
+ /* do nothing */
+ }
+ else if (r == MAILIMF_ERROR_PARSE)
+ break;
+ else {
+ res = r;
+ goto err;
+ }
+ }
+
+ r = mailimf_cbracket_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ /*
+ no_fold_literal = strndup(message + begin, cur_token - begin);
+ */
+ no_fold_literal = malloc(cur_token - begin + 1);
+ if (no_fold_literal == NULL) {
+ res = MAILIMF_NO_ERROR;
+ goto err;
+ }
+ strncpy(no_fold_literal, message + begin, cur_token - begin);
+ no_fold_literal[cur_token - begin] = '\0';
+
+ * result = no_fold_literal;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ err:
+ return res;
+}
+#endif
+
+/*
+subject = "Subject:" unstructured CRLF
+*/
+
+static int mailimf_subject_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_subject ** result)
+{
+ struct mailimf_subject * subject;
+ char * value;
+ size_t cur_token;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_token_case_insensitive_parse(message, length,
+ &cur_token, "Subject");
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_unstructured_parse(message, length, &cur_token, &value);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_unstrict_crlf_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_value;
+ }
+
+ subject = mailimf_subject_new(value);
+ if (subject == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_value;
+ }
+
+ * result = subject;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_value:
+ mailimf_unstructured_free(value);
+ err:
+ return res;
+}
+
+/*
+comments = "Comments:" unstructured CRLF
+*/
+
+static int mailimf_comments_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_comments ** result)
+{
+ struct mailimf_comments * comments;
+ char * value;
+ size_t cur_token;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_token_case_insensitive_parse(message, length,
+ &cur_token, "Comments");
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_unstructured_parse(message, length, &cur_token, &value);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_unstrict_crlf_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_value;
+ }
+
+ comments = mailimf_comments_new(value);
+ if (comments == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_value;
+ }
+
+ * result = comments;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_value:
+ mailimf_unstructured_free(value);
+ err:
+ return res;
+}
+
+/*
+keywords = "Keywords:" phrase *("," phrase) CRLF
+*/
+
+static int mailimf_keywords_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_keywords ** result)
+{
+ struct mailimf_keywords * keywords;
+ clist * list;
+ size_t cur_token;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_token_case_insensitive_parse(message, length,
+ &cur_token, "Keywords");
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_struct_list_parse(message, length, &cur_token,
+ &list, ',',
+ (mailimf_struct_parser *)
+ mailimf_phrase_parse,
+ (mailimf_struct_destructor *)
+ mailimf_phrase_free);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_unstrict_crlf_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_list;
+ }
+
+ keywords = mailimf_keywords_new(list);
+ if (keywords == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_list;
+ }
+
+ * result = keywords;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_list:
+ clist_foreach(list, (clist_func) mailimf_phrase_free, NULL);
+ clist_free(list);
+ err:
+ return res;
+}
+
+/*
+resent-date = "Resent-Date:" date-time CRLF
+*/
+
+static int
+mailimf_resent_date_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_orig_date ** result)
+{
+ struct mailimf_orig_date * orig_date;
+ struct mailimf_date_time * date_time;
+ size_t cur_token;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_token_case_insensitive_parse(message, length,
+ &cur_token, "Resent-Date");
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_date_time_parse(message, length, &cur_token, &date_time);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_unstrict_crlf_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_date_time;
+ }
+
+ orig_date = mailimf_orig_date_new(date_time);
+ if (orig_date == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_date_time;
+ }
+
+ * result = orig_date;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_date_time:
+ mailimf_date_time_free(date_time);
+ err:
+ return res;
+}
+
+/*
+resent-from = "Resent-From:" mailbox-list CRLF
+*/
+
+static int
+mailimf_resent_from_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_from ** result)
+{
+ struct mailimf_mailbox_list * mb_list;
+ struct mailimf_from * from;
+ size_t cur_token;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_token_case_insensitive_parse(message, length,
+ &cur_token, "Resent-From");
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_mailbox_list_parse(message, length, &cur_token, &mb_list);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_unstrict_crlf_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_mb_list;
+ }
+
+ from = mailimf_from_new(mb_list);
+ if (from == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_mb_list;
+ }
+
+ * result = from;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_mb_list:
+ mailimf_mailbox_list_free(mb_list);
+ err:
+ return res;
+}
+
+/*
+resent-sender = "Resent-Sender:" mailbox CRLF
+*/
+
+static int
+mailimf_resent_sender_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_sender ** result)
+{
+ struct mailimf_mailbox * mb;
+ struct mailimf_sender * sender;
+ size_t cur_token;
+ int r;
+ int res;
+
+ cur_token = length;
+
+ r = mailimf_token_case_insensitive_parse(message, length,
+ &cur_token, "Resent-Sender");
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_mailbox_parse(message, length, &cur_token, &mb);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_unstrict_crlf_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_mb;
+ }
+
+ sender = mailimf_sender_new(mb);
+ if (sender == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_mb;
+ }
+
+ * result = sender;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_mb:
+ mailimf_mailbox_free(mb);
+ err:
+ return res;
+}
+
+/*
+resent-to = "Resent-To:" address-list CRLF
+*/
+
+static int
+mailimf_resent_to_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_to ** result)
+{
+ struct mailimf_address_list * addr_list;
+ struct mailimf_to * to;
+ size_t cur_token;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_token_case_insensitive_parse(message, length,
+ &cur_token, "Resent-To");
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_address_list_parse(message, length, &cur_token, &addr_list);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_unstrict_crlf_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_addr_list;
+ }
+
+ to = mailimf_to_new(addr_list);
+ if (to == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_addr_list;
+ }
+
+ * result = to;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_addr_list:
+ mailimf_address_list_free(addr_list);
+ err:
+ return res;
+}
+
+/*
+resent-cc = "Resent-Cc:" address-list CRLF
+*/
+
+static int
+mailimf_resent_cc_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_cc ** result)
+{
+ struct mailimf_address_list * addr_list;
+ struct mailimf_cc * cc;
+ size_t cur_token;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_token_case_insensitive_parse(message, length,
+ &cur_token, "Resent-Cc");
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_address_list_parse(message, length, &cur_token, &addr_list);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_unstrict_crlf_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_addr_list;
+ }
+
+ cc = mailimf_cc_new(addr_list);
+ if (cc == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_addr_list;
+ }
+
+ * result = cc;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_addr_list:
+ mailimf_address_list_free(addr_list);
+ err:
+ return res;
+}
+
+/*
+resent-bcc = "Resent-Bcc:" (address-list / [CFWS]) CRLF
+*/
+
+static int
+mailimf_resent_bcc_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_bcc ** result)
+{
+ struct mailimf_address_list * addr_list;
+ struct mailimf_bcc * bcc;
+ size_t cur_token;
+ int r;
+ int res;
+
+ cur_token = * index;
+ bcc = NULL;
+
+ r = mailimf_token_case_insensitive_parse(message, length,
+ &cur_token, "Resent-Bcc");
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_address_list_parse(message, length, &cur_token, &addr_list);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE)) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_unstrict_crlf_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_addr_list;
+ }
+
+ bcc = mailimf_bcc_new(addr_list);
+ if (bcc == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_addr_list;
+ }
+
+ * result = bcc;
+ * index = cur_token;
+
+ return TRUE;
+
+ free_addr_list:
+ mailimf_address_list_free(addr_list);
+ err:
+ return res;
+}
+
+/*
+resent-msg-id = "Resent-Message-ID:" msg-id CRLF
+*/
+
+static int
+mailimf_resent_msg_id_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_message_id ** result)
+{
+ char * value;
+ size_t cur_token;
+ struct mailimf_message_id * message_id;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_token_case_insensitive_parse(message, length,
+ &cur_token, "Resent-Message-ID");
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_msg_id_parse(message, length, &cur_token, &value);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_unstrict_crlf_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_value;
+ }
+
+ message_id = mailimf_message_id_new(value);
+ if (message_id == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_value;
+ }
+
+ * result = message_id;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_value:
+ mailimf_msg_id_free(value);
+ err:
+ return res;
+}
+
+/*
+trace = [return]
+ 1*received
+*/
+
+#if 0
+static int mailimf_trace_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_trace ** result)
+{
+ size_t cur_token;
+ struct mailimf_return * return_path;
+ clist * received_list;
+ struct mailimf_trace * trace;
+ int r;
+ int res;
+
+ cur_token = * index;
+ return_path = NULL;
+ received_list = NULL;
+
+ r = mailimf_return_parse(message, length, &cur_token, &return_path);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE)) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_struct_multiple_parse(message, length, &cur_token,
+ &received_list,
+ (mailimf_struct_parser *)
+ mailimf_received_parse,
+ (mailimf_struct_destructor *)
+ mailimf_received_free);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE)) {
+ res = r;
+ goto err;
+ }
+
+ if ((received_list == NULL) && (return_path == NULL)) {
+ res = MAILIMF_ERROR_PARSE;
+ goto free_return;
+ }
+
+ trace = mailimf_trace_new(return_path, received_list);
+ if (trace == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_list;
+ }
+
+ * result = trace;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_list:
+ clist_foreach(received_list, (clist_func) mailimf_received_free, NULL);
+ clist_free(received_list);
+ free_return:
+ if (return_path != NULL)
+ mailimf_return_free(return_path);
+ err:
+ return res;
+}
+#endif
+
+/*
+return = "Return-Path:" path CRLF
+*/
+
+static int mailimf_return_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_return ** result)
+{
+ struct mailimf_path * path = NULL;
+ struct mailimf_return * return_path;
+ size_t cur_token;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_token_case_insensitive_parse(message, length,
+ &cur_token, "Return-Path");
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_path_parse(message, length, &cur_token, &path);
+ if ( r!= MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_unstrict_crlf_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_path;
+ }
+
+ return_path = mailimf_return_new(path);
+ if (return_path == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_path;
+ }
+
+ * result = return_path;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_path:
+ mailimf_path_free(path);
+ err:
+ return res;
+}
+
+/*
+path = ([CFWS] "<" ([CFWS] / addr-spec) ">" [CFWS]) /
+ obs-path
+*/
+
+static int mailimf_path_parse(const char * message, size_t length,
+ size_t * index, struct mailimf_path ** result)
+{
+ size_t cur_token;
+ char * addr_spec;
+ struct mailimf_path * path;
+ int res;
+ int r;
+
+ cur_token = * index;
+ addr_spec = NULL;
+
+ r = mailimf_cfws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE)) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_lower_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_addr_spec_parse(message, length, &cur_token, &addr_spec);
+ switch (r) {
+ case MAILIMF_NO_ERROR:
+ break;
+ case MAILIMF_ERROR_PARSE:
+ r = mailimf_cfws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE)) {
+ res = r;
+ goto err;
+ }
+ break;
+ default:
+ return r;
+ }
+
+ r = mailimf_greater_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ path = mailimf_path_new(addr_spec);
+ if (path == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_addr_spec;
+ }
+
+ * index = cur_token;
+ * result = path;
+
+ return MAILIMF_NO_ERROR;
+
+ free_addr_spec:
+ if (addr_spec == NULL)
+ mailimf_addr_spec_free(addr_spec);
+ err:
+ return res;
+}
+
+/*
+received = "Received:" name-val-list ";" date-time CRLF
+*/
+
+#if 0
+static int mailimf_received_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_received ** result)
+{
+ size_t cur_token;
+ struct mailimf_received * received;
+ struct mailimf_name_val_list * name_val_list;
+ struct mailimf_date_time * date_time;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_token_case_insensitive_parse(message, length,
+ &cur_token, "Received");
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_name_val_list_parse(message, length,
+ &cur_token, &name_val_list);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_semi_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_name_val_list;
+ }
+
+ r = mailimf_date_time_parse(message, length, &cur_token, &date_time);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_name_val_list;
+ }
+
+ r = mailimf_unstrict_crlf_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_date_time;
+ }
+
+ received = mailimf_received_new(name_val_list, date_time);
+ if (received == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_date_time;
+ }
+
+ * index = cur_token;
+ * result = received;
+
+ return MAILIMF_NO_ERROR;
+
+ free_date_time:
+ mailimf_date_time_free(date_time);
+ free_name_val_list:
+ mailimf_name_val_list_free(name_val_list);
+ err:
+ return res;
+}
+#endif
+
+/*
+name-val-list = [CFWS] [name-val-pair *(CFWS name-val-pair)]
+*/
+
+#if 0
+static int
+mailimf_name_val_list_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_name_val_list ** result)
+{
+ size_t cur_token;
+ struct mailimf_name_val_pair * pair;
+ struct mailimf_name_val_list * name_val_list;
+ clist* list;
+ int res;
+ int r;
+
+ cur_token = * index;
+ list = NULL;
+
+ r = mailimf_name_val_pair_parse(message, length, &cur_token, &pair);
+
+ if (r == MAILIMF_NO_ERROR){
+ size_t final_token;
+
+ list = clist_new();
+ if (list == NULL) {
+ mailimf_name_val_pair_free(pair);
+ res = MAILIMF_ERROR_MEMORY;
+ goto err;
+ }
+
+ r = clist_append(list, pair);
+ if (r < 0) {
+ mailimf_name_val_pair_free(pair);
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_list;
+ }
+
+ final_token = cur_token;
+
+ while (1) {
+ r = mailimf_cfws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE)) {
+ res = r;
+ goto free_list;
+ }
+
+ r = mailimf_name_val_pair_parse(message, length, &cur_token, &pair);
+ if (r == MAILIMF_NO_ERROR) {
+ /* do nothing */
+ }
+ else if (r == MAILIMF_ERROR_PARSE)
+ break;
+ else {
+ res = r;
+ goto free_list;
+ }
+
+ r = clist_append(list, pair);
+ if (r < 0) {
+ mailimf_name_val_pair_free(pair);
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_list;
+ }
+
+ final_token = cur_token;
+ }
+ cur_token = final_token;
+ }
+
+ name_val_list = mailimf_name_val_list_new(list);
+ if (name_val_list == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_list;
+ }
+
+ * index = cur_token;
+ * result = name_val_list;
+
+ return MAILIMF_NO_ERROR;
+
+ free_list:
+ if (list != NULL) {
+ clist_foreach(list, (clist_func) mailimf_name_val_pair_free, NULL);
+ clist_free(list);
+ }
+ err:
+ return res;
+}
+#endif
+
+/*
+name-val-pair = item-name CFWS item-value
+*/
+
+#if 0
+static int
+mailimf_name_val_pair_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_name_val_pair ** result)
+{
+ size_t cur_token;
+ char * item_name;
+ struct mailimf_item_value * item_value;
+ struct mailimf_name_val_pair * name_val_pair;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_cfws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE)) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_item_name_parse(message, length, &cur_token, &item_name);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_cfws_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_item_name;
+ }
+
+ r = mailimf_item_value_parse(message, length, &cur_token, &item_value);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_item_name;
+ }
+
+ name_val_pair = mailimf_name_val_pair_new(item_name, item_value);
+ if (name_val_pair == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_item_value;
+ }
+
+ * result = name_val_pair;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_item_value:
+ mailimf_item_value_free(item_value);
+ free_item_name:
+ mailimf_item_name_free(item_name);
+ err:
+ return res;
+}
+#endif
+
+/*
+item-name = ALPHA *(["-"] (ALPHA / DIGIT))
+*/
+
+#if 0
+static int mailimf_item_name_parse(const char * message, size_t length,
+ size_t * index, char ** result)
+{
+ size_t cur_token;
+ size_t begin;
+ char * item_name;
+ char ch;
+ int digit;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ begin = cur_token;
+
+ r = mailimf_alpha_parse(message, length, &cur_token, &ch);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ while (1) {
+ int minus_sign;
+
+ minus_sign = mailimf_minus_parse(message, length, &cur_token);
+
+ r = mailimf_alpha_parse(message, length, &cur_token, &ch);
+ if (r == MAILIMF_ERROR_PARSE)
+ r = mailimf_digit_parse(message, length, &cur_token, &digit);
+
+ if (r == MAILIMF_NO_ERROR) {
+ /* do nothing */
+ }
+ if (r == MAILIMF_ERROR_PARSE)
+ break;
+ else if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+ }
+
+ item_name = strndup(message + begin, cur_token - begin);
+ if (item_name == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto err;
+ }
+
+ * index = cur_token;
+ * result = item_name;
+
+ return MAILIMF_NO_ERROR;
+
+ err:
+ return res;
+}
+#endif
+
+/*
+item-value = 1*angle-addr / addr-spec /
+ atom / domain / msg-id
+*/
+
+#if 0
+static int is_item_value_atext(char ch)
+{
+ switch (ch) {
+ case '\t':
+ case ' ':
+ case '\r':
+ case '\n':
+ case ';':
+ return FALSE;
+ default:
+ return TRUE;
+ }
+}
+
+static int mailimf_item_value_atom_parse(const char * message, size_t length,
+ size_t * index, char ** result)
+{
+ char * atom;
+ size_t cur_token;
+ int r;
+
+ cur_token = * index;
+
+ r = mailimf_cfws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
+ return r;
+
+ r = mailimf_custom_string_parse(message, length, &cur_token,
+ &atom, is_item_value_atext);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_cfws_parse(message, length, &cur_token);
+ if ((r != MAILIMF_NO_ERROR) && (r != MAILIMF_ERROR_PARSE))
+ return r;
+
+ * index = cur_token;
+ * result = atom;
+
+ return MAILIMF_NO_ERROR;
+}
+
+static int mailimf_item_value_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_item_value ** result)
+{
+ size_t cur_token;
+ clist * angle_addr_list;
+ char * addr_spec;
+ char * atom;
+ char * domain;
+ char * msg_id;
+ int type;
+ struct mailimf_item_value * item_value;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ angle_addr_list = NULL;
+ addr_spec = NULL;
+ atom = NULL;
+ domain = NULL;
+ msg_id = NULL;
+
+ r = mailimf_struct_multiple_parse(message, length, &cur_token,
+ &angle_addr_list,
+ (mailimf_struct_parser *)
+ mailimf_angle_addr_parse,
+ (mailimf_struct_destructor *)
+ mailimf_angle_addr_free);
+ if (r == MAILIMF_NO_ERROR)
+ type = MAILIMF_ITEM_VALUE_ANGLE_ADDR_LIST;
+
+ if (r == MAILIMF_ERROR_PARSE) {
+ r = mailimf_addr_spec_parse(message, length, &cur_token,
+ &addr_spec);
+ if (r == MAILIMF_NO_ERROR)
+ type = MAILIMF_ITEM_VALUE_ADDR_SPEC;
+ }
+
+ if (r == MAILIMF_ERROR_PARSE) {
+ r = mailimf_msg_id_parse(message, length, &cur_token,
+ &msg_id);
+ if (r == MAILIMF_NO_ERROR)
+ type = MAILIMF_ITEM_VALUE_MSG_ID;
+ }
+
+ /*
+ else if (mailimf_domain_parse(message, length, &cur_token,
+ &domain))
+ type = MAILIMF_ITEM_VALUE_DOMAIN;
+ */
+ /*
+ else if (mailimf_atom_parse(message, length, &cur_token,
+ &atom))
+ type = MAILIMF_ITEM_VALUE_ATOM;
+ */
+
+ if (r == MAILIMF_ERROR_PARSE) {
+ r = mailimf_item_value_atom_parse(message, length, &cur_token,
+ &atom);
+ if (r == MAILIMF_NO_ERROR)
+ type = MAILIMF_ITEM_VALUE_ATOM;
+ }
+
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ item_value = mailimf_item_value_new(type, angle_addr_list, addr_spec,
+ atom, domain, msg_id);
+ if (item_value == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free;
+ }
+
+ * result = item_value;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free:
+ if (angle_addr_list != NULL) {
+ clist_foreach(angle_addr_list, (clist_func) mailimf_angle_addr_free, NULL);
+ clist_free(angle_addr_list);
+ }
+ if (addr_spec != NULL)
+ mailimf_addr_spec_free(addr_spec);
+ if (atom != NULL)
+ mailimf_atom_free(atom);
+ if (domain != NULL)
+ mailimf_domain_free(domain);
+ if (msg_id != NULL)
+ mailimf_msg_id_free(msg_id);
+ err:
+ return res;
+}
+#endif
+
+/*
+optional-field = field-name ":" unstructured CRLF
+*/
+
+static int
+mailimf_optional_field_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_optional_field ** result)
+{
+ char * name;
+ char * value;
+ struct mailimf_optional_field * optional_field;
+ size_t cur_token;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ r = mailimf_field_name_parse(message, length, &cur_token, &name);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_colon_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_name;
+ }
+
+ r = mailimf_unstructured_parse(message, length, &cur_token, &value);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_name;
+ }
+
+ r = mailimf_unstrict_crlf_parse(message, length, &cur_token);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_value;
+ }
+
+ optional_field = mailimf_optional_field_new(name, value);
+ if (optional_field == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_value;
+ }
+
+ * result = optional_field;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_value:
+ mailimf_unstructured_free(value);
+ free_name:
+ mailimf_field_name_free(name);
+ err:
+ return res;
+}
+
+/*
+field-name = 1*ftext
+*/
+
+static inline int is_ftext(char ch);
+
+static int mailimf_field_name_parse(const char * message, size_t length,
+ size_t * index, char ** result)
+{
+ char * field_name;
+ size_t cur_token;
+ size_t end;
+
+ cur_token = * index;
+
+ end = cur_token;
+ if (end >= length) {
+ return MAILIMF_ERROR_PARSE;
+ }
+
+ while (is_ftext(message[end])) {
+ end ++;
+ if (end >= length)
+ break;
+ }
+ if (end == cur_token) {
+ return MAILIMF_ERROR_PARSE;
+ }
+
+ /* field_name = strndup(message + cur_token, end - cur_token); */
+ field_name = malloc(end - cur_token + 1);
+ if (field_name == NULL) {
+ return MAILIMF_ERROR_MEMORY;
+ }
+ strncpy(field_name, message + cur_token, end - cur_token);
+ field_name[end - cur_token] = '\0';
+
+ cur_token = end;
+
+ * index = cur_token;
+ * result = field_name;
+
+ return MAILIMF_NO_ERROR;
+}
+
+/*
+ftext = %d33-57 / ; Any character except
+ %d59-126 ; controls, SP, and
+ ; ":".
+*/
+
+static inline int is_ftext(char ch)
+{
+ unsigned char uch = (unsigned char) ch;
+
+ if (uch < 33)
+ return FALSE;
+
+ if (uch == 58)
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+static int mailimf_ftext_parse(const char * message, size_t length,
+ size_t * index, gchar * result)
+{
+ return mailimf_typed_text_parse(message, length, index, result, is_ftext);
+}
+*/
+
+
+
+
+static int mailimf_envelope_field_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_field ** result)
+{
+ size_t cur_token;
+ int type;
+ struct mailimf_orig_date * orig_date;
+ struct mailimf_from * from;
+ struct mailimf_sender * sender;
+ struct mailimf_reply_to * reply_to;
+ struct mailimf_to * to;
+ struct mailimf_cc * cc;
+ struct mailimf_bcc * bcc;
+ struct mailimf_message_id * message_id;
+ struct mailimf_in_reply_to * in_reply_to;
+ struct mailimf_references * references;
+ struct mailimf_subject * subject;
+ struct mailimf_optional_field * optional_field;
+ struct mailimf_field * field;
+ int guessed_type;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ orig_date = NULL;
+ from = NULL;
+ sender = NULL;
+ reply_to = NULL;
+ to = NULL;
+ cc = NULL;
+ bcc = NULL;
+ message_id = NULL;
+ in_reply_to = NULL;
+ references = NULL;
+ subject = NULL;
+ optional_field = NULL;
+
+ guessed_type = guess_header_type(message, length, cur_token);
+ type = MAILIMF_FIELD_NONE;
+
+ switch (guessed_type) {
+ case MAILIMF_FIELD_ORIG_DATE:
+ r = mailimf_orig_date_parse(message, length, &cur_token,
+ &orig_date);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_FROM:
+ r = mailimf_from_parse(message, length, &cur_token,
+ &from);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_SENDER:
+ r = mailimf_sender_parse(message, length, &cur_token,
+ &sender);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_REPLY_TO:
+ r = mailimf_reply_to_parse(message, length, &cur_token,
+ &reply_to);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_TO:
+ r = mailimf_to_parse(message, length, &cur_token,
+ &to);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_CC:
+ r = mailimf_cc_parse(message, length, &cur_token,
+ &cc);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_BCC:
+ r = mailimf_bcc_parse(message, length, &cur_token,
+ &bcc);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_MESSAGE_ID:
+ r = mailimf_message_id_parse(message, length, &cur_token,
+ &message_id);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_IN_REPLY_TO:
+ r = mailimf_in_reply_to_parse(message, length, &cur_token,
+ &in_reply_to);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_REFERENCES:
+ r = mailimf_references_parse(message, length, &cur_token,
+ &references);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ case MAILIMF_FIELD_SUBJECT:
+ r = mailimf_subject_parse(message, length, &cur_token,
+ &subject);
+ if (r == MAILIMF_NO_ERROR)
+ type = guessed_type;
+ else if (r == MAILIMF_ERROR_PARSE) {
+ /* do nothing */
+ }
+ else {
+ res = r;
+ goto err;
+ }
+ break;
+ }
+
+ if (type == MAILIMF_FIELD_NONE) {
+ res = MAILIMF_ERROR_PARSE;
+ goto err;
+ }
+
+ field = mailimf_field_new(type, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ orig_date, from, sender, reply_to, to,
+ cc, bcc, message_id, in_reply_to, references,
+ subject, NULL, NULL, optional_field);
+ if (field == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_field;
+ }
+
+ * result = field;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free_field:
+ if (orig_date != NULL)
+ mailimf_orig_date_free(orig_date);
+ if (from != NULL)
+ mailimf_from_free(from);
+ if (sender != NULL)
+ mailimf_sender_free(sender);
+ if (reply_to != NULL)
+ mailimf_reply_to_free(reply_to);
+ if (to != NULL)
+ mailimf_to_free(to);
+ if (cc != NULL)
+ mailimf_cc_free(cc);
+ if (bcc != NULL)
+ mailimf_bcc_free(bcc);
+ if (message_id != NULL)
+ mailimf_message_id_free(message_id);
+ if (in_reply_to != NULL)
+ mailimf_in_reply_to_free(in_reply_to);
+ if (references != NULL)
+ mailimf_references_free(references);
+ if (subject != NULL)
+ mailimf_subject_free(subject);
+ if (optional_field != NULL)
+ mailimf_optional_field_free(optional_field);
+ err:
+ return res;
+}
+
+int mailimf_envelope_fields_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_fields ** result)
+{
+ size_t cur_token;
+ clist * list;
+ struct mailimf_fields * fields;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ list = clist_new();
+ if (list == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto err;
+ }
+
+ while (1) {
+ struct mailimf_field * elt;
+
+ r = mailimf_envelope_field_parse(message, length, &cur_token, &elt);
+ if (r == MAILIMF_NO_ERROR) {
+ r = clist_append(list, elt);
+ if (r < 0) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free;
+ }
+ }
+ else if (r == MAILIMF_ERROR_PARSE) {
+ r = mailimf_ignore_field_parse(message, length, &cur_token);
+ if (r == MAILIMF_NO_ERROR) {
+ /* do nothing */
+ }
+ else if (r == MAILIMF_ERROR_PARSE) {
+ break;
+ }
+ else {
+ res = r;
+ goto free;
+ }
+ }
+ else {
+ res = r;
+ goto free;
+ }
+ }
+
+ fields = mailimf_fields_new(list);
+ if (fields == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free;
+ }
+
+ * result = fields;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free:
+ if (list != NULL) {
+ clist_foreach(list, (clist_func) mailimf_field_free, NULL);
+ clist_free(list);
+ }
+ err:
+ return res;
+}
+
+
+static int
+mailimf_envelope_or_optional_field_parse(const char * message,
+ size_t length,
+ size_t * index,
+ struct mailimf_field ** result)
+{
+ int r;
+ size_t cur_token;
+ struct mailimf_optional_field * optional_field;
+ struct mailimf_field * field;
+
+ r = mailimf_envelope_field_parse(message, length, index, result);
+ if (r == MAILIMF_NO_ERROR)
+ return MAILIMF_NO_ERROR;
+
+ cur_token = * index;
+
+ r = mailimf_optional_field_parse(message, length, &cur_token,
+ &optional_field);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ field = mailimf_field_new(MAILIMF_FIELD_OPTIONAL_FIELD, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, optional_field);
+ if (field == NULL) {
+ mailimf_optional_field_free(optional_field);
+ return MAILIMF_ERROR_MEMORY;
+ }
+
+ * result = field;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+int
+mailimf_envelope_and_optional_fields_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_fields ** result)
+{
+ size_t cur_token;
+ clist * list;
+ struct mailimf_fields * fields;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ list = NULL;
+
+ r = mailimf_struct_multiple_parse(message, length, &cur_token,
+ &list,
+ (mailimf_struct_parser *)
+ mailimf_envelope_or_optional_field_parse,
+ (mailimf_struct_destructor *)
+ mailimf_field_free);
+ switch (r) {
+ case MAILIMF_NO_ERROR:
+ /* do nothing */
+ break;
+
+ case MAILIMF_ERROR_PARSE:
+ list = clist_new();
+ if (list == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto err;
+ }
+ break;
+
+ default:
+ res = r;
+ goto err;
+ }
+
+ fields = mailimf_fields_new(list);
+ if (fields == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free;
+ }
+
+ * result = fields;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free:
+ if (list != NULL) {
+ clist_foreach(list, (clist_func) mailimf_field_free, NULL);
+ clist_free(list);
+ }
+ err:
+ return res;
+}
+
+
+
+static int
+mailimf_only_optional_field_parse(const char * message,
+ size_t length,
+ size_t * index,
+ struct mailimf_field ** result)
+{
+ int r;
+ size_t cur_token;
+ struct mailimf_optional_field * optional_field;
+ struct mailimf_field * field;
+
+ cur_token = * index;
+
+ r = mailimf_optional_field_parse(message, length, &cur_token,
+ &optional_field);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ field = mailimf_field_new(MAILIMF_FIELD_OPTIONAL_FIELD, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, optional_field);
+ if (field == NULL) {
+ mailimf_optional_field_free(optional_field);
+ return MAILIMF_ERROR_MEMORY;
+ }
+
+ * result = field;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+int
+mailimf_optional_fields_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_fields ** result)
+{
+ size_t cur_token;
+ clist * list;
+ struct mailimf_fields * fields;
+ int r;
+ int res;
+
+ cur_token = * index;
+
+ list = NULL;
+
+ r = mailimf_struct_multiple_parse(message, length, &cur_token,
+ &list,
+ (mailimf_struct_parser *)
+ mailimf_only_optional_field_parse,
+ (mailimf_struct_destructor *)
+ mailimf_field_free);
+ switch (r) {
+ case MAILIMF_NO_ERROR:
+ /* do nothing */
+ break;
+
+ case MAILIMF_ERROR_PARSE:
+ list = clist_new();
+ if (list == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto err;
+ }
+ break;
+
+ default:
+ res = r;
+ goto err;
+ }
+
+ fields = mailimf_fields_new(list);
+ if (fields == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free;
+ }
+
+ * result = fields;
+ * index = cur_token;
+
+ return MAILIMF_NO_ERROR;
+
+ free:
+ if (list != NULL) {
+ clist_foreach(list, (clist_func) mailimf_field_free, NULL);
+ clist_free(list);
+ }
+ err:
+ return res;
+}
diff --git a/src/plugins/mailmbox/mailimf.h b/src/plugins/mailmbox/mailimf.h
@@ -0,0 +1,345 @@
+/*
+ * libEtPan! -- a mail stuff library
+ *
+ * Copyright (C) 2001, 2002 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef MAILIMF_H
+
+#define MAILIMF_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "mailimf_types.h"
+#include "mailimf_write.h"
+#include "mailimf_types_helper.h"
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+/*
+ mailimf_message_parse will parse the given message
+
+ @param message this is a string containing the message content
+ @param length this is the size of the given string
+ @param index this is a pointer to the start of the message in
+ the given string, (* index) is modified to point at the end
+ of the parsed data
+ @param result the result of the parse operation is stored in
+ (* result)
+
+ @return MAILIMF_NO_ERROR on success, MAILIMF_ERROR_XXX on error
+*/
+
+int mailimf_message_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_message ** result);
+
+/*
+ mailimf_body_parse will parse the given text part of a message
+
+ @param message this is a string containing the message text part
+ @param length this is the size of the given string
+ @param index this is a pointer to the start of the message text part in
+ the given string, (* index) is modified to point at the end
+ of the parsed data
+ @param result the result of the parse operation is stored in
+ (* result)
+
+ @return MAILIMF_NO_ERROR on success, MAILIMF_ERROR_XXX on error
+*/
+
+int mailimf_body_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_body ** result);
+
+/*
+ mailimf_fields_parse will parse the given header fields
+
+ @param message this is a string containing the header fields
+ @param length this is the size of the given string
+ @param index this is a pointer to the start of the header fields in
+ the given string, (* index) is modified to point at the end
+ of the parsed data
+ @param result the result of the parse operation is stored in
+ (* result)
+
+ @return MAILIMF_NO_ERROR on success, MAILIMF_ERROR_XXX on error
+*/
+
+int mailimf_fields_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_fields ** result);
+
+/*
+ mailimf_mailbox_list_parse will parse the given mailbox list
+
+ @param message this is a string containing the mailbox list
+ @param length this is the size of the given string
+ @param index this is a pointer to the start of the mailbox list in
+ the given string, (* index) is modified to point at the end
+ of the parsed data
+ @param result the result of the parse operation is stored in
+ (* result)
+
+ @return MAILIMF_NO_ERROR on success, MAILIMF_ERROR_XXX on error
+*/
+
+int
+mailimf_mailbox_list_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_mailbox_list ** result);
+
+/*
+ mailimf_address_list_parse will parse the given address list
+
+ @param message this is a string containing the address list
+ @param length this is the size of the given string
+ @param index this is a pointer to the start of the address list in
+ the given string, (* index) is modified to point at the end
+ of the parsed data
+ @param result the result of the parse operation is stored in
+ (* result)
+
+ @return MAILIMF_NO_ERROR on success, MAILIMF_ERROR_XXX on error
+*/
+
+int
+mailimf_address_list_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_address_list ** result);
+
+/*
+ mailimf_address_parse will parse the given address
+
+ @param message this is a string containing the address
+ @param length this is the size of the given string
+ @param index this is a pointer to the start of the address in
+ the given string, (* index) is modified to point at the end
+ of the parsed data
+ @param result the result of the parse operation is stored in
+ (* result)
+
+ @return MAILIMF_NO_ERROR on success, MAILIMF_ERROR_XXX on error
+*/
+
+int mailimf_address_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_address ** result);
+
+/*
+ mailimf_mailbox_parse will parse the given address
+
+ @param message this is a string containing the mailbox
+ @param length this is the size of the given string
+ @param index this is a pointer to the start of the mailbox in
+ the given string, (* index) is modified to point at the end
+ of the parsed data
+ @param result the result of the parse operation is stored in
+ (* result)
+
+ @return MAILIMF_NO_ERROR on success, MAILIMF_ERROR_XXX on error
+*/
+
+int mailimf_mailbox_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_mailbox ** result);
+
+/*
+ mailimf_date_time_parse will parse the given RFC 2822 date
+
+ @param message this is a string containing the date
+ @param length this is the size of the given string
+ @param index this is a pointer to the start of the date in
+ the given string, (* index) is modified to point at the end
+ of the parsed data
+ @param result the result of the parse operation is stored in
+ (* result)
+
+ @return MAILIMF_NO_ERROR on success, MAILIMF_ERROR_XXX on error
+*/
+
+int mailimf_date_time_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_date_time ** result);
+
+/*
+ mailimf_envelope_fields_parse will parse the given fields (Date,
+ From, Sender, Reply-To, To, Cc, Bcc, Message-ID, In-Reply-To,
+ References and Subject)
+
+ @param message this is a string containing the header fields
+ @param length this is the size of the given string
+ @param index this is a pointer to the start of the header fields in
+ the given string, (* index) is modified to point at the end
+ of the parsed data
+ @param result the result of the parse operation is stored in
+ (* result)
+
+ @return MAILIMF_NO_ERROR on success, MAILIMF_ERROR_XXX on error
+*/
+
+int mailimf_envelope_fields_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_fields ** result);
+
+/*
+ mailimf_ignore_field_parse will skip the given field
+
+ @param message this is a string containing the header field
+ @param length this is the size of the given string
+ @param index this is a pointer to the start of the header field in
+ the given string, (* index) is modified to point at the end
+ of the parsed data
+
+ @return MAILIMF_NO_ERROR on success, MAILIMF_ERROR_XXX on error
+*/
+
+
+int mailimf_ignore_field_parse(const char * message, size_t length,
+ size_t * index);
+
+/*
+ mailimf_envelope_fields will parse the given fields (Date,
+ From, Sender, Reply-To, To, Cc, Bcc, Message-ID, In-Reply-To,
+ References and Subject), other fields will be added as optional
+ fields.
+
+ @param message this is a string containing the header fields
+ @param length this is the size of the given string
+ @param index this is a pointer to the start of the header fields in
+ the given string, (* index) is modified to point at the end
+ of the parsed data
+ @param result the result of the parse operation is stored in
+ (* result)
+
+ @return MAILIMF_NO_ERROR on success, MAILIMF_ERROR_XXX on error
+*/
+
+
+int
+mailimf_envelope_and_optional_fields_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_fields ** result);
+
+/*
+ mailimf_envelope_fields will parse the given fields as optional
+ fields.
+
+ @param message this is a string containing the header fields
+ @param length this is the size of the given string
+ @param index this is a pointer to the start of the header fields in
+ the given string, (* index) is modified to point at the end
+ of the parsed data
+ @param result the result of the parse operation is stored in
+ (* result)
+
+ @return MAILIMF_NO_ERROR on success, MAILIMF_ERROR_XXX on error
+*/
+
+int
+mailimf_optional_fields_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_fields ** result);
+
+
+/* internal use, exported for MIME */
+
+int mailimf_fws_parse(const char * message, size_t length, size_t * index);
+
+int mailimf_cfws_parse(const char * message, size_t length,
+ size_t * index);
+
+int mailimf_char_parse(const char * message, size_t length,
+ size_t * index, char token);
+
+int mailimf_unstrict_char_parse(const char * message, size_t length,
+ size_t * index, char token);
+
+int mailimf_crlf_parse(const char * message, size_t length, size_t * index);
+
+int
+mailimf_custom_string_parse(const char * message, size_t length,
+ size_t * index, char ** result,
+ int (* is_custom_char)(char));
+
+int
+mailimf_token_case_insensitive_len_parse(const char * message, size_t length,
+ size_t * index, char * token,
+ size_t token_length);
+
+#define mailimf_token_case_insensitive_parse(message, length, index, token) \
+ mailimf_token_case_insensitive_len_parse(message, length, index, token, \
+ sizeof(token) - 1)
+
+int mailimf_quoted_string_parse(const char * message, size_t length,
+ size_t * index, char ** result);
+
+int
+mailimf_number_parse(const char * message, size_t length,
+ size_t * index, uint32_t * result);
+
+int mailimf_msg_id_parse(const char * message, size_t length,
+ size_t * index,
+ char ** result);
+
+int mailimf_msg_id_list_parse(const char * message, size_t length,
+ size_t * index, clist ** result);
+
+int mailimf_word_parse(const char * message, size_t length,
+ size_t * index, char ** result);
+
+int mailimf_atom_parse(const char * message, size_t length,
+ size_t * index, char ** result);
+
+int mailimf_fws_atom_parse(const char * message, size_t length,
+ size_t * index, char ** result);
+
+int mailimf_fws_word_parse(const char * message, size_t length,
+ size_t * index, char ** result);
+
+int mailimf_fws_quoted_string_parse(const char * message, size_t length,
+ size_t * index, char ** result);
+
+/* exported for IMAP */
+
+int mailimf_references_parse(const char * message, size_t length,
+ size_t * index,
+ struct mailimf_references ** result);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/plugins/mailmbox/mailimf_types.c b/src/plugins/mailmbox/mailimf_types.c
@@ -0,0 +1,868 @@
+/*
+ * libEtPan! -- a mail stuff library
+ *
+ * Copyright (C) 2001, 2002 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#include "mailimf_types.h"
+#include "mmapstring.h"
+#include <stdlib.h>
+
+void mailimf_atom_free(char * atom)
+{
+ free(atom);
+}
+
+void mailimf_dot_atom_free(char * dot_atom)
+{
+ free(dot_atom);
+}
+
+void mailimf_dot_atom_text_free(char * dot_atom)
+{
+ free(dot_atom);
+}
+
+void mailimf_quoted_string_free(char * quoted_string)
+{
+ free(quoted_string);
+}
+
+void mailimf_word_free(char * word)
+{
+ free(word);
+}
+
+void mailimf_phrase_free(char * phrase)
+{
+ free(phrase);
+}
+
+void mailimf_unstructured_free(char * unstructured)
+{
+ free(unstructured);
+}
+
+
+struct mailimf_date_time *
+mailimf_date_time_new(int dt_day, int dt_month, int dt_year,
+ int dt_hour, int dt_min, int dt_sec, int dt_zone)
+{
+ struct mailimf_date_time * date_time;
+
+ date_time = malloc(sizeof(* date_time));
+ if (date_time == NULL)
+ return NULL;
+
+ date_time->dt_day = dt_day;
+ date_time->dt_month = dt_month;
+ date_time->dt_year = dt_year;
+ date_time->dt_hour = dt_hour;
+ date_time->dt_min = dt_min;
+ date_time->dt_sec = dt_sec;
+ date_time->dt_zone = dt_zone;
+
+ return date_time;
+}
+
+
+void mailimf_date_time_free(struct mailimf_date_time * date_time)
+{
+ free(date_time);
+}
+
+
+
+
+struct mailimf_address *
+mailimf_address_new(int ad_type, struct mailimf_mailbox * ad_mailbox,
+ struct mailimf_group * ad_group)
+{
+ struct mailimf_address * address;
+
+ address = malloc(sizeof(* address));
+ if (address == NULL)
+ return NULL;
+
+ address->ad_type = ad_type;
+ switch (ad_type) {
+ case MAILIMF_ADDRESS_MAILBOX:
+ address->ad_data.ad_mailbox = ad_mailbox;
+ break;
+ case MAILIMF_ADDRESS_GROUP:
+ address->ad_data.ad_group = ad_group;
+ break;
+ }
+
+ return address;
+}
+
+void mailimf_address_free(struct mailimf_address * address)
+{
+ switch (address->ad_type) {
+ case MAILIMF_ADDRESS_MAILBOX:
+ mailimf_mailbox_free(address->ad_data.ad_mailbox);
+ break;
+ case MAILIMF_ADDRESS_GROUP:
+ mailimf_group_free(address->ad_data.ad_group);
+ }
+ free(address);
+}
+
+struct mailimf_mailbox *
+mailimf_mailbox_new(char * mb_display_name, char * mb_addr_spec)
+{
+ struct mailimf_mailbox * mb;
+
+ mb = malloc(sizeof(* mb));
+ if (mb == NULL)
+ return NULL;
+
+ mb->mb_display_name = mb_display_name;
+ mb->mb_addr_spec = mb_addr_spec;
+
+ return mb;
+}
+
+void mailimf_mailbox_free(struct mailimf_mailbox * mailbox)
+{
+ if (mailbox->mb_display_name != NULL)
+ mailimf_display_name_free(mailbox->mb_display_name);
+ mailimf_addr_spec_free(mailbox->mb_addr_spec);
+ free(mailbox);
+}
+
+
+void mailimf_angle_addr_free(char * angle_addr)
+{
+ free(angle_addr);
+}
+
+
+struct mailimf_group *
+mailimf_group_new(char * grp_display_name,
+ struct mailimf_mailbox_list * grp_mb_list)
+{
+ struct mailimf_group * group;
+
+ group = malloc(sizeof(* group));
+ if (group == NULL)
+ return NULL;
+
+ group->grp_display_name = grp_display_name;
+ group->grp_mb_list = grp_mb_list;
+
+ return group;
+}
+
+void mailimf_group_free(struct mailimf_group * group)
+{
+ if (group->grp_mb_list)
+ mailimf_mailbox_list_free(group->grp_mb_list);
+ mailimf_display_name_free(group->grp_display_name);
+ free(group);
+}
+
+void mailimf_display_name_free(char * display_name)
+{
+ mailimf_phrase_free(display_name);
+}
+
+
+struct mailimf_mailbox_list *
+mailimf_mailbox_list_new(clist * mb_list)
+{
+ struct mailimf_mailbox_list * mbl;
+
+ mbl = malloc(sizeof(* mbl));
+ if (mbl == NULL)
+ return NULL;
+
+ mbl->mb_list = mb_list;
+
+ return mbl;
+}
+
+void mailimf_mailbox_list_free(struct mailimf_mailbox_list * mb_list)
+{
+ clist_foreach(mb_list->mb_list, (clist_func) mailimf_mailbox_free, NULL);
+ clist_free(mb_list->mb_list);
+ free(mb_list);
+}
+
+
+
+struct mailimf_address_list *
+mailimf_address_list_new(clist * ad_list)
+{
+ struct mailimf_address_list * addr_list;
+
+ addr_list = malloc(sizeof(* addr_list));
+ if (addr_list == NULL)
+ return NULL;
+
+ addr_list->ad_list = ad_list;
+
+ return addr_list;
+}
+
+void mailimf_address_list_free(struct mailimf_address_list * addr_list)
+{
+ clist_foreach(addr_list->ad_list, (clist_func) mailimf_address_free, NULL);
+ clist_free(addr_list->ad_list);
+ free(addr_list);
+}
+
+
+void mailimf_addr_spec_free(char * addr_spec)
+{
+ free(addr_spec);
+}
+
+void mailimf_local_part_free(char * local_part)
+{
+ free(local_part);
+}
+
+void mailimf_domain_free(char * domain)
+{
+ free(domain);
+}
+
+void mailimf_domain_literal_free(char * domain_literal)
+{
+ free(domain_literal);
+}
+
+
+
+struct mailimf_message *
+mailimf_message_new(struct mailimf_fields * msg_fields,
+ struct mailimf_body * msg_body)
+{
+ struct mailimf_message * message;
+
+ message = malloc(sizeof(* message));
+ if (message == NULL)
+ return NULL;
+
+ message->msg_fields = msg_fields;
+ message->msg_body = msg_body;
+
+ return message;
+}
+
+void mailimf_message_free(struct mailimf_message * message)
+{
+ mailimf_body_free(message->msg_body);
+ mailimf_fields_free(message->msg_fields);
+ free(message);
+}
+
+
+struct mailimf_body * mailimf_body_new(const char * bd_text, size_t bd_size)
+{
+ struct mailimf_body * body;
+
+ body = malloc(sizeof(* body));
+ if (body == NULL)
+ return NULL;
+ body->bd_text = bd_text;
+ body->bd_size = bd_size;
+
+ return body;
+}
+
+void mailimf_body_free(struct mailimf_body * body)
+{
+ free(body);
+}
+
+
+
+struct mailimf_field *
+mailimf_field_new(int fld_type,
+ struct mailimf_return * fld_return_path,
+ struct mailimf_orig_date * fld_resent_date,
+ struct mailimf_from * fld_resent_from,
+ struct mailimf_sender * fld_resent_sender,
+ struct mailimf_to * fld_resent_to,
+ struct mailimf_cc * fld_resent_cc,
+ struct mailimf_bcc * fld_resent_bcc,
+ struct mailimf_message_id * fld_resent_msg_id,
+ struct mailimf_orig_date * fld_orig_date,
+ struct mailimf_from * fld_from,
+ struct mailimf_sender * fld_sender,
+ struct mailimf_reply_to * fld_reply_to,
+ struct mailimf_to * fld_to,
+ struct mailimf_cc * fld_cc,
+ struct mailimf_bcc * fld_bcc,
+ struct mailimf_message_id * fld_message_id,
+ struct mailimf_in_reply_to * fld_in_reply_to,
+ struct mailimf_references * fld_references,
+ struct mailimf_subject * fld_subject,
+ struct mailimf_comments * fld_comments,
+ struct mailimf_keywords * fld_keywords,
+ struct mailimf_optional_field * fld_optional_field)
+{
+ struct mailimf_field * field;
+
+ field = malloc(sizeof(* field));
+ if (field == NULL)
+ return NULL;
+
+ field->fld_type = fld_type;
+ switch (fld_type) {
+ case MAILIMF_FIELD_RETURN_PATH:
+ field->fld_data.fld_return_path = fld_return_path;
+ break;
+ case MAILIMF_FIELD_RESENT_DATE:
+ field->fld_data.fld_resent_date = fld_resent_date;
+ break;
+ case MAILIMF_FIELD_RESENT_FROM:
+ field->fld_data.fld_resent_from = fld_resent_from;
+ break;
+ case MAILIMF_FIELD_RESENT_SENDER:
+ field->fld_data.fld_resent_sender = fld_resent_sender;
+ break;
+ case MAILIMF_FIELD_RESENT_TO:
+ field->fld_data.fld_resent_to = fld_resent_to;
+ break;
+ case MAILIMF_FIELD_RESENT_CC:
+ field->fld_data.fld_resent_cc = fld_resent_cc;
+ break;
+ case MAILIMF_FIELD_RESENT_BCC:
+ field->fld_data.fld_resent_bcc = fld_resent_bcc;
+ break;
+ case MAILIMF_FIELD_RESENT_MSG_ID:
+ field->fld_data.fld_resent_msg_id = fld_resent_msg_id;
+ break;
+ case MAILIMF_FIELD_ORIG_DATE:
+ field->fld_data.fld_orig_date = fld_orig_date;
+ break;
+ case MAILIMF_FIELD_FROM:
+ field->fld_data.fld_from = fld_from;
+ break;
+ case MAILIMF_FIELD_SENDER:
+ field->fld_data.fld_sender = fld_sender;
+ break;
+ case MAILIMF_FIELD_REPLY_TO:
+ field->fld_data.fld_reply_to = fld_reply_to;
+ break;
+ case MAILIMF_FIELD_TO:
+ field->fld_data.fld_to = fld_to;
+ break;
+ case MAILIMF_FIELD_CC:
+ field->fld_data.fld_cc = fld_cc;
+ break;
+ case MAILIMF_FIELD_BCC:
+ field->fld_data.fld_bcc = fld_bcc;
+ break;
+ case MAILIMF_FIELD_MESSAGE_ID:
+ field->fld_data.fld_message_id = fld_message_id;
+ break;
+ case MAILIMF_FIELD_IN_REPLY_TO:
+ field->fld_data.fld_in_reply_to = fld_in_reply_to;
+ break;
+ case MAILIMF_FIELD_REFERENCES:
+ field->fld_data.fld_references = fld_references;
+ break;
+ case MAILIMF_FIELD_SUBJECT:
+ field->fld_data.fld_subject = fld_subject;
+ break;
+ case MAILIMF_FIELD_COMMENTS:
+ field->fld_data.fld_comments = fld_comments;
+ break;
+ case MAILIMF_FIELD_KEYWORDS:
+ field->fld_data.fld_keywords = fld_keywords;
+ break;
+ case MAILIMF_FIELD_OPTIONAL_FIELD:
+ field->fld_data.fld_optional_field = fld_optional_field;
+ break;
+ }
+
+ return field;
+}
+
+void mailimf_field_free(struct mailimf_field * field)
+{
+ switch (field->fld_type) {
+ case MAILIMF_FIELD_RETURN_PATH:
+ mailimf_return_free(field->fld_data.fld_return_path);
+ break;
+ case MAILIMF_FIELD_RESENT_DATE:
+ mailimf_orig_date_free(field->fld_data.fld_resent_date);
+ break;
+ case MAILIMF_FIELD_RESENT_FROM:
+ mailimf_from_free(field->fld_data.fld_resent_from);
+ break;
+ case MAILIMF_FIELD_RESENT_SENDER:
+ mailimf_sender_free(field->fld_data.fld_resent_sender);
+ break;
+ case MAILIMF_FIELD_RESENT_TO:
+ mailimf_to_free(field->fld_data.fld_resent_to);
+ break;
+ case MAILIMF_FIELD_RESENT_CC:
+ mailimf_cc_free(field->fld_data.fld_resent_cc);
+ break;
+ case MAILIMF_FIELD_RESENT_BCC:
+ mailimf_bcc_free(field->fld_data.fld_resent_bcc);
+ break;
+ case MAILIMF_FIELD_RESENT_MSG_ID:
+ mailimf_message_id_free(field->fld_data.fld_resent_msg_id);
+ break;
+ case MAILIMF_FIELD_ORIG_DATE:
+ mailimf_orig_date_free(field->fld_data.fld_orig_date);
+ break;
+ case MAILIMF_FIELD_FROM:
+ mailimf_from_free(field->fld_data.fld_from);
+ break;
+ case MAILIMF_FIELD_SENDER:
+ mailimf_sender_free(field->fld_data.fld_sender);
+ break;
+ case MAILIMF_FIELD_REPLY_TO:
+ mailimf_reply_to_free(field->fld_data.fld_reply_to);
+ break;
+ case MAILIMF_FIELD_TO:
+ mailimf_to_free(field->fld_data.fld_to);
+ break;
+ case MAILIMF_FIELD_CC:
+ mailimf_cc_free(field->fld_data.fld_cc);
+ break;
+ case MAILIMF_FIELD_BCC:
+ mailimf_bcc_free(field->fld_data.fld_bcc);
+ break;
+ case MAILIMF_FIELD_MESSAGE_ID:
+ mailimf_message_id_free(field->fld_data.fld_message_id);
+ break;
+ case MAILIMF_FIELD_IN_REPLY_TO:
+ mailimf_in_reply_to_free(field->fld_data.fld_in_reply_to);
+ break;
+ case MAILIMF_FIELD_REFERENCES:
+ mailimf_references_free(field->fld_data.fld_references);
+ break;
+ case MAILIMF_FIELD_SUBJECT:
+ mailimf_subject_free(field->fld_data.fld_subject);
+ break;
+ case MAILIMF_FIELD_COMMENTS:
+ mailimf_comments_free(field->fld_data.fld_comments);
+ break;
+ case MAILIMF_FIELD_KEYWORDS:
+ mailimf_keywords_free(field->fld_data.fld_keywords);
+ break;
+ case MAILIMF_FIELD_OPTIONAL_FIELD:
+ mailimf_optional_field_free(field->fld_data.fld_optional_field);
+ break;
+ }
+
+ free(field);
+}
+
+struct mailimf_fields * mailimf_fields_new(clist * fld_list)
+{
+ struct mailimf_fields * fields;
+
+ fields = malloc(sizeof(* fields));
+ if (fields == NULL)
+ return NULL;
+
+ fields->fld_list = fld_list;
+
+ return fields;
+}
+
+void mailimf_fields_free(struct mailimf_fields * fields)
+{
+ if (fields->fld_list != NULL) {
+ clist_foreach(fields->fld_list, (clist_func) mailimf_field_free, NULL);
+ clist_free(fields->fld_list);
+ }
+ free(fields);
+}
+
+
+struct mailimf_orig_date * mailimf_orig_date_new(struct mailimf_date_time *
+ dt_date_time)
+{
+ struct mailimf_orig_date * orig_date;
+
+ orig_date = malloc(sizeof(* orig_date));
+ if (orig_date == NULL)
+ return NULL;
+
+ orig_date->dt_date_time = dt_date_time;
+
+ return orig_date;
+}
+
+void mailimf_orig_date_free(struct mailimf_orig_date * orig_date)
+{
+ if (orig_date->dt_date_time != NULL)
+ mailimf_date_time_free(orig_date->dt_date_time);
+ free(orig_date);
+}
+
+struct mailimf_from *
+mailimf_from_new(struct mailimf_mailbox_list * frm_mb_list)
+{
+ struct mailimf_from * from;
+
+ from = malloc(sizeof(* from));
+ if (from == NULL)
+ return NULL;
+
+ from->frm_mb_list = frm_mb_list;
+
+ return from;
+}
+
+void mailimf_from_free(struct mailimf_from * from)
+{
+ if (from->frm_mb_list != NULL)
+ mailimf_mailbox_list_free(from->frm_mb_list);
+ free(from);
+}
+
+struct mailimf_sender * mailimf_sender_new(struct mailimf_mailbox * snd_mb)
+{
+ struct mailimf_sender * sender;
+
+ sender = malloc(sizeof(* sender));
+ if (sender == NULL)
+ return NULL;
+
+ sender->snd_mb = snd_mb;
+
+ return sender;
+}
+
+void mailimf_sender_free(struct mailimf_sender * sender)
+{
+ if (sender->snd_mb != NULL)
+ mailimf_mailbox_free(sender->snd_mb);
+ free(sender);
+}
+
+struct mailimf_reply_to *
+mailimf_reply_to_new(struct mailimf_address_list * rt_addr_list)
+{
+ struct mailimf_reply_to * reply_to;
+
+ reply_to = malloc(sizeof(* reply_to));
+ if (reply_to == NULL)
+ return NULL;
+
+ reply_to->rt_addr_list = rt_addr_list;
+
+ return reply_to;
+}
+
+void mailimf_reply_to_free(struct mailimf_reply_to * reply_to)
+{
+ if (reply_to->rt_addr_list != NULL)
+ mailimf_address_list_free(reply_to->rt_addr_list);
+ free(reply_to);
+}
+
+struct mailimf_to * mailimf_to_new(struct mailimf_address_list * to_addr_list)
+{
+ struct mailimf_to * to;
+
+ to = malloc(sizeof(* to));
+ if (to == NULL)
+ return NULL;
+
+ to->to_addr_list = to_addr_list;
+
+ return to;
+}
+
+void mailimf_to_free(struct mailimf_to * to)
+{
+ if (to->to_addr_list != NULL)
+ mailimf_address_list_free(to->to_addr_list);
+ free(to);
+}
+
+struct mailimf_cc * mailimf_cc_new(struct mailimf_address_list * cc_addr_list)
+{
+ struct mailimf_cc * cc;
+
+ cc = malloc(sizeof(* cc));
+ if (cc == NULL)
+ return NULL;
+
+ cc->cc_addr_list = cc_addr_list;
+
+ return cc;
+}
+
+void mailimf_cc_free(struct mailimf_cc * cc)
+{
+ if (cc->cc_addr_list != NULL)
+ mailimf_address_list_free(cc->cc_addr_list);
+ free(cc);
+}
+
+struct mailimf_bcc *
+mailimf_bcc_new(struct mailimf_address_list * bcc_addr_list)
+{
+ struct mailimf_bcc * bcc;
+
+ bcc = malloc(sizeof(* bcc));
+ if (bcc == NULL)
+ return NULL;
+
+ bcc->bcc_addr_list = bcc_addr_list;
+
+ return bcc;
+}
+
+void mailimf_bcc_free(struct mailimf_bcc * bcc)
+{
+ if (bcc->bcc_addr_list != NULL)
+ mailimf_address_list_free(bcc->bcc_addr_list);
+ free(bcc);
+}
+
+struct mailimf_message_id * mailimf_message_id_new(char * mid_value)
+{
+ struct mailimf_message_id * message_id;
+
+ message_id = malloc(sizeof(* message_id));
+ if (message_id == NULL)
+ return NULL;
+
+ message_id->mid_value = mid_value;
+
+ return message_id;
+}
+
+void mailimf_message_id_free(struct mailimf_message_id * message_id)
+{
+ if (message_id->mid_value != NULL)
+ mailimf_msg_id_free(message_id->mid_value);
+ free(message_id);
+}
+
+struct mailimf_in_reply_to * mailimf_in_reply_to_new(clist * mid_list)
+{
+ struct mailimf_in_reply_to * in_reply_to;
+
+ in_reply_to = malloc(sizeof(* in_reply_to));
+ if (in_reply_to == NULL)
+ return NULL;
+
+ in_reply_to->mid_list = mid_list;
+
+ return in_reply_to;
+}
+
+void mailimf_in_reply_to_free(struct mailimf_in_reply_to * in_reply_to)
+{
+ clist_foreach(in_reply_to->mid_list,
+ (clist_func) mailimf_msg_id_free, NULL);
+ clist_free(in_reply_to->mid_list);
+ free(in_reply_to);
+}
+
+struct mailimf_references * mailimf_references_new(clist * mid_list)
+{
+ struct mailimf_references * ref;
+
+ ref = malloc(sizeof(* ref));
+ if (ref == NULL)
+ return NULL;
+
+ ref->mid_list = mid_list;
+
+ return ref;
+}
+
+void mailimf_references_free(struct mailimf_references * references)
+{
+ clist_foreach(references->mid_list,
+ (clist_func) mailimf_msg_id_free, NULL);
+ clist_free(references->mid_list);
+ free(references);
+}
+
+void mailimf_msg_id_free(char * msg_id)
+{
+ free(msg_id);
+}
+
+void mailimf_id_left_free(char * id_left)
+{
+ free(id_left);
+}
+
+void mailimf_id_right_free(char * id_right)
+{
+ free(id_right);
+}
+
+void mailimf_no_fold_quote_free(char * nfq)
+{
+ free(nfq);
+}
+
+void mailimf_no_fold_literal_free(char * nfl)
+{
+ free(nfl);
+}
+
+struct mailimf_subject * mailimf_subject_new(char * sbj_value)
+{
+ struct mailimf_subject * subject;
+
+ subject = malloc(sizeof(* subject));
+ if (subject == NULL)
+ return NULL;
+
+ subject->sbj_value = sbj_value;
+
+ return subject;
+}
+
+void mailimf_subject_free(struct mailimf_subject * subject)
+{
+ mailimf_unstructured_free(subject->sbj_value);
+ free(subject);
+}
+
+struct mailimf_comments * mailimf_comments_new(char * cm_value)
+{
+ struct mailimf_comments * comments;
+
+ comments = malloc(sizeof(* comments));
+ if (comments == NULL)
+ return NULL;
+
+ comments->cm_value = cm_value;
+
+ return comments;
+}
+
+void mailimf_comments_free(struct mailimf_comments * comments)
+{
+ mailimf_unstructured_free(comments->cm_value);
+ free(comments);
+}
+
+struct mailimf_keywords * mailimf_keywords_new(clist * kw_list)
+{
+ struct mailimf_keywords * keywords;
+
+ keywords = malloc(sizeof(* keywords));
+ if (keywords == NULL)
+ return NULL;
+
+ keywords->kw_list = kw_list;
+
+ return keywords;
+}
+
+void mailimf_keywords_free(struct mailimf_keywords * keywords)
+{
+ clist_foreach(keywords->kw_list, (clist_func) mailimf_phrase_free, NULL);
+ clist_free(keywords->kw_list);
+ free(keywords);
+}
+
+struct mailimf_return *
+mailimf_return_new(struct mailimf_path * ret_path)
+{
+ struct mailimf_return * return_path;
+
+ return_path = malloc(sizeof(* return_path));
+ if (return_path == NULL)
+ return NULL;
+
+ return_path->ret_path = ret_path;
+
+ return return_path;
+}
+
+void mailimf_return_free(struct mailimf_return * return_path)
+{
+ mailimf_path_free(return_path->ret_path);
+ free(return_path);
+}
+
+
+struct mailimf_path * mailimf_path_new(char * pt_addr_spec)
+{
+ struct mailimf_path * path;
+
+ path = malloc(sizeof(* path));
+ if (path == NULL)
+ return NULL;
+
+ path->pt_addr_spec = pt_addr_spec;
+
+ return path;
+}
+
+void mailimf_path_free(struct mailimf_path * path)
+{
+ if (path->pt_addr_spec != NULL)
+ mailimf_addr_spec_free(path->pt_addr_spec);
+ free(path);
+}
+
+struct mailimf_optional_field *
+mailimf_optional_field_new(char * fld_name, char * fld_value)
+{
+ struct mailimf_optional_field * opt_field;
+
+ opt_field = malloc(sizeof(* opt_field));
+ if (opt_field == NULL)
+ return NULL;
+
+ opt_field->fld_name = fld_name;
+ opt_field->fld_value = fld_value;
+
+ return opt_field;
+}
+
+void mailimf_optional_field_free(struct mailimf_optional_field * opt_field)
+{
+ mailimf_field_name_free(opt_field->fld_name);
+ mailimf_unstructured_free(opt_field->fld_value);
+ free(opt_field);
+}
+
+void mailimf_field_name_free(char * field_name)
+{
+ free(field_name);
+}
diff --git a/src/plugins/mailmbox/mailimf_types.h b/src/plugins/mailmbox/mailimf_types.h
@@ -0,0 +1,797 @@
+/*
+ * libEtPan! -- a mail stuff library
+ *
+ * Copyright (C) 2001 - 2003 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+/*
+ * $Id$
+ */
+
+#ifndef MAILIMF_TYPES_H
+
+#define MAILIMF_TYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "clist.h"
+#include <sys/types.h>
+
+/*
+ IMPORTANT NOTE:
+
+ All allocation functions will take as argument allocated data
+ and will store these data in the structure they will allocate.
+ Data should be persistant during all the use of the structure
+ and will be freed by the free function of the structure
+
+ allocation functions will return NULL on failure
+*/
+
+/*
+ mailimf_date_time is a date
+
+ - day is the day of month (1 to 31)
+
+ - month (1 to 12)
+
+ - year (4 digits)
+
+ - hour (0 to 23)
+
+ - min (0 to 59)
+
+ - sec (0 to 59)
+
+ - zone (this is the decimal value that we can read, for example:
+ for "-0200", the value is -200)
+*/
+
+struct mailimf_date_time {
+ int dt_day;
+ int dt_month;
+ int dt_year;
+ int dt_hour;
+ int dt_min;
+ int dt_sec;
+ int dt_zone;
+};
+
+struct mailimf_date_time *
+mailimf_date_time_new(int dt_day, int dt_month, int dt_year,
+ int dt_hour, int dt_min, int dt_sec, int dt_zone);
+
+void mailimf_date_time_free(struct mailimf_date_time * date_time);
+
+
+
+/* this is the type of address */
+
+enum {
+ MAILIMF_ADDRESS_ERROR, /* on parse error */
+ MAILIMF_ADDRESS_MAILBOX, /* if this is a mailbox (mailbox@domain) */
+ MAILIMF_ADDRESS_GROUP, /* if this is a group
+ (group_name: address1@domain1,
+ address2@domain2; ) */
+};
+
+/*
+ mailimf_address is an address
+
+ - type can be MAILIMF_ADDRESS_MAILBOX or MAILIMF_ADDRESS_GROUP
+
+ - mailbox is a mailbox if type is MAILIMF_ADDRESS_MAILBOX
+
+ - group is a group if type is MAILIMF_ADDRESS_GROUP
+*/
+
+struct mailimf_address {
+ int ad_type;
+ union {
+ struct mailimf_mailbox * ad_mailbox; /* can be NULL */
+ struct mailimf_group * ad_group; /* can be NULL */
+ } ad_data;
+};
+
+
+struct mailimf_address *
+mailimf_address_new(int ad_type, struct mailimf_mailbox * ad_mailbox,
+ struct mailimf_group * ad_group);
+
+void mailimf_address_free(struct mailimf_address * address);
+
+
+
+/*
+ mailimf_mailbox is a mailbox
+
+ - display_name is the name that will be displayed for this mailbox,
+ for example 'name' in '"name" <mailbox@domain>,
+ should be allocated with malloc()
+
+ - addr_spec is the mailbox, for example 'mailbox@domain'
+ in '"name" <mailbox@domain>, should be allocated with malloc()
+*/
+
+struct mailimf_mailbox {
+ char * mb_display_name; /* can be NULL */
+ char * mb_addr_spec; /* != NULL */
+};
+
+struct mailimf_mailbox *
+mailimf_mailbox_new(char * mb_display_name, char * mb_addr_spec);
+
+void mailimf_mailbox_free(struct mailimf_mailbox * mailbox);
+
+
+
+/*
+ mailimf_group is a group
+
+ - display_name is the name that will be displayed for this group,
+ for example 'group_name' in
+ 'group_name: address1@domain1, address2@domain2;', should be allocated
+ with malloc()
+
+ - mb_list is a list of mailboxes
+*/
+
+struct mailimf_group {
+ char * grp_display_name; /* != NULL */
+ struct mailimf_mailbox_list * grp_mb_list; /* can be NULL */
+};
+
+struct mailimf_group *
+mailimf_group_new(char * grp_display_name,
+ struct mailimf_mailbox_list * grp_mb_list);
+
+void mailimf_group_free(struct mailimf_group * group);
+
+
+
+/*
+ mailimf_mailbox_list is a list of mailboxes
+
+ - list is a list of mailboxes
+*/
+
+struct mailimf_mailbox_list {
+ clist * mb_list; /* list of (struct mailimf_mailbox *) */
+ /* != NULL */
+};
+
+struct mailimf_mailbox_list *
+mailimf_mailbox_list_new(clist * mb_list);
+
+void mailimf_mailbox_list_free(struct mailimf_mailbox_list * mb_list);
+
+
+
+/*
+ mailimf_address_list is a list of addresses
+
+ - list is a list of addresses
+*/
+
+struct mailimf_address_list {
+ clist * ad_list; /* list of (struct mailimf_address *) */
+ /* != NULL */
+};
+
+struct mailimf_address_list *
+mailimf_address_list_new(clist * ad_list);
+
+void mailimf_address_list_free(struct mailimf_address_list * addr_list);
+
+
+
+
+
+/*
+ mailimf_body is the text part of a message
+
+ - text is the beginning of the text part, it is a substring
+ of an other string
+
+ - size is the size of the text part
+*/
+
+struct mailimf_body {
+ const char * bd_text; /* != NULL */
+ size_t bd_size;
+};
+
+struct mailimf_body * mailimf_body_new(const char * bd_text, size_t bd_size);
+
+void mailimf_body_free(struct mailimf_body * body);
+
+
+
+
+/*
+ mailimf_message is the content of the message
+
+ - fields is the header fields of the message
+
+ - body is the text part of the message
+*/
+
+struct mailimf_message {
+ struct mailimf_fields * msg_fields; /* != NULL */
+ struct mailimf_body * msg_body; /* != NULL */
+};
+
+struct mailimf_message *
+mailimf_message_new(struct mailimf_fields * msg_fields,
+ struct mailimf_body * msg_body);
+
+void mailimf_message_free(struct mailimf_message * message);
+
+
+
+
+/*
+ mailimf_fields is a list of header fields
+
+ - list is a list of header fields
+*/
+
+struct mailimf_fields {
+ clist * fld_list; /* list of (struct mailimf_field *) */
+ /* != NULL */
+};
+
+struct mailimf_fields * mailimf_fields_new(clist * fld_list);
+
+void mailimf_fields_free(struct mailimf_fields * fields);
+
+
+
+/* this is a type of field */
+
+enum {
+ MAILIMF_FIELD_NONE, /* on parse error */
+ MAILIMF_FIELD_RETURN_PATH, /* Return-Path */
+ MAILIMF_FIELD_RESENT_DATE, /* Resent-Date */
+ MAILIMF_FIELD_RESENT_FROM, /* Resent-From */
+ MAILIMF_FIELD_RESENT_SENDER, /* Resent-Sender */
+ MAILIMF_FIELD_RESENT_TO, /* Resent-To */
+ MAILIMF_FIELD_RESENT_CC, /* Resent-Cc */
+ MAILIMF_FIELD_RESENT_BCC, /* Resent-Bcc */
+ MAILIMF_FIELD_RESENT_MSG_ID, /* Resent-Message-ID */
+ MAILIMF_FIELD_ORIG_DATE, /* Date */
+ MAILIMF_FIELD_FROM, /* From */
+ MAILIMF_FIELD_SENDER, /* Sender */
+ MAILIMF_FIELD_REPLY_TO, /* Reply-To */
+ MAILIMF_FIELD_TO, /* To */
+ MAILIMF_FIELD_CC, /* Cc */
+ MAILIMF_FIELD_BCC, /* Bcc */
+ MAILIMF_FIELD_MESSAGE_ID, /* Message-ID */
+ MAILIMF_FIELD_IN_REPLY_TO, /* In-Reply-To */
+ MAILIMF_FIELD_REFERENCES, /* References */
+ MAILIMF_FIELD_SUBJECT, /* Subject */
+ MAILIMF_FIELD_COMMENTS, /* Comments */
+ MAILIMF_FIELD_KEYWORDS, /* Keywords */
+ MAILIMF_FIELD_OPTIONAL_FIELD, /* other field */
+};
+
+/*
+ mailimf_field is a field
+
+ - type is the type of the field
+
+ - return_path is the parsed content of the Return-Path field if type is
+ MAILIMF_FIELD_RETURN_PATH
+
+ - resent_date is the parsed content of the Resent-Date field if type is
+ MAILIMF_FIELD_RESENT_DATE
+
+ - resent_from is the parsed content of the Resent-From field
+
+ - resent_sender is the parsed content of the Resent-Sender field
+
+ - resent_to is the parsed content of the Resent-To field
+
+ - resent_cc is the parsed content of the Resent-Cc field
+
+ - resent_bcc is the parsed content of the Resent-Bcc field
+
+ - resent_msg_id is the parsed content of the Resent-Message-ID field
+
+ - orig_date is the parsed content of the Date field
+
+ - from is the parsed content of the From field
+
+ - sender is the parsed content of the Sender field
+
+ - reply_to is the parsed content of the Reply-To field
+
+ - to is the parsed content of the To field
+
+ - cc is the parsed content of the Cc field
+
+ - bcc is the parsed content of the Bcc field
+
+ - message_id is the parsed content of the Message-ID field
+
+ - in_reply_to is the parsed content of the In-Reply-To field
+
+ - references is the parsed content of the References field
+
+ - subject is the content of the Subject field
+
+ - comments is the content of the Comments field
+
+ - keywords is the parsed content of the Keywords field
+
+ - optional_field is an other field and is not parsed
+*/
+
+#define LIBETPAN_MAILIMF_FIELD_UNION
+
+struct mailimf_field {
+ int fld_type;
+ union {
+ struct mailimf_return * fld_return_path; /* can be NULL */
+ struct mailimf_orig_date * fld_resent_date; /* can be NULL */
+ struct mailimf_from * fld_resent_from; /* can be NULL */
+ struct mailimf_sender * fld_resent_sender; /* can be NULL */
+ struct mailimf_to * fld_resent_to; /* can be NULL */
+ struct mailimf_cc * fld_resent_cc; /* can be NULL */
+ struct mailimf_bcc * fld_resent_bcc; /* can be NULL */
+ struct mailimf_message_id * fld_resent_msg_id; /* can be NULL */
+ struct mailimf_orig_date * fld_orig_date; /* can be NULL */
+ struct mailimf_from * fld_from; /* can be NULL */
+ struct mailimf_sender * fld_sender; /* can be NULL */
+ struct mailimf_reply_to * fld_reply_to; /* can be NULL */
+ struct mailimf_to * fld_to; /* can be NULL */
+ struct mailimf_cc * fld_cc; /* can be NULL */
+ struct mailimf_bcc * fld_bcc; /* can be NULL */
+ struct mailimf_message_id * fld_message_id; /* can be NULL */
+ struct mailimf_in_reply_to * fld_in_reply_to; /* can be NULL */
+ struct mailimf_references * fld_references; /* can be NULL */
+ struct mailimf_subject * fld_subject; /* can be NULL */
+ struct mailimf_comments * fld_comments; /* can be NULL */
+ struct mailimf_keywords * fld_keywords; /* can be NULL */
+ struct mailimf_optional_field * fld_optional_field; /* can be NULL */
+ } fld_data;
+};
+
+struct mailimf_field *
+mailimf_field_new(int fld_type,
+ struct mailimf_return * fld_return_path,
+ struct mailimf_orig_date * fld_resent_date,
+ struct mailimf_from * fld_resent_from,
+ struct mailimf_sender * fld_resent_sender,
+ struct mailimf_to * fld_resent_to,
+ struct mailimf_cc * fld_resent_cc,
+ struct mailimf_bcc * fld_resent_bcc,
+ struct mailimf_message_id * fld_resent_msg_id,
+ struct mailimf_orig_date * fld_orig_date,
+ struct mailimf_from * fld_from,
+ struct mailimf_sender * fld_sender,
+ struct mailimf_reply_to * fld_reply_to,
+ struct mailimf_to * fld_to,
+ struct mailimf_cc * fld_cc,
+ struct mailimf_bcc * fld_bcc,
+ struct mailimf_message_id * fld_message_id,
+ struct mailimf_in_reply_to * fld_in_reply_to,
+ struct mailimf_references * fld_references,
+ struct mailimf_subject * fld_subject,
+ struct mailimf_comments * fld_comments,
+ struct mailimf_keywords * fld_keywords,
+ struct mailimf_optional_field * fld_optional_field);
+
+void mailimf_field_free(struct mailimf_field * field);
+
+
+
+/*
+ mailimf_orig_date is the parsed Date field
+
+ - date_time is the parsed date
+*/
+
+struct mailimf_orig_date {
+ struct mailimf_date_time * dt_date_time; /* != NULL */
+};
+
+struct mailimf_orig_date * mailimf_orig_date_new(struct mailimf_date_time *
+ dt_date_time);
+
+void mailimf_orig_date_free(struct mailimf_orig_date * orig_date);
+
+
+
+
+/*
+ mailimf_from is the parsed From field
+
+ - mb_list is the parsed mailbox list
+*/
+
+struct mailimf_from {
+ struct mailimf_mailbox_list * frm_mb_list; /* != NULL */
+};
+
+struct mailimf_from *
+mailimf_from_new(struct mailimf_mailbox_list * frm_mb_list);
+
+void mailimf_from_free(struct mailimf_from * from);
+
+
+
+/*
+ mailimf_sender is the parsed Sender field
+
+ - mb is the parsed mailbox
+*/
+
+struct mailimf_sender {
+ struct mailimf_mailbox * snd_mb; /* != NULL */
+};
+
+struct mailimf_sender * mailimf_sender_new(struct mailimf_mailbox * snd_mb);
+
+void mailimf_sender_free(struct mailimf_sender * sender);
+
+
+
+
+/*
+ mailimf_reply_to is the parsed Reply-To field
+
+ - addr_list is the parsed address list
+ */
+
+struct mailimf_reply_to {
+ struct mailimf_address_list * rt_addr_list; /* != NULL */
+};
+
+struct mailimf_reply_to *
+mailimf_reply_to_new(struct mailimf_address_list * rt_addr_list);
+
+void mailimf_reply_to_free(struct mailimf_reply_to * reply_to);
+
+
+
+
+/*
+ mailimf_to is the parsed To field
+
+ - addr_list is the parsed address list
+*/
+
+struct mailimf_to {
+ struct mailimf_address_list * to_addr_list; /* != NULL */
+};
+
+struct mailimf_to * mailimf_to_new(struct mailimf_address_list * to_addr_list);
+
+void mailimf_to_free(struct mailimf_to * to);
+
+
+
+
+/*
+ mailimf_cc is the parsed Cc field
+
+ - addr_list is the parsed addres list
+*/
+
+struct mailimf_cc {
+ struct mailimf_address_list * cc_addr_list; /* != NULL */
+};
+
+struct mailimf_cc * mailimf_cc_new(struct mailimf_address_list * cc_addr_list);
+
+void mailimf_cc_free(struct mailimf_cc * cc);
+
+
+
+
+/*
+ mailimf_bcc is the parsed Bcc field
+
+ - addr_list is the parsed addres list
+*/
+
+struct mailimf_bcc {
+ struct mailimf_address_list * bcc_addr_list; /* can be NULL */
+};
+
+struct mailimf_bcc *
+mailimf_bcc_new(struct mailimf_address_list * bcc_addr_list);
+
+void mailimf_bcc_free(struct mailimf_bcc * bcc);
+
+
+
+/*
+ mailimf_message_id is the parsed Message-ID field
+
+ - value is the message identifier
+*/
+
+struct mailimf_message_id {
+ char * mid_value; /* != NULL */
+};
+
+struct mailimf_message_id * mailimf_message_id_new(char * mid_value);
+
+void mailimf_message_id_free(struct mailimf_message_id * message_id);
+
+
+
+
+/*
+ mailimf_in_reply_to is the parsed In-Reply-To field
+
+ - msg_id_list is the list of message identifers
+*/
+
+struct mailimf_in_reply_to {
+ clist * mid_list; /* list of (char *) */
+ /* != NULL */
+};
+
+struct mailimf_in_reply_to * mailimf_in_reply_to_new(clist * mid_list);
+
+void mailimf_in_reply_to_free(struct mailimf_in_reply_to * in_reply_to);
+
+
+
+/*
+ mailimf_references is the parsed References field
+
+ - msg_id_list is the list of message identifiers
+ */
+
+struct mailimf_references {
+ clist * mid_list; /* list of (char *) */
+ /* != NULL */
+};
+
+struct mailimf_references * mailimf_references_new(clist * mid_list);
+
+void mailimf_references_free(struct mailimf_references * references);
+
+
+
+/*
+ mailimf_subject is the parsed Subject field
+
+ - value is the value of the field
+*/
+
+struct mailimf_subject {
+ char * sbj_value; /* != NULL */
+};
+
+struct mailimf_subject * mailimf_subject_new(char * sbj_value);
+
+void mailimf_subject_free(struct mailimf_subject * subject);
+
+
+/*
+ mailimf_comments is the parsed Comments field
+
+ - value is the value of the field
+*/
+
+struct mailimf_comments {
+ char * cm_value; /* != NULL */
+};
+
+struct mailimf_comments * mailimf_comments_new(char * cm_value);
+
+void mailimf_comments_free(struct mailimf_comments * comments);
+
+
+/*
+ mailimf_keywords is the parsed Keywords field
+
+ - list is the list of keywords
+*/
+
+struct mailimf_keywords {
+ clist * kw_list; /* list of (char *) */
+ /* != NULL */
+};
+
+struct mailimf_keywords * mailimf_keywords_new(clist * kw_list);
+
+void mailimf_keywords_free(struct mailimf_keywords * keywords);
+
+
+/*
+ mailimf_return is the parsed Return-Path field
+
+ - path is the parsed value of Return-Path
+*/
+
+struct mailimf_return {
+ struct mailimf_path * ret_path; /* != NULL */
+};
+
+struct mailimf_return *
+mailimf_return_new(struct mailimf_path * ret_path);
+
+void mailimf_return_free(struct mailimf_return * return_path);
+
+
+/*
+ mailimf_path is the parsed value of Return-Path
+
+ - addr_spec is a mailbox
+*/
+
+struct mailimf_path {
+ char * pt_addr_spec; /* can be NULL */
+};
+
+struct mailimf_path * mailimf_path_new(char * pt_addr_spec);
+
+void mailimf_path_free(struct mailimf_path * path);
+
+
+/*
+ mailimf_optional_field is a non-parsed field
+
+ - name is the name of the field
+
+ - value is the value of the field
+*/
+
+struct mailimf_optional_field {
+ char * fld_name; /* != NULL */
+ char * fld_value; /* != NULL */
+};
+
+struct mailimf_optional_field *
+mailimf_optional_field_new(char * fld_name, char * fld_value);
+
+void mailimf_optional_field_free(struct mailimf_optional_field * opt_field);
+
+
+/*
+ mailimf_fields is the native structure that IMF module will use,
+ this module will provide an easier structure to use when parsing fields.
+
+ mailimf_single_fields is an easier structure to get parsed fields,
+ rather than iteration over the list of fields
+
+ - orig_date is the parsed "Date" field
+
+ - from is the parsed "From" field
+
+ - sender is the parsed "Sender "field
+
+ - reply_to is the parsed "Reply-To" field
+
+ - to is the parsed "To" field
+
+ - cc is the parsed "Cc" field
+
+ - bcc is the parsed "Bcc" field
+
+ - message_id is the parsed "Message-ID" field
+
+ - in_reply_to is the parsed "In-Reply-To" field
+
+ - references is the parsed "References" field
+
+ - subject is the parsed "Subject" field
+
+ - comments is the parsed "Comments" field
+
+ - keywords is the parsed "Keywords" field
+*/
+
+struct mailimf_single_fields {
+ struct mailimf_orig_date * fld_orig_date; /* can be NULL */
+ struct mailimf_from * fld_from; /* can be NULL */
+ struct mailimf_sender * fld_sender; /* can be NULL */
+ struct mailimf_reply_to * fld_reply_to; /* can be NULL */
+ struct mailimf_to * fld_to; /* can be NULL */
+ struct mailimf_cc * fld_cc; /* can be NULL */
+ struct mailimf_bcc * fld_bcc; /* can be NULL */
+ struct mailimf_message_id * fld_message_id; /* can be NULL */
+ struct mailimf_in_reply_to * fld_in_reply_to; /* can be NULL */
+ struct mailimf_references * fld_references; /* can be NULL */
+ struct mailimf_subject * fld_subject; /* can be NULL */
+ struct mailimf_comments * fld_comments; /* can be NULL */
+ struct mailimf_keywords * fld_keywords; /* can be NULL */
+};
+
+
+
+
+
+
+/* internal use */
+
+void mailimf_atom_free(char * atom);
+
+void mailimf_dot_atom_free(char * dot_atom);
+
+void mailimf_dot_atom_text_free(char * dot_atom);
+
+void mailimf_quoted_string_free(char * quoted_string);
+
+void mailimf_word_free(char * word);
+
+void mailimf_phrase_free(char * phrase);
+
+void mailimf_unstructured_free(char * unstructured);
+
+void mailimf_angle_addr_free(char * angle_addr);
+
+void mailimf_display_name_free(char * display_name);
+
+void mailimf_addr_spec_free(char * addr_spec);
+
+void mailimf_local_part_free(char * local_part);
+
+void mailimf_domain_free(char * domain);
+
+void mailimf_domain_literal_free(char * domain);
+
+void mailimf_msg_id_free(char * msg_id);
+
+void mailimf_id_left_free(char * id_left);
+
+void mailimf_id_right_free(char * id_right);
+
+void mailimf_no_fold_quote_free(char * nfq);
+
+void mailimf_no_fold_literal_free(char * nfl);
+
+void mailimf_field_name_free(char * field_name);
+
+
+
+/* these are the possible returned error codes */
+
+enum {
+ MAILIMF_NO_ERROR = 0,
+ MAILIMF_ERROR_PARSE,
+ MAILIMF_ERROR_MEMORY,
+ MAILIMF_ERROR_INVAL,
+ MAILIMF_ERROR_FILE,
+};
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/plugins/mailmbox/mailimf_types_helper.c b/src/plugins/mailmbox/mailimf_types_helper.c
@@ -0,0 +1,1636 @@
+/*
+ * libEtPan! -- a mail stuff library
+ *
+ * Copyright (C) 2001, 2002 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "mailimf_types_helper.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "mailimf.h"
+
+struct mailimf_mailbox_list *
+mailimf_mailbox_list_new_empty()
+{
+ clist * list;
+ struct mailimf_mailbox_list * mb_list;
+
+ list = clist_new();
+ if (list == NULL)
+ return NULL;
+
+ mb_list = mailimf_mailbox_list_new(list);
+ if (mb_list == NULL)
+ return NULL;
+
+ return mb_list;
+}
+
+int mailimf_mailbox_list_add(struct mailimf_mailbox_list * mailbox_list,
+ struct mailimf_mailbox * mb)
+{
+ int r;
+
+ r = clist_append(mailbox_list->mb_list, mb);
+ if (r < 0)
+ return MAILIMF_ERROR_MEMORY;
+
+ return MAILIMF_NO_ERROR;
+}
+
+int mailimf_mailbox_list_add_parse(struct mailimf_mailbox_list * mailbox_list,
+ char * mb_str)
+{
+ int r;
+ size_t cur_token;
+ struct mailimf_mailbox * mb;
+ int res;
+
+ cur_token = 0;
+ r = mailimf_mailbox_parse(mb_str, strlen(mb_str), &cur_token, &mb);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_mailbox_list_add(mailbox_list, mb);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free;
+ }
+
+ return MAILIMF_NO_ERROR;
+
+ free:
+ mailimf_mailbox_free(mb);
+ err:
+ return res;
+}
+
+int mailimf_mailbox_list_add_mb(struct mailimf_mailbox_list * mailbox_list,
+ char * display_name, char * address)
+{
+ int r;
+ struct mailimf_mailbox * mb;
+ int res;
+
+ mb = mailimf_mailbox_new(display_name, address);
+ if (mb == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto err;
+ }
+
+ r = mailimf_mailbox_list_add(mailbox_list, mb);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free;
+ }
+
+ return MAILIMF_NO_ERROR;
+
+ free:
+ mailimf_mailbox_free(mb);
+ err:
+ return res;
+}
+
+
+
+struct mailimf_address_list *
+mailimf_address_list_new_empty()
+{
+ clist * list;
+ struct mailimf_address_list * addr_list;
+
+ list = clist_new();
+ if (list == NULL)
+ return NULL;
+
+ addr_list = mailimf_address_list_new(list);
+ if (addr_list == NULL)
+ return NULL;
+
+ return addr_list;
+}
+
+int mailimf_address_list_add(struct mailimf_address_list * address_list,
+ struct mailimf_address * addr)
+{
+ int r;
+
+ r = clist_append(address_list->ad_list, addr);
+ if (r < 0)
+ return MAILIMF_ERROR_MEMORY;
+
+ return MAILIMF_NO_ERROR;
+}
+
+int mailimf_address_list_add_parse(struct mailimf_address_list * address_list,
+ char * addr_str)
+{
+ int r;
+ size_t cur_token;
+ struct mailimf_address * addr;
+ int res;
+
+ cur_token = 0;
+ r = mailimf_address_parse(addr_str, strlen(addr_str), &cur_token, &addr);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mailimf_address_list_add(address_list, addr);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free;
+ }
+
+ return MAILIMF_NO_ERROR;
+
+ free:
+ mailimf_address_free(addr);
+ err:
+ return res;
+}
+
+int mailimf_address_list_add_mb(struct mailimf_address_list * address_list,
+ char * display_name, char * address)
+{
+ int r;
+ struct mailimf_mailbox * mb;
+ struct mailimf_address * addr;
+ int res;
+
+ mb = mailimf_mailbox_new(display_name, address);
+ if (mb == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto err;
+ }
+
+ addr = mailimf_address_new(MAILIMF_ADDRESS_MAILBOX, mb, NULL);
+ if (addr == NULL) {
+ res = MAILIMF_ERROR_MEMORY;
+ goto free_mb;
+ }
+
+ r = mailimf_address_list_add(address_list, addr);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto free_addr;
+ }
+
+ return MAILIMF_NO_ERROR;
+
+ free_addr:
+ mailimf_address_free(addr);
+ free_mb:
+ mailimf_mailbox_free(mb);
+ err:
+ return res;
+}
+
+
+#if 0
+struct mailimf_resent_fields_list *
+mailimf_resent_fields_list_new_empty()
+{
+ clist * list;
+ struct mailimf_resent_fields_list * rf_list;
+
+ list = clist_new();
+ if (list == NULL)
+ return NULL;
+
+ rf_list = mailimf_resent_fields_list_new(list);
+ if (rf_list == NULL)
+ return NULL;
+
+ return rf_list;
+}
+
+int mailimf_resent_fields_add(struct mailimf_resent_fields_list * fields,
+ struct mailimf_resent_field * field)
+{
+ int r;
+
+ r = clist_append(fields->list, field);
+ if (r < 0)
+ return MAILIMF_ERROR_MEMORY;
+
+ return MAILIMF_NO_ERROR;
+}
+#endif
+
+
+static void detach_free_common_fields(struct mailimf_orig_date * imf_date,
+ struct mailimf_from * imf_from,
+ struct mailimf_sender * imf_sender,
+ struct mailimf_to * imf_to,
+ struct mailimf_cc * imf_cc,
+ struct mailimf_bcc * imf_bcc,
+ struct mailimf_message_id * imf_msg_id)
+{
+ if (imf_date != NULL) {
+ imf_date->dt_date_time = NULL;
+ mailimf_orig_date_free(imf_date);
+ }
+ if (imf_from != NULL) {
+ imf_from->frm_mb_list = NULL;
+ mailimf_from_free(imf_from);
+ }
+ if (imf_sender != NULL) {
+ imf_sender->snd_mb = NULL;
+ mailimf_sender_free(imf_sender);
+ }
+ if (imf_to != NULL) {
+ imf_to->to_addr_list = NULL;
+ mailimf_to_free(imf_to);
+ }
+ if (imf_cc != NULL) {
+ imf_cc->cc_addr_list = NULL;
+ mailimf_to_free(imf_to);
+ }
+ if (imf_bcc != NULL) {
+ imf_bcc->bcc_addr_list = NULL;
+ mailimf_bcc_free(imf_bcc);
+ }
+ if (imf_msg_id != NULL) {
+ imf_msg_id->mid_value = NULL;
+ mailimf_message_id_free(imf_msg_id);
+ }
+}
+
+static void detach_resent_field(struct mailimf_field * field)
+{
+ field->fld_type = MAILIMF_FIELD_NONE;
+ mailimf_field_free(field);
+}
+
+int
+mailimf_resent_fields_add_data(struct mailimf_fields * fields,
+ struct mailimf_date_time * resent_date,
+ struct mailimf_mailbox_list * resent_from,
+ struct mailimf_mailbox * resent_sender,
+ struct mailimf_address_list * resent_to,
+ struct mailimf_address_list * resent_cc,
+ struct mailimf_address_list * resent_bcc,
+ char * resent_msg_id)
+{
+ struct mailimf_orig_date * imf_resent_date;
+ struct mailimf_from * imf_resent_from;
+ struct mailimf_sender * imf_resent_sender;
+ struct mailimf_to * imf_resent_to;
+ struct mailimf_cc * imf_resent_cc;
+ struct mailimf_bcc * imf_resent_bcc;
+ struct mailimf_message_id * imf_resent_msg_id;
+ struct mailimf_field * field;
+ int r;
+
+ imf_resent_date = NULL;
+ imf_resent_from = NULL;
+ imf_resent_sender = NULL;
+ imf_resent_to = NULL;
+ imf_resent_cc = NULL;
+ imf_resent_bcc = NULL;
+ imf_resent_msg_id = NULL;
+ field = NULL;
+
+ if (resent_date != NULL) {
+ imf_resent_date = mailimf_orig_date_new(resent_date);
+ if (imf_resent_date == NULL)
+ goto free;
+ field = mailimf_field_new(MAILIMF_FIELD_RESENT_DATE,
+ NULL /* return-path */,
+ imf_resent_date /* resent date */,
+ NULL /* resent from */,
+ NULL /* resent sender */,
+ NULL /* resent to */,
+ NULL /* resent cc */,
+ NULL /* resent bcc */,
+ NULL /* resent msg id */,
+ NULL /* date */,
+ NULL /* from */,
+ NULL /* sender */,
+ NULL /* reply-to */,
+ NULL /* to */,
+ NULL /* cc */,
+ NULL /* bcc */,
+ NULL /* message id */,
+ NULL /* in reply to */,
+ NULL /* references */,
+ NULL /* subject */,
+ NULL /* comments */,
+ NULL /* keywords */,
+ NULL /* optional field */);
+ if (field == NULL)
+ goto free;
+ r = mailimf_fields_add(fields, field);
+ if (r != MAILIMF_NO_ERROR)
+ goto free_field;
+ }
+
+ if (resent_from != NULL) {
+ imf_resent_from = mailimf_from_new(resent_from);
+ if (imf_resent_from == NULL)
+ goto free_field;
+ field = mailimf_field_new(MAILIMF_FIELD_RESENT_FROM,
+ NULL /* return-path */,
+ NULL /* resent date */,
+ imf_resent_from /* resent from */,
+ NULL /* resent sender */,
+ NULL /* resent to */,
+ NULL /* resent cc */,
+ NULL /* resent bcc */,
+ NULL /* resent msg id */,
+ NULL /* date */,
+ NULL /* from */,
+ NULL /* sender */,
+ NULL /* reply-to */,
+ NULL /* to */,
+ NULL /* cc */,
+ NULL /* bcc */,
+ NULL /* message id */,
+ NULL /* in reply to */,
+ NULL /* references */,
+ NULL /* subject */,
+ NULL /* comments */,
+ NULL /* keywords */,
+ NULL /* optional field */);
+ if (field == NULL)
+ goto free;
+ r = mailimf_fields_add(fields, field);
+ if (r != MAILIMF_NO_ERROR)
+ goto free_field;
+ }
+
+ if (resent_sender != NULL) {
+ imf_resent_sender = mailimf_sender_new(resent_sender);
+ if (imf_resent_sender == NULL)
+ goto free;
+ field = mailimf_field_new(MAILIMF_FIELD_RESENT_SENDER,
+ NULL /* return-path */,
+ NULL /* resent date */,
+ NULL /* resent from */,
+ imf_resent_sender /* resent sender */,
+ NULL /* resent to */,
+ NULL /* resent cc */,
+ NULL /* resent bcc */,
+ NULL /* resent msg id */,
+ NULL /* date */,
+ NULL /* from */,
+ NULL /* sender */,
+ NULL /* reply-to */,
+ NULL /* to */,
+ NULL /* cc */,
+ NULL /* bcc */,
+ NULL /* message id */,
+ NULL /* in reply to */,
+ NULL /* references */,
+ NULL /* subject */,
+ NULL /* comments */,
+ NULL /* keywords */,
+ NULL /* optional field */);
+ if (field == NULL)
+ goto free;
+ r = mailimf_fields_add(fields, field);
+ if (r != MAILIMF_NO_ERROR)
+ goto free_field;
+ }
+
+ if (resent_to != NULL) {
+ imf_resent_to = mailimf_to_new(resent_to);
+ if (imf_resent_to == NULL)
+ goto free;
+ field = mailimf_field_new(MAILIMF_FIELD_RESENT_TO,
+ NULL /* return-path */,
+ NULL /* resent date */,
+ NULL /* resent from */,
+ NULL /* resent sender */,
+ imf_resent_to /* resent to */,
+ NULL /* resent cc */,
+ NULL /* resent bcc */,
+ NULL /* resent msg id */,
+ NULL /* date */,
+ NULL /* from */,
+ NULL /* sender */,
+ NULL /* reply-to */,
+ NULL /* to */,
+ NULL /* cc */,
+ NULL /* bcc */,
+ NULL /* message id */,
+ NULL /* in reply to */,
+ NULL /* references */,
+ NULL /* subject */,
+ NULL /* comments */,
+ NULL /* keywords */,
+ NULL /* optional field */);
+ if (field == NULL)
+ goto free;
+ r = mailimf_fields_add(fields, field);
+ if (r != MAILIMF_NO_ERROR)
+ goto free_field;
+ }
+
+ if (resent_cc != NULL) {
+ imf_resent_cc = mailimf_cc_new(resent_cc);
+ if (imf_resent_cc == NULL)
+ goto free;
+ field = mailimf_field_new(MAILIMF_FIELD_RESENT_CC,
+ NULL /* return-path */,
+ NULL /* resent date */,
+ NULL /* resent from */,
+ NULL /* resent sender */,
+ NULL /* resent to */,
+ imf_resent_cc /* resent cc */,
+ NULL /* resent bcc */,
+ NULL /* resent msg id */,
+ NULL /* date */,
+ NULL /* from */,
+ NULL /* sender */,
+ NULL /* reply-to */,
+ NULL /* to */,
+ NULL /* cc */,
+ NULL /* bcc */,
+ NULL /* message id */,
+ NULL /* in reply to */,
+ NULL /* references */,
+ NULL /* subject */,
+ NULL /* comments */,
+ NULL /* keywords */,
+ NULL /* optional field */);
+ if (field == NULL)
+ goto free;
+ r = mailimf_fields_add(fields, field);
+ if (r != MAILIMF_NO_ERROR)
+ goto free_field;
+ }
+
+ if (resent_bcc != NULL) {
+ imf_resent_bcc = mailimf_bcc_new(resent_bcc);
+ if (imf_resent_bcc == NULL)
+ goto free;
+ field = mailimf_field_new(MAILIMF_FIELD_RESENT_BCC,
+ NULL /* return-path */,
+ NULL /* resent date */,
+ NULL /* resent from */,
+ NULL /* resent sender */,
+ NULL /* resent to */,
+ NULL /* resent cc */,
+ imf_resent_bcc /* resent bcc */,
+ NULL /* resent msg id */,
+ NULL /* date */,
+ NULL /* from */,
+ NULL /* sender */,
+ NULL /* reply-to */,
+ NULL /* to */,
+ NULL /* cc */,
+ NULL /* bcc */,
+ NULL /* message id */,
+ NULL /* in reply to */,
+ NULL /* references */,
+ NULL /* subject */,
+ NULL /* comments */,
+ NULL /* keywords */,
+ NULL /* optional field */);
+ if (field == NULL)
+ goto free;
+ r = mailimf_fields_add(fields, field);
+ if (r != MAILIMF_NO_ERROR)
+ goto free_field;
+ }
+
+ if (resent_msg_id != NULL) {
+ imf_resent_msg_id = mailimf_message_id_new(resent_msg_id);
+ if (imf_resent_msg_id == NULL)
+ goto free;
+ field = mailimf_field_new(MAILIMF_FIELD_RESENT_MSG_ID,
+ NULL /* return-path */,
+ NULL /* resent date */,
+ NULL /* resent from */,
+ NULL /* resent sender */,
+ NULL /* resent to */,
+ NULL /* resent cc */,
+ NULL /* resent bcc */,
+ imf_resent_msg_id /* resent msg id */,
+ NULL /* date */,
+ NULL /* from */,
+ NULL /* sender */,
+ NULL /* reply-to */,
+ NULL /* to */,
+ NULL /* cc */,
+ NULL /* bcc */,
+ NULL /* message id */,
+ NULL /* in reply to */,
+ NULL /* references */,
+ NULL /* subject */,
+ NULL /* comments */,
+ NULL /* keywords */,
+ NULL /* optional field */);
+ if (field == NULL)
+ goto free;
+ r = mailimf_fields_add(fields, field);
+ if (r != MAILIMF_NO_ERROR)
+ goto free_field;
+ }
+
+ return MAILIMF_NO_ERROR;
+
+ free_field:
+ if (field != NULL) {
+ detach_resent_field(field);
+ mailimf_field_free(field);
+ }
+ free:
+ detach_free_common_fields(imf_resent_date,
+ imf_resent_from,
+ imf_resent_sender,
+ imf_resent_to,
+ imf_resent_cc,
+ imf_resent_bcc,
+ imf_resent_msg_id);
+ return MAILIMF_ERROR_MEMORY;
+}
+
+struct mailimf_fields *
+mailimf_resent_fields_new_with_data_all(struct mailimf_date_time *
+ resent_date,
+ struct mailimf_mailbox_list *
+ resent_from,
+ struct mailimf_mailbox *
+ resent_sender,
+ struct mailimf_address_list *
+ resent_to,
+ struct mailimf_address_list *
+ resent_cc,
+ struct mailimf_address_list *
+ resent_bcc,
+ char * resent_msg_id)
+{
+ struct mailimf_fields * resent_fields;
+ int r;
+
+ resent_fields = mailimf_fields_new_empty();
+ if (resent_fields == NULL)
+ goto err;
+
+ r = mailimf_resent_fields_add_data(resent_fields,
+ resent_date, resent_from,
+ resent_sender, resent_to,
+ resent_cc, resent_bcc,
+ resent_msg_id);
+ if (r != MAILIMF_NO_ERROR)
+ goto free;
+
+ return resent_fields;
+
+ free:
+ mailimf_fields_free(resent_fields);
+ err:
+ return NULL;
+}
+
+
+struct mailimf_fields *
+mailimf_resent_fields_new_with_data(struct mailimf_mailbox_list * from,
+ struct mailimf_mailbox * sender,
+ struct mailimf_address_list * to,
+ struct mailimf_address_list * cc,
+ struct mailimf_address_list * bcc)
+{
+ struct mailimf_date_time * date;
+ char * msg_id;
+ struct mailimf_fields * fields;
+
+ date = mailimf_get_current_date();
+ if (date == NULL)
+ goto err;
+
+ msg_id = mailimf_get_message_id();
+ if (msg_id == NULL)
+ goto free_date;
+
+ fields = mailimf_resent_fields_new_with_data_all(date,
+ from, sender, to, cc, bcc, msg_id);
+ if (fields == NULL)
+ goto free_msg_id;
+
+ return fields;
+
+ free_msg_id:
+ free(msg_id);
+ free_date:
+ mailimf_date_time_free(date);
+ err:
+ return NULL;
+}
+
+
+struct mailimf_fields *
+mailimf_fields_new_empty(void)
+{
+ clist * list;
+ struct mailimf_fields * fields_list;
+
+ list = clist_new();
+ if (list == NULL)
+ return NULL;
+
+ fields_list = mailimf_fields_new(list);
+ if (fields_list == NULL)
+ return NULL;
+
+ return fields_list;
+}
+
+int mailimf_fields_add(struct mailimf_fields * fields,
+ struct mailimf_field * field)
+{
+ int r;
+
+ r = clist_append(fields->fld_list, field);
+ if (r < 0)
+ return MAILIMF_ERROR_MEMORY;
+
+ return MAILIMF_NO_ERROR;
+}
+
+static void detach_free_fields(struct mailimf_orig_date * date,
+ struct mailimf_from * from,
+ struct mailimf_sender * sender,
+ struct mailimf_reply_to * reply_to,
+ struct mailimf_to * to,
+ struct mailimf_cc * cc,
+ struct mailimf_bcc * bcc,
+ struct mailimf_message_id * msg_id,
+ struct mailimf_in_reply_to * in_reply_to,
+ struct mailimf_references * references,
+ struct mailimf_subject * subject)
+{
+ detach_free_common_fields(date,
+ from,
+ sender,
+ to,
+ cc,
+ bcc,
+ msg_id);
+
+ if (reply_to != NULL) {
+ reply_to->rt_addr_list = NULL;
+ mailimf_reply_to_free(reply_to);
+ }
+
+ if (in_reply_to != NULL) {
+ in_reply_to->mid_list = NULL;
+ mailimf_in_reply_to_free(in_reply_to);
+ }
+
+ if (references != NULL) {
+ references->mid_list = NULL;
+ mailimf_references_free(references);
+ }
+
+ if (subject != NULL) {
+ subject->sbj_value = NULL;
+ mailimf_subject_free(subject);
+ }
+}
+
+
+static void detach_field(struct mailimf_field * field)
+{
+ field->fld_type = MAILIMF_FIELD_NONE;
+ mailimf_field_free(field);
+}
+
+int mailimf_fields_add_data(struct mailimf_fields * fields,
+ struct mailimf_date_time * date,
+ struct mailimf_mailbox_list * from,
+ struct mailimf_mailbox * sender,
+ struct mailimf_address_list * reply_to,
+ struct mailimf_address_list * to,
+ struct mailimf_address_list * cc,
+ struct mailimf_address_list * bcc,
+ char * msg_id,
+ clist * in_reply_to,
+ clist * references,
+ char * subject)
+{
+ struct mailimf_orig_date * imf_date;
+ struct mailimf_from * imf_from;
+ struct mailimf_sender * imf_sender;
+ struct mailimf_reply_to * imf_reply_to;
+ struct mailimf_to * imf_to;
+ struct mailimf_cc * imf_cc;
+ struct mailimf_bcc * imf_bcc;
+ struct mailimf_message_id * imf_msg_id;
+ struct mailimf_references * imf_references;
+ struct mailimf_in_reply_to * imf_in_reply_to;
+ struct mailimf_subject * imf_subject;
+ struct mailimf_field * field;
+ int r;
+
+ imf_date = NULL;
+ imf_from = NULL;
+ imf_sender = NULL;
+ imf_reply_to = NULL;
+ imf_to = NULL;
+ imf_cc = NULL;
+ imf_bcc = NULL;
+ imf_msg_id = NULL;
+ imf_references = NULL;
+ imf_in_reply_to = NULL;
+ imf_subject =NULL;
+ field = NULL;
+
+ if (date != NULL) {
+ imf_date = mailimf_orig_date_new(date);
+ if (imf_date == NULL)
+ goto free;
+ field = mailimf_field_new(MAILIMF_FIELD_ORIG_DATE,
+ NULL /* return-path */,
+ NULL /* resent date */,
+ NULL /* resent from */,
+ NULL /* resent sender */,
+ NULL /* resent to */,
+ NULL /* resent cc */,
+ NULL /* resent bcc */,
+ NULL /* resent msg id */,
+ imf_date /* date */,
+ NULL /* from */,
+ NULL /* sender */,
+ NULL /* reply-to */,
+ NULL /* to */,
+ NULL /* cc */,
+ NULL /* bcc */,
+ NULL /* message id */,
+ NULL /* in reply to */,
+ NULL /* references */,
+ NULL /* subject */,
+ NULL /* comments */,
+ NULL /* keywords */,
+ NULL /* optional field */);
+ if (field == NULL)
+ goto free;
+ r = mailimf_fields_add(fields, field);
+ if (r != MAILIMF_NO_ERROR)
+ goto free_field;
+ }
+
+ if (from != NULL) {
+ imf_from = mailimf_from_new(from);
+ if (imf_from == NULL)
+ goto free_field;
+ field = mailimf_field_new(MAILIMF_FIELD_FROM,
+ NULL /* return-path */,
+ NULL /* resent date */,
+ NULL /* resent from */,
+ NULL /* resent sender */,
+ NULL /* resent to */,
+ NULL /* resent cc */,
+ NULL /* resent bcc */,
+ NULL /* resent msg id */,
+ NULL /* date */,
+ imf_from /* from */,
+ NULL /* sender */,
+ NULL /* reply-to */,
+ NULL /* to */,
+ NULL /* cc */,
+ NULL /* bcc */,
+ NULL /* message id */,
+ NULL /* in reply to */,
+ NULL /* references */,
+ NULL /* subject */,
+ NULL /* comments */,
+ NULL /* keywords */,
+ NULL /* optional field */);
+ if (field == NULL)
+ goto free;
+ r = mailimf_fields_add(fields, field);
+ if (r != MAILIMF_NO_ERROR)
+ goto free_field;
+ }
+
+ if (sender != NULL) {
+ imf_sender = mailimf_sender_new(sender);
+ if (imf_sender == NULL)
+ goto free;
+ field = mailimf_field_new(MAILIMF_FIELD_SENDER,
+ NULL /* return-path */,
+ NULL /* resent date */,
+ NULL /* resent from */,
+ NULL /* resent sender */,
+ NULL /* resent to */,
+ NULL /* resent cc */,
+ NULL /* resent bcc */,
+ NULL /* resent msg id */,
+ NULL /* date */,
+ NULL /* from */,
+ imf_sender /* sender */,
+ NULL /* reply-to */,
+ NULL /* to */,
+ NULL /* cc */,
+ NULL /* bcc */,
+ NULL /* message id */,
+ NULL /* in reply to */,
+ NULL /* references */,
+ NULL /* subject */,
+ NULL /* comments */,
+ NULL /* keywords */,
+ NULL /* optional field */);
+ if (field == NULL)
+ goto free;
+ r = mailimf_fields_add(fields, field);
+ if (r != MAILIMF_NO_ERROR)
+ goto free_field;
+ }
+
+ if (reply_to != NULL) {
+ imf_reply_to = mailimf_reply_to_new(reply_to);
+ if (imf_reply_to == NULL)
+ goto free;
+ field = mailimf_field_new(MAILIMF_FIELD_REPLY_TO,
+ NULL /* return-path */,
+ NULL /* resent date */,
+ NULL /* resent from */,
+ NULL /* resent sender */,
+ NULL /* resent to */,
+ NULL /* resent cc */,
+ NULL /* resent bcc */,
+ NULL /* resent msg id */,
+ NULL /* date */,
+ NULL /* from */,
+ NULL /* sender */,
+ imf_reply_to /* reply-to */,
+ NULL /* to */,
+ NULL /* cc */,
+ NULL /* bcc */,
+ NULL /* message id */,
+ NULL /* in reply to */,
+ NULL /* references */,
+ NULL /* subject */,
+ NULL /* comments */,
+ NULL /* keywords */,
+ NULL /* optional field */);
+ if (field == NULL)
+ goto free;
+ r = mailimf_fields_add(fields, field);
+ if (r != MAILIMF_NO_ERROR)
+ goto free_field;
+ }
+
+ if (to != NULL) {
+ imf_to = mailimf_to_new(to);
+ if (imf_to == NULL)
+ goto free;
+ field = mailimf_field_new(MAILIMF_FIELD_TO,
+ NULL /* return-path */,
+ NULL /* resent date */,
+ NULL /* resent from */,
+ NULL /* resent sender */,
+ NULL /* resent to */,
+ NULL /* resent cc */,
+ NULL /* resent bcc */,
+ NULL /* resent msg id */,
+ NULL /* date */,
+ NULL /* from */,
+ NULL /* sender */,
+ NULL /* reply-to */,
+ imf_to /* to */,
+ NULL /* cc */,
+ NULL /* bcc */,
+ NULL /* message id */,
+ NULL /* in reply to */,
+ NULL /* references */,
+ NULL /* subject */,
+ NULL /* comments */,
+ NULL /* keywords */,
+ NULL /* optional field */);
+ if (field == NULL)
+ goto free;
+ r = mailimf_fields_add(fields, field);
+ if (r != MAILIMF_NO_ERROR)
+ goto free_field;
+ }
+
+ if (cc != NULL) {
+ imf_cc = mailimf_cc_new(cc);
+ if (imf_cc == NULL)
+ goto free;
+ field = mailimf_field_new(MAILIMF_FIELD_CC,
+ NULL /* return-path */,
+ NULL /* resent date */,
+ NULL /* resent from */,
+ NULL /* resent sender */,
+ NULL /* resent to */,
+ NULL /* resent cc */,
+ NULL /* resent bcc */,
+ NULL /* resent msg id */,
+ NULL /* date */,
+ NULL /* from */,
+ NULL /* sender */,
+ NULL /* reply-to */,
+ NULL /* to */,
+ imf_cc /* cc */,
+ NULL /* bcc */,
+ NULL /* message id */,
+ NULL /* in reply to */,
+ NULL /* references */,
+ NULL /* subject */,
+ NULL /* comments */,
+ NULL /* keywords */,
+ NULL /* optional field */);
+ if (field == NULL)
+ goto free;
+ r = mailimf_fields_add(fields, field);
+ if (r != MAILIMF_NO_ERROR)
+ goto free_field;
+ }
+
+ if (bcc != NULL) {
+ imf_bcc = mailimf_bcc_new(bcc);
+ if (imf_bcc == NULL)
+ goto free;
+ field = mailimf_field_new(MAILIMF_FIELD_BCC,
+ NULL /* return-path */,
+ NULL /* resent date */,
+ NULL /* resent from */,
+ NULL /* resent sender */,
+ NULL /* resent to */,
+ NULL /* resent cc */,
+ NULL /* resent bcc */,
+ NULL /* resent msg id */,
+ NULL /* date */,
+ NULL /* from */,
+ NULL /* sender */,
+ NULL /* reply-to */,
+ NULL /* to */,
+ NULL /* cc */,
+ imf_bcc /* bcc */,
+ NULL /* message id */,
+ NULL /* in reply to */,
+ NULL /* references */,
+ NULL /* subject */,
+ NULL /* comments */,
+ NULL /* keywords */,
+ NULL /* optional field */);
+ if (field == NULL)
+ goto free;
+ r = mailimf_fields_add(fields, field);
+ if (r != MAILIMF_NO_ERROR)
+ goto free_field;
+ }
+
+ if (msg_id != NULL) {
+ imf_msg_id = mailimf_message_id_new(msg_id);
+ if (imf_msg_id == NULL)
+ goto free;
+ field = mailimf_field_new(MAILIMF_FIELD_MESSAGE_ID,
+ NULL /* return-path */,
+ NULL /* resent date */,
+ NULL /* resent from */,
+ NULL /* resent sender */,
+ NULL /* resent to */,
+ NULL /* resent cc */,
+ NULL /* resent bcc */,
+ NULL /* resent msg id */,
+ NULL /* date */,
+ NULL /* from */,
+ NULL /* sender */,
+ NULL /* reply-to */,
+ NULL /* to */,
+ NULL /* cc */,
+ NULL /* bcc */,
+ imf_msg_id /* message id */,
+ NULL /* in reply to */,
+ NULL /* references */,
+ NULL /* subject */,
+ NULL /* comments */,
+ NULL /* keywords */,
+ NULL /* optional field */);
+ if (field == NULL)
+ goto free;
+ r = mailimf_fields_add(fields, field);
+ if (r != MAILIMF_NO_ERROR)
+ goto free_field;
+ }
+
+ if (in_reply_to != NULL) {
+ imf_in_reply_to = mailimf_in_reply_to_new(in_reply_to);
+ if (imf_in_reply_to == NULL)
+ goto free;
+ field = mailimf_field_new(MAILIMF_FIELD_IN_REPLY_TO,
+ NULL /* return-path */,
+ NULL /* resent date */,
+ NULL /* resent from */,
+ NULL /* resent sender */,
+ NULL /* resent to */,
+ NULL /* resent cc */,
+ NULL /* resent bcc */,
+ NULL /* resent msg id */,
+ NULL /* date */,
+ NULL /* from */,
+ NULL /* sender */,
+ NULL /* reply-to */,
+ NULL /* to */,
+ NULL /* cc */,
+ NULL /* bcc */,
+ NULL /* message id */,
+ imf_in_reply_to /* in reply to */,
+ NULL /* references */,
+ NULL /* subject */,
+ NULL /* comments */,
+ NULL /* keywords */,
+ NULL /* optional field */);
+ if (field == NULL)
+ goto free;
+ r = mailimf_fields_add(fields, field);
+ if (r != MAILIMF_NO_ERROR)
+ goto free_field;
+ }
+
+ if (references != NULL) {
+ imf_references = mailimf_references_new(references);
+ if (imf_references == NULL)
+ goto free;
+ field = mailimf_field_new(MAILIMF_FIELD_REFERENCES,
+ NULL /* return-path */,
+ NULL /* resent date */,
+ NULL /* resent from */,
+ NULL /* resent sender */,
+ NULL /* resent to */,
+ NULL /* resent cc */,
+ NULL /* resent bcc */,
+ NULL /* resent msg id */,
+ NULL /* date */,
+ NULL /* from */,
+ NULL /* sender */,
+ NULL /* reply-to */,
+ NULL /* to */,
+ NULL /* cc */,
+ NULL /* bcc */,
+ NULL /* message id */,
+ NULL /* in reply to */,
+ imf_references /* references */,
+ NULL /* subject */,
+ NULL /* comments */,
+ NULL /* keywords */,
+ NULL /* optional field */);
+ if (field == NULL)
+ goto free;
+ r = mailimf_fields_add(fields, field);
+ if (r != MAILIMF_NO_ERROR)
+ goto free_field;
+ }
+
+ if (subject != NULL) {
+ imf_subject = mailimf_subject_new(subject);
+ if (imf_subject == NULL)
+ goto free;
+ field = mailimf_field_new(MAILIMF_FIELD_SUBJECT,
+ NULL /* return-path */,
+ NULL /* resent date */,
+ NULL /* resent from */,
+ NULL /* resent sender */,
+ NULL /* resent to */,
+ NULL /* resent cc */,
+ NULL /* resent bcc */,
+ NULL /* resent msg id */,
+ NULL /* date */,
+ NULL /* from */,
+ NULL /* sender */,
+ NULL /* reply-to */,
+ NULL /* to */,
+ NULL /* cc */,
+ NULL /* bcc */,
+ NULL /* message id */,
+ NULL /* in reply to */,
+ NULL /* references */,
+ imf_subject /* subject */,
+ NULL /* comments */,
+ NULL /* keywords */,
+ NULL /* optional field */);
+ if (field == NULL)
+ goto free;
+ r = mailimf_fields_add(fields, field);
+ if (r != MAILIMF_NO_ERROR)
+ goto free_field;
+ }
+
+ return MAILIMF_NO_ERROR;
+
+ free_field:
+ if (field != NULL) {
+ detach_field(field);
+ mailimf_field_free(field);
+ }
+ free:
+ detach_free_fields(imf_date,
+ imf_from,
+ imf_sender,
+ imf_reply_to,
+ imf_to,
+ imf_cc,
+ imf_bcc,
+ imf_msg_id,
+ imf_in_reply_to,
+ imf_references,
+ imf_subject);
+
+ return MAILIMF_ERROR_MEMORY;
+}
+
+struct mailimf_fields *
+mailimf_fields_new_with_data_all(struct mailimf_date_time * date,
+ struct mailimf_mailbox_list * from,
+ struct mailimf_mailbox * sender,
+ struct mailimf_address_list * reply_to,
+ struct mailimf_address_list * to,
+ struct mailimf_address_list * cc,
+ struct mailimf_address_list * bcc,
+ char * message_id,
+ clist * in_reply_to,
+ clist * references,
+ char * subject)
+{
+ struct mailimf_fields * fields;
+ int r;
+
+ fields = mailimf_fields_new_empty();
+ if (fields == NULL)
+ goto err;
+
+ r = mailimf_fields_add_data(fields,
+ date,
+ from,
+ sender,
+ reply_to,
+ to,
+ cc,
+ bcc,
+ message_id,
+ in_reply_to,
+ references,
+ subject);
+ if (r != MAILIMF_NO_ERROR)
+ goto free;
+
+ return fields;
+
+ free:
+ mailimf_fields_free(fields);
+ err:
+ return NULL;
+}
+
+struct mailimf_fields *
+mailimf_fields_new_with_data(struct mailimf_mailbox_list * from,
+ struct mailimf_mailbox * sender,
+ struct mailimf_address_list * reply_to,
+ struct mailimf_address_list * to,
+ struct mailimf_address_list * cc,
+ struct mailimf_address_list * bcc,
+ clist * in_reply_to,
+ clist * references,
+ char * subject)
+{
+ struct mailimf_date_time * date;
+ char * msg_id;
+ struct mailimf_fields * fields;
+
+ date = mailimf_get_current_date();
+ if (date == NULL)
+ goto err;
+
+ msg_id = mailimf_get_message_id();
+ if (msg_id == NULL)
+ goto free_date;
+
+ fields = mailimf_fields_new_with_data_all(date,
+ from, sender, reply_to,
+ to, cc, bcc,
+ msg_id,
+ in_reply_to, references,
+ subject);
+ if (fields == NULL)
+ goto free_msg_id;
+
+ return fields;
+
+ free_msg_id:
+ free(msg_id);
+ free_date:
+ mailimf_date_time_free(date);
+ err:
+ return NULL;
+}
+
+
+
+#define MAX_MESSAGE_ID 512
+
+char * mailimf_get_message_id(void)
+{
+ char id[MAX_MESSAGE_ID];
+ time_t now;
+ char name[MAX_MESSAGE_ID];
+ long value;
+
+ now = time(NULL);
+ value = random();
+
+ gethostname(name, MAX_MESSAGE_ID);
+ snprintf(id, MAX_MESSAGE_ID, "etPan.%lx.%lx.%x@%s",
+ now, value, getpid(), name);
+
+ return strdup(id);
+}
+
+
+
+static time_t mkgmtime(struct tm * tmp);
+
+
+struct mailimf_date_time * mailimf_get_current_date(void)
+{
+ struct tm gmt;
+ struct tm lt;
+ int off;
+ time_t now;
+ struct mailimf_date_time * date_time;
+
+ now = time(NULL);
+
+ if (gmtime_r(&now, &gmt) == NULL)
+ return NULL;
+
+ if (localtime_r(&now, <) == NULL)
+ return NULL;
+
+ off = (mkgmtime(<) - mkgmtime(&gmt)) / (60 * 60) * 100;
+
+ date_time = mailimf_date_time_new(lt.tm_mday, lt.tm_mon + 1, lt.tm_year + 1900,
+ lt.tm_hour, lt.tm_min, lt.tm_sec,
+ off);
+
+ return date_time;
+}
+
+
+
+/* mkgmtime.c - make time corresponding to a GMT timeval struct
+ $Id$
+
+ * Copyright (c) 1998-2000 Carnegie Mellon University. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For permission or any other legal
+ * details, please contact
+ * Office of Technology Transfer
+ * Carnegie Mellon University
+ * 5000 Forbes Avenue
+ * Pittsburgh, PA 15213-3890
+ * (412) 268-4387, fax: (412) 268-7395
+ * tech-transfer@andrew.cmu.edu
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by Computing Services
+ * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ *
+ */
+/*
+ * Copyright (c) 1987, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Arthur David Olson of the National Cancer Institute.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+** Adapted from code provided by Robert Elz, who writes:
+** The "best" way to do mktime I think is based on an idea of Bob
+** Kridle's (so its said...) from a long time ago. (mtxinu!kridle now).
+** It does a binary search of the time_t space. Since time_t's are
+** just 32 bits, its a max of 32 iterations (even at 64 bits it
+** would still be very reasonable).
+*/
+
+/*
+ adapted for libEtPan! by DINH V. Hoa
+*/
+
+#ifndef WRONG
+#define WRONG (-1)
+#endif /* !defined WRONG */
+
+static int tmcomp(struct tm * atmp, struct tm * btmp)
+{
+ register int result;
+
+ if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
+ (result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
+ (result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
+ (result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
+ (result = (atmp->tm_min - btmp->tm_min)) == 0)
+ result = atmp->tm_sec - btmp->tm_sec;
+ return result;
+}
+
+static time_t mkgmtime(struct tm * tmp)
+{
+ register int dir;
+ register int bits;
+ register int saved_seconds;
+ time_t t;
+ struct tm yourtm, *mytm, buft;
+
+ yourtm = *tmp;
+ saved_seconds = yourtm.tm_sec;
+ yourtm.tm_sec = 0;
+ /*
+ ** Calculate the number of magnitude bits in a time_t
+ ** (this works regardless of whether time_t is
+ ** signed or unsigned, though lint complains if unsigned).
+ */
+ for (bits = 0, t = 1; t > 0; ++bits, t <<= 1)
+ ;
+ /*
+ ** If time_t is signed, then 0 is the median value,
+ ** if time_t is unsigned, then 1 << bits is median.
+ */
+ t = (t < 0) ? 0 : ((time_t) 1 << bits);
+ for ( ; ; ) {
+ mytm = gmtime_r(&t, &buft);
+ dir = tmcomp(mytm, &yourtm);
+ if (dir != 0) {
+ if (bits-- < 0)
+ return WRONG;
+ if (bits < 0)
+ --t;
+ else if (dir > 0)
+ t -= (time_t) 1 << bits;
+ else t += (time_t) 1 << bits;
+ continue;
+ }
+ break;
+ }
+ t += saved_seconds;
+ return t;
+}
+
+
+
+
+
+
+
+void mailimf_single_fields_init(struct mailimf_single_fields * single_fields,
+ struct mailimf_fields * fields)
+{
+ clistiter * cur;
+
+ memset(single_fields, 0, sizeof(struct mailimf_single_fields));
+
+ cur = clist_begin(fields->fld_list);
+ while (cur != NULL) {
+ struct mailimf_field * field;
+
+ field = clist_content(cur);
+
+ switch (field->fld_type) {
+ case MAILIMF_FIELD_ORIG_DATE:
+ if (single_fields->fld_orig_date == NULL)
+ single_fields->fld_orig_date = field->fld_data.fld_orig_date;
+ cur = clist_next(cur);
+ break;
+ case MAILIMF_FIELD_FROM:
+ if (single_fields->fld_from == NULL) {
+ single_fields->fld_from = field->fld_data.fld_from;
+ cur = clist_next(cur);
+ }
+ else {
+ clist_concat(single_fields->fld_from->frm_mb_list->mb_list,
+ field->fld_data.fld_from->frm_mb_list->mb_list);
+ mailimf_field_free(field);
+ cur = clist_delete(fields->fld_list, cur);
+ }
+ break;
+ case MAILIMF_FIELD_SENDER:
+ if (single_fields->fld_sender == NULL)
+ single_fields->fld_sender = field->fld_data.fld_sender;
+ cur = clist_next(cur);
+ break;
+ case MAILIMF_FIELD_REPLY_TO:
+ if (single_fields->fld_reply_to == NULL) {
+ single_fields->fld_reply_to = field->fld_data.fld_reply_to;
+ cur = clist_next(cur);
+ }
+ else {
+ clist_concat(single_fields->fld_reply_to->rt_addr_list->ad_list,
+ field->fld_data.fld_reply_to->rt_addr_list->ad_list);
+ mailimf_field_free(field);
+ cur = clist_delete(fields->fld_list, cur);
+ }
+ break;
+ case MAILIMF_FIELD_TO:
+ if (single_fields->fld_to == NULL) {
+ single_fields->fld_to = field->fld_data.fld_to;
+ cur = clist_next(cur);
+ }
+ else {
+ clist_concat(single_fields->fld_to->to_addr_list->ad_list,
+ field->fld_data.fld_to->to_addr_list->ad_list);
+ mailimf_field_free(field);
+ cur = clist_delete(fields->fld_list, cur);
+ }
+ break;
+ case MAILIMF_FIELD_CC:
+ if (single_fields->fld_cc == NULL) {
+ single_fields->fld_cc = field->fld_data.fld_cc;
+ cur = clist_next(cur);
+ }
+ else {
+ clist_concat(single_fields->fld_cc->cc_addr_list->ad_list,
+ field->fld_data.fld_cc->cc_addr_list->ad_list);
+ mailimf_field_free(field);
+ cur = clist_delete(fields->fld_list, cur);
+ }
+ break;
+ case MAILIMF_FIELD_BCC:
+ if (single_fields->fld_bcc == NULL) {
+ single_fields->fld_bcc = field->fld_data.fld_bcc;
+ cur = clist_next(cur);
+ }
+ else {
+ clist_concat(single_fields->fld_bcc->bcc_addr_list->ad_list,
+ field->fld_data.fld_bcc->bcc_addr_list->ad_list);
+ mailimf_field_free(field);
+ cur = clist_delete(fields->fld_list, cur);
+ }
+ break;
+ case MAILIMF_FIELD_MESSAGE_ID:
+ if (single_fields->fld_message_id == NULL)
+ single_fields->fld_message_id = field->fld_data.fld_message_id;
+ cur = clist_next(cur);
+ break;
+ case MAILIMF_FIELD_IN_REPLY_TO:
+ if (single_fields->fld_in_reply_to == NULL)
+ single_fields->fld_in_reply_to = field->fld_data.fld_in_reply_to;
+ cur = clist_next(cur);
+ break;
+ case MAILIMF_FIELD_REFERENCES:
+ if (single_fields->fld_references == NULL)
+ single_fields->fld_references = field->fld_data.fld_references;
+ cur = clist_next(cur);
+ break;
+ case MAILIMF_FIELD_SUBJECT:
+ if (single_fields->fld_subject == NULL)
+ single_fields->fld_subject = field->fld_data.fld_subject;
+ cur = clist_next(cur);
+ break;
+ case MAILIMF_FIELD_COMMENTS:
+ if (single_fields->fld_comments == NULL)
+ single_fields->fld_comments = field->fld_data.fld_comments;
+ cur = clist_next(cur);
+ break;
+ case MAILIMF_FIELD_KEYWORDS:
+ if (single_fields->fld_keywords == NULL)
+ single_fields->fld_keywords = field->fld_data.fld_keywords;
+ cur = clist_next(cur);
+ break;
+ default:
+ cur = clist_next(cur);
+ break;
+ }
+ }
+}
+
+
+struct mailimf_single_fields *
+mailimf_single_fields_new(struct mailimf_fields * fields)
+{
+ struct mailimf_single_fields * single_fields;
+
+ single_fields = malloc(sizeof(struct mailimf_single_fields));
+ if (single_fields == NULL)
+ goto err;
+
+ mailimf_single_fields_init(single_fields, fields);
+
+ return single_fields;
+
+ err:
+ return NULL;
+}
+
+void mailimf_single_fields_free(struct mailimf_single_fields *
+ single_fields)
+{
+ free(single_fields);
+}
+
+struct mailimf_field * mailimf_field_new_custom(char * name, char * value)
+{
+ struct mailimf_optional_field * opt_field;
+ struct mailimf_field * field;
+
+ opt_field = mailimf_optional_field_new(name, value);
+ if (opt_field == NULL)
+ goto err;
+
+ field = mailimf_field_new(MAILIMF_FIELD_OPTIONAL_FIELD,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, opt_field);
+ if (field == NULL)
+ goto free_opt_field;
+
+ return field;
+
+ free_opt_field:
+ mailimf_optional_field_free(opt_field);
+ err:
+ return NULL;
+}
diff --git a/src/plugins/mailmbox/mailimf_types_helper.h b/src/plugins/mailmbox/mailimf_types_helper.h
@@ -0,0 +1,370 @@
+/*
+ * libEtPan! -- a mail stuff library
+ *
+ * Copyright (C) 2001, 2002 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef MAILIMF_TYPES_HELPER
+
+#define MAILIMF_TYPES_HELPER
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "mailimf_types.h"
+
+/*
+ IMPORTANT NOTE:
+
+ All allocation functions will take as argument allocated data
+ and will store these data in the structure they will allocate.
+ Data should be persistant during all the use of the structure
+ and will be freed by the free function of the structure
+
+ allocation functions will return NULL on failure
+*/
+
+/*
+ mailimf_mailbox_list_new_empty creates an empty list of mailboxes
+*/
+
+struct mailimf_mailbox_list *
+mailimf_mailbox_list_new_empty();
+
+/*
+ mailimf_mailbox_list_add adds a mailbox to the list of mailboxes
+
+ @return MAILIMF_NO_ERROR will be returned on success,
+ other code will be returned otherwise
+*/
+
+int mailimf_mailbox_list_add(struct mailimf_mailbox_list * mailbox_list,
+ struct mailimf_mailbox * mb);
+
+/*
+ mailimf_mailbox_list_add_parse parse the given string
+ into a mailimf_mailbox structure and adds it to the list of mailboxes
+
+ @return MAILIMF_NO_ERROR will be returned on success,
+ other code will be returned otherwise
+*/
+
+int mailimf_mailbox_list_add_parse(struct mailimf_mailbox_list * mailbox_list,
+ char * mb_str);
+
+/*
+ mailimf_mailbox creates a mailimf_mailbox structure with the given
+ arguments and adds it to the list of mailboxes
+
+ - display_name is the name that will be displayed for this mailbox,
+ for example 'name' in '"name" <mailbox@domain>,
+ should be allocated with malloc()
+
+ - address is the mailbox, for example 'mailbox@domain'
+ in '"name" <mailbox@domain>, should be allocated with malloc()
+
+ @return MAILIMF_NO_ERROR will be returned on success,
+ other code will be returned otherwise
+*/
+
+int mailimf_mailbox_list_add_mb(struct mailimf_mailbox_list * mailbox_list,
+ char * display_name, char * address);
+
+/*
+ mailimf_address_list_new_empty creates an empty list of addresses
+*/
+
+struct mailimf_address_list *
+mailimf_address_list_new_empty();
+
+/*
+ mailimf_address_list_add adds a mailbox to the list of addresses
+
+ @return MAILIMF_NO_ERROR will be returned on success,
+ other code will be returned otherwise
+*/
+
+int mailimf_address_list_add(struct mailimf_address_list * address_list,
+ struct mailimf_address * addr);
+
+/*
+ mailimf_address_list_add_parse parse the given string
+ into a mailimf_address structure and adds it to the list of addresses
+
+ @return MAILIMF_NO_ERROR will be returned on success,
+ other code will be returned otherwise
+*/
+
+int mailimf_address_list_add_parse(struct mailimf_address_list * address_list,
+ char * addr_str);
+
+/*
+ mailimf_address_list_add_mb creates a mailbox mailimf_address
+ with the given arguments and adds it to the list of addresses
+
+ - display_name is the name that will be displayed for this mailbox,
+ for example 'name' in '"name" <mailbox@domain>,
+ should be allocated with malloc()
+
+ - address is the mailbox, for example 'mailbox@domain'
+ in '"name" <mailbox@domain>, should be allocated with malloc()
+
+ @return MAILIMF_NO_ERROR will be returned on success,
+ other code will be returned otherwise
+*/
+
+int mailimf_address_list_add_mb(struct mailimf_address_list * address_list,
+ char * display_name, char * address);
+
+/*
+ mailimf_resent_fields_add_data adds a set of resent fields in the
+ given mailimf_fields structure.
+
+ if you don't want a given field in the set to be added in the list
+ of fields, you can give NULL as argument
+
+ @param resent_msg_id sould be allocated with malloc()
+
+ @return MAILIMF_NO_ERROR will be returned on success,
+ other code will be returned otherwise
+*/
+
+int
+mailimf_resent_fields_add_data(struct mailimf_fields * fields,
+ struct mailimf_date_time * resent_date,
+ struct mailimf_mailbox_list * resent_from,
+ struct mailimf_mailbox * resent_sender,
+ struct mailimf_address_list * resent_to,
+ struct mailimf_address_list * resent_cc,
+ struct mailimf_address_list * resent_bcc,
+ char * resent_msg_id);
+
+/*
+ mailimf_resent_fields_new_with_data_all creates a new mailimf_fields
+ structure with a set of resent fields
+
+ if you don't want a given field in the set to be added in the list
+ of fields, you can give NULL as argument
+
+ @param resent_msg_id sould be allocated with malloc()
+
+ @return MAILIMF_NO_ERROR will be returned on success,
+ other code will be returned otherwise
+*/
+
+struct mailimf_fields *
+mailimf_resent_fields_new_with_data_all(struct mailimf_date_time *
+ resent_date, struct mailimf_mailbox_list * resent_from,
+ struct mailimf_mailbox * resent_sender,
+ struct mailimf_address_list * resent_to,
+ struct mailimf_address_list * resent_cc,
+ struct mailimf_address_list * resent_bcc,
+ char * resent_msg_id);
+
+/*
+ mailimf_resent_fields_new_with_data_all creates a new mailimf_fields
+ structure with a set of resent fields.
+ Resent-Date and Resent-Message-ID fields will be generated for you.
+
+ if you don't want a given field in the set to be added in the list
+ of fields, you can give NULL as argument
+
+ @return MAILIMF_NO_ERROR will be returned on success,
+ other code will be returned otherwise
+*/
+
+struct mailimf_fields *
+mailimf_resent_fields_new_with_data(struct mailimf_mailbox_list * from,
+ struct mailimf_mailbox * sender,
+ struct mailimf_address_list * to,
+ struct mailimf_address_list * cc,
+ struct mailimf_address_list * bcc);
+
+/*
+ this function creates a new mailimf_fields structure with no fields
+*/
+
+struct mailimf_fields *
+mailimf_fields_new_empty(void);
+
+
+/*
+ this function adds a field to the mailimf_fields structure
+
+ @return MAILIMF_NO_ERROR will be returned on success,
+ other code will be returned otherwise
+*/
+
+int mailimf_fields_add(struct mailimf_fields * fields,
+ struct mailimf_field * field);
+
+
+/*
+ mailimf_fields_add_data adds a set of fields in the
+ given mailimf_fields structure.
+
+ if you don't want a given field in the set to be added in the list
+ of fields, you can give NULL as argument
+
+ @param msg_id sould be allocated with malloc()
+ @param subject should be allocated with malloc()
+ @param in_reply_to each elements of this list should be allocated
+ with malloc()
+ @param references each elements of this list should be allocated
+ with malloc()
+
+ @return MAILIMF_NO_ERROR will be returned on success,
+ other code will be returned otherwise
+*/
+
+int mailimf_fields_add_data(struct mailimf_fields * fields,
+ struct mailimf_date_time * date,
+ struct mailimf_mailbox_list * from,
+ struct mailimf_mailbox * sender,
+ struct mailimf_address_list * reply_to,
+ struct mailimf_address_list * to,
+ struct mailimf_address_list * cc,
+ struct mailimf_address_list * bcc,
+ char * msg_id,
+ clist * in_reply_to,
+ clist * references,
+ char * subject);
+
+/*
+ mailimf_fields_new_with_data_all creates a new mailimf_fields
+ structure with a set of fields
+
+ if you don't want a given field in the set to be added in the list
+ of fields, you can give NULL as argument
+
+ @param message_id sould be allocated with malloc()
+ @param subject should be allocated with malloc()
+ @param in_reply_to each elements of this list should be allocated
+ with malloc()
+ @param references each elements of this list should be allocated
+ with malloc()
+
+ @return MAILIMF_NO_ERROR will be returned on success,
+ other code will be returned otherwise
+*/
+
+struct mailimf_fields *
+mailimf_fields_new_with_data_all(struct mailimf_date_time * date,
+ struct mailimf_mailbox_list * from,
+ struct mailimf_mailbox * sender,
+ struct mailimf_address_list * reply_to,
+ struct mailimf_address_list * to,
+ struct mailimf_address_list * cc,
+ struct mailimf_address_list * bcc,
+ char * message_id,
+ clist * in_reply_to,
+ clist * references,
+ char * subject);
+
+/*
+ mailimf_fields_new_with_data creates a new mailimf_fields
+ structure with a set of fields
+ Date and Message-ID fields will be generated for you.
+
+ if you don't want a given field in the set to be added in the list
+ of fields, you can give NULL as argument
+
+ @param subject should be allocated with malloc()
+ @param in_reply_to each elements of this list should be allocated
+ with malloc()
+ @param references each elements of this list should be allocated
+ with malloc()
+
+ @return MAILIMF_NO_ERROR will be returned on success,
+ other code will be returned otherwise
+*/
+
+struct mailimf_fields *
+mailimf_fields_new_with_data(struct mailimf_mailbox_list * from,
+ struct mailimf_mailbox * sender,
+ struct mailimf_address_list * reply_to,
+ struct mailimf_address_list * to,
+ struct mailimf_address_list * cc,
+ struct mailimf_address_list * bcc,
+ clist * in_reply_to,
+ clist * references,
+ char * subject);
+
+/*
+ this function returns an allocated message identifier to
+ use in a Message-ID or Resent-Message-ID field
+*/
+
+char * mailimf_get_message_id(void);
+
+/*
+ this function returns a mailimf_date_time structure to
+ use in a Date or Resent-Date field
+*/
+
+struct mailimf_date_time * mailimf_get_current_date(void);
+
+
+/*
+ mailimf_single_fields_init fills a mailimf_single_fields structure
+ with the content of a mailimf_fields structure
+*/
+
+void mailimf_single_fields_init(struct mailimf_single_fields * single_fields,
+ struct mailimf_fields * fields);
+
+/*
+ mailimf_single_fields_new creates a new mailimf_single_fields and
+ fills the structure with mailimf_fields
+*/
+
+struct mailimf_single_fields *
+mailimf_single_fields_new(struct mailimf_fields * fields);
+
+void mailimf_single_fields_free(struct mailimf_single_fields *
+ single_fields);
+
+/*
+ mailimf_field_new_custom creates a new field of type optional
+
+ @param name should be allocated with malloc()
+ @param value should be allocated with malloc()
+*/
+
+struct mailimf_field * mailimf_field_new_custom(char * name, char * value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/plugins/mailmbox/mailimf_write.c b/src/plugins/mailmbox/mailimf_write.c
@@ -0,0 +1,2021 @@
+/*
+ * libEtPan! -- a mail stuff library
+ *
+ * Copyright (C) 2001, 2002 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#include "mailimf_write.h"
+
+#include <time.h>
+#include <string.h>
+#include <ctype.h>
+
+#define MAX_MAIL_COL 72
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define MAX_VALID_IMF_LINE 998
+
+static int mailimf_orig_date_write(FILE * f, int * col,
+ struct mailimf_orig_date * date);
+static int mailimf_date_time_write(FILE * f, int * col,
+ struct mailimf_date_time * date_time);
+static int mailimf_from_write(FILE * f, int * col,
+ struct mailimf_from * from);
+static int mailimf_sender_write(FILE * f, int * col,
+ struct mailimf_sender * sender);
+static int mailimf_reply_to_write(FILE * f, int * col,
+ struct mailimf_reply_to * reply_to);
+static int mailimf_to_write(FILE * f, int * col,
+ struct mailimf_to * to);
+static int mailimf_cc_write(FILE * f, int * col,
+ struct mailimf_cc * to);
+static int mailimf_bcc_write(FILE * f, int * col,
+ struct mailimf_bcc * to);
+static int mailimf_message_id_write(FILE * f, int * col,
+ struct mailimf_message_id * message_id);
+static int mailimf_msg_id_list_write(FILE * f, int * col,
+ clist * list);
+static int mailimf_in_reply_to_write(FILE * f, int * col,
+ struct mailimf_in_reply_to *
+ in_reply_to);
+static int mailimf_references_write(FILE * f, int * col,
+ struct mailimf_references * references);
+static int mailimf_subject_write(FILE * f, int * col,
+ struct mailimf_subject * subject);
+
+static int mailimf_address_write(FILE * f, int * col,
+ struct mailimf_address * addr);
+static int mailimf_group_write(FILE * f, int * col,
+ struct mailimf_group * group);
+
+static int mailimf_mailbox_write(FILE * f, int * col,
+ struct mailimf_mailbox * mb);
+
+static int mailimf_comments_write(FILE * f, int * col,
+ struct mailimf_comments * comments);
+
+static int mailimf_optional_field_write(FILE * f, int * col,
+ struct mailimf_optional_field * field);
+
+static int mailimf_keywords_write(FILE * f, int * col,
+ struct mailimf_keywords * keywords);
+
+static int mailimf_return_write(FILE * f, int * col,
+ struct mailimf_return * return_path);
+
+static int mailimf_path_write(FILE * f, int * col,
+ struct mailimf_path * path);
+
+static int mailimf_resent_date_write(FILE * f, int * col,
+ struct mailimf_orig_date * date);
+
+static int mailimf_resent_from_write(FILE * f, int * col,
+ struct mailimf_from * from);
+
+static int mailimf_resent_sender_write(FILE * f, int * col,
+ struct mailimf_sender * sender);
+
+static int mailimf_resent_to_write(FILE * f, int * col,
+ struct mailimf_to * to);
+
+static int mailimf_resent_cc_write(FILE * f, int * col,
+ struct mailimf_cc * cc);
+
+static int mailimf_resent_bcc_write(FILE * f, int * col,
+ struct mailimf_bcc * bcc);
+
+static int
+mailimf_resent_msg_id_write(FILE * f, int * col,
+ struct mailimf_message_id * message_id);
+
+
+
+/* ************************ */
+
+#if 0
+int mailimf_string_write(FILE * f, int * col,
+ char * str, size_t length)
+{
+ int r;
+
+ if (length != 0) {
+ r = fwrite(str, sizeof(char), length, f);
+ if (r < 0)
+ return MAILIMF_ERROR_FILE;
+ * col += length;
+ }
+
+ return MAILIMF_NO_ERROR;
+}
+#endif
+
+#define CRLF "\r\n"
+#define HEADER_FOLD "\r\n "
+
+static inline int flush_buf(FILE * f, const char * str, size_t length)
+{
+ if (length != 0) {
+ int r;
+
+ r = fwrite(str, 1, length, f);
+ if (r == 0)
+ return MAILIMF_ERROR_FILE;
+ }
+ return MAILIMF_NO_ERROR;
+}
+
+#define CUT_AT_MAX_VALID_IMF_LINE
+
+int mailimf_string_write(FILE * f, int * col,
+ const char * str, size_t length)
+{
+ int r;
+ size_t count;
+ const char * block_begin;
+ const char * p;
+ int done;
+
+ p = str;
+ block_begin = str;
+ count = 0;
+
+ while (length > 0) {
+#ifdef CUT_AT_MAX_VALID_IMF_LINE
+ if (count >= 998) {
+ /*
+ cut lines at maximum valid length for internet message
+ format standard (currently RFC 2822)
+
+ This should not happen.
+ In case there are some lines larger than 998 in body,
+ the encoding must be changed into base64 or quoted-printable
+ so that wrapping to 72 columns is done.
+ */
+
+ r = flush_buf(f, block_begin, count);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = fwrite(CRLF, 1, sizeof(CRLF) - 1, f);
+ if (r == 0)
+ return MAILIMF_ERROR_FILE;
+
+ count = 0;
+ block_begin = p;
+
+ * col = 0;
+ }
+#endif
+ switch (* p) {
+ case '\n':
+ r = flush_buf(f, block_begin, count);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = fwrite(CRLF, 1, sizeof(CRLF) - 1, f);
+ if (r == 0)
+ return MAILIMF_ERROR_FILE;
+
+ p ++;
+ length --;
+ count = 0;
+ block_begin = p;
+
+ * col = 0;
+ break;
+
+ case '\r':
+ done = 0;
+ if (length >= 2) {
+ if (* (p + 1) == '\n') {
+ r = flush_buf(f, block_begin, count);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = fwrite(CRLF, 1, sizeof(CRLF) - 1, f);
+ if (r == 0)
+ return MAILIMF_ERROR_FILE;
+
+ p += 2;
+ length -= 2;
+ count = 0;
+ block_begin = p;
+
+ * col = 0;
+
+ done = 1;
+ }
+ }
+ if (!done) {
+ r = flush_buf(f, block_begin, count);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = fwrite(CRLF, 1, sizeof(CRLF) - 1, f);
+ if (r == 0)
+ return MAILIMF_ERROR_FILE;
+
+ p ++;
+ length --;
+ count = 0;
+ block_begin = p;
+
+ * col = 0;
+ }
+ break;
+
+ default:
+ p ++;
+ count ++;
+ length --;
+ break;
+ }
+ }
+
+ r = flush_buf(f, block_begin, count);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ * col += count;
+
+ return MAILIMF_NO_ERROR;
+}
+
+#if 0
+int mailimf_header_string_write(FILE * f, int * col,
+ char * str, size_t length)
+{
+ char * p;
+ char * block_begin;
+ int current_col;
+ char * last_cut;
+ int r;
+ int first;
+
+ if (* col + length < MAX_MAIL_COL)
+ return mailimf_string_write(f, col, str, length);
+
+ first = 1;
+ p = str;
+ block_begin = p;
+ last_cut = block_begin;
+ current_col = * col;
+
+ while (1) {
+ if (current_col >= MAX_MAIL_COL) {
+ /* if we reach the maximum recommanded size of line */
+ if (last_cut == block_begin) {
+ /* if we could not find any place to cut */
+ if (first) {
+ /* fold the header */
+ r = mailimf_string_write(f, col, HEADER_FOLD,
+ sizeof(HEADER_FOLD) - 1);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ current_col = * col + p - block_begin;
+ first = 0;
+ }
+ else {
+ /* cut the header */
+ r = mailimf_string_write(f, col, block_begin, p - block_begin);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ r = mailimf_string_write(f, col, HEADER_FOLD,
+ sizeof(HEADER_FOLD) - 1);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ first = 0;
+ block_begin = p;
+ last_cut = block_begin;
+ current_col = * col + p - block_begin;
+ }
+ }
+ else {
+ /* if we found a place to cut */
+ r = mailimf_string_write(f, col, block_begin, last_cut - block_begin);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ r = mailimf_string_write(f, col, HEADER_FOLD,
+ sizeof(HEADER_FOLD) - 1);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ first = 0;
+ block_begin = last_cut;
+ last_cut = block_begin;
+ current_col = * col + p - block_begin;
+ continue;
+ }
+ }
+ else {
+ if (length == 0)
+ break;
+
+ switch (* p) {
+ case ' ':
+ case '\t':
+ last_cut = p;
+ current_col ++;
+ break;
+
+ case '\r':
+ case '\n':
+ current_col = 0;
+ break;
+
+ default:
+ current_col ++;
+ break;
+ }
+
+ p ++;
+ length --;
+ }
+ }
+
+ return mailimf_string_write(f, col, block_begin, p - block_begin);
+}
+#endif
+
+#if 0
+enum {
+ STATE_LOWER_72,
+ STATE_LOWER_72_CUT,
+ STATE_EQUAL_72,
+ STATE_LOWER_998,
+ STATE_EQUAL_998,
+};
+
+int mailimf_header_string_write(FILE * f, int * col,
+ const char * str, size_t length)
+{
+ int state;
+ const char * p;
+ const char * block_begin;
+ size_t size;
+ const char * cut;
+ int r;
+
+ if (* col < MAX_MAIL_COL)
+ state = STATE_LOWER_72_CUT;
+ else if (* col == MAX_MAIL_COL)
+ state = STATE_EQUAL_72;
+ else if (* col < MAX_VALID_IMF_LINE)
+ state = STATE_LOWER_998;
+ else
+ state = STATE_EQUAL_998;
+
+ p = str;
+ block_begin = p;
+ size = * col;
+ cut = p;
+
+ while (length > 0) {
+ switch (state) {
+ case STATE_LOWER_72:
+ switch (* p) {
+ case '\r':
+ case '\n':
+ p ++;
+ length --;
+ size = 0;
+ break;
+
+ case ' ':
+ case '\t':
+ cut = p;
+ p ++;
+ length --;
+ size ++;
+ state = STATE_LOWER_72_CUT;
+ break;
+
+ default:
+ if (size < MAX_MAIL_COL - 1) {
+ p ++;
+ length --;
+ size ++;
+ }
+ else {
+ state = STATE_EQUAL_72;
+ p ++;
+ length --;
+ size ++;
+ }
+ break;
+ }
+ break; /* end of STATE_LOWER_72 */
+
+ case STATE_LOWER_72_CUT:
+ switch (* p) {
+ case '\r':
+ case '\n':
+ p ++;
+ length --;
+ size = 0;
+ state = STATE_LOWER_72;
+ break;
+
+ case ' ':
+ case '\t':
+ cut = p;
+ p ++;
+ length --;
+ size ++;
+ break;
+
+ default:
+ if (size < MAX_MAIL_COL) {
+ p ++;
+ length --;
+ size ++;
+ }
+ else {
+ r = mailimf_string_write(f, col, block_begin, cut - block_begin);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ r = mailimf_string_write(f, col, HEADER_FOLD,
+ sizeof(HEADER_FOLD) - 1);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ p ++;
+ length --;
+ block_begin = cut;
+ if ((* block_begin == ' ') || (* block_begin == '\t'))
+ block_begin ++;
+ size = p - block_begin + * col;
+ state = STATE_LOWER_72;
+ }
+ break;
+ }
+ break; /* end of STATE_LOWER_72_CUT */
+
+ case STATE_EQUAL_72:
+ switch (* p) {
+ case '\r':
+ case '\n':
+ p ++;
+ length --;
+ size = 0;
+ state = STATE_LOWER_72;
+ break;
+
+ case ' ':
+ case '\t':
+ r = mailimf_string_write(f, col, block_begin, p - block_begin);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ r = mailimf_string_write(f, col, HEADER_FOLD,
+ sizeof(HEADER_FOLD) - 1);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ p ++;
+ length --;
+ block_begin = p;
+ size = p - block_begin + * col;
+ state = STATE_LOWER_72;
+ break;
+
+ default:
+ p ++;
+ length --;
+ size ++;
+ state = STATE_LOWER_998;
+ break;
+ }
+ break; /* end of STATE_EQUAL_72 */
+
+ case STATE_LOWER_998:
+ switch (* p) {
+ case '\r':
+ case '\n':
+ p ++;
+ length --;
+ size = 0;
+ state = STATE_LOWER_72;
+ break;
+
+ case ' ':
+ case '\t':
+ r = mailimf_string_write(f, col, block_begin, p - block_begin);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ r = mailimf_string_write(f, col, HEADER_FOLD,
+ sizeof(HEADER_FOLD) - 1);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ p ++;
+ length --;
+ block_begin = p;
+ size = p - block_begin + * col;
+ state = STATE_LOWER_72;
+ break;
+
+ default:
+ if (size < MAX_VALID_IMF_LINE - 1) {
+ p ++;
+ length --;
+ size ++;
+ }
+ else {
+ p ++;
+ length --;
+ size = 0;
+ state = STATE_EQUAL_998;
+ }
+ break;
+ }
+ break; /* end of STATE_LOWER_998 */
+
+ case STATE_EQUAL_998:
+ switch (* p) {
+ case '\r':
+ case '\n':
+ p ++;
+ length --;
+ size = 0;
+ state = STATE_LOWER_72;
+ break;
+
+ case ' ':
+ case '\t':
+ r = mailimf_string_write(f, col, block_begin, p - block_begin);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ r = mailimf_string_write(f, col, HEADER_FOLD,
+ sizeof(HEADER_FOLD) - 1);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ p ++;
+ length --;
+ block_begin = p;
+ size = p - block_begin + * col;
+ state = STATE_LOWER_72;
+ break;
+
+ default:
+#ifdef CUT_AT_MAX_VALID_IMF_LINE
+ r = mailimf_string_write(f, col, block_begin, p - block_begin);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ r = mailimf_string_write(f, col, HEADER_FOLD,
+ sizeof(HEADER_FOLD) - 1);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ p ++;
+ length --;
+ block_begin = p;
+ size = p - block_begin + * col;
+ state = STATE_LOWER_72;
+#else
+ p ++;
+ length --;
+ size ++;
+#endif
+ break;
+ }
+ break; /* end of STATE_EQUAL_998 */
+ }
+ }
+
+ r = mailimf_string_write(f, col, block_begin, p - block_begin);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ return MAILIMF_NO_ERROR;
+}
+#endif
+
+enum {
+ STATE_BEGIN,
+ STATE_WORD,
+ STATE_SPACE,
+};
+
+int mailimf_header_string_write(FILE * f, int * col,
+ const char * str, size_t length)
+{
+ int state;
+ const char * p;
+ const char * word_begin;
+ const char * word_end;
+ const char * next_word;
+ int first;
+
+ state = STATE_BEGIN;
+
+ p = str;
+ word_begin = p;
+ word_end = p;
+ next_word = p;
+ first = 1;
+
+ while (length > 0) {
+ switch (state) {
+ case STATE_BEGIN:
+ switch (* p) {
+ case '\r':
+ case '\n':
+ case ' ':
+ case '\t':
+ p ++;
+ length --;
+ break;
+
+ default:
+ word_begin = p;
+ state = STATE_WORD;
+ break;
+ }
+ break;
+
+ case STATE_SPACE:
+ switch (* p) {
+ case '\r':
+ case '\n':
+ case ' ':
+ case '\t':
+ p ++;
+ length --;
+ break;
+
+ default:
+ word_begin = p;
+ state = STATE_WORD;
+ break;
+ }
+ break;
+
+ case STATE_WORD:
+ switch (* p) {
+ case '\r':
+ case '\n':
+ case ' ':
+ case '\t':
+ if (p - word_begin + (* col) + 1 > MAX_MAIL_COL)
+ mailimf_string_write(f, col, HEADER_FOLD,
+ sizeof(HEADER_FOLD) - 1);
+ else {
+ if (!first)
+ mailimf_string_write(f, col, " ", 1);
+ }
+ first = 0;
+ mailimf_string_write(f, col, word_begin, p - word_begin);
+ state = STATE_SPACE;
+ break;
+
+ default:
+ if (p - word_begin + (* col) >= MAX_VALID_IMF_LINE) {
+ mailimf_string_write(f, col, word_begin, p - word_begin);
+ mailimf_string_write(f, col, HEADER_FOLD,
+ sizeof(HEADER_FOLD) - 1);
+ word_begin = p;
+ }
+ p ++;
+ length --;
+ break;
+ }
+ break;
+ }
+ }
+
+ if (state == STATE_WORD) {
+ if (p - word_begin + (* col) >= MAX_MAIL_COL)
+ mailimf_string_write(f, col, HEADER_FOLD,
+ sizeof(HEADER_FOLD) - 1);
+ else {
+ if (!first)
+ mailimf_string_write(f, col, " ", 1);
+ }
+ first = 0;
+ mailimf_string_write(f, col, word_begin, p - word_begin);
+ }
+
+ return MAILIMF_NO_ERROR;
+}
+
+int mailimf_envelope_fields_write(FILE * f, int * col,
+ struct mailimf_fields * fields)
+{
+ clistiter * cur;
+
+ for(cur = clist_begin(fields->fld_list) ; cur != NULL ;
+ cur = clist_next(cur)) {
+ int r;
+ struct mailimf_field * field;
+
+ field = clist_content(cur);
+ if (field->fld_type != MAILIMF_FIELD_OPTIONAL_FIELD) {
+ r = mailimf_field_write(f, col, field);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+ }
+
+ return MAILIMF_NO_ERROR;
+}
+
+int mailimf_fields_write(FILE * f, int * col,
+ struct mailimf_fields * fields)
+{
+ clistiter * cur;
+
+ for(cur = clist_begin(fields->fld_list) ; cur != NULL ;
+ cur = clist_next(cur)) {
+ int r;
+
+ r = mailimf_field_write(f, col, clist_content(cur));
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+
+ return MAILIMF_NO_ERROR;
+}
+
+#if 0
+int mailimf_unparsed_fields_write(FILE * f, int * col,
+ struct mailimf_unparsed_fields * fields)
+{
+ clistiter * cur;
+
+ for(cur = clist_begin(fields->list) ; cur != NULL ; cur = cur->next) {
+ int r;
+
+ r = mailimf_optional_field_write(f, col, cur->data);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+
+ return MAILIMF_NO_ERROR;
+}
+#endif
+
+int mailimf_field_write(FILE * f, int * col,
+ struct mailimf_field * field)
+{
+ int r;
+
+ switch (field->fld_type) {
+ case MAILIMF_FIELD_RETURN_PATH:
+ r = mailimf_return_write(f, col, field->fld_data.fld_return_path);
+ break;
+ case MAILIMF_FIELD_RESENT_DATE:
+ r = mailimf_resent_date_write(f, col, field->fld_data.fld_resent_date);
+ break;
+ case MAILIMF_FIELD_RESENT_FROM:
+ r = mailimf_resent_from_write(f, col, field->fld_data.fld_resent_from);
+ break;
+ case MAILIMF_FIELD_RESENT_SENDER:
+ r = mailimf_resent_sender_write(f, col, field->fld_data.fld_resent_sender);
+ break;
+ case MAILIMF_FIELD_RESENT_TO:
+ r = mailimf_resent_to_write(f, col, field->fld_data.fld_resent_to);
+ break;
+ case MAILIMF_FIELD_RESENT_CC:
+ r = mailimf_resent_cc_write(f, col, field->fld_data.fld_resent_cc);
+ break;
+ case MAILIMF_FIELD_RESENT_BCC:
+ r = mailimf_resent_bcc_write(f, col, field->fld_data.fld_resent_bcc);
+ break;
+ case MAILIMF_FIELD_RESENT_MSG_ID:
+ r = mailimf_resent_msg_id_write(f, col, field->fld_data.fld_resent_msg_id);
+ break;
+ case MAILIMF_FIELD_ORIG_DATE:
+ r = mailimf_orig_date_write(f, col, field->fld_data.fld_orig_date);
+ break;
+ case MAILIMF_FIELD_FROM:
+ r = mailimf_from_write(f, col, field->fld_data.fld_from);
+ break;
+ case MAILIMF_FIELD_SENDER:
+ r = mailimf_sender_write(f, col, field->fld_data.fld_sender);
+ break;
+ case MAILIMF_FIELD_REPLY_TO:
+ r = mailimf_reply_to_write(f, col, field->fld_data.fld_reply_to);
+ break;
+ case MAILIMF_FIELD_TO:
+ r = mailimf_to_write(f, col, field->fld_data.fld_to);
+ break;
+ case MAILIMF_FIELD_CC:
+ r = mailimf_cc_write(f, col, field->fld_data.fld_cc);
+ break;
+ case MAILIMF_FIELD_BCC:
+ r = mailimf_bcc_write(f, col, field->fld_data.fld_bcc);
+ break;
+ case MAILIMF_FIELD_MESSAGE_ID:
+ r = mailimf_message_id_write(f, col, field->fld_data.fld_message_id);
+ break;
+ case MAILIMF_FIELD_IN_REPLY_TO:
+ r = mailimf_in_reply_to_write(f, col, field->fld_data.fld_in_reply_to);
+ break;
+ case MAILIMF_FIELD_REFERENCES:
+ r = mailimf_references_write(f, col, field->fld_data.fld_references);
+ break;
+ case MAILIMF_FIELD_SUBJECT:
+ r = mailimf_subject_write(f, col, field->fld_data.fld_subject);
+ break;
+ case MAILIMF_FIELD_COMMENTS:
+ r = mailimf_comments_write(f, col, field->fld_data.fld_comments);
+ break;
+ case MAILIMF_FIELD_KEYWORDS:
+ r = mailimf_keywords_write(f, col, field->fld_data.fld_keywords);
+ break;
+ case MAILIMF_FIELD_OPTIONAL_FIELD:
+ r = mailimf_optional_field_write(f, col, field->fld_data.fld_optional_field);
+ break;
+ default:
+ r = MAILIMF_ERROR_INVAL;
+ break;
+ }
+
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+static int mailimf_orig_date_write(FILE * f, int * col,
+ struct mailimf_orig_date * date)
+{
+ int r;
+
+ r = mailimf_string_write(f, col, "Date: ", 6);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_date_time_write(f, col, date->dt_date_time);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, "\r\n", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 0;
+#endif
+
+ return MAILIMF_NO_ERROR;
+}
+
+#define MAX_DATE_STR 256
+
+/* 0 = Sunday */
+/* y > 1752 */
+
+static int dayofweek(int year, int month, int day)
+{
+ static int offset[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
+
+ year -= month < 3;
+
+ return (year + year/4 - year/100 + year/400 + offset[month-1] + day) % 7;
+}
+
+static const char * week_of_day_str[] = { "Sun", "Mon", "Tue", "Wed", "Thu",
+ "Fri", "Sat"};
+static const char * month_str[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
+static int mailimf_date_time_write(FILE * f, int * col,
+ struct mailimf_date_time * date_time)
+{
+ int r;
+ char date_str[MAX_DATE_STR];
+#if 0
+ struct tm tmval;
+ time_t timeval;
+#endif
+ int wday;
+
+#if 0
+ tmval.tm_sec = date_time->sec;
+ tmval.tm_min = date_time->min;
+ tmval.tm_hour = date_time->hour;
+ tmval.tm_sec = date_time->sec;
+ tmval.tm_mday = date_time->day;
+ tmval.tm_mon = date_time->month - 1;
+ tmval.tm_year = date_time->year - 1900;
+ tmval.tm_isdst = 1;
+
+ timeval = mktime(&tmval);
+
+ localtime_r(&timeval, &tmval);
+#endif
+
+ wday = dayofweek(date_time->dt_year, date_time->dt_month, date_time->dt_day);
+
+ snprintf(date_str, MAX_DATE_STR, "%s, %i %s %i %02i:%02i:%02i %+05i",
+ week_of_day_str[wday], date_time->dt_day,
+ month_str[date_time->dt_month - 1],
+ date_time->dt_year, date_time->dt_hour,
+ date_time->dt_min, date_time->dt_sec,
+ date_time->dt_zone);
+
+ r = mailimf_string_write(f, col, date_str, strlen(date_str));
+
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ return MAILIMF_NO_ERROR;
+}
+
+static int mailimf_from_write(FILE * f, int * col,
+ struct mailimf_from * from)
+{
+ int r;
+
+ r = mailimf_string_write(f, col, "From: ", 6);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_mailbox_list_write(f, col, from->frm_mb_list);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, "\r\n", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 0;
+#endif
+
+ return MAILIMF_NO_ERROR;
+}
+
+static int mailimf_sender_write(FILE * f, int * col,
+ struct mailimf_sender * sender)
+{
+ int r;
+
+ r = mailimf_string_write(f, col, "Sender: ", 8);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_mailbox_write(f, col, sender->snd_mb);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, "\r\n", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 0;
+#endif
+
+ return MAILIMF_NO_ERROR;
+}
+
+static int mailimf_reply_to_write(FILE * f, int * col,
+ struct mailimf_reply_to * reply_to)
+{
+ int r;
+
+ r = mailimf_string_write(f, col, "Reply-To: ", 10);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_address_list_write(f, col, reply_to->rt_addr_list);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, "\r\n", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 0;
+#endif
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+static int mailimf_to_write(FILE * f, int * col,
+ struct mailimf_to * to)
+{
+ int r;
+
+ r = mailimf_string_write(f, col, "To: ", 4);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_address_list_write(f, col, to->to_addr_list);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, "\r\n", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 0;
+#endif
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+static int mailimf_cc_write(FILE * f, int * col,
+ struct mailimf_cc * cc)
+{
+ int r;
+
+ r = mailimf_string_write(f, col, "Cc: ", 4);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_address_list_write(f, col, cc->cc_addr_list);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, "\r\n", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 0;
+#endif
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+static int mailimf_bcc_write(FILE * f, int * col,
+ struct mailimf_bcc * bcc)
+{
+ int r;
+
+ r = mailimf_string_write(f, col, "Bcc: ", 5);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ if (bcc->bcc_addr_list != NULL) {
+ r = mailimf_address_list_write(f, col, bcc->bcc_addr_list);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+
+ r = mailimf_string_write(f, col, "\r\n", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 0;
+#endif
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+static int mailimf_message_id_write(FILE * f, int * col,
+ struct mailimf_message_id * message_id)
+{
+ int r;
+
+ r = mailimf_string_write(f, col, "Message-ID: ", 12);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, "<", 1);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col,
+ message_id->mid_value,
+ strlen(message_id->mid_value));
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, ">", 1);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, "\r\n", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 0;
+#endif
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+static int mailimf_msg_id_list_write(FILE * f, int * col, clist * mid_list)
+{
+ clistiter * cur;
+ int r;
+ int first;
+
+ first = TRUE;
+
+ for(cur = clist_begin(mid_list) ; cur != NULL ; cur = clist_next(cur)) {
+ char * msgid;
+ size_t len;
+
+ msgid = clist_content(cur);
+ len = strlen(msgid);
+
+ /*
+ XXX - if this is the first message ID, don't fold.
+ This is a workaround for a bug of old versions of INN.
+ */
+ if (!first) {
+ if (* col > 1) {
+
+ if (* col + len >= MAX_MAIL_COL) {
+ r = mailimf_string_write(f, col, "\r\n ", 3);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 1;
+#endif
+ first = TRUE;
+ }
+ }
+ }
+
+ if (!first) {
+ r = mailimf_string_write(f, col, " ", 1);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+ else {
+ first = FALSE;
+ }
+
+ r = mailimf_string_write(f, col, "<", 1);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, msgid, len);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, ">", 1);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+static int mailimf_in_reply_to_write(FILE * f, int * col,
+ struct mailimf_in_reply_to * in_reply_to)
+{
+ int r;
+
+ r = mailimf_string_write(f, col, "In-Reply-To: ", 13);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_msg_id_list_write(f, col, in_reply_to->mid_list);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, "\r\n", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 0;
+#endif
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+static int mailimf_references_write(FILE * f, int * col,
+ struct mailimf_references * references)
+{
+ int r;
+
+ r = mailimf_string_write(f, col, "References: ", 12);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_msg_id_list_write(f, col, references->mid_list);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, "\r\n", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 0;
+#endif
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+
+static int mailimf_subject_write(FILE * f, int * col,
+ struct mailimf_subject * subject)
+{
+ int r;
+
+ r = mailimf_string_write(f, col, "Subject: ", 9);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_header_string_write(f, col,
+ subject->sbj_value, strlen(subject->sbj_value));
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, "\r\n", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 0;
+#endif
+
+ return MAILIMF_NO_ERROR;
+}
+
+int mailimf_address_list_write(FILE * f, int * col,
+ struct mailimf_address_list * addr_list)
+{
+ clistiter * cur;
+ int r;
+ int first;
+
+ first = TRUE;
+
+ for(cur = clist_begin(addr_list->ad_list) ; cur != NULL ;
+ cur = clist_next(cur)) {
+ struct mailimf_address * addr;
+
+ addr = clist_content(cur);
+
+ if (!first) {
+ r = mailimf_string_write(f, col, ", ", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+ else {
+ first = FALSE;
+ }
+
+ r = mailimf_address_write(f, col, addr);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+static int mailimf_address_write(FILE * f, int * col,
+ struct mailimf_address * addr)
+{
+ int r;
+
+ switch(addr->ad_type) {
+ case MAILIMF_ADDRESS_MAILBOX:
+ r = mailimf_mailbox_write(f, col, addr->ad_data.ad_mailbox);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ break;
+
+ case MAILIMF_ADDRESS_GROUP:
+ r = mailimf_group_write(f, col, addr->ad_data.ad_group);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ break;
+ }
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+static int mailimf_group_write(FILE * f, int * col,
+ struct mailimf_group * group)
+{
+ int r;
+
+ r = mailimf_header_string_write(f, col, group->grp_display_name,
+ strlen(group->grp_display_name));
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, ": ", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ if (group->grp_mb_list != NULL) {
+ r = mailimf_mailbox_list_write(f, col, group->grp_mb_list);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+
+ r = mailimf_string_write(f, col, ";", 1);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+int mailimf_mailbox_list_write(FILE * f, int * col,
+ struct mailimf_mailbox_list * mb_list)
+{
+ clistiter * cur;
+ int r;
+ int first;
+
+ first = TRUE;
+
+ for(cur = clist_begin(mb_list->mb_list) ; cur != NULL ;
+ cur = clist_next(cur)) {
+ struct mailimf_mailbox * mb;
+
+ mb = clist_content(cur);
+
+ if (!first) {
+ r = mailimf_string_write(f, col, ", ", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+ else {
+ first = FALSE;
+ }
+
+ r = mailimf_mailbox_write(f, col, mb);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+int mailimf_quoted_string_write(FILE * f, int * col,
+ const char * string, size_t len)
+{
+ int r;
+ size_t i;
+
+ fputc('\"', f);
+ for(i = 0 ; i < len ; i ++) {
+ switch (string[i]) {
+ case '\\':
+ case '\"':
+ r = fputc('\\', f);
+ if (r < 0)
+ return MAILIMF_ERROR_FILE;
+ r = fputc(string[i], f);
+ if (r < 0)
+ return MAILIMF_ERROR_FILE;
+ (* col) += 2;
+ break;
+
+ default:
+ r = fputc(string[i], f);
+ if (r < 0)
+ return MAILIMF_ERROR_FILE;
+ (* col) ++;
+ break;
+ }
+ }
+ fputc('\"', f);
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+/*
+static int
+atext = ALPHA / DIGIT / ; Any character except controls,
+ "!" / "#" / ; SP, and specials.
+ "$" / "%" / ; Used for atoms
+ "&" / "'" /
+ "*" / "+" /
+ "-" / "/" /
+ "=" / "?" /
+ "^" / "_" /
+ "`" / "{" /
+ "|" / "}" /
+ "~"
+*/
+
+static int is_atext(const char * s)
+{
+ const char * p;
+
+ for(p = s ; * p != 0 ; p ++) {
+ if (isalpha((unsigned char) * p))
+ continue;
+ if (isdigit((unsigned char) * p))
+ continue;
+ switch (*p) {
+ case ' ':
+ case '\t':
+ case '!':
+ case '#':
+ case '$':
+ case '%':
+ case '&':
+ case '\'':
+ case '*':
+ case '+':
+ case '-':
+ case '/':
+ case '=':
+ case '?':
+ case '^':
+ case '_':
+ case '`':
+ case '{':
+ case '|':
+ case '}':
+ case '~':
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int mailimf_mailbox_write(FILE * f, int * col,
+ struct mailimf_mailbox * mb)
+{
+ int r;
+ int do_fold;
+
+#if 0
+ if (* col > 1) {
+
+ if (mb->mb_display_name != NULL) {
+ if (* col + strlen(mb->mb_display_name) >= MAX_MAIL_COL) {
+ r = mailimf_string_write(f, col, "\r\n ", 3);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 1;
+#endif
+ }
+ }
+ }
+#endif
+
+ if (mb->mb_display_name) {
+
+ if (is_atext(mb->mb_display_name)) {
+ r = mailimf_header_string_write(f, col, mb->mb_display_name,
+ strlen(mb->mb_display_name));
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+ else {
+ if (mb->mb_display_name != NULL) {
+ if (* col + strlen(mb->mb_display_name) >= MAX_MAIL_COL) {
+ r = mailimf_string_write(f, col, "\r\n ", 3);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+ }
+
+ if (strlen(mb->mb_display_name) > MAX_VALID_IMF_LINE / 2)
+ return MAILIMF_ERROR_INVAL;
+
+ r = mailimf_quoted_string_write(f, col, mb->mb_display_name,
+ strlen(mb->mb_display_name));
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+
+ do_fold = 0;
+ if (* col > 1) {
+
+ if (* col + strlen(mb->mb_addr_spec) + 3 >= MAX_MAIL_COL) {
+ r = mailimf_string_write(f, col, "\r\n ", 3);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 1;
+#endif
+ do_fold = 1;
+ }
+ }
+
+ if (do_fold)
+ r = mailimf_string_write(f, col, "<", 1);
+ else
+ r = mailimf_string_write(f, col, " <", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, mb->mb_addr_spec,
+ strlen(mb->mb_addr_spec));
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, ">", 1);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+ else {
+ if (* col + strlen(mb->mb_addr_spec) >= MAX_MAIL_COL) {
+ r = mailimf_string_write(f, col, "\r\n ", 3);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+
+ r = mailimf_string_write(f, col,
+ mb->mb_addr_spec, strlen(mb->mb_addr_spec));
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+
+
+ return MAILIMF_NO_ERROR;
+}
+
+static int mailimf_comments_write(FILE * f, int * col,
+ struct mailimf_comments * comments)
+{
+ int r;
+
+ r = mailimf_string_write(f, col, "Comments: ", 10);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_header_string_write(f, col,
+ comments->cm_value, strlen(comments->cm_value));
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, "\r\n", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 0;
+#endif
+
+ return MAILIMF_NO_ERROR;
+}
+
+static int mailimf_optional_field_write(FILE * f, int * col,
+ struct mailimf_optional_field * field)
+{
+ int r;
+
+ if (strlen(field->fld_name) + 2 > MAX_VALID_IMF_LINE)
+ return MAILIMF_ERROR_INVAL;
+
+ r = mailimf_string_write(f, col, field->fld_name, strlen(field->fld_name));
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, ": ", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_header_string_write(f, col, field->fld_value,
+ strlen(field->fld_value));
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+#if 0
+ /* XXX parsing debug */
+ mailimf_string_write(f, col, " (X)", 4);
+#endif
+
+ r = mailimf_string_write(f, col, "\r\n", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 0;
+#endif
+
+ return MAILIMF_NO_ERROR;
+}
+
+static int mailimf_keywords_write(FILE * f, int * col,
+ struct mailimf_keywords * keywords)
+{
+ int r;
+ clistiter * cur;
+ int first;
+
+ r = mailimf_string_write(f, col, "Keywords: ", 10);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ first = TRUE;
+
+ for(cur = clist_begin(keywords->kw_list) ; cur != NULL ;
+ cur = clist_next(cur)) {
+ char * keyword;
+ size_t len;
+
+ keyword = clist_content(cur);
+ len = strlen(keyword);
+
+ if (!first) {
+ r = mailimf_string_write(f, col, ", ", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+ else {
+ first = FALSE;
+ }
+
+#if 0
+ if (* col > 1) {
+
+ if (* col + len >= MAX_MAIL_COL) {
+ r = mailimf_string_write(f, col, "\r\n ", 3);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 1;
+#endif
+ }
+ }
+#endif
+
+ r = mailimf_header_string_write(f, col, keyword, len);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+
+ r = mailimf_string_write(f, col, "\r\n", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 0;
+#endif
+
+ return MAILIMF_NO_ERROR;
+}
+
+#if 0
+static int mailimf_delivering_info_write(FILE * f, int * col,
+ struct mailimf_delivering_info * info)
+{
+ clistiter * cur;
+ int r;
+
+ for(cur = clist_begin(info->received_fields) ;
+ cur != NULL ; cur = cur->next) {
+ struct mailimf_trace_resent_fields * field;
+
+ field = cur->data;
+
+ r = mailimf_trace_resent_fields_write(f, col, field);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+static int
+mailimf_trace_resent_fields_write(FILE * f, int * col,
+ struct mailimf_trace_resent_fields * field)
+{
+ int r;
+
+ if (field->return_path != NULL) {
+ r = mailimf_return_write(f, col, field->return_path);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+
+ if (field->resent_fields != NULL) {
+ r = mailimf_resent_fields_write(f, col, field->resent_fields);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+
+ return MAILIMF_NO_ERROR;
+}
+#endif
+
+static int mailimf_return_write(FILE * f, int * col,
+ struct mailimf_return * return_path)
+{
+ int r;
+
+ r = mailimf_string_write(f, col, "Return-Path: ", 13);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_path_write(f, col, return_path->ret_path);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, "\r\n", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 0;
+#endif
+
+ return MAILIMF_NO_ERROR;
+}
+
+static int mailimf_path_write(FILE * f, int * col,
+ struct mailimf_path * path)
+{
+ int r;
+
+ r = mailimf_string_write(f, col, "<", 1);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, path->pt_addr_spec,
+ strlen(path->pt_addr_spec));
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, ">", 1);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ return MAILIMF_NO_ERROR;
+}
+
+#if 0
+static int mailimf_resent_fields_write(FILE * f, int * col,
+ struct mailimf_resent_fields_list *
+ resent_fields)
+{
+ clistiter * cur;
+ int r;
+
+ for(cur = clist_begin(resent_fields->list) ; cur != NULL ; cur = cur->next) {
+ struct mailimf_resent_field * field;
+
+ field = cur->data;
+
+ r = mailimf_resent_field_write(f, col, field);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+
+static int mailimf_resent_field_write(FILE * f, int * col,
+ struct mailimf_resent_field *
+ resent_field)
+{
+ int r;
+
+ switch (resent_field->type) {
+ case MAILIMF_RESENT_FIELD_DATE:
+ r = mailimf_resent_date_write(f, col, resent_field->resent_date);
+ break;
+
+ case MAILIMF_RESENT_FIELD_FROM:
+ r = mailimf_resent_from_write(f, col, resent_field->resent_from);
+ break;
+
+ case MAILIMF_RESENT_FIELD_SENDER:
+ r = mailimf_resent_sender_write(f, col, resent_field->resent_sender);
+ break;
+
+ case MAILIMF_RESENT_FIELD_TO:
+ r = mailimf_resent_to_write(f, col, resent_field->resent_to);
+ break;
+
+ case MAILIMF_RESENT_FIELD_CC:
+ r = mailimf_resent_cc_write(f, col, resent_field->resent_cc);
+ break;
+
+ case MAILIMF_RESENT_FIELD_BCC:
+ r = mailimf_resent_bcc_write(f, col, resent_field->resent_bcc);
+ break;
+
+ case MAILIMF_RESENT_FIELD_MSG_ID:
+ r = mailimf_resent_msg_id_write(f, col, resent_field->resent_msg_id);
+ break;
+ default:
+ r = MAILIMF_ERROR_INVAL;
+ break;
+ }
+
+
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ return MAILIMF_NO_ERROR;
+}
+#endif
+
+static int mailimf_resent_date_write(FILE * f, int * col,
+ struct mailimf_orig_date * date)
+{
+ int r;
+
+ r = mailimf_string_write(f, col, "Resent-Date: ", 13);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_date_time_write(f, col, date->dt_date_time);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, "\r\n", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 0;
+#endif
+
+ return MAILIMF_NO_ERROR;
+}
+
+static int mailimf_resent_from_write(FILE * f, int * col,
+ struct mailimf_from * from)
+{
+ int r;
+
+ r = mailimf_string_write(f, col, "Resent-From: ", 13);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_mailbox_list_write(f, col, from->frm_mb_list);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, "\r\n", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 0;
+#endif
+
+ return MAILIMF_NO_ERROR;
+}
+
+static int mailimf_resent_sender_write(FILE * f, int * col,
+ struct mailimf_sender * sender)
+{
+ int r;
+
+ r = mailimf_string_write(f, col, "Resent-Sender: ", 15);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_mailbox_write(f, col, sender->snd_mb);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, "\r\n", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 0;
+#endif
+
+ return MAILIMF_NO_ERROR;
+}
+
+static int mailimf_resent_to_write(FILE * f, int * col,
+ struct mailimf_to * to)
+{
+ int r;
+
+ r = mailimf_string_write(f, col, "Resent-To: ", 11);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_address_list_write(f, col, to->to_addr_list);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, "\r\n", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 0;
+#endif
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+static int mailimf_resent_cc_write(FILE * f, int * col,
+ struct mailimf_cc * cc)
+{
+ int r;
+
+ r = mailimf_string_write(f, col, "Resent-Cc: ", 11);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_address_list_write(f, col, cc->cc_addr_list);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, "\r\n", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 0;
+#endif
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+static int mailimf_resent_bcc_write(FILE * f, int * col,
+ struct mailimf_bcc * bcc)
+{
+ int r;
+
+ r = mailimf_string_write(f, col, "Resent-Bcc: ", 12);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ if (bcc->bcc_addr_list != NULL) {
+ r = mailimf_address_list_write(f, col, bcc->bcc_addr_list);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+ }
+
+ r = mailimf_string_write(f, col, "\r\n", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 0;
+#endif
+
+ return MAILIMF_NO_ERROR;
+}
+
+
+static int
+mailimf_resent_msg_id_write(FILE * f, int * col,
+ struct mailimf_message_id * message_id)
+{
+ int r;
+
+ r = mailimf_string_write(f, col, "Resent-Message-ID: ", 19);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, "<", 1);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col,
+ message_id->mid_value, strlen(message_id->mid_value));
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, ">", 1);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+
+ r = mailimf_string_write(f, col, "\r\n", 2);
+ if (r != MAILIMF_NO_ERROR)
+ return r;
+#if 0
+ * col = 0;
+#endif
+
+ return MAILIMF_NO_ERROR;
+}
diff --git a/src/plugins/mailmbox/mailimf_write.h b/src/plugins/mailmbox/mailimf_write.h
@@ -0,0 +1,134 @@
+/*
+ * libEtPan! -- a mail stuff library
+ *
+ * Copyright (C) 2001, 2002 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef MAILIMF_WRITE_H
+
+#define MAILIMF_WRITE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include "mailimf_types.h"
+
+/*
+ mailimf_string_write writes a string to a given stream
+
+ @param f is the stream
+ @param col (* col) is the column number where we will start to
+ write the text, the ending column will be stored in (* col)
+ @param str is the string to write
+*/
+
+int mailimf_string_write(FILE * f, int * col,
+ const char * str, size_t length);
+
+
+/*
+ mailimf_fields_write writes the fields to a given stream
+
+ @param f is the stream
+ @param col (* col) is the column number where we will start to
+ write the text, the ending column will be stored in (* col)
+ @param fields is the fields to write
+*/
+
+int mailimf_fields_write(FILE * f, int * col,
+ struct mailimf_fields * fields);
+
+
+/*
+ mailimf_envelope_fields_write writes only some fields to a given stream
+
+ @param f is the stream
+ @param col (* col) is the column number where we will start to
+ write the text, the ending column will be stored in (* col)
+ @param fields is the fields to write
+*/
+
+int mailimf_envelope_fields_write(FILE * f, int * col,
+ struct mailimf_fields * fields);
+
+
+/*
+ mailimf_field_write writes a field to a given stream
+
+ @param f is the stream
+ @param col (* col) is the column number where we will start to
+ write the text, the ending column will be stored in (* col)
+ @param field is the field to write
+*/
+
+int mailimf_field_write(FILE * f, int * col,
+ struct mailimf_field * field);
+
+/*
+ mailimf_quoted_string_write writes a string that is quoted
+ to a given stream
+
+ @param f is the stream
+ @param col (* col) is the column number where we will start to
+ write the text, the ending column will be stored in (* col)
+ @param string is the string to quote and write
+*/
+
+int mailimf_quoted_string_write(FILE * f, int * col,
+ const char * string, size_t len);
+
+int mailimf_address_list_write(FILE * f, int * col,
+ struct mailimf_address_list * addr_list);
+
+int mailimf_mailbox_list_write(FILE * f, int * col,
+ struct mailimf_mailbox_list * mb_list);
+
+/*
+ mailimf_header_string_write writes a header value and fold the header
+ if needed.
+
+ @param f is the stream
+ @param col (* col) is the column number where we will start to
+ write the text, the ending column will be stored in (* col)
+ @param str is the string to write
+*/
+
+int mailimf_header_string_write(FILE * f, int * col,
+ const char * str, size_t length);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/plugins/mailmbox/maillock.c b/src/plugins/mailmbox/maillock.c
@@ -0,0 +1,284 @@
+/*
+ * libEtPan! -- a mail stuff library
+ *
+ * Copyright (C) 2001, 2002 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id $
+ */
+
+#include "maillock.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+
+/* ********************************************************************** */
+
+/* lock primitives */
+
+/* the lock code is modified from the dot lock file code from mail.local.c */
+
+/*
+ SENDMAIL LICENSE
+
+The following license terms and conditions apply, unless a different
+license is obtained from Sendmail, Inc., 6425 Christie Ave, Fourth Floor,
+Emeryville, CA 94608, or by electronic mail at license@sendmail.com.
+
+License Terms:
+
+Use, Modification and Redistribution (including distribution of any
+modified or derived work) in source and binary forms is permitted only if
+each of the following conditions is met:
+
+1. Redistributions qualify as "freeware" or "Open Source Software" under
+ one of the following terms:
+
+ (a) Redistributions are made at no charge beyond the reasonable cost of
+ materials and delivery.
+
+ (b) Redistributions are accompanied by a copy of the Source Code or by an
+ irrevocable offer to provide a copy of the Source Code for up to three
+ years at the cost of materials and delivery. Such redistributions
+ must allow further use, modification, and redistribution of the Source
+ Code under substantially the same terms as this license. For the
+ purposes of redistribution "Source Code" means the complete compilable
+ and linkable source code of sendmail including all modifications.
+
+2. Redistributions of source code must retain the copyright notices as they
+ appear in each source code file, these license terms, and the
+ disclaimer/limitation of liability set forth as paragraph 6 below.
+
+3. Redistributions in binary form must reproduce the Copyright Notice,
+ these license terms, and the disclaimer/limitation of liability set
+ forth as paragraph 6 below, in the documentation and/or other materials
+ provided with the distribution. For the purposes of binary distribution
+ the "Copyright Notice" refers to the following language:
+ "Copyright (c) 1998-2002 Sendmail, Inc. All rights reserved."
+
+4. Neither the name of Sendmail, Inc. nor the University of California nor
+ the names of their contributors may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission. The name "sendmail" is a trademark of Sendmail, Inc.
+
+5. All redistributions must comply with the conditions imposed by the
+ University of California on certain embedded code, whose copyright
+ notice and conditions for redistribution are as follows:
+
+ (a) Copyright (c) 1988, 1993 The Regents of the University of
+ California. All rights reserved.
+
+ (b) Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ (i) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (ii) Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ (iii) Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+6. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY
+ SENDMAIL, INC. AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ NO EVENT SHALL SENDMAIL, INC., THE REGENTS OF THE UNIVERSITY OF
+ CALIFORNIA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+*/
+
+/*
+ TODO : lock, prefer fcntl() over flock()
+ AND use dotlock code above
+*/
+
+#define LOCKTO_RM 300 /* timeout for stale lockfile removal */
+#define LOCKTO_GLOB 400 /* global timeout for lockfile creation */
+
+static int lock_common(const char * filename, int fd, short locktype)
+{
+ char lockfilename[PATH_MAX];
+ struct flock lock;
+ /* dot lock file */
+ int statfailed = 0;
+ time_t start;
+ int r;
+ int res;
+
+ lock.l_start = 0;
+ lock.l_len = 0;
+ lock.l_pid = getpid();
+ lock.l_type = locktype;
+ lock.l_whence = SEEK_SET;
+
+ r = fcntl(fd, F_SETLKW, &lock);
+ if (r < 0) {
+ /* WARNING POSIX lock could not be applied */
+ }
+
+ /* dot lock file */
+
+ if (strlen(filename) + 6 > PATH_MAX) {
+ res = -1;
+ goto unlock;
+ }
+
+ snprintf(lockfilename, PATH_MAX, "%s.lock", filename);
+
+ time(&start);
+ while (1) {
+ int fd;
+ struct stat st;
+ time_t now;
+
+ /* global timeout */
+ time(&now);
+ if (now > start + LOCKTO_GLOB) {
+ res = -1;
+ goto unlock;
+ }
+
+ fd = open(lockfilename, O_WRONLY|O_EXCL|O_CREAT, 0);
+ if (fd >= 0) {
+ /* defeat lock checking programs which test pid */
+ write(fd, "0", 2);
+ close(fd);
+ break;
+ }
+
+ /* libEtPan! - adds a delay of 5 seconds between each tries */
+ sleep(5);
+
+ if (stat(lockfilename, &st) < 0) {
+ if (statfailed++ > 5) {
+ res = -1;
+ goto unlock;
+ }
+ continue;
+ }
+ statfailed = 0;
+ time(&now);
+
+ if (now < st.st_ctime + LOCKTO_RM)
+ continue;
+
+ /* try to remove stale lockfile */
+ if (unlink(lockfilename) < 0) {
+ res = -1;
+ goto unlock;
+ }
+
+ /*
+ libEtPan! - removes this delay of 5 seconds,
+ maybe it was misplaced ?
+ */
+#if 0
+ sleep(5);
+#endif
+ }
+
+ return 0;
+
+ unlock:
+ lock.l_start = 0;
+ lock.l_len = 0;
+ lock.l_pid = getpid();
+ lock.l_type = F_UNLCK;
+ lock.l_whence = SEEK_SET;
+
+ r = fcntl(fd, F_SETLK, &lock);
+ if (r < 0) {
+ /* WARNING POSIX lock could not be applied */
+ }
+ return res;
+}
+
+static int unlock_common(const char * filename, int fd)
+{
+ char lockfilename[PATH_MAX];
+ struct flock lock;
+ int r;
+
+ if (strlen(filename) + 6 > PATH_MAX)
+ return -1;
+
+ snprintf(lockfilename, PATH_MAX, "%s.lock", filename);
+
+ unlink(lockfilename);
+
+ lock.l_start = 0;
+ lock.l_len = 0;
+ lock.l_pid = getpid();
+ lock.l_type = F_UNLCK;
+ lock.l_whence = SEEK_SET;
+
+ r = fcntl(fd, F_SETLK, &lock);
+ if (r < 0) {
+ /* WARNING POSIX lock could not be applied */
+ }
+
+ return 0;
+}
+
+int maillock_read_lock(const char * filename, int fd)
+{
+ return lock_common(filename, fd, F_RDLCK);
+}
+
+int maillock_read_unlock(const char * filename, int fd)
+{
+ return unlock_common(filename, fd);
+}
+
+int maillock_write_lock(const char * filename, int fd)
+{
+ return lock_common(filename, fd, F_WRLCK);
+}
+
+int maillock_write_unlock(const char * filename, int fd)
+{
+ return unlock_common(filename, fd);
+}
diff --git a/src/plugins/mailmbox/maillock.h b/src/plugins/mailmbox/maillock.h
@@ -0,0 +1,53 @@
+/*
+ * libEtPan! -- a mail stuff library
+ *
+ * Copyright (C) 2001, 2002 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef MAILLOCK_H
+
+#define MAILLOCK_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int maillock_read_lock(const char * filename, int fd);
+int maillock_read_unlock(const char * filename, int fd);
+int maillock_write_lock(const char * filename, int fd);
+int maillock_write_unlock(const char * filename, int fd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/plugins/mailmbox/mailmbox.c b/src/plugins/mailmbox/mailmbox.c
@@ -0,0 +1,1473 @@
+/*
+ * libEtPan! -- a mail stuff library
+ *
+ * Copyright (C) 2001, 2002 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#include "mailmbox.h"
+
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+
+#include "mmapstring.h"
+#include "mailmbox_parse.h"
+#include "maillock.h"
+#include "utils.h"
+
+/*
+ http://www.qmail.org/qmail-manual-html/man5/mbox.html
+ RFC 2076
+*/
+
+#define TMPDIR "/tmp"
+
+/* mbox is a file with a corresponding filename */
+
+#define UID_HEADER "X-LibEtPan-UID:"
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+int claws_mailmbox_write_lock(struct claws_mailmbox_folder * folder)
+{
+ int r;
+
+ if (folder->mb_read_only)
+ return MAILMBOX_ERROR_READONLY;
+
+ r = maillock_write_lock(folder->mb_filename, folder->mb_fd);
+ if (r == 0)
+ return MAILMBOX_NO_ERROR;
+ else
+ return MAILMBOX_ERROR_FILE;
+}
+
+int claws_mailmbox_write_unlock(struct claws_mailmbox_folder * folder)
+{
+ int r;
+
+ r = maillock_write_unlock(folder->mb_filename, folder->mb_fd);
+ if (r == 0)
+ return MAILMBOX_NO_ERROR;
+ else
+ return MAILMBOX_ERROR_FILE;
+}
+
+int claws_mailmbox_read_lock(struct claws_mailmbox_folder * folder)
+{
+ int r;
+
+ r = maillock_read_lock(folder->mb_filename, folder->mb_fd);
+ if (r == 0)
+ return MAILMBOX_NO_ERROR;
+ else
+ return MAILMBOX_ERROR_FILE;
+}
+
+int claws_mailmbox_read_unlock(struct claws_mailmbox_folder * folder)
+{
+ int r;
+
+ r = maillock_read_unlock(folder->mb_filename, folder->mb_fd);
+ if (r == 0)
+ return MAILMBOX_NO_ERROR;
+ else
+ return MAILMBOX_ERROR_FILE;
+}
+
+
+/*
+ map the file into memory.
+ the file must be locked.
+*/
+
+int claws_mailmbox_map(struct claws_mailmbox_folder * folder)
+{
+ char * str;
+ struct stat buf;
+ int res;
+ int r;
+
+ r = stat(folder->mb_filename, &buf);
+ if (r < 0) {
+ debug_print("stat failed %d\n", r);
+ res = MAILMBOX_ERROR_FILE;
+ goto err;
+ }
+
+ if (buf.st_size == 0) {
+ folder->mb_mapping = NULL;
+ folder->mb_mapping_size = 0;
+ return MAILMBOX_NO_ERROR;
+ }
+ if (folder->mb_read_only)
+ str = (char *) mmap(NULL, buf.st_size, PROT_READ,
+ MAP_PRIVATE, folder->mb_fd, 0);
+ else
+ str = (char *) mmap(NULL, buf.st_size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, folder->mb_fd, 0);
+ if (str == MAP_FAILED) {
+ perror("mmap");
+ debug_print("map of %lu bytes failed\n", buf.st_size);
+ res = MAILMBOX_ERROR_FILE;
+ goto err;
+ }
+
+ folder->mb_mapping = str;
+ folder->mb_mapping_size = buf.st_size;
+
+ return MAILMBOX_NO_ERROR;
+
+ err:
+ return res;
+}
+
+/*
+ unmap the file
+*/
+
+void claws_mailmbox_unmap(struct claws_mailmbox_folder * folder)
+{
+ munmap(folder->mb_mapping, folder->mb_mapping_size);
+ folder->mb_mapping = NULL;
+ folder->mb_mapping_size = 0;
+}
+
+void claws_mailmbox_sync(struct claws_mailmbox_folder * folder)
+{
+ msync(folder->mb_mapping, folder->mb_mapping_size, MS_SYNC);
+}
+
+void claws_mailmbox_timestamp(struct claws_mailmbox_folder * folder)
+{
+ int r;
+ struct stat buf;
+
+ r = stat(folder->mb_filename, &buf);
+ if (r < 0)
+ folder->mb_mtime = (time_t) -1;
+ else
+ folder->mb_mtime = buf.st_mtime;
+}
+
+/*
+ open the file
+*/
+
+int claws_mailmbox_open(struct claws_mailmbox_folder * folder)
+{
+ int fd = -1;
+ int read_only;
+
+ if (!folder->mb_read_only) {
+ read_only = FALSE;
+ fd = open(folder->mb_filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+ }
+
+ if (folder->mb_read_only || (fd < 0)) {
+ read_only = TRUE;
+ fd = open(folder->mb_filename, O_RDONLY);
+ if (fd < 0)
+ return MAILMBOX_ERROR_FILE_NOT_FOUND;
+ }
+
+ folder->mb_fd = fd;
+ folder->mb_read_only = read_only;
+
+ return MAILMBOX_NO_ERROR;
+}
+
+/*
+ close the file
+*/
+
+void claws_mailmbox_close(struct claws_mailmbox_folder * folder)
+{
+ close(folder->mb_fd);
+ folder->mb_fd = -1;
+}
+
+
+static int claws_mailmbox_validate_lock(struct claws_mailmbox_folder * folder,
+ int (* custom_lock)(struct claws_mailmbox_folder *),
+ int (* custom_unlock)(struct claws_mailmbox_folder *))
+{
+ struct stat buf;
+ int res;
+ int r;
+
+ r = stat(folder->mb_filename, &buf);
+ if (r < 0) {
+ buf.st_mtime = (time_t) -1;
+ }
+
+ if ((buf.st_mtime != folder->mb_mtime) ||
+ ((size_t) buf.st_size != folder->mb_mapping_size)) {
+ int r;
+
+ claws_mailmbox_unmap(folder);
+ claws_mailmbox_close(folder);
+
+ r = claws_mailmbox_open(folder);
+ if (r != MAILMBOX_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = custom_lock(folder);
+ if (r != MAILMBOX_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = claws_mailmbox_map(folder);
+ if (r != MAILMBOX_NO_ERROR) {
+ res = r;
+ goto err_unlock;
+ }
+
+ r = claws_mailmbox_parse(folder);
+ if (r != MAILMBOX_NO_ERROR) {
+ res = r;
+ goto err_unlock;
+ }
+
+ folder->mb_mtime = buf.st_mtime;
+
+ return MAILMBOX_NO_ERROR;
+ }
+ else {
+ r = custom_lock(folder);
+ if (r != MAILMBOX_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+ }
+
+ return MAILMBOX_NO_ERROR;
+
+ err_unlock:
+ custom_unlock(folder);
+ err:
+ return res;
+}
+
+
+int claws_mailmbox_validate_write_lock(struct claws_mailmbox_folder * folder)
+{
+ return claws_mailmbox_validate_lock(folder,
+ claws_mailmbox_write_lock,
+ claws_mailmbox_write_unlock);
+}
+
+
+int claws_mailmbox_validate_read_lock(struct claws_mailmbox_folder * folder)
+{
+ return claws_mailmbox_validate_lock(folder,
+ claws_mailmbox_read_lock,
+ claws_mailmbox_read_unlock);
+}
+
+
+/* ********************************************************************** */
+/* append messages */
+
+#define MAX_FROM_LINE_SIZE 256
+
+static inline size_t get_line(const char * line, size_t length,
+ const char ** pnext_line, size_t * pcount)
+{
+ size_t count;
+
+ count = 0;
+
+ while (1) {
+ if (length == 0)
+ break;
+
+ if (* line == '\r') {
+ line ++;
+
+ count ++;
+ length --;
+
+ if (length > 0) {
+ if (* line == '\n') {
+ line ++;
+
+ count ++;
+ length --;
+
+ break;
+ }
+ }
+ }
+ else if (* line == '\n') {
+ line ++;
+
+ count ++;
+ length --;
+
+ break;
+ }
+ else {
+ line ++;
+ length --;
+ count ++;
+ }
+ }
+
+ * pnext_line = line;
+ * pcount = count;
+
+ return count;
+}
+
+/*
+ TODO : should strip \r\n if any
+ see also in write_fixed_line
+*/
+
+static inline size_t get_fixed_line_size(const char * line, size_t length,
+ const char ** pnext_line, size_t * pcount,
+ size_t * pfixed_count)
+{
+ size_t count;
+ const char * next_line;
+ size_t fixed_count;
+
+ if (!get_line(line, length, &next_line, &count))
+ return 0;
+
+ fixed_count = count;
+ if (count >= 5) {
+ if (line[0] == 'F') {
+ if (strncmp(line, "From ", 5) == 0)
+ fixed_count ++;
+ }
+ }
+
+ * pnext_line = next_line;
+ * pcount = count;
+ * pfixed_count = fixed_count;
+
+ return count;
+}
+
+static size_t get_fixed_message_size(const char * message, size_t size,
+ uint32_t uid, int force_no_uid)
+{
+ size_t fixed_size;
+ size_t cur_token;
+ size_t left;
+ const char * next;
+ const char * cur;
+ int end;
+ int r;
+ uint32_t tmp_uid;
+
+ cur_token = 0;
+
+ fixed_size = 0;
+
+ /* headers */
+
+ end = FALSE;
+ while (!end) {
+ size_t begin;
+ int ignore;
+
+ ignore = FALSE;
+ begin = cur_token;
+ if (cur_token + strlen(UID_HEADER) <= size) {
+ if (message[cur_token] == 'X') {
+ if (strncasecmp(message + cur_token, UID_HEADER,
+ strlen(UID_HEADER)) == 0) {
+ ignore = TRUE;
+ }
+ }
+ }
+
+ r = mailimf_ignore_field_parse(message, size, &cur_token);
+ switch (r) {
+ case MAILIMF_NO_ERROR:
+ if (!ignore)
+ fixed_size += cur_token - begin;
+ break;
+ case MAILIMF_ERROR_PARSE:
+ default:
+ end = TRUE;
+ break;
+ }
+ }
+
+ if (!force_no_uid) {
+ /* UID header */
+
+#if CRLF_BADNESS
+ fixed_size += strlen(UID_HEADER " \r\n");
+#else
+ fixed_size += strlen(UID_HEADER " \n");
+#endif
+
+ tmp_uid = uid;
+ while (tmp_uid >= 10) {
+ tmp_uid /= 10;
+ fixed_size ++;
+ }
+ fixed_size ++;
+ }
+
+ /* body */
+
+ left = size - cur_token;
+ next = message + cur_token;
+ while (left > 0) {
+ size_t count;
+ size_t fixed_count;
+
+ cur = next;
+
+ if (!get_fixed_line_size(cur, left, &next, &count, &fixed_count))
+ break;
+
+ fixed_size += fixed_count;
+ left -= count;
+ }
+
+ return fixed_size;
+}
+
+static inline char * write_fixed_line(char * str,
+ const char * line, size_t length,
+ const char ** pnext_line, size_t * pcount)
+{
+ size_t count;
+ const char * next_line;
+
+ if (!get_line(line, length, &next_line, &count))
+ return str;
+
+ if (count >= 5) {
+ if (line[0] == 'F') {
+ if (strncmp(line, "From ", 5) == 0) {
+ * str = '>';
+ str ++;
+ }
+ }
+ }
+
+ memcpy(str, line, count);
+
+ * pnext_line = next_line;
+ * pcount = count;
+ str += count;
+
+ return str;
+}
+
+static char * write_fixed_message(char * str,
+ const char * message, size_t size,
+ uint32_t uid, int force_no_uid)
+{
+ size_t fixed_size;
+ size_t cur_token;
+ size_t left;
+ int end;
+ int r;
+ const char * cur_src;
+ size_t numlen;
+
+ cur_token = 0;
+
+ fixed_size = 0;
+
+ /* headers */
+
+ end = FALSE;
+ while (!end) {
+ size_t begin;
+ int ignore;
+
+ ignore = FALSE;
+ begin = cur_token;
+ if (cur_token + strlen(UID_HEADER) <= size) {
+ if (message[cur_token] == 'X') {
+ if (strncasecmp(message + cur_token, UID_HEADER,
+ strlen(UID_HEADER)) == 0) {
+ ignore = TRUE;
+ }
+ }
+ }
+
+ r = mailimf_ignore_field_parse(message, size, &cur_token);
+ switch (r) {
+ case MAILIMF_NO_ERROR:
+ if (!ignore) {
+ memcpy(str, message + begin, cur_token - begin);
+ str += cur_token - begin;
+ }
+ break;
+ case MAILIMF_ERROR_PARSE:
+ default:
+ end = TRUE;
+ break;
+ }
+ }
+
+ if (!force_no_uid) {
+ /* UID header */
+
+ memcpy(str, UID_HEADER " ", strlen(UID_HEADER " "));
+ str += strlen(UID_HEADER " ");
+#if CRLF_BADNESS
+ numlen = snprintf(str, 20, "%i\r\n", uid);
+#else
+ numlen = snprintf(str, 20, "%i\n", uid);
+#endif
+ str += numlen;
+ }
+
+ /* body */
+
+ cur_src = message + cur_token;
+ left = size - cur_token;
+ while (left > 0) {
+ size_t count = 0;
+ const char * next = NULL;
+
+ str = write_fixed_line(str, cur_src, left, &next, &count);
+
+ cur_src = next;
+ left -= count;
+ }
+
+ return str;
+}
+
+#define DEFAULT_FROM_LINE "From - Wed Jun 30 21:49:08 1993\n"
+
+int
+claws_mailmbox_append_message_list_no_lock(struct claws_mailmbox_folder * folder,
+ carray * append_tab)
+{
+ size_t extra_size;
+ int r;
+ char from_line[MAX_FROM_LINE_SIZE] = DEFAULT_FROM_LINE;
+ struct tm time_info;
+ time_t date;
+ int res;
+ size_t old_size;
+ char * str;
+ unsigned int i;
+ size_t from_size;
+ size_t maxuid;
+ size_t left;
+ size_t crlf_count;
+
+ if (folder->mb_read_only) {
+ res = MAILMBOX_ERROR_READONLY;
+ goto err;
+ }
+
+ date = time(NULL);
+ from_size = strlen(DEFAULT_FROM_LINE);
+ if (localtime_r(&date, &time_info) != NULL)
+ from_size = strftime(from_line, MAX_FROM_LINE_SIZE, "From - %c\n", &time_info);
+
+ maxuid = /* */ folder->mb_max_uid;
+
+ extra_size = 0;
+ for(i = 0 ; i < carray_count(append_tab) ; i ++) {
+ struct claws_mailmbox_append_info * info;
+
+ info = carray_get(append_tab, i);
+ extra_size += from_size;
+ extra_size += get_fixed_message_size(info->ai_message, info->ai_size,
+ folder->mb_max_uid + i + 1,
+ folder->mb_no_uid);
+#if CRLF_BADNESS
+ extra_size += 2; /* CR LF */
+#else
+ extra_size += 1; /* CR LF */
+#endif
+ }
+
+ left = folder->mb_mapping_size;
+ crlf_count = 0;
+ while (left >= 1) {
+ if (folder->mb_mapping[left - 1] == '\n') {
+ crlf_count ++;
+ left --;
+ }
+#if CRLF_BADNESS
+ else if (folder->mb_mapping[left - 1] == '\r') {
+ left --;
+ }
+#endif
+ else
+ break;
+
+ if (crlf_count == 2)
+ break;
+ }
+
+ old_size = folder->mb_mapping_size;
+ claws_mailmbox_unmap(folder);
+
+ if (old_size != 0) {
+ if (crlf_count != 2)
+#if CRLF_BADNESS
+ extra_size += (2 - crlf_count) * 2;
+#else
+ /* Need the number of LFs, not CRLFs */
+ extra_size += (2 - crlf_count) * 1; /* 2 */
+#endif
+ }
+
+ r = ftruncate(folder->mb_fd, extra_size + old_size);
+ if (r < 0) {
+ debug_print("ftruncate failed with %d\n", r);
+ claws_mailmbox_map(folder);
+ res = MAILMBOX_ERROR_FILE;
+ goto err;
+ }
+
+ r = claws_mailmbox_map(folder);
+ if (r < 0) {
+ debug_print("claws_mailmbox_map failed with %d\n", r);
+ ftruncate(folder->mb_fd, old_size);
+ return MAILMBOX_ERROR_FILE;
+ }
+
+ str = folder->mb_mapping + old_size;
+
+ if (old_size != 0) {
+ for(i = 0 ; i < 2 - crlf_count ; i ++) {
+#if CRLF_BADNESS
+ * str = '\r';
+ str ++;
+#endif
+ * str = '\n';
+ str ++;
+ }
+ }
+
+ for(i = 0 ; i < carray_count(append_tab) ; i ++) {
+ struct claws_mailmbox_append_info * info;
+
+ info = carray_get(append_tab, i);
+
+ memcpy(str, from_line, from_size);
+
+ str += strlen(from_line);
+
+ str = write_fixed_message(str, info->ai_message, info->ai_size,
+ folder->mb_max_uid + i + 1,
+ folder->mb_no_uid);
+
+#if CRLF_BADNESS
+ * str = '\r';
+ str ++;
+#endif
+ * str = '\n';
+ str ++;
+ }
+
+ folder->mb_max_uid += carray_count(append_tab);
+
+ return MAILMBOX_NO_ERROR;
+
+ err:
+ return res;
+}
+
+int
+claws_mailmbox_append_message_list(struct claws_mailmbox_folder * folder,
+ carray * append_tab)
+{
+ int r;
+ int res;
+ size_t cur_token;
+
+ r = claws_mailmbox_validate_write_lock(folder);
+ if (r != MAILMBOX_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = claws_mailmbox_expunge_no_lock(folder);
+ if (r != MAILMBOX_NO_ERROR) {
+ res = r;
+ goto unlock;
+ }
+
+ cur_token = folder->mb_mapping_size;
+
+ r = claws_mailmbox_append_message_list_no_lock(folder, append_tab);
+ if (r != MAILMBOX_NO_ERROR) {
+ res = r;
+ goto unlock;
+ }
+
+ claws_mailmbox_sync(folder);
+
+ r = claws_mailmbox_parse_additionnal(folder, &cur_token);
+ if (r != MAILMBOX_NO_ERROR) {
+ res = r;
+ goto unlock;
+ }
+
+ claws_mailmbox_timestamp(folder);
+
+ claws_mailmbox_write_unlock(folder);
+
+ return MAILMBOX_NO_ERROR;
+
+ unlock:
+ claws_mailmbox_write_unlock(folder);
+ err:
+ return res;
+}
+
+int
+claws_mailmbox_append_message(struct claws_mailmbox_folder * folder,
+ const char * data, size_t len)
+{
+ carray * tab;
+ struct claws_mailmbox_append_info * append_info;
+ int res;
+ int r;
+
+ tab = carray_new(1);
+ if (tab == NULL) {
+ res = MAILMBOX_ERROR_MEMORY;
+ goto err;
+ }
+
+ append_info = claws_mailmbox_append_info_new(data, len);
+ if (append_info == NULL) {
+ res = MAILMBOX_ERROR_MEMORY;
+ goto free_list;
+ }
+
+ r = carray_add(tab, append_info, NULL);
+ if (r < 0) {
+ res = MAILMBOX_ERROR_MEMORY;
+ goto free_append_info;
+ }
+
+ r = claws_mailmbox_append_message_list(folder, tab);
+
+ claws_mailmbox_append_info_free(append_info);
+ carray_free(tab);
+
+ return r;
+
+ free_append_info:
+ claws_mailmbox_append_info_free(append_info);
+ free_list:
+ carray_free(tab);
+ err:
+ return res;
+}
+
+/* ********************************************************************** */
+
+int claws_mailmbox_fetch_msg_no_lock(struct claws_mailmbox_folder * folder,
+ uint32_t num, const char ** result,
+ size_t * result_len)
+{
+ struct claws_mailmbox_msg_info * info;
+ int res;
+ chashdatum key;
+ chashdatum data;
+ int r;
+
+ key.data = #
+ key.len = sizeof(num);
+
+ r = chash_get(folder->mb_hash, &key, &data);
+ if (r < 0) {
+ res = MAILMBOX_ERROR_MSG_NOT_FOUND;
+ goto err;
+ }
+
+ info = data.data;
+
+ if (info->msg_deleted) {
+ res = MAILMBOX_ERROR_MSG_NOT_FOUND;
+ goto err;
+ }
+
+ * result = folder->mb_mapping + info->msg_headers;
+ * result_len = info->msg_size - info->msg_start_len;
+
+ return MAILMBOX_NO_ERROR;
+
+ err:
+ return res;
+}
+
+int claws_mailmbox_fetch_msg_headers_no_lock(struct claws_mailmbox_folder * folder,
+ uint32_t num, const char ** result,
+ size_t * result_len)
+{
+ struct claws_mailmbox_msg_info * info;
+ int res;
+ chashdatum key;
+ chashdatum data;
+ int r;
+
+ key.data = #
+ key.len = sizeof(num);
+
+ r = chash_get(folder->mb_hash, &key, &data);
+ if (r < 0) {
+ res = MAILMBOX_ERROR_MSG_NOT_FOUND;
+ goto err;
+ }
+
+ info = data.data;
+
+ if (info->msg_deleted) {
+ res = MAILMBOX_ERROR_MSG_NOT_FOUND;
+ goto err;
+ }
+
+ * result = folder->mb_mapping + info->msg_headers;
+ * result_len = info->msg_headers_len;
+
+ return MAILMBOX_NO_ERROR;
+
+ err:
+ return res;
+}
+
+int claws_mailmbox_fetch_msg(struct claws_mailmbox_folder * folder,
+ uint32_t num, const char ** result,
+ size_t * result_len)
+{
+ MMAPString * mmapstr;
+ int res;
+ const char * data;
+ size_t len;
+ int r;
+ size_t fixed_size;
+ char * end;
+
+ r = claws_mailmbox_validate_read_lock(folder);
+ if (r != MAILMBOX_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = claws_mailmbox_fetch_msg_no_lock(folder, num, &data, &len);
+ if (r != MAILMBOX_NO_ERROR) {
+ res = r;
+ goto unlock;
+ }
+
+ /* size with no uid */
+ fixed_size = get_fixed_message_size(data, len, 0, 1 /* force no uid */);
+
+#if 0
+ mmapstr = mmap_string_new_len(data, fixed_size);
+ if (mmapstr == NULL) {
+ res = MAILMBOX_ERROR_MEMORY;
+ goto unlock;
+ }
+#endif
+ mmapstr = mmap_string_sized_new(fixed_size);
+ if (mmapstr == NULL) {
+ res = MAILMBOX_ERROR_MEMORY;
+ goto unlock;
+ }
+
+ end = write_fixed_message(mmapstr->str, data, len, 0, 1 /* force no uid */);
+ * end = '\0';
+ mmapstr->len = fixed_size;
+
+ r = mmap_string_ref(mmapstr);
+ if (r < 0) {
+ mmap_string_free(mmapstr);
+ res = MAILMBOX_ERROR_MEMORY;
+ goto unlock;
+ }
+
+ * result = mmapstr->str;
+ * result_len = mmapstr->len;
+
+ claws_mailmbox_read_unlock(folder);
+
+ return MAILMBOX_NO_ERROR;
+
+ unlock:
+ claws_mailmbox_read_unlock(folder);
+ err:
+ return res;
+}
+
+int claws_mailmbox_fetch_msg_headers(struct claws_mailmbox_folder * folder,
+ uint32_t num, const char ** result,
+ size_t * result_len)
+{
+ MMAPString * mmapstr;
+ int res;
+ const char * data;
+ size_t len;
+ int r;
+ size_t fixed_size;
+ char * end;
+
+ r = claws_mailmbox_validate_read_lock(folder);
+ if (r != MAILMBOX_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = claws_mailmbox_fetch_msg_headers_no_lock(folder, num, &data, &len);
+ if (r != MAILMBOX_NO_ERROR) {
+ res = r;
+ goto unlock;
+ }
+
+#if 0
+ mmapstr = mmap_string_new_len(data, len);
+ if (mmapstr == NULL) {
+ res = MAILMBOX_ERROR_MEMORY;
+ goto unlock;
+ }
+#endif
+ /* size with no uid */
+ fixed_size = get_fixed_message_size(data, len, 0, 1 /* force no uid */);
+
+ mmapstr = mmap_string_sized_new(fixed_size);
+ if (mmapstr == NULL) {
+ res = MAILMBOX_ERROR_MEMORY;
+ goto unlock;
+ }
+
+ end = write_fixed_message(mmapstr->str, data, len, 0, 1 /* force no uid */);
+ * end = '\0';
+ mmapstr->len = fixed_size;
+
+ r = mmap_string_ref(mmapstr);
+ if (r < 0) {
+ mmap_string_free(mmapstr);
+ res = MAILMBOX_ERROR_MEMORY;
+ goto unlock;
+ }
+
+ * result = mmapstr->str;
+ * result_len = mmapstr->len;
+
+ claws_mailmbox_read_unlock(folder);
+
+ return MAILMBOX_NO_ERROR;
+
+ unlock:
+ claws_mailmbox_read_unlock(folder);
+ err:
+ return res;
+}
+
+void claws_mailmbox_fetch_result_free(char * msg)
+{
+ mmap_string_unref(msg);
+}
+
+
+int claws_mailmbox_copy_msg_list(struct claws_mailmbox_folder * dest_folder,
+ struct claws_mailmbox_folder * src_folder,
+ carray * tab)
+{
+ int r;
+ int res;
+ carray * append_tab;
+ unsigned int i;
+
+ r = claws_mailmbox_validate_read_lock(src_folder);
+ if (r != MAILMBOX_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ append_tab = carray_new(carray_count(tab));
+ if (append_tab == NULL) {
+ res = MAILMBOX_ERROR_MEMORY;
+ goto src_unlock;
+ }
+
+ for(i = 0 ; i < carray_count(tab) ; i ++) {
+ struct claws_mailmbox_append_info * append_info;
+ const char * data;
+ size_t len;
+ uint32_t uid;
+
+ uid = * ((uint32_t *) carray_get(tab, i));
+
+ r = claws_mailmbox_fetch_msg_no_lock(src_folder, uid, &data, &len);
+ if (r != MAILMBOX_NO_ERROR) {
+ res = r;
+ goto free_list;
+ }
+
+ append_info = claws_mailmbox_append_info_new(data, len);
+ if (append_info == NULL) {
+ res = MAILMBOX_ERROR_MEMORY;
+ goto free_list;
+ }
+
+ r = carray_add(append_tab, append_info, NULL);
+ if (r < 0) {
+ claws_mailmbox_append_info_free(append_info);
+ res = MAILMBOX_ERROR_MEMORY;
+ goto free_list;
+ }
+ }
+
+ r = claws_mailmbox_append_message_list(dest_folder, append_tab);
+ if (r != MAILMBOX_NO_ERROR) {
+ res = r;
+ goto src_unlock;
+ }
+
+ for(i = 0 ; i < carray_count(append_tab) ; i ++) {
+ struct claws_mailmbox_append_info * append_info;
+
+ append_info = carray_get(append_tab, i);
+ claws_mailmbox_append_info_free(append_info);
+ }
+ carray_free(append_tab);
+
+ claws_mailmbox_read_unlock(src_folder);
+
+ return MAILMBOX_NO_ERROR;
+
+ free_list:
+ for(i = 0 ; i < carray_count(append_tab) ; i ++) {
+ struct claws_mailmbox_append_info * append_info;
+
+ append_info = carray_get(append_tab, i);
+ claws_mailmbox_append_info_free(append_info);
+ }
+ carray_free(append_tab);
+ src_unlock:
+ claws_mailmbox_read_unlock(src_folder);
+ err:
+ return res;
+}
+
+int claws_mailmbox_copy_msg(struct claws_mailmbox_folder * dest_folder,
+ struct claws_mailmbox_folder * src_folder,
+ uint32_t uid)
+{
+ carray * tab;
+ int res;
+ uint32_t * puid;
+ int r;
+
+ tab = carray_new(1);
+ if (tab == NULL) {
+ res = MAILMBOX_ERROR_MEMORY;
+ goto err;
+ }
+
+ puid = malloc(sizeof(* puid));
+ if (puid == NULL) {
+ res = MAILMBOX_ERROR_MEMORY;
+ goto free_array;
+ }
+ * puid = uid;
+
+ r = claws_mailmbox_copy_msg_list(dest_folder, src_folder, tab);
+ res = r;
+
+ free(puid);
+ free_array:
+ carray_free(tab);
+ err:
+ return res;
+}
+
+static int claws_mailmbox_expunge_to_file_no_lock(char * dest_filename, int dest_fd,
+ struct claws_mailmbox_folder * folder,
+ size_t * result_size)
+{
+ int r;
+ int res;
+ unsigned long i;
+ size_t cur_offset;
+ char * dest;
+ size_t size;
+
+ size = 0;
+ for(i = 0 ; i < carray_count(folder->mb_tab) ; i ++) {
+ struct claws_mailmbox_msg_info * info;
+
+ info = carray_get(folder->mb_tab, i);
+
+ if (!info->msg_deleted) {
+ size += info->msg_size + info->msg_padding;
+
+ if (!folder->mb_no_uid) {
+ if (!info->msg_written_uid) {
+ uint32_t uid;
+
+#if CRLF_BADNESS
+ size += strlen(UID_HEADER " \r\n");
+#else
+ size += strlen(UID_HEADER " \n");
+#endif
+ uid = info->msg_uid;
+ while (uid >= 10) {
+ uid /= 10;
+ size ++;
+ }
+ size ++;
+ }
+ }
+ }
+ }
+
+ r = ftruncate(dest_fd, size);
+ if (r < 0) {
+ res = MAILMBOX_ERROR_FILE;
+ goto err;
+ }
+
+ if (size) {
+ dest = (char *) mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, dest_fd, 0);
+ if (dest == MAP_FAILED) {
+ res = MAILMBOX_ERROR_FILE;
+ goto err;
+ }
+ }
+
+ cur_offset = 0;
+ for(i = 0 ; i < carray_count(folder->mb_tab) ; i ++) {
+ struct claws_mailmbox_msg_info * info;
+
+ info = carray_get(folder->mb_tab, i);
+
+ if (!info->msg_deleted) {
+ memcpy(dest + cur_offset, folder->mb_mapping + info->msg_start,
+ info->msg_headers_len + info->msg_start_len);
+ cur_offset += info->msg_headers_len + info->msg_start_len;
+
+ if (!folder->mb_no_uid) {
+ if (!info->msg_written_uid) {
+ size_t numlen;
+
+ memcpy(dest + cur_offset, UID_HEADER " ", strlen(UID_HEADER " "));
+ cur_offset += strlen(UID_HEADER " ");
+#if CRLF_BADNESS
+ numlen = snprintf(dest + cur_offset, size - cur_offset,
+ "%i\r\n", info->msg_uid);
+#else
+ numlen = snprintf(dest + cur_offset, size - cur_offset,
+ "%i\n", info->msg_uid);
+#endif
+ cur_offset += numlen;
+ }
+ }
+
+ memcpy(dest + cur_offset,
+ folder->mb_mapping + info->msg_headers + info->msg_headers_len,
+ info->msg_size - (info->msg_start_len + info->msg_headers_len)
+ + info->msg_padding);
+
+ cur_offset += info->msg_size -
+ (info->msg_start_len + info->msg_headers_len)
+ + info->msg_padding;
+ }
+ }
+ fflush(stdout);
+
+ if (size) {
+ msync(dest, size, MS_SYNC);
+ munmap(dest, size);
+ }
+
+ * result_size = size;
+
+ return MAILMBOX_NO_ERROR;
+
+ err:
+ return res;
+}
+
+int claws_mailmbox_expunge_no_lock(struct claws_mailmbox_folder * folder)
+{
+ char tmpfile[PATH_MAX];
+ int r;
+ int res;
+ int dest_fd;
+ size_t size;
+
+ if (folder->mb_read_only)
+ return MAILMBOX_ERROR_READONLY;
+
+ if (((folder->mb_written_uid >= folder->mb_max_uid) || folder->mb_no_uid) &&
+ (!folder->mb_changed)) {
+ /* no need to expunge */
+ return MAILMBOX_NO_ERROR;
+ }
+
+ snprintf(tmpfile, PATH_MAX, "%sXXXXXX", folder->mb_filename);
+ dest_fd = mkstemp(tmpfile);
+
+ if (dest_fd < 0) {
+ res = MAILMBOX_ERROR_FILE;
+ goto unlink;
+ }
+
+ r = claws_mailmbox_expunge_to_file_no_lock(tmpfile, dest_fd,
+ folder, &size);
+ if (r != MAILMBOX_NO_ERROR) {
+ res = r;
+ goto unlink;
+ }
+
+ close(dest_fd);
+
+ r = rename(tmpfile, folder->mb_filename);
+ if (r < 0) {
+ res = r;
+ goto err;
+ }
+
+ claws_mailmbox_unmap(folder);
+ claws_mailmbox_close(folder);
+
+ r = claws_mailmbox_open(folder);
+ if (r != MAILMBOX_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = claws_mailmbox_map(folder);
+ if (r != MAILMBOX_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = claws_mailmbox_parse(folder);
+ if (r != MAILMBOX_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ claws_mailmbox_timestamp(folder);
+
+ folder->mb_changed = FALSE;
+ folder->mb_deleted_count = 0;
+
+ return MAILMBOX_NO_ERROR;
+
+ unlink:
+ close(dest_fd);
+ unlink(tmpfile);
+ err:
+ return res;
+}
+
+int claws_mailmbox_expunge(struct claws_mailmbox_folder * folder)
+{
+ int r;
+ int res;
+
+ r = claws_mailmbox_validate_write_lock(folder);
+ if (r != MAILMBOX_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = claws_mailmbox_expunge_no_lock(folder);
+ res = r;
+
+ claws_mailmbox_write_unlock(folder);
+ err:
+ return res;
+}
+
+int claws_mailmbox_delete_msg(struct claws_mailmbox_folder * folder, uint32_t uid)
+{
+ struct claws_mailmbox_msg_info * info;
+ int res;
+ chashdatum key;
+ chashdatum data;
+ int r;
+
+ if (folder->mb_read_only) {
+ res = MAILMBOX_ERROR_READONLY;
+ goto err;
+ }
+
+ key.data = &uid;
+ key.len = sizeof(uid);
+
+ r = chash_get(folder->mb_hash, &key, &data);
+ if (r < 0) {
+ res = MAILMBOX_ERROR_MSG_NOT_FOUND;
+ goto err;
+ }
+
+ info = data.data;
+
+ if (info->msg_deleted) {
+ res = MAILMBOX_ERROR_MSG_NOT_FOUND;
+ goto err;
+ }
+
+ info->msg_deleted = TRUE;
+ folder->mb_changed = TRUE;
+ folder->mb_deleted_count ++;
+
+ return MAILMBOX_NO_ERROR;
+
+ err:
+ return res;
+}
+
+
+/*
+ INIT of MBOX
+
+ - open file
+ - map the file
+
+ - lock the file
+
+ - parse memory
+
+ - unlock the file
+*/
+
+int claws_mailmbox_init(const char * filename,
+ int force_readonly,
+ int force_no_uid,
+ uint32_t default_written_uid,
+ struct claws_mailmbox_folder ** result_folder)
+{
+ struct claws_mailmbox_folder * folder;
+ int r;
+ int res;
+
+ folder = claws_mailmbox_folder_new(filename);
+ if (folder == NULL) {
+ debug_print("folder is null for %s\n", filename);
+ res = MAILMBOX_ERROR_MEMORY;
+ goto err;
+ }
+ folder->mb_no_uid = force_no_uid;
+ folder->mb_read_only = force_readonly;
+ folder->mb_written_uid = default_written_uid;
+
+ folder->mb_changed = FALSE;
+ folder->mb_deleted_count = 0;
+
+ r = claws_mailmbox_open(folder);
+ if (r != MAILMBOX_NO_ERROR) {
+ debug_print("folder can't be opened %d\n", r);
+ res = r;
+ goto free;
+ }
+
+ r = claws_mailmbox_map(folder);
+ if (r != MAILMBOX_NO_ERROR) {
+ debug_print("folder can't be mapped %d\n", r);
+ res = r;
+ goto close;
+ }
+
+ r = claws_mailmbox_validate_read_lock(folder);
+ if (r != MAILMBOX_NO_ERROR) {
+ debug_print("folder can't be locked %d\n", r);
+ res = r;
+ goto unmap;
+ }
+
+ claws_mailmbox_read_unlock(folder);
+
+ * result_folder = folder;
+
+ return MAILMBOX_NO_ERROR;
+
+ unmap:
+ claws_mailmbox_unmap(folder);
+ close:
+ claws_mailmbox_close(folder);
+ free:
+ claws_mailmbox_folder_free(folder);
+ err:
+ return res;
+}
+
+
+/*
+ when MBOX is DONE
+
+ - check for changes
+
+ - unmap the file
+ - close file
+*/
+
+void claws_mailmbox_done(struct claws_mailmbox_folder * folder)
+{
+ if (!folder->mb_read_only)
+ claws_mailmbox_expunge(folder);
+
+ claws_mailmbox_unmap(folder);
+ claws_mailmbox_close(folder);
+
+ claws_mailmbox_folder_free(folder);
+}
diff --git a/src/plugins/mailmbox/mailmbox.h b/src/plugins/mailmbox/mailmbox.h
@@ -0,0 +1,140 @@
+/*
+ * libEtPan! -- a mail stuff library
+ *
+ * Copyright (C) 2001, 2002 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef MAILMBOX_H
+
+#define MAILMBOX_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "mailmbox_types.h"
+
+int
+claws_mailmbox_append_message_list(struct claws_mailmbox_folder * folder,
+ carray * append_tab);
+
+int
+claws_mailmbox_append_message(struct claws_mailmbox_folder * folder,
+ const char * data, size_t len);
+
+int claws_mailmbox_fetch_msg(struct claws_mailmbox_folder * folder,
+ uint32_t num, const char ** result,
+ size_t * result_len);
+
+int claws_mailmbox_fetch_msg_headers(struct claws_mailmbox_folder * folder,
+ uint32_t num, const char ** result,
+ size_t * result_len);
+
+void claws_mailmbox_fetch_result_free(char * msg);
+
+int claws_mailmbox_copy_msg_list(struct claws_mailmbox_folder * dest_folder,
+ struct claws_mailmbox_folder * src_folder,
+ carray * tab);
+
+int claws_mailmbox_copy_msg(struct claws_mailmbox_folder * dest_folder,
+ struct claws_mailmbox_folder * src_folder,
+ uint32_t uid);
+
+int claws_mailmbox_expunge(struct claws_mailmbox_folder * folder);
+
+int claws_mailmbox_delete_msg(struct claws_mailmbox_folder * folder, uint32_t uid);
+
+int claws_mailmbox_init(const char * filename,
+ int force_readonly,
+ int force_no_uid,
+ uint32_t default_written_uid,
+ struct claws_mailmbox_folder ** result_folder);
+
+void claws_mailmbox_done(struct claws_mailmbox_folder * folder);
+
+/* low-level access primitives */
+
+int claws_mailmbox_write_lock(struct claws_mailmbox_folder * folder);
+
+int claws_mailmbox_write_unlock(struct claws_mailmbox_folder * folder);
+
+int claws_mailmbox_read_lock(struct claws_mailmbox_folder * folder);
+
+int claws_mailmbox_read_unlock(struct claws_mailmbox_folder * folder);
+
+
+/* memory map */
+
+int claws_mailmbox_map(struct claws_mailmbox_folder * folder);
+
+void claws_mailmbox_unmap(struct claws_mailmbox_folder * folder);
+
+void claws_mailmbox_sync(struct claws_mailmbox_folder * folder);
+
+
+/* open & close file */
+
+int claws_mailmbox_open(struct claws_mailmbox_folder * folder);
+
+void claws_mailmbox_close(struct claws_mailmbox_folder * folder);
+
+
+/* validate cache */
+
+int claws_mailmbox_validate_write_lock(struct claws_mailmbox_folder * folder);
+
+int claws_mailmbox_validate_read_lock(struct claws_mailmbox_folder * folder);
+
+
+/* fetch message */
+
+int claws_mailmbox_fetch_msg_no_lock(struct claws_mailmbox_folder * folder,
+ uint32_t num, const char ** result,
+ size_t * result_len);
+
+int claws_mailmbox_fetch_msg_headers_no_lock(struct claws_mailmbox_folder * folder,
+ uint32_t num, const char ** result,
+ size_t * result_len);
+
+/* append message */
+
+int
+claws_mailmbox_append_message_list_no_lock(struct claws_mailmbox_folder * folder,
+ carray * append_tab);
+
+int claws_mailmbox_expunge_no_lock(struct claws_mailmbox_folder * folder);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/plugins/mailmbox/mailmbox_folder.c b/src/plugins/mailmbox/mailmbox_folder.c
@@ -0,0 +1,1142 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2003 Hiroyuki Yamamoto
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+# include "claws-features.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "defs.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#undef MEASURE_TIME
+
+#ifdef MEASURE_TIME
+# include <sys/time.h>
+#endif
+
+#include "folder.h"
+#include "procmsg.h"
+#include "procheader.h"
+#include "statusbar.h"
+#include "utils.h"
+#include "gtkutils.h"
+#include "localfolder.h"
+#include "mailmbox.h"
+#include "mailmbox_folder.h"
+#include "mailmbox_parse.h"
+
+#define MAILMBOX_CACHE_DIR "mailmboxcache"
+
+static Folder *s_claws_mailmbox_folder_new(const gchar *name, const gchar *path);
+
+static void claws_mailmbox_folder_destroy(Folder *folder);
+
+static FolderItem *claws_mailmbox_folder_item_new(Folder *folder);
+
+static void claws_mailmbox_folder_item_destroy(Folder *folder, FolderItem *_item);
+
+static gchar *claws_mailmbox_item_get_path(Folder *folder, FolderItem *item);
+
+static gint claws_mailmbox_get_num_list(Folder *folder, FolderItem *item,
+ GSList **list, gboolean *old_uids_valid);
+
+static MsgInfo *claws_mailmbox_get_msginfo(Folder *folder,
+ FolderItem *item, gint num);
+
+static GSList *claws_mailmbox_get_msginfos(Folder *folder, FolderItem *item,
+ GSList *msgnum_list);
+
+static gchar *s_claws_mailmbox_fetch_msg(Folder *folder, FolderItem *item, gint num);
+
+static gint claws_mailmbox_add_msg(Folder *folder, FolderItem *dest,
+ const gchar *file, MsgFlags *flags);
+
+static gint claws_mailmbox_add_msgs(Folder *folder, FolderItem *dest,
+ GSList *file_list,
+ GHashTable *relation);
+
+static gint s_claws_mailmbox_copy_msg(Folder *folder,
+ FolderItem *dest, MsgInfo *msginfo);
+
+static gint claws_mailmbox_copy_msgs(Folder *folder, FolderItem *dest,
+ MsgInfoList *msglist, GHashTable *relation);
+
+static gint claws_mailmbox_remove_msg(Folder *folder, FolderItem *item, gint num);
+static gint claws_mailmbox_remove_msgs( Folder *folder, FolderItem *item, MsgInfoList *msglist, GRelation *relation );
+static gint claws_mailmbox_remove_all_msg(Folder *folder, FolderItem *item);
+
+static FolderItem *claws_mailmbox_create_folder(Folder *folder, FolderItem *parent,
+ const gchar *name);
+
+static gboolean claws_mailmbox_scan_required(Folder *folder, FolderItem *_item);
+
+static gint claws_mailmbox_rename_folder(Folder *folder,
+ FolderItem *item, const gchar *name);
+
+static gint claws_mailmbox_remove_folder(Folder *folder, FolderItem *item);
+
+static gint claws_mailmbox_create_tree(Folder *folder);
+
+static gint claws_mailmbox_folder_item_close(Folder *folder, FolderItem *item);
+
+static FolderClass claws_mailmbox_class;
+
+static gchar * get_cache_dir(void)
+{
+ static gchar *mbox_cache_dir = NULL;
+
+ if (!mbox_cache_dir)
+ mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
+ MAILMBOX_CACHE_DIR, NULL);
+
+ return mbox_cache_dir;
+}
+
+
+FolderClass *claws_mailmbox_get_class(void)
+{
+ if (claws_mailmbox_class.idstr == NULL) {
+ claws_mailmbox_class.type = F_MBOX;
+ claws_mailmbox_class.idstr = "mailmbox";
+ claws_mailmbox_class.uistr = "mbox";
+
+ /* Folder functions */
+ claws_mailmbox_class.new_folder = s_claws_mailmbox_folder_new;
+ claws_mailmbox_class.destroy_folder = claws_mailmbox_folder_destroy;
+ claws_mailmbox_class.set_xml = folder_local_set_xml;
+ claws_mailmbox_class.get_xml = folder_local_get_xml;
+ claws_mailmbox_class.create_tree = claws_mailmbox_create_tree;
+
+ /* FolderItem functions */
+ claws_mailmbox_class.item_new = claws_mailmbox_folder_item_new;
+ claws_mailmbox_class.item_destroy = claws_mailmbox_folder_item_destroy;
+ claws_mailmbox_class.item_get_path = claws_mailmbox_item_get_path;
+ claws_mailmbox_class.create_folder = claws_mailmbox_create_folder;
+ claws_mailmbox_class.rename_folder = claws_mailmbox_rename_folder;
+ claws_mailmbox_class.remove_folder = claws_mailmbox_remove_folder;
+ claws_mailmbox_class.close = claws_mailmbox_folder_item_close;
+ claws_mailmbox_class.get_num_list = claws_mailmbox_get_num_list;
+ claws_mailmbox_class.scan_required = claws_mailmbox_scan_required;
+
+ /* Message functions */
+ claws_mailmbox_class.get_msginfo = claws_mailmbox_get_msginfo;
+ claws_mailmbox_class.get_msginfos = claws_mailmbox_get_msginfos;
+ claws_mailmbox_class.fetch_msg = s_claws_mailmbox_fetch_msg;
+ claws_mailmbox_class.add_msg = claws_mailmbox_add_msg;
+ claws_mailmbox_class.add_msgs = claws_mailmbox_add_msgs;
+ claws_mailmbox_class.copy_msg = s_claws_mailmbox_copy_msg;
+ claws_mailmbox_class.copy_msgs = claws_mailmbox_copy_msgs;
+ claws_mailmbox_class.remove_msg = claws_mailmbox_remove_msg;
+ claws_mailmbox_class.remove_msgs = claws_mailmbox_remove_msgs;
+ claws_mailmbox_class.remove_all_msg = claws_mailmbox_remove_all_msg;
+ }
+ return &claws_mailmbox_class;
+}
+
+
+static void claws_mailmbox_folder_init(Folder *folder,
+ const gchar *name, const gchar *path)
+{
+ folder_local_folder_init(folder, name, path);
+}
+
+static Folder *s_claws_mailmbox_folder_new(const gchar *name, const gchar *path)
+{
+ Folder *folder;
+
+ folder = (Folder *)g_new0(MAILMBOXFolder, 1);
+ folder->klass = &claws_mailmbox_class;
+ claws_mailmbox_folder_init(folder, name, path);
+
+ return folder;
+}
+
+static void claws_mailmbox_folder_destroy(Folder *folder)
+{
+ folder_local_folder_destroy(LOCAL_FOLDER(folder));
+}
+
+typedef struct _MAILMBOXFolderItem MAILMBOXFolderItem;
+struct _MAILMBOXFolderItem
+{
+ FolderItem item;
+ guint old_max_uid;
+ struct claws_mailmbox_folder * mbox;
+};
+
+static FolderItem *claws_mailmbox_folder_item_new(Folder *folder)
+{
+ MAILMBOXFolderItem *item;
+
+ item = g_new0(MAILMBOXFolderItem, 1);
+ item->mbox = NULL;
+ item->old_max_uid = 0;
+
+ return (FolderItem *)item;
+}
+
+#define MAX_UID_FILE "max-uid"
+
+static void read_max_uid_value(FolderItem *item, guint * pmax_uid)
+{
+ gchar * path;
+ gchar * file;
+ FILE * f;
+ guint max_uid;
+ size_t r;
+
+ path = folder_item_get_path(item);
+ file = g_strconcat(path, G_DIR_SEPARATOR_S, MAX_UID_FILE, NULL);
+ g_free(path);
+
+ f = fopen(file, "r");
+ g_free(file);
+ if (f == NULL)
+ return;
+ r = fread(&max_uid, sizeof(max_uid), 1, f);
+ if (r == 0) {
+ fclose(f);
+ return;
+ }
+
+ fclose(f);
+
+ * pmax_uid = max_uid;
+}
+
+static void write_max_uid_value(FolderItem *item, guint max_uid)
+{
+ gchar * path;
+ gchar * file;
+ FILE * f;
+ size_t r;
+
+ path = folder_item_get_path(item);
+ file = g_strconcat(path, G_DIR_SEPARATOR_S, MAX_UID_FILE, NULL);
+ g_free(path);
+
+ f = fopen(file, "w");
+ g_free(file);
+ if (f == NULL)
+ return;
+ r = fwrite(&max_uid, sizeof(max_uid), 1, f);
+ if (r == 0) {
+ fclose(f);
+ return;
+ }
+
+ fclose(f);
+}
+
+static void claws_mailmbox_folder_item_destroy(Folder *folder, FolderItem *_item)
+{
+ MAILMBOXFolderItem *item = (MAILMBOXFolderItem *)_item;
+
+ g_return_if_fail(item != NULL);
+
+ if (item->mbox != NULL) {
+ write_max_uid_value(_item, item->mbox->mb_written_uid);
+ claws_mailmbox_done(item->mbox);
+ }
+ g_free(_item);
+}
+
+static gint claws_mailmbox_folder_item_close(Folder *folder, FolderItem *item_)
+{
+ MAILMBOXFolderItem *item = (MAILMBOXFolderItem *)item_;
+
+ g_return_val_if_fail(folder->klass->type == F_MBOX, -1);
+ g_return_val_if_fail(item != NULL, -1);
+ g_return_val_if_fail(item->mbox != NULL, -1);
+
+ return -claws_mailmbox_expunge(item->mbox);
+}
+
+static void claws_mailmbox_folder_create_parent(const gchar * path)
+{
+ if (!is_file_exist(path)) {
+ gchar * new_path;
+
+ new_path = g_path_get_dirname(path);
+ if (new_path[strlen(new_path) - 1] == G_DIR_SEPARATOR)
+ new_path[strlen(new_path) - 1] = '\0';
+
+ if (!is_dir_exist(new_path))
+ make_dir_hier(new_path);
+ g_free(new_path);
+
+ }
+}
+
+static gchar * claws_mailmbox_folder_get_path(Folder *folder, FolderItem *item)
+{
+ gchar *folder_path;
+ gchar *path;
+
+ g_return_val_if_fail(item != NULL, NULL);
+
+ if (item->path && item->path[0] == G_DIR_SEPARATOR) {
+ claws_mailmbox_folder_create_parent(item->path);
+ return g_strdup(item->path);
+ }
+
+ folder_path = g_strdup(LOCAL_FOLDER(item->folder)->rootpath);
+ g_return_val_if_fail(folder_path != NULL, NULL);
+
+ if (folder_path[0] == G_DIR_SEPARATOR) {
+ if (item->path) {
+ path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
+ item->path, NULL);
+ }
+ else
+ path = g_strdup(folder_path);
+ } else {
+ if (item->path)
+ path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
+ folder_path, G_DIR_SEPARATOR_S,
+ item->path, NULL);
+ else
+ path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
+ folder_path, NULL);
+ }
+
+ g_free(folder_path);
+
+ claws_mailmbox_folder_create_parent(path);
+
+ return path;
+}
+
+static int claws_mailmbox_item_sync(FolderItem *_item, int validate_uid)
+{
+ MAILMBOXFolderItem *item = (MAILMBOXFolderItem *)_item;
+ int r;
+
+ if (item->mbox == NULL) {
+ guint written_uid;
+ gchar * path;
+
+ written_uid = 0;
+ read_max_uid_value(_item, &written_uid);
+ path = claws_mailmbox_folder_get_path(_item->folder, _item);
+ r = claws_mailmbox_init(path, 0, 0, written_uid, &item->mbox);
+ debug_print("init %d: %p\n", r, item->mbox);
+ g_free(path);
+ if (r != MAILMBOX_NO_ERROR)
+ return -1;
+ }
+
+ if (!validate_uid) {
+ r = claws_mailmbox_validate_read_lock(item->mbox);
+ if (r != MAILMBOX_NO_ERROR) {
+ debug_print("read lock: %d\n", r);
+ goto err;
+ }
+
+ claws_mailmbox_read_unlock(item->mbox);
+ }
+ else {
+ r = claws_mailmbox_validate_write_lock(item->mbox);
+ if (r != MAILMBOX_NO_ERROR) {
+ debug_print("write lock: %d\n", r);
+ goto err;
+ }
+
+ if (item->mbox->mb_written_uid < item->mbox->mb_max_uid) {
+ r = claws_mailmbox_expunge_no_lock(item->mbox);
+ if (r != MAILMBOX_NO_ERROR)
+ goto unlock;
+ }
+ claws_mailmbox_write_unlock(item->mbox);
+ }
+
+ return 0;
+
+ unlock:
+ claws_mailmbox_write_unlock(item->mbox);
+ err:
+ return -1;
+}
+
+static struct claws_mailmbox_folder * get_mbox(FolderItem *_item, int validate_uid)
+{
+ MAILMBOXFolderItem *item = (MAILMBOXFolderItem *)_item;
+
+ claws_mailmbox_item_sync(_item, validate_uid);
+
+ return item->mbox;
+}
+
+static gint claws_mailmbox_get_num_list(Folder *folder, FolderItem *item,
+ GSList **list, gboolean *old_uids_valid)
+{
+ gint nummsgs = 0;
+ guint i;
+ struct claws_mailmbox_folder * mbox;
+
+ g_return_val_if_fail(item != NULL, -1);
+
+ debug_print("mbox_get_last_num(): Scanning %s ...\n", item->path);
+
+ *old_uids_valid = TRUE;
+
+ mbox = get_mbox(item, 1);
+ if (mbox == NULL)
+ return -1;
+
+ for(i = 0 ; i < carray_count(mbox->mb_tab) ; i ++) {
+ struct claws_mailmbox_msg_info * msg;
+
+ msg = carray_get(mbox->mb_tab, i);
+ if (msg != NULL) {
+ *list = g_slist_prepend(*list,
+ GINT_TO_POINTER(msg->msg_uid));
+ nummsgs ++;
+ }
+ }
+
+ return nummsgs;
+}
+
+static gchar *s_claws_mailmbox_fetch_msg(Folder *folder, FolderItem *item, gint num)
+{
+ gchar *path;
+ gchar *file;
+ int r;
+ struct claws_mailmbox_folder * mbox;
+ const char * data;
+ size_t len;
+ FILE * f;
+ mode_t old_mask;
+
+ g_return_val_if_fail(item != NULL, NULL);
+ g_return_val_if_fail(num > 0, NULL);
+
+ mbox = get_mbox(item, 0);
+ if (mbox == NULL)
+ return NULL;
+
+ path = folder_item_get_path(item);
+ if (!is_dir_exist(path))
+ make_dir_hier(path);
+ file = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
+ g_free(path);
+ if (is_file_exist(file)) {
+ return file;
+ }
+
+ r = claws_mailmbox_fetch_msg(mbox, num, &data, &len);
+ if (r != MAILMBOX_NO_ERROR)
+ goto free;
+
+ old_mask = umask(0077);
+ f = fopen(file, "w");
+ umask(old_mask);
+ if (f == NULL)
+ goto free;
+
+ r = fwrite(data, 1, len, f);
+ if (r == 0)
+ goto close;
+
+ fclose(f);
+
+ return file;
+
+ close:
+ fclose(f);
+ unlink(file);
+ free:
+ free(file);
+ return NULL;
+}
+
+static MsgInfo *claws_mailmbox_parse_msg(guint uid,
+ const char * data, size_t len, FolderItem *_item)
+{
+ MsgInfo *msginfo;
+ MsgFlags flags;
+ struct claws_mailmbox_folder * mbox;
+ chashdatum key;
+ chashdatum value;
+ struct claws_mailmbox_msg_info * info;
+ int r;
+ MAILMBOXFolderItem * item = (MAILMBOXFolderItem *)_item;
+
+ flags.perm_flags = MSG_NEW|MSG_UNREAD;
+ flags.tmp_flags = 0;
+
+ g_return_val_if_fail(item != NULL, NULL);
+ g_return_val_if_fail(data != NULL, NULL);
+
+ if (_item->stype == F_QUEUE) {
+ MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
+ } else if (_item->stype == F_DRAFT) {
+ MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
+ }
+
+ mbox = item->mbox;
+
+ key.data = (char *) &uid;
+ key.len = sizeof(uid);
+
+ r = chash_get(mbox->mb_hash, &key, &value);
+ if (r < 0)
+ return NULL;
+
+ info = (struct claws_mailmbox_msg_info *) value.data;
+
+ msginfo = procheader_parse_str(data, flags, FALSE, FALSE);
+ if (!msginfo) return NULL;
+
+ msginfo->msgnum = uid;
+ msginfo->folder = _item;
+ msginfo->size = (goffset)(info->msg_size - info->msg_start_len);
+
+ return msginfo;
+}
+
+static MsgInfo *claws_mailmbox_get_msginfo(Folder *folder,
+ FolderItem *item, gint num)
+{
+ MsgInfo *msginfo;
+ int r;
+ const char * data;
+ size_t len;
+ struct claws_mailmbox_folder * mbox;
+
+ g_return_val_if_fail(item != NULL, NULL);
+ g_return_val_if_fail(num > 0, NULL);
+
+ mbox = get_mbox(item, 0);
+ if (mbox == NULL)
+ goto err;
+
+ r = claws_mailmbox_validate_read_lock(mbox);
+ if (r != MAILMBOX_NO_ERROR)
+ goto err;
+
+ r = claws_mailmbox_fetch_msg_headers_no_lock(mbox, num, &data, &len);
+ if (r != MAILMBOX_NO_ERROR)
+ goto unlock;
+
+ msginfo = claws_mailmbox_parse_msg(num, data, len, item);
+ if (!msginfo)
+ goto unlock;
+
+ claws_mailmbox_read_unlock(mbox);
+
+ return msginfo;
+
+ unlock:
+ claws_mailmbox_read_unlock(mbox);
+ err:
+ return NULL;
+}
+
+static GSList *claws_mailmbox_get_msginfos(Folder *folder, FolderItem *item,
+ GSList *msgnum_list)
+{
+ int r;
+ GSList * cur;
+ GSList * ret;
+ struct claws_mailmbox_folder * mbox;
+
+ g_return_val_if_fail(item != NULL, NULL);
+
+ mbox = get_mbox(item, 0);
+ if (mbox == NULL)
+ goto err;
+
+ r = claws_mailmbox_validate_read_lock(mbox);
+ if (r != MAILMBOX_NO_ERROR)
+ goto err;
+
+ ret = NULL;
+
+ for (cur = msgnum_list ; cur != NULL ; cur = g_slist_next(cur)) {
+ const char * data;
+ size_t len;
+ gint num;
+ MsgInfo *msginfo;
+
+ num = GPOINTER_TO_INT(cur->data);
+
+ r = claws_mailmbox_fetch_msg_headers_no_lock(mbox, num, &data, &len);
+ if (r != MAILMBOX_NO_ERROR)
+ continue;
+
+ msginfo = claws_mailmbox_parse_msg(num, data, len, item);
+ if (!msginfo)
+ continue;
+
+ ret = g_slist_append(ret, msginfo);
+ }
+
+ claws_mailmbox_read_unlock(mbox);
+
+ return ret;
+
+ claws_mailmbox_read_unlock(mbox);
+ err:
+ return NULL;
+}
+
+static gint claws_mailmbox_add_msg(Folder *folder, FolderItem *dest,
+ const gchar *file, MsgFlags *flags)
+{
+ gint ret;
+ GSList file_list;
+ MsgFileInfo fileinfo;
+
+ g_return_val_if_fail(file != NULL, -1);
+
+ fileinfo.msginfo = NULL;
+ fileinfo.file = (gchar *)file;
+ fileinfo.flags = flags;
+ file_list.data = &fileinfo;
+ file_list.next = NULL;
+
+ ret = claws_mailmbox_add_msgs(folder, dest, &file_list, NULL);
+ return ret;
+}
+
+/* ok */
+
+static gint claws_mailmbox_add_msgs(Folder *folder, FolderItem *dest,
+ GSList *file_list,
+ GHashTable *relation)
+{
+ GSList *cur;
+ gint last_num;
+ struct claws_mailmbox_folder * mbox;
+ carray * append_list;
+ struct claws_mailmbox_append_info append_info;
+ int r;
+
+ g_return_val_if_fail(dest != NULL, -1);
+ g_return_val_if_fail(file_list != NULL, -1);
+
+ mbox = get_mbox(dest, 0);
+ if (mbox == NULL) {
+ debug_print("mbox not found\n");
+ return -1;
+ }
+ r = claws_mailmbox_validate_write_lock(mbox);
+ if (r != MAILMBOX_NO_ERROR) {
+ debug_print("claws_mailmbox_validate_write_lock failed with %d\n", r);
+ return -1;
+ }
+ r = claws_mailmbox_expunge_no_lock(mbox);
+ if (r != MAILMBOX_NO_ERROR) {
+ debug_print("claws_mailmbox_expunge_no_lock failed with %d\n", r);
+ goto unlock;
+ }
+
+ last_num = -1;
+
+ append_list = carray_new(1);
+ if (append_list == NULL) {
+ debug_print("append_list is null\n");
+ goto unlock;
+ }
+
+ r = carray_set_size(append_list, 1);
+ if (r < 0) {
+ debug_print("carray_set_size failed with %d\n", r);
+ goto free;
+ }
+
+ carray_set(append_list, 0, &append_info);
+
+ for (cur = file_list; cur != NULL; cur = cur->next) {
+ int fd;
+ struct stat stat_info;
+ char * data;
+ size_t len;
+ struct claws_mailmbox_msg_info * msg;
+ size_t cur_token;
+ MsgFileInfo *fileinfo;
+
+ fileinfo = (MsgFileInfo *)cur->data;
+
+ fd = open(fileinfo->file, O_RDONLY);
+ if (fd == -1) {
+ debug_print("%s couldn't be opened\n", fileinfo->file);
+ goto err;
+ }
+
+ r = fstat(fd, &stat_info);
+ if (r < 0) {
+ debug_print("%s couldn't be stat'ed\n", fileinfo->file);
+ goto close;
+ }
+
+ len = stat_info.st_size;
+ data = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (data == MAP_FAILED) {
+ debug_print("mmap failed\n");
+ goto close;
+ }
+
+ append_info.ai_message = data;
+ append_info.ai_size = len;
+
+ cur_token = mbox->mb_mapping_size;
+
+ r = claws_mailmbox_append_message_list_no_lock(mbox, append_list);
+ if (r != MAILMBOX_NO_ERROR) {
+ debug_print("claws_mailmbox_append_message_list_no_lock failed with %d\n", r);
+ goto unmap;
+ }
+
+ munmap(data, len);
+ close(fd);
+
+ r = claws_mailmbox_parse_additionnal(mbox, &cur_token);
+ if (r != MAILMBOX_NO_ERROR) {
+ debug_print("claws_mailmbox_parse_additionnal failed with %d\n", r);
+ goto unlock;
+ }
+
+ msg = carray_get(mbox->mb_tab, carray_count(mbox->mb_tab) - 1);
+
+ if (relation != NULL)
+ g_hash_table_insert(relation,
+ fileinfo->msginfo != NULL ?
+ (gpointer) fileinfo->msginfo :
+ (gpointer) fileinfo,
+ GINT_TO_POINTER(msg->msg_uid));
+
+ last_num = msg->msg_uid;
+
+ continue;
+
+ unmap:
+ munmap(data, len);
+ close:
+ close(fd);
+ err:
+ continue;
+ }
+
+ claws_mailmbox_sync(mbox);
+
+ carray_free(append_list);
+ claws_mailmbox_write_unlock(mbox);
+
+ return last_num;
+
+ free:
+ carray_free(append_list);
+ unlock:
+ claws_mailmbox_write_unlock(mbox);
+return -1;
+}
+
+static gint s_claws_mailmbox_copy_msg(Folder *folder,
+ FolderItem *dest, MsgInfo *msginfo)
+{
+ GSList msglist;
+
+ g_return_val_if_fail(msginfo != NULL, -1);
+
+ msglist.data = msginfo;
+ msglist.next = NULL;
+
+ return claws_mailmbox_copy_msgs(folder, dest, &msglist, NULL);
+}
+
+static gint claws_mailmbox_copy_msgs(Folder *folder, FolderItem *dest,
+ MsgInfoList *msglist, GHashTable *relation)
+{
+ MsgInfo *msginfo;
+ GSList *file_list;
+ gint ret;
+
+ g_return_val_if_fail(folder != NULL, -1);
+ g_return_val_if_fail(dest != NULL, -1);
+ g_return_val_if_fail(msglist != NULL, -1);
+
+ msginfo = (MsgInfo *)msglist->data;
+ g_return_val_if_fail(msginfo->folder != NULL, -1);
+
+ file_list = procmsg_get_message_file_list(msglist);
+ g_return_val_if_fail(file_list != NULL, -1);
+
+ ret = claws_mailmbox_add_msgs(folder, dest, file_list, relation);
+
+ procmsg_message_file_list_free(file_list);
+
+ return ret;
+}
+
+
+static gint claws_mailmbox_remove_msg(Folder *folder, FolderItem *item, gint num)
+{
+ struct claws_mailmbox_folder * mbox;
+ int r;
+
+ g_return_val_if_fail(item != NULL, -1);
+
+ mbox = get_mbox(item, 0);
+ if (mbox == NULL)
+ return -1;
+
+ r = claws_mailmbox_delete_msg(mbox, num);
+ if (r != MAILMBOX_NO_ERROR)
+ return -1;
+
+ return 0;
+}
+
+static gint
+claws_mailmbox_remove_msgs( Folder *folder, FolderItem *item,
+ MsgInfoList *msglist, GRelation *relation )
+{
+ struct claws_mailmbox_folder *mbox;
+ int r;
+ gint total = 0, curnum = 0;
+
+ g_return_val_if_fail( item!=NULL, -1 );
+ mbox=get_mbox(item,0);
+ g_return_val_if_fail( mbox!=NULL, -1 );
+
+ total = g_slist_length(msglist);
+ if (total > 100) {
+ statusbar_print_all(_("Deleting messages..."));
+ }
+
+ MsgInfoList *cur;
+ for( cur=msglist; cur; cur=cur->next )
+ {
+ MsgInfo *msginfo=(MsgInfo*) cur->data;
+ if( !msginfo )
+ {
+ continue;
+ }
+ if( MSG_IS_MOVE(msginfo->flags) && MSG_IS_MOVE_DONE(msginfo->flags) )
+ {
+ msginfo->flags.tmp_flags&=~MSG_MOVE_DONE;
+ continue;
+ }
+ if (total > 100) {
+ statusbar_progress_all(curnum, total, 100);
+ if (curnum % 100 == 0)
+ GTK_EVENTS_FLUSH();
+ curnum++;
+ }
+ claws_mailmbox_delete_msg(mbox,msginfo->msgnum);
+ }
+
+ /* Fix for bug 1434
+ */
+ claws_mailmbox_expunge(mbox);
+ if (total > 100) {
+ statusbar_progress_all(0,0,0);
+ statusbar_pop_all();
+ }
+
+ return r;
+}
+
+
+static gint claws_mailmbox_remove_all_msg(Folder *folder, FolderItem *item)
+{
+ struct claws_mailmbox_folder * mbox;
+ int r;
+ guint i;
+
+ g_return_val_if_fail(item != NULL, -1);
+
+ mbox = get_mbox(item, 0);
+ if (mbox == NULL)
+ return -1;
+
+ for(i = 0 ; i < carray_count(mbox->mb_tab) ; i ++) {
+ struct claws_mailmbox_msg_info * msg;
+
+ msg = carray_get(mbox->mb_tab, i);
+ if (msg == NULL)
+ continue;
+
+ r = claws_mailmbox_delete_msg(mbox, msg->msg_uid);
+ if (r != MAILMBOX_NO_ERROR)
+ continue;
+ }
+
+ return 0;
+}
+
+
+static gchar * claws_mailmbox_get_new_path(FolderItem * parent, gchar * name)
+{
+ gchar * path;
+
+ if (strchr(name, G_DIR_SEPARATOR) == NULL) {
+ if (parent->path != NULL)
+ path = g_strconcat(parent->path, ".sbd", G_DIR_SEPARATOR_S, name, NULL);
+ else
+ path = g_strdup(name);
+ }
+ else
+ path = g_strdup(name);
+
+ return path;
+}
+
+static gchar * claws_mailmbox_get_folderitem_name(gchar * name)
+{
+ gchar * foldername;
+
+ foldername = g_path_get_basename(name);
+
+ return foldername;
+}
+
+static FolderItem *claws_mailmbox_create_folder(Folder *folder, FolderItem *parent,
+ const gchar *name)
+{
+ gchar * path;
+ FolderItem *new_item;
+ gchar * foldername;
+
+ g_return_val_if_fail(folder != NULL, NULL);
+ g_return_val_if_fail(parent != NULL, NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+
+ path = claws_mailmbox_get_new_path(parent, (gchar *) name);
+
+ foldername = claws_mailmbox_get_folderitem_name((gchar *) name);
+
+ new_item = folder_item_new(folder, foldername, path);
+ folder_item_append(parent, new_item);
+
+ if (!strcmp(name, "inbox")) {
+ new_item->stype = F_INBOX;
+ new_item->folder->inbox = new_item;
+ } else if (!strcmp(name, "outbox")) {
+ new_item->stype = F_OUTBOX;
+ new_item->folder->outbox = new_item;
+ } else if (!strcmp(name, "draft")) {
+ new_item->stype = F_DRAFT;
+ new_item->folder->draft = new_item;
+ } else if (!strcmp(name, "queue")) {
+ new_item->stype = F_QUEUE;
+ new_item->folder->queue = new_item;
+ } else if (!strcmp(name, "trash")) {
+ new_item->stype = F_TRASH;
+ new_item->folder->trash = new_item;
+ }
+
+ g_free(foldername);
+ g_free(path);
+
+ return new_item;
+}
+
+
+
+static gboolean claws_mailmbox_scan_required(Folder *folder, FolderItem *_item)
+{
+ struct claws_mailmbox_folder * mbox;
+ MAILMBOXFolderItem *item = (MAILMBOXFolderItem *)_item;
+ int scan_required;
+
+ g_return_val_if_fail(folder != NULL, FALSE);
+ g_return_val_if_fail(item != NULL, FALSE);
+
+ if (item->item.path == NULL)
+ return FALSE;
+
+ mbox = get_mbox(_item, 0);
+ if (mbox == NULL)
+ return FALSE;
+
+ scan_required = (item->old_max_uid != item->mbox->mb_max_uid);
+
+ item->old_max_uid = item->mbox->mb_max_uid;
+
+ return scan_required;
+}
+
+
+static gint claws_mailmbox_rename_folder(Folder *folder,
+ FolderItem *item, const gchar *name)
+{
+ gchar * path;
+ gchar * foldername;
+ FolderItem *parent;
+
+ g_return_val_if_fail(folder != NULL, -1);
+ g_return_val_if_fail(item != NULL, -1);
+ g_return_val_if_fail(item->path != NULL, -1);
+ g_return_val_if_fail(name != NULL, -1);
+
+ parent = folder_item_parent(item);
+ g_return_val_if_fail(parent, -1);
+
+ path = claws_mailmbox_get_new_path(parent, (gchar *) name);
+ foldername = claws_mailmbox_get_folderitem_name((gchar *) name);
+
+ if (rename(item->path, path) == -1) {
+ g_free(foldername);
+ g_free(path);
+ debug_print("Cannot rename folder item\n");
+
+ return -1;
+ }
+ else {
+ g_free(item->name);
+ g_free(item->path);
+ item->path = path;
+ item->name = foldername;
+
+ return 0;
+ }
+}
+
+static gint claws_mailmbox_remove_folder(Folder *folder, FolderItem *item)
+{
+ g_return_val_if_fail(folder != NULL, -1);
+ g_return_val_if_fail(item != NULL, -1);
+ g_return_val_if_fail(item->path != NULL, -1);
+
+ folder_item_remove(item);
+ return 0;
+}
+
+#define MAKE_DIR_IF_NOT_EXIST(dir) \
+{ \
+ if (!is_dir_exist(dir)) { \
+ if (is_file_exist(dir)) { \
+ debug_print("File `%s' already exists.\n" \
+ "Can't create folder.", dir); \
+ return -1; \
+ } \
+ if (mkdir(dir, S_IRWXU) < 0) { \
+ FILE_OP_ERROR(dir, "mkdir"); \
+ return -1; \
+ } \
+ if (chmod(dir, S_IRWXU) < 0) \
+ FILE_OP_ERROR(dir, "chmod"); \
+ } \
+}
+
+static gint claws_mailmbox_create_tree(Folder *folder)
+{
+ gchar *rootpath;
+
+ g_return_val_if_fail(folder != NULL, -1);
+
+ CHDIR_RETURN_VAL_IF_FAIL(get_home_dir(), -1);
+ rootpath = LOCAL_FOLDER(folder)->rootpath;
+ MAKE_DIR_IF_NOT_EXIST(rootpath);
+ CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1);
+
+ return 0;
+}
+
+#undef MAKE_DIR_IF_NOT_EXIST
+
+
+static char * quote_mailbox(char * mb)
+{
+ char path[PATH_MAX];
+ char * str;
+ size_t remaining;
+ char * p;
+
+ remaining = sizeof(path) - 1;
+ p = path;
+
+ while (* mb != 0) {
+
+ if (((* mb >= 'a') && (* mb <= 'z')) ||
+ ((* mb >= 'A') && (* mb <= 'Z')) ||
+ ((* mb >= '0') && (* mb <= '9'))) {
+ if (remaining < 1)
+ return NULL;
+ * p = * mb;
+ p ++;
+ remaining --;
+ }
+ else {
+ if (remaining < 3)
+ return NULL;
+ * p = '%';
+ p ++;
+ snprintf(p, 3, "%02x", (unsigned char) (* mb));
+ p += 2;
+ }
+ mb ++;
+ }
+
+ * p = 0;
+
+ str = strdup(path);
+ if (str == NULL)
+ return NULL;
+
+ return str;
+}
+
+static gchar *claws_mailmbox_item_get_path(Folder *folder, FolderItem *item)
+{
+ gchar *itempath, *path;
+ gchar * folderpath;
+
+ if (item->path == NULL)
+ return NULL;
+
+ if (folder->name == NULL)
+ return NULL;
+
+ folderpath = quote_mailbox(folder->name);
+ if (folderpath == NULL)
+ return NULL;
+ itempath = quote_mailbox(item->path);
+ if (itempath == NULL) {
+ free(folderpath);
+ return NULL;
+ }
+ path = g_strconcat(get_cache_dir(),
+ G_DIR_SEPARATOR_S, folderpath,
+ G_DIR_SEPARATOR_S, itempath, NULL);
+ free(itempath);
+ free(folderpath);
+
+ return path;
+}
diff --git a/src/plugins/mailmbox/mailmbox_folder.h b/src/plugins/mailmbox/mailmbox_folder.h
@@ -0,0 +1,39 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2003 Hiroyuki Yamamoto
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __MAILMBOX_FOLDER_H__
+#define __MAILMBOX_FOLDER_H__
+
+#include <glib.h>
+
+#include "folder.h"
+#include "localfolder.h"
+
+typedef struct _MAILMBOXFolder MAILMBOXFolder;
+
+#define MAILMBOX_FOLDER(obj) ((MAILMBOXFolder *)obj)
+
+struct _MAILMBOXFolder
+{
+ LocalFolder lfolder;
+};
+
+FolderClass *claws_mailmbox_get_class (void);
+
+#endif /* __MH_H__ */
diff --git a/src/plugins/mailmbox/mailmbox_gtk.deps b/src/plugins/mailmbox/mailmbox_gtk.deps
@@ -0,0 +1 @@
+mailmbox
diff --git a/src/plugins/mailmbox/mailmbox_parse.c b/src/plugins/mailmbox/mailmbox_parse.c
@@ -0,0 +1,624 @@
+/*
+ * libEtPan! -- a mail stuff library
+ *
+ * Copyright (C) 2001, 2002 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#include "mailmbox_parse.h"
+
+#include "mailmbox.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define UID_HEADER "X-LibEtPan-UID:"
+
+#include "utils.h"
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+enum {
+ UNSTRUCTURED_START,
+ UNSTRUCTURED_CR,
+ UNSTRUCTURED_LF,
+ UNSTRUCTURED_WSP,
+ UNSTRUCTURED_OUT
+};
+
+static inline int
+claws_mailmbox_fields_parse(char * str, size_t length,
+ size_t * index,
+ uint32_t * puid,
+ size_t * phlen)
+{
+ size_t cur_token;
+ int r;
+ size_t hlen;
+ size_t uid;
+ int end;
+
+ cur_token = * index;
+
+ end = FALSE;
+ uid = 0;
+ while (!end) {
+ size_t begin;
+
+ begin = cur_token;
+
+ r = mailimf_ignore_field_parse(str, length, &cur_token);
+ switch (r) {
+ case MAILIMF_NO_ERROR:
+ if (str[begin] == 'X') {
+
+ if (strncasecmp(str + begin, UID_HEADER, strlen(UID_HEADER)) == 0) {
+ begin += strlen(UID_HEADER);
+
+ while (str[begin] == ' ')
+ begin ++;
+
+ uid = strtoul(str + begin, NULL, 10);
+ }
+ }
+
+ break;
+ case MAILIMF_ERROR_PARSE:
+ default:
+ end = TRUE;
+ break;
+ }
+ }
+
+ hlen = cur_token - * index;
+
+ * phlen = hlen;
+ * puid = uid;
+ * index = cur_token;
+
+ return MAILMBOX_NO_ERROR;
+}
+
+enum {
+ IN_MAIL,
+ FIRST_CR,
+ FIRST_LF,
+ SECOND_CR,
+ SECOND_LF,
+ PARSING_F,
+ PARSING_R,
+ PARSING_O,
+ PARSING_M,
+ OUT_MAIL
+};
+
+
+
+
+static inline int
+claws_mailmbox_single_parse(char * str, size_t length,
+ size_t * index,
+ size_t * pstart,
+ size_t * pstart_len,
+ size_t * pheaders,
+ size_t * pheaders_len,
+ size_t * pbody,
+ size_t * pbody_len,
+ size_t * psize,
+ size_t * ppadding,
+ uint32_t * puid)
+{
+ size_t cur_token;
+ size_t start;
+ size_t start_len;
+ size_t headers;
+ size_t headers_len;
+ size_t body;
+ size_t end;
+ size_t next;
+ size_t message_length;
+ uint32_t uid;
+ int r;
+#if 0
+ int in_mail_data;
+#endif
+#if 0
+ size_t begin;
+#endif
+
+ int state;
+
+ cur_token = * index;
+
+ if (cur_token >= length)
+ return MAILMBOX_ERROR_PARSE;
+
+ start = cur_token;
+ start_len = 0;
+ headers = cur_token;
+
+ if (cur_token + 5 < length) {
+ if (strncmp(str + cur_token, "From ", 5) == 0) {
+ cur_token += 5;
+ while (str[cur_token] != '\n') {
+ cur_token ++;
+ if (cur_token >= length)
+ break;
+ }
+ if (cur_token < length) {
+ cur_token ++;
+ headers = cur_token;
+ start_len = headers - start;
+ }
+ }
+ }
+
+ next = length;
+
+ r = claws_mailmbox_fields_parse(str, length, &cur_token,
+ &uid, &headers_len);
+ if (r != MAILMBOX_NO_ERROR)
+ return r;
+
+ /* save position */
+#if 0
+ begin = cur_token;
+#endif
+
+ mailimf_crlf_parse(str, length, &cur_token);
+
+#if 0
+ if (str[cur_token] == 'F') {
+ printf("start !\n");
+ printf("%50.50s\n", str + cur_token);
+ getchar();
+ }
+#endif
+
+ body = cur_token;
+
+ /* restore position */
+ /* cur_token = begin; */
+
+ state = FIRST_LF;
+
+ end = length;
+
+#if 0
+ in_mail_data = 0;
+#endif
+ while (state != OUT_MAIL) {
+
+ if (cur_token >= length) {
+ if (state == IN_MAIL)
+ end = length;
+ next = length;
+ break;
+ }
+
+ switch(state) {
+ case IN_MAIL:
+ switch(str[cur_token]) {
+ case '\r':
+ state = FIRST_CR;
+ break;
+ case '\n':
+ state = FIRST_LF;
+ break;
+ case 'F':
+ if (cur_token == body) {
+ end = cur_token;
+ next = cur_token;
+ state = PARSING_F;
+ }
+ break;
+#if 0
+ default:
+ in_mail_data = 1;
+ break;
+#endif
+ }
+ break;
+
+ case FIRST_CR:
+ end = cur_token;
+ switch(str[cur_token]) {
+ case '\r':
+ state = SECOND_CR;
+ break;
+ case '\n':
+ state = FIRST_LF;
+ break;
+ default:
+ state = IN_MAIL;
+#if 0
+ in_mail_data = 1;
+#endif
+ break;
+ }
+ break;
+
+ case FIRST_LF:
+ end = cur_token;
+ switch(str[cur_token]) {
+ case '\r':
+ state = SECOND_CR;
+ break;
+ case '\n':
+ state = SECOND_LF;
+ break;
+ default:
+ state = IN_MAIL;
+#if 0
+ in_mail_data = 1;
+#endif
+ break;
+ }
+ break;
+
+ case SECOND_CR:
+ switch(str[cur_token]) {
+ case '\r':
+ end = cur_token;
+ break;
+ case '\n':
+ state = SECOND_LF;
+ break;
+ case 'F':
+ next = cur_token;
+ state = PARSING_F;
+ break;
+ default:
+ state = IN_MAIL;
+#if 0
+ in_mail_data = 1;
+#endif
+ break;
+ }
+ break;
+
+ case SECOND_LF:
+ switch(str[cur_token]) {
+ case '\r':
+ state = SECOND_CR;
+ break;
+ case '\n':
+ end = cur_token;
+ break;
+ case 'F':
+ next = cur_token;
+ state = PARSING_F;
+ break;
+ default:
+ state = IN_MAIL;
+#if 0
+ in_mail_data = 1;
+#endif
+ break;
+ }
+ break;
+
+ case PARSING_F:
+ switch(str[cur_token]) {
+ case 'r':
+ state = PARSING_R;
+ break;
+ default:
+ state = IN_MAIL;
+#if 0
+ in_mail_data = 1;
+#endif
+ break;
+ }
+ break;
+
+ case PARSING_R:
+ switch(str[cur_token]) {
+ case 'o':
+ state = PARSING_O;
+ break;
+ default:
+ state = IN_MAIL;
+#if 0
+ in_mail_data = 1;
+#endif
+ break;
+ }
+ break;
+
+ case PARSING_O:
+ switch(str[cur_token]) {
+ case 'm':
+ state = PARSING_M;
+ break;
+ default:
+ state = IN_MAIL;
+#if 0
+ in_mail_data = 1;
+#endif
+ break;
+ }
+ break;
+
+ case PARSING_M:
+ switch(str[cur_token]) {
+ case ' ':
+ state = OUT_MAIL;
+ break;
+ default:
+ state = IN_MAIL;
+ break;
+ }
+ break;
+ }
+
+ cur_token ++;
+ }
+
+ message_length = end - start;
+
+ * pstart = start;
+ * pstart_len = start_len;
+ * pheaders = headers;
+ * pheaders_len = headers_len;
+ * pbody = body;
+ * pbody_len = end - body;
+ * psize = message_length;
+ * ppadding = next - end;
+ * puid = uid;
+
+ * index = next;
+
+ return MAILMBOX_NO_ERROR;
+}
+
+
+int
+claws_mailmbox_parse_additionnal(struct claws_mailmbox_folder * folder,
+ size_t * index)
+{
+ size_t cur_token;
+
+ size_t start;
+ size_t start_len;
+ size_t headers;
+ size_t headers_len;
+ size_t body;
+ size_t body_len;
+ size_t size;
+ size_t padding;
+ uint32_t uid;
+ int r;
+ int res;
+
+ uint32_t max_uid;
+ uint32_t first_index;
+ unsigned int i;
+ unsigned int j;
+
+ cur_token = * index;
+
+ /* remove temporary UID that we will parse */
+
+ first_index = carray_count(folder->mb_tab);
+
+ for(i = 0 ; i < carray_count(folder->mb_tab) ; i++) {
+ struct claws_mailmbox_msg_info * info;
+
+ info = carray_get(folder->mb_tab, i);
+
+ if (info->msg_start < cur_token) {
+ continue;
+ }
+
+ if (!info->msg_written_uid) {
+ chashdatum key;
+
+ key.data = &info->msg_uid;
+ key.len = sizeof(info->msg_uid);
+
+ chash_delete(folder->mb_hash, &key, NULL);
+ carray_delete_fast(folder->mb_tab, i);
+ claws_mailmbox_msg_info_free(info);
+ if (i < first_index)
+ first_index = i;
+ }
+ }
+
+ /* make a sequence in the table */
+
+ max_uid = folder->mb_written_uid;
+
+ i = 0;
+ j = 0;
+ while (i < carray_count(folder->mb_tab)) {
+ struct claws_mailmbox_msg_info * info;
+
+ info = carray_get(folder->mb_tab, i);
+ if (info != NULL) {
+ carray_set(folder->mb_tab, j, info);
+
+ if (info->msg_uid > max_uid)
+ max_uid = info->msg_uid;
+
+ info->msg_index = j;
+ j ++;
+ }
+ i ++;
+ }
+ carray_set_size(folder->mb_tab, j);
+
+ /* parse content */
+
+ first_index = j;
+
+ while (1) {
+ struct claws_mailmbox_msg_info * info;
+ chashdatum key;
+ chashdatum data;
+
+ r = claws_mailmbox_single_parse(folder->mb_mapping, folder->mb_mapping_size,
+ &cur_token,
+ &start, &start_len,
+ &headers, &headers_len,
+ &body, &body_len,
+ &size, &padding, &uid);
+ if (r == MAILMBOX_NO_ERROR) {
+ /* do nothing */
+ }
+ else if (r == MAILMBOX_ERROR_PARSE) {
+ break;
+ } else {
+ res = r;
+ goto err;
+ }
+
+ key.data = &uid;
+ key.len = sizeof(uid);
+
+ r = chash_get(folder->mb_hash, &key, &data);
+ if (r == 0) {
+ info = data.data;
+
+ if (!info->msg_written_uid) {
+ /* some new mail has been written and override an
+ existing temporary UID */
+
+ chash_delete(folder->mb_hash, &key, NULL);
+ info->msg_uid = 0;
+
+ if (info->msg_index < first_index)
+ first_index = info->msg_index;
+ }
+ else
+ uid = 0;
+ }
+
+ if (uid > max_uid)
+ max_uid = uid;
+
+ r = claws_mailmbox_msg_info_update(folder,
+ start, start_len, headers, headers_len,
+ body, body_len, size, padding, uid);
+ if (r != MAILMBOX_NO_ERROR) {
+ debug_print("claws_mailmbox_msg_info_update failed with %d\n", r);
+ res = r;
+ goto err;
+ }
+ }
+
+ * index = cur_token;
+
+ folder->mb_written_uid = max_uid;
+
+ /* attribute uid */
+
+ for(i = first_index ; i < carray_count(folder->mb_tab) ; i ++) {
+ struct claws_mailmbox_msg_info * info;
+ chashdatum key;
+ chashdatum data;
+
+ info = carray_get(folder->mb_tab, i);
+
+ if (info->msg_uid != 0) {
+ continue;
+ }
+
+ max_uid ++;
+ info->msg_uid = max_uid;
+
+ key.data = &info->msg_uid;
+ key.len = sizeof(info->msg_uid);
+ data.data = info;
+ data.len = 0;
+
+ r = chash_set(folder->mb_hash, &key, &data, NULL);
+ if (r < 0) {
+ debug_print("chash_set failed with %d\n", r);
+ res = MAILMBOX_ERROR_MEMORY;
+ goto err;
+ }
+ }
+
+ folder->mb_max_uid = max_uid;
+
+ return MAILMBOX_NO_ERROR;
+
+ err:
+ return res;
+}
+
+static void flush_uid(struct claws_mailmbox_folder * folder)
+{
+ unsigned int i;
+
+ for(i = 0 ; i < carray_count(folder->mb_tab) ; i++) {
+ struct claws_mailmbox_msg_info * info;
+
+ info = carray_get(folder->mb_tab, i);
+ if (info != NULL)
+ claws_mailmbox_msg_info_free(info);
+ }
+
+ chash_clear(folder->mb_hash);
+ carray_set_size(folder->mb_tab, 0);
+}
+
+int claws_mailmbox_parse(struct claws_mailmbox_folder * folder)
+{
+ int r;
+ int res;
+ size_t cur_token;
+
+ flush_uid(folder);
+
+ cur_token = 0;
+
+ r = claws_mailmbox_parse_additionnal(folder, &cur_token);
+
+ if (r != MAILMBOX_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ return MAILMBOX_NO_ERROR;
+
+ err:
+ return res;
+}
diff --git a/src/plugins/mailmbox/mailmbox_parse.h b/src/plugins/mailmbox/mailmbox_parse.h
@@ -0,0 +1,56 @@
+/*
+ * libEtPan! -- a mail stuff library
+ *
+ * Copyright (C) 2001, 2002 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef MAILMBOX_PARSE_H
+
+#define MAILMBOX_PARSE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "mailmbox_types.h"
+
+int claws_mailmbox_parse(struct claws_mailmbox_folder * folder);
+
+int
+claws_mailmbox_parse_additionnal(struct claws_mailmbox_folder * folder,
+ size_t * index);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/plugins/mailmbox/mailmbox_types.c b/src/plugins/mailmbox/mailmbox_types.c
@@ -0,0 +1,250 @@
+/*
+ * libEtPan! -- a mail stuff library
+ *
+ * Copyright (C) 2001, 2002 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#include "mailmbox_types.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/* *********************************************************************** */
+
+int claws_mailmbox_msg_info_update(struct claws_mailmbox_folder * folder,
+ size_t msg_start, size_t msg_start_len,
+ size_t msg_headers, size_t msg_headers_len,
+ size_t msg_body, size_t msg_body_len,
+ size_t msg_size, size_t msg_padding,
+ uint32_t msg_uid)
+{
+ struct claws_mailmbox_msg_info * info;
+ int res;
+ chashdatum key;
+ chashdatum data;
+ int r;
+
+ key.data = &msg_uid;
+ key.len = sizeof(msg_uid);
+ r = chash_get(folder->mb_hash, &key, &data);
+ if (r < 0) {
+ unsigned int index;
+
+ info = claws_mailmbox_msg_info_new(msg_start, msg_start_len,
+ msg_headers, msg_headers_len,
+ msg_body, msg_body_len, msg_size, msg_padding, msg_uid);
+ if (info == NULL) {
+ res = MAILMBOX_ERROR_MEMORY;
+ goto err;
+ }
+
+ r = carray_add(folder->mb_tab, info, &index);
+ if (r < 0) {
+ claws_mailmbox_msg_info_free(info);
+ res = MAILMBOX_ERROR_MEMORY;
+ goto err;
+ }
+
+ if (msg_uid != 0) {
+ chashdatum key;
+ chashdatum data;
+
+ key.data = &msg_uid;
+ key.len = sizeof(msg_uid);
+ data.data = info;
+ data.len = 0;
+
+ r = chash_set(folder->mb_hash, &key, &data, NULL);
+ if (r < 0) {
+ claws_mailmbox_msg_info_free(info);
+ carray_delete(folder->mb_tab, index);
+ res = MAILMBOX_ERROR_MEMORY;
+ goto err;
+ }
+ }
+
+ info->msg_index = index;
+ }
+ else {
+ info = data.data;
+
+ info->msg_start = msg_start;
+ info->msg_start_len = msg_start_len;
+ info->msg_headers = msg_headers;
+ info->msg_headers_len = msg_headers_len;
+ info->msg_body = msg_body;
+ info->msg_body_len = msg_body_len;
+ info->msg_size = msg_size;
+ info->msg_padding = msg_padding;
+ }
+
+ return MAILMBOX_NO_ERROR;
+
+ err:
+ return res;
+}
+
+
+struct claws_mailmbox_msg_info *
+claws_mailmbox_msg_info_new(size_t msg_start, size_t msg_start_len,
+ size_t msg_headers, size_t msg_headers_len,
+ size_t msg_body, size_t msg_body_len,
+ size_t msg_size, size_t msg_padding,
+ uint32_t msg_uid)
+{
+ struct claws_mailmbox_msg_info * info;
+
+ info = malloc(sizeof(* info));
+ if (info == NULL)
+ return NULL;
+
+ info->msg_index = 0;
+ info->msg_uid = msg_uid;
+ if (msg_uid != 0)
+ info->msg_written_uid = TRUE;
+ else
+ info->msg_written_uid = FALSE;
+ info->msg_deleted = FALSE;
+
+ info->msg_start = msg_start;
+ info->msg_start_len = msg_start_len;
+
+ info->msg_headers = msg_headers;
+ info->msg_headers_len = msg_headers_len;
+
+ info->msg_body = msg_body;
+ info->msg_body_len = msg_body_len;
+
+ info->msg_size = msg_size;
+
+ info->msg_padding = msg_padding;
+
+ return info;
+}
+
+void claws_mailmbox_msg_info_free(struct claws_mailmbox_msg_info * info)
+{
+ free(info);
+}
+
+
+/* append info */
+
+struct claws_mailmbox_append_info *
+claws_mailmbox_append_info_new(const char * ai_message, size_t ai_size)
+{
+ struct claws_mailmbox_append_info * info;
+
+ info = malloc(sizeof(* info));
+ if (info == NULL)
+ return NULL;
+
+ info->ai_message = ai_message;
+ info->ai_size = ai_size;
+
+ return info;
+}
+
+void claws_mailmbox_append_info_free(struct claws_mailmbox_append_info * info)
+{
+ free(info);
+}
+
+struct claws_mailmbox_folder * claws_mailmbox_folder_new(const char * mb_filename)
+{
+ struct claws_mailmbox_folder * folder;
+
+ folder = malloc(sizeof(* folder));
+ if (folder == NULL)
+ goto err;
+
+ strncpy(folder->mb_filename, mb_filename, PATH_MAX);
+
+ folder->mb_mtime = (time_t) -1;
+
+ folder->mb_fd = -1;
+ folder->mb_read_only = TRUE;
+ folder->mb_no_uid = TRUE;
+
+ folder->mb_changed = FALSE;
+ folder->mb_deleted_count = 0;
+
+ folder->mb_mapping = NULL;
+ folder->mb_mapping_size = 0;
+
+ folder->mb_written_uid = 0;
+ folder->mb_max_uid = 0;
+
+ folder->mb_hash = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYKEY);
+ if (folder->mb_hash == NULL)
+ goto free;
+
+ folder->mb_tab = carray_new(128);
+ if (folder->mb_tab == NULL)
+ goto free_hash;
+
+ return folder;
+
+ free_hash:
+ chash_free(folder->mb_hash);
+ free:
+ free(folder);
+ err:
+ return NULL;
+}
+
+void claws_mailmbox_folder_free(struct claws_mailmbox_folder * folder)
+{
+ unsigned int i;
+
+ for(i = 0 ; i < carray_count(folder->mb_tab) ; i++) {
+ struct claws_mailmbox_msg_info * info;
+
+ info = carray_get(folder->mb_tab, i);
+ if (info != NULL)
+ claws_mailmbox_msg_info_free(info);
+ }
+
+ carray_free(folder->mb_tab);
+
+ chash_free(folder->mb_hash);
+
+ free(folder);
+}
diff --git a/src/plugins/mailmbox/mailmbox_types.h b/src/plugins/mailmbox/mailmbox_types.h
@@ -0,0 +1,141 @@
+/*
+ * libEtPan! -- a mail stuff library
+ *
+ * Copyright (C) 2001, 2002 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef MAILMBOX_TYPES_H
+
+#define MAILMBOX_TYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <limits.h>
+
+#include "mailimf.h"
+#include "carray.h"
+#include "chash.h"
+
+enum {
+ MAILMBOX_NO_ERROR = 0,
+ MAILMBOX_ERROR_PARSE,
+ MAILMBOX_ERROR_INVAL,
+ MAILMBOX_ERROR_FILE_NOT_FOUND,
+ MAILMBOX_ERROR_MEMORY,
+ MAILMBOX_ERROR_TEMPORARY_FILE,
+ MAILMBOX_ERROR_FILE,
+ MAILMBOX_ERROR_MSG_NOT_FOUND,
+ MAILMBOX_ERROR_READONLY,
+};
+
+
+struct claws_mailmbox_folder {
+ char mb_filename[PATH_MAX];
+
+ time_t mb_mtime;
+
+ int mb_fd;
+ int mb_read_only;
+ int mb_no_uid;
+
+ int mb_changed;
+ unsigned int mb_deleted_count;
+
+ char * mb_mapping;
+ size_t mb_mapping_size;
+
+ uint32_t mb_written_uid;
+ uint32_t mb_max_uid;
+
+ chash * mb_hash;
+ carray * mb_tab;
+};
+
+struct claws_mailmbox_folder * claws_mailmbox_folder_new(const char * mb_filename);
+void claws_mailmbox_folder_free(struct claws_mailmbox_folder * folder);
+
+
+struct claws_mailmbox_msg_info {
+ unsigned int msg_index;
+ uint32_t msg_uid;
+ int msg_written_uid;
+ int msg_deleted;
+
+ size_t msg_start;
+ size_t msg_start_len;
+
+ size_t msg_headers;
+ size_t msg_headers_len;
+
+ size_t msg_body;
+ size_t msg_body_len;
+
+ size_t msg_size;
+
+ size_t msg_padding;
+};
+
+
+int claws_mailmbox_msg_info_update(struct claws_mailmbox_folder * folder,
+ size_t msg_start, size_t msg_start_len,
+ size_t msg_headers, size_t msg_headers_len,
+ size_t msg_body, size_t msg_body_len,
+ size_t msg_size, size_t msg_padding,
+ uint32_t msg_uid);
+
+struct claws_mailmbox_msg_info *
+claws_mailmbox_msg_info_new(size_t msg_start, size_t msg_start_len,
+ size_t msg_headers, size_t msg_headers_len,
+ size_t msg_body, size_t msg_body_len,
+ size_t msg_size, size_t msg_padding,
+ uint32_t msg_uid);
+
+void claws_mailmbox_msg_info_free(struct claws_mailmbox_msg_info * info);
+
+struct claws_mailmbox_append_info {
+ const char * ai_message;
+ size_t ai_size;
+};
+
+struct claws_mailmbox_append_info *
+claws_mailmbox_append_info_new(const char * ai_message, size_t ai_size);
+
+void claws_mailmbox_append_info_free(struct claws_mailmbox_append_info * info);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/plugins/mailmbox/mmapstring.c b/src/plugins/mailmbox/mmapstring.c
@@ -0,0 +1,525 @@
+/*
+ * libEtPan! -- a mail stuff library
+ *
+ * Copyright (C) 2001, 2002 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#include "mmapstring.h"
+
+#include "chash.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <pthread.h>
+#include <limits.h>
+
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+#define MMAP_STRING_DEFAULT_CEIL (8 * 1024 * 1024)
+
+#define DEFAULT_TMP_PATH "/tmp"
+
+static char tmpdir[PATH_MAX] = DEFAULT_TMP_PATH;
+
+static size_t mmap_string_ceil = MMAP_STRING_DEFAULT_CEIL;
+
+/* MMAPString references */
+
+static pthread_mutex_t mmapstring_lock = PTHREAD_MUTEX_INITIALIZER;
+static chash * mmapstring_hashtable = NULL;
+
+static void mmapstring_hashtable_init()
+{
+ mmapstring_hashtable = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYKEY);
+}
+
+void mmap_string_set_tmpdir(char * directory)
+{
+ strncpy(tmpdir, directory, PATH_MAX);
+ tmpdir[PATH_MAX - 1] = 0;
+}
+
+
+int mmap_string_ref(MMAPString * string)
+{
+ chash * ht;
+ int r;
+ chashdatum key;
+ chashdatum data;
+
+ pthread_mutex_lock(&mmapstring_lock);
+ if (mmapstring_hashtable == NULL) {
+ mmapstring_hashtable_init();
+ }
+ ht = mmapstring_hashtable;
+
+ if (ht == NULL) {
+ pthread_mutex_unlock(&mmapstring_lock);
+ return -1;
+ }
+
+ key.data = &string->str;
+ key.len = sizeof(string->str);
+ data.data = string;
+ data.len = 0;
+
+ r = chash_set(mmapstring_hashtable, &key, &data, NULL);
+ pthread_mutex_unlock(&mmapstring_lock);
+
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int mmap_string_unref(char * str)
+{
+ MMAPString * string;
+ chash * ht;
+ chashdatum key;
+ chashdatum data;
+ int r;
+
+ pthread_mutex_lock(&mmapstring_lock);
+ ht = mmapstring_hashtable;
+
+ if (ht == NULL) {
+ pthread_mutex_unlock(&mmapstring_lock);
+ return -1;
+ }
+
+ key.data = &str;
+ key.len = sizeof(str);
+
+ r = chash_get(ht, &key, &data);
+ if (r < 0)
+ string = NULL;
+ else
+ string = data.data;
+
+ if (string != NULL) {
+ chash_delete(ht, &key, NULL);
+ if (chash_count(ht) == 0) {
+ chash_free(ht);
+ mmapstring_hashtable = NULL;
+ }
+ }
+
+ pthread_mutex_unlock(&mmapstring_lock);
+
+ if (string != NULL) {
+ mmap_string_free(string);
+ return 0;
+ }
+ else
+ return -1;
+}
+
+
+
+/* MMAPString */
+
+#define MY_MAXSIZE ((size_t) -1)
+
+static inline size_t
+nearest_power (size_t base, size_t num)
+{
+ if (num > MY_MAXSIZE / 2) {
+ return MY_MAXSIZE;
+ }
+ else {
+ size_t n = base;
+
+ while (n < num)
+ n <<= 1;
+
+ return n;
+ }
+}
+
+void mmap_string_set_ceil(size_t ceil)
+{
+ mmap_string_ceil = ceil;
+}
+
+/* Strings.
+ */
+
+static MMAPString * mmap_string_realloc_file(MMAPString * string)
+{
+ char * data;
+
+ if (string->fd == -1) {
+ char tmpfilename[PATH_MAX];
+ int fd;
+
+ * tmpfilename = 0;
+ strcat(tmpfilename, tmpdir);
+ strcat(tmpfilename, "/libetpan-mmapstring-XXXXXX");
+
+ fd = mkstemp(tmpfilename);
+ if (fd == -1)
+ return NULL;
+
+ if (unlink(tmpfilename) == -1) {
+ close(fd);
+ return NULL;
+ }
+
+ if (ftruncate(fd, string->allocated_len) == -1) {
+ close(fd);
+ return NULL;
+ }
+
+ data = mmap(NULL, string->allocated_len, PROT_WRITE | PROT_READ,
+ MAP_SHARED, fd, 0);
+
+ if (data == MAP_FAILED) {
+ close(fd);
+ return NULL;
+ }
+
+ if (string->str != NULL)
+ memcpy(data, string->str, string->len + 1);
+
+ string->fd = fd;
+ string->mmapped_size = string->allocated_len;
+ free(string->str);
+ string->str = data;
+ }
+ else {
+ if (munmap(string->str, string->mmapped_size) == -1)
+ return NULL;
+
+ if (ftruncate(string->fd, string->allocated_len) == -1)
+ return NULL;
+
+ data = mmap(NULL, string->allocated_len, PROT_WRITE | PROT_READ,
+ MAP_SHARED, string->fd, 0);
+
+ if (data == MAP_FAILED)
+ return NULL;
+
+ string->mmapped_size = string->allocated_len;
+ string->str = data;
+ }
+
+ return string;
+}
+
+static MMAPString * mmap_string_realloc_memory(MMAPString * string)
+{
+ char * tmp;
+
+ tmp = realloc (string->str, string->allocated_len);
+
+ if (tmp == NULL)
+ string = NULL;
+ else
+ string->str = tmp;
+
+ return string;
+}
+
+static MMAPString *
+mmap_string_maybe_expand (MMAPString* string,
+ size_t len)
+{
+ if (string->len + len >= string->allocated_len)
+ {
+ size_t old_size;
+ MMAPString * newstring;
+
+ old_size = string->allocated_len;
+
+ string->allocated_len = nearest_power (1, string->len + len + 1);
+
+#ifndef MMAP_UNAVAILABLE
+ if (string->allocated_len > mmap_string_ceil)
+ newstring = mmap_string_realloc_file(string);
+ else {
+#endif
+ newstring = mmap_string_realloc_memory(string);
+#ifndef MMAP_UNAVAILABLE
+ if (newstring == NULL)
+ newstring = mmap_string_realloc_file(string);
+ }
+#endif
+
+ if (newstring == NULL)
+ string->allocated_len = old_size;
+ }
+
+ return string;
+}
+
+MMAPString*
+mmap_string_sized_new (size_t dfl_size)
+{
+ MMAPString *string;
+
+ string = malloc(sizeof(* string));
+ if (string == NULL)
+ return NULL;
+
+ string->allocated_len = 0;
+ string->len = 0;
+ string->str = NULL;
+ string->fd = -1;
+ string->mmapped_size = 0;
+
+ if (mmap_string_maybe_expand (string, MAX (dfl_size, 2)) == NULL)
+ return NULL;
+
+ string->str[0] = 0;
+
+ return string;
+}
+
+MMAPString*
+mmap_string_new (const char *init)
+{
+ MMAPString *string;
+
+ string = mmap_string_sized_new (init ? strlen (init) + 2 : 2);
+ if (string == NULL)
+ return NULL;
+
+ if (init)
+ mmap_string_append (string, init);
+
+ return string;
+}
+
+MMAPString*
+mmap_string_new_len (const char *init,
+ size_t len)
+{
+ MMAPString *string;
+
+ if (len <= 0)
+ return mmap_string_new (init);
+ else
+ {
+ string = mmap_string_sized_new (len);
+
+ if (init)
+ mmap_string_append_len (string, init, len);
+
+ return string;
+ }
+}
+
+void
+mmap_string_free (MMAPString *string)
+{
+ if (string == NULL)
+ return;
+
+ if (string->fd != -1) {
+ munmap(string->str, string->mmapped_size);
+ close(string->fd);
+ }
+ else {
+ free (string->str);
+ }
+ free(string);
+}
+
+MMAPString*
+mmap_string_assign (MMAPString *string,
+ const char *rval)
+{
+ mmap_string_truncate (string, 0);
+ if (mmap_string_append (string, rval) == NULL)
+ return NULL;
+
+ return string;
+}
+
+MMAPString*
+mmap_string_truncate (MMAPString *string,
+ size_t len)
+{
+ string->len = MIN (len, string->len);
+ string->str[string->len] = 0;
+
+ return string;
+}
+
+/**
+ * mmap_string_set_size:
+ * @string: a #MMAPString
+ * @len: the new length
+ *
+ * Sets the length of a #MMAPString. If the length is less than
+ * the current length, the string will be truncated. If the
+ * length is greater than the current length, the contents
+ * of the newly added area are undefined. (However, as
+ * always, string->str[string->len] will be a nul byte.)
+ *
+ * Return value: @string
+ **/
+MMAPString*
+mmap_string_set_size (MMAPString *string,
+ size_t len)
+{
+ if (len >= string->allocated_len)
+ if (mmap_string_maybe_expand (string, len - string->len) == NULL)
+ return NULL;
+
+ string->len = len;
+ string->str[len] = 0;
+
+ return string;
+}
+
+/*
+static int in_mapped_zone(MMAPString * string, char * val)
+{
+ return (val >= string->str) && (val < string->str + string->mmapped_size);
+}
+*/
+
+MMAPString*
+mmap_string_insert_len (MMAPString *string,
+ size_t pos,
+ const char *val,
+ size_t len)
+{
+ if (mmap_string_maybe_expand (string, len) == NULL)
+ return NULL;
+
+ if (pos < string->len)
+ memmove (string->str + pos + len, string->str + pos, string->len - pos);
+
+ /* insert the new string */
+ memmove (string->str + pos, val, len);
+
+ string->len += len;
+
+ string->str[string->len] = 0;
+
+ return string;
+}
+
+MMAPString*
+mmap_string_append (MMAPString *string,
+ const char *val)
+{
+ return mmap_string_insert_len (string, string->len, val, strlen(val));
+}
+
+MMAPString*
+mmap_string_append_len (MMAPString *string,
+ const char *val,
+ size_t len)
+{
+ return mmap_string_insert_len (string, string->len, val, len);
+}
+
+MMAPString*
+mmap_string_append_c (MMAPString *string,
+ char c)
+{
+ return mmap_string_insert_c (string, string->len, c);
+}
+
+MMAPString*
+mmap_string_prepend (MMAPString *string,
+ const char *val)
+{
+ return mmap_string_insert_len (string, 0, val, strlen(val));
+}
+
+MMAPString*
+mmap_string_prepend_len (MMAPString *string,
+ const char *val,
+ size_t len)
+{
+ return mmap_string_insert_len (string, 0, val, len);
+}
+
+MMAPString*
+mmap_string_prepend_c (MMAPString *string,
+ char c)
+{
+ return mmap_string_insert_c (string, 0, c);
+}
+
+MMAPString*
+mmap_string_insert (MMAPString *string,
+ size_t pos,
+ const char *val)
+{
+ return mmap_string_insert_len (string, pos, val, strlen(val));
+}
+
+MMAPString*
+mmap_string_insert_c (MMAPString *string,
+ size_t pos,
+ char c)
+{
+ if (mmap_string_maybe_expand (string, 1) == NULL)
+ return NULL;
+
+ /* If not just an append, move the old stuff */
+ if (pos < string->len)
+ memmove (string->str + pos + 1, string->str + pos, string->len - pos);
+
+ string->str[pos] = c;
+
+ string->len += 1;
+
+ string->str[string->len] = 0;
+
+ return string;
+}
+
+MMAPString*
+mmap_string_erase (MMAPString *string,
+ size_t pos,
+ size_t len)
+{
+ if ((pos + len) < string->len)
+ memmove (string->str + pos, string->str + pos + len,
+ string->len - (pos + len));
+
+ string->len -= len;
+
+ string->str[string->len] = 0;
+
+ return string;
+}
diff --git a/src/plugins/mailmbox/mmapstring.h b/src/plugins/mailmbox/mmapstring.h
@@ -0,0 +1,136 @@
+/*
+ * libEtPan! -- a mail stuff library
+ *
+ * Copyright (C) 2001, 2002 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef __MMAP_STRING_H__
+
+#define __MMAP_STRING_H__
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+#define TMPDIR "/tmp"
+*/
+
+typedef struct _MMAPString MMAPString;
+
+struct _MMAPString
+{
+ char * str;
+ size_t len;
+ size_t allocated_len;
+ int fd;
+ size_t mmapped_size;
+ /*
+ char * old_non_mmapped_str;
+ */
+};
+
+/* configure location of mmaped files */
+
+void mmap_string_set_tmpdir(char * directory);
+
+/* Strings
+ */
+
+MMAPString * mmap_string_new (const char * init);
+
+MMAPString * mmap_string_new_len (const char * init,
+ size_t len);
+
+MMAPString * mmap_string_sized_new (size_t dfl_size);
+
+void mmap_string_free (MMAPString * string);
+
+MMAPString * mmap_string_assign (MMAPString * string,
+ const char * rval);
+
+MMAPString * mmap_string_truncate (MMAPString *string,
+ size_t len);
+
+MMAPString * mmap_string_set_size (MMAPString * string,
+ size_t len);
+
+MMAPString * mmap_string_insert_len (MMAPString * string,
+ size_t pos,
+ const char * val,
+ size_t len);
+
+MMAPString * mmap_string_append (MMAPString * string,
+ const char * val);
+
+MMAPString * mmap_string_append_len (MMAPString * string,
+ const char * val,
+ size_t len);
+
+MMAPString * mmap_string_append_c (MMAPString * string,
+ char c);
+
+MMAPString * mmap_string_prepend (MMAPString * string,
+ const char * val);
+
+MMAPString * mmap_string_prepend_c (MMAPString * string,
+ char c);
+
+MMAPString * mmap_string_prepend_len (MMAPString * string,
+ const char * val,
+ size_t len);
+
+MMAPString * mmap_string_insert (MMAPString * string,
+ size_t pos,
+ const char * val);
+
+MMAPString * mmap_string_insert_c (MMAPString *string,
+ size_t pos,
+ char c);
+
+MMAPString * mmap_string_erase(MMAPString * string,
+ size_t pos,
+ size_t len);
+
+void mmap_string_set_ceil(size_t ceil);
+
+int mmap_string_ref(MMAPString * string);
+int mmap_string_unref(char * str);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* __MMAP_STRING_H__ */
diff --git a/src/plugins/mailmbox/placeholder.txt b/src/plugins/mailmbox/placeholder.txt
@@ -1 +0,0 @@
-Placeholder
diff --git a/src/plugins/mailmbox/plugin.c b/src/plugins/mailmbox/plugin.c
@@ -0,0 +1,87 @@
+/*
+ * mailmbox Plugin -- mbox support for Sylpheed
+ * Copyright (C) 2003 Christoph Hohmann
+ * Copyright (C) 2003-2005 Hoa v. Dinh, Alfons Hoogervorst
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+# include "claws-features.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include "plugin.h"
+#include "folder.h"
+#include "mailmbox_folder.h"
+#include "common/version.h"
+#include "plugin_gtk.h"
+#include "plugin.h"
+#include "main.h"
+
+gint plugin_init(gchar **error)
+{
+ if (!check_plugin_version(MAKE_NUMERIC_VERSION(3,8,1,46),
+ VERSION_NUMERIC, "Mailmbox", error))
+ return -1;
+
+ folder_register_class(claws_mailmbox_get_class());
+ plugin_gtk_init(error);
+ return 0;
+}
+
+gboolean plugin_done(void)
+{
+ plugin_gtk_done();
+ if (!claws_is_exiting())
+ folder_unregister_class(claws_mailmbox_get_class());
+ return TRUE;
+}
+
+const gchar *plugin_name(void)
+{
+ return _("mailmbox folder (etPan!)");
+}
+
+const gchar *plugin_desc(void)
+{
+ return _("This is a plugin to handle mailboxes in mbox format.");
+}
+
+const gchar *plugin_type(void)
+{
+ return "GTK2";
+}
+
+const gchar *plugin_licence(void)
+{
+ return "GPL3+";
+}
+
+const gchar *plugin_version(void)
+{
+ return VERSION;
+}
+
+struct PluginFeature *plugin_provides(void)
+{
+ static struct PluginFeature features[] =
+ { {PLUGIN_FOLDERCLASS, N_("MBOX")},
+ {PLUGIN_NOTHING, NULL}};
+ return features;
+}
diff --git a/src/plugins/mailmbox/plugin_gtk.c b/src/plugins/mailmbox/plugin_gtk.c
@@ -0,0 +1,455 @@
+/*
+ * mailmbox Plugin -- mbox support for Sylpheed
+ * Copyright (C) 2003-2005 Christoph Hohmann,
+ * Hoa v. Dinh,
+ * Alfons Hoogervorst
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+# include "claws-features.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <gtk/gtk.h>
+
+#include "plugin.h"
+#include "folder.h"
+#include "mailmbox_folder.h"
+#include "mainwindow.h"
+#include "folderview.h"
+#include "inputdialog.h"
+#include "foldersel.h"
+#include "alertpanel.h"
+#include "main.h"
+#include "menu.h"
+#include "account.h"
+#include "prefs_actions.h"
+#include "summaryview.h"
+#include "folder_item_prefs.h"
+
+static void add_mailbox(GtkAction *action, gpointer callback_data);
+static void new_folder_cb(GtkAction *action, gpointer data);
+static void delete_folder_cb(GtkAction *action, gpointer data);
+static void rename_folder_cb(GtkAction *action, gpointer data);
+static void move_folder_cb(GtkAction *action, gpointer data);
+static void copy_folder_cb(GtkAction *action, gpointer data);
+static void update_tree_cb(GtkAction *action, gpointer data);
+static void remove_mailbox_cb(GtkAction *action, gpointer data);
+
+static GtkActionEntry claws_mailmbox_popup_entries[] =
+{
+ {"FolderViewPopup/CreateNewFolder", NULL, N_("Create _new folder..."), NULL, NULL, G_CALLBACK(new_folder_cb) },
+ {"FolderViewPopup/RenameFolder", NULL, N_("_Rename folder..."), NULL, NULL, G_CALLBACK(rename_folder_cb) },
+ {"FolderViewPopup/MoveFolder", NULL, N_("M_ove folder..."), NULL, NULL, G_CALLBACK(move_folder_cb) },
+ {"FolderViewPopup/CopyFolder", NULL, N_("Cop_y folder..."), NULL, NULL, G_CALLBACK(copy_folder_cb) },
+ {"FolderViewPopup/DeleteFolder", NULL, N_("_Delete folder..."), NULL, NULL, G_CALLBACK(delete_folder_cb) },
+ {"FolderViewPopup/CheckNewMessages", NULL, N_("_Check for new messages"), NULL, NULL, G_CALLBACK(update_tree_cb) }, /*0*/
+ {"FolderViewPopup/CheckNewFolders", NULL, N_("C_heck for new folders"), NULL, NULL, G_CALLBACK(update_tree_cb) }, /*1*/
+ {"FolderViewPopup/RebuildTree", NULL, N_("R_ebuild folder tree"), NULL, NULL, G_CALLBACK(update_tree_cb) }, /*2*/
+ {"FolderViewPopup/RemoveMailbox", NULL, N_("Remove _mailbox..."), NULL, NULL, G_CALLBACK(remove_mailbox_cb) },
+};
+static void set_sensitivity(GtkUIManager *ui_manager, FolderItem *item);
+static void add_menuitems(GtkUIManager *ui_manager, FolderItem *item);
+
+static FolderViewPopup claws_mailmbox_popup =
+{
+ "mailmbox",
+ "<MailmboxFolder>",
+ claws_mailmbox_popup_entries,
+ G_N_ELEMENTS(claws_mailmbox_popup_entries),
+ NULL, 0,
+ NULL, 0, 0, NULL,
+ add_menuitems,
+ set_sensitivity
+};
+
+static GtkActionEntry mainwindow_add_mailbox[] = {{
+ "File/AddMailbox/Mbox",
+ NULL, N_("mbox (etPan!)..."), NULL, NULL, G_CALLBACK(add_mailbox)
+}};
+
+static guint main_menu_id = 0;
+
+gint plugin_gtk_init(gchar **error)
+{
+ MainWindow *mainwin = mainwindow_get_mainwindow();
+
+ folderview_register_popup(&claws_mailmbox_popup);
+
+ gtk_action_group_add_actions(mainwin->action_group, mainwindow_add_mailbox,
+ 1, (gpointer)mainwin);
+ MENUITEM_ADDUI_ID_MANAGER(mainwin->ui_manager, "/Menu/File/AddMailbox", "Mbox",
+ "File/AddMailbox/Mbox", GTK_UI_MANAGER_MENUITEM,
+ main_menu_id)
+
+ return 0;
+}
+
+void plugin_gtk_done(void)
+{
+ MainWindow *mainwin = mainwindow_get_mainwindow();
+
+ if (mainwin == NULL || claws_is_exiting())
+ return;
+
+ folderview_unregister_popup(&claws_mailmbox_popup);
+
+ MENUITEM_REMUI_MANAGER(mainwin->ui_manager,mainwin->action_group, "File/AddMailbox/RSSyl", main_menu_id);
+ main_menu_id = 0;
+}
+
+static void add_menuitems(GtkUIManager *ui_manager, FolderItem *item)
+{
+ MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "CreateNewFolder", "FolderViewPopup/CreateNewFolder", GTK_UI_MANAGER_MENUITEM)
+ MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorMbox1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
+ MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RenameFolder", "FolderViewPopup/RenameFolder", GTK_UI_MANAGER_MENUITEM)
+ MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MoveFolder", "FolderViewPopup/MoveFolder", GTK_UI_MANAGER_MENUITEM)
+ MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "CopyFolder", "FolderViewPopup/CopyFolder", GTK_UI_MANAGER_MENUITEM)
+ MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorMbox2", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
+ MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "DeleteFolder", "FolderViewPopup/DeleteFolder", GTK_UI_MANAGER_MENUITEM)
+ MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorMbox3", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
+ MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "CheckNewMessages", "FolderViewPopup/CheckNewMessages", GTK_UI_MANAGER_MENUITEM)
+ MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "CheckNewFolders", "FolderViewPopup/CheckNewFolders", GTK_UI_MANAGER_MENUITEM)
+ MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RebuildTree", "FolderViewPopup/RebuildTree", GTK_UI_MANAGER_MENUITEM)
+ MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorMbox4", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
+ MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RemoveMailbox", "FolderViewPopup/RemoveMailbox", GTK_UI_MANAGER_MENUITEM)
+ MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorMbox5", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
+}
+
+static void set_sensitivity(GtkUIManager *ui_manager, FolderItem *item)
+{
+ gboolean folder_is_normal =
+ item != NULL &&
+ item->stype == F_NORMAL &&
+ !folder_has_parent_of_type(item, F_OUTBOX) &&
+ !folder_has_parent_of_type(item, F_DRAFT) &&
+ !folder_has_parent_of_type(item, F_QUEUE) &&
+ !folder_has_parent_of_type(item, F_TRASH);
+#define SET_SENS(name, sens) \
+ cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
+
+ SET_SENS("FolderViewPopup/CreateNewFolder", item->stype != F_INBOX);
+ SET_SENS("FolderViewPopup/RenameFolder", item->stype == F_NORMAL && folder_item_parent(item) != NULL);
+ SET_SENS("FolderViewPopup/MoveFolder", folder_is_normal && folder_item_parent(item) != NULL);
+ SET_SENS("FolderViewPopup/DeleteFolder", item->stype == F_NORMAL && folder_item_parent(item) != NULL);
+
+ SET_SENS("FolderViewPopup/CheckNewMessages", folder_item_parent(item) == NULL);
+ SET_SENS("FolderViewPopup/CheckNewFolders", folder_item_parent(item) == NULL);
+ SET_SENS("FolderViewPopup/RebuildTree", folder_item_parent(item) == NULL);
+
+ SET_SENS("FolderViewPopup/RemoveMailbox", folder_item_parent(item) == NULL);
+
+#undef SET_SENS
+}
+
+#define DO_ACTION(name, act) { if (!strcmp(a_name, name)) act; }
+
+static void update_tree_cb(GtkAction *action, gpointer data)
+{
+ FolderView *folderview = (FolderView *)data;
+ FolderItem *item;
+ const gchar *a_name = gtk_action_get_name(action);
+
+ item = folderview_get_selected_item(folderview);
+ g_return_if_fail(item != NULL);
+
+ summary_show(folderview->summaryview, NULL);
+
+ g_return_if_fail(item->folder != NULL);
+
+ DO_ACTION("FolderViewPopup/CheckNewMessages", folderview_check_new(item->folder));
+ DO_ACTION("FolderViewPopup/CheckNewFolders", folderview_rescan_tree(item->folder, FALSE));
+ DO_ACTION("FolderViewPopup/RebuildTree", folderview_rescan_tree(item->folder, TRUE));
+}
+
+static void add_mailbox(GtkAction *action, gpointer callback_data)
+{
+ MainWindow *mainwin = (MainWindow *) callback_data;
+ gchar *path, *basename;
+ Folder *folder;
+
+ path = input_dialog(_("Add mailbox"),
+ _("Input the location of mailbox.\n"
+ "If the existing mailbox is specified, it will be\n"
+ "scanned automatically."),
+ "Mail");
+ if (!path) return;
+ if (folder_find_from_path(path)) {
+ alertpanel_error(_("The mailbox `%s' already exists."), path);
+ g_free(path);
+ return;
+ }
+ basename = g_path_get_basename(path);
+ folder = folder_new(folder_get_class_from_string("mailmbox"),
+ !strcmp(path, "Mail") ? _("Mailbox") : basename,
+ path);
+ g_free(basename);
+ g_free(path);
+
+
+ if (folder->klass->create_tree(folder) < 0) {
+ alertpanel_error(_("Creation of the mailbox failed.\n"
+ "Maybe some files already exist, or you don't have the permission to write there."));
+ folder_destroy(folder);
+ return;
+ }
+
+ folder_add(folder);
+ folder_scan_tree(folder, TRUE);
+
+ folderview_set(mainwin->folderview);
+
+ return;
+}
+
+static void new_folder_cb(GtkAction *action, gpointer data)
+{
+ FolderView *folderview = (FolderView *)data;
+ GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
+ FolderItem *item;
+ FolderItem *new_item;
+ gchar *new_folder;
+ gchar *name;
+ gchar *p;
+
+ if (!folderview->selected) return;
+
+ item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
+ g_return_if_fail(item != NULL);
+ g_return_if_fail(item->folder != NULL);
+
+ new_folder = input_dialog(_("New folder"),
+ _("Input the name of new folder:"),
+ _("NewFolder"));
+ if (!new_folder) return;
+ AUTORELEASE_STR(new_folder, {g_free(new_folder); return;});
+
+ p = strchr(new_folder, G_DIR_SEPARATOR);
+ if (p == NULL)
+ p = strchr(new_folder, '.');
+ if (p) {
+ alertpanel_error(_("`%c' can't be included in folder name."),
+ p[0]);
+ return;
+ }
+
+ name = trim_string(new_folder, 32);
+ AUTORELEASE_STR(name, {g_free(name); return;});
+
+ /* find whether the directory already exists */
+ p = g_strconcat(item->path ? item->path : "", ".", new_folder, NULL);
+ if (folder_find_child_item_by_name(item, p)) {
+ g_free(p);
+ alertpanel_error(_("The folder `%s' already exists."), name);
+ return;
+ }
+ g_free(p);
+
+ new_item = folder_create_folder(item, new_folder);
+ if (!new_item) {
+ alertpanel_error(_("Can't create the folder `%s'."), name);
+ return;
+ }
+
+ folder_write_list();
+}
+
+static void remove_mailbox_cb(GtkAction *action, gpointer data)
+{
+ FolderView *folderview = (FolderView *)data;
+ FolderItem *item;
+ gchar *name;
+ gchar *message;
+ AlertValue avalue;
+
+ item = folderview_get_selected_item(folderview);
+ g_return_if_fail(item != NULL);
+ g_return_if_fail(item->folder != NULL);
+ if (folder_item_parent(item)) return;
+
+ name = trim_string(item->folder->name, 32);
+ message = g_strdup_printf
+ (_("Really remove the mailbox `%s' ?\n"
+ "(The messages are NOT deleted from the disk)"), name);
+ avalue = alertpanel_full(_("Remove mailbox"), message,
+ GTK_STOCK_CANCEL, _("_Remove"), NULL, FALSE,
+ NULL, ALERT_WARNING, G_ALERTDEFAULT);
+ g_free(message);
+ g_free(name);
+ if (avalue != G_ALERTALTERNATE) return;
+
+ folderview_unselect(folderview);
+ summary_clear_all(folderview->summaryview);
+
+ folder_destroy(item->folder);
+}
+
+static void delete_folder_cb(GtkAction *action, gpointer data)
+{
+ FolderView *folderview = (FolderView *)data;
+ GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
+ FolderItem *item;
+ gchar *message, *name;
+ AlertValue avalue;
+ gchar *old_path;
+ gchar *old_id;
+
+ item = folderview_get_selected_item(folderview);
+ g_return_if_fail(item != NULL);
+ g_return_if_fail(item->path != NULL);
+ g_return_if_fail(item->folder != NULL);
+
+ name = trim_string(item->name, 32);
+ AUTORELEASE_STR(name, {g_free(name); return;});
+ message = g_strdup_printf
+ (_("All folder(s) and message(s) under `%s' will be deleted.\n"
+ "Do you really want to delete?"), name);
+ avalue = alertpanel_full(_("Delete folder"), message,
+ GTK_STOCK_CANCEL, GTK_STOCK_DELETE, NULL, FALSE,
+ NULL, ALERT_NOTICE, G_ALERTDEFAULT);
+ g_free(message);
+ if (avalue != G_ALERTALTERNATE) return;
+
+ Xstrdup_a(old_path, item->path, return);
+ old_id = folder_item_get_identifier(item);
+
+ if (folderview->opened == folderview->selected ||
+ gtk_cmctree_is_ancestor(ctree,
+ folderview->selected,
+ folderview->opened)) {
+ summary_clear_all(folderview->summaryview);
+ folderview->opened = NULL;
+ }
+
+ if (item->folder->klass->remove_folder(item->folder, item) < 0) {
+ alertpanel_error(_("Can't remove the folder `%s'."), name);
+ if (folderview->opened == folderview->selected)
+ summary_show(folderview->summaryview,
+ folderview->summaryview->folder_item);
+ g_free(old_id);
+ return;
+ }
+
+ folder_write_list();
+
+ prefs_filtering_delete_path(old_id);
+ g_free(old_id);
+
+}
+
+static void move_folder_cb(GtkAction *action, gpointer data)
+{
+ FolderView *folderview = (FolderView *)data;
+ FolderItem *from_folder = NULL, *to_folder = NULL;
+
+ from_folder = folderview_get_selected_item(folderview);
+ if (!from_folder || from_folder->folder->klass != claws_mailmbox_get_class())
+ return;
+
+ to_folder = foldersel_folder_sel(from_folder->folder, FOLDER_SEL_MOVE, NULL, FALSE);
+ if (!to_folder)
+ return;
+
+ folderview_move_folder(folderview, from_folder, to_folder, 0);
+}
+
+static void copy_folder_cb(GtkAction *action, gpointer data)
+{
+ FolderView *folderview = (FolderView *)data;
+ FolderItem *from_folder = NULL, *to_folder = NULL;
+
+ from_folder = folderview_get_selected_item(folderview);
+ if (!from_folder || from_folder->folder->klass != claws_mailmbox_get_class())
+ return;
+
+ to_folder = foldersel_folder_sel(from_folder->folder, FOLDER_SEL_MOVE, NULL, FALSE);
+ if (!to_folder)
+ return;
+
+ folderview_move_folder(folderview, from_folder, to_folder, 1);
+}
+
+static void rename_folder_cb(GtkAction *action, gpointer data)
+{
+ FolderView *folderview = (FolderView *)data;
+ FolderItem *item, *parent;
+ gchar *new_folder;
+ gchar *name;
+ gchar *message;
+ gchar *old_path;
+ gchar *old_id;
+ gchar *new_id;
+ gchar *p;
+
+ item = folderview_get_selected_item(folderview);
+ g_return_if_fail(item != NULL);
+ g_return_if_fail(item->path != NULL);
+ g_return_if_fail(item->folder != NULL);
+
+ name = trim_string(item->name, 32);
+ message = g_strdup_printf(_("Input new name for `%s':"), name);
+ new_folder = input_dialog(_("Rename folder"), message, item->name);
+ g_free(message);
+ g_free(name);
+ if (!new_folder) return;
+ AUTORELEASE_STR(new_folder, {g_free(new_folder); return;});
+
+ p = strchr(new_folder, G_DIR_SEPARATOR);
+ if (p == NULL)
+ p = strchr(new_folder, '.');
+ if (p) {
+ alertpanel_error(_("`%c' can't be included in folder name."),
+ p[0]);
+ return;
+ }
+
+ parent = folder_item_parent(item);
+ p = g_strconcat(parent->path ? parent->path : "", ".", new_folder, NULL);
+ if (folder_find_child_item_by_name(parent, p)) {
+ name = trim_string(new_folder, 32);
+ alertpanel_error(_("The folder `%s' already exists."), name);
+ g_free(name);
+ return;
+ }
+
+ Xstrdup_a(old_path, item->path, {g_free(new_folder); return;});
+
+ old_id = folder_item_get_identifier(item);
+
+ if (folder_item_rename(item, new_folder) < 0) {
+ alertpanel_error(_("The folder could not be renamed.\n"
+ "The new folder name is not allowed."));
+ g_free(old_id);
+ return;
+ }
+
+ new_id = folder_item_get_identifier(item);
+ prefs_filtering_rename_path(old_id, new_id);
+ account_rename_path(old_id, new_id);
+ prefs_actions_rename_path(old_id, new_id);
+
+ g_free(old_id);
+ g_free(new_id);
+
+ folder_item_prefs_save_config_recursive(item);
+ folder_write_list();
+}
+
diff --git a/src/plugins/mailmbox/plugin_gtk.h b/src/plugins/mailmbox/plugin_gtk.h
@@ -0,0 +1,30 @@
+/*
+ * mailmbox Plugin -- mbox support for Sylpheed
+ * Copyright (C) 2003-2005 Christoph Hohmann,
+ * Hoa v. Dinh,
+ * Alfons Hoogervorst
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PLUGIN_GTK_H__
+#define __PLUGIN_GTK_H__
+
+#include <glib.h>
+
+gint plugin_gtk_init(gchar **error);
+void plugin_gtk_done(void);
+
+#endif
diff --git a/src/plugins/newmail/Makefile.am b/src/plugins/newmail/Makefile.am
@@ -0,0 +1,19 @@
+plugindir = $(pkglibdir)/plugins
+
+plugin_LTLIBRARIES = newmail.la
+
+newmail_la_SOURCES = \
+ newmail.c
+
+newmail_la_LDFLAGS = \
+ -avoid-version -module
+
+AM_CPPFLAGS = \
+ $(CLAWS_MAIL_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(GTK_CFLAGS)
+
+INCLUDES = \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/src/common \
+ -I$(top_srcdir)/src/gtk
diff --git a/src/plugins/newmail/newmail.c b/src/plugins/newmail/newmail.c
@@ -0,0 +1,168 @@
+/*
+ * newmail - A plugin for Claws Mail
+ *
+ * Copyright (C) 2005-2005 H.Merijn Brand and the Claws Mail Team
+ *
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2012 the Claws Mail Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <errno.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "version.h"
+#include "claws.h"
+#include "plugin.h"
+#include "utils.h"
+#include "hooks.h"
+#include "procmsg.h"
+
+#include <inttypes.h>
+
+#include "plugin.h"
+
+static guint hook_id;
+
+static FILE *NewLog = NULL;
+static char *LogName = NULL;
+static int truncLog = 1;
+
+static gchar *defstr (gchar *s)
+{
+ return s ? s : "(null)";
+ } /* defstr */
+
+gboolean newmail_hook (gpointer source, gpointer data)
+{
+ auto MsgInfo *msginfo = (MsgInfo *)source;
+ auto FolderItem *tof;
+
+ if (!msginfo) return (FALSE);
+
+ tof = msginfo->folder;
+ (void)fprintf (NewLog, "---\n"
+ "Date:\t%s\n"
+ "Subject:\t%s\n"
+ "From:\t%s\n"
+ "To:\t%s\n"
+ "Cc:\t%s\n"
+ "Size:\t%jd\n"
+ "Path:\t%s\n"
+ "Message:\t%d\n"
+ "Folder:\t%s\n",
+ defstr (msginfo->date),
+ defstr (msginfo->subject),
+ defstr (msginfo->from),
+ defstr (msginfo->to),
+ defstr (msginfo->cc),
+ (intmax_t) msginfo->size,
+ defstr (procmsg_get_message_file_path (msginfo)),
+ msginfo->msgnum,
+ tof ? defstr (tof->name) : "(null)");
+
+ return (FALSE);
+ } /* newmail_hook */
+
+gboolean plugin_done (void)
+{
+ if (NewLog) {
+ (void)fclose (NewLog);
+ NewLog = NULL;
+ LogName = NULL;
+ }
+ hooks_unregister_hook (MAIL_POSTFILTERING_HOOKLIST, hook_id);
+
+ printf (_("Newmail plugin unloaded\n"));
+ return TRUE;
+ } /* plugin_done */
+
+gint plugin_init (gchar **error)
+{
+ if (!check_plugin_version(MAKE_NUMERIC_VERSION(2,9,2,72),
+ VERSION_NUMERIC, _("NewMail"), error))
+ return -1;
+
+ hook_id = hooks_register_hook (MAIL_POSTFILTERING_HOOKLIST, newmail_hook, NULL);
+ if (hook_id == -1) {
+ *error = g_strdup (_("Failed to register newmail hook"));
+ return (-1);
+ }
+
+ if (!NewLog) {
+ auto char *mode = truncLog ? "w" : "a";
+ if (!LogName) {
+ auto size_t l;
+ auto char name[260];
+ (void)snprintf (name, 256, "%s/Mail/NewLog", getenv ("HOME"));
+ l = strlen (name);
+ if (l > 255 || !(LogName = (char *)malloc (l + 1))) {
+ *error = g_strdup (_("Cannot load plugin NewMail\n"
+ "$HOME is too long\n"));
+ plugin_done ();
+ return (-1);
+ }
+ (void)strcpy (LogName, name);
+ }
+ if (!(NewLog = fopen (LogName, mode))) {
+ *error = g_strdup (sys_errlist[errno]);
+ plugin_done ();
+ return (-1);
+ }
+ setbuf (NewLog, NULL);
+ }
+
+ printf (_("Newmail plugin loaded\n"
+ "Message header summaries written to %s\n"), LogName);
+ return (0);
+ } /* plugin_init */
+
+const gchar *plugin_name (void)
+{
+ return _("NewMail");
+ } /* plugin_name */
+
+const gchar *plugin_desc (void)
+{
+ return _("This Plugin writes a header summary to a log file for each "
+ "mail received after sorting.\n\n"
+ "Default is ~/Mail/NewLog");
+ } /* plugin_desc */
+
+const gchar *plugin_type (void)
+{
+ return ("Common");
+ } /* plugin_type */
+
+const gchar *plugin_licence (void)
+{
+ return ("GPL3+");
+ } /* plugin_licence */
+
+const gchar *plugin_version (void)
+{
+ return (VERSION);
+ } /* plugin_version */
+
+struct PluginFeature *plugin_provides(void)
+{
+ static struct PluginFeature features[] =
+ { {PLUGIN_NOTIFIER, N_("Log file")},
+ {PLUGIN_NOTHING, NULL}};
+ return features;
+}
diff --git a/src/plugins/newmail/placeholder.txt b/src/plugins/newmail/placeholder.txt
@@ -1 +0,0 @@
-Placeholder
diff --git a/src/plugins/notification/Makefile.am b/src/plugins/notification/Makefile.am
@@ -0,0 +1,106 @@
+if BUILD_HOTKEYS
+hotkey_lib = libcmnpgtkhotkey.la
+hotkey_lib_path = $(top_builddir)/src/plugins/notification/gtkhotkey/libcmnpgtkhotkey.la
+else
+hotkey_lib =
+hotkey_lib_path =
+endif
+
+
+EXTRA_DIST = claws.def plugin.def version.rc
+CFLAGS += "-Wall"
+
+if OS_WIN32
+
+LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RC) \
+ `echo $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) | \
+ sed -e 's/-I/--include-dir /g;s/-D/--define /g'`
+
+%.lo : %.rc
+ $(LTRCCOMPILE) -i $< -o $@
+
+plugin_res = version.lo
+plugin_res_ldflag = -Wl,.libs/version.o
+
+export_symbols = -export-symbols $(srcdir)/plugin.def
+
+plugin_deps = libclaws.a $(plugin_res) plugin.def $(hotkey_lib)
+
+libclaws.a: claws.def
+ $(DLLTOOL) --output-lib $@ --def $<
+
+plugin_ldadd = -L. -lclaws
+
+else
+plugin_res =
+plugin_res_ldflag =
+export_symbols =
+plugin_deps =
+plugin_ldadd =
+endif
+
+if PLATFORM_WIN32
+no_undefined = -no-undefined
+else
+no_undefined =
+endif
+
+if CYGWIN
+cygwin_export_lib = -L$(top_builddir)/src -lclaws-mail
+else
+cygwin_export_lib =
+endif
+
+plugindir = $(pkglibdir)/plugins
+
+plugin_LTLIBRARIES = notification_plugin.la
+
+notification_plugin_la_SOURCES = \
+ gettext.h \
+ notification_plugin.c notification_plugin.h \
+ notification_core.c notification_core.h \
+ notification_popup.c notification_popup.h \
+ notification_banner.c notification_banner.h \
+ notification_command.c notification_command.h \
+ notification_lcdproc.c notification_lcdproc.h \
+ notification_trayicon.c notification_trayicon.h \
+ notification_indicator.c notification_indicator.h \
+ notification_hotkeys.c notification_hotkeys.h \
+ notification_prefs.c notification_prefs.h \
+ notification_foldercheck.c notification_foldercheck.h \
+ notification_pixbuf.c notification_pixbuf.h \
+ raw_claws_mail_logo_64x64.h
+
+notification_plugin_la_LDFLAGS = \
+ $(plugin_res_ldflag) $(no_undefined) $(export_symbols) \
+ -avoid-version -module
+
+notification_plugin_la_DEPENDENCIES = $(plugin_deps) \
+ $(hotkey_lib_path)
+
+INCLUDES = \
+ -I$(top_srcdir)/src/plugins/notification/gtkhotkey \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/src/common \
+ -I$(top_srcdir)/src/gtk
+
+
+notification_plugin_la_LIBADD = $(plugin_ldadd) $(cygwin_export_lib) \
+ $(GTK_LIBS) \
+ $(libnotify_LIBS) \
+ $(libindicate_LIBS) \
+ $(libcanberra_gtk_LIBS) \
+ $(hotkey_lib_path)
+
+notification_plugin_la_CPPFLAGS = \
+ $(NOTIFY_CFLAGS) \
+ $(CLAWS_MAIL_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(GTK_CFLAGS) \
+ -DLOCALEDIR=\""$(localedir)"\" \
+ $(libnotify_CFLAGS) \
+ $(libindicate_CFLAGS) \
+ $(libcanberra_gtk_CFLAGS)
+
+clean-local:
+ rm -f libclaws.a
diff --git a/src/plugins/notification/claws.def b/src/plugins/notification/claws.def
@@ -0,0 +1,73 @@
+LIBRARY CLAWS-MAIL.EXE
+EXPORTS
+get_locale_dir
+check_plugin_version
+debug_print_real
+debug_srcname
+hooks_register_hook
+hooks_unregister_hook
+account_get_list
+addressbook_open
+alertpanel
+app_will_exit
+claws_is_exiting
+claws_is_starting
+compose_mail_cb
+compose_new
+compose_reply_from_messageview
+execute_command_line
+file_exist
+folder_find_item_from_identifier
+folder_func_to_all_folders
+folder_get_list
+folder_has_parent_of_type
+folder_item_get_identifier
+folder_item_get_msg_list
+folder_item_get_name
+get_rc_dir
+gtkut_convert_int_to_gdk_color
+gtkut_stock_button_set_create
+gtkut_window_new
+inc_all_account_mail_cb
+main_set_show_at_startup
+mainwindow_get_mainwindow
+main_window_hide
+main_window_empty_trash
+mainwindow_is_obscured
+mainwindow_jump_to
+main_window_show
+main_window_toggle_work_offline
+manage_window_destroy
+manage_window_focus_in
+manage_window_focus_out
+manage_window_set_transient
+manage_window_unmap
+cm_menu_create_action_group_full
+gtkut_ui_manager
+cm_menu_create_action_group
+cm_toggle_menu_set_active
+cm_menu_set_sensitive
+prefs_common_get_prefs
+prefs_common_translated_header_name
+prefs_file_close
+prefs_file_close_revert
+prefs_gtk_register_page
+prefs_gtk_unregister_page
+prefs_read_config
+prefs_set_block_label
+prefs_set_default
+prefs_write_open
+prefs_write_param
+procmsg_get_message_file_path
+procmsg_msg_list_free
+stock_pixbuf_gdk
+stock_pixbuf_gdk follow
+strcmp2
+xml_attr_new
+xml_file_put_xml_decl
+xml_free_tree
+xml_node_new
+xml_parse_file
+xml_tag_add_attr
+xml_tag_new
+xml_write_tree
diff --git a/src/plugins/notification/notification_banner.c b/src/plugins/notification/notification_banner.c
@@ -0,0 +1,503 @@
+/* Notification plugin for Claws Mail
+ * Copyright (C) 2005-2008 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "pluginconfig.h"
+
+#ifdef NOTIFICATION_BANNER
+
+#include <gtk/gtk.h>
+
+#include "gtk/gtkutils.h"
+
+#include "prefs_common.h"
+#include "mainwindow.h"
+#include "menu.h"
+#include "procmsg.h"
+#include "messageview.h"
+#include "compose.h"
+#include "menu.h"
+
+#include "notification_core.h"
+#include "notification_prefs.h"
+#include "notification_banner.h"
+
+typedef struct {
+ GtkWidget *table;
+} NotificationBannerEntry;
+
+typedef struct {
+ GtkWidget *window;
+ GtkWidget *scrolled_win;
+ GtkWidget *viewport;
+
+ NotificationBannerEntry *entries;
+ guint timeout_id;
+ gboolean scrolling;
+} NotificationBanner;
+
+typedef struct {
+ gint banner_width;
+ GtkAdjustment *adj;
+} ScrollingData;
+
+
+static void notification_banner_create(GSList*);
+static gboolean scroller(gpointer data);
+static GtkWidget* create_entrybox(GSList*);
+static gboolean notification_banner_configure(GtkWidget*, GdkEventConfigure*,
+ gpointer);
+static gboolean notification_banner_swap_colors(GtkWidget*,GdkEventCrossing*,gpointer);
+static gboolean notification_banner_button_press(GtkWidget*,GdkEventButton*,gpointer);
+static void notification_banner_show_popup(GtkWidget*,GdkEventButton*);
+static void notification_banner_popup_done(GtkMenuShell*,gpointer);
+
+static void banner_menu_reply_cb(GtkAction *,gpointer);
+
+
+static NotificationBanner banner;
+static ScrollingData sdata;
+
+static GtkWidget *banner_popup;
+static GtkUIManager *banner_ui_manager;
+static GtkActionGroup *banner_action_group;
+
+static gboolean banner_popup_open = FALSE;
+
+static MsgInfo *current_msginfo = NULL;
+
+/* Corresponding mutexes */
+G_LOCK_DEFINE_STATIC(banner);
+G_LOCK_DEFINE_STATIC(sdata);
+
+static GtkActionEntry banner_popup_entries[] =
+{
+ {"BannerPopup", NULL, "BannerPopup" },
+ {"BannerPopup/Reply", NULL, N_("_Reply"), NULL, NULL, G_CALLBACK(banner_menu_reply_cb) },
+};
+
+
+void notification_banner_show(GSList *msg_list)
+{
+ G_LOCK(banner);
+ if((notify_config.banner_show != NOTIFY_BANNER_SHOW_NEVER) &&
+ (g_slist_length(msg_list) ||
+ (notify_config.banner_show == NOTIFY_BANNER_SHOW_ALWAYS)))
+ notification_banner_create(msg_list);
+ else
+ notification_banner_destroy();
+ G_UNLOCK(banner);
+}
+
+void notification_banner_destroy(void)
+{
+ if(banner.window) {
+ if(banner.entries) {
+ g_free(banner.entries);
+ banner.entries = NULL;
+ }
+ gtk_widget_destroy(banner.window);
+ banner.window = NULL;
+ G_LOCK(sdata);
+ sdata.adj = NULL;
+ sdata.banner_width = 0;
+ G_UNLOCK(sdata);
+ if(banner.timeout_id) {
+ g_source_remove(banner.timeout_id);
+ banner.timeout_id = 0;
+ }
+ }
+}
+
+static void notification_banner_create(GSList *msg_list)
+{
+ GtkRequisition requisition, requisition_after;
+ GtkWidget *viewport;
+ GtkWidget *hbox;
+ GtkWidget *entrybox;
+ GdkColor bg;
+ gint banner_width;
+
+ /* Window */
+ if(!banner.window) {
+
+ banner.window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "notification_banner");
+ gtk_window_set_decorated(GTK_WINDOW(banner.window), FALSE);
+ if(notify_config.banner_width > 0)
+ gtk_widget_set_size_request(banner.window, notify_config.banner_width, -1);
+ else
+ gtk_widget_set_size_request(banner.window, gdk_screen_width(), -1);
+ gtk_window_set_keep_above(GTK_WINDOW(banner.window), TRUE);
+ gtk_window_set_accept_focus(GTK_WINDOW(banner.window), FALSE);
+ gtk_window_set_skip_taskbar_hint(GTK_WINDOW(banner.window), TRUE);
+ gtk_window_move(GTK_WINDOW(banner.window),
+ notify_config.banner_root_x, notify_config.banner_root_y);
+ g_signal_connect(banner.window, "configure-event",
+ G_CALLBACK(notification_banner_configure), NULL);
+ }
+ else {
+ if(banner.entries) {
+ g_free(banner.entries);
+ banner.entries = NULL;
+ }
+ gtk_widget_destroy(banner.scrolled_win);
+ }
+ if(notify_config.banner_sticky)
+ gtk_window_stick(GTK_WINDOW(banner.window));
+ else
+ gtk_window_unstick(GTK_WINDOW(banner.window));
+
+ /* Scrolled window */
+ banner.scrolled_win = gtk_scrolled_window_new(NULL, NULL);
+ gtk_container_add(GTK_CONTAINER(banner.window), banner.scrolled_win);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(banner.scrolled_win),
+ GTK_POLICY_NEVER, GTK_POLICY_NEVER);
+
+ /* Viewport */
+ viewport = gtk_viewport_new(NULL,NULL);
+ banner.viewport = viewport;
+ gtk_container_add(GTK_CONTAINER(banner.scrolled_win),viewport);
+ if(notify_config.banner_enable_colors) {
+ gtkut_convert_int_to_gdk_color(notify_config.banner_color_bg,&bg);
+ gtk_widget_modify_bg(viewport,GTK_STATE_NORMAL,&bg);
+ }
+
+ /* Hbox */
+ hbox = gtk_hbox_new(FALSE, 5);
+ gtk_container_add(GTK_CONTAINER(viewport),hbox);
+
+ /* Entrybox */
+ entrybox = create_entrybox(msg_list);
+ gtk_box_pack_start(GTK_BOX(hbox), entrybox, FALSE, FALSE, 0);
+
+ gtk_widget_show_all(banner.window);
+
+ /* Scrolling */
+ gtk_widget_size_request(hbox, &requisition);
+ if(notify_config.banner_width > 0)
+ banner_width = notify_config.banner_width;
+ else
+ banner_width = gdk_screen_width();
+ if(requisition.width > banner_width) {
+ /* Line is too big for screen! */
+ /* Double the entrybox into hbox */
+ GtkWidget *separator, *second_entrybox;
+
+ separator = gtk_vseparator_new();
+ gtk_box_pack_start(GTK_BOX(hbox), separator,
+ FALSE, FALSE, 0);
+ second_entrybox = create_entrybox(msg_list);
+ gtk_box_pack_start(GTK_BOX(hbox), second_entrybox, FALSE, FALSE, 0);
+
+ gtk_widget_show_all(banner.window);
+ gtk_widget_size_request(hbox, &requisition_after);
+
+ G_LOCK(sdata);
+ sdata.banner_width = requisition_after.width - requisition.width;
+ sdata.adj =
+ gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW
+ (banner.scrolled_win));
+ G_UNLOCK(sdata);
+
+ banner.scrolling = TRUE;
+ if(banner.timeout_id) {
+ g_source_remove(banner.timeout_id);
+ banner.timeout_id = 0;
+ }
+ banner.timeout_id =
+ g_timeout_add(notify_config.banner_speed, scroller, NULL);
+ }
+ else {
+ banner.scrolling = FALSE;
+ if(banner.timeout_id) {
+ g_source_remove(banner.timeout_id);
+ banner.timeout_id = 0;
+ }
+ G_LOCK(sdata);
+ sdata.banner_width = 0;
+ sdata.adj = NULL;
+ G_UNLOCK(sdata);
+ }
+
+ /* menu */
+ banner_ui_manager = gtk_ui_manager_new();
+ banner_action_group = cm_menu_create_action_group_full(banner_ui_manager,"BannerPopup", banner_popup_entries,
+ G_N_ELEMENTS(banner_popup_entries), (gpointer)NULL);
+ MENUITEM_ADDUI_MANAGER(banner_ui_manager, "/", "Menus", "Menus", GTK_UI_MANAGER_MENUBAR)
+ MENUITEM_ADDUI_MANAGER(banner_ui_manager, "/Menus", "BannerPopup", "BannerPopup", GTK_UI_MANAGER_MENU)
+ MENUITEM_ADDUI_MANAGER(banner_ui_manager, "/Menus/BannerPopup", "Reply", "BannerPopup/Reply", GTK_UI_MANAGER_MENUITEM)
+
+ banner_popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
+ gtk_ui_manager_get_widget(banner_ui_manager, "/Menus/BannerPopup")) );
+ g_signal_connect(banner_popup,"selection-done",
+ G_CALLBACK(notification_banner_popup_done), NULL);
+}
+
+static gboolean notification_banner_configure(GtkWidget *widget,
+ GdkEventConfigure *event,
+ gpointer data)
+{
+ gtk_window_get_position(GTK_WINDOW(gtk_widget_get_toplevel(widget)),
+ &(notify_config.banner_root_x),
+ &(notify_config.banner_root_y));
+ return TRUE;
+}
+
+static gboolean scroller(gpointer data)
+{
+ // don't scroll during popup open
+ if(banner_popup_open)
+ return banner.scrolling;
+
+ while(gtk_events_pending())
+ gtk_main_iteration();
+ G_LOCK(sdata);
+ if(sdata.adj && GTK_IS_ADJUSTMENT(sdata.adj)) {
+ if(sdata.adj->value != sdata.banner_width)
+ gtk_adjustment_set_value(sdata.adj, sdata.adj->value + 1);
+ else
+ gtk_adjustment_set_value(sdata.adj, 0);
+ gtk_adjustment_value_changed(sdata.adj);
+ }
+ G_UNLOCK(sdata);
+ while(gtk_events_pending())
+ gtk_main_iteration();
+ return banner.scrolling;
+}
+
+static GtkWidget* create_entrybox(GSList *msg_list)
+{
+ GtkWidget *entrybox;
+ GdkColor fg;
+ GdkColor bg;
+ gint list_length;
+
+ list_length = g_slist_length(msg_list);
+
+ if(notify_config.banner_enable_colors) {
+ gtkut_convert_int_to_gdk_color(notify_config.banner_color_bg,&bg);
+ gtkut_convert_int_to_gdk_color(notify_config.banner_color_fg,&fg);
+ }
+
+ if(banner.entries) {
+ g_free(banner.entries);
+ banner.entries = NULL;
+ }
+
+ entrybox = gtk_hbox_new(FALSE, 5);
+ if(list_length) {
+ GSList *walk;
+ gint ii = 0;
+ banner.entries = g_new(NotificationBannerEntry, list_length);
+ for(walk = msg_list; walk != NULL; walk = g_slist_next(walk)) {
+ GtkWidget *label1;
+ GtkWidget *label2;
+ GtkWidget *label3;
+ GtkWidget *label4;
+ GtkWidget *label5;
+ GtkWidget *label6;
+ GtkWidget *ebox;
+ CollectedMsg *cmsg = walk->data;
+
+ if(ii > 0) {
+ GtkWidget *separator;
+ separator = gtk_vseparator_new();
+ gtk_box_pack_start(GTK_BOX(entrybox), separator, FALSE, FALSE, 0);
+ }
+
+ ebox = gtk_event_box_new();
+ gtk_box_pack_start(GTK_BOX(entrybox), ebox, FALSE, FALSE, 0);
+ gtk_widget_set_events(ebox,
+ GDK_POINTER_MOTION_MASK |
+ GDK_BUTTON_PRESS_MASK);
+
+ if(notify_config.banner_enable_colors)
+ gtk_widget_modify_bg(ebox,GTK_STATE_NORMAL,&bg);
+
+ banner.entries[ii].table = gtk_table_new(3, 2, FALSE);
+ gtk_container_add(GTK_CONTAINER(ebox),banner.entries[ii].table);
+ g_signal_connect(ebox, "enter-notify-event",
+ G_CALLBACK(notification_banner_swap_colors),
+ banner.entries[ii].table);
+ g_signal_connect(ebox, "leave-notify-event",
+ G_CALLBACK(notification_banner_swap_colors),
+ banner.entries[ii].table);
+ g_signal_connect(ebox, "button-press-event",
+ G_CALLBACK(notification_banner_button_press),
+ cmsg);
+
+ label1 = gtk_label_new(prefs_common_translated_header_name("From:"));
+ gtk_misc_set_alignment(GTK_MISC(label1), 0, 0.5);
+ gtk_table_attach_defaults(GTK_TABLE(banner.entries[ii].table),
+ label1, 0, 1, 0, 1);
+ label2 = gtk_label_new(prefs_common_translated_header_name("Subject:"));
+ gtk_misc_set_alignment(GTK_MISC(label2), 0, 0.5);
+ gtk_table_attach_defaults(GTK_TABLE(banner.entries[ii].table),
+ label2, 0, 1, 1, 2);
+ label3 = gtk_label_new(_("Folder:"));
+ gtk_misc_set_alignment(GTK_MISC(label3), 0, 0.5);
+ gtk_table_attach_defaults(GTK_TABLE(banner.entries[ii].table),
+ label3, 0, 1, 2, 3);
+
+ label4 = gtk_label_new(cmsg->from);
+ gtk_misc_set_alignment(GTK_MISC(label4), 0, 0.5);
+ gtk_table_attach_defaults(GTK_TABLE(banner.entries[ii].table),
+ label4, 1, 2, 0, 1);
+ label5 = gtk_label_new(cmsg->subject);
+ gtk_misc_set_alignment(GTK_MISC(label5), 0, 0.5);
+ gtk_table_attach_defaults(GTK_TABLE(banner.entries[ii].table),
+ label5, 1, 2, 1, 2);
+ label6 = gtk_label_new(cmsg->folderitem_name);
+ gtk_misc_set_alignment(GTK_MISC(label6), 0, 0.5);
+ gtk_table_attach_defaults(GTK_TABLE(banner.entries[ii].table),
+ label6, 1, 2, 2, 3);
+ gtk_table_set_col_spacings(GTK_TABLE(banner.entries[ii].table), 5);
+ ii++;
+ /* Color */
+ if(notify_config.banner_enable_colors) {
+ gtk_widget_modify_fg(label1,GTK_STATE_NORMAL,&fg);
+ gtk_widget_modify_fg(label2,GTK_STATE_NORMAL,&fg);
+ gtk_widget_modify_fg(label3,GTK_STATE_NORMAL,&fg);
+ gtk_widget_modify_fg(label4,GTK_STATE_NORMAL,&fg);
+ gtk_widget_modify_fg(label5,GTK_STATE_NORMAL,&fg);
+ gtk_widget_modify_fg(label6,GTK_STATE_NORMAL,&fg);
+ }
+ }
+ }
+ else {
+ /* We have an empty list -- create an empty dummy element */
+ GtkWidget *label;
+
+ banner.entries = g_new(NotificationBannerEntry, 1);
+ banner.entries[0].table = gtk_table_new(3, 1, FALSE);
+ label = gtk_label_new("");
+ gtk_table_attach_defaults(GTK_TABLE(banner.entries[0].table),
+ label, 0, 1, 0, 1);
+ label = gtk_label_new("");
+ gtk_table_attach_defaults(GTK_TABLE(banner.entries[0].table),
+ label, 0, 1, 1, 2);
+ label = gtk_label_new("");
+ gtk_table_attach_defaults(GTK_TABLE(banner.entries[0].table),
+ label, 0, 1, 2, 3);
+ gtk_box_pack_start(GTK_BOX(entrybox), banner.entries[0].table,
+ FALSE, FALSE, 0);
+ }
+ return entrybox;
+}
+
+static gboolean notification_banner_swap_colors(GtkWidget *widget,
+ GdkEventCrossing *event,
+ gpointer data)
+{
+ GList *children;
+ GList *walk;
+ GdkColor *old_bg;
+
+ children = gtk_container_get_children(GTK_CONTAINER(data));
+
+ old_bg = gdk_color_copy(&(gtk_widget_get_style(widget)->bg[GTK_STATE_NORMAL]));
+ if(children)
+ gtk_widget_modify_bg(widget,GTK_STATE_NORMAL,
+ &(gtk_widget_get_style(GTK_WIDGET(children->data))->fg[GTK_STATE_NORMAL]));
+
+ for(walk = children; walk; walk = walk->next)
+ gtk_widget_modify_fg(GTK_WIDGET(walk->data),GTK_STATE_NORMAL,old_bg);
+
+ g_list_free(children);
+ gdk_color_free(old_bg);
+ return FALSE;
+}
+
+static gboolean notification_banner_button_press(GtkWidget *widget,
+ GdkEventButton *button,
+ gpointer data)
+{
+ gboolean done;
+ done = FALSE;
+ if(button->type == GDK_BUTTON_PRESS) {
+ CollectedMsg *cmsg = (CollectedMsg*) data;
+ if(button->button == 1) {
+ /* jump to that message */
+ if(cmsg->msginfo) {
+ gchar *path;
+ path = procmsg_get_message_file_path(cmsg->msginfo);
+ mainwindow_jump_to(path, TRUE);
+ g_free(path);
+ }
+ done = TRUE;
+ }
+ else if(button->button == 2) {
+ gtk_window_begin_move_drag(GTK_WINDOW(gtk_widget_get_toplevel(widget)),
+ button->button, button->x_root, button->y_root,
+ button->time);
+ }
+ else if(button->button == 3) {
+ current_msginfo = cmsg->msginfo;
+ notification_banner_show_popup(widget, button);
+ banner_popup_open = TRUE;
+ done = TRUE;
+ }
+ }
+ return done;
+}
+
+static void notification_banner_show_popup(GtkWidget *widget,
+ GdkEventButton *event)
+{
+ int button, event_time;
+
+ if(event) {
+ button = event->button;
+ event_time = event->time;
+ }
+ else {
+ button = 0;
+ event_time = gtk_get_current_event_time();
+ }
+
+ gtk_menu_popup(GTK_MENU(banner_popup), NULL, NULL, NULL, NULL,
+ button, event_time);
+}
+
+static void notification_banner_popup_done(GtkMenuShell *menushell,
+ gpointer user_data)
+{
+ current_msginfo = NULL;
+ banner_popup_open = FALSE;
+}
+
+static void banner_menu_reply_cb(GtkAction *action, gpointer data)
+{
+ MainWindow *mainwin;
+ MessageView *messageview;
+ GSList *msginfo_list = NULL;
+
+ if(!(mainwin = mainwindow_get_mainwindow()))
+ return;
+
+ if(!(messageview = (MessageView*)mainwin->messageview))
+ return;
+
+ g_return_if_fail(current_msginfo);
+
+ msginfo_list = g_slist_prepend(msginfo_list, current_msginfo);
+ compose_reply_from_messageview(messageview, msginfo_list,
+ prefs_common_get_prefs()->reply_with_quote ?
+ COMPOSE_REPLY_WITH_QUOTE : COMPOSE_REPLY_WITHOUT_QUOTE);
+ g_slist_free(msginfo_list);
+}
+
+#endif /* NOTIFICATION_BANNER */
diff --git a/src/plugins/notification/notification_banner.h b/src/plugins/notification/notification_banner.h
@@ -0,0 +1,32 @@
+/* Notification plugin for Claws-Mail
+ * Copyright (C) 2005-2007 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NOTIFICATION_BANNER_H
+#define NOTIFICATION_BANNER_H NOTIFICATION_BANNER_H
+
+#include "pluginconfig.h"
+
+#ifdef NOTIFICATION_BANNER
+
+#define BANNER_SPECIFIC_FOLDER_ID_STR "banner"
+
+void notification_banner_show(GSList*);
+void notification_banner_destroy(void);
+
+#endif /* NOTIFICATION_BANNER */
+
+#endif /* NOTIFICATION_BANNER_H */
diff --git a/src/plugins/notification/notification_command.c b/src/plugins/notification/notification_command.c
@@ -0,0 +1,119 @@
+/* Notification plugin for Claws-Mail
+ * Copyright (C) 2005-2007 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "pluginconfig.h"
+
+#ifdef NOTIFICATION_COMMAND
+
+#include <string.h>
+#include <glib.h>
+#include "common/utils.h"
+#include "folder.h"
+#include "notification_command.h"
+#include "notification_prefs.h"
+#include "notification_foldercheck.h"
+
+typedef struct {
+ gboolean blocked;
+ guint timeout_id;
+} NotificationCommand;
+
+static gboolean command_timeout_fun(gpointer data);
+
+static NotificationCommand command;
+
+G_LOCK_DEFINE_STATIC(command);
+
+void notification_command_msg(MsgInfo *msginfo)
+{
+ gchar *ret_str, *buf;
+ gsize by_read = 0, by_written = 0;
+ FolderType ftype;
+
+ if(!msginfo || !notify_config.command_enabled || !MSG_IS_NEW(msginfo->flags))
+ return;
+
+ if(!notify_config.command_enabled || !MSG_IS_NEW(msginfo->flags))
+ return;
+
+ if(notify_config.command_folder_specific) {
+ guint id;
+ GSList *list;
+ gchar *identifier;
+ gboolean found = FALSE;
+
+ if(!(msginfo->folder))
+ return;
+
+ identifier = folder_item_get_identifier(msginfo->folder);
+
+ id =
+ notification_register_folder_specific_list(COMMAND_SPECIFIC_FOLDER_ID_STR);
+ list = notification_foldercheck_get_list(id);
+ for(; (list != NULL) && !found; list = g_slist_next(list)) {
+ gchar *list_identifier;
+ FolderItem *list_item = (FolderItem*) list->data;
+
+ list_identifier = folder_item_get_identifier(list_item);
+ if(!strcmp2(list_identifier, identifier))
+ found = TRUE;
+
+ g_free(list_identifier);
+ }
+ g_free(identifier);
+
+ if(!found)
+ return;
+ } /* folder specific */
+
+ ftype = msginfo->folder->folder->klass->type;
+
+ buf = g_strdup(notify_config.command_line);
+
+ G_LOCK(command);
+
+ if(!command.blocked) {
+ /* execute command */
+ command.blocked = TRUE;
+ ret_str = g_locale_from_utf8(buf,strlen(buf),&by_read,&by_written,NULL);
+ if(ret_str && by_written) {
+ g_free(buf);
+ buf = ret_str;
+ }
+ execute_command_line(buf, TRUE);
+ g_free(buf);
+ }
+
+ /* block further execution for some time,
+ no matter if it was blocked or not */
+ if(command.timeout_id)
+ g_source_remove(command.timeout_id);
+ command.timeout_id = g_timeout_add(notify_config.command_timeout,
+ command_timeout_fun, NULL);
+ G_UNLOCK(command);
+}
+
+static gboolean command_timeout_fun(gpointer data)
+{
+ G_LOCK(command);
+ command.timeout_id = 0;
+ command.blocked = FALSE;
+ G_UNLOCK(command);
+ return FALSE;
+}
+
+#endif /* NOTIFICATION_COMMAND */
diff --git a/src/plugins/notification/notification_command.h b/src/plugins/notification/notification_command.h
@@ -0,0 +1,33 @@
+/* Notification plugin for Claws-Mail
+ * Copyright (C) 2005-2007 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NOTIFICATION_COMMAND_H
+#define NOTIFICATION_COMMAND_H NOTIFICATION_COMMAND_H
+
+#include "pluginconfig.h"
+
+#ifdef NOTIFICATION_COMMAND
+
+#include "procmsg.h"
+
+#define COMMAND_SPECIFIC_FOLDER_ID_STR "command"
+
+void notification_command_msg(MsgInfo*);
+
+#endif /* NOTIFICATION_COMMAND */
+
+#endif /* include guard */
diff --git a/src/plugins/notification/notification_core.c b/src/plugins/notification/notification_core.c
@@ -0,0 +1,699 @@
+/* Notification plugin for Claws-Mail
+ * Copyright (C) 2005-2007 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "pluginconfig.h"
+
+#include "folder.h"
+#include "folderview.h"
+#include "codeconv.h"
+#include "gtk/gtkutils.h"
+
+#include "notification_core.h"
+#include "notification_plugin.h"
+#include "notification_prefs.h"
+#include "notification_banner.h"
+#include "notification_popup.h"
+#include "notification_command.h"
+#include "notification_lcdproc.h"
+#include "notification_trayicon.h"
+#include "notification_indicator.h"
+
+#ifdef HAVE_LIBCANBERRA_GTK
+# include <canberra-gtk.h>
+#endif
+
+typedef struct {
+ GSList *collected_msgs;
+ GSList *folder_items;
+ gboolean unread_also;
+ gint max_msgs;
+ gint num_msgs;
+} TraverseCollect;
+
+static gboolean notification_traverse_collect(GNode*, gpointer);
+static void notification_new_unnotified_do_msg(MsgInfo*);
+static gboolean notification_traverse_hash_startup(GNode*, gpointer);
+
+static GHashTable *msg_count_hash;
+static NotificationMsgCount msg_count;
+
+#ifdef HAVE_LIBCANBERRA_GTK
+static gboolean canberra_new_email_is_playing = FALSE;
+#endif
+
+static void msg_count_hash_update_func(FolderItem*, gpointer);
+static void msg_count_update_from_hash(gpointer, gpointer, gpointer);
+static void msg_count_clear(NotificationMsgCount*);
+static void msg_count_add(NotificationMsgCount*,NotificationMsgCount*);
+static void msg_count_copy(NotificationMsgCount*,NotificationMsgCount*);
+
+void notification_core_global_includes_changed(void)
+{
+#ifdef NOTIFICATION_BANNER
+ notification_update_banner();
+#endif
+
+ if(msg_count_hash) {
+ g_hash_table_destroy(msg_count_hash);
+ msg_count_hash = NULL;
+ }
+ notification_update_msg_counts(NULL);
+}
+
+/* Hide/show main window */
+void notification_toggle_hide_show_window(void)
+{
+ MainWindow *mainwin;
+ if((mainwin = mainwindow_get_mainwindow()) == NULL)
+ return;
+
+ if(gtk_widget_get_visible(GTK_WIDGET(mainwin->window))) {
+ if((gdk_window_get_state(GTK_WIDGET(mainwin->window)->window)&GDK_WINDOW_STATE_ICONIFIED)
+ || mainwindow_is_obscured()) {
+ notification_show_mainwindow(mainwin);
+ }
+ else {
+ main_window_hide(mainwin);
+ }
+ }
+ else {
+ notification_show_mainwindow(mainwin);
+ }
+}
+
+void notification_update_msg_counts(FolderItem *removed_item)
+{
+ if(!msg_count_hash)
+ msg_count_hash = g_hash_table_new_full(g_str_hash,g_str_equal,
+ g_free,g_free);
+
+ folder_func_to_all_folders(msg_count_hash_update_func, msg_count_hash);
+
+ if(removed_item) {
+ gchar *identifier;
+ identifier = folder_item_get_identifier(removed_item);
+ if(identifier) {
+ g_hash_table_remove(msg_count_hash, identifier);
+ g_free(identifier);
+ }
+ }
+ msg_count_clear(&msg_count);
+ g_hash_table_foreach(msg_count_hash, msg_count_update_from_hash, NULL);
+#ifdef NOTIFICATION_LCDPROC
+ notification_update_lcdproc();
+#endif
+#ifdef NOTIFICATION_TRAYICON
+ notification_update_trayicon();
+#endif
+#ifdef NOTIFICATION_INDICATOR
+ notification_update_indicator();
+#endif
+ notification_update_urgency_hint();
+}
+
+static void msg_count_clear(NotificationMsgCount *count)
+{
+ count->new_msgs = 0;
+ count->unread_msgs = 0;
+ count->unreadmarked_msgs = 0;
+ count->marked_msgs = 0;
+ count->total_msgs = 0;
+}
+
+/* c1 += c2 */
+static void msg_count_add(NotificationMsgCount *c1,NotificationMsgCount *c2)
+{
+ c1->new_msgs += c2->new_msgs;
+ c1->unread_msgs += c2->unread_msgs;
+ c1->unreadmarked_msgs += c2->unreadmarked_msgs;
+ c1->marked_msgs += c2->marked_msgs;
+ c1->total_msgs += c2->total_msgs;
+}
+
+/* c1 = c2 */
+static void msg_count_copy(NotificationMsgCount *c1,NotificationMsgCount *c2)
+{
+ c1->new_msgs = c2->new_msgs;
+ c1->unread_msgs = c2->unread_msgs;
+ c1->unreadmarked_msgs = c2->unreadmarked_msgs;
+ c1->marked_msgs = c2->marked_msgs;
+ c1->total_msgs = c2->total_msgs;
+}
+
+gboolean get_flat_gslist_from_nodes_traverse_func(GNode *node, gpointer data)
+{
+ if(node->data) {
+ GSList **list = data;
+ *list = g_slist_prepend(*list, node->data);
+ }
+ return FALSE;
+}
+
+GSList* get_flat_gslist_from_nodes(GNode *node)
+{
+ GSList *retval = NULL;
+
+ g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, get_flat_gslist_from_nodes_traverse_func, &retval);
+ return retval;
+}
+
+void notification_core_get_msg_count_of_foldername(gchar *foldername, NotificationMsgCount *count)
+{
+ GList *list;
+ Folder *walk_folder;
+ Folder *folder = NULL;
+
+ for(list = folder_get_list(); list != NULL; list = list->next) {
+ walk_folder = list->data;
+ if(strcmp2(foldername, walk_folder->name) == 0) {
+ folder = walk_folder;
+ break;
+ }
+ }
+ if(!folder) {
+ debug_print("Notification plugin: Error: Could not find folder %s\n", foldername);
+ return;
+ }
+
+ msg_count_clear(count);
+ notification_core_get_msg_count(get_flat_gslist_from_nodes(folder->node), count);
+}
+
+void notification_core_get_msg_count(GSList *folder_list,
+ NotificationMsgCount *count)
+{
+ GSList *walk;
+
+ if(!folder_list)
+ msg_count_copy(count,&msg_count);
+ else {
+ msg_count_clear(count);
+ for(walk = folder_list; walk; walk = walk->next) {
+ gchar *identifier;
+ NotificationMsgCount *item_count;
+ FolderItem *item = (FolderItem*) walk->data;
+ identifier = folder_item_get_identifier(item);
+ if(identifier) {
+ item_count = g_hash_table_lookup(msg_count_hash,identifier);
+ g_free(identifier);
+ if(item_count)
+ msg_count_add(count, item_count);
+ }
+ }
+ }
+}
+
+static void msg_count_hash_update_func(FolderItem *item, gpointer data)
+{
+ gchar *identifier;
+ NotificationMsgCount *count;
+ GHashTable *hash = data;
+
+ if(!notify_include_folder_type(item->folder->klass->type,
+ item->folder->klass->uistr))
+ return;
+
+ identifier = folder_item_get_identifier(item);
+ if(!identifier)
+ return;
+
+ count = g_hash_table_lookup(hash, identifier);
+
+ if(!count) {
+ count = g_new0(NotificationMsgCount,1);
+ g_hash_table_insert(hash, identifier, count);
+ }
+ else
+ g_free(identifier);
+
+ count->new_msgs = item->new_msgs;
+ count->unread_msgs = item->unread_msgs;
+ count->unreadmarked_msgs = item->unreadmarked_msgs;
+ count->marked_msgs = item->marked_msgs;
+ count->total_msgs = item->total_msgs;
+}
+
+static void msg_count_update_from_hash(gpointer key, gpointer value,
+ gpointer data)
+{
+ NotificationMsgCount *count = value;
+ msg_count_add(&msg_count,count);
+}
+
+
+/* Replacement for the post-filtering hook:
+ Pseudocode by Colin:
+hook on FOLDER_ITEM_UPDATE_HOOKLIST
+ if hook flags & F_ITEM_UPDATE_MSGCOUNT
+ scan mails (folder_item_get_msg_list)
+ if MSG_IS_NEW(msginfo->flags) and not in hashtable
+ notify()
+ add to hashtable
+ procmsg_msg_list_free
+
+hook on MSGINFO_UPDATE_HOOKLIST
+ if hook flags & MSGINFO_UPDATE_FLAGS
+ if !MSG_IS_NEW(msginfo->flags)
+ remove from hashtable, it's now useless
+*/
+
+/* This hash table holds all mails that we already notified about,
+ and that still are marked as "new". The keys are the msgid's,
+ the values are just 1's stored in a pointer. */
+static GHashTable *notified_hash = NULL;
+
+
+/* Remove message from the notified_hash if
+ * - the message flags changed
+ * - the message is not new
+ * - the message is in the hash
+*/
+gboolean notification_notified_hash_msginfo_update(MsgInfoUpdate *msg_update)
+{
+ g_return_val_if_fail(msg_update != NULL, FALSE);
+
+ if((msg_update->flags & MSGINFO_UPDATE_FLAGS) &&
+ !MSG_IS_NEW(msg_update->msginfo->flags)) {
+
+ MsgInfo *msg;
+ gchar *msgid;
+
+ msg = msg_update->msginfo;
+ if(msg->msgid)
+ msgid = msg->msgid;
+ else {
+ debug_print("Notification Plugin: Message has no message ID!\n");
+ msgid = "";
+ }
+
+ g_return_val_if_fail(msg != NULL, FALSE);
+
+ if(g_hash_table_lookup(notified_hash, msgid) != NULL) {
+
+ debug_print("Notification Plugin: Removing message id %s from hash "
+ "table\n", msgid);
+ g_hash_table_remove(notified_hash, msgid);
+ }
+ }
+ return FALSE;
+}
+
+/* On startup, mark all new mails as already notified
+ * (by including them in the hash) */
+void notification_notified_hash_startup_init(void)
+{
+ GList *folder_list, *walk;
+ Folder *folder;
+
+ if(!notified_hash) {
+ notified_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, NULL);
+ debug_print("Notification Plugin: Hash table created\n");
+ }
+
+ folder_list = folder_get_list();
+ for(walk = folder_list; walk != NULL; walk = g_list_next(walk)) {
+ folder = walk->data;
+
+ g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
+ notification_traverse_hash_startup, NULL);
+ }
+}
+
+static gboolean notification_traverse_hash_startup(GNode *node, gpointer data)
+{
+ GSList *walk;
+ GSList *msg_list;
+ FolderItem *item = (FolderItem*) node->data;
+ gint new_msgs_left;
+
+ if(!(item->new_msgs))
+ return FALSE;
+
+ new_msgs_left = item->new_msgs;
+ msg_list = folder_item_get_msg_list(item);
+
+ for(walk = msg_list; walk; walk = g_slist_next(walk)) {
+ MsgInfo *msg = (MsgInfo*) walk->data;
+ if(MSG_IS_NEW(msg->flags)) {
+ gchar *msgid;
+
+ if(msg->msgid)
+ msgid = msg->msgid;
+ else {
+ debug_print("Notification Plugin: Message has no message ID!\n");
+ msgid = "";
+ }
+
+ /* If the message id is not yet in the hash, add it */
+ g_hash_table_insert(notified_hash, g_strdup(msgid),
+ GINT_TO_POINTER(1));
+ debug_print("Notification Plugin: Init: Added msg id %s to the hash\n",
+ msgid);
+ /* Decrement left count and check if we're already done */
+ new_msgs_left--;
+ if(new_msgs_left == 0)
+ break;
+ }
+ }
+ procmsg_msg_list_free(msg_list);
+ return FALSE;
+}
+
+void notification_core_free(void)
+{
+ if(notified_hash) {
+ g_hash_table_destroy(notified_hash);
+ notified_hash = NULL;
+ }
+ if(msg_count_hash) {
+ g_hash_table_destroy(msg_count_hash);
+ msg_count_hash = NULL;
+ }
+ debug_print("Notification Plugin: Freed internal data\n");
+}
+
+void notification_new_unnotified_msgs(FolderItemUpdateData *update_data)
+{
+ GSList *msg_list, *walk;
+
+ g_return_if_fail(notified_hash != NULL);
+
+ msg_list = folder_item_get_msg_list(update_data->item);
+
+ for(walk = msg_list; walk; walk = g_slist_next(walk)) {
+ MsgInfo *msg;
+ msg = (MsgInfo*) walk->data;
+
+ if(MSG_IS_NEW(msg->flags)) {
+ gchar *msgid;
+
+ if(msg->msgid)
+ msgid = msg->msgid;
+ else {
+ debug_print("Notification Plugin: Message has not message ID!\n");
+ msgid = "";
+ }
+
+ debug_print("Notification Plugin: Found msg %s, "
+ "checking if it is in hash...\n", msgid);
+ /* Check if message is in hash table */
+ if(g_hash_table_lookup(notified_hash, msgid) != NULL)
+ debug_print("yes.\n");
+ else {
+ /* Add to hashtable */
+ g_hash_table_insert(notified_hash, g_strdup(msgid),
+ GINT_TO_POINTER(1));
+ debug_print("no, added to table.\n");
+
+ /* Do the notification */
+ notification_new_unnotified_do_msg(msg);
+ }
+
+ } /* msg is 'new' */
+ } /* for all messages */
+ procmsg_msg_list_free(msg_list);
+}
+
+#ifdef HAVE_LIBCANBERRA_GTK
+static void canberra_finished_cb(ca_context *c, uint32_t id, int error, void *data)
+{
+ canberra_new_email_is_playing = FALSE;
+}
+#endif
+
+static void notification_new_unnotified_do_msg(MsgInfo *msg)
+{
+#ifdef NOTIFICATION_POPUP
+ notification_popup_msg(msg);
+#endif
+#ifdef NOTIFICATION_COMMAND
+ notification_command_msg(msg);
+#endif
+#ifdef NOTIFICATION_TRAYICON
+ notification_trayicon_msg(msg);
+#endif
+
+#ifdef HAVE_LIBCANBERRA_GTK
+ /* canberra */
+ if(notify_config.canberra_play_sounds && !canberra_new_email_is_playing) {
+ MainWindow *mainwin;
+ ca_proplist *proplist;
+ mainwin = mainwindow_get_mainwindow();
+ ca_proplist_create(&proplist);
+ ca_proplist_sets(proplist,CA_PROP_EVENT_ID ,"message-new-email");
+ canberra_new_email_is_playing = TRUE;
+ ca_context_play_full(ca_gtk_context_get(), 0, proplist, canberra_finished_cb, NULL);
+ ca_proplist_destroy(proplist);
+ }
+#endif
+}
+
+/* If folders is not NULL, then consider only those folder items
+ * If max_msgs is not 0, stop after collecting msg_msgs messages
+ */
+GSList* notification_collect_msgs(gboolean unread_also, GSList *folder_items,
+ gint max_msgs)
+{
+ GList *folder_list, *walk;
+ Folder *folder;
+ TraverseCollect collect_data;
+
+ collect_data.unread_also = unread_also;
+ collect_data.collected_msgs = NULL;
+ collect_data.folder_items = folder_items;
+ collect_data.max_msgs = max_msgs;
+ collect_data.num_msgs = 0;
+
+ folder_list = folder_get_list();
+ for(walk = folder_list; walk != NULL; walk = g_list_next(walk)) {
+ folder = walk->data;
+
+ g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
+ notification_traverse_collect, &collect_data);
+ }
+ return collect_data.collected_msgs;
+}
+
+void notification_collected_msgs_free(GSList *collected_msgs)
+{
+ if(collected_msgs) {
+ GSList *walk;
+ for(walk = collected_msgs; walk != NULL; walk = g_slist_next(walk)) {
+ CollectedMsg *msg = walk->data;
+ if(msg->from)
+ g_free(msg->from);
+ if(msg->subject)
+ g_free(msg->subject);
+ if(msg->folderitem_name)
+ g_free(msg->folderitem_name);
+ msg->msginfo = NULL;
+ g_free(msg);
+ }
+ g_slist_free(collected_msgs);
+ }
+}
+
+static gboolean notification_traverse_collect(GNode *node, gpointer data)
+{
+ TraverseCollect *cdata = data;
+ FolderItem *item = node->data;
+ gchar *folder_id_cur;
+
+ /* Obey global folder type limitations */
+ if(!notify_include_folder_type(item->folder->klass->type,
+ item->folder->klass->uistr))
+ return FALSE;
+
+ /* If a folder_items list was given, check it first */
+ if((cdata->folder_items) && (item->path != NULL) &&
+ ((folder_id_cur = folder_item_get_identifier(item)) != NULL)) {
+ FolderItem *list_item;
+ GSList *walk;
+ gchar *folder_id_list;
+ gboolean eq;
+ gboolean folder_in_list = FALSE;
+
+ for(walk = cdata->folder_items; walk != NULL; walk = g_slist_next(walk)) {
+ list_item = walk->data;
+ folder_id_list = folder_item_get_identifier(list_item);
+ eq = !strcmp2(folder_id_list,folder_id_cur);
+ g_free(folder_id_list);
+ if(eq) {
+ folder_in_list = TRUE;
+ break;
+ }
+ }
+ g_free(folder_id_cur);
+ if(!folder_in_list)
+ return FALSE;
+ }
+
+ if(item->new_msgs || (cdata->unread_also && item->unread_msgs)) {
+ GSList *msg_list = folder_item_get_msg_list(item);
+ GSList *walk;
+ for(walk = msg_list; walk != NULL; walk = g_slist_next(walk)) {
+ MsgInfo *msg_info = walk->data;
+ CollectedMsg *cmsg;
+
+ if((cdata->max_msgs != 0) && (cdata->num_msgs >= cdata->max_msgs))
+ return FALSE;
+
+ if(MSG_IS_NEW(msg_info->flags) ||
+ (MSG_IS_UNREAD(msg_info->flags) && cdata->unread_also)) {
+
+ cmsg = g_new(CollectedMsg, 1);
+ cmsg->from = g_strdup(msg_info->from ? msg_info->from : "");
+ cmsg->subject = g_strdup(msg_info->subject ? msg_info->subject : "");
+ if(msg_info->folder && msg_info->folder->name)
+ cmsg->folderitem_name = g_strdup(msg_info->folder->path);
+ else
+ cmsg->folderitem_name = g_strdup("");
+
+ cmsg->msginfo = msg_info;
+
+ cdata->collected_msgs = g_slist_prepend(cdata->collected_msgs, cmsg);
+ cdata->num_msgs++;
+ }
+ }
+ procmsg_msg_list_free(msg_list);
+ }
+
+ return FALSE;
+}
+
+gboolean notify_include_folder_type(FolderType ftype, gchar *uistr)
+{
+ gboolean retval;
+
+ retval = FALSE;
+ switch(ftype) {
+ case F_MH:
+ case F_MBOX:
+ case F_MAILDIR:
+ case F_IMAP:
+ if(notify_config.include_mail)
+ retval = TRUE;
+ break;
+ case F_NEWS:
+ if(notify_config.include_news)
+ retval = TRUE;
+ break;
+ case F_UNKNOWN:
+ if(uistr == NULL)
+ retval = FALSE;
+ else if(!strcmp(uistr, "vCalendar")) {
+ if(notify_config.include_calendar)
+ retval = TRUE;
+ }
+ else if(!strcmp(uistr, "RSSyl")) {
+ if(notify_config.include_rss)
+ retval = TRUE;
+ }
+ else
+ debug_print("Notification Plugin: Unknown folder type %d\n",ftype);
+ break;
+ default:
+ debug_print("Notification Plugin: Unknown folder type %d\n",ftype);
+ }
+
+ return retval;
+}
+
+static void fix_folderview_scroll(MainWindow *mainwin)
+{
+ static gboolean fix_done = FALSE;
+
+ if (fix_done)
+ return;
+
+ gtk_widget_queue_resize(mainwin->folderview->ctree);
+
+ fix_done = TRUE;
+}
+
+void notification_show_mainwindow(MainWindow *mainwin)
+{
+ gtk_window_deiconify(GTK_WINDOW(mainwin->window));
+ gtk_window_set_skip_taskbar_hint(GTK_WINDOW(mainwin->window), FALSE);
+ main_window_show(mainwin);
+ gtk_window_present(GTK_WINDOW(mainwin->window));
+ fix_folderview_scroll(mainwin);
+}
+
+#ifdef HAVE_LIBNOTIFY
+#define STR_MAX_LEN 511
+/* Returns a newly allocated string which needs to be freed */
+gchar* notification_libnotify_sanitize_str(gchar *in)
+{
+ gint i_out;
+ gchar tmp_str[STR_MAX_LEN+1];
+
+ if(in == NULL) return NULL;
+
+ i_out = 0;
+ while(*in) {
+ if(*in == '<') {
+ if(i_out+3 >= STR_MAX_LEN) break;
+ memcpy(&(tmp_str[i_out]),"<",4);
+ in++; i_out += 4;
+ }
+ else if(*in == '>') {
+ if(i_out+3 >= STR_MAX_LEN) break;
+ memcpy(&(tmp_str[i_out]),">",4);
+ in++; i_out += 4;
+ }
+ else if(*in == '&') {
+ if(i_out+4 >= STR_MAX_LEN) break;
+ memcpy(&(tmp_str[i_out]),"&",5);
+ in++; i_out += 5;
+ }
+ else {
+ if(i_out >= STR_MAX_LEN) break;
+ tmp_str[i_out++] = *in++;
+ }
+ }
+ tmp_str[i_out] = '\0';
+ return strdup(tmp_str);
+}
+
+gchar* notification_validate_utf8_str(gchar *text)
+{
+ gchar *utf8_str = NULL;
+
+ if(!g_utf8_validate(text, -1, NULL)) {
+ debug_print("Notification plugin: String is not valid utf8, "
+ "trying to fix it...\n");
+ /* fix it */
+ utf8_str = conv_codeset_strdup(text,
+ conv_get_locale_charset_str_no_utf8(),
+ CS_INTERNAL);
+ /* check if the fix worked */
+ if(utf8_str == NULL || !g_utf8_validate(utf8_str, -1, NULL)) {
+ debug_print("Notification plugin: String is still not valid utf8, "
+ "sanitizing...\n");
+ utf8_str = g_malloc(strlen(text)*2+1);
+ conv_localetodisp(utf8_str, strlen(text)*2+1, text);
+ }
+ }
+ else {
+ debug_print("Notification plugin: String is valid utf8\n");
+ utf8_str = g_strdup(text);
+ }
+ return utf8_str;
+}
+#endif
diff --git a/src/plugins/notification/notification_core.h b/src/plugins/notification/notification_core.h
@@ -0,0 +1,79 @@
+/* Notification plugin for Claws-Mail
+ * Copyright (C) 2005-2007 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NOTIFICATION_CORE_H
+#define NOTIFICATION_CORE_H NOTIFICATION_CORE_H
+
+#include "mainwindow.h"
+#include "folder.h"
+#include "procmsg.h"
+
+typedef struct {
+ gchar *from;
+ gchar *subject;
+ FolderItem *folder_item;
+ gchar *folderitem_name;
+ MsgInfo *msginfo;
+} CollectedMsg;
+
+typedef enum {
+ F_TYPE_MAIL=0,
+ F_TYPE_NEWS,
+ F_TYPE_CALENDAR,
+ F_TYPE_RSS,
+ F_TYPE_LAST
+} NotificationFolderType;
+
+typedef struct
+{
+ guint new_msgs;
+ guint unread_msgs;
+ guint unreadmarked_msgs;
+ guint marked_msgs;
+ guint total_msgs;
+} NotificationMsgCount;
+
+/* Collect new and possibly unread messages in all folders */
+GSList* notification_collect_msgs(gboolean, GSList*, gint);
+void notification_collected_msgs_free(GSList*);
+
+void notification_core_global_includes_changed(void);
+void notification_core_free(void);
+void notification_update_msg_counts(FolderItem*);
+void notification_core_get_msg_count(GSList*,NotificationMsgCount*);
+void notification_core_get_msg_count_of_foldername(gchar*, NotificationMsgCount*);
+void notification_new_unnotified_msgs(FolderItemUpdateData*);
+gboolean notification_notified_hash_msginfo_update(MsgInfoUpdate*);
+void notification_notified_hash_startup_init(void);
+
+void notification_show_mainwindow(MainWindow *mainwin);
+/* folder type specific settings */
+gboolean notify_include_folder_type(FolderType, gchar*);
+
+void notification_toggle_hide_show_window(void);
+
+#ifdef HAVE_LIBNOTIFY
+/* Sanitize a string to use with libnotify. Returns a freshly
+ * allocated string that must be freed by the user. */
+gchar* notification_libnotify_sanitize_str(gchar*);
+/* Returns a freshly allocated copy of the input string, which
+ * is guaranteed to be valid UTF8. The returned string has to
+ * be freed. */
+gchar* notification_validate_utf8_str(gchar*);
+#endif
+
+#endif /* NOTIFICATION_CORE_H */
diff --git a/src/plugins/notification/notification_foldercheck.c b/src/plugins/notification/notification_foldercheck.c
@@ -0,0 +1,1002 @@
+/* Notification plugin for Claws-Mail
+ * Copyright (C) 2005-2007 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* This code is based on foldersel.c in Claws Mail.
+ * Some functions are only slightly modified, almost 1:1 copies from there. */
+
+#include "pluginconfig.h"
+
+/* Basic definitions first */
+#include "common/defs.h"
+
+/* System includes */
+#include <string.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+
+/* Claws Mail includes */
+#include "manage_window.h"
+#include "folder.h"
+#include "stock_pixmap.h"
+#include "gtk/gtkutils.h"
+#include "common/utils.h"
+#include "common/prefs.h"
+#include "common/xml.h"
+#include "common/hooks.h"
+#include "prefs_common.h"
+
+/* local includes */
+#include "notification_foldercheck.h"
+
+/* enums and structures */
+enum {
+ FOLDERCHECK_FOLDERNAME,
+ FOLDERCHECK_FOLDERITEM,
+ FOLDERCHECK_PIXBUF,
+ FOLDERCHECK_PIXBUF_OPEN,
+ FOLDERCHECK_CHECK,
+ N_FOLDERCHECK_COLUMNS
+};
+
+typedef struct {
+ /* Data */
+ gchar *name;
+ GSList *list;
+ GtkTreeStore *tree_store;
+ /* Dialog box*/
+ GtkWidget *window;
+ GtkWidget *treeview;
+ gboolean cancelled;
+ gboolean finished;
+ gboolean recursive;
+} SpecificFolderArrayEntry;
+
+/* variables with file scope */
+static GdkPixbuf *folder_pixbuf;
+static GdkPixbuf *folderopen_pixbuf;
+static GdkPixbuf *foldernoselect_pixbuf;
+
+static GArray *specific_folder_array;
+static guint specific_folder_array_size;
+
+static guint hook_folder_update;
+
+
+/* defines */
+#define FOLDERCHECK_ARRAY "notification_foldercheck.xml"
+#define foldercheck_get_entry_from_id(id) \
+ ((id) < specific_folder_array_size) ? \
+ g_array_index(specific_folder_array,SpecificFolderArrayEntry*,(id)) : NULL
+
+/* function prototypes */
+static void folder_checked(guint);
+static void foldercheck_create_window(SpecificFolderArrayEntry*);
+static void foldercheck_destroy_window(SpecificFolderArrayEntry*);
+static gint foldercheck_folder_name_compare(GtkTreeModel*, GtkTreeIter*,
+ GtkTreeIter*, gpointer);
+static gboolean foldercheck_selected(GtkTreeSelection*,
+ GtkTreeModel*, GtkTreePath*,
+ gboolean, gpointer);
+
+static gint delete_event(GtkWidget*, GdkEventAny*, gpointer);
+static void foldercheck_ok(GtkButton*, gpointer);
+static void foldercheck_cancel(GtkButton*, gpointer);
+static void foldercheck_set_tree(SpecificFolderArrayEntry*);
+static void foldercheck_insert_gnode_in_store(GtkTreeStore*, GNode*,
+ GtkTreeIter*);
+static void foldercheck_append_item(GtkTreeStore*, FolderItem*,
+ GtkTreeIter*, GtkTreeIter*);
+static void foldercheck_recursive_cb(GtkToggleButton*, gpointer);
+static void folder_toggle_cb(GtkCellRendererToggle*, gchar*, gpointer);
+static void folder_toggle_recurse_tree(GtkTreeStore*, GtkTreeIter*, gint,
+ gboolean);
+static gboolean foldercheck_foreach_check(GtkTreeModel*, GtkTreePath*,
+ GtkTreeIter*, gpointer);
+static gboolean foldercheck_foreach_update_to_list(GtkTreeModel*, GtkTreePath*,
+ GtkTreeIter*, gpointer);
+static gchar *foldercheck_get_array_path(void);
+static gboolean my_folder_update_hook(gpointer, gpointer);
+static gboolean key_pressed(GtkWidget*, GdkEventKey*,gpointer);
+
+
+/* Creates an entry in the specific_folder_array, and fills it with a new
+ * SpecificFolderArrayEntry*. If specific_folder_array already has an entry
+ * with the same name, return its ID. (The ID is the index in the array.) */
+guint notification_register_folder_specific_list(gchar *node_name)
+{
+ SpecificFolderArrayEntry *entry;
+ gint ii = 0;
+
+ /* If array does not yet exist, create it. */
+ if(!specific_folder_array) {
+ specific_folder_array = g_array_new(FALSE, FALSE,
+ sizeof(SpecificFolderArrayEntry*));
+ specific_folder_array_size = 0;
+
+ /* Register hook for folder update */
+ /* "The hook is registered" is bound to "the array is allocated" */
+ hook_folder_update = hooks_register_hook(FOLDER_UPDATE_HOOKLIST,
+ my_folder_update_hook, NULL);
+ if(hook_folder_update == (guint) -1) {
+ debug_print("Warning: Failed to register hook to folder update "
+ "hooklist. "
+ "Strange things can occur when deleting folders.\n");
+ }
+ }
+
+ /* Check if we already have such a name. If so, return its id. */
+ while(ii < specific_folder_array_size) {
+ entry = g_array_index(specific_folder_array,SpecificFolderArrayEntry*,ii);
+ if(entry) {
+ if(!strcmp2(entry->name,node_name))
+ return ii;
+ }
+ ii++;
+ }
+
+ /* Create an entry with the corresponding node name. */
+ entry = g_new(SpecificFolderArrayEntry, 1);
+ entry->name = g_strdup(node_name);
+ entry->list = NULL;
+ entry->window = NULL;
+ entry->treeview = NULL;
+ entry->cancelled = FALSE;
+ entry->finished = FALSE;
+ entry->recursive = FALSE;
+ entry->tree_store = gtk_tree_store_new(N_FOLDERCHECK_COLUMNS,
+ G_TYPE_STRING,
+ G_TYPE_POINTER,
+ GDK_TYPE_PIXBUF,
+ GDK_TYPE_PIXBUF,
+ G_TYPE_BOOLEAN);
+ gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(entry->tree_store),
+ FOLDERCHECK_FOLDERNAME,
+ foldercheck_folder_name_compare,
+ NULL, NULL);
+ specific_folder_array = g_array_append_val(specific_folder_array, entry);
+ return specific_folder_array_size++;
+}
+
+/* This function is called in plugin_done. It frees the whole
+ * folder_specific_array with all its entries. */
+void notification_free_folder_specific_array(void)
+{
+ guint ii;
+ SpecificFolderArrayEntry *entry;
+
+ for(ii = 0; ii < specific_folder_array_size; ii++) {
+ entry = g_array_index(specific_folder_array,SpecificFolderArrayEntry*,ii);
+ if(entry) {
+ g_free(entry->name);
+ if(entry->list)
+ g_slist_free(entry->list);
+ if(entry->tree_store)
+ g_object_unref(G_OBJECT(entry->tree_store));
+ g_free(entry);
+ }
+ }
+ if(specific_folder_array) {
+ /* Free array */
+ g_array_free(specific_folder_array, TRUE);
+
+ /* Unregister hook */
+ hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_folder_update);
+ }
+ specific_folder_array = NULL;
+ specific_folder_array_size = 0;
+}
+
+/* Returns the list of the entry with the corresponding ID, or NULL if
+ * no such element exists. */
+GSList* notification_foldercheck_get_list(guint id)
+{
+ SpecificFolderArrayEntry *entry;
+
+ entry = foldercheck_get_entry_from_id(id);
+ if(entry) {
+ return entry->list;
+ }
+ else
+ return NULL;
+}
+
+/* Save selections in a common xml-file. Called when unloading the plugin.
+ * This is analog to folder.h::folder_write_list. */
+void notification_foldercheck_write_array(void)
+{
+ gchar *path;
+ XMLTag *tag;
+ XMLNode *xmlnode;
+ GNode *rootnode;
+ gint ii;
+ PrefFile *pfile;
+
+ /* Do nothing if foldercheck is not in use */
+ if(specific_folder_array_size == 0)
+ return;
+
+ path = foldercheck_get_array_path();
+ if((pfile = prefs_write_open(path)) == NULL) {
+ debug_print("Notification Plugin Error: Cannot open "
+ "file " FOLDERCHECK_ARRAY " for writing\n");
+ return;
+ }
+
+ /* XML declarations */
+ xml_file_put_xml_decl(pfile->fp);
+
+ /* Build up XML tree */
+
+ /* root node */
+ tag = xml_tag_new("foldercheckarray");
+ xmlnode = xml_node_new(tag, NULL);
+ rootnode = g_node_new(xmlnode);
+
+ /* branch nodes */
+ for(ii = 0; ii < specific_folder_array_size; ii++) {
+ GNode *branchnode;
+ GSList *walk;
+ SpecificFolderArrayEntry *entry;
+
+ entry = foldercheck_get_entry_from_id(ii);
+
+ tag = xml_tag_new("branch");
+ xml_tag_add_attr(tag, xml_attr_new("name",entry->name));
+ xmlnode = xml_node_new(tag, NULL);
+ branchnode = g_node_new(xmlnode);
+ g_node_append(rootnode, branchnode);
+
+ /* Write out the list as leaf nodes */
+ for(walk = entry->list; walk != NULL; walk = g_slist_next(walk)) {
+ gchar *identifier;
+ GNode *node;
+ FolderItem *item = (FolderItem*) walk->data;
+
+ identifier = folder_item_get_identifier(item);
+
+ tag = xml_tag_new("folderitem");
+ xml_tag_add_attr(tag, xml_attr_new("identifier", identifier));
+ g_free(identifier);
+ xmlnode = xml_node_new(tag, NULL);
+ node = g_node_new(xmlnode);
+ g_node_append(branchnode, node);
+ } /* for all list elements in branch node */
+
+ } /* for all branch nodes */
+
+ /* Actual writing and cleanup */
+ xml_write_tree(rootnode, pfile->fp);
+
+ if(prefs_file_close(pfile) < 0) {
+ debug_print("Notification Plugin Error: Failed to write "
+ "file " FOLDERCHECK_ARRAY "\n");
+ }
+
+ /* Free XML tree */
+ xml_free_tree(rootnode);
+}
+
+/* Read selections from a common xml-file. Called when loading the plugin.
+ * Returns TRUE if data has been read, FALSE if no data is available
+ * or an error occured.
+ * This is analog to folder.h::folder_read_list. */
+gboolean notification_foldercheck_read_array(void)
+{
+ gchar *path;
+ GNode *rootnode, *node, *branchnode;
+ XMLNode *xmlnode;
+ gboolean success = FALSE;
+
+ path = foldercheck_get_array_path();
+ if(!is_file_exist(path)) {
+ path = NULL;
+ return FALSE;
+ }
+
+ /* We don't do merging, so if the file existed, clear what we
+ have stored in memory right now.. */
+ notification_free_folder_specific_array();
+
+ /* .. and evaluate the file */
+ rootnode = xml_parse_file(path);
+ path = NULL;
+ if(!rootnode)
+ return FALSE;
+
+ xmlnode = rootnode->data;
+
+ /* Check that root entry is "foldercheckarray" */
+ if(strcmp2(xmlnode->tag->tag, "foldercheckarray") != 0) {
+ g_warning("wrong foldercheck array file\n");
+ xml_free_tree(rootnode);
+ return FALSE;
+ }
+
+ /* Process branch entries */
+ for(branchnode = rootnode->children; branchnode != NULL;
+ branchnode = branchnode->next) {
+ GList *list;
+ guint id;
+ SpecificFolderArrayEntry *entry = NULL;
+
+ xmlnode = branchnode->data;
+ if(strcmp2(xmlnode->tag->tag, "branch") != 0) {
+ g_warning("tag name != \"branch\"\n");
+ return FALSE;
+ }
+
+ /* Attributes of the branch nodes */
+ list = xmlnode->tag->attr;
+ for(; list != NULL; list = list->next) {
+ XMLAttr *attr = list->data;
+
+ if(attr && attr->name && attr->value && !strcmp2(attr->name, "name")) {
+ id = notification_register_folder_specific_list(attr->value);
+ entry = foldercheck_get_entry_from_id(id);
+ /* We have found something */
+ success = TRUE;
+ break;
+ }
+ }
+ if((list == NULL) || (entry == NULL)) {
+ g_warning("Did not find attribute \"name\" in tag \"branch\"\n");
+ continue; /* with next branch */
+ }
+
+ /* Now descent into the children of the brach, which are the folderitems */
+ for(node = branchnode->children; node != NULL; node = node->next) {
+ FolderItem *item = NULL;
+
+ /* These should all be leaves. */
+ if(!G_NODE_IS_LEAF(node))
+ g_warning("Subnodes in \"branch\" nodes should all be leaves. "
+ "Ignoring deeper subnodes..\n");
+
+ /* Check if tag is "folderitem" */
+ xmlnode = node->data;
+ if(strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
+ g_warning("tag name != \"folderitem\"\n");
+ continue; /* to next node in branch */
+ }
+
+ /* Attributes of the leaf nodes */
+ list = xmlnode->tag->attr;
+ for(; list != NULL; list = list->next) {
+ XMLAttr *attr = list->data;
+
+ if(attr && attr->name && attr->value &&
+ !strcmp2(attr->name, "identifier")) {
+ item = folder_find_item_from_identifier(attr->value);
+ break;
+ }
+ }
+ if((list == NULL) || (item == NULL)) {
+ g_warning("Did not find attribute \"identifier\" in tag "
+ "\"folderitem\"\n");
+ continue; /* with next leaf node */
+ }
+
+ /* Store all FolderItems in the list */
+ /* We started with a cleared array, so we don't need to check if
+ it's already in there. */
+ entry->list = g_slist_prepend(entry->list, item);
+
+ } /* for all subnodes in branch */
+
+ } /* for all branches */
+ return success;
+}
+
+/* Stolen from folder.c. Return value should NOT be freed. */
+static gchar *foldercheck_get_array_path(void)
+{
+ static gchar *filename = NULL;
+
+ if(!filename)
+ filename = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
+ FOLDERCHECK_ARRAY, NULL);
+ return filename;
+}
+
+/* Callback for selecting folders. If no selection dialog exists yet, create
+ * one and initialize the selection. The recurse through the whole model, and
+ * add all selected items to the list. */
+static void folder_checked(guint id)
+{
+ SpecificFolderArrayEntry *entry;
+ GSList *checked_list = NULL;
+
+ entry = foldercheck_get_entry_from_id(id);
+
+ /* Create window */
+ foldercheck_create_window(entry);
+ gtk_widget_show(entry->window);
+ manage_window_set_transient(GTK_WINDOW(entry->window));
+
+ entry->cancelled = entry->finished = FALSE;
+ while(entry->finished == FALSE)
+ gtk_main_iteration();
+
+ foldercheck_destroy_window(entry);
+
+ if(!entry->cancelled) {
+ /* recurse through the whole model, add all selected items to the list */
+ gtk_tree_model_foreach(GTK_TREE_MODEL(entry->tree_store),
+ foldercheck_foreach_check, &checked_list);
+
+ if(entry->list) {
+ g_slist_free(entry->list);
+ entry->list = NULL;
+ }
+ entry->list = g_slist_copy(checked_list);
+ g_slist_free(checked_list);
+ }
+
+ gtk_tree_store_clear(entry->tree_store);
+
+ entry->cancelled = FALSE;
+ entry->finished = FALSE;
+}
+
+/* Create the window for selecting folders with checkboxes */
+static void foldercheck_create_window(SpecificFolderArrayEntry *entry)
+{
+ GtkWidget *vbox;
+ GtkWidget *scrolledwin;
+ GtkWidget *confirm_area;
+ GtkWidget *checkbox;
+ GtkWidget *cancel_button;
+ GtkWidget *ok_button;
+ GtkTreeSelection *selection;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ static GdkGeometry geometry;
+
+ /* Create window */
+ entry->window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "notification_foldercheck");
+ gtk_window_set_title(GTK_WINDOW(entry->window), _("Select folder(s)"));
+ gtk_container_set_border_width(GTK_CONTAINER(entry->window), 4);
+ gtk_window_set_position(GTK_WINDOW(entry->window), GTK_WIN_POS_CENTER);
+ gtk_window_set_modal(GTK_WINDOW(entry->window), TRUE);
+ gtk_window_set_resizable(GTK_WINDOW(entry->window), TRUE);
+ gtk_window_set_wmclass
+ (GTK_WINDOW(entry->window), "folder_selection", "Claws Mail");
+ g_signal_connect(G_OBJECT(entry->window), "delete_event",
+ G_CALLBACK(delete_event), entry);
+ g_signal_connect(G_OBJECT(entry->window), "key_press_event",
+ G_CALLBACK(key_pressed), entry);
+ MANAGE_WINDOW_SIGNALS_CONNECT(entry->window);
+
+ /* vbox */
+ vbox = gtk_vbox_new(FALSE, 4);
+ gtk_container_add(GTK_CONTAINER(entry->window), vbox);
+
+ /* scrolled window */
+ scrolledwin = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
+ GTK_SHADOW_IN);
+ gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0);
+
+ /* pixbufs */
+ if(!folder_pixbuf)
+ stock_pixbuf_gdk(scrolledwin, STOCK_PIXMAP_DIR_CLOSE,
+ &folder_pixbuf);
+ if(!folderopen_pixbuf)
+ stock_pixbuf_gdk(scrolledwin, STOCK_PIXMAP_DIR_OPEN,
+ &folderopen_pixbuf);
+ if(!foldernoselect_pixbuf)
+ stock_pixbuf_gdk(scrolledwin, STOCK_PIXMAP_DIR_NOSELECT,
+ &foldernoselect_pixbuf);
+
+ /* Tree store */
+ foldercheck_set_tree(entry);
+ gtk_tree_model_foreach(GTK_TREE_MODEL(entry->tree_store),
+ foldercheck_foreach_update_to_list, entry);
+
+
+ /* tree view */
+ entry->treeview =
+ gtk_tree_view_new_with_model(GTK_TREE_MODEL(entry->tree_store));
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(entry->treeview), FALSE);
+ gtk_tree_view_set_search_column(GTK_TREE_VIEW(entry->treeview),
+ FOLDERCHECK_FOLDERNAME);
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(entry->treeview),
+ prefs_common_get_prefs()->use_stripes_everywhere);
+ gtk_tree_view_set_enable_tree_lines(GTK_TREE_VIEW(entry->treeview), FALSE);
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(entry->treeview));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
+ gtk_tree_selection_set_select_function(selection, foldercheck_selected,
+ NULL, NULL);
+
+ gtk_container_add(GTK_CONTAINER(scrolledwin), entry->treeview);
+
+ /* --- column 1 --- */
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_column_set_title(column, "sel");
+ gtk_tree_view_column_set_spacing(column, 2);
+
+ /* checkbox */
+ renderer = gtk_cell_renderer_toggle_new();
+ g_object_set(renderer, "xalign", 0.0, NULL);
+ gtk_tree_view_column_pack_start(column, renderer, TRUE);
+ g_signal_connect(renderer, "toggled", G_CALLBACK(folder_toggle_cb),entry);
+ gtk_tree_view_column_set_attributes(column, renderer,
+ "active", FOLDERCHECK_CHECK,NULL);
+
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(entry->treeview), column);
+
+ /* --- column 2 --- */
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_column_set_title(column, "Folder");
+ gtk_tree_view_column_set_spacing(column, 2);
+
+ /* pixbuf */
+ renderer = gtk_cell_renderer_pixbuf_new();
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ gtk_tree_view_column_set_attributes
+ (column, renderer,
+ "pixbuf", FOLDERCHECK_PIXBUF,
+ "pixbuf-expander-open", FOLDERCHECK_PIXBUF_OPEN,
+ "pixbuf-expander-closed", FOLDERCHECK_PIXBUF,
+ NULL);
+
+ /* text */
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_column_pack_start(column, renderer, TRUE);
+ gtk_tree_view_column_set_attributes(column, renderer,
+ "text", FOLDERCHECK_FOLDERNAME,
+ NULL);
+
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(entry->treeview), column);
+
+ /* recursive */
+ checkbox = gtk_check_button_new_with_label( _("select recursively"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox), FALSE);
+ g_signal_connect(G_OBJECT(checkbox), "toggled",
+ G_CALLBACK(foldercheck_recursive_cb), entry);
+ gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 10);
+
+ gtkut_stock_button_set_create(&confirm_area,
+ &cancel_button, GTK_STOCK_CANCEL,
+ &ok_button, GTK_STOCK_OK,
+ NULL, NULL);
+ gtk_box_pack_end(GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
+ gtk_widget_grab_default(ok_button);
+
+ g_signal_connect(G_OBJECT(ok_button), "clicked",
+ G_CALLBACK(foldercheck_ok), entry);
+ g_signal_connect(G_OBJECT(cancel_button), "clicked",
+ G_CALLBACK(foldercheck_cancel), entry);
+
+ if(!geometry.min_height) {
+ geometry.min_width = 360;
+ geometry.min_height = 360;
+ }
+
+ gtk_window_set_geometry_hints(GTK_WINDOW(entry->window), NULL, &geometry,
+ GDK_HINT_MIN_SIZE);
+
+ gtk_tree_view_expand_all(GTK_TREE_VIEW(entry->treeview));
+
+ gtk_widget_show_all(vbox);
+}
+
+static void foldercheck_destroy_window(SpecificFolderArrayEntry *entry)
+{
+ gtk_widget_destroy(entry->window);
+ entry->window = NULL;
+ entry->treeview = NULL;
+ entry->recursive = FALSE;
+}
+
+/* Handler for the delete event of the windows for selecting folders */
+static gint delete_event(GtkWidget *widget, GdkEventAny *event, gpointer data)
+{
+ foldercheck_cancel(NULL, data);
+ return TRUE;
+}
+
+/* sortable_set_sort_func */
+static gint foldercheck_folder_name_compare(GtkTreeModel *model,
+ GtkTreeIter *a, GtkTreeIter *b,
+ gpointer context)
+{
+ gchar *str_a = NULL, *str_b = NULL;
+ gint val = 0;
+ FolderItem *item_a = NULL, *item_b = NULL;
+ GtkTreeIter parent;
+
+ gtk_tree_model_get(model, a, FOLDERCHECK_FOLDERITEM, &item_a, -1);
+ gtk_tree_model_get(model, b, FOLDERCHECK_FOLDERITEM, &item_b, -1);
+
+ /* no sort for root folder */
+ if (!gtk_tree_model_iter_parent(GTK_TREE_MODEL(model), &parent, a))
+ return 0;
+
+ /* if both a and b are special folders, sort them according to
+ * their types (which is in-order). Note that this assumes that
+ * there are no multiple folders of a special type. */
+ if (item_a->stype != F_NORMAL && item_b->stype != F_NORMAL)
+ return item_a->stype - item_b->stype;
+
+ /* if b is normal folder, and a is not, b is smaller (ends up
+ * lower in the list) */
+ if (item_a->stype != F_NORMAL && item_b->stype == F_NORMAL)
+ return item_b->stype - item_a->stype;
+
+ /* if b is special folder, and a is not, b is larger (ends up
+ * higher in the list) */
+ if (item_a->stype == F_NORMAL && item_b->stype != F_NORMAL)
+ return item_b->stype - item_a->stype;
+
+ /* XXX g_utf8_collate_key() comparisons may speed things
+ * up when having large lists of folders */
+ gtk_tree_model_get(model, a, FOLDERCHECK_FOLDERNAME, &str_a, -1);
+ gtk_tree_model_get(model, b, FOLDERCHECK_FOLDERNAME, &str_b, -1);
+
+ /* otherwise just compare the folder names */
+ val = g_utf8_collate(str_a, str_b);
+
+ g_free(str_a);
+ g_free(str_b);
+
+ return val;
+}
+
+/* select_function of the gtk tree selection */
+static gboolean foldercheck_selected(GtkTreeSelection *selection,
+ GtkTreeModel *model, GtkTreePath *path,
+ gboolean currently_selected,gpointer data)
+{
+ GtkTreeIter iter;
+ FolderItem *item = NULL;
+
+ if (currently_selected)
+ return TRUE;
+
+ if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path))
+ return TRUE;
+
+ gtk_tree_model_get(model, &iter, FOLDERCHECK_FOLDERITEM, &item, -1);
+
+ return TRUE;
+}
+
+/* Callback for the OK-button of the folderselection dialog */
+static void foldercheck_ok(GtkButton *button, gpointer data)
+{
+ SpecificFolderArrayEntry *entry = (SpecificFolderArrayEntry*) data;
+
+ entry->finished = TRUE;
+}
+
+/* Callback for the Cancel-button of the folderselection dialog. Gets also
+ * called on a delete-event of the folderselection window. */
+static void foldercheck_cancel(GtkButton *button, gpointer data)
+{
+ SpecificFolderArrayEntry *entry = (SpecificFolderArrayEntry*) data;
+
+ entry->cancelled = TRUE;
+ entry->finished = TRUE;
+}
+
+/* Set tree of the tree-store. This includes getting the folder tree and
+ * storing it */
+static void foldercheck_set_tree(SpecificFolderArrayEntry *entry)
+{
+ Folder *folder;
+ GList *list;
+
+ for(list = folder_get_list(); list != NULL; list = list->next) {
+ folder = FOLDER(list->data);
+
+ if(folder == NULL) {
+ debug_print("Notification plugin::foldercheck_set_tree(): Found a NULL folder.\n");
+ continue;
+ }
+
+ /* Only regard built-in folders, because folders from plugins (such as RSS, calendar,
+ * or plugin-provided mailbox storage systems like Maildir or MBox) may vanish
+ * without letting us know. */
+ switch(folder->klass->type) {
+ case F_MH:
+ case F_IMAP:
+ case F_NEWS:
+ foldercheck_insert_gnode_in_store(entry->tree_store, folder->node, NULL);
+ break;
+ default:
+ break;
+ }
+ }
+
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(entry->tree_store),
+ FOLDERCHECK_FOLDERNAME,
+ GTK_SORT_ASCENDING);
+
+ if(GTK_IS_TREE_VIEW(entry->treeview))
+ gtk_tree_view_expand_all(GTK_TREE_VIEW(entry->treeview));
+}
+
+/* Helper function for foldercheck_set_tree */
+static void foldercheck_insert_gnode_in_store(GtkTreeStore *store, GNode *node,
+ GtkTreeIter *parent)
+{
+ FolderItem *item;
+ GtkTreeIter child;
+ GNode *iter;
+
+ g_return_if_fail(node != NULL);
+ g_return_if_fail(node->data != NULL);
+ g_return_if_fail(store != NULL);
+
+ item = FOLDER_ITEM(node->data);
+ foldercheck_append_item(store, item, &child, parent);
+
+ /* insert its children (this node as parent) */
+ for(iter = node->children; iter != NULL; iter = iter->next)
+ foldercheck_insert_gnode_in_store(store, iter, &child);
+}
+
+/* Helper function for foldercheck_insert_gnode_in_store */
+static void foldercheck_append_item(GtkTreeStore *store, FolderItem *item,
+ GtkTreeIter *iter, GtkTreeIter *parent)
+{
+ gchar *name, *tmpname;
+ GdkPixbuf *pixbuf, *pixbuf_open;
+ gboolean use_color;
+ PangoWeight weight = PANGO_WEIGHT_NORMAL;
+ GdkColor *foreground = NULL;
+ static GdkColor color_noselect = {0, COLOR_DIM, COLOR_DIM, COLOR_DIM};
+
+ name = tmpname = folder_item_get_name(item);
+
+ if (item->stype != F_NORMAL && FOLDER_IS_LOCAL(item->folder)) {
+ switch (item->stype) {
+ case F_INBOX:
+ if (!strcmp2(item->name, INBOX_DIR))
+ name = "Inbox";
+ break;
+ case F_OUTBOX:
+ if (!strcmp2(item->name, OUTBOX_DIR))
+ name = "Sent";
+ break;
+ case F_QUEUE:
+ if (!strcmp2(item->name, QUEUE_DIR))
+ name = "Queue";
+ break;
+ case F_TRASH:
+ if (!strcmp2(item->name, TRASH_DIR))
+ name = "Trash";
+ break;
+ case F_DRAFT:
+ if (!strcmp2(item->name, DRAFT_DIR))
+ name = "Drafts";
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0) {
+ name = g_strdup_printf("%s (%d)", name, item->total_msgs);
+ } else if (item->unread_msgs > 0) {
+ name = g_strdup_printf("%s (%d)", name, item->unread_msgs);
+ } else
+ name = g_strdup(name);
+
+ pixbuf = item->no_select ? foldernoselect_pixbuf : folder_pixbuf;
+ pixbuf_open =
+ item->no_select ? foldernoselect_pixbuf : folderopen_pixbuf;
+
+ if (folder_has_parent_of_type(item, F_DRAFT) ||
+ folder_has_parent_of_type(item, F_OUTBOX) ||
+ folder_has_parent_of_type(item, F_TRASH)) {
+ use_color = FALSE;
+ } else if (folder_has_parent_of_type(item, F_QUEUE)) {
+ use_color = (item->total_msgs > 0);
+ if (item->total_msgs > 0)
+ weight = PANGO_WEIGHT_BOLD;
+ } else {
+ if (item->unread_msgs > 0)
+ weight = PANGO_WEIGHT_BOLD;
+ use_color = (item->new_msgs > 0);
+ }
+
+ if (item->no_select)
+ foreground = &color_noselect;
+
+ /* insert this node */
+ gtk_tree_store_append(store, iter, parent);
+ gtk_tree_store_set(store, iter,
+ FOLDERCHECK_FOLDERNAME, name,
+ FOLDERCHECK_FOLDERITEM, item,
+ FOLDERCHECK_PIXBUF, pixbuf,
+ FOLDERCHECK_PIXBUF_OPEN, pixbuf_open,
+ -1);
+
+ g_free(tmpname);
+}
+
+/* Callback of the recursive-checkbox */
+static void foldercheck_recursive_cb(GtkToggleButton *button, gpointer data)
+{
+ SpecificFolderArrayEntry *entry = (SpecificFolderArrayEntry*) data;
+
+ entry->recursive = gtk_toggle_button_get_active(button);
+}
+
+/* Callback of the checkboxes corresponding to the folders. Obeys
+ * the "recursive" selection. */
+static void folder_toggle_cb(GtkCellRendererToggle *cell_renderer,
+ gchar *path_str, gpointer data)
+{
+ gboolean toggle_item;
+ GtkTreeIter iter;
+ SpecificFolderArrayEntry *entry = (SpecificFolderArrayEntry*) data;
+ GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
+
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(entry->tree_store), &iter, path);
+ gtk_tree_path_free(path);
+ gtk_tree_model_get(GTK_TREE_MODEL(entry->tree_store), &iter,
+ FOLDERCHECK_CHECK, &toggle_item, -1);
+ toggle_item = !toggle_item;
+
+ if(!entry->recursive)
+ gtk_tree_store_set(entry->tree_store, &iter,
+ FOLDERCHECK_CHECK, toggle_item, -1);
+ else {
+ GtkTreeIter child;
+ gtk_tree_store_set(entry->tree_store, &iter,
+ FOLDERCHECK_CHECK, toggle_item, -1);
+ if(gtk_tree_model_iter_children(GTK_TREE_MODEL(entry->tree_store),
+ &child, &iter))
+ folder_toggle_recurse_tree(entry->tree_store,&child,
+ FOLDERCHECK_CHECK,toggle_item);
+ }
+
+ while(gtk_events_pending())
+ gtk_main_iteration();
+}
+
+/* Helper function for folder_toggle_cb */
+/* This function calls itself recurively */
+static void folder_toggle_recurse_tree(GtkTreeStore *tree_store,
+ GtkTreeIter *iterp, gint column,
+ gboolean toggle_item)
+{
+ GtkTreeIter iter = *iterp;
+ GtkTreeIter next;
+
+ /* set the value of this iter */
+ gtk_tree_store_set(tree_store, &iter, column, toggle_item, -1);
+
+ /* do the same for the first child */
+ if(gtk_tree_model_iter_children(GTK_TREE_MODEL(tree_store),&next, &iter))
+ folder_toggle_recurse_tree(tree_store,&next,
+ FOLDERCHECK_CHECK, toggle_item);
+
+ /* do the same for the next sibling */
+ if(gtk_tree_model_iter_next(GTK_TREE_MODEL(tree_store), &iter))
+ folder_toggle_recurse_tree(tree_store, &iter,
+ FOLDERCHECK_CHECK, toggle_item);
+}
+
+/* Helper function to be used with a foreach statement of the model. Checks
+ * if a node is checked, and adds it to a list if it is. data us a (GSList**)
+ * where the result it to be stored */
+static gboolean foldercheck_foreach_check(GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter, gpointer data)
+{
+ gboolean toggled_item;
+ GSList **list = (GSList**) data;
+
+ gtk_tree_model_get(model, iter, FOLDERCHECK_CHECK, &toggled_item, -1);
+
+ if(toggled_item) {
+ FolderItem *item;
+ gtk_tree_model_get(model, iter, FOLDERCHECK_FOLDERITEM, &item, -1);
+ *list = g_slist_prepend(*list, item);
+ }
+
+ return FALSE;
+}
+
+/* Helper function to be used with a foreach statement of the model. Checks
+ * if a node is checked, and adds it to a list if it is. data us a (GSList**)
+ * where the result it to be stored */
+static gboolean foldercheck_foreach_update_to_list(GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ gchar *ident_tree, *ident_list;
+ FolderItem *item;
+ GSList *walk;
+ gboolean toggle_item = FALSE;
+ SpecificFolderArrayEntry *entry = (SpecificFolderArrayEntry*) data;
+
+ gtk_tree_model_get(model, iter, FOLDERCHECK_FOLDERITEM, &item, -1);
+
+ if(item->path != NULL)
+ ident_tree = folder_item_get_identifier(item);
+ else
+ return FALSE;
+
+ for(walk = entry->list; walk != NULL; walk = g_slist_next(walk)) {
+ FolderItem *list_item = (FolderItem*) walk->data;
+ ident_list = folder_item_get_identifier(list_item);
+ if(!strcmp2(ident_list,ident_tree)) {
+ toggle_item = TRUE;
+ g_free(ident_list);
+ break;
+ }
+ g_free(ident_list);
+ }
+ g_free(ident_tree);
+
+ gtk_tree_store_set(entry->tree_store, iter, FOLDERCHECK_CHECK,
+ toggle_item, -1);
+
+ return FALSE;
+}
+
+
+/* Callback for the folder selection dialog. Basically a wrapper around
+ * folder_checked that first resolves the name to an ID first. */
+void notification_foldercheck_sel_folders_cb(GtkButton *button, gpointer data)
+{
+ guint id;
+ gchar *name = (gchar*) data;
+
+ id = notification_register_folder_specific_list(name);
+
+ folder_checked(id);
+}
+
+static gboolean my_folder_update_hook(gpointer source, gpointer data)
+{
+ FolderUpdateData *hookdata = (FolderUpdateData*) source;
+
+ if(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
+ gint ii;
+ SpecificFolderArrayEntry *entry;
+ FolderItem *item = hookdata->item;
+
+ /* If that folder is in anywhere in the array, cut it out. */
+ for(ii = 0; ii < specific_folder_array_size; ii++) {
+ entry = foldercheck_get_entry_from_id(ii);
+ entry->list = g_slist_remove(entry->list, item);
+ } /* for all entries in the array */
+ } /* A FolderItem was deleted */
+
+ return FALSE;
+}
+
+static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
+{
+ if(event && (event->keyval == GDK_Escape)) {
+ foldercheck_cancel(NULL, data);
+ return TRUE;
+ }
+ return FALSE;
+}
diff --git a/src/plugins/notification/notification_foldercheck.h b/src/plugins/notification/notification_foldercheck.h
@@ -0,0 +1,32 @@
+/* Notification plugin for Claws-Mail
+ * Copyright (C) 2005-2007 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NOTIFICATION_FOLDERCHECK_H
+#define NOTIFICATION_FOLDERCHECK_H NOTIFICATION_FOLDERCHECK_H
+
+#include <gtk/gtk.h>
+
+#include "pluginconfig.h"
+
+void notification_foldercheck_sel_folders_cb(GtkButton*, gpointer);
+guint notification_register_folder_specific_list(gchar*);
+GSList* notification_foldercheck_get_list(guint);
+void notification_free_folder_specific_array(void);
+void notification_foldercheck_write_array(void);
+gboolean notification_foldercheck_read_array(void);
+
+#endif /* NOTIFICATION_FOLDERCHECK_H */
diff --git a/src/plugins/notification/notification_hotkeys.c b/src/plugins/notification/notification_hotkeys.c
@@ -0,0 +1,116 @@
+/* Notification plugin for Claws-Mail
+ * Copyright (C) 2005-2009 Holger Berndt and the Claws Mail Team.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "pluginconfig.h"
+
+#ifdef NOTIFICATION_HOTKEYS
+
+#include "notification_hotkeys.h"
+#include "notification_prefs.h"
+#include "notification_core.h"
+
+#include "gtkhotkey.h"
+
+#define HOTKEYS_APP_ID "claws-mail"
+#define HOTKEY_KEY_ID_TOGGLED "toggle-mainwindow"
+
+static GtkHotkeyInfo *hotkey_toggle_mainwindow = NULL;
+
+static void hotkey_toggle_mainwindow_activated(GtkHotkeyInfo *hotkey, guint event_time, gpointer data)
+{
+ g_return_if_fail(GTK_HOTKEY_IS_INFO(hotkey));
+ debug_print("Notification plugin: Toggled hide/show window due to hotkey %s activation\n", gtk_hotkey_info_get_signature(hotkey));
+ notification_toggle_hide_show_window();
+}
+
+static void unbind_toggle_mainwindow()
+{
+ GError *error;
+ GtkHotkeyRegistry *registry;
+
+ /* clean up old hotkey */
+ if(hotkey_toggle_mainwindow) {
+ if(gtk_hotkey_info_is_bound(hotkey_toggle_mainwindow)) {
+ error = NULL;
+ gtk_hotkey_info_unbind(hotkey_toggle_mainwindow, &error);
+ if(error) {
+ debug_print("Notification plugin: Failed to unbind toggle hotkey\n");
+ g_error_free(error);
+ return;
+ }
+ }
+ g_object_unref(hotkey_toggle_mainwindow);
+ hotkey_toggle_mainwindow = NULL;
+ }
+ registry = gtk_hotkey_registry_get_default();
+ if(gtk_hotkey_registry_has_hotkey(registry, HOTKEYS_APP_ID, HOTKEY_KEY_ID_TOGGLED)) {
+ error = NULL;
+ gtk_hotkey_registry_delete_hotkey(registry, HOTKEYS_APP_ID, HOTKEY_KEY_ID_TOGGLED, &error);
+ if(error) {
+ debug_print("Notification plugin: Failed to unregister toggle hotkey: %s\n", error->message);
+ g_error_free(error);
+ return;
+ }
+ }
+}
+
+static void update_hotkey_binding_toggle_mainwindow()
+{
+ GError *error;
+
+ /* don't do anything if no signature is given */
+ if(!notify_config.hotkeys_toggle_mainwindow || !strcmp(notify_config.hotkeys_toggle_mainwindow, ""))
+ return;
+
+ unbind_toggle_mainwindow();
+
+ /* (re)create hotkey info */
+ hotkey_toggle_mainwindow = gtk_hotkey_info_new(HOTKEYS_APP_ID, HOTKEY_KEY_ID_TOGGLED, notify_config.hotkeys_toggle_mainwindow, NULL);
+ if(!hotkey_toggle_mainwindow) {
+ debug_print("Notification plugin: Failed to create toggle hotkey for '%s'\n", notify_config.hotkeys_toggle_mainwindow);
+ return;
+ }
+
+ /* try to register hotkey */
+ error = NULL;
+ gtk_hotkey_info_bind(hotkey_toggle_mainwindow, &error);
+ if(error) {
+ debug_print("Notification plugin: Failed to bind toggle hotkey to '%s': %s\n", notify_config.hotkeys_toggle_mainwindow, error->message);
+ g_error_free(error);
+ return;
+ }
+
+ g_signal_connect(hotkey_toggle_mainwindow, "activated", G_CALLBACK(hotkey_toggle_mainwindow_activated), NULL);
+}
+
+void notification_hotkeys_update_bindings()
+{
+ debug_print("Notification plugin: Updating keybindings..\n");
+ if(notify_config.hotkeys_enabled) {
+ update_hotkey_binding_toggle_mainwindow();
+ }
+ else
+ notification_hotkeys_unbind_all();
+}
+
+void notification_hotkeys_unbind_all()
+{
+ debug_print("Notification plugin: Unbinding all keybindings..\n");
+ unbind_toggle_mainwindow();
+}
+
+#endif /* NOTIFICATION_HOTKEYS */
diff --git a/src/plugins/notification/notification_hotkeys.h b/src/plugins/notification/notification_hotkeys.h
@@ -0,0 +1,31 @@
+/* Notification plugin for Claws-Mail
+ * Copyright (C) 2005-2009 Holger Berndt and the Claws Mail Team.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NOTIFICATION_HOTKEYS_H
+#define NOTIFICATION_HOTKEYS_H NOTIFICATION_HOTKEYS_H
+
+#include "pluginconfig.h"
+
+#ifdef NOTIFICATION_HOTKEYS
+
+#include <glib.h>
+
+void notification_hotkeys_update_bindings();
+void notification_hotkeys_unbind_all();
+
+#endif /* NOTIFICATION_HOTKEYS */
+#endif /* NOTIFICATION_HOTKEYS_H */
diff --git a/src/plugins/notification/notification_indicator.c b/src/plugins/notification/notification_indicator.c
@@ -0,0 +1,174 @@
+/* Notification plugin for Claws-Mail
+ * Copyright (C) 2005-2009 Holger Berndt and the Claws Mail Team.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "pluginconfig.h"
+
+#ifdef NOTIFICATION_INDICATOR
+
+#include "notification_indicator.h"
+#include "notification_prefs.h"
+#include "notification_core.h"
+
+#include "folder.h"
+#include "common/utils.h"
+
+#include <libindicate/server.h>
+#include <libindicate/indicator.h>
+#include <libindicate/indicator-messages.h>
+
+static IndicateServer *server = NULL;
+static GHashTable *indicators = NULL;
+static gulong mainwin_state_changed_signal_id = 0;
+
+void notification_indicator_destroy(void)
+{
+ if(indicators) {
+ g_hash_table_destroy(indicators);
+ indicators = NULL;
+ }
+ if(server) {
+ indicate_server_hide(server);
+ g_object_unref(server);
+ server = NULL;
+ }
+ if(mainwin_state_changed_signal_id != 0) {
+ MainWindow *mainwin;
+ if((mainwin = mainwindow_get_mainwindow()) != NULL)
+ g_signal_handler_disconnect(mainwin->window, mainwin_state_changed_signal_id);
+ mainwin_state_changed_signal_id = 0;
+ }
+
+}
+
+static void show_claws_mail(IndicateIndicator *indicator, guint dummy, gpointer data)
+{
+ MainWindow *mainwin;
+
+ if((mainwin = mainwindow_get_mainwindow()) == NULL)
+ return;
+
+ notification_show_mainwindow(mainwin);
+ if(data) {
+ Folder *folder = data;
+ FolderItem *item = folder->inbox;
+
+ gchar *path = folder_item_get_identifier(item);
+ mainwindow_jump_to(path, FALSE);
+ g_free(path);
+ }
+}
+
+static void set_indicator_unread_count(IndicateIndicator *indicator, gint new, gint unread)
+{
+ gchar *count_str;
+
+ count_str = g_strdup_printf("%d / %d", new, unread);
+ indicate_indicator_set_property(indicator, INDICATE_INDICATOR_MESSAGES_PROP_COUNT, count_str);
+ g_free(count_str);
+}
+
+static void create_indicators(void)
+{
+ IndicateIndicator *indicator;
+ GList *cur_mb;
+
+ indicators = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_object_unref);
+
+ for(cur_mb = folder_get_list(); cur_mb; cur_mb = cur_mb->next) {
+ gchar *name;
+ Folder *folder = cur_mb->data;
+
+ if(!folder->name) {
+ debug_print("Notification plugin: Warning: Ignoring unnamed mailbox in indicator applet\n");
+ continue;
+ }
+ name = g_strdup(folder->name);
+
+ indicator = indicate_indicator_new();
+ indicate_indicator_set_property(indicator, INDICATE_INDICATOR_MESSAGES_PROP_NAME, name);
+ set_indicator_unread_count(indicator, 0, 0);
+ g_object_set_data(G_OBJECT(indicator), "new_msgs", GINT_TO_POINTER(0));
+ g_object_set_data(G_OBJECT(indicator), "unread_msgs", GINT_TO_POINTER(0));
+ g_signal_connect(indicator, "user-display", G_CALLBACK (show_claws_mail), folder);
+ indicate_indicator_show(indicator);
+ g_hash_table_insert(indicators, name, indicator);
+ }
+}
+
+static gboolean mainwin_state_event(GtkWidget *widget, GdkEventWindowState *event, gpointer user_data)
+{
+ if(notify_config.indicator_hide_minimized) {
+ MainWindow *mainwin;
+
+ if((mainwin = mainwindow_get_mainwindow()) == NULL)
+ return FALSE;
+
+ if((event->changed_mask & GDK_WINDOW_STATE_ICONIFIED) && (event->new_window_state & GDK_WINDOW_STATE_ICONIFIED)) {
+ gtk_window_set_skip_taskbar_hint(GTK_WINDOW(mainwin->window), TRUE);
+ }
+ else if((event->changed_mask & GDK_WINDOW_STATE_ICONIFIED) && !(event->new_window_state & GDK_WINDOW_STATE_ICONIFIED)) {
+ gtk_window_set_skip_taskbar_hint(GTK_WINDOW(mainwin->window), FALSE);
+ }
+ }
+ return FALSE;
+}
+
+void notification_update_indicator(void)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ if(!mainwin_state_changed_signal_id) {
+ MainWindow *mainwin;
+
+ if((mainwin = mainwindow_get_mainwindow()) != NULL)
+ mainwin_state_changed_signal_id = g_signal_connect(G_OBJECT(mainwin->window), "window-state-event", G_CALLBACK(mainwin_state_event), NULL);
+ }
+
+
+ if(!notify_config.indicator_enabled)
+ return;
+
+ if(!server) {
+ server = indicate_server_ref_default();
+ indicate_server_set_type (server, "message.mail");
+ indicate_server_set_desktop_file(server, get_desktop_file());
+ g_signal_connect(server, "server-display", G_CALLBACK(show_claws_mail), NULL);
+ indicate_server_show(server);
+ }
+
+ if(!indicators)
+ create_indicators();
+
+ /* check accounts for new/unread counts */
+ g_hash_table_iter_init(&iter, indicators);
+ while(g_hash_table_iter_next(&iter, &key, &value)) {
+ NotificationMsgCount count;
+ gchar *foldername = key;
+ IndicateIndicator *indicator = value;
+
+ notification_core_get_msg_count_of_foldername(foldername, &count);
+
+ set_indicator_unread_count(indicator, count.new_msgs, count.unread_msgs);
+ indicate_indicator_set_property(indicator, INDICATE_INDICATOR_MESSAGES_PROP_ATTENTION,
+ (count.new_msgs > 0) ? "true" : "false");
+ g_object_set_data(G_OBJECT(indicator), "new_msgs", GINT_TO_POINTER(count.new_msgs));
+ g_object_set_data(G_OBJECT(indicator), "unread_msgs", GINT_TO_POINTER(count.unread_msgs));
+ }
+}
+
+#endif /* NOTIFICATION_INDICATOR */
diff --git a/src/plugins/notification/notification_indicator.h b/src/plugins/notification/notification_indicator.h
@@ -0,0 +1,31 @@
+/* Notification plugin for Claws-Mail
+ * Copyright (C) 2005-2009 Holger Berndt and the Claws Mail Team.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NOTIFICATION_INDICATOR_H
+#define NOTIFICATION_INDICATOR_H NOTIFICATION_INDICATOR_H
+
+#include "pluginconfig.h"
+
+#ifdef NOTIFICATION_INDICATOR
+
+#include <glib.h>
+
+void notification_update_indicator(void);
+void notification_indicator_destroy(void);
+
+#endif /* NOTIFICATION_INDICATOR */
+#endif /* NOTIFICATION_INDICATOR_H */
diff --git a/src/plugins/notification/notification_lcdproc.c b/src/plugins/notification/notification_lcdproc.c
@@ -0,0 +1,172 @@
+/* Notification plugin for Claws-Mail
+ * Copyright (C) 2005-2007 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "pluginconfig.h"
+
+#ifdef NOTIFICATION_LCDPROC
+
+#include "notification_lcdproc.h"
+#include "notification_prefs.h"
+#include "notification_core.h"
+
+#include "common/socket.h"
+
+#include <string.h>
+#include <sys/socket.h>
+
+#define NOTIFICATION_LCDPROC_BUFFER_SIZE 8192
+
+void notification_sock_puts(SockInfo*, gchar*);
+void notification_lcdproc_send(gchar*);
+
+static SockInfo *sock = NULL;
+
+void notification_lcdproc_connect(void)
+{
+ gint len, count;
+ gchar buf[NOTIFICATION_LCDPROC_BUFFER_SIZE];
+
+ if(!notify_config.lcdproc_enabled)
+ return;
+
+ if(sock)
+ notification_lcdproc_disconnect();
+
+ sock = sock_connect(notify_config.lcdproc_hostname,
+ notify_config.lcdproc_port);
+ /*
+ * Quietly return when a connection fails; next attempt
+ * will be made when some folder info has been changed.
+ */
+ if(sock == NULL || sock->state == CONN_FAILED) {
+ debug_print("Could not connect to LCDd\n");
+ if(sock && sock->state == CONN_FAILED) {
+ sock_close(sock);
+ sock = NULL;
+ }
+ return;
+ }
+ else
+ debug_print("Connected to LCDd\n");
+
+ sock_set_nonblocking_mode(sock, TRUE);
+
+ /* Friendly people say "hello" first */
+ notification_sock_puts(sock, "hello");
+
+ /* FIXME: Ouch. Is this really the way to go? */
+ count = 50;
+ len = 0;
+ while((len <= 0) && (count-- >= 0)) {
+ g_usleep(125000);
+ len = sock_read(sock, buf, NOTIFICATION_LCDPROC_BUFFER_SIZE);
+ }
+
+/*
+ * This might not be a LCDProc server.
+ * FIXME: check LCD size, LCDd version etc
+ */
+
+ if (len <= 0) {
+ debug_print("Notification plugin: Can't communicate with "
+ "LCDd server! Are you sure that "
+ "there is a LCDd server running on %s:%d?\n",
+ notify_config.lcdproc_hostname, notify_config.lcdproc_port);
+ notification_lcdproc_disconnect();
+ return;
+ }
+
+ notification_lcdproc_send("client_set -name \"{Claws-Mail}\"");
+
+ notification_lcdproc_send("screen_add msg_counts");
+ notification_lcdproc_send("screen_set msg_counts -name {Claws-Mail Message Count}");
+
+ notification_lcdproc_send("widget_add msg_counts title title");
+ notification_lcdproc_send("widget_set msg_counts title {Claws-Mail}");
+ notification_lcdproc_send("widget_add msg_counts line1 string");
+ notification_lcdproc_send("widget_add msg_counts line2 string");
+ notification_lcdproc_send("widget_add msg_counts line3 string");
+
+ notification_update_msg_counts(NULL);
+}
+
+void notification_lcdproc_disconnect(void)
+{
+ if(sock) {
+#ifndef G_OS_WIN32
+ shutdown(sock->sock, SHUT_RDWR);
+#endif
+ sock_close(sock);
+ sock = NULL;
+ }
+}
+
+void notification_update_lcdproc(void)
+{
+ NotificationMsgCount count;
+ gchar *buf;
+
+ if(!notify_config.lcdproc_enabled || !sock)
+ return;
+
+ if(!sock || sock->state == CONN_FAILED) {
+ notification_lcdproc_connect();
+ return;
+ }
+
+ notification_core_get_msg_count(NULL, &count);
+
+ if((count.new_msgs + count.unread_msgs) > 0) {
+ buf =
+ g_strdup_printf("widget_set msg_counts line1 1 2 {%s: %d}",_("New"),
+ count.new_msgs);
+ notification_lcdproc_send(buf);
+ buf =
+ g_strdup_printf("widget_set msg_counts line2 1 3 {%s: %d}",_("Unread"),
+ count.unread_msgs);
+ notification_lcdproc_send(buf);
+ buf =
+ g_strdup_printf("widget_set msg_counts line3 1 4 {%s: %d}",_("Total"),
+ count.total_msgs);
+ notification_lcdproc_send(buf);
+ }
+ else {
+ buf = g_strdup_printf("widget_set msg_counts line1 1 2 {%s}",
+ _("No new messages"));
+ notification_lcdproc_send(buf);
+ buf = g_strdup_printf("widget_set msg_counts line2 1 3 {}");
+ notification_lcdproc_send(buf);
+ buf = g_strdup_printf("widget_set msg_counts line3 1 4 {}");
+ notification_lcdproc_send(buf);
+ }
+
+ g_free(buf);
+}
+
+void notification_sock_puts(SockInfo *socket, gchar *string)
+{
+ sock_write(socket, string, strlen(string));
+ sock_write(socket, "\n", 1);
+}
+
+void notification_lcdproc_send(gchar *string)
+{
+ notification_sock_puts(sock, string);
+ /* TODO: Check return message from LCDd */
+}
+
+#endif /* NOTIFICATION_LCDPROC */
diff --git a/src/plugins/notification/notification_lcdproc.h b/src/plugins/notification/notification_lcdproc.h
@@ -0,0 +1,34 @@
+/* Notification plugin for Claws-Mail
+ * Copyright (C) 2005-2007 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NOTIFICATION_LCDPROC_H
+#define NOTIFICATION_LCDPROC_H NOTIFICATION_LCDPROC_H
+
+#include "pluginconfig.h"
+
+#ifdef NOTIFICATION_LCDPROC
+
+#include <glib.h>
+
+void notification_lcdproc_connect(void);
+void notification_lcdproc_disconnect(void);
+
+void notification_update_lcdproc(void);
+
+#endif /* NOTIFICATION_LCDPROC */
+
+#endif /* include guard */
diff --git a/src/plugins/notification/notification_pixbuf.c b/src/plugins/notification/notification_pixbuf.c
@@ -0,0 +1,97 @@
+/* Notification plugin for Claws-Mail
+ * Copyright (C) 2005-2007 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "notification_pixbuf.h"
+
+/* The following files were created from the respective .png or
+ * xpm files with the command
+ * gdk-pixbuf-csource --raw --name=<name> file.png > <name>.h
+ */
+/* From the Claws-Mail distribution */
+#include "raw_claws_mail_logo_64x64.h"
+
+#include "stock_pixmap.h"
+
+static GdkPixbuf* notification_pixbuf[NOTIFICATION_PIXBUF_LAST];
+
+GdkPixbuf* notification_pixbuf_get(NotificationPixbuf wanted)
+{
+ if(!notification_pixbuf[wanted]) {
+ switch(wanted) {
+ case NOTIFICATION_CM_LOGO_64x64:
+ notification_pixbuf[wanted] =
+ gdk_pixbuf_new_from_inline(-1, raw_claws_mail_logo_64x64,
+ FALSE, NULL);
+ break;
+ case NOTIFICATION_TRAYICON_NEWMAIL:
+ stock_pixbuf_gdk(NULL, STOCK_PIXMAP_TRAY_NEWMAIL, &(notification_pixbuf[wanted]));
+ g_object_ref(notification_pixbuf[wanted]);
+ break;
+ case NOTIFICATION_TRAYICON_NEWMAIL_OFFLINE:
+ stock_pixbuf_gdk(NULL, STOCK_PIXMAP_TRAY_NEWMAIL_OFFLINE, &(notification_pixbuf[wanted]));
+ g_object_ref(notification_pixbuf[wanted]);
+ break;
+ case NOTIFICATION_TRAYICON_NEWMARKEDMAIL:
+ stock_pixbuf_gdk(NULL, STOCK_PIXMAP_TRAY_NEWMARKEDMAIL, &(notification_pixbuf[wanted]));
+ g_object_ref(notification_pixbuf[wanted]);
+ break;
+ case NOTIFICATION_TRAYICON_NEWMARKEDMAIL_OFFLINE:
+ stock_pixbuf_gdk(NULL, STOCK_PIXMAP_TRAY_NEWMARKEDMAIL_OFFLINE, &(notification_pixbuf[wanted]));
+ g_object_ref(notification_pixbuf[wanted]);
+ break;
+ case NOTIFICATION_TRAYICON_NOMAIL:
+ stock_pixbuf_gdk(NULL, STOCK_PIXMAP_TRAY_NOMAIL, &(notification_pixbuf[wanted]));
+ g_object_ref(notification_pixbuf[wanted]);
+ break;
+ case NOTIFICATION_TRAYICON_NOMAIL_OFFLINE:
+ stock_pixbuf_gdk(NULL, STOCK_PIXMAP_TRAY_NOMAIL_OFFLINE, &(notification_pixbuf[wanted]));
+ g_object_ref(notification_pixbuf[wanted]);
+ break;
+ case NOTIFICATION_TRAYICON_UNREADMAIL:
+ stock_pixbuf_gdk(NULL, STOCK_PIXMAP_TRAY_UNREADMAIL, &(notification_pixbuf[wanted]));
+ g_object_ref(notification_pixbuf[wanted]);
+ break;
+ case NOTIFICATION_TRAYICON_UNREADMAIL_OFFLINE:
+ stock_pixbuf_gdk(NULL, STOCK_PIXMAP_TRAY_UNREADMAIL_OFFLINE, &(notification_pixbuf[wanted]));
+ g_object_ref(notification_pixbuf[wanted]);
+ break;
+ case NOTIFICATION_TRAYICON_UNREADMARKEDMAIL:
+ stock_pixbuf_gdk(NULL, STOCK_PIXMAP_TRAY_UNREADMARKEDMAIL, &(notification_pixbuf[wanted]));
+ g_object_ref(notification_pixbuf[wanted]);
+ break;
+ case NOTIFICATION_TRAYICON_UNREADMARKEDMAIL_OFFLINE:
+ stock_pixbuf_gdk(NULL, STOCK_PIXMAP_TRAY_UNREADMARKEDMAIL_OFFLINE, &(notification_pixbuf[wanted]));
+ g_object_ref(notification_pixbuf[wanted]);
+ break;
+ case NOTIFICATION_PIXBUF_LAST:
+ break;
+ }
+ }
+ return notification_pixbuf[wanted];
+}
+
+void notification_pixbuf_free_all(void)
+{
+ gint ii;
+
+ for(ii = NOTIFICATION_CM_LOGO_64x64; ii < NOTIFICATION_PIXBUF_LAST; ii++) {
+ if(notification_pixbuf[ii]) {
+ g_object_unref(notification_pixbuf[ii]);
+ notification_pixbuf[ii] = NULL;
+ }
+ }
+}
diff --git a/src/plugins/notification/notification_pixbuf.h b/src/plugins/notification/notification_pixbuf.h
@@ -0,0 +1,47 @@
+/* Notification plugin for Claws-Mail
+ * Copyright (C) 2005-2007 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NOTIFICATION_PIXBUF_H
+#define NOTIFICATION_PIXBUF_H NOTIFICATION_PIXBUF_H
+
+#include <gdk/gdk.h>
+
+/* By convention, the online icons for the trayicon are
+ * immediately followed by the offline icons, so one can do
+ * offline = array[online+1] */
+typedef enum {
+ NOTIFICATION_CM_LOGO_64x64 = 0,
+ NOTIFICATION_TRAYICON_NEWMAIL,
+ NOTIFICATION_TRAYICON_NEWMAIL_OFFLINE,
+ NOTIFICATION_TRAYICON_NEWMARKEDMAIL,
+ NOTIFICATION_TRAYICON_NEWMARKEDMAIL_OFFLINE,
+ NOTIFICATION_TRAYICON_NOMAIL,
+ NOTIFICATION_TRAYICON_NOMAIL_OFFLINE,
+ NOTIFICATION_TRAYICON_UNREADMAIL,
+ NOTIFICATION_TRAYICON_UNREADMAIL_OFFLINE,
+ NOTIFICATION_TRAYICON_UNREADMARKEDMAIL,
+ NOTIFICATION_TRAYICON_UNREADMARKEDMAIL_OFFLINE,
+ NOTIFICATION_PIXBUF_LAST
+} NotificationPixbuf;
+
+/* The reference to the returned GdkPixbuf's belongs to notification_pixbuf,
+ * and shall not be removed outside. */
+GdkPixbuf* notification_pixbuf_get(NotificationPixbuf);
+
+void notification_pixbuf_free_all(void);
+
+#endif /* NOTIFICATION_PIXBUF_H */
diff --git a/src/plugins/notification/notification_plugin.c b/src/plugins/notification/notification_plugin.c
@@ -0,0 +1,479 @@
+/* Notification plugin for Claws-Mail
+ * Copyright (C) 2005-2007 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "pluginconfig.h"
+
+#include "common/claws.h"
+#include "common/version.h"
+#include "common/utils.h"
+#include "common/defs.h"
+#include "folder.h"
+#include "common/hooks.h"
+#include "procmsg.h"
+#include "mainwindow.h"
+#include "main.h"
+#include "gtk/gtkutils.h"
+
+#include "notification_plugin.h"
+#include "notification_core.h"
+#include "notification_prefs.h"
+#include "notification_banner.h"
+#include "notification_lcdproc.h"
+#include "notification_trayicon.h"
+#include "notification_indicator.h"
+#include "notification_hotkeys.h"
+#include "notification_foldercheck.h"
+#include "notification_pixbuf.h"
+#include "plugin.h"
+
+#if HAVE_LIBNOTIFY
+# include <libnotify/notify.h>
+#endif
+
+
+static gboolean my_folder_item_update_hook(gpointer, gpointer);
+static gboolean my_folder_update_hook(gpointer, gpointer);
+static gboolean my_msginfo_update_hook(gpointer, gpointer);
+static gboolean my_offline_switch_hook(gpointer, gpointer);
+static gboolean my_main_window_close_hook(gpointer, gpointer);
+static gboolean my_main_window_got_iconified_hook(gpointer, gpointer);
+static gboolean my_account_list_changed_hook(gpointer, gpointer);
+static gboolean my_update_theme_hook(gpointer, gpointer);
+
+#ifdef NOTIFICATION_TRAYICON
+static gboolean trayicon_startup_idle(gpointer);
+#endif
+
+static guint hook_f_item;
+static guint hook_f;
+static guint hook_m_info;
+static guint hook_offline;
+static guint hook_mw_close;
+static guint hook_got_iconified;
+static guint hook_account;
+static guint hook_theme_changed;
+
+#ifdef NOTIFICATION_BANNER
+static GSList* banner_collected_msgs;
+#endif
+
+void notification_update_urgency_hint(void)
+{
+ MainWindow *mainwin;
+ mainwin = mainwindow_get_mainwindow();
+ if(mainwin) {
+ NotificationMsgCount count;
+ gboolean active;
+ active = FALSE;
+ if(notify_config.urgency_hint_new || notify_config.urgency_hint_unread) {
+ notification_core_get_msg_count(NULL, &count);
+ if(notify_config.urgency_hint_new)
+ active = (active || (count.new_msgs > 0));
+ if(notify_config.urgency_hint_unread)
+ active = (active || (count.unread_msgs > 0));
+ }
+ gtk_window_set_urgency_hint(GTK_WINDOW(mainwin->window), active);
+ }
+}
+
+static gboolean my_account_list_changed_hook(gpointer source,
+ gpointer data)
+{
+ gboolean retVal = FALSE;
+
+#ifdef NOTIFICATION_TRAYICON
+ notification_update_msg_counts(NULL);
+ retVal = notification_trayicon_account_list_changed(source, data);
+
+#endif
+ return retVal;
+}
+
+static gboolean my_update_theme_hook(gpointer source, gpointer data)
+{
+ notification_pixbuf_free_all();
+#ifdef NOTIFICATION_TRAYICON
+ notification_update_trayicon();
+#endif
+ return FALSE;
+}
+
+static gboolean my_main_window_got_iconified_hook(gpointer source,
+ gpointer data)
+{
+ gboolean retVal = FALSE;
+#ifdef NOTIFICATION_TRAYICON
+ notification_update_msg_counts(NULL);
+ retVal = notification_trayicon_main_window_got_iconified(source, data);
+#endif
+ return retVal;
+}
+
+static gboolean my_main_window_close_hook(gpointer source, gpointer data)
+{
+ gboolean retVal = FALSE;
+#ifdef NOTIFICATION_TRAYICON
+ notification_update_msg_counts(NULL);
+ retVal = notification_trayicon_main_window_close(source, data);
+#endif
+ return retVal;
+}
+
+static gboolean my_offline_switch_hook(gpointer source, gpointer data)
+{
+#ifdef NOTIFICATION_TRAYICON
+ notification_update_msg_counts(NULL);
+#endif
+ return FALSE;
+}
+
+static gboolean my_folder_item_update_hook(gpointer source, gpointer data)
+{
+ FolderItemUpdateData *update_data = source;
+ FolderType ftype;
+ gchar *uistr;
+
+ g_return_val_if_fail(source != NULL, FALSE);
+
+#if defined(NOTIFICATION_LCDPROC) || defined(NOTIFICATION_TRAYICON) || defined(NOTIFICATION_INDICATOR)
+ notification_update_msg_counts(NULL);
+#else
+ if(notify_config.urgency_hint)
+ notification_update_msg_counts(NULL);
+#endif
+
+ /* Check if the folder types is to be notified about */
+ ftype = update_data->item->folder->klass->type;
+ uistr = update_data->item->folder->klass->uistr;
+ if(!notify_include_folder_type(ftype, uistr))
+ return FALSE;
+
+ if(update_data->update_flags & F_ITEM_UPDATE_MSGCNT) {
+#ifdef NOTIFICATION_BANNER
+ notification_update_banner();
+#endif
+#if defined(NOTIFICATION_POPUP) || defined(NOTIFICATION_COMMAND)
+ notification_new_unnotified_msgs(update_data);
+#endif
+ }
+ return FALSE;
+}
+
+static gboolean my_folder_update_hook(gpointer source, gpointer data)
+{
+ FolderUpdateData *hookdata;
+
+ g_return_val_if_fail(source != NULL, FALSE);
+ hookdata = source;
+
+#if defined(NOTIFICATION_LCDPROC) || defined(NOTIFICATION_TRAYICON)
+ if(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM)
+ notification_update_msg_counts(hookdata->item);
+ else
+ notification_update_msg_counts(NULL);
+#endif
+
+ return FALSE;
+
+}
+
+
+static gboolean my_msginfo_update_hook(gpointer source, gpointer data)
+{
+ return notification_notified_hash_msginfo_update((MsgInfoUpdate*)source);
+}
+
+gint plugin_init(gchar **error)
+{
+ gchar *rcpath;
+
+ /* Version check */
+ /* No be able to test against new-contacts */
+ if(!check_plugin_version(MAKE_NUMERIC_VERSION(3,8,1,46),
+ VERSION_NUMERIC, _("Notification"), error))
+ return -1;
+
+ /* Check if threading is enabled */
+ if(!g_thread_supported()) {
+ *error = g_strdup(_("The Notification plugin needs threading support."));
+ return -1;
+ }
+
+ hook_f_item = hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST,
+ my_folder_item_update_hook, NULL);
+ if(hook_f_item == (guint) -1) {
+ *error = g_strdup(_("Failed to register folder item update hook in the "
+ "Notification plugin"));
+ return -1;
+ }
+
+ hook_f = hooks_register_hook(FOLDER_UPDATE_HOOKLIST,
+ my_folder_update_hook, NULL);
+ if(hook_f == (guint) -1) {
+ *error = g_strdup(_("Failed to register folder update hook in the "
+ "Notification plugin"));
+ hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
+ return -1;
+ }
+
+
+ hook_m_info = hooks_register_hook(MSGINFO_UPDATE_HOOKLIST,
+ my_msginfo_update_hook, NULL);
+ if(hook_m_info == (guint) -1) {
+ *error = g_strdup(_("Failed to register msginfo update hook in the "
+ "Notification plugin"));
+ hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
+ hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
+ return -1;
+ }
+
+ hook_offline = hooks_register_hook(OFFLINE_SWITCH_HOOKLIST,
+ my_offline_switch_hook, NULL);
+ if(hook_offline == (guint) -1) {
+ *error = g_strdup(_("Failed to register offline switch hook in the "
+ "Notification plugin"));
+ hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
+ hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
+ hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, hook_m_info);
+ return -1;
+ }
+
+ hook_mw_close = hooks_register_hook(MAIN_WINDOW_CLOSE,
+ my_main_window_close_hook, NULL);
+ if(hook_mw_close == (guint) -1) {
+ *error = g_strdup(_("Failed to register main window close hook in the "
+ "Notification plugin"));
+ hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
+ hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
+ hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, hook_m_info);
+ hooks_unregister_hook(OFFLINE_SWITCH_HOOKLIST, hook_offline);
+ return -1;
+ }
+
+ hook_got_iconified = hooks_register_hook(MAIN_WINDOW_GOT_ICONIFIED,
+ my_main_window_got_iconified_hook,
+ NULL);
+ if(hook_got_iconified == (guint) -1) {
+ *error = g_strdup(_("Failed to register got iconified hook in the "
+ "Notification plugin"));
+ hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
+ hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
+ hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, hook_m_info);
+ hooks_unregister_hook(OFFLINE_SWITCH_HOOKLIST, hook_offline);
+ hooks_unregister_hook(MAIN_WINDOW_CLOSE, hook_mw_close);
+ return -1;
+ }
+
+ hook_account = hooks_register_hook(ACCOUNT_LIST_CHANGED_HOOKLIST,
+ my_account_list_changed_hook, NULL);
+ if (hook_account == (guint) -1) {
+ *error = g_strdup(_("Failed to register account list changed hook in the "
+ "Notification plugin"));
+ hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
+ hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
+ hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, hook_m_info);
+ hooks_unregister_hook(OFFLINE_SWITCH_HOOKLIST, hook_offline);
+ hooks_unregister_hook(MAIN_WINDOW_CLOSE, hook_mw_close);
+ hooks_unregister_hook(MAIN_WINDOW_GOT_ICONIFIED, hook_got_iconified);
+ return -1;
+ }
+
+ hook_theme_changed = hooks_register_hook(THEME_CHANGED_HOOKLIST, my_update_theme_hook, NULL);
+ if(hook_theme_changed == (guint)-1) {
+ *error = g_strdup(_("Failed to register theme change hook int the "
+ "Notification plugin"));
+ hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
+ hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
+ hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, hook_m_info);
+ hooks_unregister_hook(OFFLINE_SWITCH_HOOKLIST, hook_offline);
+ hooks_unregister_hook(MAIN_WINDOW_CLOSE, hook_mw_close);
+ hooks_unregister_hook(MAIN_WINDOW_GOT_ICONIFIED, hook_got_iconified);
+ hooks_unregister_hook(ACCOUNT_LIST_CHANGED_HOOKLIST, hook_account);
+ return -1;
+ }
+
+ /* Configuration */
+ prefs_set_default(notify_param);
+ rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
+ prefs_read_config(notify_param, "NotificationPlugin", rcpath, NULL);
+ g_free(rcpath);
+
+ /* Folder specific stuff */
+ notification_foldercheck_read_array();
+
+ notification_notified_hash_startup_init();
+
+ notify_gtk_init();
+
+#ifdef NOTIFICATION_BANNER
+ notification_update_banner();
+#endif
+#ifdef NOTIFICATION_LCDPROC
+ notification_lcdproc_connect();
+#endif
+#ifdef NOTIFICATION_TRAYICON
+ if(notify_config.trayicon_enabled &&
+ notify_config.trayicon_hide_at_startup && claws_is_starting()) {
+ MainWindow *mainwin = mainwindow_get_mainwindow();
+
+ g_idle_add(trayicon_startup_idle,NULL);
+ if(mainwin && gtk_widget_get_visible(GTK_WIDGET(mainwin->window)))
+ main_window_hide(mainwin);
+ main_set_show_at_startup(FALSE);
+ }
+#endif
+ my_account_list_changed_hook(NULL,NULL);
+
+ if(notify_config.urgency_hint_new || notify_config.urgency_hint_unread)
+ notification_update_msg_counts(NULL);
+
+#ifdef NOTIFICATION_HOTKEYS
+ notification_hotkeys_update_bindings();
+#endif
+
+ debug_print("Notification plugin loaded\n");
+
+ return 0;
+}
+
+gboolean plugin_done(void)
+{
+ hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST, hook_f_item);
+ hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST, hook_f);
+ hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, hook_m_info);
+ hooks_unregister_hook(OFFLINE_SWITCH_HOOKLIST, hook_offline);
+ hooks_unregister_hook(MAIN_WINDOW_CLOSE, hook_mw_close);
+ hooks_unregister_hook(MAIN_WINDOW_GOT_ICONIFIED, hook_got_iconified);
+ hooks_unregister_hook(ACCOUNT_LIST_CHANGED_HOOKLIST, hook_account);
+ hooks_unregister_hook(THEME_CHANGED_HOOKLIST, hook_theme_changed);
+
+ notify_save_config();
+
+ notify_gtk_done();
+
+ /* foldercheck cleanup */
+ notification_foldercheck_write_array();
+ notification_free_folder_specific_array();
+
+#ifdef NOTIFICATION_BANNER
+ notification_collected_msgs_free(banner_collected_msgs);
+ banner_collected_msgs = NULL;
+ notification_banner_destroy();
+#endif
+#ifdef NOTIFICATION_LCDPROC
+ notification_lcdproc_disconnect();
+#endif
+#ifdef NOTIFICATION_TRAYICON
+ notification_trayicon_destroy();
+#endif
+#ifdef NOTIFICATION_INDICATOR
+ notification_indicator_destroy();
+#endif
+ notification_core_free();
+
+#ifdef HAVE_LIBNOTIFY
+ if(notify_is_initted())
+ notify_uninit();
+#endif
+
+#ifdef NOTIFICATION_HOTKEYS
+ notification_hotkeys_unbind_all();
+#endif
+
+ notification_pixbuf_free_all();
+
+ debug_print("Notification plugin unloaded\n");
+
+ /* Returning FALSE here means that g_module_close() will not be called on the plugin.
+ * This is necessary, as needed libraries are not designed to be unloaded. */
+ return FALSE;
+}
+
+const gchar *plugin_name(void)
+{
+ return _("Notification");
+}
+
+const gchar *plugin_desc(void)
+{
+ return _("This plugin provides various ways "
+ "to notify the user of new and unread email.\n"
+ "The plugin is extensively configurable in the "
+ "plugins section of the preferences dialog.\n\n"
+ "Feedback to <berndth@gmx.de> is welcome.");
+}
+
+const gchar *plugin_type(void)
+{
+ return "GTK2";
+}
+
+const gchar *plugin_licence(void)
+{
+ return "GPL3+";
+}
+
+const gchar *plugin_version(void)
+{
+ return VERSION;
+}
+
+struct PluginFeature *plugin_provides(void)
+{
+ static struct PluginFeature features[] =
+ { {PLUGIN_NOTIFIER, N_("Various tools")},
+ {PLUGIN_NOTHING, NULL}};
+ return features;
+}
+
+#ifdef NOTIFICATION_BANNER
+void notification_update_banner(void)
+{
+ notification_collected_msgs_free(banner_collected_msgs);
+ banner_collected_msgs = NULL;
+
+ if(notify_config.banner_show != NOTIFY_BANNER_SHOW_NEVER) {
+ guint id;
+ GSList *folder_list = NULL;
+
+ if(notify_config.banner_folder_specific) {
+ id = notification_register_folder_specific_list
+ (BANNER_SPECIFIC_FOLDER_ID_STR);
+ folder_list = notification_foldercheck_get_list(id);
+ }
+
+ if(!(notify_config.banner_folder_specific && (folder_list == NULL)))
+ banner_collected_msgs =
+ notification_collect_msgs(notify_config.banner_include_unread,
+ notify_config.banner_folder_specific ?
+ folder_list : NULL, notify_config.banner_max_msgs);
+ }
+
+ notification_banner_show(banner_collected_msgs);
+}
+#endif
+
+#ifdef NOTIFICATION_TRAYICON
+static gboolean trayicon_startup_idle(gpointer data)
+{
+ /* if the trayicon is not available,
+ simulate click on it to show mainwindow */
+ if(!notification_trayicon_is_available())
+ notification_trayicon_on_activate(NULL,data);
+ return FALSE;
+}
+#endif
diff --git a/src/plugins/notification/notification_plugin.h b/src/plugins/notification/notification_plugin.h
@@ -0,0 +1,29 @@
+/* Notification plugin for Claws-Mail
+ * Copyright (C) 2005-2007 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NOTIFICATION_PLUGIN_H
+#define NOTIFICATION_PLUGIN_H NOTIFICATION_PLUGIN_H
+
+#include "pluginconfig.h"
+
+#ifdef NOTIFICATION_BANNER
+void notification_update_banner(void);
+#endif
+
+void notification_update_urgency_hint(void);
+
+#endif /* NOTIFICATION_PLUGIN_H */
diff --git a/src/plugins/notification/notification_popup.c b/src/plugins/notification/notification_popup.c
@@ -0,0 +1,677 @@
+/* Notification plugin for Claws-Mail
+ * Copyright (C) 2005-2007 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "pluginconfig.h"
+
+#ifdef NOTIFICATION_POPUP
+
+#include <gtk/gtk.h>
+
+#include "mainwindow.h"
+#include "procmsg.h"
+#include "folder.h"
+#ifndef USE_NEW_ADDRBOOK
+ #include "addrindex.h"
+#endif
+#include "common/utils.h"
+#include "gtk/gtkutils.h"
+
+#include "notification_popup.h"
+#include "notification_prefs.h"
+#include "notification_foldercheck.h"
+#include "notification_pixbuf.h"
+#include "notification_core.h"
+
+#ifdef HAVE_LIBNOTIFY
+# include <libnotify/notify.h>
+#endif
+
+#ifndef NOTIFY_CHECK_VERSION
+# define NOTIFY_CHECK_VERSION(a,b,c) 0
+#endif
+
+typedef struct {
+ gint count;
+ gchar *msg_path;
+#ifdef HAVE_LIBNOTIFY
+ NotifyNotification *notification;
+ GError *error;
+#else /* !HAVE_LIBNOTIFY */
+ guint timeout_id;
+ GtkWidget *window;
+ GtkWidget *frame;
+ GtkWidget *event_box;
+ GtkWidget *vbox;
+ GtkWidget *label1;
+ GtkWidget *label2;
+#endif
+} NotificationPopup;
+
+G_LOCK_DEFINE_STATIC(popup);
+
+#ifdef HAVE_LIBNOTIFY
+static NotificationPopup popup[F_TYPE_LAST];
+
+static void popup_timeout_fun(NotifyNotification*, gpointer data);
+
+static gboolean notification_libnotify_create(MsgInfo*, NotificationFolderType);
+static gboolean notification_libnotify_add_msg(MsgInfo*, NotificationFolderType);
+static void default_action_cb(NotifyNotification*, const char*,void*);
+static void notification_libnotify_free_func(gpointer);
+
+#else
+static NotificationPopup popup;
+
+static gboolean popup_timeout_fun(gpointer data);
+
+static gboolean notification_popup_add_msg(MsgInfo*);
+static gboolean notification_popup_create(MsgInfo*);
+static gboolean notification_popup_button(GtkWidget*, GdkEventButton*, gpointer);
+#endif
+
+
+void notification_popup_msg(MsgInfo *msginfo)
+{
+ gboolean retval;
+ FolderType ftype;
+ NotificationPopup *ppopup;
+#if HAVE_LIBNOTIFY
+ gchar *uistr;
+#endif
+ NotificationFolderType nftype;
+
+ nftype = F_TYPE_MAIL;
+
+ if(!msginfo || !notify_config.popup_show)
+ return;
+
+ if(notify_config.popup_folder_specific) {
+ guint id;
+ GSList *list;
+ gchar *identifier;
+ gboolean found = FALSE;
+
+ if(!(msginfo->folder))
+ return;
+
+ identifier = folder_item_get_identifier(msginfo->folder);
+
+ id =
+ notification_register_folder_specific_list(POPUP_SPECIFIC_FOLDER_ID_STR);
+ list = notification_foldercheck_get_list(id);
+ for(; (list != NULL) && !found; list = g_slist_next(list)) {
+ gchar *list_identifier;
+ FolderItem *list_item = (FolderItem*) list->data;
+
+ list_identifier = folder_item_get_identifier(list_item);
+ if(!strcmp2(list_identifier, identifier))
+ found = TRUE;
+
+ g_free(list_identifier);
+ }
+ g_free(identifier);
+
+ if(!found)
+ return;
+ }
+
+ ftype = msginfo->folder->folder->klass->type;
+
+ G_LOCK(popup);
+#ifdef HAVE_LIBNOTIFY
+ /* Check out which type to notify about */
+ switch(ftype) {
+ case F_MH:
+ case F_MBOX:
+ case F_MAILDIR:
+ case F_IMAP:
+ nftype = F_TYPE_MAIL;
+ break;
+ case F_NEWS:
+ nftype = F_TYPE_NEWS;
+ break;
+ case F_UNKNOWN:
+ if((uistr = msginfo->folder->folder->klass->uistr) == NULL) {
+ G_UNLOCK(popup);
+ return;
+ }
+ else if(!strcmp(uistr, "vCalendar"))
+ nftype = F_TYPE_CALENDAR;
+ else if(!strcmp(uistr, "RSSyl"))
+ nftype = F_TYPE_RSS;
+ else {
+ debug_print("Notification Plugin: Unknown folder type %d\n",ftype);
+ G_UNLOCK(popup);
+ return;
+ }
+ break;
+ default:
+ debug_print("Notification Plugin: Unknown folder type %d\n",ftype);
+ G_UNLOCK(popup);
+ return;
+ }
+
+ ppopup = &(popup[nftype]);
+ retval = notification_libnotify_add_msg(msginfo, nftype);
+#else /* !HAVE_LIBNOTIFY */
+ ppopup = &popup;
+ retval = notification_popup_add_msg(msginfo);
+
+ /* Renew timeout only when the above call was successful */
+ if(retval) {
+ if(ppopup->timeout_id)
+ g_source_remove(ppopup->timeout_id);
+ ppopup->timeout_id = g_timeout_add(notify_config.popup_timeout,
+ popup_timeout_fun,
+ GINT_TO_POINTER(nftype));
+ }
+
+ #endif /* !HAVE_LIBNOTIFY */
+
+ G_UNLOCK(popup);
+
+#ifndef HAVE_LIBNOTIFY
+ /* GUI update */
+ while(gtk_events_pending())
+ gtk_main_iteration();
+#endif /* !HAVE_LIBNOTIFY */
+
+}
+
+#ifdef HAVE_LIBNOTIFY
+static void popup_timeout_fun(NotifyNotification *nn, gpointer data)
+{
+ NotificationPopup *ppopup;
+ NotificationFolderType nftype;
+
+ nftype = GPOINTER_TO_INT(data);
+
+ G_LOCK(popup);
+
+ ppopup = &(popup[nftype]);
+
+ g_object_unref(G_OBJECT(ppopup->notification));
+ ppopup->notification = NULL;
+ g_clear_error(&(ppopup->error));
+
+ if(ppopup->msg_path) {
+ g_free(ppopup->msg_path);
+ ppopup->msg_path = NULL;
+ }
+ ppopup->count = 0;
+ G_UNLOCK(popup);
+ debug_print("Notification Plugin: Popup closed due to timeout.\n");
+}
+
+#else
+static gboolean popup_timeout_fun(gpointer data)
+{
+ NotificationPopup *ppopup;
+ NotificationFolderType nftype;
+
+ nftype = GPOINTER_TO_INT(data);
+
+ G_LOCK(popup);
+
+ ppopup = &popup;
+ if(ppopup->window) {
+ gtk_widget_destroy(ppopup->window);
+ ppopup->window = NULL;
+ }
+ ppopup->timeout_id = 0;
+
+ if(ppopup->msg_path) {
+ g_free(ppopup->msg_path);
+ ppopup->msg_path = NULL;
+ }
+ ppopup->count = 0;
+ G_UNLOCK(popup);
+ debug_print("Notification Plugin: Popup closed due to timeout.\n");
+ return FALSE;
+}
+#endif
+
+#ifdef HAVE_LIBNOTIFY
+static void default_action_cb(NotifyNotification *notification,
+ const char *action,
+ void *user_data)
+{
+ if(strcmp("default", action))
+ return;
+
+ MainWindow *mainwin;
+ mainwin = mainwindow_get_mainwindow();
+ if(mainwin) {
+ NotificationFolderType nftype;
+
+ /* Let mainwindow pop up */
+ notification_show_mainwindow(mainwin);
+ /* If there is only one new mail message, jump to this message */
+ nftype = (NotificationFolderType)GPOINTER_TO_INT(user_data);
+ if(nftype == F_TYPE_MAIL) {
+ if(popup[F_TYPE_MAIL].count == 1) {
+ gchar *select_str;
+ G_LOCK(popup);
+ select_str = g_strdup(popup[F_TYPE_MAIL].msg_path);
+ G_UNLOCK(popup);
+ debug_print("Select message %s\n", select_str);
+ mainwindow_jump_to(select_str, FALSE);
+ g_free(select_str);
+ }
+ }
+ }
+}
+
+static gboolean notification_libnotify_create(MsgInfo *msginfo,
+ NotificationFolderType nftype)
+{
+ GdkPixbuf *pixbuf;
+ NotificationPopup *ppopup;
+ gchar *summary = NULL;
+ gchar *text = NULL;
+ gchar *utf8_str = NULL;
+ gchar *subj = NULL;
+ gchar *from = NULL;
+ gchar *foldname = NULL;
+ GList *caps = NULL;
+ gboolean support_actions = FALSE;
+
+ g_return_val_if_fail(msginfo, FALSE);
+
+ ppopup = &(popup[nftype]);
+
+ /* init libnotify if necessary */
+ if(!notify_is_initted()) {
+ if(!notify_init("claws-mail")) {
+ debug_print("Notification Plugin: Failed to initialize libnotify. "
+ "No popup will be shown.\n");
+ return FALSE;
+ }
+ }
+
+ switch(nftype) {
+ case F_TYPE_MAIL:
+ summary = _("New Mail message");
+ from = notification_libnotify_sanitize_str(msginfo->from ?
+ msginfo->from : _("(No From)"));
+ subj = notification_libnotify_sanitize_str(msginfo->subject ?
+ msginfo->subject : _("(No Subject)"));
+ if (notify_config.popup_display_folder_name) {
+ foldname = notification_libnotify_sanitize_str(msginfo->folder->path);
+ text = g_strconcat(from,"\n\n", subj, "\n\n", foldname, NULL);
+ }
+ else
+ text = g_strconcat(from, "\n\n",subj, NULL);
+
+ /* Make sure text is valid UTF8 */
+ utf8_str = notification_validate_utf8_str(text);
+ g_free(text);
+
+ if(from) g_free(from);
+ if(subj) g_free(subj);
+ if(foldname) g_free(foldname);
+ break;
+ case F_TYPE_NEWS:
+ summary = _("New News post");
+ utf8_str = g_strdup(_("A new message arrived"));
+ break;
+ case F_TYPE_CALENDAR:
+ summary = _("New Calendar message");
+ utf8_str = g_strdup(_("A new calendar message arrived"));
+ break;
+ case F_TYPE_RSS:
+ summary = _("New RSS feed article");
+ utf8_str = g_strdup(_("A new article in a RSS feed arrived"));
+ break;
+ default:
+ summary = _("New unknown message");
+ utf8_str = g_strdup(_("Unknown message type arrived"));
+ break;
+ }
+
+ ppopup->notification = notify_notification_new(summary, utf8_str, NULL
+#if !NOTIFY_CHECK_VERSION(0, 7, 0)
+ , NULL
+#endif
+ );
+ g_free(utf8_str);
+ if(ppopup->notification == NULL) {
+ debug_print("Notification Plugin: Failed to create a new "
+ "notification.\n");
+ return FALSE;
+ }
+
+ caps = notify_get_server_caps();
+ if(caps != NULL) {
+ GList *c;
+ for(c = caps; c != NULL; c = c->next) {
+ if(strcmp((char*)c->data, "actions") == 0 ) {
+ support_actions = TRUE;
+ break;
+ }
+ }
+
+ g_list_foreach(caps, (GFunc)g_free, NULL);
+ g_list_free(caps);
+ }
+
+ /* Default action */
+ if (support_actions)
+ notify_notification_add_action(ppopup->notification,
+ "default", "Present main window",
+ (NotifyActionCallback)default_action_cb,
+ GINT_TO_POINTER(nftype),
+ notification_libnotify_free_func);
+
+ /* Icon */
+ pixbuf = NULL;
+#ifndef USE_NEW_ADDRBOOK
+ if(msginfo && msginfo->from) {
+ gchar *icon_path;
+ icon_path = addrindex_get_picture_file(msginfo->from);
+ if(is_file_exist(icon_path)) {
+ GError *error = NULL;
+ gint w, h;
+
+ gdk_pixbuf_get_file_info(icon_path, &w, &h);
+ if((w > 64) || (h > 64))
+ pixbuf = gdk_pixbuf_new_from_file_at_scale(icon_path,
+ 64, 64, TRUE, &error);
+ else
+ pixbuf = gdk_pixbuf_new_from_file(icon_path, &error);
+
+ if(!pixbuf) {
+ debug_print("Could not load picture file: %s\n",
+ error ? error->message : "no details");
+ g_error_free(error);
+ }
+ }
+ else
+ debug_print("Picture path does not exist: %s\n",icon_path);
+ g_free(icon_path);
+ }
+#endif
+ if(!pixbuf)
+ pixbuf = g_object_ref(notification_pixbuf_get(NOTIFICATION_CM_LOGO_64x64));
+
+ if(pixbuf) {
+ notify_notification_set_icon_from_pixbuf(ppopup->notification, pixbuf);
+ g_object_unref(pixbuf);
+ }
+ else /* This is not fatal */
+ debug_print("Notification plugin: Icon could not be loaded.\n");
+
+ /* timeout */
+ notify_notification_set_timeout(ppopup->notification, notify_config.popup_timeout);
+
+ /* Category */
+ notify_notification_set_category(ppopup->notification, "email.arrived");
+
+ /* get notified on bubble close */
+ g_signal_connect(G_OBJECT(popup->notification), "closed", G_CALLBACK(popup_timeout_fun), NULL);
+
+ /* Show the popup */
+ notify_notification_set_hint_string(ppopup->notification, "desktop-entry", "claws-mail");
+ if(!notify_notification_show(ppopup->notification, &(ppopup->error))) {
+ debug_print("Notification Plugin: Failed to send notification: %s\n",
+ ppopup->error->message);
+ g_clear_error(&(ppopup->error));
+ g_object_unref(G_OBJECT(ppopup->notification));
+ ppopup->notification = NULL;
+ return FALSE;
+ }
+
+ debug_print("Notification Plugin: Popup created with libnotify.\n");
+ ppopup->count = 1;
+
+ /* Store path to message */
+ if(nftype == F_TYPE_MAIL) {
+ if(msginfo->folder && msginfo->folder) {
+ gchar *ident;
+ ident = folder_item_get_identifier(msginfo->folder);
+ ppopup->msg_path = g_strdup_printf("%s%s%u", ident,G_DIR_SEPARATOR_S,
+ msginfo->msgnum);
+ g_free(ident);
+ }
+ else
+ ppopup->msg_path = NULL;
+ }
+
+ return TRUE;
+}
+
+static gboolean notification_libnotify_add_msg(MsgInfo *msginfo,
+ NotificationFolderType nftype)
+{
+ gchar *summary;
+ gchar *text;
+ gboolean retval;
+ NotificationPopup *ppopup;
+ GdkPixbuf *pixbuf;
+
+ ppopup = &(popup[nftype]);
+
+ if(!ppopup->notification)
+ return notification_libnotify_create(msginfo,nftype);
+
+ ppopup->count++;
+
+ if(ppopup->msg_path) {
+ g_free(ppopup->msg_path);
+ ppopup->msg_path = NULL;
+ }
+
+ /* make sure we show a logo on many msg arrival */
+ pixbuf = notification_pixbuf_get(NOTIFICATION_CM_LOGO_64x64);
+ if(pixbuf)
+ notify_notification_set_icon_from_pixbuf(ppopup->notification, pixbuf);
+
+ switch(nftype) {
+ case F_TYPE_MAIL:
+ summary = _("Mail message");
+ text = g_strdup_printf(ngettext("%d new message arrived",
+ "%d new messages arrived",
+ ppopup->count), ppopup->count);
+ break;
+ case F_TYPE_NEWS:
+ summary = _("News message");
+ text = g_strdup_printf(ngettext("%d new message arrived",
+ "%d new messages arrived",
+ ppopup->count), ppopup->count);
+ break;
+ case F_TYPE_CALENDAR:
+ summary = _("Calendar message");
+ text = g_strdup_printf(ngettext("%d new calendar message arrived",
+ "%d new calendar messages arrived",
+ ppopup->count), ppopup->count);
+ break;
+ case F_TYPE_RSS:
+ summary = _("RSS news feed");
+ text = g_strdup_printf(ngettext("%d new article in a RSS feed arrived",
+ "%d new articles in a RSS feed arrived",
+ ppopup->count), ppopup->count);
+ break;
+ default:
+ /* Should not happen */
+ debug_print("Notification Plugin: Unknown folder type ignored\n");
+ return FALSE;
+ }
+
+ retval = notify_notification_update(ppopup->notification, summary,
+ text, NULL);
+ g_free(text);
+ if(!retval) {
+ debug_print("Notification Plugin: Failed to update notification.\n");
+ return FALSE;
+ }
+
+ /* Show the popup */
+ notify_notification_set_hint_string(ppopup->notification, "desktop-entry", "claws-mail");
+ if(!notify_notification_show(ppopup->notification, &(ppopup->error))) {
+ debug_print("Notification Plugin: Failed to send updated notification: "
+ "%s\n", ppopup->error->message);
+ g_clear_error(&(ppopup->error));
+ return FALSE;
+ }
+
+ debug_print("Notification Plugin: Popup successfully modified "
+ "with libnotify.\n");
+ return TRUE;
+}
+
+void notification_libnotify_free_func(gpointer data)
+{
+ if(popup[F_TYPE_MAIL].msg_path) {
+ g_free(popup[F_TYPE_MAIL].msg_path);
+ popup[F_TYPE_MAIL].msg_path = NULL;
+ }
+ debug_print("Freed notification data\n");
+}
+
+#else /* !HAVE_LIBNOTIFY */
+static gboolean notification_popup_add_msg(MsgInfo *msginfo)
+{
+ gchar *message;
+ NotificationPopup *ppopup;
+
+ ppopup = &popup;
+
+ if(!ppopup->window)
+ return notification_popup_create(msginfo);
+
+ ppopup->count++;
+
+ if(ppopup->msg_path) {
+ g_free(ppopup->msg_path);
+ ppopup->msg_path = NULL;
+ }
+
+ if(ppopup->label2)
+ gtk_widget_destroy(ppopup->label2);
+
+ message = g_strdup_printf(ngettext("%d new message",
+ "%d new messages",
+ ppopup->count), ppopup->count);
+ gtk_label_set_text(GTK_LABEL(ppopup->label1), message);
+ g_free(message);
+ return TRUE;
+}
+
+static gboolean notification_popup_create(MsgInfo *msginfo)
+{
+ GdkColor bg;
+ GdkColor fg;
+ NotificationPopup *ppopup;
+
+ g_return_val_if_fail(msginfo, FALSE);
+
+ ppopup = &popup;
+
+ /* Window */
+ ppopup->window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "notification_popup");
+ gtk_window_set_decorated(GTK_WINDOW(ppopup->window), FALSE);
+ gtk_window_set_keep_above(GTK_WINDOW(ppopup->window), TRUE);
+ gtk_window_set_accept_focus(GTK_WINDOW(ppopup->window), FALSE);
+ gtk_window_set_skip_taskbar_hint(GTK_WINDOW(ppopup->window), TRUE);
+ gtk_window_set_skip_pager_hint(GTK_WINDOW(ppopup->window), TRUE);
+ gtk_window_move(GTK_WINDOW(ppopup->window), notify_config.popup_root_x,
+ notify_config.popup_root_y);
+ gtk_window_resize(GTK_WINDOW(ppopup->window), notify_config.popup_width, 1);
+ if(notify_config.popup_sticky)
+ gtk_window_stick(GTK_WINDOW(ppopup->window));
+ /* Signals */
+ gtk_widget_set_events(ppopup->window,
+ GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
+ g_signal_connect(ppopup->window, "button_press_event",
+ G_CALLBACK(notification_popup_button), NULL);
+
+ /* Event box */
+ ppopup->event_box = gtk_event_box_new();
+ gtk_container_add(GTK_CONTAINER(ppopup->window), ppopup->event_box);
+
+ /* Frame */
+ ppopup->frame = gtk_frame_new(NULL);
+ gtk_frame_set_shadow_type(GTK_FRAME(ppopup->frame), GTK_SHADOW_ETCHED_OUT);
+ gtk_container_add(GTK_CONTAINER(ppopup->event_box), ppopup->frame);
+
+ /* Vbox with labels */
+ ppopup->vbox = gtk_vbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(ppopup->vbox), 5);
+ ppopup->label1 = gtk_label_new(msginfo->from ?
+ msginfo->from : _("(No From)"));
+ gtk_box_pack_start(GTK_BOX(ppopup->vbox), ppopup->label1, FALSE, FALSE, 0);
+
+ ppopup->label2 = gtk_label_new(msginfo->subject ?
+ msginfo->subject : _("(No Subject)"));
+ gtk_box_pack_start(GTK_BOX(ppopup->vbox), ppopup->label2, FALSE, FALSE, 0);
+
+ gtk_container_add(GTK_CONTAINER(ppopup->frame), ppopup->vbox);
+ gtk_widget_set_size_request(ppopup->vbox, notify_config.popup_width, -1);
+
+ /* Color */
+ if(notify_config.popup_enable_colors) {
+ gtkut_convert_int_to_gdk_color(notify_config.popup_color_bg,&bg);
+ gtkut_convert_int_to_gdk_color(notify_config.popup_color_fg,&fg);
+ gtk_widget_modify_bg(ppopup->event_box,GTK_STATE_NORMAL,&bg);
+ gtk_widget_modify_fg(ppopup->label1,GTK_STATE_NORMAL,&fg);
+ gtk_widget_modify_fg(ppopup->label2,GTK_STATE_NORMAL,&fg);
+ }
+
+ gtk_widget_show_all(ppopup->window);
+
+ ppopup->count = 1;
+
+ if(msginfo->folder && msginfo->folder->name) {
+ gchar *ident;
+ ident = folder_item_get_identifier(msginfo->folder);
+ ppopup->msg_path = g_strdup_printf("%s%s%u", ident,G_DIR_SEPARATOR_S,
+ msginfo->msgnum);
+ g_free(ident);
+ }
+
+ return TRUE;
+}
+
+static gboolean notification_popup_button(GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer data)
+{
+ if(event->type == GDK_BUTTON_PRESS) {
+ if(event->button == 1) {
+ MainWindow *mainwin;
+ /* Let mainwindow pop up */
+ mainwin = mainwindow_get_mainwindow();
+ if(!mainwin)
+ return TRUE;
+ notification_show_mainwindow(mainwin);
+ /* If there is only one new mail message, jump to this message */
+ if(popup.count == 1) {
+ gchar *select_str;
+ G_LOCK(popup);
+ select_str = g_strdup(popup.msg_path);
+ G_UNLOCK(popup);
+ debug_print("Select message %s\n", select_str);
+ mainwindow_jump_to(select_str, FALSE);
+ g_free(select_str);
+ }
+ }
+ }
+ return TRUE;
+}
+#endif /* !HAVE_LIBNOTIFY */
+
+#endif /* NOTIFICATION_POPUP */
diff --git a/src/plugins/notification/notification_popup.h b/src/plugins/notification/notification_popup.h
@@ -0,0 +1,33 @@
+/* Notification plugin for Claws-Mail
+ * Copyright (C) 2005-2007 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NOTIFICATION_POPUP_H
+#define NOTIFICATION_POPUP_H NOTIFICATION_POPUP_H
+
+#include "pluginconfig.h"
+
+#ifdef NOTIFICATION_POPUP
+
+#include "procmsg.h"
+
+#define POPUP_SPECIFIC_FOLDER_ID_STR "popup"
+
+void notification_popup_msg(MsgInfo*);
+
+#endif /* NOTIFICATION_POPUP */
+
+#endif /* NOTIFICATION_POPUP_H */
diff --git a/src/plugins/notification/notification_prefs.c b/src/plugins/notification/notification_prefs.c
@@ -0,0 +1,1952 @@
+/* Notification plugin for Claws-Mail
+ * Copyright (C) 2005-2007 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "pluginconfig.h"
+
+#include <math.h>
+
+#include "prefs_gtk.h"
+#include "common/prefs.h"
+#include "common/utils.h"
+#include "common/defs.h"
+#include "gtk/gtkutils.h"
+#include "main.h"
+
+#include "notification_prefs.h"
+#include "notification_plugin.h"
+#include "notification_core.h"
+
+#include "notification_popup.h"
+#include "notification_command.h"
+#include "notification_lcdproc.h"
+#include "notification_trayicon.h"
+#include "notification_indicator.h"
+#include "notification_hotkeys.h"
+
+#include "notification_foldercheck.h"
+
+#ifdef HAVE_LIBNOTIFY
+/* allow infinite timeout with libnotify */
+# define TIMEOUT_SPINNER_MIN 0.0
+#else
+/* don't allow infinite timeout with self-handled popups */
+# define TIMEOUT_SPINNER_MIN 0.2
+#endif
+
+#define TIMEOUT_SPINNER_MAX 3600.0
+#define TIMEOUT_SPINNER_STEP 0.5
+
+typedef struct
+{
+ PrefsPage page;
+ GtkWidget *include_mail;
+ GtkWidget *include_news;
+ GtkWidget *include_rss;
+ GtkWidget *include_calendar;
+ GtkWidget *urgency_hint_new;
+ GtkWidget *urgency_hint_unread;
+#ifdef HAVE_LIBCANBERRA_GTK
+ GtkWidget *canberra_play_sounds;
+#endif
+} NotifyPage;
+
+NotifyPrefs notify_config;
+NotifyPage notify_page;
+
+#ifdef NOTIFICATION_BANNER
+typedef struct {
+ PrefsPage page;
+ GtkWidget *banner_show;
+ GtkWidget *banner_speed;
+ GtkWidget *banner_width;
+ GtkWidget *banner_include_unread;
+ GtkWidget *banner_max_msgs;
+ GtkWidget *banner_sticky;
+ GtkWidget *banner_folder_specific;
+ GtkWidget *banner_enable_colors;
+ GtkWidget *banner_color_bg;
+ GtkWidget *banner_color_fg;
+ GtkWidget *banner_cont_enable;
+ GtkWidget *banner_cont_folder_specific;
+ GtkWidget *banner_cont_color_sel;
+}NotifyBannerPage;
+NotifyBannerPage banner_page;
+#endif /* NOTIFICATION_BANNER */
+
+#ifdef NOTIFICATION_POPUP
+typedef struct {
+ PrefsPage page;
+ GtkWidget *popup_show;
+ GtkWidget *popup_timeout;
+ GtkWidget *popup_folder_specific;
+ GtkWidget *popup_cont_enable;
+ GtkWidget *popup_cont_folder_specific;
+#ifndef HAVE_LIBNOTIFY
+ GtkWidget *popup_sticky;
+ GtkWidget *popup_enable_colors;
+ GtkWidget *popup_color_bg;
+ GtkWidget *popup_color_fg;
+ GtkWidget *popup_cont_color_sel;
+#else /* HAVE_LIBNOTIFY */
+ GtkWidget *popup_display_folder_name;
+#endif /* HAVE_LIBNOTIFY */
+}NotifyPopupPage;
+NotifyPopupPage popup_page;
+#endif /* NOTIFICATION_POPUP */
+
+#ifdef NOTIFICATION_COMMAND
+typedef struct {
+ PrefsPage page;
+ GtkWidget *command_enabled;
+ GtkWidget *command_timeout;
+ GtkWidget *command_folder_specific;
+ GtkWidget *command_line;
+ GtkWidget *command_cont_enable;
+ GtkWidget *command_cont_folder_specific;
+}NotifyCommandPage;
+NotifyCommandPage command_page;
+#endif /* NOTIFICATION_COMMAND */
+
+#ifdef NOTIFICATION_LCDPROC
+typedef struct {
+ PrefsPage page;
+ GtkWidget *lcdproc_enabled;
+ GtkWidget *lcdproc_cont_enable;
+ GtkWidget *lcdproc_hostname;
+ GtkWidget *lcdproc_port;
+}NotifyLCDProcPage;
+NotifyLCDProcPage lcdproc_page;
+#endif
+
+#ifdef NOTIFICATION_TRAYICON
+typedef struct {
+ PrefsPage page;
+ GtkWidget *trayicon_enabled;
+ GtkWidget *trayicon_cont_enable;
+ GtkWidget *trayicon_hide_at_startup;
+ GtkWidget *trayicon_close_to_tray;
+ GtkWidget *trayicon_hide_when_iconified;
+ GtkWidget *trayicon_folder_specific;
+ GtkWidget *trayicon_cont_folder_specific;
+#ifdef HAVE_LIBNOTIFY
+ GtkWidget *trayicon_display_folder_name;
+ GtkWidget *trayicon_popup_enabled;
+ GtkWidget *trayicon_popup_cont_enable;
+ GtkWidget *trayicon_popup_timeout;
+#endif
+}NotifyTrayiconPage;
+NotifyTrayiconPage trayicon_page;
+#endif
+
+#ifdef NOTIFICATION_INDICATOR
+typedef struct {
+ PrefsPage page;
+ GtkWidget *indicator_enabled;
+ GtkWidget *indicator_cont_enable;
+ GtkWidget *indicator_register;
+ GtkWidget *indicator_hide_minimized;
+}NotifyIndicatorPage;
+NotifyIndicatorPage indicator_page;
+#endif
+
+#ifdef NOTIFICATION_HOTKEYS
+typedef struct {
+ PrefsPage page;
+ GtkWidget *hotkeys_enabled;
+ GtkWidget *hotkeys_cont_enable;
+ GtkWidget *hotkeys_toggle_mainwindow;
+}NotifyHotkeysPage;
+NotifyHotkeysPage hotkeys_page;
+#endif
+
+
+PrefParam
+ notify_param[] =
+ {
+
+ { "include_mail", "TRUE", ¬ify_config.include_mail, P_BOOL, NULL,
+ NULL, NULL },
+ { "include_news", "TRUE", ¬ify_config.include_news, P_BOOL, NULL,
+ NULL, NULL },
+ { "include_rss", "TRUE", ¬ify_config.include_rss, P_BOOL, NULL,
+ NULL, NULL },
+ { "include_calendar", "TRUE", ¬ify_config.include_calendar, P_BOOL,
+ NULL, NULL, NULL },
+ { "urgency_hint_new", "FALSE", ¬ify_config.urgency_hint_new, P_BOOL,
+ NULL, NULL, NULL },
+ { "urgency_hint_unread", "FALSE", ¬ify_config.urgency_hint_unread, P_BOOL,
+ NULL, NULL, NULL },
+
+#ifdef HAVE_LIBCANBERRA_GTK
+ { "canberra_play_sounds", "TRUE", ¬ify_config.canberra_play_sounds, P_BOOL,
+ NULL, NULL, NULL },
+#endif
+
+#ifdef NOTIFICATION_BANNER
+ { "banner_show", "0", ¬ify_config.banner_show, P_INT, NULL, NULL, NULL},
+ { "banner_speed", "30", ¬ify_config.banner_speed, P_INT, NULL, NULL, NULL},
+ { "banner_width", "0", ¬ify_config.banner_width, P_INT, NULL, NULL, NULL},
+ { "banner_include_unread", "FALSE", ¬ify_config.banner_include_unread,
+ P_BOOL, NULL, NULL, NULL},
+ { "banner_max_msgs", "100", ¬ify_config.banner_max_msgs, P_INT,
+ NULL, NULL, NULL},
+ { "banner_sticky", "FALSE", ¬ify_config.banner_sticky,
+ P_BOOL, NULL, NULL, NULL},
+ { "banner_root_x", "0", ¬ify_config.banner_root_x, P_INT,
+ NULL, NULL, NULL},
+ { "banner_root_y", "10", ¬ify_config.banner_root_y, P_INT,
+ NULL, NULL, NULL},
+ { "banner_folder_specific", "FALSE", ¬ify_config.banner_folder_specific,
+ P_BOOL, NULL, NULL, NULL},
+ { "banner_enable_colors", "FALSE", ¬ify_config.banner_enable_colors,
+ P_BOOL, NULL, NULL, NULL},
+ { "banner_color_bg", "0", ¬ify_config.banner_color_bg, P_COLOR,
+ NULL, NULL, NULL},
+ { "banner_color_fg", "16776704", ¬ify_config.banner_color_fg, P_COLOR,
+ NULL, NULL, NULL},
+#endif
+
+#ifdef NOTIFICATION_POPUP
+ { "popup_show", "TRUE", ¬ify_config.popup_show, P_BOOL, NULL, NULL, NULL},
+ { "popup_timeout", "5000", ¬ify_config.popup_timeout,
+ P_INT, NULL, NULL, NULL},
+ { "popup_folder_specific", "FALSE", ¬ify_config.popup_folder_specific,
+ P_BOOL, NULL, NULL, NULL},
+#ifndef HAVE_LIBNOTIFY
+ { "popup_sticky", "TRUE", ¬ify_config.popup_sticky, P_BOOL,
+ NULL, NULL, NULL},
+ { "popup_root_x", "10", ¬ify_config.popup_root_x,
+ P_INT, NULL, NULL, NULL},
+ { "popup_root_y", "10", ¬ify_config.popup_root_y,
+ P_INT, NULL, NULL, NULL},
+ { "popup_width", "100", ¬ify_config.popup_width,
+ P_INT, NULL, NULL, NULL},
+ { "popup_enable_colors", "FALSE", ¬ify_config.popup_enable_colors, P_BOOL,
+ NULL, NULL, NULL},
+ { "popup_color_bg", "0", ¬ify_config.popup_color_bg, P_COLOR,
+ NULL, NULL, NULL},
+ { "popup_color_fg", "16776704", ¬ify_config.popup_color_fg, P_COLOR,
+ NULL, NULL, NULL},
+#else /* HAVE_LIBNOTIFY */
+ { "popup_display_folder_name", "FALSE", ¬ify_config.popup_display_folder_name,
+ P_BOOL, NULL, NULL, NULL},
+#endif /* HAVE_LIBNOTIFY */
+#endif
+
+#ifdef NOTIFICATION_COMMAND
+ { "command_enabled", "FALSE", ¬ify_config.command_enabled, P_BOOL,
+ NULL, NULL, NULL},
+ { "command_timeout", "60000", ¬ify_config.command_timeout, P_INT,
+ NULL, NULL, NULL},
+ { "command_folder_specific", "FALSE", ¬ify_config.command_folder_specific,
+ P_BOOL, NULL, NULL, NULL},
+ { "command_line", "", ¬ify_config.command_line, P_STRING,
+ NULL, NULL, NULL},
+#endif
+
+#ifdef NOTIFICATION_LCDPROC
+ { "lcdproc_enabled", "FALSE", ¬ify_config.lcdproc_enabled, P_BOOL,
+ NULL, NULL, NULL},
+ { "lcdproc_hostname", "localhost", ¬ify_config.lcdproc_hostname, P_STRING,
+ NULL, NULL, NULL},
+ { "lcdproc_port", "13666", ¬ify_config.lcdproc_port, P_INT,
+ NULL, NULL, NULL},
+#endif
+
+#ifdef NOTIFICATION_TRAYICON
+#ifndef G_OS_WIN32
+ { "trayicon_enabled", "FALSE", ¬ify_config.trayicon_enabled, P_BOOL,
+ NULL, NULL, NULL},
+#else
+ { "trayicon_enabled", "TRUE", ¬ify_config.trayicon_enabled, P_BOOL,
+ NULL, NULL, NULL},
+#endif
+ { "trayicon_hide_at_startup", "FALSE",
+ ¬ify_config.trayicon_hide_at_startup, P_BOOL, NULL, NULL, NULL},
+ { "trayicon_close_to_tray", "FALSE",
+ ¬ify_config.trayicon_close_to_tray, P_BOOL, NULL, NULL, NULL},
+ { "trayicon_hide_when_iconified", "FALSE",
+ ¬ify_config.trayicon_hide_when_iconified, P_BOOL, NULL, NULL, NULL},
+ { "trayicon_folder_specific", "FALSE",
+ ¬ify_config.trayicon_folder_specific,
+ P_BOOL, NULL, NULL, NULL},
+#ifdef HAVE_LIBNOTIFY
+ { "trayicon_display_folder_name", "FALSE",
+ ¬ify_config.trayicon_display_folder_name,
+ P_BOOL, NULL, NULL, NULL},
+ { "trayicon_popup_enabled", "TRUE", ¬ify_config.trayicon_popup_enabled,
+ P_BOOL, NULL, NULL, NULL},
+ { "trayicon_popup_timeout", "5000", ¬ify_config.trayicon_popup_timeout,
+ P_INT, NULL, NULL, NULL},
+#endif /* HAVE_LIBNOTIFY */
+#endif
+
+#ifdef NOTIFICATION_INDICATOR
+ { "indicator_enabled", "FALSE", ¬ify_config.indicator_enabled, P_BOOL,
+ NULL, NULL, NULL},
+ { "indicator_hide_minimized", "FALSE", ¬ify_config.indicator_hide_minimized, P_BOOL,
+ NULL, NULL, NULL},
+#endif /* NOTIFICATION_INDICATOR */
+#ifdef NOTIFICATION_HOTKEYS
+ { "hotkeys_enabled", "FALSE", ¬ify_config.hotkeys_enabled, P_BOOL,
+ NULL, NULL, NULL},
+ { "hotkeys_toggle_mainwindow", "", ¬ify_config.hotkeys_toggle_mainwindow,
+ P_STRING, NULL, NULL, NULL},
+#endif /* NOTIFICATION_HOTKEYS */
+ { NULL, NULL, NULL, P_OTHER, NULL, NULL, NULL } };
+
+static void notify_create_prefs_page(PrefsPage*, GtkWindow*, gpointer);
+static void notify_destroy_prefs_page(PrefsPage*);
+static void notify_save_prefs(PrefsPage*);
+
+#ifdef NOTIFICATION_BANNER
+static void notify_create_banner_page(PrefsPage*, GtkWindow*, gpointer);
+static void notify_destroy_banner_page(PrefsPage*);
+static void notify_save_banner(PrefsPage*);
+static void notify_banner_enable_set_sensitivity(GtkComboBox*, gpointer);
+static void notify_banner_color_sel_set_sensitivity(GtkToggleButton*,gpointer);
+static void notify_banner_folder_specific_set_sensitivity(GtkToggleButton*,
+ gpointer);
+#endif
+
+#ifdef NOTIFICATION_POPUP
+static void notify_create_popup_page(PrefsPage*, GtkWindow*, gpointer);
+static void notify_destroy_popup_page(PrefsPage*);
+static void notify_save_popup(PrefsPage*);
+static void notify_popup_folder_specific_set_sensitivity(GtkToggleButton*,
+ gpointer);
+static void notify_popup_enable_set_sensitivity(GtkToggleButton*, gpointer);
+#ifndef HAVE_LIBNOTIFY
+static void notify_popup_set_done_cb(GtkWidget*, gpointer);
+static void notify_popup_set_cb(GtkWidget*, gpointer);
+static void notify_popup_color_sel_set_sensitivity(GtkToggleButton*,gpointer);
+#endif /* !HAVE_LIBNOTIFY */
+#endif
+
+#ifdef NOTIFICATION_COMMAND
+static void notify_create_command_page(PrefsPage*, GtkWindow*, gpointer);
+static void notify_destroy_command_page(PrefsPage*);
+static void notify_save_command(PrefsPage*);
+static void notify_command_enable_set_sensitivity(GtkToggleButton*, gpointer);
+static void notify_command_folder_specific_set_sensitivity(GtkToggleButton*,
+ gpointer);
+#endif
+
+#ifdef NOTIFICATION_LCDPROC
+static void notify_create_lcdproc_page(PrefsPage*, GtkWindow*, gpointer);
+static void notify_destroy_lcdproc_page(PrefsPage*);
+static void notify_save_lcdproc(PrefsPage*);
+static void notify_lcdproc_enable_set_sensitivity(GtkToggleButton*, gpointer);
+#endif
+
+#ifdef NOTIFICATION_TRAYICON
+static void notify_create_trayicon_page(PrefsPage*, GtkWindow*, gpointer);
+static void notify_destroy_trayicon_page(PrefsPage*);
+static void notify_save_trayicon(PrefsPage*);
+static void notify_trayicon_enable_set_sensitivity(GtkToggleButton*, gpointer);
+static void notify_trayicon_folder_specific_set_sensitivity(GtkToggleButton*,
+ gpointer);
+#ifdef HAVE_LIBNOTIFY
+static void notify_trayicon_popup_enable_set_sensitivity(GtkToggleButton*,
+ gpointer);
+#endif
+#endif
+
+#ifdef NOTIFICATION_INDICATOR
+static void notify_create_indicator_page(PrefsPage*, GtkWindow*, gpointer);
+static void notify_destroy_indicator_page(PrefsPage*);
+static void notify_save_indicator(PrefsPage*);
+static void notify_indicator_enable_set_sensitivity(GtkToggleButton*, gpointer);
+#endif /* NOTIFICATION_INDICATOR */
+
+#ifdef NOTIFICATION_HOTKEYS
+static void notify_create_hotkeys_page(PrefsPage*, GtkWindow*, gpointer);
+static void notify_destroy_hotkeys_page(PrefsPage*);
+static void notify_save_hotkeys(PrefsPage*);
+static void notify_hotkeys_enable_set_sensitivity(GtkToggleButton*, gpointer);
+#endif /* NOTIFICATION_HOTKEYS */
+
+
+static gint conv_color_to_int(GdkColor*);
+
+void notify_gtk_init(void)
+{
+ static gchar *path[3];
+
+ path[0] = D_("claws-mail", "Plugins");
+ path[1] = _("Notification");
+ path[2] = NULL;
+
+ notify_page.page.path = path;
+ notify_page.page.create_widget = notify_create_prefs_page;
+ notify_page.page.destroy_widget = notify_destroy_prefs_page;
+ notify_page.page.save_page = notify_save_prefs;
+ prefs_gtk_register_page((PrefsPage*) ¬ify_page);
+
+#ifdef NOTIFICATION_HOTKEYS
+ {
+ static gchar *hotkeys_path[4];
+
+ hotkeys_path[0] = D_("claws-mail", "Plugins");
+ hotkeys_path[1] = _("Notification");
+ hotkeys_path[2] = _("Hotkeys");
+ hotkeys_path[3] = NULL;
+
+ hotkeys_page.page.path = hotkeys_path;
+ hotkeys_page.page.create_widget = notify_create_hotkeys_page;
+ hotkeys_page.page.destroy_widget = notify_destroy_hotkeys_page;
+ hotkeys_page.page.save_page = notify_save_hotkeys;
+ prefs_gtk_register_page((PrefsPage*) &hotkeys_page);
+ }
+#endif /* NOTIFICATION_HOTKEYS */
+
+
+#ifdef NOTIFICATION_BANNER
+ {
+ static gchar *banner_path[4];
+
+ banner_path[0] = D_("claws-mail", "Plugins");
+ banner_path[1] = _("Notification");
+ banner_path[2] = _("Banner");
+ banner_path[3] = NULL;
+
+ banner_page.page.path = banner_path;
+ banner_page.page.create_widget = notify_create_banner_page;
+ banner_page.page.destroy_widget = notify_destroy_banner_page;
+ banner_page.page.save_page = notify_save_banner;
+ prefs_gtk_register_page((PrefsPage*) &banner_page);
+ }
+#endif /* NOTIFICATION_BANNER */
+
+#ifdef NOTIFICATION_POPUP
+ {
+ static gchar *popup_path[4];
+
+ popup_path[0] = D_("claws-mail", "Plugins");
+ popup_path[1] = _("Notification");
+ popup_path[2] = _("Popup");
+ popup_path[3] = NULL;
+
+ popup_page.page.path = popup_path;
+ popup_page.page.create_widget = notify_create_popup_page;
+ popup_page.page.destroy_widget = notify_destroy_popup_page;
+ popup_page.page.save_page = notify_save_popup;
+ prefs_gtk_register_page((PrefsPage*) &popup_page);
+ }
+#endif /* NOTIFICATION_POPUP */
+
+#ifdef NOTIFICATION_COMMAND
+ {
+ static gchar *command_path[4];
+
+ command_path[0] = D_("claws-mail", "Plugins");
+ command_path[1] = _("Notification");
+ command_path[2] = _("Command");
+ command_path[3] = NULL;
+
+ command_page.page.path = command_path;
+ command_page.page.create_widget = notify_create_command_page;
+ command_page.page.destroy_widget = notify_destroy_command_page;
+ command_page.page.save_page = notify_save_command;
+ prefs_gtk_register_page((PrefsPage*) &command_page);
+ }
+#endif /* NOTIFICATION_COMMAND */
+
+#ifdef NOTIFICATION_LCDPROC
+ {
+ static gchar *lcdproc_path[4];
+
+ lcdproc_path[0] = D_("claws-mail", "Plugins");
+ lcdproc_path[1] = _("Notification");
+ lcdproc_path[2] = _("LCD");
+ lcdproc_path[3] = NULL;
+
+ lcdproc_page.page.path = lcdproc_path;
+ lcdproc_page.page.create_widget = notify_create_lcdproc_page;
+ lcdproc_page.page.destroy_widget = notify_destroy_lcdproc_page;
+ lcdproc_page.page.save_page = notify_save_lcdproc;
+ prefs_gtk_register_page((PrefsPage*) &lcdproc_page);
+ }
+#endif /* NOTIFICATION_LCDPROC */
+
+#ifdef NOTIFICATION_TRAYICON
+ {
+ static gchar *trayicon_path[4];
+
+ trayicon_path[0] = D_("claws-mail", "Plugins");
+ trayicon_path[1] = _("Notification");
+ trayicon_path[2] = _("SysTrayicon");
+ trayicon_path[3] = NULL;
+
+ trayicon_page.page.path = trayicon_path;
+ trayicon_page.page.create_widget = notify_create_trayicon_page;
+ trayicon_page.page.destroy_widget = notify_destroy_trayicon_page;
+ trayicon_page.page.save_page = notify_save_trayicon;
+ prefs_gtk_register_page((PrefsPage*) &trayicon_page);
+ }
+#endif /* NOTIFICATION_TRAYICON */
+
+#ifdef NOTIFICATION_INDICATOR
+ {
+ static gchar *indicator_path[4];
+
+ indicator_path[0] = D_("claws-mail", "Plugins");
+ indicator_path[1] = _("Notification");
+ indicator_path[2] = _("Indicator");
+ indicator_path[3] = NULL;
+
+ indicator_page.page.path = indicator_path;
+ indicator_page.page.create_widget = notify_create_indicator_page;
+ indicator_page.page.destroy_widget = notify_destroy_indicator_page;
+ indicator_page.page.save_page = notify_save_indicator;
+ prefs_gtk_register_page((PrefsPage*) &indicator_page);
+ }
+#endif /* NOTIFICATION_INDICATOR */
+}
+
+void notify_gtk_done(void)
+{
+ if (claws_is_exiting())
+ return;
+ prefs_gtk_unregister_page((PrefsPage*) ¬ify_page);
+#ifdef NOTIFICATION_BANNER
+ prefs_gtk_unregister_page((PrefsPage*) &banner_page);
+#endif
+#ifdef NOTIFICATION_POPUP
+ prefs_gtk_unregister_page((PrefsPage*) &popup_page);
+#endif
+#ifdef NOTIFICATION_COMMAND
+ prefs_gtk_unregister_page((PrefsPage*) &command_page);
+#endif
+#ifdef NOTIFICATION_LCDPROC
+ prefs_gtk_unregister_page((PrefsPage*) &lcdproc_page);
+#endif
+#ifdef NOTIFICATION_TRAYICON
+ prefs_gtk_unregister_page((PrefsPage*) &trayicon_page);
+#endif
+#ifdef NOTIFICATION_INDICATOR
+ prefs_gtk_unregister_page((PrefsPage*) &indicator_page);
+#endif
+#ifdef NOTIFICATION_HOTKEYS
+ prefs_gtk_unregister_page((PrefsPage*) &hotkeys_page);
+#endif
+}
+
+void notify_save_config(void)
+{
+ PrefFile *pfile;
+ gchar *rcpath;
+
+ debug_print("Saving Notification plugin configuration...\n");
+
+ rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
+ pfile = prefs_write_open(rcpath);
+ g_free(rcpath);
+ if (!pfile || (prefs_set_block_label(pfile, "NotificationPlugin") < 0))
+ return;
+
+ if (prefs_write_param(notify_param, pfile->fp) < 0) {
+ debug_print("failed!\n");
+ g_warning(_("\nNotification Plugin: Failed to write plugin configuration "
+ "to file\n"));
+ prefs_file_close_revert(pfile);
+ return;
+ }
+ if (fprintf(pfile->fp, "\n") < 0) {
+ FILE_OP_ERROR(rcpath, "fprintf");
+ prefs_file_close_revert(pfile);
+ }
+ else
+ prefs_file_close(pfile);
+ debug_print("done.\n");
+}
+
+static void notify_create_prefs_page(PrefsPage *page, GtkWindow *window,
+ gpointer data)
+{
+ GtkWidget *pvbox;
+ GtkWidget *vbox;
+ GtkWidget *checkbox;
+ GtkWidget *frame;
+ GtkWidget *label;
+
+ /* Page vbox */
+ pvbox = gtk_vbox_new(FALSE, 0);
+
+ /* Frame */
+ frame = gtk_frame_new(_("Include folder types"));
+ gtk_container_set_border_width(GTK_CONTAINER(frame), 10);
+ gtk_box_pack_start(GTK_BOX(pvbox), frame, FALSE, FALSE, 0);
+
+ /* Frame vbox */
+ vbox = gtk_vbox_new(FALSE, 4);
+ gtk_container_add(GTK_CONTAINER(frame), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
+
+ /* Include mail folders */
+ checkbox = gtk_check_button_new_with_label(_("Mail folders"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.include_mail);
+ gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
+ gtk_widget_show(checkbox);
+ notify_page.include_mail = checkbox;
+
+ /* Include news folders */
+ checkbox = gtk_check_button_new_with_label(_("News folders"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.include_news);
+ gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
+ gtk_widget_show(checkbox);
+ notify_page.include_news = checkbox;
+
+ /* Include RSS folders */
+ checkbox = gtk_check_button_new_with_label(_("RSS folders"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.include_rss);
+ gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
+ gtk_widget_show(checkbox);
+ notify_page.include_rss = checkbox;
+
+ /* Include calendar folders */
+ checkbox = gtk_check_button_new_with_label(_("Calendar folders"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.include_calendar);
+ gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
+ gtk_widget_show(checkbox);
+ notify_page.include_calendar = checkbox;
+
+ /* Warning-Label */
+ label = gtk_label_new(_("These settings overwrite folder-specific "
+ "selections."));
+ gtk_misc_set_alignment(GTK_MISC(label),0,0.5);
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+
+ /* done with the frame */
+ gtk_widget_show(frame);
+ gtk_widget_show(vbox);
+
+ /* Frame */
+ frame = gtk_frame_new(_("Global notification settings"));
+ gtk_container_set_border_width(GTK_CONTAINER(frame), 10);
+ gtk_box_pack_start(GTK_BOX(pvbox), frame, FALSE, FALSE, 0);
+
+ /* Frame vbox */
+ vbox = gtk_vbox_new(FALSE, 4);
+ gtk_container_add(GTK_CONTAINER(frame), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
+
+ /* urgency hint new */
+ checkbox = gtk_check_button_new_with_label(_("Set window manager "
+ "urgency hint when new messages exist"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.urgency_hint_new);
+ gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
+ gtk_widget_show(checkbox);
+ notify_page.urgency_hint_new = checkbox;
+
+ /* urgency hint new */
+ checkbox = gtk_check_button_new_with_label(_("Set window manager "
+ "urgency hint when unread messages exist"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.urgency_hint_unread);
+ gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
+ gtk_widget_show(checkbox);
+ notify_page.urgency_hint_unread = checkbox;
+
+#ifdef HAVE_LIBCANBERRA_GTK
+ /* canberra */
+ checkbox = gtk_check_button_new_with_label(_("Use sound theme"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.canberra_play_sounds);
+ gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
+ gtk_widget_show(checkbox);
+ notify_page.canberra_play_sounds = checkbox;
+#endif
+
+ /* done with the frame */
+ gtk_widget_show(frame);
+ gtk_widget_show(vbox);
+
+ /* done with the page */
+ gtk_widget_show(pvbox);
+ page->widget = pvbox;
+}
+
+static void notify_destroy_prefs_page(PrefsPage *page)
+{
+}
+
+static void notify_save_prefs(PrefsPage *page)
+{
+ notify_config.include_mail =gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(notify_page.include_mail));
+ notify_config.include_news =gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(notify_page.include_news));
+ notify_config.include_rss =gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(notify_page.include_rss));
+ notify_config.include_calendar
+ = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(notify_page.include_calendar));
+ notify_config.urgency_hint_new = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(notify_page.urgency_hint_new));
+ notify_config.urgency_hint_unread = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(notify_page.urgency_hint_unread));
+#ifdef HAVE_LIBCANBERRA_GTK
+ notify_config.canberra_play_sounds = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(notify_page.canberra_play_sounds));
+#endif
+
+ notification_core_global_includes_changed();
+}
+
+#ifdef NOTIFICATION_BANNER
+static void notify_create_banner_page(PrefsPage *page, GtkWindow *window,
+ gpointer data)
+{
+ GtkRequisition requisition;
+ GtkWidget *pvbox;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *table;
+ GtkWidget *checkbox;
+ GtkWidget *button;
+ GtkWidget *combo;
+ GtkWidget *spinner;
+ GtkWidget *label;
+ GtkWidget *slider;
+ GtkWidget *color_sel;
+ GdkColor bg;
+ GdkColor fg;
+
+ pvbox = gtk_vbox_new(FALSE, 20);
+ gtk_container_set_border_width(GTK_CONTAINER(pvbox), 10);
+
+ /* Always / Never / Only when non-empty */
+ hbox = gtk_hbox_new(FALSE, 20);
+ gtk_box_pack_start(GTK_BOX(pvbox), hbox, FALSE, FALSE, 0);
+ label = gtk_label_new(_("Show banner"));
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+ combo = gtk_combo_box_new_text();
+ gtk_combo_box_insert_text(GTK_COMBO_BOX(combo), NOTIFY_BANNER_SHOW_NEVER,
+ _("Never"));
+ gtk_combo_box_insert_text(GTK_COMBO_BOX(combo), NOTIFY_BANNER_SHOW_ALWAYS,
+ _("Always"));
+ gtk_combo_box_insert_text(GTK_COMBO_BOX(combo), NOTIFY_BANNER_SHOW_NONEMPTY,
+ _("Only when not empty"));
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combo), notify_config.banner_show);
+ gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 0);
+ g_signal_connect(G_OBJECT(combo), "changed",
+ G_CALLBACK(notify_banner_enable_set_sensitivity), NULL);
+ gtk_widget_show(combo);
+ gtk_widget_show(hbox);
+ banner_page.banner_show = combo;
+
+ /* Container vbox for greying out everything */
+ vbox = gtk_vbox_new(FALSE, 10);
+ gtk_box_pack_start(GTK_BOX(pvbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show(vbox);
+ banner_page.banner_cont_enable = vbox;
+
+ /* Banner speed */
+ table = gtk_table_new(2, 3, FALSE);
+ gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
+ label = gtk_label_new(_("slow"));
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 0, 1);
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+ gtk_widget_show(label);
+ label = gtk_label_new(_("fast"));
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 2, 3, 0, 1);
+ gtk_misc_set_alignment(GTK_MISC(label), 1, 0);
+ gtk_widget_show(label);
+ label = gtk_label_new(_("Banner speed"));
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
+ gtk_widget_show(label);
+ slider = gtk_hscale_new_with_range(10., 70., 10.);
+ gtk_scale_set_digits(GTK_SCALE(slider), 0);
+ gtk_widget_size_request(combo, &requisition);
+ gtk_widget_set_size_request(slider, requisition.width, -1);
+ gtk_range_set_increments(GTK_RANGE(slider), 10., 10.);
+ gtk_range_set_inverted(GTK_RANGE(slider), TRUE);
+ gtk_scale_set_draw_value(GTK_SCALE(slider), FALSE);
+ gtk_range_set_value(GTK_RANGE(slider), notify_config.banner_speed);
+ gtk_table_attach_defaults(GTK_TABLE(table), slider, 1, 3, 1, 2);
+ gtk_widget_show(slider);
+ gtk_widget_show(table);
+ banner_page.banner_speed = slider;
+
+ /* Maximum number of messages in banner */
+ hbox = gtk_hbox_new(FALSE, 10);
+ label = gtk_label_new(_("Maximum number of messages (0 means unlimited)"));
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+ spinner = gtk_spin_button_new_with_range(0., 1000., 1.);
+ gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spinner), 0);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinner), notify_config.banner_max_msgs);
+ gtk_box_pack_start(GTK_BOX(hbox), spinner, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show(spinner);
+ gtk_widget_show(hbox);
+ banner_page.banner_max_msgs = spinner;
+
+ /* banner width */
+ hbox = gtk_hbox_new(FALSE, 10);
+ label = gtk_label_new(_("Banner width in pixels (0 means screen size)"));
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+ spinner = gtk_spin_button_new_with_range(0.,5000., 50);
+ gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spinner),0);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinner),notify_config.banner_width);
+ gtk_box_pack_start(GTK_BOX(hbox), spinner, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show(spinner);
+ gtk_widget_show(hbox);
+ banner_page.banner_width = spinner;
+
+ /* Include unread */
+ checkbox = gtk_check_button_new_with_label(_("Include unread mails in banner"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.banner_include_unread);
+ gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
+ gtk_widget_show(checkbox);
+ banner_page.banner_include_unread = checkbox;
+
+ /* Check button sticky */
+ checkbox = gtk_check_button_new_with_label(_("Make banner sticky"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.banner_sticky);
+ gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
+ gtk_widget_show(checkbox);
+ banner_page.banner_sticky = checkbox;
+
+ /* Check box for enabling folder specific selection */
+ hbox = gtk_hbox_new(FALSE, 10);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+ checkbox = gtk_check_button_new_with_label(_("Only include selected folders"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.banner_folder_specific);
+ gtk_box_pack_start(GTK_BOX(hbox), checkbox, FALSE, FALSE, 0);
+ g_signal_connect(G_OBJECT(checkbox), "toggled",
+ G_CALLBACK(notify_banner_folder_specific_set_sensitivity),
+ NULL);
+ gtk_widget_show(checkbox);
+ banner_page.banner_folder_specific = checkbox;
+ button = gtk_button_new_with_label(_("Select folders..."));
+ g_signal_connect(G_OBJECT(button), "clicked",
+ G_CALLBACK(notification_foldercheck_sel_folders_cb),
+ BANNER_SPECIFIC_FOLDER_ID_STR);
+ gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+ banner_page.banner_cont_folder_specific = button;
+ gtk_widget_show(button);
+ gtk_widget_show(hbox);
+
+ /* Check box for enabling custom colors */
+ checkbox = gtk_check_button_new_with_label(_("Use custom colors"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.banner_enable_colors);
+ gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
+ g_signal_connect(G_OBJECT(checkbox), "toggled",
+ G_CALLBACK(notify_banner_color_sel_set_sensitivity), NULL);
+ gtk_widget_show(checkbox);
+ banner_page.banner_enable_colors = checkbox;
+
+ /* Color selection dialogs for foreground and background color */
+ /* foreground */
+ table = gtk_table_new(2, 2, FALSE);
+ gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
+ label = gtk_label_new(_("Foreground"));
+ gtk_table_attach_defaults(GTK_TABLE(table),label,0,1,0,1);
+ gtk_widget_show(label);
+ color_sel = gtk_color_button_new();
+ gtkut_convert_int_to_gdk_color(notify_config.banner_color_fg,&fg);
+ gtk_color_button_set_color(GTK_COLOR_BUTTON(color_sel),&fg);
+ gtk_color_button_set_title(GTK_COLOR_BUTTON(color_sel),_("Foreground color"));
+ gtk_table_attach_defaults(GTK_TABLE(table),color_sel,1,2,0,1);
+ gtk_widget_show(color_sel);
+ banner_page.banner_color_fg = color_sel;
+ /* background */
+ label = gtk_label_new(_("Background"));
+ gtk_table_attach_defaults(GTK_TABLE(table),label,0,1,1,2);
+ gtk_widget_show(label);
+ color_sel = gtk_color_button_new();
+ gtkut_convert_int_to_gdk_color(notify_config.banner_color_bg,&bg);
+ gtk_color_button_set_color(GTK_COLOR_BUTTON(color_sel),&bg);
+ gtk_color_button_set_title(GTK_COLOR_BUTTON(color_sel), _("Background color"));
+ gtk_table_attach_defaults(GTK_TABLE(table),color_sel,1,2,1,2);
+ gtk_widget_show(color_sel);
+ gtk_widget_show(table);
+ banner_page.banner_color_bg = color_sel;
+ banner_page.banner_cont_color_sel = table;
+
+ notify_banner_color_sel_set_sensitivity
+ (GTK_TOGGLE_BUTTON(banner_page.banner_enable_colors), NULL);
+ notify_banner_folder_specific_set_sensitivity
+ (GTK_TOGGLE_BUTTON(banner_page.banner_folder_specific), NULL);
+ notify_banner_enable_set_sensitivity(GTK_COMBO_BOX(combo), NULL);
+ gtk_widget_show(pvbox);
+ banner_page.page.widget = pvbox;
+}
+
+static void notify_destroy_banner_page(PrefsPage *page)
+{
+}
+
+static void notify_save_banner(PrefsPage *page)
+{
+ gdouble range_val;
+ GdkColor color;
+
+ notify_config.banner_show =
+ gtk_combo_box_get_active(GTK_COMBO_BOX(banner_page.banner_show));
+ notify_config.banner_max_msgs =
+ gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(banner_page.banner_max_msgs));
+ notify_config.banner_width =
+ gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(banner_page.banner_width));
+ notify_config.banner_include_unread =
+ gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(banner_page.banner_include_unread));
+ range_val= gtk_range_get_value(GTK_RANGE(banner_page.banner_speed));
+ notify_config.banner_speed = (gint)ceil(range_val);
+
+ notify_config.banner_sticky =
+ gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(banner_page.banner_sticky));
+
+ notify_config.banner_folder_specific =
+ gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(banner_page.banner_folder_specific));
+
+ notify_config.banner_enable_colors =
+ gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(banner_page.banner_enable_colors));
+
+ /* Color dialogs are a bit more complicated */
+ gtk_color_button_get_color(GTK_COLOR_BUTTON(banner_page.banner_color_fg),
+ &color);
+ notify_config.banner_color_fg = conv_color_to_int(&color);
+ gtk_color_button_get_color(GTK_COLOR_BUTTON(banner_page.banner_color_bg),
+ &color);
+ notify_config.banner_color_bg = conv_color_to_int(&color);
+
+ notification_banner_destroy();
+ notification_update_banner();
+}
+
+static void notify_banner_enable_set_sensitivity(GtkComboBox *combo,
+ gpointer data)
+{
+ NotifyBannerShow show;
+
+ show = gtk_combo_box_get_active(GTK_COMBO_BOX(banner_page.banner_show));
+ gtk_widget_set_sensitive(banner_page.banner_cont_enable,
+ (show == NOTIFY_BANNER_SHOW_NEVER) ? FALSE : TRUE);
+}
+
+static void notify_banner_color_sel_set_sensitivity(GtkToggleButton *button,
+ gpointer data)
+{
+ gboolean active;
+ active = gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(banner_page.banner_enable_colors));
+ gtk_widget_set_sensitive(banner_page.banner_cont_color_sel, active);
+}
+
+static void notify_banner_folder_specific_set_sensitivity(GtkToggleButton *bu,
+ gpointer data)
+{
+ gboolean active;
+ active = gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(banner_page.banner_folder_specific));
+ gtk_widget_set_sensitive(banner_page.banner_cont_folder_specific, active);
+}
+#endif /* NOTIFICATION_BANNER */
+
+#ifdef NOTIFICATION_POPUP
+static void notify_create_popup_page(PrefsPage *page, GtkWindow *window,
+ gpointer data)
+{
+ gdouble timeout;
+ GtkWidget *pvbox;
+ GtkWidget *vbox;
+ GtkWidget *checkbox;
+ GtkWidget *hbox;
+ GtkWidget *spinner;
+ GtkWidget *label;
+ GtkWidget *button;
+#ifndef HAVE_LIBNOTIFY
+ GdkColor bg;
+ GdkColor fg;
+ GtkWidget *table;
+ GtkWidget *color_sel;
+#endif /* !HAVE_LIBNOTIFY */
+
+ pvbox = gtk_vbox_new(FALSE, 20);
+ gtk_container_set_border_width(GTK_CONTAINER(pvbox), 10);
+
+ /* Enable popup */
+ checkbox = gtk_check_button_new_with_label(_("Enable popup"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.popup_show);
+ gtk_box_pack_start(GTK_BOX(pvbox), checkbox, FALSE, FALSE, 0);
+ g_signal_connect(G_OBJECT(checkbox), "toggled",
+ G_CALLBACK(notify_popup_enable_set_sensitivity), NULL);
+ gtk_widget_show(checkbox);
+ popup_page.popup_show = checkbox;
+
+ /* Container vbox for greying out everything */
+ vbox = gtk_vbox_new(FALSE, 10);
+ gtk_box_pack_start(GTK_BOX(pvbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show(vbox);
+ popup_page.popup_cont_enable = vbox;
+
+ /* Popup timeout */
+ hbox = gtk_hbox_new(FALSE, 10);
+ label = gtk_label_new(_("Popup timeout:"));
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+ spinner = gtk_spin_button_new_with_range(TIMEOUT_SPINNER_MIN, TIMEOUT_SPINNER_MAX, TIMEOUT_SPINNER_STEP);
+ gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spinner), 1);
+ timeout = notify_config.popup_timeout/1000.;
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinner), timeout);
+ gtk_box_pack_start(GTK_BOX(hbox), spinner, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show(spinner);
+ label = gtk_label_new(_("seconds"));
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+ gtk_widget_show(hbox);
+ popup_page.popup_timeout = spinner;
+
+ /* Check box for enabling folder specific selection */
+ hbox = gtk_hbox_new(FALSE, 10);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+ checkbox = gtk_check_button_new_with_label(_("Only include selected folders"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.popup_folder_specific);
+ gtk_box_pack_start(GTK_BOX(hbox), checkbox, FALSE, FALSE, 0);
+ g_signal_connect(G_OBJECT(checkbox), "toggled",
+ G_CALLBACK(notify_popup_folder_specific_set_sensitivity),
+ NULL);
+ gtk_widget_show(checkbox);
+ popup_page.popup_folder_specific = checkbox;
+ button = gtk_button_new_with_label(_("Select folders..."));
+ g_signal_connect(G_OBJECT(button), "clicked",
+ G_CALLBACK(notification_foldercheck_sel_folders_cb),
+ POPUP_SPECIFIC_FOLDER_ID_STR);
+ gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+ popup_page.popup_cont_folder_specific = button;
+ gtk_widget_show(button);
+ gtk_widget_show(hbox);
+
+#ifndef HAVE_LIBNOTIFY
+ /* Sticky check button */
+ checkbox = gtk_check_button_new_with_label(_("Make popup sticky"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.popup_sticky);
+ gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
+ gtk_widget_show(checkbox);
+ popup_page.popup_sticky = checkbox;
+
+ /* Button to set size and position of popup window */
+ hbox = gtk_hbox_new(FALSE, 10);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+ button = gtk_button_new_with_label(_("Set popup window width and position"));
+ g_signal_connect(G_OBJECT(button), "clicked",
+ G_CALLBACK(notify_popup_set_cb), NULL);
+ gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show(button);
+ label = gtk_label_new(_("(the window manager is free to ignore this)"));
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+ gtk_widget_show(hbox);
+
+ /* Check box for enabling custom colors */
+ checkbox = gtk_check_button_new_with_label(_("Use custom colors"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.popup_enable_colors);
+ gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
+ g_signal_connect(G_OBJECT(checkbox), "toggled",
+ G_CALLBACK(notify_popup_color_sel_set_sensitivity), NULL);
+ gtk_widget_show(checkbox);
+ popup_page.popup_enable_colors = checkbox;
+
+ /* Color selection dialogs for foreground and background color */
+ table = gtk_table_new(2,2,FALSE);
+ gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
+ /* foreground */
+ label = gtk_label_new(_("Foreground"));
+ gtk_table_attach_defaults(GTK_TABLE(table),label,0,1,0,1);
+ gtk_widget_show(label);
+ color_sel = gtk_color_button_new();
+ gtkut_convert_int_to_gdk_color(notify_config.popup_color_fg,&fg);
+ gtk_color_button_set_color(GTK_COLOR_BUTTON(color_sel),&fg);
+ gtk_color_button_set_title(GTK_COLOR_BUTTON(color_sel),_("Foreground color"));
+ gtk_table_attach_defaults(GTK_TABLE(table),color_sel,1,2,0,1);
+ gtk_widget_show(color_sel);
+ popup_page.popup_color_fg = color_sel;
+ /* background */
+ label = gtk_label_new(_("Background"));
+ gtk_table_attach_defaults(GTK_TABLE(table),label,0,1,1,2);
+ gtk_widget_show(label);
+ color_sel = gtk_color_button_new();
+ gtkut_convert_int_to_gdk_color(notify_config.popup_color_bg,&bg);
+ gtk_color_button_set_color(GTK_COLOR_BUTTON(color_sel),&bg);
+ gtk_color_button_set_title(GTK_COLOR_BUTTON(color_sel),_("Background color"));
+ gtk_table_attach_defaults(GTK_TABLE(table),color_sel,1,2,1,2);
+ gtk_widget_show(color_sel);
+ gtk_widget_show(table);
+ popup_page.popup_color_bg = color_sel;
+ popup_page.popup_cont_color_sel = table;
+
+ notify_popup_color_sel_set_sensitivity
+ (GTK_TOGGLE_BUTTON(popup_page.popup_enable_colors), NULL);
+#else /* HAVE_LIBNOTIFY */
+ hbox = gtk_hbox_new(FALSE, 10);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+ checkbox = gtk_check_button_new_with_label(_("Display folder name"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.popup_display_folder_name);
+ gtk_box_pack_start(GTK_BOX(hbox), checkbox, FALSE, FALSE, 0);
+ gtk_widget_show(checkbox);
+ gtk_widget_show(hbox);
+ popup_page.popup_display_folder_name = checkbox;
+#endif /* HAVE_LIBNOTIFY */
+
+ notify_popup_enable_set_sensitivity
+ (GTK_TOGGLE_BUTTON(popup_page.popup_show), NULL);
+ notify_popup_folder_specific_set_sensitivity
+ (GTK_TOGGLE_BUTTON(popup_page.popup_folder_specific), NULL);
+ gtk_widget_show(pvbox);
+ popup_page.page.widget = pvbox;
+}
+
+static void notify_destroy_popup_page(PrefsPage *page)
+{
+}
+
+static void notify_save_popup(PrefsPage *page)
+{
+ gdouble timeout;
+
+#ifndef HAVE_LIBNOTIFY
+ GdkColor color;
+#endif /* !HAVE_LIBNOTIFY */
+
+ notify_config.popup_show =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(popup_page.popup_show));
+ timeout =
+ gtk_spin_button_get_value(GTK_SPIN_BUTTON(popup_page.popup_timeout));
+ notify_config.popup_timeout = (gint)floor(timeout*1000+0.5);
+ notify_config.popup_folder_specific =
+ gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(popup_page.popup_folder_specific));
+
+#ifndef HAVE_LIBNOTIFY
+ notify_config.popup_sticky =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(popup_page.popup_sticky));
+ notify_config.popup_enable_colors =
+ gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(popup_page.popup_enable_colors));
+
+ /* Color dialogs are a bit more complicated */
+ gtk_color_button_get_color(GTK_COLOR_BUTTON(popup_page.popup_color_fg),
+ &color);
+ notify_config.popup_color_fg = conv_color_to_int(&color);
+ gtk_color_button_get_color(GTK_COLOR_BUTTON(popup_page.popup_color_bg),
+ &color);
+ notify_config.popup_color_bg = conv_color_to_int(&color);
+#else /* HAVE_LIBNOTIFY */
+ notify_config.popup_display_folder_name =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(popup_page.popup_display_folder_name));
+#endif /* HAVE_LIBNOTIFY */
+}
+
+#ifndef HAVE_LIBNOTIFY
+static void notify_popup_set_cb(GtkWidget *widget, gpointer data)
+{
+ GtkWidget *win;
+ GtkWidget *button;
+
+ win = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "notification_prefs");
+ gtk_window_set_title(GTK_WINDOW(win), _("Sample popup window"));
+ gtk_window_set_modal(GTK_WINDOW(win), TRUE);
+ gtk_window_set_keep_above(GTK_WINDOW(win), TRUE);
+ gtk_window_set_resizable(GTK_WINDOW(win), TRUE);
+ gtk_container_set_border_width(GTK_CONTAINER(win), 5);
+
+ button = gtk_button_new_with_label(_("Done"));
+ g_signal_connect(G_OBJECT(button), "clicked",
+ G_CALLBACK(notify_popup_set_done_cb), win);
+ gtk_container_add(GTK_CONTAINER(win), button);
+
+ gtk_widget_show_all(win);
+}
+
+static void notify_popup_set_done_cb(GtkWidget *widget, gpointer data)
+{
+ GtkWidget *win = data;
+ gint dummy;
+
+ gtk_window_get_position(GTK_WINDOW(win),
+ &(notify_config.popup_root_x),
+ &(notify_config.popup_root_y));
+ gtk_window_get_size(GTK_WINDOW(win),
+ &(notify_config.popup_width), &dummy);
+ gtk_widget_destroy(win);
+}
+
+static void notify_popup_color_sel_set_sensitivity(GtkToggleButton *button,
+ gpointer data)
+{
+ gboolean active;
+ active = gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(popup_page.popup_enable_colors));
+ gtk_widget_set_sensitive(popup_page.popup_cont_color_sel, active);
+}
+#endif /* !HAVE_LIBNOTIFY */
+
+static void notify_popup_enable_set_sensitivity(GtkToggleButton *button,
+ gpointer data)
+{
+ gboolean active;
+ active = gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(popup_page.popup_show));
+ gtk_widget_set_sensitive(popup_page.popup_cont_enable, active);
+}
+
+static void notify_popup_folder_specific_set_sensitivity(GtkToggleButton *bu,
+ gpointer data)
+{
+ gboolean active;
+ active = gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(popup_page.popup_folder_specific));
+ gtk_widget_set_sensitive(popup_page.popup_cont_folder_specific, active);
+}
+
+#endif /* NOTIFICATION_POPUP */
+
+#ifdef NOTIFICATION_COMMAND
+static void notify_create_command_page(PrefsPage *page, GtkWindow *window,
+ gpointer data)
+{
+ GtkWidget *pvbox;
+ GtkWidget *vbox;
+ GtkWidget *checkbox;
+ GtkWidget *hbox;
+ GtkWidget *spinner;
+ GtkWidget *entry;
+ GtkWidget *label;
+ GtkWidget *button;
+ gdouble timeout;
+
+ pvbox = gtk_vbox_new(FALSE, 20);
+ gtk_container_set_border_width(GTK_CONTAINER(pvbox), 10);
+
+ /* Enable command */
+ checkbox = gtk_check_button_new_with_label(_("Enable command"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.command_enabled);
+ gtk_box_pack_start(GTK_BOX(pvbox), checkbox, FALSE, FALSE, 0);
+ g_signal_connect(G_OBJECT(checkbox), "toggled",
+ G_CALLBACK(notify_command_enable_set_sensitivity), NULL);
+ gtk_widget_show(checkbox);
+ command_page.command_enabled = checkbox;
+
+ /* Container vbox for greying out everything */
+ vbox = gtk_vbox_new(FALSE, 10);
+ gtk_box_pack_start(GTK_BOX(pvbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show(vbox);
+ command_page.command_cont_enable = vbox;
+
+ /* entry field for command to execute */
+ hbox = gtk_hbox_new(FALSE, 10);
+ label = gtk_label_new(_("Command to execute:"));
+ gtk_widget_show(label);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+ entry = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(entry), notify_config.command_line);
+ gtk_widget_show(entry);
+ gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 0);
+ gtk_widget_show(hbox);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+ command_page.command_line = entry;
+
+ /* Spin button for command timeout */
+ hbox = gtk_hbox_new(FALSE, 10);
+ label = gtk_label_new(_("Block command after execution for"));
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+ spinner = gtk_spin_button_new_with_range(0., 600., 1.);
+ gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spinner), 0);
+ timeout = notify_config.command_timeout/1000.;
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinner), timeout);
+ gtk_box_pack_start(GTK_BOX(hbox), spinner, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show(spinner);
+ label = gtk_label_new(_("seconds"));
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+ gtk_widget_show(hbox);
+ command_page.command_timeout = spinner;
+
+ /* Check box for enabling folder specific selection */
+ hbox = gtk_hbox_new(FALSE, 10);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+ checkbox = gtk_check_button_new_with_label(_("Only include selected folders"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.command_folder_specific);
+ gtk_box_pack_start(GTK_BOX(hbox), checkbox, FALSE, FALSE, 0);
+ g_signal_connect(G_OBJECT(checkbox), "toggled",
+ G_CALLBACK(notify_command_folder_specific_set_sensitivity),
+ NULL);
+ gtk_widget_show(checkbox);
+ command_page.command_folder_specific = checkbox;
+ button = gtk_button_new_with_label(_("Select folders..."));
+ g_signal_connect(G_OBJECT(button), "clicked",
+ G_CALLBACK(notification_foldercheck_sel_folders_cb),
+ COMMAND_SPECIFIC_FOLDER_ID_STR);
+ gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+ command_page.command_cont_folder_specific = button;
+ gtk_widget_show(button);
+ gtk_widget_show(hbox);
+
+ notify_command_enable_set_sensitivity
+ (GTK_TOGGLE_BUTTON(command_page.command_enabled), NULL);
+ notify_command_folder_specific_set_sensitivity
+ (GTK_TOGGLE_BUTTON(command_page.command_folder_specific), NULL);
+ gtk_widget_show(pvbox);
+ command_page.page.widget = pvbox;
+}
+
+static void notify_destroy_command_page(PrefsPage *page)
+{
+}
+
+static void notify_save_command(PrefsPage *page)
+{
+ gdouble timeout;
+ const gchar *tmp_str;
+
+ notify_config.command_enabled =
+ gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(command_page.command_enabled));
+
+ timeout =
+ gtk_spin_button_get_value(GTK_SPIN_BUTTON(command_page.command_timeout));
+ notify_config.command_timeout = (gint)floor(timeout*1000+0.5);
+ notify_config.command_folder_specific =
+ gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(command_page.command_folder_specific));
+
+ tmp_str = gtk_entry_get_text(GTK_ENTRY(command_page.command_line));
+ if(notify_config.command_line)
+ g_free(notify_config.command_line);
+ notify_config.command_line = g_strdup(tmp_str);
+}
+
+static void notify_command_enable_set_sensitivity(GtkToggleButton *button,
+ gpointer data)
+{
+ gboolean active;
+ active = gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(command_page.command_enabled));
+ gtk_widget_set_sensitive(command_page.command_cont_enable, active);
+}
+
+static void notify_command_folder_specific_set_sensitivity(GtkToggleButton *bu,
+ gpointer data)
+{
+ gboolean active;
+ active = gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(command_page.command_folder_specific));
+ gtk_widget_set_sensitive(command_page.command_cont_folder_specific, active);
+}
+
+#endif /* NOTIFICATION_COMMAND */
+
+#ifdef NOTIFICATION_LCDPROC
+static void notify_create_lcdproc_page(PrefsPage *page, GtkWindow *window,
+ gpointer data)
+{
+ GtkWidget *pvbox;
+ GtkWidget *vbox;
+ GtkWidget *label;
+ GtkWidget *entry;
+ GtkWidget *spinner;
+ GtkWidget *checkbox;
+ GtkWidget *hbox;
+
+ pvbox = gtk_vbox_new(FALSE, 20);
+ gtk_container_set_border_width(GTK_CONTAINER(pvbox), 10);
+
+ /* Enable lcdproc */
+ checkbox = gtk_check_button_new_with_label(_("Enable LCD"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.lcdproc_enabled);
+ gtk_box_pack_start(GTK_BOX(pvbox), checkbox, FALSE, FALSE, 0);
+ g_signal_connect(G_OBJECT(checkbox), "toggled",
+ G_CALLBACK(notify_lcdproc_enable_set_sensitivity), NULL);
+ gtk_widget_show(checkbox);
+ lcdproc_page.lcdproc_enabled = checkbox;
+
+ /* Container vbox for greying out everything */
+ vbox = gtk_vbox_new(FALSE, 10);
+ gtk_box_pack_start(GTK_BOX(pvbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show(vbox);
+ lcdproc_page.lcdproc_cont_enable = vbox;
+
+ /* Hostname and port information */
+ hbox = gtk_hbox_new(FALSE, 10);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show(hbox);
+ label = gtk_label_new(_("Hostname:Port of LCDd server:"));
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+ entry = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(entry), notify_config.lcdproc_hostname);
+ gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 0);
+ gtk_widget_show(entry);
+ gtk_widget_show(label);
+ label = gtk_label_new(":");
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+ spinner = gtk_spin_button_new_with_range(1., 65535., 1.);
+ gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spinner), 0);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinner),
+ notify_config.lcdproc_port);
+ gtk_box_pack_start(GTK_BOX(hbox), spinner, FALSE, FALSE, 0);
+ gtk_widget_show(spinner);
+ lcdproc_page.lcdproc_hostname = entry;
+ lcdproc_page.lcdproc_port = spinner;
+
+ notify_lcdproc_enable_set_sensitivity
+ (GTK_TOGGLE_BUTTON(lcdproc_page.lcdproc_enabled), NULL);
+ gtk_widget_show(pvbox);
+ lcdproc_page.page.widget = pvbox;
+}
+
+static void notify_destroy_lcdproc_page(PrefsPage *page)
+{
+}
+
+static void notify_save_lcdproc(PrefsPage *page)
+{
+ notify_config.lcdproc_enabled =
+ gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(lcdproc_page.lcdproc_enabled));
+
+ if(notify_config.lcdproc_hostname)
+ g_free(notify_config.lcdproc_hostname);
+ notify_config.lcdproc_hostname =
+ g_strdup(gtk_entry_get_text(GTK_ENTRY(lcdproc_page.lcdproc_hostname)));
+
+ notify_config.lcdproc_port =
+ gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(lcdproc_page.lcdproc_port));
+
+ if(notify_config.lcdproc_enabled)
+ notification_lcdproc_connect();
+ else
+ notification_lcdproc_disconnect();
+}
+
+static void notify_lcdproc_enable_set_sensitivity(GtkToggleButton *button,
+ gpointer data)
+{
+ gboolean active;
+ active = gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(lcdproc_page.lcdproc_enabled));
+ gtk_widget_set_sensitive(lcdproc_page.lcdproc_cont_enable, active);
+}
+#endif /* NOTIFICATION_LCDPROC */
+
+#ifdef NOTIFICATION_TRAYICON
+static void notify_create_trayicon_page(PrefsPage *page, GtkWindow *window,
+ gpointer data)
+{
+ GtkWidget *pvbox;
+ GtkWidget *vbox;
+ GtkWidget *checkbox;
+ GtkWidget *hbox;
+ GtkWidget *button;
+
+#if defined(HAVE_LIBNOTIFY)
+ GtkWidget *frame;
+ GtkWidget *svbox;
+ GtkWidget *label;
+#endif
+
+#ifdef HAVE_LIBNOTIFY
+ GtkWidget *ssvbox;
+ GtkWidget *spinner;
+ gdouble timeout;
+#endif
+
+ pvbox = gtk_vbox_new(FALSE, 20);
+ gtk_container_set_border_width(GTK_CONTAINER(pvbox), 10);
+
+ /* Enable trayicon */
+ checkbox = gtk_check_button_new_with_label(_("Enable Trayicon"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.trayicon_enabled);
+ gtk_box_pack_start(GTK_BOX(pvbox), checkbox, FALSE, FALSE, 0);
+ g_signal_connect(G_OBJECT(checkbox), "toggled",
+ G_CALLBACK(notify_trayicon_enable_set_sensitivity), NULL);
+ gtk_widget_show(checkbox);
+ trayicon_page.trayicon_enabled = checkbox;
+
+ /* Container vbox for greying out everything */
+ vbox = gtk_vbox_new(FALSE, 10);
+ gtk_box_pack_start(GTK_BOX(pvbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show(vbox);
+ trayicon_page.trayicon_cont_enable = vbox;
+
+ /* Hide at startup */
+ checkbox = gtk_check_button_new_with_label(_("Hide at start-up"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.trayicon_hide_at_startup);
+ gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
+ gtk_widget_show(checkbox);
+ trayicon_page.trayicon_hide_at_startup = checkbox;
+
+ /* Close to tray */
+ checkbox = gtk_check_button_new_with_label(_("Close to tray"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.trayicon_close_to_tray);
+ gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
+ gtk_widget_show(checkbox);
+ trayicon_page.trayicon_close_to_tray = checkbox;
+
+ /* Hide when iconified */
+ checkbox = gtk_check_button_new_with_label(_("Hide when iconified"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.trayicon_hide_when_iconified);
+ gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
+ gtk_widget_show(checkbox);
+ trayicon_page.trayicon_hide_when_iconified = checkbox;
+
+ /* folder specific */
+ hbox = gtk_hbox_new(FALSE, 10);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show(hbox);
+ checkbox = gtk_check_button_new_with_label(_("Only include selected folders"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.trayicon_folder_specific);
+ gtk_box_pack_start(GTK_BOX(hbox), checkbox, FALSE, FALSE, 0);
+ g_signal_connect(G_OBJECT(checkbox), "toggled",
+ G_CALLBACK(notify_trayicon_folder_specific_set_sensitivity),
+ NULL);
+ gtk_widget_show(checkbox);
+ trayicon_page.trayicon_folder_specific = checkbox;
+ button = gtk_button_new_with_label(_("Select folders..."));
+ g_signal_connect(G_OBJECT(button), "clicked",
+ G_CALLBACK(notification_foldercheck_sel_folders_cb),
+ TRAYICON_SPECIFIC_FOLDER_ID_STR);
+ gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+ trayicon_page.trayicon_cont_folder_specific = button;
+ gtk_widget_show(button);
+
+#ifdef HAVE_LIBNOTIFY
+ /* Frame for trayicon popup stuff */
+
+ /* TRANSLATORS: "Toaster" does not refer to the kitchen appliance.
+ A toast is a small passive (non-focus-stealing)
+ notification bubble. If your language does not have a word
+ for that, go for something along the lines of "passive popup"
+ instead.See also
+ http://en.wikipedia.org/wiki/Toast_(computing) */
+ frame = gtk_frame_new(_("Passive toaster popup"));
+ gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show(frame);
+
+ /* vbox for frame */
+ svbox = gtk_vbox_new(FALSE, 10);
+ gtk_container_set_border_width(GTK_CONTAINER(svbox), 5);
+ gtk_container_add(GTK_CONTAINER(frame), svbox);
+ gtk_widget_show(svbox);
+
+ /* Enable popup for the tray icon */
+ checkbox = gtk_check_button_new_with_label(_("Enable Popup"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.trayicon_popup_enabled);
+ g_signal_connect(G_OBJECT(checkbox), "toggled",
+ G_CALLBACK(notify_trayicon_popup_enable_set_sensitivity),
+ NULL);
+ gtk_box_pack_start(GTK_BOX(svbox), checkbox, FALSE, FALSE, 0);
+ gtk_widget_show(checkbox);
+ trayicon_page.trayicon_popup_enabled = checkbox;
+
+ /* vbox for trayicon popup stuff enabled/disabled container */
+ ssvbox = gtk_vbox_new(FALSE, 10);
+ gtk_box_pack_start(GTK_BOX(svbox), ssvbox, FALSE, FALSE, 0);
+ gtk_widget_show(ssvbox);
+ trayicon_page.trayicon_popup_cont_enable = ssvbox;
+
+ /* timeout */
+ hbox = gtk_hbox_new(FALSE, 10);
+ label = gtk_label_new(_("Popup timeout:"));
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+ spinner = gtk_spin_button_new_with_range(TIMEOUT_SPINNER_MIN, TIMEOUT_SPINNER_MAX, TIMEOUT_SPINNER_STEP);
+ gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spinner), 1);
+ timeout = notify_config.trayicon_popup_timeout/1000.;
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinner), timeout);
+ gtk_box_pack_start(GTK_BOX(hbox), spinner, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(ssvbox), hbox, FALSE, FALSE, 0);
+ label = gtk_label_new(_("seconds"));
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+ checkbox = gtk_check_button_new_with_label(_("Display folder name"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.trayicon_display_folder_name);
+ gtk_box_pack_start(GTK_BOX(ssvbox), checkbox, FALSE, FALSE, 0);
+ trayicon_page.trayicon_display_folder_name = checkbox;
+ gtk_widget_show(checkbox);
+
+ gtk_widget_show(spinner);
+ gtk_widget_show(label);
+ gtk_widget_show(hbox);
+ trayicon_page.trayicon_popup_timeout = spinner;
+
+#endif
+
+ notify_trayicon_enable_set_sensitivity
+ (GTK_TOGGLE_BUTTON(trayicon_page.trayicon_enabled), NULL);
+
+#ifdef HAVE_LIBNOTIFY
+ notify_trayicon_popup_enable_set_sensitivity
+ (GTK_TOGGLE_BUTTON(trayicon_page.trayicon_popup_enabled), NULL);
+#endif
+
+ notify_trayicon_folder_specific_set_sensitivity
+ (GTK_TOGGLE_BUTTON(trayicon_page.trayicon_folder_specific), NULL);
+
+ gtk_widget_show(pvbox);
+ trayicon_page.page.widget = pvbox;
+}
+
+static void notify_destroy_trayicon_page(PrefsPage *page)
+{
+}
+
+static void notify_save_trayicon(PrefsPage *page)
+{
+#ifdef HAVE_LIBNOTIFY
+ gdouble timeout;
+#endif
+
+ notify_config.trayicon_enabled =
+ gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(trayicon_page.trayicon_enabled));
+
+ notify_config.trayicon_hide_at_startup =
+ gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(trayicon_page.trayicon_hide_at_startup));
+
+ notify_config.trayicon_close_to_tray =
+ gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(trayicon_page.trayicon_close_to_tray));
+
+ notify_config.trayicon_hide_when_iconified =
+ gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(trayicon_page.trayicon_hide_when_iconified));
+
+#ifdef HAVE_LIBNOTIFY
+ notify_config.trayicon_popup_enabled =
+ gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(trayicon_page.trayicon_popup_enabled));
+
+ timeout =
+ gtk_spin_button_get_value(GTK_SPIN_BUTTON
+ (trayicon_page.trayicon_popup_timeout));
+ notify_config.trayicon_popup_timeout = (gint)floor(timeout*1000+0.5);
+
+ notify_config.trayicon_display_folder_name =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(trayicon_page.trayicon_display_folder_name));
+#endif
+
+ notify_config.trayicon_folder_specific =
+ gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(trayicon_page.trayicon_folder_specific));
+
+ if(notify_config.trayicon_enabled)
+ notification_update_msg_counts(NULL);
+ else
+ notification_trayicon_destroy();
+}
+
+static void notify_trayicon_enable_set_sensitivity(GtkToggleButton *button,
+ gpointer data)
+{
+ gboolean active;
+ active = gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(trayicon_page.trayicon_enabled));
+ gtk_widget_set_sensitive(trayicon_page.trayicon_cont_enable, active);
+}
+
+static void notify_trayicon_folder_specific_set_sensitivity(GtkToggleButton *bu,
+ gpointer data)
+{
+ gboolean active;
+ active = gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(trayicon_page.trayicon_folder_specific));
+ gtk_widget_set_sensitive(trayicon_page.trayicon_cont_folder_specific, active);
+}
+
+#ifdef HAVE_LIBNOTIFY
+static void notify_trayicon_popup_enable_set_sensitivity(GtkToggleButton *bu,
+ gpointer data)
+{
+ gboolean active;
+ active = gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(trayicon_page.trayicon_popup_enabled));
+ gtk_widget_set_sensitive(trayicon_page.trayicon_popup_cont_enable, active);
+}
+
+#endif /* HAVE_LIBNOTIFY */
+
+#endif /* NOTIFICATION_TRAYICON */
+
+#ifdef NOTIFICATION_INDICATOR
+
+#define NOTIFICATION_INDICATOR_REGISTER_PATH ".config/indicators/messages/applications"
+
+static gboolean indicator_register_file_exists(void)
+{
+ gboolean retval;
+ gchar *filepath;
+ filepath = g_strconcat(get_home_dir(), "/" NOTIFICATION_INDICATOR_REGISTER_PATH "/claws-mail", NULL);
+ retval = is_file_exist(filepath);
+ g_free(filepath);
+ return retval;
+}
+
+static gboolean indicator_register_file_create(void)
+{
+ FILE *fp;
+ gchar *path, *filepath;
+ gboolean success;
+
+ /* make sure directory path exists */
+ path = g_strconcat(get_home_dir(), "/" NOTIFICATION_INDICATOR_REGISTER_PATH, NULL);
+ if(!is_dir_exist(path)) {
+ if(make_dir_hier(path) != -1)
+ debug_print("Notification plugin: Created directory '%s'\n", path);
+ else {
+ debug_print("Notification plugin: Error creating directory '%s'\n", path);
+ g_free(path);
+ return FALSE;
+ }
+ }
+
+ /* create register file */
+ filepath = g_strconcat(path, "/claws-mail", NULL);
+ g_free(path);
+ fp = fopen(filepath, "w");
+ success = (fp != NULL);
+ if(fp) {
+ fprintf(fp, "%s\n", get_desktop_file());
+ fclose(fp);
+ debug_print("Notification plugin: Created desktop indicator file '%s'\n", filepath);
+ }
+ else
+ debug_print("Notification plugin: Could not create desktop indicator file '%s'\n", filepath);
+ g_free(filepath);
+
+ return success;
+}
+
+static gboolean indicator_register_file_remove(void)
+{
+ gchar *filepath;
+ int retval;
+
+ filepath = g_strconcat(get_home_dir(), "/" NOTIFICATION_INDICATOR_REGISTER_PATH "/claws-mail", NULL);
+ retval = claws_unlink(filepath);
+ if(retval != -1)
+ debug_print("Notification plugin: Deleted file '%s'\n", filepath);
+ else
+ debug_print("Notification plugin: Error deleting file '%s'\n", filepath);
+ g_free(filepath);
+
+ return (retval != -1);
+}
+
+static void notify_create_indicator_page(PrefsPage *page, GtkWindow *window,
+ gpointer data)
+{
+ GtkWidget *pvbox;
+ GtkWidget *vbox;
+ GtkWidget *checkbox;
+ GtkWidget *ind_reg;
+
+ pvbox = gtk_vbox_new(FALSE, 20);
+ gtk_container_set_border_width(GTK_CONTAINER(pvbox), 10);
+
+ /* Enable indicator */
+ checkbox = gtk_check_button_new_with_label(_("Add to Indicator Applet"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+ notify_config.indicator_enabled);
+ gtk_box_pack_start(GTK_BOX(pvbox), checkbox, FALSE, FALSE, 0);
+ g_signal_connect(G_OBJECT(checkbox), "toggled",
+ G_CALLBACK(notify_indicator_enable_set_sensitivity), NULL);
+ indicator_page.indicator_enabled = checkbox;
+
+ /* Container vbox for greying out everything */
+ vbox = gtk_vbox_new(FALSE, 10);
+ gtk_box_pack_start(GTK_BOX(pvbox), vbox, FALSE, FALSE, 0);
+ indicator_page.indicator_cont_enable = vbox;
+
+ /* hide when minimized */
+ checkbox = gtk_check_button_new_with_label(_("Hide mainwindow when minimized"));
+ gtk_box_pack_start(GTK_BOX(indicator_page.indicator_cont_enable), checkbox, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox), notify_config.indicator_hide_minimized);
+ indicator_page.indicator_hide_minimized = checkbox;
+
+ /* register */
+ ind_reg = gtk_check_button_new_with_label(_("Register Claws Mail"));
+ gtk_box_pack_start(GTK_BOX(indicator_page.indicator_cont_enable), ind_reg, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ind_reg), indicator_register_file_exists());
+ indicator_page.indicator_register = ind_reg;
+
+ notify_indicator_enable_set_sensitivity(GTK_TOGGLE_BUTTON(indicator_page.indicator_enabled), NULL);
+ gtk_widget_show_all(pvbox);
+ indicator_page.page.widget = pvbox;
+}
+
+static void notify_destroy_indicator_page(PrefsPage *page)
+{
+}
+
+static void notify_save_indicator(PrefsPage *page)
+{
+ gboolean ind_reg;
+
+ notify_config.indicator_enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(indicator_page.indicator_enabled));
+
+ notify_config.indicator_hide_minimized = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(indicator_page.indicator_hide_minimized));
+
+ ind_reg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(indicator_page.indicator_register));
+ if(ind_reg) {
+ if(!indicator_register_file_exists())
+ indicator_register_file_create();
+ }
+ else {
+ if(indicator_register_file_exists())
+ indicator_register_file_remove();
+ }
+
+ notification_indicator_destroy();
+ notification_update_indicator();
+}
+
+static void notify_indicator_enable_set_sensitivity(GtkToggleButton *button,
+ gpointer data)
+{
+ gboolean active;
+ active = gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(indicator_page.indicator_enabled));
+ gtk_widget_set_sensitive(indicator_page.indicator_cont_enable, active);
+}
+#endif /* NOTIFICATION_INDICATOR */
+
+#ifdef NOTIFICATION_HOTKEYS
+static void notify_create_hotkeys_page(PrefsPage *page, GtkWindow *window, gpointer data)
+{
+ GtkWidget *pvbox;
+ GtkWidget *vbox;
+ GtkWidget *checkbox;
+ GtkWidget *label;
+ gchar *markup;
+ GtkWidget *table;
+ GtkWidget *entry;
+
+ pvbox = gtk_vbox_new(FALSE, 20);
+ gtk_container_set_border_width(GTK_CONTAINER(pvbox), 10);
+
+ /* Enable hotkeys */
+ checkbox = gtk_check_button_new_with_label(_("Enable global hotkeys"));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox), notify_config.hotkeys_enabled);
+ gtk_box_pack_start(GTK_BOX(pvbox), checkbox, FALSE, FALSE, 0);
+ g_signal_connect(G_OBJECT(checkbox), "toggled",
+ G_CALLBACK(notify_hotkeys_enable_set_sensitivity), NULL);
+ hotkeys_page.hotkeys_enabled = checkbox;
+
+ /* Container vbox for greying out everything */
+ vbox = gtk_vbox_new(FALSE, 10);
+ gtk_box_pack_start(GTK_BOX(pvbox), vbox, FALSE, FALSE, 0);
+ hotkeys_page.hotkeys_cont_enable = vbox;
+
+ /* description */
+ label = gtk_label_new("");
+ markup = g_markup_printf_escaped(_("Examples for hotkeys include <b>%s</b> and <b>%s</b>"), _("<control><shift>F11"), _("<alt>N"));
+ gtk_label_set_markup(GTK_LABEL(label), markup);
+ gtk_misc_set_alignment(GTK_MISC(label), 0., 0.);
+ g_free(markup);
+ gtk_box_pack_start(GTK_BOX(hotkeys_page.hotkeys_cont_enable), label, FALSE, FALSE, 0);
+
+ /* table for entry fields */
+ table = gtk_table_new(1, 2, FALSE);
+ gtk_box_pack_start(GTK_BOX(hotkeys_page.hotkeys_cont_enable), table, FALSE, FALSE, 0);
+
+ /* toggle mainwindow */
+ label = gtk_label_new(_("Toggle minimize:"));
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
+ entry = gtk_entry_new();
+ gtk_table_attach_defaults(GTK_TABLE(table), entry, 1, 2, 0, 1);
+ if(notify_config.hotkeys_toggle_mainwindow)
+ gtk_entry_set_text(GTK_ENTRY(entry), notify_config.hotkeys_toggle_mainwindow);
+ hotkeys_page.hotkeys_toggle_mainwindow = entry;
+
+ notify_hotkeys_enable_set_sensitivity(GTK_TOGGLE_BUTTON(hotkeys_page.hotkeys_enabled), NULL);
+ gtk_widget_show_all(pvbox);
+ hotkeys_page.page.widget = pvbox;
+}
+
+static void notify_destroy_hotkeys_page(PrefsPage *page)
+{
+}
+
+static void notify_save_hotkeys(PrefsPage *page)
+{
+ const gchar *tmp_str;
+ notify_config.hotkeys_enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hotkeys_page.hotkeys_enabled));
+
+ tmp_str = gtk_entry_get_text(GTK_ENTRY(hotkeys_page.hotkeys_toggle_mainwindow));
+ if(notify_config.hotkeys_toggle_mainwindow)
+ g_free(notify_config.hotkeys_toggle_mainwindow);
+ notify_config.hotkeys_toggle_mainwindow = g_strdup(tmp_str);
+
+ notification_hotkeys_update_bindings();
+}
+
+static void notify_hotkeys_enable_set_sensitivity(GtkToggleButton *button,
+ gpointer data)
+{
+ gboolean active;
+ active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hotkeys_page.hotkeys_enabled));
+ gtk_widget_set_sensitive(hotkeys_page.hotkeys_cont_enable, active);
+}
+#endif /* hotkeys */
+
+
+/* This feels so wrong... */
+static gint conv_color_to_int(GdkColor *color)
+{
+ gint result;
+ guint red, green, blue;
+
+ red = (guint) ((gdouble)(color->red) /65535.*255.);
+ green = (guint) ((gdouble)(color->green) /65535.*255.);
+ blue = (guint) ((gdouble)(color->blue) /65535.*255.);
+
+ result = (gint) (blue | (green<<8)| (red<<16));
+
+ return result;
+}
diff --git a/src/plugins/notification/notification_prefs.h b/src/plugins/notification/notification_prefs.h
@@ -0,0 +1,118 @@
+/* Notification plugin for Claws-Mail
+ * Copyright (C) 2005-2007 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NOTIFICATION_PREFS_H
+#define NOTIFICATION_PREFS_H NOTIFICATION_PREFS_H
+
+#include "pluginconfig.h"
+
+#include <glib.h>
+
+#include "prefs_gtk.h"
+
+#include "notification_banner.h"
+
+#ifdef NOTIFICATION_BANNER
+typedef enum {
+ NOTIFY_BANNER_SHOW_NEVER = 0,
+ NOTIFY_BANNER_SHOW_ALWAYS,
+ NOTIFY_BANNER_SHOW_NONEMPTY
+} NotifyBannerShow;
+#endif
+
+typedef struct {
+ gboolean include_mail;
+ gboolean include_news;
+ gboolean include_rss;
+ gboolean include_calendar;
+ gboolean urgency_hint_new;
+ gboolean urgency_hint_unread;
+#ifdef HAVE_LIBCANBERRA_GTK
+ gboolean canberra_play_sounds;
+#endif
+#ifdef NOTIFICATION_BANNER
+ NotifyBannerShow banner_show;
+ gint banner_speed;
+ gboolean banner_include_unread;
+ gint banner_max_msgs;
+ gboolean banner_sticky;
+ gint banner_root_x;
+ gint banner_root_y;
+ gboolean banner_folder_specific;
+ gboolean banner_enable_colors;
+ gulong banner_color_bg;
+ gulong banner_color_fg;
+ gint banner_width;
+#endif
+#ifdef NOTIFICATION_POPUP
+ gboolean popup_show;
+ gint popup_timeout;
+ gboolean popup_folder_specific;
+#ifndef HAVE_LIBNOTIFY
+ gboolean popup_sticky;
+ gint popup_root_x;
+ gint popup_root_y;
+ gint popup_width;
+ gboolean popup_enable_colors;
+ gulong popup_color_bg;
+ gulong popup_color_fg;
+#else /* HAVE_LIBNOTIFY */
+ gboolean popup_display_folder_name;
+#endif /* HAVE_LIBNOTIFY */
+#endif /* Popup */
+#ifdef NOTIFICATION_COMMAND
+ gboolean command_enabled;
+ gint command_timeout;
+ gboolean command_folder_specific;
+ gchar* command_line;
+#endif
+#ifdef NOTIFICATION_LCDPROC
+ gboolean lcdproc_enabled;
+ gchar* lcdproc_hostname;
+ guint lcdproc_port;
+
+#endif
+#ifdef NOTIFICATION_TRAYICON
+ gboolean trayicon_enabled;
+ gboolean trayicon_hide_at_startup;
+ gboolean trayicon_close_to_tray;
+ gboolean trayicon_hide_when_iconified;
+ gboolean trayicon_folder_specific;
+#ifdef HAVE_LIBNOTIFY
+ gboolean trayicon_display_folder_name;
+ gboolean trayicon_popup_enabled;
+ gint trayicon_popup_timeout;
+#endif /* HAVE_LIBNOTIFY */
+#endif /* Trayicon */
+#ifdef NOTIFICATION_INDICATOR
+ gboolean indicator_enabled;
+ gboolean indicator_hide_minimized;
+#endif /* NOTIFICATION_INDICATOR */
+#ifdef NOTIFICATION_HOTKEYS
+ gboolean hotkeys_enabled;
+ gchar* hotkeys_toggle_mainwindow;
+#endif /* Hotkeys */
+} NotifyPrefs;
+
+extern NotifyPrefs notify_config;
+extern PrefParam notify_param[];
+
+void notify_gtk_init(void);
+void notify_gtk_done(void);
+void notify_save_config(void);
+
+#endif /* NOTIFICATION_PREFS_H */
diff --git a/src/plugins/notification/notification_trayicon.c b/src/plugins/notification/notification_trayicon.c
@@ -0,0 +1,948 @@
+/* Notification plugin for Claws-Mail
+ * Copyright (C) 2005-2007 Holger Berndt and the Claws Mail Team.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* This module is of course inspired by the trayicon plugin which is
+ * shipped with Claws-Mail, copyrighted by the Claws-Mail Team. */
+
+#include "pluginconfig.h"
+
+#ifdef NOTIFICATION_TRAYICON
+
+#include "notification_trayicon.h"
+#include "notification_prefs.h"
+#include "notification_core.h"
+#include "notification_hotkeys.h"
+#include "notification_pixbuf.h"
+#include "notification_foldercheck.h"
+
+#include "main.h"
+#include "account.h"
+#include "mainwindow.h"
+#include "prefs_common.h"
+#include "alertpanel.h"
+#include "gtk/menu.h"
+#ifndef USE_NEW_ADDRBOOK
+ #include "addressbook.h"
+ #include "addrindex.h"
+#else
+ #include "addressbook-dbus.h"
+#endif
+
+#include "gtk/manage_window.h"
+#include "common/utils.h"
+#include "gtk/gtkutils.h"
+
+static GdkPixbuf* notification_trayicon_create(void);
+
+static void notification_trayicon_on_popup_menu(GtkStatusIcon*,guint,
+ guint,gpointer);
+static gboolean notification_trayicon_on_size_changed(GtkStatusIcon*,
+ gint, gpointer);
+
+static void trayicon_get_all_cb(GtkAction*, gpointer);
+static void trayicon_compose_cb(GtkAction*, gpointer);
+static void trayicon_compose_acc_cb(GtkMenuItem*, gpointer);
+static void trayicon_addressbook_cb(GtkAction*, gpointer);
+static void trayicon_exit_cb(GtkAction*,gpointer);
+static void trayicon_toggle_offline_cb(GtkAction*,gpointer);
+#ifdef HAVE_LIBNOTIFY
+static void trayicon_toggle_notify_cb(GtkAction*,gpointer);
+#endif
+
+#ifdef HAVE_LIBNOTIFY
+#include <libnotify/notify.h>
+
+#ifndef NOTIFY_CHECK_VERSION
+# define NOTIFY_CHECK_VERSION(a,b,c) 0
+#endif
+
+typedef struct {
+ gint count;
+ gint num_mail;
+ gint num_news;
+ gint num_calendar;
+ gint num_rss;
+ gchar *msg_path;
+ NotifyNotification *notification;
+ GError *error;
+} NotificationTrayiconPopup;
+
+static NotificationTrayiconPopup popup;
+
+static gboolean notification_trayicon_popup_add_msg(MsgInfo*,
+ NotificationFolderType);
+static gboolean notification_trayicon_popup_create(MsgInfo*,
+ NotificationFolderType);
+static void popup_timeout_fun(NotifyNotification*, gpointer);
+static void notification_trayicon_popup_free_func(gpointer);
+static void notification_trayicon_popup_default_action_cb(NotifyNotification*,
+ const char*,void*);
+static gchar* notification_trayicon_popup_assemble_summary(void);
+static gchar* notification_trayicon_popup_assemble_body(MsgInfo*);
+static void notification_trayicon_popup_count_msgs(NotificationFolderType);
+
+G_LOCK_DEFINE_STATIC(trayicon_popup);
+
+#endif
+
+static GtkStatusIcon *trayicon;
+static gboolean updating_menu = FALSE;
+static GtkWidget *traymenu_popup;
+
+static GtkActionEntry trayicon_popup_menu_entries[] = {
+ {"SysTrayiconPopup", NULL, "SysTrayiconPopup" },
+ {"SysTrayiconPopup/GetMail", NULL, N_("_Get Mail"), NULL, NULL, G_CALLBACK(trayicon_get_all_cb) },
+ {"SysTrayiconPopup/---", NULL, "---" },
+ {"SysTrayiconPopup/Email", NULL, N_("_Email"), NULL, NULL, G_CALLBACK(trayicon_compose_cb) },
+ {"SysTrayiconPopup/EmailAcc", NULL, N_("E_mail from account"), NULL, NULL, NULL },
+ {"SysTrayiconPopup/OpenAB", NULL, N_("Open A_ddressbook"), NULL, NULL, G_CALLBACK(trayicon_addressbook_cb) },
+ {"SysTrayiconPopup/Exit", NULL, N_("E_xit Claws Mail"), NULL, NULL, G_CALLBACK(trayicon_exit_cb) },
+};
+
+static GtkToggleActionEntry trayicon_popup_toggle_menu_entries[] =
+{
+ {"SysTrayiconPopup/ToggleOffline", NULL, N_("_Work Offline"), NULL, NULL, G_CALLBACK(trayicon_toggle_offline_cb) },
+#ifdef HAVE_LIBNOTIFY
+ {"SysTrayiconPopup/ShowBubbles", NULL, N_("Show Trayicon Notifications"), NULL, NULL, G_CALLBACK(trayicon_toggle_notify_cb) },
+#endif
+};
+
+
+void notification_trayicon_msg(MsgInfo *msginfo)
+{
+#ifndef HAVE_LIBNOTIFY
+ return;
+
+#else
+ FolderType ftype;
+ NotificationFolderType nftype;
+ gchar *uistr;
+
+ nftype = F_TYPE_MAIL;
+
+ if(!msginfo || !notify_config.trayicon_enabled ||
+ !notify_config.trayicon_popup_enabled ||
+ !MSG_IS_NEW(msginfo->flags))
+ return;
+
+ if(notify_config.trayicon_folder_specific) {
+ guint id;
+ GSList *list;
+ gchar *identifier;
+ gboolean found = FALSE;
+
+ if(!(msginfo->folder))
+ return;
+
+ identifier = folder_item_get_identifier(msginfo->folder);
+
+ id =
+ notification_register_folder_specific_list
+ (TRAYICON_SPECIFIC_FOLDER_ID_STR);
+ list = notification_foldercheck_get_list(id);
+ for(; (list != NULL) && !found; list = g_slist_next(list)) {
+ gchar *list_identifier;
+ FolderItem *list_item = (FolderItem*) list->data;
+
+ list_identifier = folder_item_get_identifier(list_item);
+ if(!strcmp2(list_identifier, identifier))
+ found = TRUE;
+
+ g_free(list_identifier);
+ }
+ g_free(identifier);
+
+ if(!found)
+ return;
+ } /* folder specific */
+
+ ftype = msginfo->folder->folder->klass->type;
+
+ G_LOCK(trayicon_popup);
+ /* Check out which type to notify about */
+ switch(ftype) {
+ case F_MH:
+ case F_MBOX:
+ case F_MAILDIR:
+ case F_IMAP:
+ nftype = F_TYPE_MAIL;
+ break;
+ case F_NEWS:
+ nftype = F_TYPE_NEWS;
+ break;
+ case F_UNKNOWN:
+ if((uistr = msginfo->folder->folder->klass->uistr) == NULL) {
+ G_UNLOCK(trayicon_popup);
+ return;
+ }
+ else if(!strcmp(uistr, "vCalendar"))
+ nftype = F_TYPE_CALENDAR;
+ else if(!strcmp(uistr, "RSSyl"))
+ nftype = F_TYPE_RSS;
+ else {
+ debug_print("Notification Plugin: Unknown folder type %d\n",ftype);
+ G_UNLOCK(trayicon_popup);
+ return;
+ }
+ break;
+ default:
+ debug_print("Notification Plugin: Unknown folder type %d\n",ftype);
+ G_UNLOCK(trayicon_popup);
+ return;
+ }
+
+
+ notification_trayicon_popup_add_msg(msginfo, nftype);
+
+ G_UNLOCK(trayicon_popup);
+
+#endif /* HAVE_LIBNOTIFY */
+}
+
+void notification_trayicon_destroy(void)
+{
+ if(trayicon) {
+ gtk_status_icon_set_visible(trayicon, FALSE);
+ g_object_unref(trayicon);
+ trayicon = NULL;
+ }
+}
+
+void notification_update_trayicon()
+{
+ gchar *buf;
+ static GdkPixbuf *old_icon = NULL;
+ GdkPixbuf *new_icon;
+ gint offset;
+ NotificationMsgCount count;
+ GSList *list;
+
+ if(!notify_config.trayicon_enabled)
+ return;
+
+ if(notify_config.trayicon_folder_specific) {
+ guint id;
+ id =
+ notification_register_folder_specific_list
+ (TRAYICON_SPECIFIC_FOLDER_ID_STR);
+ list = notification_foldercheck_get_list(id);
+ }
+ else
+ list = NULL;
+
+ notification_core_get_msg_count(list, &count);
+
+ if(!trayicon) {
+
+#ifdef NOTIFICATION_HOTKEYS
+ notification_hotkeys_update_bindings();
+#endif
+
+ old_icon = notification_trayicon_create();
+ if(!trayicon) {
+ debug_print("Notification plugin: Could not create trayicon\n");
+ return;
+ }
+ }
+
+ /* Tooltip */
+ buf = g_strdup_printf(_("New %d, Unread: %d, Total: %d"),
+ count.new_msgs, count.unread_msgs,
+ count.total_msgs);
+#if GTK_CHECK_VERSION(2,16,0)
+ gtk_status_icon_set_tooltip_text(trayicon, buf);
+#else
+ gtk_status_icon_set_tooltip(trayicon, buf);
+#endif
+ g_free(buf);
+
+ /* Pixmap */
+ (prefs_common_get_prefs()->work_offline) ? (offset = 1) : (offset = 0);
+
+ if((count.new_msgs > 0) && (count.unreadmarked_msgs > 0))
+ new_icon =
+ notification_pixbuf_get(NOTIFICATION_TRAYICON_NEWMARKEDMAIL+offset);
+ else if(count.new_msgs > 0)
+ new_icon =
+ notification_pixbuf_get(NOTIFICATION_TRAYICON_NEWMAIL+offset);
+ else if(count.unreadmarked_msgs > 0)
+ new_icon =
+ notification_pixbuf_get(NOTIFICATION_TRAYICON_UNREADMARKEDMAIL+offset);
+ else if(count.unread_msgs > 0)
+ new_icon =
+ notification_pixbuf_get(NOTIFICATION_TRAYICON_UNREADMAIL+offset);
+ else
+ new_icon =
+ notification_pixbuf_get(NOTIFICATION_TRAYICON_NOMAIL+offset);
+
+ if(new_icon != old_icon) {
+ gtk_status_icon_set_from_pixbuf(trayicon, new_icon);
+ old_icon = new_icon;
+ }
+}
+
+gboolean notification_trayicon_main_window_close(gpointer source, gpointer data)
+{
+ if(!notify_config.trayicon_enabled)
+ return FALSE;
+
+ if(source) {
+ gboolean *close_allowed = (gboolean*)source;
+
+ if(notify_config.trayicon_close_to_tray) {
+ MainWindow *mainwin = mainwindow_get_mainwindow();
+
+ *close_allowed = FALSE;
+ if(mainwin && gtk_widget_get_visible(GTK_WIDGET(mainwin->window)))
+ main_window_hide(mainwin);
+ }
+ }
+ return FALSE;
+}
+
+gboolean notification_trayicon_main_window_got_iconified(gpointer source,
+ gpointer data)
+{
+ MainWindow *mainwin = mainwindow_get_mainwindow();
+
+ if(!notify_config.trayicon_enabled)
+ return FALSE;
+
+ if(notify_config.trayicon_hide_when_iconified &&
+ mainwin && gtk_widget_get_visible(GTK_WIDGET(mainwin->window))
+ && !gtk_window_get_skip_taskbar_hint(GTK_WINDOW(mainwin->window))) {
+ gtk_window_set_skip_taskbar_hint(GTK_WINDOW(mainwin->window), TRUE);
+ }
+ return FALSE;
+}
+
+gboolean notification_trayicon_account_list_changed(gpointer source,
+ gpointer data)
+{
+ GList *cur_ac;
+ GtkWidget *menu, *submenu;
+ GtkWidget *menuitem;
+ PrefsAccount *ac_prefs;
+
+ GList *account_list = account_get_list();
+
+ if(!notify_config.trayicon_enabled)
+ return FALSE;
+
+ menu = gtk_ui_manager_get_widget(gtkut_ui_manager(), "/Menus/SysTrayiconPopup/EmailAcc");
+ gtk_widget_show(menu);
+
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu), NULL);
+ submenu = gtk_menu_new();
+
+ for(cur_ac = account_list; cur_ac != NULL; cur_ac = cur_ac->next) {
+ ac_prefs = (PrefsAccount *)cur_ac->data;
+
+ menuitem = gtk_menu_item_new_with_label
+ (ac_prefs->account_name ? ac_prefs->account_name
+ : _("Untitled"));
+ gtk_widget_show(menuitem);
+ gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
+ g_signal_connect(G_OBJECT(menuitem), "activate",
+ G_CALLBACK(trayicon_compose_acc_cb),
+ ac_prefs);
+ }
+ gtk_widget_show(submenu);
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu), submenu);
+ return FALSE;
+}
+
+static GdkPixbuf* notification_trayicon_create(void)
+{
+ GdkPixbuf *trayicon_nomail;
+ GtkActionGroup *action_group;
+
+ trayicon_nomail = notification_pixbuf_get(NOTIFICATION_TRAYICON_NOMAIL);
+
+ notification_trayicon_destroy();
+
+ trayicon = gtk_status_icon_new_from_pixbuf(trayicon_nomail);
+
+ g_signal_connect(G_OBJECT(trayicon), "activate",
+ G_CALLBACK(notification_trayicon_on_activate), NULL);
+ g_signal_connect(G_OBJECT(trayicon), "popup-menu",
+ G_CALLBACK(notification_trayicon_on_popup_menu), NULL);
+ g_signal_connect(G_OBJECT(trayicon), "size-changed",
+ G_CALLBACK(notification_trayicon_on_size_changed), NULL);
+
+ /* Popup-Menu */
+ action_group = cm_menu_create_action_group("SysTrayiconPopup", trayicon_popup_menu_entries,
+ G_N_ELEMENTS(trayicon_popup_menu_entries), NULL);
+ gtk_action_group_add_toggle_actions(action_group, trayicon_popup_toggle_menu_entries,
+ G_N_ELEMENTS(trayicon_popup_toggle_menu_entries), NULL);
+
+ MENUITEM_ADDUI("/Menus", "SysTrayiconPopup", "SysTrayiconPopup", GTK_UI_MANAGER_MENU)
+ MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "GetMail", "SysTrayiconPopup/GetMail", GTK_UI_MANAGER_MENUITEM)
+ MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "Separator1", "SysTrayiconPopup/---", GTK_UI_MANAGER_SEPARATOR)
+ MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "Email", "SysTrayiconPopup/Email", GTK_UI_MANAGER_MENUITEM)
+ MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "EmailAcc", "SysTrayiconPopup/EmailAcc", GTK_UI_MANAGER_MENU)
+ MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "Separator2", "SysTrayiconPopup/---", GTK_UI_MANAGER_SEPARATOR)
+ MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "OpenAB", "SysTrayiconPopup/OpenAB", GTK_UI_MANAGER_MENUITEM)
+ MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "Separator3", "SysTrayiconPopup/---", GTK_UI_MANAGER_SEPARATOR)
+ MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "ToggleOffline", "SysTrayiconPopup/ToggleOffline", GTK_UI_MANAGER_MENUITEM)
+#ifdef HAVE_LIBNOTIFY
+ MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "ShowBubbles", "SysTrayiconPopup/ShowBubbles", GTK_UI_MANAGER_MENUITEM)
+#endif
+ MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "Separator4", "SysTrayiconPopup/---", GTK_UI_MANAGER_SEPARATOR)
+ MENUITEM_ADDUI("/Menus/SysTrayiconPopup", "Exit", "SysTrayiconPopup/Exit", GTK_UI_MANAGER_MENUITEM)
+
+ traymenu_popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
+ gtk_ui_manager_get_widget(gtkut_ui_manager(), "/Menus/SysTrayiconPopup")));
+
+
+ return trayicon_nomail;
+}
+
+void notification_trayicon_on_activate(GtkStatusIcon *status_icon, gpointer user_data)
+{
+ notification_toggle_hide_show_window();
+}
+
+static void notification_trayicon_on_popup_menu(GtkStatusIcon *status_icon,
+ guint button, guint activate_time,
+ gpointer user_data)
+{
+ MainWindow *mainwin = mainwindow_get_mainwindow();
+
+ if(!mainwin)
+ return;
+
+ /* tell callbacks to skip any event */
+ updating_menu = TRUE;
+ /* initialize checkitems according to current states */
+ cm_toggle_menu_set_active("SysTrayiconPopup/ToggleOffline", prefs_common_get_prefs()->work_offline);
+#ifdef HAVE_LIBNOTIFY
+ cm_toggle_menu_set_active("SysTrayiconPopup/ShowBubbles", notify_config.trayicon_popup_enabled);
+#endif
+ cm_menu_set_sensitive("SysTrayiconPopup/GetMail", mainwin->lock_count == 0);
+
+ updating_menu = FALSE;
+
+#ifndef G_OS_WIN32
+ gtk_menu_popup(GTK_MENU(traymenu_popup), NULL, NULL, NULL, NULL,
+ button, activate_time);
+#else
+ /* http://bugzilla.gnome.org/show_bug.cgi?id=552642 */
+ gtk_menu_popup(GTK_MENU(traymenu_popup), NULL, NULL, NULL, NULL,
+ 0, activate_time);
+#endif
+}
+
+static gboolean notification_trayicon_on_size_changed(GtkStatusIcon *icon,
+ gint size,
+ gpointer user_data)
+{
+ notification_update_msg_counts(NULL);
+ return FALSE;
+}
+
+/* popup menu callbacks */
+static void trayicon_get_all_cb(GtkAction *action, gpointer data)
+{
+ MainWindow *mainwin = mainwindow_get_mainwindow();
+ inc_all_account_mail_cb(mainwin, 0, NULL);
+}
+
+static void trayicon_compose_cb(GtkAction *action, gpointer data)
+{
+ MainWindow *mainwin = mainwindow_get_mainwindow();
+ compose_mail_cb(mainwin, 0, NULL);
+}
+
+static void trayicon_compose_acc_cb(GtkMenuItem *menuitem, gpointer data)
+{
+ compose_new((PrefsAccount *)data, NULL, NULL);
+}
+
+static void trayicon_addressbook_cb(GtkAction *action, gpointer data)
+{
+#ifndef USE_NEW_ADDRBOOK
+ addressbook_open(NULL);
+#else
+ GError* error = NULL;
+
+ addressbook_dbus_open(FALSE, &error);
+ if (error) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ }
+#endif
+}
+
+static void trayicon_toggle_offline_cb(GtkAction *action, gpointer data)
+{
+ /* toggle offline mode if menu checkitem has been clicked */
+ if(!updating_menu) {
+ MainWindow *mainwin = mainwindow_get_mainwindow();
+ main_window_toggle_work_offline(mainwin, !prefs_common_get_prefs()->work_offline, TRUE);
+ }
+}
+
+#ifdef HAVE_LIBNOTIFY
+static void trayicon_toggle_notify_cb(GtkAction *action, gpointer data)
+{
+ if(!updating_menu) {
+ notify_config.trayicon_popup_enabled = !notify_config.trayicon_popup_enabled;
+ }
+}
+#endif
+
+static void app_exit_cb(MainWindow *mainwin, guint action, GtkWidget *widget)
+{
+ if(prefs_common_get_prefs()->confirm_on_exit) {
+ if(alertpanel(_("Exit"), _("Exit Claws Mail?"),
+ GTK_STOCK_CANCEL, GTK_STOCK_OK,
+ NULL) != G_ALERTALTERNATE) {
+ return;
+ }
+ manage_window_focus_in(mainwin->window, NULL, NULL);
+ }
+
+ if (prefs_common_get_prefs()->clean_on_exit) {
+ if (!main_window_empty_trash(mainwin, prefs_common_get_prefs()->ask_on_clean, TRUE))
+ return;
+ }
+
+ app_will_exit(NULL, mainwin);
+}
+
+static void trayicon_exit_cb(GtkAction *action, gpointer data)
+{
+ MainWindow *mainwin = mainwindow_get_mainwindow();
+
+ if(mainwin->lock_count == 0) {
+ app_exit_cb(mainwin, 0, NULL);
+ }
+}
+
+#ifdef HAVE_LIBNOTIFY
+static gboolean notification_trayicon_popup_add_msg(MsgInfo *msginfo,
+ NotificationFolderType nftype)
+{
+ gchar *summary;
+ gchar *utf8_str;
+ gboolean retval;
+ GdkPixbuf *pixbuf;
+
+ g_return_val_if_fail(msginfo, FALSE);
+
+ if(!popup.notification)
+ return notification_trayicon_popup_create(msginfo,nftype);
+
+ /* Count messages */
+ notification_trayicon_popup_count_msgs(nftype);
+
+ if(popup.msg_path) {
+ g_free(popup.msg_path);
+ popup.msg_path = NULL;
+ }
+
+ summary = notification_trayicon_popup_assemble_summary();
+ utf8_str = notification_trayicon_popup_assemble_body(msginfo);
+
+ /* make sure we show a logo on many msg arrival */
+ pixbuf = notification_pixbuf_get(NOTIFICATION_CM_LOGO_64x64);
+ if(pixbuf)
+ notify_notification_set_icon_from_pixbuf(popup.notification, pixbuf);
+
+ retval = notify_notification_update(popup.notification, summary,
+ utf8_str, NULL);
+ g_free(summary);
+ g_free(utf8_str);
+ if(!retval) {
+ debug_print("Notification Plugin: Failed to update notification.\n");
+ return FALSE;
+ }
+ /* Show the popup */
+ notify_notification_set_hint_string(popup.notification, "desktop-entry", "claws-mail");
+ if(!notify_notification_show(popup.notification, &(popup.error))) {
+ debug_print("Notification Plugin: Failed to send updated notification: "
+ "%s\n", popup.error->message);
+ g_clear_error(&(popup.error));
+ return FALSE;
+ }
+
+ debug_print("Notification Plugin: Popup successfully modified "
+ "with libnotify.\n");
+
+ return TRUE;
+}
+
+static gboolean notification_trayicon_popup_create(MsgInfo *msginfo,
+ NotificationFolderType nftype)
+{
+ gchar *summary = NULL;
+ gchar *utf8_str = NULL;
+ GdkPixbuf *pixbuf;
+ GList *caps = NULL;
+ gboolean support_actions = FALSE;
+
+ /* init libnotify if necessary */
+ if(!notify_is_initted()) {
+ if(!notify_init("claws-mail")) {
+ debug_print("Notification Plugin: Failed to initialize libnotify. "
+ "No popups will be shown.\n");
+ return FALSE;
+ }
+ }
+
+ /* Count messages */
+ notification_trayicon_popup_count_msgs(nftype);
+
+ summary = notification_trayicon_popup_assemble_summary();
+ utf8_str = notification_trayicon_popup_assemble_body(msginfo);
+
+#if NOTIFY_CHECK_VERSION(0, 7, 0)
+ popup.notification = notify_notification_new(summary, utf8_str, NULL);
+#else
+ popup.notification = notify_notification_new(summary, utf8_str, NULL, NULL);
+ notify_notification_attach_to_status_icon(popup.notification, trayicon);
+#endif
+
+ g_free(summary);
+ g_free(utf8_str);
+
+ caps = notify_get_server_caps();
+ if(caps != NULL) {
+ GList *c;
+ for(c = caps; c != NULL; c = c->next) {
+ if(strcmp((char*)c->data, "actions") == 0 ) {
+ support_actions = TRUE;
+ break;
+ }
+ }
+
+ g_list_foreach(caps, (GFunc)g_free, NULL);
+ g_list_free(caps);
+ }
+
+ /* Default action */
+ if (support_actions)
+ notify_notification_add_action(popup.notification,
+ "default", "Present main window",
+ (NotifyActionCallback)
+ notification_trayicon_popup_default_action_cb,
+ GINT_TO_POINTER(nftype),
+ notification_trayicon_popup_free_func);
+
+ if(popup.notification == NULL) {
+ debug_print("Notification Plugin: Failed to create a new notification.\n");
+ return FALSE;
+ }
+
+ /* Icon */
+ pixbuf = NULL;
+#ifndef USE_NEW_ADDRBOOK
+ if(msginfo && msginfo->from) {
+ gchar *icon_path;
+ icon_path = addrindex_get_picture_file(msginfo->from);
+ if(is_file_exist(icon_path)) {
+ GError *error = NULL;
+ gint w, h;
+
+ gdk_pixbuf_get_file_info(icon_path, &w, &h);
+ if((w > 64) || (h > 64))
+ pixbuf = gdk_pixbuf_new_from_file_at_scale(icon_path,
+ 64, 64, TRUE, &error);
+ else
+ pixbuf = gdk_pixbuf_new_from_file(icon_path, &error);
+
+ if(!pixbuf) {
+ debug_print("Could not load picture file: %s\n",
+ error ? error->message : "no details");
+ g_error_free(error);
+ }
+ }
+ else
+ debug_print("Picture path does not exist: %s\n",icon_path);
+ g_free(icon_path);
+ }
+#endif
+ if(!pixbuf)
+ pixbuf = g_object_ref(notification_pixbuf_get(NOTIFICATION_CM_LOGO_64x64));
+
+ if(pixbuf) {
+ notify_notification_set_icon_from_pixbuf(popup.notification, pixbuf);
+ g_object_unref(pixbuf);
+ }
+ else /* This is not fatal */
+ debug_print("Notification plugin: Icon could not be loaded.\n");
+
+ /* timeout */
+ notify_notification_set_timeout(popup.notification, notify_config.trayicon_popup_timeout);
+
+ /* Category */
+ notify_notification_set_category(popup.notification, "email.arrived");
+
+ /* get notified on bubble close */
+ g_signal_connect(G_OBJECT(popup.notification), "closed", G_CALLBACK(popup_timeout_fun), NULL);
+
+ /* Show the popup */
+ notify_notification_set_hint_string(popup.notification, "desktop-entry", "claws-mail");
+ if(!notify_notification_show(popup.notification, &(popup.error))) {
+ debug_print("Notification Plugin: Failed to send notification: %s\n",
+ popup.error->message);
+ g_clear_error(&(popup.error));
+ g_object_unref(G_OBJECT(popup.notification));
+ popup.notification = NULL;
+ return FALSE;
+ }
+
+ /* Store path to message */
+ if(nftype == F_TYPE_MAIL) {
+ if(msginfo->folder && msginfo->folder) {
+ gchar *ident;
+ ident = folder_item_get_identifier(msginfo->folder);
+ popup.msg_path = g_strdup_printf("%s%s%u", ident,G_DIR_SEPARATOR_S,
+ msginfo->msgnum);
+ g_free(ident);
+ }
+ else
+ popup.msg_path = NULL;
+ }
+
+ debug_print("Notification Plugin: Popup created with libnotify.\n");
+
+ return TRUE;
+}
+
+static void popup_timeout_fun(NotifyNotification *nn, gpointer data)
+{
+ G_LOCK(trayicon_popup);
+
+ g_object_unref(G_OBJECT(popup.notification));
+
+ popup.notification = NULL;
+ g_clear_error(&(popup.error));
+
+ popup.count = 0;
+ popup.num_mail = 0;
+ popup.num_news = 0;
+ popup.num_calendar = 0;
+ popup.num_rss = 0;
+
+ if(popup.msg_path) {
+ g_free(popup.msg_path);
+ popup.msg_path = NULL;
+ }
+
+ G_UNLOCK(trayicon_popup);
+}
+
+static void notification_trayicon_popup_free_func(gpointer data)
+{
+ if(popup.msg_path) {
+ g_free(popup.msg_path);
+ popup.msg_path = NULL;
+ }
+
+ debug_print("Freed notification data\n");
+}
+
+static void notification_trayicon_popup_default_action_cb(NotifyNotification
+ *notification,
+ const char *action,
+ void *user_data)
+{
+ if(strcmp("default", action))
+ return;
+
+ MainWindow *mainwin;
+ mainwin = mainwindow_get_mainwindow();
+ if(mainwin) {
+ NotificationFolderType nftype;
+
+ /* Let mainwindow pop up */
+ notification_show_mainwindow(mainwin);
+ /* If there is only one new mail message, jump to this message */
+ nftype = (NotificationFolderType)GPOINTER_TO_INT(user_data);
+ if((popup.count == 1) && (nftype == F_TYPE_MAIL)) {
+ gchar *select_str;
+ G_LOCK(trayicon_popup);
+ select_str = g_strdup(popup.msg_path);
+ G_UNLOCK(trayicon_popup);
+ debug_print("Notification plugin: Select message %s\n", select_str);
+ mainwindow_jump_to(select_str, FALSE);
+ g_free(select_str);
+ }
+ }
+}
+
+static void notification_trayicon_popup_count_msgs(NotificationFolderType nftype)
+{
+ switch(nftype) {
+ case F_TYPE_MAIL:
+ popup.num_mail++;
+ break;
+ case F_TYPE_NEWS:
+ popup.num_news++;
+ break;
+ case F_TYPE_CALENDAR:
+ popup.num_calendar++;
+ break;
+ case F_TYPE_RSS:
+ popup.num_rss++;
+ break;
+ default:
+ debug_print("Notification plugin: Unknown folder type\n");
+ return;
+ }
+ popup.count++;
+}
+
+/* The returned value has to be freed by the caller */
+static gchar* notification_trayicon_popup_assemble_summary(void)
+{
+ gchar *summary = NULL;
+
+ if(popup.count == 1) {
+ if(popup.num_mail)
+ summary = g_strdup(_("New mail message"));
+ else if(popup.num_news)
+ summary = g_strdup(_("New news post"));
+ else if(popup.num_calendar)
+ summary = g_strdup(_("New calendar message"));
+ else
+ summary = g_strdup(_("New article in RSS feed"));
+ } /* One new message */
+ else {
+ summary = g_strdup(_("New messages arrived"));
+ } /* Many new messages */
+
+ return summary;
+}
+
+/* The returned value has to be freed by the caller */
+static gchar* notification_trayicon_popup_assemble_body(MsgInfo *msginfo)
+{
+ gchar *utf8_str;
+
+ if(popup.count == 1) {
+ if(popup.num_mail || popup.num_news) {
+ gchar *from;
+ gchar *subj;
+ gchar *text;
+ gchar *foldname = NULL;
+
+ from = notification_libnotify_sanitize_str(msginfo->from ?
+ msginfo->from :
+ _("(No From)"));
+ subj = notification_libnotify_sanitize_str(msginfo->subject ?
+ msginfo->subject :
+ _("(No Subject)"));
+ if (notify_config.trayicon_display_folder_name) {
+ foldname = notification_libnotify_sanitize_str(msginfo->folder->path);
+ text = g_strconcat(from,"\n\n", subj, "\n\n", foldname, NULL);
+ }
+ else
+ text = g_strconcat(from, "\n\n",subj, NULL);
+
+
+ /* Make sure text is valid UTF8 */
+ utf8_str = notification_validate_utf8_str(text);
+ g_free(text);
+
+ if(from) g_free(from);
+ if(subj) g_free(subj);
+ if(foldname) g_free(foldname);
+ }
+ else if(popup.num_calendar) {
+ utf8_str = g_strdup(_("A new calendar message arrived"));
+ }
+ else {
+ utf8_str = g_strdup(_("A new article in a RSS feed arrived"));
+ }
+ } /* One message */
+
+ else {
+ gchar *msg;
+ gchar *tmp;
+ gboolean str_empty = TRUE;
+
+ utf8_str = g_strdup("");
+
+ if(popup.num_mail) {
+ msg = g_strdup_printf(ngettext("%d new mail message arrived",
+ "%d new mail messages arrived",
+ popup.num_mail),
+ popup.num_mail);
+ tmp = g_strdup_printf("%s%s%s",utf8_str,str_empty?"":"\n",msg);
+ g_free(msg);
+ g_free(utf8_str);
+ utf8_str = tmp;
+ str_empty = FALSE;
+ }
+ if(popup.num_news) {
+ msg = g_strdup_printf(ngettext("%d new news post arrived",
+ "%d new news posts arrived",
+ popup.num_news),
+ popup.num_news);
+ tmp = g_strdup_printf("%s%s%s",utf8_str,str_empty?"":"\n",msg);
+ g_free(msg);
+ g_free(utf8_str);
+ utf8_str = tmp;
+ str_empty = FALSE;
+ }
+ if(popup.num_calendar) {
+ msg = g_strdup_printf(ngettext("%d new calendar message arrived",
+ "%d new calendar messages arrived",
+ popup.num_calendar),
+ popup.num_calendar);
+ tmp = g_strdup_printf("%s%s%s",utf8_str,str_empty?"":"\n",msg);
+ g_free(msg);
+ g_free(utf8_str);
+ utf8_str = tmp;
+ str_empty = FALSE;
+ }
+ if(popup.num_rss) {
+ msg = g_strdup_printf(ngettext("%d new article in RSS feeds arrived",
+ "%d new articles in RSS feeds arrived",
+ popup.num_rss),
+ popup.num_rss);
+ tmp = g_strdup_printf("%s%s%s",utf8_str,str_empty?"":"\n",msg);
+ g_free(msg);
+ g_free(utf8_str);
+ utf8_str = tmp;
+ str_empty = FALSE;
+ }
+ } /* Many messages */
+
+ return utf8_str;
+}
+
+#endif /* HAVE_LIBNOTIFY */
+
+gboolean notification_trayicon_is_available(void)
+{
+ gboolean is_available;
+ is_available = FALSE;
+
+ if(trayicon) {
+ if(gtk_status_icon_is_embedded(trayicon) &&
+ gtk_status_icon_get_visible(trayicon))
+ is_available = TRUE;
+ }
+
+ return is_available;
+}
+
+#endif /* NOTIFICATION_TRAYICON */
diff --git a/src/plugins/notification/notification_trayicon.h b/src/plugins/notification/notification_trayicon.h
@@ -0,0 +1,47 @@
+/* Notification plugin for Claws-Mail
+ * Copyright (C) 2005-2007 Holger Berndt and the Claws Mail Team.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NOTIFICATION_TRAYICON_H
+#define NOTIFICATION_TRAYICON_H NOTIFICATION_TRAYICON_H
+
+#include "pluginconfig.h"
+
+#ifdef NOTIFICATION_TRAYICON
+
+#include <glib.h>
+
+#include "procmsg.h"
+
+#define TRAYICON_SPECIFIC_FOLDER_ID_STR "trayicon"
+
+void notification_trayicon_msg(MsgInfo*);
+
+void notification_trayicon_destroy(void);
+
+/* creates it, if necessary */
+void notification_update_trayicon(void);
+
+gboolean notification_trayicon_main_window_close(gpointer, gpointer);
+gboolean notification_trayicon_main_window_got_iconified(gpointer, gpointer);
+gboolean notification_trayicon_account_list_changed(gpointer, gpointer);
+
+gboolean notification_trayicon_is_available(void);
+void notification_trayicon_on_activate(GtkStatusIcon*,gpointer);
+
+#endif /* NOTIFICATION_TRAYICON */
+
+#endif /* include guard */
diff --git a/src/plugins/notification/placeholder.txt b/src/plugins/notification/placeholder.txt
@@ -1 +0,0 @@
-Placeholder
diff --git a/src/plugins/notification/plugin.def b/src/plugins/notification/plugin.def
@@ -0,0 +1,10 @@
+EXPORTS
+ plugin_desc
+ plugin_done
+ plugin_init
+ plugin_licence
+ plugin_name
+ plugin_type
+ plugin_provides
+ plugin_version
+
diff --git a/src/plugins/notification/raw_claws_mail_logo_64x64.h b/src/plugins/notification/raw_claws_mail_logo_64x64.h
@@ -0,0 +1,684 @@
+/* GdkPixbuf RGBA C-Source image dump */
+
+#ifdef __SUNPRO_C
+#pragma align 4 (raw_claws_mail_logo_64x64)
+#endif
+#ifdef __GNUC__
+static const guint8 raw_claws_mail_logo_64x64[] __attribute__ ((__aligned__ (4))) =
+#else
+static const guint8 raw_claws_mail_logo_64x64[] =
+#endif
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (16384) */
+ "\0\0@\30"
+ /* pixdata_type (0x1010002) */
+ "\1\1\0\2"
+ /* rowstride (256) */
+ "\0\0\1\0"
+ /* width (64) */
+ "\0\0\0@"
+ /* height (64) */
+ "\0\0\0@"
+ /* pixel_data: */
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\1\0\0\0\1\0\0\0\3\0\0\0\5\0\0\0\7\0\0\0\10\0\0\0\10\0\0\0\7\0"
+ "\0\0\6\0\0\0\4\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0"
+ "\0\0\1\0\0\0\3\0\0\0\6\0\0\0\15\0\0\0\27\0\0\0(\0\0\0=\15\30\35W\"@M"
+ "w-Sc\2073]n\2226`q\2250Ve\214)IW}\26&-_\4\6\7B\0\0\0+\0\0\0\31\0\0\0"
+ "\16\0\0\0\6\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\1\0\0\0\2\0\0\0\4\0\0\0\14\0\0\0\31\0\0\0""0\24&.c8g{\244T\215\245"
+ "\321s\257\310\362\215\304\333\375\243\321\344\377\261\327\350\377\271"
+ "\333\352\377\273\335\352\377\265\331\350\377\252\323\346\377\227\311"
+ "\337\376\200\267\316\366b\230\260\335Gw\213\263&@Kv\4\4\4""7\0\0\0\31"
+ "\0\0\0\12\0\0\0\4\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\4\0\0\0\14\0\0"
+ "\0\40\0\5\13P\25""7P\242Y\225\255\333\211\302\333\375\265\330\350\377"
+ "\321\343\355\377\343\354\362\377\346\354\361\377\353\362\365\377\360"
+ "\367\372\377\360\367\372\377\360\367\372\377\360\367\372\377\360\367"
+ "\372\377\360\366\372\377\353\364\370\377\336\356\364\377\304\340\355"
+ "\377\235\315\340\376o\246\275\351Fr\204\254\20\30\34O\0\0\0\37\0\0\0"
+ "\12\0\0\0\3\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\1\0\0\0\3\0\0\0\10\0\0\0\26\0\2\4C\22""4N\250:g\212\361\236"
+ "\304\327\377\311\334\346\377\331\341\350\377\336\344\352\377\336\344"
+ "\352\377\336\344\352\377\337\346\353\377\346\356\363\377\345\354\361"
+ "\377\344\353\357\377\344\353\357\377\346\357\363\377\352\363\370\377"
+ "\353\364\370\377\351\363\367\377\346\355\363\377\352\363\366\377\352"
+ "\364\370\377\334\354\364\377\265\332\350\377~\264\314\365Jv\212\263\14"
+ "\21\24H\0\0\0\30\0\0\0\7\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\1\0\0\0\4\0\0\0\15\0\0\0-\22)6zCz\227\337\220\271\320\377\316"
+ "\340\352\377\325\336\346\377\324\334\344\377\324\334\344\377\324\334"
+ "\344\377\324\334\344\377\325\334\344\377\325\335\344\377\326\335\345"
+ "\377\326\335\345\377\326\336\345\377\327\336\346\377\327\336\346\377"
+ "\330\340\346\377\331\342\350\377\330\337\346\377\330\337\346\377\330"
+ "\340\347\377\335\345\354\377\340\352\360\377\344\357\365\377\334\355"
+ "\364\377\260\326\346\377r\251\300\3555Tb\214\0\0\0*\0\0\0\13\0\0\0\3"
+ "\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\5\0\0\0\21\0\0\0""3\40G_\257i\234"
+ "\272\376\242\270\313\377\306\321\334\377\312\324\336\377\312\324\336"
+ "\377\312\324\336\377\312\324\336\377\312\324\336\377\313\325\336\377"
+ "\314\325\337\377\314\326\337\377\315\326\337\377\315\327\340\377\316"
+ "\327\340\377\316\327\340\377\317\330\341\377\317\330\341\377\317\330"
+ "\341\377\320\331\341\377\320\331\342\377\320\331\342\377\320\331\342"
+ "\377\320\331\342\377\320\331\342\377\326\342\351\377\334\352\361\377"
+ "\313\342\354\377\220\303\330\375Q\200\223\276\6\11\12@\0\0\0\22\0\0\0"
+ "\5\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\2\0\0\0\6\0\0\0\27\0\5\12N\13/O\303Sz\234\376\262\301\320"
+ "\377\277\314\330\377\277\314\330\377\277\314\330\377\277\314\330\377"
+ "\277\314\330\377\277\314\330\377\300\315\330\377\301\315\331\377\302"
+ "\316\331\377\303\317\332\377\303\317\332\377\276\312\324\377\266\302"
+ "\314\377\302\316\327\377\200\207\216\377\277\313\325\377\307\321\334"
+ "\377\307\321\335\377\307\322\335\377\310\322\335\377\306\320\333\377"
+ "\310\322\335\377\310\322\335\377\310\322\335\377\310\322\334\377\310"
+ "\322\334\377\300\315\331\377\227\273\320\377T\204\235\334\25\37$`\0\0"
+ "\0\36\0\0\0\6\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\2\0\0\0\6\0\0\0\27\0\7\16W\17""8`\344j\210\244\377\255\275\315"
+ "\377\265\304\322\377\265\304\322\377\265\304\322\377\265\304\322\377"
+ "\265\304\322\377\265\304\322\377\266\305\322\377\267\305\323\377\270"
+ "\306\324\377\271\307\324\377\270\306\323\377\226\240\253\377\201\213"
+ "\224\377py\200\377\267\304\320\377\40\"#\377\216\225\233\377\304\317"
+ "\331\377\275\310\325\377\275\310\324\377\237\251\264\377\203\213\224"
+ "\377\257\272\306\377\300\314\330\377\300\314\327\377\300\314\327\377"
+ "\300\314\330\377\300\313\327\377\274\311\325\377\207\236\263\377T\200"
+ "\236\372\32-=\206\0\0\0\33\0\0\0\6\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\2\0\0\0\6\0\0\0\26\0\3\10O\13""2Z\334n\213\247\377\251\272"
+ "\313\377\252\274\314\377\252\274\314\377\252\274\314\377\252\274\314"
+ "\377\252\273\314\377\252\273\314\377\253\275\314\377\254\275\315\377"
+ "\255\276\316\377\256\277\316\377\260\300\317\377\252\271\307\377^fn\377"
+ "(,/\377ox\202\377\240\256\272\377!$&\377\26\27\30\377\216\223\230\377"
+ "\235\247\257\377dkq\377\32\34\36\377r{\203\377lt{\377\237\253\266\377"
+ "\271\307\324\377\270\305\323\377\272\310\326\377\270\305\322\377\267"
+ "\305\322\377\267\304\322\377\253\304\325\377[\212\246\362\14\26\36d\0"
+ "\0\0\26\0\0\0\4\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0\24\0\4\10"
+ "K\12""1X\333_~\236\377\236\260\304\377\240\263\306\377\240\263\306\377"
+ "\240\263\306\377\240\263\306\377\240\263\306\377\240\263\306\377\240"
+ "\263\306\377\242\264\307\377\243\265\310\377\244\266\310\377\243\265"
+ "\305\377\233\253\273\377\240\260\301\377x\204\220\377\24\26\30\377|\210"
+ "\225\377\203\215\231\377\34\36\40\377\0\0\0\377\36\37\37\377ORU\377\3"
+ "\3\3\377\16\17\21\377\26\30\31\377_in\377\265\304\323\377\277\323\337"
+ "\377\267\310\326\377\304\331\344\377\261\277\316\377\262\301\317\377"
+ "\300\325\341\377\311\343\355\377\260\325\344\377Q~\232\351\6\11\14J\0"
+ "\0\0\17\0\0\0\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\4\0\0\0\15\0\0\0""1\4$D\273Tv\227"
+ "\377\223\250\276\377\226\253\300\377\226\253\300\377\226\253\300\377"
+ "\226\253\300\377\226\253\300\377\226\253\300\377\226\253\300\377\227"
+ "\254\300\377\231\255\301\377\232\256\302\377\234\257\303\377\233\257"
+ "\302\377\212\232\253\377q\177\214\3777>D\377\37\"%\377`ir\377mu}\377"
+ "\27\31\33\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\21\23"
+ "\24\377\207\231\241\377\276\327\342\377\311\344\357\377\311\344\357\377"
+ "\260\303\321\377\252\272\312\377\260\302\321\377\310\342\355\377\306"
+ "\341\355\377\305\337\353\377\241\314\340\377>f~\311\5\5\5""3\0\0\0\12"
+ "\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\2\0\0\0\11\0\0\0\36\0\17\37z&Qz\374\204\234\265\377\214"
+ "\242\271\377\214\242\271\377\214\242\271\377\214\242\271\377\214\242"
+ "\272\377\214\242\272\377\214\242\272\377\214\242\272\377\216\244\273"
+ "\377\220\245\274\377\221\246\275\377\223\250\276\377\210\235\260\377"
+ "9BJ\377\2\2\2\377\0\0\0\377\20\22\24\377\40#&\377#&)\377\6\7\5\377)9"
+ "\6\377*;\6\377\25\35\3\377\6\11\0\377\0\0\0\377(.1\377s\203\213\377\211"
+ "\234\245\377\215\241\251\377\304\341\355\377\263\312\331\377\244\265"
+ "\307\377\246\271\312\377\302\340\354\377\300\337\353\377\300\336\353"
+ "\377\275\334\352\377\205\267\320\376\35""3D\220\0\0\0\34\0\0\0\5\0\0"
+ "\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0"
+ "\0\5\0\0\0\22\0\0\1C\12""2Z\336c\202\241\377\201\232\263\377\202\232"
+ "\263\377\202\232\263\377\202\232\263\377\202\232\264\377\202\232\264"
+ "\377\202\232\264\377\202\232\264\377\202\233\265\377\203\234\265\377"
+ "}\230\261\377o\215\251\377[|\235\377Gj\216\3775Y}\377\34""3J\377\2\4"
+ "\6\377\0\0\0\377\0\0\0\377\1\2\0\377d\207\21\377\202\262\26\377o\226"
+ "\23\377`\202\21\377F`\12\377\24\35\2\377\0\0\0\377\12\14\15\377.46\377"
+ "z\214\224\377\255\311\325\377\267\324\340\377\236\261\303\377\256\307"
+ "\327\377\276\336\353\377\274\334\351\377\274\334\351\377\273\334\351"
+ "\377\256\324\344\377V\206\242\353\7\10\11E\0\0\0\16\0\0\0\3\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\12\0\0\0#\0"
+ "\27-\2224]\205\377t\220\254\377w\222\255\377w\222\255\377w\222\255\377"
+ "w\222\255\377w\222\255\377w\222\256\377v\222\256\377n\213\250\377Vx\233"
+ "\3777`\210\377\37Mz\377\25Ft\377\23Dr\377\27Ft\377\32Ep\377!Kw\377\37"
+ "7O\377\6\12\16\377\0\0\0\377;P\16\377\207\267\34\377q\233\24\377k\222"
+ "\23\377m\225\24\377k\223\23\377Hc\12\377\23\33\1\377\10\13\6\377h~\207"
+ "\377\246\310\327\377\243\304\322\377\270\330\345\377\271\331\347\377"
+ "\273\334\352\377\272\334\351\377\271\333\351\377\270\331\351\377\266"
+ "\331\347\377\264\327\346\377\221\305\334\377>bp\236\0\0\0\37\0\0\0\6"
+ "\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0\21"
+ "\0\0\0<\10""0W\333Xz\232\377l\212\246\377l\212\247\377l\212\247\377l"
+ "\212\247\377l\212\247\377k\211\247\377[}\236\3775]\204\377\22:_\377\30"
+ ";L\377/PA\3776P=\377OhC\377RfA\377o\210C\377f{C\377Xk;\377Uh0\377GQ3"
+ "\377La\36\377o\2114\377t\2210\377l\215\"\377h\206&\377h\211\34\377c\204"
+ "\31\377Eb\11\3771F\6\377\27!\2\377\25\32\34\377L]d\377@S\\\377\222\302"
+ "\327\377\237\317\343\377\257\326\347\377\265\331\350\377\264\331\350"
+ "\377\263\327\347\377\261\327\346\377\260\325\345\377\246\320\342\377"
+ "k\243\271\350\4\5\5B\0\0\0\15\0\0\0\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\2\0\0\0\11\0\0\0\34\0\15\32t\40Lx\376`\200\240\377b\202\241"
+ "\377b\202\241\377b\202\241\377a\201\240\377Qt\230\377'R{\377\35>F\377"
+ "6I/\377y\222C\377\200\231G\377\201\234A\377\200\2378\377}\233;\377{\232"
+ "7\377o\2106\377f}3\377e\201%\377\\t&\377e\200+\377s\224'\377y\2320\377"
+ "h\210\40\377b\177\33\377c\204\32\377h\210#\377f\210\33\377M_)\377FZ\33"
+ "\377$/\24\377k\212\230\3775IR\377#29\377\177\264\313\377\207\303\334"
+ "\377\212\303\333\377\230\312\337\377\251\323\344\377\256\325\346\377"
+ "\255\324\345\377\254\323\343\377\251\321\343\377\211\301\331\3778Xe\220"
+ "\0\0\0\32\0\0\0\5\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\15"
+ "\0\0\0+\2#C\270:b\210\377Xz\232\377Xz\232\377Wy\232\377Nr\226\377*V\200"
+ "\377\7""6c\377\20\34(\377q\204G\377\224\271=\377\205\2516\377y\241\""
+ "\377y\235%\377t\223/\377k\211'\377O_(\377Ma\40\377IY(\377IZ#\377e}-\377"
+ "f\204\"\377k\216\34\377k\213%\377_~\31\377Vn\36\377b\203\32\377l\222"
+ "\26\377y\234*\377[o.\377d~*\377c~*\377XmT\377t\244\270\377\211\300\330"
+ "\377\213\304\335\377\210\303\334\377\206\302\334\377\204\300\331\377"
+ "\206\301\332\377\225\310\336\377\245\320\342\377\246\321\342\377\245"
+ "\317\341\377\230\311\335\377[\216\244\324\0\0\0""0\0\0\0\12\0\0\0\2\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\6\0\0\0\23\0\0\0\77\15""8_\342Dj\216"
+ "\377Nr\224\377Lp\224\3775^\206\377\16\77p\377\0""1`\377QRJ\377\306\264"
+ "\223\3771*%\377Sh#\377j\217\27\377e\206\31\377f\210\30\377Pe\40\377F"
+ "U&\377>L#\377N[/\377g~3\377\214\257<\377f\201'\377i\213\32\377g\203("
+ "\377a|$\377Ob&\377EX\34\377[w\32\377l\221\26\377\205\2520\377o\205>\377"
+ "e\200,\377z\236'\377|\2326\377Nb1\377i\222\250\377\215\306\337\377\212"
+ "\304\335\377\210\303\334\377\204\300\332\377\202\277\331\377\177\276"
+ "\327\377\205\300\330\377\226\310\335\377\237\314\337\377\234\313\336"
+ "\377w\263\315\372\22\30\33X\0\0\0\20\0\0\0\3\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\11\0\0\0\34\1\10\17d\24Dq\374Ag\214\377>d\212\377\36Kx\377"
+ "\3""6i\377\0""4g\377)9A\377\362\352\326\377\325\275\214\377pbH\377\77"
+ "H/\377Vi*\377Na#\377CP%\377\40'\21\377!,\40\377\27#\35\377`u1\377\227"
+ "\265N\377}\230@\377o\2121\377e\201'\377f\205\37\377K^!\377>L\34\377C"
+ "T\36\377[|\22\377k\213&\377\210\250<\377u\2179\377e\203\"\377e\201&\377"
+ "n\215,\377v\221:\377\\p>\377w\247\271\377\214\305\335\377\210\303\334"
+ "\377\206\301\333\377\202\277\331\377\177\276\327\377}\274\326\377|\273"
+ "\325\377\210\300\330\377\225\310\334\377\203\276\326\3777Vc\221\0\0\0"
+ "\32\0\0\0\5\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\14\0\0\0%\0\34""6\237\40"
+ "Ly\377/Y\202\377\16\77o\377\0""4g\377\0""4h\377\0""1a\377\220\205l\377"
+ "\353\341\310\377\316\265\200\377H@1\377+3\36\377=K\36\3778D\33\377)4"
+ "#\377\12(E\377\25*4\377i\200:\377\224\270@\377{\227;\377`s2\377Ym.\377"
+ "Pd\40\377I[!\377\77G,\377,5\30\377BT\36\377Xr\36\377s\223*\377\216\256"
+ "D\377at6\377Qd'\377Zv\32\377Ti$\377x\220B\377\242\303S\377cxC\377p\235"
+ "\261\377\210\303\334\377\206\301\333\377\202\277\331\377\200\276\330"
+ "\377}\275\326\377z\272\325\377w\270\323\377~\272\324\377\202\276\326"
+ "\377O\177\222\300\0\0\0&\0\0\0\10\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\20"
+ "\0\0\0/\1$H\302\30Ft\377\7""9j\377\0""4g\377\0""4g\377\0""5h\377\0/\\"
+ "\377\277\260\220\377\335\312\243\377d\\G\377\0\31""2\377\26\35\34\377"
+ "#+\22\377*0\35\377\14!.\377#6/\377\203\242>\377}\230@\377w\2253\377p"
+ "\220(\377`y#\377Pi\35\377FV#\377*;\32\377\0\1\0\377\32\40\37\377<L\32"
+ "\377Tr\22\377s\2204\377\200\231E\377Yo'\377%,\32\377IY&\377Un\37\377"
+ "Zs\"\377\220\263\77\377\216\256D\377YqI\377v\252\300\377\205\301\333"
+ "\377\202\277\331\377\177\276\330\377}\274\326\377z\272\325\377w\270\323"
+ "\377t\266\322\377s\266\322\377[\223\253\337\0\0\0""2\0\0\0\13\0\0\0\2"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\23\0\0\0""7\0(P\327\2""5f\377\0""4f\377\0"
+ "4g\377\0""4g\377\0""5h\377\0""1a\377\220\204d\377\326\300\220\377\22"
+ "%4\377\0&L\377\0#G\377\7\"=\377\6\33""1\377,;&\377x\222<\377~\2348\377"
+ "l\212,\377Ys\34\377]q1\3773@\27\3775>\40\377)3\34\377\16\26\34\377\31"
+ "!%\377F]Y\377J]\37\377Wk*\377|\2351\377\211\245G\377r\2138\377\".3\377"
+ ".67\377:F#\377Nk\15\377`{#\377x\231-\377\241\310J\377Vh;\377m\236\256"
+ "\377\201\277\331\377\177\276\330\377}\274\326\377z\272\325\377v\270\322"
+ "\377t\266\322\377p\264\321\377d\242\274\361\3\3\3C\0\0\0\16\0\0\0\3\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\26\0\0\0>\0-V\343\0""4f\377\0""4f\377\0""4g"
+ "\377\0""4h\377\0""5h\377\0""3e\3776==\377\330\276\211\377\37-7\377\0"
+ "&L\377\0#F\377\25,4\377Xj6\377}\2363\377f\205!\377_|\34\377]x!\377Rf"
+ "$\37719!\377\26/D\377\31""8U\377\30""1J\377$D`\377U\200\221\377\77S8"
+ "\377I[!\377Ti'\377f\207\36\377\234\301H\377\232\271M\377Ool\377d\213"
+ "\235\3773E:\377=H%\377Ql\24\377h\205%\377s\221.\377\206\2515\377Pd5\377"
+ "q\246\275\377~\275\330\377{\274\326\377z\271\325\377v\270\322\377t\266"
+ "\322\377p\264\321\377j\255\311\373\11\14\15R\0\0\0\21\0\0\0\4\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\30\0\0\0C\22Go\353\2""7i\377\0""4f\377\0""4g\377\0"
+ "4h\377\0""5h\377\0""1`\377\0!B\377e^J\377\211z\\\377\3\33.\3776D*\377"
+ "x\222>\377t\2158\377o\221\40\377g\206$\377a{(\377J_\37\377IT0\377\32"
+ "-3\377\31:Z\377\33;[\377\36=\\\377&Ge\377Z\214\240\377AU\77\377L`!\377"
+ "^z\35\377g\210\35\377\214\253C\377{\222C\377Vxy\377e\217\240\377Y\177"
+ "\216\377&,$\3773B\17\377Wo#\377_x#\377k\206*\377\227\274@\377UiB\377"
+ "f\230\251\377{\273\325\377x\271\325\377u\267\322\377r\265\321\377p\264"
+ "\321\377k\257\314\375\25\35\"a\0\0\0\23\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\31\0\0\0D\34S{\356\30S\202\377\0""4f\377\0""4g\377\0""4g\377\0""5"
+ "h\377\0/]\377\0&K\377\0\"C\377CM,\377gy:\377\214\254A\377y\233-\377c"
+ "\200\"\377_u-\377Zn*\377K^\36\377'2\35\377\22'/\377\23""5U\377\27""8"
+ "X\377\32:Z\377\35=\\\377Dt\223\377u\265\316\377=UE\377Re#\377`x'\377"
+ "e\205\36\377\202\241;\377\211\246D\377Ruz\377c\216\240\377c\216\240\377"
+ "Lkx\377484\377La\37\377_y'\377g\207#\377}\2338\377u\215<\377La<\377v"
+ "\265\316\377w\270\324\377t\267\322\377r\265\321\377n\263\320\377l\261"
+ "\316\376\31'-g\0\0\0\24\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\31\0\0\0E\6"
+ "6c\356P\237\304\377)k\226\377\2""6i\377\0""4g\377\0""5h\377\0""1`\377"
+ "\0$E\3770G)\377w\2253\377\200\237=\377f\203%\377Yq\"\377Nb#\377M`!\377"
+ "Yp(\377.:!\377\15+I\377\21""3V\377\24""6W\377\27""8X\377\32<\\\377Eu"
+ "\223\377z\301\337\377w\272\325\377:M#\377H\\\36\377e\205\33\377p\224"
+ "\40\377\233\302C\377]l8\377a\215\232\377f\225\250\377_\213\235\377`\213"
+ "\236\377A_f\377FT*\377Tj$\377b\177$\377p\223\"\377}\2372\377z\223A\377"
+ "_\221\242\377v\267\324\377s\266\321\377p\264\321\377m\263\320\377i\260"
+ "\315\376\26#'d\0\0\0\24\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\31\0\0\0B\1"
+ ".Z\350\24O~\377a\265\330\377D\217\270\377\2""7j\377\0""4h\377\0""4f\377"
+ "\15';\377Xi2\377;E$\3771<\35\377Mb\35\377J_\34\377ES'\377FT(\377CU\33"
+ "\377.=%\377\14""0S\377\17""2T\377\31@a\377D~\244\377h\254\316\377x\301"
+ "\340\377z\303\340\377Eie\377M^)\377Vk(\377c\201\40\377j\217\32\377u\216"
+ ";\377z\222\77\377^tx\377\217\236\245\377lop\377[u\200\377>RV\377ET#\377"
+ "Na#\377Vp\33\377g\206\"\377r\221.\377m\202=\377V\177\210\377t\267\323"
+ "\377r\266\321\377n\263\320\377l\261\320\377h\254\311\374\14\23\25Z\0"
+ "\0\0\24\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\30\0\0\0:\7""0X\330*l\230\377"
+ "a\265\330\377a\265\331\3771v\240\377\0""4h\377\0""5h\377\2$G\377J@*\377"
+ "p\\8\377TE)\377\40\34\37\377@H0\3779F\36\3774A\27\377=J\37\377\33,(\377"
+ "\34Ff\377<o\211\377e\254\313\377t\300\340\377u\300\340\377v\301\337\377"
+ "^\230\253\377EU&\377IZ%\377]z\34\377k\221\23\377v\231$\377\204\237H\377"
+ "Ra0\377\215\215\214\377\262\262\262\377\276\276\275\377kx}\3778MQ\377"
+ "4@\26\377=M\35\377JZ'\377[v\36\377r\2172\377\212\247D\377\\\204\204\377"
+ "s\266\323\377p\265\321\377m\263\320\377k\261\317\377d\246\303\370\3\4"
+ "\4N\0\0\0\22\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\26\0\0\0""2\20""8X\300"
+ "H\224\273\377`\264\330\377a\265\330\377Z\254\321\377\2""6i\377\0""5h"
+ "\377\1""5e\377\221\204a\377\376\370\356\377\312\260z\377yg>\377\14\15"
+ "\21\3779D\35\3772<\33\377;E%\377\12#7\3772f\205\377c\253\312\377p\277"
+ "\337\377q\274\335\377p\271\330\377u\256\306\377K^T\377=K#\377FT'\377"
+ "Zv\36\377Zn0\377ay/\377w\212F\377bw0\377\263\263\262\377\263\263\262"
+ "\377\375\375\373\377\234\237\240\3777NV\3779F\35\377;K\27\377AN#\377"
+ "m\212)\377\201\2453\377\203\235J\377U~\177\377p\265\321\377n\263\320"
+ "\377l\262\317\377i\260\316\377Y\226\260\350\0\0\0>\0\0\0\20\0\0\0\4\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\23\0\0\0+\27;S\245D\216\265\377`\264\327\377"
+ "a\265\330\377a\265\331\377\32X\206\377\0""4g\377%`\211\377\214\177Z\377"
+ "\370\363\347\377\306\256z\377\301\250u\377\26\24\24\3774;%\3775@\37\377"
+ "\20\37\35\377\5-S\377\14>m\377]\225\262\377\201\251\271\377\230\254\265"
+ "\377\257\263\265\377\300\300\300\377T]@\377IZ(\377[n+\377c}%\377b{)\377"
+ "}\232<\377\217\252L\377T\\A\377\235\235\234\377\371\371\367\377\376\376"
+ "\374\377\332\332\330\377Ry\213\377!*\37\377@N$\377K]$\377\40$\23\377"
+ "&'\37\377OZ4\377Z\221\243\377n\264\321\377m\262\317\377j\261\317\377"
+ "h\257\316\377N\205\234\320\0\0\0""2\0\0\0\16\0\0\0\3\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\20\0\0\0$\26*4wQ\236\303\377_\262\325\377_\262\325\377`\263"
+ "\326\377<\204\254\377\0""4f\377\37Sw\377ohI\377\364\354\335\377\305\255"
+ "{\377\311\261\177\377\21\40(\377\20\35,\377\25$.\3778I[\377it\177\377"
+ "\256\261\263\377\302\302\302\377\310\310\310\377\314\314\314\377\317"
+ "\317\317\377\322\322\322\377[\\W\377563\377\24\25\33\377\26\23\23\377"
+ "4(\32\377\15\16\13\377h~7\377VXP\377\343\343\342\377\376\376\374\377"
+ "\376\376\374\377\372\372\370\377Smy\377Aet\377+<6\377G>+\377\260\242"
+ "\213\377\346\331\276\377\221~S\377V\215\244\377l\261\316\377j\260\316"
+ "\377h\256\314\377e\255\313\377;ev\251\0\0\0'\0\0\0\14\0\0\0\3\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\15\0\0\0\36\1\2\2N1o\223\361\\\255\317\377\\\255\317"
+ "\377]\256\320\377\\\256\317\377\34X\204\377\22Ck\377CSM\377\335\315\250"
+ "\377\327\306\241\377\314\265\200\377SVR\377\200\202\204\377\214\214\214"
+ "\377\217\217\217\377\264\264\264\377\317\317\317\377\322\322\322\377"
+ "\326\326\326\377\331\331\331\377\334\334\334\377\337\337\337\377xxy\377"
+ ",-0\377>4\"\377\270\235d\377\350\327\265\377hW5\377##$\377\232\232\231"
+ "\377\366\366\364\377\376\376\374\377\376\376\374\377\375\375\373\377"
+ "fv{\377Q\200\225\377\77dt\377\235\210]\377\357\346\322\377\332\307\240"
+ "\377\230\203T\377W\216\246\377h\254\310\377f\253\310\377d\252\306\377"
+ "a\250\305\377\32-6v\0\0\0\35\0\0\0\11\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\12\0\0\0\30\0\0\0""8(Xs\306Z\250\310\377Z\251\312\377Y\247\310\377"
+ "X\245\305\377f\240\272\377k\212\233\377\\in\377\213wL\377\360\346\322"
+ "\377\313\264\200\377\202tV\377\202\202\202\377\226\226\226\377\233\233"
+ "\233\377\322\322\322\377\334\334\334\377\332\332\332\377\340\340\340"
+ "\377\217\215\207\377\334\334\334\377\352\352\352\377\264\264\264\377"
+ "a[P\377\263\227\\\377\317\271\213\377\360\350\323\377m\\8\377www\377"
+ "\274\274\272\377\362\362\360\377\376\376\374\377\374\374\372\377\371"
+ "\370\366\377\200\203\205\377Jv\210\377NSI\377\317\271\211\377\363\355"
+ "\341\377\310\257x\377a\\C\377\\\232\265\377c\247\303\377`\246\302\377"
+ "_\244\301\377R\216\247\352\0\0\1F\0\0\0\26\0\0\0\7\0\0\0\1\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\7\0\0\0\23\0\0\0(\23*7\207P\230\267\377t\244\270\377"
+ "\242\264\274\377\275\300\302\377\307\307\306\377\273\273\273\377\222"
+ "\222\222\377ca[\377\257\234v\377\335\312\243\377\316\266\203\377~rV\377"
+ "mic\377\207\206\204\377\227\224\217\377\215\204p\377i^F\377\310\310\310"
+ "\377\203pE\377\253\253\251\377\362\362\362\377usp\377\226}F\377\303\252"
+ "t\377\354\341\313\377\244\217f\377\\[X\377\261\261\260\377\237\237\235"
+ "\377rdE\377\223\214\177\377\271\271\267\377\324\324\322\377\205\204\204"
+ "\377STI\377\305\260\200\377\355\343\316\377\317\270\211\377\234\205S"
+ "\3777Wc\377_\241\274\377_\241\275\377\\\240\274\377Z\236\273\3778cu\264"
+ "\0\0\0-\0\0\0\20\0\0\0\5\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\17"
+ "\0\0\0\37\0\0\0L7l\204\351\242\265\275\377\330\330\327\377\300\300\300"
+ "\377\320\320\320\377\315\315\315\377\235\235\235\377\234\234\234\377"
+ "nmj\377\207wW\377\314\264\200\377\324\273\207\377\324\274\207\377\323"
+ "\272\207\377\323\271\201\377\220{N\377\255\253\247\377\340\340\340\377"
+ "\230~H\377zkJ\377\203|n\377\227\200M\377\304\253u\377\337\316\254\377"
+ "\264\243\202\377ROH\377\251\251\251\377\270\267\266\377\364\363\361\377"
+ "\211\202u\377\250\213P\377\272\241k\377\253\230l\377\300\253{\377\323"
+ "\273\210\377\330\305\233\377\310\256x\377\213uF\3775LR\377Du\211\377"
+ "Z\233\267\377Z\234\267\377X\232\266\377S\223\257\372\20\35\"i\0\0\0\37"
+ "\0\0\0\15\0\0\0\4\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\12\0\0\0\30"
+ "\0\0\0""2\36""9E\251v\226\244\377\375\375\373\377\354\354\352\377\267"
+ "\267\266\377\265\265\265\377\250\250\250\377\247\247\247\377\252\252"
+ "\252\377\235\235\235\377\220\216\211\377\225\214x\377\227\213r\377\230"
+ "\214u\377\225\220\205\377\326\326\326\377\376\376\376\377\365\365\365"
+ "\377~mF\377\300\243f\377\307\256w\377\314\265\201\377\324\277\217\377"
+ "\241\217g\377QOI\377\240\240\240\377\264\264\263\377\330\327\326\377"
+ "\367\366\364\377\360\357\356\377\243\240\234\377\203uW\377\220zO\377"
+ "\237\211Y\377\231\204V\377wlL\377EMC\3771S`\377<fx\377>l\177\377O\213"
+ "\245\377O\213\244\377N\212\243\3775_q\313\0\0\0I\0\0\0+\0\0\0!\0\0\0"
+ "\23\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\6\0\0\0\22\0\0\0$\0\2\2"
+ "_Lp\200\357\356\356\354\377\376\376\374\377\376\376\374\377\334\334\332"
+ "\377\216\216\215\377~~~\377\241\241\241\377\264\264\264\377\310\310\310"
+ "\377\373\373\373\377\374\374\374\377\375\375\375\377\376\376\376\377"
+ "\376\376\376\377\374\374\374\377\373\373\373\377\252\251\247\377\201"
+ "rS\377\234\206X\377\234\207Y\377ujS\377VVU\377\234\234\232\377\263\263"
+ "\261\377\304\303\302\377\363\361\357\377\354\354\352\377\356\354\353"
+ "\377\356\354\352\377\350\346\345\377\331\327\325\377\316\313\312\377"
+ "\202\202\201\377t{\177\377u}\200\377t|\177\377r{\177\377\205\217\223"
+ "\377\236\252\256\377\234\250\255\377\234\247\253\376\236\237\237\344"
+ "\245\245\244\327\244\244\244\325\243\243\242\323LLLr\0\0\0\22\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\14\0\0\0\32\0\0\0:\24$*\241\320\324"
+ "\325\377\376\376\374\377\376\376\374\377\376\376\374\377\374\374\372"
+ "\377\252\252\251\377oon\377www\377\230\230\230\377\304\304\304\377\337"
+ "\337\337\377\344\344\344\377\333\333\333\377\310\310\310\377\310\310"
+ "\310\377\353\353\353\377\271\271\271\377\362\362\362\377\344\344\344"
+ "\377\264\264\264\377kkj\377\233\233\232\377\257\257\255\377\266\266\264"
+ "\377\352\351\347\377\362\360\356\377\311\310\307\377\263\262\260\377"
+ "\312\310\306\377\331\327\325\377\333\330\327\377\312\307\305\377\206"
+ "\204\203\377\233\233\232\377\266\266\264\377\266\266\264\377\273\273"
+ "\271\377\364\364\362\377\376\376\374\377\376\376\374\377\361\341\330"
+ "\377\362\335\323\377\362\337\325\377\363\341\331\377\374\372\367\377"
+ "kkk\246\0\0\0\34\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\7\0\0\0\23"
+ "\0\0\0'\0\0\0c\233\244\250\361\376\376\374\377\376\376\374\377\376\376"
+ "\374\377\376\376\374\377\374\374\372\377\322\322\322\377\225\225\224"
+ "\377vvu\377ssr\377\214\214\214\377\262\262\262\377\266\266\266\377\277"
+ "\277\277\377\361\361\361\377\345\345\345\377\266\266\266\377\311\311"
+ "\311\377\305\305\305\377jjj\377\231\231\230\377\252\252\250\377\263\263"
+ "\262\377\340\337\336\377\360\356\354\377\356\353\352\377\345\343\341"
+ "\377\265\262\261\377\244\242\240\377\242\240\237\377\241\236\236\377"
+ "\237\234\233\377\230\225\223\377vvu\377\266\266\264\377\274\274\272\377"
+ "\354\354\352\377\376\376\374\377\376\376\374\377\376\376\374\377\343"
+ "yj\377\340F9\377\3325$\377\315Q=\377\365\363\356\377fff\250\0\0\0\37"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\14\0\0\0\35\0\0\0"
+ "Gmmm\267\375\375\373\377\376\376\374\377\376\376\374\377\376\376\374"
+ "\377\376\376\374\377\376\376\374\377\362\362\360\377\320\320\316\377"
+ "\245\245\243\377\216\216\216\377\177\177\177\377\234\234\234\377\352"
+ "\352\352\377\376\376\376\377\361\361\361\377\267\267\267\377\266\266"
+ "\266\377nnn\377\223\223\222\377\240\240\237\377\260\257\256\377\336\335"
+ "\333\377\356\354\352\377\354\352\350\377\350\346\344\377\346\344\342"
+ "\377\340\334\333\377\302\277\275\377\250\246\245\377\236\233\232\377"
+ "\233\230\227\377\233\230\226\377oon\377\327\327\325\377\367\367\365\377"
+ "\376\376\374\377\376\376\374\377\376\376\374\377\376\376\374\377\337"
+ "\236\227\377\312\277\272\377\343\240\231\377\325TD\377\365\362\352\377"
+ "ccc\250\0\0\0\40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\10"
+ "\0\0\0\25\0\0\0""8@@@}\353\353\351\377\376\376\374\377\376\376\374\377"
+ "\376\376\374\377\376\376\374\377\376\376\374\377\376\376\374\377\376"
+ "\376\374\377\376\376\374\377\357\357\356\377\326\326\325\377\313\313"
+ "\312\377\241\241\240\377\245\245\245\377\342\342\342\377\340\340\340"
+ "\377}}|\377\215\215\214\377\240\237\236\377\274\272\271\377\334\333\332"
+ "\377\344\342\340\377\344\342\340\377\346\343\342\377\344\342\340\377"
+ "\342\337\335\377\340\334\332\377\334\331\330\377\331\325\323\377\322"
+ "\316\315\377\316\312\310\377\315\310\307\377\211\206\205\377\373\373"
+ "\371\377\376\376\374\377\376\376\374\377\376\376\374\377\376\376\374"
+ "\377\371\371\367\377\312\204x\377\322\310\313\377\323\313\313\377\343"
+ "ob\377\366\362\352\377```\250\0\0\0\40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\1\0\0\0\5\0\0\0\17\0\0\0*\5\5\5V\316\316\315\366\376\376"
+ "\374\377\376\376\374\377\376\376\374\377\376\376\374\377\376\376\374"
+ "\377\376\376\374\377\376\376\374\377\376\376\374\377\376\376\374\377"
+ "\374\374\372\377\350\350\346\377\332\332\331\377\330\330\330\377\251"
+ "\251\250\377\220\220\220\377\265\265\263\377\326\325\324\377\340\336"
+ "\335\377\344\343\341\377\342\340\336\377\340\335\334\377\334\332\331"
+ "\377\332\330\326\377\333\330\326\377\334\330\326\377\334\327\326\377"
+ "\330\324\322\377\326\322\320\377\324\316\315\377\320\314\312\377\316"
+ "\310\307\377\233\227\226\377\332\332\330\377\376\376\374\377\376\376"
+ "\374\377\376\376\374\377\376\376\374\377\356\356\354\377\314pc\377\315"
+ "ZP\377\321{v\377\341RB\377\365\361\351\377\\\\\\\250\0\0\0\40\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\13\0\0\0\37\0\0\0"
+ "L\250\250\250\316\376\376\374\377\376\376\374\377\376\376\374\377\376"
+ "\376\374\377\376\376\374\377\376\376\374\377\376\376\374\377\376\376"
+ "\374\377\376\376\374\377\370\370\366\377\365\365\363\377\361\361\357"
+ "\377\346\345\344\377\337\336\334\377\336\336\334\377\336\335\334\377"
+ "\342\340\337\377\344\341\337\377\340\336\334\377\336\334\332\377\334"
+ "\331\330\377\331\326\324\377\326\323\322\377\324\320\317\377\322\316"
+ "\314\377\320\314\313\377\322\316\314\377\322\314\313\377\320\312\310"
+ "\377\314\307\306\377\312\304\302\377\277\271\270\377\237\237\236\377"
+ "\376\376\374\377\376\376\374\377\376\376\374\377\376\376\374\377\375"
+ "\375\373\377\306\233\220\377\317\202u\377\332\226\210\377\344\263\243"
+ "\377\362\357\353\377YXX\250\0\0\0\40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\2\0\0\0\11\0\0\0\30\0\0\0Bppp\245\375\375\373\377"
+ "\376\376\374\377\376\376\374\377\376\376\374\377\376\376\374\377\376"
+ "\376\374\377\376\376\374\377\376\376\374\377\373\373\371\377\364\364"
+ "\362\377\362\361\357\377\356\356\354\377\354\353\351\377\350\347\345"
+ "\377\342\340\336\377\340\336\335\377\342\337\336\377\336\334\333\377"
+ "\334\332\330\377\332\330\326\377\330\324\323\377\326\322\320\377\322"
+ "\317\315\377\320\314\312\377\316\311\307\377\312\306\304\377\310\303"
+ "\302\377\307\302\301\377\310\303\302\377\310\302\300\377\306\277\276"
+ "\377\303\274\273\377\207\205\204\377\375\375\373\377\376\376\374\377"
+ "\376\376\374\377\376\375\373\377\375\374\372\377\366\365\364\377\371"
+ "\370\366\377\366\366\364\377\367\366\364\377\365\364\362\377UUS\250\0"
+ "\0\0\40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\6\0"
+ "\0\0\23\0\0\0""8<<<z\350\350\346\377\376\376\374\377\376\376\374\377"
+ "\376\376\374\377\376\376\374\377\376\376\374\377\376\376\374\377\375"
+ "\375\373\377\363\363\361\377\360\357\355\377\355\354\352\377\352\351"
+ "\347\377\350\346\344\377\346\343\342\377\342\340\337\377\340\336\334"
+ "\377\336\333\332\377\333\330\327\377\330\326\324\377\326\323\321\377"
+ "\324\320\316\377\321\315\313\377\316\312\310\377\314\307\306\377\312"
+ "\304\302\377\306\301\300\377\304\277\276\377\302\274\273\377\277\272"
+ "\270\377\276\270\266\377\276\270\266\377\276\270\266\377xut\377\372\372"
+ "\370\377\374\374\372\377\373\373\371\377\371\371\367\377\370\367\365"
+ "\377\366\366\364\377\365\364\362\377\363\362\360\377\362\360\357\377"
+ "\360\357\355\377PPP\250\0\0\0\40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0\16\0\0\0*\6\6\6V\310\310\307\367\376\376"
+ "\374\377\376\376\374\377\376\376\374\377\376\376\374\377\376\376\374"
+ "\377\374\374\372\377\364\364\362\377\356\356\354\377\354\352\350\377"
+ "\350\350\346\377\346\344\343\377\344\342\340\377\341\337\335\377\336"
+ "\334\332\377\334\332\330\377\332\327\325\377\330\324\322\377\324\321"
+ "\320\377\322\316\314\377\320\313\312\377\314\310\306\377\312\306\304"
+ "\377\310\302\301\377\305\300\276\377\302\276\274\377\300\273\271\377"
+ "\276\270\266\377\274\264\263\377\271\262\260\377\241\233\231\377wsr\377"
+ "jhg\377\362\362\360\377\370\367\365\377\366\365\363\377\365\364\362\377"
+ "\363\362\360\377\362\360\356\377\360\357\355\377\357\355\353\377\355"
+ "\353\351\377\354\351\350\377LLL\250\0\0\0\40\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\13\0\0\0\40\0\0\0L\240\240"
+ "\237\321\376\376\374\377\376\376\374\377\376\376\374\377\375\375\373"
+ "\377\372\372\370\377\366\365\364\377\354\354\352\377\352\350\347\377"
+ "\350\346\344\377\344\343\341\377\342\340\336\377\340\335\334\377\334"
+ "\332\331\377\332\330\326\377\330\325\324\377\326\322\320\377\324\317"
+ "\316\377\320\314\312\377\316\312\310\377\314\306\305\377\310\304\302"
+ "\377\306\300\300\377\304\276\275\377\300\274\272\377\277\271\270\377"
+ "\247\241\240\377~zy\377vtt\377xwv\377\220\220\217\377\267\267\267\377"
+ "\271\270\266\377\345\343\342\377\363\362\360\377\362\360\356\377\360"
+ "\356\355\377\357\355\353\377\355\353\351\377\353\351\347\377\352\350"
+ "\346\377\350\346\344\377\347\344\342\377GGG\250\0\0\0\40\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\11\0\0\0\30\0"
+ "\0\0Cfff\247\376\376\374\377\376\376\374\377\374\374\372\377\372\371"
+ "\367\377\366\366\364\377\354\353\352\377\350\347\345\377\346\344\342"
+ "\377\344\341\337\377\340\336\334\377\336\334\332\377\334\331\330\377"
+ "\331\326\324\377\326\323\322\377\324\320\317\377\322\316\314\377\317"
+ "\313\311\377\314\310\306\377\312\304\303\377\310\302\300\377\304\277"
+ "\276\377\255\250\247\377\204\200\177\377ron\377_^]\377eed\377\214\213"
+ "\213\377\232\231\230\377\247\246\245\377\274\273\271\377\302\302\300"
+ "\377\350\346\344\377\360\356\355\377\356\355\353\377\355\353\351\377"
+ "\353\351\347\377\352\347\346\377\350\346\344\377\347\344\342\377\345"
+ "\342\341\377\344\341\337\377\342\337\335\377DCC\250\0\0\0\40\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\6\0\0\0\23"
+ "\0\0\0""8555}\345\345\343\377\372\372\370\377\370\367\365\377\365\364"
+ "\362\377\356\355\353\377\346\345\343\377\344\342\340\377\342\337\336"
+ "\377\336\334\333\377\334\332\330\377\332\330\326\377\330\324\323\377"
+ "\326\322\320\377\322\317\315\377\320\314\312\377\316\311\307\377\312"
+ "\306\304\377\263\257\255\377\213\210\207\377\177~}\377\202\201\200\377"
+ "\230\230\230\377\277\277\275\377\313\313\311\377\302\301\300\377\272"
+ "\271\270\377\300\277\275\377\320\317\315\377\332\331\327\377\333\332"
+ "\330\377\353\351\347\377\355\353\351\377\353\351\347\377\352\347\345"
+ "\377\350\346\344\377\347\344\342\377\345\342\341\377\344\340\337\377"
+ "\342\337\335\377\340\335\333\377\337\333\332\377\335\332\330\377@\77"
+ "\77\250\0\0\0\40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\1\0\0\0\5\0\0\0\16\0\0\0+\7\7\7W\275\275\274\370\366\365\364\377"
+ "\364\362\360\377\357\356\354\377\346\344\342\377\342\340\337\377\340"
+ "\336\334\377\336\333\332\377\333\330\327\377\330\326\324\377\326\323"
+ "\321\377\324\320\316\377\322\315\314\377\271\266\264\377\224\221\220"
+ "\377\212\210\207\377\205\204\203\377\220\220\217\377\233\233\231\377"
+ "\225\225\225\377\230\230\226\377\251\250\247\377\302\301\277\377\327"
+ "\325\324\377\335\335\333\377\326\325\323\377\316\314\312\377\312\311"
+ "\310\377\310\306\304\377\334\332\330\377\352\347\345\377\350\345\344"
+ "\377\347\344\342\377\345\342\340\377\343\340\337\377\342\337\335\377"
+ "\340\335\333\377\337\333\331\377\335\332\330\377\334\330\326\377\332"
+ "\326\325\377\331\324\323\377<;;\250\0\0\0\40\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\13\0\0\0\40\0\0\0"
+ "M\223\223\223\323\362\360\356\377\357\356\354\377\346\344\342\377\342"
+ "\337\335\377\336\334\332\377\334\332\330\377\332\327\325\377\330\324"
+ "\322\377\277\274\273\377\231\227\226\376\221\217\216\377\215\214\213"
+ "\377\240\240\237\377\303\303\301\377\316\315\314\377\325\325\324\377"
+ "\337\336\334\377\350\347\346\377\357\356\354\377\361\360\356\377\362"
+ "\360\356\377\361\357\356\377\357\356\354\377\356\354\352\377\354\352"
+ "\351\377\353\351\347\377\351\347\345\377\350\345\343\377\346\344\342"
+ "\377\345\342\340\377\343\340\337\377\342\336\335\377\340\335\333\377"
+ "\337\333\331\377\335\331\330\377\333\330\326\377\332\326\324\377\330"
+ "\324\323\377\327\323\321\377\325\321\317\377\324\317\315\377866\250\0"
+ "\0\0\40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\2\0\0\0\11\0\0\0\31\0\0\0DYYY\253\356\354\352\377\350\345\344\377"
+ "\340\335\334\377\334\332\331\377\305\303\302\377\237\235\235\372zyy\327"
+ "HGG\254\35\35\35\213www\337\317\317\315\377\327\326\324\377\340\340\336"
+ "\377\352\351\347\377\360\357\355\377\362\361\360\377\363\362\360\377"
+ "\362\361\357\377\361\357\355\377\357\356\354\377\356\354\352\377\354"
+ "\352\351\377\353\351\347\377\351\347\345\377\350\345\343\377\346\343"
+ "\342\377\345\342\340\377\343\340\336\377\341\336\335\377\340\335\333"
+ "\377\336\333\331\377\335\331\327\377\333\330\326\377\332\326\324\377"
+ "\330\324\323\377\327\322\321\377\325\321\317\377\324\317\315\377\322"
+ "\315\314\377\321\313\312\377\317\312\310\377322\250\0\0\0\40\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\6"
+ "\0\0\0\23\0\0\0""8+++~\274\272\271\377\247\245\244\372\202\200\200\326"
+ "NNN\254%%%\201\6\6\6X\0\0\0M\0\0\0F\0\0\0K\222\222\221\330\361\361\357"
+ "\377\364\363\361\377\364\363\362\377\364\363\361\377\362\361\357\377"
+ "\361\357\355\377\357\355\354\377\356\354\352\377\354\352\350\377\353"
+ "\350\347\377\351\347\345\377\350\345\343\377\346\343\341\377\345\342"
+ "\340\377\343\340\336\377\341\336\335\377\340\334\333\377\336\333\331"
+ "\377\335\331\327\377\333\327\326\377\332\326\324\377\330\324\322\377"
+ "\327\322\321\377\325\321\317\377\324\317\315\377\322\315\313\377\321"
+ "\313\312\377\317\312\310\377\316\310\307\377\314\306\305\377\312\305"
+ "\303\377/..\250\0\0\0\40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\5\0\0\0\16\0\0\0)\0\0\0P\"\"\"v\7\7\7"
+ "X\0\0\0M\0\0\0D\0\0\0""9\0\0\0-\0\0\0$\0\0\0#\0\0\0""5\227\227\226\326"
+ "\364\363\361\377\362\361\357\377\361\357\355\377\357\355\354\377\356"
+ "\354\352\377\354\352\350\377\353\350\347\377\351\347\345\377\347\345"
+ "\343\377\346\343\341\377\344\341\340\377\343\340\336\377\341\336\334"
+ "\377\340\334\333\377\336\333\331\377\335\331\327\377\333\327\325\377"
+ "\332\326\324\377\330\324\322\377\327\322\321\377\325\320\317\377\323"
+ "\317\315\377\322\315\313\377\320\313\312\377\317\312\310\377\315\310"
+ "\306\377\314\306\305\377\312\305\303\377\311\303\301\377\307\301\277"
+ "\377\306\277\276\377+**\250\0\0\0\40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\12\0\0\0\30\0\0\0"
+ "0\0\0\0""5\0\0\0,\0\0\0\"\0\0\0\34\0\0\0\26\0\0\0\22\0\0\0\21\0\0\0\27"
+ "\0\0\0""1\223\222\222\326\357\355\353\377\355\353\352\377\354\352\350"
+ "\377\352\350\346\377\351\347\345\377\347\345\343\377\346\343\341\377"
+ "\344\341\340\377\343\340\336\377\341\336\334\377\340\334\333\377\336"
+ "\332\331\377\335\331\327\377\333\327\325\377\331\325\324\377\330\324"
+ "\322\377\326\322\320\377\325\320\317\377\323\317\315\377\322\315\313"
+ "\377\320\313\311\377\317\311\310\377\315\310\306\377\314\306\305\377"
+ "\312\304\303\377\311\303\301\377\307\301\277\377\306\277\276\377\304"
+ "\276\274\377\302\274\272\377\301\272\271\377'&&\250\0\0\0\40\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1"
+ "\0\0\0\5\0\0\0\14\0\0\0\21\0\0\0\23\0\0\0\21\0\0\0\16\0\0\0\13\0\0\0"
+ "\10\0\0\0\6\0\0\0\10\0\0\0\22\0\0\0/\216\215\214\325\352\350\346\377"
+ "\351\346\345\377\347\345\343\377\346\343\341\377\344\341\337\377\342"
+ "\337\336\377\341\336\334\377\337\334\332\377\336\332\331\377\334\331"
+ "\327\377\333\327\325\377\331\325\324\377\330\324\322\377\326\322\320"
+ "\377\325\320\317\377\323\316\315\377\322\315\313\377\320\313\311\377"
+ "\317\311\310\377\315\310\306\377\314\306\304\377\312\304\303\377\311"
+ "\303\301\377\307\301\277\377\306\277\276\377\304\275\274\377\302\274"
+ "\272\377\301\272\271\377\277\270\267\377\276\267\265\377\274\265\263"
+ "\377!\40\40\250\0\0\0\40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\4\0\0\0\6\0\0\0\7\0\0"
+ "\0\5\0\0\0\4\0\0\0\3\0\0\0\2\0\0\0\2\0\0\0\5\0\0\0\17\0\0\0'777|VUU\252"
+ "TTT\252RRR\252QPP\252ONN\252MLL\252LKK\252III\252HHG\252FEE\252DCC\252"
+ "CBB\252@@@\252\77>=\252=<<\252<::\252999\252776\252654\252433\252311"
+ "\252100\252.--\252-,+\252+**\252*((\252(''\252%%$\252$#\"\252\"!!\252"
+ "\37\36\36\251\1\1\1_\0\0\0\34\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1"
+ "\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\14\0\0\0\32\0"
+ "\0\0&\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,"
+ "\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0"
+ ",\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0\0,\0\0"
+ "\0,\0\0\0,\0\0\0*\0\0\0\"\0\0\0\23\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\7\0\0\0"
+ "\17\0\0\0\26\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0"
+ "\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30"
+ "\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0"
+ "\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0\0\0\30\0"
+ "\0\0\30\0\0\0\27\0\0\0\22\0\0\0\13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0"};
+
+
diff --git a/src/plugins/notification/version.rc b/src/plugins/notification/version.rc
@@ -0,0 +1,36 @@
+1 VERSIONINFO
+ FILEVERSION 0, 0, 0, 0
+ PRODUCTVERSION 0, 0, 0, 0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "FileDescription", "notification Plugin\0"
+ VALUE "FileVersion", "0.0.0.0\0"
+ VALUE "ProductVersion", "0.0.0.0 Win32\0"
+ VALUE "LegalCopyright", "GPL / © 1999-2008 Hiroyuki Yamamoto & The Claws Mail Team\0"
+ VALUE "CompanyName", "GNU / Free Software Foundation\0"
+ VALUE "ProductName", "Claws Mail\0"
+// VALUE "Comments", "\0"
+// VALUE "InternalName", "\0"
+// VALUE "LegalTrademarks", "\0"
+// VALUE "OriginalFilename", "\0"
+// VALUE "PrivateBuild", "\0"
+// VALUE "SpecialBuild", "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0, 1200
+ END
+END
diff --git a/src/plugins/pdf_viewer/Makefile.am b/src/plugins/pdf_viewer/Makefile.am
@@ -0,0 +1,26 @@
+plugindir = $(pkglibdir)/plugins
+
+plugin_LTLIBRARIES = pdf_viewer.la
+
+pdf_viewer_la_SOURCES = \
+ poppler_viewer.c \
+ poppler_viewer.h \
+ gettext.h
+
+pdf_viewer_la_LDFLAGS = \
+ -avoid-version -module \
+ $(GTK_LIBS) \
+ $(POPPLER_LIBS)
+
+INCLUDES = \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/src/common \
+ -I$(top_srcdir)/src/gtk
+
+AM_CPPFLAGS = \
+ -Wall \
+ $(CLAWS_MAIL_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(GTK_CFLAGS) \
+ $(POPPLER_CFLAGS) \
+ -DLOCALEDIR=\""$(localedir)"\"
diff --git a/src/plugins/pdf_viewer/doc_index.xpm b/src/plugins/pdf_viewer/doc_index.xpm
@@ -0,0 +1,21 @@
+/* XPM */
+static char * doc_index_xpm[] = {
+"14 14 4 1",
+" c None",
+". c #1F0000",
+"+ c #4D0202",
+"@ c #000000",
+"........ ",
+".++++++. ",
+".+@@@@@@ ",
+".+@ @ ",
+".+@ @ ",
+".+@ @ ",
+"...@@@@@ ",
+" @@ @@@",
+" @@@@@@@@@@",
+" @@ @@@",
+" @@ ",
+" @@ @@@",
+" @@@@@@@@@@",
+" @@@"};
diff --git a/src/plugins/pdf_viewer/doc_index_close.xpm b/src/plugins/pdf_viewer/doc_index_close.xpm
@@ -0,0 +1,19 @@
+/* XPM */
+static char * doc_index_close_xpm[] = {
+"14 14 2 1",
+" c None",
+". c #000000",
+" ",
+" ",
+" ... ... ",
+" .... .... ",
+" ... ... ",
+" ..... ",
+" ..... ",
+" ... ",
+" ..... ",
+" ..... ",
+" ... ... ",
+" ... ... ",
+" ... ... ",
+" "};
diff --git a/src/plugins/pdf_viewer/doc_info.xpm b/src/plugins/pdf_viewer/doc_info.xpm
@@ -0,0 +1,104 @@
+/* XPM */
+static char * doc_info_xpm[] = {
+"14 14 87 1",
+" c None",
+". c #000000",
+"+ c #030303",
+"@ c #4F4F4F",
+"# c #ABABAB",
+"$ c #D3D3D3",
+"% c #ADADAD",
+"& c #515151",
+"* c #040404",
+"= c #121212",
+"- c #B1B1B1",
+"; c #EFEFEF",
+"> c #8F8F8F",
+", c #4C4C4C",
+"' c #939393",
+") c #E7E7E7",
+"! c #B4B4B4",
+"~ c #141414",
+"{ c #D4D4D4",
+"] c #373737",
+"^ c #8E8E8E",
+"/ c #929292",
+"( c #0C0C0C",
+"_ c #323232",
+": c #CECECE",
+"< c #B6B6B6",
+"[ c #505050",
+"} c #EBEBEB",
+"| c #424242",
+"1 c #010101",
+"2 c #737373",
+"3 c #747474",
+"4 c #090909",
+"5 c #0B0B0B",
+"6 c #484848",
+"7 c #F6F6F6",
+"8 c #555555",
+"9 c #919191",
+"0 c #0A0A0A",
+"a c #060606",
+"b c #2A2A2A",
+"c c #252525",
+"d c #0E0E0E",
+"e c #161616",
+"f c #B3B3B3",
+"g c #D5D5D5",
+"h c #050505",
+"i c #ACACAC",
+"j c #E0E0E0",
+"k c #080808",
+"l c #7B7B7B",
+"m c #DADADA",
+"n c #D6D6D6",
+"o c #020202",
+"p c #979797",
+"q c #070707",
+"r c #6A6A6A",
+"s c #DBDBDB",
+"t c #B0B0B0",
+"u c #898989",
+"v c #969696",
+"w c #DEDEDE",
+"x c #0F0F0F",
+"y c #B5B5B5",
+"z c #1B1B1B",
+"A c #111111",
+"B c #191919",
+"C c #E4E4E4",
+"D c #5A5A5A",
+"E c #B9B9B9",
+"F c #C0C0C0",
+"G c #262626",
+"H c #181818",
+"I c #BEBEBE",
+"J c #BDBDBD",
+"K c #BABABA",
+"L c #E8E8E8",
+"M c #A5A5A5",
+"N c #4B4B4B",
+"O c #828282",
+"P c #E3E3E3",
+"Q c #171717",
+"R c #595959",
+"S c #DCDCDC",
+"T c #DDDDDD",
+"U c #B7B7B7",
+"V c #5B5B5B",
+" .++. ",
+" +@#$$%&* ",
+" =-;>,,')!~ ",
+" +-{].^/(_:<* ",
+" [}|1.2345678.",
+".%90.abcade<f.",
+"*g8h*4ij+kdlma",
+"*n80*opj1qdrsa",
+".tuah.vw15xuy.",
+".8)zaA%$xoBCD.",
+" *EFGao..HIJh ",
+" eKLMNNOPJQ ",
+" hR<STUVa ",
+" ..qq.. "};
diff --git a/src/plugins/pdf_viewer/first_arrow.xpm b/src/plugins/pdf_viewer/first_arrow.xpm
@@ -0,0 +1,49 @@
+/* XPM */
+static char * first_arrow_xpm[] = {
+"16 16 30 1",
+" c None",
+". c #1D2F3F",
+"+ c #1D2F40",
+"@ c #1C2C3C",
+"# c #3F6588",
+"$ c #000000",
+"% c #1C2E3E",
+"& c #5B88B2",
+"* c #5080AD",
+"= c #9EB8D1",
+"- c #192937",
+"; c #080D11",
+"> c #9CB7D1",
+", c #A4BDD5",
+"' c #B6CADD",
+") c #B2C7DB",
+"! c #AFC5DA",
+"~ c #B5C9DC",
+"{ c #5F8BB4",
+"] c #97B3CE",
+"^ c #A0BAD3",
+"/ c #9AB5CF",
+"( c #9BB6D0",
+"_ c #4C79A3",
+": c #95B2CE",
+"< c #91B0CC",
+"[ c #2A435B",
+"} c #49749C",
+"| c #375978",
+"1 c #456F96",
+" ",
+".+ ",
+"@# $ ",
+"%# $$ ",
+"@# $&$ ",
+"@# $*=$$$$$$$ ",
+"-# ;*>,''')!~$ ",
+"%#${]>^/(>>^($ ",
+"@$_:^>>>(//=<$ ",
+"@#$[########}$ ",
+"@# $[##|||||1$ ",
+"@# $[#$$$$$$$ ",
+"@# $[$ ",
+"%# $$ ",
+"@% $ ",
+" "};
diff --git a/src/plugins/pdf_viewer/last_arrow.xpm b/src/plugins/pdf_viewer/last_arrow.xpm
@@ -0,0 +1,49 @@
+/* XPM */
+static char * last_arrow_xpm[] = {
+"16 16 30 1",
+" c None",
+". c #1D2F40",
+"+ c #1D2F3F",
+"@ c #000000",
+"# c #3F6588",
+"$ c #1C2C3C",
+"% c #1C2E3E",
+"& c #5B88B2",
+"* c #9EB8D1",
+"= c #5080AD",
+"- c #B5C9DC",
+"; c #AFC5DA",
+"> c #B2C7DB",
+", c #B6CADD",
+"' c #A4BDD5",
+") c #9CB7D1",
+"! c #080D11",
+"~ c #192937",
+"{ c #9BB6D0",
+"] c #A0BAD3",
+"^ c #9AB5CF",
+"/ c #97B3CE",
+"( c #5F8BB4",
+"_ c #91B0CC",
+": c #95B2CE",
+"< c #4C79A3",
+"[ c #49749C",
+"} c #2A435B",
+"| c #456F96",
+"1 c #375978",
+" ",
+" .+",
+" @ #$",
+" @@ #%",
+" @&@ #$",
+" @@@@@@@*=@ #$",
+" @-;>,,,')=! #~",
+" @{])){^])/(@#%",
+" @_*^^{)))]:<@$",
+" @[########}@#$",
+" @|11111##}@ #$",
+" @@@@@@@#}@ #$",
+" @}@ #$",
+" @@ #%",
+" @ %$",
+" "};
diff --git a/src/plugins/pdf_viewer/left_arrow.xpm b/src/plugins/pdf_viewer/left_arrow.xpm
@@ -0,0 +1,44 @@
+/* XPM */
+static char * left_arrow_xpm[] = {
+"16 16 25 1",
+" c None",
+". c #000000",
+"+ c #5B88B2",
+"@ c #5080AD",
+"# c #9EB8D1",
+"$ c #080D11",
+"% c #9CB7D1",
+"& c #A4BDD5",
+"* c #B6CADD",
+"= c #B2C7DB",
+"- c #AFC5DA",
+"; c #B5C9DC",
+"> c #5F8BB4",
+", c #97B3CE",
+"' c #A0BAD3",
+") c #9AB5CF",
+"! c #9BB6D0",
+"~ c #4C79A3",
+"{ c #95B2CE",
+"] c #91B0CC",
+"^ c #2A435B",
+"/ c #3F6588",
+"( c #49749C",
+"_ c #375978",
+": c #456F96",
+" ",
+" ",
+" . ",
+" .. ",
+" .+. ",
+" .@#....... ",
+" $@%&***=-;. ",
+" .>,%')!%%'!. ",
+" .~{'%%%!))#]. ",
+" .^////////(. ",
+" .^//_____:. ",
+" .^/....... ",
+" .^. ",
+" .. ",
+" . ",
+" "};
diff --git a/src/plugins/pdf_viewer/placeholder.txt b/src/plugins/pdf_viewer/placeholder.txt
@@ -1 +0,0 @@
-Placeholder
diff --git a/src/plugins/pdf_viewer/poppler_viewer.c b/src/plugins/pdf_viewer/poppler_viewer.c
@@ -0,0 +1,2106 @@
+/*
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright(C) 1999-2007 the Claws Mail Team
+ * This file Copyright (C) 2007 Salvatore De Paolis
+ * <iwkse@claws-mail.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ *(at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#include "claws-features.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "poppler_viewer.h"
+#include "printing.h"
+#include "prefs_common.h"
+#include "gtk/gtkutils.h"
+#include "mimeview.h"
+#ifndef POPPLER_WITH_GDK
+#include "stdbool.h"
+#endif
+
+static FileType pdf_viewer_mimepart_get_type(MimeInfo *partinfo);
+static MimeViewerFactory pdf_viewer_factory;
+
+static void pdf_viewer_show_mimepart(MimeViewer *_viewer, const gchar *infile,
+ MimeInfo *partinfo);
+
+static MimeViewer *pdf_viewer_create(void);
+static void pdf_viewer_clear(MimeViewer *_viewer);
+static void pdf_viewer_destroy(MimeViewer *_viewer);
+static void pdf_viewer_update(MimeViewer *_viewer, gboolean reload_file, int page_num);
+
+static GtkWidget *pdf_viewer_get_widget(MimeViewer *_viewer);
+
+static void pdf_viewer_hide_index_pane(PdfViewer *viewer);
+static void pdf_viewer_set_index_button_sensitive(PdfViewer *viewer);
+static void pdf_viewer_scroll_to(PdfViewer *viewer, gfloat x, gfloat y);
+
+static void search_matches_free(PdfViewer *viewer);
+static gboolean pdf_viewer_text_search(MimeViewer *_viewer, gboolean backward,
+ const gchar *str, gboolean case_sens);
+static void pdf_viewer_render_selection(PdfViewer *viewer, PopplerRectangle *rect, PageResult *page_results);
+static void pdf_viewer_render_page(PopplerPage *page, GtkWidget *view, double width, double height, double zoom, gint rotate);
+
+static char * pdf_viewer_get_document_format_data(GTime utime);
+static void pdf_viewer_get_document_index(PdfViewer *viewer, PopplerIndexIter *index_iter, GtkTreeIter *parentiter);
+static void pdf_viewer_index_row_activated(GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ gpointer data);
+
+static GtkTable * pdf_viewer_fill_info_table(PdfViewer *viewer);
+
+/* Callbacks */
+static void pdf_viewer_move_events_cb(GtkWidget *widget, GdkEventMotion *event, PdfViewer *viewer);
+static void pdf_viewer_button_press_events_cb(GtkWidget *widget, GdkEventButton *event, PdfViewer *viewer);
+static void pdf_viewer_mouse_scroll_destroy_cb(GtkWidget *widget, GdkEventButton *event, PdfViewer *viewer);
+static void pdf_viewer_button_first_page_cb(GtkButton *button, PdfViewer *viewer);
+static void pdf_viewer_button_last_page_cb(GtkButton *button, PdfViewer *viewer);
+static void pdf_viewer_button_zoom_in_cb(GtkButton *button, PdfViewer *viewer);
+static void pdf_viewer_button_zoom_out_cb(GtkButton *button, PdfViewer *viewer);
+static void pdf_viewer_button_zoom_fit_cb(GtkButton *button, PdfViewer *viewer);
+static void pdf_viewer_button_zoom_width_cb(GtkButton *button, PdfViewer *viewer);
+static void pdf_viewer_button_rotate_right_cb(GtkButton *button, PdfViewer *viewer);
+static void pdf_viewer_button_rotate_left_cb(GtkButton *button, PdfViewer *viewer);
+static void pdf_viewer_spin_change_page_cb(GtkSpinButton *button, PdfViewer *viewer);
+static void pdf_viewer_spin_zoom_scroll_cb(GtkSpinButton *button, PdfViewer *viewer);
+/* Show/Hide the index pane */
+static void pdf_viewer_show_document_index_cb(GtkButton *button, PdfViewer *viewer);
+static void pdf_viewer_button_document_info_cb(GtkButton *button, PdfViewer *viewer);
+
+static void pdf_viewer_show_controls(PdfViewer *viewer, gboolean show);
+static gboolean pdf_viewer_scroll_page(MimeViewer *_viewer, gboolean up);
+static void pdf_viewer_scroll_one_line(MimeViewer *_viewer, gboolean up);
+
+/** Claws-Mail Plugin functions*/
+gint plugin_init(gchar **error);
+const gchar *plugin_name(void);
+const gchar *plugin_desc(void);
+const gchar *plugin_type(void);
+const gchar *plugin_licence(void);
+const gchar *plugin_version(void);
+struct PluginFeature *plugin_provides(void);
+
+#ifndef POPPLER_WITH_GDK
+static void
+copy_cairo_surface_to_pixbuf (cairo_surface_t *surface,
+ GdkPixbuf *pixbuf)
+{
+ int cairo_width, cairo_height, cairo_rowstride;
+ unsigned char *pixbuf_data, *dst, *cairo_data;
+ int pixbuf_rowstride, pixbuf_n_channels;
+ unsigned int *src;
+ int x, y;
+
+ cairo_width = cairo_image_surface_get_width (surface);
+ cairo_height = cairo_image_surface_get_height (surface);
+ cairo_rowstride = cairo_image_surface_get_stride (surface);
+ cairo_data = cairo_image_surface_get_data (surface);
+
+ pixbuf_data = gdk_pixbuf_get_pixels (pixbuf);
+ pixbuf_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ pixbuf_n_channels = gdk_pixbuf_get_n_channels (pixbuf);
+
+ if (cairo_width > gdk_pixbuf_get_width (pixbuf))
+ cairo_width = gdk_pixbuf_get_width (pixbuf);
+ if (cairo_height > gdk_pixbuf_get_height (pixbuf))
+ cairo_height = gdk_pixbuf_get_height (pixbuf);
+ for (y = 0; y < cairo_height; y++) {
+ src = (unsigned int *) (cairo_data + y * cairo_rowstride);
+ dst = pixbuf_data + y * pixbuf_rowstride;
+ for (x = 0; x < cairo_width; x++) {
+ dst[0] = (*src >> 16) & 0xff;
+ dst[1] = (*src >> 8) & 0xff;
+ dst[2] = (*src >> 0) & 0xff;
+ if (pixbuf_n_channels == 4)
+ dst[3] = (*src >> 24) & 0xff;
+ dst += pixbuf_n_channels;
+ src++;
+ }
+ }
+}
+static void
+_poppler_page_render_to_pixbuf (PopplerPage *page,
+ int src_x, int src_y,
+ int src_width, int src_height,
+ double scale,
+ int rotation,
+ gboolean printing,
+ GdkPixbuf *pixbuf)
+{
+ cairo_t *cr;
+ cairo_surface_t *surface;
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ src_width, src_height);
+ cr = cairo_create (surface);
+ cairo_save (cr);
+ switch (rotation) {
+ case 90:
+ cairo_translate (cr, src_x + src_width, -src_y);
+ break;
+ case 180:
+ cairo_translate (cr, src_x + src_width, src_y + src_height);
+ break;
+ case 270:
+ cairo_translate (cr, -src_x, src_y + src_height);
+ break;
+ default:
+ cairo_translate (cr, -src_x, -src_y);
+ }
+
+ if (scale != 1.0)
+ cairo_scale (cr, scale, scale);
+
+ if (rotation != 0)
+ cairo_rotate (cr, rotation * G_PI / 180.0);
+
+ if (printing)
+ poppler_page_render_for_printing (page, cr);
+ else
+ poppler_page_render (page, cr);
+ cairo_restore (cr);
+
+ cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER);
+ cairo_set_source_rgb (cr, 1., 1., 1.);
+ cairo_paint (cr);
+
+ cairo_destroy (cr);
+
+ copy_cairo_surface_to_pixbuf (surface, pixbuf);
+ cairo_surface_destroy (surface);
+}
+
+/**
+ * poppler_page_render_to_pixbuf:
+ * @page: the page to render from
+ * @src_x: x coordinate of upper left corner
+ * @src_y: y coordinate of upper left corner
+ * @src_width: width of rectangle to render
+ * @src_height: height of rectangle to render
+ * @scale: scale specified as pixels per point
+ * @rotation: rotate the document by the specified degree
+ * @pixbuf: pixbuf to render into
+ *
+ * First scale the document to match the specified pixels per point,
+ * then render the rectangle given by the upper left corner at
+ * (src_x, src_y) and src_width and src_height.
+ * This function is for rendering a page that will be displayed.
+ * If you want to render a page that will be printed use
+ * poppler_page_render_to_pixbuf_for_printing() instead
+ *
+ * Deprecated: 0.16
+ **/
+static void
+poppler_page_render_to_pixbuf (PopplerPage *page,
+ int src_x, int src_y,
+ int src_width, int src_height,
+ double scale,
+ int rotation,
+ GdkPixbuf *pixbuf)
+{
+ g_return_if_fail (POPPLER_IS_PAGE (page));
+ g_return_if_fail (scale > 0.0);
+ g_return_if_fail (pixbuf != NULL);
+
+ _poppler_page_render_to_pixbuf (page, src_x, src_y,
+ src_width, src_height,
+ scale, rotation,
+ FALSE,
+ pixbuf);
+}
+#endif
+static GtkWidget *pdf_viewer_get_widget(MimeViewer *_viewer)
+{
+ PdfViewer *viewer = (PdfViewer *) _viewer;
+ debug_print("pdf_viewer_get_widget: %p\n", viewer->vbox);
+
+ return GTK_WIDGET(viewer->vbox);
+}
+/** Hide the index panel */
+static void pdf_viewer_hide_index_pane(PdfViewer *viewer)
+{
+ if (viewer->pdf_index) {
+ poppler_index_iter_free(viewer->pdf_index);
+ viewer->pdf_index = NULL;
+ gtk_widget_hide(GTK_WIDGET(viewer->frame_index));
+ }
+}
+
+static void search_matches_free(PdfViewer *viewer)
+{
+ GList *cur;
+ for(cur = viewer->text_found; cur; cur = cur->next) {
+ PageResult *res = (PageResult *)cur->data;
+ g_list_free(res->results);
+ g_free(res);
+ }
+ g_list_free(viewer->text_found);
+ viewer->text_found = NULL;
+ g_free(viewer->last_search);
+ viewer->last_search = NULL;
+ if (viewer->last_rect && viewer->last_page_result) {
+ viewer->last_rect = NULL;
+ viewer->last_page_result = NULL;
+ pdf_viewer_update((MimeViewer *)viewer,
+ FALSE,
+ gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page)));
+ }
+}
+
+static void pdf_viewer_scroll_to(PdfViewer *viewer, gfloat x, gfloat y)
+{
+ GtkAdjustment *vadj;
+ GtkAdjustment *hadj;
+ vadj = gtk_scrolled_window_get_vadjustment(
+ GTK_SCROLLED_WINDOW(viewer->scrollwin));
+
+ if (y < vadj->value) {
+ vadj->value = y;
+ }
+ else {
+ while(y > vadj->value + vadj->page_size) {
+ vadj->value += vadj->page_size;
+ }
+ }
+
+ hadj = gtk_scrolled_window_get_hadjustment(
+ GTK_SCROLLED_WINDOW(viewer->scrollwin));
+
+ if (x < hadj->value) {
+ hadj->value = x;
+ }
+ else {
+ while(x > hadj->value + hadj->page_size) {
+ hadj->value += hadj->page_size;
+ }
+ }
+
+ g_signal_emit_by_name(G_OBJECT(hadj), "value-changed", 0);
+ g_signal_emit_by_name(G_OBJECT(vadj), "value-changed", 0);
+}
+static void pdf_viewer_render_page(PopplerPage *page, GtkWidget *view, double width,
+ double height, double zoom, gint rotate)
+{
+ GdkPixbuf *pb;
+
+ debug_print("width: %f\n", width);
+ pb = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
+ FALSE, 8,
+ (int)(width * zoom),
+ (int)(height * zoom));
+
+ poppler_page_render_to_pixbuf(page, 0, 0,
+ (int)(width * zoom),
+ (int)(height * zoom),
+ zoom, rotate, pb);
+
+ gtk_image_set_from_pixbuf(GTK_IMAGE(view), pb);
+ g_object_unref(G_OBJECT(pb));
+}
+static void pdf_viewer_render_selection(PdfViewer *viewer, PopplerRectangle *rect, PageResult *page_results)
+{
+ gint selw, selh;
+ double width_points, height_points;
+ gint width, height;
+ GdkPixbuf *sel_pb, *page_pb;
+ gfloat x1, x2, y1, y2;
+
+
+ gint cur_page_num =
+ gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page));
+
+ viewer->last_match = viewer->res_cnt;
+
+ viewer->last_rect = NULL;
+ viewer->last_page_result = NULL;
+ if (cur_page_num != page_results->page_num) {
+ /* we changed page. update the view */
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->cur_page),
+ (gdouble) page_results->page_num);
+ }
+
+ viewer->last_rect = rect;
+ viewer->last_page_result = page_results;
+
+ GTK_EVENTS_FLUSH();
+
+ poppler_page_get_size(POPPLER_PAGE(viewer->pdf_page), &width_points, &height_points);
+ width = (int)((width_points * viewer->zoom) + 0.5);
+ height = (int)((height_points * viewer->zoom) + 0.5);
+
+ if (viewer->rotate == 90) {
+ x1 = MIN(rect->y1,rect->y2) * viewer->zoom;
+ x2 = MAX(rect->y1,rect->y2) * viewer->zoom;
+ y1 = MAX(rect->x1,rect->x2) * viewer->zoom;
+ y2 = MIN(rect->x1,rect->x2) * viewer->zoom;
+ selw = (x2 - x1);
+ selh = (y1 - y2);
+
+ } else if (viewer->rotate == 180) {
+ x1 = width - rect->x2 * viewer->zoom;
+ x2 = width - rect->x1 * viewer->zoom;
+ y1 = height - rect->y2 * viewer->zoom;
+ y2 = height - rect->y1 * viewer->zoom;
+ selw = (x2 - x1);
+ selh = (y2 - y1);
+ y1 = height - y1;
+ y2 = height - y2;
+
+ } else if (viewer->rotate == 270) {
+ x1 = height - MAX(rect->y1,rect->y2) * viewer->zoom;
+ x2 = height - MIN(rect->y1,rect->y2) * viewer->zoom;
+ y1 = width - MIN(rect->x1,rect->x2) * viewer->zoom;
+ y2 = width - MAX(rect->x1,rect->x2) * viewer->zoom;
+ selw = (x2 - x1);
+ selh = (y1 - y2);
+ } else {
+ x1 = rect->x1 * viewer->zoom;
+ x2 = rect->x2 * viewer->zoom;
+ y1 = rect->y1 * viewer->zoom;
+ y2 = rect->y2 * viewer->zoom;
+ selw = (x2 - x1);
+ selh = (y2 - y1);
+ y1 = height - y1;
+ y2 = height - y2;
+ }
+
+ sel_pb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
+ selw, selh);
+
+ gdk_pixbuf_fill(sel_pb, SELECTION_COLOR);
+
+ page_pb = gtk_image_get_pixbuf(GTK_IMAGE(viewer->pdf_view));
+
+ page_pb = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
+ FALSE, 8,
+ (int)(viewer->width * viewer->zoom),
+ (int)(viewer->height * viewer->zoom));
+
+ poppler_page_render_to_pixbuf(viewer->pdf_page,
+ 0,
+ 0,
+ (int)(viewer->width * viewer->zoom),
+ (int)(viewer->height * viewer->zoom),
+ viewer->zoom,
+ viewer->rotate,
+ page_pb);
+
+ gdk_pixbuf_composite(sel_pb, page_pb,
+ x1, y2, selw, selh, 0, 0,
+ viewer->zoom, viewer->zoom,
+ GDK_INTERP_BILINEAR, ALPHA_CHANNEL);
+
+ gtk_image_set_from_pixbuf(GTK_IMAGE(viewer->pdf_view), page_pb);
+
+ pdf_viewer_scroll_to(viewer, MIN(x1,x2), MIN(y1,y2));
+
+ g_object_unref(G_OBJECT(sel_pb));
+ g_object_unref(G_OBJECT(page_pb));
+
+}
+
+static gboolean pdf_viewer_text_search(MimeViewer *_viewer, gboolean backward,
+ const gchar *str, gboolean case_sens)
+{
+ PdfViewer *viewer = (PdfViewer *)_viewer;
+ GList *all_pages_results, *cur_page_results;
+ viewer->res_cnt = 0;
+
+ debug_print("pdf_viewer_text_search: %s\n", str);
+ main_window_cursor_wait(mainwindow_get_mainwindow());
+
+ if (viewer->last_search && strcmp(str, viewer->last_search)) {
+ search_matches_free(viewer);
+ viewer->last_match = -1;
+ viewer->num_matches = 0;
+ } else if (!viewer->last_search) {
+ viewer->last_match = -1;
+ viewer->num_matches = 0;
+ }
+ /* It's a new search, build list of matches
+ * across all pages */
+ if (viewer->last_match == -1) {
+ gint i;
+
+ for(i = 1; i <= viewer->num_pages; i++) {
+
+ PopplerPage *pdf_page = poppler_document_get_page(viewer->pdf_doc, i - 1);
+ viewer->page_results = poppler_page_find_text(pdf_page, str);
+
+ if (viewer->page_results != NULL) {
+ debug_print("page_results %p\n", viewer->page_results);
+ /* store results for this page */
+ gint num_res = 0;
+ PageResult *res = g_new0(PageResult, 1);
+ res->results = viewer->page_results;
+ res->page_num = i;
+ /* found text, prepend this page(faster than append) */
+ viewer->text_found = g_list_prepend(viewer->text_found, res);
+ num_res = g_list_length(viewer->page_results);
+ debug_print("%d results on page %d\n", num_res, i);
+ viewer->num_matches += num_res;
+ }
+ g_object_unref(G_OBJECT(pdf_page));
+ }
+
+ if (viewer->text_found == NULL) {
+ main_window_cursor_normal(mainwindow_get_mainwindow());
+ return FALSE;
+ }
+ /* put back the list in the correct order */
+ viewer->text_found = g_list_reverse(viewer->text_found);
+ }
+
+ if (!viewer->text_found) {
+ main_window_cursor_normal(mainwindow_get_mainwindow());
+ return FALSE;
+ } else {
+ viewer->last_search = g_strdup(str);
+ }
+
+ if (backward) {
+ /* if backward, we have to initialize stuff to search
+ * from the end */
+ viewer->res_cnt = viewer->num_matches-1;
+ if (viewer->last_match == -1) {
+ viewer->last_match = viewer->num_matches+1;
+ }
+ all_pages_results = g_list_last(viewer->text_found);
+ }
+ else {
+ all_pages_results = viewer->text_found;
+ }
+
+ for(; all_pages_results; all_pages_results = (backward?all_pages_results->prev:all_pages_results->next)) {
+
+ PageResult * page_results = (PageResult *)all_pages_results->data;
+
+ if (backward) {
+ cur_page_results = g_list_last(page_results->results);
+ }
+ else {
+ cur_page_results = page_results->results;
+ }
+
+ for(; cur_page_results; cur_page_results = (backward?cur_page_results->prev:cur_page_results->next)) {
+
+ gboolean valid = FALSE;
+ /* first valid result is the last+1 if searching
+ * forward, last-1 if searching backward */
+ if (backward) {
+ valid = (viewer->res_cnt < viewer->last_match);
+ }
+ else {
+ valid = (viewer->res_cnt > viewer->last_match);
+ }
+ if (valid) {
+ pdf_viewer_render_selection(viewer,
+ (PopplerRectangle *)cur_page_results->data,
+ page_results);
+ main_window_cursor_normal(mainwindow_get_mainwindow());
+ return TRUE;
+ }
+
+ if (backward) {
+ viewer->res_cnt--;
+ }
+ else {
+ viewer->res_cnt++;
+ }
+ }
+ }
+ main_window_cursor_normal(mainwindow_get_mainwindow());
+ search_matches_free(viewer);
+ return FALSE;
+}
+
+static void pdf_viewer_get_document_index(PdfViewer *viewer, PopplerIndexIter *index_iter, GtkTreeIter *parentiter)
+{
+ PopplerAction *action;
+ PopplerIndexIter *child;
+ GtkTreeIter childiter;
+
+ debug_print("get document index\n");
+ do {
+ gint page_num = 0;
+
+ action = poppler_index_iter_get_action(index_iter);
+
+ if (action->type != POPPLER_ACTION_GOTO_DEST) {
+ poppler_action_free(action);
+ continue;
+ }
+
+ if (action->goto_dest.dest->type == POPPLER_DEST_XYZ || action->goto_dest.dest->type == POPPLER_DEST_FITH) {
+ page_num = action->goto_dest.dest->page_num;
+ }
+#ifdef HAVE_POPPLER_DEST_NAMED
+ else if (action->goto_dest.dest->type == POPPLER_DEST_NAMED) {
+ PopplerDest *dest = poppler_document_find_dest(
+ viewer->pdf_doc, action->goto_dest.dest->named_dest);
+ if (dest->type != POPPLER_DEST_XYZ) {
+ g_warning("couldn't figure out link\n");
+ poppler_dest_free(dest);
+ continue;
+ }
+ page_num = dest->page_num;
+ poppler_dest_free(dest);
+ }
+#endif
+ else {
+#ifdef HAVE_POPPLER_DEST_NAMED
+ g_warning("unhandled link type %d\nplease contact developers\n", action->goto_dest.dest->type);
+#else
+ g_warning("unhandled link type %d\nplease upgrade libpoppler-glib to 0.5.4\n", action->goto_dest.dest->type);
+#endif
+ continue;
+ }
+ gtk_tree_store_append(GTK_TREE_STORE(viewer->index_model), &childiter, parentiter);
+ gtk_tree_store_set(GTK_TREE_STORE(viewer->index_model), &childiter,
+ INDEX_NAME, action->named.title,
+ INDEX_PAGE, page_num,
+ INDEX_TOP, action->goto_dest.dest->top,
+ -1);
+ poppler_action_free(action);
+ child = poppler_index_iter_get_child(index_iter);
+ if (child) {
+ pdf_viewer_get_document_index(viewer, child, &childiter);
+ poppler_index_iter_free(child);
+ }
+ }
+ while(poppler_index_iter_next(index_iter));
+}
+
+static void pdf_viewer_index_row_activated(GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ gpointer data)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model = gtk_tree_view_get_model(tree_view);
+ PdfViewer *viewer = (PdfViewer *)data;
+ gint page_num = 0;
+
+ debug_print("index_row_activated\n");
+ if (!gtk_tree_model_get_iter(model, &iter, path)) return;
+
+ gtk_tree_model_get(model, &iter,
+ INDEX_PAGE, &page_num,
+ -1);
+
+ if (page_num > 0) {
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->cur_page),(gdouble)page_num);
+ debug_print("Page num: %d\n", page_num);
+ }
+ GTK_EVENTS_FLUSH();
+}
+
+/** Disable the index button if the document doesn't have an index */
+static void pdf_viewer_set_index_button_sensitive(PdfViewer *viewer)
+{
+ viewer->pdf_index = poppler_index_iter_new(viewer->pdf_doc);
+ if (viewer->pdf_index) {
+ if (!gtk_widget_is_sensitive(viewer->doc_index)) {
+ gtk_widget_set_sensitive(viewer->doc_index, TRUE);
+ }
+ }
+ else {
+ gtk_widget_set_sensitive(viewer->doc_index, FALSE);
+ }
+
+ poppler_index_iter_free(viewer->pdf_index);
+ viewer->pdf_index = NULL;
+}
+
+static char * pdf_viewer_get_document_format_data(GTime utime)
+{
+ time_t time = (time_t) utime;
+ struct tm t;
+ char s[256];
+ const char *fmt_hack = "%c";
+ size_t len;
+
+ if (time == 0 || !localtime_r(&time, &t)) return NULL;
+
+ len = strftime(s, sizeof(s), fmt_hack, &t);
+
+ if (len == 0 || s[0] == '\0') return NULL;
+
+ return g_locale_to_utf8(s, -1, NULL, NULL, NULL);
+}
+
+#define ADD_TO_TABLE(LABEL, VALUE) \
+ label = gtk_label_new(LABEL); \
+ gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); \
+ gtk_misc_set_padding(GTK_MISC(label), 4, 0); \
+ gtk_table_attach(viewer->table_doc_info, label, 0, 1, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0); \
+ \
+ label = gtk_label_new(VALUE); \
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
+ gtk_misc_set_padding(GTK_MISC(label), 4, 0); \
+ gtk_table_attach(viewer->table_doc_info, label, 1, 2, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0); \
+ row++;
+
+
+static GtkTable * pdf_viewer_fill_info_table(PdfViewer *viewer)
+{
+ GtkWidget *label;
+ const gchar *title, *format, *author, *subject, *keywords, *creator, *producer;
+ gboolean linearized;
+ gchar *tmp;
+ gint row = 0;
+
+ GTime creation_date, mod_date;
+
+ PopplerPageLayout layout;
+ PopplerPageMode mode;
+ PopplerPermissions permissions;
+ PopplerViewerPreferences view_prefs;
+
+ title = format = author = subject = keywords = creator = producer = tmp = 0;
+
+ g_object_get(viewer->pdf_doc,
+ "title", &title,
+ "format", &format,
+ "author", &author,
+ "subject", &subject,
+ "keywords", &keywords,
+ "creation-date", &creation_date,
+ "permissions", &permissions,
+ "mod-date", &mod_date,
+ "creator", &creator,
+ "producer", &producer,
+ "linearized", &linearized,
+ "page-mode", &mode,
+ "page-layout", &layout,
+ "viewer-preferences", &view_prefs,
+ NULL);
+
+ viewer->table_doc_info = GTK_TABLE(gtk_table_new(13, 2, FALSE));
+
+ ADD_TO_TABLE(_("Filename:"), viewer->target_filename)
+ ADD_TO_TABLE(_("Size:"), to_human_readable(viewer->to_load->length))
+ ADD_TO_TABLE(NULL, NULL)
+ ADD_TO_TABLE(_("Title:"), title)
+ ADD_TO_TABLE(_("Subject:"), subject)
+ ADD_TO_TABLE(_("Author:"), author)
+ ADD_TO_TABLE(_("Keywords:"), keywords)
+ ADD_TO_TABLE(_("Creator:"), creator)
+ ADD_TO_TABLE(_("Producer:"), producer)
+
+ tmp = pdf_viewer_get_document_format_data(creation_date);
+ ADD_TO_TABLE(_("Created:"), tmp)
+ g_free(tmp);
+
+ tmp = pdf_viewer_get_document_format_data(mod_date);
+ ADD_TO_TABLE(_("Modified:"), tmp)
+ g_free(tmp);
+
+ ADD_TO_TABLE(_("Format:"), format)
+ if (linearized) {
+ ADD_TO_TABLE(_("Optimized:"), _("Yes"))
+ }
+ else {
+ ADD_TO_TABLE(_("Optimized:"), _("No"))
+ }
+ //ADD_TO_TABLE(_("Page Mode:"), pdf_viewer_get_document_info_mode(mode))
+ //ADD_TO_TABLE(_("Page Layout:"), pdf_viewer_get_document_info_layout(layout))
+
+ return(GtkTable *) viewer->table_doc_info;
+}
+#undef ADD_TO_TABLE
+
+static FileType pdf_viewer_mimepart_get_type(MimeInfo *partinfo)
+{
+ gchar *content_type = NULL;
+ FileType type = TYPE_UNKNOWN;
+ debug_print("mimepart_get_type\n");
+ if ((partinfo->type == MIMETYPE_APPLICATION) &&
+ (!g_ascii_strcasecmp(partinfo->subtype, "octet-stream"))) {
+
+ const gchar *filename;
+
+ filename = procmime_mimeinfo_get_parameter(partinfo, "filename");
+
+ if (filename == NULL)
+ filename = procmime_mimeinfo_get_parameter(partinfo, "name");
+ if (filename != NULL)
+ content_type = procmime_get_mime_type(filename);
+ }
+ else {
+ content_type = procmime_get_content_type_str(partinfo->type, partinfo->subtype);
+ }
+
+ if (content_type == NULL) type = TYPE_UNKNOWN;
+ else if (!strcmp(content_type, "application/pdf")) type = TYPE_PDF;
+ else if (!strcmp(content_type, "application/postscript")) type = TYPE_PS;
+ else type = TYPE_UNKNOWN;
+
+ g_free(content_type);
+ return type;
+}
+
+/* Callbacks */
+static void pdf_viewer_button_first_page_cb(GtkButton *button, PdfViewer *viewer)
+{
+
+ gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_HOME, 1);
+}
+
+static void pdf_viewer_button_prev_page_cb(GtkButton *button, PdfViewer *viewer)
+{
+
+ gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_STEP_BACKWARD, 1);
+}
+
+static void pdf_viewer_button_next_page_cb(GtkButton *button, PdfViewer *viewer)
+{
+
+ gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_STEP_FORWARD, 1);
+}
+
+static void pdf_viewer_button_last_page_cb(GtkButton *button, PdfViewer *viewer)
+{
+
+ gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_END, 1);
+}
+
+static void pdf_viewer_spin_change_page_cb(GtkSpinButton *button, PdfViewer *viewer)
+{
+ pdf_viewer_update((MimeViewer *)viewer,
+ FALSE,
+ gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page)));
+}
+
+static void pdf_viewer_button_zoom_in_cb(GtkButton *button, PdfViewer *viewer)
+{
+
+ gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->zoom_scroll), GTK_SPIN_STEP_FORWARD, ZOOM_FACTOR);
+}
+
+static void pdf_viewer_spin_zoom_scroll_cb(GtkSpinButton *button, PdfViewer *viewer)
+{
+ viewer->zoom = gtk_spin_button_get_value(GTK_SPIN_BUTTON(viewer->zoom_scroll));
+ pdf_viewer_update((MimeViewer *)viewer,
+ FALSE,
+ gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page)));
+}
+
+static void pdf_viewer_button_zoom_out_cb(GtkButton *button, PdfViewer *viewer)
+{
+
+ gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->zoom_scroll), GTK_SPIN_STEP_BACKWARD, ZOOM_FACTOR);
+
+}
+
+static void pdf_viewer_button_press_events_cb(GtkWidget *widget, GdkEventButton *event, PdfViewer *viewer)
+{
+ gchar *uri;
+ #ifdef HAVE_POPPLER_DEST_NAMED
+ PopplerDest *dest;
+ #endif
+ static GdkCursor *hand_cur = NULL;
+
+ if (!hand_cur) hand_cur = gdk_cursor_new(GDK_FLEUR);
+
+ /* Execute Poppler Links */
+ if (event->button == 1 && viewer->in_link) {
+ switch (viewer->link_action->type) {
+ case POPPLER_ACTION_UNKNOWN:
+ debug_print("action unknown\n");
+ break;
+ case POPPLER_ACTION_GOTO_DEST:
+ if (viewer->link_action->goto_dest.dest->type == POPPLER_DEST_XYZ ||
+ viewer->link_action->goto_dest.dest->type == POPPLER_DEST_FITH) {
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->cur_page),
+ (gdouble)viewer->link_action->goto_dest.dest->page_num);
+ }
+ #ifdef HAVE_POPPLER_DEST_NAMED
+ else if (viewer->link_action->goto_dest.dest->type == POPPLER_DEST_NAMED) {
+ dest = poppler_document_find_dest(
+ viewer->pdf_doc, viewer->link_action->goto_dest.dest->named_dest);
+ if (dest->type != POPPLER_DEST_XYZ) {
+ g_warning("couldn't figure out link\n");
+ poppler_dest_free(dest);
+ break;
+ }
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->cur_page),
+ (gdouble)dest->page_num);
+ poppler_dest_free(dest);
+ }
+ #endif
+ break;
+ case POPPLER_ACTION_GOTO_REMOTE:
+ #ifdef HAVE_POPPLER_DEST_NAMED
+ dest = poppler_document_find_dest(
+ viewer->pdf_doc, viewer->link_action->goto_remote.dest->named_dest);
+ if (dest->type != POPPLER_DEST_XYZ) {
+ g_warning ("couldn't figure out link\n");
+ poppler_dest_free(dest);
+ break;
+ }
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->cur_page),
+ (gdouble)dest->page_num);
+ poppler_dest_free(dest);
+ #endif
+ break;
+ case POPPLER_ACTION_LAUNCH:
+ debug_print("action launch not yet implemented\n");
+ break;
+ case POPPLER_ACTION_URI:
+ uri = g_strdup(viewer->link_action->uri.uri);
+ if (!g_ascii_strncasecmp(uri, "mailto:", 7))
+ compose_new(NULL, uri + 7, NULL);
+ else
+ open_uri(uri, prefs_common_get_uri_cmd());
+ g_free(uri);
+ break;
+ case POPPLER_ACTION_NAMED:
+ debug_print("action named not yet implemented\n");
+ break;
+ case POPPLER_ACTION_MOVIE:
+ debug_print("yoyoyo ;-) a movie?\n");
+ break;
+ }
+ if (((MimeViewer *)viewer)->mimeview &&
+ ((MimeViewer *)viewer)->mimeview->messageview &&
+ ((MimeViewer *)viewer)->mimeview->messageview->window &&
+ ((MimeViewer *)viewer)->mimeview->messageview->window->window)
+ gdk_window_set_cursor (((MimeViewer *)viewer)->mimeview->messageview->window->window, NULL);
+ else
+ gdk_window_set_cursor (mainwindow_get_mainwindow()->window->window, NULL);
+ }
+
+ /* Init document to be scrolled with left mouse click */
+ if (event->button == 1 && !viewer->in_link) {
+ viewer->pdf_view_scroll = TRUE;
+ if (((MimeViewer *)viewer)->mimeview &&
+ ((MimeViewer *)viewer)->mimeview->messageview &&
+ ((MimeViewer *)viewer)->mimeview->messageview->window &&
+ ((MimeViewer *)viewer)->mimeview->messageview->window->window)
+ gdk_window_set_cursor (((MimeViewer *)viewer)->mimeview->messageview->window->window, hand_cur);
+ else
+ gdk_window_set_cursor (mainwindow_get_mainwindow()->window->window, hand_cur);
+
+ viewer->last_x = event->x;
+ viewer->last_y = event->y;
+ viewer->last_dir_x = 0;
+ viewer->last_dir_y = 0;
+ }
+}
+/* Set the normal cursor*/
+static void pdf_viewer_mouse_scroll_destroy_cb(GtkWidget *widget, GdkEventButton *event, PdfViewer *viewer)
+{
+
+ if (event->button == 1) {
+ viewer->pdf_view_scroll = FALSE;
+ if (((MimeViewer *)viewer)->mimeview &&
+ ((MimeViewer *)viewer)->mimeview->messageview &&
+ ((MimeViewer *)viewer)->mimeview->messageview->window &&
+ ((MimeViewer *)viewer)->mimeview->messageview->window->window)
+ gdk_window_set_cursor (((MimeViewer *)viewer)->mimeview->messageview->window->window, NULL);
+ else
+ gdk_window_set_cursor (mainwindow_get_mainwindow()->window->window, NULL);
+ }
+}
+
+static void pdf_viewer_move_events_cb(GtkWidget *widget, GdkEventMotion *event, PdfViewer *viewer)
+{
+ /* Grab the document and scroll it with mouse */
+ if (viewer->pdf_view_scroll) {
+
+ viewer->pdf_view_vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(viewer->scrollwin));
+ viewer->pdf_view_hadj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(viewer->scrollwin));
+
+ if (event->x < viewer->last_x
+ && viewer->pdf_view_hadj->value < (viewer->pdf_view_hadj->upper - viewer->pdf_view_hadj->page_size)) {
+ if (viewer->last_dir_x == -1) {
+ viewer->pdf_view_hadj->value += viewer->last_x - event->x;
+ g_signal_emit_by_name(G_OBJECT(viewer->pdf_view_hadj),
+ "value_changed", 0);
+ }
+ viewer->last_dir_x = -1;
+ }
+ else if (event->x > viewer->last_x
+ && viewer->pdf_view_hadj->value > 0.0) {
+ if (viewer->last_dir_x == +1) {
+ viewer->pdf_view_hadj->value += viewer->last_x - event->x;
+ g_signal_emit_by_name(G_OBJECT(viewer->pdf_view_hadj),
+ "value_changed", 0);
+ }
+ viewer->last_dir_x = +1;
+ }
+
+ if (event->y < viewer->last_y
+ && viewer->pdf_view_vadj->value < (viewer->pdf_view_vadj->upper - viewer->pdf_view_vadj->page_size)) {
+ if (viewer->last_dir_y == -1) {
+ viewer->pdf_view_vadj->value += viewer->last_y - event->y;
+ g_signal_emit_by_name(G_OBJECT(viewer->pdf_view_vadj),
+ "value_changed", 0);
+ }
+ viewer->last_dir_y = -1;
+ }
+ else if (event->y > viewer->last_y
+ && viewer->pdf_view_vadj->value > 0.0) {
+ if (viewer->last_dir_y == +1) {
+ viewer->pdf_view_vadj->value += viewer->last_y - event->y;
+ g_signal_emit_by_name(G_OBJECT(viewer->pdf_view_vadj),
+ "value_changed", 0);
+ }
+ viewer->last_dir_y = +1;
+ }
+ viewer->last_x = event->x;
+ viewer->last_y = event->y;
+ GTK_EVENTS_FLUSH();
+ }
+ else {
+ /* Link Mapping */
+ static GList *l;
+ static GdkCursor *link_cur = NULL;
+ static GtkRequisition size;
+ static gdouble x,y, x1, y1, x2, y2;
+ gboolean ccur;
+
+ viewer->pdf_view_vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(viewer->scrollwin));
+ viewer->pdf_view_hadj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(viewer->scrollwin));
+
+ if (!link_cur) link_cur = gdk_cursor_new(GDK_HAND2);
+
+ ccur = FALSE;
+ viewer->in_link = FALSE;
+ for (l = viewer->link_map; l; l = g_list_next (l)) {
+ PopplerLinkMapping *lmapping;
+ lmapping = (PopplerLinkMapping *)l->data;
+
+ x1 = lmapping->area.x1;
+ y1 = lmapping->area.y1;
+ x2 = lmapping->area.x2;
+ y2 = lmapping->area.y2;
+ gtk_widget_size_request(viewer->pdf_view, &size);
+
+ switch (viewer->rotate) {
+ case 0:
+ case 360:
+ if (size.width != viewer->pdf_view_hadj->upper)
+ x = (event->x - (viewer->pdf_view_hadj->upper - size.width) / 2) / viewer->zoom;
+ else
+ x = event->x / viewer->zoom;
+
+ y = (viewer->pdf_view_vadj->upper - event->y) / viewer->zoom;
+ break;
+ case 90:
+ if (size.width != viewer->pdf_view_hadj->upper)
+ y = (event->x - (viewer->pdf_view_hadj->upper - size.width) / 2) / viewer->zoom;
+ else
+ y = event->x / viewer->zoom;
+
+ x = (event->y) / viewer->zoom;
+ break;
+ case 180:
+ if (size.width != viewer->pdf_view_hadj->upper)
+ x = ((viewer->pdf_view_hadj->upper - event->x) - ((viewer->pdf_view_hadj->upper - size.width) / 2)) / viewer->zoom;
+ else
+ x = ((viewer->pdf_view_hadj->upper - event->x) - (viewer->pdf_view_hadj->upper - size.width)) / viewer->zoom;
+
+ y = (event->y) / viewer->zoom;
+ break;
+ case 270:
+ if (size.width != viewer->pdf_view_hadj->upper)
+ y = ((viewer->pdf_view_hadj->upper - event->x) - ((viewer->pdf_view_hadj->upper - size.width) / 2)) / viewer->zoom;
+ else
+ y = ((viewer->pdf_view_hadj->upper - event->x) - (viewer->pdf_view_hadj->upper - size.width)) / viewer->zoom;
+
+ x = (viewer->pdf_view_vadj->upper - event->y) / viewer->zoom;
+ break;
+ }
+
+ if ( (x > x1 && x < x2) && (y > y1 && y < y2) ) {
+ viewer->in_link = TRUE;
+ if (((MimeViewer *)viewer)->mimeview &&
+ ((MimeViewer *)viewer)->mimeview->messageview &&
+ ((MimeViewer *)viewer)->mimeview->messageview->window &&
+ ((MimeViewer *)viewer)->mimeview->messageview->window->window)
+ gdk_window_set_cursor (((MimeViewer *)viewer)->mimeview->messageview->window->window, link_cur);
+ else
+ gdk_window_set_cursor (mainwindow_get_mainwindow()->window->window, link_cur);
+
+ viewer->link_action = lmapping->action;
+ ccur = TRUE;
+ }
+ if (!ccur) {
+ if (((MimeViewer *)viewer)->mimeview &&
+ ((MimeViewer *)viewer)->mimeview->messageview &&
+ ((MimeViewer *)viewer)->mimeview->messageview->window &&
+ ((MimeViewer *)viewer)->mimeview->messageview->window->window)
+ gdk_window_set_cursor (((MimeViewer *)viewer)->mimeview->messageview->window->window, NULL);
+ else
+ gdk_window_set_cursor (mainwindow_get_mainwindow()->window->window, NULL);
+ }
+ }
+ g_free(l);
+ }
+}
+static gboolean pdf_viewer_scroll_cb(GtkWidget *widget, GdkEventScroll *event,
+ PdfViewer *viewer)
+{
+ GtkAdjustment *adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(viewer->scrollwin));
+ static gboolean in_scroll_cb = FALSE;
+ gboolean handled = FALSE;
+ gint cur_p = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page));
+
+ if (in_scroll_cb)
+ return FALSE;
+
+ in_scroll_cb = TRUE;
+
+ if (event->direction == GDK_SCROLL_UP &&
+ adj->value == adj->lower &&
+ cur_p > 1) {
+ gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_STEP_BACKWARD, 1);
+ adj->value = adj->upper - adj->page_size;
+ handled = TRUE;
+ } else if (event->direction == GDK_SCROLL_DOWN &&
+ adj->value + adj->page_size == adj->upper &&
+ cur_p < viewer->num_pages) {
+ gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_STEP_FORWARD, 1);
+ adj->value = 0.0;
+ handled = TRUE;
+ }
+ in_scroll_cb = FALSE;
+ return handled;
+}
+
+static void pdf_viewer_button_zoom_fit_cb(GtkButton *button, PdfViewer *viewer)
+{
+ GtkAllocation *allocation;
+ double xratio, yratio;
+ allocation = &(viewer->scrollwin->allocation);
+ debug_print("width: %d\n", allocation->width);
+ debug_print("height: %d\n", allocation->height);
+ xratio = allocation->width / viewer->width;
+ yratio = allocation->height / viewer->height;
+
+ if (xratio >= yratio) {
+ viewer->zoom = yratio;
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->zoom_scroll),viewer->zoom);
+ }
+ else {
+ viewer->zoom = xratio;
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->zoom_scroll),viewer->zoom);
+ }
+}
+
+static void pdf_viewer_button_zoom_width_cb(GtkButton *button, PdfViewer *viewer)
+{
+ GtkAllocation *allocation;
+ double xratio;
+ allocation = &(viewer->scrollwin->allocation);
+ debug_print("width: %d\n", allocation->width);
+ xratio = allocation->width / viewer->width;
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->zoom_scroll), xratio);
+}
+
+static void pdf_viewer_button_rotate_right_cb(GtkButton *button, PdfViewer *viewer)
+{
+ if (viewer->rotate == 360) {
+ viewer->rotate = 0;
+ }
+
+ viewer->rotate += (gint) ROTATION;
+ pdf_viewer_update((MimeViewer *)viewer, FALSE,
+ gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page)));
+}
+
+static void pdf_viewer_button_rotate_left_cb(GtkButton *button, PdfViewer *viewer)
+{
+ if (viewer->rotate == 0) {
+ viewer->rotate = 360;
+ }
+
+ viewer->rotate = abs(viewer->rotate -(gint) ROTATION);
+ pdf_viewer_update((MimeViewer *)viewer, FALSE,
+ gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page)));
+}
+
+/* Show/Hide the index pane */
+static void pdf_viewer_show_document_index_cb(GtkButton *button, PdfViewer *viewer)
+{
+ if (!viewer->pdf_index) {
+ viewer->pdf_index = poppler_index_iter_new(viewer->pdf_doc);
+ }
+
+ gtk_tree_store_clear(GTK_TREE_STORE(viewer->index_model));
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(viewer->doc_index))) {
+ pdf_viewer_get_document_index(viewer,(PopplerIndexIter *) viewer->pdf_index, NULL);
+ gtk_widget_show(GTK_WIDGET(viewer->frame_index));
+ }
+ else {
+ pdf_viewer_hide_index_pane(viewer);
+ }
+
+}
+
+static void pdf_viewer_button_document_info_cb(GtkButton *button, PdfViewer *viewer)
+{
+ gchar *buf;
+ buf = g_strdup_printf(_("PDF Viewer Plugin"));
+
+ alertpanel_full(buf, NULL, GTK_STOCK_CLOSE, NULL, NULL,
+ FALSE,(GtkWidget *) pdf_viewer_fill_info_table(viewer),
+ ALERT_NOTICE,
+ G_ALERTDEFAULT);
+ g_free(buf);
+}
+
+/*
+static const char * poppler_get_document_info_mode(PopplerPageMode mode)
+{
+ GEnumValue *enum_value;
+
+ enum_value = g_enum_get_value((GEnumClass *) g_type_class_peek(POPPLER_TYPE_PAGE_MODE), mode);
+ return(gchar *) enum_value->value_name;
+}
+static const char * poppler_get_document_info_layout(PopplerPageLayout layout)
+{
+
+ GEnumValue *enum_value;
+
+ enum_value = g_enum_get_value((GEnumClass *) g_type_class_peek(POPPLER_TYPE_PAGE_LAYOUT), layout);
+ return(gchar *) enum_value->value_name;
+}
+*/
+static void pdf_viewer_show_controls(PdfViewer *viewer, gboolean show)
+{
+ if (show) {
+ gtk_widget_show(viewer->first_page);
+ gtk_widget_show(viewer->cur_page);
+ gtk_widget_show(viewer->prev_page);
+ gtk_widget_show(viewer->next_page);
+ gtk_widget_show(viewer->last_page);
+ gtk_widget_show(viewer->zoom_in);
+ gtk_widget_show(viewer->zoom_out);
+ gtk_widget_show(viewer->zoom_fit);
+ gtk_widget_show(viewer->zoom_width);
+ gtk_widget_show(viewer->zoom_scroll);
+ gtk_widget_show(viewer->widgets_table);
+ gtk_widget_show(viewer->rotate_right);
+ gtk_widget_show(viewer->rotate_left);
+ gtk_widget_show(viewer->doc_info);
+ gtk_widget_show(viewer->doc_index);
+ } else {
+ gtk_widget_hide(viewer->first_page);
+ gtk_widget_hide(viewer->cur_page);
+ gtk_widget_hide(viewer->prev_page);
+ gtk_widget_hide(viewer->next_page);
+ gtk_widget_hide(viewer->last_page);
+ gtk_widget_hide(viewer->zoom_in);
+ gtk_widget_hide(viewer->zoom_out);
+ gtk_widget_hide(viewer->zoom_fit);
+ gtk_widget_hide(viewer->zoom_width);
+ gtk_widget_hide(viewer->widgets_table);
+ gtk_widget_hide(viewer->rotate_right);
+ gtk_widget_hide(viewer->rotate_left);
+ gtk_widget_hide(viewer->doc_info);
+ gtk_widget_hide(viewer->doc_index);
+ gtk_widget_hide(viewer->zoom_scroll);
+ }
+}
+/** Render the current page, page_num on the viewer */
+static void pdf_viewer_update(MimeViewer *_viewer, gboolean reload_file, int page_num)
+{
+
+ PdfViewer *viewer = (PdfViewer *) _viewer;
+ GError *error;
+ gchar *tmpfile = NULL;
+ gchar *tmp;
+
+ debug_print("pdf_viewer_update\n");
+
+ error = NULL;
+ if (reload_file) {
+ if (viewer->pdf_doc) {
+ g_object_unref(G_OBJECT(viewer->pdf_doc));
+ viewer->pdf_doc = NULL;
+ }
+
+ if (pdf_viewer_mimepart_get_type(viewer->to_load) == TYPE_PS) {
+ stock_pixbuf_gdk(viewer->hbox,
+ STOCK_PIXMAP_MIME_PS,
+ &viewer->icon_pixbuf);
+ gtk_image_set_from_pixbuf(GTK_IMAGE(viewer->icon_type),
+ viewer->icon_pixbuf);
+ }
+ else if (pdf_viewer_mimepart_get_type(viewer->to_load) == TYPE_PDF) {
+ stock_pixbuf_gdk(viewer->hbox,
+ STOCK_PIXMAP_MIME_PDF,
+ &viewer->icon_pixbuf);
+ gtk_image_set_from_pixbuf(GTK_IMAGE(viewer->icon_type),
+ viewer->icon_pixbuf);
+ }
+ else {
+ stock_pixbuf_gdk(viewer->hbox,
+ STOCK_PIXMAP_MIME_APPLICATION,
+ &viewer->icon_pixbuf);
+ gtk_image_set_from_pixbuf(GTK_IMAGE(viewer->icon_type),
+ viewer->icon_pixbuf);
+ }
+
+ gtk_label_set_text(GTK_LABEL(viewer->doc_label), _("Loading..."));
+ pdf_viewer_show_controls(viewer, FALSE);
+ main_window_cursor_wait(mainwindow_get_mainwindow());
+
+ GTK_EVENTS_FLUSH();
+
+ if (pdf_viewer_mimepart_get_type(viewer->to_load) == TYPE_PS) {
+ gchar *cmdline = NULL, *tmp = NULL, *gspath = NULL;
+ gint result = 0;
+
+ if ((gspath = g_find_program_in_path("gs")) != NULL) {
+ g_free(gspath);
+ /* convert postscript to pdf */
+ tmpfile = get_tmp_file();
+ cmdline = g_strdup_printf(
+ "gs -dSAFER -dCompatibilityLevel=1.2 -q -dNOPAUSE -dBATCH "
+ "-sDEVICE=pdfwrite -sOutputFile=%s -c .setpdfwrite -f \"%s\"",
+ tmpfile, viewer->filename);
+ result = execute_command_line(cmdline, FALSE);
+ if (result == 0) {
+ tmp = g_filename_to_uri(tmpfile, NULL, NULL);
+ viewer->pdf_doc = poppler_document_new_from_file( tmp, NULL, &error);
+ g_free(tmp);
+ }
+ else {
+ g_warning("gs conversion failed: %s returned %d\n", cmdline, result);
+ tmp = g_strdup_printf("gs: err %d", result);
+ alertpanel_warning("%s", tmp);
+ g_free(tmp);
+ }
+
+ g_free(cmdline);
+ claws_unlink(tmpfile);
+ g_free(tmpfile);
+ }
+ else {
+ g_warning("gs conversion disabled: gs binary was not found\n");
+ alertpanel_warning("PostScript view disabled: required gs program not found");
+ result = 1;
+
+ }
+ if (result != 0) {
+ main_window_cursor_normal(mainwindow_get_mainwindow());
+ return;
+ }
+ }
+ else {
+ viewer->pdf_doc = poppler_document_new_from_file( viewer->fsname, NULL, &error);
+ }
+
+ viewer->num_pages = poppler_document_get_n_pages(viewer->pdf_doc);
+
+ g_signal_handlers_block_by_func(G_OBJECT(viewer->cur_page), pdf_viewer_spin_change_page_cb,(gpointer *)viewer);
+ gtk_spin_button_set_range(GTK_SPIN_BUTTON(viewer->cur_page),
+ 1,
+ (gdouble)viewer->num_pages );
+
+ g_signal_handlers_unblock_by_func(G_OBJECT(viewer->cur_page), pdf_viewer_spin_change_page_cb,(gpointer *)viewer);
+ gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_HOME, 1);
+ tmp = g_strdup_printf(_("%s Document"),pdf_viewer_mimepart_get_type(viewer->to_load) == TYPE_PDF ? "PDF":"Postscript");
+ CLAWS_SET_TIP(
+ GTK_WIDGET(viewer->icon_type_ebox),
+ tmp);
+ g_free(tmp);
+
+ tmp = g_strdup_printf(_("of %d"), viewer->num_pages);
+ gtk_label_set_text(GTK_LABEL(viewer->doc_label), tmp);
+ g_free(tmp);
+
+ pdf_viewer_show_controls(viewer, TRUE);
+ main_window_cursor_normal(mainwindow_get_mainwindow());
+ }
+ if (viewer->pdf_doc == NULL) {
+ strretchomp(error->message);
+ stock_pixbuf_gdk(viewer->hbox,
+ STOCK_PIXMAP_MIME_APPLICATION,
+ &viewer->icon_pixbuf);
+
+ gtk_image_set_from_pixbuf(GTK_IMAGE(viewer->icon_type), viewer->icon_pixbuf);
+ alertpanel_error("%s", error->message);
+
+ pdf_viewer_show_controls(viewer, FALSE);
+ g_error_free(error);
+ return;
+ }
+
+ if (page_num == 1) {
+ gtk_widget_set_sensitive(viewer->first_page, FALSE);
+ gtk_widget_set_sensitive(viewer->prev_page, FALSE);
+ }
+ else {
+ gtk_widget_set_sensitive(viewer->first_page, TRUE);
+ gtk_widget_set_sensitive(viewer->prev_page, TRUE);
+ }
+
+ if (page_num == viewer->num_pages) {
+ gtk_widget_set_sensitive(viewer->last_page, FALSE);
+ gtk_widget_set_sensitive(viewer->next_page, FALSE);
+ }
+ else {
+ gtk_widget_set_sensitive(viewer->last_page, TRUE);
+ gtk_widget_set_sensitive(viewer->next_page, TRUE);
+ }
+
+ /* check for the index if exists */
+ pdf_viewer_set_index_button_sensitive((PdfViewer *) viewer);
+
+ if (page_num > 0 && page_num <= viewer->num_pages) {
+
+ GTK_EVENTS_FLUSH();
+
+ if (viewer->pdf_page) {
+ g_object_unref(G_OBJECT(viewer->pdf_page));
+ }
+
+ viewer->pdf_page = poppler_document_get_page(viewer->pdf_doc, page_num - 1);
+
+ if (viewer->pdf_page == NULL) {
+ g_warning("Page not found\n");
+ return;
+ }
+
+ if (viewer->rotate == 90 || viewer->rotate == 270) {
+ poppler_page_get_size(viewer->pdf_page, &viewer->height, &viewer->width);
+ }
+ else {
+ poppler_page_get_size(viewer->pdf_page, &viewer->width, &viewer->height);
+ }
+
+ if (viewer->last_rect && viewer->last_page_result &&
+ viewer->last_page_result->page_num == page_num) {
+ pdf_viewer_render_selection(viewer, viewer->last_rect, viewer->last_page_result);
+ }
+ else {
+ pdf_viewer_render_page(viewer->pdf_page, viewer->pdf_view, viewer->width,
+ viewer->height, viewer->zoom, viewer->rotate);
+
+ }
+
+ /* Get Links Mapping */
+ if (viewer->link_map) {
+ poppler_page_free_link_mapping(viewer->link_map);
+ }
+ viewer->link_map = poppler_page_get_link_mapping(viewer->pdf_page);
+
+ }
+}
+
+
+static void pdf_viewer_show_mimepart(MimeViewer *_viewer, const gchar *infile,
+ MimeInfo *partinfo)
+{
+ PdfViewer *viewer = (PdfViewer *) _viewer;
+ gchar buf[4096];
+ const gchar *charset = NULL;
+ MessageView *messageview = ((MimeViewer *)viewer)->mimeview
+ ?((MimeViewer *)viewer)->mimeview->messageview
+ : NULL;
+
+ viewer->rotate = 0;
+ viewer->to_load = partinfo;
+
+ memset(buf, 0, sizeof(buf));
+ messageview->updating = TRUE;
+ debug_print("pdf_viewer_show_mimepart\n");
+
+ if (viewer->filename != NULL) {
+ claws_unlink(viewer->filename);
+ g_free(viewer->filename);
+ viewer->filename = NULL;
+ }
+
+ viewer->mimeinfo = NULL;
+
+ if (partinfo) {
+ viewer->target_filename = procmime_get_part_file_name(partinfo);
+ viewer->filename = procmime_get_tmp_file_name(partinfo);
+ viewer->fsname = g_filename_to_uri(viewer->filename, NULL, NULL);
+ }
+
+ if (partinfo && !(procmime_get_part(viewer->filename, partinfo) < 0)) {
+
+ if (_viewer && _viewer->mimeview &&
+ _viewer->mimeview->messageview->forced_charset) {
+ charset = _viewer->mimeview->messageview->forced_charset;
+ }
+ else {
+ charset = procmime_mimeinfo_get_parameter(partinfo, "charset");
+ }
+ if (charset == NULL) {
+ charset = conv_get_locale_charset_str();
+ }
+
+ debug_print("using charset %s\n", charset);
+
+ viewer->mimeinfo = partinfo;
+ }
+
+ pdf_viewer_update((MimeViewer *)viewer, TRUE, 1);
+
+ messageview->updating = FALSE;
+}
+
+static void pdf_viewer_clear(MimeViewer *_viewer)
+{
+ PdfViewer *viewer = (PdfViewer *) _viewer;
+ GtkAdjustment *vadj;
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(viewer->doc_index), FALSE);
+ gtk_widget_hide(viewer->frame_index);
+
+ debug_print("pdf_viewer_clear\n");
+ viewer->to_load = NULL;
+
+ if (viewer->pdf_doc) {
+ g_object_unref(G_OBJECT(viewer->pdf_doc));
+ viewer->pdf_doc = NULL;
+ }
+
+ vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(viewer->scrollwin));
+ vadj->value = 0.0;
+ g_signal_emit_by_name(G_OBJECT(vadj), "value-changed", 0);
+ vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(viewer->scrollwin_index));
+ vadj->value = 0.0;
+ g_signal_emit_by_name(G_OBJECT(vadj), "value-changed", 0);
+ gtk_tree_store_clear(GTK_TREE_STORE(viewer->index_model));
+ gtk_image_set_from_pixbuf(GTK_IMAGE(viewer->pdf_view), NULL);
+}
+
+static void pdf_viewer_destroy(MimeViewer *_viewer)
+{
+ PdfViewer *viewer = (PdfViewer *) _viewer;
+
+ debug_print("pdf_viewer_destroy\n");
+
+ if (viewer->pdf_index) poppler_index_iter_free(viewer->pdf_index);
+
+ poppler_page_free_link_mapping (viewer->link_map);
+ g_object_unref(GTK_WIDGET(viewer->vbox));
+ g_object_unref(GTK_WIDGET(viewer->pdf_view));
+ g_object_unref(GTK_WIDGET(viewer->doc_index_pane));
+ g_object_unref(GTK_WIDGET(viewer->scrollwin));
+ g_object_unref(GTK_WIDGET(viewer->scrollwin_index));
+ claws_unlink(viewer->filename);
+ g_free(viewer->filename);
+ g_free(viewer);
+}
+
+static gboolean pdf_viewer_scroll_page(MimeViewer *_viewer, gboolean up)
+{
+ PdfViewer *viewer = (PdfViewer *)_viewer;
+ GtkAdjustment *vadj = gtk_scrolled_window_get_vadjustment(
+ GTK_SCROLLED_WINDOW(viewer->scrollwin));
+
+ gint cur_p = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page));
+
+ if (viewer->pdf_view == NULL) return FALSE;
+
+ if (!gtkutils_scroll_page(GTK_WIDGET(viewer->pdf_view), vadj, up)) {
+ if (!up && cur_p != viewer->num_pages) {
+ gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_STEP_FORWARD, 1);
+ vadj = gtk_scrolled_window_get_vadjustment(
+ GTK_SCROLLED_WINDOW(viewer->scrollwin));
+ vadj->value = 0.0;
+ g_signal_emit_by_name(G_OBJECT(vadj), "value-changed", 0);
+ return TRUE;
+ }
+ else if (up && cur_p != 1) {
+ gtk_spin_button_spin(GTK_SPIN_BUTTON(viewer->cur_page), GTK_SPIN_STEP_BACKWARD, 1);
+ vadj = gtk_scrolled_window_get_vadjustment(
+ GTK_SCROLLED_WINDOW(viewer->scrollwin));
+ vadj->value = vadj->upper - vadj->page_size;
+ g_signal_emit_by_name(G_OBJECT(vadj), "value-changed", 0);
+ return TRUE;
+ }
+ return FALSE;
+ }
+ else return TRUE;
+}
+
+static void pdf_viewer_scroll_one_line(MimeViewer *_viewer, gboolean up)
+{
+ PdfViewer *viewer = (PdfViewer *)_viewer;
+ GtkAdjustment *vadj = gtk_scrolled_window_get_vadjustment(
+ GTK_SCROLLED_WINDOW(viewer->scrollwin));
+ gint cur_p;
+ cur_p = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(viewer->cur_page));
+
+ if (viewer->pdf_view == NULL) return;
+ debug_print("up: %d\n", up);
+ if (vadj->value <(vadj->upper - vadj->page_size)) {
+ gtkutils_scroll_one_line(GTK_WIDGET(viewer->pdf_view), vadj, up);
+ }
+ else {
+ if (cur_p != viewer->num_pages) {
+ pdf_viewer_scroll_page((MimeViewer *)viewer, up);
+ }
+ }
+
+}
+
+#define BUTTON_H_PADDING 3
+#define ADD_BUTTON_TO_TABLE(widget, stock_image) \
+ widget = gtk_button_new(); \
+ img = gtk_image_new_from_stock(stock_image, GTK_ICON_SIZE_MENU); \
+ gtk_button_set_image(GTK_BUTTON(widget), img); \
+ gtk_table_attach(GTK_TABLE(viewer->widgets_table), GTK_WIDGET(widget), \
+ col, col+1, 0, 1, 0, 0, BUTTON_H_PADDING, 0); \
+ col++;
+
+#define ADD_SEP_TO_TABLE \
+ sep = gtk_label_new(""); \
+ gtk_table_attach(GTK_TABLE(viewer->widgets_table), GTK_WIDGET(sep), \
+ col, col+1, 0, 1, 0, 0, 0, 0); \
+ gtk_table_set_col_spacing(GTK_TABLE(viewer->widgets_table), col, 3*BUTTON_H_PADDING); \
+ col++;
+
+#if GTK_CHECK_VERSION(2,10,0) && POPPLER_HAS_CAIRO && !USE_LIBGNOMEPRINT
+static PangoContext *pdf_viewer_get_pango_context(gpointer data)
+{
+ return NULL;
+}
+
+static gpointer pdf_viewer_get_data_to_print(gpointer data, gint sel_start, gint sel_end)
+{
+ return NULL; /* we don't need it */
+}
+
+static void pdf_viewer_cb_begin_print(GtkPrintOperation *op, GtkPrintContext *context,
+ gpointer user_data)
+{
+ PrintData *print_data;
+ PopplerDocument *pdf_doc;
+ gint n_pages = 0;
+ print_data = (PrintData*) user_data;
+ pdf_doc = (PopplerDocument *)printing_get_renderer_data(print_data);
+
+ debug_print("Preparing print job...\n");
+
+ n_pages = poppler_document_get_n_pages(pdf_doc);
+ printing_set_n_pages(print_data, n_pages);
+ gtk_print_operation_set_n_pages(op, n_pages);
+
+ debug_print("Starting print job...\n");
+}
+
+static void pdf_viewer_cb_draw_page(GtkPrintOperation *op, GtkPrintContext *context,
+ int page_nr, gpointer user_data)
+{
+ cairo_t *cr;
+ PrintData *print_data;
+ PopplerDocument *pdf_doc;
+ PopplerPage *pdf_page;
+
+ print_data = (PrintData*) user_data;
+ pdf_doc = (PopplerDocument *)printing_get_renderer_data(print_data);
+ pdf_page = poppler_document_get_page(pdf_doc, page_nr);
+
+ cr = gtk_print_context_get_cairo_context(context);
+ cairo_scale(cr, printing_get_zoom(print_data), printing_get_zoom(print_data));
+ cairo_set_source_rgb(cr, 0., 0., 0.);
+
+ poppler_page_render(pdf_page, cr);
+
+ g_object_unref(G_OBJECT(pdf_page));
+
+ debug_print("Sent page %d to printer\n", page_nr+1);
+}
+
+static void pdf_viewer_print(MimeViewer *mviewer)
+{
+ PdfViewer *viewer = (PdfViewer *)mviewer;
+ PrintRenderer *pdf_renderer = g_new0(PrintRenderer, 1);
+ MainWindow *mainwin = mainwindow_get_mainwindow();
+
+ pdf_renderer->get_pango_context = pdf_viewer_get_pango_context;
+ pdf_renderer->get_data_to_print = pdf_viewer_get_data_to_print;
+ pdf_renderer->cb_begin_print = pdf_viewer_cb_begin_print;
+ pdf_renderer->cb_draw_page = pdf_viewer_cb_draw_page;
+
+ printing_print_full(mainwin ? GTK_WINDOW(mainwin->window):NULL,
+ pdf_renderer, viewer->pdf_doc, -1, -1);
+
+ g_free(pdf_renderer);
+}
+#endif
+
+static MimeViewer *pdf_viewer_create(void)
+{
+ PdfViewer *viewer;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeStore *tree_store;
+ GtkWidget *sep;
+ GtkWidget *img;
+ gint col = 0;
+
+ viewer = g_new0(PdfViewer, 1);
+ debug_print("pdf_viewer_create\n");
+
+ viewer->last_x = 0;
+ viewer->last_y = 0;
+ viewer->mimeviewer.factory = &pdf_viewer_factory;
+ viewer->mimeviewer.get_widget = pdf_viewer_get_widget;
+ viewer->mimeviewer.show_mimepart = pdf_viewer_show_mimepart;
+ viewer->mimeviewer.clear_viewer = pdf_viewer_clear;
+ viewer->mimeviewer.destroy_viewer = pdf_viewer_destroy;
+ viewer->mimeviewer.text_search = pdf_viewer_text_search;
+ viewer->mimeviewer.scroll_page = pdf_viewer_scroll_page;
+ viewer->mimeviewer.scroll_one_line = pdf_viewer_scroll_one_line;
+#if GTK_CHECK_VERSION(2,10,0) && POPPLER_HAS_CAIRO && !USE_LIBGNOMEPRINT
+ viewer->mimeviewer.print = pdf_viewer_print;
+#endif
+ viewer->scrollwin = gtk_scrolled_window_new(NULL, NULL);
+ viewer->scrollwin_index = gtk_scrolled_window_new(NULL, NULL);
+ viewer->pdf_view_ebox = gtk_event_box_new();
+ gtk_event_box_set_visible_window(GTK_EVENT_BOX(viewer->pdf_view_ebox), FALSE);
+
+ viewer->mimeinfo = NULL;
+
+ viewer->pdf_view = gtk_image_new();
+ gtk_widget_set_events(viewer->pdf_view_ebox,
+ GDK_BUTTON_RELEASE_MASK
+ | GDK_POINTER_MOTION_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_BUTTON_MOTION_MASK
+ );
+ gtk_container_add (GTK_CONTAINER(viewer->pdf_view_ebox), viewer->pdf_view);
+ viewer->icon_type = gtk_image_new();
+ viewer->icon_type_ebox = gtk_event_box_new();
+
+ gtk_container_add(GTK_CONTAINER(viewer->icon_type_ebox), viewer->icon_type);
+
+ viewer->doc_label = gtk_label_new("");
+
+ viewer->widgets_table = gtk_table_new(1, 1, FALSE);
+
+ viewer->doc_index_pane = gtk_hpaned_new();
+
+ viewer->frame_index = gtk_frame_new(NULL);
+ gtk_frame_set_shadow_type(GTK_FRAME(viewer->frame_index), GTK_SHADOW_IN);
+ gtk_widget_set_size_request(viewer->frame_index, 18, -1);
+ gtk_frame_set_label(GTK_FRAME(viewer->frame_index), _("Document Index"));
+
+#if !(GTK_CHECK_VERSION(2,12,0))
+ viewer->button_bar_tips = tips;
+#endif
+
+ ADD_SEP_TO_TABLE
+ ADD_BUTTON_TO_TABLE(viewer->first_page, GTK_STOCK_GOTO_FIRST)
+ ADD_BUTTON_TO_TABLE(viewer->prev_page, GTK_STOCK_GO_BACK)
+ viewer->cur_page = gtk_spin_button_new_with_range(0.0, 0.0, 1.0);
+ viewer->zoom_scroll = gtk_spin_button_new_with_range(0.20, 8.0, 0.20);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(viewer->zoom_scroll), 1.0);
+ viewer->zoom = 1.0;
+ gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(viewer->cur_page), TRUE);
+ gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(viewer->zoom_scroll), TRUE);
+ gtk_table_attach(GTK_TABLE(viewer->widgets_table), GTK_WIDGET(viewer->cur_page),
+ col, col+1,
+ 0, 1, 0, 0,
+ BUTTON_H_PADDING,
+ 0);
+ col++;
+ gtk_table_attach(GTK_TABLE(viewer->widgets_table), GTK_WIDGET(viewer->doc_label),
+ col, col+1,
+ 0, 1, 0, 0,
+ BUTTON_H_PADDING,
+ 0);
+ col++;
+
+ ADD_BUTTON_TO_TABLE(viewer->next_page, GTK_STOCK_GO_FORWARD)
+ ADD_BUTTON_TO_TABLE(viewer->last_page, GTK_STOCK_GOTO_LAST)
+ ADD_SEP_TO_TABLE
+ ADD_BUTTON_TO_TABLE(viewer->zoom_fit, GTK_STOCK_ZOOM_FIT)
+ ADD_BUTTON_TO_TABLE(viewer->zoom_in, GTK_STOCK_ZOOM_IN)
+ gtk_table_attach(GTK_TABLE(viewer->widgets_table), GTK_WIDGET(viewer->zoom_scroll),
+ col, col+1,
+ 0, 1, 0, 0,
+ BUTTON_H_PADDING,
+ 0);
+ col++;
+ ADD_BUTTON_TO_TABLE(viewer->zoom_out, GTK_STOCK_ZOOM_OUT)
+ ADD_BUTTON_TO_TABLE(viewer->zoom_width, GTK_STOCK_FULLSCREEN)
+ ADD_SEP_TO_TABLE
+ ADD_BUTTON_TO_TABLE(viewer->rotate_left, GTK_STOCK_UNDO)
+ ADD_BUTTON_TO_TABLE(viewer->rotate_right, GTK_STOCK_REDO)
+ ADD_SEP_TO_TABLE
+ ADD_BUTTON_TO_TABLE(viewer->doc_info, GTK_STOCK_INFO)
+
+ viewer->doc_index = GTK_WIDGET(gtk_toggle_tool_button_new_from_stock(GTK_STOCK_INDEX));
+ gtk_widget_set_size_request(GTK_WIDGET(viewer->doc_index), 26, 26);
+ gtk_table_attach(GTK_TABLE(viewer->widgets_table), GTK_WIDGET(viewer->doc_index),
+ col, col+1,
+ 0, 1, 0, 0,
+ BUTTON_H_PADDING,
+ 0);
+ col++;
+
+ gtk_scrolled_window_set_policy(
+ GTK_SCROLLED_WINDOW(viewer->scrollwin),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+
+ gtk_scrolled_window_set_shadow_type(
+ GTK_SCROLLED_WINDOW(viewer->scrollwin),
+ GTK_SHADOW_IN);
+
+ gtk_scrolled_window_add_with_viewport(
+ GTK_SCROLLED_WINDOW(viewer->scrollwin),
+ viewer->pdf_view_ebox);
+
+ viewer->vbox = gtk_vbox_new(FALSE, 4);
+ viewer->hbox = gtk_hbox_new(FALSE, 4);
+
+ /* treeview */
+ tree_store = gtk_tree_store_new(N_INDEX_COLUMNS,
+ G_TYPE_STRING,
+ G_TYPE_INT,
+ G_TYPE_DOUBLE);
+
+ viewer->index_list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(tree_store));
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer, "text", 0, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(viewer->index_list), column);
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(viewer->index_list), FALSE);
+
+ viewer->index_model = GTK_TREE_MODEL(tree_store);
+
+ gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(viewer->index_list)),
+ GTK_SELECTION_SINGLE);
+
+ g_signal_connect(G_OBJECT(viewer->index_list), "row_activated",
+ G_CALLBACK(pdf_viewer_index_row_activated),
+ viewer);
+
+ gtk_scrolled_window_set_policy(
+ GTK_SCROLLED_WINDOW(viewer->scrollwin_index),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+
+ gtk_scrolled_window_set_shadow_type(
+ GTK_SCROLLED_WINDOW(viewer->scrollwin_index),
+ GTK_SHADOW_IN);
+
+ gtk_scrolled_window_add_with_viewport(
+ GTK_SCROLLED_WINDOW(viewer->scrollwin_index),
+ viewer->index_list);
+
+ /* end treeview */
+
+ stock_pixbuf_gdk(viewer->hbox,
+ STOCK_PIXMAP_MIME_TEXT_PLAIN,
+ &viewer->icon_pixbuf);
+
+ gtk_image_set_from_pixbuf(GTK_IMAGE(viewer->icon_type),
+ viewer->icon_pixbuf);
+
+ /* pack widgets*/
+ gtk_box_pack_start(GTK_BOX(viewer->hbox), viewer->icon_type_ebox, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(viewer->hbox), viewer->widgets_table, FALSE, FALSE, 0);
+
+ gtk_container_add(GTK_CONTAINER(viewer->frame_index), viewer->scrollwin_index);
+
+ gtk_paned_pack1(GTK_PANED(viewer->doc_index_pane), viewer->frame_index, FALSE, FALSE);
+ gtk_paned_pack2(GTK_PANED(viewer->doc_index_pane), viewer->scrollwin, FALSE, FALSE);
+
+ gtk_box_pack_start(GTK_BOX(viewer->vbox), viewer->hbox, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(viewer->vbox), viewer->doc_index_pane, TRUE, TRUE, 0);
+ /* show widgets */
+ gtk_widget_show(GTK_WIDGET(viewer->doc_index_pane));
+ g_object_ref(GTK_WIDGET(viewer->doc_index_pane));
+ gtk_widget_show(GTK_WIDGET(viewer->scrollwin));
+ g_object_ref(GTK_WIDGET(viewer->scrollwin));
+ gtk_widget_show(GTK_WIDGET(viewer->icon_type_ebox));
+ g_object_ref(GTK_WIDGET(viewer->icon_type_ebox));
+ gtk_widget_show(GTK_WIDGET(viewer->pdf_view_ebox));
+ g_object_ref(GTK_WIDGET(viewer->pdf_view_ebox));
+ gtk_widget_show(GTK_WIDGET(viewer->scrollwin_index));
+ g_object_ref(GTK_WIDGET(viewer->scrollwin_index));
+ gtk_widget_show(GTK_WIDGET(viewer->hbox));
+ g_object_ref(GTK_WIDGET(viewer->hbox));
+ gtk_widget_show(GTK_WIDGET(viewer->vbox));
+ g_object_ref(GTK_WIDGET(viewer->vbox));
+
+ gtk_widget_show(GTK_WIDGET(viewer->widgets_table));
+ g_object_ref(GTK_WIDGET(viewer->widgets_table));
+
+ gtk_widget_show(GTK_WIDGET(viewer->cur_page));
+ g_object_ref(GTK_WIDGET(viewer->cur_page));
+
+ gtk_widget_show(GTK_WIDGET(viewer->first_page));
+ g_object_ref(GTK_WIDGET(viewer->first_page));
+
+ gtk_widget_show(GTK_WIDGET(viewer->last_page));
+ g_object_ref(GTK_WIDGET(viewer->last_page));
+
+ gtk_widget_show(GTK_WIDGET(viewer->prev_page));
+ g_object_ref(GTK_WIDGET(viewer->prev_page));
+
+ gtk_widget_show(GTK_WIDGET(viewer->next_page));
+ g_object_ref(GTK_WIDGET(viewer->next_page));
+
+ gtk_widget_show(GTK_WIDGET(viewer->zoom_in));
+ g_object_ref(GTK_WIDGET(viewer->zoom_in));
+ gtk_widget_show(GTK_WIDGET(viewer->zoom_out));
+ g_object_ref(GTK_WIDGET(viewer->zoom_out));
+ gtk_widget_show(GTK_WIDGET(viewer->zoom_fit));
+ g_object_ref(GTK_WIDGET(viewer->zoom_fit));
+ gtk_widget_show(GTK_WIDGET(viewer->zoom_width));
+ g_object_ref(GTK_WIDGET(viewer->zoom_width));
+
+ gtk_widget_show(GTK_WIDGET(viewer->rotate_right));
+ g_object_ref(GTK_WIDGET(viewer->rotate_right));
+ gtk_widget_show(GTK_WIDGET(viewer->rotate_left));
+ g_object_ref(GTK_WIDGET(viewer->rotate_left));
+ gtk_widget_show(GTK_WIDGET(viewer->doc_info));
+ g_object_ref(GTK_WIDGET(viewer->doc_info));
+ gtk_widget_show(GTK_WIDGET(viewer->doc_index));
+ g_object_ref(GTK_WIDGET(viewer->doc_index));
+
+ gtk_widget_show(GTK_WIDGET(viewer->doc_label));
+ g_object_ref(GTK_WIDGET(viewer->doc_label));
+ gtk_widget_show(GTK_WIDGET(viewer->icon_type));
+ g_object_ref(GTK_WIDGET(viewer->icon_type));
+ gtk_widget_show(GTK_WIDGET(viewer->pdf_view));
+ g_object_ref(GTK_WIDGET(viewer->pdf_view));
+ gtk_widget_show(GTK_WIDGET(viewer->zoom_scroll));
+ g_object_ref(GTK_WIDGET(viewer->zoom_scroll));
+
+ gtk_widget_show(GTK_WIDGET(viewer->index_list));
+ g_object_ref(GTK_WIDGET(viewer->index_list));
+
+ /* Set Tooltips*/
+ CLAWS_SET_TIP(viewer->first_page,
+ _("First Page"));
+
+ CLAWS_SET_TIP(viewer->prev_page,
+ _("Previous Page"));
+
+ CLAWS_SET_TIP(viewer->next_page,
+ _("Next Page"));
+
+ CLAWS_SET_TIP(viewer->last_page,
+ _("Last Page"));
+
+ CLAWS_SET_TIP(viewer->zoom_in,
+ _("Zoom In"));
+ CLAWS_SET_TIP(viewer->zoom_out,
+ _("Zoom Out"));
+
+ CLAWS_SET_TIP(viewer->zoom_fit,
+ _("Fit Page"));
+
+ CLAWS_SET_TIP(viewer->zoom_width,
+ _("Fit Page Width"));
+
+ CLAWS_SET_TIP(viewer->rotate_left,
+ _("Rotate Left"));
+
+ CLAWS_SET_TIP(viewer->rotate_right,
+ _("Rotate Right"));
+
+ CLAWS_SET_TIP(viewer->doc_info,
+ _("Document Info"));
+
+ CLAWS_SET_TIP(viewer->doc_index,
+ _("Document Index"));
+ CLAWS_SET_TIP(viewer->cur_page,
+ _("Page Number"));
+ CLAWS_SET_TIP(viewer->zoom_scroll,
+ _("Zoom Factor"));
+ /* Connect Signals */
+ g_signal_connect(G_OBJECT(viewer->cur_page),
+ "value-changed",
+ G_CALLBACK(pdf_viewer_spin_change_page_cb),
+ (gpointer) viewer);
+
+ g_signal_connect(G_OBJECT(viewer->first_page),
+ "clicked",
+ G_CALLBACK(pdf_viewer_button_first_page_cb),
+ (gpointer) viewer);
+ g_signal_connect(G_OBJECT(viewer->prev_page),
+ "clicked",
+ G_CALLBACK(pdf_viewer_button_prev_page_cb),
+ (gpointer) viewer);
+ g_signal_connect(G_OBJECT(viewer->next_page),
+ "clicked",
+ G_CALLBACK(pdf_viewer_button_next_page_cb),
+ (gpointer) viewer);
+ g_signal_connect(G_OBJECT(viewer->last_page),
+ "clicked",
+ G_CALLBACK(pdf_viewer_button_last_page_cb),
+ (gpointer) viewer);
+ g_signal_connect(G_OBJECT(viewer->zoom_in),
+ "clicked",
+ G_CALLBACK(pdf_viewer_button_zoom_in_cb),
+ (gpointer) viewer);
+ g_signal_connect(G_OBJECT(viewer->zoom_out),
+ "clicked",
+ G_CALLBACK(pdf_viewer_button_zoom_out_cb),
+ (gpointer) viewer);
+ g_signal_connect(G_OBJECT(viewer->zoom_scroll),
+ "value-changed",
+ G_CALLBACK(pdf_viewer_spin_zoom_scroll_cb),
+ (gpointer) viewer);
+
+ g_signal_connect(G_OBJECT(viewer->zoom_fit),
+ "clicked",
+ G_CALLBACK(pdf_viewer_button_zoom_fit_cb),
+ (gpointer) viewer);
+
+ g_signal_connect(G_OBJECT(viewer->zoom_width),
+ "clicked",
+ G_CALLBACK(pdf_viewer_button_zoom_width_cb),
+ (gpointer) viewer);
+
+ g_signal_connect(G_OBJECT(viewer->rotate_right),
+ "clicked",
+ G_CALLBACK(pdf_viewer_button_rotate_right_cb),
+ (gpointer) viewer);
+
+ g_signal_connect(G_OBJECT(viewer->rotate_left),
+ "clicked",
+ G_CALLBACK(pdf_viewer_button_rotate_left_cb),
+ (gpointer) viewer);
+
+ g_signal_connect(G_OBJECT(viewer->doc_info),
+ "clicked",
+ G_CALLBACK(pdf_viewer_button_document_info_cb),
+ (gpointer) viewer);
+
+ g_signal_connect(G_OBJECT(viewer->doc_index),
+ "clicked",
+ G_CALLBACK(pdf_viewer_show_document_index_cb),
+ (gpointer) viewer);
+ g_signal_connect(G_OBJECT(viewer->scrollwin),
+ "scroll-event",
+ G_CALLBACK(pdf_viewer_scroll_cb),
+ (gpointer) viewer);
+ g_signal_connect(G_OBJECT(viewer->pdf_view_ebox),
+ "button_press_event",
+ G_CALLBACK(pdf_viewer_button_press_events_cb),
+ (gpointer) viewer);
+ g_signal_connect(G_OBJECT(viewer->pdf_view_ebox),
+ "button_release_event",
+ G_CALLBACK(pdf_viewer_mouse_scroll_destroy_cb),
+ (gpointer) viewer);
+ g_signal_connect(G_OBJECT(viewer->pdf_view_ebox),
+ "motion_notify_event",
+ G_CALLBACK(pdf_viewer_move_events_cb),
+ (gpointer) viewer);
+
+ viewer->target_filename = NULL;
+ viewer->filename = NULL;
+ viewer->fsname = NULL;
+
+ return(MimeViewer *) viewer;
+}
+
+#undef ADD_BUTTON_TO_TABLE
+#undef ADD_SEP_TO_TABLE
+#undef BUTTON_H_PADDING
+#undef SEP_H_PADDING
+
+static MimeViewerFactory pdf_viewer_factory =
+{
+ content_types,
+ 0,
+ pdf_viewer_create,
+};
+
+gint plugin_init(gchar **error)
+{
+ gchar *gspath = NULL;
+
+ msg = g_strdup_printf(_("This plugin enables the viewing of PDF and PostScript "
+ "attachments using the Poppler %s Lib and the gs tool.\n\n"
+ "Any feedback is welcome: iwkse@claws-mail.org"
+ ), poppler_get_version());
+
+ if (!check_plugin_version(MAKE_NUMERIC_VERSION(3,8,1,46),
+ VERSION_NUMERIC, _("PDF Viewer"), error)) return -1;
+
+ if ((gspath = g_find_program_in_path("gs")) == NULL) {
+ gchar *pmsg = msg;
+ msg = g_strdup_printf(_("Warning: could not find ghostscript binary (gs) required "
+ "for %s plugin to process PostScript attachments, only PDF "
+ "attachments will be displayed. To enable PostScript "
+ "support please install gs program.\n\n%s"
+ ), _("PDF Viewer"), pmsg);
+ g_free(pmsg);
+ }
+ else {
+ g_free(gspath);
+ }
+
+ mimeview_register_viewer_factory(&pdf_viewer_factory);
+ return 0;
+}
+
+gboolean plugin_done(void)
+{
+ g_free(msg);
+ mimeview_unregister_viewer_factory(&pdf_viewer_factory);
+ return TRUE;
+}
+
+const gchar *plugin_name(void)
+{
+ return _("PDF Viewer");
+}
+
+const gchar *plugin_desc(void)
+{
+ return msg;
+}
+
+const gchar *plugin_type(void)
+{
+ return "GTK2";
+}
+
+const gchar *plugin_licence(void)
+{
+ return "GPL3+";
+}
+
+const gchar *plugin_version(void)
+{
+ return VERSION;
+}
+
+struct PluginFeature *plugin_provides(void)
+{
+ static struct PluginFeature features[] =
+ { {PLUGIN_MIMEVIEWER, "application/pdf"},
+ {PLUGIN_MIMEVIEWER, "application/postscript"},
+ {PLUGIN_NOTHING, NULL} };
+ return features;
+}
+
diff --git a/src/plugins/pdf_viewer/poppler_viewer.h b/src/plugins/pdf_viewer/poppler_viewer.h
@@ -0,0 +1,154 @@
+/*
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2007 the Claws Mail Team
+ * This file Copyright (C) 2007 Salvatore De Paolis
+ * <iwkse@claws-mail.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#ifndef POPPLER_VIEWER_H
+#define POPPLER_VIEWER_H
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+//#include <unistd.h>
+#include <stdio.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <gdk/gdkkeysyms.h>
+#include <poppler.h>
+#include <version.h>
+#include <plugin.h>
+#include <messageview.h>
+#include <mimeview.h>
+#include <alertpanel.h>
+#include <mimeview.h>
+
+/*#ifdef USE_PTHREAD
+ *#include <pthread.h>
+ * #endif*/
+
+#define ZOOM_FACTOR 0.25
+#define ROTATION 90
+#define ALPHA_CHANNEL 60
+#define SELECTION_COLOR 0xFF00FF
+
+static gchar *msg = NULL;
+
+struct _PageResult
+{
+ GList *results;
+ gint page_num;
+};
+
+typedef struct _PageResult PageResult;
+
+struct _PdfViewer
+{
+ MimeViewer mimeviewer;
+ GtkWidget *widgets_table;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *frame_index;
+ GtkWidget *pdf_view;
+ GtkWidget *scrollwin;
+ GtkWidget *scrollwin_index;
+ GtkWidget *pdf_view_ebox;
+ GtkWidget *icon_type_ebox;
+ GtkWidget *icon_type;
+ GdkPixbuf *icon_pixbuf;
+ GtkWidget *doc_label;
+ GtkWidget *cur_page;
+ GtkWidget *doc_index_pane;
+ GtkWidget *index_list;
+ /* begin GtkButtons */
+ GtkWidget *first_page;
+ GtkWidget *last_page;
+ GtkWidget *prev_page;
+ GtkWidget *next_page;
+ GtkWidget *zoom_in;
+ GtkWidget *zoom_out;
+ GtkWidget *zoom_scroll;
+ GtkWidget *zoom_fit;
+ GtkWidget *zoom_width;
+ GtkWidget *rotate_left;
+ GtkWidget *rotate_right;
+ GtkWidget *doc_info;
+ GtkWidget *doc_index;
+ /* end GtkButtons */
+ GtkTable *table_doc_info;
+#if !GTK_CHECK_VERSION(2,12,0)
+ GtkTooltips *button_bar_tips;
+#endif
+ PopplerDocument *pdf_doc;
+ PopplerPage *pdf_page;
+ PopplerIndexIter *pdf_index;
+ PopplerRectangle *last_rect;
+ PopplerAction *link_action;
+ PageResult *last_page_result;
+ GtkAdjustment *pdf_view_vadj;
+ GtkAdjustment *pdf_view_hadj;
+ GtkTreeModel *index_model;
+
+ GList *link_map;
+ GList *page_results;
+ GList *text_found; /* GList of PageResults */
+ gchar *last_search;
+ gint last_match;
+ gint num_matches;
+
+ gchar *target_filename;
+ gchar *filename;
+ gchar *fsname;
+ gchar *doc_info_text;
+
+ gint res_cnt;
+ gint rotate;
+ gint num_pages;
+ gdouble zoom;
+ gdouble width;
+ gdouble height;
+ gdouble last_x;
+ gdouble last_y;
+ gint last_dir_x;
+ gint last_dir_y;
+ gboolean pdf_view_scroll;
+ gboolean in_link;
+ MimeInfo *mimeinfo;
+ MimeInfo *to_load;
+
+};
+static gchar *content_types[] =
+ {"application/pdf",
+ "application/postscript",
+ NULL};
+typedef enum {
+ TYPE_UNKNOWN,
+ TYPE_PDF,
+ TYPE_PS
+} FileType;
+
+enum {
+ INDEX_NAME,
+ INDEX_PAGE,
+ INDEX_TOP,
+ N_INDEX_COLUMNS
+};
+
+typedef struct _PdfViewer PdfViewer;
+
+#endif /* POPPLER_VIEWER_H */
diff --git a/src/plugins/pdf_viewer/right_arrow.xpm b/src/plugins/pdf_viewer/right_arrow.xpm
@@ -0,0 +1,44 @@
+/* XPM */
+static char * right_arrow_xpm[] = {
+"16 16 25 1",
+" c None",
+". c #000000",
+"+ c #5B88B2",
+"@ c #9EB8D1",
+"# c #5080AD",
+"$ c #B5C9DC",
+"% c #AFC5DA",
+"& c #B2C7DB",
+"* c #B6CADD",
+"= c #A4BDD5",
+"- c #9CB7D1",
+"; c #080D11",
+"> c #9BB6D0",
+", c #A0BAD3",
+"' c #9AB5CF",
+") c #97B3CE",
+"! c #5F8BB4",
+"~ c #91B0CC",
+"{ c #95B2CE",
+"] c #4C79A3",
+"^ c #49749C",
+"/ c #3F6588",
+"( c #2A435B",
+"_ c #456F96",
+": c #375978",
+" ",
+" ",
+" . ",
+" .. ",
+" .+. ",
+" .......@#. ",
+" .$%&***=-#; ",
+" .>,-->',-)!. ",
+" .~@''>---,{]. ",
+" .^////////(. ",
+" ._::::://(. ",
+" ......./(. ",
+" .(. ",
+" .. ",
+" . ",
+" "};
diff --git a/src/plugins/pdf_viewer/rotate_left.xpm b/src/plugins/pdf_viewer/rotate_left.xpm
@@ -0,0 +1,44 @@
+/* XPM */
+static char * rotate_left_xpm[] = {
+"16 16 25 1",
+" c None",
+". c #000000",
+"+ c #5B88B2",
+"@ c #5080AD",
+"# c #9EB8D1",
+"$ c #080D11",
+"% c #9CB7D1",
+"& c #A4BDD5",
+"* c #B6CADD",
+"= c #B2C7DB",
+"- c #AFC5DA",
+"; c #B5C9DC",
+"> c #5F8BB4",
+", c #97B3CE",
+"' c #A0BAD3",
+") c #9AB5CF",
+"! c #9BB6D0",
+"~ c #4C79A3",
+"{ c #95B2CE",
+"] c #91B0CC",
+"^ c #2A435B",
+"/ c #3F6588",
+"( c #375978",
+"_ c #456F96",
+": c #49749C",
+" . ",
+" .. ",
+" .+. ",
+" .@#....... ",
+" $@%&***=-;;. ",
+" .>,%')!%%'!!!. ",
+".~{'%%%!))#]]!;.",
+" .^/////////]!;.",
+" .^//(((((/]!;.",
+" .^/...((/]!;.",
+" .^. .(/!!*.",
+" .. .(/)%*.",
+" . .(/)%=.",
+" .(/#'-.",
+" ._:]!;.",
+" ......."};
diff --git a/src/plugins/pdf_viewer/rotate_right.xpm b/src/plugins/pdf_viewer/rotate_right.xpm
@@ -0,0 +1,44 @@
+/* XPM */
+static char * rotate_right_xpm[] = {
+"16 16 25 1",
+" c None",
+". c #000000",
+"+ c #5B88B2",
+"@ c #9EB8D1",
+"# c #5080AD",
+"$ c #B5C9DC",
+"% c #AFC5DA",
+"& c #B2C7DB",
+"* c #B6CADD",
+"= c #A4BDD5",
+"- c #9CB7D1",
+"; c #080D11",
+"> c #9BB6D0",
+", c #A0BAD3",
+"' c #9AB5CF",
+") c #97B3CE",
+"! c #5F8BB4",
+"~ c #91B0CC",
+"{ c #95B2CE",
+"] c #4C79A3",
+"^ c #3F6588",
+"/ c #2A435B",
+"( c #375978",
+"_ c #49749C",
+": c #456F96",
+" . ",
+" .. ",
+" .+. ",
+" .......@#. ",
+" .$$%&***=-#; ",
+" .>>>,-->',-)!. ",
+".$>~~@''>---,{].",
+".$>~^^^^^^^^^/. ",
+".$>~^(((((^^/. ",
+".$>~^((...^/. ",
+".*>>^(. ./. ",
+".*-'^(. .. ",
+".&-'^(. . ",
+".%,@^(. ",
+".$>~_:. ",
+"....... "};
diff --git a/src/plugins/pdf_viewer/zoom_fit.xpm b/src/plugins/pdf_viewer/zoom_fit.xpm
@@ -0,0 +1,66 @@
+/* XPM */
+static char * zoom_fit_xpm[] = {
+"14 14 49 1",
+" c None",
+". c #46463E",
+"+ c #090908",
+"@ c #4A4A42",
+"# c #33332D",
+"$ c #B6B6AB",
+"% c #E2E2D6",
+"& c #DDDDD0",
+"* c #ABABA0",
+"= c #34342F",
+"- c #45453E",
+"; c #B6B6AA",
+"> c #10101A",
+", c #0B0B0F",
+"' c #DBDCD0",
+") c #2B2B35",
+"! c #A3A398",
+"~ c #4B4B43",
+"{ c #080807",
+"] c #E6E6D9",
+"^ c #030303",
+"/ c #DEDED2",
+"( c #D6D6CC",
+"_ c #CECEC4",
+": c #31313B",
+"< c #C0C0B8",
+"[ c #080808",
+"} c #E0E0D4",
+"| c #ECECE6",
+"1 c #D7D7CD",
+"2 c #D0D0C6",
+"3 c #C7C7BF",
+"4 c #AEAEA4",
+"5 c #262630",
+"6 c #D2D2C8",
+"7 c #CACAC0",
+"8 c #C2C2BA",
+"9 c #3D3D45",
+"0 c #BCBCB4",
+"a c #2D2D37",
+"b c #97978D",
+"c c #4B4B44",
+"d c #33332E",
+"e c #A6A69C",
+"f c #C7C7BE",
+"g c #BFBFB7",
+"h c #98988E",
+"i c #000000",
+"j c #404040",
+" .+++@ ",
+" #$%&**= ",
+"-;>,'))!~ ",
+"{]^/(_:<+ ",
+"[}|1233<+ ",
+"+4567890+ ",
+"-45a799bc ",
+" def<ghii ",
+" @+++jiii ",
+" iii ",
+" iii ",
+" iii ",
+" ii ",
+" "};
diff --git a/src/plugins/pdf_viewer/zoom_in.xpm b/src/plugins/pdf_viewer/zoom_in.xpm
@@ -0,0 +1,63 @@
+/* XPM */
+static char * zoom_in_xpm[] = {
+"14 14 46 1",
+" c None",
+". c #46463E",
+"+ c #090908",
+"@ c #4A4A42",
+"# c #33332D",
+"$ c #B6B6AB",
+"% c #E2E2D6",
+"& c #DDDDD0",
+"* c #ABABA0",
+"= c #34342F",
+"- c #45453E",
+"; c #B6B6AA",
+"> c #EFEFE5",
+", c #F4F4F0",
+"' c #24232F",
+") c #D4D4CA",
+"! c #A3A398",
+"~ c #4B4B43",
+"{ c #080807",
+"] c #E6E6D9",
+"^ c #FCFCFC",
+"/ c #DEDED2",
+"( c #292933",
+"_ c #CECEC4",
+": c #C0C0B8",
+"< c #080808",
+"[ c #E0E0D4",
+"} c #131319",
+"| c #282832",
+"1 c #2F2F39",
+"2 c #383840",
+"3 c #AEAEA4",
+"4 c #D9D9CF",
+"5 c #D2D2C8",
+"6 c #35353F",
+"7 c #C2C2BA",
+"8 c #BCBCB4",
+"9 c #97978D",
+"0 c #4B4B44",
+"a c #33332E",
+"b c #A6A69C",
+"c c #C7C7BE",
+"d c #BFBFB7",
+"e c #98988E",
+"f c #000000",
+"g c #404040",
+" .+++@ ",
+" #$%&**= ",
+"-;>,'))!~ ",
+"{]^/(__:+ ",
+"<[}|122:+ ",
+"+3456778+ ",
+"-34567790 ",
+" abc:deff ",
+" @+++gfff ",
+" fff ",
+" fff ",
+" fff ",
+" ff ",
+" "};
diff --git a/src/plugins/pdf_viewer/zoom_out.xpm b/src/plugins/pdf_viewer/zoom_out.xpm
@@ -0,0 +1,63 @@
+/* XPM */
+static char * zoom_out_xpm[] = {
+"14 14 46 1",
+" c None",
+". c #46463E",
+"+ c #090908",
+"@ c #4A4A42",
+"# c #33332D",
+"$ c #B6B6AB",
+"% c #E2E2D6",
+"& c #DDDDD0",
+"* c #ABABA0",
+"= c #34342F",
+"- c #45453E",
+"; c #B6B6AA",
+"> c #EFEFE5",
+", c #F4F4F0",
+"' c #DBDCD0",
+") c #D4D4CA",
+"! c #A3A398",
+"~ c #4B4B43",
+"{ c #080807",
+"] c #E6E6D9",
+"^ c #FCFCFC",
+"/ c #DEDED2",
+"( c #D6D6CC",
+"_ c #CECEC4",
+": c #C0C0B8",
+"< c #080808",
+"[ c #E0E0D4",
+"} c #131319",
+"| c #282832",
+"1 c #2F2F39",
+"2 c #383840",
+"3 c #AEAEA4",
+"4 c #D9D9CF",
+"5 c #D2D2C8",
+"6 c #CACAC0",
+"7 c #C2C2BA",
+"8 c #BCBCB4",
+"9 c #97978D",
+"0 c #4B4B44",
+"a c #33332E",
+"b c #A6A69C",
+"c c #C7C7BE",
+"d c #BFBFB7",
+"e c #98988E",
+"f c #000000",
+"g c #404040",
+" .+++@ ",
+" #$%&**= ",
+"-;>,'))!~ ",
+"{]^/(__:+ ",
+"<[}|122:+ ",
+"+3456778+ ",
+"-34567790 ",
+" abc:deff ",
+" @+++gfff ",
+" fff ",
+" fff ",
+" fff ",
+" ff ",
+" "};
diff --git a/src/plugins/pdf_viewer/zoom_width.xpm b/src/plugins/pdf_viewer/zoom_width.xpm
@@ -0,0 +1,66 @@
+/* XPM */
+static char * zoom_width_xpm[] = {
+"14 14 49 1",
+" c None",
+". c #46463E",
+"+ c #090908",
+"@ c #4A4A42",
+"# c #33332D",
+"$ c #B6B6AB",
+"% c #E2E2D6",
+"& c #DDDDD0",
+"* c #ABABA0",
+"= c #34342F",
+"- c #45453E",
+"; c #B6B6AA",
+"> c #EFEFE5",
+", c #F4F4F0",
+"' c #DBDCD0",
+") c #D4D4CA",
+"! c #A3A398",
+"~ c #4B4B43",
+"{ c #080807",
+"] c #E6E6D9",
+"^ c #FCFCFC",
+"/ c #21212D",
+"( c #D6D6CC",
+"_ c #31313B",
+": c #CECEC4",
+"< c #C0C0B8",
+"[ c #080808",
+"} c #E0E0D4",
+"| c #131319",
+"1 c #282832",
+"2 c #2F2F39",
+"3 c #383840",
+"4 c #AEAEA4",
+"5 c #D9D9CF",
+"6 c #2D2D37",
+"7 c #CACAC0",
+"8 c #3D3D45",
+"9 c #C2C2BA",
+"0 c #BCBCB4",
+"a c #D2D2C8",
+"b c #97978D",
+"c c #4B4B44",
+"d c #33332E",
+"e c #A6A69C",
+"f c #C7C7BE",
+"g c #BFBFB7",
+"h c #98988E",
+"i c #000000",
+"j c #404040",
+" .+++@ ",
+" #$%&**= ",
+"-;>,'))!~ ",
+"{]^/(_:<+ ",
+"[}|1233<+ ",
+"+4567890+ ",
+"-45a799bc ",
+" def<ghii ",
+" @+++jiii ",
+" iii ",
+" iii ",
+" iii ",
+" ii ",
+" "};
diff --git a/src/plugins/perl/Makefile.am b/src/plugins/perl/Makefile.am
@@ -0,0 +1,25 @@
+plugindir = $(pkglibdir)/plugins
+
+plugin_LTLIBRARIES = perl_plugin.la
+
+perl_plugin_la_SOURCES = \
+ perl_plugin.c perl_plugin.h \
+ perl_gtk.c perl_gtk.h
+
+perl_plugin_la_LDFLAGS = \
+ -avoid-version -module
+
+perl_plugin_la_LIBADD = \
+ $(PERL_LDFLAGS) \
+ $(GTK_LIBS)
+
+AM_CPPFLAGS = \
+ $(CLAWS_MAIL_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(GTK_CFLAGS) \
+ $(PERL_CFLAGS)
+
+INCLUDES = \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/src/common \
+ -I$(top_srcdir)/src/gtk
diff --git a/src/plugins/perl/perl_gtk.c b/src/plugins/perl/perl_gtk.c
@@ -0,0 +1,104 @@
+/* Perl plugin -- Perl Support for Claws Mail
+ *
+ * Copyright (C) 2004-2007 Holger Berndt
+ *
+ * Sylpheed and Claws-Mail are GTK+ based, lightweight, and fast e-mail clients
+ * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#include "claws-features.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "common/utils.h"
+#include "mainwindow.h"
+#include "prefs_common.h"
+#include "main.h"
+#include "menu.h"
+
+#include "perl_plugin.h"
+#include "perl_gtk.h"
+
+static void perl_filter_edit(GtkAction *,gpointer);
+
+
+static GtkActionEntry mainwindow_tools_perl_edit[] = {{
+ "Tools/EditPerlRules",
+ NULL, N_("Edit perl filter rules (ext)..."), NULL, NULL, G_CALLBACK(perl_filter_edit)
+}};
+
+static gint main_menu_id = 0;
+
+
+void perl_gtk_init(void)
+{
+ MainWindow *mainwin;
+
+ mainwin = mainwindow_get_mainwindow();
+
+ gtk_action_group_add_actions(mainwin->action_group, mainwindow_tools_perl_edit,
+ 1, (gpointer)mainwin);
+ MENUITEM_ADDUI_ID_MANAGER(mainwin->ui_manager, "/Menu/Tools", "EditPerlRules",
+ "Tools/EditPerlRules", GTK_UI_MANAGER_MENUITEM,
+ main_menu_id)
+}
+
+void perl_gtk_done(void)
+{
+ MainWindow *mainwin;
+
+ mainwin = mainwindow_get_mainwindow();
+
+ if(mainwin && !claws_is_exiting()) {
+ MENUITEM_REMUI_MANAGER(mainwin->ui_manager,mainwin->action_group, "Tools/EditPerlRules", main_menu_id);
+ main_menu_id = 0;
+ }
+}
+
+static void perl_filter_edit(GtkAction *action, gpointer callback_data)
+{
+ gchar *perlfilter;
+ gchar *pp;
+ gchar buf[1024];
+ gchar **cmdline;
+
+ perlfilter = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, PERLFILTER, NULL);
+ if (prefs_common_get_ext_editor_cmd() &&
+ (pp = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
+ *(pp + 1) == 's' && !strchr(pp + 2, '%')) {
+ g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), perlfilter);
+ }
+ else {
+ if (prefs_common_get_ext_editor_cmd())
+ g_warning("Perl Plugin: External editor command-line is invalid: `%s'",
+ prefs_common_get_ext_editor_cmd());
+ g_snprintf(buf, sizeof(buf), "emacs %s", perlfilter);
+ }
+ g_free(perlfilter);
+ cmdline = strsplit_with_quote(buf, " ", 1024);
+ execute_detached(cmdline);
+ g_strfreev(cmdline);
+}
diff --git a/src/plugins/perl/perl_gtk.h b/src/plugins/perl/perl_gtk.h
@@ -0,0 +1,28 @@
+/* Perl plugin -- Perl Support for Claws Mail
+ *
+ * Copyright (C) 2004-2007 Holger Berndt
+ *
+ * Sylpheed and Claws-Mail are GTK+ based, lightweight, and fast e-mail clients
+ * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SC_PERL_GTK_H
+#define SC_PERL_GTK_H SC_PERL_GTK_H
+
+void perl_gtk_init(void);
+void perl_gtk_done(void);
+
+#endif
diff --git a/src/plugins/perl/perl_plugin.c b/src/plugins/perl/perl_plugin.c
@@ -0,0 +1,2400 @@
+/* Perl plugin -- Perl Support for Claws Mail
+ *
+ * Copyright (C) 2004-2007 Holger Berndt
+ *
+ * Sylpheed and Claws-Mail are GTK+ based, lightweight, and fast e-mail clients
+ * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#include "claws-features.h"
+#endif
+
+#include "common/version.h"
+#include "common/defs.h"
+#include "common/utils.h"
+#include "common/claws.h"
+#include "common/prefs.h"
+#include "procmsg.h"
+#include "procheader.h"
+#include "folder.h"
+#include "account.h"
+#include "compose.h"
+#include "addrindex.h"
+#include "addritem.h"
+#include "addr_compl.h"
+#include "statusbar.h"
+#include "alertpanel.h"
+#include "common/hooks.h"
+#include "prefs_common.h"
+#include "prefs_gtk.h"
+#include "common/log.h"
+#include "common/plugin.h"
+#include "common/tags.h"
+
+#include <EXTERN.h>
+#include <perl.h>
+#include <XSUB.h>
+
+#ifdef _
+# undef _
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "perl_plugin.h"
+#include "perl_gtk.h"
+
+
+/* XSRETURN_UV was introduced in Perl 5.8.1,
+ this fixes things for 5.8.0. */
+#ifndef XSRETURN_UV
+# ifndef XST_mUV
+# define XST_mUV(i,v) (ST(i) = sv_2mortal(newSVuv(v)) )
+# endif /* XST_mUV */
+# define XSRETURN_UV(v) STMT_START { XST_mUV(0,v); XSRETURN(1); } STMT_END
+#endif /* XSRETURN_UV */
+
+/* set this to "1" to recompile the Perl script for every mail,
+ even if it hasn't changed */
+#define DO_CLEAN "0"
+
+/* distinguish between automatic and manual filtering */
+#define AUTO_FILTER 0
+#define MANU_FILTER 1
+
+/* embedded Perl stuff */
+static PerlInterpreter *my_perl = NULL;
+EXTERN_C void xs_init(pTHX);
+EXTERN_C void boot_DynaLoader (pTHX_ CV* cv);
+
+/* plugin stuff */
+static guint filtering_hook_id;
+static guint manual_filtering_hook_id;
+static MailFilteringData *mail_filtering_data = NULL;
+static MsgInfo *msginfo = NULL;
+static gboolean stop_filtering = FALSE;
+static gboolean manual_filtering = FALSE;
+static gboolean wrote_filter_log_head = FALSE;
+static gint filter_log_verbosity;
+static FILE *message_file = NULL;
+static gchar *attribute_key = NULL;
+
+/* configuration */
+static PerlPluginConfig config;
+
+static PrefParam param[] = {
+ {"filter_log_verbosity", "2", &config.filter_log_verbosity,
+ P_INT, NULL, NULL, NULL},
+ {NULL, NULL, NULL, P_OTHER, NULL, NULL, NULL}
+};
+
+
+/* Utility functions */
+
+/* fire and forget */
+gint execute_detached(gchar **cmdline)
+{
+ pid_t pid;
+
+ if((pid = fork()) < 0) { /* fork error */
+ perror("fork");
+ return 0;
+ }
+ else if(pid > 0) { /* parent */
+ waitpid(pid, NULL, 0);
+ return 1;
+ }
+ else { /* child */
+ if((pid = fork()) < 0) { /* fork error */
+ perror("fork");
+ return 0;
+ }
+ else if(pid > 0) { /* child */
+ /* make grand child an orphan */
+ _exit(0);
+ }
+ else { /* grand child */
+ execvp(cmdline[0], cmdline);
+ perror("execvp");
+ _exit(1);
+ }
+ }
+ return 0;
+}
+
+
+/* filter logfile */
+#define LOG_MANUAL 1
+#define LOG_ACTION 2
+#define LOG_MATCH 3
+
+static void filter_log_write(gint type, gchar *text) {
+ if(filter_log_verbosity >= type) {
+ if(!wrote_filter_log_head) {
+ log_message(LOG_PROTOCOL, "From: %s || Subject: %s || Message-ID: %s\n",
+ msginfo->from ? msginfo->from : "<no From header>",
+ msginfo->subject ? msginfo->subject : "<no Subject header>",
+ msginfo->msgid ? msginfo->msgid : "<no message id>");
+ wrote_filter_log_head = TRUE;
+ }
+ switch(type) {
+ case LOG_MANUAL:
+ log_message(LOG_PROTOCOL, " MANUAL: %s\n", text?text:"<no text specified>");
+ break;
+ case LOG_ACTION:
+ log_message(LOG_PROTOCOL, " ACTION: %s\n", text?text:"<no text specified>");
+ break;
+ case LOG_MATCH:
+ log_message(LOG_PROTOCOL, " MATCH: %s\n", text?text:"<no text specified>");
+ break;
+ default:
+ g_warning("Perl Plugin: Wrong use of filter_log_write");
+ break;
+ }
+ }
+}
+
+/* Addressbook interface */
+
+static PerlPluginTimedSList *email_slist = NULL;
+static GHashTable *attribute_hash = NULL;
+
+/* addressbook email collector callback */
+static gint add_to_email_slist(ItemPerson *person, const gchar *bookname)
+{
+ PerlPluginEmailEntry *ee;
+ GList *nodeM;
+
+ /* Process each E-Mail address */
+ nodeM = person->listEMail;
+ while(nodeM) {
+ ItemEMail *email = nodeM->data;
+ ee = g_new0(PerlPluginEmailEntry,1);
+ g_return_val_if_fail(ee != NULL, -1);
+
+ if(email->address != NULL) ee->address = g_strdup(email->address);
+ else ee->address = NULL;
+ if(bookname != NULL) ee->bookname = g_strdup(bookname);
+ else ee->bookname = NULL;
+
+ email_slist->g_slist = g_slist_prepend(email_slist->g_slist,ee);
+ nodeM = g_list_next(nodeM);
+ }
+ return 0;
+}
+
+/* free a GSList of PerlPluginEmailEntry's. */
+static void free_PerlPluginEmailEntry_slist(GSList *slist)
+{
+ GSList *walk;
+
+ if(slist == NULL)
+ return;
+
+ walk = slist;
+ for(; walk != NULL; walk = g_slist_next(walk)) {
+ PerlPluginEmailEntry *ee = (PerlPluginEmailEntry *) walk->data;
+ if(ee != NULL) {
+ if(ee->address != NULL) g_free(ee->address);
+ if(ee->bookname != NULL) g_free(ee->bookname);
+ g_free(ee);
+ ee = NULL;
+ }
+ }
+ g_slist_free(slist);
+
+ debug_print("PerlPluginEmailEntry slist freed\n");
+}
+
+/* free email_slist */
+static void free_email_slist(void)
+{
+ if(email_slist == NULL)
+ return;
+
+ free_PerlPluginEmailEntry_slist(email_slist->g_slist);
+ email_slist->g_slist = NULL;
+
+ g_free(email_slist);
+ email_slist = NULL;
+
+ debug_print("email_slist freed\n");
+}
+
+/* check if tl->g_slist exists and is recent enough */
+static gboolean update_PerlPluginTimedSList(PerlPluginTimedSList *tl)
+{
+ gboolean retVal;
+ gchar *indexfile;
+ struct stat filestat;
+
+ if(tl->g_slist == NULL)
+ return TRUE;
+
+ indexfile = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ADDRESSBOOK_INDEX_FILE, NULL);
+ if((stat(indexfile,&filestat) == 0) && filestat.st_mtime <= tl->mtime)
+ retVal = FALSE;
+ else
+ retVal = TRUE;
+
+ g_free(indexfile);
+ return retVal;
+}
+
+/* (re)initialize email slist */
+static void init_email_slist(void)
+{
+ gchar *indexfile;
+ struct stat filestat;
+
+ if(email_slist->g_slist != NULL) {
+ free_PerlPluginEmailEntry_slist(email_slist->g_slist);
+ email_slist->g_slist = NULL;
+ }
+
+ addrindex_load_person_attribute(NULL,add_to_email_slist);
+
+ indexfile = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ADDRESSBOOK_INDEX_FILE, NULL);
+ if(stat(indexfile,&filestat) == 0)
+ email_slist->mtime = filestat.st_mtime;
+ g_free(indexfile);
+ debug_print("Initialisation of email slist completed\n");
+}
+
+/* check if given address is in given addressbook */
+static gboolean addr_in_addressbook(gchar *addr, gchar *bookname)
+{
+ /* If no book is given, check the address completion list
+ * (there may be other addresses that are not in the address book,
+ * added by other plugins). */
+ if(bookname == NULL) {
+ gboolean found;
+ start_address_completion(NULL);
+ found = (complete_matches_found(addr) > 0);
+ end_address_completion();
+ return found;
+ }
+ else {
+ GSList *walk;
+
+ /* check if email_list exists */
+ if(email_slist == NULL) {
+ email_slist = g_new0(PerlPluginTimedSList,1);
+ email_slist->g_slist = NULL;
+ debug_print("email_slist created\n");
+ }
+
+ if(update_PerlPluginTimedSList(email_slist))
+ init_email_slist();
+
+ walk = email_slist->g_slist;
+ for(; walk != NULL; walk = g_slist_next(walk)) {
+ PerlPluginEmailEntry *ee = (PerlPluginEmailEntry *) walk->data;
+ gchar *a = g_utf8_casefold(ee->address, -1);
+ gchar *b = g_utf8_casefold(addr, -1);
+ if((!g_utf8_collate(a,b)) &&
+ ((bookname == NULL) || (!strcmp(ee->bookname,bookname)))) {
+ g_free(a);
+ g_free(b);
+ return TRUE;
+ }
+ g_free(a);
+ g_free(b);
+ }
+ }
+
+ return FALSE;
+}
+
+/* attribute hash collector callback */
+static gint add_to_attribute_hash(ItemPerson *person, const gchar *bookname)
+{
+ PerlPluginTimedSList *tl;
+ PerlPluginAttributeEntry *ae;
+ GList *nodeA;
+ GList *nodeM;
+
+ nodeA = person->listAttrib;
+ /* Process each User Attribute */
+ while(nodeA) {
+ UserAttribute *attrib = nodeA->data;
+ if(attrib->name && !strcmp(attrib->name,attribute_key) ) {
+ /* Process each E-Mail address */
+ nodeM = person->listEMail;
+ while(nodeM) {
+ ItemEMail *email = nodeM->data;
+
+ ae = g_new0(PerlPluginAttributeEntry,1);
+ g_return_val_if_fail(ae != NULL, -1);
+
+ if(email->address != NULL) ae->address = g_strdup(email->address);
+ else ae->address = NULL;
+ if(attrib->value != NULL) ae->value = g_strdup(attrib->value);
+ else ae->value = NULL;
+ if(bookname != NULL) ae->bookname = g_strdup(bookname);
+ else ae->bookname = NULL;
+
+ tl = (PerlPluginTimedSList *) g_hash_table_lookup(attribute_hash,attribute_key);
+ tl->g_slist = g_slist_prepend(tl->g_slist,ae);
+
+ nodeM = g_list_next(nodeM);
+ }
+ }
+ nodeA = g_list_next(nodeA);
+ }
+
+ return 0;
+}
+
+/* free a key of the attribute hash */
+static gboolean free_attribute_hash_key(gpointer key, gpointer value, gpointer user_data)
+{
+ GSList *walk;
+ PerlPluginTimedSList *tl;
+
+ debug_print("Freeing key `%s' from attribute_hash\n",key?(char*)key:"");
+
+ tl = (PerlPluginTimedSList *) value;
+
+ if(tl != NULL) {
+ if(tl->g_slist != NULL) {
+ walk = tl->g_slist;
+ for(; walk != NULL; walk = g_slist_next(walk)) {
+ PerlPluginAttributeEntry *ae = (PerlPluginAttributeEntry *) walk->data;
+ if(ae != NULL) {
+ if(ae->address != NULL) g_free(ae->address);
+ if(ae->value != NULL) g_free(ae->value);
+ if(ae->bookname != NULL) g_free(ae->bookname);
+ g_free(ae);
+ ae = NULL;
+ }
+ }
+ g_slist_free(tl->g_slist);
+ tl->g_slist = NULL;
+ }
+ g_free(tl);
+ tl = NULL;
+ }
+
+ if(key != NULL) {
+ g_free(key);
+ key = NULL;
+ }
+
+ return TRUE;
+}
+
+/* free whole attribute hash */
+static void free_attribute_hash(void)
+{
+ if(attribute_hash == NULL)
+ return;
+
+ g_hash_table_foreach_remove(attribute_hash,free_attribute_hash_key,NULL);
+ g_hash_table_destroy(attribute_hash);
+ attribute_hash = NULL;
+
+ debug_print("attribute_hash freed\n");
+}
+
+/* Free the key if it exists. Insert the new key. */
+static void insert_attribute_hash(gchar *attr)
+{
+ PerlPluginTimedSList *tl;
+ gchar *indexfile;
+ struct stat filestat;
+
+ /* Check if key exists. Free it if it does. */
+ if((tl = g_hash_table_lookup(attribute_hash,attr)) != NULL) {
+ gpointer origkey;
+ gpointer value;
+ g_hash_table_lookup_extended(attribute_hash,attr,&origkey,&value);
+ g_hash_table_remove(attribute_hash,origkey);
+ free_attribute_hash_key(origkey,value,NULL);
+ debug_print("Existing key `%s' freed.\n",attr);
+ }
+
+ tl = g_new0(PerlPluginTimedSList,1);
+ tl->g_slist = NULL;
+
+ attribute_key = g_strdup(attr);
+ g_hash_table_insert(attribute_hash,attribute_key,tl);
+
+ addrindex_load_person_attribute(attribute_key,add_to_attribute_hash);
+
+ indexfile = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ADDRESSBOOK_INDEX_FILE, NULL);
+ if(stat(indexfile,&filestat) == 0)
+ tl->mtime = filestat.st_mtime;
+ g_free(indexfile);
+
+ debug_print("added key `%s' to attribute_hash\n",attribute_key?attribute_key:"");
+}
+
+/* check if an update of the attribute hash entry is necessary */
+static gboolean update_attribute_hash(const gchar *attr)
+{
+ PerlPluginTimedSList *tl;
+
+ /* check if key attr exists in the attribute hash */
+ if((tl = (PerlPluginTimedSList*) g_hash_table_lookup(attribute_hash,attr)) == NULL)
+ return TRUE;
+
+ /* check if entry is recent enough */
+ return update_PerlPluginTimedSList(tl);
+}
+
+/* given an email address, return attribute value of specific book */
+static gchar* get_attribute_value(gchar *email, gchar *attr, gchar *bookname)
+{
+ GSList *walk;
+ PerlPluginTimedSList *tl;
+
+ /* check if attribute hash exists */
+ if(attribute_hash == NULL) {
+ attribute_hash = g_hash_table_new(g_str_hash,g_str_equal);
+ debug_print("attribute_hash created\n");
+ }
+
+ if(update_attribute_hash(attr)) {
+ debug_print("Initialisation of attribute hash entry `%s' is necessary\n",attr);
+ insert_attribute_hash(attr);
+ }
+
+ if((tl = (PerlPluginTimedSList*) g_hash_table_lookup(attribute_hash,attr)) == NULL)
+ return NULL;
+
+ walk = tl->g_slist;
+ for(; walk != NULL; walk = g_slist_next(walk)) {
+ PerlPluginAttributeEntry *ae = (PerlPluginAttributeEntry *) walk->data;
+ gchar *a, *b;
+ a = g_utf8_strdown(ae->address, -1);
+ b = g_utf8_strdown(email, -1);
+ if(!g_utf8_collate(a, b)) {
+ if((bookname == NULL) ||
+ ((ae->bookname != NULL) && !strcmp(bookname,ae->bookname))) {
+ g_free(a); g_free(b);
+ return ae->value;
+ }
+ }
+ g_free(a); g_free(b);
+ }
+ return NULL;
+}
+
+/* free up all memory allocated with lists */
+static void free_all_lists(void)
+{
+ /* email list */
+ free_email_slist();
+
+ /* attribute hash */
+ free_attribute_hash();
+}
+
+
+
+/* ClawsMail::C module */
+
+/* Initialization */
+
+/* ClawsMail::C::filter_init(int) */
+static XS(XS_ClawsMail_filter_init)
+{
+ int flag;
+ /* flags:
+ *
+ * msginfo
+ * 1 size
+ * 2 date
+ * 3 from
+ * 4 to
+ * 5 cc
+ * 6 newsgroups
+ * 7 subject
+ * 8 msgid
+ * 9 inreplyto
+ * 10 xref
+ * 11 xface
+ * 12 dispositionnotificationto
+ * 13 returnreceiptto
+ * 14 references
+ * 15 score
+ * 16 not used anymore
+ * 17 plaintext_file
+ * 18 not used anymore
+ * 19 hidden
+ * 20 message file path
+ * 21 partial_recv
+ * 22 total_size
+ * 23 account_server
+ * 24 account_login
+ * 25 planned_download
+ *
+ * general
+ * 100 manual
+ */
+ char *charp;
+ gchar buf[BUFFSIZE];
+ GSList *walk;
+ int ii;
+
+ dXSARGS;
+ if(items != 1) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::init");
+ XSRETURN_UNDEF;
+ }
+ flag = SvIV(ST(0));
+ switch(flag) {
+
+ /* msginfo */
+ case 1:
+ msginfo->size ? XSRETURN_UV(msginfo->size) : XSRETURN_UNDEF;
+ case 2:
+ msginfo->date ? XSRETURN_PV(msginfo->date) : XSRETURN_UNDEF;
+ case 3:
+ msginfo->from ? XSRETURN_PV(msginfo->from) : XSRETURN_UNDEF;
+ case 4:
+ msginfo->to ? XSRETURN_PV(msginfo->to) : XSRETURN_UNDEF;
+ case 5:
+ msginfo->cc ? XSRETURN_PV(msginfo->cc) : XSRETURN_UNDEF;
+ case 6:
+ msginfo->newsgroups ? XSRETURN_PV(msginfo->newsgroups) : XSRETURN_UNDEF;
+ case 7:
+ msginfo->subject ? XSRETURN_PV(msginfo->subject) : XSRETURN_UNDEF;
+ case 8:
+ msginfo->msgid ? XSRETURN_PV(msginfo->msgid) : XSRETURN_UNDEF;
+ case 9:
+ msginfo->inreplyto ? XSRETURN_PV(msginfo->inreplyto) : XSRETURN_UNDEF;
+ case 10:
+ msginfo->xref ? XSRETURN_PV(msginfo->xref) : XSRETURN_UNDEF;
+ case 11:
+ (msginfo->extradata && msginfo->extradata->xface) ?
+ XSRETURN_PV(msginfo->extradata->xface) : XSRETURN_UNDEF;
+ case 12:
+ (msginfo->extradata && msginfo->extradata->dispositionnotificationto) ?
+ XSRETURN_PV(msginfo->extradata->dispositionnotificationto) : XSRETURN_UNDEF;
+ case 13:
+ (msginfo->extradata && msginfo->extradata->returnreceiptto) ?
+ XSRETURN_PV(msginfo->extradata->returnreceiptto) : XSRETURN_UNDEF;
+ case 14:
+ EXTEND(SP, g_slist_length(msginfo->references));
+ ii = 0;
+ for(walk = msginfo->references; walk != NULL; walk = g_slist_next(walk))
+ XST_mPV(ii++,walk->data ? (gchar*) walk->data: "");
+ ii ? XSRETURN(ii) : XSRETURN_UNDEF;
+ case 15:
+ msginfo->score ? XSRETURN_IV(msginfo->score) : XSRETURN_UNDEF;
+ case 17:
+ msginfo->plaintext_file ?
+ XSRETURN_PV(msginfo->plaintext_file) : XSRETURN_UNDEF;
+ case 19:
+ msginfo->hidden ? XSRETURN_IV(msginfo->hidden) : XSRETURN_UNDEF;
+ case 20:
+ if((charp = procmsg_get_message_file_path(msginfo)) != NULL) {
+ strncpy2(buf,charp,sizeof(buf));
+ g_free(charp);
+ XSRETURN_PV(buf);
+ }
+ else
+ XSRETURN_UNDEF;
+ case 21:
+ (msginfo->extradata && msginfo->extradata->partial_recv) ?
+ XSRETURN_PV(msginfo->extradata->partial_recv) : XSRETURN_UNDEF;
+ case 22:
+ msginfo->total_size ? XSRETURN_IV(msginfo->total_size) : XSRETURN_UNDEF;
+ case 23:
+ (msginfo->extradata && msginfo->extradata->account_server) ?
+ XSRETURN_PV(msginfo->extradata->account_server) : XSRETURN_UNDEF;
+ case 24:
+ (msginfo->extradata && msginfo->extradata->account_login) ?
+ XSRETURN_PV(msginfo->extradata->account_login) : XSRETURN_UNDEF;
+ case 25:
+ msginfo->planned_download ?
+ XSRETURN_IV(msginfo->planned_download) : XSRETURN_UNDEF;
+
+ /* general */
+ case 100:
+ if(manual_filtering)
+ XSRETURN_YES;
+ else
+ XSRETURN_NO;
+ default:
+ g_warning("Perl Plugin: Wrong argument to ClawsMail::C::init");
+ XSRETURN_UNDEF;
+ }
+}
+
+/* ClawsMail::C::open_mail_file */
+static XS(XS_ClawsMail_open_mail_file)
+{
+ char *file;
+ gchar buf[BUFFSIZE];
+
+ dXSARGS;
+ if(items != 0) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::open_mail_file");
+ XSRETURN_UNDEF;
+ }
+ file = procmsg_get_message_file_path(msginfo);
+ if(!file)
+ XSRETURN_UNDEF;
+ strncpy2(buf,file,sizeof(buf));
+ g_free(file);
+ if((message_file = fopen(buf, "rb")) == NULL) {
+ FILE_OP_ERROR(buf, "fopen");
+ g_warning("Perl Plugin: File open error in ClawsMail::C::open_mail_file");
+ XSRETURN_UNDEF;
+ }
+}
+
+/* ClawsMail::C::close_mail_file */
+static XS(XS_ClawsMail_close_mail_file)
+{
+ dXSARGS;
+ if(items != 0) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::close_mail_file");
+ XSRETURN_UNDEF;
+ }
+ if(message_file != NULL)
+ fclose(message_file);
+ XSRETURN_YES;
+}
+
+/* ClawsMail::C::get_next_header */
+static XS(XS_ClawsMail_get_next_header)
+{
+ gchar buf[BUFFSIZE];
+ Header *header;
+
+ dXSARGS;
+ if(items != 0) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::get_next_header");
+ XSRETURN_EMPTY;
+ }
+ if(message_file == NULL) {
+ g_warning("Perl Plugin: Message file not open. Use ClawsMail::C::open_message_file first.");
+ XSRETURN_EMPTY;
+ }
+ if(procheader_get_one_field(buf, sizeof(buf), message_file, NULL) != -1) {
+ header = procheader_parse_header(buf);
+ EXTEND(SP, 2);
+ if(header) {
+ XST_mPV(0,header->name);
+ XST_mPV(1,header->body);
+ procheader_header_free(header);
+ }
+ else {
+ XST_mPV(0,"");
+ XST_mPV(1,"");
+ }
+ XSRETURN(2);
+ }
+ else
+ XSRETURN_EMPTY;
+}
+
+/* ClawsMail::C::get_next_body_line */
+static XS(XS_ClawsMail_get_next_body_line)
+{
+ gchar buf[BUFFSIZE];
+
+ dXSARGS;
+ if(items != 0) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::get_next_body_line");
+ XSRETURN_UNDEF;
+ }
+ if(message_file == NULL) {
+ g_warning("Perl Plugin: Message file not open. Use ClawsMail::C::open_message_file first.");
+ XSRETURN_UNDEF;
+ }
+ if(fgets(buf, sizeof(buf), message_file) != NULL)
+ XSRETURN_PV(buf);
+ else
+ XSRETURN_UNDEF;
+}
+
+
+/* Filter matchers */
+
+/* ClawsMail::C::check_flag(int) */
+static XS(XS_ClawsMail_check_flag)
+{
+ int flag;
+ /* flags: 1 marked
+ * 2 unread
+ * 3 deleted
+ * 4 new
+ * 5 replied
+ * 6 forwarded
+ * 7 locked
+ * 8 ignore thread
+ */
+
+ dXSARGS;
+ if(items != 1) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::check_flag");
+ XSRETURN_UNDEF;
+ }
+ flag = SvIV(ST(0));
+
+ switch(flag) {
+ case 1:
+ if(MSG_IS_MARKED(msginfo->flags)) {
+ filter_log_write(LOG_MATCH,"marked");
+ XSRETURN_YES;
+ }
+ else
+ XSRETURN_NO;
+ case 2:
+ if(MSG_IS_UNREAD(msginfo->flags)) {
+ filter_log_write(LOG_MATCH,"unread");
+ XSRETURN_YES;
+ }
+ else
+ XSRETURN_NO;
+ case 3:
+ if(MSG_IS_DELETED(msginfo->flags)) {
+ filter_log_write(LOG_MATCH,"deleted");
+ XSRETURN_YES;
+ }
+ else
+ XSRETURN_NO;
+ case 4:
+ if(MSG_IS_NEW(msginfo->flags)) {
+ filter_log_write(LOG_MATCH,"new");
+ XSRETURN_YES;
+ }
+ else
+ XSRETURN_NO;
+ case 5:
+ if(MSG_IS_REPLIED(msginfo->flags)) {
+ filter_log_write(LOG_MATCH,"replied");
+ XSRETURN_YES;
+ }
+ else
+ XSRETURN_NO;
+ case 6:
+ if(MSG_IS_FORWARDED(msginfo->flags)) {
+ filter_log_write(LOG_MATCH,"forwarded");
+ XSRETURN_YES;
+ }
+ else
+ XSRETURN_NO;
+ case 7:
+ if(MSG_IS_LOCKED(msginfo->flags)) {
+ filter_log_write(LOG_MATCH,"locked");
+ XSRETURN_YES;
+ }
+ else
+ XSRETURN_NO;
+ case 8:
+ if(MSG_IS_IGNORE_THREAD(msginfo->flags)) {
+ filter_log_write(LOG_MATCH,"ignore_thread");
+ XSRETURN_YES;
+ }
+ else
+ XSRETURN_NO;
+ default:
+ g_warning("Perl Plugin: Unknown argument to ClawsMail::C::check_flag");
+ XSRETURN_UNDEF;
+ }
+}
+
+/* ClawsMail::C::colorlabel(int) */
+static XS(XS_ClawsMail_colorlabel)
+{
+ int color;
+
+ dXSARGS;
+ if(items != 1) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::colorlabel");
+ XSRETURN_UNDEF;
+ }
+ color = SvIV(ST(0));
+
+ if((MSG_GET_COLORLABEL_VALUE(msginfo->flags) == (guint32)color)) {
+ filter_log_write(LOG_MATCH,"colorlabel");
+ XSRETURN_YES;
+ }
+ else
+ XSRETURN_NO;
+}
+
+/* ClawsMail::C::age_greater(int) */
+static XS(XS_ClawsMail_age_greater)
+{
+ int age;
+ time_t t;
+
+ dXSARGS;
+ if(items != 1) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::age_greater");
+ XSRETURN_UNDEF;
+ }
+ age = SvIV(ST(0));
+ t = time(NULL);
+ if(((t - msginfo->date_t) / 86400) >= age) {
+ filter_log_write(LOG_MATCH,"age_greater");
+ XSRETURN_YES;
+ }
+ else
+ XSRETURN_NO;
+}
+
+/* ClawsMail::C::age_lower(int) */
+static XS(XS_ClawsMail_age_lower)
+{
+ int age;
+ time_t t;
+
+ dXSARGS;
+ if(items != 1) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::age_lower");
+ XSRETURN_UNDEF;
+ }
+ age = SvIV(ST(0));
+ t = time(NULL);
+ if(((t - msginfo->date_t) / 86400) <= age) {
+ filter_log_write(LOG_MATCH,"age_lower");
+ XSRETURN_YES;
+ }
+ else
+ XSRETURN_NO;
+}
+
+/* ClawsMail::C::tagged() */
+static XS(XS_ClawsMail_tagged)
+{
+ dXSARGS;
+ if(items != 0) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::tagged");
+ XSRETURN_UNDEF;
+ }
+
+ return msginfo->tags ? XSRETURN_YES : XSRETURN_NO;
+}
+
+/* ClawsMail::C::get_tags() */
+static XS(XS_ClawsMail_get_tags)
+{
+ guint iTag;
+ guint num_tags;
+ GSList *walk;
+
+ dXSARGS;
+ if(items != 0) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::get_tags");
+ XSRETURN_UNDEF;
+ }
+
+ num_tags = g_slist_length(msginfo->tags);
+
+ EXTEND(SP, num_tags);
+ iTag = 0;
+ for(walk = msginfo->tags; walk != NULL; walk = g_slist_next(walk)) {
+ const char *tag_str;
+ tag_str = tags_get_tag(GPOINTER_TO_INT(walk->data));
+ XST_mPV(iTag++, tag_str ? tag_str: "");
+ }
+
+ XSRETURN(num_tags);
+}
+
+
+
+/* ClawsMail::C::set_tag(char*) */
+static XS(XS_ClawsMail_set_tag)
+{
+ gchar *tag_str;
+ gint tag_id;
+
+ dXSARGS;
+ if(items != 1) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::set_tag");
+ XSRETURN_UNDEF;
+ }
+
+ tag_str = SvPV_nolen(ST(0));
+ tag_id = tags_get_id_for_str(tag_str);
+ if(tag_id == -1) {
+ g_warning("Perl Plugin: set_tag requested setting of a non-existing tag");
+ XSRETURN_UNDEF;
+ }
+
+ procmsg_msginfo_update_tags(msginfo, TRUE, tag_id);
+
+ XSRETURN_YES;
+}
+
+/* ClawsMail::C::unset_tag(char*) */
+static XS(XS_ClawsMail_unset_tag)
+{
+ gchar *tag_str;
+ gint tag_id;
+
+ dXSARGS;
+ if(items != 1) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::unset_tag");
+ XSRETURN_UNDEF;
+ }
+
+ tag_str = SvPV_nolen(ST(0));
+ tag_id = tags_get_id_for_str(tag_str);
+ if(tag_id == -1) {
+ g_warning("Perl Plugin: unset_tag requested setting of a non-existing tag");
+ XSRETURN_UNDEF;
+ }
+
+ procmsg_msginfo_update_tags(msginfo, FALSE, tag_id);
+
+ XSRETURN_YES;
+}
+
+/* ClawsMail::C::clear_tags() */
+static XS(XS_ClawsMail_clear_tags)
+{
+ dXSARGS;
+ if(items != 0) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::clear_tags");
+ XSRETURN_UNDEF;
+ }
+
+ procmsg_msginfo_clear_tags(msginfo);
+ XSRETURN_YES;
+}
+
+
+/* ClawsMail::C::make_sure_tag_exists(char*) */
+static XS(XS_ClawsMail_make_sure_tag_exists)
+{
+ gchar *tag_str;
+ gint tag_id;
+
+ dXSARGS;
+ if(items != 1) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::make_sure_tag_exists");
+ XSRETURN_UNDEF;
+ }
+
+ tag_str = SvPV_nolen(ST(0));
+
+ if(IS_NOT_RESERVED_TAG(tag_str) == FALSE) {
+ g_warning("Perl Plugin: Trying to create a tag with a reserved name: %s", tag_str);
+ XSRETURN_UNDEF;
+ }
+
+ tags_add_tag(tag_str);
+
+ XSRETURN_YES;
+}
+
+
+
+/* ClawsMail::C::make_sure_folder_exists(char*) */
+static XS(XS_ClawsMail_make_sure_folder_exists)
+{
+ gchar *identifier;
+ FolderItem *item;
+
+ dXSARGS;
+ if(items != 1) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::make_sure_folder_exists");
+ XSRETURN_UNDEF;
+ }
+
+ identifier = SvPV_nolen(ST(0));
+ item = folder_get_item_from_identifier(identifier);
+ if(item)
+ XSRETURN_YES;
+ else
+ XSRETURN_NO;
+}
+
+
+/* ClawsMail::C::addr_in_addressbook(char* [, char*]) */
+static XS(XS_ClawsMail_addr_in_addressbook)
+{
+ gchar *addr;
+ gchar *bookname;
+ gboolean found;
+
+ dXSARGS;
+ if(items != 1 && items != 2) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::addr_in_addressbook");
+ XSRETURN_UNDEF;
+ }
+
+ addr = SvPV_nolen(ST(0));
+
+ if(items == 1) {
+ found = addr_in_addressbook(addr,NULL);
+ }
+ else {
+ bookname = SvPV_nolen(ST(1));
+ found = addr_in_addressbook(addr,bookname);
+ }
+
+ if(found) {
+ filter_log_write(LOG_MATCH,"addr_in_addressbook");
+ XSRETURN_YES;
+ }
+ else
+ XSRETURN_NO;
+}
+
+
+/* Filter actions */
+
+/* ClawsMail::C::set_flag(int) */
+static XS(XS_ClawsMail_set_flag)
+{
+ int flag;
+ /* flags: 1 mark
+ * 2 mark as unread
+ * 7 lock
+ */
+
+ dXSARGS;
+ if(items != 1) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::set_flag");
+ XSRETURN_UNDEF;
+ }
+ flag = SvIV(ST(0));
+
+ switch(flag) {
+ case 1:
+ MSG_SET_PERM_FLAGS(msginfo->flags, MSG_MARKED);
+ procmsg_msginfo_set_flags(msginfo, MSG_MARKED,0);
+ filter_log_write(LOG_ACTION,"mark");
+ XSRETURN_YES;
+ case 2:
+ MSG_SET_PERM_FLAGS(msginfo->flags, MSG_UNREAD);
+ procmsg_msginfo_set_flags(msginfo, MSG_UNREAD,0);
+ filter_log_write(LOG_ACTION,"mark_as_unread");
+ XSRETURN_YES;
+ case 7:
+ MSG_SET_PERM_FLAGS(msginfo->flags, MSG_LOCKED);
+ procmsg_msginfo_set_flags(msginfo, MSG_LOCKED,0);
+ filter_log_write(LOG_ACTION,"lock");
+ XSRETURN_YES;
+ default:
+ g_warning("Perl Plugin: Unknown argument to ClawsMail::C::set_flag");
+ XSRETURN_UNDEF;
+ }
+}
+
+/* ClawsMail::C::unset_flag(int) */
+static XS(XS_ClawsMail_unset_flag)
+{
+ int flag;
+ /*
+ * flags: 1 unmark
+ * 2 mark as read
+ * 7 unlock
+ */
+
+ dXSARGS;
+ if(items != 1) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::unset_flag");
+ XSRETURN_UNDEF;
+ }
+ flag = SvIV(ST(0));
+
+ switch(flag) {
+ case 1:
+ MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_MARKED);
+ procmsg_msginfo_unset_flags(msginfo, MSG_MARKED,0);
+ filter_log_write(LOG_ACTION,"unmark");
+ XSRETURN_YES;
+ case 2:
+ MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_UNREAD | MSG_NEW);
+ procmsg_msginfo_unset_flags(msginfo, MSG_UNREAD | MSG_NEW,0);
+ filter_log_write(LOG_ACTION,"mark_as_read");
+ XSRETURN_YES;
+ case 7:
+ MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_LOCKED);
+ procmsg_msginfo_unset_flags(msginfo, MSG_LOCKED,0);
+ filter_log_write(LOG_ACTION,"unlock");
+ XSRETURN_YES;
+ default:
+ g_warning("Perl Plugin: Unknown argument to ClawsMail::C::unset_flag");
+ XSRETURN_UNDEF;
+ }
+}
+
+/* ClawsMail::C::move(char*) */
+static XS(XS_ClawsMail_move)
+{
+ gchar *targetfolder;
+ gchar *logtext;
+ FolderItem *dest_folder;
+
+ dXSARGS;
+ if(items != 1) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::move");
+ XSRETURN_UNDEF;
+ }
+
+ targetfolder = SvPV_nolen(ST(0));
+ dest_folder = folder_find_item_from_identifier(targetfolder);
+
+ if (!dest_folder) {
+ g_warning("Perl Plugin: move: folder not found '%s'",
+ targetfolder ? targetfolder :"");
+ XSRETURN_UNDEF;
+ }
+ if (folder_item_move_msg(dest_folder, msginfo) == -1) {
+ g_warning("Perl Plugin: move: could not move message");
+ XSRETURN_UNDEF;
+ }
+ stop_filtering = TRUE;
+ logtext = g_strconcat("move to ", targetfolder, NULL);
+ filter_log_write(LOG_ACTION, logtext);
+ g_free(logtext);
+ XSRETURN_YES;
+}
+
+/* ClawsMail::C::copy(char*) */
+static XS(XS_ClawsMail_copy)
+{
+ char *targetfolder;
+ gchar *logtext;
+ FolderItem *dest_folder;
+
+ dXSARGS;
+ if(items != 1) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::copy");
+ XSRETURN_UNDEF;
+ }
+ targetfolder = SvPV_nolen(ST(0));
+ dest_folder = folder_find_item_from_identifier(targetfolder);
+
+ if (!dest_folder) {
+ g_warning("Perl Plugin: copy: folder not found '%s'",
+ targetfolder ? targetfolder :"");
+ XSRETURN_UNDEF;
+ }
+ if (folder_item_copy_msg(dest_folder, msginfo) == -1) {
+ g_warning("Perl Plugin: copy: could not copy message");
+ XSRETURN_UNDEF;
+ }
+ logtext = g_strconcat("copy to ", targetfolder, NULL);
+ filter_log_write(LOG_ACTION, logtext);
+ g_free(logtext);
+ XSRETURN_YES;
+}
+
+/* ClawsMail::C::delete */
+static XS(XS_ClawsMail_delete)
+{
+ dXSARGS;
+ if(items != 0) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::delete");
+ XSRETURN_UNDEF;
+ }
+ folder_item_remove_msg(msginfo->folder, msginfo->msgnum);
+ stop_filtering = TRUE;
+ filter_log_write(LOG_ACTION, "delete");
+ XSRETURN_YES;
+}
+
+/* ClawsMail::C::hide */
+static XS(XS_ClawsMail_hide)
+{
+ dXSARGS;
+ if(items != 0) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::hide");
+ XSRETURN_UNDEF;
+ }
+ msginfo->hidden = TRUE;
+ filter_log_write(LOG_ACTION, "hide");
+ XSRETURN_YES;
+}
+
+
+/* ClawsMail::C::color(int) */
+static XS(XS_ClawsMail_color)
+{
+ int color;
+ gchar *logtext;
+
+ dXSARGS;
+ if(items != 1) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::color");
+ XSRETURN_UNDEF;
+ }
+ color = SvIV(ST(0));
+ procmsg_msginfo_unset_flags(msginfo, MSG_CLABEL_FLAG_MASK, 0);
+ procmsg_msginfo_set_flags(msginfo, MSG_COLORLABEL_TO_FLAGS(color), 0);
+ MSG_SET_COLORLABEL_VALUE(msginfo->flags,color);
+
+ logtext = g_strdup_printf("color: %d", color);
+ filter_log_write(LOG_ACTION, logtext);
+ g_free(logtext);
+
+ XSRETURN_YES;
+}
+
+/* ClawsMail::C::change_score(int) */
+static XS(XS_ClawsMail_change_score)
+{
+ int score;
+ gchar *logtext;
+
+ dXSARGS;
+ if(items != 1) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::change_score");
+ XSRETURN_UNDEF;
+ }
+ score = SvIV(ST(0));
+ msginfo->score += score;
+
+ logtext = g_strdup_printf("change score: %+d", score);
+ filter_log_write(LOG_ACTION, logtext);
+ g_free(logtext);
+
+ XSRETURN_IV(msginfo->score);
+}
+
+/* ClawsMail::C::set_score(int) */
+static XS(XS_ClawsMail_set_score)
+{
+ int score;
+ gchar *logtext;
+
+ dXSARGS;
+ if(items != 1) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::set_score");
+ XSRETURN_UNDEF;
+ }
+ score = SvIV(ST(0));
+ msginfo->score = score;
+
+ logtext = g_strdup_printf("set score: %d", score);
+ filter_log_write(LOG_ACTION, logtext);
+ g_free(logtext);
+
+ XSRETURN_IV(msginfo->score);
+}
+
+/* ClawsMail::C::forward(int,int,char*) */
+static XS(XS_ClawsMail_forward)
+{
+ int flag;
+ /* flags: 1 forward
+ * 2 forward as attachment
+ */
+ int account_id,val;
+ char *dest;
+ gchar *logtext;
+ PrefsAccount *account;
+ Compose *compose;
+
+ dXSARGS;
+ if(items != 3) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::forward");
+ XSRETURN_UNDEF;
+ }
+
+ flag = SvIV(ST(0));
+ account_id = SvIV(ST(1));
+ dest = SvPV_nolen(ST(2));
+
+ account = account_find_from_id(account_id);
+ compose = compose_forward(account, msginfo,
+ flag == 1 ? FALSE : TRUE,
+ NULL, TRUE, TRUE);
+ compose_entry_append(compose, dest,
+ compose->account->protocol == A_NNTP ?
+ COMPOSE_NEWSGROUPS : COMPOSE_TO, PREF_NONE);
+
+ val = compose_send(compose);
+
+ if(val == 0) {
+
+ logtext = g_strdup_printf("forward%s to %s",
+ flag==2 ? " as attachment" : "",
+ dest ? dest : "<unknown destination>");
+ filter_log_write(LOG_ACTION, logtext);
+ g_free(logtext);
+
+ XSRETURN_YES;
+ }
+ else
+ XSRETURN_UNDEF;
+}
+
+/* ClawsMail::C::redirect(int,char*) */
+static XS(XS_ClawsMail_redirect)
+{
+ int account_id,val;
+ char *dest;
+ gchar *logtext;
+ PrefsAccount *account;
+ Compose *compose;
+
+ dXSARGS;
+ if(items != 2) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::redirect");
+ XSRETURN_UNDEF;
+ }
+
+ account_id = SvIV(ST(0));
+ dest = SvPV_nolen(ST(1));
+
+ account = account_find_from_id(account_id);
+ compose = compose_redirect(account, msginfo, TRUE);
+
+ if (compose->account->protocol == A_NNTP)
+ XSRETURN_UNDEF;
+ else
+ compose_entry_append(compose, dest, COMPOSE_TO, PREF_NONE);
+
+ val = compose_send(compose);
+
+ if(val == 0) {
+
+ logtext = g_strdup_printf("redirect to %s",
+ dest ? dest : "<unknown destination>");
+ filter_log_write(LOG_ACTION, logtext);
+ g_free(logtext);
+
+ XSRETURN_YES;
+ }
+ else
+ XSRETURN_UNDEF;
+}
+
+
+/* Utilities */
+
+/* ClawsMail::C::move_to_trash */
+static XS(XS_ClawsMail_move_to_trash)
+{
+ FolderItem *dest_folder;
+
+ dXSARGS;
+ if(items != 0) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::move_to_trash");
+ XSRETURN_UNDEF;
+ }
+ dest_folder = folder_get_default_trash();
+ if (!dest_folder) {
+ g_warning("Perl Plugin: move_to_trash: Trash folder not found");
+ XSRETURN_UNDEF;
+ }
+ if (folder_item_move_msg(dest_folder, msginfo) == -1) {
+ g_warning("Perl Plugin: move_to_trash: could not move message to trash");
+ XSRETURN_UNDEF;
+ }
+ stop_filtering = TRUE;
+ filter_log_write(LOG_ACTION, "move_to_trash");
+ XSRETURN_YES;
+}
+
+/* ClawsMail::C::abort */
+static XS(XS_ClawsMail_abort)
+{
+ FolderItem *inbox_folder;
+
+ dXSARGS;
+ if(items != 0) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::abort");
+ XSRETURN_UNDEF;
+ }
+ if(!manual_filtering) {
+ inbox_folder = folder_get_default_inbox();
+ if (!inbox_folder) {
+ g_warning("Perl Plugin: abort: Inbox folder not found");
+ XSRETURN_UNDEF;
+ }
+ if (folder_item_move_msg(inbox_folder, msginfo) == -1) {
+ g_warning("Perl Plugin: abort: Could not move message to default inbox");
+ XSRETURN_UNDEF;
+ }
+ filter_log_write(LOG_ACTION, "abort -- message moved to default inbox");
+ }
+ else
+ filter_log_write(LOG_ACTION, "abort");
+
+ stop_filtering = TRUE;
+ XSRETURN_YES;
+}
+
+/* ClawsMail::C::get_attribute_value(char*,char*[,char*]) */
+static XS(XS_ClawsMail_get_attribute_value)
+{
+ char *addr;
+ char *attr;
+ char *attribute_value;
+ char *bookname;
+
+ dXSARGS;
+ if(items != 2 && items != 3) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::get_attribute_value");
+ XSRETURN_UNDEF;
+ }
+ addr = SvPV_nolen(ST(0));
+ attr = SvPV_nolen(ST(1));
+
+ if(items == 2)
+ attribute_value = get_attribute_value(addr,attr,NULL);
+ else {
+ bookname = SvPV_nolen(ST(2));
+ attribute_value = get_attribute_value(addr,attr,bookname);
+ }
+
+ if(attribute_value)
+ XSRETURN_PV(attribute_value);
+ XSRETURN_PV("");
+}
+
+/* ClawsMail::C::filter_log(char*,char*) */
+static XS(XS_ClawsMail_filter_log)
+{
+ char *text;
+ char *type;
+
+ dXSARGS;
+ if(items != 2) {
+ g_warning("Perl Plugin: Wrong number of arguments to ClawsMail::C::filter_log");
+ XSRETURN_UNDEF;
+ }
+ type = SvPV_nolen(ST(0));
+ text = SvPV_nolen(ST(1));
+ if(!strcmp(type, "LOG_ACTION"))
+ filter_log_write(LOG_ACTION, text);
+ else if(!strcmp(type, "LOG_MANUAL"))
+ filter_log_write(LOG_MANUAL, text);
+ else if(!strcmp(type, "LOG_MATCH"))
+ filter_log_write(LOG_MATCH, text);
+ else {
+ g_warning("Perl Plugin: ClawsMail::C::filter_log -- wrong first argument");
+ XSRETURN_UNDEF;
+ }
+ XSRETURN_YES;
+}
+
+/* ClawsMail::C::filter_log_verbosity(int) */
+static XS(XS_ClawsMail_filter_log_verbosity)
+{
+ int retval;
+
+ dXSARGS;
+ if(items != 1 && items != 0) {
+ g_warning("Perl Plugin: Wrong number of arguments to "
+ "ClawsMail::C::filter_log_verbosity");
+ XSRETURN_UNDEF;
+ }
+ retval = filter_log_verbosity;
+
+ if(items == 1)
+ filter_log_verbosity = SvIV(ST(0));
+
+ XSRETURN_IV(retval);
+}
+
+/* register extensions */
+EXTERN_C void xs_init(pTHX)
+{
+ char *file = __FILE__;
+ dXSUB_SYS;
+ newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
+ newXS("ClawsMail::C::filter_init", XS_ClawsMail_filter_init, "ClawsMail::C");
+ newXS("ClawsMail::C::check_flag", XS_ClawsMail_check_flag, "ClawsMail::C");
+ newXS("ClawsMail::C::age_greater", XS_ClawsMail_age_greater, "ClawsMail::C");
+ newXS("ClawsMail::C::age_lower", XS_ClawsMail_age_lower, "ClawsMail::C");
+ newXS("ClawsMail::C::tagged", XS_ClawsMail_tagged, "ClawsMail::C");
+ newXS("ClawsMail::C::set_flag", XS_ClawsMail_set_flag, "ClawsMail::C");
+ newXS("ClawsMail::C::unset_flag", XS_ClawsMail_unset_flag, "ClawsMail::C");
+ newXS("ClawsMail::C::delete", XS_ClawsMail_delete, "ClawsMail::C");
+ newXS("ClawsMail::C::move", XS_ClawsMail_move, "ClawsMail::C");
+ newXS("ClawsMail::C::copy", XS_ClawsMail_copy, "ClawsMail::C");
+ newXS("ClawsMail::C::color", XS_ClawsMail_color, "ClawsMail::C");
+ newXS("ClawsMail::C::colorlabel", XS_ClawsMail_colorlabel, "ClawsMail::C");
+ newXS("ClawsMail::C::change_score", XS_ClawsMail_change_score, "ClawsMail::C");
+ newXS("ClawsMail::C::set_score", XS_ClawsMail_set_score, "ClawsMail::C");
+ newXS("ClawsMail::C::hide", XS_ClawsMail_hide, "ClawsMail::C");
+ newXS("ClawsMail::C::forward", XS_ClawsMail_forward, "ClawsMail::C");
+ newXS("ClawsMail::C::redirect", XS_ClawsMail_redirect, "ClawsMail::C");
+ newXS("ClawsMail::C::set_tag", XS_ClawsMail_set_tag, "ClawsMail::C");
+ newXS("ClawsMail::C::unset_tag", XS_ClawsMail_unset_tag, "ClawsMail::C");
+ newXS("ClawsMail::C::clear_tags", XS_ClawsMail_clear_tags, "ClawsMail::C");
+ newXS("ClawsMail::C::make_sure_folder_exists",
+ XS_ClawsMail_make_sure_folder_exists,"ClawsMail::C");
+ newXS("ClawsMail::C::make_sure_tag_exists", XS_ClawsMail_make_sure_tag_exists,"ClawsMail::C");
+ newXS("ClawsMail::C::get_tags", XS_ClawsMail_get_tags,"ClawsMail::C");
+ newXS("ClawsMail::C::addr_in_addressbook",
+ XS_ClawsMail_addr_in_addressbook,"ClawsMail::C");
+ newXS("ClawsMail::C::open_mail_file",
+ XS_ClawsMail_open_mail_file,"ClawsMail::C");
+ newXS("ClawsMail::C::close_mail_file",
+ XS_ClawsMail_close_mail_file,"ClawsMail::C");
+ newXS("ClawsMail::C::get_next_header",
+ XS_ClawsMail_get_next_header,"ClawsMail::C");
+ newXS("ClawsMail::C::get_next_body_line",
+ XS_ClawsMail_get_next_body_line,"ClawsMail::C");
+ newXS("ClawsMail::C::move_to_trash",XS_ClawsMail_move_to_trash,"ClawsMail::C");
+ newXS("ClawsMail::C::abort", XS_ClawsMail_abort, "ClawsMail::C");
+ newXS("ClawsMail::C::get_attribute_value",
+ XS_ClawsMail_get_attribute_value,"ClawsMail::C");
+ newXS("ClawsMail::C::filter_log", XS_ClawsMail_filter_log, "ClawsMail::C");
+ newXS("ClawsMail::C::filter_log_verbosity",
+ XS_ClawsMail_filter_log_verbosity, "ClawsMail::C");
+}
+
+/*
+ * The workhorse.
+ * Returns: 0 on success
+ * 1 error in scriptfile or invocation of external
+ * editor -> retry
+ * 2 error in scriptfile -> abort
+ * (Yes, I know..)
+ */
+static int perl_load_file(void)
+{
+ gchar *args[] = {"", DO_CLEAN, NULL};
+ gchar *noargs[] = { NULL };
+ gchar *perlfilter;
+ gchar **cmdline;
+ gchar buf[1024];
+ gchar *pp;
+ STRLEN n_a;
+
+ call_argv("ClawsMail::Filter::Matcher::filter_init_",
+ G_DISCARD | G_EVAL | G_NOARGS,noargs);
+ /* check $@ */
+ if(SvTRUE(ERRSV)) {
+ debug_print("%s", SvPV(ERRSV,n_a));
+ return 1;
+ }
+ perlfilter = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, PERLFILTER, NULL);
+ args[0] = perlfilter;
+ call_argv("ClawsMail::Persistent::eval_file",
+ G_DISCARD | G_EVAL, args);
+ g_free(perlfilter);
+ if(SvTRUE(ERRSV)) {
+ AlertValue val;
+ gchar *message;
+
+ if(strstr(SvPV(ERRSV,n_a),"intended"))
+ return 0;
+
+ debug_print("%s", SvPV(ERRSV,n_a));
+ message = g_strdup_printf("Error processing Perl script file: "
+ "(line numbers may not be valid)\n%s",
+ SvPV(ERRSV,n_a));
+ val = alertpanel("Perl Plugin error",message,"Retry","Abort","Edit");
+ g_free(message);
+
+ if(val == G_ALERTOTHER) {
+ /* Open PERLFILTER in an external editor */
+ perlfilter = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, PERLFILTER, NULL);
+ if (prefs_common_get_ext_editor_cmd() &&
+ (pp = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
+ *(pp + 1) == 's' && !strchr(pp + 2, '%')) {
+ g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), perlfilter);
+ }
+ else {
+ if (prefs_common_get_ext_editor_cmd())
+ g_warning("Perl Plugin: External editor command-line is invalid: `%s'",
+ prefs_common_get_ext_editor_cmd());
+ g_snprintf(buf, sizeof(buf), "emacs %s", perlfilter);
+ }
+ g_free(perlfilter);
+ cmdline = strsplit_with_quote(buf, " ", 1024);
+ execute_detached(cmdline);
+ g_strfreev(cmdline);
+ return 1;
+ }
+ else if(val == G_ALERTDEFAULT)
+ return 1;
+ else
+ return 2;
+ }
+
+ return 0;
+}
+
+
+/* let there be magic */
+static int perl_init(void)
+{
+ int exitstatus;
+ char *initialize[] = { "", "-w", "-e", "1;"};
+ /* The `persistent' module is taken from the Perl documentation
+ and has only slightly been modified. */
+ const char perl_persistent[] = {
+"package ClawsMail::Persistent;\n"
+"\n"
+"use strict;\n"
+"our %Cache;\n"
+"use Symbol qw(delete_package);\n"
+"\n"
+"sub valid_package_name {\n"
+" my($string) = @_;\n"
+" $string =~ s/([^A-Za-z0-9\\/])/sprintf(\"_%2x\",unpack(\"C\",$1))/eg;\n"
+" # second pass only for words starting with a digit\n"
+" $string =~ s|/(\\d)|sprintf(\"/_%2x\",unpack(\"C\",$1))|eg;\n"
+" \n"
+" # Dress it up as a real package name\n"
+" $string =~ s|/|::|g;\n"
+" return \"ClawsMail\" . $string;\n"
+"}\n"
+"\n"
+"sub eval_file {\n"
+" my($file, $delete) = @_;\n"
+" my $package = valid_package_name($file);\n"
+" my $mtime = -M $file;\n"
+" if(!(defined $Cache{$package}{mtime} &&\n"
+" $Cache{$package}{mtime} <= $mtime)) {\n"
+" delete_package($package) if defined $Cache{$package}{mtime};\n"
+" local *FH;\n"
+" open FH, $file or die \"Failed to open '$file': $!\";\n"
+" local($/) = undef;\n"
+" my $sub = <FH>;\n"
+" close FH;\n"
+" #wrap the code into a subroutine inside our unique package\n"
+" my $eval = qq{package $package;\n"
+" use ClawsMail::Filter::Matcher;\n"
+" use ClawsMail::Filter::Action;\n"
+" use ClawsMail::Utils;\n"
+" sub handler { $sub; }};\n"
+" {\n"
+" # hide our variables within this block\n"
+" my($file,$mtime,$package,$sub);\n"
+" eval $eval;\n"
+" }\n"
+" die $@ if $@;\n"
+" #cache it unless we're cleaning out each time\n"
+" $Cache{$package}{mtime} = $mtime unless $delete;\n"
+" }\n"
+" eval {$package->handler;};\n"
+" die $@ if $@;\n"
+" delete_package($package) if $delete;\n"
+"}\n"
+ };
+ const char perl_filter_matcher[] = {
+"BEGIN {$INC{'ClawsMail/Filter/Matcher.pm'} = 1;}\n"
+"package ClawsMail::Filter::Matcher;\n"
+"use locale;\n"
+"use base qw(Exporter);\n"
+"use strict;\n"
+"our @EXPORT = (qw(header body filepath manual),\n"
+" qw(filter_log_verbosity filter_log),\n"
+" qw(all marked unread deleted new replied),\n"
+" qw(forwarded locked colorlabel match matchcase),\n"
+" qw(regexp regexpcase test),\n"
+" qw(to cc subject from to_or_cc newsgroups inreplyto),\n"
+" qw(references body_part headers_part message),\n"
+" qw(size_greater size_smaller size_equal),\n"
+" qw(score_greater score_lower score_equal),\n"
+" qw(age_greater age_lower partial tagged $permanent));\n"
+"# Global Variables\n"
+"our(%header,$body,%msginfo,$mail_done,$manual);\n"
+"our %colors = ('none' => 0,'orange' => 1,'red' => 2,\n"
+" 'pink' => 3,'sky blue' => 4,'blue' => 5,\n"
+" 'green' => 6,'brown' => 7);\n"
+"# For convenience\n"
+"sub lc2_ {\n"
+" my $arg = shift;\n"
+" if(defined $arg) {\n"
+" return lc $arg;\n"
+" }\n"
+" else {\n"
+" return \"\";\n"
+" }\n"
+"}\n"
+"sub to { return \"to\"; }\n"
+"sub cc { return \"cc\"; }\n"
+"sub from { return \"from\"; }\n"
+"sub subject { return \"subject\"; }\n"
+"sub to_or_cc { return \"to_or_cc\"; }\n"
+"sub newsgroups { return \"newsgroups\"; }\n"
+"sub inreplyto { return \"in-reply-to\"; }\n"
+"sub references { return \"references\"; }\n"
+"sub body_part { return \"body_part\"; }\n"
+"sub headers_part { return \"headers_part\"; }\n"
+"sub message { return \"message\"; }\n"
+"# access the mail directly\n"
+"sub header {\n"
+" my $key = shift;\n"
+" if(not defined $key) {\n"
+" init_();\n"
+" return keys %header;\n"
+" }\n"
+" $key = lc2_ $key; $key =~ s/:$//;\n"
+" init_() unless exists $header{$key};\n"
+" if(exists $header{$key}) {\n"
+" wantarray ? return @{$header{$key}} : return $header{$key}->[-1];\n"
+" }\n"
+" return undef;\n"
+"}\n"
+"sub body {init_();return $body;}\n"
+"sub filepath {return $msginfo{\"filepath\"};}\n"
+"sub manual {\n"
+" ClawsMail::C::filter_log(\"LOG_MATCH\",\"manual\") if $manual;\n"
+" return $manual;\n"
+"}\n"
+"sub filter_log {\n"
+" my $arg1 = shift;\n"
+" my $arg2 = shift;\n"
+" return ClawsMail::C::filter_log($arg1,$arg2)\n"
+" if defined($arg2);\n"
+" return ClawsMail::C::filter_log(\"LOG_MANUAL\",$arg1);\n"
+"}\n"
+"sub filter_log_verbosity {\n"
+" $_ = shift;\n"
+" return ClawsMail::C::filter_log_verbosity($_)\n"
+" if defined($_);\n"
+" return ClawsMail::C::filter_log_verbosity();\n"
+"}\n"
+"# Public Matcher Tests\n"
+"sub all { ClawsMail::C::filter_log(\"LOG_MATCH\",\"all\");return 1; }\n"
+"sub marked { return ClawsMail::C::check_flag(1);}\n"
+"sub unread { return ClawsMail::C::check_flag(2);}\n"
+"sub deleted { return ClawsMail::C::check_flag(3);}\n"
+"sub new { return ClawsMail::C::check_flag(4);}\n"
+"sub replied { return ClawsMail::C::check_flag(5);}\n"
+"sub forwarded { return ClawsMail::C::check_flag(6);}\n"
+"sub locked { return ClawsMail::C::check_flag(7);}\n"
+"sub ignore_thread { return ClawsMail::C::check_flag(8);}\n"
+"sub age_greater {return ClawsMail::C::age_greater(@_);}\n"
+"sub age_lower {return ClawsMail::C::age_lower(@_); }\n"
+"sub tagged {return ClawsMail::C::tagged(@_); }\n"
+"sub score_equal {\n"
+" my $my_score = shift;\n"
+" return 0 unless (defined($msginfo{\"score\"}) and defined($my_score));\n"
+" if($my_score == $msginfo{\"score\"}) {\n"
+" ClawsMail::C::filter_log(\"LOG_MATCH\",\"score_equal\");\n"
+" return 1;\n"
+" }else{return 0;}\n"
+"}\n"
+"sub score_greater {\n"
+" my $my_score = shift;\n"
+" return 0 unless (defined($msginfo{\"score\"}) and defined($my_score));\n"
+" if($msginfo{\"score\"} > $my_score) {\n"
+" ClawsMail::C::filter_log(\"LOG_MATCH\",\"score_greater\");\n"
+" return 1;\n"
+" }else{return 0;}\n"
+"}\n"
+"sub score_lower {\n"
+" my $my_score = shift;\n"
+" return 0 unless (defined($msginfo{\"score\"}) and defined($my_score));\n"
+" if($msginfo{\"score\"} < $my_score) {\n"
+" ClawsMail::C::filter_log(\"LOG_MATCH\",\"score_lower\");\n"
+" return 1;\n"
+" }else{return 0;}\n"
+"}\n"
+"sub colorlabel {\n"
+" my $color = shift;\n"
+" $color = lc2_ $color;\n"
+" $color = $colors{$color} if exists $colors{$color};\n"
+" $color = 0 if $color =~ m/\\D/;\n"
+" return ClawsMail::C::colorlabel($color);\n"
+"}\n"
+"sub size_greater {\n"
+" my $my_size = shift;\n"
+" return 0 unless (defined($msginfo{\"size\"}) and defined($my_size));\n"
+" if($msginfo{\"size\"} > $my_size) {\n"
+" ClawsMail::C::filter_log(\"LOG_MATCH\",\"size_greater\");\n"
+" return 1;\n"
+" }else{return 0;}\n"
+"}\n"
+"sub size_smaller {\n"
+" my $my_size = shift;\n"
+" return 0 unless (defined($msginfo{\"size\"}) and defined($my_size));\n"
+" if($msginfo{\"size\"} < $my_size) {\n"
+" ClawsMail::C::filter_log(\"LOG_MATCH\",\"size_smaller\");\n"
+" return 1;\n"
+" }else{return 0;}\n"
+"}\n"
+"sub size_equal {\n"
+" my $my_size = shift;\n"
+" return 0 unless (defined($msginfo{\"size\"}) and defined($my_size));\n"
+" if($msginfo{\"size\"} == $my_size) {\n"
+" ClawsMail::C::filter_log(\"LOG_MATCH\",\"size_equal\");\n"
+" return 1;\n"
+" }else{return 0;}\n"
+"}\n"
+"sub partial {\n"
+" return 0 unless defined($msginfo{\"total_size\"})\n"
+" and defined($msginfo{\"size\"});\n"
+" if($msginfo{\"total_size\"} != 0\n"
+" && $msginfo{\"size\"} != $msginfo{\"total_size\"}) {\n"
+" ClawsMail::C::filter_log(\"LOG_MATCH\",\"partial\");\n"
+" return 1;\n"
+" }else{return 0;}\n"
+"}\n"
+"sub test {\n"
+" $_ = shift; my $command = \"\"; my $hl=\"\"; my $re=\"\"; my $retval;\n"
+" my $cmdline = $_;\n"
+" s/\\\"/\"/g; #fool stupid emacs perl mode\";\n"
+" s/([^%]*)//; $command .= $1;\n"
+" while($_) {\n"
+" if (/^%%/){s/^%%([^%]*)//;$command .= \"\\\\%\".$1; next;}\n"
+" elsif(/^%s/){s/^%s([^%]*)//;$hl=header(\"subject\");$re=$1;}\n"
+" elsif(/^%f/){s/^%f([^%]*)//;$hl=header(\"from\");$re=$1;}\n"
+" elsif(/^%t/){s/^%t([^%]*)//;$hl=header(\"to\");$re=$1;}\n"
+" elsif(/^%c/){s/^%c([^%]*)//;$hl=header(\"cc\");$re=$1;}\n"
+" elsif(/^%d/){s/^%d([^%]*)//;$hl=header(\"date\");$re=$1;}\n"
+" elsif(/^%i/){s/^%i([^%]*)//;$hl=header(\"message-id\");$re=$1;}\n"
+" elsif(/^%n/){s/^%n([^%]*)//;$hl=header(\"newsgroups\");$re=$1;}\n"
+" elsif(/^%r/){s/^%r([^%]*)//;$hl=header(\"references\");$re=$1;}\n"
+" elsif(/^%F/){s/^%F([^%]*)//;$hl=filepath();$re=$1;}\n"
+" else {s/^(%[^%]*)//; $command .= $1;}\n"
+" $command .= \"\\Q$hl\\E\" if defined $hl;$hl=\"\";\n"
+" $command .= $re;$re=\"\";\n"
+" }\n"
+" $retval = !(system($command)>>8);\n"
+" ClawsMail::C::filter_log(\"LOG_MATCH\",\"test: $cmdline\")\n"
+" if $retval;\n"
+" return $retval;\n"
+"}\n"
+"sub matchcase {\n"
+" my $retval;\n"
+" $retval = match_(@_,\"i\");\n"
+" ClawsMail::C::filter_log(\"LOG_MATCH\",\"matchcase: $_[0], $_[1]\")\n"
+" if $retval;\n"
+" return $retval;\n"
+"}\n"
+"sub match {\n"
+" my $retval;\n"
+" $retval = match_(@_);\n"
+" ClawsMail::C::filter_log(\"LOG_MATCH\",\"match: $_[0], $_[1]\")\n"
+" if $retval;\n"
+" return $retval;\n"
+"}\n"
+"sub regexpcase {\n"
+" my $retval;\n"
+" $retval = match_(@_,\"ri\");\n"
+" ClawsMail::C::filter_log(\"LOG_MATCH\",\"regexpcase: $_[0], $_[1]\")\n"
+" if $retval;\n"
+" return $retval;\n"
+"}\n"
+"sub regexp {\n"
+" my $retval;\n"
+" $retval = match_(@_,\"r\");\n"
+" ClawsMail::C::filter_log(\"LOG_MATCH\",\"regexp: $_[0], $_[1]\")\n"
+" if $retval;\n"
+" return $retval;\n"
+"}\n"
+"# Internals\n"
+"sub add_header_entries_ {\n"
+" my($key,@values) = @_; $key = lc2_ $key; $key =~ s/:$//;\n"
+" $header{$key} = [] unless exists $header{$key};\n"
+" push @{$header{$key}},@values;\n"
+"}\n"
+"# read whole mail\n"
+"sub init_ {\n"
+" return 0 if $mail_done;\n"
+" ClawsMail::C::open_mail_file();\n"
+" read_headers_();\n"
+" read_body_();\n"
+" ClawsMail::C::close_mail_file();\n"
+" $mail_done = 1;\n"
+"}\n"
+"sub filter_init_ {\n"
+" %header = (); %msginfo = (); undef $body; $mail_done = 0;\n"
+" $manual = ClawsMail::C::filter_init(100);\n"
+" $msginfo{\"size\"} = ClawsMail::C::filter_init( 1) ;\n"
+" add_header_entries_(\"date\", ClawsMail::C::filter_init( 2));\n"
+" add_header_entries_(\"from\", ClawsMail::C::filter_init( 3));\n"
+" add_header_entries_(\"to\", ClawsMail::C::filter_init( 4));\n"
+" add_header_entries_(\"cc\", ClawsMail::C::filter_init( 5));\n"
+" add_header_entries_(\"newsgroups\",ClawsMail::C::filter_init( 6));\n"
+" add_header_entries_(\"subject\", ClawsMail::C::filter_init( 7));\n"
+" add_header_entries_(\"msgid\", ClawsMail::C::filter_init( 8));\n"
+" add_header_entries_(\"inreplyto\", ClawsMail::C::filter_init( 9));\n"
+" add_header_entries_(\"xref\", ClawsMail::C::filter_init(10));\n"
+" add_header_entries_(\"xface\", ClawsMail::C::filter_init(11));\n"
+" add_header_entries_(\"dispositionnotificationto\",\n"
+" ClawsMail::C::filter_init(12));\n"
+" add_header_entries_(\"returnreceiptto\",\n"
+" ClawsMail::C::filter_init(13));\n"
+" add_header_entries_(\"references\",ClawsMail::C::filter_init(14));\n"
+" $msginfo{\"score\"} = ClawsMail::C::filter_init(15);\n"
+" $msginfo{\"plaintext_file\"} = ClawsMail::C::filter_init(17);\n"
+" $msginfo{\"hidden\"} = ClawsMail::C::filter_init(19);\n"
+" $msginfo{\"filepath\"} = ClawsMail::C::filter_init(20);\n"
+" $msginfo{\"partial_recv\"} = ClawsMail::C::filter_init(21);\n"
+" $msginfo{\"total_size\"} = ClawsMail::C::filter_init(22);\n"
+" $msginfo{\"account_server\"} = ClawsMail::C::filter_init(23);\n"
+" $msginfo{\"account_login\"} = ClawsMail::C::filter_init(24);\n"
+" $msginfo{\"planned_download\"} = ClawsMail::C::filter_init(25);\n"
+"} \n"
+"sub read_headers_ {\n"
+" my($key,$value);\n"
+" %header = ();\n"
+" while(($key,$value) = ClawsMail::C::get_next_header()) {\n"
+" next unless $key =~ /:$/;\n"
+" add_header_entries_($key,$value);\n"
+" }\n"
+"}\n"
+"sub read_body_ {\n"
+" my $line;\n"
+" while(defined($line = ClawsMail::C::get_next_body_line())) {\n"
+" $body .= $line;\n"
+" } \n"
+"}\n"
+"sub match_ {\n"
+" my ($where,$what,$modi) = @_; $modi ||= \"\";\n"
+" my $nocase=\"\"; $nocase = \"1\" if (index($modi,\"i\") != -1);\n"
+" my $regexp=\"\"; $regexp = \"1\" if (index($modi,\"r\") != -1);\n"
+" if($where eq \"to_or_cc\") {\n"
+" if(not $regexp) { \n"
+" return ((index(header(\"to\"),$what) != -1) or\n"
+" (index(header(\"cc\"),$what) != -1)) unless $nocase;\n"
+" return ((index(lc2_(header(\"to\")),lc2_($what)) != -1) or\n"
+" (index(lc2_(header(\"cc\")),lc2_($what)) != -1))\n"
+" } else {\n"
+" return ((header(\"to\") =~ m/$what/) or\n"
+" (header(\"cc\") =~ m/$what/)) unless $nocase;\n"
+" return ((header(\"to\") =~ m/$what/i) or\n"
+" (header(\"cc\") =~ m/$what/i));\n"
+" }\n"
+" } elsif($where eq \"body_part\") {\n"
+" my $mybody = body(); $mybody =~ s/\\s+/ /g;\n"
+" if(not $regexp) {\n"
+" return (index($mybody,$what) != -1) unless $nocase;\n"
+" return (index(lc2_($mybody),lc2_($what)) != -1);\n"
+" } else {\n"
+" return ($body =~ m/$what/) unless $nocase;\n"
+" return ($body =~ m/$what/i);\n"
+" }\n"
+" } elsif($where eq \"headers_part\") {\n"
+" my $myheader = header_as_string_();\n"
+" if(not $regexp) {\n"
+" $myheader =~ s/\\s+/ /g;\n"
+" return (index($myheader,$what) != -1) unless $nocase;\n"
+" return (index(lc2_($myheader),lc2_($what)) != -1);\n"
+" } else {\n"
+" return ($myheader =~ m/$what/) unless $nocase;\n"
+" return ($myheader =~ m/$what/i);\n"
+" }\n"
+" } elsif($where eq \"message\") {\n"
+" my $message = header_as_string_();\n"
+" $message .= \"\\n\".body();\n"
+" if(not $regexp) {\n"
+" $message =~ s/\\s+/ /g;\n"
+" return (index($message,$what) != -1) unless $nocase;\n"
+" return (index(lc2_($message),lc2_($what)) != -1);\n"
+" } else {\n"
+" return ($message =~ m/$what/) unless $nocase;\n"
+" return ($message =~ m/$what/i);\n"
+" }\n"
+" } elsif($where eq \"tag\") {\n"
+" my $found = 0;\n"
+" sub ClawsMail::Utils::get_tags;"
+" foreach my $tag (ClawsMail::Utils::get_tags) {\n"
+" if(not $regexp) {\n"
+" if($nocase) {\n"
+" $found = (index(lc2_($tag),lc2_($what)) != -1);\n"
+" } else {\n"
+" $found = (index($tag,$what) != -1);\n"
+" }\n"
+" } else {\n"
+" if ($nocase) {\n"
+" $found = ($tag =~ m/$what/i);\n"
+" } else {\n"
+" $found = ($tag =~ m/$what/);\n"
+" }\n"
+" }\n"
+" last if $found;\n"
+" }\n"
+" return $found;"
+" } else {\n"
+" $where = lc2_ $where;\n"
+" my $myheader = header(lc2_ $where); $myheader ||= \"\";\n"
+" return 0 unless $myheader;\n"
+" if(not $regexp) { \n"
+" return (index(header($where),$what) != -1) unless $nocase;\n"
+" return (index(lc2_(header($where)),lc2_($what)) != -1);\n"
+" } else {\n"
+" return (header($where) =~ m/$what/) unless $nocase;\n"
+" return (header($where) =~ m/$what/i);\n"
+" } \n"
+" }\n"
+"}\n"
+"sub header_as_string_ {\n"
+" my $headerstring=\"\";\n"
+" my @headerkeys = header(); my(@fields,$field);\n"
+" foreach $field (@headerkeys) {\n"
+" @fields = header($field);\n"
+" foreach (@fields) {\n"
+" $headerstring .= $field.\": \".$_.\"\\n\";\n"
+" }\n"
+" }\n"
+" return $headerstring;\n"
+"}\n"
+"our $permanent = \"\";\n"
+"1;\n"
+ };
+ const char perl_filter_action[] = {
+"BEGIN {$INC{'ClawsMail/Filter/Action.pm'} = 1;}\n"
+"package ClawsMail::Filter::Action;\n"
+"use base qw(Exporter);\n"
+"our @EXPORT = (qw(mark unmark dele mark_as_unread mark_as_read),\n"
+" qw(lock unlock move copy color execute),\n"
+" qw(hide set_score change_score stop exit),\n"
+" qw(forward forward_as_attachment redirect),\n"
+" qw(set_tag unset_tag clear_tags),\n"
+" );\n"
+"our %colors = ('none' => 0,'orange' => 1,\n"
+" 'red' => 2,'pink' => 3,\n"
+" 'sky blue' => 4,'blue' => 5,\n"
+" 'green' => 6,'brown' => 7);\n"
+"sub mark { ClawsMail::C::set_flag (1);}\n"
+"sub unmark { ClawsMail::C::unset_flag(1);}\n"
+"sub mark_as_unread { ClawsMail::C::set_flag (2);}\n"
+"sub mark_as_read { ClawsMail::C::unset_flag(2);}\n"
+"sub lock { ClawsMail::C::set_flag (7);}\n"
+"sub unlock { ClawsMail::C::unset_flag(7);}\n"
+"sub copy { ClawsMail::C::copy (@_);}\n"
+"sub forward { ClawsMail::C::forward(1,@_);}\n"
+"sub forward_as_attachment {ClawsMail::C::forward(2,@_);}\n"
+"sub redirect { ClawsMail::C::redirect(@_); }\n"
+"sub hide { ClawsMail::C::hide(); }\n"
+"sub exit {\n"
+" ClawsMail::C::filter_log(\"LOG_ACTION\",\"exit\");\n"
+" stop(1);\n"
+"}\n"
+"sub stop {\n"
+" my $nolog = shift;\n"
+" ClawsMail::C::filter_log(\"LOG_ACTION\", \"stop\")\n"
+" unless defined($nolog);\n"
+" die 'intended';\n"
+"}\n"
+"sub set_score {\n"
+" $ClawsMail::Filter::Matcher::msginfo{\"score\"} =\n"
+" ClawsMail::C::set_score(@_);\n"
+"}\n"
+"sub change_score {\n"
+" $ClawsMail::Filter::Matcher::msginfo{\"score\"} =\n"
+" ClawsMail::C::change_score(@_);\n"
+"}\n"
+"sub execute {\n"
+" my $flv; my $cmd = shift; return 0 unless defined($cmd);\n"
+" $flv = ClawsMail::C::filter_log_verbosity(0);\n"
+" ClawsMail::Filter::Matcher::test($cmd);\n"
+" ClawsMail::C::filter_log_verbosity($flv);\n"
+" ClawsMail::C::filter_log(\"LOG_ACTION\", \"execute: $cmd\");\n"
+" 1;\n"
+"}\n"
+"sub move { ClawsMail::C::move(@_); stop(1);}\n"
+"sub dele { ClawsMail::C::delete(); stop(1);}\n"
+"sub color {\n"
+" ($color) = @_;$color = lc2_ $color;\n"
+" $color = $colors{$color} if exists $colors{$color};\n"
+" $color = 0 if $color =~ m/\\D/;\n"
+" ClawsMail::C::color($color);\n"
+"}\n"
+"sub set_tag { ClawsMail::C::set_tag(@_);}\n"
+"sub unset_tag { ClawsMail::C::unset_tag(@_);}\n"
+"sub clear_tags { ClawsMail::C::clear_tags(@_);}\n"
+"1;\n"
+ };
+ const char perl_utils[] = {
+"BEGIN {$INC{'ClawsMail/Utils.pm'} = 1;}\n"
+"package ClawsMail::Utils;\n"
+"use base qw(Exporter);\n"
+"our @EXPORT = (\n"
+" qw(SA_is_spam extract_addresses move_to_trash abort),\n"
+" qw(addr_in_addressbook from_in_addressbook),\n"
+" qw(get_attribute_value make_sure_folder_exists),\n"
+" qw(make_sure_tag_exists get_tags),\n"
+" );\n"
+"# Spam\n"
+"sub SA_is_spam {\n"
+" my $retval;\n"
+" $retval = not ClawsMail::Filter::Matcher::test('spamc -c < %F > /dev/null');\n"
+" ClawsMail::C::filter_log(\"LOG_MATCH\",\"SA_is_spam\") if $retval;\n"
+" return $retval;\n"
+"}\n"
+"# simple extract email addresses from a header field\n"
+"sub extract_addresses {\n"
+" my $hf = shift; return undef unless defined($hf);\n"
+" my @addr = ();\n"
+" while($hf =~ m/[-.+\\w]+\\@[-.+\\w]+/) {\n"
+" $hf =~ s/^.*?([-.+\\w]+\\@[-.+\\w]+)//;\n"
+" push @addr,$1;\n"
+" }\n"
+" push @addr,\"\" unless @addr;\n"
+" return @addr;\n"
+"}\n"
+"# move to trash\n"
+"sub move_to_trash {\n"
+" ClawsMail::C::move_to_trash();\n"
+" ClawsMail::Filter::Action::stop(1);\n"
+"}\n"
+"# make sure a folder with a given id exists\n"
+"sub make_sure_folder_exists {\n"
+" ClawsMail::C::make_sure_folder_exists(@_);\n"
+"}\n"
+"sub make_sure_tag_exists {\n"
+" ClawsMail::C::make_sure_tag_exists(@_);\n"
+"}\n"
+"sub get_tags {\n"
+" ClawsMail::C::get_tags(@_);\n"
+"}\n"
+"# abort: stop() and do not continue with built-in filtering\n"
+"sub abort {\n"
+" ClawsMail::C::abort();\n"
+" ClawsMail::Filter::Action::stop(1);\n"
+"}\n"
+"# addressbook query\n"
+"sub addr_in_addressbook {\n"
+" return ClawsMail::C::addr_in_addressbook(@_) if @_;\n"
+" return 0;\n"
+"}\n"
+"sub from_in_addressbook {\n"
+" my ($from) = extract_addresses(ClawsMail::Filter::Matcher::header(\"from\"));\n"
+" return 0 unless $from;\n"
+" return addr_in_addressbook($from,@_);\n"
+"}\n"
+"sub get_attribute_value {\n"
+" my $email = shift; my $key = shift;\n"
+" return \"\" unless ($email and $key);\n"
+" return ClawsMail::C::get_attribute_value($email,$key,@_);\n"
+"}\n"
+"1;\n"
+ };
+
+ if((my_perl = perl_alloc()) == NULL) {
+ g_warning("Perl Plugin: Not enough memory to allocate Perl interpreter");
+ return -1;
+ }
+ PL_perl_destruct_level = 1;
+ perl_construct(my_perl);
+
+ exitstatus = perl_parse(my_perl, xs_init, 4, initialize, NULL);
+ PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
+ eval_pv(perl_filter_matcher,TRUE);
+ eval_pv(perl_filter_action,TRUE);
+ eval_pv(perl_persistent,TRUE);
+ eval_pv(perl_utils,TRUE);
+ return exitstatus;
+}
+
+static gboolean my_filtering_hook(gpointer source, gpointer data)
+{
+ int retry;
+
+ g_return_val_if_fail(source != NULL, FALSE);
+
+ mail_filtering_data = (MailFilteringData *) source;
+ msginfo = mail_filtering_data->msginfo;
+ if (!msginfo)
+ return FALSE;
+ stop_filtering = FALSE;
+ wrote_filter_log_head = FALSE;
+ filter_log_verbosity = config.filter_log_verbosity;
+ if(GPOINTER_TO_UINT(data) == AUTO_FILTER)
+ manual_filtering = FALSE;
+ else if(GPOINTER_TO_UINT(data) == MANU_FILTER)
+ manual_filtering = TRUE;
+ else
+ debug_print("Invalid user data ignored.\n");
+
+ if(!manual_filtering)
+ statusbar_print_all("Perl Plugin: filtering message...");
+
+ /* Process Skript File */
+ retry = perl_load_file();
+ while(retry == 1) {
+ debug_print("Error processing Perl script file. Retrying..\n");
+ retry = perl_load_file();
+ }
+ if(retry == 2) {
+ debug_print("Error processing Perl script file. Aborting..\n");
+ stop_filtering = FALSE;
+ }
+ return stop_filtering;
+}
+
+static void perl_plugin_save_config(void)
+{
+ PrefFile *pfile;
+ gchar *rcpath;
+
+ debug_print("Saving Perl Plugin Configuration\n");
+
+ rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
+ pfile = prefs_write_open(rcpath);
+ g_free(rcpath);
+ if (!pfile || (prefs_set_block_label(pfile, "PerlPlugin") < 0))
+ return;
+
+ if (prefs_write_param(param, pfile->fp) < 0) {
+ g_warning("Perl Plugin: Failed to write Perl Plugin configuration to file");
+ prefs_file_close_revert(pfile);
+ return;
+ }
+ if (fprintf(pfile->fp, "\n") < 0) {
+ FILE_OP_ERROR(rcpath, "fprintf");
+ prefs_file_close_revert(pfile);
+ } else
+ prefs_file_close(pfile);
+}
+
+gint plugin_init(gchar **error)
+{
+ int argc;
+ char **argv;
+ char **env;
+ int status = 0;
+ FILE *fp;
+ gchar *perlfilter;
+ gchar *rcpath;
+
+ /* version check */
+ if(!check_plugin_version(MAKE_NUMERIC_VERSION(3,7,4,6),
+ VERSION_NUMERIC, "Perl", error))
+ return -1;
+
+ /* register hook for automatic and manual filtering */
+ filtering_hook_id = hooks_register_hook(MAIL_FILTERING_HOOKLIST,
+ my_filtering_hook,
+ GUINT_TO_POINTER(AUTO_FILTER));
+ if(filtering_hook_id == (guint) -1) {
+ *error = g_strdup("Failed to register mail filtering hook");
+ return -1;
+ }
+ manual_filtering_hook_id = hooks_register_hook(MAIL_MANUAL_FILTERING_HOOKLIST,
+ my_filtering_hook,
+ GUINT_TO_POINTER(MANU_FILTER));
+ if(manual_filtering_hook_id == (guint) -1) {
+ hooks_unregister_hook(MAIL_FILTERING_HOOKLIST, filtering_hook_id);
+ *error = g_strdup("Failed to register manual mail filtering hook");
+ return -1;
+ }
+
+ rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
+ prefs_read_config(param, "PerlPlugin", rcpath, NULL);
+ g_free(rcpath);
+
+ /* make sure we have at least an empty scriptfile */
+ perlfilter = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, PERLFILTER, NULL);
+ if((fp = fopen(perlfilter, "a")) == NULL) {
+ *error = g_strdup("Failed to create blank scriptfile");
+ g_free(perlfilter);
+ hooks_unregister_hook(MAIL_FILTERING_HOOKLIST,
+ filtering_hook_id);
+ hooks_unregister_hook(MAIL_MANUAL_FILTERING_HOOKLIST,
+ manual_filtering_hook_id);
+ return -1;
+ }
+ /* chmod for security */
+ if (change_file_mode_rw(fp, perlfilter) < 0) {
+ FILE_OP_ERROR(perlfilter, "chmod");
+ g_warning("Perl Plugin: Can't change file mode");
+ }
+ fclose(fp);
+ g_free(perlfilter);
+
+ argc = 1;
+ argv = g_new0(char*, 1);
+ argv[0] = NULL;
+ env = g_new0(char*, 1);
+ env[0] = NULL;
+
+
+ /* Initialize Perl Interpreter */
+ PERL_SYS_INIT3(&argc, &argv, &env);
+ g_free(argv);
+ g_free(env);
+ if(my_perl == NULL)
+ status = perl_init();
+ if(status) {
+ *error = g_strdup("Failed to load Perl Interpreter\n");
+ hooks_unregister_hook(MAIL_FILTERING_HOOKLIST,
+ filtering_hook_id);
+ hooks_unregister_hook(MAIL_MANUAL_FILTERING_HOOKLIST,
+ manual_filtering_hook_id);
+ return -1;
+ }
+
+ perl_gtk_init();
+ debug_print("Perl Plugin loaded\n");
+ return 0;
+}
+
+gboolean plugin_done(void)
+{
+ hooks_unregister_hook(MAIL_FILTERING_HOOKLIST,
+ filtering_hook_id);
+ hooks_unregister_hook(MAIL_MANUAL_FILTERING_HOOKLIST,
+ manual_filtering_hook_id);
+
+ free_all_lists();
+
+ if(my_perl != NULL) {
+ PL_perl_destruct_level = 1;
+ perl_destruct(my_perl);
+ perl_free(my_perl);
+ }
+ PERL_SYS_TERM();
+
+ perl_plugin_save_config();
+
+ perl_gtk_done();
+ debug_print("Perl Plugin unloaded\n");
+ return TRUE;
+}
+
+const gchar *plugin_name(void)
+{
+ return "Perl";
+}
+
+const gchar *plugin_desc(void)
+{
+ return "This plugin provides a Perl scripting "
+ "interface for mail filters.\nFeedback "
+ "to <berndth@gmx.de> is welcome.";
+}
+
+const gchar *plugin_type(void)
+{
+ return "GTK2";
+}
+
+const gchar *plugin_licence(void)
+{
+ return "GPL3+";
+}
+
+const gchar *plugin_version(void)
+{
+ return VERSION;
+}
+
+struct PluginFeature *plugin_provides(void)
+{
+ static struct PluginFeature features[] =
+ { {PLUGIN_FILTERING, N_("Perl integration")},
+ {PLUGIN_NOTHING, NULL}};
+ return features;
+}
diff --git a/src/plugins/perl/perl_plugin.h b/src/plugins/perl/perl_plugin.h
@@ -0,0 +1,50 @@
+/* Perl plugin -- Perl Support for Claws Mail
+ *
+ * Copyright (C) 2004-2007 Holger Berndt
+ *
+ * Sylpheed and Claws-Mail are GTK+ based, lightweight, and fast e-mail clients
+ * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SC_PERL_PLUGIN_H
+#define SC_PERL_PLUGIN_H SC_PERL_PLUGIN_H
+
+/* the name of the filtering Perl script file */
+#define PERLFILTER "perl_filter"
+
+typedef struct {
+ gchar *address;
+ gchar *bookname;
+} PerlPluginEmailEntry;
+
+typedef struct {
+ gchar *address;
+ gchar *value;
+ gchar *bookname;
+} PerlPluginAttributeEntry;
+
+typedef struct {
+ GSList *g_slist;
+ time_t mtime;
+} PerlPluginTimedSList;
+
+typedef struct {
+ gint filter_log_verbosity;
+} PerlPluginConfig;
+
+gint execute_detached(gchar**);
+
+#endif /* include guard */
diff --git a/src/plugins/perl/placeholder.txt b/src/plugins/perl/placeholder.txt
@@ -1 +0,0 @@
-Placeholder
diff --git a/src/plugins/python/Makefile.am b/src/plugins/python/Makefile.am
@@ -0,0 +1,45 @@
+plugindir = $(pkglibdir)/plugins
+
+plugin_LTLIBRARIES = python_plugin.la
+
+python_plugin_la_SOURCES = \
+ clawsmailmodule.c \
+ clawsmailmodule.h \
+ composewindowtype.c \
+ composewindowtype.h \
+ foldertype.c \
+ foldertype.h \
+ messageinfotype.c \
+ messageinfotype.h \
+ nodetype.c \
+ nodetype.h \
+ python_plugin.c \
+ python-hooks.c \
+ python-hooks.h \
+ python-shell.c \
+ python-shell.h \
+ gettext.h
+
+python_plugin_la_LDFLAGS = \
+ -avoid-version -module \
+ $(GLIB_LIBS) \
+ $(GTK_LIBS) \
+ $(PYGTK_LIBS) \
+ $(PYTHON_LIBS)
+
+INCLUDES = \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/src/common \
+ -I$(top_srcdir)/src/gtk
+
+AM_CPPFLAGS = \
+ -Wall \
+ $(CLAWS_MAIL_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(GTK_CFLAGS) \
+ $(PYTHON_CFLAGS) \
+ $(PYGTK_CFLAGS) \
+ -DPYTHON_SHARED_LIB="\"$(PYTHON_SHARED_LIB)\"" \
+ -DENABLE_PYTHON \
+ -fno-strict-aliasing \
+ -DLOCALEDIR=\""$(localedir)"\"
diff --git a/src/plugins/python/clawsmailmodule.c b/src/plugins/python/clawsmailmodule.c
@@ -0,0 +1,764 @@
+/* Python plugin for Claws-Mail
+ * Copyright (C) 2009-2012 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "clawsmailmodule.h"
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#include "claws-features.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "nodetype.h"
+#include "composewindowtype.h"
+#include "foldertype.h"
+#include "messageinfotype.h"
+
+#include <pygobject.h>
+#include <pygtk/pygtk.h>
+
+#include "main.h"
+#include "mainwindow.h"
+#include "summaryview.h"
+#include "quicksearch.h"
+#include "toolbar.h"
+#include "prefs_common.h"
+#include "common/tags.h"
+
+#include <glib.h>
+
+static PyObject *cm_module = NULL;
+
+PyObject* get_gobj_from_address(gpointer addr)
+{
+ GObject *obj;
+
+ if (!G_IS_OBJECT(addr))
+ return NULL;
+
+ obj = G_OBJECT(addr);
+
+ if (!obj)
+ return NULL;
+
+ return pygobject_new(obj);
+}
+
+static PyObject* private_wrap_gobj(PyObject *self, PyObject *args)
+{
+ void *addr;
+
+ if (!PyArg_ParseTuple(args, "l", &addr))
+ return NULL;
+
+ return get_gobj_from_address(addr);
+}
+
+static PyObject *get_mainwindow_action_group(PyObject *self, PyObject *args)
+{
+ MainWindow *mainwin;
+
+ mainwin = mainwindow_get_mainwindow();
+ if(mainwin)
+ return get_gobj_from_address(mainwin->action_group);
+ else
+ return NULL;
+}
+
+static PyObject *get_mainwindow_ui_manager(PyObject *self, PyObject *args)
+{
+ MainWindow *mainwin;
+
+ mainwin = mainwindow_get_mainwindow();
+ if(mainwin)
+ return get_gobj_from_address(mainwin->ui_manager);
+ else
+ return NULL;
+}
+
+static PyObject *get_folderview_selected_folder(PyObject *self, PyObject *args)
+{
+ MainWindow *mainwin;
+
+ mainwin = mainwindow_get_mainwindow();
+ if(mainwin && mainwin->folderview) {
+ FolderItem *item;
+ item = folderview_get_selected_item(mainwin->folderview);
+ if(item)
+ return clawsmail_folder_new(item);
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *folderview_select_folder(PyObject *self, PyObject *args)
+{
+ MainWindow *mainwin;
+
+ mainwin = mainwindow_get_mainwindow();
+ if(mainwin && mainwin->folderview) {
+ FolderItem *item;
+ PyObject *folder;
+ folder = PyTuple_GetItem(args, 0);
+ if(!folder)
+ return NULL;
+ Py_INCREF(folder);
+ item = clawsmail_folder_get_item(folder);
+ Py_DECREF(folder);
+ if(item)
+ folderview_select(mainwin->folderview, item);
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static gboolean setup_folderitem_node(GNode *item_node, GNode *item_parent, PyObject **pyparent)
+{
+ PyObject *pynode, *children;
+ int retval, n_children, i_child;
+ PyObject *folder;
+
+ /* create a python node for the folderitem node */
+ pynode = clawsmail_node_new(cm_module);
+ if(!pynode)
+ return FALSE;
+
+ /* store Folder in pynode */
+ folder = clawsmail_folder_new(item_node->data);
+ retval = PyObject_SetAttrString(pynode, "data", folder);
+ Py_DECREF(folder);
+ if(retval == -1) {
+ Py_DECREF(pynode);
+ return FALSE;
+ }
+
+ if(pyparent && *pyparent) {
+ /* add this node to the parent's childs */
+ children = PyObject_GetAttrString(*pyparent, "children");
+ retval = PyList_Append(children, pynode);
+ Py_DECREF(children);
+
+ if(retval == -1) {
+ Py_DECREF(pynode);
+ return FALSE;
+ }
+ }
+ else if(pyparent) {
+ *pyparent = pynode;
+ Py_INCREF(pynode);
+ }
+
+ /* call this function recursively for all children of the new node */
+ n_children = g_node_n_children(item_node);
+ for(i_child = 0; i_child < n_children; i_child++) {
+ if(!setup_folderitem_node(g_node_nth_child(item_node, i_child), item_node, &pynode)) {
+ Py_DECREF(pynode);
+ return FALSE;
+ }
+ }
+
+ Py_DECREF(pynode);
+ return TRUE;
+}
+
+static PyObject* get_folder_tree_from_account_name(const char *str)
+{
+ PyObject *result;
+ GList *walk;
+
+ result = Py_BuildValue("[]");
+ if(!result)
+ return NULL;
+
+ for(walk = folder_get_list(); walk; walk = walk->next) {
+ Folder *folder = walk->data;
+ if((!str || !g_strcmp0(str, folder->name)) && folder->node) {
+ PyObject *root;
+ int n_children, i_child, retval;
+
+ /* create root nodes */
+ root = clawsmail_node_new(cm_module);
+ if(!root) {
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ n_children = g_node_n_children(folder->node);
+ for(i_child = 0; i_child < n_children; i_child++) {
+ if(!setup_folderitem_node(g_node_nth_child(folder->node, i_child), folder->node, &root)) {
+ Py_DECREF(root);
+ Py_DECREF(result);
+ return NULL;
+ }
+ }
+ retval = PyList_Append(result, root);
+ Py_DECREF(root);
+ if(retval == -1) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ }
+ }
+ return result;
+}
+
+static PyObject* get_folder_tree_from_folderitem(FolderItem *item)
+{
+ PyObject *result;
+ GList *walk;
+
+ for(walk = folder_get_list(); walk; walk = walk->next) {
+ Folder *folder = walk->data;
+ if(folder->node) {
+ GNode *root_node;
+
+ root_node = g_node_find(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
+ if(!root_node)
+ continue;
+
+ result = NULL;
+ if(!setup_folderitem_node(root_node, NULL, &result))
+ return NULL;
+ else
+ return result;
+ }
+ }
+
+ PyErr_SetString(PyExc_LookupError, "Folder not found");
+ return NULL;
+}
+
+static PyObject* get_folder_tree(PyObject *self, PyObject *args)
+{
+ PyObject *arg;
+ PyObject *result;
+ int retval;
+
+ Py_INCREF(Py_None);
+ arg = Py_None;
+ retval = PyArg_ParseTuple(args, "|O", &arg);
+ Py_DECREF(Py_None);
+ if(!retval)
+ return NULL;
+
+ /* calling possibilities:
+ * nothing, the mailbox name in a string, a Folder object */
+
+ /* no arguments: build up a list of folder trees */
+ if(PyTuple_Size(args) == 0) {
+ result = get_folder_tree_from_account_name(NULL);
+ }
+ else if(PyString_Check(arg)){
+ const char *str;
+ str = PyString_AsString(arg);
+ if(!str)
+ return NULL;
+
+ result = get_folder_tree_from_account_name(str);
+ }
+ else if(PyObject_TypeCheck(arg, clawsmail_folder_get_type_object())) {
+ result = get_folder_tree_from_folderitem(clawsmail_folder_get_item(arg));
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError, "Parameter must be nothing, a mailbox string or a Folder object.");
+ return NULL;
+ }
+
+ return result;
+}
+
+static PyObject* quicksearch_search(PyObject *self, PyObject *args)
+{
+ const char *string;
+ int searchtype;
+ QuickSearch *qs;
+ MainWindow *mainwin;
+
+ /* must be given exactly one argument, which is a string */
+ searchtype = prefs_common.summary_quicksearch_type;
+ if(!PyArg_ParseTuple(args, "s|i", &string, &searchtype))
+ return NULL;
+
+ mainwin = mainwindow_get_mainwindow();
+ if(!mainwin || !mainwin->summaryview || !mainwin->summaryview->quicksearch) {
+ PyErr_SetString(PyExc_LookupError, "Quicksearch not found");
+ return NULL;
+ }
+
+ qs = mainwin->summaryview->quicksearch;
+ quicksearch_set(qs, searchtype, string);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject* quicksearch_clear(PyObject *self, PyObject *args)
+{
+ QuickSearch *qs;
+ MainWindow *mainwin;
+
+ mainwin = mainwindow_get_mainwindow();
+ if(!mainwin || !mainwin->summaryview || !mainwin->summaryview->quicksearch) {
+ PyErr_SetString(PyExc_LookupError, "Quicksearch not found");
+ return NULL;
+ }
+
+ qs = mainwin->summaryview->quicksearch;
+ quicksearch_set(qs, prefs_common.summary_quicksearch_type, "");
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject* summaryview_select_messages(PyObject *self, PyObject *args)
+{
+ PyObject *olist;
+ MainWindow *mainwin;
+ Py_ssize_t size, iEl;
+ GSList *msginfos;
+
+ mainwin = mainwindow_get_mainwindow();
+ if(!mainwin || !mainwin->summaryview) {
+ PyErr_SetString(PyExc_LookupError, "SummaryView not found");
+ return NULL;
+ }
+
+ if(!PyArg_ParseTuple(args, "O!", &PyList_Type, &olist)) {
+ PyErr_SetString(PyExc_LookupError, "Argument must be a list of MessageInfo objects.");
+ return NULL;
+ }
+
+ msginfos = NULL;
+ size = PyList_Size(olist);
+ for(iEl = 0; iEl < size; iEl++) {
+ PyObject *element = PyList_GET_ITEM(olist, iEl);
+
+ if(!element || !PyObject_TypeCheck(element, clawsmail_messageinfo_get_type_object())) {
+ PyErr_SetString(PyExc_LookupError, "Argument must be a list of MessageInfo objects.");
+ return NULL;
+ }
+
+ msginfos = g_slist_prepend(msginfos, clawsmail_messageinfo_get_msginfo(element));
+ }
+
+ summary_unselect_all(mainwin->summaryview);
+ summary_select_by_msg_list(mainwin->summaryview, msginfos);
+ g_slist_free(msginfos);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject* get_summaryview_selected_message_list(PyObject *self, PyObject *args)
+{
+ MainWindow *mainwin;
+ GSList *list, *walk;
+ PyObject *result;
+
+ mainwin = mainwindow_get_mainwindow();
+ if(!mainwin || !mainwin->summaryview) {
+ PyErr_SetString(PyExc_LookupError, "SummaryView not found");
+ return NULL;
+ }
+
+ result = Py_BuildValue("[]");
+ if(!result)
+ return NULL;
+
+ list = summary_get_selected_msg_list(mainwin->summaryview);
+ for(walk = list; walk; walk = walk->next) {
+ PyObject *msg;
+ msg = clawsmail_messageinfo_new(walk->data);
+ if(PyList_Append(result, msg) == -1) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ }
+ g_slist_free(list);
+
+ return result;
+}
+
+static PyObject* is_exiting(PyObject *self, PyObject *args)
+{
+ if(claws_is_exiting())
+ Py_RETURN_TRUE;
+ else
+ Py_RETURN_FALSE;
+}
+
+static PyObject* get_tags(PyObject *self, PyObject *args)
+{
+ Py_ssize_t num_tags;
+ PyObject *tags_tuple;
+ GSList *tags_list;
+
+ tags_list = tags_get_list();
+ num_tags = g_slist_length(tags_list);
+
+ tags_tuple = PyTuple_New(num_tags);
+ if(tags_tuple != NULL) {
+ Py_ssize_t iTag;
+ PyObject *tag_object;
+ GSList *walk;
+
+ iTag = 0;
+ for(walk = tags_list; walk; walk = walk->next) {
+ tag_object = Py_BuildValue("s", tags_get_tag(GPOINTER_TO_INT(walk->data)));
+ if(tag_object == NULL) {
+ Py_DECREF(tags_tuple);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(tags_tuple, iTag++, tag_object);
+ }
+ }
+
+ g_slist_free(tags_list);
+
+ return tags_tuple;
+}
+
+static PyObject* make_sure_tag_exists(PyObject *self, PyObject *args)
+{
+ int retval;
+ const char *tag_str;
+
+ retval = PyArg_ParseTuple(args, "s", &tag_str);
+ if(!retval)
+ return NULL;
+
+ if(IS_NOT_RESERVED_TAG(tag_str) == FALSE) {
+ /* tag name is reserved, raise KeyError */
+ PyErr_SetString(PyExc_ValueError, "Tag name is reserved");
+ return NULL;
+ }
+
+ tags_add_tag(tag_str);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject* delete_tag(PyObject *self, PyObject *args)
+{
+ int retval;
+ const char *tag_str;
+ gint tag_id;
+ MainWindow *mainwin;
+
+ retval = PyArg_ParseTuple(args, "s", &tag_str);
+ if(!retval)
+ return NULL;
+
+ tag_id = tags_get_id_for_str(tag_str);
+ if(tag_id == -1) {
+ PyErr_SetString(PyExc_KeyError, "Tag does not exist");
+ return NULL;
+ }
+
+ tags_remove_tag(tag_id);
+
+ /* update display */
+ mainwin = mainwindow_get_mainwindow();
+ if(mainwin)
+ summary_redisplay_msg(mainwin->summaryview);
+
+ Py_RETURN_NONE;
+}
+
+
+static PyObject* rename_tag(PyObject *self, PyObject *args)
+{
+ int retval;
+ const char *old_tag_str;
+ const char *new_tag_str;
+ gint tag_id;
+ MainWindow *mainwin;
+
+ retval = PyArg_ParseTuple(args, "ss", &old_tag_str, &new_tag_str);
+ if(!retval)
+ return NULL;
+
+ if((IS_NOT_RESERVED_TAG(new_tag_str) == FALSE) ||(IS_NOT_RESERVED_TAG(old_tag_str) == FALSE)) {
+ PyErr_SetString(PyExc_ValueError, "Tag name is reserved");
+ return NULL;
+ }
+
+ tag_id = tags_get_id_for_str(old_tag_str);
+ if(tag_id == -1) {
+ PyErr_SetString(PyExc_KeyError, "Tag does not exist");
+ return NULL;
+ }
+
+ tags_update_tag(tag_id, new_tag_str);
+
+ /* update display */
+ mainwin = mainwindow_get_mainwindow();
+ if(mainwin)
+ summary_redisplay_msg(mainwin->summaryview);
+
+ Py_RETURN_NONE;
+}
+
+static gboolean get_message_list_for_move_or_copy(PyObject *messagelist, PyObject *folder, GSList **list)
+{
+ Py_ssize_t size, iEl;
+ FolderItem *folderitem;
+
+ *list = NULL;
+
+ folderitem = clawsmail_folder_get_item(folder);
+ if(!folderitem) {
+ PyErr_SetString(PyExc_LookupError, "Brokern Folder object.");
+ return FALSE;
+ }
+
+ size = PyList_Size(messagelist);
+ for(iEl = 0; iEl < size; iEl++) {
+ PyObject *element = PyList_GET_ITEM(messagelist, iEl);
+ MsgInfo *msginfo;
+
+ if(!element || !PyObject_TypeCheck(element, clawsmail_messageinfo_get_type_object())) {
+ PyErr_SetString(PyExc_TypeError, "Argument must be a list of MessageInfo objects.");
+ return FALSE;
+ }
+
+ msginfo = clawsmail_messageinfo_get_msginfo(element);
+ if(!msginfo) {
+ PyErr_SetString(PyExc_LookupError, "Broken MessageInfo object.");
+ return FALSE;
+ }
+
+ procmsg_msginfo_set_to_folder(msginfo, folderitem);
+ *list = g_slist_prepend(*list, msginfo);
+ }
+
+ return TRUE;
+}
+
+static PyObject* move_or_copy_messages(PyObject *self, PyObject *args, gboolean move)
+{
+ PyObject *messagelist;
+ PyObject *folder;
+ int retval;
+ GSList *list = NULL;
+
+ retval = PyArg_ParseTuple(args, "O!O!",
+ &PyList_Type, &messagelist,
+ clawsmail_folder_get_type_object(), &folder);
+ if(!retval )
+ return NULL;
+
+ folder_item_update_freeze();
+
+ if(!get_message_list_for_move_or_copy(messagelist, folder, &list))
+ goto err;
+
+ if(move)
+ procmsg_move_messages(list);
+ else
+ procmsg_copy_messages(list);
+
+ folder_item_update_thaw();
+ g_slist_free(list);
+ Py_RETURN_NONE;
+
+err:
+ folder_item_update_thaw();
+ g_slist_free(list);
+ return NULL;
+}
+
+static PyObject* move_messages(PyObject *self, PyObject *args)
+{
+ return move_or_copy_messages(self, args, TRUE);
+}
+
+
+static PyObject* copy_messages(PyObject *self, PyObject *args)
+{
+ return move_or_copy_messages(self, args, FALSE);
+}
+
+static PyMethodDef ClawsMailMethods[] = {
+ /* public */
+ {"get_mainwindow_action_group", get_mainwindow_action_group, METH_NOARGS,
+ "get_mainwindow_action_group() - get action group of main window menu\n"
+ "\n"
+ "Returns the gtk.ActionGroup for the main window."},
+
+ {"get_mainwindow_ui_manager", get_mainwindow_ui_manager, METH_NOARGS,
+ "get_mainwindow_ui_manager() - get ui manager of main window\n"
+ "\n"
+ "Returns the gtk.UIManager for the main window."},
+
+ {"get_folder_tree", get_folder_tree, METH_VARARGS,
+ "get_folder_tree([root]) - get a folder tree\n"
+ "\n"
+ "Without arguments, get a list of folder trees for all mailboxes.\n"
+ "\n"
+ "If the optional root argument is a string, it is supposed to be a\n"
+ "mailbox name. The function then returns a tree of folders of that mailbox.\n"
+ "\n"
+ "If the optional root argument is a clawsmail.Folder, the function\n"
+ "returns a tree of subfolders with the given folder as root element.\n"
+ "\n"
+ "In any case, a tree consists of elements of the type clawsmail.Node."},
+
+ {"get_folderview_selected_folder", get_folderview_selected_folder, METH_NOARGS,
+ "get_folderview_selected_folder() - get selected folder in folderview\n"
+ "\n"
+ "Returns the currently selected folder as a clawsmail.Folder."},
+ {"folderview_select_folder", folderview_select_folder, METH_VARARGS,
+ "folderview_select_folder(folder) - select folder in folderview\n"
+ "\n"
+ "Takes an argument of type clawsmail.Folder, and selects the corresponding folder."},
+
+ {"quicksearch_search", quicksearch_search, METH_VARARGS,
+ "quicksearch_search(string [, type]) - perform a quicksearch\n"
+ "\n"
+ "Perform a quicksearch of the given string. The optional type argument can be\n"
+ "one of clawsmail.QUICK_SEARCH_SUBJECT, clawsmail.QUICK_SEARCH_FROM, clawsmail.QUICK_SEARCH_TO,\n"
+ "clawsmail.QUICK_SEARCH_EXTENDED, clawsmail.QUICK_SEARCH_MIXED, or clawsmail.QUICK_SEARCH_TAG.\n"
+ "If it is omitted, the current selection is used. The string argument has to be a valid search\n"
+ "string corresponding to the type."},
+
+ {"quicksearch_clear", quicksearch_clear, METH_NOARGS,
+ "quicksearch_clear() - clear the quicksearch"},
+
+ {"get_summaryview_selected_message_list", get_summaryview_selected_message_list, METH_NOARGS,
+ "get_summaryview_selected_message_list() - get selected message list\n"
+ "\n"
+ "Get a list of clawsmail.MessageInfo objects of the current selection."},
+
+ {"summaryview_select_messages", summaryview_select_messages, METH_VARARGS,
+ "summaryview_select_messages(message_list) - select a list of messages in the summary view\n"
+ "\n"
+ "Select a list of clawsmail.MessageInfo objects in the summary view."},
+
+ {"is_exiting", is_exiting, METH_NOARGS,
+ "is_exiting() - test whether Claws Mail is currently exiting\n"
+ "\n"
+ "Returns True if Claws Mail is currently exiting. The most common usage for this is to skip\n"
+ "unnecessary cleanup tasks in a shutdown script when Claws Mail is exiting anyways. If the Python\n"
+ "plugin is explicitly unloaded, the shutdown script will still be called, but this function will\n"
+ "return False."},
+
+ {"move_messages", move_messages, METH_VARARGS,
+ "move_messages(message_list, target_folder) - move a list of messages to a target folder\n"
+ "\n"
+ "Move a list of clawsmail.MessageInfo objects to a target folder.\n"
+ "The target_folder argument has to be a clawsmail.Folder object."},
+
+ {"copy_messages", copy_messages, METH_VARARGS,
+ "copy_messages(message_list, target_folder) - copy a list of messages to a target folder\n"
+ "\n"
+ "Copy a list of clawsmail.MessageInfo objects to a target folder.\n"
+ "The target_folder argument has to be a clawsmail.Folder object."},
+
+ {"get_tags", get_tags, METH_NOARGS,
+ "get_tags() - get a tuple of all tags that Claws Mail knows about\n"
+ "\n"
+ "Get a tuple of strings representing all tags that are defined in Claws Mail."},
+
+ {"make_sure_tag_exists", make_sure_tag_exists, METH_VARARGS,
+ "make_sure_tag_exists(tag) - make sure that the specified tag exists\n"
+ "\n"
+ "This function creates the given tag if it does not exist yet.\n"
+ "It is not an error if the tag already exists. In this case, this function does nothing.\n"
+ "However, if a reserved tag name is chosen, a ValueError exception is raised."},
+
+ {"delete_tag", delete_tag, METH_VARARGS,
+ "delete_tag(tag) - delete a tag\n"
+ "\n"
+ "This function deletes an existing tag.\n"
+ "Raises a KeyError exception if the tag does not exist."},
+
+ {"rename_tag", rename_tag, METH_VARARGS,
+ "rename_tag(old_tag, new_tag) - rename tag old_tag to new_tag\n"
+ "\n"
+ "This function renames an existing tag.\n"
+ "Raises a KeyError exception if the tag does not exist.\n"
+ "Raises a ValueError exception if the old or new tag name is a reserved name."},
+
+ /* private */
+ {"__gobj", private_wrap_gobj, METH_VARARGS,
+ "__gobj(ptr) - transforms a C GObject pointer into a PyGObject\n"
+ "\n"
+ "For internal usage only."},
+
+ {NULL, NULL, 0, NULL}
+};
+
+static void initmiscstuff(PyObject *module)
+{
+ PyObject *dict;
+ PyObject *res;
+ const char *cmd =
+ "QUICK_SEARCH_SUBJECT = 0\n"
+ "QUICK_SEARCH_FROM = 1\n"
+ "QUICK_SEARCH_TO = 2\n"
+ "QUICK_SEARCH_EXTENDED = 3\n"
+ "QUICK_SEARCH_MIXED = 4\n"
+ "QUICK_SEARCH_TAG = 5\n"
+ "\n";
+ dict = PyModule_GetDict(module);
+ res = PyRun_String(cmd, Py_file_input, dict, dict);
+ Py_XDECREF(res);
+}
+
+
+void claws_mail_python_init(void)
+{
+ if (!Py_IsInitialized())
+ Py_Initialize();
+
+ /* create module */
+ cm_module = Py_InitModule3("clawsmail", ClawsMailMethods,
+ "This module can be used to access some of Claws Mail's data structures\n"
+ "in order to extend or modify the user interface or automate repetitive tasks.\n"
+ "\n"
+ "Whenever possible, the interface works with standard GTK+ widgets\n"
+ "via the PyGTK bindings, so you can refer to the GTK+ / PyGTK documentation\n"
+ "to find out about all possible options.\n"
+ "\n"
+ "The interface to Claws Mail in this module is extended on a 'as-needed' basis.\n"
+ "If you're missing something specific, try contacting the author.");
+
+ /* initialize classes */
+ initnode(cm_module);
+ initcomposewindow(cm_module);
+ initfolder(cm_module);
+ initmessageinfo(cm_module);
+
+ /* initialize misc things */
+ initmiscstuff(cm_module);
+
+ PyRun_SimpleString("import clawsmail\n");
+ PyRun_SimpleString("clawsmail.compose_window = None\n");
+}
+
+
+void put_composewindow_into_module(Compose *compose)
+{
+ PyObject *pycompose;
+
+ pycompose = clawsmail_compose_new(cm_module, compose);
+ PyObject_SetAttrString(cm_module, "compose_window", pycompose);
+ Py_DECREF(pycompose);
+}
diff --git a/src/plugins/python/clawsmailmodule.h b/src/plugins/python/clawsmailmodule.h
@@ -0,0 +1,33 @@
+/* Python plugin for Claws-Mail
+ * Copyright (C) 2009 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef CLAWSMAILMODULE_H
+#define CLAWSMAILMODULE_H
+
+#include <Python.h>
+#include <glib.h>
+
+#include "compose.h"
+
+
+void claws_mail_python_init(void);
+
+PyObject* get_gobj_from_address(gpointer addr);
+void put_composewindow_into_module(Compose *compose);
+
+#endif /* CLAWSMAILMODULE_H */
diff --git a/src/plugins/python/composewindowtype.c b/src/plugins/python/composewindowtype.c
@@ -0,0 +1,617 @@
+/* Python plugin for Claws-Mail
+ * Copyright (C) 2009 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#include "claws-features.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "composewindowtype.h"
+
+#include "clawsmailmodule.h"
+#include "foldertype.h"
+#include "messageinfotype.h"
+
+#include "mainwindow.h"
+#include "account.h"
+#include "summaryview.h"
+
+#include <structmember.h>
+
+#include <string.h>
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *ui_manager;
+ PyObject *text;
+ PyObject *replyinfo;
+ Compose *compose;
+} clawsmail_ComposeWindowObject;
+
+static void ComposeWindow_dealloc(clawsmail_ComposeWindowObject* self)
+{
+ Py_XDECREF(self->ui_manager);
+ Py_XDECREF(self->text);
+ Py_XDECREF(self->replyinfo);
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static void flush_gtk_queue(void)
+{
+ while(gtk_events_pending())
+ gtk_main_iteration();
+}
+
+static void store_py_object(PyObject **target, PyObject *obj)
+{
+ Py_XDECREF(*target);
+ if(obj)
+ {
+ Py_INCREF(obj);
+ *target = obj;
+ }
+ else {
+ Py_INCREF(Py_None);
+ *target = Py_None;
+ }
+}
+
+static void composewindow_set_compose(clawsmail_ComposeWindowObject *self, Compose *compose)
+{
+ self->compose = compose;
+
+ store_py_object(&(self->ui_manager), get_gobj_from_address(compose->ui_manager));
+ store_py_object(&(self->text), get_gobj_from_address(compose->text));
+
+ store_py_object(&(self->replyinfo), clawsmail_messageinfo_new(compose->replyinfo));
+}
+
+static int ComposeWindow_init(clawsmail_ComposeWindowObject *self, PyObject *args, PyObject *kwds)
+{
+ MainWindow *mainwin;
+ PrefsAccount *ac = NULL;
+ FolderItem *item;
+ GList* list;
+ GList* cur;
+ gboolean did_find_compose;
+ Compose *compose = NULL;
+ const char *ss;
+ unsigned char open_window;
+ /* if __open_window is set to 0/False,
+ * composewindow_set_compose must be called before this object is valid */
+ static char *kwlist[] = {"address", "__open_window", NULL};
+
+ ss = NULL;
+ open_window = 1;
+ PyArg_ParseTupleAndKeywords(args, kwds, "|sb", kwlist, &ss, &open_window);
+
+ if(open_window) {
+ mainwin = mainwindow_get_mainwindow();
+ item = mainwin->summaryview->folder_item;
+ did_find_compose = FALSE;
+
+ if(ss) {
+ ac = account_find_from_address(ss, FALSE);
+ if (ac && ac->protocol != A_NNTP) {
+ compose = compose_new_with_folderitem(ac, item, NULL);
+ did_find_compose = TRUE;
+ }
+ }
+ if(!did_find_compose) {
+ if (item) {
+ ac = account_find_from_item(item);
+ if (ac && ac->protocol != A_NNTP) {
+ compose = compose_new_with_folderitem(ac, item, NULL);
+ did_find_compose = TRUE;
+ }
+ }
+
+ /* use current account */
+ if (!did_find_compose && cur_account && (cur_account->protocol != A_NNTP)) {
+ compose = compose_new_with_folderitem(cur_account, item, NULL);
+ did_find_compose = TRUE;
+ }
+
+ if(!did_find_compose) {
+ /* just get the first one */
+ list = account_get_list();
+ for (cur = list ; cur != NULL ; cur = g_list_next(cur)) {
+ ac = (PrefsAccount *) cur->data;
+ if (ac->protocol != A_NNTP) {
+ compose = compose_new_with_folderitem(ac, item, NULL);
+ did_find_compose = TRUE;
+ }
+ }
+ }
+ }
+
+ if(!did_find_compose)
+ return -1;
+
+ composewindow_set_compose(self, compose);
+ gtk_widget_show_all(compose->window);
+ flush_gtk_queue();
+ }
+ return 0;
+}
+
+/* this is here because wrapping GTK_EDITABLEs in PyGTK is buggy */
+static PyObject* get_python_object_from_gtk_entry(GtkWidget *entry)
+{
+ return Py_BuildValue("s", gtk_entry_get_text(GTK_ENTRY(entry)));
+}
+
+static PyObject* set_gtk_entry_from_python_object(GtkWidget *entry, PyObject *args)
+{
+ const char *ss;
+
+ if(!PyArg_ParseTuple(args, "s", &ss))
+ return NULL;
+
+ gtk_entry_set_text(GTK_ENTRY(entry), ss);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject* ComposeWindow_get_subject(clawsmail_ComposeWindowObject *self, PyObject *args)
+{
+ return get_python_object_from_gtk_entry(self->compose->subject_entry);
+}
+
+static PyObject* ComposeWindow_set_subject(clawsmail_ComposeWindowObject *self, PyObject *args)
+{
+ PyObject *ret;
+ ret = set_gtk_entry_from_python_object(self->compose->subject_entry, args);
+ flush_gtk_queue();
+ return ret;
+}
+
+static PyObject* ComposeWindow_get_from(clawsmail_ComposeWindowObject *self, PyObject *args)
+{
+ return get_python_object_from_gtk_entry(self->compose->from_name);
+}
+
+static PyObject* ComposeWindow_set_from(clawsmail_ComposeWindowObject *self, PyObject *args)
+{
+ PyObject *ret;
+ ret = set_gtk_entry_from_python_object(self->compose->from_name, args);
+ flush_gtk_queue();
+ return ret;
+}
+
+static PyObject* ComposeWindow_add_To(clawsmail_ComposeWindowObject *self, PyObject *args)
+{
+ const char *ss;
+
+ if(!PyArg_ParseTuple(args, "s", &ss))
+ return NULL;
+
+ compose_entry_append(self->compose, ss, COMPOSE_TO, PREF_NONE);
+
+ flush_gtk_queue();
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject* ComposeWindow_add_Cc(clawsmail_ComposeWindowObject *self, PyObject *args)
+{
+ const char *ss;
+
+ if(!PyArg_ParseTuple(args, "s", &ss))
+ return NULL;
+
+ compose_entry_append(self->compose, ss, COMPOSE_CC, PREF_NONE);
+
+ flush_gtk_queue();
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject* ComposeWindow_add_Bcc(clawsmail_ComposeWindowObject *self, PyObject *args)
+{
+ const char *ss;
+
+ if(!PyArg_ParseTuple(args, "s", &ss))
+ return NULL;
+
+ compose_entry_append(self->compose, ss, COMPOSE_BCC, PREF_NONE);
+
+ flush_gtk_queue();
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject* ComposeWindow_attach(clawsmail_ComposeWindowObject *self, PyObject *args)
+{
+ PyObject *olist;
+ Py_ssize_t size, iEl;
+ GList *list = NULL;
+
+ if(!PyArg_ParseTuple(args, "O!", &PyList_Type, &olist))
+ return NULL;
+
+ size = PyList_Size(olist);
+ for(iEl = 0; iEl < size; iEl++) {
+ char *ss;
+ PyObject *element = PyList_GET_ITEM(olist, iEl);
+
+ if(!element)
+ continue;
+
+ Py_INCREF(element);
+ if(!PyArg_Parse(element, "s", &ss)) {
+ Py_DECREF(element);
+ if(list)
+ g_list_free(list);
+ return NULL;
+ }
+ list = g_list_prepend(list, ss);
+ Py_DECREF(element);
+ }
+
+ compose_attach_from_list(self->compose, list, FALSE);
+ g_list_free(list);
+
+ flush_gtk_queue();
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject* ComposeWindow_get_header_list(clawsmail_ComposeWindowObject *self, PyObject *args)
+{
+ GSList *walk;
+ PyObject *retval;
+
+ retval = Py_BuildValue("[]");
+ for(walk = self->compose->header_list; walk; walk = walk->next) {
+ ComposeHeaderEntry *headerentry = walk->data;
+ const gchar *header;
+ const gchar *text;
+
+ header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(headerentry->combo))), 0, -1);
+ text = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
+
+ if(text && strcmp("", text)) {
+ PyObject *ee;
+ int ok;
+
+ ee = Py_BuildValue("(ss)", header, text);
+ ok = PyList_Append(retval, ee);
+ Py_DECREF(ee);
+ if(ok == -1) {
+ Py_DECREF(retval);
+ return NULL;
+ }
+ }
+ }
+ return retval;
+}
+
+static PyObject* ComposeWindow_add_header(clawsmail_ComposeWindowObject *self, PyObject *args)
+{
+ const char *header;
+ const char *text;
+ gint num;
+
+ if(!PyArg_ParseTuple(args, "ss", &header, &text))
+ return NULL;
+
+ /* add a dummy, and modify it then */
+ compose_entry_append(self->compose, "dummy1dummy2dummy3", COMPOSE_TO, PREF_NONE);
+ num = g_slist_length(self->compose->header_list);
+ if(num > 1) {
+ ComposeHeaderEntry *headerentry;
+ headerentry = g_slist_nth_data(self->compose->header_list, num-2);
+ if(headerentry) {
+ GtkEditable *editable;
+ gint pos;
+ gtk_entry_set_text(GTK_ENTRY(headerentry->entry), text);
+ editable = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(headerentry->combo)));
+ gtk_editable_delete_text(editable, 0, -1);
+ gtk_editable_insert_text(editable, header, -1, &pos);
+ }
+ }
+
+ flush_gtk_queue();
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* this is pretty ugly, as the compose struct does not maintain a pointer to the account selection combo */
+static PyObject* ComposeWindow_get_account_selection(clawsmail_ComposeWindowObject *self, PyObject *args)
+{
+ GList *children, *walk;
+
+ children = gtk_container_get_children(GTK_CONTAINER(self->compose->header_table));
+ for(walk = children; walk; walk = walk->next) {
+ if(GTK_IS_HBOX(walk->data)) {
+ GList *children2, *walk2;
+ children2 = gtk_container_get_children(GTK_CONTAINER(walk->data));
+ for(walk2 = children2; walk2; walk2 = walk2->next) {
+ if(GTK_IS_EVENT_BOX(walk2->data)) {
+ return get_gobj_from_address(gtk_container_get_children(GTK_CONTAINER(walk2->data))->data);
+ }
+ }
+ }
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject* ComposeWindow_save_message_to(clawsmail_ComposeWindowObject *self, PyObject *args)
+{
+ PyObject *arg;
+
+ if(!PyArg_ParseTuple(args, "O", &arg))
+ return NULL;
+
+ if(PyString_Check(arg)) {
+ GtkEditable *editable;
+ gint pos;
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->compose->savemsg_checkbtn), TRUE);
+
+ editable = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(self->compose->savemsg_combo)));
+ gtk_editable_delete_text(editable, 0, -1);
+ gtk_editable_insert_text(editable, PyString_AsString(arg), -1, &pos);
+ }
+ else if(clawsmail_folder_check(arg)) {
+ GtkEditable *editable;
+ gint pos;
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->compose->savemsg_checkbtn), TRUE);
+
+ editable = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(self->compose->savemsg_combo)));
+ gtk_editable_delete_text(editable, 0, -1);
+ gtk_editable_insert_text(editable, folder_item_get_identifier(clawsmail_folder_get_item(arg)), -1, &pos);
+ }
+ else if (arg == Py_None){
+ /* turn off checkbutton */
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->compose->savemsg_checkbtn), FALSE);
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError, "function takes exactly one argument which may be a folder object, a string, or None");
+ return NULL;
+ }
+
+ flush_gtk_queue();
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject* ComposeWindow_set_modified(clawsmail_ComposeWindowObject *self, PyObject *args)
+{
+ char modified = 0;
+ gboolean old_modified;
+
+ if(!PyArg_ParseTuple(args, "b", &modified))
+ return NULL;
+
+ old_modified = self->compose->modified;
+
+ self->compose->modified = (modified != 0);
+
+ /* If the modified state changed, rewrite window title.
+ * This partly duplicates functionality in compose.c::compose_set_title().
+ * While it's nice to not have to modify Claws Mail for this to work,
+ * it would be cleaner to export that function in Claws Mail. */
+ if((strcmp(gtk_window_get_title(GTK_WINDOW(self->compose->window)), _("Compose message")) != 0) &&
+ (old_modified != self->compose->modified)) {
+ gchar *str;
+ gchar *edited;
+ gchar *subject;
+
+ edited = self->compose->modified ? _(" [Edited]") : "";
+ subject = gtk_editable_get_chars(GTK_EDITABLE(self->compose->subject_entry), 0, -1);
+ if(subject && strlen(subject))
+ str = g_strdup_printf(_("%s - Compose message%s"),
+ subject, edited);
+ else
+ str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
+ gtk_window_set_title(GTK_WINDOW(self->compose->window), str);
+ g_free(str);
+ g_free(subject);
+ }
+
+ flush_gtk_queue();
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyMethodDef ComposeWindow_methods[] = {
+ {"set_subject", (PyCFunction)ComposeWindow_set_subject, METH_VARARGS,
+ "set_subject(text) - set subject to text\n"
+ "\n"
+ "Set the subject to text. text must be a string."},
+
+ {"get_subject", (PyCFunction)ComposeWindow_get_subject, METH_NOARGS,
+ "get_subject() - get subject\n"
+ "\n"
+ "Get a string of the current subject entry."},
+
+ {"set_from", (PyCFunction)ComposeWindow_set_from, METH_VARARGS,
+ "set_from(text) - set From header entry to text\n"
+ "\n"
+ "Set the From header entry to text. text must be a string.\n"
+ "Beware: No sanity checking is performed."},
+
+ {"get_from", (PyCFunction)ComposeWindow_get_from, METH_NOARGS,
+ "get_from - get From header entry\n"
+ "\n"
+ "Get a string of the current From header entry."},
+
+ {"add_To", (PyCFunction)ComposeWindow_add_To, METH_VARARGS,
+ "add_To(text) - append another To header with text\n"
+ "\n"
+ "Add another header line with the combo box set to To:, and the\n"
+ "content set to text."},
+
+ {"add_Cc", (PyCFunction)ComposeWindow_add_Cc, METH_VARARGS,
+ "add_Cc(text) - append another Cc header with text\n"
+ "\n"
+ "Add another header line with the combo box set to Cc:, and the\n"
+ "content set to text."},
+
+ {"add_Bcc", (PyCFunction)ComposeWindow_add_Bcc, METH_VARARGS,
+ "add_Bcc(text) - append another Bcc header with text\n"
+ "\n"
+ "Add another header line with the combo box set to Bcc:, and the\n"
+ "content set to text."},
+
+ {"add_header", (PyCFunction)ComposeWindow_add_header, METH_VARARGS,
+ "add_header(headername, text) - add a custom header\n"
+ "\n"
+ "Adds a custom header with the header set to headername, and the\n"
+ "contents set to text."},
+
+ {"get_header_list", (PyCFunction)ComposeWindow_get_header_list, METH_NOARGS,
+ "get_header_list() - get list of headers\n"
+ "\n"
+ "Gets a list of headers that are currently defined in the compose window.\n"
+ "The return value is a list of tuples, where the first tuple element is\n"
+ "the header name (entry in the combo box) and the second element is the contents."},
+
+ {"attach", (PyCFunction)ComposeWindow_attach, METH_VARARGS,
+ "attach(filenames) - attach a list of files\n"
+ "\n"
+ "Attach files to the mail. The filenames argument is a list of\n"
+ "string of the filenames that are being attached."},
+
+ {"get_account_selection", (PyCFunction)ComposeWindow_get_account_selection, METH_NOARGS,
+ "get_account_selection() - get account selection widget\n"
+ "\n"
+ "Returns the account selection combo box as a gtk.ComboBox"},
+
+ {"save_message_to", (PyCFunction)ComposeWindow_save_message_to, METH_VARARGS,
+ "save_message_to(folder) - save message to folder id\n"
+ "\n"
+ "Set the folder where the sent message will be saved to. folder may be\n"
+ "a Folder, a string of the folder identifier (e.g. #mh/foo/bar), or\n"
+ "None is which case the message will not be saved at all."},
+
+ {"set_modified", (PyCFunction)ComposeWindow_set_modified, METH_VARARGS,
+ "set_modified(bool) - set or unset modification marker of compose window\n"
+ "\n"
+ "Set or unset the modification marker of the compose window. This marker determines\n"
+ "for example whether you get a confirmation dialog when closing the compose window\n"
+ "or not.\n"
+ "In the usual case, Claws Mail keeps track of the modification status itself.\n"
+ "However, there are cases when it might be desirable to overwrite the marker,\n"
+ "for example because a compose_any script modifies the body or subject which\n"
+ "can be regarded compose window preprocessing and should not trigger a confirmation\n"
+ "dialog on close like a manual edit."},
+
+ {NULL}
+};
+
+static PyMemberDef ComposeWindow_members[] = {
+ {"ui_manager", T_OBJECT_EX, offsetof(clawsmail_ComposeWindowObject, ui_manager), 0,
+ "ui_manager - the gtk.UIManager of the compose window"},
+
+ {"text", T_OBJECT_EX, offsetof(clawsmail_ComposeWindowObject, text), 0,
+ "text - the gtk.TextView widget of the message body"},
+
+ {"replyinfo", T_OBJECT_EX, offsetof(clawsmail_ComposeWindowObject, replyinfo), 0,
+ "replyinfo - The MessageInfo object of the message that is being replied to, or None"},
+
+ {NULL}
+};
+
+static PyTypeObject clawsmail_ComposeWindowType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "clawsmail.ComposeWindow", /*tp_name*/
+ sizeof(clawsmail_ComposeWindowObject), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)ComposeWindow_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ /* tp_doc */
+ "ComposeWindow objects. Optional argument to constructor: sender account address. ",
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ ComposeWindow_methods, /* tp_methods */
+ ComposeWindow_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)ComposeWindow_init, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+PyMODINIT_FUNC initcomposewindow(PyObject *module)
+{
+ clawsmail_ComposeWindowType.tp_new = PyType_GenericNew;
+ if(PyType_Ready(&clawsmail_ComposeWindowType) < 0)
+ return;
+
+ Py_INCREF(&clawsmail_ComposeWindowType);
+ PyModule_AddObject(module, "ComposeWindow", (PyObject*)&clawsmail_ComposeWindowType);
+}
+
+PyObject* clawsmail_compose_new(PyObject *module, Compose *compose)
+{
+ PyObject *class, *dict;
+ PyObject *self, *args, *kw;
+
+ if(!compose) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ dict = PyModule_GetDict(module);
+ class = PyDict_GetItemString(dict, "ComposeWindow");
+ args = Py_BuildValue("()");
+ kw = Py_BuildValue("{s:b}", "__open_window", 0);
+ self = PyObject_Call(class, args, kw);
+ Py_DECREF(args);
+ Py_DECREF(kw);
+ composewindow_set_compose((clawsmail_ComposeWindowObject*)self, compose);
+ return self;
+}
diff --git a/src/plugins/python/composewindowtype.h b/src/plugins/python/composewindowtype.h
@@ -0,0 +1,33 @@
+/* Python plugin for Claws-Mail
+ * Copyright (C) 2009 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef COMPOSEWINDOWTYPE_H
+#define COMPOSEWINDOWTYPE_H
+
+#include <Python.h>
+
+#include "compose.h"
+
+#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
+#define PyMODINIT_FUNC void
+#endif
+
+PyMODINIT_FUNC initcomposewindow(PyObject *module);
+
+PyObject* clawsmail_compose_new(PyObject *module, Compose *compose);
+
+#endif /* COMPOSEWINDOWTYPE_H */
diff --git a/src/plugins/python/foldertype.c b/src/plugins/python/foldertype.c
@@ -0,0 +1,271 @@
+/* Python plugin for Claws-Mail
+ * Copyright (C) 2009 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#include "claws-features.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "foldertype.h"
+#include "messageinfotype.h"
+
+#include <structmember.h>
+
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *name;
+ PyObject *path;
+ PyObject *mailbox_name;
+ FolderItem *folderitem;
+} clawsmail_FolderObject;
+
+
+static void Folder_dealloc(clawsmail_FolderObject* self)
+{
+ Py_XDECREF(self->name);
+ Py_XDECREF(self->path);
+ Py_XDECREF(self->mailbox_name);
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+#define FOLDERITEM_STRING_TO_PYTHON_FOLDER_MEMBER(self,fis, pms) \
+ do { \
+ if(fis) { \
+ PyObject *str; \
+ str = PyString_FromString(fis); \
+ if(str) { \
+ int retval; \
+ retval = PyObject_SetAttrString((PyObject*)self, pms, str); \
+ Py_DECREF(str); \
+ if(retval == -1) \
+ goto err; \
+ } \
+ } \
+ } while(0)
+
+static int Folder_init(clawsmail_FolderObject *self, PyObject *args, PyObject *kwds)
+{
+ const char *ss = NULL;
+ FolderItem *folderitem = NULL;
+ char create = 0;
+
+ /* optional constructor argument: folderitem id string */
+ if(!PyArg_ParseTuple(args, "|sb", &ss, &create))
+ return -1;
+
+ Py_INCREF(Py_None);
+ self->name = Py_None;
+
+ Py_INCREF(Py_None);
+ self->path = Py_None;
+
+ Py_INCREF(Py_None);
+ self->mailbox_name = Py_None;
+
+ if(ss) {
+ if(create == 0) {
+ folderitem = folder_find_item_from_identifier(ss);
+ if(!folderitem) {
+ PyErr_SetString(PyExc_ValueError, "A folder with that path does not exist, and the create parameter was False.");
+ return -1;
+ }
+ }
+ else {
+ folderitem = folder_get_item_from_identifier(ss);
+ if(!folderitem) {
+ PyErr_SetString(PyExc_IOError, "A folder with that path does not exist, and could not be created.");
+ return -1;
+ }
+ }
+ }
+
+ if(folderitem) {
+ FOLDERITEM_STRING_TO_PYTHON_FOLDER_MEMBER(self, folderitem->name, "name");
+ FOLDERITEM_STRING_TO_PYTHON_FOLDER_MEMBER(self, folderitem->path, "path");
+ FOLDERITEM_STRING_TO_PYTHON_FOLDER_MEMBER(self, folderitem->folder->name, "mailbox_name");
+ self->folderitem = folderitem;
+ }
+
+ return 0;
+
+ err:
+ return -1;
+}
+
+static PyObject* Folder_str(PyObject *self)
+{
+ PyObject *str;
+ str = PyString_FromString("Folder: ");
+ PyString_ConcatAndDel(&str, PyObject_GetAttrString(self, "name"));
+ return str;
+}
+
+static PyObject* Folder_get_identifier(clawsmail_FolderObject *self, PyObject *args)
+{
+ PyObject *obj;
+ gchar *id;
+ if(!self->folderitem)
+ return NULL;
+ id = folder_item_get_identifier(self->folderitem);
+ obj = Py_BuildValue("s", id);
+ g_free(id);
+ return obj;
+}
+
+static PyObject* Folder_get_messages(clawsmail_FolderObject *self, PyObject *args)
+{
+ GSList *msglist, *walk;
+ PyObject *retval;
+ Py_ssize_t pos;
+
+ if(!self->folderitem)
+ return NULL;
+
+ msglist = folder_item_get_msg_list(self->folderitem);
+ retval = PyTuple_New(g_slist_length(msglist));
+ if(!retval) {
+ procmsg_msg_list_free(msglist);
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ for(pos = 0, walk = msglist; walk; walk = walk->next, ++pos) {
+ PyObject *msg;
+ msg = clawsmail_messageinfo_new(walk->data);
+ PyTuple_SET_ITEM(retval, pos, msg);
+ }
+ procmsg_msg_list_free(msglist);
+
+ return retval;
+}
+
+static PyMethodDef Folder_methods[] = {
+ {"get_identifier", (PyCFunction)Folder_get_identifier, METH_NOARGS,
+ "get_identifier() - get identifier\n"
+ "\n"
+ "Get identifier for folder as a string (e.g. #mh/foo/bar)."},
+ {"get_messages", (PyCFunction)Folder_get_messages, METH_NOARGS,
+ "get_messages() - get a tuple of messages in folder\n"
+ "\n"
+ "Get a tuple of MessageInfos for the folder."},
+ {NULL}
+};
+
+static PyMemberDef Folder_members[] = {
+ {"name", T_OBJECT_EX, offsetof(clawsmail_FolderObject, name), 0,
+ "name - name of folder"},
+
+ {"path", T_OBJECT_EX, offsetof(clawsmail_FolderObject, path), 0,
+ "path - path of folder"},
+
+ {"mailbox_name", T_OBJECT_EX, offsetof(clawsmail_FolderObject, mailbox_name), 0,
+ "mailbox_name - name of the corresponding mailbox"},
+
+ {NULL}
+};
+
+static PyTypeObject clawsmail_FolderType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size*/
+ "clawsmail.Folder", /* tp_name*/
+ sizeof(clawsmail_FolderObject), /* tp_basicsize*/
+ 0, /* tp_itemsize*/
+ (destructor)Folder_dealloc, /* tp_dealloc*/
+ 0, /* tp_print*/
+ 0, /* tp_getattr*/
+ 0, /* tp_setattr*/
+ 0, /* tp_compare*/
+ 0, /* tp_repr*/
+ 0, /* tp_as_number*/
+ 0, /* tp_as_sequence*/
+ 0, /* tp_as_mapping*/
+ 0, /* tp_hash */
+ 0, /* tp_call*/
+ Folder_str, /* tp_str*/
+ 0, /* tp_getattro*/
+ 0, /* tp_setattro*/
+ 0, /* tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /* tp_flags*/
+ "Folder objects.\n\n" /* tp_doc */
+ "The __init__ function takes two optional arguments:\n"
+ "folder = Folder(identifier, [create_if_not_existing=False])\n"
+ "The identifier is an id string (e.g. '#mh/Mail/foo/bar'),"
+ "create_if_not_existing is a boolean expression.",
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ Folder_methods, /* tp_methods */
+ Folder_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)Folder_init, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+PyMODINIT_FUNC initfolder(PyObject *module)
+{
+ clawsmail_FolderType.tp_new = PyType_GenericNew;
+ if(PyType_Ready(&clawsmail_FolderType) < 0)
+ return;
+
+ Py_INCREF(&clawsmail_FolderType);
+ PyModule_AddObject(module, "Folder", (PyObject*)&clawsmail_FolderType);
+}
+
+PyObject* clawsmail_folder_new(FolderItem *folderitem)
+{
+ clawsmail_FolderObject *ff;
+ PyObject *arglist;
+ gchar *id;
+
+ if(!folderitem)
+ return NULL;
+
+ id = folder_item_get_identifier(folderitem);
+ arglist = Py_BuildValue("(s)", id);
+ g_free(id);
+ ff = (clawsmail_FolderObject*) PyObject_CallObject((PyObject*) &clawsmail_FolderType, arglist);
+ Py_DECREF(arglist);
+ return (PyObject*)ff;
+}
+
+FolderItem* clawsmail_folder_get_item(PyObject *self)
+{
+ return ((clawsmail_FolderObject*)self)->folderitem;
+}
+
+PyTypeObject* clawsmail_folder_get_type_object()
+{
+ return &clawsmail_FolderType;
+}
+
+gboolean clawsmail_folder_check(PyObject *self)
+{
+ return (PyObject_TypeCheck(self, &clawsmail_FolderType) != 0);
+}
diff --git a/src/plugins/python/foldertype.h b/src/plugins/python/foldertype.h
@@ -0,0 +1,37 @@
+/* Python plugin for Claws-Mail
+ * Copyright (C) 2009 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef FOLDERTYPE_H
+#define FOLDERTYPE_H
+
+#include <Python.h>
+
+#include "folder.h"
+
+#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
+#define PyMODINIT_FUNC void
+#endif
+
+PyMODINIT_FUNC initfolder(PyObject *module);
+
+PyObject* clawsmail_folder_new(FolderItem *folderitem);
+FolderItem* clawsmail_folder_get_item(PyObject *self);
+PyTypeObject* clawsmail_folder_get_type_object();
+
+gboolean clawsmail_folder_check(PyObject *self);
+
+#endif /* FOLDERTYPE_H */
diff --git a/src/plugins/python/messageinfotype.c b/src/plugins/python/messageinfotype.c
@@ -0,0 +1,396 @@
+/* Python plugin for Claws-Mail
+ * Copyright (C) 2009-2012 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#include "claws-features.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "messageinfotype.h"
+
+#include "common/tags.h"
+#include "mainwindow.h"
+#include "summaryview.h"
+
+#include <structmember.h>
+
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *from;
+ PyObject *to;
+ PyObject *subject;
+ PyObject *msgid;
+ PyObject *filepath;
+ MsgInfo *msginfo;
+} clawsmail_MessageInfoObject;
+
+
+static void MessageInfo_dealloc(clawsmail_MessageInfoObject* self)
+{
+ Py_XDECREF(self->from);
+ Py_XDECREF(self->to);
+ Py_XDECREF(self->subject);
+ Py_XDECREF(self->msgid);
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static int MessageInfo_init(clawsmail_MessageInfoObject *self, PyObject *args, PyObject *kwds)
+{
+ Py_INCREF(Py_None);
+ self->from = Py_None;
+
+ Py_INCREF(Py_None);
+ self->to = Py_None;
+
+ Py_INCREF(Py_None);
+ self->subject = Py_None;
+
+ Py_INCREF(Py_None);
+ self->msgid = Py_None;
+
+ return 0;
+}
+
+static PyObject* MessageInfo_str(PyObject *self)
+{
+ PyObject *str;
+ str = PyString_FromString("MessageInfo: ");
+ PyString_ConcatAndDel(&str, PyObject_GetAttrString(self, "From"));
+ PyString_ConcatAndDel(&str, PyString_FromString(" / "));
+ PyString_ConcatAndDel(&str, PyObject_GetAttrString(self, "Subject"));
+ return str;
+}
+
+static PyObject *py_boolean_return_value(gboolean val)
+{
+ if(val) {
+ Py_INCREF(Py_True);
+ return Py_True;
+ }
+ else {
+ Py_INCREF(Py_False);
+ return Py_False;
+ }
+}
+
+static PyObject *is_new(PyObject *self, PyObject *args)
+{
+ return py_boolean_return_value(MSG_IS_NEW(((clawsmail_MessageInfoObject*)self)->msginfo->flags));
+}
+
+static PyObject *is_unread(PyObject *self, PyObject *args)
+{
+ return py_boolean_return_value(MSG_IS_UNREAD(((clawsmail_MessageInfoObject*)self)->msginfo->flags));
+}
+
+static PyObject *is_marked(PyObject *self, PyObject *args)
+{
+ return py_boolean_return_value(MSG_IS_MARKED(((clawsmail_MessageInfoObject*)self)->msginfo->flags));
+}
+
+static PyObject *is_replied(PyObject *self, PyObject *args)
+{
+ return py_boolean_return_value(MSG_IS_REPLIED(((clawsmail_MessageInfoObject*)self)->msginfo->flags));
+}
+
+static PyObject *is_locked(PyObject *self, PyObject *args)
+{
+ return py_boolean_return_value(MSG_IS_LOCKED(((clawsmail_MessageInfoObject*)self)->msginfo->flags));
+}
+
+static PyObject *is_forwarded(PyObject *self, PyObject *args)
+{
+ return py_boolean_return_value(MSG_IS_FORWARDED(((clawsmail_MessageInfoObject*)self)->msginfo->flags));
+}
+
+static PyObject* get_tags(PyObject *self, PyObject *args)
+{
+ GSList *tags_list;
+ Py_ssize_t num_tags;
+ PyObject *tags_tuple;
+
+ tags_list = ((clawsmail_MessageInfoObject*)self)->msginfo->tags;
+ num_tags = g_slist_length(tags_list);
+
+ tags_tuple = PyTuple_New(num_tags);
+ if(tags_tuple != NULL) {
+ Py_ssize_t iTag;
+ PyObject *tag_object;
+ GSList *walk;
+
+ iTag = 0;
+ for(walk = tags_list; walk; walk = walk->next) {
+ tag_object = Py_BuildValue("s", tags_get_tag(GPOINTER_TO_INT(walk->data)));
+ if(tag_object == NULL) {
+ Py_DECREF(tags_tuple);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(tags_tuple, iTag++, tag_object);
+ }
+ }
+
+ return tags_tuple;
+}
+
+
+static PyObject* add_or_remove_tag(PyObject *self, PyObject *args, gboolean add)
+{
+ int retval;
+ const char *tag_str;
+ gint tag_id;
+ MsgInfo *msginfo;
+ MainWindow *mainwin;
+
+ retval = PyArg_ParseTuple(args, "s", &tag_str);
+ if(!retval)
+ return NULL;
+
+ tag_id = tags_get_id_for_str(tag_str);
+ if(tag_id == -1) {
+ PyErr_SetString(PyExc_ValueError, "Tag does not exist");
+ return NULL;
+ }
+
+ msginfo = ((clawsmail_MessageInfoObject*)self)->msginfo;
+
+ if(!add) {
+ /* raise KeyError if tag is not set */
+ if(!g_slist_find(msginfo->tags, GINT_TO_POINTER(tag_id))) {
+ PyErr_SetString(PyExc_KeyError, "Tag is not set on this message");
+ return NULL;
+ }
+ }
+
+ procmsg_msginfo_update_tags(msginfo, add, tag_id);
+
+ /* update display */
+ mainwin = mainwindow_get_mainwindow();
+ if(mainwin)
+ summary_redisplay_msg(mainwin->summaryview);
+
+ Py_RETURN_NONE;
+}
+
+
+
+static PyObject* add_tag(PyObject *self, PyObject *args)
+{
+ return add_or_remove_tag(self, args, TRUE);
+}
+
+
+static PyObject* remove_tag(PyObject *self, PyObject *args)
+{
+ return add_or_remove_tag(self, args, FALSE);
+}
+
+
+static PyMethodDef MessageInfo_methods[] = {
+ {"is_new", is_new, METH_NOARGS,
+ "is_new() - checks if the message is new\n"
+ "\n"
+ "Returns True if the new flag of the message is set."},
+
+ {"is_unread", is_unread, METH_NOARGS,
+ "is_unread() - checks if the message is unread\n"
+ "\n"
+ "Returns True if the unread flag of the message is set."},
+
+ {"is_marked", is_marked, METH_NOARGS,
+ "is_marked() - checks if the message is marked\n"
+ "\n"
+ "Returns True if the marked flag of the message is set."},
+
+ {"is_replied", is_replied, METH_NOARGS,
+ "is_replied() - checks if the message has been replied to\n"
+ "\n"
+ "Returns True if the replied flag of the message is set."},
+
+ {"is_locked", is_locked, METH_NOARGS,
+ "is_locked() - checks if the message has been locked\n"
+ "\n"
+ "Returns True if the locked flag of the message is set."},
+
+ {"is_forwarded", is_forwarded, METH_NOARGS,
+ "is_forwarded() - checks if the message has been forwarded\n"
+ "\n"
+ "Returns True if the forwarded flag of the message is set."},
+
+ {"get_tags", get_tags, METH_NOARGS,
+ "get_tags() - get message tags\n"
+ "\n"
+ "Returns a tuple of tags that apply to this message."},
+
+ {"add_tag", add_tag, METH_VARARGS,
+ "add_tag(tag) - add a tag to this message\n"
+ "\n"
+ "Add a tag to this message. If the tag is already set, nothing is done.\n"
+ "If the tag does not exist, a ValueError exception is raised."},
+
+ {"remove_tag", remove_tag, METH_VARARGS,
+ "remove_tag(tag) - remove a tag from this message\n"
+ "\n"
+ "Remove a tag from this message. If the tag is not set, a KeyError exception is raised.\n"
+ "If the tag does not exist, a ValueError exception is raised."},
+
+ {NULL}
+};
+
+static PyMemberDef MessageInfo_members[] = {
+ {"From", T_OBJECT_EX, offsetof(clawsmail_MessageInfoObject, from), 0,
+ "From - the From header of the message"},
+
+ {"To", T_OBJECT_EX, offsetof(clawsmail_MessageInfoObject, to), 0,
+ "To - the To header of the message"},
+
+ {"Subject", T_OBJECT_EX, offsetof(clawsmail_MessageInfoObject, subject), 0,
+ "Subject - the subject header of the message"},
+
+ {"MessageID", T_OBJECT_EX, offsetof(clawsmail_MessageInfoObject, msgid), 0,
+ "MessageID - the Message-ID header of the message"},
+
+ {"FilePath", T_OBJECT_EX, offsetof(clawsmail_MessageInfoObject, filepath), 0,
+ "FilePath - path and filename of the message"},
+
+ {NULL}
+};
+
+static PyTypeObject clawsmail_MessageInfoType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size*/
+ "clawsmail.MessageInfo", /* tp_name*/
+ sizeof(clawsmail_MessageInfoObject), /* tp_basicsize*/
+ 0, /* tp_itemsize*/
+ (destructor)MessageInfo_dealloc, /* tp_dealloc*/
+ 0, /* tp_print*/
+ 0, /* tp_getattr*/
+ 0, /* tp_setattr*/
+ 0, /* tp_compare*/
+ 0, /* tp_repr*/
+ 0, /* tp_as_number*/
+ 0, /* tp_as_sequence*/
+ 0, /* tp_as_mapping*/
+ 0, /* tp_hash */
+ 0, /* tp_call*/
+ MessageInfo_str, /* tp_str*/
+ 0, /* tp_getattro*/
+ 0, /* tp_setattro*/
+ 0, /* tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /* tp_flags*/
+ "A MessageInfo represents" /* tp_doc */
+ "a single message.",
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ MessageInfo_methods, /* tp_methods */
+ MessageInfo_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)MessageInfo_init,/* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+PyMODINIT_FUNC initmessageinfo(PyObject *module)
+{
+ clawsmail_MessageInfoType.tp_new = PyType_GenericNew;
+ if(PyType_Ready(&clawsmail_MessageInfoType) < 0)
+ return;
+
+ Py_INCREF(&clawsmail_MessageInfoType);
+ PyModule_AddObject(module, "MessageInfo", (PyObject*)&clawsmail_MessageInfoType);
+}
+
+#define MSGINFO_STRING_TO_PYTHON_MESSAGEINFO_MEMBER(fis, pms) \
+ do { \
+ if(fis) { \
+ PyObject *str; \
+ str = PyString_FromString(fis); \
+ if(str) { \
+ int retval; \
+ retval = PyObject_SetAttrString((PyObject*)ff, pms, str); \
+ Py_DECREF(str); \
+ if(retval == -1) \
+ goto err; \
+ } \
+ } \
+ } while(0)
+
+static gboolean update_members(clawsmail_MessageInfoObject *ff, MsgInfo *msginfo)
+{
+ gchar *filepath;
+
+ MSGINFO_STRING_TO_PYTHON_MESSAGEINFO_MEMBER(msginfo->from, "From");
+ MSGINFO_STRING_TO_PYTHON_MESSAGEINFO_MEMBER(msginfo->to, "To");
+ MSGINFO_STRING_TO_PYTHON_MESSAGEINFO_MEMBER(msginfo->subject, "Subject");
+ MSGINFO_STRING_TO_PYTHON_MESSAGEINFO_MEMBER(msginfo->msgid, "MessageID");
+
+ filepath = procmsg_get_message_file_path(msginfo);
+ if(filepath) {
+ MSGINFO_STRING_TO_PYTHON_MESSAGEINFO_MEMBER(filepath, "FilePath");
+ g_free(filepath);
+ }
+ else {
+ MSGINFO_STRING_TO_PYTHON_MESSAGEINFO_MEMBER("", "FilePath");
+ }
+
+ return TRUE;
+err:
+ return FALSE;
+}
+
+PyObject* clawsmail_messageinfo_new(MsgInfo *msginfo)
+{
+ clawsmail_MessageInfoObject *ff;
+
+ if(!msginfo)
+ return NULL;
+
+ ff = (clawsmail_MessageInfoObject*) PyObject_CallObject((PyObject*) &clawsmail_MessageInfoType, NULL);
+ if(!ff)
+ return NULL;
+
+ ff->msginfo = msginfo;
+
+ if(update_members(ff, msginfo))
+ return (PyObject*)ff;
+ else {
+ Py_XDECREF(ff);
+ return NULL;
+ }
+}
+
+PyTypeObject* clawsmail_messageinfo_get_type_object()
+{
+ return &clawsmail_MessageInfoType;
+}
+
+MsgInfo* clawsmail_messageinfo_get_msginfo(PyObject *self)
+{
+ return ((clawsmail_MessageInfoObject*)self)->msginfo;
+}
diff --git a/src/plugins/python/messageinfotype.h b/src/plugins/python/messageinfotype.h
@@ -0,0 +1,35 @@
+/* Python plugin for Claws-Mail
+ * Copyright (C) 2009 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MESSAGEINFOTYPE_H
+#define MESSAGEINFOTYPE_H
+
+#include <Python.h>
+
+#include "procmsg.h"
+
+#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
+#define PyMODINIT_FUNC void
+#endif
+
+PyMODINIT_FUNC initmessageinfo(PyObject *module);
+
+PyObject* clawsmail_messageinfo_new(MsgInfo *msginfo);
+MsgInfo* clawsmail_messageinfo_get_msginfo(PyObject *self);
+PyTypeObject* clawsmail_messageinfo_get_type_object();
+
+#endif /* MESSAGEINFOTYPE_H */
diff --git a/src/plugins/python/nodetype.c b/src/plugins/python/nodetype.c
@@ -0,0 +1,98 @@
+/* Python plugin for Claws-Mail
+ * Copyright (C) 2009 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#include "claws-features.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "nodetype.h"
+
+#include <structmember.h>
+
+PyMODINIT_FUNC initnode(PyObject *module)
+{
+ PyObject *dict;
+ PyObject *res;
+ const char *cmd =
+ "class Node(object):\n"
+ " \"\"\"A general purpose tree container type\"\"\"\n"
+ "\n"
+ " def __init__(self):\n"
+ " self.data = None\n"
+ " self.children = []\n"
+ "\n"
+ " def __str__(self):\n"
+ " return '\\n'.join(self.get_str_list(0))\n"
+ "\n"
+ " def get_str_list(self, level):\n"
+ " \"\"\"get_str_list(level) - get a list of string-representations of the tree data\n"
+ " \n"
+ " The nesting of the tree elements is represented by various levels of indentation.\"\"\"\n"
+ " str = []\n"
+ " indent = ' '*level\n"
+ " if self.data:\n"
+ " str.append(indent + self.data.__str__())\n"
+ " else:\n"
+ " str.append(indent + 'None')\n"
+ " for child in self.children:\n"
+ " str.extend(child.get_str_list(level+1))\n"
+ " return str\n"
+ "\n"
+ " def traverse(self, callback, arg=None):\n"
+ " \"\"\"traverse(callback [, arg=None]) - traverse the tree\n"
+ " \n"
+ " Traverse the tree, calling the callback function for each node element,\n"
+ " with optional arg as user-data. The expected callback function signature is\n"
+ " callback(node_data [, arg]).\"\"\"\n"
+ " if self.data:\n"
+ " if arg is not None:\n"
+ " callback(self.data, arg)\n"
+ " else:\n"
+ " callback(self.data)\n"
+ " for child in self.children:\n"
+ " child.traverse(callback, arg)\n"
+ "\n"
+ " def flat_list(self):\n"
+ " \"\"\"flat_list() - get a flat list of the tree\n"
+ " \n"
+ " Returns a flat list of the tree, disregarding the nesting structure.\"\"\"\n"
+ " flat_list = []\n"
+ " self.traverse(lambda data,list: list.append(data), flat_list)\n"
+ " return flat_list\n"
+ "\n";
+
+ dict = PyModule_GetDict(module);
+
+ if(PyDict_GetItemString(dict, "__builtins__") == NULL)
+ PyDict_SetItemString(dict, "__builtins__", PyEval_GetBuiltins());
+
+ res = PyRun_String(cmd, Py_file_input, dict, dict);
+ Py_XDECREF(res);
+}
+
+PyObject* clawsmail_node_new(PyObject *module)
+{
+ PyObject *class, *dict;
+
+ dict = PyModule_GetDict(module);
+ class = PyDict_GetItemString(dict, "Node");
+ return PyObject_CallObject(class, NULL);
+}
diff --git a/src/plugins/python/nodetype.h b/src/plugins/python/nodetype.h
@@ -0,0 +1,31 @@
+/* Python plugin for Claws-Mail
+ * Copyright (C) 2009 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NODETYPE_H
+#define NODETYPE_H
+
+#include <Python.h>
+
+#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
+#define PyMODINIT_FUNC void
+#endif
+
+PyMODINIT_FUNC initnode(PyObject *module);
+
+PyObject* clawsmail_node_new(PyObject *module);
+
+#endif /* NODETYPE_H */
diff --git a/src/plugins/python/placeholder.txt b/src/plugins/python/placeholder.txt
@@ -1 +0,0 @@
-Placeholder
diff --git a/src/plugins/python/python-hooks.c b/src/plugins/python/python-hooks.c
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2008-2009 Christian Hammond
+ * Copyright (c) 2008-2009 David Trowbridge
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#include "claws-features.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#ifdef ENABLE_PYTHON
+#include <Python.h>
+#include <pygobject.h>
+#include <pygtk/pygtk.h>
+#endif // ENABLE_PYTHON
+
+#include <dlfcn.h>
+
+#include <signal.h>
+
+#include "python-hooks.h"
+
+
+static gboolean python_enabled = FALSE;
+
+#ifdef ENABLE_PYTHON
+static GString *captured_stdout = NULL;
+static GString *captured_stderr = NULL;
+
+
+static PyObject *
+capture_stdout(PyObject *self, PyObject *args)
+{
+ char *str = NULL;
+
+ if (!PyArg_ParseTuple(args, "s", &str))
+ return NULL;
+
+ g_string_append(captured_stdout, str);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+capture_stderr(PyObject *self, PyObject *args)
+{
+ char *str = NULL;
+
+ if (!PyArg_ParseTuple(args, "s", &str))
+ return NULL;
+
+ g_string_append(captured_stderr, str);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+capture_stdin(PyObject *self, PyObject *args)
+{
+ /* Return an empty string.
+ * This is what read() returns when hitting EOF. */
+ return PyString_FromString("");
+}
+
+static PyObject *
+wrap_gobj(PyObject *self, PyObject *args)
+{
+ void *addr;
+ GObject *obj;
+
+ if (!PyArg_ParseTuple(args, "l", &addr))
+ return NULL;
+
+ if (!G_IS_OBJECT(addr))
+ return NULL; // XXX
+
+ obj = G_OBJECT(addr);
+
+ if (!obj)
+ return NULL; // XXX
+
+ return pygobject_new(obj);
+}
+
+static PyMethodDef parasite_python_methods[] = {
+ {"capture_stdout", capture_stdout, METH_VARARGS, "Captures stdout"},
+ {"capture_stderr", capture_stderr, METH_VARARGS, "Captures stderr"},
+ {"capture_stdin", capture_stdin, METH_VARARGS, "Captures stdin"},
+ {"gobj", wrap_gobj, METH_VARARGS, "Wraps a C GObject"},
+ {NULL, NULL, 0, NULL}
+};
+
+
+static gboolean
+is_blacklisted(void)
+{
+ const char *prgname = g_get_prgname();
+
+ return (!strcmp(prgname, "gimp"));
+}
+#endif // ENABLE_PYTHON
+
+void
+parasite_python_init(void)
+{
+#ifdef ENABLE_PYTHON
+ int res;
+ struct sigaction old_sigint;
+ PyObject *pygtk;
+
+ if (is_blacklisted())
+ return;
+
+ /* This prevents errors such as "undefined symbol: PyExc_ImportError" */
+ if (!dlopen(PYTHON_SHARED_LIB, RTLD_NOW | RTLD_GLOBAL))
+ {
+ g_error("%s\n", dlerror());
+ return;
+ }
+
+ captured_stdout = g_string_new("");
+ captured_stderr = g_string_new("");
+
+ /* Back up and later restore SIGINT so Python doesn't steal it from us. */
+ res = sigaction(SIGINT, NULL, &old_sigint);
+
+ if (!Py_IsInitialized())
+ Py_Initialize();
+
+ res = sigaction(SIGINT, &old_sigint, NULL);
+
+ Py_InitModule("parasite", parasite_python_methods);
+ PyRun_SimpleString(
+ "import parasite\n"
+ "import sys\n"
+ "\n"
+ "class StdoutCatcher:\n"
+ " def write(self, str):\n"
+ " parasite.capture_stdout(str)\n"
+ " def flush(self):\n"
+ " pass\n"
+ "\n"
+ "class StderrCatcher:\n"
+ " def write(self, str):\n"
+ " parasite.capture_stderr(str)\n"
+ " def flush(self):\n"
+ " pass\n"
+ "\n"
+ "class StdinCatcher:\n"
+ " def readline(self, size=-1):\n"
+ " return parasite.capture_stdin(size)\n"
+ " def read(self, size=-1):\n"
+ " return parasite.capture_stdin(size)\n"
+ " def flush(self):\n"
+ " pass\n"
+ "\n"
+ );
+
+ if (!pygobject_init(-1, -1, -1))
+ return;
+
+ pygtk = PyImport_ImportModule("gtk");
+
+ if (pygtk != NULL)
+ {
+ PyObject *module_dict = PyModule_GetDict(pygtk);
+ PyObject *cobject = PyDict_GetItemString(module_dict, "_PyGtk_API");
+
+ /*
+ * This seems to be NULL when we're running a PyGTK program.
+ * We really need to find out why.
+ */
+ if (cobject != NULL)
+ {
+ if (PyCObject_Check(cobject))
+ _PyGtk_API = (struct _PyGtk_FunctionStruct*)
+ PyCObject_AsVoidPtr(cobject);
+ else {
+ PyErr_SetString(PyExc_RuntimeError,
+ "could not find _PyGtk_API object");
+ return;
+ }
+ }
+ } else {
+ PyErr_SetString(PyExc_ImportError, "could not import gtk");
+ return;
+ }
+
+ python_enabled = TRUE;
+#endif // ENABLE_PYTHON
+}
+
+void
+parasite_python_run(const char *command,
+ ParasitePythonLogger stdout_logger,
+ ParasitePythonLogger stderr_logger,
+ gpointer user_data)
+{
+#ifdef ENABLE_PYTHON
+ PyGILState_STATE gstate;
+ PyObject *module;
+ PyObject *dict;
+ PyObject *obj;
+ const char *cp;
+
+ /* empty string as command is a noop */
+ if(!strcmp(command, ""))
+ return;
+
+ /* if first non-whitespace character is '#', command is also a noop */
+ cp = command;
+ while(cp && (*cp != '\0') && g_ascii_isspace(*cp))
+ cp++;
+ if(cp && *cp == '#')
+ return;
+
+ gstate = PyGILState_Ensure();
+
+ module = PyImport_AddModule("__main__");
+ dict = PyModule_GetDict(module);
+
+ PyRun_SimpleString("old_stdout = sys.stdout\n"
+ "old_stderr = sys.stderr\n"
+ "old_stdin = sys.stdin\n"
+ "sys.stdout = StdoutCatcher()\n"
+ "sys.stderr = StderrCatcher()\n"
+ "sys.stdin = StdinCatcher()\n");
+
+ obj = PyRun_String(command, Py_single_input, dict, dict);
+ if(PyErr_Occurred())
+ PyErr_Print();
+ PyRun_SimpleString("sys.stdout = old_stdout\n"
+ "sys.stderr = old_stderr\n"
+ "sys.stdin = old_stdin\n");
+
+ if (stdout_logger != NULL)
+ stdout_logger(captured_stdout->str, user_data);
+
+ if (stderr_logger != NULL)
+ stderr_logger(captured_stderr->str, user_data);
+
+ // Print any returned object
+ if (obj != NULL && obj != Py_None) {
+ PyObject *repr = PyObject_Repr(obj);
+ if (repr != NULL) {
+ char *string = PyString_AsString(repr);
+
+ stdout_logger(string, user_data);
+ stdout_logger("\n", user_data);
+ }
+
+ Py_XDECREF(repr);
+ }
+ Py_XDECREF(obj);
+
+ PyGILState_Release(gstate);
+ g_string_erase(captured_stdout, 0, -1);
+ g_string_erase(captured_stderr, 0, -1);
+#endif // ENABLE_PYTHON
+}
+
+gboolean
+parasite_python_is_enabled(void)
+{
+ return python_enabled;
+}
+
+// vim: set et sw=4 ts=4:
diff --git a/src/plugins/python/python-hooks.h b/src/plugins/python/python-hooks.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2008-2009 Christian Hammond
+ * Copyright (c) 2008-2009 David Trowbridge
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef _GTKPARASITE_PYTHON_MODULE_H_
+#define _GTKPARASITE_PYTHON_MODULE_H_
+
+#include <glib.h>
+
+
+typedef void (*ParasitePythonLogger)(const char *text, gpointer user_data);
+
+void parasite_python_init(void);
+void parasite_python_run(const char *command,
+ ParasitePythonLogger stdout_logger,
+ ParasitePythonLogger stderr_logger,
+ gpointer user_data);
+gboolean parasite_python_is_enabled(void);
+
+#endif // _GTKPARASITE_PYTHON_MODULE_H_
diff --git a/src/plugins/python/python-shell.c b/src/plugins/python/python-shell.c
@@ -0,0 +1,438 @@
+/*
+ * Copyright (c) 2008-2009 Christian Hammond
+ * Copyright (c) 2008-2009 David Trowbridge
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <gdk/gdkkeysyms.h>
+#include <string.h>
+
+#include "python-hooks.h"
+#include "python-shell.h"
+
+#define MAX_HISTORY_LENGTH 20
+
+#define PARASITE_PYTHON_SHELL_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), PARASITE_TYPE_PYTHON_SHELL, \
+ ParasitePythonShellPrivate))
+
+typedef struct
+{
+ GtkWidget *textview;
+
+ GtkTextMark *scroll_mark;
+ GtkTextMark *line_start_mark;
+
+ GQueue *history;
+ GList *cur_history_item;
+
+ GString *pending_command;
+ gboolean in_block;
+
+} ParasitePythonShellPrivate;
+
+enum
+{
+ LAST_SIGNAL
+};
+
+
+/* Widget functions */
+static void parasite_python_shell_finalize(GObject *obj);
+
+/* Python integration */
+static void parasite_python_shell_write_prompt(GtkWidget *python_shell);
+static char *parasite_python_shell_get_input(GtkWidget *python_shell);
+
+/* Callbacks */
+static gboolean parasite_python_shell_key_press_cb(GtkWidget *textview,
+ GdkEventKey *event,
+ GtkWidget *python_shell);
+
+
+static GtkVBoxClass *parent_class = NULL;
+//static guint signals[LAST_SIGNAL] = {0};
+
+G_DEFINE_TYPE(ParasitePythonShell, parasite_python_shell, GTK_TYPE_VBOX);
+
+
+static void
+parasite_python_shell_class_init(ParasitePythonShellClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ object_class->finalize = parasite_python_shell_finalize;
+
+ g_type_class_add_private(klass, sizeof(ParasitePythonShellPrivate));
+}
+
+static void
+parasite_python_shell_init(ParasitePythonShell *python_shell)
+{
+ ParasitePythonShellPrivate *priv =
+ PARASITE_PYTHON_SHELL_GET_PRIVATE(python_shell);
+ GtkWidget *swin;
+ GtkTextBuffer *buffer;
+ GtkTextIter iter;
+ PangoFontDescription *font_desc;
+
+ priv->history = g_queue_new();
+
+ gtk_box_set_spacing(GTK_BOX(python_shell), 6);
+
+ swin = gtk_scrolled_window_new(NULL, NULL);
+ gtk_widget_show(swin);
+ gtk_box_pack_start(GTK_BOX(python_shell), swin, TRUE, TRUE, 0);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(swin),
+ GTK_SHADOW_IN);
+
+ priv->textview = gtk_text_view_new();
+ gtk_widget_show(priv->textview);
+ gtk_container_add(GTK_CONTAINER(swin), priv->textview);
+ gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(priv->textview), TRUE);
+ gtk_text_view_set_pixels_above_lines(GTK_TEXT_VIEW(priv->textview), 3);
+ gtk_text_view_set_left_margin(GTK_TEXT_VIEW(priv->textview), 3);
+ gtk_text_view_set_right_margin(GTK_TEXT_VIEW(priv->textview), 3);
+
+ g_signal_connect(G_OBJECT(priv->textview), "key_press_event",
+ G_CALLBACK(parasite_python_shell_key_press_cb),
+ python_shell);
+
+ /* Make the textview monospaced */
+ font_desc = pango_font_description_from_string("monospace");
+ pango_font_description_set_size(font_desc, 10 * PANGO_SCALE);
+ gtk_widget_modify_font(priv->textview, font_desc);
+ pango_font_description_free(font_desc);
+
+ /* Create the end-of-buffer mark */
+ buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(priv->textview));
+ gtk_text_buffer_get_end_iter(buffer, &iter);
+ priv->scroll_mark = gtk_text_buffer_create_mark(buffer, "scroll_mark",
+ &iter, FALSE);
+
+ /* Create the beginning-of-line mark */
+ priv->line_start_mark = gtk_text_buffer_create_mark(buffer,
+ "line_start_mark",
+ &iter, TRUE);
+
+ /* Register some tags */
+ gtk_text_buffer_create_tag(buffer, "stdout", NULL);
+ gtk_text_buffer_create_tag(buffer, "stderr",
+ "foreground", "red",
+ "paragraph-background", "#FFFFE0",
+ NULL);
+ gtk_text_buffer_create_tag(buffer, "prompt",
+ "foreground", "blue",
+ NULL);
+
+ parasite_python_shell_write_prompt(GTK_WIDGET(python_shell));
+}
+
+static void
+parasite_python_shell_finalize(GObject *python_shell)
+{
+ ParasitePythonShellPrivate *priv =
+ PARASITE_PYTHON_SHELL_GET_PRIVATE(python_shell);
+
+ g_queue_free(priv->history);
+}
+
+static void
+parasite_python_shell_log_stdout(const char *text, gpointer python_shell)
+{
+ parasite_python_shell_append_text(PARASITE_PYTHON_SHELL(python_shell),
+ text, "stdout");
+}
+
+static void
+parasite_python_shell_log_stderr(const char *text, gpointer python_shell)
+{
+ parasite_python_shell_append_text(PARASITE_PYTHON_SHELL(python_shell),
+ text, "stderr");
+}
+
+static void
+parasite_python_shell_write_prompt(GtkWidget *python_shell)
+{
+ ParasitePythonShellPrivate *priv =
+ PARASITE_PYTHON_SHELL_GET_PRIVATE(python_shell);
+ GtkTextBuffer *buffer =
+ gtk_text_view_get_buffer(GTK_TEXT_VIEW(priv->textview));
+ GtkTextIter iter;
+ const char *prompt = (priv->pending_command == NULL ? ">>> " : "... ");
+
+ parasite_python_shell_append_text(PARASITE_PYTHON_SHELL(python_shell),
+ prompt, "prompt");
+
+ gtk_text_buffer_get_end_iter(buffer, &iter);
+ gtk_text_buffer_move_mark(buffer, priv->line_start_mark, &iter);
+}
+
+static void
+parasite_python_shell_process_line(GtkWidget *python_shell)
+{
+ ParasitePythonShellPrivate *priv =
+ PARASITE_PYTHON_SHELL_GET_PRIVATE(python_shell);
+
+ char *command = parasite_python_shell_get_input(python_shell);
+ char last_char;
+
+ parasite_python_shell_append_text(PARASITE_PYTHON_SHELL(python_shell),
+ "\n", NULL);
+
+ if (*command != '\0')
+ {
+ /* Save this command in the history. */
+ g_queue_push_head(priv->history, command);
+ priv->cur_history_item = NULL;
+
+ if (g_queue_get_length(priv->history) > MAX_HISTORY_LENGTH)
+ g_free(g_queue_pop_tail(priv->history));
+ }
+
+ last_char = command[MAX(0, strlen(command) - 1)];
+
+ if (last_char == ':' || last_char == '\\' ||
+ (priv->in_block && g_ascii_isspace(command[0])))
+ {
+ printf("in block.. %c, %d, %d\n",
+ last_char, priv->in_block,
+ g_ascii_isspace(command[0]));
+ /* This is a multi-line expression */
+ if (priv->pending_command == NULL)
+ priv->pending_command = g_string_new(command);
+ else
+ g_string_append(priv->pending_command, command);
+
+ g_string_append_c(priv->pending_command, '\n');
+
+ if (last_char == ':')
+ priv->in_block = TRUE;
+ }
+ else
+ {
+ if (priv->pending_command != NULL)
+ {
+ g_string_append(priv->pending_command, command);
+ g_string_append_c(priv->pending_command, '\n');
+
+ /* We're not actually leaking this. It's in the history. */
+ command = g_string_free(priv->pending_command, FALSE);
+ }
+ parasite_python_run(command,
+ parasite_python_shell_log_stdout,
+ parasite_python_shell_log_stderr,
+ python_shell);
+ if (priv->pending_command != NULL)
+ {
+ /* Now do the cleanup. */
+ g_free(command);
+ priv->pending_command = NULL;
+ priv->in_block = FALSE;
+ }
+ }
+ parasite_python_shell_write_prompt(python_shell);
+}
+
+static void
+parasite_python_shell_replace_input(GtkWidget *python_shell,
+ const char *text)
+{
+ ParasitePythonShellPrivate *priv =
+ PARASITE_PYTHON_SHELL_GET_PRIVATE(python_shell);
+
+ GtkTextBuffer *buffer =
+ gtk_text_view_get_buffer(GTK_TEXT_VIEW(priv->textview));
+ GtkTextIter start_iter;
+ GtkTextIter end_iter;
+
+ gtk_text_buffer_get_iter_at_mark(buffer, &start_iter,
+ priv->line_start_mark);
+ gtk_text_buffer_get_end_iter(buffer, &end_iter);
+
+ gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
+ gtk_text_buffer_insert(buffer, &end_iter, text, -1);
+}
+
+static char *
+parasite_python_shell_get_input(GtkWidget *python_shell)
+{
+ ParasitePythonShellPrivate *priv =
+ PARASITE_PYTHON_SHELL_GET_PRIVATE(python_shell);
+ GtkTextBuffer *buffer =
+ gtk_text_view_get_buffer(GTK_TEXT_VIEW(priv->textview));
+ GtkTextIter start_iter;
+ GtkTextIter end_iter;
+
+ gtk_text_buffer_get_iter_at_mark(buffer, &start_iter,
+ priv->line_start_mark);
+ gtk_text_buffer_get_end_iter(buffer, &end_iter);
+
+ return gtk_text_buffer_get_text(buffer, &start_iter, &end_iter, FALSE);
+}
+
+static const char *
+parasite_python_shell_get_history_back(GtkWidget *python_shell)
+{
+ ParasitePythonShellPrivate *priv =
+ PARASITE_PYTHON_SHELL_GET_PRIVATE(python_shell);
+
+ if (priv->cur_history_item == NULL)
+ {
+ priv->cur_history_item = g_queue_peek_head_link(priv->history);
+
+ if (priv->cur_history_item == NULL)
+ return "";
+ }
+ else if (priv->cur_history_item->next != NULL)
+ priv->cur_history_item = priv->cur_history_item->next;
+
+ return (const char *)priv->cur_history_item->data;
+}
+
+static const char *
+parasite_python_shell_get_history_forward(GtkWidget *python_shell)
+{
+ ParasitePythonShellPrivate *priv =
+ PARASITE_PYTHON_SHELL_GET_PRIVATE(python_shell);
+
+ if (priv->cur_history_item == NULL || priv->cur_history_item->prev == NULL)
+ {
+ priv->cur_history_item = NULL;
+ return "";
+ }
+
+ priv->cur_history_item = priv->cur_history_item->prev;
+
+ return (const char *)priv->cur_history_item->data;
+}
+
+static gboolean
+parasite_python_shell_key_press_cb(GtkWidget *textview,
+ GdkEventKey *event,
+ GtkWidget *python_shell)
+{
+ if (event->keyval == GDK_Return)
+ {
+ parasite_python_shell_process_line(python_shell);
+ return TRUE;
+ }
+ else if (event->keyval == GDK_Up)
+ {
+ parasite_python_shell_replace_input(python_shell,
+ parasite_python_shell_get_history_back(python_shell));
+ return TRUE;
+ }
+ else if (event->keyval == GDK_Down)
+ {
+ parasite_python_shell_replace_input(python_shell,
+ parasite_python_shell_get_history_forward(python_shell));
+ return TRUE;
+ }
+ else if (event->string != NULL)
+ {
+ ParasitePythonShellPrivate *priv =
+ PARASITE_PYTHON_SHELL_GET_PRIVATE(python_shell);
+ GtkTextBuffer *buffer =
+ gtk_text_view_get_buffer(GTK_TEXT_VIEW(priv->textview));
+ GtkTextMark *insert_mark = gtk_text_buffer_get_insert(buffer);
+ GtkTextMark *selection_mark =
+ gtk_text_buffer_get_selection_bound(buffer);
+ GtkTextIter insert_iter;
+ GtkTextIter selection_iter;
+ GtkTextIter start_iter;
+ gint cmp_start_insert;
+ gint cmp_start_select;
+ gint cmp_insert_select;
+
+ gtk_text_buffer_get_iter_at_mark(buffer, &start_iter,
+ priv->line_start_mark);
+ gtk_text_buffer_get_iter_at_mark(buffer, &insert_iter, insert_mark);
+ gtk_text_buffer_get_iter_at_mark(buffer, &selection_iter,
+ selection_mark);
+
+ cmp_start_insert = gtk_text_iter_compare(&start_iter, &insert_iter);
+ cmp_start_select = gtk_text_iter_compare(&start_iter, &selection_iter);
+ cmp_insert_select = gtk_text_iter_compare(&insert_iter,
+ &selection_iter);
+
+ if (cmp_start_insert == 0 && cmp_start_select == 0 &&
+ (event->keyval == GDK_BackSpace ||
+ event->keyval == GDK_Left))
+ {
+ return TRUE;
+ }
+ if (cmp_start_insert <= 0 && cmp_start_select <= 0)
+ {
+ return FALSE;
+ }
+ else if (cmp_start_insert > 0 && cmp_start_select > 0)
+ {
+ gtk_text_buffer_place_cursor(buffer, &start_iter);
+ }
+ else if (cmp_insert_select < 0)
+ {
+ gtk_text_buffer_move_mark(buffer, insert_mark, &start_iter);
+ }
+ else if (cmp_insert_select > 0)
+ {
+ gtk_text_buffer_move_mark(buffer, selection_mark, &start_iter);
+ }
+ }
+
+ return FALSE;
+}
+
+GtkWidget *
+parasite_python_shell_new(void)
+{
+ return g_object_new(PARASITE_TYPE_PYTHON_SHELL, NULL);
+}
+
+void
+parasite_python_shell_append_text(ParasitePythonShell *python_shell,
+ const char *str,
+ const char *tag)
+{
+ ParasitePythonShellPrivate *priv =
+ PARASITE_PYTHON_SHELL_GET_PRIVATE(python_shell);
+
+ GtkTextIter end;
+ GtkTextBuffer *buffer =
+ gtk_text_view_get_buffer(GTK_TEXT_VIEW(priv->textview));
+ GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
+
+ gtk_text_buffer_get_end_iter(buffer, &end);
+ gtk_text_buffer_move_mark(buffer, mark, &end);
+ gtk_text_buffer_insert_with_tags_by_name(buffer, &end, str, -1, tag, NULL);
+ gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(priv->textview), mark,
+ 0, TRUE, 0, 1);
+}
+
+void
+parasite_python_shell_focus(ParasitePythonShell *python_shell)
+{
+ gtk_widget_grab_focus(PARASITE_PYTHON_SHELL_GET_PRIVATE(python_shell)->textview);
+}
+
+// vim: set et ts=4:
diff --git a/src/plugins/python/python-shell.h b/src/plugins/python/python-shell.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2008-2009 Christian Hammond
+ * Copyright (c) 2008-2009 David Trowbridge
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef _PARASITE_PYTHON_SHELL_H_
+#define _PARASITE_PYTHON_SHELL_H_
+
+typedef struct _ParasitePythonShell ParasitePythonShell;
+typedef struct _ParasitePythonShellClass ParasitePythonShellClass;
+
+#include <gtk/gtk.h>
+
+#define PARASITE_TYPE_PYTHON_SHELL (parasite_python_shell_get_type())
+#define PARASITE_PYTHON_SHELL(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), PARASITE_TYPE_PYTHON_SHELL, ParasitePythonShell))
+#define PARASITE_PYTHON_SHELL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), PARASITE_TYPE_PYTHON_SHELL, ParasitePythonShellClass))
+#define PARASITE_IS_PYTHON_SHELL(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), PARASITE_TYPE_PYTHON_SHELL))
+#define PARASITE_IS_PYTHON_SHELL_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), PARASITE_TYPE_PYTHON_SHELL))
+#define PARASITE_PYTHON_SHELL_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), PARASITE_TYPE_PYTHON_SHELL, ParasitePythonShellClass))
+
+
+struct _ParasitePythonShell
+{
+ GtkVBox parent_object;
+
+ void (*gtk_reserved1)(void);
+ void (*gtk_reserved2)(void);
+ void (*gtk_reserved3)(void);
+ void (*gtk_reserved4)(void);
+};
+
+struct _ParasitePythonShellClass
+{
+ GtkVBoxClass parent_class;
+
+ void (*gtk_reserved1)(void);
+ void (*gtk_reserved2)(void);
+ void (*gtk_reserved3)(void);
+ void (*gtk_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+GType parasite_python_shell_get_type(void);
+
+GtkWidget *parasite_python_shell_new(void);
+void parasite_python_shell_append_text(ParasitePythonShell *python_shell,
+ const char *str,
+ const char *tag);
+void parasite_python_shell_focus(ParasitePythonShell *python_shell);
+
+G_END_DECLS
+
+#endif // _PARASITE_PYTHON_SHELL_H_
diff --git a/src/plugins/python/python_plugin.c b/src/plugins/python/python_plugin.c
@@ -0,0 +1,639 @@
+/* Python plugin for Claws-Mail
+ * Copyright (C) 2009 Holger Berndt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#include "claws-features.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <Python.h>
+
+#include "common/hooks.h"
+#include "common/plugin.h"
+#include "common/version.h"
+#include "common/utils.h"
+#include "gtk/menu.h"
+#include "main.h"
+#include "mainwindow.h"
+#include "prefs_toolbar.h"
+
+#include "python-shell.h"
+#include "python-hooks.h"
+#include "clawsmailmodule.h"
+
+#define PYTHON_SCRIPTS_BASE_DIR "python-scripts"
+#define PYTHON_SCRIPTS_MAIN_DIR "main"
+#define PYTHON_SCRIPTS_COMPOSE_DIR "compose"
+#define PYTHON_SCRIPTS_AUTO_DIR "auto"
+#define PYTHON_SCRIPTS_AUTO_STARTUP "startup"
+#define PYTHON_SCRIPTS_AUTO_SHUTDOWN "shutdown"
+#define PYTHON_SCRIPTS_AUTO_COMPOSE "compose_any"
+#define PYTHON_SCRIPTS_ACTION_PREFIX "Tools/PythonScripts/"
+
+static GSList *menu_id_list = NULL;
+static GSList *python_mainwin_scripts_id_list = NULL;
+static GSList *python_mainwin_scripts_names = NULL;
+static GSList *python_compose_scripts_names = NULL;
+
+static GtkWidget *python_console = NULL;
+
+static guint hook_compose_create;
+
+static gboolean python_console_delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ MainWindow *mainwin;
+ GtkToggleAction *action;
+
+ mainwin = mainwindow_get_mainwindow();
+ action = GTK_TOGGLE_ACTION(gtk_action_group_get_action(mainwin->action_group, "Tools/ShowPythonConsole"));
+ gtk_toggle_action_set_active(action, FALSE);
+ return TRUE;
+}
+
+static void setup_python_console(void)
+{
+ GtkWidget *vbox;
+ GtkWidget *console;
+
+ python_console = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_size_request(python_console, 600, 400);
+
+ vbox = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(python_console), vbox);
+
+ console = parasite_python_shell_new();
+ gtk_box_pack_start(GTK_BOX(vbox), console, TRUE, TRUE, 0);
+
+ g_signal_connect(python_console, "delete-event", G_CALLBACK(python_console_delete_event), NULL);
+
+ gtk_widget_show_all(python_console);
+
+ parasite_python_shell_focus(PARASITE_PYTHON_SHELL(console));
+}
+
+static void show_hide_python_console(GtkToggleAction *action, gpointer callback_data)
+{
+ if(gtk_toggle_action_get_active(action)) {
+ if(!python_console)
+ setup_python_console();
+ gtk_widget_show(python_console);
+ }
+ else {
+ gtk_widget_hide(python_console);
+ }
+}
+
+static void remove_python_scripts_menus(void)
+{
+ GSList *walk;
+ MainWindow *mainwin;
+
+ mainwin = mainwindow_get_mainwindow();
+
+ /* toolbar */
+ for(walk = python_mainwin_scripts_names; walk; walk = walk->next)
+ prefs_toolbar_unregister_plugin_item(TOOLBAR_MAIN, "Python", walk->data);
+
+ /* ui */
+ for(walk = python_mainwin_scripts_id_list; walk; walk = walk->next)
+ gtk_ui_manager_remove_ui(mainwin->ui_manager, GPOINTER_TO_UINT(walk->data));
+ g_slist_free(python_mainwin_scripts_id_list);
+ python_mainwin_scripts_id_list = NULL;
+
+ /* actions */
+ for(walk = python_mainwin_scripts_names; walk; walk = walk->next) {
+ GtkAction *action;
+ gchar *entry;
+ entry = g_strconcat(PYTHON_SCRIPTS_ACTION_PREFIX, walk->data, NULL);
+ action = gtk_action_group_get_action(mainwin->action_group, entry);
+ g_free(entry);
+ if(action)
+ gtk_action_group_remove_action(mainwin->action_group, action);
+ g_free(walk->data);
+ }
+ g_slist_free(python_mainwin_scripts_names);
+ python_mainwin_scripts_names = NULL;
+
+ /* compose scripts */
+ for(walk = python_compose_scripts_names; walk; walk = walk->next) {
+ prefs_toolbar_unregister_plugin_item(TOOLBAR_COMPOSE, "Python", walk->data);
+ g_free(walk->data);
+ }
+ g_slist_free(python_compose_scripts_names);
+ python_compose_scripts_names = NULL;
+}
+
+static gchar* extract_filename(const gchar *str)
+{
+ gchar *filename;
+
+ filename = g_strrstr(str, "/");
+ if(!filename || *(filename+1) == '\0') {
+ debug_print("Error: Could not extract filename from %s\n", str);
+ return NULL;
+ }
+ filename++;
+ return filename;
+}
+
+static void run_script_file(const gchar *filename, Compose *compose)
+{
+ FILE *fp;
+ fp = fopen(filename, "r");
+ if(!fp) {
+ g_print("Error: Could not open file '%s'\n", filename);
+ return;
+ }
+ put_composewindow_into_module(compose);
+ PyRun_SimpleFile(fp, filename);
+ fclose(fp);
+}
+
+static void run_auto_script_file_if_it_exists(const gchar *autofilename, Compose *compose)
+{
+ gchar *auto_filepath;
+
+ /* execute auto/autofilename, if it exists */
+ auto_filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
+ PYTHON_SCRIPTS_BASE_DIR, G_DIR_SEPARATOR_S,
+ PYTHON_SCRIPTS_AUTO_DIR, G_DIR_SEPARATOR_S, autofilename, NULL);
+ if(file_exist(auto_filepath, FALSE))
+ run_script_file(auto_filepath, compose);
+ g_free(auto_filepath);
+}
+
+static void python_mainwin_script_callback(GtkAction *action, gpointer data)
+{
+ char *filename;
+
+ filename = extract_filename(data);
+ if(!filename)
+ return;
+ filename = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, PYTHON_SCRIPTS_BASE_DIR, G_DIR_SEPARATOR_S, PYTHON_SCRIPTS_MAIN_DIR, G_DIR_SEPARATOR_S, filename, NULL);
+ run_script_file(filename, NULL);
+ g_free(filename);
+}
+
+typedef struct _ComposeActionData ComposeActionData;
+struct _ComposeActionData {
+ gchar *name;
+ Compose *compose;
+};
+
+static void python_compose_script_callback(GtkAction *action, gpointer data)
+{
+ char *filename;
+ ComposeActionData *dat = (ComposeActionData*)data;
+
+ filename = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, PYTHON_SCRIPTS_BASE_DIR, G_DIR_SEPARATOR_S, PYTHON_SCRIPTS_COMPOSE_DIR, G_DIR_SEPARATOR_S, dat->name, NULL);
+ run_script_file(filename, dat->compose);
+
+ g_free(filename);
+}
+
+static void mainwin_toolbar_callback(gpointer parent, const gchar *item_name, gpointer data)
+{
+ gchar *script;
+ script = g_strconcat(PYTHON_SCRIPTS_ACTION_PREFIX, item_name, NULL);
+ python_mainwin_script_callback(NULL, script);
+ g_free(script);
+}
+
+static void compose_toolbar_callback(gpointer parent, const gchar *item_name, gpointer data)
+{
+ gchar *filename;
+
+ filename = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
+ PYTHON_SCRIPTS_BASE_DIR, G_DIR_SEPARATOR_S,
+ PYTHON_SCRIPTS_COMPOSE_DIR, G_DIR_SEPARATOR_S,
+ item_name, NULL);
+ run_script_file(filename, (Compose*)parent);
+ g_free(filename);
+}
+
+static void make_sure_script_directory_exists(const gchar *subdir)
+{
+ char *dir;
+ dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, PYTHON_SCRIPTS_BASE_DIR, G_DIR_SEPARATOR_S, subdir, NULL);
+ if(!g_file_test(dir, G_FILE_TEST_IS_DIR)) {
+ if(g_mkdir(dir, 0777) != 0)
+ debug_print("Python plugin: Could not create directory '%s'\n", dir);
+ }
+ g_free(dir);
+}
+
+static void make_sure_directories_exist(void)
+{
+ make_sure_script_directory_exists("");
+ make_sure_script_directory_exists(PYTHON_SCRIPTS_MAIN_DIR);
+ make_sure_script_directory_exists(PYTHON_SCRIPTS_COMPOSE_DIR);
+ make_sure_script_directory_exists(PYTHON_SCRIPTS_AUTO_DIR);
+}
+
+static void migrate_scripts_out_of_base_dir(void)
+{
+ char *base_dir;
+ GDir *dir;
+ const char *filename;
+ gchar *dest_dir;
+
+ base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, PYTHON_SCRIPTS_BASE_DIR, NULL);
+ dir = g_dir_open(base_dir, 0, NULL);
+ g_free(base_dir);
+ if(!dir)
+ return;
+
+ dest_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
+ PYTHON_SCRIPTS_BASE_DIR, G_DIR_SEPARATOR_S,
+ PYTHON_SCRIPTS_MAIN_DIR, NULL);
+ if(!g_file_test(dest_dir, G_FILE_TEST_IS_DIR)) {
+ if(g_mkdir(dest_dir, 0777) != 0) {
+ g_free(dest_dir);
+ g_dir_close(dir);
+ return;
+ }
+ }
+
+ while((filename = g_dir_read_name(dir)) != NULL) {
+ gchar *filepath;
+ filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, PYTHON_SCRIPTS_BASE_DIR, G_DIR_SEPARATOR_S, filename, NULL);
+ if(g_file_test(filepath, G_FILE_TEST_IS_REGULAR)) {
+ gchar *dest_file;
+ dest_file = g_strconcat(dest_dir, G_DIR_SEPARATOR_S, filename, NULL);
+ if(move_file(filepath, dest_file, FALSE) == 0)
+ g_print("Python plugin: Moved file '%s' to %s subdir\n", filename, PYTHON_SCRIPTS_MAIN_DIR);
+ else
+ g_print("Python plugin: Warning: Could not move file '%s' to %s subdir\n", filename, PYTHON_SCRIPTS_MAIN_DIR);
+ g_free(dest_file);
+ }
+ g_free(filepath);
+ }
+ g_dir_close(dir);
+ g_free(dest_dir);
+}
+
+
+static void create_mainwindow_menus_and_items(GSList *filenames, gint num_entries)
+{
+ MainWindow *mainwin;
+ gint ii;
+ GSList *walk;
+ GtkActionEntry *entries;
+
+ /* create menu items */
+ entries = g_new0(GtkActionEntry, num_entries);
+ ii = 0;
+ mainwin = mainwindow_get_mainwindow();
+ for(walk = filenames; walk; walk = walk->next) {
+ entries[ii].name = g_strconcat(PYTHON_SCRIPTS_ACTION_PREFIX, walk->data, NULL);
+ entries[ii].label = walk->data;
+ entries[ii].callback = G_CALLBACK(python_mainwin_script_callback);
+ gtk_action_group_add_actions(mainwin->action_group, &(entries[ii]), 1, (gpointer)entries[ii].name);
+ ii++;
+ }
+ for(ii = 0; ii < num_entries; ii++) {
+ guint id;
+
+ python_mainwin_scripts_names = g_slist_prepend(python_mainwin_scripts_names, g_strdup(entries[ii].label));
+ MENUITEM_ADDUI_ID_MANAGER(mainwin->ui_manager, "/Menu/" PYTHON_SCRIPTS_ACTION_PREFIX, entries[ii].label,
+ entries[ii].name, GTK_UI_MANAGER_MENUITEM, id)
+ python_mainwin_scripts_id_list = g_slist_prepend(python_mainwin_scripts_id_list, GUINT_TO_POINTER(id));
+
+ prefs_toolbar_register_plugin_item(TOOLBAR_MAIN, "Python", entries[ii].label, mainwin_toolbar_callback, NULL);
+ }
+
+ g_free(entries);
+}
+
+
+/* this function doesn't really create menu items, but prepares a list that can be used
+ * in the compose create hook. It does however register the scripts for the toolbar editor */
+static void create_compose_menus_and_items(GSList *filenames)
+{
+ GSList *walk;
+ for(walk = filenames; walk; walk = walk->next) {
+ python_compose_scripts_names = g_slist_prepend(python_compose_scripts_names, g_strdup((gchar*)walk->data));
+ prefs_toolbar_register_plugin_item(TOOLBAR_COMPOSE, "Python", (gchar*)walk->data, compose_toolbar_callback, NULL);
+ }
+}
+
+static GtkActionEntry compose_tools_python_actions[] = {
+ {"Tools/PythonScripts", NULL, N_("Python scripts") },
+};
+
+static void ComposeActionData_destroy_cb(gpointer data)
+{
+ ComposeActionData *dat = (ComposeActionData*)data;
+ g_free(dat->name);
+ g_free(dat);
+}
+
+static gboolean my_compose_create_hook(gpointer cw, gpointer data)
+{
+ gint ii;
+ GSList *walk;
+ GtkActionEntry *entries;
+ GtkActionGroup *action_group;
+ Compose *compose = (Compose*)cw;
+ guint num_entries = g_slist_length(python_compose_scripts_names);
+
+ action_group = gtk_action_group_new("PythonPlugin");
+ gtk_action_group_add_actions(action_group, compose_tools_python_actions, 1, NULL);
+ entries = g_new0(GtkActionEntry, num_entries);
+ ii = 0;
+ for(walk = python_compose_scripts_names; walk; walk = walk->next) {
+ ComposeActionData *dat;
+
+ entries[ii].name = walk->data;
+ entries[ii].label = walk->data;
+ entries[ii].callback = G_CALLBACK(python_compose_script_callback);
+
+ dat = g_new0(ComposeActionData, 1);
+ dat->name = g_strdup(walk->data);
+ dat->compose = compose;
+
+ gtk_action_group_add_actions_full(action_group, &(entries[ii]), 1, dat, ComposeActionData_destroy_cb);
+ ii++;
+ }
+ gtk_ui_manager_insert_action_group(compose->ui_manager, action_group, 0);
+
+ MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "PythonScripts",
+ "Tools/PythonScripts", GTK_UI_MANAGER_MENU)
+
+ for(ii = 0; ii < num_entries; ii++) {
+ MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/" PYTHON_SCRIPTS_ACTION_PREFIX, entries[ii].label,
+ entries[ii].name, GTK_UI_MANAGER_MENUITEM)
+ }
+
+ g_free(entries);
+
+ run_auto_script_file_if_it_exists(PYTHON_SCRIPTS_AUTO_COMPOSE, compose);
+
+ return FALSE;
+}
+
+
+static void refresh_scripts_in_dir(const gchar *subdir, ToolbarType toolbar_type)
+{
+ char *scripts_dir;
+ GDir *dir;
+ GError *error = NULL;
+ const char *filename;
+ GSList *filenames = NULL;
+ GSList *walk;
+ gint num_entries;
+
+ scripts_dir = g_strconcat(get_rc_dir(),
+ G_DIR_SEPARATOR_S, PYTHON_SCRIPTS_BASE_DIR,
+ G_DIR_SEPARATOR_S, subdir,
+ NULL);
+ debug_print("Refreshing: %s\n", scripts_dir);
+
+ dir = g_dir_open(scripts_dir, 0, &error);
+ g_free(scripts_dir);
+
+ if(!dir) {
+ g_print("Could not open directory '%s': %s\n", subdir, error->message);
+ g_error_free(error);
+ return;
+ }
+
+ /* get filenames */
+ num_entries = 0;
+ while((filename = g_dir_read_name(dir)) != NULL) {
+ char *fn;
+
+ fn = g_strdup(filename);
+ filenames = g_slist_prepend(filenames, fn);
+ num_entries++;
+ }
+ g_dir_close(dir);
+
+ if(toolbar_type == TOOLBAR_MAIN)
+ create_mainwindow_menus_and_items(filenames, num_entries);
+ else if(toolbar_type == TOOLBAR_COMPOSE)
+ create_compose_menus_and_items(filenames);
+
+ /* cleanup */
+ for(walk = filenames; walk; walk = walk->next)
+ g_free(walk->data);
+ g_slist_free(filenames);
+}
+
+static void browse_python_scripts_dir(GtkAction *action, gpointer data)
+{
+ gchar *uri;
+ GdkAppLaunchContext *launch_context;
+ GError *error = NULL;
+ MainWindow *mainwin;
+
+ mainwin = mainwindow_get_mainwindow();
+ if(!mainwin) {
+ debug_print("Browse Python scripts: Problems getting the mainwindow\n");
+ return;
+ }
+ launch_context = gdk_app_launch_context_new();
+ gdk_app_launch_context_set_screen(launch_context, gtk_widget_get_screen(mainwin->window));
+ uri = g_strconcat("file://", get_rc_dir(), G_DIR_SEPARATOR_S, PYTHON_SCRIPTS_BASE_DIR, G_DIR_SEPARATOR_S, NULL);
+ g_app_info_launch_default_for_uri(uri, launch_context, &error);
+
+ if(error) {
+ debug_print("Could not open scripts dir browser: '%s'\n", error->message);
+ g_error_free(error);
+ }
+
+ g_object_unref(launch_context);
+ g_free(uri);
+}
+
+static void refresh_python_scripts_menus(GtkAction *action, gpointer data)
+{
+ remove_python_scripts_menus();
+
+ migrate_scripts_out_of_base_dir();
+
+ refresh_scripts_in_dir(PYTHON_SCRIPTS_MAIN_DIR, TOOLBAR_MAIN);
+ refresh_scripts_in_dir(PYTHON_SCRIPTS_COMPOSE_DIR, TOOLBAR_COMPOSE);
+}
+
+static GtkToggleActionEntry mainwindow_tools_python_toggle[] = {
+ {"Tools/ShowPythonConsole", NULL, N_("Show Python console..."),
+ NULL, NULL, G_CALLBACK(show_hide_python_console), FALSE},
+};
+
+static GtkActionEntry mainwindow_tools_python_actions[] = {
+ {"Tools/PythonScripts", NULL, N_("Python scripts") },
+ {"Tools/PythonScripts/Refresh", NULL, N_("Refresh"),
+ NULL, NULL, G_CALLBACK(refresh_python_scripts_menus) },
+ {"Tools/PythonScripts/Browse", NULL, N_("Browse"),
+ NULL, NULL, G_CALLBACK(browse_python_scripts_dir) },
+ {"Tools/PythonScripts/---", NULL, "---" },
+};
+
+void python_menu_init(void)
+{
+ MainWindow *mainwin;
+ guint id;
+
+ mainwin = mainwindow_get_mainwindow();
+
+ gtk_action_group_add_toggle_actions(mainwin->action_group, mainwindow_tools_python_toggle, 1, mainwin);
+ gtk_action_group_add_actions(mainwin->action_group, mainwindow_tools_python_actions, 3, mainwin);
+
+ MENUITEM_ADDUI_ID_MANAGER(mainwin->ui_manager, "/Menu/Tools", "ShowPythonConsole",
+ "Tools/ShowPythonConsole", GTK_UI_MANAGER_MENUITEM, id)
+ menu_id_list = g_slist_prepend(menu_id_list, GUINT_TO_POINTER(id));
+
+ MENUITEM_ADDUI_ID_MANAGER(mainwin->ui_manager, "/Menu/Tools", "PythonScripts",
+ "Tools/PythonScripts", GTK_UI_MANAGER_MENU, id)
+ menu_id_list = g_slist_prepend(menu_id_list, GUINT_TO_POINTER(id));
+
+ MENUITEM_ADDUI_ID_MANAGER(mainwin->ui_manager, "/Menu/Tools/PythonScripts", "Refresh",
+ "Tools/PythonScripts/Refresh", GTK_UI_MANAGER_MENUITEM, id)
+ menu_id_list = g_slist_prepend(menu_id_list, GUINT_TO_POINTER(id));
+
+ MENUITEM_ADDUI_ID_MANAGER(mainwin->ui_manager, "/Menu/Tools/PythonScripts", "Browse",
+ "Tools/PythonScripts/Browse", GTK_UI_MANAGER_MENUITEM, id)
+ menu_id_list = g_slist_prepend(menu_id_list, GUINT_TO_POINTER(id));
+
+ MENUITEM_ADDUI_ID_MANAGER(mainwin->ui_manager, "/Menu/Tools/PythonScripts", "Separator1",
+ "Tools/PythonScripts/---", GTK_UI_MANAGER_SEPARATOR, id)
+ menu_id_list = g_slist_prepend(menu_id_list, GUINT_TO_POINTER(id));
+
+ refresh_python_scripts_menus(NULL, NULL);
+}
+
+void python_menu_done(void)
+{
+ MainWindow *mainwin;
+
+ mainwin = mainwindow_get_mainwindow();
+
+ if(mainwin && !claws_is_exiting()) {
+ GSList *walk;
+
+ remove_python_scripts_menus();
+
+ for(walk = menu_id_list; walk; walk = walk->next)
+ gtk_ui_manager_remove_ui(mainwin->ui_manager, GPOINTER_TO_UINT(walk->data));
+ MENUITEM_REMUI_MANAGER(mainwin->ui_manager, mainwin->action_group, "Tools/ShowPythonConsole", 0);
+ MENUITEM_REMUI_MANAGER(mainwin->ui_manager, mainwin->action_group, "Tools/PythonScripts", 0);
+ MENUITEM_REMUI_MANAGER(mainwin->ui_manager, mainwin->action_group, "Tools/PythonScripts/Refresh", 0);
+ MENUITEM_REMUI_MANAGER(mainwin->ui_manager, mainwin->action_group, "Tools/PythonScripts/Browse", 0);
+ MENUITEM_REMUI_MANAGER(mainwin->ui_manager, mainwin->action_group, "Tools/PythonScripts/---", 0);
+ }
+}
+
+gint plugin_init(gchar **error)
+{
+ /* Version check */
+ if(!check_plugin_version(MAKE_NUMERIC_VERSION(3,7,6,9),
+ VERSION_NUMERIC, _("Python"), error))
+ return -1;
+
+ /* load hooks */
+ hook_compose_create = hooks_register_hook(COMPOSE_CREATED_HOOKLIST, my_compose_create_hook, NULL);
+ if(hook_compose_create == (guint)-1) {
+ *error = g_strdup(_("Failed to register \"compose create hook\" in the Python plugin"));
+ return -1;
+ }
+
+ /* script directories */
+ make_sure_directories_exist();
+
+ /* initialize python interpreter */
+ Py_Initialize();
+
+ /* initialize python interactive shell */
+ parasite_python_init();
+
+ /* initialize Claws Mail Python module */
+ claws_mail_python_init();
+
+ /* load menu options */
+ python_menu_init();
+
+ run_auto_script_file_if_it_exists(PYTHON_SCRIPTS_AUTO_STARTUP, NULL);
+
+ debug_print("Python plugin loaded\n");
+
+ return 0;
+}
+
+gboolean plugin_done(void)
+{
+ hooks_unregister_hook(COMPOSE_CREATED_HOOKLIST, hook_compose_create);
+
+ run_auto_script_file_if_it_exists(PYTHON_SCRIPTS_AUTO_SHUTDOWN, NULL);
+
+ python_menu_done();
+
+ if(python_console) {
+ gtk_widget_destroy(python_console);
+ python_console = NULL;
+ }
+
+ /* finialize python interpreter */
+ Py_Finalize();
+
+ debug_print("Python plugin done and unloaded.\n");
+ return FALSE;
+}
+
+const gchar *plugin_name(void)
+{
+ return _("Python");
+}
+
+const gchar *plugin_desc(void)
+{
+ return _("This plugin provides Python integration features.\n"
+ "\nFor the most up-to-date API documentation, type\n"
+ "\n help(clawsmail)\n"
+ "\nin the interactive Python console under Tools -> Show Python console.\n"
+ "\nThe source distribution of this plugin comes with various example scripts "
+ "in the \"examples\" subdirectory. If you wrote a script that you would be "
+ "interested in sharing, feel free to send it to me to have it considered "
+ "for inclusion in the examples.\n"
+ "\nFeedback to <berndth@gmx.de> is welcome.");
+}
+
+const gchar *plugin_type(void)
+{
+ return "GTK2";
+}
+
+const gchar *plugin_licence(void)
+{
+ return "GPL3+";
+}
+
+const gchar *plugin_version(void)
+{
+ return VERSION;
+}
+
+struct PluginFeature *plugin_provides(void)
+{
+ static struct PluginFeature features[] =
+ { {PLUGIN_UTILITY, N_("Python integration")},
+ {PLUGIN_NOTHING, NULL}};
+ return features;
+}
diff --git a/src/plugins/vcalendar/Makefile.in b/src/plugins/vcalendar/Makefile.in
@@ -215,6 +215,8 @@ ENCHANT_LIBS = @ENCHANT_LIBS@
EXEEXT = @EXEEXT@
EXTRA_VERSION = @EXTRA_VERSION@
FGREP = @FGREP@
+GDATA_CFLAGS = @GDATA_CFLAGS@
+GDATA_LIBS = @GDATA_LIBS@
GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
GLIB_CFLAGS = @GLIB_CFLAGS@
@@ -300,11 +302,27 @@ PACKAGE_VERSION = @PACKAGE_VERSION@
PASSCRYPT_KEY = @PASSCRYPT_KEY@
PATH_SEPARATOR = @PATH_SEPARATOR@
PERL = @PERL@
+PERL_CFLAGS = @PERL_CFLAGS@
+PERL_LDFLAGS = @PERL_LDFLAGS@
+PERL_PATH = @PERL_PATH@
PKG_CONFIG = @PKG_CONFIG@
PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_LIBS = @POPPLER_LIBS@
POSUB = @POSUB@
PTHREAD_LIBS = @PTHREAD_LIBS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYTHON = @PYTHON@
+PYTHON_CFLAGS = @PYTHON_CFLAGS@
+PYTHON_CONFIG = @PYTHON_CONFIG@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_LIBS = @PYTHON_LIBS@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_SHARED_LIB = @PYTHON_SHARED_LIB@
+PYTHON_VERSION = @PYTHON_VERSION@
RANLIB = @RANLIB@
RC = @RC@
SED = @SED@
@@ -370,10 +388,15 @@ manualdir = @manualdir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
sbindir = @sbindir@
+sedpath = @sedpath@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@