[evolution-patches] New hook for junk plugin



hi,

I have created a hook that wraps up camelJunkPlugin and allows its
methods to be exposed to the plugins. Using this hook one can write a
plugin to use its own method for spam handling. As an example I have
moved the spamassasin junk mail handling to a plugin which used this new
hook.

I have added two files em-format-hook.c em-format-hook.h in the /mail
and removed em-junk-filter.[ch] from here. I have moved em-junk-filter.c
to the newly written sa-junk-plugin.

I am attaching the following:

1. Patch for mail/
2. two new files
3. sa-junk-plugin


Thanks,
Vivek Jain

Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/evolution/mail/ChangeLog,v
retrieving revision 1.3647
diff -u -p -r1.3647 ChangeLog
--- ChangeLog	21 Jun 2005 05:33:47 -0000	1.3647
+++ ChangeLog	23 Jun 2005 15:27:39 -0000
@@ -1,3 +1,15 @@
+2005-06-23  Vivek Jain <jvivek novell com>
+	
+	* em-junk-filter.c : moved to a sa-junk-plugin
+	* em-junk-filter.h :removed
+	* em-junk.plugin.[ch]: removed
+	* em-junk-hook.[ch] : added new files to have a new hook
+	* mail-session.c: (mail_session_init): use 
+	 em_junk_get_plugin to get the junk plugin
+	* mail-component-factory.c : (make_factory)
+	register em junk hook
+	* Makefile.am :updated to removed/add these files.
+
 2005-06-21 Brian Mury <b mury ieee org>
 
 	** See bug #301466.
Index: Makefile.am
===================================================================
RCS file: /cvs/gnome/evolution/mail/Makefile.am,v
retrieving revision 1.263
diff -u -p -r1.263 Makefile.am
--- Makefile.am	2 Jun 2005 04:30:16 -0000	1.263
+++ Makefile.am	23 Jun 2005 15:27:39 -0000
@@ -80,6 +80,7 @@ mailinclude_HEADERS =				\
 	em-folder-utils.h			\
 	em-folder-view.h			\
 	em-format-hook.h			\
+	em-junk-hook.h				\
 	em-format-html-display.h		\
 	em-format-html-print.h			\
 	em-format-html.h			\
@@ -147,6 +148,7 @@ libevolution_mail_la_SOURCES =			\
 	em-folder-utils.c			\
 	em-folder-view.c			\
 	em-format-hook.c			\
+	em-junk-hook.c				\
 	em-format-html-display.c		\
 	em-format-html-print.c			\
 	em-format-html.c			\
@@ -155,10 +157,6 @@ libevolution_mail_la_SOURCES =			\
 	em-html-stream.c			\
 	em-icon-stream.c			\
 	em-inline-filter.c			\
-	em-junk-filter.c			\
-	em-junk-filter.h			\
-	em-junk-plugin.c			\
-	em-junk-plugin.h			\
 	em-mailer-prefs.c			\
 	em-mailer-prefs.h			\
 	em-menu.c				\
Index: mail-session.c
===================================================================
RCS file: /cvs/gnome/evolution/mail/mail-session.c,v
retrieving revision 1.104
diff -u -p -r1.104 mail-session.c
--- mail-session.c	16 May 2005 06:15:38 -0000	1.104
+++ mail-session.c	23 Jun 2005 15:27:39 -0000
@@ -54,7 +54,6 @@
 #include "mail-ops.h"
 #include <libedataserverui/e-passwords.h>
 #include "libedataserver/e-msgport.h"
-#include "em-junk-filter.h"
 #include "e-util/e-error.h"
 
 #define d(x)
@@ -645,7 +644,7 @@ mail_session_init (const char *base_dire
 	session_check_junk_notify_id = gconf_client_notify_add (gconf, "/apps/evolution/mail/junk",
 								(GConfClientNotifyFunc) mail_session_check_junk_notify,
 								session, NULL, NULL);
-	session->junk_plugin = CAMEL_JUNK_PLUGIN (em_junk_filter_get_plugin ());
+	session->junk_plugin = CAMEL_JUNK_PLUGIN (em_junk_get_plugin ());
 	if (session->junk_plugin)
 		camel_junk_plugin_init (session->junk_plugin);
 
Index: mail-component-factory.c
===================================================================
RCS file: /cvs/gnome/evolution/mail/mail-component-factory.c,v
retrieving revision 1.15
diff -u -p -r1.15 mail-component-factory.c
--- mail-component-factory.c	8 Dec 2004 01:58:48 -0000	1.15
+++ mail-component-factory.c	23 Jun 2005 15:27:39 -0000
@@ -40,6 +40,7 @@
 #include "em-event.h"
 #include "em-config.h"
 #include "em-format-hook.h"
+#include "em-junk-hook.h"
 #include "em-format-html-display.h"
 
 #include "importers/mail-importer.h"
@@ -96,12 +97,12 @@ make_factory (PortableServer_POA poa, co
 		e_plugin_hook_register_type(em_popup_hook_get_type());
 		e_plugin_hook_register_type(em_menu_hook_get_type());
 		e_plugin_hook_register_type(em_config_hook_get_type());
-
 		em_format_hook_register_type(em_format_get_type());
+		em_junk_hook_register_type(emj_get_type());
 		em_format_hook_register_type(em_format_html_get_type());
 		em_format_hook_register_type(em_format_html_display_get_type());
 		e_plugin_hook_register_type(em_format_hook_get_type());
-
+		e_plugin_hook_register_type(em_junk_hook_get_type());
 		e_plugin_hook_register_type(em_event_hook_get_type());
 	}
 
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 *  Authors: Vivek Jain <jvivek novell com>
 *
 *  Copyright 2004 Novell, Inc. (www.novell.com)
 *
 *  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 Street #330, Boston, MA 02111-1307, USA.
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <string.h>
#include <stdlib.h>
#include <glib.h>
#include "em-junk-hook.h"
#include <e-util/e-icon-factory.h>
#include <camel/camel-junk-plugin.h>
#include <glib/gi18n.h>

static GHashTable *emjh_types;
static GObjectClass *parent_class = NULL;

static void *emjh_parent_class;
static GObjectClass *emj_parent;
#define emjh ((EMJunkHook *)eph)

#define d(x)
static void em_junk_get_name();
static gboolean em_junk_check_junk(CamelMimeMessage *m);
static void em_junk_report_junk(CamelMimeMessage *m);
static void em_junk_report_non_junk(CamelMimeMessage *m);
static void em_junk_commit_reports();
static void em_junk_init();

static const EPluginHookTargetKey emjh_flag_map[] = {
	{ 0 }
};

static EMJunk junk_plugin = {
	{
		em_junk_get_name,
		1,
		em_junk_check_junk,
		em_junk_report_junk,
		em_junk_report_non_junk,
		em_junk_commit_reports,
		em_junk_init,
	}
};

static struct _EMJunkHookItem *item = NULL; 

EMJunk *
em_junk_get_plugin()
{
	return &junk_plugin;
}

static void 
em_junk_init()
{
}

static void 
em_junk_get_name()
{

}

static gboolean 
em_junk_check_junk(CamelMimeMessage *m)
{
	if (item->hook->hook.plugin->enabled) {
		EMJunkHookTarget target = {
			 item, m
		};

		e_plugin_invoke(item->hook->hook.plugin, item->check_junk, &target);
	}
}

static void 
em_junk_report_junk(CamelMimeMessage *m)
{
	if (item->hook->hook.plugin->enabled) {
		EMJunkHookTarget target = {
			 item, m
		};

		e_plugin_invoke(item->hook->hook.plugin, item->report_junk, &target);
	}
}

static void 
em_junk_report_non_junk(CamelMimeMessage *m)
{
	if (item->hook->hook.plugin->enabled) {
		EMJunkHookTarget target = {
			item, m
		};
		e_plugin_invoke(item->hook->hook.plugin, item->report_non_junk, &target);
	}
}

static void 
em_junk_commit_reports()
{
	if (item->hook->hook.plugin->enabled) {
		EMJunkHookTarget target = {
			 item, NULL
		};

		e_plugin_invoke(item->hook->hook.plugin, item->commit_reports, &target);
	}
}
	
static void 
emj_dispose (GObject *object)
{
	if (parent_class->dispose)
		(* parent_class->dispose) (object);
}

static void 
emj_finalize (GObject *object)
{
	if (parent_class->finalize)
		(* parent_class->finalize) (object);
}

static void
emjh_free_item(struct _EMFormatHookItem *item)
{
	g_free(item);
}

static void
emjh_free_group(struct _EMJunkHookGroup *group)
{
	g_slist_foreach(group->items, (GFunc)emjh_free_item, NULL);
	g_slist_free(group->items);

	g_free(group->id);
	g_free(group);
}

static struct _EMJunkHookItem *
emjh_construct_item(EPluginHook *eph, EMJunkHookGroup *group, xmlNodePtr root)
{
	d(printf("  loading group item\n"));
	item = g_malloc0(sizeof(*item));

	item->check_junk = e_plugin_xml_prop(root, "check_junk");
	item->report_junk = e_plugin_xml_prop(root, "report_junk");
	item->report_non_junk = e_plugin_xml_prop(root, "report_non_junk");
	item->commit_reports = e_plugin_xml_prop(root, "commit_reports");
	item->hook = emjh;

	return item;
}

static struct _EMJunkHookGroup *
emjh_construct_group(EPluginHook *eph, xmlNodePtr root)
{
	struct _EMJunkHookGroup *group;
	xmlNodePtr node;

	d(printf(" loading group\n"));
	group = g_malloc0(sizeof(*group));

	group->id = e_plugin_xml_prop(root, "id");
	if (group->id == NULL)
		goto error;

	node = root->children;
	while (node) {
		if (0 == strcmp(node->name, "item")) {
			struct _EMJunkHookItem *item;

			item = emjh_construct_item(eph, group, node);
			if (item)
				group->items = g_slist_append(group->items, item);
		}
		node = node->next;
	}

	return group;
error:
	emjh_free_group(group);
	return NULL;
}

static int
emjh_construct(EPluginHook *eph, EPlugin *ep, xmlNodePtr root)
{
	xmlNodePtr node;

	d(printf("loading junk hook\n"));

	if (((EPluginHookClass *)emjh_parent_class)->construct(eph, ep, root) == -1)
		return -1;

	node = root->children;
	while (node) {
		if (strcmp(node->name, "group") == 0) {
			struct _EMJunkHookGroup *group;

			group = emjh_construct_group(eph, node);
			if (group) {
				emjh->groups = g_slist_append(emjh->groups, group);
			}
		}
		node = node->next;
	}

	eph->plugin = ep;

	return 0;
}

/*XXX: do i need this?*/
static void
emjh_enable(EPluginHook *eph, int state)
{
	GSList *g, *l;
	EMJunk *klass;

	g = emjh->groups;
	if (emjh_types == NULL)
		return;

}

static void
emjh_finalise(GObject *o)
{
	EPluginHook *eph = (EPluginHook *)o;

	g_slist_foreach(emjh->groups, (GFunc)emjh_free_group, NULL);
	g_slist_free(emjh->groups);

	((GObjectClass *)emjh_parent_class)->finalize(o);
}

static void
emjh_class_init(EPluginHookClass *klass)
{
	((GObjectClass *)klass)->finalize = emjh_finalise;
	klass->construct = emjh_construct;
	klass->enable = emjh_enable;
	klass->id = "org.gnome.evolution.mail.junk:1.0";
}

GType
em_junk_hook_get_type(void)
{
	static GType type = 0;
	
	if (!type) {
		static const GTypeInfo info = {
			sizeof(EMJunkHookClass), NULL, NULL, (GClassInitFunc) emjh_class_init, NULL, NULL,
			sizeof(EMJunkHook), 0, (GInstanceInitFunc) NULL,
		};

		emjh_parent_class = g_type_class_ref(e_plugin_hook_get_type());
		type = g_type_register_static(e_plugin_hook_get_type(), "EMJunkHook", &info, 0);
	}
	
	return type;
}

static void
emj_class_init (EMJunkClass *klass)
{

	GObjectClass *object_class = G_OBJECT_CLASS (klass);
	parent_class = g_type_class_peek_parent (klass);
	object_class->dispose = emj_dispose;
	object_class->finalize = emj_finalize;
}

GType
emj_get_type(void)
{
	static GType type = 0;
	
	if (!type) {
		static const GTypeInfo info = {
			sizeof(EMJunkClass), NULL, NULL, (GClassInitFunc) emj_class_init, NULL, NULL,
			sizeof(EMJunk), 0, (GInstanceInitFunc) NULL,
		};

		emj_parent = g_type_class_ref(G_TYPE_OBJECT);
		type = g_type_register_static(G_TYPE_OBJECT, "EMJunk", &info, 0);
	}
	
	return type;
}

void em_junk_hook_register_type(GType type)
{
	EMJunk *klass;

	if (emjh_types == NULL)
		emjh_types = g_hash_table_new(g_str_hash, g_str_equal);

	printf("registering junk plugin type '%s'\n", g_type_name(type));

	klass = g_type_class_ref(type);
	g_hash_table_insert(emjh_types, (void *)g_type_name(type), klass);
}
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
 *
 *  Authors: Vivek Jain <jvivek novell com>
 *
 *  Copyright 2004 Novell, Inc. (www.novell.com)
 *
 *  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 Street #330, Boston, MA 02111-1307, USA.
 *
 */

#ifndef __EM_FORMAT_HOOK_H__
#define __EM_FORMAT_HOOK_H__

#include <glib-object.h>
#include "libedataserver/e-msgport.h"
#include "e-util/e-plugin.h"
#include <camel/camel-junk-plugin.h>
#include <camel/camel-mime-message.h>
#ifdef __cplusplus
extern "C" {
#pragma }
#endif /* __cplusplus */

typedef struct _EMJunkHookItem EMJunkHookItem;
typedef struct _EMJunkHookGroup EMJunkHookGroup;
typedef struct _EMJunkHook EMJunkHook;
typedef struct _EMJunkHookClass EMJunkHookClass;
typedef struct _EMJunk EMJunk;
typedef struct _EMJunkClass EMJunkClass;

typedef struct _EMJunkHookTarget EMJunkHookTarget;

typedef void (*EMJunkHookFunc)(struct _EPlugin *plugin, EMJunkHookTarget *data);

struct _EMJunkHookTarget {
	struct _EMJunkHookItem *item;
	CamelMimeMessage *m;
};

struct _EMJunkHookItem {
	struct _EMJunkHook *hook; /* parent pointer */
	char *check_junk;
	char *report_junk;
	char *report_non_junk;
	char *commit_reports;
};

struct _EMJunkHookGroup {
	struct _EMJunkHook *hook; /* parent pointer */
	char *id;		/* target id */
	GSList *items;		/* items to consider */
};

struct _EMJunkHook {
	EPluginHook hook;
	GSList *groups;
};

struct _EMJunkHookClass {
	EPluginHookClass hook_class;

	/* which class to add matching items to */
	GHashTable *junk_classes;
};

GType em_junk_hook_get_type(void);
void em_junk_hook_register_type(GType type);

struct _EMJunk {
        CamelJunkPlugin csp;
};

struct _EMJunkClass {
	GObjectClass parent_class;
	/* which class to add matching items to */
	GHashTable *junk_classes;
};

GType emj_get_type(void);
void em_junk_hook_register_type(GType type);

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* __EM_JUNK_HOOK_H__ */
<?xml version="1.0"?>
<e-plugin-list>
	<e-plugin id="org.gnome.evolution.sa_junk_plugin" 
		type="shlib" _name="Sa junk-plugin"
		location="@PLUGINDIR@/liborg-gnome-junk-plugin SOEXT@">
		<_description>learns junk messages using spam assasin.</_description>
		<author name="Vivek Jain" email="jvivek novell com"/>
	
		<hook class="org.gnome.evolution.mail.junk:1.0">
			<group id="EMJunk">
				<item    
				        check_junk="em_junk_sa_check_name" 
				        report_junk="em_junk_sa_report_junk" 
					report_non_junk="em_junk_sa_report_non_junk" 
				      	commit_reports="em_junk_sa_commit_reports"/>
			</group>
				</hook>

	</e-plugin>
</e-plugin-list>
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * Author:
 *  Radek Doulik <rodo ximian com>
 *
 * Copyright 2003 Ximian, Inc. (www.ximian.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of version 2 of the GNU General Public
 * License as published by the Free Software Foundation.
 *
 * 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 <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <time.h>

#include <camel/camel-debug.h>
#include <camel/camel-file-utils.h>
#include <camel/camel-data-wrapper.h>
#include <camel/camel-stream-fs.h>
#include <camel/camel-stream-mem.h>
#include <camel/camel-i18n.h>
#include <mail/em-junk-hook.h>
#include <mail/em-utils.h>
#include <e-util/e-mktemp.h>

#include <gconf/gconf-client.h>

#define d(x) (camel_debug("junk")?(x):0)

static pthread_mutex_t em_junk_sa_init_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t em_junk_sa_report_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t em_junk_sa_preferred_socket_path_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t em_junk_sa_spamd_restart_lock = PTHREAD_MUTEX_INITIALIZER;

static const char *em_junk_sa_get_name (void);
gboolean em_junk_sa_check_junk (EPlugin *ep, EMJunkHookTarget *target);
void em_junk_sa_report_junk (EPlugin *ep, EMJunkHookTarget *target);
void em_junk_sa_report_notjunk (EPlugin *ep, EMJunkHookTarget *target);
void em_junk_sa_commit_reports (EPlugin *ep, EMJunkHookTarget *target);
static void em_junk_sa_init (void);
static void em_junk_sa_finalize (void);
static void em_junk_sa_kill_spamd (void);


static gboolean em_junk_sa_tested = FALSE;
static gboolean em_junk_sa_spamd_tested = FALSE;
static gboolean em_junk_sa_use_spamc = FALSE;
static gboolean em_junk_sa_available = FALSE;
static gboolean em_junk_sa_system_spamd_available = FALSE;
static gboolean em_junk_sa_new_daemon_started = FALSE;
static char *em_junk_sa_socket_path = NULL;
static char *em_junk_sa_spamd_pidfile = NULL;
static char *em_junk_sa_spamc_binary = NULL;
static GConfClient *em_junk_sa_gconf = NULL;

/* volatile so not cached between threads */
static volatile gboolean em_junk_sa_local_only;
static volatile gboolean em_junk_sa_use_daemon;
static char * em_junk_sa_preferred_socket_path;

static char *em_junk_sa_spamc_binaries [3] = {"spamc", "/usr/sbin/spamc", NULL};
static char *em_junk_sa_spamd_binaries [3] = {"spamd", "/usr/sbin/spamd", NULL};

#define SPAMD_RESTARTS_SIZE 8
static time_t em_junk_sa_spamd_restarts [SPAMD_RESTARTS_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0};
static int em_junk_sa_spamd_restarts_count = 0;

char *em_junk_sa_spamc_gconf_binary = NULL;
char *em_junk_sa_spamd_gconf_binary = NULL;

static const char *
em_junk_sa_get_name (void)
{
	return _("Spamassassin (built-in)");
}

static int
pipe_to_sa_full (CamelMimeMessage *msg, const char *in, char **argv, int rv_err, int wait_for_termination, GByteArray *output_buffer)
{
	int result, status, errnosav, fds[2], out_fds[2];
	CamelStream *stream;
	char *program;
	pid_t pid;
	

	if (camel_debug_start ("junk")) {
		int i;
		
		printf ("pipe_to_sa ");
		for (i = 0; argv[i]; i++)
			printf ("%s ", argv[i]);
		printf ("\n");
		camel_debug_end ();
	}

	program = g_find_program_in_path (argv [0]);
	if (program == NULL) {
		d(printf ("program not found, returning %d\n", rv_err));
		return rv_err;
	}
	g_free (program);
	
	if (pipe (fds) == -1) {
		errnosav = errno;
		d(printf ("failed to create a pipe (for use with spamassassin: %s\n", strerror (errno)));
		errno = errnosav;
		return rv_err;
	}
	
	if (output_buffer && pipe (out_fds) == -1) {
		errnosav = errno;
		d(printf ("failed to create a pipe (for use with spamassassin: %s\n", strerror (errno)));
		errno = errnosav;
		return rv_err;
	}
	
	if (!(pid = fork ())) {
		/* child process */
		int maxfd, fd, nullfd;
		
		nullfd = open ("/dev/null", O_WRONLY);
		
		if (dup2 (fds[0], STDIN_FILENO) == -1 ||
		    dup2 (nullfd, STDERR_FILENO) == -1 ||
		    (output_buffer == NULL && dup2 (nullfd, STDOUT_FILENO) == -1) ||
		    (output_buffer != NULL && dup2 (out_fds[1], STDOUT_FILENO) == -1))
			_exit (rv_err & 0377);
		close (fds [0]);
		if (output_buffer)
			close (out_fds [1]);

		setsid ();
		
		maxfd = sysconf (_SC_OPEN_MAX);
		for (fd = 3; fd < maxfd; fd++)
			fcntl (fd, F_SETFD, FD_CLOEXEC);
		
		execvp (argv[0], argv);
		_exit (rv_err & 0377);
	} else if (pid < 0) {
		errnosav = errno;
		close (fds[0]);
		close (fds[1]);
		errno = errnosav;
		return rv_err;
	}
	
	/* parent process */
	close (fds[0]);
	if (output_buffer)
		close (out_fds [1]);
	
	if (msg) {
		stream = camel_stream_fs_new_with_fd (fds[1]);
		
		camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (msg), stream);
		camel_stream_flush (stream);
		camel_stream_close (stream);
		camel_object_unref (stream);
	} else if (in) {
		camel_write (fds[1], in, strlen (in));
		close (fds[1]);
	}

	if (output_buffer) {
		CamelStreamMem *memstream;

		stream = camel_stream_fs_new_with_fd (out_fds[0]);
		
		memstream = (CamelStreamMem *) camel_stream_mem_new ();
		camel_stream_mem_set_byte_array (memstream, output_buffer);
		
		camel_stream_write_to_stream (stream, (CamelStream *) memstream);
		camel_object_unref (stream);
		g_byte_array_append (output_buffer, "", 1);

		d(printf ("child process output: %s len: %d\n", output_buffer->data, output_buffer->len));
	}
	
	if (wait_for_termination) {
		d(printf ("wait for child %d termination\n", pid));
		result = waitpid (pid, &status, 0);
	
		d(printf ("child %d terminated with result %d status %d exited %d exitstatus %d\n", pid, result, status, WIFEXITED (status), WEXITSTATUS (status)));

		if (result == -1 && errno == EINTR) {
			/* child process is hanging... */
			kill (pid, SIGTERM);
			sleep (1);
			result = waitpid (pid, &status, WNOHANG);
			if (result == 0) {
				/* ...still hanging, set phasers to KILL */
				kill (pid, SIGKILL);
				sleep (1);
				result = waitpid (pid, &status, WNOHANG);
			}
		}
	
		if (result != -1 && WIFEXITED (status))
			return WEXITSTATUS (status);
		else
			return rv_err;
	} else
		return 0;
}

static int
pipe_to_sa (CamelMimeMessage *msg, const char *in, char **argv)
{
	return pipe_to_sa_full (msg, in, argv, -1, 1, NULL);
}

static char *
em_junk_sa_get_socket_path ()
{
	if (em_junk_sa_preferred_socket_path)
		return em_junk_sa_preferred_socket_path;
	else
		return em_junk_sa_socket_path;
}

static gboolean
em_junk_sa_test_spamd_running (char *binary, gboolean system)
{
	char *argv[5];
	int i = 0;
	gboolean rv;

	pthread_mutex_lock (&em_junk_sa_preferred_socket_path_lock);

	d(fprintf (stderr, "test if spamd is running (system %d) or using socket path %s\n", system, em_junk_sa_get_socket_path ()));
	
	argv[i++] = binary;
	argv[i++] = "-x";
	
	if (!system) {
		argv[i++] = "-U";
		argv[i++] = em_junk_sa_get_socket_path ();
	}
	
	argv[i] = NULL;
	
	rv = pipe_to_sa (NULL, "From test 127 0 0 1", argv) == 0;

	d(fprintf (stderr, "result: %d (%s)\n", rv, rv ? "success" : "failed"));

	pthread_mutex_unlock (&em_junk_sa_preferred_socket_path_lock);

	return rv;
}

static void
em_junk_sa_test_spamassassin (void)
{
	char *argv [3] = {
		"spamassassin",
		"--version",
		NULL,
	};
	
	if (pipe_to_sa (NULL, NULL, argv) != 0)
		em_junk_sa_available = FALSE;
	else
		em_junk_sa_available = TRUE;

	em_junk_sa_tested = TRUE;
}

#define MAX_SPAMD_PORTS 1

static gboolean
em_junk_sa_run_spamd (char *binary)
{
	char *argv[8];
	int i;
	gboolean rv = FALSE;

	pthread_mutex_lock (&em_junk_sa_preferred_socket_path_lock);

	d(fprintf (stderr, "looks like spamd is not running\n"));

	i = 0;
	argv[i++] = binary;
	argv[i++] = "--socketpath";
	argv[i++] = em_junk_sa_get_socket_path ();
		
	if (em_junk_sa_local_only)
		argv[i++] = "--local";
		
	//argv[i++] = "--daemonize";
	argv[i++] = "--pidfile";
	argv[i++] = em_junk_sa_spamd_pidfile;
	argv[i] = NULL;

	d(fprintf (stderr, "trying to run %s with socket path %s\n", binary, em_junk_sa_get_socket_path ()));
			
	if (!pipe_to_sa_full (NULL, NULL, argv, -1, 0, NULL)) {
		int i;
		struct timespec time_req;
		struct stat stat_buf;

		d(fprintf (stderr, "success\n"));
		d(fprintf (stderr, "waiting for spamd to come up\n"));

		time_req.tv_sec = 0;
		time_req.tv_nsec = 50000000;

		for (i = 0; i < 100; i ++) {
			if (stat (em_junk_sa_get_socket_path (), &stat_buf) == 0) {
				d(fprintf (stderr, "socket created\n"));
				break;
			}
			nanosleep (&time_req, NULL);
		}
		d(fprintf (stderr, "waiting is over (after %dms)\n", 50*i));

		rv = TRUE;
	}

	pthread_mutex_unlock (&em_junk_sa_preferred_socket_path_lock);

	return rv;
}

static void
em_junk_sa_start_own_daemon ()
{
	int b;

	em_junk_sa_new_daemon_started = FALSE;

	em_junk_sa_socket_path = e_mktemp ("spamd-socket-path-XXXXXX");
	em_junk_sa_spamd_pidfile = e_mktemp ("spamd-pid-file-XXXXXX");

	for (b = 0; em_junk_sa_spamd_binaries [b]; b ++) {
		em_junk_sa_use_spamc = em_junk_sa_run_spamd (em_junk_sa_spamd_binaries [b]);
		if (em_junk_sa_use_spamc) {
			em_junk_sa_new_daemon_started = TRUE;
			break;
		}
	}
}

static void
em_junk_sa_find_spamc ()
{
	if (em_junk_sa_use_spamc && em_junk_sa_new_daemon_started) {
		int b;

		em_junk_sa_use_spamc = FALSE;
		for (b = 0; em_junk_sa_spamc_binaries [b]; b ++) {
			em_junk_sa_spamc_binary = em_junk_sa_spamc_binaries [b];
			if (em_junk_sa_test_spamd_running (em_junk_sa_spamc_binary, FALSE)) {
				em_junk_sa_use_spamc = TRUE;
				break;
			}
		}
	}
}

static void
em_junk_sa_test_spamd (void)
{
	char *argv[4];
	int i, b;
	gboolean try_system_spamd = TRUE;

	if (em_junk_sa_spamc_gconf_binary) {
		em_junk_sa_spamc_binaries [0] = em_junk_sa_spamc_gconf_binary;
		em_junk_sa_spamc_binaries [1] = NULL;
	}
  
	if (em_junk_sa_spamd_gconf_binary) {
		em_junk_sa_spamd_binaries [0] = em_junk_sa_spamd_gconf_binary;
		em_junk_sa_spamd_binaries [1] = NULL;
		try_system_spamd = FALSE;
  	}

	em_junk_sa_use_spamc = FALSE;

	if (em_junk_sa_local_only && try_system_spamd) {
		   i = 0;
		   argv [i++] = "/bin/sh";
		   argv [i++] = "-c";
		   argv [i++] = "ps ax|grep -v grep|grep -E 'spamd.*(\\-L|\\-\\-local)'|grep -E -v '\\ \\-p\\ |\\ \\-\\-port\\ '";
		   argv[i] = NULL;

		   if (pipe_to_sa (NULL, NULL, argv) != 0) {
			   try_system_spamd = FALSE;
			   d(fprintf (stderr, "there's no system spamd with -L/--local parameter running\n"));
		   }
	}

	/* try to use sytem spamd first */
	if (try_system_spamd) {
		for (b = 0; em_junk_sa_spamc_binaries [b]; b ++) {
			em_junk_sa_spamc_binary = em_junk_sa_spamc_binaries [b];
			if (em_junk_sa_test_spamd_running (em_junk_sa_spamc_binary, TRUE)) {
				em_junk_sa_use_spamc = TRUE;
				em_junk_sa_system_spamd_available = TRUE;
				break;
			}
		}
	}

	/* if there's no system spamd running, try to use user one with user specified socket */
	if (!em_junk_sa_use_spamc && em_junk_sa_preferred_socket_path) {
		for (b = 0; em_junk_sa_spamc_binaries [b]; b ++) {
			em_junk_sa_spamc_binary = em_junk_sa_spamc_binaries [b];
			if (em_junk_sa_test_spamd_running (em_junk_sa_spamc_binary, FALSE)) {
				em_junk_sa_use_spamc = TRUE;
				em_junk_sa_system_spamd_available = FALSE;
				break;
			}
		}
	}

	/* unsuccessful? try to run one ourselfs */
	if (!em_junk_sa_use_spamc)
		em_junk_sa_start_own_daemon ();

	/* new daemon started => let find spamc binary */
	em_junk_sa_find_spamc ();

	d(fprintf (stderr, "use spamd: %s\n", em_junk_sa_use_spamc ? "yes" : "no"));
	
	em_junk_sa_spamd_tested = TRUE;
}

static gboolean
em_junk_sa_is_available (void)
{
	pthread_mutex_lock (&em_junk_sa_init_lock);

	if (!em_junk_sa_tested)
		em_junk_sa_test_spamassassin ();

	if (em_junk_sa_available && !em_junk_sa_spamd_tested && em_junk_sa_use_daemon)
		em_junk_sa_test_spamd ();

	pthread_mutex_unlock (&em_junk_sa_init_lock);
	
	return em_junk_sa_available;
}

static gboolean
em_junk_sa_check_respawn_too_fast ()
{
	time_t time_now = time (NULL);
	gboolean rv;

	pthread_mutex_lock (&em_junk_sa_spamd_restart_lock);

	if (em_junk_sa_spamd_restarts_count >= SPAMD_RESTARTS_SIZE) {
		/* all restarts in last 5 minutes */
		rv = (time_now - em_junk_sa_spamd_restarts [em_junk_sa_spamd_restarts_count % SPAMD_RESTARTS_SIZE] < 5*60);
	} else
		rv = FALSE;

	em_junk_sa_spamd_restarts [em_junk_sa_spamd_restarts_count % SPAMD_RESTARTS_SIZE] = time_now;
	em_junk_sa_spamd_restarts_count ++;

	pthread_mutex_unlock (&em_junk_sa_spamd_restart_lock);

	d(printf ("em_junk_sa_check_respawn_too_fast: %d\n", rv));

	return rv;
}

static gboolean
em_junk_sa_respawn_spamd ()
{
	d(printf ("em_junk_sa_respawn_spamd\n"));
	if (em_junk_sa_test_spamd_running (em_junk_sa_spamc_binary, em_junk_sa_system_spamd_available)) {
		/* false alert */
		d(printf ("false alert, spamd still running\n"));

		return FALSE;
	}

	d(printf ("going to kill old spamd and start new one\n"));
	em_junk_sa_kill_spamd ();

	if (em_junk_sa_check_respawn_too_fast ()) {
		g_warning ("respawning of spamd too fast => fallback to use spamassassin directly");

		em_junk_sa_use_spamc = em_junk_sa_use_daemon = FALSE;
		return FALSE;
	}

	em_junk_sa_start_own_daemon ();
	em_junk_sa_find_spamc ();

	d(printf ("%s\n", em_junk_sa_use_spamc ? "success" : "failed"));

	return em_junk_sa_use_spamc;
}

gboolean
em_junk_sa_check_junk(EPlugin *ep, EMJunkHookTarget *target)
{
	GByteArray *out = NULL;
	char *argv[7], *to_free = NULL;
	int i = 0, socket_i;
	gboolean rv;
	CamelMimeMessage *msg = target->m;

	d(fprintf (stderr, "em_junk_sa_check_junk\n"));
	g_print ("checking for junk..?\n");
	
	if (!em_junk_sa_is_available ())
		return FALSE;

	if (em_junk_sa_use_spamc && em_junk_sa_use_daemon) {
		out = g_byte_array_new ();
		argv[i++] = em_junk_sa_spamc_binary;
		argv[i++] = "-c";
		argv[i++] = "-t";
		argv[i++] = "60";
		if (!em_junk_sa_system_spamd_available) {
			argv[i++] = "-U";

			pthread_mutex_lock (&em_junk_sa_preferred_socket_path_lock);
			socket_i = i;
			argv[i++] = to_free = g_strdup (em_junk_sa_get_socket_path ());
			pthread_mutex_unlock (&em_junk_sa_preferred_socket_path_lock);
		}
	} else {
		argv [i++] = "spamassassin";
		argv [i++] = "--exit-code";
		if (em_junk_sa_local_only)
			argv [i++] = "--local";
	}
	
	argv[i] = NULL;

	rv = pipe_to_sa_full (msg, NULL, argv, 0, 1, out) != 0;

	if (!rv && out && !strcmp (out->data, "0/0\n")) {
		/* an error occured */
		if (em_junk_sa_respawn_spamd ()) {
			g_byte_array_set_size (out, 0);

			pthread_mutex_lock (&em_junk_sa_preferred_socket_path_lock);
			g_free (to_free);
			argv [socket_i] = to_free = g_strdup (em_junk_sa_get_socket_path ());
			pthread_mutex_unlock (&em_junk_sa_preferred_socket_path_lock);

			rv = pipe_to_sa_full (msg, NULL, argv, 0, 1, out) != 0;
		} else if (!em_junk_sa_use_spamc)
			/* in case respawning were too fast we fallback to spamassassin */
			rv = em_junk_sa_check_junk (ep, target);
	}

	g_free (to_free);

	d(fprintf (stderr, "em_junk_sa_check_junk rv = %d\n", rv));

	if (out)
		g_byte_array_free (out, TRUE);

	return rv;
}

void
em_junk_sa_report_junk (EPlugin *ep, EMJunkHookTarget *target)
{
	char *argv[6] = {
		"sa-learn",
		"--no-rebuild",
		"--spam",
		"--single",
		NULL,
		NULL
	};
	gchar *sub = NULL;	
	CamelMimeMessage *msg = target->m;
	
	sub = g_strdup (camel_mime_message_get_subject (msg));
	g_print ("\nreport junk?? %s\n", sub);
		
	d(fprintf (stderr, "em_junk_sa_report_junk\n"));
	
	if (em_junk_sa_is_available ()) {
		if (em_junk_sa_local_only)
			argv[4] = "--local";
		
		pthread_mutex_lock (&em_junk_sa_report_lock);
		pipe_to_sa (msg, NULL, argv);
		pthread_mutex_unlock (&em_junk_sa_report_lock);
	}
}

void
em_junk_sa_report_non_junk (EPlugin *ep, EMJunkHookTarget *target)
{
	char *argv[6] = {
		"sa-learn",
		"--no-rebuild",
		"--ham",
		"--single",
		NULL,
		NULL
	};
	CamelMimeMessage *msg = target->m;

	d(fprintf (stderr, "em_junk_sa_report_notjunk\n"));
	
	if (em_junk_sa_is_available ()) {
		if (em_junk_sa_local_only)
			argv[4] = "--local";
		
		pthread_mutex_lock (&em_junk_sa_report_lock);
		pipe_to_sa (msg, NULL, argv);
		pthread_mutex_unlock (&em_junk_sa_report_lock);
	}
}

void
em_junk_sa_commit_reports (EPlugin *ep, EMJunkHookTarget *target)
{
	char *argv[4] = {
		"sa-learn",
		"--rebuild",
		NULL,
		NULL
	};
	
	d(fprintf (stderr, "em_junk_sa_commit_reports\n"));
	
	if (em_junk_sa_is_available ()) {
		if (em_junk_sa_local_only)
			argv[2] = "--local";
		
		pthread_mutex_lock (&em_junk_sa_report_lock);
		pipe_to_sa (NULL, NULL, argv);
		pthread_mutex_unlock (&em_junk_sa_report_lock);
	}
}

static void
em_junk_sa_setting_notify(GConfClient *gconf, guint cnxn_id, GConfEntry *entry, void *data)
{
	GConfValue *value;
	char *tkey;

	g_return_if_fail (gconf_entry_get_key (entry) != NULL);
	
	if (!(value = gconf_entry_get_value (entry)))
		return;
	
	tkey = strrchr(entry->key, '/');
	g_return_if_fail (tkey != NULL);

	if (!strcmp(tkey, "local_only"))
		em_junk_sa_local_only = gconf_value_get_bool(value);
	else if (!strcmp(tkey, "use_daemon"))
		em_junk_sa_use_daemon = gconf_value_get_bool(value);
	else if (!strcmp(tkey, "socket_path")) {
		pthread_mutex_lock (&em_junk_sa_preferred_socket_path_lock);
		g_free (em_junk_sa_preferred_socket_path);
		em_junk_sa_preferred_socket_path = g_strdup (gconf_value_get_string(value));
		pthread_mutex_unlock (&em_junk_sa_preferred_socket_path_lock);
	}
}

static void
em_junk_sa_init (void)
{
	pthread_mutex_lock (&em_junk_sa_init_lock);

	if (!em_junk_sa_gconf) {
		em_junk_sa_gconf = gconf_client_get_default();
		gconf_client_add_dir (em_junk_sa_gconf, "/apps/evolution/mail/junk/sa", GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);

		em_junk_sa_local_only = gconf_client_get_bool (em_junk_sa_gconf, "/apps/evolution/mail/junk/sa/local_only", NULL);
		em_junk_sa_use_daemon = gconf_client_get_bool (em_junk_sa_gconf, "/apps/evolution/mail/junk/sa/use_daemon", NULL);

		pthread_mutex_lock (&em_junk_sa_preferred_socket_path_lock);
		g_free (em_junk_sa_preferred_socket_path);
		em_junk_sa_preferred_socket_path = g_strdup (gconf_client_get_string (em_junk_sa_gconf, "/apps/evolution/mail/junk/sa/socket_path", NULL));
		pthread_mutex_unlock (&em_junk_sa_preferred_socket_path_lock);

		gconf_client_notify_add(em_junk_sa_gconf, "/apps/evolution/mail/junk/sa",
					(GConfClientNotifyFunc)em_junk_sa_setting_notify,
					NULL, NULL, NULL);

		em_junk_sa_spamc_gconf_binary = gconf_client_get_string (em_junk_sa_gconf, "/apps/evolution/mail/junk/sa/spamc_binary", NULL);
		em_junk_sa_spamd_gconf_binary = gconf_client_get_string (em_junk_sa_gconf, "/apps/evolution/mail/junk/sa/spamd_binary", NULL);
	}

	pthread_mutex_unlock (&em_junk_sa_init_lock);

	atexit (em_junk_sa_finalize);
}

static void
em_junk_sa_kill_spamd (void)
{
	pthread_mutex_lock (&em_junk_sa_preferred_socket_path_lock);
	g_free (em_junk_sa_preferred_socket_path);
	em_junk_sa_preferred_socket_path = NULL;
	pthread_mutex_unlock (&em_junk_sa_preferred_socket_path_lock);

	if (em_junk_sa_new_daemon_started) {
		int fd = open (em_junk_sa_spamd_pidfile, O_RDONLY);

		if (fd != -1) {
			char pid_str [16];
			int bytes;

			bytes = read (fd, pid_str, 15);
			if (bytes > 0) {
				int pid;

				pid_str [bytes] = 0;
				pid = atoi (pid_str);

				if (pid > 0) {
					kill (pid, SIGTERM);
					d(fprintf (stderr, "em_junk_sa_finalize send SIGTERM to daemon with pid %d\n", pid));
					waitpid (pid, NULL, 0);
				}
			}

			close (fd);
		}
	}
}

static void
em_junk_sa_finalize (void)
{
	d(fprintf (stderr, "em_junk_sa_finalize\n"));

	em_junk_sa_kill_spamd ();
}
INCLUDES =						\
	-I$(top_srcdir)					\
	$(EVOLUTION_MAIL_CFLAGS)

@EVO_PLUGIN_RULE@

plugin_DATA = org-gnome-sa-junk-plugin.eplug
plugin_LTLIBRARIES = liborg-gnome-sa-junk-plugin.la

liborg_gnome_sa_junk_plugin_la_SOURCES = em-junk-filter.c
liborg_gnome_sa_junk_plugin_la_LDFLAGS = -module -avoid-version


BUILT_SOURCES = $(plugin_DATA) $(error_DATA)

CLEANFILES = $(BUILT_SOURCES)

EXTRA_DIST = \
	org-gnome-sa-junk-plugin.eplug.xml




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]