3d490d09459946bd64616967d3cb91d0bf19d087
[gpgme.git] / gpgme / engine-gpgsm.c
1 /* engine-gpgsm.c -  GpgSM engine
2  *      Copyright (C) 2000 Werner Koch (dd9jn)
3  *      Copyright (C) 2001, 2002 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 #include <unistd.h>
38 #include <locale.h>
39 #include <fcntl.h> /* FIXME */
40
41 #include "rungpg.h"
42 #include "status-table.h"
43
44 #include "gpgme.h"
45 #include "util.h"
46 #include "types.h"
47 #include "ops.h"
48 #include "wait.h"
49 #include "io.h"
50 #include "key.h"
51 #include "sema.h"
52
53 #include "engine-gpgsm.h"
54
55 #include "assuan.h"
56
57 #define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
58                      *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
59 #define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
60
61
62
63 typedef struct
64 {
65   int fd;       /* FD we talk about.  */
66   int dir;      /* Inbound/Outbound, maybe given implicit?  */
67   void *data;   /* Handler-specific data.  */
68   void *tag;    /* ID from the user for gpgme_remove_io_callback.  */
69 } iocb_data_t;
70
71 struct gpgsm_object_s
72 {
73   ASSUAN_CONTEXT assuan_ctx;
74
75   iocb_data_t status_cb;
76
77   /* Input, output etc are from the servers perspective.  */
78   iocb_data_t input_cb;
79   int input_fd_server;
80
81   iocb_data_t output_cb;
82   int output_fd_server;
83
84   iocb_data_t message_cb;
85   int message_fd_server;
86
87   char *command;
88
89   struct
90   {
91     GpgStatusHandler fnc;
92     void *fnc_value;
93   } status;
94
95   struct
96   {
97     GpgColonLineHandler fnc;
98     void *fnc_value;
99     struct
100     {
101       unsigned char *line;
102       int linesize;
103       int linelen;
104     } attic;
105   } colon; 
106
107   struct GpgmeIOCbs io_cbs;
108 };
109
110
111 const char *
112 _gpgme_gpgsm_get_version (void)
113 {
114   static const char *gpgsm_version;
115   DEFINE_STATIC_LOCK (gpgsm_version_lock);
116
117   LOCK (gpgsm_version_lock);
118   if (!gpgsm_version)
119     gpgsm_version = _gpgme_get_program_version (_gpgme_get_gpgsm_path ());
120   UNLOCK (gpgsm_version_lock);
121
122   return gpgsm_version;
123 }
124
125
126 GpgmeError
127 _gpgme_gpgsm_check_version (void)
128 {
129     return _gpgme_compare_versions (_gpgme_gpgsm_get_version (),
130                                   NEED_GPGSM_VERSION)
131     ? 0 : mk_error (Invalid_Engine);
132 }
133
134
135 static void
136 close_notify_handler (int fd, void *opaque)
137 {
138   GpgsmObject gpgsm = opaque;
139   int possibly_done = 0;
140
141   assert (fd != -1);
142   if (gpgsm->status_cb.fd == fd)
143     {
144       if (gpgsm->status_cb.tag)
145         {
146           (*gpgsm->io_cbs.remove) (gpgsm->status_cb.tag);
147           possibly_done = 1;
148         }
149       gpgsm->status_cb.fd = -1;
150     }
151   else if (gpgsm->input_cb.fd == fd)
152     {
153       if (gpgsm->input_cb.tag)
154         {
155           (*gpgsm->io_cbs.remove) (gpgsm->input_cb.tag);
156           possibly_done = 1;
157         }
158       gpgsm->input_cb.fd = -1;
159     }
160   else if (gpgsm->output_cb.fd == fd)
161     {
162       if (gpgsm->output_cb.tag)
163         {      
164           (*gpgsm->io_cbs.remove) (gpgsm->output_cb.tag);
165           possibly_done = 1;
166         }
167       gpgsm->output_cb.fd = -1;
168     }
169   else if (gpgsm->message_cb.fd == fd)
170     {
171       if (gpgsm->message_cb.tag)
172         {
173           (*gpgsm->io_cbs.remove) (gpgsm->message_cb.tag);
174           possibly_done = 1;
175         }
176       gpgsm->message_cb.fd = -1;
177     }
178   if (possibly_done && gpgsm->io_cbs.event
179       && gpgsm->status_cb.fd == -1 && gpgsm->input_cb.fd == -1
180       && gpgsm->output_cb.fd == -1 && gpgsm->message_cb.fd == -1)
181     (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, GPGME_EVENT_DONE, NULL);
182 }
183
184
185 static GpgmeError
186 map_assuan_error (AssuanError err)
187 {
188   switch (err)
189     {
190     case ASSUAN_No_Error:
191       return mk_error (No_Error);
192     case ASSUAN_General_Error:
193       return mk_error (General_Error);
194     case ASSUAN_Out_Of_Core:
195       return mk_error (Out_Of_Core);
196     case ASSUAN_Invalid_Value:
197       return mk_error (Invalid_Value);
198     case ASSUAN_Read_Error:
199       return mk_error (Read_Error);
200     case ASSUAN_Write_Error:
201       return mk_error (Write_Error);
202
203     case ASSUAN_Timeout:
204     case ASSUAN_Problem_Starting_Server:
205     case ASSUAN_Not_A_Server:
206     case ASSUAN_Not_A_Client:
207     case ASSUAN_Nested_Commands:
208     case ASSUAN_Invalid_Response:
209     case ASSUAN_No_Data_Callback:
210     case ASSUAN_No_Inquire_Callback:
211     case ASSUAN_Connect_Failed:
212     case ASSUAN_Accept_Failed:
213       return mk_error (General_Error);
214
215       /* The following error codes are meant as status codes.  */
216     case ASSUAN_Not_Implemented:
217       return mk_error (Not_Implemented);
218     case ASSUAN_Canceled:
219       return mk_error (Canceled);
220     case ASSUAN_Unsupported_Algorithm:
221       return mk_error (Not_Implemented);  /* XXX Argh.  */
222       
223       /* These are errors internal to GPGME.  */
224     case ASSUAN_No_Data_Available:
225     case ASSUAN_No_Input:
226     case ASSUAN_No_Output:
227     case ASSUAN_Invalid_Command:
228     case ASSUAN_Unknown_Command:
229     case ASSUAN_Syntax_Error:
230     case ASSUAN_Parameter_Error:
231     case ASSUAN_Parameter_Conflict:
232     case ASSUAN_Line_Too_Long:
233     case ASSUAN_Line_Not_Terminated:
234     case ASSUAN_Invalid_Data:
235     case ASSUAN_Unexpected_Command:
236     case ASSUAN_Too_Much_Data:
237     case ASSUAN_Inquire_Unknown:
238     case ASSUAN_Inquire_Error:
239     case ASSUAN_Invalid_Option:
240     case ASSUAN_Invalid_Index:
241     case ASSUAN_Unexpected_Status:
242     case ASSUAN_Unexpected_Data:
243     case ASSUAN_Invalid_Status:
244     case ASSUAN_Not_Confirmed:
245       return mk_error (General_Error);
246
247       /* These are errors in the server.  */
248     case ASSUAN_Server_Fault:
249     case ASSUAN_Server_Resource_Problem:
250     case ASSUAN_Server_IO_Error:
251     case ASSUAN_Server_Bug:
252     case ASSUAN_No_Agent:
253     case ASSUAN_Agent_Error:
254       return mk_error (Invalid_Engine);  /* XXX:  Need something more useful.  */
255
256     case ASSUAN_Bad_Certificate:
257     case ASSUAN_Bad_Certificate_Path:
258     case ASSUAN_Missing_Certificate:
259     case ASSUAN_No_Public_Key:
260     case ASSUAN_No_Secret_Key:
261     case ASSUAN_Invalid_Name:
262     case ASSUAN_Card_Error:     /* XXX: Oh well.  */
263     case ASSUAN_Invalid_Card:   /* XXX: Oh well.  */
264     case ASSUAN_No_PKCS15_App:  /* XXX: Oh well.  */
265     case ASSUAN_Card_Not_Present:       /* XXX: Oh well.  */
266     case ASSUAN_Invalid_Id:     /* XXX: Oh well.  */
267       return mk_error (Invalid_Key);
268
269     case ASSUAN_Bad_Signature:
270       return mk_error (Invalid_Key);  /* XXX: This is wrong.  */
271
272     case ASSUAN_Cert_Revoked:
273     case ASSUAN_No_CRL_For_Cert:
274     case ASSUAN_CRL_Too_Old:
275     case ASSUAN_Not_Trusted:
276       return mk_error (Invalid_Key);  /* XXX Some more details would be good.  */
277
278     default:
279       return mk_error (General_Error);
280     }
281 }
282
283
284 GpgmeError
285 _gpgme_gpgsm_new (GpgsmObject *r_gpgsm)
286 {
287   GpgmeError err = 0;
288   GpgsmObject gpgsm;
289   char *argv[3];
290   int fds[2];
291   int child_fds[4];
292   char *dft_display = NULL;
293   char *dft_ttyname = NULL;
294   char *dft_ttytype = NULL;
295   char *old_lc = NULL;
296   char *dft_lc = NULL;
297   char *optstr;
298   int fdlist[5];
299   int nfds;
300
301   *r_gpgsm = NULL;
302   gpgsm = xtrycalloc (1, sizeof *gpgsm);
303   if (!gpgsm)
304     {
305       err = mk_error (Out_Of_Core);
306       return err;
307     }
308
309   gpgsm->status_cb.fd = -1;
310   gpgsm->status_cb.tag = 0;
311
312   gpgsm->input_cb.fd = -1;
313   gpgsm->input_cb.tag = 0;
314   gpgsm->input_fd_server = -1;
315   gpgsm->output_cb.fd = -1;
316   gpgsm->output_cb.tag = 0;
317   gpgsm->output_fd_server = -1;
318   gpgsm->message_cb.fd = -1;
319   gpgsm->message_cb.tag = 0;
320   gpgsm->message_fd_server = -1;
321
322   gpgsm->status.fnc = 0;
323   gpgsm->colon.fnc = 0;
324   gpgsm->colon.attic.line = 0;
325   gpgsm->colon.attic.linesize = 0;
326   gpgsm->colon.attic.linelen = 0;
327
328   gpgsm->io_cbs.add = NULL;
329   gpgsm->io_cbs.add_priv = NULL;
330   gpgsm->io_cbs.remove = NULL;
331   gpgsm->io_cbs.event = NULL;
332   gpgsm->io_cbs.event_priv = NULL;
333
334   if (_gpgme_io_pipe (fds, 0) < 0)
335     {
336       err = mk_error (Pipe_Error);
337       goto leave;
338     }
339   gpgsm->input_cb.fd = fds[1];
340   gpgsm->input_cb.dir = 0;
341   gpgsm->input_fd_server = fds[0];
342
343   if (_gpgme_io_pipe (fds, 1) < 0)
344     {
345       err = mk_error (Pipe_Error);
346       goto leave;
347     }
348   gpgsm->output_cb.fd = fds[0];
349   gpgsm->output_cb.dir = 1;
350   gpgsm->output_fd_server = fds[1];
351
352   if (_gpgme_io_pipe (fds, 0) < 0)
353     {
354       err = mk_error (Pipe_Error);
355       goto leave;
356     }
357   gpgsm->message_cb.fd = fds[1];
358   gpgsm->message_cb.dir = 0;
359   gpgsm->message_fd_server = fds[0];
360
361   child_fds[0] = gpgsm->input_fd_server;
362   child_fds[1] = gpgsm->output_fd_server;
363   child_fds[2] = gpgsm->message_fd_server;
364   child_fds[3] = -1;
365
366   argv[0] = "gpgsm";
367   argv[1] = "--server";
368   argv[2] = NULL;
369
370   err = assuan_pipe_connect2 (&gpgsm->assuan_ctx,
371                               _gpgme_get_gpgsm_path (), argv, child_fds,
372                               1 /* dup stderr to /dev/null */);
373
374   /* We need to know the fd used by assuan for reads.  We do this by
375      using the assumption that the first returned fd from
376      assuan_get_active_fds() is always this one.  */
377   nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */,
378                                 fdlist, DIM (fdlist));
379   if (nfds < 1)
380     {
381       err = mk_error (General_Error);  /* FIXME */
382       goto leave;
383     }
384   /* We duplicate the file descriptor, so we can close it without
385      disturbing assuan.  Alternatively, we could special case
386      status_fd and register/unregister it manually as needed, but this
387      increases code duplication and is more complicated as we can not
388      use the close notifications etc.  */
389   gpgsm->status_cb.fd = dup (fdlist[0]);
390   if (gpgsm->status_cb.fd < 0)
391     {
392       err = mk_error (General_Error);   /* FIXME */
393       goto leave;
394     }
395   gpgsm->status_cb.dir = 1;
396   gpgsm->status_cb.data = gpgsm;
397
398   dft_display = getenv ("DISPLAY");
399   if (dft_display)
400     {
401       if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
402         {
403           err = mk_error (Out_Of_Core);
404           goto leave;
405         }
406       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
407                              NULL, NULL, NULL);
408       free (optstr);
409       if (err)
410         {
411           err = map_assuan_error (err);
412           goto leave;
413         }
414     }
415   dft_ttyname = ttyname (1);
416   if (dft_ttyname)
417     {
418       if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
419         {
420           err = mk_error (Out_Of_Core);
421           goto leave;
422         }
423       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
424                              NULL);
425       free (optstr);
426       if (err)
427         {
428           err = map_assuan_error (err);
429           goto leave;
430         }
431
432       dft_ttytype = getenv ("TERM");
433       if (dft_ttytype)
434         {
435           if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
436             {
437               err = mk_error (Out_Of_Core);
438               goto leave;
439             }
440           err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
441                                  NULL);
442           free (optstr);
443           if (err)
444             {
445               err = map_assuan_error (err);
446               goto leave;
447             }
448         }
449       old_lc = setlocale (LC_CTYPE, NULL);
450       dft_lc = setlocale (LC_CTYPE, "");
451       if (dft_lc)
452         {
453           if (asprintf (&optstr, "OPTION lc-ctype=%s", dft_lc) < 0)
454             err = mk_error (Out_Of_Core);
455           else
456             {
457               err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
458                                      NULL);
459               free (optstr);
460               if (err)
461                 err = map_assuan_error (err);
462             }
463         }
464       if (old_lc)
465         setlocale (LC_CTYPE, old_lc);
466       if (err)
467         goto leave;
468
469       old_lc = setlocale (LC_MESSAGES, NULL);
470       dft_lc = setlocale (LC_MESSAGES, "");
471       if (dft_lc)
472         {
473           if (asprintf (&optstr, "OPTION lc-messages=%s", dft_lc) < 0)
474             err = mk_error (Out_Of_Core);
475           else
476             {
477               err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
478                                      NULL);
479               free (optstr);
480               if (err)
481                 err = map_assuan_error (err);
482             }
483         }
484       if (old_lc)
485         setlocale (LC_MESSAGES, old_lc);
486       if (err)
487         goto leave;
488     }
489
490   if (!err &&
491       (_gpgme_io_set_close_notify (gpgsm->status_cb.fd,
492                                    close_notify_handler, gpgsm)
493        || _gpgme_io_set_close_notify (gpgsm->input_cb.fd,
494                                    close_notify_handler, gpgsm)
495        || _gpgme_io_set_close_notify (gpgsm->output_cb.fd,
496                                       close_notify_handler, gpgsm)
497        || _gpgme_io_set_close_notify (gpgsm->message_cb.fd,
498                                       close_notify_handler, gpgsm)))
499     {
500       err = mk_error (General_Error);
501       goto leave;
502     }
503       
504  leave:
505   /* Close the server ends of the pipes.  Our ends are closed in
506      _gpgme_gpgsm_release.  */
507   if (gpgsm->input_fd_server != -1)
508     _gpgme_io_close (gpgsm->input_fd_server);
509   if (gpgsm->output_fd_server != -1)
510     _gpgme_io_close (gpgsm->output_fd_server);
511   if (gpgsm->message_fd_server != -1)
512     _gpgme_io_close (gpgsm->message_fd_server);
513
514   if (err)
515     _gpgme_gpgsm_release (gpgsm);
516   else
517     *r_gpgsm = gpgsm;
518
519   return err;
520 }
521
522
523 void
524 _gpgme_gpgsm_release (GpgsmObject gpgsm)
525 {
526   if (!gpgsm)
527     return;
528
529   if (gpgsm->status_cb.fd != -1)
530     _gpgme_io_close (gpgsm->status_cb.fd);
531   if (gpgsm->input_cb.fd != -1)
532     _gpgme_io_close (gpgsm->input_cb.fd);
533   if (gpgsm->output_cb.fd != -1)
534     _gpgme_io_close (gpgsm->output_cb.fd);
535   if (gpgsm->message_cb.fd != -1)
536     _gpgme_io_close (gpgsm->message_cb.fd);
537
538   assuan_disconnect (gpgsm->assuan_ctx);
539
540   xfree (gpgsm->colon.attic.line);
541   xfree (gpgsm->command);
542   xfree (gpgsm);
543 }
544
545
546 static GpgmeError
547 gpgsm_assuan_simple_command (ASSUAN_CONTEXT ctx, char *cmd)
548 {
549   AssuanError err;
550   char *line;
551   size_t linelen;
552
553   err = assuan_write_line (ctx, cmd);
554   if (err)
555     return map_assuan_error (err);
556
557   do
558     {
559       err = assuan_read_line (ctx, &line, &linelen);
560       if (err)
561         return map_assuan_error (err);
562     }
563   while (*line == '#' || !linelen);
564   
565   if (linelen >= 2
566       && line[0] == 'O' && line[1] == 'K'
567       && (line[2] == '\0' || line[2] == ' '))
568     return 0;
569
570   if (linelen >= 4
571       && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
572       && line[3] == ' ')
573     err = map_assuan_error (atoi (&line[4]));
574
575   if (!err)
576     err = mk_error (General_Error);
577   return 0;
578 }
579
580
581 #define COMMANDLINELEN 40
582 static GpgmeError
583 gpgsm_set_fd (ASSUAN_CONTEXT ctx, const char *which, int fd, const char *opt)
584 {
585   char line[COMMANDLINELEN];
586
587   if (opt)
588     snprintf (line, COMMANDLINELEN, "%s FD=%i %s", which, fd, opt);
589   else
590     snprintf (line, COMMANDLINELEN, "%s FD=%i", which, fd);
591
592   return gpgsm_assuan_simple_command (ctx, line);
593 }
594
595
596 static const char *
597 map_input_enc (GpgmeData d)
598 {
599   switch (gpgme_data_get_encoding (d))
600     {
601     case GPGME_DATA_ENCODING_NONE:
602       break;
603     case GPGME_DATA_ENCODING_BINARY:
604       return "--binary";
605     case GPGME_DATA_ENCODING_BASE64:
606       return "--base64";
607     case GPGME_DATA_ENCODING_ARMOR:
608       return "--armor";
609     default:
610       break;
611     }
612   return NULL;
613 }
614
615
616 GpgmeError
617 _gpgme_gpgsm_op_decrypt (GpgsmObject gpgsm, GpgmeData ciph, GpgmeData plain)
618 {
619   GpgmeError err;
620
621   if (!gpgsm)
622     return mk_error (Invalid_Value);
623
624   gpgsm->command = xtrystrdup ("DECRYPT");
625   if (!gpgsm->command)
626     return mk_error (Out_Of_Core);
627
628   gpgsm->input_cb.data = ciph;
629   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server, 
630                       map_input_enc (gpgsm->input_cb.data));
631   if (err)
632     return mk_error (General_Error);    /* FIXME */
633   gpgsm->output_cb.data = plain;
634   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server, 0);
635   if (err)
636     return mk_error (General_Error);    /* FIXME */
637   _gpgme_io_close (gpgsm->message_cb.fd);
638
639   return 0;
640 }
641
642
643 GpgmeError
644 _gpgme_gpgsm_op_delete (GpgsmObject gpgsm, GpgmeKey key, int allow_secret)
645 {
646   /* FIXME */
647   return mk_error (Not_Implemented);
648 }
649
650
651 static GpgmeError
652 gpgsm_set_recipients (GpgsmObject gpgsm, GpgmeRecipients recp)
653 {
654   GpgmeError err;
655   ASSUAN_CONTEXT ctx = gpgsm->assuan_ctx;
656   char *line;
657   int linelen;
658   struct user_id_s *r;
659   int valid_recipients = 0;
660
661   linelen = 10 + 40 + 1;        /* "RECIPIENT " + guess + '\0'.  */
662   line = xtrymalloc (10 + 40 + 1);
663   if (!line)
664     return mk_error (Out_Of_Core);
665   strcpy (line, "RECIPIENT ");
666   for (r = recp->list; r; r = r->next)
667     {
668       int newlen = 11 + strlen (r->name);
669       if (linelen < newlen)
670         {
671           char *newline = xtryrealloc (line, newlen);
672           if (! newline)
673             {
674               xfree (line);
675               return mk_error (Out_Of_Core);
676             }
677           line = newline;
678           linelen = newlen;
679         }
680       strcpy (&line[10], r->name);
681       
682       err = gpgsm_assuan_simple_command (ctx, line);
683       if (!err)
684         valid_recipients = 1;
685       else if (err == GPGME_Invalid_Key && gpgsm->status.fnc)
686         {
687           /* FIXME: Include other reasons.  */
688           line[8] = '0';        /* FIXME: Report detailed reason.  */
689           gpgsm->status.fnc (gpgsm->status.fnc_value, STATUS_INV_RECP, &line[8]);
690           line[8] = 'T';
691         }
692       else if (err != GPGME_Invalid_Key)
693         {
694           xfree (line);
695           return err;
696         }
697     }
698   xfree (line);
699   if (!valid_recipients && gpgsm->status.fnc)
700     gpgsm->status.fnc (gpgsm->status.fnc_value, STATUS_NO_RECP, "");
701   return 0;
702 }
703
704
705 GpgmeError
706 _gpgme_gpgsm_op_encrypt (GpgsmObject gpgsm, GpgmeRecipients recp,
707                          GpgmeData plain, GpgmeData ciph, int use_armor)
708 {
709   GpgmeError err;
710
711   if (!gpgsm)
712     return mk_error (Invalid_Value);
713   if (!recp)
714     return mk_error (Not_Implemented);
715
716   gpgsm->command = xtrystrdup ("ENCRYPT");
717   if (!gpgsm->command)
718     return mk_error (Out_Of_Core);
719
720   gpgsm->input_cb.data = plain;
721   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
722                       map_input_enc (gpgsm->input_cb.data));
723   if (err)
724     return err;
725   gpgsm->output_cb.data = ciph;
726   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
727                       use_armor ? "--armor" : 0);
728   if (err)
729     return err;
730   _gpgme_io_close (gpgsm->message_cb.fd);
731
732   err = gpgsm_set_recipients (gpgsm, recp);
733   if (err)
734     return err;
735
736   return 0;
737 }
738
739
740 GpgmeError
741 _gpgme_gpgsm_op_export (GpgsmObject gpgsm, GpgmeRecipients recp,
742                         GpgmeData keydata, int use_armor)
743 {
744   /* FIXME */
745   return mk_error (Not_Implemented);
746 }
747
748
749 GpgmeError
750 _gpgme_gpgsm_op_genkey (GpgsmObject gpgsm, GpgmeData help_data, int use_armor,
751                         GpgmeData pubkey, GpgmeData seckey)
752 {
753   GpgmeError err;
754
755   if (!gpgsm || !pubkey || seckey)
756     return mk_error (Invalid_Value);
757
758   gpgsm->command = xtrystrdup ("GENKEY");
759   if (!gpgsm->command)
760     return mk_error (Out_Of_Core);
761
762   gpgsm->input_cb.data = help_data;
763   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
764                       map_input_enc (gpgsm->input_cb.data));
765   if (err)
766     return err;
767   gpgsm->output_cb.data = pubkey;
768   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
769                       use_armor ? "--armor" : 0);
770   if (err)
771     return err;
772   _gpgme_io_close (gpgsm->message_cb.fd);
773
774   return 0;
775 }
776
777
778 GpgmeError
779 _gpgme_gpgsm_op_import (GpgsmObject gpgsm, GpgmeData keydata)
780 {
781   GpgmeError err;
782
783   if (!gpgsm)
784     return mk_error (Invalid_Value);
785
786   gpgsm->command = xtrystrdup ("IMPORT");
787   if (!gpgsm->command)
788     return mk_error (Out_Of_Core);
789
790   gpgsm->input_cb.data = keydata;
791   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
792                       map_input_enc (gpgsm->input_cb.data));
793   if (err)
794     return err;
795   _gpgme_io_close (gpgsm->output_cb.fd);
796   _gpgme_io_close (gpgsm->message_cb.fd);
797
798   return 0;
799 }
800
801
802 GpgmeError
803 _gpgme_gpgsm_op_keylist (GpgsmObject gpgsm, const char *pattern,
804                          int secret_only, int keylist_mode)
805 {
806   char *line;
807   GpgmeError err;
808
809   if (!pattern)
810     pattern = "";
811
812   if (asprintf (&line, "OPTION list-mode=%d", (keylist_mode & 3)) < 0)
813     return mk_error (Out_Of_Core);
814   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line);
815   free (line);
816   if (err)
817     return err;
818
819   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
820   line = xtrymalloc (15 + strlen (pattern) + 1);
821   if (!line)
822     return mk_error (Out_Of_Core);
823   if (secret_only)
824     {
825       strcpy (line, "LISTSECRETKEYS ");
826       strcpy (&line[15], pattern);
827     }
828   else
829     {
830       strcpy (line, "LISTKEYS ");
831       strcpy (&line[9], pattern);
832     }
833
834   _gpgme_io_close (gpgsm->input_cb.fd);
835   _gpgme_io_close (gpgsm->output_cb.fd);
836   _gpgme_io_close (gpgsm->message_cb.fd);
837
838   gpgsm->command = line;
839   return 0;
840 }
841
842
843 GpgmeError
844 _gpgme_gpgsm_op_keylist_ext (GpgsmObject gpgsm, const char *pattern[],
845                              int secret_only, int reserved, int keylist_mode)
846 {
847   char *line;
848   GpgmeError err;
849   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
850   int length = 15 + 1;
851   char *linep;
852   
853   if (reserved)
854     return mk_error (Invalid_Value);
855
856   if (asprintf (&line, "OPTION list-mode=%d", (keylist_mode & 3)) < 0)
857     return mk_error (Out_Of_Core);
858   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line);
859   free (line);
860   if (err)
861     return err;
862
863   if (pattern && *pattern)
864     {
865       const char **pat = pattern;
866
867       while (*pat)
868         {
869           const char *patlet = *pat;
870
871           while (*patlet)
872             {
873               length++;
874               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
875                 length += 2;
876               patlet++;
877             }
878           pat++;
879           /* This will allocate one byte more than necessary.  */
880           length++;
881         }
882     }
883   line = xtrymalloc (length);
884   if (!line)
885     return mk_error (Out_Of_Core);
886   if (secret_only)
887     {
888       strcpy (line, "LISTSECRETKEYS ");
889       linep = &line[15];
890     }
891   else
892     {
893       strcpy (line, "LISTKEYS ");
894       linep = &line[9];
895     }
896
897   if (pattern && *pattern)
898     {
899       while (*pattern)
900         {
901           const char *patlet = *pattern;
902
903           while (*patlet)
904             {
905               switch (*patlet)
906                 {
907                 case '%':
908                   *(linep++) = '%';
909                   *(linep++) = '2';
910                   *(linep++) = '5';
911                   break;
912                 case ' ':
913                   *(linep++) = '%';
914                   *(linep++) = '2';
915                   *(linep++) = '0';
916                   break;
917                 case '+':
918                   *(linep++) = '%';
919                   *(linep++) = '2';
920                   *(linep++) = 'B';
921                   break;
922                 default:
923                   *(linep++) = *patlet;
924                   break;
925                 }
926               patlet++;
927             }
928           pattern++;
929         }
930     }
931   *linep = '\0';
932
933   _gpgme_io_close (gpgsm->input_cb.fd);
934   _gpgme_io_close (gpgsm->output_cb.fd);
935   _gpgme_io_close (gpgsm->message_cb.fd);
936
937   gpgsm->command = line;
938   return 0;
939 }
940
941
942 GpgmeError
943 _gpgme_gpgsm_op_sign (GpgsmObject gpgsm, GpgmeData in, GpgmeData out,
944                       GpgmeSigMode mode, int use_armor,
945                       int use_textmode, int include_certs,
946                       GpgmeCtx ctx /* FIXME */)
947 {
948   GpgmeError err;
949   char *assuan_cmd;
950
951   if (!gpgsm)
952     return mk_error (Invalid_Value);
953
954   gpgsm->command = xtrystrdup (mode == GPGME_SIG_MODE_DETACH
955                                ? "SIGN --detached" : "SIGN");
956   if (!gpgsm->command)
957     return mk_error (Out_Of_Core);
958
959   if (asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0)
960     return mk_error (Out_Of_Core);
961   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, assuan_cmd);
962   free (assuan_cmd);
963   if (err)
964     return err;
965
966   gpgsm->input_cb.data = in;
967   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
968                       map_input_enc (gpgsm->input_cb.data));
969   if (err)
970     return err;
971   gpgsm->output_cb.data = out;
972   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
973                       use_armor ? "--armor" : 0);
974   if (err)
975     return err;
976   _gpgme_io_close (gpgsm->message_cb.fd);
977
978   return 0;
979 }
980
981
982 GpgmeError
983 _gpgme_gpgsm_op_trustlist (GpgsmObject gpgsm, const char *pattern)
984 {
985   /* FIXME */
986   return mk_error (Not_Implemented);
987 }
988
989
990 GpgmeError
991 _gpgme_gpgsm_op_verify (GpgsmObject gpgsm, GpgmeData sig, GpgmeData text)
992 {
993   GpgmeError err;
994
995   if (!gpgsm)
996     return mk_error (Invalid_Value);
997
998   gpgsm->command = xtrystrdup ("VERIFY");
999   if (!gpgsm->command)
1000     return mk_error (Out_Of_Core);
1001
1002   gpgsm->input_cb.data = sig;
1003   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1004                       map_input_enc (gpgsm->input_cb.data));
1005   if (err)
1006     return err;
1007   if (_gpgme_data_get_mode (text) == GPGME_DATA_MODE_IN)
1008     {
1009       /* Normal or cleartext signature.  */
1010       gpgsm->output_cb.data = text;
1011       err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1012                           0);
1013       _gpgme_io_close (gpgsm->message_cb.fd);
1014     }
1015   else
1016     {
1017       /* Detached signature.  */
1018       gpgsm->message_cb.data = text;
1019       err = gpgsm_set_fd (gpgsm->assuan_ctx, "MESSAGE",
1020                           gpgsm->message_fd_server, 0);
1021       _gpgme_io_close (gpgsm->output_cb.fd);
1022     }
1023   if (err)
1024     return err;
1025
1026   return 0;
1027 }
1028
1029
1030 static int
1031 status_cmp (const void *ap, const void *bp)
1032 {
1033   const struct status_table_s *a = ap;
1034   const struct status_table_s *b = bp;
1035
1036   return strcmp (a->name, b->name);
1037 }
1038
1039
1040 static void
1041 gpgsm_status_handler (void *opaque, int fd)
1042 {
1043   AssuanError err;
1044   GpgsmObject gpgsm = opaque;
1045   char *line;
1046   size_t linelen;
1047
1048   do
1049     {
1050       err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
1051
1052       if (err
1053           || (linelen >= 2
1054               && line[0] == 'O' && line[1] == 'K'
1055               && (line[2] == '\0' || line[2] == ' '))
1056           || (linelen >= 3
1057               && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
1058               && (line[3] == '\0' || line[3] == ' ')))
1059         {
1060           /* XXX: If an error occured, find out what happened, then
1061              save the error value before running the status handler
1062              (so it takes precedence).  */
1063           if (!err && line[0] == 'E' && line[3] == ' ')
1064             {
1065               err = map_assuan_error (atoi (&line[4]));
1066               if (!err)
1067                 err = mk_error (General_Error);
1068             }
1069           if (err)
1070             {
1071               /* XXX Kludge ahead.  We really, really, really must not
1072                  make use of status.fnc_value.  */
1073               GpgmeCtx ctx = (GpgmeCtx) gpgsm->status.fnc_value;
1074               if (!ctx->error)
1075                 ctx->error = err;
1076             }
1077
1078           if (gpgsm->status.fnc)
1079             gpgsm->status.fnc (gpgsm->status.fnc_value, STATUS_EOF, "");
1080
1081           /* XXX: Try our best to terminate the connection.  */
1082           if (err)
1083             assuan_write_line (gpgsm->assuan_ctx, "BYE");
1084
1085           _gpgme_io_close (gpgsm->status_cb.fd);
1086           return;
1087         }
1088
1089       if (linelen > 2
1090           && line[0] == 'D' && line[1] == ' '
1091           && gpgsm->colon.fnc)
1092         {
1093           /* We are using the colon handler even for plain inline data
1094              - strange name for that function but for historic reasons
1095              we keep it.  */
1096           /* FIXME We can't use this for binary data because we
1097              assume this is a string.  For the current usage of colon
1098              output it is correct.  */
1099           unsigned char *src = line + 2;
1100           unsigned char *end = line + linelen;
1101           unsigned char *dst;
1102           unsigned char **aline = &gpgsm->colon.attic.line;
1103           int *alinelen = &gpgsm->colon.attic.linelen;
1104
1105           if (gpgsm->colon.attic.linesize
1106               < *alinelen + linelen + 1)
1107             {
1108               unsigned char *newline = xtryrealloc (*aline,
1109                                                     *alinelen + linelen + 1);
1110               if (!newline)
1111                 {
1112                   _gpgme_io_close (gpgsm->status_cb.fd);
1113                   return;
1114                 }
1115               *aline = newline;
1116               gpgsm->colon.attic.linesize += linelen + 1;
1117             }
1118
1119           dst = *aline + *alinelen;
1120
1121           while (src < end)
1122             {
1123               if (*src == '%' && src + 2 < end)
1124                 {
1125                   /* Handle escaped characters.  */
1126                   ++src;
1127                   *dst = xtoi_2 (src);
1128                   (*alinelen)++;
1129                   src += 2;
1130                 }
1131               else
1132                 {
1133                   *dst = *src++;
1134                   (*alinelen)++;
1135                 }
1136
1137               if (*dst == '\n')
1138                 {
1139                   /* Terminate the pending line, pass it to the colon
1140                      handler and reset it.  */
1141
1142                   if (*alinelen > 1 && *(dst - 1) == '\r')
1143                     dst--;
1144                   *dst = '\0';
1145
1146                   /* FIXME How should we handle the return code? */
1147                   gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline);
1148                   dst = *aline;
1149                   *alinelen = 0;
1150                 }
1151               else
1152                 dst++;
1153             }
1154         }
1155       else if (linelen > 2
1156           && line[0] == 'S' && line[1] == ' ')
1157         {
1158           struct status_table_s t, *r;
1159           char *rest;
1160           
1161           rest = strchr (line + 2, ' ');
1162           if (!rest)
1163             rest = line + linelen; /* set to an empty string */
1164           else
1165             *rest++ = 0;
1166
1167           t.name = line + 2;
1168           r = bsearch (&t, status_table, DIM(status_table) - 1,
1169                        sizeof t, status_cmp);
1170
1171           if (r)
1172             {
1173               if (gpgsm->status.fnc)
1174                 gpgsm->status.fnc (gpgsm->status.fnc_value, r->code, rest);
1175             }
1176           else
1177             fprintf (stderr, "[UNKNOWN STATUS]%s %s", t.name, rest);
1178         }
1179     }
1180   while (assuan_pending_line (gpgsm->assuan_ctx));
1181 }
1182
1183
1184 void
1185 _gpgme_gpgsm_set_status_handler (GpgsmObject gpgsm,
1186                                  GpgStatusHandler fnc, void *fnc_value) 
1187 {
1188   assert (gpgsm);
1189
1190   gpgsm->status.fnc = fnc;
1191   gpgsm->status.fnc_value = fnc_value;
1192 }
1193
1194
1195 void
1196 _gpgme_gpgsm_set_colon_line_handler (GpgsmObject gpgsm,
1197                                      GpgColonLineHandler fnc, void *fnc_value) 
1198 {
1199   assert (gpgsm);
1200
1201   gpgsm->colon.fnc = fnc;
1202   gpgsm->colon.fnc_value = fnc_value;
1203 }
1204
1205
1206 static GpgmeError
1207 _gpgme_gpgsm_add_io_cb (GpgsmObject gpgsm, iocb_data_t *iocbd,
1208                         GpgmeIOCb handler)
1209 {
1210   GpgmeError err = 0;
1211
1212   iocbd->tag = (*gpgsm->io_cbs.add) (gpgsm->io_cbs.add_priv,
1213                                      iocbd->fd, iocbd->dir,
1214                                      handler, iocbd->data);
1215   if (!iocbd->tag)
1216     err = mk_error (General_Error);
1217   if (!err && !iocbd->dir)
1218     /* FIXME Kludge around poll() problem.  */
1219     err = _gpgme_io_set_nonblocking (iocbd->fd);
1220   return err;
1221 }
1222
1223 GpgmeError
1224 _gpgme_gpgsm_start (GpgsmObject gpgsm, void *opaque)
1225 {
1226   GpgmeError err = 0;
1227   pid_t pid;
1228
1229   if (!gpgsm)
1230     return mk_error (Invalid_Value);
1231
1232   pid = assuan_get_pid (gpgsm->assuan_ctx);
1233
1234   err = _gpgme_gpgsm_add_io_cb (gpgsm, &gpgsm->status_cb,
1235                                 gpgsm_status_handler);
1236   if (gpgsm->input_cb.fd != -1)
1237     err = _gpgme_gpgsm_add_io_cb (gpgsm, &gpgsm->input_cb,
1238                                   _gpgme_data_outbound_handler);
1239   if (!err && gpgsm->output_cb.fd != -1)
1240     err = _gpgme_gpgsm_add_io_cb (gpgsm, &gpgsm->output_cb,
1241                                   _gpgme_data_inbound_handler);
1242   if (!err && gpgsm->message_cb.fd != -1)
1243     err = _gpgme_gpgsm_add_io_cb (gpgsm, &gpgsm->message_cb,
1244                                   _gpgme_data_outbound_handler);
1245
1246   if (!err)
1247     err = assuan_write_line (gpgsm->assuan_ctx, gpgsm->command);
1248
1249   return err;
1250 }
1251
1252 void
1253 _gpgme_gpgsm_set_io_cbs (GpgsmObject gpgsm, struct GpgmeIOCbs *io_cbs)
1254 {
1255   gpgsm->io_cbs = *io_cbs;
1256 }
1257
1258
1259 #else   /* ENABLE_GPGSM */
1260
1261
1262 #include <stddef.h>
1263 #include "util.h"
1264
1265 #include "engine-gpgsm.h"
1266
1267
1268 const char *
1269 _gpgme_gpgsm_get_version (void)
1270 {
1271   return NULL;
1272 }
1273
1274
1275 GpgmeError
1276 _gpgme_gpgsm_check_version (void)
1277 {
1278   return mk_error (Invalid_Engine);
1279 }
1280
1281
1282 GpgmeError
1283 _gpgme_gpgsm_new (GpgsmObject *r_gpgsm)
1284 {
1285   return mk_error (Invalid_Engine);
1286 }
1287
1288
1289 void
1290 _gpgme_gpgsm_release (GpgsmObject gpgsm)
1291 {
1292   return;
1293 }
1294
1295
1296 void
1297 _gpgme_gpgsm_set_status_handler (GpgsmObject gpgsm,
1298                                  GpgStatusHandler fnc, void *fnc_value) 
1299 {
1300   return;
1301 }
1302
1303
1304 GpgmeError
1305 _gpgme_gpgsm_op_decrypt (GpgsmObject gpgsm, GpgmeData ciph, GpgmeData plain)
1306 {
1307   return mk_error (Invalid_Engine);
1308 }
1309
1310
1311 GpgmeError
1312 _gpgme_gpgsm_op_delete (GpgsmObject gpgsm, GpgmeKey key, int allow_secret)
1313 {
1314   return mk_error (Invalid_Engine);
1315 }
1316
1317
1318 GpgmeError
1319 _gpgme_gpgsm_op_encrypt (GpgsmObject gpgsm, GpgmeRecipients recp,
1320                          GpgmeData plain, GpgmeData ciph, int use_armor)
1321 {
1322   return mk_error (Invalid_Engine);
1323 }
1324
1325
1326 GpgmeError
1327 _gpgme_gpgsm_op_export (GpgsmObject gpgsm, GpgmeRecipients recp,
1328                         GpgmeData keydata, int use_armor)
1329 {
1330   return mk_error (Invalid_Engine);
1331 }
1332
1333
1334 GpgmeError
1335 _gpgme_gpgsm_op_genkey (GpgsmObject gpgsm, GpgmeData help_data, int use_armor,
1336                         GpgmeData pubkey, GpgmeData seckey)
1337 {
1338   return mk_error (Invalid_Engine);
1339 }
1340   
1341
1342 GpgmeError
1343 _gpgme_gpgsm_op_import (GpgsmObject gpgsm, GpgmeData keydata)
1344 {
1345   return mk_error (Invalid_Engine);
1346 }
1347
1348
1349 GpgmeError
1350 _gpgme_gpgsm_op_keylist (GpgsmObject gpgsm, const char *pattern,
1351                          int secret_only, int keylist_mode)
1352 {
1353   return mk_error (Invalid_Engine);
1354 }
1355
1356
1357 GpgmeError
1358 _gpgme_gpgsm_op_keylist_ext (GpgsmObject gpgsm, const char *pattern[],
1359                              int secret_only, int reserved, int keylist_mode)
1360 {
1361   return mk_error (Invalid_Engine);
1362 }
1363
1364 GpgmeError
1365 _gpgme_gpgsm_op_sign (GpgsmObject gpgsm, GpgmeData in, GpgmeData out,
1366                       GpgmeSigMode mode, int use_armor,
1367                       int use_textmode, int include_certs,
1368                       GpgmeCtx ctx /* FIXME */)
1369 {
1370   return mk_error (Invalid_Engine);
1371 }
1372
1373
1374 GpgmeError
1375 _gpgme_gpgsm_op_trustlist (GpgsmObject gpgsm, const char *pattern)
1376 {
1377   return mk_error (Invalid_Engine);
1378 }
1379
1380
1381 GpgmeError
1382 _gpgme_gpgsm_op_verify (GpgsmObject gpgsm, GpgmeData sig, GpgmeData text)
1383 {
1384   return mk_error (Invalid_Engine);
1385 }
1386
1387
1388 void
1389 _gpgme_gpgsm_set_colon_line_handler (GpgsmObject gpgsm,
1390                                      GpgColonLineHandler fnc, void *fnc_value) 
1391 {
1392 }
1393
1394
1395 GpgmeError
1396 _gpgme_gpgsm_start (GpgsmObject gpgsm, void *opaque)
1397 {
1398   return mk_error (Invalid_Engine);
1399 }
1400
1401 void _gpgme_gpgsm_set_io_cbs (GpgsmObject gpgsm, GpgmeIOCbs io_cbs)
1402 {
1403 }
1404
1405 #endif  /* ! ENABLE_GPGSM */