estreams symbols for python bindings
[gpgme.git] / src / version.c
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 \f
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 do_subsystem_inits (void)
62 {
63   static int done = 0;
64
65   if (done)
66     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   _gpgme_debug_subsystem_init ();
78   _gpgme_io_subsystem_init ();
79   _gpgme_status_init ();
80
81   done = 1;
82 }
83
84
85 /* Put vesion information into the binary.  */
86 static const char *
87 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   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 parse_version_number (const char *str, int *number)
105 {
106 #define MAXVAL ((INT_MAX - 10) / 10)
107   int val = 0;
108
109   /* Leading zeros are not allowed.  */
110   if (*str == '0' && isdigit(str[1]))
111     return NULL;
112
113   while (isdigit (*str) && val <= MAXVAL)
114     {
115       val *= 10;
116       val += *(str++) - '0';
117     }
118   *number = val;
119   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 parse_version_string (const char *str, int *major, int *minor, int *micro)
131 {
132   str = parse_version_number (str, major);
133   if (!str || *str != '.')
134     return NULL;
135   str++;
136
137   str = parse_version_number (str, minor);
138   if (!str || *str != '.')
139     return NULL;
140   str++;
141
142   str = parse_version_number (str, micro);
143   if (!str)
144     return NULL;
145
146   /* A patchlevel might follow.  */
147   return str;
148 }
149
150
151 /* Return true if MY_VERSION is at least REQ_VERSION, and false
152    otherwise.  */
153 int
154 _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   if (!rq_version)
162     return 1;
163   if (!my_version)
164     return 0;
165
166   my_plvl = parse_version_string (my_version, &my_major, &my_minor, &my_micro);
167   if (!my_plvl)
168     return 0;
169
170   rq_plvl = parse_version_string (rq_version, &rq_major, &rq_minor, &rq_micro);
171   if (!rq_plvl)
172     return 0;
173
174   if (my_major > rq_major
175       || (my_major == rq_major && my_minor > rq_minor)
176       || (my_major == rq_major && my_minor == rq_minor
177           && my_micro > rq_micro)
178       || (my_major == rq_major && my_minor == rq_minor
179           && my_micro == rq_micro && strcmp (my_plvl, rq_plvl) >= 0))
180     return 1;
181
182   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 gpgme_check_version (const char *req_version)
197 {
198   const char *result;
199   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   TRACE2 (DEBUG_INIT, "gpgme_check_version", 0,
206           "req_version=%s, VERSION=%s",
207           req_version? req_version:"(null)", VERSION);
208
209   result = _gpgme_compare_versions (VERSION, req_version) ? VERSION : NULL;
210   if (result != NULL)
211     _gpgme_selftest = 0;
212
213   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 gpgme_check_version_internal (const char *req_version,
221                               size_t offset_sig_validity)
222 {
223   const char *result;
224
225   if (req_version && req_version[0] == 1 && req_version[1] == 1)
226     return cright_blurb ();
227   result = gpgme_check_version (req_version);
228   if (result == NULL)
229     return result;
230
231   /* Catch-22, see above.  */
232   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   if (offset_sig_validity != offsetof (struct _gpgme_signature, validity))
237     {
238       TRACE1 (DEBUG_INIT, "gpgme_check_version_internal", 0,
239               "offset_sig_validity mismatch: expected %i",
240               offsetof (struct _gpgme_signature, validity));
241       _gpgme_selftest = GPG_ERR_SELFTEST_FAILED;
242     }
243
244   return result;
245 }
246
247 \f
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 extract_version_string (const char *string, size_t *r_len)
265 {
266   const char *s;
267   int count, len;
268
269   for (s=string; *s; s++)
270     if (*s == ' ' || *s == '\t')
271         break;
272   while (*s == ' ' || *s == '\t')
273     s++;
274   if (*s == '(')
275     {
276       for (count=1, s++; count && *s; s++)
277         if (*s == '(')
278           count++;
279         else if (*s == ')')
280           count--;
281     }
282   /* For robustness we look for a digit.  */
283   while ( *s && !(*s >= '0' && *s <= '9') )
284     s++;
285   if (*s >= '0' && *s <= '9')
286     {
287       for (len=0; s[len]; len++)
288         if (s[len] == ' ' || s[len] == '\t')
289           break;
290     }
291   else
292     len = 0;
293
294   *r_len = len;
295   return s;
296 }
297
298
299 /* Retrieve the version number from the --version output of the
300    program FILE_NAME.  */
301 char *
302 _gpgme_get_program_version (const char *const file_name)
303 {
304   char line[LINELENGTH] = "";
305   int linelen = 0;
306   char *mark = NULL;
307   int rp[2];
308   int nread;
309   char *argv[] = {NULL /* file_name */, (char*)"--version", 0};
310   struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
311                                    {-1, -1} };
312   int status;
313
314   if (!file_name)
315     return NULL;
316   argv[0] = (char *) file_name;
317
318   if (_gpgme_io_pipe (rp, 1) < 0)
319     return NULL;
320
321   cfd[0].fd = rp[1];
322
323   status = _gpgme_io_spawn (file_name, argv,
324                             IOSPAWN_FLAG_DETACHED, cfd, NULL, NULL, NULL);
325   if (status < 0)
326     {
327       _gpgme_io_close (rp[0]);
328       _gpgme_io_close (rp[1]);
329       return NULL;
330     }
331
332   do
333     {
334       nread = _gpgme_io_read (rp[0], &line[linelen], LINELENGTH - linelen - 1);
335       if (nread > 0)
336         {
337           line[linelen + nread] = '\0';
338           mark = strchr (&line[linelen], '\n');
339           if (mark)
340             {
341               if (mark > &line[0] && *mark == '\r')
342                 mark--;
343               *mark = '\0';
344               break;
345             }
346           linelen += nread;
347         }
348     }
349   while (nread > 0 && linelen < LINELENGTH - 1);
350
351   _gpgme_io_close (rp[0]);
352
353   if (mark)
354     {
355       size_t len;
356       const char *s;
357
358       s = extract_version_string (line, &len);
359       if (!len)
360         return NULL;
361       mark = malloc (len + 1);
362       if (!mark)
363         return NULL;
364       memcpy (mark, s, len);
365       mark[len] = 0;
366       return mark;
367     }
368
369   return NULL;
370 }