0b09f18a4d12e52586fcb5a34f857992d3df646b
[gpgme.git] / gpgme / engine-gpgsm.c
1 /* engine-gpgsm.c -  GpgSM engine
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 #if HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 /* FIXME: Correct check?  */
27 #ifdef GPGSM_PATH
28 #define ENABLE_GPGSM 1
29 #endif
30
31 #ifdef ENABLE_GPGSM
32
33 #include <sys/types.h>
34 #include <assert.h>
35
36 /* FIXME */
37 #include "../assuan/assuan-defs.h"
38 #undef xtrymalloc
39 #undef xtrycalloc
40 #undef xtryrealloc
41 #undef xfree
42
43 #include "gpgme.h"
44 #include "util.h"
45 #include "types.h"
46 #include "ops.h"
47 #include "wait.h"
48 #include "io.h"
49
50 #include "engine-gpgsm.h"
51
52 #include "assuan.h"
53
54 struct gpgsm_object_s
55 {
56   ASSUAN_CONTEXT assuan_ctx;
57
58   /* Input, output etc are from the servers perspective.  */
59   int input_fd;
60   int input_fd_server;
61   GpgmeData input_data;
62   int output_fd;
63   int output_fd_server;
64   GpgmeData output_data;
65   int message_fd;
66   int message_fd_server;
67   GpgmeData message_data;
68
69   char *command;
70
71   struct
72   {
73     GpgStatusHandler fnc;
74     void *fnc_value;
75   } status;
76   
77 };
78
79 const char *
80 _gpgme_gpgsm_get_version (void)
81 {
82   static const char *gpgsm_version;
83
84   /* FIXME: Locking.  */
85   if (!gpgsm_version)
86     gpgsm_version = _gpgme_get_program_version (_gpgme_get_gpgsm_path ());
87
88   return gpgsm_version;
89 }
90
91 GpgmeError
92 _gpgme_gpgsm_check_version (void)
93 {
94   return _gpgme_compare_versions (_gpgme_gpgsm_get_version (),
95                                   NEED_GPGSM_VERSION)
96     ? 0 : mk_error (Invalid_Engine);
97 }
98
99 GpgmeError
100 _gpgme_gpgsm_new (GpgsmObject *r_gpgsm)
101 {
102   GpgmeError err = 0;
103   GpgsmObject gpgsm;
104   char *argv[] = { "gpgsm", "--server", NULL };
105   int ip[2] = { -1, -1 };
106   int op[2] = { -1, -1 };
107   int mp[2] = { -1, -1 };
108
109   *r_gpgsm = NULL;
110   gpgsm = xtrycalloc (1, sizeof *gpgsm);
111   if (!gpgsm)
112     {
113       err = mk_error (Out_Of_Core);
114       goto leave;
115     }
116
117   if (_gpgme_io_pipe (ip, 0) < 0)
118     {
119       err = mk_error (General_Error);
120       goto leave;
121     }
122   gpgsm->input_fd = ip[1];
123   gpgsm->input_fd_server = ip[0];
124   if (_gpgme_io_pipe (op, 1) < 0)
125     {
126       err = mk_error (General_Error);
127       goto leave;
128     }
129   gpgsm->output_fd = op[0];
130   gpgsm->output_fd_server = op[1];
131   if (_gpgme_io_pipe (mp, 0) < 0)
132     {
133       err = mk_error (General_Error);
134       goto leave;
135     }
136   gpgsm->message_fd = mp[1];
137   gpgsm->message_fd_server = mp[0];
138
139   err = assuan_pipe_connect (&gpgsm->assuan_ctx,
140                              _gpgme_get_gpgsm_path (), argv);
141
142  leave:
143   if (ip[0] != -1)
144     _gpgme_io_close (ip[0]);
145   if (op[1] != -1)
146     _gpgme_io_close (op[1]);
147   if (mp[0] != -1)
148     _gpgme_io_close (mp[0]);
149
150   if (err)
151     _gpgme_gpgsm_release (gpgsm);
152   else
153     *r_gpgsm = gpgsm;
154
155   return err;
156 }
157
158 void
159 _gpgme_gpgsm_release (GpgsmObject gpgsm)
160 {
161   pid_t pid;
162
163   if (!gpgsm)
164     return;
165
166   pid = assuan_get_pid (gpgsm->assuan_ctx);
167   if (pid != -1)
168     _gpgme_remove_proc_from_wait_queue (pid);
169
170   if (gpgsm->input_fd != -1)
171     _gpgme_io_close (gpgsm->input_fd);
172   if (gpgsm->output_fd != -1)
173     _gpgme_io_close (gpgsm->output_fd);
174   if (gpgsm->message_fd != -1)
175     _gpgme_io_close (gpgsm->message_fd);
176
177   assuan_pipe_disconnect (gpgsm->assuan_ctx);
178   xfree (gpgsm);
179 }
180
181 #define COMMANDLINELEN 40
182 static AssuanError
183 gpgsm_set_fd (ASSUAN_CONTEXT ctx, const char *which, int fd)
184 {
185   AssuanError err;
186   char line[COMMANDLINELEN];
187
188   snprintf (line, COMMANDLINELEN, "%s FD=%i", which, fd);
189   err = _assuan_write_line (ctx, line);
190   if (err)
191     return err;
192
193   do
194     {
195       err = _assuan_read_line (ctx);
196       if (err)
197         return err;
198     }
199   while (*ctx->inbound.line == '#' || !ctx->inbound.linelen);
200   
201   if (ctx->inbound.linelen >= 2
202       && ctx->inbound.line[0] == 'O' && ctx->inbound.line[1] == 'K'
203       && (ctx->inbound.line[2] == '\0' || ctx->inbound.line[2] == ' '))
204     return 0;
205   else
206     return ASSUAN_General_Error;
207 }
208
209 GpgmeError
210 _gpgme_gpgsm_op_verify (GpgsmObject gpgsm, GpgmeData sig, GpgmeData text)
211 {
212   AssuanError err;
213
214   if (!gpgsm)
215     return mk_error (Invalid_Value);
216
217   gpgsm->input_data = sig;
218   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server);
219   if (err)
220     return mk_error (General_Error);    /* FIXME */
221   gpgsm->message_data = sig;
222   err = gpgsm_set_fd (gpgsm->assuan_ctx, "MESSAGE", gpgsm->message_fd_server);
223   if (err)
224     return mk_error (General_Error);    /* FIXME */
225   _gpgme_io_close (gpgsm->output_fd);
226   gpgsm->output_fd = -1;
227
228   gpgsm->command = "VERIFY";
229   return 0;
230 }
231
232 static int
233 gpgsm_status_handler (void *opaque, int pid, int fd)
234 {
235   int err;
236   GpgsmObject gpgsm = opaque;
237   ASSUAN_CONTEXT actx = gpgsm->assuan_ctx;
238
239  assert (fd == gpgsm->assuan_ctx->inbound.fd);
240
241   err = _assuan_read_line (gpgsm->assuan_ctx);
242
243   if (actx->inbound.line[0] == '#' || !actx->inbound.linelen)
244     return 0;  /* FIXME */
245
246   if (actx->inbound.linelen >= 2
247       && actx->inbound.line[0] == 'O' && actx->inbound.line[1] == 'K'
248       && (actx->inbound.line[2] == '\0' || actx->inbound.line[2] == ' '))
249     {
250       if (gpgsm->status.fnc)
251         gpgsm->status.fnc (gpgsm->status.fnc_value, STATUS_EOF, "");
252       return 1;
253     }
254   /* FIXME: Parse the status and call the handler.  */
255
256   fprintf (stderr, "[UNCAUGHT STATUS]%s", actx->inbound.line);
257   return 0;
258 }
259
260 void
261 _gpgme_gpgsm_set_status_handler (GpgsmObject gpgsm,
262                                  GpgStatusHandler fnc, void *fnc_value) 
263 {
264   assert (gpgsm);
265
266   gpgsm->status.fnc = fnc;
267   gpgsm->status.fnc_value = fnc_value;
268 }
269
270 GpgmeError
271 _gpgme_gpgsm_start (GpgsmObject gpgsm, void *opaque)
272 {
273   GpgmeError err = 0;
274   pid_t pid;
275
276   if (!gpgsm)
277     return mk_error (Invalid_Value);
278
279   pid = assuan_get_pid (gpgsm->assuan_ctx);
280
281   err = _gpgme_register_pipe_handler (opaque, gpgsm_status_handler, gpgsm, pid,
282                                       gpgsm->assuan_ctx->inbound.fd, 1);
283
284   if (gpgsm->input_fd != -1)
285     {
286       err = _gpgme_register_pipe_handler (opaque, _gpgme_data_outbound_handler,
287                                           gpgsm->input_data, pid,
288                                           gpgsm->input_fd, 0);
289       if (!err) /* FIXME Kludge around poll() problem.  */
290         err = _gpgme_io_set_nonblocking (gpgsm->input_fd);
291     }
292   if (!err && gpgsm->output_fd != -1)
293     err = _gpgme_register_pipe_handler (opaque, _gpgme_data_inbound_handler,
294                                         gpgsm->output_data, pid,
295                                         gpgsm->output_fd, 1);
296   if (!err && gpgsm->message_fd != -1)
297     {
298       err = _gpgme_register_pipe_handler (opaque, _gpgme_data_outbound_handler,
299                                           gpgsm->message_data, pid,
300                                           gpgsm->message_fd, 0);
301       if (!err) /* FIXME Kludge around poll() problem.  */
302         err = _gpgme_io_set_nonblocking (gpgsm->message_fd);
303     }
304
305   if (!err)
306     err = _assuan_write_line (gpgsm->assuan_ctx, gpgsm->command);
307
308   return err;
309 }
310
311 #else   /* ENABLE_GPGSM */
312
313 #include <stddef.h>
314 #include "util.h"
315
316 #include "engine-gpgsm.h"
317
318 const char *
319 _gpgme_gpgsm_get_version (void)
320 {
321   return NULL;
322 }
323
324 GpgmeError
325 _gpgme_gpgsm_check_version (void)
326 {
327   return mk_error (Invalid_Engine);
328 }
329
330 GpgmeError
331 _gpgme_gpgsm_new (GpgsmObject *r_gpgsm)
332 {
333   return mk_error (Invalid_Engine);
334 }
335
336 void
337 _gpgme_gpgsm_release (GpgsmObject gpgsm)
338 {
339   return;
340 }
341
342 void
343 _gpgme_gpgsm_set_status_handler (GpgsmObject gpgsm,
344                                  GpgStatusHandler fnc, void *fnc_value) 
345 {
346   return;
347 }
348
349 GpgmeError
350  _gpgme_gpgsm_op_verify (GpgsmObject gpgsm, GpgmeData sig, GpgmeData text)
351 {
352   return mk_error (Invalid_Engine);
353 }
354
355 GpgmeError
356 _gpgme_gpgsm_start (GpgsmObject gpgsm, void *opaque)
357 {
358   return mk_error (Invalid_Engine);
359 }
360
361 #endif  /* ! ENABLE_GPGSM */