[pan2: 4/8] * misc. refactoring * added virtual folders for sent and drafts



commit dcc2556424ef0eafc20c6b8fb9390cfda7a6a21a
Author: Heinrich MÃller <henmull src gnome org>
Date:   Wed Dec 19 20:49:41 2012 +0100

    * misc. refactoring
    * added virtual folders for sent and drafts

 pan/data-impl/data-impl.h      |    3 +-
 pan/data-impl/groups.cc        |    2 +-
 pan/data-impl/headers.cc       |    2 +-
 pan/data-impl/xover.cc         |   17 +-
 pan/data/cert-store.cc         |  629 +++++++++++++++++++---------------------
 pan/data/data.h                |    3 +-
 pan/gui/action-manager.h       |    1 +
 pan/gui/group-pane.cc          |   72 ++++-
 pan/gui/gui.cc                 |   10 +-
 pan/gui/gui.h                  |    1 +
 pan/gui/post-ui.cc             |  167 +++++++----
 pan/gui/post-ui.h              |    3 +-
 pan/usenet-utils/mime-utils.cc |    4 +-
 pan/usenet-utils/mime-utils.h  |    2 +-
 14 files changed, 513 insertions(+), 403 deletions(-)
---
diff --git a/pan/data-impl/data-impl.h b/pan/data-impl/data-impl.h
index c742269..f1945f3 100644
--- a/pan/data-impl/data-impl.h
+++ b/pan/data-impl/data-impl.h
@@ -643,7 +643,8 @@ namespace pan
                                          const StringView     & references,
                                          const unsigned long    byte_count,
                                          const unsigned long    line_count,
-                                         const StringView     & xref);
+                                         const StringView     & xref,
+                                         const bool             is_virtual=false);
 
       /** useful for xover unit testing */
       virtual void xover_flush   (const Quark           & group);
diff --git a/pan/data-impl/groups.cc b/pan/data-impl/groups.cc
index 6286f01..c76d5a1 100644
--- a/pan/data-impl/groups.cc
+++ b/pan/data-impl/groups.cc
@@ -526,7 +526,7 @@ DataImpl :: add_groups (const Quark       & server,
   }
 
   {
-    // build lists of the groups that should and should not be in _moderated and _nopost.
+    // build lists of the groups that should and should not be in _moderated and _nopost.t
     // this is pretty cumbersome, but since these lists almost never change it's still
     // a worthwhile tradeoff to get the speed/memory wins of a sorted_vector
     groups_t mod, notmod, post, nopost, tmp;
diff --git a/pan/data-impl/headers.cc b/pan/data-impl/headers.cc
index fee1ba2..a6227eb 100644
--- a/pan/data-impl/headers.cc
+++ b/pan/data-impl/headers.cc
@@ -192,7 +192,7 @@ DataImpl :: unref_group   (const Quark& group)
 //  std::cerr << LINE_ID << " group " << group << " refcount down to " << h->_ref << std::endl;
   if (h->_ref == 0)
   {
-    if (h->_dirty )
+//    if (h->_dirty )
       save_headers (*_data_io, group);
     h->_dirty = false;
     free_group_headers_memory (group);
diff --git a/pan/data-impl/xover.cc b/pan/data-impl/xover.cc
index 0d4428a..5ab992e 100644
--- a/pan/data-impl/xover.cc
+++ b/pan/data-impl/xover.cc
@@ -232,20 +232,25 @@ DataImpl :: xover_add (const Quark         & server,
                        const StringView    & references_in,
                        const unsigned long   byte_count,
                        const unsigned long   line_count,
-                       const StringView    & xref)
+                       const StringView    & xref,
+                       const bool            is_virtual)
 {
+  if (is_virtual)
+    ref_group(group);
+
   GroupHeaders * h (get_group_headers (group));
-  if (!h) {
+  if (!h && !is_virtual) {
     Log::add_err_va (_("Error reading from %s: unknown group \"%s\""),
                      get_server_address(server).c_str(),
                      group.c_str());
     return 0;
   }
 
+
 //  std::cerr<<"xover add : "<<subject<<" "<<author<<" "<<message_id<<" lines "<<line_count<<" bytes "<<byte_count<<std::endl;
 
   const Article* new_article (0);
-  h->_dirty = true;
+
   XOverEntry& workarea (xover_get_workarea (group));
   const std::string references (
     GNKSA :: remove_broken_message_ids_from_references (references_in));
@@ -254,6 +259,8 @@ DataImpl :: xover_add (const Quark         & server,
   **** Multipart Handling
   ***/
 
+  h->_dirty = true;
+
   int part_index, part_count;
   std::string multipart_subject;
   find_parts (subject, group, line_count, part_index, part_count, multipart_subject);
@@ -278,6 +285,7 @@ DataImpl :: xover_add (const Quark         & server,
     }
   }
 
+
   if (art_mid.empty())
   {
     art_mid = message_id;
@@ -322,5 +330,8 @@ DataImpl :: xover_add (const Quark         & server,
   if ((time(0) - workarea._last_flush_time) >= 10)
     xover_flush (group);
 
+  if (is_virtual)
+      unref_group(group);
+
   return new_article;
 }
diff --git a/pan/data/cert-store.cc b/pan/data/cert-store.cc
index 8a33d32..aa65772 100644
--- a/pan/data/cert-store.cc
+++ b/pan/data/cert-store.cc
@@ -35,8 +35,8 @@
 #include <string>
 
 extern "C" {
-  #include <glib/gi18n.h>
-  #include <glib.h>
+#include <glib/gi18n.h>
+#include <glib.h>
 }
 
 #include <pan/general/debug.h>
@@ -55,353 +55,324 @@ using namespace pan;
 
 #ifdef HAVE_GNUTLS
 
-namespace pan
-{
-
-  struct SaveCBStruct
-  {
-    CertStore& cs;
-    const Quark server;
-    Data& data;
-    SaveCBStruct(CertStore& store, const Quark& s, Data& d) : cs(store), server(s), data(d) {}
-  };
-
-  gboolean
-  save_server_props_cb (gpointer gp)
-  {
-    SaveCBStruct*  data (static_cast<SaveCBStruct*>(gp));
-    data->data.save_server_info(data->server);
-    delete data;
-    return false;
-  }
-
-  int
-  verify_callback(gnutls_session_t session)
-  {
-
-    mydata_t* mydata = (mydata_t*)gnutls_session_get_ptr (session);
-
-    unsigned int status;
-    const gnutls_datum_t *cert_list;
-    unsigned int cert_list_size;
-    int ret;
-    gnutls_x509_crt_t cert;
-    bool fail(false);
-    bool fatal(false);
-
-    ret = gnutls_certificate_verify_peers2 (session, &status);
-
-    if (ret < 0)
-      return GNUTLS_E_CERTIFICATE_ERROR;
-
-    if (status & GNUTLS_CERT_INVALID)
-    {
-      if (!mydata->always_trust)
-      {
-        g_warning ("The certificate is not trusted.\n");
-        fail = true;
-      }
-    }
-
-    if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
-    {
-      if (!mydata->always_trust)
-      {
-        g_warning ("The certificate hasn't got a known issuer.\n");
-        fail = true;
-      }
-    }
-
-    if (status & GNUTLS_CERT_REVOKED)
-    {
-      if (!mydata->always_trust)
-      {
-        g_warning ("The certificate has been revoked.\n");
-        fail = true;
-      }
-    }
-
-    if (status & GNUTLS_CERT_EXPIRED)
-    {
-      if (!mydata->always_trust)
-      {
-        g_warning ("The certificate has expired\n");
-        fail = true;
-      }
-    }
-
-    if (status & GNUTLS_CERT_NOT_ACTIVATED)
-    {
-      if (!mydata->always_trust)
-      {
-        g_warning ("The certificate is not yet activated\n");
-        fail = true;
-      }
-    }
-
-    /* Up to here the process is the same for X.509 certificates and
-     * OpenPGP keys. From now on X.509 certificates are assumed. This can
-     * be easily extended to work with openpgp keys as well.
-     */
-    if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509)
-    {
-      g_warning ("The certificate is not a X509 certificate!\n");
-      fail = true;
-      fatal = true;
-    }
-
-    if (gnutls_x509_crt_init (&cert) < 0)
-    {
-      g_warning ("Error in initialization\n");
-      fail = true;
-      goto _fatal;
-    }
-
-
-    cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
-    if (cert_list == NULL)
-    {
-      g_warning ("No certificate found!\n");
-      fail = true;
-      goto _fatal;
-    }
-
-    /* TODO verify whole chain perhaps?
-     */
-    if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0)
-    {
-      g_warning ("Error parsing certificate!\n");
-      fail = true;
-      goto _fatal;
-    }
-
-    if (!gnutls_x509_crt_check_hostname (cert, mydata->hostname_full.c_str()))
-    {
-      if (!mydata->always_trust)
-      {
-        g_warning ("The certificate's owner does not match hostname '%s' !\n", mydata->hostname_full.c_str());
-        fail = true;
-      }
-    }
-
-    /* auto-add new cert if we always trust this server and the cert isn't already stored in the store */
-    /* fail is only set if we don't always trust this server and a critical condition occurred, e.g. hostname mismatch */
-    if (mydata->always_trust && ret < 0)
-      mydata->cs->add(cert, mydata->host);
-    else if (fail) goto _fail;
-
-    /* notify gnutls to continue handshake normally */
-    return 0;
-
-    _fatal:
-    gnutls_x509_crt_deinit(cert);
-    return GNUTLS_E_CERTIFICATE_ERROR;
-
-    _fail:
-    mydata->cs->verify_failed (cert, mydata->host.c_str(), status);
-    return GNUTLS_E_CERTIFICATE_ERROR;
-
-  }
-
-  bool
-  CertStore :: import_from_file (const Quark& server, const char* fn)
-  {
-
-    size_t filelen;
-
-    Data::Server* s(_data.find_server(server));
-    if (!s) return false;
-    if (s->cert.empty()) return false;
-
-    const char* filename(fn ? fn : file::absolute_fn("ssl_certs", s->cert).c_str());
-    if (!filename) return false;
-
-    FILE * fp = fopen(filename, "rb");
-    if (!fp) return false;
-
-    fseek (fp, 0, SEEK_END);
-    filelen = ftell (fp);
-    fseek (fp, 0, SEEK_SET);
-    char * buf = new char[filelen];
-    size_t dummy (fread (buf, sizeof(char), filelen, fp)); // silence compiler
-
-    gnutls_datum_t in;
-    in.data = (unsigned char*)buf;
-    in.size = filelen;
-    gnutls_x509_crt_t cert;
-    gnutls_x509_crt_init(&cert);
-    gnutls_x509_crt_import(cert, &in, GNUTLS_X509_FMT_PEM);
-
-    delete buf;
-
-    int ret = gnutls_certificate_set_x509_trust(_creds, &cert, 1);
-
-    if (ret < 0)
-    {
-      s->cert.clear();
-      gnutls_x509_crt_deinit (cert);
-      return false;
-    }
-
-    _cert_to_server[server] = cert;
-
-    return true;
-  }
-
-  int
-  CertStore :: get_all_certs_from_disk()
-  {
-
-    int cnt(0);
-    quarks_t servers(_data.get_servers());
-    int ret(0);
-    GError* err(NULL);
-
-    foreach_const(quarks_t, servers, it)
-    {
-      if (import_from_file(*it))
-      {
-        ++cnt;
-      }
-      else
-      {
-        Data::Server* s(_data.find_server(*it));
-        s->cert.clear();
-      }
-    }
-
-    // get certs from ssl certs directory
-    char * ssldir(0);
-    ssldir = getenv("SSL_CERT_DIR");
-    if (!ssldir) ssldir = getenv("SSL_DIR");
-    if (!ssldir) return cnt;
-
-    GDir * dir = g_dir_open (ssldir, 0, &err);
-    if (err != NULL)
-    {
-      Log::add_err_va (_("Error opening SSL certificate directory: \"%s\": %s"), ssldir, err->message);
-      g_error_free (err);
-    }
-    else
-    {
-      char filename[PATH_MAX];
-      const char * fname;
-      while ((fname = g_dir_read_name (dir)))
-      {
-        struct stat stat_p;
-        g_snprintf (filename, sizeof(filename), "%s%c%s", ssldir, G_DIR_SEPARATOR, fname);
-        if (!stat (filename, &stat_p))
-        {
-          if (!S_ISREG(stat_p.st_mode)) continue;
-          ret = gnutls_certificate_set_x509_trust_file(_creds, filename, GNUTLS_X509_FMT_PEM);
-          if (ret > 0) cnt += ret;
-        }
-      }
-      g_dir_close (dir);
-    }
-
-    return cnt;
-  }
-
-
-  void
-  CertStore :: init()
-  {
-    int r(0);
-    r = get_all_certs_from_disk ();
-
-    if (r != 0) Log::add_info_va(_("Successfully added %d SSL PEM certificate(s) to Certificate Store."), r);
-
-  }
-
-  void
-  CertStore :: remove_hard(const Quark& server)
-  {
-    std::string fn = _data.get_server_cert(server);
-    unlink(fn.c_str());
-  }
-
-  void
-  CertStore :: remove (const Quark& server)
-  {
-    _cert_to_server.erase(server);
-    remove_hard (server);
-  }
-
-  CertStore :: CertStore (Data& data): _data(data)
-  {
-	_path = file::absolute_fn("ssl_certs", "");
-    if (!file::ensure_dir_exists (_path))
-    {
-      std::cerr<<_("Error initializing Certificate Store. Check that the permissions for the folders "
-                   "~/.pan2 and ~/.pan2/ssl_certs are set correctly. Fatal, exiting.");
-      file::print_file_info(std::cerr, _path.c_str());
-      exit(EXIT_FAILURE);
-    }
+namespace pan {
+
+struct SaveCBStruct {
+	CertStore& cs;
+	const Quark server;
+	Data& data;
+	SaveCBStruct(CertStore& store, const Quark& s, Data& d) :
+			cs(store), server(s), data(d) {
+	}
+};
+
+gboolean save_server_props_cb(gpointer gp) {
+	SaveCBStruct* data(static_cast<SaveCBStruct*>(gp));
+	data->data.save_server_info(data->server);
+	delete data;
+	return false;
+}
 
-    gnutls_certificate_allocate_credentials (&_creds);
-    gnutls_certificate_set_verify_function(_creds, verify_callback);
+int verify_callback(gnutls_session_t session) {
+
+	mydata_t* mydata = (mydata_t*) gnutls_session_get_ptr(session);
+
+	unsigned int status;
+	const gnutls_datum_t *cert_list;
+	unsigned int cert_list_size;
+	int ret;
+	gnutls_x509_crt_t cert;
+	bool fail(false);
+	bool fatal(false);
+
+	ret = gnutls_certificate_verify_peers2(session, &status);
+
+	if (ret < 0)
+		return GNUTLS_E_CERTIFICATE_ERROR;
+
+	if (status & GNUTLS_CERT_INVALID) {
+		if (!mydata->always_trust) {
+			g_warning("The certificate is not trusted.\n");
+			fail = true;
+		}
+	}
+
+	if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
+		if (!mydata->always_trust) {
+			g_warning("The certificate hasn't got a known issuer.\n");
+			fail = true;
+		}
+	}
+
+	if (status & GNUTLS_CERT_REVOKED) {
+		if (!mydata->always_trust) {
+			g_warning("The certificate has been revoked.\n");
+			fail = true;
+		}
+	}
+
+	if (status & GNUTLS_CERT_EXPIRED) {
+		if (!mydata->always_trust) {
+			g_warning("The certificate has expired\n");
+			fail = true;
+		}
+	}
+
+	if (status & GNUTLS_CERT_NOT_ACTIVATED) {
+		if (!mydata->always_trust) {
+			g_warning("The certificate is not yet activated\n");
+			fail = true;
+		}
+	}
+
+	/* Up to here the process is the same for X.509 certificates and
+	 * OpenPGP keys. From now on X.509 certificates are assumed. This can
+	 * be easily extended to work with openpgp keys as well.
+	 */
+	if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509) {
+		g_warning("The certificate is not a X509 certificate!\n");
+		fail = true;
+		fatal = true;
+	}
+
+	if (gnutls_x509_crt_init(&cert) < 0) {
+		g_warning("Error in initialization\n");
+		fail = true;
+		goto _fatal;
+	}
+
+	cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
+	if (cert_list == NULL) {
+		g_warning("No certificate found!\n");
+		fail = true;
+		goto _fatal;
+	}
+
+	/* TODO verify whole chain perhaps?
+	 */
+	if (gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) {
+		g_warning("Error parsing certificate!\n");
+		fail = true;
+		goto _fatal;
+	}
+
+	if (!gnutls_x509_crt_check_hostname(cert, mydata->hostname_full.c_str())) {
+		if (!mydata->always_trust) {
+			g_warning(
+					"The certificate's owner does not match hostname '%s' !\n", mydata->hostname_full.c_str());
+			fail = true;
+		}
+	}
+
+	/* auto-add new cert if we always trust this server and the cert isn't already stored in the store */
+	/* fail is only set if we don't always trust this server and a critical condition occurred, e.g. hostname mismatch */
+	if (mydata->always_trust && ret < 0)
+		mydata->cs->add(cert, mydata->host);
+	else if (fail)
+		goto _fail;
+
+	/* notify gnutls to continue handshake normally */
+	return 0;
+
+	_fatal: gnutls_x509_crt_deinit(cert);
+	return GNUTLS_E_CERTIFICATE_ERROR;
+
+	_fail: mydata->cs->verify_failed(cert, mydata->host.c_str(), status);
+	return GNUTLS_E_CERTIFICATE_ERROR;
 
-  }
+}
 
-  CertStore :: ~CertStore ()
-  {
-    gnutls_certificate_free_credentials (_creds);
-    foreach (certs_m, _cert_to_server, it)
-      if (it->second)
-        gnutls_x509_crt_deinit(it->second);
-  }
+bool CertStore::import_from_file(const Quark& server, const char* fn) {
 
+	size_t filelen;
 
+	Data::Server* s(_data.find_server(server));
+	if (!s)
+		return false;
+	if (s->cert.empty())
+		return false;
 
-  bool
-  CertStore :: add (gnutls_x509_crt_t cert, const Quark& server)
-  {
-    if (!cert || server.empty()) return false;
+	const char* filename(
+			fn ? fn : file::absolute_fn("ssl_certs", s->cert).c_str());
+	if (!filename)
+		return false;
 
-    std::string addr; int port;
-    _data.get_server_addr(server, addr, port);
-    _cert_to_server[server] = cert;
+	FILE * fp = fopen(filename, "rb");
+	if (!fp)
+		return false;
 
-    std::stringstream buffer;
-    buffer << addr << ".pem";
-    const char* buf (buffer.str().c_str());
+	fseek(fp, 0, SEEK_END);
+	filelen = ftell(fp);
+	fseek(fp, 0, SEEK_SET);
+	char * buf = new char[filelen];
+	size_t dummy(fread(buf, sizeof(char), filelen, fp)); // silence compiler
 
-    std::cerr<<"adding cert "<<buf<<"\n";
+	gnutls_datum_t in;
+	in.data = (unsigned char*) buf;
+	in.size = filelen;
+	gnutls_x509_crt_t cert;
+	gnutls_x509_crt_init(&cert);
+	gnutls_x509_crt_import(cert, &in, GNUTLS_X509_FMT_PEM);
 
-    FILE * fp = fopen(file::absolute_fn("ssl_certs", buf).c_str(), "wb");
-    if (!fp) return false;
+	delete buf;
 
-    _data.set_server_cert(server, buf);
+	int ret = gnutls_certificate_set_x509_trust(_creds, &cert, 1);
 
-    SaveCBStruct* cbstruct = new SaveCBStruct(*this, server, _data);
-    g_idle_add (save_server_props_cb, cbstruct);
+	if (ret < 0) {
+		s->cert.clear();
+		gnutls_x509_crt_deinit(cert);
+		return false;
+	}
 
-    size_t outsize;
-    /* make up for dumbness of this function */
-    gnutls_x509_crt_export (cert, GNUTLS_X509_FMT_PEM, NULL, &outsize);
-    char* out = new char[outsize];
-    gnutls_x509_crt_export (cert, GNUTLS_X509_FMT_PEM, out, &outsize);
+	_cert_to_server[server] = cert;
 
-    fputs ((const char*)out, fp);
+	return true;
+}
 
-    debug_SSL_verbatim("\n===========================================");
-    debug_SSL_verbatim(out);
-    debug_SSL_verbatim("\n===========================================");
+int CertStore::get_all_certs_from_disk() {
+
+	int cnt(0);
+	quarks_t servers(_data.get_servers());
+	int ret(0);
+	GError* err(NULL);
+
+	foreach_const(quarks_t, servers, it){
+	if (import_from_file(*it))
+	{
+		++cnt;
+	}
+	else
+	{
+		Data::Server* s(_data.find_server(*it));
+		s->cert.clear();
+	}
+}
 
-    delete out;
-    fclose(fp);
-    chmod (buf, 0600);
+// get certs from ssl certs directory
+	char * ssldir(0);
+	ssldir = getenv("SSL_CERT_DIR");
+	if (!ssldir)
+		ssldir = getenv("SSL_DIR");
+	if (!ssldir)
+		return cnt;
+
+	GDir * dir = g_dir_open(ssldir, 0, &err);
+	if (err != NULL) {
+		Log::add_err_va(
+				_("Error opening SSL certificate directory: \"%s\": %s"),
+				ssldir, err->message);
+		g_error_free(err);
+	} else {
+		char filename[PATH_MAX];
+		const char * fname;
+		while ((fname = g_dir_read_name(dir))) {
+			struct stat stat_p;
+			g_snprintf(filename, sizeof(filename), "%s%c%s", ssldir,
+					G_DIR_SEPARATOR, fname);
+			if (!stat(filename, &stat_p)) {
+				if (!S_ISREG(stat_p.st_mode))
+					continue;
+				ret = gnutls_certificate_set_x509_trust_file(_creds, filename,
+						GNUTLS_X509_FMT_PEM);
+				if (ret > 0)
+					cnt += ret;
+			}
+		}
+		g_dir_close(dir);
+	}
+
+	return cnt;
+}
 
-    gnutls_certificate_set_x509_trust(_creds, &cert, 1); // for now, only 1 is saved
-    valid_cert_added(cert, server.c_str());
+void CertStore::init() {
+	int r(0);
+	r = get_all_certs_from_disk();
 
-    debug("adding server cert "<<server<<" "<<cert);
+	if (r != 0)
+		Log::add_info_va(
+				_("Successfully added %d SSL PEM certificate(s) to Certificate Store."),
+				r);
 
-    return true;
-  }
+}
 
-}  // namespace pan
+void CertStore::remove_hard(const Quark& server) {
+	std::string fn = _data.get_server_cert(server);
+	unlink(fn.c_str());
+}
 
+void CertStore::remove(const Quark& server) {
+	_cert_to_server.erase(server);
+	remove_hard(server);
+}
+
+CertStore::CertStore(Data& data) :
+		_data(data) {
+	_path = file::absolute_fn("ssl_certs", "");
+	if (!file::ensure_dir_exists(_path)) {
+		std::cerr
+				<<_("Error initializing Certificate Store. Check that the permissions for the folders "
+						"~/.pan2 and ~/.pan2/ssl_certs are set correctly. Fatal, exiting.");
+		file::print_file_info(std::cerr, _path.c_str());
+		exit(EXIT_FAILURE);
+	}
+
+	gnutls_certificate_allocate_credentials(&_creds);
+	gnutls_certificate_set_verify_function(_creds, verify_callback);
+
+}
+
+CertStore::~CertStore() {
+	gnutls_certificate_free_credentials(_creds);
+	foreach (certs_m, _cert_to_server, it)if (it->second)
+	gnutls_x509_crt_deinit(it->second);
+}
+
+bool CertStore::add(gnutls_x509_crt_t cert, const Quark& server) {
+	if (!cert || server.empty())
+		return false;
+
+	std::string addr;
+	int port;
+	_data.get_server_addr(server, addr, port);
+	_cert_to_server[server] = cert;
+
+	std::stringstream buffer;
+	buffer << addr << ".pem";
+	const char* buf(buffer.str().c_str());
+
+	FILE * fp = fopen(file::absolute_fn("ssl_certs", buf).c_str(), "wb");
+	if (!fp)
+		return false;
+
+	_data.set_server_cert(server, buf);
+
+	SaveCBStruct* cbstruct = new SaveCBStruct(*this, server, _data);
+	g_idle_add(save_server_props_cb, cbstruct);
+
+	size_t outsize;
+	/* make up for dumbness of this function */
+	gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_PEM, NULL, &outsize);
+	char* out = new char[outsize];
+	gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_PEM, out, &outsize);
+
+	fputs((const char*) out, fp);
+
+	debug_SSL_verbatim("\n===========================================");
+	debug_SSL_verbatim(out);
+	debug_SSL_verbatim("\n===========================================");
+
+	delete out;
+	fclose(fp);
+	chmod(buf, 0600);
+
+	gnutls_certificate_set_x509_trust(_creds, &cert, 1); // for now, only 1 is saved
+	valid_cert_added(cert, server.c_str());
+
+	debug("adding server cert "<<server<<" "<<cert);
+
+	return true;
+}
+
+}  // namespace pan
 
 #endif
diff --git a/pan/data/data.h b/pan/data/data.h
index 684961d..717a6e2 100644
--- a/pan/data/data.h
+++ b/pan/data/data.h
@@ -678,7 +678,8 @@ namespace pan
                                          const StringView     & references,
                                          const unsigned long    byte_count,
                                          const unsigned long    line_count,
-                                         const StringView     & xref) = 0;
+                                         const StringView     & xref,
+                                         const bool             is_virtual=false) = 0;
 
       /**
        * The last call to xover_unref() for a group can indicate to Data
diff --git a/pan/gui/action-manager.h b/pan/gui/action-manager.h
index 642b755..09220b8 100644
--- a/pan/gui/action-manager.h
+++ b/pan/gui/action-manager.h
@@ -14,6 +14,7 @@ struct ActionManager
   virtual void activate_action (const char * action) const = 0;
   virtual void toggle_action (const char * action, bool) const = 0;
   virtual void sensitize_action (const char * action, bool) const = 0;
+  virtual void hide_action (const char * key, bool b) const = 0;
   virtual GtkWidget* get_action_widget (const char * key) const = 0;
   virtual void disable_accelerators_when_focused (GtkWidget * entry) const = 0;
 };
diff --git a/pan/gui/group-pane.cc b/pan/gui/group-pane.cc
index e074b67..547b7c2 100644
--- a/pan/gui/group-pane.cc
+++ b/pan/gui/group-pane.cc
@@ -60,10 +60,12 @@ namespace
       }
   };
 
+  Quark * virtual_title_quark (0);
   Quark * sub_title_quark (0);
   Quark * other_title_quark (0);
+
   bool is_group (const Quark& name) {
-    return !name.empty() && name!=*sub_title_quark && name!=*other_title_quark;
+    return !name.empty() && name!=*sub_title_quark && name!=*other_title_quark && name!=*virtual_title_quark;
   }
 
   std::string
@@ -318,6 +320,15 @@ namespace
 
   NoopRowDispose noop_row_dispose;
 
+  namespace
+  {
+  	//Local folders
+  	static const char* folders_groupnames[] = {
+            _("Sent"),
+            _("Drafts")
+          };
+  }
+
   PanTreeStore*
   build_model (const Data         & data,
                const TextMatch    * match,
@@ -329,7 +340,7 @@ namespace
     store->set_row_dispose (&noop_row_dispose);
 
     // find the groups that we'll be adding.
-    std::vector<Quark> groups, sub, unsub;
+    std::vector<Quark> groups, local_folders, sub, unsub;
     data.get_other_groups (groups);
     find_matching_groups (match, groups, unsub);
     groups.clear ();
@@ -340,16 +351,40 @@ namespace
     group_rows.clear ();
     group_rows.reserve (sub.size() + unsub.size());
 
-    MyRow * headers = new MyRow[2];
-    headers[0].groupname = *sub_title_quark;
-    headers[1].groupname = *other_title_quark;
+    MyRow * headers = new MyRow[3];
+    headers[0].groupname = *virtual_title_quark;
+    headers[1].groupname = *sub_title_quark;
+    headers[2].groupname = *other_title_quark;
     g_object_weak_ref (G_OBJECT(store), delete_rows, headers);
 
     //
+    // local folders
+    //
+    MyRow * row = &headers[0];
+    store->append (NULL, row);
+    {
+		const size_t n (G_N_ELEMENTS(folders_groupnames));
+		std::vector<PanTreeStore::Row*> appendme;
+		appendme.reserve (n);
+		MyRow *rows(new MyRow [n]), *r(rows);
+		g_object_weak_ref (G_OBJECT(store), delete_rows, rows);
+
+		unsigned long unused;
+		for (size_t i(0); i!=n; ++i, ++r)
+		{
+			r->groupname = folders_groupnames[i];
+			data.get_group_counts (r->groupname, r->unread, unused);
+			appendme.push_back (r);
+			group_rows.push_back (r);
+		}
+		store->append (row, appendme);
+		expandme.push_back (store->get_iter (row));
+    }
+    //
     //  subscribed
     //
 
-    MyRow * row = &headers[0];
+    row = &headers[1];
     store->append (NULL, row);
     if (!sub.empty())
     {
@@ -374,8 +409,7 @@ namespace
     //
     // unsubscribed
     //
-
-    row = &headers[1];
+    row = &headers[2];
     store->append (NULL, row);
     if (!unsub.empty())
     {
@@ -925,8 +959,28 @@ GroupPane :: on_selection_changed (GtkTreeSelection*, gpointer pane_gpointer)
     "read-selected-group", "mark-groups-read", "delete-groups-articles",
     "get-new-headers-in-selected-groups", "download-headers"
   };
+  static const char* actions_in_nonvirtual_group[] = {
+    "show-group-preferences-dialog",
+    "subscribe", "unsubscribe",
+    "get-new-headers-in-selected-groups", "download-headers",
+    "refresh-group-list", "get-new-headers-in-subscribed-groups", "post"
+  };
   for (int i=0, n=G_N_ELEMENTS(actions_that_require_a_group); i<n; ++i)
     self->_action_manager.sensitize_action (actions_that_require_a_group[i], have_group);
+
+  // disable some functions for virtual mailbox folder
+  bool is_virtual = false;
+  for (int i(0); i != G_N_ELEMENTS(folders_groupnames); ++i)
+  {
+	  if (group == folders_groupnames[i])
+	  {
+		  is_virtual = true;
+		  break;
+	  }
+  }
+
+  for (int i=0, n=G_N_ELEMENTS(actions_in_nonvirtual_group); i<n; ++i)
+    self->_action_manager.hide_action (actions_in_nonvirtual_group[i], is_virtual);
 }
 
 GroupPane :: GroupPane (ActionManager& action_manager, Data& data, Prefs& prefs, GroupPrefs& group_prefs):
@@ -943,6 +997,7 @@ GroupPane :: GroupPane (ActionManager& action_manager, Data& data, Prefs& prefs,
   def_fg = colors.def_fg;
 
   shorten = prefs.get_flag ("shorten-group-names", false);
+  virtual_title_quark = new Quark (_("Local Folders"));
   sub_title_quark = new Quark (_("Subscribed Groups"));
   other_title_quark = new Quark (_("Other Groups"));
 
@@ -999,6 +1054,7 @@ GroupPane :: GroupPane (ActionManager& action_manager, Data& data, Prefs& prefs,
 
 GroupPane :: ~GroupPane ()
 {
+  delete virtual_title_quark;
   delete sub_title_quark;
   delete other_title_quark;
 
diff --git a/pan/gui/gui.cc b/pan/gui/gui.cc
index b726a06..f5f7ea1 100644
--- a/pan/gui/gui.cc
+++ b/pan/gui/gui.cc
@@ -547,6 +547,14 @@ GUI :: sensitize_action (const char * key, bool b) const
   //gtk_action_set_sensitive (get_action(key), b);
 }
 
+void
+GUI :: hide_action (const char * key, bool b) const
+{
+  ensure_action_map_loaded (_ui_manager);
+  g_object_set (get_action(key), "visible", gboolean(!b), NULL);
+  //gtk_action_set_sensitive (get_action(key), b);
+}
+
 
 void
 GUI :: toggle_action (const char * key, bool b) const
@@ -2036,7 +2044,7 @@ void GUI :: do_read_selected_group ()
   // otherwise if get-new-headers is turned on, queue an xover-new task.
   unsigned long unread(0), total(0);
 
-  if (changed && !group.empty() && _queue.is_online()) {
+  if (changed && !group.empty()){// && _queue.is_online()) {
     _data.get_group_counts (group, unread, total);
     if (!total)
       activate_action ("download-headers");
diff --git a/pan/gui/gui.h b/pan/gui/gui.h
index b5b9633..1d44935 100644
--- a/pan/gui/gui.h
+++ b/pan/gui/gui.h
@@ -89,6 +89,7 @@ namespace pan
       virtual void activate_action (const char * action_name) const;
       virtual void toggle_action (const char * action_name, bool) const;
       virtual void sensitize_action (const char * action_name, bool) const;
+      virtual void hide_action (const char * key, bool b) const;
       virtual GtkWidget* get_action_widget (const char * key) const;
       virtual void disable_accelerators_when_focused (GtkWidget * entry) const;
 
diff --git a/pan/gui/post-ui.cc b/pan/gui/post-ui.cc
index 1809c44..7976baa 100644
--- a/pan/gui/post-ui.cc
+++ b/pan/gui/post-ui.cc
@@ -348,7 +348,6 @@ PostUI :: get_body () const
   line_start = line_end = body_start;
   while ((gtk_text_view_forward_display_line (view, &line_end))) {
     char * line = gtk_text_buffer_get_text (buf, &line_start, &line_end, false);
-//    std::cerr<<"line : "<<line<<"\n";
     body += line;
     g_free (line);
     if (wrap && *body.rbegin() != '\n')
@@ -429,7 +428,7 @@ namespace
     { "cut", GTK_STOCK_CUT, 0, 0, 0, G_CALLBACK(do_cut) },
     { "copy", GTK_STOCK_COPY, 0, 0, 0, G_CALLBACK(do_copy) },
     { "paste", GTK_STOCK_PASTE, 0, 0, 0, G_CALLBACK(do_paste) },
-    { "rot13", GTK_STOCK_REFRESH, N_("_Rot13"), 0, N_("Rot13 Selected Text"), G_CALLBACK(do_rot13) },
+    { "rot13", GTK_STOCK_REFRESH, N_("_Rot13"), "<control>r", N_("Rot13 Selected Text"), G_CALLBACK(do_rot13) },
     { "run-editor", GTK_STOCK_JUMP_TO, N_("Run _Editor"), "<control>e", N_("Run Editor"), G_CALLBACK(do_edit) },
     { "manage-profiles", GTK_STOCK_EDIT, N_("Edit P_osting Profiles"), 0, 0, G_CALLBACK(do_profiles) },
     { "add-files", GTK_STOCK_ADD, N_("Add _Files to Queue"), "<control>O", N_("Add Files to Queue"), G_CALLBACK(do_add_files) },
@@ -626,12 +625,12 @@ PostUI :: add_actions (GtkWidget * box)
   gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (gtk_action_group_get_action (_agroup, "wrap")),
                                 _prefs.get_flag ("compose-wrap-enabled", true));
 
-
-
    //add popup actions
   gtk_action_group_add_actions (_agroup, filequeue_popup_entries, G_N_ELEMENTS(filequeue_popup_entries), this);
   gtk_ui_manager_insert_action_group (_uim, _agroup, 0);
 
+  gtk_action_group_set_sensitive(_agroup, true);
+
 }
 
 void
@@ -999,18 +998,77 @@ PostUI :: on_progress_finished (Progress&, int status) // posting finished
 }
 
 bool
+PostUI :: save_message_in_local_folder(const Mode& mode, const std::string& folder)
+{
+	  // the following message is constructed solely for the purpose of adding the current message to
+	  // a local folder of pan
+	  GMimeMessage* msg = new_message_from_ui(mode);
+	  Profile p(get_current_profile());
+
+	  //domain name
+	  std::string d;
+	  d = !p.fqdn.empty()
+		? GNKSA::generate_message_id (p.fqdn)
+		: GNKSA::generate_message_id_from_email_address (p.address);
+	  StringView d2(d);
+	  StringView domain;
+		const char * pch = d2.strchr ('@');
+		if (pch != NULL)
+		  domain = d2.substr (pch+1, NULL);
+		else
+		  domain = d2;
+
+	  std::string author;
+	  p.get_from_header(author);
+	  std::string subject(utf8ize (g_mime_message_get_subject (msg)));
+	  const char * refs = g_mime_object_get_header(GMIME_OBJECT(msg), "References");
+	  g_mime_object_set_header((GMimeObject *) msg, "Newsgroups", folder.c_str());
+
+	  // pseudo mid to get data from cache
+	  std::string mid;
+	  generate_unique_id(domain, 42, mid);
+	  std::string message_id = pan_g_mime_message_set_message_id(msg, mid.c_str());
+
+	  std::stringstream xref;
+	  xref << folder << ":42";
+
+	  const Article* article = _data.xover_add (p.posting_server, folder, subject, author, time(0), message_id, refs, sizeof(*msg), 42, xref.str(), true);
+	  // set adjusted time from article
+	  if (article)
+	  {
+		  g_mime_message_set_date(msg, article->time_posted, 0);
+		  ArticleCache& cache(_data.get_cache());
+		  ArticleCache :: CacheResponse response = cache.add(mid, g_mime_object_to_string(GMIME_OBJECT(msg)));
+		  g_object_unref(msg);
+
+		  if (response.type != ArticleCache::CACHE_OK)
+		  {
+			  std::string reason(response.type == ArticleCache::CACHE_IO_ERR
+				? _("IO Error") : _("No space left on device"));
+			  Log::add_err_va(_("Error copying message to %s folder. Reason : %s"), folder.c_str(), reason.c_str());
+			  return false;
+		  }
+	  }
+	  else
+	  {
+		  Log::add_err_va(_("Error creating message in %s mail folder. Article isn't valid!"), folder.c_str());
+		  return false;
+	  }
+	  return true;
+}
+
+bool
 PostUI :: maybe_post_message (GMimeMessage * message)
 {
   /**
   ***  Find the server to use
   **/
-
   g_return_val_if_fail(message, false);
 
   // get the profile...
-  const Profile profile (get_current_profile ());
+  const Profile p (get_current_profile ());
   // get the server associated with that profile...
-  const Quark& server (profile.posting_server);
+  const Quark& server (p.posting_server);
   // if the server's invalid, bitch about it to the user
   std::string error_msg;
   bool error = false;
@@ -1045,7 +1103,6 @@ PostUI :: maybe_post_message (GMimeMessage * message)
   /**
   ***  Make sure the message is OK...
   **/
-
   if (!check_message (server, message, !_file_queue_empty))
     return false;
 
@@ -1082,10 +1139,12 @@ PostUI :: maybe_post_message (GMimeMessage * message)
     _queue.set_online (true);
   }
 
+  //TODO implement callback!!
   gtk_widget_hide (_root);
 
+  save_message_in_local_folder(POSTING, "Sent");
+
   GMimeMessage* msg = new_message_from_ui(POSTING);
-  Profile p(get_current_profile());
 
   if(_file_queue_empty)
   {
@@ -1140,27 +1199,30 @@ PostUI :: maybe_post_message (GMimeMessage * message)
 
     // generate domain name for upload if the flag is set / a save-file is set
     bool custom_mid(_prefs.get_flag(MESSAGE_ID_PREFS_KEY,false) || !_save_file.empty());
-    std::string d;
-    d = !profile.fqdn.empty()
-      ? GNKSA::generate_message_id (profile.fqdn)
-      : GNKSA::generate_message_id_from_email_address (profile.address);
-    StringView d2(d);
-    StringView domain;
-      const char * pch = d2.strchr ('@');
-      if (pch != NULL)
-        domain = d2.substr (pch+1, NULL);
-      else
-        domain = d2;
 
     std::string last_mid;
     std::string first_mid;
 
+    //domain name
+    std::string d;
+	d = !p.fqdn.empty()
+		? GNKSA::generate_message_id (p.fqdn)
+		: GNKSA::generate_message_id_from_email_address (p.address);
+    StringView d2(d);
+    StringView domain;
+	const char * pch = d2.strchr ('@');
+	if (pch != NULL)
+	  domain = d2.substr (pch+1, NULL);
+	else
+	  domain = d2;
+
     Article a;
     TaskUpload * tmp (dynamic_cast<TaskUpload*>(tasks[0]));
     if (tmp) a = tmp->_article;
 
     if (master_reply)
     {
+
       // master article, other attachments are threaded as replies to this
       const Profile profile (get_current_profile ());
       std::string out;
@@ -1187,45 +1249,41 @@ PostUI :: maybe_post_message (GMimeMessage * message)
 
 
     /* init taskupload variables before adding the tasks to the queue for processing */
+    char buf[2048];
+    int cnt(0);
 
+    foreach (PostUI::tasks_t, tasks, it)
     {
 
-      char buf[2048];
-      int cnt(0);
-
-      foreach (PostUI::tasks_t, tasks, it)
-      {
-
-        TaskUpload * t (dynamic_cast<TaskUpload*>(*it));
+      TaskUpload * t (dynamic_cast<TaskUpload*>(*it));
 
-        const char* basename = t->_basename.c_str();
-        TaskUpload::Needed n;
+      const char* basename = t->_basename.c_str();
+      TaskUpload::Needed n;
 
-        foreach (std::set<int>, t->_wanted, pit)
+      foreach (std::set<int>, t->_wanted, pit)
+      {
+        if (custom_mid)
         {
-          if (custom_mid)
-          {
-              std::string out;
-              generate_unique_id(domain, *pit,out);
-              n.mid = out;
-              if (first_mid.empty()) first_mid = out;
-          }
-
-          g_snprintf(buf,sizeof(buf),"%s.%d", basename, *pit);
-          n.message_id = buf;
-          n.partno = *pit;
-          n.last_mid = last_mid;
-          t->_first_mid = first_mid;
-          last_mid = n.mid;
-          t->_needed.insert(std::pair<int,TaskUpload::Needed>(*pit,n));
+            std::string out;
+            generate_unique_id(domain, *pit,out);
+            n.mid = out;
+            if (first_mid.empty()) first_mid = out;
         }
-        t->build_needed_tasks();
-        t->_save_file = _save_file;
-        t->_queue_pos = cnt++;
 
-        _queue.add_task (*it, Queue::BOTTOM);
-        t->add_listener(this);
+        g_snprintf(buf,sizeof(buf),"%s.%d", basename, *pit);
+        n.message_id = buf;
+        n.partno = *pit;
+        n.last_mid = last_mid;
+        t->_first_mid = first_mid;
+        last_mid = n.mid;
+        t->_needed.insert(std::pair<int,TaskUpload::Needed>(*pit,n));
       }
+      t->build_needed_tasks();
+      t->_save_file = _save_file;
+      t->_queue_pos = cnt++;
+
+      _queue.add_task (*it, Queue::BOTTOM);
+      t->add_listener(this);
     }
   }
 
@@ -1575,7 +1633,7 @@ PostUI :: new_message_from_ui (Mode mode, bool copy_body)
     g_mime_object_set_header ((GMimeObject *) msg, "User-Agent", get_user_agent());
 
   // Message-ID for single text-only posts
-  if ((mode==POSTING || mode==UPLOADING) && _prefs.get_flag (MESSAGE_ID_PREFS_KEY, false)) {
+  if (mode==DRAFTING || ((mode==POSTING || mode==UPLOADING) && _prefs.get_flag (MESSAGE_ID_PREFS_KEY, false))) {
     const std::string message_id = !profile.fqdn.empty()
       ? GNKSA::generate_message_id (profile.fqdn)
       : GNKSA::generate_message_id_from_email_address (profile.address);
@@ -1638,7 +1696,6 @@ PostUI :: save_draft ()
 
   if (gtk_dialog_run(GTK_DIALOG(d)) == GTK_RESPONSE_ACCEPT)
   {
-    //dbg DRAFTING
     GMimeMessage * msg = new_message_from_ui (UPLOADING);
     char * filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(d));
     draft_filename = filename;
@@ -1664,6 +1721,8 @@ PostUI :: save_draft ()
     g_free (filename);
     g_object_unref (msg);
 
+    save_message_in_local_folder(DRAFTING, "Drafts");
+
     _unchanged_body = get_body ();
   }
 
@@ -1992,8 +2051,6 @@ PostUI :: apply_profile_to_body ()
     body += sig;
   }
 
-  std::cerr<<body.empty()<<" "<<sig.empty()<<"\n==================================\n"<<body<<"\n====================================\n";
-
   GtkTextBuffer * buf (_body_buf);
   if (!body.empty())
     gtk_text_buffer_set_text (buf, body.c_str(), body.size());
@@ -3172,7 +3229,7 @@ PostUI :: prompt_user_for_queueable_files (GtkWindow * parent, const Prefs& pref
       foreach_const (quarks_t, groups, git)
          a.xref.insert (profile.posting_server, *git,0);
       ui.total = get_total_parts((const char*)cur->data);
-      tmp = new TaskUpload(std::string((const char*)cur->data),
+      tmp = new TaskUpload((const char*)cur->data,
                         profile.posting_server, _cache, a, ui, msg);
 
       // insert wanted parts to upload
diff --git a/pan/gui/post-ui.h b/pan/gui/post-ui.h
index 6739165..84d8ee7 100644
--- a/pan/gui/post-ui.h
+++ b/pan/gui/post-ui.h
@@ -104,6 +104,8 @@ namespace pan
       void done_sending_message (GMimeMessage*, bool);
       void maybe_mail_message (GMimeMessage*);
       bool maybe_post_message (GMimeMessage*);
+      enum Mode { DRAFTING, POSTING, UPLOADING};
+      bool save_message_in_local_folder(const Mode& mode, const std::string& folder);
 
     private:
       void update_widgetry ();
@@ -178,7 +180,6 @@ namespace pan
       void add_actions (GtkWidget* box);
       void apply_profile_to_body ();
       void apply_profile_to_headers ();
-      enum Mode { DRAFTING, POSTING, UPLOADING};
       GMimeMessage * new_message_from_ui (Mode mode, bool copy_body=true);
       bool check_message (const Quark& server, GMimeMessage*, bool binpost=false);
       bool check_charset ();
diff --git a/pan/usenet-utils/mime-utils.cc b/pan/usenet-utils/mime-utils.cc
index b18a007..158533b 100644
--- a/pan/usenet-utils/mime-utils.cc
+++ b/pan/usenet-utils/mime-utils.cc
@@ -1549,12 +1549,14 @@ void pan::pan_g_mime_message_add_recipients_from_string (GMimeMessage *message,
 /**
 * Works around a GMime bug that uses `Message-Id' rather than `Message-ID'
 */
-void pan::pan_g_mime_message_set_message_id (GMimeMessage *msg, const char *mid)
+std::string pan::pan_g_mime_message_set_message_id (GMimeMessage *msg, const char *mid)
 {
     g_mime_object_append_header ((GMimeObject *) msg, "Message-ID", mid);
     char * bracketed = g_strdup_printf ("<%s>", mid);
     g_mime_header_list_set (GMIME_OBJECT(msg)->headers, "Message-ID", bracketed);
+    std::string ret (bracketed);
     g_free (bracketed);
+    return ret;
 }
 
 namespace pan
diff --git a/pan/usenet-utils/mime-utils.h b/pan/usenet-utils/mime-utils.h
index ac8c58a..fd3326e 100644
--- a/pan/usenet-utils/mime-utils.h
+++ b/pan/usenet-utils/mime-utils.h
@@ -108,7 +108,7 @@ namespace pan
 
   char *pan_g_mime_message_get_body (GMimeMessage *message, gboolean *is_html);
   void pan_g_mime_message_add_recipients_from_string (GMimeMessage *message, GMimeRecipientType type, const char *string);
-  void pan_g_mime_message_set_message_id (GMimeMessage *msg, const char *mid);
+  std::string pan_g_mime_message_set_message_id (GMimeMessage *msg, const char *mid);
 
   extern iconv_t conv;
   extern bool iconv_inited;



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