Add offline mode support for CMS keylisting
[gpgme.git] / src / engine-gpgsm.c
index c4272a4..3771157 100644 (file)
@@ -120,14 +120,14 @@ static char *
 gpgsm_get_version (const char *file_name)
 {
   return _gpgme_get_program_version (file_name ? file_name
-                                    : _gpgme_get_gpgsm_path ());
+                                    : _gpgme_get_default_gpgsm_name ());
 }
 
 
 static const char *
 gpgsm_get_req_version (void)
 {
-  return NEED_GPGSM_VERSION;
+  return "2.0.4";
 }
 
 \f
@@ -239,6 +239,7 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
 {
   gpgme_error_t err = 0;
   engine_gpgsm_t gpgsm;
+  const char *pgmname;
   const char *argv[5];
   int argc;
 #if !USE_DESCRIPTOR_PASSING
@@ -321,8 +322,10 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
   child_fds[3] = -1;
 #endif
 
+  pgmname = file_name ? file_name : _gpgme_get_default_gpgsm_name ();
+
   argc = 0;
-  argv[argc++] = "gpgsm";
+  argv[argc++] = _gpgme_get_basename (pgmname);
   if (home_dir)
     {
       argv[argc++] = "--homedir";
@@ -339,9 +342,8 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
   assuan_ctx_set_system_hooks (gpgsm->assuan_ctx, &_gpgme_assuan_system_hooks);
 
 #if USE_DESCRIPTOR_PASSING
-  err = assuan_pipe_connect
-    (gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (),
-     argv, NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
+  err = assuan_pipe_connect (gpgsm->assuan_ctx, pgmname, argv,
+                             NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
 #else
   {
     assuan_fd_t achild_fds[4];
@@ -351,9 +353,8 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir)
     for (i = 0; i < 4; i++)
       achild_fds[i] = (assuan_fd_t) child_fds[i];
 
-    err = assuan_pipe_connect
-      (gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (),
-       argv, achild_fds, NULL, NULL, 0);
+    err = assuan_pipe_connect (gpgsm->assuan_ctx, pgmname, argv,
+                               achild_fds, NULL, NULL, 0);
 
     /* For now... */
     for (i = 0; i < 4; i++)
@@ -563,7 +564,7 @@ gpgsm_assuan_simple_command (assuan_context_t ctx, char *cmd,
                             engine_status_handler_t status_fnc,
                             void *status_fnc_value)
 {
-  gpg_error_t err;
+  gpg_error_t err, cb_err;
   char *line;
   size_t linelen;
 
@@ -571,6 +572,7 @@ gpgsm_assuan_simple_command (assuan_context_t ctx, char *cmd,
   if (err)
     return err;
 
+  cb_err = 0;
   do
     {
       err = assuan_read_line (ctx, &line, &linelen);
@@ -583,32 +585,45 @@ gpgsm_assuan_simple_command (assuan_context_t ctx, char *cmd,
       if (linelen >= 2
          && line[0] == 'O' && line[1] == 'K'
          && (line[2] == '\0' || line[2] == ' '))
-       return 0;
+       return cb_err;
       else if (linelen >= 4
          && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
          && line[3] == ' ')
-       err = atoi (&line[4]);
+        {
+          /* We prefer a callback generated error because that one is
+             more related to gpgme and thus probably more important
+             than the error returned by the engine.  */
+          err = cb_err? cb_err : atoi (&line[4]);
+        }
       else if (linelen >= 2
               && line[0] == 'S' && line[1] == ' ')
        {
-         char *rest;
-         gpgme_status_code_t r;
+          /* After an error from a status callback we skip all further
+             status lines.  */
+          if (!cb_err)
+            {
+              char *rest;
+              gpgme_status_code_t r;
 
-         rest = strchr (line + 2, ' ');
-         if (!rest)
-           rest = line + linelen; /* set to an empty string */
-         else
-           *(rest++) = 0;
+              rest = strchr (line + 2, ' ');
+              if (!rest)
+                rest = line + linelen; /* set to an empty string */
+              else
+                *(rest++) = 0;
 
-         r = _gpgme_parse_status (line + 2);
+              r = _gpgme_parse_status (line + 2);
 
-         if (r >= 0 && status_fnc)
-           err = status_fnc (status_fnc_value, r, rest);
-         else
-           err = gpg_error (GPG_ERR_GENERAL);
+              if (r >= 0 && status_fnc)
+                cb_err = status_fnc (status_fnc_value, r, rest);
+            }
        }
       else
-       err = gpg_error (GPG_ERR_GENERAL);
+        {
+          /* Invalid line or INQUIRY.  We can't do anything else than
+             to stop.  As with ERR we prefer a status callback
+             generated error code, though.  */
+          err = cb_err ? cb_err : gpg_error (GPG_ERR_GENERAL);
+        }
     }
   while (!err);
 
@@ -835,7 +850,7 @@ status_handler (void *opaque, int fd)
              else
                {
                  *aline = newline;
-                 gpgsm->colon.attic.linesize += linelen + 1;
+                 gpgsm->colon.attic.linesize = *alinelen + linelen + 1;
                }
            }
          if (!err)
@@ -891,7 +906,7 @@ status_handler (void *opaque, int fd)
           char *src = line + 2;
          char *end = line + linelen;
          char *dst = src;
-          ssize_t nwritten;
+          gpgme_ssize_t nwritten;
 
           linelen = 0;
           while (src < end)
@@ -1527,7 +1542,7 @@ gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
 
 static gpgme_error_t
 gpgsm_keylist (void *engine, const char *pattern, int secret_only,
-              gpgme_keylist_mode_t mode)
+              gpgme_keylist_mode_t mode, int engine_flags)
 {
   engine_gpgsm_t gpgsm = engine;
   char *line;
@@ -1550,7 +1565,7 @@ gpgsm_keylist (void *engine, const char *pattern, int secret_only,
      the agent.  However on a fresh installation no public keys are
      available and thus there is no need for gpgsm to ask the agent
      whether a secret key exists for the public key.  */
-  if (secret_only)
+  if (secret_only || (mode & GPGME_KEYLIST_MODE_WITH_SECRET))
     gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "GETINFO agent-check",
                                  NULL, NULL);
 
@@ -1579,6 +1594,16 @@ gpgsm_keylist (void *engine, const char *pattern, int secret_only,
                                "OPTION with-ephemeral-keys=1":
                                "OPTION with-ephemeral-keys=0" ,
                                NULL, NULL);
+  gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
+                               (mode & GPGME_KEYLIST_MODE_WITH_SECRET)?
+                               "OPTION with-secret=1":
+                               "OPTION with-secret=0" ,
+                               NULL, NULL);
+  gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
+                               (engine_flags & GPGME_ENGINE_FLAG_OFFLINE)?
+                               "OPTION offline=1":
+                               "OPTION offline=0" ,
+                               NULL, NULL);
 
 
   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
@@ -1609,7 +1634,7 @@ gpgsm_keylist (void *engine, const char *pattern, int secret_only,
 
 static gpgme_error_t
 gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
-                  int reserved, gpgme_keylist_mode_t mode)
+                  int reserved, gpgme_keylist_mode_t mode, int engine_flags)
 {
   engine_gpgsm_t gpgsm = engine;
   char *line;
@@ -1644,7 +1669,16 @@ gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
                                "OPTION with-validation=1":
                                "OPTION with-validation=0" ,
                                NULL, NULL);
-
+  gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
+                               (mode & GPGME_KEYLIST_MODE_WITH_SECRET)?
+                               "OPTION with-secret=1":
+                               "OPTION with-secret=0" ,
+                               NULL, NULL);
+  gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
+                               (engine_flags & GPGME_ENGINE_FLAG_OFFLINE)?
+                               "OPTION offline=1":
+                               "OPTION offline=0" ,
+                               NULL, NULL);
 
   if (pattern && *pattern)
     {
@@ -1945,7 +1979,7 @@ gpgsm_passwd (void *engine, gpgme_key_t key, unsigned int flags)
 struct engine_ops _gpgme_engine_ops_gpgsm =
   {
     /* Static functions.  */
-    _gpgme_get_gpgsm_path,
+    _gpgme_get_default_gpgsm_name,
     NULL,
     gpgsm_get_version,
     gpgsm_get_req_version,
@@ -1986,5 +2020,7 @@ struct engine_ops _gpgme_engine_ops_gpgsm =
     gpgsm_io_event,
     gpgsm_cancel,
     NULL,              /* cancel_op */
-    gpgsm_passwd
+    gpgsm_passwd,
+    NULL,               /* set_pinentry_mode */
+    NULL                /* opspawn */
   };