[pan2: 4/8] * misc. refactoring * added virtual folders for sent and drafts
- From: Heinrich MÃller <henmull src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pan2: 4/8] * misc. refactoring * added virtual folders for sent and drafts
- Date: Fri, 21 Dec 2012 22:03:14 +0000 (UTC)
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]