2001-11-22 Marcus Brinkmann <marcus@g10code.de>
[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_decrypt (GpgsmObject gpgsm, GpgmeData ciph, GpgmeData plain)
211 {
212   AssuanError err;
213
214   if (!gpgsm)
215     return mk_error (Invalid_Value);
216
217   gpgsm->input_data = ciph;
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->output_data = plain;
222   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server);
223   if (err)
224     return mk_error (General_Error);    /* FIXME */
225   _gpgme_io_close (gpgsm->message_fd);
226   gpgsm->message_fd = -1;
227
228   gpgsm->command = "DECRYPT";
229   return 0;
230 }
231
232 GpgmeError
233 _gpgme_gpgsm_op_import (GpgsmObject gpgsm, GpgmeData keydata)
234 {
235   AssuanError err;
236
237   if (!gpgsm)
238     return mk_error (Invalid_Value);
239
240   gpgsm->input_data = keydata;
241   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server);
242   if (err)
243     return mk_error (General_Error);    /* FIXME */
244   _gpgme_io_close (gpgsm->output_fd);
245   gpgsm->output_fd = -1;
246   _gpgme_io_close (gpgsm->message_fd);
247   gpgsm->message_fd = -1;
248
249   gpgsm->command = "DECRYPT";
250   return 0;
251 }
252
253 GpgmeError
254 _gpgme_gpgsm_op_verify (GpgsmObject gpgsm, GpgmeData sig, GpgmeData text)
255 {
256   AssuanError err;
257
258   if (!gpgsm)
259     return mk_error (Invalid_Value);
260
261   gpgsm->input_data = sig;
262   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server);
263   if (err)
264     return mk_error (General_Error);    /* FIXME */
265   gpgsm->message_data = sig;
266   err = gpgsm_set_fd (gpgsm->assuan_ctx, "MESSAGE", gpgsm->message_fd_server);
267   if (err)
268     return mk_error (General_Error);    /* FIXME */
269   _gpgme_io_close (gpgsm->output_fd);
270   gpgsm->output_fd = -1;
271
272   gpgsm->command = "VERIFY";
273   return 0;
274 }
275
276 static int
277 gpgsm_status_handler (void *opaque, int pid, int fd)
278 {
279   int err;
280   GpgsmObject gpgsm = opaque;
281   ASSUAN_CONTEXT actx = gpgsm->assuan_ctx;
282
283  assert (fd == gpgsm->assuan_ctx->inbound.fd);
284
285   err = _assuan_read_line (gpgsm->assuan_ctx);
286
287   if (actx->inbound.line[0] == '#' || !actx->inbound.linelen)
288     return 0;  /* FIXME */
289
290   if (actx->inbound.linelen >= 2
291       && actx->inbound.line[0] == 'O' && actx->inbound.line[1] == 'K'
292       && (actx->inbound.line[2] == '\0' || actx->inbound.line[2] == ' '))
293     {
294       if (gpgsm->status.fnc)
295         gpgsm->status.fnc (gpgsm->status.fnc_value, STATUS_EOF, "");
296       return 1;
297     }
298   /* FIXME: Parse the status and call the handler.  */
299
300   fprintf (stderr, "[UNCAUGHT STATUS]%s", actx->inbound.line);
301   return 0;
302 }
303
304 void
305 _gpgme_gpgsm_set_status_handler (GpgsmObject gpgsm,
306                                  GpgStatusHandler fnc, void *fnc_value) 
307 {
308   assert (gpgsm);
309
310   gpgsm->status.fnc = fnc;
311   gpgsm->status.fnc_value = fnc_value;
312 }
313
314 GpgmeError
315 _gpgme_gpgsm_start (GpgsmObject gpgsm, void *opaque)
316 {
317   GpgmeError err = 0;
318   pid_t pid;
319
320   if (!gpgsm)
321     return mk_error (Invalid_Value);
322
323   pid = assuan_get_pid (gpgsm->assuan_ctx);
324
325   err = _gpgme_register_pipe_handler (opaque, gpgsm_status_handler, gpgsm, pid,
326                                       gpgsm->assuan_ctx->inbound.fd, 1);
327
328   if (gpgsm->input_fd != -1)
329     {
330       err = _gpgme_register_pipe_handler (opaque, _gpgme_data_outbound_handler,
331                                           gpgsm->input_data, pid,
332                                           gpgsm->input_fd, 0);
333       if (!err) /* FIXME Kludge around poll() problem.  */
334         err = _gpgme_io_set_nonblocking (gpgsm->input_fd);
335     }
336   if (!err && gpgsm->output_fd != -1)
337     err = _gpgme_register_pipe_handler (opaque, _gpgme_data_inbound_handler,
338                                         gpgsm->output_data, pid,
339                                         gpgsm->output_fd, 1);
340   if (!err && gpgsm->message_fd != -1)
341     {
342       err = _gpgme_register_pipe_handler (opaque, _gpgme_data_outbound_handler,
343                                           gpgsm->message_data, pid,
344                                           gpgsm->message_fd, 0);
345       if (!err) /* FIXME Kludge around poll() problem.  */
346         err = _gpgme_io_set_nonblocking (gpgsm->message_fd);
347     }
348
349   if (!err)
350     err = _assuan_write_line (gpgsm->assuan_ctx, gpgsm->command);
351
352   return err;
353 }
354
355 #else   /* ENABLE_GPGSM */
356
357 #include <stddef.h>
358 #include "util.h"
359
360 #include "engine-gpgsm.h"
361
362 const char *
363 _gpgme_gpgsm_get_version (void)
364 {
365   return NULL;
366 }
367
368 GpgmeError
369 _gpgme_gpgsm_check_version (void)
370 {
371   return mk_error (Invalid_Engine);
372 }
373
374 GpgmeError
375 _gpgme_gpgsm_new (GpgsmObject *r_gpgsm)
376 {
377   return mk_error (Invalid_Engine);
378 }
379
380 void
381 _gpgme_gpgsm_release (GpgsmObject gpgsm)
382 {
383   return;
384 }
385
386 void
387 _gpgme_gpgsm_set_status_handler (GpgsmObject gpgsm,
388                                  GpgStatusHandler fnc, void *fnc_value) 
389 {
390   return;
391 }
392
393 GpgmeError
394 _gpgme_gpgsm_op_decrypt (GpgsmObject gpgsm, GpgmeData ciph, GpgmeData plain)
395 {
396   return mk_error (Invalid_Engine);
397 }
398
399 GpgmeError
400 _gpgme_gpgsm_op_import (GpgsmObject gpgsm, GpgmeData keydata)
401 {
402   return mk_error (Invalid_Engine);
403 }
404
405 GpgmeError
406 _gpgme_gpgsm_op_verify (GpgsmObject gpgsm, GpgmeData sig, GpgmeData text)
407 {
408   return mk_error (Invalid_Engine);
409 }
410
411 GpgmeError
412 _gpgme_gpgsm_start (GpgsmObject gpgsm, void *opaque)
413 {
414   return mk_error (Invalid_Engine);
415 }
416
417 #endif  /* ! ENABLE_GPGSM */