auto updated version number.
[gpgme.git] / gpgme / version.c
index 5c486f2..2b2874b 100644 (file)
 #include "sema.h"
 #include "util.h"
 #include "key.h" /* for key_cache_init */
-
-static int lineno;
-static char *tmp_engine_version;
-
-static const char *get_engine_info (void);
+#include "io.h"
 
 
 static void
 do_subsystem_inits (void)
 {
-    static int done = 0;
+  static int done = 0;
 
-    if (done)
-        return;
-    _gpgme_sema_subsystem_init ();
-    _gpgme_key_cache_init ();
+  if (done)
+    return;
+  _gpgme_sema_subsystem_init ();
+  _gpgme_key_cache_init ();
+  done = 1;
 }
 
-
-
 static const char*
-parse_version_number ( const char *s, int *number )
+parse_version_number (const char *s, int *number)
 {
-    int val = 0;
-
-    if ( *s == '0' && isdigit(s[1]) )
-       return NULL; /* leading zeros are not allowed */
-    for ( ; isdigit(*s); s++ ) {
-       val *= 10;
-       val += *s - '0';
+  int val = 0;
+
+  if (*s == '0' && isdigit(s[1]))
+    return NULL;  /* Leading zeros are not allowed.  */
+  for (; isdigit(*s); s++)
+    {
+      val *= 10;
+      val += *s - '0';
     }
-    *number = val;
-    return val < 0? NULL : s;
+  *number = val;
+  return val < 0 ? NULL : s;
 }
 
-
 static const char *
-parse_version_string( const char *s, int *major, int *minor, int *micro )
+parse_version_string (const char *s, int *major, int *minor, int *micro)
 {
-    s = parse_version_number ( s, major );
-    if ( !s || *s != '.' )
-       return NULL;
-    s++;
-    s = parse_version_number ( s, minor );
-    if ( !s || *s != '.' )
-       return NULL;
-    s++;
-    s = parse_version_number ( s, micro );
-    if ( !s )
-       return NULL;
-    return s; /* patchlevel */
+  s = parse_version_number (s, major);
+  if (!s || *s != '.')
+    return NULL;
+  s++;
+  s = parse_version_number (s, minor);
+  if (!s || *s != '.')
+    return NULL;
+  s++;
+  s = parse_version_number (s, micro);
+  if (!s)
+    return NULL;
+  return s;  /* Patchlevel.  */
 }
 
-static const char *
-compare_versions ( const char *my_version, const char *req_version )
+const char *
+_gpgme_compare_versions (const char *my_version,
+                        const char *req_version)
 {
-    int my_major, my_minor, my_micro;
-    int rq_major, rq_minor, rq_micro;
-    const char *my_plvl, *rq_plvl;
-
-    if ( !req_version )
-       return my_version;
+  int my_major, my_minor, my_micro;
+  int rq_major, rq_minor, rq_micro;
+  const char *my_plvl, *rq_plvl;
 
-    my_plvl = parse_version_string ( my_version,
-                                     &my_major, &my_minor, &my_micro );
-    if ( !my_plvl )
-       return NULL;  /* very strange: our own version is bogus */
-    rq_plvl = parse_version_string( req_version,
-                                    &rq_major, &rq_minor, &rq_micro );
-    if ( !rq_plvl )
-       return NULL;  /* req version string is invalid */
+  if (!req_version)
+    return my_version;
+  if (!my_version)
+    return NULL;
 
-    if ( my_major > rq_major
-         || (my_major == rq_major && my_minor > rq_minor)
-         || (my_major == rq_major && my_minor == rq_minor 
-             && my_micro > rq_micro)
-         || (my_major == rq_major && my_minor == rq_minor
-             && my_micro == rq_micro
-             && strcmp( my_plvl, rq_plvl ) >= 0) ) {
-       return my_version;
+  my_plvl = parse_version_string (my_version, &my_major, &my_minor, &my_micro);
+  if (!my_plvl)
+    return NULL;       /* Very strange: our own version is bogus.  */
+  rq_plvl = parse_version_string(req_version,
+                                &rq_major, &rq_minor, &rq_micro);
+  if (!rq_plvl)
+    return NULL;       /* Requested version string is invalid.  */
+
+  if (my_major > rq_major
+       || (my_major == rq_major && my_minor > rq_minor)
+      || (my_major == rq_major && my_minor == rq_minor 
+         && my_micro > rq_micro)
+      || (my_major == rq_major && my_minor == rq_minor
+         && my_micro == rq_micro
+         && strcmp( my_plvl, rq_plvl ) >= 0))
+    {
+      return my_version;
     }
-    return NULL;
+  return NULL;
 }
 
-
 /**
  * gpgme_check_version:
  * @req_version: A string with a version
@@ -131,177 +128,148 @@ compare_versions ( const char *my_version, const char *req_version )
  * Return value: The version string or NULL
  **/
 const char *
-gpgme_check_version ( const char *req_version )
+gpgme_check_version (const char *req_version)
 {
-    do_subsystem_inits ();
-    return compare_versions ( VERSION, req_version );
+  do_subsystem_inits ();
+  return _gpgme_compare_versions (VERSION, req_version);
 }
 
-
 /**
  * gpgme_get_engine_info:
  *  
- * Return information about the underlying crypto engine.  This is an
- * XML string with various information.  To get the version of the
- * crypto engine it should be sufficient to grep for the first
- * <literal>version</literal> tag and use it's content.  A string is
- * always returned even if the crypto engine is not installed; in this
- * case a XML string with some error information is returned.
+ * Return information about the underlying crypto engines.  This is an
+ * XML string with various information.  A string is always returned
+ * even if the crypto engines is not installed; in this case a XML
+ * string with some error information is returned.
  * 
- * Return value: A XML string with information about the crypto engine.
+ * Return value: A XML string with information about the crypto
+ * engines.
  **/
 const char *
 gpgme_get_engine_info ()
 {
-    do_subsystem_inits ();
-    return get_engine_info ();
+  static const char *engine_info;
+  DEFINE_STATIC_LOCK (engine_info_lock);
+
+  LOCK (engine_info_lock);
+  if (!engine_info)
+    {
+      const char *openpgp_info = _gpgme_engine_get_info (GPGME_PROTOCOL_OpenPGP);
+      const char *cms_info = _gpgme_engine_get_info (GPGME_PROTOCOL_CMS);
+      char *info;
+
+      if (!openpgp_info && !cms_info)
+       info = "<EngineInfo>\n</EngineInfo>\n";
+      else if (!openpgp_info || !cms_info)
+       {
+         const char *fmt = "<EngineInfo>\n"
+           "%s"
+           "</EngineInfo>\n";
+
+         info = xtrymalloc (strlen (fmt)
+                            + strlen (openpgp_info
+                                     ? openpgp_info : cms_info) + 1);
+         if (info)
+           sprintf (info, fmt, openpgp_info ? openpgp_info : cms_info);
+       }
+      else
+       {
+         const char *fmt = "<EngineInfo>\n"
+           "%s%s"
+           "</EngineInfo>\n";
+         info = xtrymalloc (strlen (fmt) + strlen (openpgp_info)
+                            + strlen (cms_info) + 1);
+         if (info)
+           sprintf (info, fmt, openpgp_info, cms_info);
+       }
+      if (!info)
+       info = "<EngineInfo>\n"
+         "  <error>Out of core</error>\n"
+         "</EngineInfo>\n";
+      engine_info = info;
+    }
+  UNLOCK (engine_info_lock);
+  return engine_info;
 }
 
+
 /**
  * gpgme_check_engine:
  * 
- * Check whether the installed crypto engine matches the requirement of
- * GPGME.
+ * Check whether the installed crypto engine for the OpenPGP protocol
+ * matches the requirement of GPGME.  This function is deprecated,
+ * instead use gpgme_engine_get_info() with the specific protocol you
+ * need.
  *
  * Return value: 0 or an error code.
  **/
 GpgmeError
 gpgme_check_engine ()
 {
-    const char *info = gpgme_get_engine_info ();
-    const char *s, *s2;
-
-    s = strstr (info, "<version>");
-    if (s) {
-        s += 9;
-        s2 = strchr (s, '<');
-        if (s2) {
-            char *ver = xtrymalloc (s2 - s + 1);
-            if (!ver)
-                return mk_error (Out_Of_Core);
-            memcpy (ver, s, s2-s);
-            ver[s2-s] = 0;
-            s = compare_versions ( ver, NEED_GPG_VERSION );
-            xfree (ver);
-            if (s)
-                return 0;
-        }
-    }
-    return mk_error (Invalid_Engine);
+  return gpgme_engine_check_version (GPGME_PROTOCOL_OpenPGP);
 }
 
-
 \f
-static void
-version_line_handler ( GpgmeCtx c, char *line )
-{
-    char *p;
-    size_t len;
-
-    lineno++;
-    if ( c->out_of_core )
-        return;
-    if (!line)
-        return; /* EOF */
-    if (lineno==1) {
-        if ( memcmp (line, "gpg ", 4) )
-            return;
-        if ( !(p = strpbrk (line, "0123456789")) )
-            return;
-        len = strcspn (p, " \t\r\n()<>" );
-        p[len] = 0;
-        tmp_engine_version = xtrystrdup (p);
-    }
-}
+#define LINELENGTH 80
 
-
-static const char *
-get_engine_info (void)
+char *
+_gpgme_get_program_version (const char *const path)
 {
-    static const char *engine_info =NULL;
-    GpgmeCtx c = NULL;
-    GpgmeError err = 0;
-    const char *path = NULL;
-
-    /* FIXME: make sure that only one instance does run */
-    if (engine_info)
-        return engine_info;
-
-    path = _gpgme_get_gpg_path ();
-    err = gpgme_new (&c);
-    if (err) 
-        goto leave;
-    err = _gpgme_gpg_new ( &c->gpg );
-    if (err)
-        goto leave;
+  char line[LINELENGTH] = "";
+  int linelen = 0;
+  char *mark = NULL;
+  int rp[2];
+  pid_t pid;
+  int nread;
+  char *argv[] = {(char *) path, "--version", 0};
+  struct spawn_fd_item_s pfd[] = { {0, -1}, {-1, -1} };
+  struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */}, {-1, -1} };
+  int status, signal;
+
+  if (!path)
+    return NULL;
 
-    err = _gpgme_gpg_set_simple_line_handler ( c->gpg,
-                                               version_line_handler, c );
-    if (err)
-        goto leave;
+  if (_gpgme_io_pipe (rp, 1) < 0)
+    return NULL;
 
-    _gpgme_gpg_add_arg ( c->gpg, "--version" );
-    lineno = 0;
-    xfree (tmp_engine_version); tmp_engine_version = NULL;
-    err = _gpgme_gpg_spawn ( c->gpg, c );
-    if (err)
-        goto leave;
-    gpgme_wait (c, 1);
-    if (tmp_engine_version) {
-        const char *fmt;
-        char *p;
+  pfd[0].fd = rp[1];
+  cfd[0].fd = rp[1];
 
-        fmt = "<GnupgInfo>\n"
-              " <engine>\n"
-              "  <version>%s</version>\n"
-              "  <path>%s</path>\n"
-              " </engine>\n"
-              "</GnupgInfo>\n";
-        /*(yes, I know that we allocating 2 extra bytes)*/
-        p = xtrymalloc ( strlen(fmt) + strlen(path)
-                         + strlen (tmp_engine_version) + 1);
-        if (!p) {
-            err = mk_error (Out_Of_Core);
-            goto leave;
-        }
-        sprintf (p, fmt, tmp_engine_version, path);
-        engine_info = p;
-        xfree (tmp_engine_version); tmp_engine_version = NULL;
-    }
-    else {
-        err = mk_error (General_Error);
+  pid = _gpgme_io_spawn (path, argv, cfd, pfd);
+  if (pid < 0)
+    {
+      _gpgme_io_close (rp[0]);
+      _gpgme_io_close (rp[1]);
+      return NULL;
     }
 
- leave:
-    if (err) {
-        const char *fmt;
-        const char *errstr = gpgme_strerror (err);
-        char *p;
-
-        fmt = "<GnupgInfo>\n"
-            " <engine>\n"
-            "  <error>%s</error>\n"                
-            "  <path>%s</path>\n"
-            " </engine>\n"
-            "</GnupgInfo>\n";
-
-        p = xtrymalloc ( strlen(fmt) + strlen(errstr) + strlen(path) + 1);
-        if (p) { 
-            sprintf (p, fmt, errstr, path);
-            engine_info = p;
-        }
-        else {
-            engine_info = "<GnupgInfo>\n"
-                          "  <error>Out of core</error>\n"
-                          "</GnupgInfo>\n";
-        }
+  do
+    {
+      nread = _gpgme_io_read (rp[0], &line[linelen], LINELENGTH - linelen - 1);
+      if (nread > 0)
+       {
+         line[linelen + nread] = '\0';
+         mark = strchr (&line[linelen], '\n');
+         if (mark)
+           {
+             *mark = '\0';
+             break;
+           }
+         linelen += nread;
+       }
     }
-    gpgme_release ( c );
-    return engine_info;
-}
-
-
-
+  while (nread > 0 && linelen < LINELENGTH - 1);
 
+  _gpgme_io_close (rp[0]);
+  _gpgme_io_waitpid (pid, 1, &status, &signal);
 
+  if (mark)
+    {
+      mark = strrchr (line, ' ');
+      if (!mark)
+       return NULL;
+      return xtrystrdup (mark + 1);
+    }
 
+  return NULL;
+}