Hacking Pan - Code Overview

PGP Signing / Encryption

Signing and encrypting is done with the help of gmime2.6..
That's why it is needed for a build with crypto support.
The main advantage of this is that we can use the advancements in this library and are not limited to gpgme or some other library.
Also, the gmime devs strive to adhere to the MIME specs as closely as possible.
Here is a snippet of the main structs needed for the handling of PGP-signed messages.

   1 struct Signer
   2   {
   3     std::string name;
   4     std::string key_id;
   5     std::string fpr;
   6     std::string trust;
   7     std::string status;
   8     std::string error;
   9 
  10     bool ok;
  11     bool never_expires;
  12     time_t created, expires;
  13 
  14     Signer() : ok(false), never_expires(false), created((time_t)0), expires((time_t)0)  {}
  15   };
  16 
  17   struct GPGSignersInfo
  18   {
  19     std::vector<Signer> signers;
  20   };
  21 
  22   typedef std::map<std::string,GPGSignersInfo> signers_m;
  23 
  24   extern GMimeCryptoContext* gpg_ctx;
  25   extern bool gpg_inited;
  26 
  27   enum GPGDecType
  28   {
  29     GPG_VERIFY,
  30     GPG_DECODE,
  31     GPG_VERIFY_AND_DECODE
  32   };
  33 
  34   enum GPGEncType
  35   {
  36     GPG_ENCODE,
  37     GPG_SIGN,
  38     GPG_ENCODE_AND_SIGN
  39   };
  40 
  41   /* Error struct for gpg_decrypt */
  42   struct GPGDecErr
  43   {
  44     GError* err;
  45     std::string output;
  46     bool dec_ok;
  47     bool verify_ok;
  48     bool no_sigs;
  49     GPGDecType type;
  50     GPGSignersInfo signers;
  51     GMimeObject * decrypted;
  52     GMimeDecryptResult * result;
  53 
  54     GPGDecErr(GPGDecType t) : err(NULL), type(t), decrypted(NULL), result(g_mime_decrypt_result_new())  {}
  55     ~GPGDecErr()
  56     {
  57       if (err) g_error_free(err);
  58       err = NULL;
  59     }
  60 
  61     GPGDecErr() : no_sigs(true), err(NULL), type(GPG_DECODE), decrypted(NULL), result(g_mime_decrypt_result_new()) {}

The GPGEncErr and GPGDecErr structs are passed through the MIME decomposition functions and are global variables that belong to the active HeaderPane. The values in there are filled by gmime functions.
The signer struct is just there for the GUI part. It is needed for the popup appearing after hovering over the verbose header display in the BodyPane.
The two enums are used for deciding what to do with the message: either decode, verify, encode or sign it or both.

D-BUS

D-BUS support was added from a bugzilla feature request. The main calls can be found in pan/gui/pan.cc. The reference for the D-DBUS code can be found here
It is used to add batch addition features to Pan. If one instance is running and another instance "sees" it on the message bus, the latter one is closed automatically. But the user is able to attach some more tasks to the first instance's Task Queue by invoking Pan from the command line with the nzb option. Once Pan gets this message from D-BUS, it parses its contents and decides whether or not to act on it. If the line contains a valid nzb, its contents are validated and the appropriate files are added to the queue. If not, it is silently discarded.

   1   #define PAN_DBUS_SERVICE_NAME      "news.pan.NZB"
   2   #define PAN_DBUS_SERVICE_PATH      "/news/pan/NZB"
   3 
   4   /** Struct for dbus handling */
   5   struct Pan
   6   {
   7     Data& data;
   8     Queue& queue;
   9     ArticleCache& cache;
  10     EncodeCache& encode_cache;
  11     Prefs& prefs;
  12     GroupPrefs& group_prefs;
  13     int dbus_id;
  14     bool name_valid;
  15     bool lost_name;
  16 
  17     GDBusNodeInfo * busnodeinfo;
  18     GDBusInterfaceVTable ifacetable;
  19 
  20     Pan(Data& d, Queue& q, ArticleCache& c, EncodeCache& ec, Prefs& p, GroupPrefs& gp) :
  21       dbus_id(-1), busnodeinfo(0),
  22       data(d), queue(q), cache(c), encode_cache(ec), prefs(p), group_prefs(gp),
  23       lost_name(false), name_valid(false)
  24       {}
  25   };
  26 
  27   static void
  28   nzb_method_call  (GDBusConnection      *connection,
  29                    const gchar           *sender,
  30                    const gchar           *object_path,
  31                    const gchar           *interface_name,
  32                    const gchar           *method_name,
  33                    GVariant              *parameters,
  34                    GDBusMethodInvocation *invocation,
  35                    gpointer               user_data)
  36   {
  37 
  38     Pan* pan(static_cast<Pan*>(user_data));
  39     if (!pan) return;
  40 
  41     gboolean nzb(false);
  42     gchar* groups;
  43     gchar* nzb_output_path;
  44     gchar* nzbs;
  45     strings_v nzb_files;
  46 
  47     if (g_strcmp0 (method_name, "NZBEnqueue") == 0)
  48     {
  49       g_variant_get (parameters, "(sssb)", &groups, &nzb_output_path, &nzbs, &nzb);
  50 
  51       if (groups)
  52         if (strlen(groups)!=0)
  53         {
  54           StringView tok, v(groups);
  55           while (v.pop_token(tok,','))
  56             pan->queue.add_task (new TaskXOver (pan->data, tok, TaskXOver::NEW), Queue::BOTTOM);
  57         }
  58 
  59       if (nzb && nzbs)
  60       {
  61         //parse the files
  62         StringView tok, nzb(nzbs);
  63         while (nzb.pop_token(tok))
  64           nzb_files.push_back(tok);
  65 
  66         // load the nzb files...
  67         std::vector<Task*> tasks;
  68         foreach_const (strings_v, nzb_files, it)
  69           NZB :: tasks_from_nzb_file (*it, nzb_output_path, pan->cache, pan->encode_cache, pan->data, pan->data, pan->data, tasks);
  70         pan->queue.add_tasks (tasks, Queue::BOTTOM);
  71       }
  72     }
  73 
  74     g_dbus_method_invocation_return_value (invocation, NULL);
  75   }
  76 
  77   static GDBusConnection *dbus_connection(NULL);
  78 
  79   static const gchar xml[]=
  80   "<node>"
  81   "  <interface name='news.pan.NZB'>"
  82   "    <method name='NZBEnqueue'>"
  83   "      <arg type='s' name='groups'    direction='in'/>"
  84   "      <arg type='s' name='nzb_files' direction='in'/>"
  85   "      <arg type='s' name='nzb_path'  direction='in'/>"
  86   "      <arg type='b' name='nzb'       direction='in'/>"
  87   "    </method>"
  88   "  </interface>"
  89   "</node>";

The first instance of Pan reserves a name on the bus that the second instance looks for and then sends the message to. The message consists of a GVariant ("(sssb)"), that's three strings and one boolean value.
The whole instance data is kept in a Pan struct that is a shallow copy of all important pointers the D-BUS invocation method needs. It is passed as an opaque pointer to it if another instance calls the NZBEnqueue method. It consists of the values bound to the interface name news.pan.NZB.
"groups" is a comma-separated list of the groups the user wants to a XOVER of.
"nzb_files" is a list of all the NZBs the user wants to queue.
"nzb_path" is the output path of the resulting queued files.

Apps/Pan/CodeOverview (last edited 2013-11-23 00:11:59 by WilliamJonMcCann)