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