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 <stdlib.h>
34 #include <string.h>
35 #include <sys/types.h>
36 #include <assert.h>
37
38 /* FIXME */
39 #include "../assuan/assuan-defs.h"
40 #undef xtrymalloc
41 #undef xtrycalloc
42 #undef xtryrealloc
43 #undef xfree
44
45 #include "rungpg.h"
46 #include "status-table.h"
47
48 #include "gpgme.h"
49 #include "util.h"
50 #include "types.h"
51 #include "ops.h"
52 #include "wait.h"
53 #include "io.h"
54 #include "key.h"
55
56 #include "engine-gpgsm.h"
57
58 #include "assuan.h"
59
60 struct gpgsm_object_s
61 {
62   ASSUAN_CONTEXT assuan_ctx;
63
64   /* Input, output etc are from the servers perspective.  */
65   int input_fd;
66   int input_fd_server;
67   GpgmeData input_data;
68   int output_fd;
69   int output_fd_server;
70   GpgmeData output_data;
71   int message_fd;
72   int message_fd_server;
73   GpgmeData message_data;
74
75   char *command;
76
77   struct
78   {
79     GpgStatusHandler fnc;
80     void *fnc_value;
81   } status;
82   
83 };
84
85 const char *
86 _gpgme_gpgsm_get_version (void)
87 {
88   static const char *gpgsm_version;
89
90   /* FIXME: Locking.  */
91   if (!gpgsm_version)
92     gpgsm_version = _gpgme_get_program_version (_gpgme_get_gpgsm_path ());
93
94   return gpgsm_version;
95 }
96
97 GpgmeError
98 _gpgme_gpgsm_check_version (void)
99 {
100   return _gpgme_compare_versions (_gpgme_gpgsm_get_version (),
101                                   NEED_GPGSM_VERSION)
102     ? 0 : mk_error (Invalid_Engine);
103 }
104
105 GpgmeError
106 _gpgme_gpgsm_new (GpgsmObject *r_gpgsm)
107 {
108   GpgmeError err = 0;
109   GpgsmObject gpgsm;
110   char *argv[] = { "gpgsm", "--server", NULL };
111   int ip[2] = { -1, -1 };
112   int op[2] = { -1, -1 };
113   int mp[2] = { -1, -1 };
114
115   *r_gpgsm = NULL;
116   gpgsm = xtrycalloc (1, sizeof *gpgsm);
117   if (!gpgsm)
118     {
119       err = mk_error (Out_Of_Core);
120       goto leave;
121     }
122
123   if (_gpgme_io_pipe (ip, 0) < 0)
124     {
125       err = mk_error (General_Error);
126       goto leave;
127     }
128   gpgsm->input_fd = ip[1];
129   gpgsm->input_fd_server = ip[0];
130   if (_gpgme_io_pipe (op, 1) < 0)
131     {
132       err = mk_error (General_Error);
133       goto leave;
134     }
135   gpgsm->output_fd = op[0];
136   gpgsm->output_fd_server = op[1];
137   if (_gpgme_io_pipe (mp, 0) < 0)
138     {
139       err = mk_error (General_Error);
140       goto leave;
141     }
142   gpgsm->message_fd = mp[1];
143   gpgsm->message_fd_server = mp[0];
144
145   err = assuan_pipe_connect (&gpgsm->assuan_ctx,
146                              _gpgme_get_gpgsm_path (), argv);
147
148  leave:
149   if (ip[0] != -1)
150     _gpgme_io_close (ip[0]);
151   if (op[1] != -1)
152     _gpgme_io_close (op[1]);
153   if (mp[0] != -1)
154     _gpgme_io_close (mp[0]);
155
156   if (err)
157     _gpgme_gpgsm_release (gpgsm);
158   else
159     *r_gpgsm = gpgsm;
160
161   return err;
162 }
163
164 void
165 _gpgme_gpgsm_release (GpgsmObject gpgsm)
166 {
167   pid_t pid;
168
169   if (!gpgsm)
170     return;
171
172   pid = assuan_get_pid (gpgsm->assuan_ctx);
173   if (pid != -1)
174     _gpgme_remove_proc_from_wait_queue (pid);
175
176   if (gpgsm->input_fd != -1)
177     _gpgme_io_close (gpgsm->input_fd);
178   if (gpgsm->output_fd != -1)
179     _gpgme_io_close (gpgsm->output_fd);
180   if (gpgsm->message_fd != -1)
181     _gpgme_io_close (gpgsm->message_fd);
182
183   assuan_pipe_disconnect (gpgsm->assuan_ctx);
184
185   xfree (gpgsm->command);
186   xfree (gpgsm);
187 }
188
189 static AssuanError
190 gpgsm_assuan_simple_command (ASSUAN_CONTEXT ctx, char *line)
191 {
192   AssuanError err;
193
194   err = _assuan_write_line (ctx, line);
195   if (err)
196     return err;
197
198   do
199     {
200       err = _assuan_read_line (ctx);
201       if (err)
202         return err;
203     }
204   while (*ctx->inbound.line == '#' || !ctx->inbound.linelen);
205   
206   if (ctx->inbound.linelen >= 2
207       && ctx->inbound.line[0] == 'O' && ctx->inbound.line[1] == 'K'
208       && (ctx->inbound.line[2] == '\0' || ctx->inbound.line[2] == ' '))
209     return 0;
210   else
211     return ASSUAN_General_Error;
212 }
213
214 #define COMMANDLINELEN 40
215 static AssuanError
216 gpgsm_set_fd (ASSUAN_CONTEXT ctx, const char *which, int fd)
217 {
218   char line[COMMANDLINELEN];
219
220   snprintf (line, COMMANDLINELEN, "%s FD=%i", which, fd);
221   return gpgsm_assuan_simple_command (ctx, line);
222 }
223
224 GpgmeError
225 _gpgme_gpgsm_op_decrypt (GpgsmObject gpgsm, GpgmeData ciph, GpgmeData plain)
226 {
227   AssuanError err;
228
229   if (!gpgsm)
230     return mk_error (Invalid_Value);
231
232   gpgsm->command = xtrystrdup ("DECRYPT");
233   if (!gpgsm->command)
234     return mk_error (Out_Of_Core);
235
236   gpgsm->input_data = ciph;
237   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server);
238   if (err)
239     return mk_error (General_Error);    /* FIXME */
240   gpgsm->output_data = plain;
241   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server);
242   if (err)
243     return mk_error (General_Error);    /* FIXME */
244   _gpgme_io_close (gpgsm->message_fd);
245   gpgsm->message_fd = -1;
246
247   return 0;
248 }
249
250 GpgmeError
251 _gpgme_gpgsm_op_delete (GpgsmObject gpgsm, GpgmeKey key, int allow_secret)
252 {
253   /* FIXME */
254   return mk_error (Not_Implemented);
255 }
256
257 static AssuanError
258 gpgsm_set_recipients (ASSUAN_CONTEXT ctx, GpgmeRecipients recp)
259 {
260   AssuanError err;
261   char *line;
262   int linelen;
263   struct user_id_s *r;
264
265   linelen = 10 + 40 + 1;        /* "RECIPIENT " + guess + '\0'.  */
266   line = xtrymalloc (10 + 40 + 1);
267   if (!line)
268     return ASSUAN_Out_Of_Core;
269   strcpy (line, "RECIPIENT ");
270   for (r = recp->list; r; r = r->next)
271     {
272       int newlen = 11 + strlen (r->name);
273       if (linelen < newlen)
274         {
275           char *newline = xtryrealloc (line, newlen);
276           if (! newline)
277             {
278               xfree (line);
279               return ASSUAN_Out_Of_Core;
280             }
281           line = newline;
282           linelen = newlen;
283         }
284       strcpy (&line[10], r->name);
285       
286       err = gpgsm_assuan_simple_command (ctx, line);
287       if (err)
288         {
289           xfree (line);
290           return err;
291         }
292     }
293   return 0;
294 }
295
296 GpgmeError
297 _gpgme_gpgsm_op_encrypt (GpgsmObject gpgsm, GpgmeRecipients recp,
298                          GpgmeData plain, GpgmeData ciph, int use_armor)
299 {
300   AssuanError err;
301
302   if (!gpgsm)
303     return mk_error (Invalid_Value);
304
305   gpgsm->command = xtrystrdup (use_armor ? "ENCRYPT armor" : "ENCRYPT");
306   if (!gpgsm->command)
307     return mk_error (Out_Of_Core);
308
309   gpgsm->input_data = plain;
310   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server);
311   if (err)
312     return mk_error (General_Error);    /* FIXME */
313   gpgsm->output_data = ciph;
314   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server);
315   if (err)
316     return mk_error (General_Error);    /* FIXME */
317   _gpgme_io_close (gpgsm->message_fd);
318   gpgsm->message_fd = -1;
319
320   err = gpgsm_set_recipients (gpgsm->assuan_ctx, recp);
321   if (err)
322     return mk_error (General_Error);
323
324   return 0;
325 }
326
327 GpgmeError
328 _gpgme_gpgsm_op_export (GpgsmObject gpgsm, GpgmeRecipients recp,
329                         GpgmeData keydata, int use_armor)
330 {
331   /* FIXME */
332   return mk_error (Not_Implemented);
333 }
334
335 GpgmeError
336 _gpgme_gpgsm_op_genkey (GpgsmObject gpgsm, GpgmeData help_data, int use_armor)
337 {
338   /* FIXME */
339   return mk_error (Not_Implemented);
340 }
341
342 GpgmeError
343 _gpgme_gpgsm_op_import (GpgsmObject gpgsm, GpgmeData keydata)
344 {
345   AssuanError err;
346
347   if (!gpgsm)
348     return mk_error (Invalid_Value);
349
350   gpgsm->command = xtrystrdup ("IMPORT");
351   if (!gpgsm->command)
352     return mk_error (Out_Of_Core);
353
354   gpgsm->input_data = keydata;
355   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server);
356   if (err)
357     return mk_error (General_Error);    /* FIXME */
358   _gpgme_io_close (gpgsm->output_fd);
359   gpgsm->output_fd = -1;
360   _gpgme_io_close (gpgsm->message_fd);
361   gpgsm->message_fd = -1;
362
363   return 0;
364 }
365
366 GpgmeError
367 _gpgme_gpgsm_op_keylist (GpgsmObject gpgsm, const char *pattern,
368                          int secret_only, int keylist_mode)
369 {
370   char *line;
371
372   line = xtrymalloc (9 + strlen (pattern) + 1); /* "LISTKEYS " + p + '\0'.  */
373   if (!line)
374     return mk_error (Out_Of_Core);
375   strcpy (line, "LISTKEYS ");
376   strcpy (&line[9], pattern);
377
378   _gpgme_io_close (gpgsm->input_fd);
379   gpgsm->input_fd = -1;
380   _gpgme_io_close (gpgsm->output_fd);
381   gpgsm->output_fd = -1;
382   _gpgme_io_close (gpgsm->message_fd);
383   gpgsm->message_fd = -1;
384
385   gpgsm->command = line;
386   return 0;
387 }
388
389 GpgmeError
390 _gpgme_gpgsm_op_sign (GpgsmObject gpgsm, GpgmeData in, GpgmeData out,
391                       GpgmeSigMode mode, int use_armor,
392                       int use_textmode, GpgmeCtx ctx /* FIXME */)
393 {
394   /* FIXME */
395   return mk_error (Not_Implemented);
396 }
397
398 GpgmeError
399 _gpgme_gpgsm_op_trustlist (GpgsmObject gpgsm, const char *pattern)
400 {
401   /* FIXME */
402   return mk_error (Not_Implemented);
403 }
404
405 GpgmeError
406 _gpgme_gpgsm_op_verify (GpgsmObject gpgsm, GpgmeData sig, GpgmeData text)
407 {
408   AssuanError err;
409
410   if (!gpgsm)
411     return mk_error (Invalid_Value);
412
413   gpgsm->command = xtrystrdup ("VERIFY");
414   if (!gpgsm->command)
415     return mk_error (Out_Of_Core);
416
417   gpgsm->input_data = sig;
418   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server);
419   if (err)
420     return mk_error (General_Error);    /* FIXME */
421   gpgsm->message_data = sig;
422   err = gpgsm_set_fd (gpgsm->assuan_ctx, "MESSAGE", gpgsm->message_fd_server);
423   if (err)
424     return mk_error (General_Error);    /* FIXME */
425   _gpgme_io_close (gpgsm->output_fd);
426   gpgsm->output_fd = -1;
427
428   return 0;
429 }
430
431 static int
432 status_cmp (const void *ap, const void *bp)
433 {
434   const struct status_table_s *a = ap;
435   const struct status_table_s *b = bp;
436
437   return strcmp (a->name, b->name);
438 }
439
440 static int
441 gpgsm_status_handler (void *opaque, int pid, int fd)
442 {
443   int err;
444   GpgsmObject gpgsm = opaque;
445   ASSUAN_CONTEXT actx = gpgsm->assuan_ctx;
446
447   assert (fd == gpgsm->assuan_ctx->inbound.fd);
448
449   err = _assuan_read_line (gpgsm->assuan_ctx);
450
451   if (actx->inbound.line[0] == '#' || !actx->inbound.linelen)
452     return 0;  /* FIXME */
453
454   if ((actx->inbound.linelen >= 2
455        && actx->inbound.line[0] == 'O' && actx->inbound.line[1] == 'K'
456        && (actx->inbound.line[2] == '\0' || actx->inbound.line[2] == ' '))
457       || (actx->inbound.linelen >= 3
458           && actx->inbound.line[0] == 'E' && actx->inbound.line[1] == 'R'
459           && actx->inbound.line[2] == 'R'
460           && (actx->inbound.line[3] == '\0' || actx->inbound.line[3] == ' ')))
461     {
462       /* FIXME Save error somewhere.  */
463       if (gpgsm->status.fnc)
464         gpgsm->status.fnc (gpgsm->status.fnc_value, STATUS_EOF, "");
465       return 1;
466     }
467   /* FIXME: Parse the status and call the handler.  */
468
469   if (actx->inbound.linelen > 2
470       && actx->inbound.line[0] == 'S' && actx->inbound.line[1] == ' ')
471     {
472       struct status_table_s t, *r;
473       char *rest;
474
475       rest = strchr (actx->inbound.line + 2, ' ');
476       if (!rest)
477         rest = actx->inbound.line + actx->inbound.linelen; /* set to an empty string */
478       else
479         *rest++ = 0;
480
481       t.name = actx->inbound.line + 2;
482       r = bsearch (&t, status_table, DIM(status_table) - 1,
483                    sizeof t, status_cmp);
484
485       if (gpgsm->status.fnc)
486         gpgsm->status.fnc (gpgsm->status.fnc_value, r->code, rest);
487     }
488   else
489     fprintf (stderr, "[UNCAUGHT STATUS]%s", actx->inbound.line);
490   return 0;
491 }
492
493 void
494 _gpgme_gpgsm_set_status_handler (GpgsmObject gpgsm,
495                                  GpgStatusHandler fnc, void *fnc_value) 
496 {
497   assert (gpgsm);
498
499   gpgsm->status.fnc = fnc;
500   gpgsm->status.fnc_value = fnc_value;
501 }
502
503 GpgmeError
504 _gpgme_gpgsm_start (GpgsmObject gpgsm, void *opaque)
505 {
506   GpgmeError err = 0;
507   pid_t pid;
508
509   if (!gpgsm)
510     return mk_error (Invalid_Value);
511
512   pid = assuan_get_pid (gpgsm->assuan_ctx);
513
514   err = _gpgme_register_pipe_handler (opaque, gpgsm_status_handler, gpgsm, pid,
515                                       gpgsm->assuan_ctx->inbound.fd, 1);
516
517   if (gpgsm->input_fd != -1)
518     {
519       err = _gpgme_register_pipe_handler (opaque, _gpgme_data_outbound_handler,
520                                           gpgsm->input_data, pid,
521                                           gpgsm->input_fd, 0);
522       if (!err) /* FIXME Kludge around poll() problem.  */
523         err = _gpgme_io_set_nonblocking (gpgsm->input_fd);
524     }
525   if (!err && gpgsm->output_fd != -1)
526     err = _gpgme_register_pipe_handler (opaque, _gpgme_data_inbound_handler,
527                                         gpgsm->output_data, pid,
528                                         gpgsm->output_fd, 1);
529   if (!err && gpgsm->message_fd != -1)
530     {
531       err = _gpgme_register_pipe_handler (opaque, _gpgme_data_outbound_handler,
532                                           gpgsm->message_data, pid,
533                                           gpgsm->message_fd, 0);
534       if (!err) /* FIXME Kludge around poll() problem.  */
535         err = _gpgme_io_set_nonblocking (gpgsm->message_fd);
536     }
537
538   if (!err)
539     err = _assuan_write_line (gpgsm->assuan_ctx, gpgsm->command);
540
541   return err;
542 }
543
544 #else   /* ENABLE_GPGSM */
545
546 #include <stddef.h>
547 #include "util.h"
548
549 #include "engine-gpgsm.h"
550
551 const char *
552 _gpgme_gpgsm_get_version (void)
553 {
554   return NULL;
555 }
556
557 GpgmeError
558 _gpgme_gpgsm_check_version (void)
559 {
560   return mk_error (Invalid_Engine);
561 }
562
563 GpgmeError
564 _gpgme_gpgsm_new (GpgsmObject *r_gpgsm)
565 {
566   return mk_error (Invalid_Engine);
567 }
568
569 void
570 _gpgme_gpgsm_release (GpgsmObject gpgsm)
571 {
572   return;
573 }
574
575 void
576 _gpgme_gpgsm_set_status_handler (GpgsmObject gpgsm,
577                                  GpgStatusHandler fnc, void *fnc_value) 
578 {
579   return;
580 }
581
582 GpgmeError
583 _gpgme_gpgsm_op_decrypt (GpgsmObject gpgsm, GpgmeData ciph, GpgmeData plain)
584 {
585   return mk_error (Invalid_Engine);
586 }
587
588 GpgmeError
589 _gpgme_gpgsm_op_delete (GpgsmObject gpgsm, GpgmeKey key, int allow_secret)
590 {
591   return mk_error (Invalid_Engine);
592 }
593
594 GpgmeError
595 _gpgme_gpgsm_op_encrypt (GpgsmObject gpgsm, GpgmeRecipients recp,
596                          GpgmeData plain, GpgmeData ciph, int use_armor)
597 {
598   return mk_error (Invalid_Engine);
599 }
600
601 GpgmeError
602 _gpgme_gpgsm_op_export (GpgsmObject gpgsm, GpgmeRecipients recp,
603                         GpgmeData keydata, int use_armor)
604 {
605   return mk_error (Invalid_Engine);
606 }
607
608 GpgmeError
609 _gpgme_gpgsm_op_genkey (GpgsmObject gpgsm, GpgmeData help_data, int use_armor)
610 {
611   return mk_error (Invalid_Engine);
612 }
613   
614 GpgmeError
615 _gpgme_gpgsm_op_import (GpgsmObject gpgsm, GpgmeData keydata)
616 {
617   return mk_error (Invalid_Engine);
618 }
619
620 GpgmeError
621 _gpgme_gpgsm_op_keylist (GpgsmObject gpgsm, const char *pattern,
622                          int secret_only, int keylist_mode)
623 {
624   return mk_error (Invalid_Engine);
625 }
626
627 GpgmeError
628 _gpgme_gpgsm_op_sign (GpgsmObject gpgsm, GpgmeData in, GpgmeData out,
629                       GpgmeSigMode mode, int use_armor,
630                       int use_textmode, GpgmeCtx ctx /* FIXME */)
631 {
632   return mk_error (Invalid_Engine);
633 }
634
635 GpgmeError
636 _gpgme_gpgsm_op_trustlist (GpgsmObject gpgsm, const char *pattern)
637 {
638   return mk_error (Invalid_Engine);
639 }
640
641 GpgmeError
642 _gpgme_gpgsm_op_verify (GpgsmObject gpgsm, GpgmeData sig, GpgmeData text)
643 {
644   return mk_error (Invalid_Engine);
645 }
646
647 GpgmeError
648 _gpgme_gpgsm_start (GpgsmObject gpgsm, void *opaque)
649 {
650   return mk_error (Invalid_Engine);
651 }
652
653 #endif  /* ! ENABLE_GPGSM */