[evolution-patches] Re: [Evolution-hackers] Post-to header implementation



PS i've moved this reply to evolution-patches, cc'd you incase you're
not on it.

Ok, here's a review of your latest patch, i didn't do a whole lot of
testing, but the basics of replying/etc seem to work as they should, so
i'm pretty happy with the functionality (thats not saying there aren't
bugs in there, its a big patch).  Thanks for getting reply to all
working, it seems pretty well all the basic functions for a news reader
have been done.

Anyway, I would say the patch can go in as is (almost!), to make the
ui freeze deadline, then the changes below can be applied afterwards.
Most are cosmetic, but a couple are important (g_ascii_strcasecmp, and
the folder persistent meta-data storage).

You must fix the C89 inline-variable declaration(s? i only saw one)
though.

And write some ChangeLog's too (or include them in the diff's if you
haven't been).  Given that you're in Europe, I think if JP agrees, and
its needed for the deadline, Jeff can apply/commit the patch as is (with
the c99 fix, assuming he too has no material objections), but we'll
still need changelogs asap.

Thanks a lot for all this effort too.

 Michael

> --- /home/meilof/cvs/orig/evolution//configure.in	2004-01-10
16:25:57.000000000 -0500
> +++ configure.in	2004-01-10 16:26:19.000000000 -0500
> @@ -338,9 +338,9 @@
>  dnl NNTP support.
>  dnl **************************************************
>  AC_ARG_ENABLE(nntp, 
> -[  --enable-nntp=[no/yes]      Attempt to compile incomplete,
unsupported NNTP code],,enable_nntp=no)
> +[  --enable-nntp=[no/yes]      Build Usenet news (NNTP) backend],,
enable_nntp=yes)
>  if test "x$enable_nntp" = "xyes"; then
> -	AC_DEFINE(ENABLE_NNTP,1,[Don't try this at home])
> +	AC_DEFINE(ENABLE_NNTP,1,[Build NNTP backend])
>  	msg_nntp=yes
>  else
>  	msg_nntp=no
> --- /home/meilof/cvs/orig/evolution//camel/providers/nntp/camel-nntp-
folder.c	2003-07-09 15:21:58.000000000 -0400
> +++ camel/providers/nntp/camel-nntp-folder.c	2004-01-09
21:58:20.000000000 -0500
> @@ -44,13 +44,21 @@
>  #include "camel/camel-session.h"
>  #include "camel/camel-data-cache.h"
>  
> +#include "camel/camel-mime-filter-crlf.h"
> +#include "camel/camel-stream-filter.h"
> +#include "camel/camel-mime-message.h"
> +#include "camel/camel-multipart.h"
> +#include "camel/camel-mime-part.h"
> +#include "camel/camel-stream-buffer.h"
> +
>  #include "camel-nntp-summary.h"
>  #include "camel-nntp-store.h"
>  #include "camel-nntp-folder.h"
>  #include "camel-nntp-store.h"
>  #include "camel-nntp-private.h"
>  
> -static CamelFolderClass *parent_class = NULL;
> +static CamelFolderClass *folder_class = NULL;
> +static CamelDiscoFolderClass *parent_class = NULL;
>  
>  /* Returns the class for a CamelNNTPFolder */
>  #define CNNTPF_CLASS(so) CAMEL_NNTP_FOLDER_CLASS
(CAMEL_OBJECT_GET_CLASS(so))
> @@ -58,7 +66,7 @@
>  #define CNNTPS_CLASS(so) CAMEL_STORE_CLASS (CAMEL_OBJECT_GET_CLASS
(so))
>  
>  static void
> -nntp_folder_sync (CamelFolder *folder, gboolean expunge,
CamelException *ex)
> +nntp_folder_sync_online (CamelFolder *folder, /*gboolean expunge, */
CamelException *ex)
>  {
>  	CamelNNTPStore *nntp_store;
>  	CamelFolderChangeInfo *changes = NULL;
> @@ -86,9 +94,80 @@
>  }
>  
>  static void
> +nntp_folder_sync_offline (CamelFolder *folder, CamelException *ex)
> +{
> +	camel_folder_summary_save (folder->summary);
> +}
> +
> +static void
>  nntp_folder_set_message_flags(CamelFolder *folder, const char *uid,
guint32 flags, guint32 set)
>  {
> -        ((CamelFolderClass *)parent_class)->set_message_flags(folder,
uid, flags, set);
> +        ((CamelFolderClass *)folder_class)->set_message_flags(folder,
uid, flags, set);
> +}
> +
> +static int
> +nntp_folder_download_message(CamelStream **retstream, CamelNNTPStore
*nntp_store,
> +                             CamelNNTPFolder *nntp_folder, const char
*msgid, CamelException *ex)


Arguments should start with 'folder'.  You don't need to pass
nntp_store since its on folder anyway.  The prototype should be:

static CamelStream *
nntp_folder_download_message(CamelNNTPFolder *nntp_folder, const char
*msgid, CamelException *ex)

Also ...

I still don't get why you need more than success or failure for your
return code, at the end of the day its still failed.  This function
should set any exception required to be set on exit, then you don't
need to do this.  It should also return the stream as its return
value, NULL indicating failure.

> +static void
> +nntp_folder_cache_message(CamelDiscoFolder *disco_folder,
> +                          const char *uid, CamelException *ex)
> +{
> +	CamelStream *stream;
> +	const char *msgid;
> +	CamelNNTPStore *nntp_store = (CamelNNTPStore *)((CamelFolder *)
disco_folder)->parent_store;
> +
> +	msgid = strchr(uid, ',');
> +	if (msgid == 0) {

Should check against NULL.

>  
>  static CamelMimeMessage *
> @@ -99,8 +178,7 @@
>  	CamelFolderChangeInfo *changes;
>  	CamelNNTPFolder *nntp_folder;
>  	CamelStream *stream = NULL;
> -	int ret;
> -	char *line;
> +	char *line = NULL;
>  	const char *msgid;
>  
>  	nntp_store = (CamelNNTPStore *)folder->parent_store;
> @@ -119,25 +197,16 @@
>  	/* Lookup in cache, NEWS is global messageid's so use a global cache
path */
>  	stream = camel_data_cache_get(nntp_store->cache, "cache", msgid,
NULL);
>  	if (stream == NULL) {
> -		/* Not in cache, retrieve and put in cache */
> -		if (camel_nntp_store_set_folder(nntp_store, folder, nntp_folder-
>changes, ex) == -1)
> +		if (camel_disco_store_status ((CamelDiscoStore*) nntp_store) ==
CAMEL_DISCO_STORE_OFFLINE) {
> +			camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
> +					     _("This message is not currently available"));
>  			goto fail;
> -
> -		ret = camel_nntp_command(nntp_store, &line, "article %s", msgid);
> -		if (ret == -1)
> -			goto error;
> -
> -		if (ret == 220) {
> -			stream = camel_data_cache_add(nntp_store->cache, "cache", msgid,
NULL);
> -			if (stream) {
> -				if (camel_stream_write_to_stream((CamelStream *)nntp_store-
>stream, stream) == -1)
> -					goto error;
> -				if (camel_stream_reset(stream) == -1)
> -					goto error;
> -			} else {
> -				stream = (CamelStream *)nntp_store->stream;
> -				camel_object_ref((CamelObject *)stream);
>  			}

^^ indenting here is wrong when the patch is applied.

> +
> +		switch (nntp_folder_download_message(&stream, nntp_store,
nntp_folder, msgid, ex)) {
> +			case -1: goto error;
> +			case -2: goto fail;
> +			default: break;

I'd like to see this with the changes outlined above, so it only tests
for failure or success.

>  		}
>  	}
>  
> @@ -259,6 +328,136 @@
>  }
>  
>  static void           
> +nntp_folder_append_message_online(CamelFolder *folder,
CamelMimeMessage *mime_message,
> +                                  const CamelMessageInfo *info, char
**appended_uid,
> +                                  CamelException *ex) {
> +	CamelNNTPStore *nntp_store = (CamelNNTPStore *)folder->parent_store;
> +	CamelStream *stream = (CamelStream*)nntp_store->stream;
> +	CamelStreamFilter *filtered_stream;
> +	CamelMimeFilter *crlffilter;
> +	int ret;
> +	unsigned int u;
> +	struct _camel_header_raw *header, *savedhdrs, *n, *tail;
> +	
> +	unsigned char *line;
> +	char *cmdbuf = NULL, *respbuf = NULL;
> +	
> +	CAMEL_NNTP_STORE_LOCK(nntp_store, command_lock);
> +
> +	/* send 'POST' command */
> +	ret = camel_nntp_command(nntp_store, (char **)&line, "post");
> +
> +	if (ret != 340) {
> +		camel_exception_setv (ex,
CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION,
> +		                      _("Posting not allowed by news server"));
> +		CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock);
> +		return;
> +	}
> +
> +	/* send the 'Newsgroups: ' header */
> +	cmdbuf = g_strdup_printf ("Newsgroups: %s\r\n", folder->full_name);
> +
> +	if (camel_stream_write (stream, cmdbuf, strlen (cmdbuf)) == -1) {
> +		g_free (cmdbuf);
> +		camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
> +				      _("Failed to send newsgroups header: %s: message not
posted"),
> +				      g_strerror (errno));
> +		CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock);
> +		return;
> +	}
> +	g_free (cmdbuf);
> +
> +	/* setup stream filtering */
> +	crlffilter = camel_mime_filter_crlf_new
(CAMEL_MIME_FILTER_CRLF_ENCODE, CAMEL_MIME_FILTER_CRLF_MODE_CRLF_DOTS);
> +	filtered_stream = camel_stream_filter_new_with_stream (stream);
> +	camel_stream_filter_add (filtered_stream, CAMEL_MIME_FILTER
(crlffilter));
> +	camel_object_unref (crlffilter);
> +
> +	/* remove mail 'To', 'CC', and 'BCC' headers */
> +	savedhdrs = NULL;
> +	tail = (struct _camel_header_raw *) &savedhdrs;
> +
> +	header = (struct _camel_header_raw *) &CAMEL_MIME_PART
(mime_message)->headers;
> +	n = header->next;
> +	while (n != NULL) {
> +		if (!strcasecmp (n->name, "To") || !strcasecmp (n->name, "CC") || !
strcasecmp(n->name, "BCC")) {

use g_ascii_strcasecmp here, since strcasecmp is locale specific.

> +			header->next = n->next;
> +			tail->next = n;
> +			n->next = NULL;
> +			tail = n;
> +		} else {
> +			header = n;
> +		}
> +
> +		n = header->next;
> +	}
> +
> +	/* write the message */
> +	ret = camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER
(mime_message), CAMEL_STREAM (filtered_stream));
> +
> +	/* restore the mail headers */
> +	header->next = savedhdrs;

^^ changes the order of the headers, but i guess its not important.

> +	if (ret == -1) {
> +		camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
> +				      _("Error posting to newsgroup: %s: message not posted"),
> +				      g_strerror (errno));
> +		camel_object_unref (filtered_stream);
> +		CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock);
> +		return;
> +	}
> +
> +	camel_stream_flush (CAMEL_STREAM (filtered_stream));
> +	camel_object_unref (filtered_stream);
> +
> +	/* terminate the message body */
> +	if (camel_stream_write (stream, "\r\n.\r\n", 5) == -1) {
> +		camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
> +				      _("Error posting to newsgroup: %s: message not posted"),
> +				      g_strerror (errno));
> +		CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock);
> +		return;
> +	}
> +
> +	if (camel_nntp_stream_line(nntp_store->stream, (unsigned char **)
&respbuf, &u) == -1)
> +		respbuf = NULL;
> +
> +	if (!respbuf || strncmp (respbuf, "240", 3)) {
> +		if (!respbuf)
> +			camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
> +					      _("Error reading response to posted message: message not
posted"));
> +		else
> +			camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
> +					      _("Error posting message: %s: message not posted"),
respbuf);
> +		CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock);
> +		return;
> +	}
> +
> +	CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock);
> +
> +	return;
> +}
> +
> +static void
> +nntp_folder_append_message_offline(CamelFolder *folder,
CamelMimeMessage *mime_message,
> +                                   const CamelMessageInfo *info, char
**appended_uid,
> +                                   CamelException *ex) {
> +	camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
> +	                      _("You cannot post NNTP messages while working
offline!"));	
> +}
> +
> +/* I do not know what to do this exactly. Looking at the IMAP
implementation for this, it
> +   seems to assume the message is copied to a folder on the same
store. In that case, an
> +   NNTP implementation doesn't seem to make any sense. */

Yeah its for optimising message copies, and is only called by camel-
folder if the store is the
same.  It should do nothing for nntp, as your code does.

> +static void
> +nntp_folder_transfer_message(CamelFolder *source, GPtrArray *uids,
CamelFolder *dest,
> +                             GPtrArray **transferred_uids, gboolean
delete_orig, CamelException *ex)
> +{
> +	camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
> +	                      _("You cannot copy messages from a NNTP
folder!"));
> +}
> +
> +static void           
>  nntp_folder_init(CamelNNTPFolder *nntp_folder, CamelNNTPFolderClass
*klass)
>  {
>  	struct _CamelNNTPFolderPrivate *p;
> @@ -274,7 +473,7 @@
>  {
>  	struct _CamelNNTPFolderPrivate *p;
>  
> -	g_free(nntp_folder->storage_path);
> +	camel_folder_summary_save(((CamelFolder*)nntp_folder)->summary);
>  	
>  	p = nntp_folder->priv;
>  	g_mutex_free(p->search_lock);
> @@ -285,14 +484,26 @@
>  static void
>  nntp_folder_class_init (CamelNNTPFolderClass
*camel_nntp_folder_class)
>  {
> +	CamelDiscoFolderClass *camel_disco_folder_class =
CAMEL_DISCO_FOLDER_CLASS (camel_nntp_folder_class);
>  	CamelFolderClass *camel_folder_class = CAMEL_FOLDER_CLASS
(camel_nntp_folder_class);
>  
> -	parent_class = CAMEL_FOLDER_CLASS (camel_type_get_global_classfuncs
(camel_folder_get_type ()));
> +	parent_class = CAMEL_DISCO_FOLDER_CLASS
(camel_type_get_global_classfuncs (camel_disco_folder_get_type ()));
> +	folder_class = CAMEL_FOLDER_CLASS (camel_type_get_global_classfuncs
(camel_folder_get_type ()));

This is a hangover from older code, you only need to call

 folder_class = (CamelFolderClass *)camel_folder_get_type();

Actually you only need to get the parent class anyway,
CameDiscoFolderClass.  You shouldn't access CamelFolderClass that way.

>  	/* virtual method definition */
>  
>  	/* virtual method overload */
> -	camel_folder_class->sync = nntp_folder_sync;
> +        camel_disco_folder_class->sync_online =
nntp_folder_sync_online;
> +        camel_disco_folder_class->sync_resyncing =
nntp_folder_sync_offline;
> +        camel_disco_folder_class->sync_offline =
nntp_folder_sync_offline;        
> +        camel_disco_folder_class->cache_message =
nntp_folder_cache_message;
> +        camel_disco_folder_class->append_online =
nntp_folder_append_message_online;
> +        camel_disco_folder_class->append_resyncing =
nntp_folder_append_message_online;
> +        camel_disco_folder_class->append_offline =
nntp_folder_append_message_offline;
> +        camel_disco_folder_class->transfer_online =
nntp_folder_transfer_message;
> +        camel_disco_folder_class->transfer_resyncing =
nntp_folder_transfer_message;
> +        camel_disco_folder_class->transfer_offline =
nntp_folder_transfer_message;
> +        
>  	camel_folder_class->set_message_flags =
nntp_folder_set_message_flags;
>  	camel_folder_class->get_message = nntp_folder_get_message;
>  	camel_folder_class->search_by_expression =
nntp_folder_search_by_expression;
> @@ -306,7 +517,7 @@
>  	static CamelType camel_nntp_folder_type = CAMEL_INVALID_TYPE;
>  	
>  	if (camel_nntp_folder_type == CAMEL_INVALID_TYPE)	{
> -		camel_nntp_folder_type = camel_type_register (CAMEL_FOLDER_TYPE,
"CamelNNTPFolder",
> +		camel_nntp_folder_type = camel_type_register
(CAMEL_DISCO_FOLDER_TYPE, "CamelNNTPFolder",
>  							      sizeof (CamelNNTPFolder),
>  							      sizeof (CamelNNTPFolderClass),
>  							      (CamelObjectClassInitFunc) nntp_folder_class_init,
> @@ -369,6 +580,8 @@
>  #ifdef ASYNC_SUMMARY
>  	struct _folder_check_msg *m;
>  #endif
> +	CamelStoreInfo *si;
> +	gboolean subscribed = TRUE;
>  
>  	service = (CamelService *)parent;
>  	root = camel_session_get_storage_path(service->session, service,
ex);
> @@ -389,17 +602,25 @@
>  
>  	folder->summary = (CamelFolderSummary *)camel_nntp_summary_new
(nntp_folder);
>  	camel_folder_summary_load (folder->summary);
> +
> +	si = camel_store_summary_path((CamelStoreSummary *)
((CamelNNTPStore*)parent)->summary, folder_name);
> +	if (si) {
> +		subscribed = (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) != 0;
> +		camel_store_summary_info_free((CamelStoreSummary *)
((CamelNNTPStore*)parent)->summary, si);
> +	}
> +
> +	if (subscribed) {
>  #ifdef ASYNC_SUMMARY
> -	m = camel_session_thread_msg_new(service->session,
&folder_check_ops, sizeof(*m));
> -	m->folder = nntp_folder;
> -	camel_object_ref((CamelObject *)folder);
> -	camel_session_thread_queue(service->session, &m->msg, 0);
> +		m = camel_session_thread_msg_new(service->session,
&folder_check_ops, sizeof(*m));
> +		m->folder = nntp_folder;
> +		camel_object_ref((CamelObject *)folder);
> +		camel_session_thread_queue(service->session, &m->msg, 0);
>  #else
> -	if (camel_nntp_summary_check((CamelNNTPSummary *)folder->summary,
nntp_folder->changes, ex) == -1) {
> -		camel_object_unref((CamelObject *)folder);
> -		folder = NULL;
> -	}
> +		if (camel_nntp_summary_check((CamelNNTPSummary *)folder->summary,
nntp_folder->changes, ex) == -1) {
> +			camel_object_unref((CamelObject *)folder);
> +			folder = NULL;
> +		}
>  #endif
> -
> +        } else printf("not subscribed... skipping xover\n");

Seems a reasonable approach, don't really open the folder if we're only
going to write to it.

FWIW we want to have camel-folders have more state, i.e. the object is
created, the folder it references isn't opened till you ask it to be.
But its not there yet.


camel_nntp_folder_new (or even camel_nntp_folder_construct)
also needs to setup the persistent metadata file on the folder object.

Call it ("%s.cmeta", nntp_folder->storage_path)

Set it and load it using:

camel_object_set(folder, NULL, CAMEL_OBJECT_STATE_FILE, path, NULL);
camel_object_state_read(folder);

And in the sync online/offline methods you need to call
camel_object_state_write(folder)


>  	return folder;
>  }
> --- /home/meilof/cvs/orig/evolution//camel/providers/nntp/camel-nntp-
provider.c	2002-07-04 19:15:16.000000000 -0400
> +++ camel/providers/nntp/camel-nntp-provider.c	2004-01-09
21:58:20.000000000 -0500
> @@ -36,6 +36,17 @@
>  static gint check_equal (char *s1, char *s2);
>  static gint nntp_url_equal (gconstpointer a, gconstpointer b);
>  
> +CamelProviderConfEntry nntp_conf_entries[] = {
> +	{ CAMEL_PROVIDER_CONF_SECTION_START, "folders", NULL,
> +	  N_("Folders") },
> +	{ CAMEL_PROVIDER_CONF_CHECKBOX, "show_short_notation", NULL,
> +	  N_("Show folders in short notation (e.g. c.o.linux rather than
comp.os.linux)"), "1" },
> +	{ CAMEL_PROVIDER_CONF_CHECKBOX, "folder_hierarchy_relative", NULL,
> +	  N_("In the subscription dialog, show relative folder names"),
"0" },

Please default this to on.  I don't think it should even be an option
personally, but it can stay.

> +	{ CAMEL_PROVIDER_CONF_SECTION_END },
> +	{ CAMEL_PROVIDER_CONF_END }
> +};
> +
>  static CamelProvider news_provider = {
>  	"nntp",
>  	N_("USENET news"),
> @@ -51,9 +62,21 @@
>  	CAMEL_URL_NEED_HOST | CAMEL_URL_ALLOW_USER |
>  	CAMEL_URL_ALLOW_PASSWORD | CAMEL_URL_ALLOW_AUTH,
>  
> +	nntp_conf_entries
> +
>  	/* ... */
>  };
>  
> +CamelServiceAuthType camel_nntp_password_authtype = {
> +	N_("Password"),
> +
> +	N_("This option will authenticate with the NNTP server using a "
> +	   "plaintext password."),
> +
> +	"",
> +	TRUE
> +};
> +
>  void
>  camel_provider_module_init (CamelSession *session)
>  {
> @@ -62,6 +85,7 @@
>  
>  	news_provider.url_hash = nntp_url_hash;
>  	news_provider.url_equal = nntp_url_equal;
> +	news_provider.authtypes = g_list_append (NULL,
&camel_nntp_password_authtype);
>  	
>  	camel_session_register_provider (session, &news_provider);
>  }
> @@ -107,7 +131,8 @@
>  {
>  	const CamelURL *u1 = a, *u2 = b;
>  	
> -	return check_equal (u1->user, u2->user)
> +	return check_equal(u1->protocol, u2->protocol)
> +		&& check_equal (u1->user, u2->user)
>  		&& check_equal (u1->host, u2->host)
>  		&& u1->port == u2->port;
>  }
> --- /home/meilof/cvs/orig/evolution//camel/providers/nntp/camel-nntp-
store.c	2003-09-22 11:00:59.000000000 -0400
> +++ camel/providers/nntp/camel-nntp-store.c	2004-01-10
16:24:58.000000000 -0500
> @@ -40,10 +40,15 @@
>  #include <camel/camel-tcp-stream-raw.h>
>  #include <camel/camel-tcp-stream-ssl.h>
>  
> +#include <camel/camel-disco-store.h>
> +#include <camel/camel-disco-diary.h>
> +
>  #include "camel-nntp-summary.h"
>  #include "camel-nntp-store.h"
> +#include "camel-nntp-store-summary.h"
>  #include "camel-nntp-folder.h"
>  #include "camel-nntp-private.h"
> +#include "camel-nntp-resp-codes.h"
>  
>  #define w(x)
>  extern int camel_verbose_debug;
> @@ -54,10 +59,7 @@
>  
>  #define DUMP_EXTENSIONS
>  
> -/* define if you want the subscribe ui to show folders in tree form
*/
> -/* #define INFO_AS_TREE */
> -
> -static CamelStoreClass *parent_class = NULL;
> +static CamelDiscoStoreClass *parent_class = NULL;
>  static CamelServiceClass *service_class = NULL;
>  
>  /* Returns the class for a CamelNNTPStore */
> @@ -65,6 +67,16 @@
>  #define CF_CLASS(so) CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
>  #define CNNTPF_CLASS(so) CAMEL_NNTP_FOLDER_CLASS
(CAMEL_OBJECT_GET_CLASS(so))
>  
> +static void nntp_construct (CamelService *service, CamelSession
*session,
> +		            CamelProvider *provider, CamelURL *url,
> +		            CamelException *ex);
> +
> +
> +static gboolean
> +nntp_can_work_offline(CamelDiscoStore *store)
> +{
> +	return TRUE;
> +}
>  
>  enum {
>  	USE_SSL_NEVER,
> @@ -76,25 +88,23 @@
>  connect_to_server (CamelService *service, int ssl_mode,
CamelException *ex)
>  {
>  	CamelNNTPStore *store = (CamelNNTPStore *) service;
> +	CamelDiscoStore *disco_store = (CamelDiscoStore*) service;
>  	CamelStream *tcp_stream;
>  	gboolean retval = FALSE;
>  	unsigned char *buf;
>  	unsigned int len;
>  	struct hostent *h;
>  	int port, ret;
> +	char *path;
>  	
>  	CAMEL_NNTP_STORE_LOCK(store, command_lock);
> -		
> +
>  	/* setup store-wide cache */
>  	if (store->cache == NULL) {
> -		char *root;
> -		
> -		root = camel_session_get_storage_path (service->session, service,
ex);
> -		if (root == NULL)
> +		if (store->storage_path == NULL)
>  			goto fail;
>  		
> -		store->cache = camel_data_cache_new (root, 0, ex);
> -		g_free (root);
> +		store->cache = camel_data_cache_new (store->storage_path, 0, ex);
>  		if (store->cache == NULL)
>  			goto fail;
>  		
> @@ -169,11 +179,17 @@
>  	
>  	/* set 'reader' mode & ignore return code */
>  	camel_nntp_command (store, (char **) &buf, "mode reader");
> +	/* hack: inn seems to close connections if nothing is done within
> +	   the first ten seconds. a non-existent command is enough though */
> +	camel_nntp_command (store, (char **) &buf, "inn-do-not-timeout");

So why couldn't you call a real command here, like date?

FWIW this should still check the return value, there's no guarantee on
how long it will take to execute the two above lines.

>  	retval = TRUE;
>  	
> +	path = g_strdup_printf ("%s/journal", store->storage_path);
> +	disco_store->diary = camel_disco_diary_new (disco_store, path, ex);
> +	g_free (path);	
> +	

^^ Call this .ev-journal (i know imap uses journal, but its something
that'll be cleaned up eventually).

>   fail:
>  	CAMEL_NNTP_STORE_UNLOCK(store, command_lock);
> -	
>  	return retval;
>  }
>  
> @@ -189,7 +205,7 @@
>  };
>  
>  static gboolean
> -nntp_connect (CamelService *service, CamelException *ex)
> +nntp_connect_online (CamelService *service, CamelException *ex)
>  {
>  #ifdef HAVE_SSL
>  	const char *use_ssl;
> @@ -230,7 +246,38 @@
>  }
>  
>  static gboolean
> -nntp_disconnect (CamelService *service, gboolean clean,
CamelException *ex)
> +nntp_connect_offline(CamelService *service, CamelException *ex)
> +{
> +	CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE(service);
> +	CamelDiscoStore *disco_store = (CamelDiscoStore*) nntp_store;
> +	char *path;
> +
> +	if (nntp_store->storage_path == NULL)
> +		return FALSE;
> +
> +	/* setup store-wide cache */
> +	if (nntp_store->cache == NULL) {
> +		nntp_store->cache = camel_data_cache_new (nntp_store->storage_path,
0, ex);
> +		if (nntp_store->cache == NULL)
> +			return FALSE;

It should probably still be able to operate with no cache.

> +		/* Default cache expiry - 2 weeks old, or not visited in 5 days */
> +		camel_data_cache_set_expire_age (nntp_store->cache, 60*60*24*14);
> +		camel_data_cache_set_expire_access (nntp_store->cache, 60*60*24*5);
> +	}	
> +	
> +	path = g_strdup_printf ("%s/journal", nntp_store->storage_path);

should really use g_build_filename for filenames.

> +	disco_store->diary = camel_disco_diary_new (disco_store, path, ex);
> +	g_free (path);
> +	
> +	if (!disco_store->diary)
> +		return FALSE;
> +
> +	return TRUE;
> +}
> +
> +static gboolean
> +nntp_disconnect_online (CamelService *service, gboolean clean,
CamelException *ex)
>  {
>  	CamelNNTPStore *store = CAMEL_NNTP_STORE (service);
>  	char *line;
> @@ -240,8 +287,10 @@
>  	if (clean)
>  		camel_nntp_command (store, &line, "quit");
>  
> -	if (!service_class->disconnect (service, clean, ex))
> +	if (!service_class->disconnect (service, clean, ex)) {
> +		CAMEL_NNTP_STORE_UNLOCK(store, command_lock);	
>  		return FALSE;
> +	}
>  
>  	camel_object_unref((CamelObject *)store->stream);
>  	store->stream = NULL;
> @@ -251,6 +300,22 @@
>  	return TRUE;
>  }
>  
> +static gboolean
> +nntp_disconnect_offline (CamelService *service, gboolean clean,
CamelException *ex)
> +{
> +	CamelDiscoStore *disco = CAMEL_DISCO_STORE(service);
> +	
> +	if (!service_class->disconnect (service, clean, ex))
> +		return FALSE;
> +
> +	if (disco->diary) {
> +		camel_object_unref (CAMEL_OBJECT (disco->diary));
> +		disco->diary = NULL;
> +	}
> +
> +	return TRUE;
> +}
> +
>  static char *
>  nntp_store_get_name (CamelService *service, gboolean brief)
>  {
> @@ -261,26 +326,17 @@
>  	
>  }
>  
> -static CamelServiceAuthType password_authtype = {
> -	N_("Password"),
> -	
> -	N_("This option will authenticate with the NNTP server using a "
> -	   "plaintext password."),
> -	
> -	"",
> -	TRUE
> -};
> +extern CamelServiceAuthType camel_nntp_password_authtype;
>  
>  static GList *
>  nntp_store_query_auth_types (CamelService *service, CamelException
*ex)
>  {
> -	g_warning ("nntp::query_auth_types: not implemented. Defaulting.");
> -	
> -	return g_list_append (NULL, &password_authtype);
> +	return g_list_append (NULL, &camel_nntp_password_authtype);
> +	return NULL;
>  }
>  
>  static CamelFolder *
> -nntp_store_get_folder(CamelStore *store, const char *folder_name,
guint32 flags, CamelException *ex)
> +nntp_get_folder(CamelStore *store, const char *folder_name, guint32
flags, CamelException *ex)
>  {
>  	CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (store);
>  	CamelFolder *folder;
> @@ -294,111 +350,485 @@
>  	return folder;
>  }
>  
> +/*
> + * Converts a fully-fledged newsgroup name to a name in short dotted
notation,
> + * e.g. nl.comp.os.linux.programmeren becomes n.c.o.l.programmeren
> + */
> +
> +static char *
> +nntp_newsgroup_name_short(char *name)
> +{
> +	char *tmp = g_malloc0(strlen(name) + 1), *resptr = tmp, *ptr2;
> +	
> +	while ((ptr2 = strchr(name, '.'))) {
> +		if (ptr2 == name) {
> +			name++;
> +			continue;
> +		}
> +		
> +		*resptr++ = *name;
> +		*resptr++ = '.';
> +		name = ptr2 + 1;
> +	}
> +	
> +	strcpy(resptr, name);
> +	return tmp;
> +}
> +
> +/*
> + * This function converts a NNTPStoreSummary item to a FolderInfo
item that
> + * can be returned by the get_folders() call to the store. Both
structs have
> + * essentially the same fields.
> + */
> +
>  static CamelFolderInfo *
> -nntp_store_get_folder_info(CamelStore *store, const char *top,
guint32 flags, CamelException *ex)
> +nntp_folder_info_from_store_info(gboolean short_notation, CamelURL
*base_url, CamelStoreInfo *si)
>  {

Should have storeinfo as the first argument, followed by base_url and
short_notation.

You should probably just pass in the store as the first arg actually,
with no url and no short_notation, since you can get them both from
the store pointer.

> +static CamelFolderInfo *
> +nntp_folder_info_from_name(gboolean short_notation, CamelURL
*base_url, char *name) {

Same goes for this function.

> +static CamelStoreInfo *
> +nntp_store_info_from_line(CamelStoreSummary *summ, CamelURL
*base_url, char *line)
> +{

Similarly here, pass in the nntp store.

> +static CamelFolderInfo *
> +nntp_store_get_subscribed_folder_info(CamelNNTPStore *store, const
char *top, guint flags, CamelException *ex)
> +{
> +	CamelURL *url = CAMEL_SERVICE (store)->url;
> +	int i;
> +	CamelStoreInfo *si;
> +	CamelFolderInfo *first = NULL, *last = NULL, *fi = NULL;
> +
> +	/* since we do not do a tree, any request that is not for root is
sure to give no results */
> +	if (top != NULL && top[0] != 0) return NULL;

You should actually be able to do get_folder_info("news.group") and get
back 'news.group' (if it exists).

> +/*
> + * get folder info, using the information in our StoreSummary
> + */
> +static CamelFolderInfo *
> +nntp_store_get_cached_folder_info(CamelNNTPStore *store, const char
*orig_top, guint flags, CamelException *ex)
> +{
> +	CamelURL *url = CAMEL_SERVICE (store)->url;
> +	int len, i;
> +	int subscribed_or_flag = (flags &
CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) ? 0 : 1,
> +	    root_or_flag = (orig_top == NULL || orig_top[0] == '\0') ? 1 :
0,
> +	    recursive_flag = flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE;
> +	CamelStoreInfo *si;
> +	CamelFolderInfo *first = NULL, *last = NULL, *fi = NULL;
> +	char *tmpname;
> +	char *top = g_strconcat(orig_top?orig_top:"", ".", NULL);
> +	int toplen = strlen(top);
> +	
> +	for (i=0;(si = camel_store_summary_index((CamelStoreSummary *)store-
>summary, i));i++) {
> +		if ( (subscribed_or_flag || (si->flags &
CAMEL_STORE_INFO_FOLDER_SUBSCRIBED)) &&
> +		     (root_or_flag || strncasecmp(si->path, top, toplen) == 0)) {

use g_ascii_strncasecmp.

> +			if (recursive_flag ||                         /* we need to add
all subitems */
> +			    strchr(si->path + toplen, '.') == NULL) { /* a direct subitem
*/				                        
> +				/* add the item */
> +				fi = nntp_folder_info_from_store_info(FALSE, url, si);
> +				if (!fi)
> +					continue;
> +				if (store->folder_hierarchy_relative) {
> +					g_free(fi->name);
> +					fi->name = g_strdup(si->path + ((toplen==1)?0:toplen));

use NULL rather than 0 for NULL (its *only* for readability).

> +				}
> +			} else {
> +				/* apparently, this is an indirect subitem. if it's not a subitem
of
> +				   the item we added last, we need to add a portion of this item
to
> +				   the list as a placeholder */
> +				if (!last ||
> +				    strncasecmp(si->path, last->full_name, (len = strlen(last-
>full_name))) ||

g_strncasecmp.

> +				    si->path[len] != '.') {

Also follow the GNU style for multi-line boolean expressions:

if (!last
    || g_strncasecmp(s->path, last->full_name, ...
    || si->path[len] != '.')

etc.

Since len is only used in that else block you should probably make it
local to the block that includes it, and calculate it separately to
the if expression.

> +					tmpname = g_strdup(si->path);
> +					*(strchr(tmpname + toplen, '.')) = '\0';
> +					fi = nntp_folder_info_from_name(FALSE, url, tmpname);
> +					fi->flags |= CAMEL_FOLDER_NOSELECT;
> +					if (store->folder_hierarchy_relative) {
> +						g_free(fi->name);
> +						fi->name = g_strdup(tmpname + ((toplen==1)?0:toplen));
> +					}
> +					g_free(tmpname);
> +				} else {
> +					continue;
> +				}
> +			}
>  			if (last)
>  				last->sibling = fi;
>  			else
> -				groups = fi;
> +				first = fi;
>  			last = fi;
> +		} else if (subscribed_or_flag && first) {
> +			/* we have already added subitems, but this item is no longer a
subitem */
> +			camel_store_summary_info_free((CamelStoreSummary *)store->summary,
si);
> +			break;
>  		}
> +		camel_store_summary_info_free((CamelStoreSummary *)store->summary,
si);
>  	}
>  
> -	if (ret < 0)
> -		goto error;
> +	g_free(top);
> +	return first;
> +}
>  
> -	CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock);
> +/* retrieves the date from the NNTP server */
> +static gboolean
> +nntp_get_date(CamelNNTPStore *nntp_store)
> +{
> +	unsigned char *line;
> +	int ret = camel_nntp_command(nntp_store, (char **)&line, "date");
> +	char *ptr;
> +	
> +	nntp_store->summary->last_newslist[0] = 0;
> +
> +	if (ret == 111) {
> +		ptr = line + 3;
> +		while (*ptr == ' ' || *ptr == '\t')
> +			ptr++;
> +		
> +		if (strlen(ptr) == NNTP_DATE_SIZE) {
> +			memcpy(nntp_store->summary->last_newslist, ptr, NNTP_DATE_SIZE);
> +			return TRUE;
> +		}
> +	}
> +	return FALSE;
> +}
>  
> -	return groups;
> +static gint
> +store_info_sort(gconstpointer a, gconstpointer b)
> +{
> +	return strcmp(
> +	  (*(CamelNNTPStoreInfo**)a)->full_name,
> +	  (*(CamelNNTPStoreInfo**)b)->full_name);
> +}
> +
> +static CamelFolderInfo *
> +nntp_store_get_folder_info_all(CamelNNTPStore *nntp_store, const char
*top, guint32 flags, gboolean online, CamelException *ex)
> +{
> +	CamelURL *url = CAMEL_SERVICE (nntp_store)->url;
> +	CamelNNTPStoreSummary *summary = nntp_store->summary;
> +	CamelStoreInfo *si;
> +	unsigned int len;
> +	unsigned char *line, *space;
> +	int ret = -1;
> +	if (top == NULL) top = "";
> +
> +	if (online && (top == NULL || top[0] == 0)) {
> +		/* we may need to update */
> +		if (summary->last_newslist[0] != 0) {
> +			char date[14];

add a blank line after declarations.
and a newline after an if expression or open brace, always.

> +			memcpy(date, summary->last_newslist + 2, 6); /* YYMMDDD */
> +			date[6] = ' ';
> +			memcpy(date + 7, summary->last_newslist + 8, 6); /* HHMMSS */
> +			date[13] = '\0';
> +
> +			nntp_get_date(nntp_store);
> +
> +			ret = camel_nntp_command(nntp_store, (char **)&line, "newgroups %
s", date);
> +			if (ret != 231) {
> +				/* newgroups not supported :S so reload the complete list */
> +				ret = -1;
> +				camel_store_summary_clear((CamelStoreSummary*)summary);
> +				summary->last_newslist[0] = 0;
> +				goto do_complete_list;
> +			}
> +        
> +			while ( (ret = camel_nntp_stream_line(nntp_store->stream, &line,
&len)) > 0) {
> +				space = strchr(line, ' ');
> +				if (space)
> +					*space = 0;
> +				si = camel_store_summary_path((CamelStoreSummary *)nntp_store-
>summary, line);
> +				if (si) {
> +					camel_store_summary_info_free((CamelStoreSummary *)nntp_store-
>summary, si);
> +				} else {
> +					si = nntp_store_info_from_line(CAMEL_STORE_SUMMARY(nntp_store-
>summary), url, line);
> +					camel_store_summary_add((CamelStoreSummary*)nntp_store->summary,
si);
> +				}
> +			}
> +
> +			/* sort the list */
> +			/* TODO: insertion sort is probably faster... */
> +			g_ptr_array_sort(CAMEL_STORE_SUMMARY(nntp_store->summary)-
>folders, store_info_sort);

Its not.  qsort (which is all g_ptr_array_sort calls), is faster (in
glibc it's a merge sort, so it is also stable and reliably worst-case
faster).

> +		} else {
> +do_complete_list:
> +			/* seems we do need a complete list */
> +			/* at first, we do a DATE to find out the last load occasion */
> +			nntp_get_date(nntp_store);
> +
> +			ret = camel_nntp_command(nntp_store, (char **)&line, "list");
> +			if (ret != 215) {
> +				if (ret < 0) line = "Stream error";
> +				ret = -1;
> +				camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_INVALID,
> +						      _("Error retrieving newsgroups:\n\n%s"), line);
> +				goto error;
> +			}
>  
> +			while ( (ret = camel_nntp_stream_line(nntp_store->stream, &line,
&len)) > 0) {
> +				space = strchr(line, ' ');
> +				if (space)
> +					*space = 0;
> +				si = nntp_store_info_from_line(CAMEL_STORE_SUMMARY(nntp_store-
>summary), url, line);
> +				camel_store_summary_add((CamelStoreSummary*)nntp_store->summary,
si);
> +				/* check to see if it answers our current query */
> +			}
> +
> +			g_ptr_array_sort(CAMEL_STORE_SUMMARY(nntp_store->summary)-
>folders, store_info_sort);

Since you call this twice, you should just put this outside the if/else
block.

> +			if (ret < 0) goto error;

newline!

> +		}
> +
> +		camel_store_summary_save((CamelStoreSummary *)nntp_store->summary);
> +	}
> +	return nntp_store_get_cached_folder_info(nntp_store, top, flags,
ex);
>  error:
> +	return NULL;
> +}
> +
> +static CamelFolderInfo *
> +nntp_get_folder_info(CamelStore *store, const char *top, guint32
flags, gboolean online, CamelException *ex)
> +{
> +	CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE(store);
> +
> +	dd(printf("g_f_i: fast %d subscr %d recursive %d online %d top \"%s
\"\n",
> +		flags & CAMEL_STORE_FOLDER_INFO_FAST,
> +		flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED,
> +		flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE,
> +		online,
> +		top?top:""));
> +
> +	CAMEL_NNTP_STORE_LOCK(nntp_store, command_lock);
> +
> +	CamelFolderInfo *first = NULL;
> +
> +	if (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED)
> +		first = nntp_store_get_subscribed_folder_info(nntp_store, top,
flags, ex);
> +	else
> +		first = nntp_store_get_folder_info_all(nntp_store, top, flags,
online, ex);
> +
>  	CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock);
> +	return first;
> +}
>  
> -	if (groups)
> -		camel_store_free_folder_info(store, groups);
> +static CamelFolderInfo *
> +nntp_get_folder_info_online(CamelStore *store, const char *top,
guint32 flags, CamelException *ex)
> +{
> +	return nntp_get_folder_info(store, top, flags, TRUE, ex);
> +}
>  
> -	return NULL;
> +static CamelFolderInfo *
> +nntp_get_folder_info_offline(CamelStore *store, const char *top,
guint32 flags, CamelException *ex)
> +{
> +	return nntp_get_folder_info(store, top, flags, FALSE, ex);
>  }
>  
>  static gboolean
>  nntp_store_folder_subscribed (CamelStore *store, const char
*folder_name)
>  {
>  	CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (store);
> +	CamelStoreInfo *si;
>  
> -	nntp_store = nntp_store;
> +	int truth = FALSE;
>  
> -	/* FIXME: implement */
> +	si = camel_store_summary_path((CamelStoreSummary *)nntp_store-
>summary, folder_name);
> +	if (si) {
> +		truth = (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) != 0;
> +		camel_store_summary_info_free((CamelStoreSummary *)nntp_store-
>summary, si);
> +	}
>  
> -	return TRUE;
> +	return truth;
>  }
>  
>  static void
>  nntp_store_subscribe_folder (CamelStore *store, const char
*folder_name,
>  			     CamelException *ex)
>  {
> -	CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (store);
> +	CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE(store);
> +	CamelStoreInfo *fitem;

for consistency this should probably be 'si'.

> +	CamelFolderInfo *fi;
> +
> +	CAMEL_NNTP_STORE_LOCK(nntp_store, command_lock);
>  
> -	nntp_store = nntp_store;
> +	fitem = camel_store_summary_path(CAMEL_STORE_SUMMARY(nntp_store-
>summary), folder_name);
> +	if (!fitem) {
> +		camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID,
> +				      _("You cannot subscribe to this newsgroup:\n\n"
> +				        "No such newsgroup. The selected item is a probably a
parent folder."));
> +	} else {
> +		if (!(fitem->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED)) {
> +			fitem->flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
> +			fi = nntp_folder_info_from_store_info(nntp_store-
>do_short_folder_notation, CAMEL_SERVICE(store)->url, fitem);
> +			fi->flags |= CAMEL_FOLDER_NOINFERIORS | CAMEL_FOLDER_NOCHILDREN;
> +			camel_object_trigger_event ((CamelObject*)nntp_store,
"folder_subscribed", fi);

you should probably trigger the event outside the command lock.

> +			camel_folder_info_free (fi);
> +			camel_store_summary_touch((CamelStoreSummary*)nntp_store-
>summary);
> +			camel_store_summary_save((CamelStoreSummary*)nntp_store->summary);
> +		}
> +	}
>  
> -	/* FIXME: implement */
> +	CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock);
>  }
>  
>  static void
>  nntp_store_unsubscribe_folder (CamelStore *store, const char
*folder_name,
>  			       CamelException *ex)
>  {
> -	CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (store);
> +	CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE(store);
> +	CamelFolderInfo *fi;
> +	CamelStoreInfo *fitem;
> +	CAMEL_NNTP_STORE_LOCK(nntp_store, command_lock);
> +
> +	fitem = camel_store_summary_path(CAMEL_STORE_SUMMARY(nntp_store-
>summary), folder_name);
> +	
> +	if (!fitem) {
> +		camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID,
> +				      _("You cannot unsubscribe to this newsgroup:\n\n"
> +				        "newsgroup does not exist!"));
> +	} else {
> +		if (fitem->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) {
> +			fitem->flags &= ~CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
> +			fi = nntp_folder_info_from_store_info(nntp_store-
>do_short_folder_notation, CAMEL_SERVICE(store)->url, fitem);
> +			camel_object_trigger_event (CAMEL_OBJECT (nntp_store),
"folder_unsubscribed", fi);
> +			camel_folder_info_free (fi);
> +			camel_store_summary_touch(CAMEL_STORE_SUMMARY(nntp_store-
>summary));
> +			camel_store_summary_save(CAMEL_STORE_SUMMARY(nntp_store-
>summary));
> +		}
> +	}
> +
> +	CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock);
> +}
> +
> +/* stubs for various folder operations we're not implementing */
>  
> -	nntp_store = nntp_store;
> +static CamelFolderInfo *
> +nntp_create_folder (CamelStore *store, const char *parent_name,
> +                    const char *folder_name, CamelException *ex)
> +{
> +	camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID,
> +	            _("You cannot create a folder in a News store.\n\n"
> +	              "To subscribe to a folder, use the Subscribe option
from the Tools menu."));
> +	return NULL;
> +}
> +
> +static void
> +nntp_rename_folder (CamelStore *store, const char *old_name, const
char *new_name_in, CamelException *ex)
> +{
> +	camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID,
> +	          _("You cannot rename a folder in a News store."));
> +}
>  
> -	/* FIXME: implement */
> +static void
> +nntp_delete_folder (CamelStore *store, const char *folder_name,
CamelException *ex)
> +{
> +	nntp_store_subscribe_folder(store, folder_name, ex);
> +	camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID,
> +	          _("You cannot remove a folder in a News store.\n\n"
> +	            "To remove a folder, use the Subscribe option from the
Tools menu."));

These secondary hints are assuming you're running this from evolution,
which may not always be the case (camel may one day be a separate
library).  Just the first line should be enough.

> +	return;
>  }
>  
>  static void
> -nntp_store_finalise (CamelObject *object)
> +nntp_store_finalize (CamelObject *object)
>  {
> +	/* call base finalize */
>  	CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (object);
>  	struct _CamelNNTPStorePrivate *p = nntp_store->priv;
>  
>  	camel_service_disconnect((CamelService *)object, TRUE, NULL);
>  
> +	if (nntp_store->summary) {
> +		camel_store_summary_save((CamelStoreSummary *)nntp_store->summary);
> +		camel_object_unref(nntp_store->summary);
> +	}	
> +
>  	camel_object_unref((CamelObject *)nntp_store->mem);
>  	nntp_store->mem = NULL;
>  	if (nntp_store->stream)
>  		camel_object_unref((CamelObject *)nntp_store->stream);
>  	
> +	if (nntp_store->base_url)
> +		g_free (nntp_store->base_url);
> +	if (nntp_store->storage_path)
> +		g_free (nntp_store->storage_path);
> +	
>  	e_mutex_destroy(p->command_lock);
>  	
>  	g_free(p);
> @@ -407,28 +837,87 @@
>  static void
>  nntp_store_class_init (CamelNNTPStoreClass *camel_nntp_store_class)
>  {
> +	CamelDiscoStoreClass *camel_disco_store_class =
CAMEL_DISCO_STORE_CLASS (camel_nntp_store_class);
>  	CamelStoreClass *camel_store_class = CAMEL_STORE_CLASS
(camel_nntp_store_class);
>  	CamelServiceClass *camel_service_class = CAMEL_SERVICE_CLASS
(camel_nntp_store_class);
>  
> -	parent_class = CAMEL_STORE_CLASS (camel_type_get_global_classfuncs
(camel_store_get_type ()));
> -
> +	parent_class = CAMEL_DISCO_STORE_CLASS
(camel_type_get_global_classfuncs (camel_disco_store_get_type ()));
>  	service_class = CAMEL_SERVICE_CLASS
(camel_type_get_global_classfuncs (camel_service_get_type ()));
>  	
>  	/* virtual method overload */
> -	camel_service_class->connect = nntp_connect;
> -	camel_service_class->disconnect = nntp_disconnect;
> +	camel_service_class->construct = nntp_construct;
>  	camel_service_class->query_auth_types = nntp_store_query_auth_types;
>  	camel_service_class->get_name = nntp_store_get_name;
>  	
> -	camel_store_class->get_folder = nntp_store_get_folder;
> -	camel_store_class->get_folder_info = nntp_store_get_folder_info;
> +	camel_disco_store_class->can_work_offline = nntp_can_work_offline;
> +	camel_disco_store_class->connect_online = nntp_connect_online;
> +	camel_disco_store_class->connect_offline = nntp_connect_offline;
> +	camel_disco_store_class->disconnect_online = nntp_disconnect_online;
> +	camel_disco_store_class->disconnect_offline =
nntp_disconnect_offline;
> +	camel_disco_store_class->get_folder_online = nntp_get_folder;
> +	camel_disco_store_class->get_folder_resyncing = nntp_get_folder;
> +	camel_disco_store_class->get_folder_offline = nntp_get_folder;
> +	
> +	camel_disco_store_class->get_folder_info_online =
nntp_get_folder_info_online;
> +	camel_disco_store_class->get_folder_info_resyncing =
nntp_get_folder_info_online;
> +	camel_disco_store_class->get_folder_info_offline =
nntp_get_folder_info_offline;
> +	
>  	camel_store_class->free_folder_info =
camel_store_free_folder_info_full;
>  	
>  	camel_store_class->folder_subscribed = nntp_store_folder_subscribed;
>  	camel_store_class->subscribe_folder = nntp_store_subscribe_folder;
>  	camel_store_class->unsubscribe_folder =
nntp_store_unsubscribe_folder;
> +
> +	camel_store_class->create_folder = nntp_create_folder;
> +	camel_store_class->delete_folder = nntp_delete_folder;
> +	camel_store_class->rename_folder = nntp_rename_folder;
> +}
> +
> +/* construction function in which we set some basic store properties
*/
> +static void nntp_construct (CamelService *service, CamelSession
*session,
> +		            CamelProvider *provider, CamelURL *url,
> +		            CamelException *ex) {
> +	CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE(service);
> +	CamelURL *summary_url;
> +	char *tmp;
> +
> +	/* construct the parent first */
> +	CAMEL_SERVICE_CLASS (parent_class)->construct (service, session,
provider, url, ex);
> +	if (camel_exception_is_set (ex))
> +		return;
> +
> +	/* find out the storage path, base url */
> +	nntp_store->storage_path = camel_session_get_storage_path (session,
service, ex);
> +	if (!nntp_store->storage_path)
> +		return;
> +
> +	/* FIXME */
> +	nntp_store->base_url = camel_url_to_string (service->url,
(CAMEL_URL_HIDE_PASSWORD |
> +								   CAMEL_URL_HIDE_PARAMS |
> +								   CAMEL_URL_HIDE_AUTH));
> +
> +	tmp = alloca(strlen(nntp_store->storage_path)+32);
> +	sprintf(tmp, "%s/.ev-store-summary", nntp_store->storage_path);
> +	nntp_store->summary = camel_nntp_store_summary_new();
> +	camel_store_summary_set_filename((CamelStoreSummary *)nntp_store-
>summary, tmp);	
> +	summary_url = camel_url_new(nntp_store->base_url, NULL);
> +	camel_store_summary_set_uri_base((CamelStoreSummary *)nntp_store-
>summary, summary_url);
> +	camel_url_free(summary_url);
> +	if (camel_store_summary_load((CamelStoreSummary *)nntp_store-
>summary) == 0);
> +
> +	/* get options */
> +	if (camel_url_get_param (url, "show_short_notation"))
> +		nntp_store->do_short_folder_notation = TRUE;
> +	else
> +		nntp_store->do_short_folder_notation = FALSE;
> +	if (camel_url_get_param (url, "folder_hierarchy_relative"))
> +		nntp_store->folder_hierarchy_relative = TRUE;
> +	else
> +		nntp_store->folder_hierarchy_relative = FALSE;
>  }
>  
> +
>  static void
>  nntp_store_init (gpointer object, gpointer klass)
>  {
> @@ -451,14 +940,14 @@
>  	
>  	if (camel_nntp_store_type == CAMEL_INVALID_TYPE) {
>  		camel_nntp_store_type =
> -			camel_type_register (CAMEL_STORE_TYPE,
> +			camel_type_register (CAMEL_DISCO_STORE_TYPE,
>  					     "CamelNNTPStore",
>  					     sizeof (CamelNNTPStore),
>  					     sizeof (CamelNNTPStoreClass),
>  					     (CamelObjectClassInitFunc) nntp_store_class_init,
>  					     NULL,
>  					     (CamelObjectInitFunc) nntp_store_init,
> -					     (CamelObjectFinalizeFunc) nntp_store_finalise);
> +					     (CamelObjectFinalizeFunc) nntp_store_finalize);
>  	}
>  	
>  	return camel_nntp_store_type;
> @@ -483,10 +972,54 @@
>  }
>  
>  static gboolean
> +camel_nntp_try_authenticate(CamelNNTPStore *store) {
> +	CamelService *service = (CamelService *) store;
> +	CamelSession *session = camel_service_get_session (service);
> +	CamelException *ex;
> +	int ret;
> +	char *line;
> +
> +	if (!service->url->user) return FALSE;
> +
> +	/* if nessecary, prompt for the password */
> +	if (!service->url->passwd) {
> +		char *prompt;
> +		prompt = g_strdup_printf (_("Please enter the NNTP password for %s@
%s"),
> +					  service->url->user,
> +					  service->url->host);
> +					  
> +		ex =  camel_exception_new();
> +		service->url->passwd =
> +			camel_session_get_password (session, prompt, FALSE, TRUE,
> +						    service, "password", ex);
> +		camel_exception_free(ex);
> +		g_free (prompt);
> +
> +		if (!service->url->passwd) {
> +			return FALSE;
> +		}
> +	}
> +
> +	/* now, send auth info (currently, only authinfo user/pass is
supported) */
> +	ret = camel_nntp_command(store, &line, "authinfo user %s", service-
>url->user);
> +	if (ret == NNTP_AUTH_ACCEPTED)
> +		return TRUE;
> +	else if (ret == NNTP_AUTH_CONTINUE) {
> +		ret = camel_nntp_command(store, &line, "authinfo pass %s", service-
>url->passwd);
> +		if (ret == NNTP_AUTH_ACCEPTED)
> +			return TRUE;
> +		else
> +			return FALSE;
> +	} else
> +		return FALSE;
> +}
> +
> +static gboolean
>  nntp_connected (CamelNNTPStore *store, CamelException *ex)
>  {
>  	if (store->stream == NULL)
>  		return camel_service_connect (CAMEL_SERVICE (store), ex);
> +
>  	return TRUE;
>  }
>  
> @@ -505,7 +1038,7 @@
>  
>  	if (!nntp_connected (store, NULL))
>  		return -1;
> -	
> +
>  	/* Check for unprocessed data, ! */
>  	if (store->stream->mode == CAMEL_NNTP_STREAM_DATA) {
>  		g_warning("Unprocessed data left in stream, flushing");
> @@ -514,6 +1047,7 @@
>  	}
>  	camel_nntp_stream_set_mode(store->stream, CAMEL_NNTP_STREAM_LINE);
>  
> +command_begin_send:
>  	va_start(ap, fmt);
>  	ps = p = fmt;
>  	while ( (c = *p++) ) {
> @@ -557,7 +1091,22 @@
>  	camel_stream_write((CamelStream *)store->mem, ps, p-ps-1);
>  	dd(printf("NNTP_COMMAND: '%.*s'\n", (int)store->mem->buffer->len,
store->mem->buffer->data));
>  	camel_stream_write((CamelStream *)store->mem, "\r\n", 2);
> -	camel_stream_write((CamelStream *)store->stream, store->mem->buffer-
>data, store->mem->buffer->len);
> +
> +	if (camel_stream_write((CamelStream *)store->stream, store->mem-
>buffer->data, store->mem->buffer->len) == -1 && errno != EINTR) {
> +		camel_stream_reset((CamelStream *)store->mem);

> +		/* FIXME: hack */
> +		g_byte_array_set_size(store->mem->buffer, 0);
> +

instead of doing this, command_begin_send could be just beofre the if
(camel_stream_write()) call, 2 lines above.

Actually the whole lot should just be a while () loop with no
command_begin_send, since the content of the command string (the mem
stream) will not change.

The array_set_size call is required if you exit though.  Or better,
move camel_stream_reset(mem); and g_byte_array_set_size(store->mem, 0)
to where you currently have command_begin_send label, so its always
reset before a new command is created.

> +reconnect:
> +		/* some error, re-connect */
> +		camel_service_disconnect (CAMEL_SERVICE (store), FALSE, NULL);
> +
> +		if (!nntp_connected(store, NULL))
> +			return -1;
> +
> +		goto command_begin_send;
> +	}
> +
>  	camel_stream_reset((CamelStream *)store->mem);
>  	/* FIXME: hack */
>  	g_byte_array_set_size(store->mem->buffer, 0);

^^ i.e. move this one, remove the other one.

> @@ -567,6 +1116,15 @@
>  
>  	u = strtoul(*line, NULL, 10);
>  
> +	/* Check for 'authentication required' codes */
> +	if (u == NNTP_AUTH_REQUIRED &&
> +	    camel_nntp_try_authenticate(store))
> +		goto command_begin_send;
> +
> +	/* the server doesn't like us anymore, but we still like her! */
> +	if (u == 401 || u == 503)
> +		goto reconnect;
> +
>  	/* Handle all switching to data mode here, to make callers job
easier */
>  	if (u == 215 || (u >= 220 && u <=224) || (u >= 230 && u <= 231))
>  		camel_nntp_stream_set_mode(store->stream, CAMEL_NNTP_STREAM_DATA);
> --- /home/meilof/cvs/orig/evolution//camel/providers/nntp/camel-nntp-
store-summary.c	1969-12-31 19:00:00.000000000 -0500
> +++ camel/providers/nntp/camel-nntp-store-summary.c	2004-01-09
21:58:20.000000000 -0500
> @@ -0,0 +1,431 @@
> +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8
-*- */
> +/*
> + * Copyright (C) 2002 Ximian Inc.
> + *
> + * Authors: Michael Zucchi <notzed 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.
> + */
> +
> +/* currently, this is just a straigt s/imap/nntp from the IMAP
file*/ 
> +
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include <unistd.h>
> +#include <ctype.h>
> +#include <string.h>
> +#include <errno.h>
> +#include <stdlib.h>
> +
> +#include "camel-nntp-store-summary.h"
> +
> +#include "camel-file-utils.h"
> +
> +#include "e-util/md5-utils.h"
> +#include "e-util/e-memory.h"
> +
> +#include "camel-private.h"
> +#include "camel-utf8.h"
> +
> +#define d(x)
> +#define io(x)			/* io debug */
> +
> +#define CAMEL_NNTP_STORE_SUMMARY_VERSION_0 (0)
> +
> +#define CAMEL_NNTP_STORE_SUMMARY_VERSION (0)
> +
> +#define _PRIVATE(o) (((CamelNNTPStoreSummary *)(o))->priv)
> +
> +static int summary_header_load(CamelStoreSummary *, FILE *);
> +static int summary_header_save(CamelStoreSummary *, FILE *);
> +
> +/*static CamelStoreInfo * store_info_new(CamelStoreSummary *, const
char *);*/
> +static CamelStoreInfo * store_info_load(CamelStoreSummary *, FILE *);
> +static int		 store_info_save(CamelStoreSummary *, FILE *,
CamelStoreInfo *);
> +static void		 store_info_free(CamelStoreSummary *, CamelStoreInfo *);
> +
> +static const char *store_info_string(CamelStoreSummary *, const
CamelStoreInfo *, int);
> +static void store_info_set_string(CamelStoreSummary *, CamelStoreInfo
*, int, const char *);
> +
> +static void camel_nntp_store_summary_class_init
(CamelNNTPStoreSummaryClass *klass);
> +static void camel_nntp_store_summary_init
(CamelNNTPStoreSummary *obj);
> +static void camel_nntp_store_summary_finalise   (CamelObject *obj);
> +
> +static CamelStoreSummaryClass *camel_nntp_store_summary_parent;
> +
> +static void
> +camel_nntp_store_summary_class_init (CamelNNTPStoreSummaryClass
*klass)
> +{
> +	CamelStoreSummaryClass *ssklass = (CamelStoreSummaryClass *)klass;
> +
> +	ssklass->summary_header_load = summary_header_load;
> +	ssklass->summary_header_save = summary_header_save;
> +
> +	/*ssklass->store_info_new  = store_info_new;*/
> +	ssklass->store_info_load = store_info_load;
> +	ssklass->store_info_save = store_info_save;
> +	ssklass->store_info_free = store_info_free;
> +
> +	ssklass->store_info_string = store_info_string;
> +	ssklass->store_info_set_string = store_info_set_string;
> +}
> +
> +static void
> +camel_nntp_store_summary_init (CamelNNTPStoreSummary *s)
> +{
> +	/*struct _CamelNNTPStoreSummaryPrivate *p;
> +
> +	  p = _PRIVATE(s) = g_malloc0(sizeof(*p));*/
> +
> +	((CamelStoreSummary *)s)->store_info_size = sizeof
(CamelNNTPStoreInfo);
> +	s->version = CAMEL_NNTP_STORE_SUMMARY_VERSION;
> +	memset(&s->last_newslist, 0, sizeof(s->last_newslist));
> +}
> +
> +static void
> +camel_nntp_store_summary_finalise (CamelObject *obj)
> +{
> +	/*struct _CamelNNTPStoreSummaryPrivate *p;*/
> +	/*CamelNNTPStoreSummary *s = (CamelNNTPStoreSummary *)obj;*/
> +
> +	/*p = _PRIVATE(obj);
> +	  g_free(p);*/
> +}
> +
> +CamelType
> +camel_nntp_store_summary_get_type (void)
> +{
> +	static CamelType type = CAMEL_INVALID_TYPE;
> +
> +	if (type == CAMEL_INVALID_TYPE) {
> +		camel_nntp_store_summary_parent = (CamelStoreSummaryClass *)
camel_store_summary_get_type();
> +		type = camel_type_register((CamelType)
camel_nntp_store_summary_parent, "CamelNNTPStoreSummary",
> +					   sizeof (CamelNNTPStoreSummary),
> +					   sizeof (CamelNNTPStoreSummaryClass),
> +					   (CamelObjectClassInitFunc)
camel_nntp_store_summary_class_init,
> +					   NULL,
> +					   (CamelObjectInitFunc) camel_nntp_store_summary_init,
> +					   (CamelObjectFinalizeFunc) camel_nntp_store_summary_finalise);
> +	}
> +
> +	return type;
> +}
> +
> +/**
> + * camel_nntp_store_summary_new:
> + *
> + * Create a new CamelNNTPStoreSummary object.
> + *
> + * Return value: A new CamelNNTPStoreSummary widget.
> + **/
> +CamelNNTPStoreSummary *
> +camel_nntp_store_summary_new (void)
> +{
> +	CamelNNTPStoreSummary *new = CAMEL_NNTP_STORE_SUMMARY
( camel_object_new (camel_nntp_store_summary_get_type ()));
> +
> +	return new;
> +}
> +
> +/**
> + * camel_nntp_store_summary_full_name:
> + * @s:
> + * @path:
> + *
> + * Retrieve a summary item by full name.
> + *
> + * A referenced to the summary item is returned, which may be
> + * ref'd or free'd as appropriate.
> + *
> + * Return value: The summary item, or NULL if the @full_name name
> + * is not available.
> + * It must be freed using camel_store_summary_info_free().
> + **/
> +CamelNNTPStoreInfo *
> +camel_nntp_store_summary_full_name(CamelNNTPStoreSummary *s, const
char *full_name)

> +char *
> +camel_nntp_store_summary_full_to_path(CamelNNTPStoreSummary *s, const
char *full_name, char dir_sep)

> +char *
> +camel_nntp_store_summary_path_to_full(CamelNNTPStoreSummary *s, const
char *path, char dir_sep)

> +CamelNNTPStoreInfo *
> +camel_nntp_store_summary_add_from_full(CamelNNTPStoreSummary *s,
const char *full, char dir_sep)


^^ this stuff is imap specific, should either be removed or changed for
nntp.


> +static int
> +summary_header_load(CamelStoreSummary *s, FILE *in)
> +{
> +	CamelNNTPStoreSummary *is = (CamelNNTPStoreSummary *)s;
> +	gint32 version, nil;
> +
> +	if (camel_nntp_store_summary_parent->summary_header_load
((CamelStoreSummary *)s, in) == -1
> +	    || camel_file_util_decode_fixed_int32(in, &version) == -1)
> +		return -1;
> +
> +	is->version = version;
> +
> +	if (version < CAMEL_NNTP_STORE_SUMMARY_VERSION_0) {
> +		g_warning("Store summary header version too low");
> +		return -1;
> +	}
> +
> +	if (fread(is->last_newslist, 1, NNTP_DATE_SIZE, in) <
NNTP_DATE_SIZE) return -1;

newline after the if ()

> @@ -237,6 +240,7 @@
>  				     _("No such folder: %s"), line);
>  		return -1;
>  	} else if (ret != 211) {
> +		if (ret < 0) line = "";

newline

>  		camel_exception_setv(ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
>  				     _("Could not get group: %s"), line);
>  		return -1;
> @@ -247,9 +251,6 @@
>  	f = strtoul(line, &line, 10);
>  	l = strtoul(line, &line, 10);
>  
> -	dd(printf("nntp_summary: got last '%u' first '%u'\n"
> -		  "nntp_summary: high '%u' low '%u'\n", l, f, cns->high, cns-
>low));
> -
>  	if (cns->low == f && cns->high == l) {
>  		dd(printf("nntp_summary: no work to do!\n"));
>  		return 0;
> @@ -301,6 +302,13 @@
>  
>  	camel_folder_summary_touch(s);
>  
> +	/* TODO: not from here */
> +	camel_folder_summary_save(s);
> +
> +	if (ret < 0)
> +		camel_exception_setv(ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
> +				     _("Could not get messages: unspecificed error"));
> +
>  	return ret;
>  }
>  
> --- /home/meilof/cvs/orig/evolution//camel/providers/nntp/camel-nntp-
folder.h	2001-11-29 22:09:38.000000000 -0500
> +++ camel/providers/nntp/camel-nntp-folder.h	2004-01-09
21:58:20.000000000 -0500
> @@ -33,6 +33,7 @@
>  #endif /* __cplusplus }*/
>  
>  #include "camel/camel-folder.h"
> +#include "camel/camel-disco-folder.h"
>  
>  /*  #include "camel-store.h" */
>  
> @@ -42,7 +43,7 @@
>  #define CAMEL_IS_NNTP_FOLDER(o)    (CAMEL_CHECK_TYPE((o),
CAMEL_NNTP_FOLDER_TYPE))
>  
>  typedef struct _CamelNNTPFolder {
> -	CamelFolder parent;
> +	CamelDiscoFolder parent;
>  
>  	struct _CamelNNTPFolderPrivate *priv;
>  
> @@ -52,7 +53,7 @@
>  } CamelNNTPFolder;
>  
>  typedef struct _CamelNNTPFolderClass {
> -	CamelFolderClass parent;
> +	CamelDiscoFolderClass parent;
>  
>  	/* Virtual methods */	
>  	
> --- /home/meilof/cvs/orig/evolution//camel/providers/nntp/camel-nntp-
grouplist.h	2001-10-27 12:59:31.000000000 -0400
> +++ camel/providers/nntp/camel-nntp-grouplist.h	2004-01-09
21:58:20.000000000 -0500
> @@ -40,6 +40,9 @@
>  	GList *group_list;
>  };
>  
> +typedef struct CamelNNTPGroupList CamelNNTPGroupList;
> +typedef struct CamelNNTPGroupListEntry CamelNNTPGroupListEntry;

We use a convention of

struct _Name

for the struct, and 

Name

for the corresponding typedef'd equivalent.

Its quite important to maintain this.

> --- /home/meilof/cvs/orig/evolution//composer/e-msg-composer.c	2004-
01-10 16:26:20.000000000 -0500
> +++ composer/e-msg-composer.c	2004-01-10 16:26:36.000000000 -0500
> @@ -1869,11 +1869,37 @@
>  {
>  	if (type != Bonobo_UIComponent_STATE_CHANGED)
>  		return;
> -	
> +
>  	e_msg_composer_set_view_replyto (E_MSG_COMPOSER (user_data), atoi
(state));
>  }
>  
>  static void
> +menu_view_to_cb (BonoboUIComponent           *component,
> +		      const char                  *path,
> +		      Bonobo_UIComponent_EventType type,
> +		      const char                  *state,
> +		      gpointer                     user_data)
> +{
> +	if (type != Bonobo_UIComponent_STATE_CHANGED)
> +		return;
> +
> +	e_msg_composer_set_view_to (E_MSG_COMPOSER (user_data), atoi
(state));
> +}
> +
> +static void
> +menu_view_postto_cb (BonoboUIComponent           *component,
> +		      const char                  *path,
> +		      Bonobo_UIComponent_EventType type,
> +		      const char                  *state,
> +		      gpointer                     user_data)
> +{
> +	if (type != Bonobo_UIComponent_STATE_CHANGED)
> +		return;
> +
> +	e_msg_composer_set_view_postto (E_MSG_COMPOSER (user_data), atoi
(state));
> +}
> +
> +static void
>  menu_view_cc_cb (BonoboUIComponent           *component,
>  		 const char                  *path,
>  		 Bonobo_UIComponent_EventType type,
> @@ -2119,6 +2145,22 @@
>  		composer->uic, "ViewReplyTo",
>  		menu_view_replyto_cb, composer);
>  	
> +	/* View/To */
> +	bonobo_ui_component_set_prop (
> +		composer->uic, "/commands/ViewTo",
> +		"state", composer->view_to ? "1" : "0", NULL);
> +	bonobo_ui_component_add_listener (
> +		composer->uic, "ViewTo",
> +		menu_view_to_cb, composer);
> +
> +	/* View/PostTo */
> +	bonobo_ui_component_set_prop (
> +		composer->uic, "/commands/ViewPostTo",
> +		"state", composer->view_postto ? "1" : "0", NULL);
> +	bonobo_ui_component_add_listener (
> +		composer->uic, "ViewPostTo",
> +		menu_view_postto_cb, composer);
> +
>  	/* View/CC */
>  	bonobo_ui_component_set_prop (
>  		composer->uic, "/commands/ViewCC",
> @@ -2718,7 +2760,7 @@
>  }
>  
>  static void
> -e_msg_composer_load_config (EMsgComposer *composer)
> +e_msg_composer_load_config (EMsgComposer *composer, int visible_mask)
>  {
>  	GConfClient *gconf;
>  	
> @@ -2726,14 +2768,38 @@
>  	
>  	composer->view_from = gconf_client_get_bool (
>  		gconf, "/apps/evolution/mail/composer/view/From", NULL);
> -	composer->view_replyto = gconf_client_get_bool ( 
> +	composer->view_replyto = gconf_client_get_bool (
>  		gconf, "/apps/evolution/mail/composer/view/ReplyTo", NULL);
> +	composer->view_to = gconf_client_get_bool (
> +		gconf, "/apps/evolution/mail/composer/view/To", NULL);
> +	composer->view_postto = gconf_client_get_bool (
> +		gconf, "/apps/evolution/mail/composer/view/PostTo", NULL);
>  	composer->view_cc = gconf_client_get_bool ( 
>  		gconf, "/apps/evolution/mail/composer/view/Cc", NULL);
>  	composer->view_bcc = gconf_client_get_bool (
>  		gconf, "/apps/evolution/mail/composer/view/Bcc", NULL);
>  	composer->view_subject = gconf_client_get_bool (
>  		gconf, "/apps/evolution/mail/composer/view/Subject", NULL);
> +
> +	/* if we're mailing, you cannot disable to so it should appear
checked */
> +	if (visible_mask & E_MSG_COMPOSER_VISIBLE_TO)
> +		composer->view_to = TRUE;
> +	else
> +		composer->view_to = FALSE;
> +
> +	/* ditto for post-to */
> +	if (visible_mask & E_MSG_COMPOSER_VISIBLE_POSTTO)
> +		composer->view_postto = TRUE;
> +	else
> +		composer->view_postto = FALSE;
> +
> +
> +	/* we set these to false initially if we're posting */
> +	if (!(visible_mask & E_MSG_COMPOSER_VISIBLE_CC))
> +		composer->view_cc = FALSE;
> +
> +	if (!(visible_mask & E_MSG_COMPOSER_VISIBLE_BCC))
> +		composer->view_bcc = FALSE;
>  	
>  	g_object_unref (gconf);
>  }
> @@ -2747,6 +2813,10 @@
>  		flags |= E_MSG_COMPOSER_VISIBLE_FROM;
>  	if (composer->view_replyto)
>  		flags |= E_MSG_COMPOSER_VISIBLE_REPLYTO;
> +	if (composer->view_to)
> +		flags |= E_MSG_COMPOSER_VISIBLE_TO;
> +	if (composer->view_postto)
> +		flags |= E_MSG_COMPOSER_VISIBLE_POSTTO;
>  	if (composer->view_cc)
>  		flags |= E_MSG_COMPOSER_VISIBLE_CC;
>  	if (composer->view_bcc)
> @@ -2948,7 +3018,7 @@
>  			   drop_types, num_drop_types, GDK_ACTION_COPY);
>  	g_signal_connect (composer, "drag_data_received",
>  			  G_CALLBACK (drag_data_received), NULL);
> -	e_msg_composer_load_config (composer);
> +	e_msg_composer_load_config (composer, visible_mask);
>  	
>  	setup_ui (composer);
>  	
> @@ -3101,64 +3171,59 @@
>  	/* printf ("set_editor_signature end\n"); */
>  }
>  
> -
>  /**
> - * e_msg_composer_new:
> + * e_msg_composer_new_with_type:
> + *
> + * Create a new message composer widget. The type can be
> + * E_MSG_COMPOSER_MAIL, E_MSG_COMPOSER_POST or
E_MSG_COMPOSER_MAIL_POST.
>   *
> - * Create a new message composer widget.
> - * 
>   * Return value: A pointer to the newly created widget
>   **/
> +
>  EMsgComposer *
> -e_msg_composer_new (void)
> +e_msg_composer_new_with_type (int type)
>  {
>  	gboolean send_html;
>  	GConfClient *gconf;
>  	EMsgComposer *new;
> -	
> +
>  	gconf = gconf_client_get_default ();
>  	send_html = gconf_client_get_bool (gconf, "/apps/evolution/mail/
composer/send_html", NULL);
>  	g_object_unref (gconf);
> -	
> -	new = create_composer (E_MSG_COMPOSER_VISIBLE_MASK_MAIL);
> +
> +	switch (type) {
> +		case E_MSG_COMPOSER_MAIL:
> +			new = create_composer (E_MSG_COMPOSER_VISIBLE_MASK_MAIL);
> +			break;
> +		case E_MSG_COMPOSER_POST:
> +			new = create_composer (E_MSG_COMPOSER_VISIBLE_MASK_POST);
> +			break;
> +		default:
> +			new = create_composer (E_MSG_COMPOSER_VISIBLE_MASK_MAIL |
E_MSG_COMPOSER_VISIBLE_MASK_POST);
> +	}
> +
>  	if (new) {
>  		e_msg_composer_set_send_html (new, send_html);
>  		set_editor_text (new, "");
>  		set_editor_signature (new);
>  	}
> -	
> +
>  	return new;
>  }
>  
>  /**
> - * e_msg_composer_new_post:
> + * e_msg_composer_new:
>   *
>   * Create a new message composer widget.
>   * 
>   * Return value: A pointer to the newly created widget
>   **/
>  EMsgComposer *
> -e_msg_composer_new_post (void)
> +e_msg_composer_new (void)
>  {
> -	gboolean send_html;
> -	GConfClient *gconf;
> -	EMsgComposer *new;
> -	
> -	gconf = gconf_client_get_default ();
> -	send_html = gconf_client_get_bool (gconf, "/apps/evolution/mail/
composer/send_html", NULL);
> -	g_object_unref (gconf);
> -	
> -	new = create_composer (E_MSG_COMPOSER_VISIBLE_MASK_POST);
> -	if (new) {
> -		e_msg_composer_set_send_html (new, send_html);
> -		set_editor_text (new, "");
> -		set_editor_signature (new);
> -	}
> -	
> -	return new;
> +	return e_msg_composer_new_with_type (E_MSG_COMPOSER_MAIL);
>  }
>  
> -
>  static gboolean
>  is_special_header (const char *hdr_name)
>  {
> @@ -4651,16 +4716,16 @@
>  /**
>   * e_msg_composer_get_view_replyto:
>   * @composer: A message composer widget
> - * 
> + *
>   * Get the status of the "View Reply-To header" flag.
> - * 
> + *
>   * Return value: The status of the "View Reply-To header" flag.
>   **/
>  gboolean
>  e_msg_composer_get_view_replyto (EMsgComposer *composer)
>  {
>  	g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE);
> -	
> +
>  	return composer->view_replyto;
>  }
>  
> @@ -4676,26 +4741,129 @@
>  e_msg_composer_set_view_replyto (EMsgComposer *composer, gboolean
view_replyto)
>  {
>  	GConfClient *gconf;
> -	
> +
>  	g_return_if_fail (E_IS_MSG_COMPOSER (composer));
> -	
> +
>  	if ((composer->view_replyto && view_replyto) ||
>  	    (!composer->view_replyto && !view_replyto))
>  		return;
> -	
> +
>  	composer->view_replyto = view_replyto;
>  	bonobo_ui_component_set_prop (composer->uic, "/commands/
ViewReplyTo",
>  				      "state", composer->view_replyto ? "1" : "0", NULL);
> -	
> +
> +	/* we do this /only/ if the fields is in the visible_mask */
>  	gconf = gconf_client_get_default ();
>  	gconf_client_set_bool (gconf, "/apps/evolution/mail/composer/view/
ReplyTo", view_replyto, NULL);
>  	g_object_unref (gconf);
> -	
> +
> +	e_msg_composer_hdrs_set_visible (E_MSG_COMPOSER_HDRS (composer-
>hdrs),
> +					 e_msg_composer_get_visible_flags (composer));
> +}
> +
> +
> +/**
> + * e_msg_composer_get_view_to:
> + * @composer: A message composer widget
> + *
> + * Get the status of the "View To header" flag.
> + *
> + * Return value: The status of the "View To header" flag.
> + **/
> +gboolean
> +e_msg_composer_get_view_to (EMsgComposer *composer)
> +{
> +	g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE);
> +
> +	return composer->view_to;
> +}
> +
> +
> +/**
> + * e_msg_composer_set_view_to:
> + * @composer: A message composer widget
> + * @state: whether to show or hide the To selector
> + *
> + * Controls the state of the To selector
> + */
> +void
> +e_msg_composer_set_view_to (EMsgComposer *composer, gboolean view_to)
> +{
> +	GConfClient *gconf;
> +
> +	g_return_if_fail (E_IS_MSG_COMPOSER (composer));
> +
> +	if ((composer->view_to && view_to) ||
> +	    (!composer->view_to && !view_to))
> +		return;
> +
> +	composer->view_to = view_to;
> +	bonobo_ui_component_set_prop (composer->uic, "/commands/ViewTo",
> +				      "state", composer->view_to ? "1" : "0", NULL);
> +
> +	if ((E_MSG_COMPOSER_HDRS(composer->hdrs))->visible_mask &
E_MSG_COMPOSER_VISIBLE_TO) {
> +		gconf = gconf_client_get_default ();
> +		gconf_client_set_bool (gconf, "/apps/evolution/mail/composer/view/
To", view_to, NULL);
> +		g_object_unref (gconf);
> +	}
> +
> +	e_msg_composer_hdrs_set_visible (E_MSG_COMPOSER_HDRS (composer-
>hdrs),
> +					 e_msg_composer_get_visible_flags (composer));
> +}
> +
> +
> +
> +/**
> + * e_msg_composer_get_view_postto:
> + * @composer: A message composer widget
> + *
> + * Get the status of the "View PostTo header" flag.
> + *
> + * Return value: The status of the "View PostTo header" flag.
> + **/
> +gboolean
> +e_msg_composer_get_view_postto (EMsgComposer *composer)
> +{
> +	g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE);
> +
> +	return composer->view_postto;
> +}
> +
> +
> +/**
> + * e_msg_composer_set_view_postto:
> + * @composer: A message composer widget
> + * @state: whether to show or hide the PostTo selector
> + *
> + * Controls the state of the PostTo selector
> + */
> +void
> +e_msg_composer_set_view_postto (EMsgComposer *composer, gboolean
view_postto)
> +{
> +	GConfClient *gconf;
> +
> +	g_return_if_fail (E_IS_MSG_COMPOSER (composer));
> +
> +	if ((composer->view_postto && view_postto) ||
> +	    (!composer->view_postto && !view_postto))
> +		return;
> +
> +	composer->view_postto = view_postto;
> +	bonobo_ui_component_set_prop (composer->uic, "/commands/ViewPostTo",
> +				      "state", composer->view_postto ? "1" : "0", NULL);
> +
> +	if ((E_MSG_COMPOSER_HDRS(composer->hdrs))->visible_mask &
E_MSG_COMPOSER_VISIBLE_POSTTO) {
> +		gconf = gconf_client_get_default ();
> +		gconf_client_set_bool (gconf, "/apps/evolution/mail/composer/view/
PostTo", view_postto, NULL);
> +		g_object_unref (gconf);
> +	}
> +
>  	e_msg_composer_hdrs_set_visible (E_MSG_COMPOSER_HDRS (composer-
>hdrs),
>  					 e_msg_composer_get_visible_flags (composer));
>  }
>  
>  
> +
>  /**
>   * e_msg_composer_get_view_cc:
>   * @composer: A message composer widget
> @@ -4735,9 +4903,11 @@
>  	bonobo_ui_component_set_prop (composer->uic, "/commands/ViewCC",
>  				      "state", composer->view_cc ? "1" : "0", NULL);
>  	
> -	gconf = gconf_client_get_default ();
> -	gconf_client_set_bool (gconf, "/apps/evolution/mail/composer/view/
Cc", view_cc, NULL);
> -	g_object_unref (gconf);
> +	if ((E_MSG_COMPOSER_HDRS(composer->hdrs))->visible_mask &
E_MSG_COMPOSER_VISIBLE_CC) {
> +		gconf = gconf_client_get_default ();
> +		gconf_client_set_bool (gconf, "/apps/evolution/mail/composer/view/
Cc", view_cc, NULL);
> +		g_object_unref (gconf);
> +	}
>  	
>  	e_msg_composer_hdrs_set_visible (E_MSG_COMPOSER_HDRS (composer-
>hdrs),
>  					 e_msg_composer_get_visible_flags (composer));
> @@ -4783,9 +4953,11 @@
>  	bonobo_ui_component_set_prop (composer->uic, "/commands/ViewBCC",
>  				      "state", composer->view_bcc ? "1" : "0", NULL);
>  	
> -	gconf = gconf_client_get_default ();
> -	gconf_client_set_bool (gconf, "/apps/evolution/mail/composer/view/
Bcc", view_bcc, NULL);
> -	g_object_unref (gconf);
> +	if ((E_MSG_COMPOSER_HDRS(composer->hdrs))->visible_mask &
E_MSG_COMPOSER_VISIBLE_BCC) {
> +		gconf = gconf_client_get_default ();
> +		gconf_client_set_bool (gconf, "/apps/evolution/mail/composer/view/
Bcc", view_bcc, NULL);
> +		g_object_unref (gconf);
> +	}
>  	
>  	e_msg_composer_hdrs_set_visible (E_MSG_COMPOSER_HDRS (composer-
>hdrs),
>  					 e_msg_composer_get_visible_flags (composer));
> --- /home/meilof/cvs/orig/evolution//composer/e-msg-composer.h	2003-
12-01 10:20:49.000000000 -0500
> +++ composer/e-msg-composer.h	2004-01-09 21:58:24.000000000 -0500
> @@ -94,6 +94,8 @@
>  	guint32 smime_encrypt          : 1;
>  	guint32 view_from              : 1;
>  	guint32 view_replyto           : 1;
> +	guint32 view_to                : 1;
> +	guint32 view_postto            : 1;
>  	guint32 view_bcc               : 1;
>  	guint32 view_cc                : 1;
>  	guint32 view_subject           : 1;
> @@ -124,7 +126,12 @@
>  GtkType                  e_msg_composer_get_type
(void);
>  
>  EMsgComposer            *e_msg_composer_new
(void);
> -EMsgComposer            *e_msg_composer_new_post
(void);
> +
> +#define E_MSG_COMPOSER_MAIL 1
> +#define E_MSG_COMPOSER_POST 2
> +#define E_MSG_COMPOSER_MAIL_POST E_MSG_COMPOSER_MAIL|
E_MSG_COMPOSER_POST
> +
> +EMsgComposer            *e_msg_composer_new_with_type
(int type);
>  
>  EMsgComposer            *e_msg_composer_new_with_message
(CamelMimeMessage  *msg);
>  EMsgComposer            *e_msg_composer_new_from_url
(const char        *url);
> @@ -164,9 +171,15 @@
>  gboolean                 e_msg_composer_get_view_from
(EMsgComposer      *composer);
>  void                     e_msg_composer_set_view_from
(EMsgComposer      *composer,
>  									  gboolean           view_from);
> +gboolean                 e_msg_composer_get_view_to
(EMsgComposer      *composer);
> +void                     e_msg_composer_set_view_to
(EMsgComposer      *composer,
> +									  gboolean           view_replyto);
>  gboolean                 e_msg_composer_get_view_replyto
(EMsgComposer      *composer);
>  void                     e_msg_composer_set_view_replyto
(EMsgComposer      *composer,
>  									  gboolean           view_replyto);
> +gboolean                 e_msg_composer_get_view_postto
(EMsgComposer      *composer);
> +void                     e_msg_composer_set_view_postto
(EMsgComposer      *composer,
> +									  gboolean           view_replyto);
>  gboolean                 e_msg_composer_get_view_cc
(EMsgComposer      *composer);
>  void                     e_msg_composer_set_view_cc
(EMsgComposer      *composer,
>  									  gboolean           view_cc);
> --- /home/meilof/cvs/orig/evolution//composer/e-msg-composer-hdrs.
c	2003-12-04 12:24:23.000000000 -0500
> +++ composer/e-msg-composer-hdrs.c	2004-01-10 16:56:37.000000000 -0500
> @@ -50,7 +50,10 @@
>  #include <camel/camel.h>
>  #include "e-msg-composer-hdrs.h"
>  #include "mail/mail-config.h"
> -#include "mail/em-folder-selection-button.h"
> +/*#include "mail/em-folder-selection-button.h"*/
> +#include "mail/em-folder-selector.h"
> +#include "mail/mail-component.h"
> +#include "mail/em-folder-tree.h"
>  
>  
>  
> @@ -78,9 +81,11 @@
>  	
>  	/* The tooltips.  */
>  	GtkTooltips *tooltips;
> -	
> +
>  	EAccountList *accounts;
>  	GSList *from_options;
> +
> +	gboolean post_custom;
>  	
>  	/* Standard headers.  */
>  	EMsgComposerHdrPair from, reply_to, to, cc, bcc, post_to, subject;
> @@ -179,6 +184,10 @@
>  {
>  	EMsgComposerHdrs *hdrs = E_MSG_COMPOSER_HDRS (data);
>  	const char *reply_to;
> +	GList *post_items = NULL;
> +
> +	/* this will retrieve items relative to the previous account */
> +	if (!hdrs->priv->post_custom) post_items =
e_msg_composer_hdrs_get_post_to(hdrs);
>  	
>  	hdrs->account = g_object_get_data ((GObject *) item, "account");
>  	
> @@ -186,6 +195,13 @@
>  	   because we don't want to change the visibility of the header */
>  	reply_to = hdrs->account->id->reply_to;
>  	gtk_entry_set_text (GTK_ENTRY (hdrs->priv->reply_to.entry),
reply_to ? reply_to : "");
> +
> +	/* folders should be made relative to the new from */
> +	if (!hdrs->priv->post_custom) {
> +		e_msg_composer_hdrs_set_post_to_list (hdrs, post_items);
> +		g_list_foreach (post_items, (GFunc)g_free, NULL);
> +		g_list_free(post_items);
> +	}
>  	
>  	g_signal_emit (hdrs, signals [FROM_CHANGED], 0);
>  }
> @@ -460,6 +476,51 @@
>  	return control_widget;
>  }
>  
> +static void
> +post_browser_response (EMFolderSelector *emfs, int response,
EMsgComposerHdrs *hdrs)
> +{
> +	if (response == GTK_RESPONSE_OK) {
> +		GList *uris = em_folder_selector_get_selected_uris (emfs);
> +		e_msg_composer_hdrs_set_post_to_list (hdrs, uris);
> +		hdrs->priv->post_custom = FALSE;
> +		g_list_foreach (uris, (GFunc)g_free, NULL);
> +		g_list_free(uris);
> +	}
> +
> +	gtk_widget_destroy ((GtkWidget *) emfs);
> +}
> +
> +static void
> +post_browser_clicked_cb (GtkButton *button, EMsgComposerHdrs *hdrs)
> +{
> +	EMFolderTreeModel *model;
> +	EMFolderTree *emft;
> +	GtkWidget *dialog;
> +	GList *post_items;
> +
> +	model = mail_component_peek_tree_model (mail_component_peek ());
> +	emft = (EMFolderTree *) em_folder_tree_new_with_model (model);
> +	em_folder_tree_set_multiselect(emft, TRUE);
> +
> +	dialog = em_folder_selector_new (emft,
EM_FOLDER_SELECTOR_CAN_CREATE,
> +	                                 _("Posting destination"),
> +	                                 _("Choose folders to post the
message to."));
> +
> +	post_items = e_msg_composer_hdrs_get_post_to (hdrs);	
> +	em_folder_selector_set_selected_list((EMFolderSelector *) dialog,
post_items);
> +	g_list_foreach(post_items, (GFunc) g_free, NULL);
> +	g_list_free(post_items);
> +
> +	g_signal_connect (dialog, "response", G_CALLBACK
(post_browser_response), hdrs);
> +	gtk_widget_show (dialog);
> +}
> +
> +static void
> +post_entry_changed_cb (GtkButton *button, EMsgComposerHdrs *hdrs)
> +{
> +	hdrs->priv->post_custom = TRUE;
> +}
> +
>  static EMsgComposerHdrPair 
>  header_new_recipient (EMsgComposerHdrs *hdrs, const char *name, const
char *tip)
>  {
> @@ -544,8 +605,17 @@
>  	/*
>  	 * Post-To
>  	 */
> -	priv->post_to.label = gtk_label_new (_("Post To:"));
> -	priv->post_to.entry = em_folder_selection_button_new (_("Posting
destination"), _("Choose a folder to post the message to."));
> +	priv->post_to.label = gtk_button_new_with_label (_("Post To:"));
> +	GTK_OBJECT_UNSET_FLAGS (priv->post_to.label, GTK_CAN_FOCUS);
> +	g_signal_connect(priv->post_to.label, "clicked",
> +			 G_CALLBACK (post_browser_clicked_cb), hdrs);
> +	gtk_tooltips_set_tip (hdrs->priv->tooltips, priv->post_to.label,
> +			      _("Click here to select folders to post to"),
> +			      NULL);
> +			      
> +	priv->post_to.entry = gtk_entry_new();
> +	g_signal_connect(priv->post_to.entry, "changed",
> +			 G_CALLBACK (post_entry_changed_cb), hdrs);
>  }
>  
>  static void
> @@ -579,7 +649,7 @@
>  static void
>  set_pair_visibility (EMsgComposerHdrs *h, EMsgComposerHdrPair *pair,
int visible)
>  {
> -	if (visible & h->visible_mask) {
> +	if (visible/* & h->visible_mask*/) {
>  		gtk_widget_show (pair->label);
>  		gtk_widget_show (pair->entry);
>  	} else {
> @@ -611,11 +681,20 @@
>  static void
>  headers_set_sensitivity (EMsgComposerHdrs *h)
>  {
> -	bonobo_ui_component_set_prop (
> +/*	bonobo_ui_component_set_prop (
>  		h->priv->uic, "/commands/ViewFrom", "sensitive",
> -		h->visible_mask & E_MSG_COMPOSER_VISIBLE_FROM ? "1" : "0", NULL);
> -	
> +		h->visible_mask & E_MSG_COMPOSER_VISIBLE_FROM ? "1" : "0", NULL);*/
> +
> +	/* these ones are always on */
> +	bonobo_ui_component_set_prop (
> +		h->priv->uic, "/commands/ViewTo", "sensitive",
> +		h->visible_mask & E_MSG_COMPOSER_VISIBLE_TO ? "0" : "1", NULL);
> +
>  	bonobo_ui_component_set_prop (
> +		h->priv->uic, "/commands/ViewPostTo", "sensitive",
> +		h->visible_mask & E_MSG_COMPOSER_VISIBLE_POSTTO ? "0" : "1", NULL);
> +
> +/*	bonobo_ui_component_set_prop (
>  		h->priv->uic, "/commands/ViewReplyTo", "sensitive",
>  		h->visible_mask & E_MSG_COMPOSER_VISIBLE_REPLYTO ? "1" : "0",
NULL);
>  	
> @@ -625,7 +704,7 @@
>  	
>  	bonobo_ui_component_set_prop (
>  		h->priv->uic, "/commands/ViewBCC", "sensitive",
> -		h->visible_mask & E_MSG_COMPOSER_VISIBLE_BCC ? "1" : "0", NULL);
> +		h->visible_mask & E_MSG_COMPOSER_VISIBLE_BCC ? "1" : "0", NULL); */

Do you really mean to comment out this block of code?

>  }
>  
>  void
> @@ -775,6 +854,8 @@
>  	
>  	priv->accounts = mail_config_get_accounts ();
>  	g_object_ref (priv->accounts);
> +
> +	priv->post_custom = FALSE;
>  	
>  	hdrs->priv = priv;
>  }
> @@ -959,9 +1040,9 @@
>  	}
>  	
>  	if (hdrs->visible_mask & E_MSG_COMPOSER_VISIBLE_POSTTO) {
> -		header = e_msg_composer_hdrs_get_post_to (hdrs);
> +		/*header = e_msg_composer_hdrs_get_post_to (hdrs);
>  		camel_medium_set_header (CAMEL_MEDIUM (msg), "X-Evolution-PostTo",
header);
> -		g_free (header);
> +		g_free (header);*/
>  	}
>  }
>  
> @@ -1055,7 +1136,7 @@
>  	char *str;
>  	
>  	g_return_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs));
> -	
> +
>  	str = eab_destination_exportv (to_destv);
>  	bonobo_widget_set_property (BONOBO_WIDGET (hdrs->priv->to.entry),
"destinations", TC_CORBA_string, str ? str : "", NULL); 
>  	g_free (str);
> @@ -1096,12 +1177,158 @@
>  e_msg_composer_hdrs_set_post_to (EMsgComposerHdrs *hdrs,
>  				 const char *post_to)
>  {
> +	gchar *post_to_copy;
>  	g_return_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs));
>  	g_return_if_fail (post_to != NULL);
> +
> +
> +	post_to_copy = g_strdup(post_to);
> +	GList *list = g_list_append(NULL, post_to_copy);
>

Eek, this is C99, we need to stick to C89, declarations are at the
start of the block, before any statements.

> +	e_msg_composer_hdrs_set_post_to_list (hdrs, list);
> +
> +	g_list_free(list);
> +	g_free(post_to_copy);
>  	
> -	em_folder_selection_button_set_selection ((EMFolderSelectionButton
*) hdrs->priv->post_to.entry, post_to);
>  }
>  
> +static char *
> +string_trim (char *str)
> +{
> +	char *ptr;
> +	
> +	while (*str == ' ')
> +		str++;
> +		
> +	ptr = str + strlen(str) - 1;
> +	while (ptr >= str && *ptr == ' ')
> +		*ptr-- = '\0';
> +	
> +	return str;

use g_strstrip instead of this.

> +}
> +
> +static GList *
> +newsgroups_list_split (char *list)
> +{
> +	GList *lst = NULL;
> +	char *tmp;
> +	
> +	gchar **items = g_strsplit(list, ",", 0),
> +	      **cur_ptr = items;

don't add a blank line in the middle of variable decls.

> +	while (*cur_ptr) {
> +		tmp = string_trim(*cur_ptr);
> +
> +		if (tmp[0])
> +			lst = g_list_append(lst, g_strdup(tmp));
> +
> +		cur_ptr++;
> +	}
> +
> +	g_strfreev(items);
> +
> +	return lst;
> +}
> +
> +static char *
> +get_account_store_url(EMsgComposerHdrs *hdrs)
> +{
> +	CamelURL *url;
> +	char *ret = NULL;
> +	if (hdrs->account->source && hdrs->account->source->url) {
> +		url = camel_url_new (hdrs->account->source->url, NULL);
> +		ret = camel_url_to_string(url, CAMEL_URL_HIDE_ALL);
> +		camel_url_free(url);
> +	}
> +	return ret;
> +}                                            
> +
> +static char *
> +folder_name_to_string(EMsgComposerHdrs *hdrs, const char *uri)
> +{
> +	char *storeurl = get_account_store_url(hdrs);
> +	int len;
> +
> +	if (storeurl) {
> +		len = strlen(storeurl);
> +		
> +		if (strncasecmp(uri, storeurl, len) == 0) {

g_ascii...

> +			g_free(storeurl);
> +			return g_strdup(uri + len);
> +		}
> +		
> +		g_free(storeurl);
> +	}
> +	
> +	return g_strdup(uri);
> +}
> +
> +void
> +e_msg_composer_hdrs_set_post_to_list (EMsgComposerHdrs *hdrs, GList
*urls)
> +{
> +	/* compile the name */
> +	char *caption, *tmp, *tmp2;
> +	gboolean post_custom;
> +
> +	if (hdrs->priv->post_to.entry == NULL) return;
> +
> +	caption = g_strdup("");
> +
> +	while (urls) {
> +		tmp = folder_name_to_string(hdrs, (char *)urls->data);

How does this work with imap/other store's folder names?

> +		if (tmp) {
> +			tmp2 = g_strconcat(caption, ", ", tmp, NULL);
> +			g_free(caption);

instead of this append + free stuff, use a g_string to build the
result (equivalent to java's StringBuffer vs using String's).

> +			caption = tmp2;
> +			g_free(tmp);
> +		}
> +		urls = g_list_next(urls);
> +	}
> +
> +	post_custom = hdrs->priv->post_custom;
> +	gtk_entry_set_text(GTK_ENTRY(hdrs->priv->post_to.entry), caption
[0] ? caption + 2 : "");
> +	hdrs->priv->post_custom = post_custom;
> +	g_free(caption);
> +}
> +
> +void
> +e_msg_composer_hdrs_set_post_to_base (EMsgComposerHdrs *hdrs,
> +                                      const char *base, const char
*post_to)
> +{
> +	GList *lst, *curlist;
> +	char *hdr_copy = g_strdup(post_to), *caption, *tmp, *tmp2;
> +	gboolean post_custom;
> +
> +	/* split to newsgroup names */
> +	lst = newsgroups_list_split(hdr_copy);
> +	curlist = lst;
> +
> +	/* compile the name */
> +	caption = g_strdup("");

same here, use a GString.

> +	while (curlist) {
> +		tmp2 = g_strdup_printf("%s/%s", base, (char *)curlist->data);
> +		tmp = folder_name_to_string(hdrs, tmp2);
> +		g_free(tmp2);
> +		if (tmp) {
> +			tmp2 = g_strconcat(caption, ", ", tmp, NULL);
> +			g_free(caption);
> +			caption = tmp2;
> +			g_free(tmp);
> +		}
> +		curlist = g_list_next(curlist);
> +	}
> +
> +	
> +	post_custom = hdrs->priv->post_custom;
> +	gtk_entry_set_text(GTK_ENTRY(hdrs->priv->post_to.entry), caption
[0] ? caption + 2 : "");
> +	hdrs->priv->post_custom = post_custom;
> +	g_free(caption);
> +	
> +        g_list_foreach(lst, (GFunc)g_free, NULL);
> +	g_list_free(lst);
> +	g_free(hdr_copy);
> +}
>  
>  void
>  e_msg_composer_hdrs_set_subject (EMsgComposerHdrs *hdrs,
> @@ -1255,16 +1482,37 @@
>  }
>  
>  
> -char *
> +GList *
>  e_msg_composer_hdrs_get_post_to (EMsgComposerHdrs *hdrs)
>  {
> -	const char *uri;
> +	GList *uris, *cur;
> +	char *storeurl = NULL, *tmp;
>  	
>  	g_return_val_if_fail (E_IS_MSG_COMPOSER_HDRS (hdrs), NULL);
> -	
> -	uri = em_folder_selection_button_get_selection
((EMFolderSelectionButton *) hdrs->priv->post_to.entry);
> -	
> -	return g_strdup (uri);
> +
> +	if (hdrs->priv->post_to.entry == NULL)
> +		return NULL;
> +
> +	tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(hdrs->priv->post_to.
entry)));
> +	uris = newsgroups_list_split(tmp);
> +	g_free(tmp);
> +
> +	cur = uris;
> +	while (cur) {
> +		if (strstr((char *)cur->data, "://") == NULL) {
> +			/* relative folder name: convert to absolute */
> +			if (!storeurl) storeurl = get_account_store_url(hdrs);
> +			if (!storeurl) break;

newlines

> +			tmp = g_strconcat(storeurl, (char *)cur->data, NULL);
> +			g_free((char *)cur->data);
> +			cur->data = tmp;
> +		}
> +		cur = g_list_next(cur);
> +	}
> +
> +	g_free(storeurl);
> +
> +	return uris;
>  }
>  
>  
> --- /home/meilof/cvs/orig/evolution//composer/e-msg-composer-hdrs.
h	2003-12-01 17:14:31.000000000 -0500
> +++ composer/e-msg-composer-hdrs.h	2004-01-10 01:20:45.000000000 -0500
> @@ -82,7 +82,7 @@
>  	E_MSG_COMPOSER_VISIBLE_CC         = (1 << 3),
>  	E_MSG_COMPOSER_VISIBLE_BCC        = (1 << 4),
>  	E_MSG_COMPOSER_VISIBLE_POSTTO     = (1 << 5),  /* for posting to
folders */
> -	E_MSG_COMPOSER_VISIBLE_NEWSGROUP  = (1 << 6),  /* for posting to
newsgroups */
> +	/*E_MSG_COMPOSER_VISIBLE_NEWSGROUP  = (1 << 6),*/  /* for posting to
newsgroups */

i take it this was dropped in favour of postto, should remove it not
just comment it.

PS by this stage i'm kinda tired of reviewing this long patch so the
rest i wont look at too closely ...

>  	E_MSG_COMPOSER_VISIBLE_SUBJECT    = (1 << 7)
>  } EMsgComposerHeaderVisibleFlags;
>  
> @@ -91,7 +91,7 @@
>  #define E_MSG_COMPOSER_VISIBLE_MASK_RECIPIENTS
(E_MSG_COMPOSER_VISIBLE_TO | E_MSG_COMPOSER_VISIBLE_CC |
E_MSG_COMPOSER_VISIBLE_BCC)
>  
>  #define E_MSG_COMPOSER_VISIBLE_MASK_MAIL
(E_MSG_COMPOSER_VISIBLE_MASK_BASIC |
E_MSG_COMPOSER_VISIBLE_MASK_RECIPIENTS)
> -#define E_MSG_COMPOSER_VISIBLE_MASK_NEWS
(E_MSG_COMPOSER_VISIBLE_MASK_BASIC | E_MSG_COMPOSER_VISIBLE_NEWSGROUP)
> +/*#define E_MSG_COMPOSER_VISIBLE_MASK_NEWS
(E_MSG_COMPOSER_VISIBLE_MASK_BASIC | E_MSG_COMPOSER_VISIBLE_NEWSGROUP)*/
>  #define E_MSG_COMPOSER_VISIBLE_MASK_POST
(E_MSG_COMPOSER_VISIBLE_MASK_BASIC | E_MSG_COMPOSER_VISIBLE_POSTTO)
>  
>  
> @@ -117,6 +117,11 @@
>  						    EABDestination    **bcc_destv);
>  void        e_msg_composer_hdrs_set_post_to        (EMsgComposerHdrs
*hdrs,
>  						    const char       *post_to);
> +void        e_msg_composer_hdrs_set_post_to_list   (EMsgComposerHdrs
*hdrs,
> +						    GList *urls);
> +void        e_msg_composer_hdrs_set_post_to_base   (EMsgComposerHdrs
*hdrs,
> +					            const char       *base,
> +						    const char       *post_to);
>  void        e_msg_composer_hdrs_set_subject        (EMsgComposerHdrs
*hdrs,
>  						    const char       *subject);
>  
> @@ -127,9 +132,11 @@
>  EABDestination **e_msg_composer_hdrs_get_cc         (EMsgComposerHdrs
*hdrs);
>  EABDestination **e_msg_composer_hdrs_get_bcc        (EMsgComposerHdrs
*hdrs);
>  EABDestination **e_msg_composer_hdrs_get_recipients (EMsgComposerHdrs
*hdrs);
> -char          *e_msg_composer_hdrs_get_post_to     (EMsgComposerHdrs
*hdrs);
>  const char    *e_msg_composer_hdrs_get_subject     (EMsgComposerHdrs
*hdrs);
>  
> +/* list of gchar* uris; this data is to be freed by the caller */
> +GList         *e_msg_composer_hdrs_get_post_to     (EMsgComposerHdrs
*hdrs);
> +
>  GtkWidget  *e_msg_composer_hdrs_get_from_hbox      (EMsgComposerHdrs
*hdrs);
>  GtkWidget  *e_msg_composer_hdrs_get_from_omenu     (EMsgComposerHdrs
*hdrs);
>  GtkWidget  *e_msg_composer_hdrs_get_reply_to_entry (EMsgComposerHdrs
*hdrs);
> --- /home/meilof/cvs/orig/evolution//mail/em-composer-utils.c	2003-12-
02 00:16:21.000000000 -0500
> +++ mail/em-composer-utils.c	2004-01-10 02:00:38.000000000 -0500
> @@ -231,7 +231,7 @@
>  }
>  
>  static CamelMimeMessage *
> -composer_get_message (EMsgComposer *composer, gboolean post, gboolean
save_html_object_data)
> +composer_get_message (EMsgComposer *composer, gboolean post, gboolean
save_html_object_data, gboolean *no_recipients)
>  {
>  	CamelMimeMessage *message = NULL;
>  	EABDestination **recipients, **recipients_bcc;
> @@ -296,10 +296,14 @@
>  	camel_object_unref (cia);
>  	
>  	/* I'm sensing a lack of love, er, I mean recipients. */
> -	if (num == 0 && !post) {
> -		e_notice ((GtkWindow *) composer, GTK_MESSAGE_WARNING,
> -			  _("You must specify recipients in order to send this
message."));
> -		goto finished;
> +	if (num == 0) {
> +		if (post) {
> +			if (no_recipients) *no_recipients = TRUE;
> +		} else {
> +			e_notice ((GtkWindow *) composer, GTK_MESSAGE_WARNING,
> +				  _("You must specify recipients in order to send this
message."));
> +			goto finished;
> +		}
>  	}
>  	
>  	if (num > 0 && (num == num_bcc || shown == 0)) {
> @@ -389,55 +393,85 @@
>  	CamelMimeMessage *message;
>  	CamelMessageInfo *info;
>  	struct _send_data *send;
> -	gboolean post = FALSE;
> -	CamelFolder *folder;
> +	gboolean no_recipients = FALSE;
> +	CamelFolder *mail_folder = NULL, *tmpfldr;
> +	GList *post_folders = NULL, *post_ptr;
>  	XEvolution *xev;
> -	char *url;
> -	
> -	url = e_msg_composer_hdrs_get_post_to ((EMsgComposerHdrs *)
composer->hdrs);
> -	if (url && *url) {
> -		post = TRUE;
> -		
> -		mail_msg_wait (mail_get_folder (url, 0, got_post_folder, &folder,
mail_thread_new));
> -		
> -		if (!folder) {
> -			g_free (url);
> -			return;
> -		}
> -	} else {
> -		folder = outbox_folder;
> -		camel_object_ref (folder);
> +	GList *postlist;
> +
> +	postlist = e_msg_composer_hdrs_get_post_to((EMsgComposerHdrs *)
composer->hdrs);
> +	while (postlist) {
> +		mail_msg_wait (mail_get_folder ((char*)postlist->data, 0,
got_post_folder, &tmpfldr, mail_thread_new));
> +		if (tmpfldr)
> +			post_folders = g_list_append(post_folders, tmpfldr);
> +		postlist = g_list_next(postlist);
>  	}
>  	
> -	g_free (url);
> +	mail_folder = outbox_folder;
> +	camel_object_ref (mail_folder);
>  	
> -	message = composer_get_message (composer, post, FALSE);
> +	if (!post_folders && !mail_folder) return;
> +
> +	message = composer_get_message (composer, post_folders != NULL,
FALSE, &no_recipients);
>  	if (!message)
>  		return;
> -	
> -	if (post) {
> +
> +	if (no_recipients) {
> +		/* we're doing a post with no recipients */
> +		camel_object_unref (mail_folder);
> +		mail_folder = NULL;
> +	}
> +
> +	if (mail_folder) {
> +		/* mail the message */
> +		info = camel_message_info_new ();
> +		info->flags = CAMEL_MESSAGE_SEEN;
> +
> +		send = g_malloc (sizeof (*send));
> +		send->emcs = user_data;
> +		if (send->emcs)
> +			emcs_ref (send->emcs);
> +		send->send = TRUE;
> +		send->composer = composer;
> +		g_object_ref (composer);
> +		gtk_widget_hide (GTK_WIDGET (composer));
> +
> +		e_msg_composer_set_enable_autosave (composer, FALSE);
> +
> +		mail_append_mail (mail_folder, message, info,
composer_send_queued_cb, send);
> +		camel_object_unref (mail_folder);
> +	}
> +
> +	if (post_folders) {
>  		/* Remove the X-Evolution* headers if we are in Post-To mode */
>  		xev = mail_tool_remove_xevolution_headers (message);
>  		mail_tool_destroy_xevolution (xev);
> +
> +		/* mail the message */
> +		info = camel_message_info_new ();
> +		info->flags = CAMEL_MESSAGE_SEEN;
> +
> +		post_ptr = post_folders;
> +		while (post_ptr) {
> +			send = g_malloc (sizeof (*send));
> +			send->emcs = user_data;
> +			if (send->emcs)
> +				emcs_ref (send->emcs);
> +			send->send = FALSE;
> +			send->composer = composer;
> +			g_object_ref (composer);
> +			gtk_widget_hide (GTK_WIDGET (composer));
> +
> +			e_msg_composer_set_enable_autosave (composer, FALSE);
> +
> +			mail_append_mail ((CamelFolder*)post_ptr->data, message, info,
composer_send_queued_cb, send);
> +			camel_object_unref ((CamelFolder*)post_ptr->data);
> +
> +			post_ptr = g_list_next(post_ptr);
> +		}
>  	}
> -	
> -	info = camel_message_info_new ();
> -	info->flags = CAMEL_MESSAGE_SEEN;
> -	
> -	send = g_malloc (sizeof (*send));
> -	send->emcs = user_data;
> -	if (send->emcs)
> -		emcs_ref (send->emcs);
> -	send->send = !post;
> -	send->composer = composer;
> -	g_object_ref (composer);
> -	gtk_widget_hide (GTK_WIDGET (composer));
> -	
> -	e_msg_composer_set_enable_autosave (composer, FALSE);
> -	
> -	mail_append_mail (folder, message, info, composer_send_queued_cb,
send);
> +
>  	camel_object_unref (message);
> -	camel_object_unref (folder);
>  }
>  
>  struct _save_draft_info {
> --- /home/meilof/cvs/orig/evolution//mail/em-folder-browser.c	2004-01-
10 16:26:28.000000000 -0500
> +++ mail/em-folder-browser.c	2004-01-10 16:57:01.000000000 -0500
> @@ -618,11 +618,7 @@
>  emfb_mail_post(BonoboUIComponent *uid, void *data, const char *path)
>  {
>  	EMFolderView *emfv = data;
> -	char *url;
> -	
> -	url = mail_tools_folder_to_url (emfv->folder);
> -	em_utils_post_to_url (url);
> -	g_free (url);
> +	em_utils_post_to_folder (emfv->folder);
>  }
>  
>  static void
> --- /home/meilof/cvs/orig/evolution//mail/em-folder-selection-button.
c	2003-12-10 13:36:13.000000000 -0500
> +++ mail/em-folder-selection-button.c	2004-01-09 21:58:46.000000000 -
0500
> @@ -50,10 +50,13 @@
>  	GtkWidget *icon;
>  	GtkWidget *label;
>  	
> -	char *uri;
> +	char *uri;   /* for single-select mode */
> +	GList *uris; /* for multi-select mode */
>  	
>  	char *title;
>  	char *caption;
> +
> +	gboolean multiple_select;
>  };
>  
>  enum {
> @@ -120,25 +123,14 @@
>  set_contents (EMFolderSelectionButton *button)
>  {
>  	struct _EMFolderSelectionButtonPrivate *priv = button->priv;
> -	const char *folder_name;
> -	CamelURL *url;
> -	
> -	if (priv->uri == NULL
> -	    || (url = camel_url_new (priv->uri, NULL)) == NULL) {
> -		set_contents_unselected (button);
> -		return;
> -	}
> -	
> -	folder_name = url->fragment ? url->fragment : url->path + 1;
> +	char *folder_name = em_utils_folder_name_from_uri(priv->uri);
>  	
> -	if (folder_name == NULL) {
> -		camel_url_free (url);
> +	if (folder_name) {
> +		gtk_label_set_text (GTK_LABEL (priv->label), folder_name);
> +		g_free(folder_name);
> +	} else {
>  		set_contents_unselected (button);
> -		return;
>  	}
> -	
> -	gtk_label_set_text (GTK_LABEL (priv->label), folder_name);
> -	camel_url_free (url);
>  }
>  
>  static void
> @@ -149,6 +141,8 @@
>  	
>  	priv = g_new0 (struct _EMFolderSelectionButtonPrivate, 1);
>  	emfsb->priv = priv;
> +
> +	priv->multiple_select = FALSE;
>  	
>  	box = gtk_hbox_new (FALSE, 4);
>  	
> @@ -178,6 +172,10 @@
>  em_folder_selection_button_finalize (GObject *obj)
>  {
>  	struct _EMFolderSelectionButtonPrivate *priv =
((EMFolderSelectionButton *) obj)->priv;
> +
> +	GList *lst = ((EMFolderSelectionButton*)obj)->priv->uris;
> +	g_list_foreach (lst, (GFunc)g_free, NULL);
> +	g_list_free(lst);
>  	
>  	g_free (priv->title);
>  	g_free (priv->caption);
> @@ -191,10 +189,17 @@
>  emfsb_selector_response (EMFolderSelector *emfs, int response,
EMFolderSelectionButton *button)
>  {
>  	if (response == GTK_RESPONSE_OK) {
> -		const char *uri = em_folder_selector_get_selected_uri (emfs);
> +		if (button->priv->multiple_select) {
> +			GList *uris = em_folder_selector_get_selected_uris (emfs);
> +
> +			em_folder_selection_button_set_selection_mult (button, uris);
> +			g_signal_emit (button, signals[SELECTED], 0);

Do you have to free the uri list here?

> +		} else {
> +			const char *uri = em_folder_selector_get_selected_uri (emfs);
>  		
> -		em_folder_selection_button_set_selection (button, uri);
> -		g_signal_emit (button, signals[SELECTED], 0);
> +			em_folder_selection_button_set_selection (button, uri);
> +			g_signal_emit (button, signals[SELECTED], 0);
> +		}
>  	}
>  	
>  	gtk_widget_destroy ((GtkWidget *) emfs);
> @@ -213,9 +218,13 @@
>  	
>  	model = mail_component_peek_tree_model (mail_component_peek ());
>  	emft = (EMFolderTree *) em_folder_tree_new_with_model (model);
> +	em_folder_tree_set_multiselect(emft, priv->multiple_select);
>  	
>  	dialog = em_folder_selector_new (emft,
EM_FOLDER_SELECTOR_CAN_CREATE, priv->title, priv->caption);
> -	em_folder_selector_set_selected ((EMFolderSelector *) dialog, priv-
>uri);
> +	if (priv->multiple_select)
> +		em_folder_selector_set_selected_list((EMFolderSelector *) dialog,
priv->uris);
> +	else
> +		em_folder_selector_set_selected ((EMFolderSelector *) dialog, priv-
>uri);
>  	g_signal_connect (dialog, "response", G_CALLBACK
(emfsb_selector_response), button);
>  	gtk_widget_show (dialog);
>  }
> @@ -253,3 +262,68 @@
>  	
>  	return button->priv->uri;
>  }
> +
> +void
> +em_folder_selection_button_set_selection_mult
(EMFolderSelectionButton *button, GList *uris)
> +{
> +	struct _EMFolderSelectionButtonPrivate *priv = button->priv;
> +	char *caption, *tmp, *tmp2;
> +
> +	g_return_if_fail (EM_IS_FOLDER_SELECTION_BUTTON (button));
> +
> +	if (priv->uris) {
> +		g_list_foreach (priv->uris, (GFunc)g_free, NULL);
> +		g_list_free(priv->uris);
> +		priv->uris = NULL;
> +	}
> +
> +	priv->uris = uris;
> +
> +	/* compile the name */
> +	caption = g_strdup("");

This looks familiar, use a g_string again.

> +	while (uris) {
> +		tmp = em_utils_folder_name_from_uri(uris->data);
> +		if (tmp) {
> +			tmp2 = g_strconcat(caption, ", ", tmp, NULL);
> +			g_free(caption);
> +			caption = tmp2;
> +			g_free(tmp);
> +			uris = g_list_next(uris);
> +		} else {
> +			/* apparently, we do not know this folder, so we'll just skip it
*/
> +			g_free(uris->data);
> +			uris = g_list_next(uris);
> +			priv->uris = g_list_remove(priv->uris, uris->data);

priv->uris = g_list_remove_link(priv->uris, uris) is more efficient
(you have to have another pointer to keep next though).


> +		}
> +	}
> +
> +	if (caption[0])
> +		gtk_label_set_text (GTK_LABEL (priv->label), caption + 2);
> +	else
> +		set_contents_unselected(button);
> +	
> +	g_free(caption);
> +}
> +
> +GList *
> +em_folder_selection_button_get_selection_mult
(EMFolderSelectionButton *button)
> +{
> +	g_return_val_if_fail (EM_IS_FOLDER_SELECTION_BUTTON (button), NULL);
> +
> +	return button->priv->uris;
> +}
> +
> +void
> +em_folder_selection_button_set_multiselect (EMFolderSelectionButton
*button, gboolean value)
> +{
> +	button->priv->multiple_select = value;
> +
> +	
> +}
> +
> +gboolean
> +em_folder_selection_button_get_multiselect (EMFolderSelectionButton
*button)
> +{
> +	return button->priv->multiple_select;
> +}
> --- /home/meilof/cvs/orig/evolution//mail/em-folder-selector.c	2003-
12-04 15:04:43.000000000 -0500
> +++ mail/em-folder-selector.c	2004-01-09 21:58:46.000000000 -0500
> @@ -280,6 +280,11 @@
>  	em_folder_tree_set_selected (emfs->emft, uri);
>  }
>  
> +void
> +em_folder_selector_set_selected_list (EMFolderSelector *emfs, GList
*list)
> +{
> +	em_folder_tree_set_selected_list (emfs->emft, list);
> +}
>  
>  const char *
>  em_folder_selector_get_selected_uri (EMFolderSelector *emfs)
> @@ -329,6 +334,13 @@
>  	return uri;
>  }
>  
> +GList *em_folder_selector_get_selected_uris (EMFolderSelector *emfs)
{
> +	return em_folder_tree_get_selected_uris (emfs->emft);
> +}


> +GList *em_folder_selector_get_selected_paths (EMFolderSelector *emfs)
{
> +	return em_folder_tree_get_selected_paths (emfs->emft);
> +}

for consistency, use the standard layout:

type
name(args)
{
}

>  
>  const char *
>  em_folder_selector_get_selected_path (EMFolderSelector *emfs)
> --- /home/meilof/cvs/orig/evolution//mail/em-folder-tree.c	2003-12-14
19:09:47.000000000 -0500
> +++ mail/em-folder-tree.c	2004-01-10 16:20:47.000000000 -0500
> @@ -71,6 +71,10 @@
>  	
>  	char *selected_uri;
>  	char *selected_path;
> +
> +	gboolean do_multiselect;
> +	/* when doing a multiselect, folders that we didn't find */
> +	GList *lost_folders;
>  	
>  	guint save_state_id;
>  	
> @@ -130,6 +134,36 @@
>  static gboolean emft_tree_button_press (GtkWidget *treeview,
GdkEventButton *event, EMFolderTree *emft);
>  static void emft_tree_selection_changed (GtkTreeSelection *selection,
EMFolderTree *emft);
>  
> +typedef struct {
> +	GtkTreeModel *model;
> +	GtkTreeIter *iter;
> +	gboolean set;
> +} __gtk_my_tree_selection_data;

this shouldn't be prefixed with __gtk since its not part of gtk, perhaps
call it
 emft_selection_data_t

if typedefed, or even just a named struct:

 struct _emft_selection_data

(which is a convention used fairly widely in the mailer for internal
types)

> +static void selfunc(GtkTreeModel *model,
> +                    GtkTreePath *path,
> +                    GtkTreeIter *iter,
> +                    gpointer data)
> +{
> +	__gtk_my_tree_selection_data *dat = (__gtk_my_tree_selection_data*)
data;
> +	dat->model = model;
> +	if (!dat->set) *(dat->iter) = *iter;
> +	dat->set = TRUE;
> +}
> +
> +static gboolean gtk_my_tree_selection_get_selected(GtkTreeSelection
*selection,

don't start this with gtk_, i think emft_selection_get_selected would
be appropriate for this internal, static function.

(emft = 'evolution mail folder tree')

> +                                                   GtkTreeModel
**model,
> +                                                   GtkTreeIter *iter)
> +{
> +	__gtk_my_tree_selection_data dat = { NULL, iter, FALSE };
> +	if (gtk_tree_selection_get_mode(selection) ==
GTK_SELECTION_MULTIPLE) {
> +		gtk_tree_selection_selected_foreach(selection, selfunc, &dat);
> +		if (model) *model = dat.model;
> +		return dat.set;
> +	} else {
> +		return gtk_tree_selection_get_selected(selection, model, iter);
> +	}
> +}
>  
>  static GtkVBoxClass *parent_class = NULL;
>  
> @@ -289,6 +323,7 @@
>  	struct _EMFolderTreePrivate *priv;
>  	
>  	priv = g_new0 (struct _EMFolderTreePrivate, 1);
> +	priv->lost_folders = NULL;
>  	priv->selected_uri = NULL;
>  	priv->selected_path = NULL;
>  	priv->treeview = NULL;
> @@ -300,6 +335,13 @@
>  em_folder_tree_finalize (GObject *obj)
>  {
>  	EMFolderTree *emft = (EMFolderTree *) obj;
> +
> +	/* clear list of lost uris */
> +	if (emft->priv->lost_folders) {
> +		g_list_foreach (emft->priv->lost_folders, (GFunc)g_free, NULL);
> +		g_list_free(emft->priv->lost_folders);
> +		emft->priv->lost_folders = NULL;
> +	}
>  	
>  	g_free (emft->priv->selected_uri);
>  	g_free (emft->priv->selected_path);
> @@ -431,7 +473,7 @@
>  		return;
>  	
>  	id = g_strndup (key, p - key);
> -	if ((account = mail_config_get_account_by_uid (id))) {
> +	if ((account = mail_config_get_account_by_uid (id)) && account-
>enabled) {
>  		CamelException ex;
>  		CamelStore *store;
>  		
> @@ -531,6 +573,81 @@
>  					      GDK_ACTION_COPY | GDK_ACTION_MOVE);
>  }
>  
> +void
> +em_folder_tree_set_multiselect(EMFolderTree *tree, gboolean mode)
> +{
> +	GtkTreeSelection *sel = gtk_tree_view_get_selection((GtkTreeView *)
tree->priv->treeview);
> +	tree->priv->do_multiselect = mode;
> +	gtk_tree_selection_set_mode(sel, (mode) ? GTK_SELECTION_MULTIPLE :
GTK_SELECTION_SINGLE);
> +}
> +
> +static void get_selected_uris_iterate(GtkTreeModel *model,
> +                                      GtkTreePath *path,
> +                                      GtkTreeIter *iter,
> +                                      gpointer data)
> +{
> +	char *uri;
> +	GList **list = (GList **)data;
> +	gtk_tree_model_get (model, iter, /*COL_STRING_FOLDER_PATH, &path,*/
> +			    COL_STRING_URI, &uri, -1);
> +	*list = g_list_append(*list, g_strdup(uri));
> +}
> +
> +GList *
> +em_folder_tree_get_selected_uris (EMFolderTree *emft)
> +{
> +	GList *list = NULL;
> +	GList *lost = emft->priv->lost_folders;
> +	GtkTreeSelection *selection = gtk_tree_view_get_selection (emft-
>priv->treeview);
> +	
> +	/* at first, add lost uris */
> +	while (lost) {
> +		list = g_list_append(list, g_strdup((char*)lost->data));
> +		lost = g_list_next(lost);
> +	}
> +	
> +	gtk_tree_selection_selected_foreach (selection,
get_selected_uris_iterate, &list);
> +	return list;
> +}
> +
> +static void get_selected_uris_path_iterate(GtkTreeModel *model,
> +                                           GtkTreePath *treepath,
> +                                           GtkTreeIter *iter,
> +                                           gpointer data)
> +{
> +	char *path;
> +	GList **list = (GList **)data;

add blanks after declaration blocks

> +	gtk_tree_model_get (model, iter, COL_STRING_FOLDER_PATH, &path, -1);
> +	*list = g_list_append(*list, g_strdup(path));
> +}
> +
> +GList *
> +em_folder_tree_get_selected_paths (EMFolderTree *emft)
> +{
> +	GList *list = NULL;
> +	GtkTreeSelection *selection = gtk_tree_view_get_selection (emft-
>priv->treeview);
> +	gtk_tree_selection_selected_foreach (selection,
get_selected_uris_path_iterate, &list);
> +	return list;
> +}
> +
> +void
> +em_folder_tree_set_selected_list (EMFolderTree *emft, GList *list)
> +{
> +	struct _EMFolderTreePrivate *priv = emft->priv;
> +	
> +	/* clear list of lost uris */
> +	if (priv->lost_folders) {
> +		g_list_foreach (priv->lost_folders, (GFunc)g_free, NULL);
> +		g_list_free(priv->lost_folders);
> +		priv->lost_folders = NULL;
> +	}
> +
> +	while (list) {
> +		em_folder_tree_set_selected(emft, (char *)list->data);
> +		list = g_list_next(list);
> +	}
> +}
> +
>  
>  #if 0
>  static void
> @@ -592,7 +709,7 @@
>  	GtkTreeStore *model;
>  	GtkTreePath *path;
>  	gboolean load;
> -	
> +
>  	/* check that we haven't been destroyed */
>  	if (priv->treeview == NULL)
>  		return;
> @@ -607,7 +724,7 @@
>  	}
>  	
>  	model = (GtkTreeStore *) gtk_tree_view_get_model (priv->treeview);
> -	
> +
>  	path = gtk_tree_row_reference_get_path (m->root);
>  	gtk_tree_model_get_iter ((GtkTreeModel *) model, &root, path);
>  	gtk_tree_path_free (path);
> @@ -616,8 +733,11 @@
>  	gtk_tree_model_get ((GtkTreeModel *) model, &root,
>  			    COL_BOOL_LOAD_SUBDIRS, &load,
>  			    -1);
> -	if (!load)
> +	if (!load) {
> +		if (priv->do_multiselect && m->select_uri)
> +			priv->lost_folders = g_list_append(priv->lost_folders, g_strdup(m-
>select_uri));
>  		return;
> +	}
>  	
>  	/* get the first child (which will be a dummy node) */
>  	gtk_tree_model_iter_children ((GtkTreeModel *) model, &iter, &root);
> @@ -655,7 +775,7 @@
>  emft_get_folder_info__free (struct _mail_msg *mm)
>  {
>  	struct _EMFolderTreeGetFolderInfo *m = (struct
_EMFolderTreeGetFolderInfo *) mm;
> -	
> +
>  	camel_store_free_folder_info (m->store, m->fi);
>  	
>  	gtk_tree_row_reference_free (m->root);
> @@ -1172,7 +1292,7 @@
>  		return;
>  	
>  	selection = gtk_tree_view_get_selection (priv->treeview);
> -	gtk_tree_selection_get_selected (selection, &model, &iter);
> +	gtk_my_tree_selection_get_selected (selection, &model, &iter);
>  	gtk_tree_model_get (model, &iter, COL_STRING_FOLDER_PATH, &path,
>  			    COL_POINTER_CAMEL_STORE, &store, -1);
>  	
> @@ -1195,7 +1315,7 @@
>  	char *title, *path;
>  	
>  	selection = gtk_tree_view_get_selection (priv->treeview);
> -	gtk_tree_selection_get_selected (selection, &model, &iter);
> +	gtk_my_tree_selection_get_selected (selection, &model, &iter);
>  	gtk_tree_model_get (model, &iter, COL_STRING_FOLDER_PATH, &path, -
1);
>  	
>  	dialog = gtk_message_dialog_new (NULL,
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
> @@ -1232,7 +1352,7 @@
>  	size_t base_len;
>  	
>  	selection = gtk_tree_view_get_selection (priv->treeview);
> -	gtk_tree_selection_get_selected (selection, &model, &iter);
> +	gtk_my_tree_selection_get_selected (selection, &model, &iter);
>  	gtk_tree_model_get (model, &iter, COL_STRING_FOLDER_PATH,
&folder_path,
>  			    COL_STRING_DISPLAY_NAME, &name,
>  			    COL_POINTER_CAMEL_STORE, &store,
> @@ -1481,7 +1601,7 @@
>  	char *uri;
>  	
>  	selection = gtk_tree_view_get_selection (priv->treeview);
> -	gtk_tree_selection_get_selected (selection, &model, &iter);
> +	gtk_my_tree_selection_get_selected (selection, &model, &iter);
>  	gtk_tree_model_get (model, &iter, COL_STRING_URI, &uri, -1);
>  	
>  	mail_get_folder (uri, 0, emft_popup_properties_got_folder, emft,
mail_thread_new);
> @@ -1550,7 +1670,7 @@
>  	GtkTreeIter iter;
>  	char *path, *uri;
>  	
> -	if (!gtk_tree_selection_get_selected (selection, &model, &iter))
> +	if (!gtk_my_tree_selection_get_selected (selection, &model, &iter))
>  		return;
>  	
>  	gtk_tree_model_get (model, &iter, COL_STRING_FOLDER_PATH, &path,
> @@ -1580,7 +1700,7 @@
>  	const char *top;
>  	char *path, *p;
>  	CamelURL *url;
> -	
> +
>  	g_return_if_fail (EM_IS_FOLDER_TREE (emft));
>  	
>  	priv = emft->priv;
> @@ -1642,7 +1762,7 @@
>  		row = si->row;
>  		top = NULL;
>  	}
> -	
> +
>  	/* FIXME: this gets all the subfolders of our first loaded
>  	 * parent folder - ideally we'd only get what we needed, but
>  	 * it's probably not worth the effort */
> --- /home/meilof/cvs/orig/evolution//mail/em-folder-selection-button.
h	2003-11-13 14:02:07.000000000 -0500
> +++ mail/em-folder-selection-button.h	2004-01-09 21:58:59.000000000 -
0500
> @@ -62,6 +62,12 @@
>  void        em_folder_selection_button_set_selection
(EMFolderSelectionButton *button, const char *uri);
>  const char *em_folder_selection_button_get_selection
(EMFolderSelectionButton *button);
>  
> +void   em_folder_selection_button_set_selection_mult
(EMFolderSelectionButton *button, GList *uris);
> +GList *em_folder_selection_button_get_selection_mult
(EMFolderSelectionButton *button);
> +
> +void     em_folder_selection_button_set_multiselect
(EMFolderSelectionButton *button, gboolean value);
> +gboolean em_folder_selection_button_get_multiselect
(EMFolderSelectionButton *button);
> +
>  #ifdef __cplusplus
>  }
>  #endif /* __cplusplus */
> --- /home/meilof/cvs/orig/evolution//mail/em-folder-selector.h	2003-
12-10 00:29:06.000000000 -0500
> +++ mail/em-folder-selector.h	2004-01-09 21:58:59.000000000 -0500
> @@ -75,10 +75,14 @@
>  GtkWidget *em_folder_selector_create_new (struct _EMFolderTree *emft,
guint32 flags, const char *title, const char *text);
>  
>  void em_folder_selector_set_selected (EMFolderSelector *emfs, const
char *uri);
> +void em_folder_selector_set_selected_list (EMFolderSelector *emfs,
GList *list);
>  
>  const char *em_folder_selector_get_selected_uri (EMFolderSelector
*emfs);
>  const char *em_folder_selector_get_selected_path (EMFolderSelector
*emfs);
>  
> +GList *em_folder_selector_get_selected_uris (EMFolderSelector *emfs);
> +GList *em_folder_selector_get_selected_paths (EMFolderSelector
*emfs);
> +
>  #ifdef cplusplus
>  }
>  #endif /* cplusplus */
> --- /home/meilof/cvs/orig/evolution//mail/em-folder-tree.h	2003-12-04
15:04:43.000000000 -0500
> +++ mail/em-folder-tree.h	2004-01-09 21:58:59.000000000 -0500
> @@ -65,6 +65,12 @@
>  
>  void em_folder_tree_enable_drag_and_drop (EMFolderTree *emft);
>  
> +void em_folder_tree_set_multiselect (EMFolderTree *emft, gboolean
mode);
> +
> +void em_folder_tree_set_selected_list (EMFolderTree *emft, GList
*list);
> +GList *em_folder_tree_get_selected_uris (EMFolderTree *emft);
> +GList *em_folder_tree_get_selected_paths (EMFolderTree *emft);
> +
>  void em_folder_tree_set_selected (EMFolderTree *emft, const char
*uri);
>  const char *em_folder_tree_get_selected_uri (EMFolderTree *emft);
>  const char *em_folder_tree_get_selected_path (EMFolderTree *emft);
> --- /home/meilof/cvs/orig/evolution//mail/em-utils.c	2004-01-10
16:26:30.000000000 -0500
> +++ mail/em-utils.c	2004-01-10 17:00:38.000000000 -0500
> @@ -52,7 +52,7 @@
>  #include "em-composer-utils.h"
>  #include "em-format-quote.h"
>  
> -static EAccount *guess_account (CamelMimeMessage *message);
> +static EAccount *guess_account (CamelMimeMessage *message,
CamelFolder *folder);
>  static void emu_save_part_done (CamelMimePart *part, char *name, int
done, void *data);
>  
>  /**
> @@ -343,6 +343,43 @@
>  }
>  
>  /**
> + * em_utils_post_to_folder:
> + * @folder: folder
> + *
> + * Opens a new composer window as a child window of @parent's
toplevel
> + * window. If @folder is non-NULL, the composer will default to
posting
> + * mail to the folder specified by @folder.
> + **/
> +void
> +em_utils_post_to_folder (CamelFolder *folder)
> +{
> +	EMsgComposer *composer;
> +	EAccount *account;
> +
> +	composer = e_msg_composer_new_with_type (E_MSG_COMPOSER_POST);
> +
> +	if (folder != NULL) {
> +		char *url = mail_tools_folder_to_url (folder);

blank line

> +		e_msg_composer_hdrs_set_post_to ((EMsgComposerHdrs *)
((EMsgComposer *) composer)->hdrs, url);
> +		g_free (url);
> +
> +		url = camel_url_to_string (CAMEL_SERVICE(folder->parent_store)-
>url, CAMEL_URL_HIDE_ALL);
> +		account = mail_config_get_account_by_source_url(url);
> +		g_free(url);
> +
> +		if (account)
> +			e_msg_composer_set_headers(composer, account->name, NULL, NULL,
NULL, "");
> +	}
> +
> +	em_composer_utils_setup_default_callbacks (composer);
> +
> +	e_msg_composer_unset_changed (composer);
> +	e_msg_composer_drop_editor_undo (composer);
> +
> +	gtk_widget_show ((GtkWidget *) composer);
> +}
> +
> +/**
>   * em_utils_post_to_url:
>   * @url: mailto url
>   *
> @@ -355,7 +392,7 @@
>  {
>  	EMsgComposer *composer;
>  	
> -	composer = e_msg_composer_new_post ();
> +	composer = e_msg_composer_new_with_type (E_MSG_COMPOSER_POST);
>  	
>  	if (url != NULL)
>  		e_msg_composer_hdrs_set_post_to ((EMsgComposerHdrs *)
((EMsgComposer *) composer)->hdrs, url);
> @@ -647,7 +684,7 @@
>  	while (camel_medium_get_header (CAMEL_MEDIUM (message), "Delivered-
To"))
>  		camel_medium_remove_header (CAMEL_MEDIUM (message), "Delivered-
To");
>  	
> -	account = guess_account (message);
> +	account = guess_account (message, NULL);
>  	
>  	composer = e_msg_composer_new_redirect (message, account ? account-
>name : NULL);
>  	
> @@ -795,7 +832,8 @@
>  
>  static EMsgComposer *
>  reply_get_composer (CamelMimeMessage *message, EAccount *account,
> -		    CamelInternetAddress *to, CamelInternetAddress *cc)
> +		    CamelInternetAddress *to, CamelInternetAddress *cc,
> +		    CamelFolder *folder, const char *postto)
>  {
>  	const char *message_id, *references;
>  	EABDestination **tov, **ccv;
> @@ -805,13 +843,19 @@
>  	g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
>  	g_return_val_if_fail (to == NULL || CAMEL_IS_INTERNET_ADDRESS (to),
NULL);
>  	g_return_val_if_fail (cc == NULL || CAMEL_IS_INTERNET_ADDRESS (cc),
NULL);
> -	
> -	composer = e_msg_composer_new ();
> -	
> +
>  	/* construct the tov/ccv */
>  	tov = em_utils_camel_address_to_destination (to);
>  	ccv = em_utils_camel_address_to_destination (cc);
> -	
> +
> +	if (tov || ccv) {
> +		if (postto)
> +			composer = e_msg_composer_new_with_type
(E_MSG_COMPOSER_MAIL_POST);
> +		else
> +			composer = e_msg_composer_new_with_type (E_MSG_COMPOSER_MAIL);
> +	} else
> +		composer = e_msg_composer_new_with_type (E_MSG_COMPOSER_POST);
> +
>  	/* Set the subject of the new message. */
>  	if ((subject = (char *) camel_mime_message_get_subject (message))) {
>  		if (strncasecmp (subject, "Re: ", 4) != 0)
> @@ -821,11 +865,25 @@
>  	} else {
>  		subject = g_strdup ("");
>  	}
> -	
> +
>  	e_msg_composer_set_headers (composer, account ? account->name :
NULL, tov, ccv, NULL, subject);
>  	
>  	g_free (subject);
>  	
> +	/* add post-to, if nessecary */
> +	if (postto) {
> +		char *store_url = NULL;
> +
> +		if (folder) {
> +			store_url = camel_url_to_string (CAMEL_SERVICE(folder-
>parent_store)->url, CAMEL_URL_HIDE_ALL);
> +			if (store_url[strlen(store_url)-1] == '/')
> +				store_url[strlen(store_url)-1] = '\0';
> +		}
> +
> +		e_msg_composer_hdrs_set_post_to_base (E_MSG_COMPOSER_HDRS
(composer->hdrs), store_url ? store_url : "", postto);
> +		g_free(store_url);
> +	}
> +
>  	/* Add In-Reply-To and References. */
>  	message_id = camel_medium_get_header (CAMEL_MEDIUM (message),
"Message-Id");
>  	references = camel_medium_get_header (CAMEL_MEDIUM (message),
"References");
> @@ -851,14 +909,26 @@
>  }
>  
>  static EAccount *
> -guess_account (CamelMimeMessage *message)
> +guess_account (CamelMimeMessage *message, CamelFolder *folder)
>  {
>  	const CamelInternetAddress *to, *cc;
>  	GHashTable *account_hash;
>  	EAccount *account = NULL;
>  	const char *addr;
>  	int i;
> -	
> +
> +	/* check for newsgroup header */
> +	const char *posthdr = camel_medium_get_header (CAMEL_MEDIUM
(message), "Newsgroups");
> +	char *tmp;

declarations in the declaration block, no blank lines.

> +	if (posthdr && folder) {
> +		/* this was posted at a newsgroup! */
> +		tmp = camel_url_to_string (CAMEL_SERVICE(folder->parent_store)-
>url, CAMEL_URL_HIDE_ALL);
> +		account = mail_config_get_account_by_source_url(tmp);
> +		g_free(tmp);
> +		if (account)
> +			goto found;
> +	}
> +
>  	to = camel_mime_message_get_recipients (message,
CAMEL_RECIPIENT_TYPE_TO);
>  	cc = camel_mime_message_get_recipients (message,
CAMEL_RECIPIENT_TYPE_CC);
>  	
> @@ -891,12 +961,20 @@
>  }
>  
>  static void
> -get_reply_sender (CamelMimeMessage *message, CamelInternetAddress
**to)
> +get_reply_sender (CamelMimeMessage *message, CamelInternetAddress
**to, const char **postto)
>  {
>  	const CamelInternetAddress *reply_to;
> -	const char *name, *addr;
> +	const char *name, *addr, *posthdr;
>  	int i;
>  	
> +	/* check whether there is a 'Newsgroups: ' header in there */
> +	posthdr = camel_medium_get_header (CAMEL_MEDIUM(message),
"Newsgroups");
> +	if (posthdr && postto) {
> +		*postto = posthdr;
> +		while (**postto == ' ') (*postto)++;

Should postto be a list of addresses?  I presume at the moment it is
taken as a space-separated string list of addresses?

> +		return;
> +	} 
> +
>  	reply_to = camel_mime_message_get_reply_to (message);
>  	if (!reply_to)
>  		reply_to = camel_mime_message_get_from (message);
> @@ -907,6 +985,7 @@
>  		for (i = 0; camel_internet_address_get (reply_to, i, &name, &addr);
i++)
>  			camel_internet_address_add (*to, name, addr);
>  	}
> +
>  }
>  
>  static gboolean
> @@ -967,13 +1046,20 @@
>  }
>  
>  static void
> -get_reply_all (CamelMimeMessage *message, CamelInternetAddress **to,
CamelInternetAddress **cc)
> +get_reply_all (CamelMimeMessage *message, CamelInternetAddress **to,
CamelInternetAddress **cc, const char **postto)
>  {
>  	const CamelInternetAddress *reply_to, *to_addrs, *cc_addrs;
> -	const char *name, *addr;
> +	const char *name, *addr, *posthdr;
>  	GHashTable *rcpt_hash;
>  	int i;
>  	
> +	/* check whether there is a 'Newsgroups: ' header in there */
> +	posthdr = camel_medium_get_header (CAMEL_MEDIUM(message),
"Newsgroups");
> +	if (posthdr && postto) {
> +		*postto = posthdr;
> +		while (**postto == ' ') (*postto)++;

This looks familiar.  Any way to reuse the code?  Should probably have
a camel-mime-utils function to parse the newgroups header into a list
of newsgroups.

We have a CamelNewsAddress class which cam be used like the
CamelInternetAddress 'to' and 'cc' addresses, to store a list of
addresses (newgroups).  Perhaps that should be finished/used
everwhere, rather than strings (but it might need a lot of work yet).

> +	}
> +
>  	rcpt_hash = generate_account_hash ();
>  	
>  	reply_to = camel_mime_message_get_reply_to (message);
> @@ -1079,21 +1165,21 @@
>  	EMsgComposer *composer;
>  	EAccount *account;
>  	
> -	account = guess_account (message);
> +	account = guess_account (message, NULL);
>  	
>  	switch (mode) {
>  	case REPLY_MODE_SENDER:
> -		get_reply_sender (message, &to);
> +		get_reply_sender (message, &to, NULL);
>  		break;
>  	case REPLY_MODE_LIST:
>  		if (get_reply_list (message, &to))
>  			break;
>  	case REPLY_MODE_ALL:
> -		get_reply_all (message, &to, &cc);
> +		get_reply_all (message, &to, &cc, NULL);
>  		break;
>  	}
>  	
> -	composer = reply_get_composer (message, account, to, cc);
> +	composer = reply_get_composer (message, account, to, cc, NULL,
NULL);
>  	e_msg_composer_add_message_attachments (composer, message, TRUE);
>  	
>  	if (to != NULL)
> @@ -1114,19 +1200,20 @@
>  reply_to_message (CamelFolder *folder, const char *uid,
CamelMimeMessage *message, void *user_data)
>  {
>  	CamelInternetAddress *to = NULL, *cc = NULL;
> +	const char *postto = NULL;
>  	EMsgComposer *composer;
>  	EAccount *account;
>  	guint32 flags;
>  	int mode;
>  	
>  	mode = GPOINTER_TO_INT (user_data);
> -	
> -	account = guess_account (message);
> +
> +	account = guess_account (message, folder);
>  	flags = CAMEL_MESSAGE_ANSWERED | CAMEL_MESSAGE_SEEN;
>  	
>  	switch (mode) {
>  	case REPLY_MODE_SENDER:
> -		get_reply_sender (message, &to);
> +		get_reply_sender (message, &to, &postto);
>  		break;
>  	case REPLY_MODE_LIST:
>  		flags |= CAMEL_MESSAGE_ANSWERED_ALL;
> @@ -1134,11 +1221,11 @@
>  			break;
>  	case REPLY_MODE_ALL:
>  		flags |= CAMEL_MESSAGE_ANSWERED_ALL;
> -		get_reply_all (message, &to, &cc);
> +		get_reply_all (message, &to, &cc, &postto);
>  		break;
>  	}
>  	
> -	composer = reply_get_composer (message, account, to, cc);
> +	composer = reply_get_composer (message, account, to, cc, folder,
postto);
>  	e_msg_composer_add_message_attachments (composer, message, TRUE);
>  	
>  	if (to != NULL)
> @@ -1187,12 +1274,12 @@
>  	EAccount *account;
>  	guint32 flags;
>  	
> -	account = guess_account (message);
> +	account = guess_account (message, folder);
>  	flags = CAMEL_MESSAGE_ANSWERED | CAMEL_MESSAGE_SEEN;
>  	
> -	get_reply_sender (message, &to);
> +	get_reply_sender (message, &to, NULL);
>  	
> -	composer = e_msg_composer_new_post ();
> +	composer = e_msg_composer_new_with_type (E_MSG_COMPOSER_MAIL_POST);
>  	
>  	/* construct the tov/ccv */
>  	tov = em_utils_camel_address_to_destination (to);
> @@ -2374,3 +2461,25 @@
>  	/* Now empty the local trash folder */
>  	mail_empty_trash (NULL, NULL, NULL);
>  }
> +
> +char *
> +em_utils_folder_name_from_uri (char *uri)
> +{
> +	CamelURL *url;
> +	char *folder_name;
> +
> +	if (uri == NULL
> +	    || (url = camel_url_new (uri, NULL)) == NULL)
> +	    return NULL;
> +
> +	folder_name = url->fragment ? url->fragment : url->path + 1;

Stricly speaking you need to check the provider flags to see if you
can use the fragment or not, but i think this check is ok for all
practical cases (a name of '' could confuse it).

> +	if (folder_name == NULL) {
> +		camel_url_free (url);
> +		return NULL;
> +	}
> +
> +	folder_name = g_strdup(folder_name);
> +	camel_url_free (url);
> +	return folder_name;
> +}
> --- /home/meilof/cvs/orig/evolution//mail/em-utils.h	2003-12-22
09:50:57.000000000 -0500
> +++ mail/em-utils.h	2004-01-09 21:59:04.000000000 -0500
> @@ -59,6 +59,7 @@
>  
>  /* FIXME: mailto?  url?  should make up its mind what its called.
imho use 'uri' */
>  void em_utils_compose_new_message_with_mailto (const char *url);
> +void em_utils_post_to_folder (struct _CamelFolder *folder);
>  void em_utils_post_to_url (const char *url);
>  
>  void em_utils_edit_message (struct _CamelMimeMessage *message);
> @@ -121,6 +122,10 @@
>  void em_utils_expunge_folder (struct _GtkWidget *parent, struct
_CamelFolder *folder);
>  void em_utils_empty_trash (struct _GtkWidget *parent);
>  
> +/* returns the folder name portion of an URI */
> +char *em_utils_folder_name_from_uri (char *uri);
> +
> +
>  #ifdef __cplusplus
>  }
>  #endif /* __cplusplus */
> --- /home/meilof/cvs/orig/evolution//mail/em-subscribe-editor.c	2003-
12-02 00:16:22.000000000 -0500
> +++ mail/em-subscribe-editor.c	2004-01-09 21:58:59.000000000 -0500
> @@ -127,7 +127,6 @@
>  
>  static void sub_editor_busy(EMSubscribeEditor *se, int dir);
>  static int sub_queue_fill_level(EMSubscribe *sub, EMSubscribeNode
*node);
> -static void sub_selection_changed(GtkTreeSelection *selection,
EMSubscribe *sub);
>  
>  static void
>  sub_node_free(char *key, EMSubscribeNode *node, EMSubscribe *sub)
> @@ -195,7 +194,7 @@
>  		camel_store_unsubscribe_folder (m->sub->store, m->node->info-
>full_name, &mm->ex);
>  }
>  
> -static void 
> +static void
>  sub_folder_subscribed (struct _mail_msg *mm)
>  {
>  	struct _zsubscribe_msg *m = (struct _zsubscribe_msg *)mm, *next;
> @@ -503,6 +502,29 @@
>  	gtk_widget_set_sensitive(sub->editor->unsubscribe_button, dounsub);
>  }
>  
> +/* double-clicking causes a node item to be evaluated directly */
> +static void sub_row_activated(GtkTreeView *tree, GtkTreePath *path,
GtkTreeViewColumn *col, EMSubscribe *sub) {
> +	EMSubscribeNode *node;
> +	GtkTreeIter iter;
> +	GtkTreeModel *model = gtk_tree_view_get_model(tree);
> +
> +	if (gtk_tree_model_get_iter(model, &iter, path) != TRUE) return;

add line feeds to the prototype and if statments.

> +	gtk_tree_model_get(model, &iter, 2, &node, -1);
> +
> +	/* check whether the item is already processed */
> +	if (node->path == NULL)
> +		return;
> +
> +	/* remove it from wherever in the list it is, and place it in front
instead */
> +	e_dlist_remove((EDListNode *)node);
> +	e_dlist_addhead(&sub->pending, (EDListNode *)node);
> +
> +	if (sub->pending_id == -1
> +	    && (node = (EMSubscribeNode *)e_dlist_remtail(&sub->pending)))
> +		sub_queue_fill_level(sub, node);
> +}
> +
>  static void
>  sub_row_expanded(GtkTreeView *tree, GtkTreeIter *iter, GtkTreePath
*path, EMSubscribe *sub)
>  {
> @@ -621,6 +643,7 @@
>  		gtk_tree_view_set_headers_visible (sub->tree, FALSE);
>  
>  		g_signal_connect(sub->tree, "row-expanded", G_CALLBACK
(sub_row_expanded), sub);
> +		g_signal_connect(sub->tree, "row-activated", G_CALLBACK
(sub_row_activated), sub);
>  		g_signal_connect(sub->tree, "destroy", G_CALLBACK(sub_destroy),
sub);
>  
>  		sub_selection_changed(selection, sub);
> --- /home/meilof/cvs/orig/evolution//mail/mail-ops.c	2004-01-10
16:26:31.000000000 -0500
> +++ mail/mail-ops.c	2004-01-10 17:02:07.000000000 -0500
> @@ -887,6 +887,10 @@
>  	g_assert(CAMEL_IS_FOLDER (folder));
>  	g_assert(CAMEL_IS_MIME_MESSAGE (message));
>  	
> +	if (!camel_medium_get_header (CAMEL_MEDIUM (message), "X-Mailer"))
> +		camel_medium_set_header (CAMEL_MEDIUM (message), "X-Mailer",
> +					 "Ximian Evolution " VERSION SUB_VERSION " " VERSION_COMMENT);
> +

Hmm, I guess this is ok, append_mail is only used for evo-generated
mails, not for general movement of messages.

>  	m = mail_msg_new (&append_mail_op, NULL, sizeof (*m));
>  	m->folder = folder;
>  	camel_object_ref(folder);
> --- /home/meilof/cvs/orig/evolution//ui/evolution-message-composer.
xml	2004-01-10 16:27:10.000000000 -0500
> +++ ui/evolution-message-composer.xml	2004-01-09 21:59:05.000000000 -
0500
> @@ -36,6 +36,14 @@
>  		<cmd name="ViewFrom" _label="_From Field"
>  		_tip="Toggles whether the From chooser is displayed" type="toggle"
state="0"/>
>  		
> +                <cmd name="ViewTo" _label="_To Field"
> +		_tip="Toggles whether the To field is displayed"
> +		type="toggle" state="0"/>
> +
> +                <cmd name="ViewPostTo" _label="_Post-To Field"
> +		_tip="Toggles whether the Post-To field is displayed"
> +		type="toggle" state="0"/>
> +
>                  <cmd name="ViewReplyTo" _label="_Reply-To Field"
>  		_tip="Toggles whether the Reply-To field is displayed"
>  		type="toggle" state="0"/>
> @@ -112,6 +120,8 @@
>  			<menuitem name="ViewAttach" verb="" _label="Show _attachments"/>
>  			<separator f="" name="emailcomposer"/>
>  	                <menuitem name="ViewFrom" verb=""/>
> +			<menuitem name="ViewTo" verb=""/>
> +			<menuitem name="ViewPostTo" verb=""/>
>                          <menuitem name="ViewReplyTo" verb=""/>
>  	                <menuitem name="ViewCC" verb=""/>
>  	                <menuitem name="ViewBCC" verb=""/>
> 




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