Some new but untested functions
[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 "util.h"
31
32
33 static int lineno;
34 static char *tmp_engine_version;
35
36 static const char *get_engine_info (void);
37
38
39 static const char*
40 parse_version_number ( const char *s, int *number )
41 {
42     int val = 0;
43
44     if ( *s == '0' && isdigit(s[1]) )
45         return NULL; /* leading zeros are not allowed */
46     for ( ; isdigit(*s); s++ ) {
47         val *= 10;
48         val += *s - '0';
49     }
50     *number = val;
51     return val < 0? NULL : s;
52 }
53
54
55 static const char *
56 parse_version_string( const char *s, int *major, int *minor, int *micro )
57 {
58     s = parse_version_number ( s, major );
59     if ( !s || *s != '.' )
60         return NULL;
61     s++;
62     s = parse_version_number ( s, minor );
63     if ( !s || *s != '.' )
64         return NULL;
65     s++;
66     s = parse_version_number ( s, micro );
67     if ( !s )
68         return NULL;
69     return s; /* patchlevel */
70 }
71
72 static const char *
73 compare_versions ( const char *my_version, const char *req_version )
74 {
75     int my_major, my_minor, my_micro;
76     int rq_major, rq_minor, rq_micro;
77     const char *my_plvl, *rq_plvl;
78
79     if ( !req_version )
80         return my_version;
81
82     my_plvl = parse_version_string ( my_version,
83                                      &my_major, &my_minor, &my_micro );
84     if ( !my_plvl )
85         return NULL;  /* very strange: our own version is bogus */
86     rq_plvl = parse_version_string( req_version,
87                                     &rq_major, &rq_minor, &rq_micro );
88     if ( !rq_plvl )
89         return NULL;  /* req version string is invalid */
90
91     if ( my_major > rq_major
92          || (my_major == rq_major && my_minor > rq_minor)
93          || (my_major == rq_major && my_minor == rq_minor
94              && my_micro > rq_micro)
95          || (my_major == rq_major && my_minor == rq_minor
96              && my_micro == rq_micro
97              && strcmp( my_plvl, rq_plvl ) >= 0) ) {
98         return my_version;
99     }
100     return NULL;
101 }
102
103
104 /**
105  * gpgme_check_version:
106  * @req_version: A string with a version
107  * 
108  * Check that the the version of the library is at minimum the requested one
109  * and return the version string; return NULL if the condition is not
110  * met.  If a NULL is passed to this function, no check is done and
111  * the version string is simply returned.
112  * 
113  * Return value: The version string or NULL
114  **/
115 const char *
116 gpgme_check_version ( const char *req_version )
117 {
118     return compare_versions ( VERSION, req_version );
119 }
120
121
122 /**
123  * gpgme_get_engine_info:
124  *  
125  * Return information about the underlying crypto engine.  This is an
126  * XML string with various information.  To get the version of the
127  * crypto engine it should be sufficient to grep for the first
128  * <literal>version</literal> tag and use it's content.  A string is
129  * always returned even if the crypto engine is not installed; in this
130  * case a XML string with some error information is returned.
131  * 
132  * Return value: A XML string with information about the crypto engine.
133  **/
134 const char *
135 gpgme_get_engine_info ()
136 {
137     return get_engine_info ();
138 }
139
140 /**
141  * gpgme_check_engine:
142  * 
143  * Check whether the installed crypto engine matches the requirement of
144  * GPGME.
145  *
146  * Return value: 0 or an error code.
147  **/
148 GpgmeError
149 gpgme_check_engine ()
150 {
151     const char *info = gpgme_get_engine_info ();
152     const char *s, *s2;
153
154     s = strstr (info, "<version>");
155     if (s) {
156         s += 9;
157         s2 = strchr (s, '>');
158         if (s2) {
159             char *ver = xtrymalloc (s2 - s + 1);
160             if (!ver)
161                 return mk_error (Out_Of_Core);
162             memcpy (ver, s, s2-s);
163             ver[s2-s] = 0;
164             s = compare_versions ( ver, NEED_GPG_VERSION );
165             xfree (ver);
166             if (s)
167                 return 0;
168         }
169     }
170     return mk_error (Invalid_Engine);
171 }
172
173
174 \f
175 static void
176 version_line_handler ( GpgmeCtx c, char *line )
177 {
178     char *p;
179     size_t len;
180
181     lineno++;
182     if ( c->out_of_core )
183         return;
184     if (!line)
185         return; /* EOF */
186     if (lineno==1) {
187         if ( memcmp (line, "gpg ", 4) )
188             return;
189         if ( !(p = strpbrk (line, "0123456789")) )
190             return;
191         len = strcspn (p, " \t\r\n()<>" );
192         p[len] = 0;
193         tmp_engine_version = xtrystrdup (p);
194     }
195 }
196
197
198 static const char *
199 get_engine_info (void)
200 {
201     const char *engine_info =NULL;
202     GpgmeCtx c = NULL;
203     GpgmeError err = 0;
204
205     /* FIXME: make sure that only one instance does run */
206     if (engine_info)
207         goto leave;
208
209     err = gpgme_new (&c);
210     if (err) 
211         goto leave;
212     err = _gpgme_gpg_new ( &c->gpg );
213     if (err)
214         goto leave;
215
216     err = _gpgme_gpg_set_simple_line_handler ( c->gpg,
217                                                version_line_handler, c );
218     if (err)
219         goto leave;
220
221     _gpgme_gpg_add_arg ( c->gpg, "--version" );
222     lineno = 0;
223     xfree (tmp_engine_version); tmp_engine_version = NULL;
224     err = _gpgme_gpg_spawn ( c->gpg, c );
225     if (err)
226         goto leave;
227     gpgme_wait (c, 1);
228     if (tmp_engine_version) {
229         const char *fmt;
230         char *p;
231
232         fmt = "<GnupgInfo>\n"
233               " <engine>\n"
234               "  <version>%s</version>\n"
235               " </engine>\n"
236               "</GnupgInfo>\n";
237         /*(yes, I know that we allocating 2 extra bytes)*/
238         p = xtrymalloc ( strlen(fmt) + strlen (tmp_engine_version) + 1);
239         if (!p) {
240             err = mk_error (Out_Of_Core);
241             goto leave;
242         }
243         sprintf (p, fmt, tmp_engine_version);
244         engine_info = p;
245         xfree (tmp_engine_version); tmp_engine_version = NULL;
246     }
247     else {
248         err = mk_error (General_Error);
249     }
250
251  leave:
252     if (err)
253         engine_info = "<GnupgInfo>\n<error>No engine</error>\n</GnupgInfo>\n";
254     gpgme_release ( c );
255     return engine_info;
256 }
257
258