1 /* engine-gpgsm.c - GpgSM engine.
2 Copyright (C) 2000 Werner Koch (dd9jn)
3 Copyright (C) 2001, 2002 g10 Code GmbH
5 This file is part of GPGME.
7 GPGME is free software; you can redistribute it and/or modify it
8 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.
12 GPGME is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GPGME; if not, write to the Free Software Foundation,
19 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
27 #include <sys/types.h>
31 #include <fcntl.h> /* FIXME */
43 #include "status-table.h"
45 #include "engine-backend.h"
48 #define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \
49 *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
50 #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1))
55 int fd; /* FD we talk about. */
56 int dir; /* Inbound/Outbound, maybe given implicit? */
57 void *data; /* Handler-specific data. */
58 void *tag; /* ID from the user for gpgme_remove_io_callback. */
64 ASSUAN_CONTEXT assuan_ctx;
66 iocb_data_t status_cb;
68 /* Input, output etc are from the servers perspective. */
72 iocb_data_t output_cb;
75 iocb_data_t message_cb;
76 int message_fd_server;
82 GpgmeStatusHandler fnc;
88 GpgmeColonLineHandler fnc;
96 int any; /* any data line seen */
99 struct GpgmeIOCbs io_cbs;
104 gpgsm_get_version (void)
106 static const char *gpgsm_version;
107 DEFINE_STATIC_LOCK (gpgsm_version_lock);
109 LOCK (gpgsm_version_lock);
111 gpgsm_version = _gpgme_get_program_version (_gpgme_get_gpgsm_path ());
112 UNLOCK (gpgsm_version_lock);
114 return gpgsm_version;
119 gpgsm_check_version (void)
121 return _gpgme_compare_versions (gpgsm_get_version (), NEED_GPGSM_VERSION)
122 ? 0 : mk_error (Invalid_Engine);
127 close_notify_handler (int fd, void *opaque)
129 GpgsmObject gpgsm = opaque;
130 int possibly_done = 0;
133 if (gpgsm->status_cb.fd == fd)
135 if (gpgsm->status_cb.tag)
137 (*gpgsm->io_cbs.remove) (gpgsm->status_cb.tag);
140 gpgsm->status_cb.fd = -1;
142 else if (gpgsm->input_cb.fd == fd)
144 if (gpgsm->input_cb.tag)
146 (*gpgsm->io_cbs.remove) (gpgsm->input_cb.tag);
149 gpgsm->input_cb.fd = -1;
151 else if (gpgsm->output_cb.fd == fd)
153 if (gpgsm->output_cb.tag)
155 (*gpgsm->io_cbs.remove) (gpgsm->output_cb.tag);
158 gpgsm->output_cb.fd = -1;
160 else if (gpgsm->message_cb.fd == fd)
162 if (gpgsm->message_cb.tag)
164 (*gpgsm->io_cbs.remove) (gpgsm->message_cb.tag);
167 gpgsm->message_cb.fd = -1;
169 if (possibly_done && gpgsm->io_cbs.event
170 && gpgsm->status_cb.fd == -1 && gpgsm->input_cb.fd == -1
171 && gpgsm->output_cb.fd == -1 && gpgsm->message_cb.fd == -1)
172 (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, GPGME_EVENT_DONE, NULL);
177 map_assuan_error (AssuanError err)
181 case ASSUAN_No_Error:
182 return mk_error (No_Error);
183 case ASSUAN_General_Error:
184 return mk_error (General_Error);
185 case ASSUAN_Out_Of_Core:
186 return mk_error (Out_Of_Core);
187 case ASSUAN_Invalid_Value:
188 return mk_error (Invalid_Value);
189 case ASSUAN_Read_Error:
190 return mk_error (Read_Error);
191 case ASSUAN_Write_Error:
192 return mk_error (Write_Error);
195 case ASSUAN_Problem_Starting_Server:
196 case ASSUAN_Not_A_Server:
197 case ASSUAN_Not_A_Client:
198 case ASSUAN_Nested_Commands:
199 case ASSUAN_Invalid_Response:
200 case ASSUAN_No_Data_Callback:
201 case ASSUAN_No_Inquire_Callback:
202 case ASSUAN_Connect_Failed:
203 case ASSUAN_Accept_Failed:
204 return mk_error (General_Error);
206 /* The following error codes are meant as status codes. */
207 case ASSUAN_Not_Implemented:
208 return mk_error (Not_Implemented);
209 case ASSUAN_Canceled:
210 return mk_error (Canceled);
211 case ASSUAN_Unsupported_Algorithm:
212 return mk_error (Not_Implemented); /* XXX Argh. */
214 case ASSUAN_No_Data_Available:
215 return mk_error (EOF);
217 /* These are errors internal to GPGME. */
218 case ASSUAN_No_Input:
219 case ASSUAN_No_Output:
220 case ASSUAN_Invalid_Command:
221 case ASSUAN_Unknown_Command:
222 case ASSUAN_Syntax_Error:
223 case ASSUAN_Parameter_Error:
224 case ASSUAN_Parameter_Conflict:
225 case ASSUAN_Line_Too_Long:
226 case ASSUAN_Line_Not_Terminated:
227 case ASSUAN_Invalid_Data:
228 case ASSUAN_Unexpected_Command:
229 case ASSUAN_Too_Much_Data:
230 case ASSUAN_Inquire_Unknown:
231 case ASSUAN_Inquire_Error:
232 case ASSUAN_Invalid_Option:
233 case ASSUAN_Invalid_Index:
234 case ASSUAN_Unexpected_Status:
235 case ASSUAN_Unexpected_Data:
236 case ASSUAN_Invalid_Status:
237 case ASSUAN_Not_Confirmed:
238 return mk_error (General_Error);
240 /* These are errors in the server. */
241 case ASSUAN_Server_Fault:
242 case ASSUAN_Server_Resource_Problem:
243 case ASSUAN_Server_IO_Error:
244 case ASSUAN_Server_Bug:
245 case ASSUAN_No_Agent:
246 case ASSUAN_Agent_Error:
247 return mk_error (Invalid_Engine); /* XXX: Need something more useful. */
249 case ASSUAN_Bad_Certificate:
250 case ASSUAN_Bad_Certificate_Path:
251 case ASSUAN_Missing_Certificate:
252 case ASSUAN_No_Public_Key:
253 case ASSUAN_No_Secret_Key:
254 case ASSUAN_Invalid_Name:
255 case ASSUAN_Card_Error: /* XXX: Oh well. */
256 case ASSUAN_Invalid_Card: /* XXX: Oh well. */
257 case ASSUAN_No_PKCS15_App: /* XXX: Oh well. */
258 case ASSUAN_Card_Not_Present: /* XXX: Oh well. */
259 case ASSUAN_Invalid_Id: /* XXX: Oh well. */
260 return mk_error (Invalid_Key);
262 case ASSUAN_Bad_Signature:
263 return mk_error (Invalid_Key); /* XXX: This is wrong. */
265 case ASSUAN_Cert_Revoked:
266 case ASSUAN_No_CRL_For_Cert:
267 case ASSUAN_CRL_Too_Old:
268 case ASSUAN_Not_Trusted:
269 return mk_error (Invalid_Key); /* XXX Some more details would be good. */
272 return mk_error (General_Error);
278 gpgsm_release (void *engine)
280 GpgsmObject gpgsm = engine;
285 if (gpgsm->status_cb.fd != -1)
286 _gpgme_io_close (gpgsm->status_cb.fd);
287 if (gpgsm->input_cb.fd != -1)
288 _gpgme_io_close (gpgsm->input_cb.fd);
289 if (gpgsm->output_cb.fd != -1)
290 _gpgme_io_close (gpgsm->output_cb.fd);
291 if (gpgsm->message_cb.fd != -1)
292 _gpgme_io_close (gpgsm->message_cb.fd);
294 assuan_disconnect (gpgsm->assuan_ctx);
296 free (gpgsm->colon.attic.line);
297 free (gpgsm->command);
303 gpgsm_new (void **engine)
310 char *dft_display = NULL;
311 char *dft_ttyname = NULL;
312 char *dft_ttytype = NULL;
319 gpgsm = calloc (1, sizeof *gpgsm);
322 err = mk_error (Out_Of_Core);
326 gpgsm->status_cb.fd = -1;
327 gpgsm->status_cb.tag = 0;
329 gpgsm->input_cb.fd = -1;
330 gpgsm->input_cb.tag = 0;
331 gpgsm->input_fd_server = -1;
332 gpgsm->output_cb.fd = -1;
333 gpgsm->output_cb.tag = 0;
334 gpgsm->output_fd_server = -1;
335 gpgsm->message_cb.fd = -1;
336 gpgsm->message_cb.tag = 0;
337 gpgsm->message_fd_server = -1;
339 gpgsm->status.fnc = 0;
340 gpgsm->colon.fnc = 0;
341 gpgsm->colon.attic.line = 0;
342 gpgsm->colon.attic.linesize = 0;
343 gpgsm->colon.attic.linelen = 0;
344 gpgsm->colon.any = 0;
346 gpgsm->io_cbs.add = NULL;
347 gpgsm->io_cbs.add_priv = NULL;
348 gpgsm->io_cbs.remove = NULL;
349 gpgsm->io_cbs.event = NULL;
350 gpgsm->io_cbs.event_priv = NULL;
352 if (_gpgme_io_pipe (fds, 0) < 0)
354 err = mk_error (Pipe_Error);
357 gpgsm->input_cb.fd = fds[1];
358 gpgsm->input_cb.dir = 0;
359 gpgsm->input_fd_server = fds[0];
361 if (_gpgme_io_pipe (fds, 1) < 0)
363 err = mk_error (Pipe_Error);
366 gpgsm->output_cb.fd = fds[0];
367 gpgsm->output_cb.dir = 1;
368 gpgsm->output_fd_server = fds[1];
370 if (_gpgme_io_pipe (fds, 0) < 0)
372 err = mk_error (Pipe_Error);
375 gpgsm->message_cb.fd = fds[1];
376 gpgsm->message_cb.dir = 0;
377 gpgsm->message_fd_server = fds[0];
379 child_fds[0] = gpgsm->input_fd_server;
380 child_fds[1] = gpgsm->output_fd_server;
381 child_fds[2] = gpgsm->message_fd_server;
385 argv[1] = "--server";
388 err = assuan_pipe_connect2 (&gpgsm->assuan_ctx,
389 _gpgme_get_gpgsm_path (), argv, child_fds,
390 1 /* dup stderr to /dev/null */);
392 /* We need to know the fd used by assuan for reads. We do this by
393 using the assumption that the first returned fd from
394 assuan_get_active_fds() is always this one. */
395 nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */,
396 fdlist, DIM (fdlist));
399 err = mk_error (General_Error); /* FIXME */
402 /* We duplicate the file descriptor, so we can close it without
403 disturbing assuan. Alternatively, we could special case
404 status_fd and register/unregister it manually as needed, but this
405 increases code duplication and is more complicated as we can not
406 use the close notifications etc. */
407 gpgsm->status_cb.fd = dup (fdlist[0]);
408 if (gpgsm->status_cb.fd < 0)
410 err = mk_error (General_Error); /* FIXME */
413 gpgsm->status_cb.dir = 1;
414 gpgsm->status_cb.data = gpgsm;
416 dft_display = getenv ("DISPLAY");
419 if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
421 err = mk_error (Out_Of_Core);
424 err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
429 err = map_assuan_error (err);
433 dft_ttyname = ttyname (1);
436 if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
438 err = mk_error (Out_Of_Core);
441 err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
446 err = map_assuan_error (err);
450 dft_ttytype = getenv ("TERM");
453 if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
455 err = mk_error (Out_Of_Core);
458 err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
463 err = map_assuan_error (err);
468 old_lc = setlocale (LC_CTYPE, NULL);
471 old_lc = strdup (old_lc);
474 err = GPGME_Out_Of_Core;
478 dft_lc = setlocale (LC_CTYPE, "");
481 if (asprintf (&optstr, "OPTION lc-ctype=%s", dft_lc) < 0)
482 err = mk_error (Out_Of_Core);
485 err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
486 NULL, NULL, NULL, NULL);
489 err = map_assuan_error (err);
494 setlocale (LC_CTYPE, old_lc);
501 old_lc = setlocale (LC_MESSAGES, NULL);
504 old_lc = strdup (old_lc);
507 err = GPGME_Out_Of_Core;
511 dft_lc = setlocale (LC_MESSAGES, "");
514 if (asprintf (&optstr, "OPTION lc-messages=%s", dft_lc) < 0)
515 err = mk_error (Out_Of_Core);
518 err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
522 err = map_assuan_error (err);
527 setlocale (LC_MESSAGES, old_lc);
535 (_gpgme_io_set_close_notify (gpgsm->status_cb.fd,
536 close_notify_handler, gpgsm)
537 || _gpgme_io_set_close_notify (gpgsm->input_cb.fd,
538 close_notify_handler, gpgsm)
539 || _gpgme_io_set_close_notify (gpgsm->output_cb.fd,
540 close_notify_handler, gpgsm)
541 || _gpgme_io_set_close_notify (gpgsm->message_cb.fd,
542 close_notify_handler, gpgsm)))
544 err = mk_error (General_Error);
549 /* Close the server ends of the pipes. Our ends are closed in
551 if (gpgsm->input_fd_server != -1)
552 _gpgme_io_close (gpgsm->input_fd_server);
553 if (gpgsm->output_fd_server != -1)
554 _gpgme_io_close (gpgsm->output_fd_server);
555 if (gpgsm->message_fd_server != -1)
556 _gpgme_io_close (gpgsm->message_fd_server);
559 gpgsm_release (gpgsm);
567 /* Forward declaration. */
568 static GpgmeStatusCode parse_status (const char *name);
571 gpgsm_assuan_simple_command (ASSUAN_CONTEXT ctx, char *cmd, GpgmeStatusHandler status_fnc,
572 void *status_fnc_value)
578 err = assuan_write_line (ctx, cmd);
580 return map_assuan_error (err);
584 err = assuan_read_line (ctx, &line, &linelen);
586 return map_assuan_error (err);
588 if (*line == '#' || !linelen)
592 && line[0] == 'O' && line[1] == 'K'
593 && (line[2] == '\0' || line[2] == ' '))
595 else if (linelen >= 4
596 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
598 err = map_assuan_error (atoi (&line[4]));
599 else if (linelen >= 2
600 && line[0] == 'S' && line[1] == ' ')
605 rest = strchr (line + 2, ' ');
607 rest = line + linelen; /* set to an empty string */
611 r = parse_status (line + 2);
613 if (r >= 0 && status_fnc)
614 status_fnc (status_fnc_value, r, rest);
616 err = mk_error (General_Error);
619 err = mk_error (General_Error);
627 #define COMMANDLINELEN 40
629 gpgsm_set_fd (ASSUAN_CONTEXT ctx, const char *which, int fd, const char *opt)
631 char line[COMMANDLINELEN];
634 snprintf (line, COMMANDLINELEN, "%s FD=%i %s", which, fd, opt);
636 snprintf (line, COMMANDLINELEN, "%s FD=%i", which, fd);
638 return gpgsm_assuan_simple_command (ctx, line, NULL, NULL);
643 map_input_enc (GpgmeData d)
645 switch (gpgme_data_get_encoding (d))
647 case GPGME_DATA_ENCODING_NONE:
649 case GPGME_DATA_ENCODING_BINARY:
651 case GPGME_DATA_ENCODING_BASE64:
653 case GPGME_DATA_ENCODING_ARMOR:
663 gpgsm_decrypt (void *engine, GpgmeData ciph, GpgmeData plain)
665 GpgsmObject gpgsm = engine;
669 return mk_error (Invalid_Value);
671 gpgsm->command = strdup ("DECRYPT");
673 return mk_error (Out_Of_Core);
675 gpgsm->input_cb.data = ciph;
676 err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
677 map_input_enc (gpgsm->input_cb.data));
679 return mk_error (General_Error); /* FIXME */
680 gpgsm->output_cb.data = plain;
681 err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server, 0);
683 return mk_error (General_Error); /* FIXME */
684 _gpgme_io_close (gpgsm->message_cb.fd);
691 gpgsm_delete (void *engine, GpgmeKey key, int allow_secret)
693 GpgsmObject gpgsm = engine;
694 char *fpr = (char *) gpgme_key_get_string_attr (key, GPGME_ATTR_FPR, NULL, 0);
697 int length = 8; /* "DELKEYS " */
700 return mk_error (Invalid_Key);
705 if (*linep == '%' || *linep == ' ' || *linep == '+')
711 line = malloc (length);
713 return mk_error (Out_Of_Core);
715 strcpy (line, "DELKEYS ");
745 gpgsm->command = line;
746 _gpgme_io_close (gpgsm->output_cb.fd);
747 _gpgme_io_close (gpgsm->input_cb.fd);
748 _gpgme_io_close (gpgsm->message_cb.fd);
755 set_recipients (GpgsmObject gpgsm, GpgmeRecipients recp)
758 ASSUAN_CONTEXT ctx = gpgsm->assuan_ctx;
762 int valid_recipients = 0;
764 linelen = 10 + 40 + 1; /* "RECIPIENT " + guess + '\0'. */
765 line = malloc (10 + 40 + 1);
767 return mk_error (Out_Of_Core);
768 strcpy (line, "RECIPIENT ");
769 for (r = recp->list; r; r = r->next)
771 int newlen = 11 + strlen (r->name);
772 if (linelen < newlen)
774 char *newline = realloc (line, newlen);
778 return mk_error (Out_Of_Core);
783 strcpy (&line[10], r->name);
785 err = gpgsm_assuan_simple_command (ctx, line, gpgsm->status.fnc,
786 gpgsm->status.fnc_value);
788 valid_recipients = 1;
789 else if (err != GPGME_Invalid_Key)
796 if (!valid_recipients && gpgsm->status.fnc)
797 gpgsm->status.fnc (gpgsm->status.fnc_value, GPGME_STATUS_NO_RECP, "");
803 gpgsm_encrypt (void *engine, GpgmeRecipients recp, GpgmeData plain,
804 GpgmeData ciph, int use_armor)
806 GpgsmObject gpgsm = engine;
810 return mk_error (Invalid_Value);
812 return mk_error (Not_Implemented);
814 gpgsm->command = strdup ("ENCRYPT");
816 return mk_error (Out_Of_Core);
818 gpgsm->input_cb.data = plain;
819 err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
820 map_input_enc (gpgsm->input_cb.data));
823 gpgsm->output_cb.data = ciph;
824 err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
825 use_armor ? "--armor" : 0);
828 _gpgme_io_close (gpgsm->message_cb.fd);
830 err = set_recipients (gpgsm, recp);
839 gpgsm_export (void *engine, GpgmeRecipients recp, GpgmeData keydata,
842 GpgsmObject gpgsm = engine;
849 return mk_error (Invalid_Value);
851 cmd = malloc (cmdlen);
853 return mk_error (Out_Of_Core);
854 strcpy (cmd, "EXPORT");
862 err = gpgme_recipients_enum_open (recp, &ec);
863 while (!err && (s = gpgme_recipients_enum_read (recp, &ec)))
865 int slen = strlen (s);
866 /* New string is old string + ' ' + s + '\0'. */
867 if (cmdlen < cmdi + 1 + slen + 1)
869 char *newcmd = realloc (cmd, cmdlen * 2);
873 return mk_error (Out_Of_Core);
879 strcpy (cmd + cmdi, s);
883 err = gpgme_recipients_enum_close (recp, &ec);
888 gpgsm->command = cmd;
890 gpgsm->output_cb.data = keydata;
891 err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
892 use_armor ? "--armor" : 0);
895 _gpgme_io_close (gpgsm->input_cb.fd);
896 _gpgme_io_close (gpgsm->message_cb.fd);
903 gpgsm_genkey (void *engine, GpgmeData help_data, int use_armor,
904 GpgmeData pubkey, GpgmeData seckey)
906 GpgsmObject gpgsm = engine;
909 if (!gpgsm || !pubkey || seckey)
910 return mk_error (Invalid_Value);
912 gpgsm->command = strdup ("GENKEY");
914 return mk_error (Out_Of_Core);
916 gpgsm->input_cb.data = help_data;
917 err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
918 map_input_enc (gpgsm->input_cb.data));
921 gpgsm->output_cb.data = pubkey;
922 err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
923 use_armor ? "--armor" : 0);
926 _gpgme_io_close (gpgsm->message_cb.fd);
933 gpgsm_import (void *engine, GpgmeData keydata)
935 GpgsmObject gpgsm = engine;
939 return mk_error (Invalid_Value);
941 gpgsm->command = strdup ("IMPORT");
943 return mk_error (Out_Of_Core);
945 gpgsm->input_cb.data = keydata;
946 err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
947 map_input_enc (gpgsm->input_cb.data));
950 _gpgme_io_close (gpgsm->output_cb.fd);
951 _gpgme_io_close (gpgsm->message_cb.fd);
958 gpgsm_keylist (void *engine, const char *pattern, int secret_only,
961 GpgsmObject gpgsm = engine;
968 if (asprintf (&line, "OPTION list-mode=%d", (keylist_mode & 3)) < 0)
969 return mk_error (Out_Of_Core);
970 err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
975 /* Length is "LISTSECRETKEYS " + p + '\0'. */
976 line = malloc (15 + strlen (pattern) + 1);
978 return mk_error (Out_Of_Core);
981 strcpy (line, "LISTSECRETKEYS ");
982 strcpy (&line[15], pattern);
986 strcpy (line, "LISTKEYS ");
987 strcpy (&line[9], pattern);
990 _gpgme_io_close (gpgsm->input_cb.fd);
991 _gpgme_io_close (gpgsm->output_cb.fd);
992 _gpgme_io_close (gpgsm->message_cb.fd);
994 gpgsm->command = line;
1000 gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
1001 int reserved, int keylist_mode)
1003 GpgsmObject gpgsm = engine;
1006 /* Length is "LISTSECRETKEYS " + p + '\0'. */
1007 int length = 15 + 1;
1011 return mk_error (Invalid_Value);
1013 if (asprintf (&line, "OPTION list-mode=%d", (keylist_mode & 3)) < 0)
1014 return mk_error (Out_Of_Core);
1015 err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
1020 if (pattern && *pattern)
1022 const char **pat = pattern;
1026 const char *patlet = *pat;
1031 if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
1036 /* This will allocate one byte more than necessary. */
1040 line = malloc (length);
1042 return mk_error (Out_Of_Core);
1045 strcpy (line, "LISTSECRETKEYS ");
1050 strcpy (line, "LISTKEYS ");
1054 if (pattern && *pattern)
1058 const char *patlet = *pattern;
1080 *(linep++) = *patlet;
1090 _gpgme_io_close (gpgsm->input_cb.fd);
1091 _gpgme_io_close (gpgsm->output_cb.fd);
1092 _gpgme_io_close (gpgsm->message_cb.fd);
1094 gpgsm->command = line;
1100 gpgsm_sign (void *engine, GpgmeData in, GpgmeData out, GpgmeSigMode mode,
1101 int use_armor, int use_textmode, int include_certs,
1102 GpgmeCtx ctx /* FIXME */)
1104 GpgsmObject gpgsm = engine;
1111 return mk_error (Invalid_Value);
1113 gpgsm->command = strdup (mode == GPGME_SIG_MODE_DETACH
1114 ? "SIGN --detached" : "SIGN");
1115 if (!gpgsm->command)
1116 return mk_error (Out_Of_Core);
1118 if (asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0)
1119 return mk_error (Out_Of_Core);
1120 err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, assuan_cmd, NULL,NULL);
1125 /* We must do a reset becuase we need to reset the list of signers. Note
1126 that RESET does not reset OPTION commands. */
1127 err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "RESET", NULL, NULL);
1131 for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++)
1133 const char *s = gpgme_key_get_string_attr (key, GPGME_ATTR_FPR,
1135 if (s && strlen (s) < 80)
1139 strcpy (stpcpy (buf, "SIGNER "), s);
1140 err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, buf,
1144 err = GPGME_Invalid_Key;
1145 gpgme_key_unref (key);
1150 gpgsm->input_cb.data = in;
1151 err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1152 map_input_enc (gpgsm->input_cb.data));
1155 gpgsm->output_cb.data = out;
1156 err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1157 use_armor ? "--armor" : 0);
1160 _gpgme_io_close (gpgsm->message_cb.fd);
1167 gpgsm_trustlist (void *engine, const char *pattern)
1170 return mk_error (Not_Implemented);
1175 gpgsm_verify (void *engine, GpgmeData sig, GpgmeData signed_text,
1176 GpgmeData plaintext)
1178 GpgsmObject gpgsm = engine;
1182 return mk_error (Invalid_Value);
1184 gpgsm->command = strdup ("VERIFY");
1185 if (!gpgsm->command)
1186 return mk_error (Out_Of_Core);
1188 gpgsm->input_cb.data = sig;
1189 err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1190 map_input_enc (gpgsm->input_cb.data));
1195 /* Normal or cleartext signature. */
1196 gpgsm->output_cb.data = plaintext;
1197 err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1199 _gpgme_io_close (gpgsm->message_cb.fd);
1203 /* Detached signature. */
1204 gpgsm->message_cb.data = signed_text;
1205 err = gpgsm_set_fd (gpgsm->assuan_ctx, "MESSAGE",
1206 gpgsm->message_fd_server, 0);
1207 _gpgme_io_close (gpgsm->output_cb.fd);
1217 status_cmp (const void *ap, const void *bp)
1219 const struct status_table_s *a = ap;
1220 const struct status_table_s *b = bp;
1222 return strcmp (a->name, b->name);
1226 static GpgmeStatusCode
1227 parse_status (const char *name)
1229 struct status_table_s t, *r;
1231 r = bsearch (&t, status_table, DIM(status_table) - 1,
1232 sizeof t, status_cmp);
1233 return r ? r->code : -1;
1238 status_handler (void *opaque, int fd)
1241 GpgsmObject gpgsm = opaque;
1247 err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
1251 && line[0] == 'O' && line[1] == 'K'
1252 && (line[2] == '\0' || line[2] == ' '))
1254 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
1255 && (line[3] == '\0' || line[3] == ' ')))
1257 /* XXX: If an error occured, find out what happened, then
1258 save the error value before running the status handler
1259 (so it takes precedence). */
1260 if (!err && line[0] == 'E' && line[3] == ' ')
1262 err = map_assuan_error (atoi (&line[4]));
1264 err = mk_error (General_Error);
1268 /* XXX Kludge ahead. We really, really, really must not
1269 make use of status.fnc_value. */
1270 GpgmeCtx ctx = (GpgmeCtx) gpgsm->status.fnc_value;
1275 if (gpgsm->status.fnc)
1276 gpgsm->status.fnc (gpgsm->status.fnc_value, GPGME_STATUS_EOF, "");
1277 if (gpgsm->colon.fnc && gpgsm->colon.any )
1279 /* We must tell a colon fucntion about the EOF. We do
1280 this only when we have seen any data lines. Note
1281 that this inlined use of colon data lines will
1282 eventually be changed into using a regular data
1284 gpgsm->colon.any = 0;
1285 gpgsm->colon.fnc (gpgsm->colon.fnc_value, NULL);
1288 /* XXX: Try our best to terminate the connection. */
1290 assuan_write_line (gpgsm->assuan_ctx, "BYE");
1292 _gpgme_io_close (gpgsm->status_cb.fd);
1297 && line[0] == 'D' && line[1] == ' '
1298 && gpgsm->colon.fnc)
1300 /* We are using the colon handler even for plain inline data
1301 - strange name for that function but for historic reasons
1303 /* FIXME We can't use this for binary data because we
1304 assume this is a string. For the current usage of colon
1305 output it is correct. */
1306 unsigned char *src = line + 2;
1307 unsigned char *end = line + linelen;
1309 unsigned char **aline = &gpgsm->colon.attic.line;
1310 int *alinelen = &gpgsm->colon.attic.linelen;
1312 if (gpgsm->colon.attic.linesize
1313 < *alinelen + linelen + 1)
1315 unsigned char *newline = realloc (*aline,
1316 *alinelen + linelen + 1);
1319 _gpgme_io_close (gpgsm->status_cb.fd);
1323 gpgsm->colon.attic.linesize += linelen + 1;
1326 dst = *aline + *alinelen;
1330 if (*src == '%' && src + 2 < end)
1332 /* Handle escaped characters. */
1334 *dst = xtoi_2 (src);
1346 /* Terminate the pending line, pass it to the colon
1347 handler and reset it. */
1349 gpgsm->colon.any = 1;
1350 if (*alinelen > 1 && *(dst - 1) == '\r')
1354 /* FIXME How should we handle the return code? */
1355 gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline);
1363 else if (linelen > 2
1364 && line[0] == 'S' && line[1] == ' ')
1369 rest = strchr (line + 2, ' ');
1371 rest = line + linelen; /* set to an empty string */
1375 r = parse_status (line + 2);
1379 if (gpgsm->status.fnc)
1380 gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest);
1383 fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
1386 while (assuan_pending_line (gpgsm->assuan_ctx));
1391 gpgsm_set_status_handler (void *engine, GpgmeStatusHandler fnc,
1394 GpgsmObject gpgsm = engine;
1396 gpgsm->status.fnc = fnc;
1397 gpgsm->status.fnc_value = fnc_value;
1402 gpgsm_set_colon_line_handler (void *engine, GpgmeColonLineHandler fnc,
1405 GpgsmObject gpgsm = engine;
1407 gpgsm->colon.fnc = fnc;
1408 gpgsm->colon.fnc_value = fnc_value;
1409 gpgsm->colon.any = 0;
1415 add_io_cb (GpgsmObject gpgsm, iocb_data_t *iocbd, GpgmeIOCb handler)
1419 err = (*gpgsm->io_cbs.add) (gpgsm->io_cbs.add_priv,
1420 iocbd->fd, iocbd->dir,
1421 handler, iocbd->data, &iocbd->tag);
1425 /* FIXME Kludge around poll() problem. */
1426 err = _gpgme_io_set_nonblocking (iocbd->fd);
1432 gpgsm_start (void *engine, void *opaque)
1434 GpgsmObject gpgsm = engine;
1439 return mk_error (Invalid_Value);
1441 pid = assuan_get_pid (gpgsm->assuan_ctx);
1443 err = add_io_cb (gpgsm, &gpgsm->status_cb, status_handler);
1444 if (gpgsm->input_cb.fd != -1)
1445 err = add_io_cb (gpgsm, &gpgsm->input_cb, _gpgme_data_outbound_handler);
1446 if (!err && gpgsm->output_cb.fd != -1)
1447 err = add_io_cb (gpgsm, &gpgsm->output_cb, _gpgme_data_inbound_handler);
1448 if (!err && gpgsm->message_cb.fd != -1)
1449 err = add_io_cb (gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler);
1452 err = assuan_write_line (gpgsm->assuan_ctx, gpgsm->command);
1459 gpgsm_set_io_cbs (void *engine, struct GpgmeIOCbs *io_cbs)
1461 GpgsmObject gpgsm = engine;
1462 gpgsm->io_cbs = *io_cbs;
1467 gpgsm_io_event (void *engine, GpgmeEventIO type, void *type_data)
1469 GpgsmObject gpgsm = engine;
1471 if (gpgsm->io_cbs.event)
1472 (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, type, type_data);
1476 struct engine_ops _gpgme_engine_ops_gpgsm =
1478 /* Static functions. */
1479 _gpgme_get_gpgsm_path,
1481 gpgsm_check_version,
1484 /* Member functions. */
1486 gpgsm_set_status_handler,
1487 NULL, /* set_command_handler */
1488 gpgsm_set_colon_line_handler,
1489 NULL, /* set_verbosity */