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