1 /* wks-utils.c - Common helper fucntions for wks tools
2 * Copyright (C) 2016 g10 Code GmbH
4 * This file is part of GnuPG.
6 * GnuPG is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * GnuPG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <https://www.gnu.org/licenses/>.
29 #include "mbox-util.h"
30 #include "mime-maker.h"
31 #include "send-mail.h"
34 /* The stream to output the status information. Output is disabled if
36 static estream_t statusfp;
40 /* Set the status FD. */
42 wks_set_status_fd (int fd)
44 static int last_fd = -1;
46 if (fd != -1 && last_fd == fd)
49 if (statusfp && statusfp != es_stdout && statusfp != es_stderr)
60 statusfp = es_fdopen (fd, "w");
63 log_fatal ("can't open fd %d for status output: %s\n",
64 fd, gpg_strerror (gpg_error_from_syserror ()));
70 /* Write a status line with code NO followed by the outout of the
71 * printf style FORMAT. The caller needs to make sure that LFs and
72 * CRs are not printed. */
74 wks_write_status (int no, const char *format, ...)
79 return; /* Not enabled. */
81 es_fputs ("[GNUPG:] ", statusfp);
82 es_fputs (get_status_string (no), statusfp);
85 es_putc (' ', statusfp);
86 va_start (arg_ptr, format);
87 es_vfprintf (statusfp, format, arg_ptr);
90 es_putc ('\n', statusfp);
95 /* Helper for wks_list_key. */
97 list_key_status_cb (void *opaque, const char *keyword, char *args)
102 log_debug ("gpg status: %s %s\n", keyword, args);
106 /* Run gpg on KEY and store the primary fingerprint at R_FPR and the
107 * list of mailboxes at R_MBOXES. Returns 0 on success; on error NULL
108 * is stored at R_FPR and R_MBOXES and an error code is returned. */
110 wks_list_key (estream_t key, char **r_fpr, strlist_t *r_mboxes)
117 size_t length_of_line = 0;
120 char **fields = NULL;
125 strlist_t mboxes = NULL;
130 /* Open a memory stream. */
131 listing = es_fopenmem (0, "w+b");
134 err = gpg_error_from_syserror ();
135 log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
139 ccparray_init (&ccp, 0);
141 ccparray_put (&ccp, "--no-options");
143 ccparray_put (&ccp, "--quiet");
144 else if (opt.verbose > 1)
145 ccparray_put (&ccp, "--verbose");
146 ccparray_put (&ccp, "--batch");
147 ccparray_put (&ccp, "--status-fd=2");
148 ccparray_put (&ccp, "--always-trust");
149 ccparray_put (&ccp, "--with-colons");
150 ccparray_put (&ccp, "--dry-run");
151 ccparray_put (&ccp, "--import-options=import-minimal,import-show");
152 ccparray_put (&ccp, "--import");
154 ccparray_put (&ccp, NULL);
155 argv = ccparray_get (&ccp, NULL);
158 err = gpg_error_from_syserror ();
161 err = gnupg_exec_tool_stream (opt.gpg_program, argv, key,
163 list_key_status_cb, NULL);
166 log_error ("import failed: %s\n", gpg_strerror (err));
172 maxlen = 2048; /* Set limit. */
173 while ((len = es_read_line (listing, &line, &length_of_line, &maxlen)) > 0)
178 log_error ("received line too long\n");
179 err = gpg_error (GPG_ERR_LINE_TOO_LONG);
182 /* Strip newline and carriage return, if present. */
184 && (line[len - 1] == '\n' || line[len - 1] == '\r'))
186 /* log_debug ("line '%s'\n", line); */
189 fields = strtokenize (line, ":");
192 err = gpg_error_from_syserror ();
193 log_error ("strtokenize failed: %s\n", gpg_strerror (err));
196 for (nfields = 0; fields[nfields]; nfields++)
200 err = gpg_error (GPG_ERR_INV_ENGINE);
203 if (!strcmp (fields[0], "sec"))
205 /* gpg may return "sec" as the first record - but we do not
206 * accept secret keys. */
207 err = gpg_error (GPG_ERR_NO_PUBKEY);
210 if (lnr == 1 && strcmp (fields[0], "pub"))
212 /* First record is not a public key. */
213 err = gpg_error (GPG_ERR_INV_ENGINE);
216 if (lnr > 1 && !strcmp (fields[0], "pub"))
218 /* More than one public key. */
219 err = gpg_error (GPG_ERR_TOO_MANY);
222 if (!strcmp (fields[0], "sub") || !strcmp (fields[0], "ssb"))
223 break; /* We can stop parsing here. */
225 if (!strcmp (fields[0], "fpr") && nfields > 9 && !fpr)
227 fpr = xtrystrdup (fields[9]);
230 err = gpg_error_from_syserror ();
234 else if (!strcmp (fields[0], "uid") && nfields > 9)
236 /* Fixme: Unescape fields[9] */
238 mbox = mailbox_from_userid (fields[9]);
239 if (mbox && !append_to_strlist_try (&mboxes, mbox))
241 err = gpg_error_from_syserror ();
246 if (len < 0 || es_ferror (listing))
248 err = gpg_error_from_syserror ();
249 log_error ("error reading memory stream\n");
270 /* Helper to write mail to the output(s). */
272 wks_send_mime (mime_maker_t mime)
277 /* Without any option we take a short path. */
278 if (!opt.use_sendmail && !opt.output)
279 return mime_maker_make (mime, es_stdout);
281 mail = es_fopenmem (0, "w+b");
284 err = gpg_error_from_syserror ();
288 err = mime_maker_make (mime, mail);
290 if (!err && opt.output)
293 err = send_mail_to_file (mail, opt.output);
296 if (!err && opt.use_sendmail)
299 err = send_mail (mail);
307 /* Parse the policy flags by reading them from STREAM and storing them
308 * into FLAGS. If IGNORE_UNKNOWN is iset unknown keywords are
311 wks_parse_policy (policy_flags_t flags, estream_t stream, int ignore_unknown)
323 { "mailbox-only", TOK_MAILBOX_ONLY },
324 { "dane-only", TOK_DANE_ONLY },
325 { "auth-submit", TOK_AUTH_SUBMIT },
326 { "max-pending", TOK_MAX_PENDING }
331 char *p, *keyword, *value;
334 memset (flags, 0, sizeof *flags);
336 while (es_fgets (line, DIM(line)-1, stream) )
340 if (!n || line[n-1] != '\n')
342 err = gpg_error (*line? GPG_ERR_LINE_TOO_LONG
343 : GPG_ERR_INCOMPLETE_LINE);
346 trim_trailing_spaces (line);
347 /* Skip empty and comment lines. */
348 for (p=line; spacep (p); p++)
350 if (!*p || *p == '#')
355 err = gpg_error (GPG_ERR_SYNTAX);
361 if ((p = strchr (p, ':')))
363 /* Colon found: Keyword with value. */
365 for (; spacep (p); p++)
369 err = gpg_error (GPG_ERR_MISSING_VALUE);
375 for (i=0; i < DIM (keywords); i++)
376 if (!ascii_strcasecmp (keywords[i].name, keyword))
378 if (!(i < DIM (keywords)))
382 err = gpg_error (GPG_ERR_INV_NAME);
386 switch (keywords[i].token)
388 case TOK_MAILBOX_ONLY: flags->mailbox_only = 1; break;
389 case TOK_DANE_ONLY: flags->dane_only = 1; break;
390 case TOK_AUTH_SUBMIT: flags->auth_submit = 1; break;
391 case TOK_MAX_PENDING:
394 err = gpg_error (GPG_ERR_SYNTAX);
397 /* FIXME: Define whether these are seconds, hours, or days
398 * and decide whether to allow other units. */
399 flags->max_pending = atoi (value);
404 if (!err && !es_feof (stream))
405 err = gpg_error_from_syserror ();
409 log_error ("error reading '%s', line %d: %s\n",
410 es_fname_get (stream), lnr, gpg_strerror (err));