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