e97a8a59474546ce333ac3641ed9b1b9a0789388
[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     case ASSUAN_Invalid_Index:
308     case ASSUAN_Unexpected_Status:
309     case ASSUAN_Unexpected_Data:
310     case ASSUAN_Invalid_Status:
311     case ASSUAN_Not_Confirmed:
312       return mk_error (General_Error);
313
314       /* These are errors in the server.  */
315     case ASSUAN_Server_Fault:
316     case ASSUAN_Server_Resource_Problem:
317     case ASSUAN_Server_IO_Error:
318     case ASSUAN_Server_Bug:
319     case ASSUAN_No_Agent:
320     case ASSUAN_Agent_Error:
321       return mk_error (Invalid_Engine);  /* XXX:  Need something more useful.  */
322
323     case ASSUAN_Bad_Certificate:
324     case ASSUAN_Bad_Certificate_Path:
325     case ASSUAN_Missing_Certificate:
326     case ASSUAN_No_Public_Key:
327     case ASSUAN_No_Secret_Key:
328     case ASSUAN_Invalid_Name:
329     case ASSUAN_Card_Error:     /* XXX: Oh well.  */
330     case ASSUAN_Invalid_Card:   /* XXX: Oh well.  */
331     case ASSUAN_No_PKCS15_App:  /* XXX: Oh well.  */
332     case ASSUAN_Card_Not_Present:       /* XXX: Oh well.  */
333     case ASSUAN_Invalid_Id:     /* XXX: Oh well.  */
334       return mk_error(Invalid_Key);
335
336     case ASSUAN_Bad_Signature:
337       return mk_error(Invalid_Key);  /* XXX: This is wrong.  */
338
339     case ASSUAN_Cert_Revoked:
340     case ASSUAN_No_CRL_For_Cert:
341     case ASSUAN_CRL_Too_Old:
342     case ASSUAN_Not_Trusted:
343       return mk_error(Invalid_Key);  /* XXX Some more details would be good.  */
344
345     default:
346       return mk_error (General_Error);
347     }
348 }
349
350
351 static GpgmeError
352 gpgsm_assuan_simple_command (ASSUAN_CONTEXT ctx, char *cmd)
353 {
354   AssuanError err;
355   char *line;
356   size_t linelen;
357
358   err = assuan_write_line (ctx, cmd);
359   if (err)
360     return map_assuan_error (err);
361
362   do
363     {
364       err = assuan_read_line (ctx, &line, &linelen);
365       if (err)
366         return map_assuan_error (err);
367     }
368   while (*line == '#' || !linelen);
369   
370   if (linelen >= 2
371       && line[0] == 'O' && line[1] == 'K'
372       && (line[2] == '\0' || line[2] == ' '))
373     return 0;
374
375   if (linelen >= 4
376       && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
377       && line[3] == ' ')
378     err = map_assuan_error (atoi (&line[4]));
379
380   if (!err)
381     err = mk_error (General_Error);
382   return 0;
383 }
384
385
386 #define COMMANDLINELEN 40
387 static GpgmeError
388 gpgsm_set_fd (ASSUAN_CONTEXT ctx, const char *which, int fd, const char *opt)
389 {
390   char line[COMMANDLINELEN];
391
392   if (opt)
393     snprintf (line, COMMANDLINELEN, "%s FD=%i %s", which, fd, opt);
394   else
395     snprintf (line, COMMANDLINELEN, "%s FD=%i", which, fd);
396
397   return gpgsm_assuan_simple_command (ctx, line);
398 }
399
400
401 GpgmeError
402 _gpgme_gpgsm_op_decrypt (GpgsmObject gpgsm, GpgmeData ciph, GpgmeData plain)
403 {
404   GpgmeError err;
405
406   if (!gpgsm)
407     return mk_error (Invalid_Value);
408
409   gpgsm->command = xtrystrdup ("DECRYPT");
410   if (!gpgsm->command)
411     return mk_error (Out_Of_Core);
412
413   gpgsm->input_data = ciph;
414   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server, 0);
415   if (err)
416     return mk_error (General_Error);    /* FIXME */
417   gpgsm->output_data = plain;
418   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server, 0);
419   if (err)
420     return mk_error (General_Error);    /* FIXME */
421   _gpgme_io_close (gpgsm->message_fd);
422
423   return 0;
424 }
425
426
427 GpgmeError
428 _gpgme_gpgsm_op_delete (GpgsmObject gpgsm, GpgmeKey key, int allow_secret)
429 {
430   /* FIXME */
431   return mk_error (Not_Implemented);
432 }
433
434
435 static GpgmeError
436 gpgsm_set_recipients (GpgsmObject gpgsm, GpgmeRecipients recp)
437 {
438   GpgmeError err;
439   ASSUAN_CONTEXT ctx = gpgsm->assuan_ctx;
440   char *line;
441   int linelen;
442   struct user_id_s *r;
443   int valid_recipients = 0;
444
445   linelen = 10 + 40 + 1;        /* "RECIPIENT " + guess + '\0'.  */
446   line = xtrymalloc (10 + 40 + 1);
447   if (!line)
448     return mk_error (Out_Of_Core);
449   strcpy (line, "RECIPIENT ");
450   for (r = recp->list; r; r = r->next)
451     {
452       int newlen = 11 + strlen (r->name);
453       if (linelen < newlen)
454         {
455           char *newline = xtryrealloc (line, newlen);
456           if (! newline)
457             {
458               xfree (line);
459               return mk_error(Out_Of_Core);
460             }
461           line = newline;
462           linelen = newlen;
463         }
464       strcpy (&line[10], r->name);
465       
466       err = gpgsm_assuan_simple_command (ctx, line);
467       if (!err)
468         valid_recipients = 1;
469       else if (err == GPGME_Invalid_Key && gpgsm->status.fnc)
470         {
471           /* FIXME: Include other reasons.  */
472           line[8] = '0';        /* FIXME: Report detailed reason.  */
473           gpgsm->status.fnc (gpgsm->status.fnc_value, STATUS_INV_RECP, &line[8]);
474           line[8] = 'T';
475         }
476       else if (err != GPGME_Invalid_Key)
477         {
478           xfree (line);
479           return err;
480         }
481     }
482   xfree (line);
483   if (!valid_recipients && gpgsm->status.fnc)
484     gpgsm->status.fnc (gpgsm->status.fnc_value, STATUS_NO_RECP, "");
485   return 0;
486 }
487
488
489 GpgmeError
490 _gpgme_gpgsm_op_encrypt (GpgsmObject gpgsm, GpgmeRecipients recp,
491                          GpgmeData plain, GpgmeData ciph, int use_armor)
492 {
493   GpgmeError err;
494
495   if (!gpgsm)
496     return mk_error (Invalid_Value);
497   if (!recp)
498     return mk_error (Not_Implemented);
499
500   gpgsm->command = xtrystrdup ("ENCRYPT");
501   if (!gpgsm->command)
502     return mk_error (Out_Of_Core);
503
504   gpgsm->input_data = plain;
505   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server, 0);
506   if (err)
507     return err;
508   gpgsm->output_data = ciph;
509   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
510                       use_armor ? "--armor" : 0);
511   if (err)
512     return err;
513   _gpgme_io_close (gpgsm->message_fd);
514
515   err = gpgsm_set_recipients (gpgsm, recp);
516   if (err)
517     return err;
518
519   return 0;
520 }
521
522
523 GpgmeError
524 _gpgme_gpgsm_op_export (GpgsmObject gpgsm, GpgmeRecipients recp,
525                         GpgmeData keydata, int use_armor)
526 {
527   /* FIXME */
528   return mk_error (Not_Implemented);
529 }
530
531
532 GpgmeError
533 _gpgme_gpgsm_op_genkey (GpgsmObject gpgsm, GpgmeData help_data, int use_armor,
534                         GpgmeData pubkey, GpgmeData seckey)
535 {
536   GpgmeError err;
537
538   if (!gpgsm || !pubkey || seckey)
539     return mk_error (Invalid_Value);
540
541   gpgsm->command = xtrystrdup ("GENKEY");
542   if (!gpgsm->command)
543     return mk_error (Out_Of_Core);
544
545   gpgsm->input_data = help_data;
546   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server, 0);
547   if (err)
548     return err;
549   gpgsm->output_data = pubkey;
550   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
551                       use_armor ? "--armor" : 0);
552   if (err)
553     return err;
554   _gpgme_io_close (gpgsm->message_fd);
555
556   return 0;
557 }
558
559
560 GpgmeError
561 _gpgme_gpgsm_op_import (GpgsmObject gpgsm, GpgmeData keydata)
562 {
563   GpgmeError err;
564
565   if (!gpgsm)
566     return mk_error (Invalid_Value);
567
568   gpgsm->command = xtrystrdup ("IMPORT");
569   if (!gpgsm->command)
570     return mk_error (Out_Of_Core);
571
572   gpgsm->input_data = keydata;
573   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server, 0);
574   if (err)
575     return err;
576   _gpgme_io_close (gpgsm->output_fd);
577   _gpgme_io_close (gpgsm->message_fd);
578
579   return 0;
580 }
581
582
583 GpgmeError
584 _gpgme_gpgsm_op_keylist (GpgsmObject gpgsm, const char *pattern,
585                          int secret_only, int keylist_mode)
586 {
587   char *line;
588
589   if (!pattern)
590     pattern = "";
591
592   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
593   line = xtrymalloc (15 + strlen (pattern) + 1);
594   if (!line)
595     return mk_error (Out_Of_Core);
596   if (secret_only)
597     {
598       strcpy (line, "LISTSECRETKEYS ");
599       strcpy (&line[15], pattern);
600     }
601   else
602     {
603       strcpy (line, "LISTKEYS ");
604       strcpy (&line[9], pattern);
605     }
606
607   _gpgme_io_close (gpgsm->input_fd);
608   _gpgme_io_close (gpgsm->output_fd);
609   _gpgme_io_close (gpgsm->message_fd);
610
611   gpgsm->command = line;
612   return 0;
613 }
614
615
616 GpgmeError
617 _gpgme_gpgsm_op_keylist_ext (GpgsmObject gpgsm, const char *pattern[],
618                              int secret_only, int reserved, int keylist_mode)
619 {
620   char *line;
621   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
622   int length = 15 + 1;
623   char *linep;
624
625   if (reserved)
626     return mk_error (Invalid_Value);
627
628   if (pattern && *pattern)
629     {
630       const char **pat = pattern;
631
632       while (*pat)
633         {
634           const char *patlet = *pat;
635
636           while (*patlet)
637             {
638               length++;
639               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
640                 length += 2;
641               patlet++;
642             }
643           pat++;
644           /* This will allocate one byte more than necessary.  */
645           length++;
646         }
647     }
648   line = xtrymalloc (length);
649   if (!line)
650     return mk_error (Out_Of_Core);
651   if (secret_only)
652     {
653       strcpy (line, "LISTSECRETKEYS ");
654       linep = &line[15];
655     }
656   else
657     {
658       strcpy (line, "LISTKEYS ");
659       linep = &line[9];
660     }
661
662   if (pattern && *pattern)
663     {
664       while (*pattern)
665         {
666           const char *patlet = *pattern;
667
668           while (*patlet)
669             {
670               switch (*patlet)
671                 {
672                 case '%':
673                   *(linep++) = '%';
674                   *(linep++) = '2';
675                   *(linep++) = '5';
676                   break;
677                 case ' ':
678                   *(linep++) = '%';
679                   *(linep++) = '2';
680                   *(linep++) = '0';
681                   break;
682                 case '+':
683                   *(linep++) = '%';
684                   *(linep++) = '2';
685                   *(linep++) = 'B';
686                   break;
687                 default:
688                   *(linep++) = *patlet;
689                   break;
690                 }
691               patlet++;
692             }
693           pattern++;
694         }
695     }
696   *linep = '\0';
697
698   _gpgme_io_close (gpgsm->input_fd);
699   _gpgme_io_close (gpgsm->output_fd);
700   _gpgme_io_close (gpgsm->message_fd);
701
702   gpgsm->command = line;
703   return 0;
704 }
705
706
707 GpgmeError
708 _gpgme_gpgsm_op_sign (GpgsmObject gpgsm, GpgmeData in, GpgmeData out,
709                       GpgmeSigMode mode, int use_armor,
710                       int use_textmode, int include_certs,
711                       GpgmeCtx ctx /* FIXME */)
712 {
713   GpgmeError err;
714   char *assuan_cmd;
715
716   if (!gpgsm)
717     return mk_error (Invalid_Value);
718
719   gpgsm->command = xtrystrdup (mode == GPGME_SIG_MODE_DETACH
720                                ? "SIGN --detached" : "SIGN");
721   if (!gpgsm->command)
722     return mk_error (Out_Of_Core);
723
724   if (asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0)
725     return mk_error (Out_Of_Core);
726   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, assuan_cmd);
727   free (assuan_cmd);
728   if (err)
729     return err;
730
731   gpgsm->input_data = in;
732   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server, 0);
733   if (err)
734     return err;
735   gpgsm->output_data = out;
736   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
737                       use_armor ? "--armor" : 0);
738   if (err)
739     return err;
740   _gpgme_io_close (gpgsm->message_fd);
741
742   return 0;
743 }
744
745
746 GpgmeError
747 _gpgme_gpgsm_op_trustlist (GpgsmObject gpgsm, const char *pattern)
748 {
749   /* FIXME */
750   return mk_error (Not_Implemented);
751 }
752
753
754 GpgmeError
755 _gpgme_gpgsm_op_verify (GpgsmObject gpgsm, GpgmeData sig, GpgmeData text)
756 {
757   GpgmeError err;
758
759   if (!gpgsm)
760     return mk_error (Invalid_Value);
761
762   gpgsm->command = xtrystrdup ("VERIFY");
763   if (!gpgsm->command)
764     return mk_error (Out_Of_Core);
765
766   gpgsm->input_data = sig;
767   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server, 0);
768   if (err)
769     return err;
770   if (_gpgme_data_get_mode (text) == GPGME_DATA_MODE_IN)
771     {
772       /* Normal or cleartext signature.  */
773       gpgsm->output_data = text;
774       err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
775                           0);
776     }
777   else
778     {
779       /* Detached signature.  */
780       gpgsm->message_data = text;
781       err = gpgsm_set_fd (gpgsm->assuan_ctx, "MESSAGE",
782                           gpgsm->message_fd_server, 0);
783     }
784   if (err)
785     return err;
786   _gpgme_io_close (gpgsm->output_fd);
787
788   return 0;
789 }
790
791
792 static int
793 status_cmp (const void *ap, const void *bp)
794 {
795   const struct status_table_s *a = ap;
796   const struct status_table_s *b = bp;
797
798   return strcmp (a->name, b->name);
799 }
800
801
802 static int
803 gpgsm_status_handler (void *opaque, int pid, int fd)
804 {
805   AssuanError err;
806   GpgsmObject gpgsm = opaque;
807   char *line;
808   size_t linelen;
809
810   do
811     {
812       err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
813
814       if (err
815           || (linelen >= 2
816               && line[0] == 'O' && line[1] == 'K'
817               && (line[2] == '\0' || line[2] == ' '))
818           || (linelen >= 3
819               && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
820               && (line[3] == '\0' || line[3] == ' ')))
821         {
822           /* XXX: If an error occured, find out what happened, then save the error value
823              before running the status handler (so it takes precedence).  */
824           if (!err && line[0] == 'E' && line[3] == ' ')
825             {
826               err = map_assuan_error (atoi (&line[4]));
827               if (!err)
828                 err = mk_error (General_Error);
829             }
830           if (err)
831             {
832               /* XXX Kludge ahead.  We really, really, really must not
833                  make use of status.fnc_value.  */
834               GpgmeCtx ctx = (GpgmeCtx) gpgsm->status.fnc_value;
835               if (!ctx->error)
836                 ctx->error = err;
837             }
838
839           if (gpgsm->status.fnc)
840             gpgsm->status.fnc (gpgsm->status.fnc_value, STATUS_EOF, "");
841
842           /* XXX: Try our best to terminate the connection.  */
843           if (err)
844             assuan_write_line (gpgsm->assuan_ctx, "BYE");
845
846           return 1;
847         }
848
849       if (linelen > 2
850           && line[0] == 'D' && line[1] == ' '
851           && gpgsm->colon.fnc)
852         {
853           /* We are using the colon handler even for plain inline data
854              - strange name for that function but for historic reasons
855              we keep it.  */
856           /* FIXME We can't use this for binary data because we
857              assume this is a string.  For the current usage of colon
858              output it is correct.  */
859           unsigned char *src = line + 2;
860           unsigned char *end = line + linelen;
861           unsigned char *dst;
862           unsigned char **aline = &gpgsm->colon.attic.line;
863           int *alinelen = &gpgsm->colon.attic.linelen;
864
865           if (gpgsm->colon.attic.linesize
866               < *alinelen + linelen + 1)
867             {
868               unsigned char *newline = xtryrealloc (*aline,
869                                                     *alinelen + linelen + 1);
870               if (!newline)
871                 return mk_error (Out_Of_Core);
872               *aline = newline;
873               gpgsm->colon.attic.linesize += linelen + 1;
874             }
875
876           dst = *aline + *alinelen;
877
878           while (src < end)
879             {
880               if (*src == '%' && src + 2 < end)
881                 {
882                   /* Handle escaped characters.  */
883                   ++src;
884                   *dst = xtoi_2 (src);
885                   (*alinelen)++;
886                   src += 2;
887                 }
888               else
889                 {
890                   *dst = *src++;
891                   (*alinelen)++;
892                 }
893
894               if (*dst == '\n')
895                 {
896                   /* Terminate the pending line, pass it to the colon
897                      handler and reset it.  */
898
899                   if (*alinelen > 1 && *(dst - 1) == '\r')
900                     dst--;
901                   *dst = '\0';
902
903                   /* FIXME How should we handle the return code? */
904                   gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline);
905                   dst = *aline;
906                   *alinelen = 0;
907                 }
908               else
909                 dst++;
910             }
911         }
912       else if (linelen > 2
913           && line[0] == 'S' && line[1] == ' ')
914         {
915           struct status_table_s t, *r;
916           char *rest;
917           
918           rest = strchr (line + 2, ' ');
919           if (!rest)
920             rest = line + linelen; /* set to an empty string */
921           else
922             *rest++ = 0;
923
924           t.name = line + 2;
925           r = bsearch (&t, status_table, DIM(status_table) - 1,
926                        sizeof t, status_cmp);
927
928           if (r)
929             {
930               if (gpgsm->status.fnc)
931                 gpgsm->status.fnc (gpgsm->status.fnc_value, r->code, rest);
932             }
933           else
934             fprintf (stderr, "[UNKNOWN STATUS]%s %s", t.name, rest);
935         }
936     }
937   while (assuan_pending_line (gpgsm->assuan_ctx));
938   
939   return 0;
940 }
941
942
943 void
944 _gpgme_gpgsm_set_status_handler (GpgsmObject gpgsm,
945                                  GpgStatusHandler fnc, void *fnc_value) 
946 {
947   assert (gpgsm);
948
949   gpgsm->status.fnc = fnc;
950   gpgsm->status.fnc_value = fnc_value;
951 }
952
953
954 void
955 _gpgme_gpgsm_set_colon_line_handler (GpgsmObject gpgsm,
956                                      GpgColonLineHandler fnc, void *fnc_value) 
957 {
958   assert (gpgsm);
959
960   gpgsm->colon.fnc = fnc;
961   gpgsm->colon.fnc_value = fnc_value;
962 }
963
964
965 GpgmeError
966 _gpgme_gpgsm_start (GpgsmObject gpgsm, void *opaque)
967 {
968   GpgmeError err = 0;
969   pid_t pid;
970   int fdlist[5];
971   int nfds;
972
973   if (!gpgsm)
974     return mk_error (Invalid_Value);
975
976   pid = assuan_get_pid (gpgsm->assuan_ctx);
977
978   /* We need to know the fd used by assuan for reads.  We do this by
979      using the assumption that the first returned fd from
980      assuan_get_active_fds() is always this one. */
981   nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */,
982                                 fdlist, DIM (fdlist));
983   if (nfds < 1)
984     return mk_error (General_Error);  /* FIXME */
985   err = _gpgme_register_pipe_handler (opaque, gpgsm_status_handler, gpgsm, pid,
986                                       fdlist[0], 1);
987
988
989   if (gpgsm->input_fd != -1)
990     {
991       err = _gpgme_register_pipe_handler (opaque, _gpgme_data_outbound_handler,
992                                           gpgsm->input_data, pid,
993                                           gpgsm->input_fd, 0);
994       if (!err) /* FIXME Kludge around poll() problem.  */
995         err = _gpgme_io_set_nonblocking (gpgsm->input_fd);
996     }
997   if (!err && gpgsm->output_fd != -1)
998     err = _gpgme_register_pipe_handler (opaque, _gpgme_data_inbound_handler,
999                                         gpgsm->output_data, pid,
1000                                         gpgsm->output_fd, 1);
1001   if (!err && gpgsm->message_fd != -1)
1002     {
1003       err = _gpgme_register_pipe_handler (opaque, _gpgme_data_outbound_handler,
1004                                           gpgsm->message_data, pid,
1005                                           gpgsm->message_fd, 0);
1006       if (!err) /* FIXME Kludge around poll() problem.  */
1007         err = _gpgme_io_set_nonblocking (gpgsm->message_fd);
1008     }
1009
1010   if (!err)
1011     err = assuan_write_line (gpgsm->assuan_ctx, gpgsm->command);
1012
1013   return err;
1014 }
1015
1016
1017 #else   /* ENABLE_GPGSM */
1018
1019
1020 #include <stddef.h>
1021 #include "util.h"
1022
1023 #include "engine-gpgsm.h"
1024
1025
1026 const char *
1027 _gpgme_gpgsm_get_version (void)
1028 {
1029   return NULL;
1030 }
1031
1032
1033 GpgmeError
1034 _gpgme_gpgsm_check_version (void)
1035 {
1036   return mk_error (Invalid_Engine);
1037 }
1038
1039
1040 GpgmeError
1041 _gpgme_gpgsm_new (GpgsmObject *r_gpgsm)
1042 {
1043   return mk_error (Invalid_Engine);
1044 }
1045
1046
1047 void
1048 _gpgme_gpgsm_release (GpgsmObject gpgsm)
1049 {
1050   return;
1051 }
1052
1053
1054 void
1055 _gpgme_gpgsm_set_status_handler (GpgsmObject gpgsm,
1056                                  GpgStatusHandler fnc, void *fnc_value) 
1057 {
1058   return;
1059 }
1060
1061
1062 GpgmeError
1063 _gpgme_gpgsm_op_decrypt (GpgsmObject gpgsm, GpgmeData ciph, GpgmeData plain)
1064 {
1065   return mk_error (Invalid_Engine);
1066 }
1067
1068
1069 GpgmeError
1070 _gpgme_gpgsm_op_delete (GpgsmObject gpgsm, GpgmeKey key, int allow_secret)
1071 {
1072   return mk_error (Invalid_Engine);
1073 }
1074
1075
1076 GpgmeError
1077 _gpgme_gpgsm_op_encrypt (GpgsmObject gpgsm, GpgmeRecipients recp,
1078                          GpgmeData plain, GpgmeData ciph, int use_armor)
1079 {
1080   return mk_error (Invalid_Engine);
1081 }
1082
1083
1084 GpgmeError
1085 _gpgme_gpgsm_op_export (GpgsmObject gpgsm, GpgmeRecipients recp,
1086                         GpgmeData keydata, int use_armor)
1087 {
1088   return mk_error (Invalid_Engine);
1089 }
1090
1091
1092 GpgmeError
1093 _gpgme_gpgsm_op_genkey (GpgsmObject gpgsm, GpgmeData help_data, int use_armor,
1094                         GpgmeData pubkey, GpgmeData seckey)
1095 {
1096   return mk_error (Invalid_Engine);
1097 }
1098   
1099
1100 GpgmeError
1101 _gpgme_gpgsm_op_import (GpgsmObject gpgsm, GpgmeData keydata)
1102 {
1103   return mk_error (Invalid_Engine);
1104 }
1105
1106
1107 GpgmeError
1108 _gpgme_gpgsm_op_keylist (GpgsmObject gpgsm, const char *pattern,
1109                          int secret_only, int keylist_mode)
1110 {
1111   return mk_error (Invalid_Engine);
1112 }
1113
1114
1115 GpgmeError
1116 _gpgme_gpgsm_op_keylist_ext (GpgsmObject gpgsm, const char *pattern[],
1117                              int secret_only, int reserved, int keylist_mode)
1118 {
1119   return mk_error (Invalid_Engine);
1120 }
1121
1122 GpgmeError
1123 _gpgme_gpgsm_op_sign (GpgsmObject gpgsm, GpgmeData in, GpgmeData out,
1124                       GpgmeSigMode mode, int use_armor,
1125                       int use_textmode, int include_certs,
1126                       GpgmeCtx ctx /* FIXME */)
1127 {
1128   return mk_error (Invalid_Engine);
1129 }
1130
1131
1132 GpgmeError
1133 _gpgme_gpgsm_op_trustlist (GpgsmObject gpgsm, const char *pattern)
1134 {
1135   return mk_error (Invalid_Engine);
1136 }
1137
1138
1139 GpgmeError
1140 _gpgme_gpgsm_op_verify (GpgsmObject gpgsm, GpgmeData sig, GpgmeData text)
1141 {
1142   return mk_error (Invalid_Engine);
1143 }
1144
1145
1146 void
1147 _gpgme_gpgsm_set_colon_line_handler (GpgsmObject gpgsm,
1148                                      GpgColonLineHandler fnc, void *fnc_value) 
1149 {
1150 }
1151
1152
1153 GpgmeError
1154 _gpgme_gpgsm_start (GpgsmObject gpgsm, void *opaque)
1155 {
1156   return mk_error (Invalid_Engine);
1157 }
1158
1159
1160 #endif  /* ! ENABLE_GPGSM */