Release 0.2.1
[gpgme.git] / gpgme / version.c
1 /* version.c -  version check
2  *      Copyright (C) 2000 Werner Koch (dd9jn)
3  *      Copyright (C) 2001 g10 Code GmbH
4  *
5  * This file is part of GPGME.
6  *
7  * GPGME is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * GPGME is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
20  */
21
22 #include <config.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27
28 #include "gpgme.h"
29 #include "context.h"
30 #include "rungpg.h"
31 #include "sema.h"
32 #include "util.h"
33
34
35 static int lineno;
36 static char *tmp_engine_version;
37
38 static const char *get_engine_info (void);
39
40
41 static void
42 do_subsystem_inits (void)
43 {
44     static int done = 0;
45
46     if (done)
47         return;
48     _gpgme_sema_subsystem_init ();
49 }
50
51
52
53 static const char*
54 parse_version_number ( const char *s, int *number )
55 {
56     int val = 0;
57
58     if ( *s == '0' && isdigit(s[1]) )
59         return NULL; /* leading zeros are not allowed */
60     for ( ; isdigit(*s); s++ ) {
61         val *= 10;
62         val += *s - '0';
63     }
64     *number = val;
65     return val < 0? NULL : s;
66 }
67
68
69 static const char *
70 parse_version_string( const char *s, int *major, int *minor, int *micro )
71 {
72     s = parse_version_number ( s, major );
73     if ( !s || *s != '.' )
74         return NULL;
75     s++;
76     s = parse_version_number ( s, minor );
77     if ( !s || *s != '.' )
78         return NULL;
79     s++;
80     s = parse_version_number ( s, micro );
81     if ( !s )
82         return NULL;
83     return s; /* patchlevel */
84 }
85
86 static const char *
87 compare_versions ( const char *my_version, const char *req_version )
88 {
89     int my_major, my_minor, my_micro;
90     int rq_major, rq_minor, rq_micro;
91     const char *my_plvl, *rq_plvl;
92
93     if ( !req_version )
94         return my_version;
95
96     my_plvl = parse_version_string ( my_version,
97                                      &my_major, &my_minor, &my_micro );
98     if ( !my_plvl )
99         return NULL;  /* very strange: our own version is bogus */
100     rq_plvl = parse_version_string( req_version,
101                                     &rq_major, &rq_minor, &rq_micro );
102     if ( !rq_plvl )
103         return NULL;  /* req version string is invalid */
104
105     if ( my_major > rq_major
106          || (my_major == rq_major && my_minor > rq_minor)
107          || (my_major == rq_major && my_minor == rq_minor
108              && my_micro > rq_micro)
109          || (my_major == rq_major && my_minor == rq_minor
110              && my_micro == rq_micro
111              && strcmp( my_plvl, rq_plvl ) >= 0) ) {
112         return my_version;
113     }
114     return NULL;
115 }
116
117
118 /**
119  * gpgme_check_version:
120  * @req_version: A string with a version
121  * 
122  * Check that the the version of the library is at minimum the requested one
123  * and return the version string; return NULL if the condition is not
124  * met.  If a NULL is passed to this function, no check is done and
125  * the version string is simply returned.  It is a pretty good idea to
126  * run this function as soon as poossible, becuase it also intializes 
127  * some subsystems.  In a multithreaded environment if should be called
128  * before the first thread is created.
129  * 
130  * Return value: The version string or NULL
131  **/
132 const char *
133 gpgme_check_version ( const char *req_version )
134 {
135     do_subsystem_inits ();
136     return compare_versions ( VERSION, req_version );
137 }
138
139
140 /**
141  * gpgme_get_engine_info:
142  *  
143  * Return information about the underlying crypto engine.  This is an
144  * XML string with various information.  To get the version of the
145  * crypto engine it should be sufficient to grep for the first
146  * <literal>version</literal> tag and use it's content.  A string is
147  * always returned even if the crypto engine is not installed; in this
148  * case a XML string with some error information is returned.
149  * 
150  * Return value: A XML string with information about the crypto engine.
151  **/
152 const char *
153 gpgme_get_engine_info ()
154 {
155     do_subsystem_inits ();
156     return get_engine_info ();
157 }
158
159 /**
160  * gpgme_check_engine:
161  * 
162  * Check whether the installed crypto engine matches the requirement of
163  * GPGME.
164  *
165  * Return value: 0 or an error code.
166  **/
167 GpgmeError
168 gpgme_check_engine ()
169 {
170     const char *info = gpgme_get_engine_info ();
171     const char *s, *s2;
172
173     s = strstr (info, "<version>");
174     if (s) {
175         s += 9;
176         s2 = strchr (s, '>');
177         if (s2) {
178             char *ver = xtrymalloc (s2 - s + 1);
179             if (!ver)
180                 return mk_error (Out_Of_Core);
181             memcpy (ver, s, s2-s);
182             ver[s2-s] = 0;
183             s = compare_versions ( ver, NEED_GPG_VERSION );
184             xfree (ver);
185             if (s)
186                 return 0;
187         }
188     }
189     return mk_error (Invalid_Engine);
190 }
191
192
193 \f
194 static void
195 version_line_handler ( GpgmeCtx c, char *line )
196 {
197     char *p;
198     size_t len;
199
200     lineno++;
201     if ( c->out_of_core )
202         return;
203     if (!line)
204         return; /* EOF */
205     if (lineno==1) {
206         if ( memcmp (line, "gpg ", 4) )
207             return;
208         if ( !(p = strpbrk (line, "0123456789")) )
209             return;
210         len = strcspn (p, " \t\r\n()<>" );
211         p[len] = 0;
212         tmp_engine_version = xtrystrdup (p);
213     }
214 }
215
216
217 static const char *
218 get_engine_info (void)
219 {
220     static const char *engine_info =NULL;
221     GpgmeCtx c = NULL;
222     GpgmeError err = 0;
223     const char *path = NULL;
224
225     /* FIXME: make sure that only one instance does run */
226     if (engine_info)
227         return engine_info;
228
229     path = _gpgme_get_gpg_path ();
230     err = gpgme_new (&c);
231     if (err) 
232         goto leave;
233     err = _gpgme_gpg_new ( &c->gpg );
234     if (err)
235         goto leave;
236
237     err = _gpgme_gpg_set_simple_line_handler ( c->gpg,
238                                                version_line_handler, c );
239     if (err)
240         goto leave;
241
242     _gpgme_gpg_add_arg ( c->gpg, "--version" );
243     lineno = 0;
244     xfree (tmp_engine_version); tmp_engine_version = NULL;
245     err = _gpgme_gpg_spawn ( c->gpg, c );
246     if (err)
247         goto leave;
248     gpgme_wait (c, 1);
249     if (tmp_engine_version) {
250         const char *fmt;
251         char *p;
252
253         fmt = "<GnupgInfo>\n"
254               " <engine>\n"
255               "  <version>%s</version>\n"
256               "  <path>%s</path>\n"
257               " </engine>\n"
258               "</GnupgInfo>\n";
259         /*(yes, I know that we allocating 2 extra bytes)*/
260         p = xtrymalloc ( strlen(fmt) + strlen(path)
261                          + strlen (tmp_engine_version) + 1);
262         if (!p) {
263             err = mk_error (Out_Of_Core);
264             goto leave;
265         }
266         sprintf (p, fmt, tmp_engine_version, path);
267         engine_info = p;
268         xfree (tmp_engine_version); tmp_engine_version = NULL;
269     }
270     else {
271         err = mk_error (General_Error);
272     }
273
274  leave:
275     if (err) {
276         const char *fmt;
277         const char *errstr = gpgme_strerror (err);
278         char *p;
279
280         fmt = "<GnupgInfo>\n"
281             " <engine>\n"
282             "  <error>%s</error>\n"                
283             "  <path>%s</path>\n"
284             " </engine>\n"
285             "</GnupgInfo>\n";
286
287         p = xtrymalloc ( strlen(fmt) + strlen(errstr) + strlen(path) + 1);
288         if (p) { 
289             sprintf (p, fmt, errstr, path);
290             engine_info = p;
291         }
292         else {
293             engine_info = "<GnupgInfo>\n"
294                           "  <error>Out of core</error>\n"
295                           "</GnupgInfo>\n";
296         }
297     }
298     gpgme_release ( c );
299     return engine_info;
300 }
301
302
303
304
305
306