doc/
[gpgme.git] / gpgme / version.c
1 /* version.c -  version check
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003 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 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, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    General Public License for more details.
16  
17    You should have received a copy of the GNU General Public License
18    along with GPGME; if not, write to the Free Software Foundation,
19    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
20
21 #if HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <ctype.h>
28
29 #include "gpgme.h"
30 #include "context.h"
31 #include "sema.h"
32 #include "util.h"
33 #include "key.h" /* for key_cache_init */
34 #include "io.h"
35
36
37 static void
38 do_subsystem_inits (void)
39 {
40   static int done = 0;
41
42   if (done)
43     return;
44   _gpgme_sema_subsystem_init ();
45   _gpgme_key_cache_init ();
46   done = 1;
47 }
48
49 static const char*
50 parse_version_number (const char *s, int *number)
51 {
52   int val = 0;
53
54   if (*s == '0' && isdigit(s[1]))
55     return NULL;  /* Leading zeros are not allowed.  */
56   for (; isdigit(*s); s++)
57     {
58       val *= 10;
59       val += *s - '0';
60     }
61   *number = val;
62   return val < 0 ? NULL : s;
63 }
64
65 static const char *
66 parse_version_string (const char *s, int *major, int *minor, int *micro)
67 {
68   s = parse_version_number (s, major);
69   if (!s || *s != '.')
70     return NULL;
71   s++;
72   s = parse_version_number (s, minor);
73   if (!s || *s != '.')
74     return NULL;
75   s++;
76   s = parse_version_number (s, micro);
77   if (!s)
78     return NULL;
79   return s;  /* Patchlevel.  */
80 }
81
82 const char *
83 _gpgme_compare_versions (const char *my_version,
84                          const char *req_version)
85 {
86   int my_major, my_minor, my_micro;
87   int rq_major, rq_minor, rq_micro;
88   const char *my_plvl, *rq_plvl;
89
90   if (!req_version)
91     return my_version;
92   if (!my_version)
93     return NULL;
94
95   my_plvl = parse_version_string (my_version, &my_major, &my_minor, &my_micro);
96   if (!my_plvl)
97     return NULL;        /* Very strange: our own version is bogus.  */
98   rq_plvl = parse_version_string(req_version,
99                                  &rq_major, &rq_minor, &rq_micro);
100   if (!rq_plvl)
101     return NULL;        /* Requested version string is invalid.  */
102
103   if (my_major > rq_major
104         || (my_major == rq_major && my_minor > rq_minor)
105       || (my_major == rq_major && my_minor == rq_minor 
106           && my_micro > rq_micro)
107       || (my_major == rq_major && my_minor == rq_minor
108           && my_micro == rq_micro
109           && strcmp( my_plvl, rq_plvl ) >= 0))
110     {
111       return my_version;
112     }
113   return NULL;
114 }
115
116 /**
117  * gpgme_check_version:
118  * @req_version: A string with a version
119  * 
120  * Check that the the version of the library is at minimum the requested one
121  * and return the version string; return NULL if the condition is not
122  * met.  If a NULL is passed to this function, no check is done and
123  * the version string is simply returned.  It is a pretty good idea to
124  * run this function as soon as possible, because it also intializes 
125  * some subsystems.  In a multithreaded environment if should be called
126  * before the first thread is created.
127  * 
128  * Return value: The version string or NULL
129  **/
130 const char *
131 gpgme_check_version (const char *req_version)
132 {
133   do_subsystem_inits ();
134   return _gpgme_compare_versions (VERSION, req_version);
135 }
136
137
138 /* Get the information about the configured and installed engines.  A
139    pointer to the first engine in the statically allocated linked list
140    is returned in *INFO.  If an error occurs, it is returned.  */
141 GpgmeError
142 gpgme_get_engine_info (GpgmeEngineInfo *info)
143 {
144   static GpgmeEngineInfo engine_info;
145   DEFINE_STATIC_LOCK (engine_info_lock);
146
147   LOCK (engine_info_lock);
148   if (!engine_info)
149     {
150       GpgmeEngineInfo *lastp = &engine_info;
151       GpgmeProtocol proto_list[] = { GPGME_PROTOCOL_OpenPGP,
152                                      GPGME_PROTOCOL_CMS };
153       int proto;
154
155       for (proto = 0; proto < DIM (proto_list); proto++)
156         {
157           const char *path = _gpgme_engine_get_path (proto_list[proto]);
158
159           if (!path)
160             continue;
161
162           *lastp = malloc (sizeof (*engine_info));
163           if (!*lastp)
164             {
165               while (engine_info)
166                 {
167                   GpgmeEngineInfo next_info = engine_info->next;
168                   free (engine_info);
169                   engine_info = next_info;
170                 }
171               UNLOCK (engine_info_lock);
172               return GPGME_Out_Of_Core;
173             }
174
175           (*lastp)->protocol = proto_list[proto];
176           (*lastp)->path = path;
177           (*lastp)->version = _gpgme_engine_get_version (proto_list[proto]);
178           (*lastp)->req_version
179             = _gpgme_engine_get_req_version (proto_list[proto]);
180           lastp = &(*lastp)->next;
181         }
182     }
183   UNLOCK (engine_info_lock);
184   *info = engine_info;
185   return 0;
186 }
187
188 \f
189 #define LINELENGTH 80
190
191 char *
192 _gpgme_get_program_version (const char *const path)
193 {
194   char line[LINELENGTH] = "";
195   int linelen = 0;
196   char *mark = NULL;
197   int rp[2];
198   int nread;
199   char *argv[] = {NULL /* path */, "--version", 0};
200   struct spawn_fd_item_s pfd[] = { {0, -1}, {-1, -1} };
201   struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */}, {-1, -1} };
202   int status;
203
204   if (!path)
205     return NULL;
206   argv[0] = (char *) path;
207
208   if (_gpgme_io_pipe (rp, 1) < 0)
209     return NULL;
210
211   pfd[0].fd = rp[1];
212   cfd[0].fd = rp[1];
213
214   status = _gpgme_io_spawn (path, argv, cfd, pfd);
215   if (status < 0)
216     {
217       _gpgme_io_close (rp[0]);
218       _gpgme_io_close (rp[1]);
219       return NULL;
220     }
221
222   do
223     {
224       nread = _gpgme_io_read (rp[0], &line[linelen], LINELENGTH - linelen - 1);
225       if (nread > 0)
226         {
227           line[linelen + nread] = '\0';
228           mark = strchr (&line[linelen], '\n');
229           if (mark)
230             {
231               *mark = '\0';
232               break;
233             }
234           linelen += nread;
235         }
236     }
237   while (nread > 0 && linelen < LINELENGTH - 1);
238
239   _gpgme_io_close (rp[0]);
240
241   if (mark)
242     {
243       mark = strrchr (line, ' ');
244       if (!mark)
245         return NULL;
246       return strdup (mark + 1);
247     }
248
249   return NULL;
250 }