LCOV - code coverage report
Current view: top level - src - version.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 103 127 81.1 %
Date: 2018-11-15 08:49:49 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         150 : do_subsystem_inits (void)
      62             : {
      63             :   static int done = 0;
      64             : 
      65         150 :   if (done)
      66          31 :     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         119 :   _gpgme_debug_subsystem_init ();
      78         119 :   _gpgme_io_subsystem_init ();
      79         119 :   _gpgme_status_init ();
      80             : 
      81         119 :   done = 1;
      82             : }
      83             : 
      84             : 
      85             : /* Put vesion information into the binary.  */
      86             : static const char *
      87           0 : cright_blurb (void)
      88             : {
      89             :   static const char blurb[] =
      90             :     "\n\n"
      91             :     "This is GPGME " PACKAGE_VERSION " - The GnuPG Made Easy library\n"
      92             :     CRIGHTBLURB
      93             :     "\n"
      94             :     "(" BUILD_REVISION " " BUILD_TIMESTAMP ")\n"
      95             :     "\n\n";
      96           0 :   return blurb;
      97             : }
      98             : 
      99             : 
     100             : /* Read the next number in the version string STR and return it in
     101             :    *NUMBER.  Return a pointer to the tail of STR after parsing, or
     102             :    *NULL if the version string was invalid.  */
     103             : static const char *
     104        9417 : parse_version_number (const char *str, int *number)
     105             : {
     106             : #define MAXVAL ((INT_MAX - 10) / 10)
     107        9417 :   int val = 0;
     108             : 
     109             :   /* Leading zeros are not allowed.  */
     110        9417 :   if (*str == '0' && isdigit(str[1]))
     111           0 :     return NULL;
     112             : 
     113       31066 :   while (isdigit (*str) && val <= MAXVAL)
     114             :     {
     115       12232 :       val *= 10;
     116       12232 :       val += *(str++) - '0';
     117             :     }
     118        9416 :   *number = val;
     119        9416 :   return val > MAXVAL ? NULL : str;
     120             : }
     121             : 
     122             : 
     123             : /* Parse the version string STR in the format MAJOR.MINOR.MICRO (for
     124             :    example, 9.3.2) and return the components in MAJOR, MINOR and MICRO
     125             :    as integers.  The function returns the tail of the string that
     126             :    follows the version number.  This might be the empty string if there
     127             :    is nothing following the version number, or a patchlevel.  The
     128             :    function returns NULL if the version string is not valid.  */
     129             : static const char *
     130        3140 : parse_version_string (const char *str, int *major, int *minor, int *micro)
     131             : {
     132        3140 :   str = parse_version_number (str, major);
     133        3141 :   if (!str || *str != '.')
     134           0 :     return NULL;
     135        3141 :   str++;
     136             : 
     137        3141 :   str = parse_version_number (str, minor);
     138        3141 :   if (!str || *str != '.')
     139           0 :     return NULL;
     140        3141 :   str++;
     141             : 
     142        3141 :   str = parse_version_number (str, micro);
     143        3141 :   if (!str)
     144           0 :     return NULL;
     145             : 
     146             :   /* A patchlevel might follow.  */
     147        3141 :   return str;
     148             : }
     149             : 
     150             : 
     151             : /* Return true if MY_VERSION is at least REQ_VERSION, and false
     152             :    otherwise.  */
     153             : int
     154        1719 : _gpgme_compare_versions (const char *my_version,
     155             :                          const char *rq_version)
     156             : {
     157             :   int my_major, my_minor, my_micro;
     158             :   int rq_major, rq_minor, rq_micro;
     159             :   const char *my_plvl, *rq_plvl;
     160             : 
     161        1719 :   if (!rq_version)
     162         148 :     return 1;
     163        1571 :   if (!my_version)
     164           0 :     return 0;
     165             : 
     166        1571 :   my_plvl = parse_version_string (my_version, &my_major, &my_minor, &my_micro);
     167        1571 :   if (!my_plvl)
     168           0 :     return 0;
     169             : 
     170        1571 :   rq_plvl = parse_version_string (rq_version, &rq_major, &rq_minor, &rq_micro);
     171        1570 :   if (!rq_plvl)
     172           0 :     return 0;
     173             : 
     174        1570 :   if (my_major > rq_major
     175        1354 :       || (my_major == rq_major && my_minor > rq_minor)
     176          34 :       || (my_major == rq_major && my_minor == rq_minor
     177          34 :           && my_micro > rq_micro)
     178          17 :       || (my_major == rq_major && my_minor == rq_minor
     179          17 :           && my_micro == rq_micro && strcmp (my_plvl, rq_plvl) >= 0))
     180        1569 :     return 1;
     181             : 
     182           1 :   return 0;
     183             : }
     184             : 
     185             : 
     186             : /* Check that the the version of the library is at minimum the
     187             :    requested one and return the version string; return NULL if the
     188             :    condition is not met.  If a NULL is passed to this function, no
     189             :    check is done and the version string is simply returned.
     190             : 
     191             :    This function must be run once at startup, as it also initializes
     192             :    some subsystems.  Its invocation must be synchronized against
     193             :    calling any of the other functions in a multi-threaded
     194             :    environments.  */
     195             : const char *
     196         150 : gpgme_check_version (const char *req_version)
     197             : {
     198             :   const char *result;
     199         150 :   do_subsystem_inits ();
     200             : 
     201             :   /* Catch-22: We need to get at least the debug subsystem ready
     202             :      before using the trace facility.  If we won't the trace would
     203             :      automagically initialize the debug system with out the locks
     204             :      being initialized and missing the assuan log level setting. */
     205         150 :   TRACE2 (DEBUG_INIT, "gpgme_check_version", 0,
     206             :           "req_version=%s, VERSION=%s",
     207             :           req_version? req_version:"(null)", VERSION);
     208             : 
     209         150 :   result = _gpgme_compare_versions (VERSION, req_version) ? VERSION : NULL;
     210         150 :   if (result != NULL)
     211         149 :     _gpgme_selftest = 0;
     212             : 
     213         150 :   return result;
     214             : }
     215             : 
     216             : /* Check the version and also at runtime if the struct layout of the
     217             :    library matches the one of the user.  This is particular useful for
     218             :    Windows targets (-mms-bitfields).  */
     219             : const char *
     220         150 : gpgme_check_version_internal (const char *req_version,
     221             :                               size_t offset_sig_validity)
     222             : {
     223             :   const char *result;
     224             : 
     225         150 :   if (req_version && req_version[0] == 1 && req_version[1] == 1)
     226           0 :     return cright_blurb ();
     227         150 :   result = gpgme_check_version (req_version);
     228         150 :   if (result == NULL)
     229           1 :     return result;
     230             : 
     231             :   /* Catch-22, see above.  */
     232         149 :   TRACE2 (DEBUG_INIT, "gpgme_check_version_internal", 0,
     233             :           "req_version=%s, offset_sig_validity=%i",
     234             :           req_version ? req_version : "(null)", offset_sig_validity);
     235             : 
     236         149 :   if (offset_sig_validity != offsetof (struct _gpgme_signature, validity))
     237             :     {
     238           0 :       TRACE1 (DEBUG_INIT, "gpgme_check_version_internal", 0,
     239             :               "offset_sig_validity mismatch: expected %i",
     240             :               offsetof (struct _gpgme_signature, validity));
     241           0 :       _gpgme_selftest = GPG_ERR_SELFTEST_FAILED;
     242             :     }
     243             : 
     244         149 :   return result;
     245             : }
     246             : 
     247             : 
     248             : #define LINELENGTH 80
     249             : 
     250             : /* Extract the version string of a program from STRING.  The version
     251             :    number is expected to be in GNU style format:
     252             : 
     253             :      foo 1.2.3
     254             :      foo (bar system) 1.2.3
     255             :      foo 1.2.3 cruft
     256             :      foo (bar system) 1.2.3 cruft.
     257             : 
     258             :   Spaces and tabs are skipped and used as delimiters, a term in
     259             :   (nested) parenthesis before the version string is skipped, the
     260             :   version string may consist of any non-space and non-tab characters
     261             :   but needs to bstart with a digit.
     262             : */
     263             : static const char *
     264         765 : extract_version_string (const char *string, size_t *r_len)
     265             : {
     266             :   const char *s;
     267             :   int count, len;
     268             : 
     269        3772 :   for (s=string; *s; s++)
     270        3772 :     if (*s == ' ' || *s == '\t')
     271             :         break;
     272        2295 :   while (*s == ' ' || *s == '\t')
     273         765 :     s++;
     274         765 :   if (*s == '(')
     275             :     {
     276        5355 :       for (count=1, s++; count && *s; s++)
     277        4590 :         if (*s == '(')
     278           0 :           count++;
     279        4590 :         else if (*s == ')')
     280         765 :           count--;
     281             :     }
     282             :   /* For robustness we look for a digit.  */
     283        2295 :   while ( *s && !(*s >= '0' && *s <= '9') )
     284         765 :     s++;
     285        1530 :   if (*s >= '0' && *s <= '9')
     286             :     {
     287        9945 :       for (len=0; s[len]; len++)
     288        9180 :         if (s[len] == ' ' || s[len] == '\t')
     289             :           break;
     290             :     }
     291             :   else
     292           0 :     len = 0;
     293             : 
     294         765 :   *r_len = len;
     295         765 :   return s;
     296             : }
     297             : 
     298             : 
     299             : /* Retrieve the version number from the --version output of the
     300             :    program FILE_NAME.  */
     301             : char *
     302         881 : _gpgme_get_program_version (const char *const file_name)
     303             : {
     304         881 :   char line[LINELENGTH] = "";
     305         881 :   int linelen = 0;
     306         881 :   char *mark = NULL;
     307             :   int rp[2];
     308             :   int nread;
     309         881 :   char *argv[] = {NULL /* file_name */, (char*)"--version", 0};
     310         881 :   struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
     311             :                                    {-1, -1} };
     312             :   int status;
     313             : 
     314         881 :   if (!file_name)
     315         117 :     return NULL;
     316         764 :   argv[0] = (char *) file_name;
     317             : 
     318         764 :   if (_gpgme_io_pipe (rp, 1) < 0)
     319           0 :     return NULL;
     320             : 
     321         764 :   cfd[0].fd = rp[1];
     322             : 
     323         764 :   status = _gpgme_io_spawn (file_name, argv,
     324             :                             IOSPAWN_FLAG_DETACHED, cfd, NULL, NULL, NULL);
     325         766 :   if (status < 0)
     326             :     {
     327           0 :       _gpgme_io_close (rp[0]);
     328           0 :       _gpgme_io_close (rp[1]);
     329           0 :       return NULL;
     330             :     }
     331             : 
     332             :   do
     333             :     {
     334         766 :       nread = _gpgme_io_read (rp[0], &line[linelen], LINELENGTH - linelen - 1);
     335         765 :       if (nread > 0)
     336             :         {
     337         765 :           line[linelen + nread] = '\0';
     338         765 :           mark = strchr (&line[linelen], '\n');
     339         765 :           if (mark)
     340             :             {
     341         765 :               if (mark > &line[0] && mark[-1] == '\r')
     342           0 :                 mark--;
     343         765 :               *mark = '\0';
     344         765 :               break;
     345             :             }
     346           0 :           linelen += nread;
     347             :         }
     348             :     }
     349           0 :   while (nread > 0 && linelen < LINELENGTH - 1);
     350             : 
     351         765 :   _gpgme_io_close (rp[0]);
     352             : 
     353         765 :   if (mark)
     354             :     {
     355             :       size_t len;
     356             :       const char *s;
     357             : 
     358         765 :       s = extract_version_string (line, &len);
     359         765 :       if (!len)
     360           0 :         return NULL;
     361         765 :       mark = malloc (len + 1);
     362         765 :       if (!mark)
     363           0 :         return NULL;
     364         765 :       memcpy (mark, s, len);
     365         765 :       mark[len] = 0;
     366         765 :       return mark;
     367             :     }
     368             : 
     369           0 :   return NULL;
     370             : }

Generated by: LCOV version 1.13