1 /* engine-gpgsm.c - GpgSM engine.
2 Copyright (C) 2000 Werner Koch (dd9jn)
3 Copyright (C) 2001, 2002, 2003 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;
80 GpgmeStatusHandler fnc;
86 GpgmeColonLineHandler fnc;
94 int any; /* any data line seen */
97 struct GpgmeIOCbs io_cbs;
102 gpgsm_get_version (void)
104 static const char *gpgsm_version;
105 DEFINE_STATIC_LOCK (gpgsm_version_lock);
107 LOCK (gpgsm_version_lock);
109 gpgsm_version = _gpgme_get_program_version (_gpgme_get_gpgsm_path ());
110 UNLOCK (gpgsm_version_lock);
112 return gpgsm_version;
117 gpgsm_check_version (void)
119 return _gpgme_compare_versions (gpgsm_get_version (), NEED_GPGSM_VERSION)
120 ? 0 : GPGME_Invalid_Engine;
125 close_notify_handler (int fd, void *opaque)
127 GpgsmObject gpgsm = opaque;
130 if (gpgsm->status_cb.fd == fd)
132 if (gpgsm->status_cb.tag)
133 (*gpgsm->io_cbs.remove) (gpgsm->status_cb.tag);
134 gpgsm->status_cb.fd = -1;
136 else if (gpgsm->input_cb.fd == fd)
138 if (gpgsm->input_cb.tag)
139 (*gpgsm->io_cbs.remove) (gpgsm->input_cb.tag);
140 gpgsm->input_cb.fd = -1;
142 else if (gpgsm->output_cb.fd == fd)
144 if (gpgsm->output_cb.tag)
145 (*gpgsm->io_cbs.remove) (gpgsm->output_cb.tag);
146 gpgsm->output_cb.fd = -1;
148 else if (gpgsm->message_cb.fd == fd)
150 if (gpgsm->message_cb.tag)
151 (*gpgsm->io_cbs.remove) (gpgsm->message_cb.tag);
152 gpgsm->message_cb.fd = -1;
158 map_assuan_error (AssuanError err)
162 case ASSUAN_No_Error:
163 return GPGME_No_Error;
164 case ASSUAN_General_Error:
165 return GPGME_General_Error;
166 case ASSUAN_Out_Of_Core:
167 return GPGME_Out_Of_Core;
168 case ASSUAN_Invalid_Value:
169 return GPGME_Invalid_Value;
170 case ASSUAN_Read_Error:
171 return GPGME_Read_Error;
172 case ASSUAN_Write_Error:
173 return GPGME_Write_Error;
176 case ASSUAN_Problem_Starting_Server:
177 case ASSUAN_Not_A_Server:
178 case ASSUAN_Not_A_Client:
179 case ASSUAN_Nested_Commands:
180 case ASSUAN_Invalid_Response:
181 case ASSUAN_No_Data_Callback:
182 case ASSUAN_No_Inquire_Callback:
183 case ASSUAN_Connect_Failed:
184 case ASSUAN_Accept_Failed:
185 return GPGME_General_Error;
187 /* The following error codes are meant as status codes. */
188 case ASSUAN_Not_Implemented:
189 return GPGME_Not_Implemented;
190 case ASSUAN_Canceled:
191 return GPGME_Canceled;
192 case ASSUAN_Unsupported_Algorithm:
193 return GPGME_Not_Implemented; /* XXX Argh. */
195 case ASSUAN_No_Data_Available:
198 /* These are errors internal to GPGME. */
199 case ASSUAN_No_Input:
200 case ASSUAN_No_Output:
201 case ASSUAN_Invalid_Command:
202 case ASSUAN_Unknown_Command:
203 case ASSUAN_Syntax_Error:
204 case ASSUAN_Parameter_Error:
205 case ASSUAN_Parameter_Conflict:
206 case ASSUAN_Line_Too_Long:
207 case ASSUAN_Line_Not_Terminated:
208 case ASSUAN_Invalid_Data:
209 case ASSUAN_Unexpected_Command:
210 case ASSUAN_Too_Much_Data:
211 case ASSUAN_Inquire_Unknown:
212 case ASSUAN_Inquire_Error:
213 case ASSUAN_Invalid_Option:
214 case ASSUAN_Invalid_Index:
215 case ASSUAN_Unexpected_Status:
216 case ASSUAN_Unexpected_Data:
217 case ASSUAN_Invalid_Status:
218 case ASSUAN_Not_Confirmed:
219 return GPGME_General_Error;
221 /* These are errors in the server. */
222 case ASSUAN_Server_Fault:
223 case ASSUAN_Server_Resource_Problem:
224 case ASSUAN_Server_IO_Error:
225 case ASSUAN_Server_Bug:
226 case ASSUAN_No_Agent:
227 case ASSUAN_Agent_Error:
228 return GPGME_Invalid_Engine; /* XXX: Need something more useful. */
230 case ASSUAN_Bad_Certificate:
231 case ASSUAN_Bad_Certificate_Path:
232 case ASSUAN_Missing_Certificate:
233 case ASSUAN_No_Public_Key:
234 case ASSUAN_No_Secret_Key:
235 case ASSUAN_Invalid_Name:
236 case ASSUAN_Card_Error: /* XXX: Oh well. */
237 case ASSUAN_Invalid_Card: /* XXX: Oh well. */
238 case ASSUAN_No_PKCS15_App: /* XXX: Oh well. */
239 case ASSUAN_Card_Not_Present: /* XXX: Oh well. */
240 case ASSUAN_Invalid_Id: /* XXX: Oh well. */
241 return GPGME_Invalid_Key;
243 case ASSUAN_Bad_Signature:
244 return GPGME_Invalid_Key; /* XXX: This is wrong. */
246 case ASSUAN_Cert_Revoked:
247 case ASSUAN_No_CRL_For_Cert:
248 case ASSUAN_CRL_Too_Old:
249 case ASSUAN_Not_Trusted:
250 return GPGME_Invalid_Key; /* XXX Some more details would be good. */
253 return GPGME_General_Error;
259 gpgsm_release (void *engine)
261 GpgsmObject gpgsm = engine;
266 if (gpgsm->status_cb.fd != -1)
267 _gpgme_io_close (gpgsm->status_cb.fd);
268 if (gpgsm->input_cb.fd != -1)
269 _gpgme_io_close (gpgsm->input_cb.fd);
270 if (gpgsm->output_cb.fd != -1)
271 _gpgme_io_close (gpgsm->output_cb.fd);
272 if (gpgsm->message_cb.fd != -1)
273 _gpgme_io_close (gpgsm->message_cb.fd);
275 assuan_disconnect (gpgsm->assuan_ctx);
277 free (gpgsm->colon.attic.line);
283 gpgsm_new (void **engine)
290 char *dft_display = NULL;
291 char *dft_ttyname = NULL;
292 char *dft_ttytype = NULL;
299 gpgsm = calloc (1, sizeof *gpgsm);
302 err = GPGME_Out_Of_Core;
306 gpgsm->status_cb.fd = -1;
307 gpgsm->status_cb.tag = 0;
309 gpgsm->input_cb.fd = -1;
310 gpgsm->input_cb.tag = 0;
311 gpgsm->input_fd_server = -1;
312 gpgsm->output_cb.fd = -1;
313 gpgsm->output_cb.tag = 0;
314 gpgsm->output_fd_server = -1;
315 gpgsm->message_cb.fd = -1;
316 gpgsm->message_cb.tag = 0;
317 gpgsm->message_fd_server = -1;
319 gpgsm->status.fnc = 0;
320 gpgsm->colon.fnc = 0;
321 gpgsm->colon.attic.line = 0;
322 gpgsm->colon.attic.linesize = 0;
323 gpgsm->colon.attic.linelen = 0;
324 gpgsm->colon.any = 0;
326 gpgsm->io_cbs.add = NULL;
327 gpgsm->io_cbs.add_priv = NULL;
328 gpgsm->io_cbs.remove = NULL;
329 gpgsm->io_cbs.event = NULL;
330 gpgsm->io_cbs.event_priv = NULL;
332 if (_gpgme_io_pipe (fds, 0) < 0)
334 err = GPGME_Pipe_Error;
337 gpgsm->input_cb.fd = fds[1];
338 gpgsm->input_cb.dir = 0;
339 gpgsm->input_fd_server = fds[0];
341 if (_gpgme_io_pipe (fds, 1) < 0)
343 err = GPGME_Pipe_Error;
346 gpgsm->output_cb.fd = fds[0];
347 gpgsm->output_cb.dir = 1;
348 gpgsm->output_fd_server = fds[1];
350 if (_gpgme_io_pipe (fds, 0) < 0)
352 err = GPGME_Pipe_Error;
355 gpgsm->message_cb.fd = fds[1];
356 gpgsm->message_cb.dir = 0;
357 gpgsm->message_fd_server = fds[0];
359 child_fds[0] = gpgsm->input_fd_server;
360 child_fds[1] = gpgsm->output_fd_server;
361 child_fds[2] = gpgsm->message_fd_server;
365 argv[1] = "--server";
368 err = assuan_pipe_connect2 (&gpgsm->assuan_ctx,
369 _gpgme_get_gpgsm_path (), argv, child_fds,
370 1 /* dup stderr to /dev/null */);
372 /* We need to know the fd used by assuan for reads. We do this by
373 using the assumption that the first returned fd from
374 assuan_get_active_fds() is always this one. */
375 nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */,
376 fdlist, DIM (fdlist));
379 err = GPGME_General_Error; /* FIXME */
382 /* We duplicate the file descriptor, so we can close it without
383 disturbing assuan. Alternatively, we could special case
384 status_fd and register/unregister it manually as needed, but this
385 increases code duplication and is more complicated as we can not
386 use the close notifications etc. */
387 gpgsm->status_cb.fd = dup (fdlist[0]);
388 if (gpgsm->status_cb.fd < 0)
390 err = GPGME_General_Error; /* FIXME */
393 gpgsm->status_cb.dir = 1;
394 gpgsm->status_cb.data = gpgsm;
396 dft_display = getenv ("DISPLAY");
399 if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
401 err = GPGME_Out_Of_Core;
404 err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
409 err = map_assuan_error (err);
413 dft_ttyname = ttyname (1);
416 if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
418 err = GPGME_Out_Of_Core;
421 err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
426 err = map_assuan_error (err);
430 dft_ttytype = getenv ("TERM");
433 if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
435 err = GPGME_Out_Of_Core;
438 err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
443 err = map_assuan_error (err);
448 old_lc = setlocale (LC_CTYPE, NULL);
451 old_lc = strdup (old_lc);
454 err = GPGME_Out_Of_Core;
458 dft_lc = setlocale (LC_CTYPE, "");
461 if (asprintf (&optstr, "OPTION lc-ctype=%s", dft_lc) < 0)
462 err = GPGME_Out_Of_Core;
465 err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
466 NULL, NULL, NULL, NULL);
469 err = map_assuan_error (err);
474 setlocale (LC_CTYPE, old_lc);
481 old_lc = setlocale (LC_MESSAGES, NULL);
484 old_lc = strdup (old_lc);
487 err = GPGME_Out_Of_Core;
491 dft_lc = setlocale (LC_MESSAGES, "");
494 if (asprintf (&optstr, "OPTION lc-messages=%s", dft_lc) < 0)
495 err = GPGME_Out_Of_Core;
498 err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
502 err = map_assuan_error (err);
507 setlocale (LC_MESSAGES, old_lc);
515 (_gpgme_io_set_close_notify (gpgsm->status_cb.fd,
516 close_notify_handler, gpgsm)
517 || _gpgme_io_set_close_notify (gpgsm->input_cb.fd,
518 close_notify_handler, gpgsm)
519 || _gpgme_io_set_close_notify (gpgsm->output_cb.fd,
520 close_notify_handler, gpgsm)
521 || _gpgme_io_set_close_notify (gpgsm->message_cb.fd,
522 close_notify_handler, gpgsm)))
524 err = GPGME_General_Error;
529 /* Close the server ends of the pipes. Our ends are closed in
531 if (gpgsm->input_fd_server != -1)
532 _gpgme_io_close (gpgsm->input_fd_server);
533 if (gpgsm->output_fd_server != -1)
534 _gpgme_io_close (gpgsm->output_fd_server);
535 if (gpgsm->message_fd_server != -1)
536 _gpgme_io_close (gpgsm->message_fd_server);
539 gpgsm_release (gpgsm);
547 /* Forward declaration. */
548 static GpgmeStatusCode parse_status (const char *name);
551 gpgsm_assuan_simple_command (ASSUAN_CONTEXT ctx, char *cmd, GpgmeStatusHandler status_fnc,
552 void *status_fnc_value)
558 err = assuan_write_line (ctx, cmd);
560 return map_assuan_error (err);
564 err = assuan_read_line (ctx, &line, &linelen);
566 return map_assuan_error (err);
568 if (*line == '#' || !linelen)
572 && line[0] == 'O' && line[1] == 'K'
573 && (line[2] == '\0' || line[2] == ' '))
575 else if (linelen >= 4
576 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
578 err = map_assuan_error (atoi (&line[4]));
579 else if (linelen >= 2
580 && line[0] == 'S' && line[1] == ' ')
585 rest = strchr (line + 2, ' ');
587 rest = line + linelen; /* set to an empty string */
591 r = parse_status (line + 2);
593 if (r >= 0 && status_fnc)
594 status_fnc (status_fnc_value, r, rest);
596 err = GPGME_General_Error;
599 err = GPGME_General_Error;
607 #define COMMANDLINELEN 40
609 gpgsm_set_fd (ASSUAN_CONTEXT ctx, const char *which, int fd, const char *opt)
611 char line[COMMANDLINELEN];
614 snprintf (line, COMMANDLINELEN, "%s FD=%i %s", which, fd, opt);
616 snprintf (line, COMMANDLINELEN, "%s FD=%i", which, fd);
618 return gpgsm_assuan_simple_command (ctx, line, NULL, NULL);
623 map_input_enc (GpgmeData d)
625 switch (gpgme_data_get_encoding (d))
627 case GPGME_DATA_ENCODING_NONE:
629 case GPGME_DATA_ENCODING_BINARY:
631 case GPGME_DATA_ENCODING_BASE64:
633 case GPGME_DATA_ENCODING_ARMOR:
643 status_cmp (const void *ap, const void *bp)
645 const struct status_table_s *a = ap;
646 const struct status_table_s *b = bp;
648 return strcmp (a->name, b->name);
652 static GpgmeStatusCode
653 parse_status (const char *name)
655 struct status_table_s t, *r;
657 r = bsearch (&t, status_table, DIM(status_table) - 1,
658 sizeof t, status_cmp);
659 return r ? r->code : -1;
664 status_handler (void *opaque, int fd)
666 AssuanError assuan_err;
668 GpgsmObject gpgsm = opaque;
674 assuan_err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
677 /* Try our best to terminate the connection friendly. */
678 assuan_write_line (gpgsm->assuan_ctx, "BYE");
679 err = map_assuan_error (assuan_err);
681 else if (linelen >= 3
682 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
683 && (line[3] == '\0' || line[3] == ' '))
686 err = map_assuan_error (atoi (&line[4]));
688 err = GPGME_General_Error;
690 else if (linelen >= 2
691 && line[0] == 'O' && line[1] == 'K'
692 && (line[2] == '\0' || line[2] == ' '))
694 if (gpgsm->status.fnc)
695 err = gpgsm->status.fnc (gpgsm->status.fnc_value,
696 GPGME_STATUS_EOF, "");
698 if (!err && gpgsm->colon.fnc && gpgsm->colon.any )
700 /* We must tell a colon function about the EOF. We do
701 this only when we have seen any data lines. Note
702 that this inlined use of colon data lines will
703 eventually be changed into using a regular data
705 gpgsm->colon.any = 0;
706 err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, NULL);
708 _gpgme_io_close (gpgsm->status_cb.fd);
712 && line[0] == 'D' && line[1] == ' '
715 /* We are using the colon handler even for plain inline data
716 - strange name for that function but for historic reasons
718 /* FIXME We can't use this for binary data because we
719 assume this is a string. For the current usage of colon
720 output it is correct. */
721 unsigned char *src = line + 2;
722 unsigned char *end = line + linelen;
724 unsigned char **aline = &gpgsm->colon.attic.line;
725 int *alinelen = &gpgsm->colon.attic.linelen;
727 if (gpgsm->colon.attic.linesize
728 < *alinelen + linelen + 1)
730 unsigned char *newline = realloc (*aline,
731 *alinelen + linelen + 1);
733 err = GPGME_Out_Of_Core;
737 gpgsm->colon.attic.linesize += linelen + 1;
742 dst = *aline + *alinelen;
744 while (!err && src < end)
746 if (*src == '%' && src + 2 < end)
748 /* Handle escaped characters. */
762 /* Terminate the pending line, pass it to the colon
763 handler and reset it. */
765 gpgsm->colon.any = 1;
766 if (*alinelen > 1 && *(dst - 1) == '\r')
770 /* FIXME How should we handle the return code? */
771 err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline);
784 && line[0] == 'S' && line[1] == ' ')
789 rest = strchr (line + 2, ' ');
791 rest = line + linelen; /* set to an empty string */
795 r = parse_status (line + 2);
799 if (gpgsm->status.fnc)
800 err = gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest);
803 fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
806 while (!err && assuan_pending_line (gpgsm->assuan_ctx));
808 _gpgme_io_close (gpgsm->status_cb.fd);
814 add_io_cb (GpgsmObject gpgsm, iocb_data_t *iocbd, GpgmeIOCb handler)
818 err = (*gpgsm->io_cbs.add) (gpgsm->io_cbs.add_priv,
819 iocbd->fd, iocbd->dir,
820 handler, iocbd->data, &iocbd->tag);
824 /* FIXME Kludge around poll() problem. */
825 err = _gpgme_io_set_nonblocking (iocbd->fd);
831 start (GpgsmObject gpgsm, const char *command)
835 err = add_io_cb (gpgsm, &gpgsm->status_cb, status_handler);
836 if (gpgsm->input_cb.fd != -1)
837 err = add_io_cb (gpgsm, &gpgsm->input_cb, _gpgme_data_outbound_handler);
838 if (!err && gpgsm->output_cb.fd != -1)
839 err = add_io_cb (gpgsm, &gpgsm->output_cb, _gpgme_data_inbound_handler);
840 if (!err && gpgsm->message_cb.fd != -1)
841 err = add_io_cb (gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler);
844 err = assuan_write_line (gpgsm->assuan_ctx, command);
846 (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, GPGME_EVENT_START, NULL);
853 gpgsm_decrypt (void *engine, GpgmeData ciph, GpgmeData plain)
855 GpgsmObject gpgsm = engine;
859 return GPGME_Invalid_Value;
861 gpgsm->input_cb.data = ciph;
862 err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
863 map_input_enc (gpgsm->input_cb.data));
865 return GPGME_General_Error; /* FIXME */
866 gpgsm->output_cb.data = plain;
867 err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server, 0);
869 return GPGME_General_Error; /* FIXME */
870 _gpgme_io_close (gpgsm->message_cb.fd);
872 err = start (engine, "DECRYPT");
878 gpgsm_delete (void *engine, GpgmeKey key, int allow_secret)
880 GpgsmObject gpgsm = engine;
882 char *fpr = (char *) gpgme_key_get_string_attr (key, GPGME_ATTR_FPR, NULL, 0);
885 int length = 8; /* "DELKEYS " */
888 return GPGME_Invalid_Key;
893 if (*linep == '%' || *linep == ' ' || *linep == '+')
899 line = malloc (length);
901 return GPGME_Out_Of_Core;
903 strcpy (line, "DELKEYS ");
933 _gpgme_io_close (gpgsm->output_cb.fd);
934 _gpgme_io_close (gpgsm->input_cb.fd);
935 _gpgme_io_close (gpgsm->message_cb.fd);
937 err = start (gpgsm, line);
945 set_recipients (GpgsmObject gpgsm, GpgmeRecipients recp)
948 ASSUAN_CONTEXT ctx = gpgsm->assuan_ctx;
952 int valid_recipients = 0;
954 linelen = 10 + 40 + 1; /* "RECIPIENT " + guess + '\0'. */
955 line = malloc (10 + 40 + 1);
957 return GPGME_Out_Of_Core;
958 strcpy (line, "RECIPIENT ");
959 for (r = recp->list; r; r = r->next)
961 int newlen = 11 + strlen (r->name);
962 if (linelen < newlen)
964 char *newline = realloc (line, newlen);
968 return GPGME_Out_Of_Core;
973 strcpy (&line[10], r->name);
975 err = gpgsm_assuan_simple_command (ctx, line, gpgsm->status.fnc,
976 gpgsm->status.fnc_value);
978 valid_recipients = 1;
979 else if (err != GPGME_Invalid_Key)
986 if (!valid_recipients && gpgsm->status.fnc)
987 gpgsm->status.fnc (gpgsm->status.fnc_value, GPGME_STATUS_NO_RECP, "");
993 gpgsm_encrypt (void *engine, GpgmeRecipients recp, GpgmeData plain,
994 GpgmeData ciph, int use_armor)
996 GpgsmObject gpgsm = engine;
1000 return GPGME_Invalid_Value;
1002 return GPGME_Not_Implemented;
1004 gpgsm->input_cb.data = plain;
1005 err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1006 map_input_enc (gpgsm->input_cb.data));
1009 gpgsm->output_cb.data = ciph;
1010 err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1011 use_armor ? "--armor" : 0);
1014 _gpgme_io_close (gpgsm->message_cb.fd);
1016 err = set_recipients (gpgsm, recp);
1019 err = start (gpgsm, "ENCRYPT");
1026 gpgsm_export (void *engine, GpgmeRecipients recp, GpgmeData keydata,
1029 GpgsmObject gpgsm = engine;
1036 return GPGME_Invalid_Value;
1038 cmd = malloc (cmdlen);
1040 return GPGME_Out_Of_Core;
1041 strcpy (cmd, "EXPORT");
1049 err = gpgme_recipients_enum_open (recp, &ec);
1050 while (!err && (s = gpgme_recipients_enum_read (recp, &ec)))
1052 int slen = strlen (s);
1053 /* New string is old string + ' ' + s + '\0'. */
1054 if (cmdlen < cmdi + 1 + slen + 1)
1056 char *newcmd = realloc (cmd, cmdlen * 2);
1060 return GPGME_Out_Of_Core;
1066 strcpy (cmd + cmdi, s);
1070 err = gpgme_recipients_enum_close (recp, &ec);
1075 gpgsm->output_cb.data = keydata;
1076 err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1077 use_armor ? "--armor" : 0);
1080 _gpgme_io_close (gpgsm->input_cb.fd);
1081 _gpgme_io_close (gpgsm->message_cb.fd);
1083 err = start (gpgsm, cmd);
1090 gpgsm_genkey (void *engine, GpgmeData help_data, int use_armor,
1091 GpgmeData pubkey, GpgmeData seckey)
1093 GpgsmObject gpgsm = engine;
1096 if (!gpgsm || !pubkey || seckey)
1097 return GPGME_Invalid_Value;
1099 gpgsm->input_cb.data = help_data;
1100 err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1101 map_input_enc (gpgsm->input_cb.data));
1104 gpgsm->output_cb.data = pubkey;
1105 err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1106 use_armor ? "--armor" : 0);
1109 _gpgme_io_close (gpgsm->message_cb.fd);
1111 err = start (gpgsm, "GENKEY");
1117 gpgsm_import (void *engine, GpgmeData keydata)
1119 GpgsmObject gpgsm = engine;
1123 return GPGME_Invalid_Value;
1125 gpgsm->input_cb.data = keydata;
1126 err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1127 map_input_enc (gpgsm->input_cb.data));
1130 _gpgme_io_close (gpgsm->output_cb.fd);
1131 _gpgme_io_close (gpgsm->message_cb.fd);
1133 err = start (gpgsm, "IMPORT");
1139 gpgsm_keylist (void *engine, const char *pattern, int secret_only,
1142 GpgsmObject gpgsm = engine;
1149 if (asprintf (&line, "OPTION list-mode=%d", (keylist_mode & 3)) < 0)
1150 return GPGME_Out_Of_Core;
1151 err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
1156 /* Length is "LISTSECRETKEYS " + p + '\0'. */
1157 line = malloc (15 + strlen (pattern) + 1);
1159 return GPGME_Out_Of_Core;
1162 strcpy (line, "LISTSECRETKEYS ");
1163 strcpy (&line[15], pattern);
1167 strcpy (line, "LISTKEYS ");
1168 strcpy (&line[9], pattern);
1171 _gpgme_io_close (gpgsm->input_cb.fd);
1172 _gpgme_io_close (gpgsm->output_cb.fd);
1173 _gpgme_io_close (gpgsm->message_cb.fd);
1175 err = start (gpgsm, line);
1182 gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
1183 int reserved, int keylist_mode)
1185 GpgsmObject gpgsm = engine;
1188 /* Length is "LISTSECRETKEYS " + p + '\0'. */
1189 int length = 15 + 1;
1193 return GPGME_Invalid_Value;
1195 if (asprintf (&line, "OPTION list-mode=%d", (keylist_mode & 3)) < 0)
1196 return GPGME_Out_Of_Core;
1197 err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
1202 if (pattern && *pattern)
1204 const char **pat = pattern;
1208 const char *patlet = *pat;
1213 if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
1218 /* This will allocate one byte more than necessary. */
1222 line = malloc (length);
1224 return GPGME_Out_Of_Core;
1227 strcpy (line, "LISTSECRETKEYS ");
1232 strcpy (line, "LISTKEYS ");
1236 if (pattern && *pattern)
1240 const char *patlet = *pattern;
1262 *(linep++) = *patlet;
1272 _gpgme_io_close (gpgsm->input_cb.fd);
1273 _gpgme_io_close (gpgsm->output_cb.fd);
1274 _gpgme_io_close (gpgsm->message_cb.fd);
1276 err = start (gpgsm, line);
1283 gpgsm_sign (void *engine, GpgmeData in, GpgmeData out, GpgmeSigMode mode,
1284 int use_armor, int use_textmode, int include_certs,
1285 GpgmeCtx ctx /* FIXME */)
1287 GpgsmObject gpgsm = engine;
1294 return GPGME_Invalid_Value;
1296 if (asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0)
1297 return GPGME_Out_Of_Core;
1298 err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, assuan_cmd, NULL,NULL);
1303 /* We must do a reset becuase we need to reset the list of signers. Note
1304 that RESET does not reset OPTION commands. */
1305 err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "RESET", NULL, NULL);
1309 for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++)
1311 const char *s = gpgme_key_get_string_attr (key, GPGME_ATTR_FPR,
1313 if (s && strlen (s) < 80)
1317 strcpy (stpcpy (buf, "SIGNER "), s);
1318 err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, buf,
1322 err = GPGME_Invalid_Key;
1323 gpgme_key_unref (key);
1328 gpgsm->input_cb.data = in;
1329 err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1330 map_input_enc (gpgsm->input_cb.data));
1333 gpgsm->output_cb.data = out;
1334 err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1335 use_armor ? "--armor" : 0);
1338 _gpgme_io_close (gpgsm->message_cb.fd);
1340 err = start (gpgsm, mode == GPGME_SIG_MODE_DETACH
1341 ? "SIGN --detached" : "SIGN");
1347 gpgsm_trustlist (void *engine, const char *pattern)
1350 return GPGME_Not_Implemented;
1355 gpgsm_verify (void *engine, GpgmeData sig, GpgmeData signed_text,
1356 GpgmeData plaintext)
1358 GpgsmObject gpgsm = engine;
1362 return GPGME_Invalid_Value;
1364 gpgsm->input_cb.data = sig;
1365 err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1366 map_input_enc (gpgsm->input_cb.data));
1371 /* Normal or cleartext signature. */
1372 gpgsm->output_cb.data = plaintext;
1373 err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1375 _gpgme_io_close (gpgsm->message_cb.fd);
1379 /* Detached signature. */
1380 gpgsm->message_cb.data = signed_text;
1381 err = gpgsm_set_fd (gpgsm->assuan_ctx, "MESSAGE",
1382 gpgsm->message_fd_server, 0);
1383 _gpgme_io_close (gpgsm->output_cb.fd);
1387 err = start (gpgsm, "VERIFY");
1394 gpgsm_set_status_handler (void *engine, GpgmeStatusHandler fnc,
1397 GpgsmObject gpgsm = engine;
1399 gpgsm->status.fnc = fnc;
1400 gpgsm->status.fnc_value = fnc_value;
1405 gpgsm_set_colon_line_handler (void *engine, GpgmeColonLineHandler fnc,
1408 GpgsmObject gpgsm = engine;
1410 gpgsm->colon.fnc = fnc;
1411 gpgsm->colon.fnc_value = fnc_value;
1412 gpgsm->colon.any = 0;
1418 gpgsm_set_io_cbs (void *engine, struct GpgmeIOCbs *io_cbs)
1420 GpgsmObject gpgsm = engine;
1421 gpgsm->io_cbs = *io_cbs;
1426 gpgsm_io_event (void *engine, GpgmeEventIO type, void *type_data)
1428 GpgsmObject gpgsm = engine;
1430 if (gpgsm->io_cbs.event)
1431 (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, type, type_data);
1435 struct engine_ops _gpgme_engine_ops_gpgsm =
1437 /* Static functions. */
1438 _gpgme_get_gpgsm_path,
1440 gpgsm_check_version,
1443 /* Member functions. */
1445 gpgsm_set_status_handler,
1446 NULL, /* set_command_handler */
1447 gpgsm_set_colon_line_handler,
1448 NULL, /* set_verbosity */