pseudo code

/* code mostly stolen from http://www.dovecot.org/patches/1.0/copy_plugin.c */

/*
  make "plugins" directory right beside "src", then copy this into there and run

  gcc -fPIC -shared -Wall \
    -I../src/lib \
    -I../.. \
    -I../src/lib-storage \
    -I../src/lib-mail \
    -I../src/lib-imap \
    -I../src/imap/ \
    -DHAVE_CONFIG_H spam_plugin.c -o spam_plugin.so
*/

#include "common.h"
#include "commands.h"
#include "imap-search.h"

static void
copy_reclassified (struct client *client,
                   struct mail_search_arg *search_args,
                   int is_spam)
{
        /* TODO: iteration */
        for each message in mail_search_arg do {
                if (is_spam) {
                        remove message from SPAM.new-ham if it exists;
                        /* how do I find out if that exact message exists there? */
                        copy message into SPAM.new-spam
                } else {
                        /* similar to above */
                        remove message from SPAM.new-spam if it exists
                        /* see above */
                        copy message into SPAM.new-ham
                }
        }
}

static int cmd_append_spam_plugin(struct client *client)
{
        const char *mailbox;

        /* <mailbox> */
        if (!client_read_string_args(client, 1, &mailbox))
                return FALSE;

        /* TODO: is this really the best way to handle this? maybe more logic could be provided */
        if (mailbox_name_equals(mailbox, "SPAM"))
        {
                client_send_tagline (client, "NO Cannot APPEND to SPAM box, sorry.");
                return TRUE;
        }

        return cmd_append (client);
}

static int cmd_copy_spam_plugin(struct client *client)
{
        const char *messageset, *mailbox;
        struct mail_search_arg *search_arg;
        int spam_folder = 0;

        /* <message set> <mailbox> */
        if (!client_read_string_args(client, 2, &messageset, &mailbox))
                return FALSE;

        spam_folder = mailbox_name_equals(mailbox_get_name(client->mailbox), "SPAM")
                   || mailbox_name_equals(mailbox, "SPAM");

        if (!cmd_copy(client))
                return FALSE;

        /* if the copy failed there's no point in doing anything with these messages */

        /* only act on spam */
        if ( !spam_folder )
                return TRUE; /* do nothing but copying */

        /* TODO: I wonder if there's a better way to get at the messages
                 since the copy command already did this */
        search_arg = imap_search_get_arg(client, messageset, client->cmd_uid);
        if (search_arg == NULL)
                return TRUE;

        copy_reclassified(client, search_arg, mailbox_name_equals(mailbox, "SPAM"));

        return TRUE;
}

void spam_plugin_init(void)
{
        command_unregister("COPY");
        command_unregister("APPEND");
        /* i_strdup() here is a kludge to avoid crashing in commands_deinit()
           since modules are unloaded before it's called, this "COPY" string
           would otherwise point to nonexisting memory. */
        command_register(i_strdup("COPY"), cmd_copy_spam_plugin);
        command_register(i_strdup("APPEND"), cmd_append_spam_plugin);
}

void spam_plugin_deinit(void)
{
}