7e26f0dda7baffa0784d3768441e64368d9f1e4d
[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 /* Forward declaration.  */
546 static GpgStatusCode parse_status (const char *name);
547
548 static GpgmeError
549 gpgsm_assuan_simple_command (ASSUAN_CONTEXT ctx, char *cmd, GpgStatusHandler status_fnc,
550                              void *status_fnc_value)
551 {
552   AssuanError err;
553   char *line;
554   size_t linelen;
555
556   err = assuan_write_line (ctx, cmd);
557   if (err)
558     return map_assuan_error (err);
559
560   do
561     {
562       err = assuan_read_line (ctx, &line, &linelen);
563       if (err)
564         return map_assuan_error (err);
565
566       if (*line == '#' || !linelen)
567         continue;
568
569       if (linelen >= 2
570           && line[0] == 'O' && line[1] == 'K'
571           && (line[2] == '\0' || line[2] == ' '))
572         return 0;
573       else if (linelen >= 4
574           && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
575           && line[3] == ' ')
576         err = map_assuan_error (atoi (&line[4]));
577       else if (linelen >= 2
578                && line[0] == 'S' && line[1] == ' ')
579         {
580           char *rest;
581           GpgStatusCode r;
582
583           rest = strchr (line + 2, ' ');
584           if (!rest)
585             rest = line + linelen; /* set to an empty string */
586           else
587             *(rest++) = 0;
588
589           r = parse_status (line + 2);
590
591           if (r >= 0 && status_fnc)
592             status_fnc (status_fnc_value, r, rest);
593           else
594             err = mk_error (General_Error);
595         }
596       else
597         err = mk_error (General_Error);
598     }
599   while (!err);
600
601   return err;
602 }
603
604
605 #define COMMANDLINELEN 40
606 static GpgmeError
607 gpgsm_set_fd (ASSUAN_CONTEXT ctx, const char *which, int fd, const char *opt)
608 {
609   char line[COMMANDLINELEN];
610
611   if (opt)
612     snprintf (line, COMMANDLINELEN, "%s FD=%i %s", which, fd, opt);
613   else
614     snprintf (line, COMMANDLINELEN, "%s FD=%i", which, fd);
615
616   return gpgsm_assuan_simple_command (ctx, line, NULL, NULL);
617 }
618
619
620 static const char *
621 map_input_enc (GpgmeData d)
622 {
623   switch (gpgme_data_get_encoding (d))
624     {
625     case GPGME_DATA_ENCODING_NONE:
626       break;
627     case GPGME_DATA_ENCODING_BINARY:
628       return "--binary";
629     case GPGME_DATA_ENCODING_BASE64:
630       return "--base64";
631     case GPGME_DATA_ENCODING_ARMOR:
632       return "--armor";
633     default:
634       break;
635     }
636   return NULL;
637 }
638
639
640 GpgmeError
641 _gpgme_gpgsm_op_decrypt (GpgsmObject gpgsm, GpgmeData ciph, GpgmeData plain)
642 {
643   GpgmeError err;
644
645   if (!gpgsm)
646     return mk_error (Invalid_Value);
647
648   gpgsm->command = xtrystrdup ("DECRYPT");
649   if (!gpgsm->command)
650     return mk_error (Out_Of_Core);
651
652   gpgsm->input_cb.data = ciph;
653   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server, 
654                       map_input_enc (gpgsm->input_cb.data));
655   if (err)
656     return mk_error (General_Error);    /* FIXME */
657   gpgsm->output_cb.data = plain;
658   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server, 0);
659   if (err)
660     return mk_error (General_Error);    /* FIXME */
661   _gpgme_io_close (gpgsm->message_cb.fd);
662
663   return 0;
664 }
665
666
667 GpgmeError
668 _gpgme_gpgsm_op_delete (GpgsmObject gpgsm, GpgmeKey key, int allow_secret)
669 {
670   /* FIXME */
671   return mk_error (Not_Implemented);
672 }
673
674
675 static GpgmeError
676 gpgsm_set_recipients (GpgsmObject gpgsm, GpgmeRecipients recp)
677 {
678   GpgmeError err;
679   ASSUAN_CONTEXT ctx = gpgsm->assuan_ctx;
680   char *line;
681   int linelen;
682   struct user_id_s *r;
683   int valid_recipients = 0;
684
685   linelen = 10 + 40 + 1;        /* "RECIPIENT " + guess + '\0'.  */
686   line = xtrymalloc (10 + 40 + 1);
687   if (!line)
688     return mk_error (Out_Of_Core);
689   strcpy (line, "RECIPIENT ");
690   for (r = recp->list; r; r = r->next)
691     {
692       int newlen = 11 + strlen (r->name);
693       if (linelen < newlen)
694         {
695           char *newline = xtryrealloc (line, newlen);
696           if (! newline)
697             {
698               xfree (line);
699               return mk_error (Out_Of_Core);
700             }
701           line = newline;
702           linelen = newlen;
703         }
704       strcpy (&line[10], r->name);
705       
706       err = gpgsm_assuan_simple_command (ctx, line, gpgsm->status.fnc,
707                                          gpgsm->status.fnc_value);
708       if (!err)
709         valid_recipients = 1;
710       else if (err != GPGME_Invalid_Key)
711         {
712           xfree (line);
713           return err;
714         }
715     }
716   xfree (line);
717   if (!valid_recipients && gpgsm->status.fnc)
718     gpgsm->status.fnc (gpgsm->status.fnc_value, STATUS_NO_RECP, "");
719   return 0;
720 }
721
722
723 GpgmeError
724 _gpgme_gpgsm_op_encrypt (GpgsmObject gpgsm, GpgmeRecipients recp,
725                          GpgmeData plain, GpgmeData ciph, int use_armor)
726 {
727   GpgmeError err;
728
729   if (!gpgsm)
730     return mk_error (Invalid_Value);
731   if (!recp)
732     return mk_error (Not_Implemented);
733
734   gpgsm->command = xtrystrdup ("ENCRYPT");
735   if (!gpgsm->command)
736     return mk_error (Out_Of_Core);
737
738   gpgsm->input_cb.data = plain;
739   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
740                       map_input_enc (gpgsm->input_cb.data));
741   if (err)
742     return err;
743   gpgsm->output_cb.data = ciph;
744   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
745                       use_armor ? "--armor" : 0);
746   if (err)
747     return err;
748   _gpgme_io_close (gpgsm->message_cb.fd);
749
750   err = gpgsm_set_recipients (gpgsm, recp);
751   if (err)
752     return err;
753
754   return 0;
755 }
756
757
758 GpgmeError
759 _gpgme_gpgsm_op_export (GpgsmObject gpgsm, GpgmeRecipients recp,
760                         GpgmeData keydata, int use_armor)
761 {
762   /* FIXME */
763   return mk_error (Not_Implemented);
764 }
765
766
767 GpgmeError
768 _gpgme_gpgsm_op_genkey (GpgsmObject gpgsm, GpgmeData help_data, int use_armor,
769                         GpgmeData pubkey, GpgmeData seckey)
770 {
771   GpgmeError err;
772
773   if (!gpgsm || !pubkey || seckey)
774     return mk_error (Invalid_Value);
775
776   gpgsm->command = xtrystrdup ("GENKEY");
777   if (!gpgsm->command)
778     return mk_error (Out_Of_Core);
779
780   gpgsm->input_cb.data = help_data;
781   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
782                       map_input_enc (gpgsm->input_cb.data));
783   if (err)
784     return err;
785   gpgsm->output_cb.data = pubkey;
786   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
787                       use_armor ? "--armor" : 0);
788   if (err)
789     return err;
790   _gpgme_io_close (gpgsm->message_cb.fd);
791
792   return 0;
793 }
794
795
796 GpgmeError
797 _gpgme_gpgsm_op_import (GpgsmObject gpgsm, GpgmeData keydata)
798 {
799   GpgmeError err;
800
801   if (!gpgsm)
802     return mk_error (Invalid_Value);
803
804   gpgsm->command = xtrystrdup ("IMPORT");
805   if (!gpgsm->command)
806     return mk_error (Out_Of_Core);
807
808   gpgsm->input_cb.data = keydata;
809   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
810                       map_input_enc (gpgsm->input_cb.data));
811   if (err)
812     return err;
813   _gpgme_io_close (gpgsm->output_cb.fd);
814   _gpgme_io_close (gpgsm->message_cb.fd);
815
816   return 0;
817 }
818
819
820 GpgmeError
821 _gpgme_gpgsm_op_keylist (GpgsmObject gpgsm, const char *pattern,
822                          int secret_only, int keylist_mode)
823 {
824   char *line;
825   GpgmeError err;
826
827   if (!pattern)
828     pattern = "";
829
830   if (asprintf (&line, "OPTION list-mode=%d", (keylist_mode & 3)) < 0)
831     return mk_error (Out_Of_Core);
832   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
833   free (line);
834   if (err)
835     return err;
836
837   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
838   line = xtrymalloc (15 + strlen (pattern) + 1);
839   if (!line)
840     return mk_error (Out_Of_Core);
841   if (secret_only)
842     {
843       strcpy (line, "LISTSECRETKEYS ");
844       strcpy (&line[15], pattern);
845     }
846   else
847     {
848       strcpy (line, "LISTKEYS ");
849       strcpy (&line[9], pattern);
850     }
851
852   _gpgme_io_close (gpgsm->input_cb.fd);
853   _gpgme_io_close (gpgsm->output_cb.fd);
854   _gpgme_io_close (gpgsm->message_cb.fd);
855
856   gpgsm->command = line;
857   return 0;
858 }
859
860
861 GpgmeError
862 _gpgme_gpgsm_op_keylist_ext (GpgsmObject gpgsm, const char *pattern[],
863                              int secret_only, int reserved, int keylist_mode)
864 {
865   char *line;
866   GpgmeError err;
867   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
868   int length = 15 + 1;
869   char *linep;
870   
871   if (reserved)
872     return mk_error (Invalid_Value);
873
874   if (asprintf (&line, "OPTION list-mode=%d", (keylist_mode & 3)) < 0)
875     return mk_error (Out_Of_Core);
876   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
877   free (line);
878   if (err)
879     return err;
880
881   if (pattern && *pattern)
882     {
883       const char **pat = pattern;
884
885       while (*pat)
886         {
887           const char *patlet = *pat;
888
889           while (*patlet)
890             {
891               length++;
892               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
893                 length += 2;
894               patlet++;
895             }
896           pat++;
897           /* This will allocate one byte more than necessary.  */
898           length++;
899         }
900     }
901   line = xtrymalloc (length);
902   if (!line)
903     return mk_error (Out_Of_Core);
904   if (secret_only)
905     {
906       strcpy (line, "LISTSECRETKEYS ");
907       linep = &line[15];
908     }
909   else
910     {
911       strcpy (line, "LISTKEYS ");
912       linep = &line[9];
913     }
914
915   if (pattern && *pattern)
916     {
917       while (*pattern)
918         {
919           const char *patlet = *pattern;
920
921           while (*patlet)
922             {
923               switch (*patlet)
924                 {
925                 case '%':
926                   *(linep++) = '%';
927                   *(linep++) = '2';
928                   *(linep++) = '5';
929                   break;
930                 case ' ':
931                   *(linep++) = '%';
932                   *(linep++) = '2';
933                   *(linep++) = '0';
934                   break;
935                 case '+':
936                   *(linep++) = '%';
937                   *(linep++) = '2';
938                   *(linep++) = 'B';
939                   break;
940                 default:
941                   *(linep++) = *patlet;
942                   break;
943                 }
944               patlet++;
945             }
946           pattern++;
947         }
948     }
949   *linep = '\0';
950
951   _gpgme_io_close (gpgsm->input_cb.fd);
952   _gpgme_io_close (gpgsm->output_cb.fd);
953   _gpgme_io_close (gpgsm->message_cb.fd);
954
955   gpgsm->command = line;
956   return 0;
957 }
958
959
960 GpgmeError
961 _gpgme_gpgsm_op_sign (GpgsmObject gpgsm, GpgmeData in, GpgmeData out,
962                       GpgmeSigMode mode, int use_armor,
963                       int use_textmode, int include_certs,
964                       GpgmeCtx ctx /* FIXME */)
965 {
966   GpgmeError err;
967   char *assuan_cmd;
968
969   if (!gpgsm)
970     return mk_error (Invalid_Value);
971
972   gpgsm->command = xtrystrdup (mode == GPGME_SIG_MODE_DETACH
973                                ? "SIGN --detached" : "SIGN");
974   if (!gpgsm->command)
975     return mk_error (Out_Of_Core);
976
977   if (asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0)
978     return mk_error (Out_Of_Core);
979   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, assuan_cmd, NULL, NULL);
980   free (assuan_cmd);
981   if (err)
982     return err;
983
984   gpgsm->input_cb.data = in;
985   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
986                       map_input_enc (gpgsm->input_cb.data));
987   if (err)
988     return err;
989   gpgsm->output_cb.data = out;
990   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
991                       use_armor ? "--armor" : 0);
992   if (err)
993     return err;
994   _gpgme_io_close (gpgsm->message_cb.fd);
995
996   return 0;
997 }
998
999
1000 GpgmeError
1001 _gpgme_gpgsm_op_trustlist (GpgsmObject gpgsm, const char *pattern)
1002 {
1003   /* FIXME */
1004   return mk_error (Not_Implemented);
1005 }
1006
1007
1008 GpgmeError
1009 _gpgme_gpgsm_op_verify (GpgsmObject gpgsm, GpgmeData sig, GpgmeData text)
1010 {
1011   GpgmeError err;
1012
1013   if (!gpgsm)
1014     return mk_error (Invalid_Value);
1015
1016   gpgsm->command = xtrystrdup ("VERIFY");
1017   if (!gpgsm->command)
1018     return mk_error (Out_Of_Core);
1019
1020   gpgsm->input_cb.data = sig;
1021   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1022                       map_input_enc (gpgsm->input_cb.data));
1023   if (err)
1024     return err;
1025   if (_gpgme_data_get_mode (text) == GPGME_DATA_MODE_IN)
1026     {
1027       /* Normal or cleartext signature.  */
1028       gpgsm->output_cb.data = text;
1029       err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1030                           0);
1031       _gpgme_io_close (gpgsm->message_cb.fd);
1032     }
1033   else
1034     {
1035       /* Detached signature.  */
1036       gpgsm->message_cb.data = text;
1037       err = gpgsm_set_fd (gpgsm->assuan_ctx, "MESSAGE",
1038                           gpgsm->message_fd_server, 0);
1039       _gpgme_io_close (gpgsm->output_cb.fd);
1040     }
1041   if (err)
1042     return err;
1043
1044   return 0;
1045 }
1046
1047
1048 static int
1049 status_cmp (const void *ap, const void *bp)
1050 {
1051   const struct status_table_s *a = ap;
1052   const struct status_table_s *b = bp;
1053
1054   return strcmp (a->name, b->name);
1055 }
1056
1057
1058 static GpgStatusCode
1059 parse_status (const char *name)
1060 {
1061   struct status_table_s t, *r;
1062   t.name = name;
1063   r = bsearch (&t, status_table, DIM(status_table) - 1,
1064                sizeof t, status_cmp);
1065   return r ? r->code : -1;
1066 }
1067
1068
1069 static void
1070 gpgsm_status_handler (void *opaque, int fd)
1071 {
1072   AssuanError err;
1073   GpgsmObject gpgsm = opaque;
1074   char *line;
1075   size_t linelen;
1076
1077   do
1078     {
1079       err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
1080
1081       if (err
1082           || (linelen >= 2
1083               && line[0] == 'O' && line[1] == 'K'
1084               && (line[2] == '\0' || line[2] == ' '))
1085           || (linelen >= 3
1086               && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
1087               && (line[3] == '\0' || line[3] == ' ')))
1088         {
1089           /* XXX: If an error occured, find out what happened, then
1090              save the error value before running the status handler
1091              (so it takes precedence).  */
1092           if (!err && line[0] == 'E' && line[3] == ' ')
1093             {
1094               err = map_assuan_error (atoi (&line[4]));
1095               if (!err)
1096                 err = mk_error (General_Error);
1097             }
1098           if (err)
1099             {
1100               /* XXX Kludge ahead.  We really, really, really must not
1101                  make use of status.fnc_value.  */
1102               GpgmeCtx ctx = (GpgmeCtx) gpgsm->status.fnc_value;
1103               if (!ctx->error)
1104                 ctx->error = err;
1105             }
1106
1107           if (gpgsm->status.fnc)
1108             gpgsm->status.fnc (gpgsm->status.fnc_value, STATUS_EOF, "");
1109
1110           /* XXX: Try our best to terminate the connection.  */
1111           if (err)
1112             assuan_write_line (gpgsm->assuan_ctx, "BYE");
1113
1114           _gpgme_io_close (gpgsm->status_cb.fd);
1115           return;
1116         }
1117
1118       if (linelen > 2
1119           && line[0] == 'D' && line[1] == ' '
1120           && gpgsm->colon.fnc)
1121         {
1122           /* We are using the colon handler even for plain inline data
1123              - strange name for that function but for historic reasons
1124              we keep it.  */
1125           /* FIXME We can't use this for binary data because we
1126              assume this is a string.  For the current usage of colon
1127              output it is correct.  */
1128           unsigned char *src = line + 2;
1129           unsigned char *end = line + linelen;
1130           unsigned char *dst;
1131           unsigned char **aline = &gpgsm->colon.attic.line;
1132           int *alinelen = &gpgsm->colon.attic.linelen;
1133
1134           if (gpgsm->colon.attic.linesize
1135               < *alinelen + linelen + 1)
1136             {
1137               unsigned char *newline = xtryrealloc (*aline,
1138                                                     *alinelen + linelen + 1);
1139               if (!newline)
1140                 {
1141                   _gpgme_io_close (gpgsm->status_cb.fd);
1142                   return;
1143                 }
1144               *aline = newline;
1145               gpgsm->colon.attic.linesize += linelen + 1;
1146             }
1147
1148           dst = *aline + *alinelen;
1149
1150           while (src < end)
1151             {
1152               if (*src == '%' && src + 2 < end)
1153                 {
1154                   /* Handle escaped characters.  */
1155                   ++src;
1156                   *dst = xtoi_2 (src);
1157                   (*alinelen)++;
1158                   src += 2;
1159                 }
1160               else
1161                 {
1162                   *dst = *src++;
1163                   (*alinelen)++;
1164                 }
1165
1166               if (*dst == '\n')
1167                 {
1168                   /* Terminate the pending line, pass it to the colon
1169                      handler and reset it.  */
1170
1171                   if (*alinelen > 1 && *(dst - 1) == '\r')
1172                     dst--;
1173                   *dst = '\0';
1174
1175                   /* FIXME How should we handle the return code? */
1176                   gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline);
1177                   dst = *aline;
1178                   *alinelen = 0;
1179                 }
1180               else
1181                 dst++;
1182             }
1183         }
1184       else if (linelen > 2
1185           && line[0] == 'S' && line[1] == ' ')
1186         {
1187           char *rest;
1188           GpgStatusCode r;
1189
1190           rest = strchr (line + 2, ' ');
1191           if (!rest)
1192             rest = line + linelen; /* set to an empty string */
1193           else
1194             *(rest++) = 0;
1195
1196           r = parse_status (line + 2);
1197
1198           if (r >= 0)
1199             {
1200               if (gpgsm->status.fnc)
1201                 gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest);
1202             }
1203           else
1204             fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
1205         }
1206     }
1207   while (assuan_pending_line (gpgsm->assuan_ctx));
1208 }
1209
1210
1211 void
1212 _gpgme_gpgsm_set_status_handler (GpgsmObject gpgsm,
1213                                  GpgStatusHandler fnc, void *fnc_value) 
1214 {
1215   assert (gpgsm);
1216
1217   gpgsm->status.fnc = fnc;
1218   gpgsm->status.fnc_value = fnc_value;
1219 }
1220
1221
1222 void
1223 _gpgme_gpgsm_set_colon_line_handler (GpgsmObject gpgsm,
1224                                      GpgColonLineHandler fnc, void *fnc_value) 
1225 {
1226   assert (gpgsm);
1227
1228   gpgsm->colon.fnc = fnc;
1229   gpgsm->colon.fnc_value = fnc_value;
1230 }
1231
1232
1233 static GpgmeError
1234 _gpgme_gpgsm_add_io_cb (GpgsmObject gpgsm, iocb_data_t *iocbd,
1235                         GpgmeIOCb handler)
1236 {
1237   GpgmeError err = 0;
1238
1239   iocbd->tag = (*gpgsm->io_cbs.add) (gpgsm->io_cbs.add_priv,
1240                                      iocbd->fd, iocbd->dir,
1241                                      handler, iocbd->data);
1242   if (!iocbd->tag)
1243     err = mk_error (General_Error);
1244   if (!err && !iocbd->dir)
1245     /* FIXME Kludge around poll() problem.  */
1246     err = _gpgme_io_set_nonblocking (iocbd->fd);
1247   return err;
1248 }
1249
1250 GpgmeError
1251 _gpgme_gpgsm_start (GpgsmObject gpgsm, void *opaque)
1252 {
1253   GpgmeError err = 0;
1254   pid_t pid;
1255
1256   if (!gpgsm)
1257     return mk_error (Invalid_Value);
1258
1259   pid = assuan_get_pid (gpgsm->assuan_ctx);
1260
1261   err = _gpgme_gpgsm_add_io_cb (gpgsm, &gpgsm->status_cb,
1262                                 gpgsm_status_handler);
1263   if (gpgsm->input_cb.fd != -1)
1264     err = _gpgme_gpgsm_add_io_cb (gpgsm, &gpgsm->input_cb,
1265                                   _gpgme_data_outbound_handler);
1266   if (!err && gpgsm->output_cb.fd != -1)
1267     err = _gpgme_gpgsm_add_io_cb (gpgsm, &gpgsm->output_cb,
1268                                   _gpgme_data_inbound_handler);
1269   if (!err && gpgsm->message_cb.fd != -1)
1270     err = _gpgme_gpgsm_add_io_cb (gpgsm, &gpgsm->message_cb,
1271                                   _gpgme_data_outbound_handler);
1272
1273   if (!err)
1274     err = assuan_write_line (gpgsm->assuan_ctx, gpgsm->command);
1275
1276   return err;
1277 }
1278
1279 void
1280 _gpgme_gpgsm_set_io_cbs (GpgsmObject gpgsm, struct GpgmeIOCbs *io_cbs)
1281 {
1282   gpgsm->io_cbs = *io_cbs;
1283 }
1284
1285
1286 #else   /* ENABLE_GPGSM */
1287
1288
1289 #include <stddef.h>
1290 #include "util.h"
1291
1292 #include "engine-gpgsm.h"
1293
1294
1295 const char *
1296 _gpgme_gpgsm_get_version (void)
1297 {
1298   return NULL;
1299 }
1300
1301
1302 GpgmeError
1303 _gpgme_gpgsm_check_version (void)
1304 {
1305   return mk_error (Invalid_Engine);
1306 }
1307
1308
1309 GpgmeError
1310 _gpgme_gpgsm_new (GpgsmObject *r_gpgsm)
1311 {
1312   return mk_error (Invalid_Engine);
1313 }
1314
1315
1316 void
1317 _gpgme_gpgsm_release (GpgsmObject gpgsm)
1318 {
1319   return;
1320 }
1321
1322
1323 void
1324 _gpgme_gpgsm_set_status_handler (GpgsmObject gpgsm,
1325                                  GpgStatusHandler fnc, void *fnc_value) 
1326 {
1327   return;
1328 }
1329
1330
1331 GpgmeError
1332 _gpgme_gpgsm_op_decrypt (GpgsmObject gpgsm, GpgmeData ciph, GpgmeData plain)
1333 {
1334   return mk_error (Invalid_Engine);
1335 }
1336
1337
1338 GpgmeError
1339 _gpgme_gpgsm_op_delete (GpgsmObject gpgsm, GpgmeKey key, int allow_secret)
1340 {
1341   return mk_error (Invalid_Engine);
1342 }
1343
1344
1345 GpgmeError
1346 _gpgme_gpgsm_op_encrypt (GpgsmObject gpgsm, GpgmeRecipients recp,
1347                          GpgmeData plain, GpgmeData ciph, int use_armor)
1348 {
1349   return mk_error (Invalid_Engine);
1350 }
1351
1352
1353 GpgmeError
1354 _gpgme_gpgsm_op_export (GpgsmObject gpgsm, GpgmeRecipients recp,
1355                         GpgmeData keydata, int use_armor)
1356 {
1357   return mk_error (Invalid_Engine);
1358 }
1359
1360
1361 GpgmeError
1362 _gpgme_gpgsm_op_genkey (GpgsmObject gpgsm, GpgmeData help_data, int use_armor,
1363                         GpgmeData pubkey, GpgmeData seckey)
1364 {
1365   return mk_error (Invalid_Engine);
1366 }
1367   
1368
1369 GpgmeError
1370 _gpgme_gpgsm_op_import (GpgsmObject gpgsm, GpgmeData keydata)
1371 {
1372   return mk_error (Invalid_Engine);
1373 }
1374
1375
1376 GpgmeError
1377 _gpgme_gpgsm_op_keylist (GpgsmObject gpgsm, const char *pattern,
1378                          int secret_only, int keylist_mode)
1379 {
1380   return mk_error (Invalid_Engine);
1381 }
1382
1383
1384 GpgmeError
1385 _gpgme_gpgsm_op_keylist_ext (GpgsmObject gpgsm, const char *pattern[],
1386                              int secret_only, int reserved, int keylist_mode)
1387 {
1388   return mk_error (Invalid_Engine);
1389 }
1390
1391 GpgmeError
1392 _gpgme_gpgsm_op_sign (GpgsmObject gpgsm, GpgmeData in, GpgmeData out,
1393                       GpgmeSigMode mode, int use_armor,
1394                       int use_textmode, int include_certs,
1395                       GpgmeCtx ctx /* FIXME */)
1396 {
1397   return mk_error (Invalid_Engine);
1398 }
1399
1400
1401 GpgmeError
1402 _gpgme_gpgsm_op_trustlist (GpgsmObject gpgsm, const char *pattern)
1403 {
1404   return mk_error (Invalid_Engine);
1405 }
1406
1407
1408 GpgmeError
1409 _gpgme_gpgsm_op_verify (GpgsmObject gpgsm, GpgmeData sig, GpgmeData text)
1410 {
1411   return mk_error (Invalid_Engine);
1412 }
1413
1414
1415 void
1416 _gpgme_gpgsm_set_colon_line_handler (GpgsmObject gpgsm,
1417                                      GpgColonLineHandler fnc, void *fnc_value) 
1418 {
1419 }
1420
1421
1422 GpgmeError
1423 _gpgme_gpgsm_start (GpgsmObject gpgsm, void *opaque)
1424 {
1425   return mk_error (Invalid_Engine);
1426 }
1427
1428 void _gpgme_gpgsm_set_io_cbs (GpgsmObject gpgsm, GpgmeIOCbs io_cbs)
1429 {
1430 }
1431
1432 #endif  /* ! ENABLE_GPGSM */