Add command --list-preorder to payproc-post.
authorWerner Koch <wk@gnupg.org>
Thu, 15 Oct 2015 14:08:24 +0000 (16:08 +0200)
committerWerner Koch <wk@gnupg.org>
Thu, 15 Oct 2015 14:08:24 +0000 (16:08 +0200)
* src/commands.c (write_data_line): Factor some code out to ...
(write_data_value): new.
(cmd_listpreorder): New.
* src/preorder.c: Include membuf.h.
(format_columns): New.
(list_preorder_records): New.
(preorder_list_records): New.
* src/payproc-post.c (main): Add command --list-preorder.
(listpreorder): New.

src/commands.c
src/payproc-post.c
src/preorder.c
src/preorder.h

index 6ee550b..bd6bbab 100644 (file)
@@ -136,17 +136,10 @@ id_from_connection_obj (conn_t conn)
 
 
 static void
-write_data_line (keyvalue_t kv, estream_t fp)
+write_data_value (const char *value, estream_t fp)
 {
-  const char *value;
-
-  if (!kv)
-    return;
-  value = kv->value;
   if (!value)
-    return;
-  es_fputs (kv->name, fp);
-  es_fputs (": ", fp);
+    value = "";
   for ( ; *value; value++)
     {
       if (*value == '\n')
@@ -160,6 +153,21 @@ write_data_line (keyvalue_t kv, estream_t fp)
   es_putc ('\n', fp);
 }
 
+static void
+write_data_line (keyvalue_t kv, estream_t fp)
+{
+  const char *value;
+
+  if (!kv)
+    return;
+  value = kv->value;
+  if (!value)
+    return;
+  es_fputs (kv->name, fp);
+  es_fputs (": ", fp);
+  write_data_value (value, fp);
+}
+
 
 \f
 /* SESSION is a multipurpose command to help implement a state-full
@@ -908,6 +916,55 @@ cmd_getpreorder (conn_t conn, char *args)
 }
 
 
+/* The LISTPREORDER command retrieves a record from the preorder table.
+
+   Refnn:      The reference suffix (-NN).  If this is not given all
+               records are listed in reverse chronological order.
+
+   On success these items are returned:
+
+   Count:      Number of records
+   D[n]:       A formatted line with the data.  N starts at 0.
+
+ */
+static gpg_error_t
+cmd_listpreorder (conn_t conn, char *args)
+{
+  gpg_error_t err;
+  char *buf = NULL;
+  unsigned int n, count;
+  char key[30];
+
+  (void)args;
+
+  err = preorder_list_records (&conn->dataitems, &count);
+
+  if (err)
+    {
+      es_fprintf (conn->stream, "ERR %d (%s)\n", err,
+                  conn->errdesc? conn->errdesc : gpg_strerror (err));
+      write_data_line (keyvalue_find (conn->dataitems, "failure"),
+                       conn->stream);
+      write_data_line (keyvalue_find (conn->dataitems, "failure-mesg"),
+                       conn->stream);
+    }
+  else
+    {
+      es_fprintf (conn->stream, "OK\nCount: %u\n", count);
+      for (n=0; n < count; n++)
+        {
+          snprintf (key, sizeof key, "D[%u]", n);
+          es_fprintf (conn->stream, "%s: ", key);
+          write_data_value (keyvalue_get_string (conn->dataitems, key),
+                            conn->stream);
+        }
+    }
+
+  es_free (buf);
+  return err;
+}
+
+
 \f
 /* The CHECKAMOUNT command checks whether a given amount is within the
    configured limits for payment.  It may eventually provide
@@ -1088,6 +1145,7 @@ static struct
     { "PING",           cmd_ping },
     { "COMMITPREORDER", cmd_commitpreorder, 1 },
     { "GETPREORDER",    cmd_getpreorder, 1 },
+    { "LISTPREORDER",   cmd_listpreorder, 1 },
     { "HELP",           cmd_help },
     { NULL, NULL}
   };
index d1140c2..9ceb4bf 100644 (file)
@@ -54,6 +54,7 @@ enum opt_values
     aPing,
     aSepaPreorder,
     aGetPreorder,
+    aListPreorder,
 
     oLast
   };
@@ -66,6 +67,7 @@ static ARGPARSE_OPTS opts[] = {
   ARGPARSE_c (aPing, "ping",  "Send a ping"),
   ARGPARSE_c (aSepaPreorder, "sepa-preorder",  "Insert a SEPA preorder"),
   ARGPARSE_c (aGetPreorder,  "get-preorder",   "Read one preorder"),
+  ARGPARSE_c (aListPreorder,  "list-preorder",  "List preorders"),
 
   ARGPARSE_group (301, "@\nOptions:\n "),
   ARGPARSE_s_n (oVerbose, "verbose",  "verbose diagnostics"),
@@ -87,6 +89,7 @@ static gpg_error_t send_request (const char *command,
                                  keyvalue_t indata, keyvalue_t *outdata);
 static void post_sepa (const char *refstring, const char *amountstr);
 static void getpreorder (const char *refstring);
+static void listpreorder (const char *refstring);
 static void sepapreorder (const char *amountstr, const char *name,
                           const char *email, const char *desc);
 
@@ -150,6 +153,7 @@ main (int argc, char **argv)
         case aPing:
         case aSepaPreorder:
         case aGetPreorder:
+        case aListPreorder:
           if (cmd && cmd != pargs.r_opt)
             {
               log_error ("conflicting commands\n");
@@ -191,6 +195,12 @@ main (int argc, char **argv)
       ascii_strupr (argv[0]);
       getpreorder (argv[0]);
     }
+  else if (cmd == aListPreorder)
+    {
+      if (argc > 1)
+        wrong_args ("--list-preorder [NN]");
+      listpreorder (argc? argv[0] : NULL);
+    }
   else if (cmd == aSepaPreorder)
     {
       if (!argc || argc > 4)
@@ -364,6 +374,76 @@ getpreorder (const char *refstring)
 
 
 static void
+listpreorder (const char *refstring)
+{
+  gpg_error_t err;
+  keyvalue_t input = NULL;
+  keyvalue_t output = NULL;
+  unsigned int n, count;
+  char key[30];
+  const char *s, *t;
+  char **tokens;
+  int i;
+  int len;
+
+  if (refstring)
+    {
+      err = keyvalue_put (&input, "Refnn", refstring);
+      if (err)
+        log_fatal ("keyvalue_put failed: %s\n", gpg_strerror (err));
+    }
+
+  if (!send_request ("LISTPREORDER", input, &output))
+    {
+      count = keyvalue_get_uint (output, "Count");
+      es_printf ("Number of records: %u\n", count);
+      for (n=0; n < count; n++)
+        {
+          snprintf (key, sizeof key, "D[%u]", n);
+          s = keyvalue_get_string (output, key);
+          tokens = strtokenize (*s=='|'? s+1:s, "|");
+          if (!tokens)
+            log_fatal ("strtokenize failed: %s\n",
+                       gpg_strerror (gpg_error_from_syserror ()));
+          es_putc ('|', es_stdout);
+          for (i=0; (s = tokens[i]); i++)
+            {
+              if (!*s && !tokens[i+1])
+                continue; /* Skip an empty last field.  */
+              switch (i)
+                {
+                case 1: /* Created.   */
+                case 2: /* Last Paid - print only date.  */
+                  es_printf (" %10.10s |", s );
+                  break;
+                case 4:
+                  t = strchr (s, '.');
+                  len = t? (t-s) : strlen (s);
+                  es_printf (" %3.*s |", len, s );
+                  break;
+                case 5: /* Always EUR - don't print.  */
+                  break;
+                case 6: /* Don't print the description.  */
+                  break;
+                case 7: /* Email */
+                  es_printf (" %-20s |", s );
+                  break;
+                default:
+                  es_printf (" %s |", s );
+                  break;
+                }
+            }
+          es_putc ('\n', es_stdout);
+          xfree (tokens);
+        }
+    }
+
+  keyvalue_release (input);
+  keyvalue_release (output);
+}
+
+
+static void
 sepapreorder (const char *amountstr, const char *name,
               const char *email, const char *desc)
 {
index 0acb312..a004216 100644 (file)
@@ -59,7 +59,8 @@
 #include "util.h"
 #include "logging.h"
 #include "payprocd.h"
-#include "journal.h"  /* Temporary for meta_field_to_string.  */
+#include "journal.h"
+#include "membuf.h"
 #include "preorder.h"
 
 
@@ -498,6 +499,77 @@ get_columns (sqlite3_stmt *stmt, int idx, keyvalue_t *dictp)
 }
 
 
+/* Format columns and put the formatted line into DICTP under the
+   key "D[idx]".  */
+static gpg_error_t
+format_columns (sqlite3_stmt *stmt, int idx, keyvalue_t *dictp)
+{
+  gpg_error_t err;
+  membuf_t mb;
+  const char *s;
+  int i;
+
+  init_membuf (&mb, 512);
+
+  s = sqlite3_column_text (stmt, 0);
+  if (!s && sqlite3_errcode (preorder_db) == SQLITE_NOMEM)
+    {
+      err = gpg_error (GPG_ERR_ENOMEM);
+      goto leave;
+    }
+
+  i = sqlite3_column_int (stmt, 1);
+  if (!i && sqlite3_errcode (preorder_db) == SQLITE_NOMEM)
+    {
+      err = gpg_error (GPG_ERR_ENOMEM);
+      goto leave;
+    }
+  put_membuf_printf (&mb, "|%s-%02d", s, i);
+
+  for (i = 2; i <= 9; i++)
+    {
+      put_membuf_chr (&mb, '|');
+      s = sqlite3_column_text (stmt, i);
+      if (!s && sqlite3_errcode (preorder_db) == SQLITE_NOMEM)
+        {
+          err = gpg_error (GPG_ERR_ENOMEM);
+          goto leave;
+        }
+      if (!s)
+        ;
+      else if (strchr (s, '|'))
+        {
+          for (; *s; s++)
+            if (*s == '|')
+              put_membuf_str (&mb, "=7C");
+            else
+              put_membuf_chr (&mb, *s);
+        }
+      else
+        put_membuf_str (&mb, s);
+    }
+  put_membuf_chr (&mb, '|');
+
+  {
+    char *p;
+
+    put_membuf_chr (&mb, 0);
+    p = get_membuf (&mb, NULL);
+    if (!p)
+      err = gpg_error_from_syserror ();
+    else
+      {
+        err = keyvalue_put_idx (dictp, "D", idx, p);
+        xfree (p);
+      }
+  }
+
+ leave:
+  xfree (get_membuf (&mb, NULL));
+  return err;
+}
+
+
 /* Get a record from the preorder table.  The values are stored at the
    dictionary at DICTP.  */
 static gpg_error_t
@@ -547,6 +619,69 @@ get_preorder_record (const char *ref, keyvalue_t *dictp)
 }
 
 
+/* List records from the preorder table.  The values are stored at the
+   dictionary at DICTP with a D[n] key.  The number of records is
+   stored at R_COUNT.  */
+static gpg_error_t
+list_preorder_records (const char *refnn,
+                       keyvalue_t *dictp, unsigned int *r_count)
+{
+  gpg_error_t err;
+  sqlite3_stmt *stmt;
+  int count = 0;
+  int res;
+
+  stmt = *refnn? preorder_selectrefnn_stmt : preorder_selectlist_stmt;
+
+  sqlite3_reset (stmt);
+
+  if (*refnn)
+    {
+      res = sqlite3_bind_text (stmt, 1, refnn, -1, SQLITE_TRANSIENT);
+      if (res)
+        {
+          log_error ("error binding a value for the preorder table: %s\n",
+                     sqlite3_errstr (res));
+          return gpg_error (GPG_ERR_GENERAL);
+        }
+    }
+
+ next:
+  res = sqlite3_step (stmt);
+  if (res == SQLITE_ROW)
+    {
+      res = SQLITE_OK;
+      err = format_columns (stmt, count, dictp);
+      if (!err)
+        {
+          if (++count)
+            goto next;
+          err = gpg_error (GPG_ERR_WOULD_WRAP);
+        }
+    }
+  else if (res == SQLITE_DONE)
+    {
+      res = SQLITE_OK;
+      err = 0;
+    }
+  else
+    err = gpg_error (GPG_ERR_GENERAL);
+
+  if (err)
+    {
+      if (res == SQLITE_OK)
+        log_error ("error selecting from preorder table: %s\n",
+                   gpg_strerror (err));
+      else
+        log_error ("error selecting from preorder table: %s [%s (%d)]\n",
+                   gpg_strerror (err), sqlite3_errstr (res), res);
+    }
+  else
+    *r_count = count;
+  return err;
+}
+
+
 /* Update a row specified by REF in the preorder table.  Also update
    the timestamp field at DICTP. */
 static gpg_error_t
@@ -611,7 +746,6 @@ preorder_store_record (keyvalue_t *dictp)
     return err;
 
   err = insert_preorder_record (dictp);
-
   close_preorder_db (0);
 
   return err;
@@ -649,6 +783,39 @@ preorder_get_record (keyvalue_t *dictp)
 }
 
 
+/* Take the number Sepa-Ref from DICTP, fetch the row, and update DICTP with
+   that data.  On error return an error code.  Note that DICTP may
+   even be changed on error.  */
+gpg_error_t
+preorder_list_records (keyvalue_t *dictp, unsigned int *r_count)
+{
+  gpg_error_t err;
+  char refnn[3];
+  const char *s;
+
+  *r_count = 0;
+  s = keyvalue_get (*dictp, "Refnn");
+  if (s)
+    {
+      if (strlen (s) != 2)
+        return gpg_error (GPG_ERR_INV_LENGTH);
+      strcpy (refnn, s);
+    }
+  else
+    *refnn = 0;
+
+  err = open_preorder_db ();
+  if (err)
+    return err;
+
+  err = list_preorder_records (refnn, dictp, r_count);
+
+  close_preorder_db (0);
+
+  return err;
+}
+
+
 /* Take the Sepa-Ref from NEWDATA and update the corresponding row with
    the other data from NEWDATA.  On error return an error code.  */
 gpg_error_t
index cc4573a..e720980 100644 (file)
@@ -23,6 +23,7 @@
 gpg_error_t preorder_store_record (keyvalue_t *dictp);
 gpg_error_t preorder_update_record (keyvalue_t dict);
 gpg_error_t preorder_get_record (keyvalue_t *dictp);
+gpg_error_t preorder_list_records (keyvalue_t *dictp, unsigned int *r_count);
 
 
 #endif /*PREORDER_H*/