2013-02-17 [colin] 3.9.0cvs75
authorColin Leroy <colin@colino.net>
Sun, 17 Feb 2013 21:22:03 +0000 (21:22 +0000)
committerColin Leroy <colin@colino.net>
Sun, 17 Feb 2013 21:22:03 +0000 (21:22 +0000)
* 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.

134 files changed:
ChangeLog
PATCHSETS
configure.ac
src/plugins/Makefile.am
src/plugins/archive/Makefile.am
src/plugins/clamd/Makefile.am [new file with mode: 0644]
src/plugins/clamd/clamav_plugin.c [new file with mode: 0644]
src/plugins/clamd/clamav_plugin.h [new file with mode: 0644]
src/plugins/clamd/clamav_plugin_gtk.c [new file with mode: 0644]
src/plugins/clamd/libclamd/Makefile.am [new file with mode: 0644]
src/plugins/clamd/libclamd/clamd-plugin.c [new file with mode: 0644]
src/plugins/clamd/libclamd/clamd-plugin.h [new file with mode: 0644]
src/plugins/clamd/placeholder.txt [deleted file]
src/plugins/fetchinfo/Makefile.am [new file with mode: 0644]
src/plugins/fetchinfo/fetchinfo_plugin.c [new file with mode: 0644]
src/plugins/fetchinfo/fetchinfo_plugin.h [new file with mode: 0644]
src/plugins/fetchinfo/fetchinfo_plugin_gtk.c [new file with mode: 0644]
src/plugins/fetchinfo/placeholder.txt [deleted file]
src/plugins/gdata/Makefile.am [new file with mode: 0644]
src/plugins/gdata/cm_gdata_contacts.c [new file with mode: 0644]
src/plugins/gdata/cm_gdata_contacts.h [new file with mode: 0644]
src/plugins/gdata/cm_gdata_prefs.c [new file with mode: 0644]
src/plugins/gdata/cm_gdata_prefs.h [new file with mode: 0644]
src/plugins/gdata/gdata_plugin.c [new file with mode: 0644]
src/plugins/gdata/gdata_plugin.h [new file with mode: 0644]
src/plugins/gdata/placeholder.txt [deleted file]
src/plugins/geolocation/placeholder.txt [deleted file]
src/plugins/gtkhtml2_viewer/placeholder.txt [deleted file]
src/plugins/mailmbox/Makefile.am [new file with mode: 0644]
src/plugins/mailmbox/carray.c [new file with mode: 0644]
src/plugins/mailmbox/carray.h [new file with mode: 0644]
src/plugins/mailmbox/chash.c [new file with mode: 0644]
src/plugins/mailmbox/chash.h [new file with mode: 0644]
src/plugins/mailmbox/clist.c [new file with mode: 0644]
src/plugins/mailmbox/clist.h [new file with mode: 0644]
src/plugins/mailmbox/mailimf.c [new file with mode: 0644]
src/plugins/mailmbox/mailimf.h [new file with mode: 0644]
src/plugins/mailmbox/mailimf_types.c [new file with mode: 0644]
src/plugins/mailmbox/mailimf_types.h [new file with mode: 0644]
src/plugins/mailmbox/mailimf_types_helper.c [new file with mode: 0644]
src/plugins/mailmbox/mailimf_types_helper.h [new file with mode: 0644]
src/plugins/mailmbox/mailimf_write.c [new file with mode: 0644]
src/plugins/mailmbox/mailimf_write.h [new file with mode: 0644]
src/plugins/mailmbox/maillock.c [new file with mode: 0644]
src/plugins/mailmbox/maillock.h [new file with mode: 0644]
src/plugins/mailmbox/mailmbox.c [new file with mode: 0644]
src/plugins/mailmbox/mailmbox.h [new file with mode: 0644]
src/plugins/mailmbox/mailmbox_folder.c [new file with mode: 0644]
src/plugins/mailmbox/mailmbox_folder.h [new file with mode: 0644]
src/plugins/mailmbox/mailmbox_gtk.deps [new file with mode: 0644]
src/plugins/mailmbox/mailmbox_parse.c [new file with mode: 0644]
src/plugins/mailmbox/mailmbox_parse.h [new file with mode: 0644]
src/plugins/mailmbox/mailmbox_types.c [new file with mode: 0644]
src/plugins/mailmbox/mailmbox_types.h [new file with mode: 0644]
src/plugins/mailmbox/mmapstring.c [new file with mode: 0644]
src/plugins/mailmbox/mmapstring.h [new file with mode: 0644]
src/plugins/mailmbox/placeholder.txt [deleted file]
src/plugins/mailmbox/plugin.c [new file with mode: 0644]
src/plugins/mailmbox/plugin_gtk.c [new file with mode: 0644]
src/plugins/mailmbox/plugin_gtk.h [new file with mode: 0644]
src/plugins/newmail/Makefile.am [new file with mode: 0644]
src/plugins/newmail/newmail.c [new file with mode: 0644]
src/plugins/newmail/placeholder.txt [deleted file]
src/plugins/notification/Makefile.am [new file with mode: 0644]
src/plugins/notification/claws.def [new file with mode: 0644]
src/plugins/notification/notification_banner.c [new file with mode: 0644]
src/plugins/notification/notification_banner.h [new file with mode: 0644]
src/plugins/notification/notification_command.c [new file with mode: 0644]
src/plugins/notification/notification_command.h [new file with mode: 0644]
src/plugins/notification/notification_core.c [new file with mode: 0644]
src/plugins/notification/notification_core.h [new file with mode: 0644]
src/plugins/notification/notification_foldercheck.c [new file with mode: 0644]
src/plugins/notification/notification_foldercheck.h [new file with mode: 0644]
src/plugins/notification/notification_hotkeys.c [new file with mode: 0644]
src/plugins/notification/notification_hotkeys.h [new file with mode: 0644]
src/plugins/notification/notification_indicator.c [new file with mode: 0644]
src/plugins/notification/notification_indicator.h [new file with mode: 0644]
src/plugins/notification/notification_lcdproc.c [new file with mode: 0644]
src/plugins/notification/notification_lcdproc.h [new file with mode: 0644]
src/plugins/notification/notification_pixbuf.c [new file with mode: 0644]
src/plugins/notification/notification_pixbuf.h [new file with mode: 0644]
src/plugins/notification/notification_plugin.c [new file with mode: 0644]
src/plugins/notification/notification_plugin.h [new file with mode: 0644]
src/plugins/notification/notification_popup.c [new file with mode: 0644]
src/plugins/notification/notification_popup.h [new file with mode: 0644]
src/plugins/notification/notification_prefs.c [new file with mode: 0644]
src/plugins/notification/notification_prefs.h [new file with mode: 0644]
src/plugins/notification/notification_trayicon.c [new file with mode: 0644]
src/plugins/notification/notification_trayicon.h [new file with mode: 0644]
src/plugins/notification/placeholder.txt [deleted file]
src/plugins/notification/plugin.def [new file with mode: 0644]
src/plugins/notification/raw_claws_mail_logo_64x64.h [new file with mode: 0644]
src/plugins/notification/version.rc [new file with mode: 0644]
src/plugins/pdf_viewer/Makefile.am [new file with mode: 0644]
src/plugins/pdf_viewer/doc_index.xpm [new file with mode: 0644]
src/plugins/pdf_viewer/doc_index_close.xpm [new file with mode: 0644]
src/plugins/pdf_viewer/doc_info.xpm [new file with mode: 0644]
src/plugins/pdf_viewer/first_arrow.xpm [new file with mode: 0644]
src/plugins/pdf_viewer/last_arrow.xpm [new file with mode: 0644]
src/plugins/pdf_viewer/left_arrow.xpm [new file with mode: 0644]
src/plugins/pdf_viewer/placeholder.txt [deleted file]
src/plugins/pdf_viewer/poppler_viewer.c [new file with mode: 0644]
src/plugins/pdf_viewer/poppler_viewer.h [new file with mode: 0644]
src/plugins/pdf_viewer/right_arrow.xpm [new file with mode: 0644]
src/plugins/pdf_viewer/rotate_left.xpm [new file with mode: 0644]
src/plugins/pdf_viewer/rotate_right.xpm [new file with mode: 0644]
src/plugins/pdf_viewer/zoom_fit.xpm [new file with mode: 0644]
src/plugins/pdf_viewer/zoom_in.xpm [new file with mode: 0644]
src/plugins/pdf_viewer/zoom_out.xpm [new file with mode: 0644]
src/plugins/pdf_viewer/zoom_width.xpm [new file with mode: 0644]
src/plugins/perl/Makefile.am [new file with mode: 0644]
src/plugins/perl/perl_gtk.c [new file with mode: 0644]
src/plugins/perl/perl_gtk.h [new file with mode: 0644]
src/plugins/perl/perl_plugin.c [new file with mode: 0644]
src/plugins/perl/perl_plugin.h [new file with mode: 0644]
src/plugins/perl/placeholder.txt [deleted file]
src/plugins/python/Makefile.am [new file with mode: 0644]
src/plugins/python/clawsmailmodule.c [new file with mode: 0644]
src/plugins/python/clawsmailmodule.h [new file with mode: 0644]
src/plugins/python/composewindowtype.c [new file with mode: 0644]
src/plugins/python/composewindowtype.h [new file with mode: 0644]
src/plugins/python/foldertype.c [new file with mode: 0644]
src/plugins/python/foldertype.h [new file with mode: 0644]
src/plugins/python/messageinfotype.c [new file with mode: 0644]
src/plugins/python/messageinfotype.h [new file with mode: 0644]
src/plugins/python/nodetype.c [new file with mode: 0644]
src/plugins/python/nodetype.h [new file with mode: 0644]
src/plugins/python/placeholder.txt [deleted file]
src/plugins/python/python-hooks.c [new file with mode: 0644]
src/plugins/python/python-hooks.h [new file with mode: 0644]
src/plugins/python/python-shell.c [new file with mode: 0644]
src/plugins/python/python-shell.h [new file with mode: 0644]
src/plugins/python/python_plugin.c [new file with mode: 0644]
src/plugins/vcalendar/Makefile.in

index 490aabf..52cddaf 100644 (file)
--- 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
index 0411f24..cedcf98 100644 (file)
--- a/PATCHSETS
+++ b/PATCHSETS
 ( 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
index 26c6db8..cff9df3 100644 (file)
@@ -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
index 12c1e70..73850f2 100644 (file)
@@ -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) \
index 5e1d118..1bf7007 100644 (file)
@@ -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
new file mode 100644 (file)
index 0000000..f2fccb2
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..03a8017
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..e6530af
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..f01555a
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..38e8de8
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..63f8d1e
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..1dfc004
--- /dev/null
@@ -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
deleted file mode 100644 (file)
index 3b94f91..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Placeholder
diff --git a/src/plugins/fetchinfo/Makefile.am b/src/plugins/fetchinfo/Makefile.am
new file mode 100644 (file)
index 0000000..fde86c2
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..d3ef5ec
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..c5ad7b8
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..80f4a6d
--- /dev/null
@@ -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
deleted file mode 100644 (file)
index 3b94f91..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Placeholder
diff --git a/src/plugins/gdata/Makefile.am b/src/plugins/gdata/Makefile.am
new file mode 100644 (file)
index 0000000..abc62ee
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..fc48918
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..1be697c
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..5098a5b
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..533bf00
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..5985f1d
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..eb9c363
--- /dev/null
@@ -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
deleted file mode 100644 (file)
index 3b94f91..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Placeholder
diff --git a/src/plugins/geolocation/placeholder.txt b/src/plugins/geolocation/placeholder.txt
deleted file mode 100644 (file)
index 3b94f91..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Placeholder
diff --git a/src/plugins/gtkhtml2_viewer/placeholder.txt b/src/plugins/gtkhtml2_viewer/placeholder.txt
deleted file mode 100644 (file)
index 3b94f91..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Placeholder
diff --git a/src/plugins/mailmbox/Makefile.am b/src/plugins/mailmbox/Makefile.am
new file mode 100644 (file)
index 0000000..234f602
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..a8e78c9
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..68858e3
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..2055221
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..3b2b7d3
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..e5c680d
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..bd97f59
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..03b1c19
--- /dev/null
@@ -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 {
+