LCOV - code coverage report
Current view: top level - src - version.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 103 128 80.5 %
Date: 2015-11-05 17:14:26 Functions: 8 9 88.9 %

          Line data    Source code
       1             : /* version.c - Version check routines.
       2             :    Copyright (C) 2000 Werner Koch (dd9jn)
       3             :    Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008 g10 Code GmbH
       4             : 
       5             :    This file is part of GPGME.
       6             : 
       7             :    GPGME is free software; you can redistribute it and/or modify it
       8             :    under the terms of the GNU Lesser General Public License as
       9             :    published by the Free Software Foundation; either version 2.1 of
      10             :    the License, or (at your option) any later version.
      11             : 
      12             :    GPGME is distributed in the hope that it will be useful, but
      13             :    WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15             :    Lesser General Public License for more details.
      16             : 
      17             :    You should have received a copy of the GNU Lesser General Public
      18             :    License along with this program; if not, write to the Free Software
      19             :    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
      20             :    02111-1307, USA.  */
      21             : 
      22             : #if HAVE_CONFIG_H
      23             : #include <config.h>
      24             : #endif
      25             : #include <stdlib.h>
      26             : #include <string.h>
      27             : #include <limits.h>
      28             : #include <ctype.h>
      29             : #ifdef HAVE_W32_SYSTEM
      30             : #include <winsock2.h>
      31             : #endif
      32             : 
      33             : #include "gpgme.h"
      34             : #include "priv-io.h"
      35             : #include "debug.h"
      36             : #include "context.h"
      37             : 
      38             : /* For _gpgme_sema_subsystem_init and _gpgme_status_init.  */
      39             : #include "sema.h"
      40             : #include "util.h"
      41             : 
      42             : #ifdef HAVE_ASSUAN_H
      43             : #include "assuan.h"
      44             : #endif
      45             : 
      46             : #ifdef HAVE_W32_SYSTEM
      47             : #include "windows.h"
      48             : #endif
      49             : 
      50             : /* We implement this function, so we have to disable the overriding
      51             :    macro.  */
      52             : #undef gpgme_check_version
      53             : 
      54             : 
      55             : /* Bootstrap the subsystems needed for concurrent operation.  This
      56             :    must be done once at startup.  We can not guarantee this using a
      57             :    lock, though, because the semaphore subsystem needs to be
      58             :    initialized itself before it can be used.  So we expect that the
      59             :    user performs the necessary synchronization.  */
      60             : static void
      61          51 : do_subsystem_inits (void)
      62             : {
      63             :   static int done = 0;
      64             : 
      65          51 :   if (done)
      66          73 :     return;
      67             : 
      68             : #ifdef HAVE_W32_SYSTEM
      69             :   /* We need to make sure that the sockets are initialized.  */
      70             :   {
      71             :     WSADATA wsadat;
      72             : 
      73             :     WSAStartup (0x202, &wsadat);
      74             :   }
      75             : #endif
      76             : 
      77          29 :   _gpgme_sema_subsystem_init ();
      78          29 :   _gpgme_debug_subsystem_init ();
      79          29 :   _gpgme_io_subsystem_init ();
      80          29 :   _gpgme_status_init ();
      81             : 
      82          29 :   done = 1;
      83             : }
      84             : 
      85             : 
      86             : /* Put vesion information into the binary.  */
      87             : static const char *
      88           0 : cright_blurb (void)
      89             : {
      90             :   static const char blurb[] =
      91             :     "\n\n"
      92             :     "This is GPGME " PACKAGE_VERSION " - The GnuPG Made Easy library\n"
      93             :     CRIGHTBLURB
      94             :     "\n"
      95             :     "(" BUILD_REVISION " " BUILD_TIMESTAMP ")\n"
      96             :     "\n\n";
      97           0 :   return blurb;
      98             : }
      99             : 
     100             : 
     101             : /* Read the next number in the version string STR and return it in
     102             :    *NUMBER.  Return a pointer to the tail of STR after parsing, or
     103             :    *NULL if the version string was invalid.  */
     104             : static const char *
     105         300 : parse_version_number (const char *str, int *number)
     106             : {
     107             : #define MAXVAL ((INT_MAX - 10) / 10)
     108         300 :   int val = 0;
     109             : 
     110             :   /* Leading zeros are not allowed.  */
     111         300 :   if (*str == '0' && isdigit(str[1]))
     112           0 :     return NULL;
     113             : 
     114         948 :   while (isdigit (*str) && val <= MAXVAL)
     115             :     {
     116         348 :       val *= 10;
     117         348 :       val += *(str++) - '0';
     118             :     }
     119         300 :   *number = val;
     120         300 :   return val > MAXVAL ? NULL : str;
     121             : }
     122             : 
     123             : 
     124             : /* Parse the version string STR in the format MAJOR.MINOR.MICRO (for
     125             :    example, 9.3.2) and return the components in MAJOR, MINOR and MICRO
     126             :    as integers.  The function returns the tail of the string that
     127             :    follows the version number.  This might be te empty string if there
     128             :    is nothing following the version number, or a patchlevel.  The
     129             :    function returns NULL if the version string is not valid.  */
     130             : static const char *
     131         100 : parse_version_string (const char *str, int *major, int *minor, int *micro)
     132             : {
     133         100 :   str = parse_version_number (str, major);
     134         100 :   if (!str || *str != '.')
     135           0 :     return NULL;
     136         100 :   str++;
     137             : 
     138         100 :   str = parse_version_number (str, minor);
     139         100 :   if (!str || *str != '.')
     140           0 :     return NULL;
     141         100 :   str++;
     142             : 
     143         100 :   str = parse_version_number (str, micro);
     144         100 :   if (!str)
     145           0 :     return NULL;
     146             : 
     147             :   /* A patchlevel might follow.  */
     148         100 :   return str;
     149             : }
     150             : 
     151             : 
     152             : /* Return true if MY_VERSION is at least REQ_VERSION, and false
     153             :    otherwise.  */
     154             : int
     155          99 : _gpgme_compare_versions (const char *my_version,
     156             :                          const char *rq_version)
     157             : {
     158             :   int my_major, my_minor, my_micro;
     159             :   int rq_major, rq_minor, rq_micro;
     160             :   const char *my_plvl, *rq_plvl;
     161             : 
     162          99 :   if (!rq_version)
     163          49 :     return 1;
     164          50 :   if (!my_version)
     165           0 :     return 0;
     166             : 
     167          50 :   my_plvl = parse_version_string (my_version, &my_major, &my_minor, &my_micro);
     168          50 :   if (!my_plvl)
     169           0 :     return 0;
     170             : 
     171          50 :   rq_plvl = parse_version_string (rq_version, &rq_major, &rq_minor, &rq_micro);
     172          50 :   if (!rq_plvl)
     173           0 :     return 0;
     174             : 
     175          50 :   if (my_major > rq_major
     176           9 :       || (my_major == rq_major && my_minor > rq_minor)
     177           2 :       || (my_major == rq_major && my_minor == rq_minor
     178           2 :           && my_micro > rq_micro)
     179           2 :       || (my_major == rq_major && my_minor == rq_minor
     180           2 :           && my_micro == rq_micro && strcmp (my_plvl, rq_plvl) >= 0))
     181          49 :     return 1;
     182             : 
     183           1 :   return 0;
     184             : }
     185             : 
     186             : 
     187             : /* Check that the the version of the library is at minimum the
     188             :    requested one and return the version string; return NULL if the
     189             :    condition is not met.  If a NULL is passed to this function, no
     190             :    check is done and the version string is simply returned.
     191             : 
     192             :    This function must be run once at startup, as it also initializes
     193             :    some subsystems.  Its invocation must be synchronized against
     194             :    calling any of the other functions in a multi-threaded
     195             :    environments.  */
     196             : const char *
     197          51 : gpgme_check_version (const char *req_version)
     198             : {
     199             :   char *result;
     200          51 :   do_subsystem_inits ();
     201             : 
     202             :   /* Catch-22: We need to get at least the debug subsystem ready
     203             :      before using the trace facility.  If we won't the trace would
     204             :      automagically initialize the debug system with out the locks
     205             :      being initialized and missing the assuan log level setting. */
     206          51 :   TRACE2 (DEBUG_INIT, "gpgme_check_version", 0,
     207             :           "req_version=%s, VERSION=%s",
     208             :           req_version? req_version:"(null)", VERSION);
     209             : 
     210          51 :   result = _gpgme_compare_versions (VERSION, req_version) ? VERSION : NULL;
     211          51 :   if (result != NULL)
     212          50 :     _gpgme_selftest = 0;
     213             : 
     214          51 :   return result;
     215             : }
     216             : 
     217             : /* Check the version and also at runtime if the struct layout of the
     218             :    library matches the one of the user.  This is particular useful for
     219             :    Windows targets (-mms-bitfields).  */
     220             : const char *
     221          51 : gpgme_check_version_internal (const char *req_version,
     222             :                               size_t offset_sig_validity)
     223             : {
     224             :   const char *result;
     225             : 
     226          51 :   if (req_version && req_version[0] == 1 && req_version[1] == 1)
     227           0 :     return cright_blurb ();
     228          51 :   result = gpgme_check_version (req_version);
     229          51 :   if (result == NULL)
     230           1 :     return result;
     231             : 
     232             :   /* Catch-22, see above.  */
     233          50 :   TRACE2 (DEBUG_INIT, "gpgme_check_version_internal", 0,
     234             :           "req_version=%s, offset_sig_validity=%i",
     235             :           req_version ? req_version : "(null)", offset_sig_validity);
     236             : 
     237          50 :   if (offset_sig_validity != offsetof (struct _gpgme_signature, validity))
     238             :     {
     239           0 :       TRACE1 (DEBUG_INIT, "gpgme_check_version_internal", 0,
     240             :               "offset_sig_validity mismatch: expected %i",
     241             :               offsetof (struct _gpgme_signature, validity));
     242           0 :       _gpgme_selftest = GPG_ERR_SELFTEST_FAILED;
     243             :     }
     244             : 
     245          50 :   return result;
     246             : }
     247             : 
     248             : 
     249             : #define LINELENGTH 80
     250             : 
     251             : /* Extract the version string of a program from STRING.  The version
     252             :    number is expected to be in GNU style format:
     253             : 
     254             :      foo 1.2.3
     255             :      foo (bar system) 1.2.3
     256             :      foo 1.2.3 cruft
     257             :      foo (bar system) 1.2.3 cruft.
     258             : 
     259             :   Spaces and tabs are skipped and used as delimiters, a term in
     260             :   (nested) parenthesis before the version string is skipped, the
     261             :   version string may consist of any non-space and non-tab characters
     262             :   but needs to bstart with a digit.
     263             : */
     264             : static const char *
     265         136 : extract_version_string (const char *string, size_t *r_len)
     266             : {
     267             :   const char *s;
     268             :   int count, len;
     269             : 
     270         714 :   for (s=string; *s; s++)
     271         714 :     if (*s == ' ' || *s == '\t')
     272             :         break;
     273         408 :   while (*s == ' ' || *s == '\t')
     274         136 :     s++;
     275         136 :   if (*s == '(')
     276             :     {
     277         952 :       for (count=1, s++; count && *s; s++)
     278         816 :         if (*s == '(')
     279           0 :           count++;
     280         816 :         else if (*s == ')')
     281         136 :           count--;
     282             :     }
     283             :   /* For robustness we look for a digit.  */
     284         408 :   while ( *s && !(*s >= '0' && *s <= '9') )
     285         136 :     s++;
     286         272 :   if (*s >= '0' && *s <= '9')
     287             :     {
     288        1904 :       for (len=0; s[len]; len++)
     289        1768 :         if (s[len] == ' ' || s[len] == '\t')
     290             :           break;
     291             :     }
     292             :   else
     293           0 :     len = 0;
     294             : 
     295         136 :   *r_len = len;
     296         136 :   return s;
     297             : }
     298             : 
     299             : 
     300             : /* Retrieve the version number from the --version output of the
     301             :    program FILE_NAME.  */
     302             : char *
     303         136 : _gpgme_get_program_version (const char *const file_name)
     304             : {
     305         136 :   char line[LINELENGTH] = "";
     306         136 :   int linelen = 0;
     307         136 :   char *mark = NULL;
     308             :   int rp[2];
     309             :   int nread;
     310         136 :   char *argv[] = {NULL /* file_name */, "--version", 0};
     311         136 :   struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
     312             :                                    {-1, -1} };
     313             :   int status;
     314             : 
     315         136 :   if (!file_name)
     316           0 :     return NULL;
     317         136 :   argv[0] = (char *) file_name;
     318             : 
     319         136 :   if (_gpgme_io_pipe (rp, 1) < 0)
     320           0 :     return NULL;
     321             : 
     322         136 :   cfd[0].fd = rp[1];
     323             : 
     324         136 :   status = _gpgme_io_spawn (file_name, argv,
     325             :                             IOSPAWN_FLAG_DETACHED, cfd, NULL, NULL, NULL);
     326         136 :   if (status < 0)
     327             :     {
     328           0 :       _gpgme_io_close (rp[0]);
     329           0 :       _gpgme_io_close (rp[1]);
     330           0 :       return NULL;
     331             :     }
     332             : 
     333             :   do
     334             :     {
     335         136 :       nread = _gpgme_io_read (rp[0], &line[linelen], LINELENGTH - linelen - 1);
     336         136 :       if (nread > 0)
     337             :         {
     338         136 :           line[linelen + nread] = '\0';
     339         136 :           mark = strchr (&line[linelen], '\n');
     340         136 :           if (mark)
     341             :             {
     342         136 :               if (mark > &line[0] && *mark == '\r')
     343           0 :                 mark--;
     344         136 :               *mark = '\0';
     345         136 :               break;
     346             :             }
     347           0 :           linelen += nread;
     348             :         }
     349             :     }
     350           0 :   while (nread > 0 && linelen < LINELENGTH - 1);
     351             : 
     352         136 :   _gpgme_io_close (rp[0]);
     353             : 
     354         136 :   if (mark)
     355             :     {
     356             :       size_t len;
     357             :       const char *s;
     358             : 
     359         136 :       s = extract_version_string (line, &len);
     360         136 :       if (!len)
     361           0 :         return NULL;
     362         136 :       mark = malloc (len + 1);
     363         136 :       if (!mark)
     364           0 :         return NULL;
     365         136 :       memcpy (mark, s, len);
     366         136 :       mark[len] = 0;
     367         136 :       return mark;
     368             :     }
     369             : 
     370           0 :   return NULL;
     371             : }

Generated by: LCOV version 1.11