add Clam AntiVirus plugin
authorPaul Mangan <paul@claws-mail.org>
Sat, 12 Apr 2003 17:52:24 +0000 (17:52 +0000)
committerPaul Mangan <paul@claws-mail.org>
Sat, 12 Apr 2003 17:52:24 +0000 (17:52 +0000)
ChangeLog.claws
configure.ac
src/plugins/Makefile.am
src/plugins/clamav/Makefile.am [new file with mode: 0644]
src/plugins/clamav/README [new file with mode: 0644]
src/plugins/clamav/clamav_plugin.c [new file with mode: 0644]
src/plugins/clamav/clamav_plugin.h [new file with mode: 0644]
src/plugins/clamav/clamav_plugin_gtk.c [new file with mode: 0644]

index eed3b2b..b648647 100644 (file)
@@ -1,3 +1,12 @@
+2003-04-12 [paul]      0.8.11claws86
+
+       * src/plugins/Makefile.am
+         src/plugins/clamav/Makefile.am                ** NEW FILE **
+         src/plugins/clamav/README                     ** NEW FILE ** 
+         src/plugins/clamav/clamav_plugin.[ch]         ** NEW FILES **
+         src/plugins/clamav/clamav_plugin_gtk.c        ** NEW FILE **
+               add Clam AntiVirus plugin
+
 2003-04-12 [paul]      0.8.11claws85
 
        * sync with 0.8.11cvs21
index 43f0176..4c0c359 100644 (file)
@@ -11,7 +11,7 @@ MINOR_VERSION=8
 MICRO_VERSION=11
 INTERFACE_AGE=0
 BINARY_AGE=0
-EXTRA_VERSION=claws85
+EXTRA_VERSION=claws86
 VERSION=$MAJOR_VERSION.$MINOR_VERSION.$MICRO_VERSION$EXTRA_VERSION
 
 dnl set $target
@@ -431,6 +431,20 @@ if test x"$ac_cv_enable_trayicon_plugin" = xyes; then
        PLUGINS="trayicon $PLUGINS"
 fi
 
+AC_ARG_ENABLE(clamav-plugin,
+       [  --enable-clamav-plugin    Build clamav plugin [default=no]],
+       [ac_cv_enable_clamav_plugin=$enableval], [ac_cv_enable_clamav_plugin=no])
+AM_CONDITIONAL(BUILD_CLAMAV_PLUGIN, test x"$ac_cv_enable_clamav_plugin" = xyes)
+if test x"$ac_cv_enable_clamav_plugin" = xyes; then
+       AC_CHECK_LIB(clamav, cl_scanfile, clamav_lib=-lclamav)
+       if test -z "${clamav_lib}"
+       then
+               AC_MSG_ERROR(clamav library not found)
+       fi
+       LIBS="${clamav_lib} ${LIBS}"
+       PLUGINS="clamav $PLUGINS"
+fi
+
 dnl ****************************
 dnl ** Final configure output **
 dnl ****************************
@@ -453,6 +467,7 @@ src/plugins/mathml_viewer/Makefile
 src/plugins/dillo_viewer/Makefile
 src/plugins/image_viewer/Makefile
 src/plugins/trayicon/Makefile
+src/plugins/clamav/Makefile
 faq/Makefile
 faq/de/Makefile
 faq/en/Makefile
index d489af7..04532ab 100644 (file)
@@ -22,9 +22,14 @@ if BUILD_TRAYICON_PLUGIN
 trayicon_dir = trayicon
 endif
 
+if BUILD_CLAMAV_PLUGIN
+clamav_dir = clamav
+endif
+
 SUBDIRS = $(demo_dir) \
        $(spamassasssin_dir) \
        $(mathml_viewer_dir) \
        $(dillo_viewer_dir) \
        $(image_viewer_dir) \
-       $(trayicon_dir)
+       $(trayicon_dir) \
+       $(clamav_dir)
diff --git a/src/plugins/clamav/Makefile.am b/src/plugins/clamav/Makefile.am
new file mode 100644 (file)
index 0000000..7e5f04e
--- /dev/null
@@ -0,0 +1,32 @@
+plugindir = $(pkglibdir)/plugins
+
+plugin_LTLIBRARIES = clamav_plugin.la clamav_plugin_gtk.la
+
+clamav_plugin_la_SOURCES = \
+       clamav_plugin.c clamav_plugin.h
+
+clamav_plugin_la_LDFLAGS = \
+       -avoid-version -module \
+       $(GTK_LIBS)
+
+clamav_plugin_gtk_la_SOURCES = \
+       clamav_plugin_gtk.c clamav_plugin.h
+
+clamav_plugin_gtk_la_LDFLAGS = \
+       -avoid-version -module \
+       $(GTK_LIBS)
+
+INCLUDES = \
+       -I../.. \
+       -I../../common \
+       -I../../gtk \
+       $(CLAMAV_CFLAGS) \
+       -I$(includedir)
+
+CPPFLAGS = \
+       $(ASPELL_CFLAGS) \
+       $(GLIB_CFLAGS) \
+       $(GTK_CFLAGS)
+
+EXTRA_DIST = \
+       README
diff --git a/src/plugins/clamav/README b/src/plugins/clamav/README
new file mode 100644 (file)
index 0000000..3c16833
--- /dev/null
@@ -0,0 +1,47 @@
+Clam AntiVirus Plugin
+---------------------
+
+This plugin will scan incoming messages for viruses using 
+Clam AntiVirus.
+
+To build the plugin run configure with --enable-clamav-plugin
+
+Messages that have infected attachments can be deleted or moved
+to a folder that can be specified in the configuration, if the
+folder is not specified or not found the default trash folder will
+be used.
+
+Please note that the clamav_plugin.so plugin only provides the
+actual scanning. By default it is not enabled. You probably also 
+want to load the clamav_plugin_gtk.so to get a configuration page
+under "Configuration/Other Preferences..." for the Clam AntiVirus
+plugin.
+
+The Clam AntiVirus plugin uses its own block in the sylpheedrc file
+in your ~/.sylpheed directory. If you really want to configure it
+without the GTK plugin you can specify the following options:
+
+[ClamAV]
+clamav_enable=1                                enable scanning with Clam AntiVirus
+                                       [default: 0]
+
+clamav_enable_archive=1                        enable archive scanning with Clam 
+                                       AntiVirus
+                                       [default: 0]
+
+clamav_max_size=1                      maximum size an attachment can have
+                                       (in MB), larger attachments will not
+                                       be scanned
+                                       [default: 1]
+
+clamav_receive_infected=1              enable to save infected messages to a 
+                                       mailfolder. If not enabled the mails will 
+                                       be deleted
+                                       [default: 1]
+clamav_save_folder=#mh/Mailbox/virus   the folder that should be used to save
+                                       infected mails, if empty the default 
+                                       trash folder is used
+                                       [default: none, use default trash]
+
+Clam AntiVirus is licensed under the GPL and can be downloaded from
+http://clamav.elektrapro.com/
diff --git a/src/plugins/clamav/clamav_plugin.c b/src/plugins/clamav/clamav_plugin.c
new file mode 100644 (file)
index 0000000..5b999aa
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2003 Hiroyuki Yamamoto and the Sylpheed-Claws 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 2 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"
+#endif
+
+#include "defs.h"
+
+#include <glib.h>
+
+#if HAVE_LOCALE_H
+#  include <locale.h>
+#endif
+
+#include <clamav.h>
+
+#include "common/plugin.h"
+#include "common/utils.h"
+#include "hooks.h"
+#include "inc.h"
+#include "mimeview.h"
+#include "folder.h"
+#include "prefs.h"
+#include "prefs_gtk.h"
+
+#include "clamav_plugin.h"
+
+static gint hook_id;
+
+static ClamAvConfig config;
+
+static PrefParam param[] = {
+       {"clamav_enable", "FALSE", &config.clamav_enable, P_BOOL,
+        NULL, NULL, NULL},
+       {"clamav_enable_archive", "FALSE", &config.clamav_archive_enable, P_BOOL,
+        NULL, NULL, NULL},
+       {"clamav_max_size", "1", &config.clamav_max_size, P_USHORT,
+        NULL, NULL, NULL},
+       {"clamav_receive_infected", "TRUE", &config.clamav_receive_infected, P_BOOL,
+        NULL, NULL, NULL},
+       {"clamav_save_folder", NULL, &config.clamav_save_folder, P_STRING,
+        NULL, NULL, NULL},
+
+       {NULL, NULL, NULL, P_OTHER, NULL, NULL, NULL}
+};
+
+#define IS_FIRST_PART_TEXT(info) \
+       ((info->mime_type == MIME_TEXT || info->mime_type == MIME_TEXT_HTML || \
+         info->mime_type == MIME_TEXT_ENRICHED) || \
+        (info->mime_type == MIME_MULTIPART && info->content_type && \
+         !strcasecmp(info->content_type, "multipart/alternative") && \
+         (info->children && \
+          (info->children->mime_type == MIME_TEXT || \
+           info->children->mime_type == MIME_TEXT_HTML || \
+           info->children->mime_type == MIME_TEXT_ENRICHED))))
+
+static gboolean mail_filtering_hook(gpointer source, gpointer data)
+{
+       MailFilteringData *mail_filtering_data = (MailFilteringData *) source;
+       MsgInfo *msginfo = mail_filtering_data->msginfo;
+       gboolean is_infected = FALSE;
+       FILE  *fp;
+       MimeInfo *mimeinfo;
+       MimeInfo *child;
+       gchar *infile;
+       gchar *outfile;
+
+       int ret, no;
+       unsigned long int size;
+       long double kb;
+       char *virname;
+       struct cl_node *root = NULL;
+       struct cl_limits limits;
+
+       if (!config.clamav_enable)
+               return FALSE;
+
+       debug_print("Scanning message %d for viruses\n", msginfo->msgnum);
+
+       fp = procmsg_open_message(msginfo);
+       if (fp == NULL) {
+               debug_print("failed to open message file");
+       } 
+       
+       mimeinfo = procmime_scan_message(msginfo);
+       if (!mimeinfo) return FALSE;
+
+       child = mimeinfo->children;
+       if (!child || IS_FIRST_PART_TEXT(mimeinfo)) {
+               procmime_mimeinfo_free_all(mimeinfo);
+               return FALSE;
+       }
+
+       if (IS_FIRST_PART_TEXT(child))
+               child = child->next;
+
+       infile = procmsg_get_message_file_path(msginfo);
+
+
+       if((ret = cl_loaddbdir(cl_retdbdir(), &root, &no))) {
+               debug_print("cl_loaddbdir: %s\n", cl_perror(ret));
+               exit(2);
+       }
+
+       debug_print("Loaded %d viruses.\n", no);
+
+       cl_buildtrie(root);
+
+       limits.maxfiles = 1000; /* max files */
+       limits.maxfilesize = config.clamav_max_size * 1048576; /* maximal archived file size == 10 Mb */
+       limits.maxreclevel = 8; /* maximal recursion level */
+
+       while (child != NULL) {
+               if (child->children || child->mime_type == MIME_MULTIPART) {
+                       child = procmime_mimeinfo_next(child);
+                       continue;
+               }
+               if(child->parent && child->parent->parent
+               && !strcasecmp(child->parent->parent->content_type, "multipart/signed")
+               && child->mime_type == MIME_TEXT) {
+                       /* this is the main text part of a signed message */
+                       child = procmime_mimeinfo_next(child);
+                       continue;
+               }
+               outfile = procmime_get_tmp_file_name(child);
+               if (procmime_get_part(outfile, infile, child) < 0)
+                       g_warning("Can't get the part of multipart message.");
+               else {
+                       debug_print("Scanning %s\n", outfile);
+
+                       if((ret = cl_scanfile(outfile, &virname, &size, root, 
+                                             &limits, CL_ARCHIVE)) == CL_VIRUS) {
+                               is_infected = TRUE;
+                               debug_print("Detected %s virus.\n", virname); 
+                       } else {
+                               debug_print("No virus detected.\n");
+                               if(ret != CL_CLEAN)
+                                       debug_print("Error: %s\n", cl_perror(ret));
+                       }
+
+                       kb = size * (CL_COUNT_PRECISION / 1024);
+                       debug_print("Data scanned: %2.2Lf Kb\n", kb);
+    
+                       if (is_infected) {
+                               debug_print("message part(s) infected with %s\n", virname);
+                               if (config.clamav_receive_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);
+                                       folder_item_move_msg(clamav_save_folder, msginfo);
+                               } else {
+                                       folder_item_remove_msg(msginfo->folder, msginfo->msgnum);
+                               }
+
+                               return TRUE;
+                       }
+                               child = child->next;
+               }
+       }
+       cl_freetrie(root);
+       g_free(infile);
+       procmime_mimeinfo_free_all(mimeinfo);
+
+       return FALSE;
+}
+
+#undef IS_FIRST_PART_TEXT
+
+ClamAvConfig *clamav_get_config()
+{
+       return &config;
+}
+
+void clamav_save_config()
+{
+       PrefFile *pfile;
+       gchar *rcpath;
+
+       debug_print("Saving ClamAV 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 ClamAV configuration to file\n");
+               prefs_file_close_revert(pfile);
+               return;
+       }
+       fprintf(pfile->fp, "\n");
+
+       prefs_file_close(pfile);
+}
+
+gint plugin_init(gchar **error)
+{
+       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);
+       prefs_read_config(param, "ClamAV", COMMON_RC);
+
+       debug_print("ClamAV plugin loaded\n");
+
+       return 0;
+       
+}
+
+void plugin_done()
+{
+       hooks_unregister_hook(MAIL_FILTERING_HOOKLIST, hook_id);
+       g_free(config.clamav_save_folder);
+       
+       debug_print("ClamAV plugin unloaded\n");
+}
+
+const gchar *plugin_name()
+{
+       return "Clam AntiVirus";
+}
+
+const gchar *plugin_desc()
+{
+       return "This plugin checks all message attachments that are received "
+              "from a POP account for viruses using Clam AntiVirus. You will "
+              "need to have ClamAv installed.\n"
+              "\n"
+              "When a message attachment is found to contain a virus it can be "
+              "deleted or saved in a special folder.\n"
+              "\n"
+              "This plugin only contains the actual function for filtering "
+              "and deleting or moving the message. You probably want to load "
+              "a User Interface plugin too, otherwise you will have to "
+              "manually write the plugin configuration.\n";
+}
+
+const gchar *plugin_type()
+{
+       return "Common";
+}
diff --git a/src/plugins/clamav/clamav_plugin.h b/src/plugins/clamav/clamav_plugin.h
new file mode 100644 (file)
index 0000000..07d1b49
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2003 Hiroyuki Yamamoto and the Sylpheed-Claws 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 2 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 CLAMAV_PLUGIN_H
+#define CLAMAV_PLUGIN_H 1
+
+#include <glib.h>
+
+typedef struct _ClamAvConfig ClamAvConfig;
+
+struct _ClamAvConfig
+{
+       gboolean         clamav_enable;
+       gboolean         clamav_archive_enable;
+       guint            clamav_max_size;
+       gboolean         clamav_receive_infected;
+       gchar           *clamav_save_folder;
+};
+
+ClamAvConfig *clamav_get_config();
+void         clamav_save_config();
+
+#endif
diff --git a/src/plugins/clamav/clamav_plugin_gtk.c b/src/plugins/clamav/clamav_plugin_gtk.c
new file mode 100644 (file)
index 0000000..bbd3d2f
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2003 Hiroyuki Yamamoto and the Sylpheed-Claws 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 2 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"
+#endif
+
+#include "defs.h"
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "intl.h"
+#include "common/plugin.h"
+#include "common/utils.h"
+#include "prefs.h"
+#include "folder.h"
+#include "prefs_gtk.h"
+#include "foldersel.h"
+#include "clamav_plugin.h"
+
+struct ClamAvPage
+{
+       PrefsPage page;
+       
+       GtkWidget *enable_clamav;
+       GtkWidget *enable_arc;
+       GtkWidget *max_size;
+       GtkWidget *recv_infected;
+       GtkWidget *save_folder;
+};
+
+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);
+       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 clamav_create_widget_func(PrefsPage * _page, GtkWindow *window, gpointer data)
+{
+       struct ClamAvPage *page = (struct ClamAvPage *) _page;
+       ClamAvConfig *config;
+        
+       GtkWidget *table;
+       GtkWidget *enable_clamav;
+       GtkWidget *label1;
+       GtkWidget *label2;
+       GtkWidget *label3;
+       GtkWidget *enable_arc;
+       GtkWidget *arc_scanning;
+       GtkWidget *label24;
+       GtkObject *max_size_adj;
+       GtkWidget *max_size;
+       GtkWidget *mb;
+       GtkWidget *label26;
+       GtkWidget *recv_infected;
+       GtkWidget *label27;
+       GtkWidget *save_folder;
+       GtkWidget *save_folder_select;
+
+       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);
+
+       enable_clamav = gtk_check_button_new_with_label ("");
+       gtk_widget_show (enable_clamav);
+       gtk_table_attach (GTK_TABLE (table), enable_clamav, 1, 2, 0, 1,
+                         (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+                         (GtkAttachOptions) (0), 0, 0);
+
+       label1 = gtk_label_new ("Enable ClamAV filtering");
+       gtk_widget_show (label1);
+       gtk_table_attach (GTK_TABLE (table), label1, 0, 1, 0, 1,
+                         (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+                         (GtkAttachOptions) (0), 2, 4);
+       gtk_label_set_justify(GTK_LABEL(label1), GTK_JUSTIFY_LEFT);
+       gtk_misc_set_alignment (GTK_MISC (label1), 0, 0.5);
+
+       enable_arc = gtk_check_button_new_with_label ("");
+       gtk_widget_show (enable_arc);
+       gtk_table_attach (GTK_TABLE (table), enable_arc, 1, 2, 1, 2,
+                         (GtkAttachOptions) (GTK_FILL),
+                         (GtkAttachOptions) (0), 0, 0);
+
+       arc_scanning = gtk_label_new ("Enable archive scanning");
+       gtk_widget_show (arc_scanning);
+       gtk_table_attach (GTK_TABLE (table), arc_scanning, 0, 1, 1, 2,
+                         (GtkAttachOptions) (GTK_FILL),
+                         (GtkAttachOptions) (0), 2, 4);
+       gtk_misc_set_alignment (GTK_MISC (arc_scanning), 0, 0.5);
+
+       label24 = gtk_label_new ("Maximum message size");
+       gtk_widget_show (label24);
+       gtk_table_attach (GTK_TABLE (table), label24, 0, 1, 4, 5,
+                         (GtkAttachOptions) (GTK_FILL),
+                         (GtkAttachOptions) (0), 2, 4);
+       gtk_misc_set_alignment (GTK_MISC (label24), 0, 0.5);
+
+       max_size_adj = gtk_adjustment_new (1, 0, 100, 1, 10, 10);
+       max_size = gtk_spin_button_new (GTK_ADJUSTMENT (max_size_adj), 1, 0);
+       gtk_widget_show (max_size);
+       gtk_table_attach (GTK_TABLE (table), max_size, 1, 2, 4, 5,
+                         (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+                         (GtkAttachOptions) (0), 0, 0);
+
+       mb = gtk_label_new ("MB");
+       gtk_widget_show (mb);
+       gtk_table_attach (GTK_TABLE (table), mb, 2, 3, 4, 5,
+                         (GtkAttachOptions) (GTK_FILL),
+                         (GtkAttachOptions) (0), 0, 0);
+       gtk_misc_set_alignment (GTK_MISC (mb), 0, 0.5);
+
+       label26 = gtk_label_new ("Receive infected email");
+       gtk_widget_show (label26);
+       gtk_table_attach (GTK_TABLE (table), label26, 0, 1, 5, 6,
+                         (GtkAttachOptions) (GTK_FILL),
+                         (GtkAttachOptions) (0), 2, 4);
+       gtk_misc_set_alignment (GTK_MISC (label26), 0, 0.5);
+
+       recv_infected = gtk_check_button_new_with_label ("");
+       gtk_widget_show (recv_infected);
+       gtk_table_attach (GTK_TABLE (table), recv_infected, 1, 2, 5, 6,
+                         (GtkAttachOptions) (GTK_FILL),
+                         (GtkAttachOptions) (0), 0, 0);
+
+       label27 = gtk_label_new ("Save folder");
+       gtk_widget_show (label27);
+       gtk_table_attach (GTK_TABLE (table), label27, 0, 1, 6, 7,
+                         (GtkAttachOptions) (GTK_FILL),
+                         (GtkAttachOptions) (0), 2, 4);
+       gtk_misc_set_alignment (GTK_MISC (label27), 0, 0.5);
+
+       save_folder = gtk_entry_new ();
+       gtk_widget_show (save_folder);
+       gtk_table_attach (GTK_TABLE (table), save_folder, 1, 2, 6, 7,
+                         (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+                         (GtkAttachOptions) (0), 0, 0);
+
+       save_folder_select = gtk_button_new_with_label (" ... ");
+       gtk_widget_show (save_folder_select);
+       gtk_table_attach (GTK_TABLE (table), save_folder_select, 2, 3, 6, 7,
+                         (GtkAttachOptions) (0),
+                         (GtkAttachOptions) (0), 0, 0);
+
+       config = clamav_get_config();
+
+       gtk_signal_connect(GTK_OBJECT(save_folder_select), "released", GTK_SIGNAL_FUNC(foldersel_cb), page);
+
+       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_archive_enable);
+       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_receive_infected);
+       if (config->clamav_save_folder != NULL)
+               gtk_entry_set_text(GTK_ENTRY(save_folder), config->clamav_save_folder);
+       
+       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->page.widget = table;
+}
+
+static void clamav_destroy_widget_func(PrefsPage *_page)
+{
+       debug_print("Destroying ClamAV widget\n");
+}
+
+static void clamav_save_func(PrefsPage *_page)
+{
+       struct ClamAvPage *page = (struct ClamAvPage *) _page;
+       ClamAvConfig *config;
+
+       debug_print("Saving ClamAV Page\n");
+
+       config = clamav_get_config();
+
+       config->clamav_enable = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->enable_clamav));
+       config->clamav_archive_enable = 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_receive_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);
+
+       clamav_save_config();
+}
+
+static struct ClamAvPage clamav_page;
+
+gint plugin_init(gchar **error)
+{
+       clamav_page.page.path = _("Filtering/Clam AntiVirus");
+       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;
+       
+       prefs_gtk_register_page((PrefsPage *) &clamav_page);
+
+       debug_print("ClamAV GTK plugin loaded\n");
+       return 0;       
+}
+
+void plugin_done()
+{
+       prefs_gtk_unregister_page((PrefsPage *) &clamav_page);
+
+       debug_print("ClamAV GTK plugin unloaded\n");
+}
+
+const gchar *plugin_name()
+{
+       return "Clam AntiVirus GTK";
+}
+
+const gchar *plugin_desc()
+{
+       return "This plugin provides a Preferences page for the Clam AntiVirus "
+              "plugin.\n"
+              "\n"
+              "You will find the options in the Other Preferences window "
+              "under Filtering/Clam AntiVirus.\n"
+              "\n"
+              "With this plugin you can enable the filtering, enable archive "
+              "scanning, set the maximum size of messages to be checked, (if "
+              "the message is larger it will not be checked), configure "
+              "whether infected mail should be received (default: Yes) "
+              "and select the folder where infected mail will be saved.\n";
+}
+
+const gchar *plugin_type()
+{
+       return "GTK";
+}