Blame src/version.c

Packit d7e8d0
/* version.c - Version check routines.
Packit d7e8d0
   Copyright (C) 2000 Werner Koch (dd9jn)
Packit d7e8d0
   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008 g10 Code GmbH
Packit d7e8d0
Packit d7e8d0
   This file is part of GPGME.
Packit d7e8d0
Packit d7e8d0
   GPGME is free software; you can redistribute it and/or modify it
Packit d7e8d0
   under the terms of the GNU Lesser General Public License as
Packit d7e8d0
   published by the Free Software Foundation; either version 2.1 of
Packit d7e8d0
   the License, or (at your option) any later version.
Packit d7e8d0
Packit d7e8d0
   GPGME is distributed in the hope that it will be useful, but
Packit d7e8d0
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit d7e8d0
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit d7e8d0
   Lesser General Public License for more details.
Packit d7e8d0
Packit d7e8d0
   You should have received a copy of the GNU Lesser General Public
Packit d7e8d0
   License along with this program; if not, write to the Free Software
Packit d7e8d0
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
Packit d7e8d0
   02111-1307, USA.  */
Packit d7e8d0
Packit d7e8d0
#if HAVE_CONFIG_H
Packit d7e8d0
#include <config.h>
Packit d7e8d0
#endif
Packit d7e8d0
#include <stdlib.h>
Packit d7e8d0
#include <string.h>
Packit d7e8d0
#include <limits.h>
Packit d7e8d0
#include <ctype.h>
Packit d7e8d0
#ifdef HAVE_W32_SYSTEM
Packit d7e8d0
#include <winsock2.h>
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
#include "gpgme.h"
Packit d7e8d0
#include "priv-io.h"
Packit d7e8d0
#include "debug.h"
Packit d7e8d0
#include "context.h"
Packit d7e8d0
Packit d7e8d0
/* For _gpgme_sema_subsystem_init and _gpgme_status_init.  */
Packit d7e8d0
#include "sema.h"
Packit d7e8d0
#include "util.h"
Packit d7e8d0
Packit d7e8d0
#ifdef HAVE_ASSUAN_H
Packit d7e8d0
#include "assuan.h"
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
#ifdef HAVE_W32_SYSTEM
Packit d7e8d0
#include "windows.h"
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
/* We implement this function, so we have to disable the overriding
Packit d7e8d0
   macro.  */
Packit d7e8d0
#undef gpgme_check_version
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
/* Bootstrap the subsystems needed for concurrent operation.  This
Packit d7e8d0
   must be done once at startup.  We can not guarantee this using a
Packit d7e8d0
   lock, though, because the semaphore subsystem needs to be
Packit d7e8d0
   initialized itself before it can be used.  So we expect that the
Packit d7e8d0
   user performs the necessary synchronization.  */
Packit d7e8d0
static void
Packit d7e8d0
do_subsystem_inits (void)
Packit d7e8d0
{
Packit d7e8d0
  static int done = 0;
Packit d7e8d0
Packit d7e8d0
  if (done)
Packit d7e8d0
    return;
Packit d7e8d0
Packit d7e8d0
#ifdef HAVE_W32_SYSTEM
Packit d7e8d0
  /* We need to make sure that the sockets are initialized.  */
Packit d7e8d0
  {
Packit d7e8d0
    WSADATA wsadat;
Packit d7e8d0
Packit d7e8d0
    WSAStartup (0x202, &wsadat);
Packit d7e8d0
  }
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
  _gpgme_debug_subsystem_init ();
Packit d7e8d0
  _gpgme_io_subsystem_init ();
Packit d7e8d0
  _gpgme_status_init ();
Packit d7e8d0
Packit d7e8d0
  done = 1;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Put vesion information into the binary.  */
Packit d7e8d0
static const char *
Packit d7e8d0
cright_blurb (void)
Packit d7e8d0
{
Packit d7e8d0
  static const char blurb[] =
Packit d7e8d0
    "\n\n"
Packit d7e8d0
    "This is GPGME " PACKAGE_VERSION " - The GnuPG Made Easy library\n"
Packit d7e8d0
    CRIGHTBLURB
Packit d7e8d0
    "\n"
Packit d7e8d0
    "(" BUILD_REVISION " " BUILD_TIMESTAMP ")\n"
Packit d7e8d0
    "\n\n";
Packit d7e8d0
  return blurb;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Read the next number in the version string STR and return it in
Packit d7e8d0
   *NUMBER.  Return a pointer to the tail of STR after parsing, or
Packit d7e8d0
   *NULL if the version string was invalid.  */
Packit d7e8d0
static const char *
Packit d7e8d0
parse_version_number (const char *str, int *number)
Packit d7e8d0
{
Packit d7e8d0
#define MAXVAL ((INT_MAX - 10) / 10)
Packit d7e8d0
  int val = 0;
Packit d7e8d0
Packit d7e8d0
  /* Leading zeros are not allowed.  */
Packit d7e8d0
  if (*str == '0' && isdigit(str[1]))
Packit d7e8d0
    return NULL;
Packit d7e8d0
Packit d7e8d0
  while (isdigit (*str) && val <= MAXVAL)
Packit d7e8d0
    {
Packit d7e8d0
      val *= 10;
Packit d7e8d0
      val += *(str++) - '0';
Packit d7e8d0
    }
Packit d7e8d0
  *number = val;
Packit d7e8d0
  return val > MAXVAL ? NULL : str;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Parse the version string STR in the format MAJOR.MINOR.MICRO (for
Packit d7e8d0
   example, 9.3.2) and return the components in MAJOR, MINOR and MICRO
Packit d7e8d0
   as integers.  The function returns the tail of the string that
Packit d7e8d0
   follows the version number.  This might be the empty string if there
Packit d7e8d0
   is nothing following the version number, or a patchlevel.  The
Packit d7e8d0
   function returns NULL if the version string is not valid.  */
Packit d7e8d0
static const char *
Packit d7e8d0
parse_version_string (const char *str, int *major, int *minor, int *micro)
Packit d7e8d0
{
Packit d7e8d0
  str = parse_version_number (str, major);
Packit d7e8d0
  if (!str || *str != '.')
Packit d7e8d0
    return NULL;
Packit d7e8d0
  str++;
Packit d7e8d0
Packit d7e8d0
  str = parse_version_number (str, minor);
Packit d7e8d0
  if (!str || *str != '.')
Packit d7e8d0
    return NULL;
Packit d7e8d0
  str++;
Packit d7e8d0
Packit d7e8d0
  str = parse_version_number (str, micro);
Packit d7e8d0
  if (!str)
Packit d7e8d0
    return NULL;
Packit d7e8d0
Packit d7e8d0
  /* A patchlevel might follow.  */
Packit d7e8d0
  return str;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Return true if MY_VERSION is at least REQ_VERSION, and false
Packit d7e8d0
   otherwise.  */
Packit d7e8d0
int
Packit d7e8d0
_gpgme_compare_versions (const char *my_version,
Packit d7e8d0
			 const char *rq_version)
Packit d7e8d0
{
Packit d7e8d0
  int my_major, my_minor, my_micro;
Packit d7e8d0
  int rq_major, rq_minor, rq_micro;
Packit d7e8d0
  const char *my_plvl, *rq_plvl;
Packit d7e8d0
Packit d7e8d0
  if (!rq_version)
Packit d7e8d0
    return 1;
Packit d7e8d0
  if (!my_version)
Packit d7e8d0
    return 0;
Packit d7e8d0
Packit d7e8d0
  my_plvl = parse_version_string (my_version, &my_major, &my_minor, &my_micro);
Packit d7e8d0
  if (!my_plvl)
Packit d7e8d0
    return 0;
Packit d7e8d0
Packit d7e8d0
  rq_plvl = parse_version_string (rq_version, &rq_major, &rq_minor, &rq_micro);
Packit d7e8d0
  if (!rq_plvl)
Packit d7e8d0
    return 0;
Packit d7e8d0
Packit d7e8d0
  if (my_major > rq_major
Packit d7e8d0
      || (my_major == rq_major && my_minor > rq_minor)
Packit d7e8d0
      || (my_major == rq_major && my_minor == rq_minor
Packit d7e8d0
	  && my_micro > rq_micro)
Packit d7e8d0
      || (my_major == rq_major && my_minor == rq_minor
Packit d7e8d0
	  && my_micro == rq_micro && strcmp (my_plvl, rq_plvl) >= 0))
Packit d7e8d0
    return 1;
Packit d7e8d0
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Check that the the version of the library is at minimum the
Packit d7e8d0
   requested one and return the version string; return NULL if the
Packit d7e8d0
   condition is not met.  If a NULL is passed to this function, no
Packit d7e8d0
   check is done and the version string is simply returned.
Packit d7e8d0
Packit d7e8d0
   This function must be run once at startup, as it also initializes
Packit d7e8d0
   some subsystems.  Its invocation must be synchronized against
Packit d7e8d0
   calling any of the other functions in a multi-threaded
Packit d7e8d0
   environments.  */
Packit d7e8d0
const char *
Packit d7e8d0
gpgme_check_version (const char *req_version)
Packit d7e8d0
{
Packit d7e8d0
  const char *result;
Packit d7e8d0
  do_subsystem_inits ();
Packit d7e8d0
Packit d7e8d0
  /* Catch-22: We need to get at least the debug subsystem ready
Packit d7e8d0
     before using the trace facility.  If we won't the trace would
Packit d7e8d0
     automagically initialize the debug system with out the locks
Packit d7e8d0
     being initialized and missing the assuan log level setting. */
Packit d7e8d0
  TRACE2 (DEBUG_INIT, "gpgme_check_version", 0,
Packit d7e8d0
	  "req_version=%s, VERSION=%s",
Packit d7e8d0
          req_version? req_version:"(null)", VERSION);
Packit d7e8d0
Packit d7e8d0
  result = _gpgme_compare_versions (VERSION, req_version) ? VERSION : NULL;
Packit d7e8d0
  if (result != NULL)
Packit d7e8d0
    _gpgme_selftest = 0;
Packit d7e8d0
Packit d7e8d0
  return result;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
/* Check the version and also at runtime if the struct layout of the
Packit d7e8d0
   library matches the one of the user.  This is particular useful for
Packit d7e8d0
   Windows targets (-mms-bitfields).  */
Packit d7e8d0
const char *
Packit d7e8d0
gpgme_check_version_internal (const char *req_version,
Packit d7e8d0
			      size_t offset_sig_validity)
Packit d7e8d0
{
Packit d7e8d0
  const char *result;
Packit d7e8d0
Packit d7e8d0
  if (req_version && req_version[0] == 1 && req_version[1] == 1)
Packit d7e8d0
    return cright_blurb ();
Packit d7e8d0
  result = gpgme_check_version (req_version);
Packit d7e8d0
  if (result == NULL)
Packit d7e8d0
    return result;
Packit d7e8d0
Packit d7e8d0
  /* Catch-22, see above.  */
Packit d7e8d0
  TRACE2 (DEBUG_INIT, "gpgme_check_version_internal", 0,
Packit d7e8d0
	  "req_version=%s, offset_sig_validity=%i",
Packit d7e8d0
	  req_version ? req_version : "(null)", offset_sig_validity);
Packit d7e8d0
Packit d7e8d0
  if (offset_sig_validity != offsetof (struct _gpgme_signature, validity))
Packit d7e8d0
    {
Packit d7e8d0
      TRACE1 (DEBUG_INIT, "gpgme_check_version_internal", 0,
Packit d7e8d0
	      "offset_sig_validity mismatch: expected %i",
Packit d7e8d0
	      offsetof (struct _gpgme_signature, validity));
Packit d7e8d0
      _gpgme_selftest = GPG_ERR_SELFTEST_FAILED;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  return result;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
#define LINELENGTH 80
Packit d7e8d0
Packit d7e8d0
/* Extract the version string of a program from STRING.  The version
Packit d7e8d0
   number is expected to be in GNU style format:
Packit d7e8d0
Packit d7e8d0
     foo 1.2.3
Packit d7e8d0
     foo (bar system) 1.2.3
Packit d7e8d0
     foo 1.2.3 cruft
Packit d7e8d0
     foo (bar system) 1.2.3 cruft.
Packit d7e8d0
Packit d7e8d0
  Spaces and tabs are skipped and used as delimiters, a term in
Packit d7e8d0
  (nested) parenthesis before the version string is skipped, the
Packit d7e8d0
  version string may consist of any non-space and non-tab characters
Packit d7e8d0
  but needs to bstart with a digit.
Packit d7e8d0
*/
Packit d7e8d0
static const char *
Packit d7e8d0
extract_version_string (const char *string, size_t *r_len)
Packit d7e8d0
{
Packit d7e8d0
  const char *s;
Packit d7e8d0
  int count, len;
Packit d7e8d0
Packit d7e8d0
  for (s=string; *s; s++)
Packit d7e8d0
    if (*s == ' ' || *s == '\t')
Packit d7e8d0
        break;
Packit d7e8d0
  while (*s == ' ' || *s == '\t')
Packit d7e8d0
    s++;
Packit d7e8d0
  if (*s == '(')
Packit d7e8d0
    {
Packit d7e8d0
      for (count=1, s++; count && *s; s++)
Packit d7e8d0
        if (*s == '(')
Packit d7e8d0
          count++;
Packit d7e8d0
        else if (*s == ')')
Packit d7e8d0
          count--;
Packit d7e8d0
    }
Packit d7e8d0
  /* For robustness we look for a digit.  */
Packit d7e8d0
  while ( *s && !(*s >= '0' && *s <= '9') )
Packit d7e8d0
    s++;
Packit d7e8d0
  if (*s >= '0' && *s <= '9')
Packit d7e8d0
    {
Packit d7e8d0
      for (len=0; s[len]; len++)
Packit d7e8d0
        if (s[len] == ' ' || s[len] == '\t')
Packit d7e8d0
          break;
Packit d7e8d0
    }
Packit d7e8d0
  else
Packit d7e8d0
    len = 0;
Packit d7e8d0
Packit d7e8d0
  *r_len = len;
Packit d7e8d0
  return s;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Retrieve the version number from the --version output of the
Packit d7e8d0
   program FILE_NAME.  */
Packit d7e8d0
char *
Packit d7e8d0
_gpgme_get_program_version (const char *const file_name)
Packit d7e8d0
{
Packit d7e8d0
  char line[LINELENGTH] = "";
Packit d7e8d0
  int linelen = 0;
Packit d7e8d0
  char *mark = NULL;
Packit d7e8d0
  int rp[2];
Packit d7e8d0
  int nread;
Packit d7e8d0
  char *argv[] = {NULL /* file_name */, (char*)"--version", 0};
Packit d7e8d0
  struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
Packit d7e8d0
				   {-1, -1} };
Packit d7e8d0
  int status;
Packit d7e8d0
Packit d7e8d0
  if (!file_name)
Packit d7e8d0
    return NULL;
Packit d7e8d0
  argv[0] = (char *) file_name;
Packit d7e8d0
Packit d7e8d0
  if (_gpgme_io_pipe (rp, 1) < 0)
Packit d7e8d0
    return NULL;
Packit d7e8d0
Packit d7e8d0
  cfd[0].fd = rp[1];
Packit d7e8d0
Packit d7e8d0
  status = _gpgme_io_spawn (file_name, argv,
Packit d7e8d0
                            IOSPAWN_FLAG_DETACHED, cfd, NULL, NULL, NULL);
Packit d7e8d0
  if (status < 0)
Packit d7e8d0
    {
Packit d7e8d0
      _gpgme_io_close (rp[0]);
Packit d7e8d0
      _gpgme_io_close (rp[1]);
Packit d7e8d0
      return NULL;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  do
Packit d7e8d0
    {
Packit d7e8d0
      nread = _gpgme_io_read (rp[0], &line[linelen], LINELENGTH - linelen - 1);
Packit d7e8d0
      if (nread > 0)
Packit d7e8d0
	{
Packit d7e8d0
	  line[linelen + nread] = '\0';
Packit d7e8d0
	  mark = strchr (&line[linelen], '\n');
Packit d7e8d0
	  if (mark)
Packit d7e8d0
	    {
Packit d7e8d0
	      if (mark > &line[0] && *mark == '\r')
Packit d7e8d0
		mark--;
Packit d7e8d0
	      *mark = '\0';
Packit d7e8d0
	      break;
Packit d7e8d0
	    }
Packit d7e8d0
	  linelen += nread;
Packit d7e8d0
	}
Packit d7e8d0
    }
Packit d7e8d0
  while (nread > 0 && linelen < LINELENGTH - 1);
Packit d7e8d0
Packit d7e8d0
  _gpgme_io_close (rp[0]);
Packit d7e8d0
Packit d7e8d0
  if (mark)
Packit d7e8d0
    {
Packit d7e8d0
      size_t len;
Packit d7e8d0
      const char *s;
Packit d7e8d0
Packit d7e8d0
      s = extract_version_string (line, &len;;
Packit d7e8d0
      if (!len)
Packit d7e8d0
        return NULL;
Packit d7e8d0
      mark = malloc (len + 1);
Packit d7e8d0
      if (!mark)
Packit d7e8d0
	return NULL;
Packit d7e8d0
      memcpy (mark, s, len);
Packit d7e8d0
      mark[len] = 0;
Packit d7e8d0
      return mark;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  return NULL;
Packit d7e8d0
}