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