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