aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2025-06-02 10:42:59 +0000
committerWerner Koch <[email protected]>2025-06-02 10:42:59 +0000
commite6463d7fe097b39c9e8952ef9f5758fa0ee0e4bd (patch)
tree38222b8338276d8de0b92130dd43b003ec46754c /tools
parentgpg-mail-tube: Support templates. (diff)
downloadgnupg-e6463d7fe097b39c9e8952ef9f5758fa0ee0e4bd.tar.gz
gnupg-e6463d7fe097b39c9e8952ef9f5758fa0ee0e4bd.zip
wks: Use templates for the server responses.
* common/helpfile.c (gnupg_get_template): Add arg locale_override and adjust all callers. * tools/wks-receive.c (struct receive_ctx_s): Add field ct_language. (get_language): New. (new_part): Call it. (wks_receive): Pass language to the result callback. * tools/gpg-wks-client.c (short_locale): New. (main): Get and store the current locale. (command_create): Fix a glitch for the Posteo hack. Insert the locale into the confirmation request. (send_confirmation_response): Ditto. * tools/gpg-wks-server.c (struct server_ctx_s): Add field language. (only_ascii): New. (struct my_subst_vars_s, my_subst_vars_cb, my_subst_vars): New. (send_confirmation_request): Use a template. (send_congratulation_message): Ditto. (check_and_publish): Pss ctx to send_congratulation_message. (command_receive_cb): Add arg language. * doc/wks-utils.txt, doc/wks-utils.de.txt: New. * doc/Makefile.am (helpfiles): Add them. -- GnuPG-bug-id: 7381 Note that the subject is not yet translated or templated due to a missing header encoding function.
Diffstat (limited to 'tools')
-rw-r--r--tools/gpg-mail-tube.c5
-rw-r--r--tools/gpg-wks-client.c70
-rw-r--r--tools/gpg-wks-server.c184
-rw-r--r--tools/gpg-wks.h1
-rw-r--r--tools/wks-receive.c39
5 files changed, 234 insertions, 65 deletions
diff --git a/tools/gpg-mail-tube.c b/tools/gpg-mail-tube.c
index 7c3c6d0e3..c672651de 100644
--- a/tools/gpg-mail-tube.c
+++ b/tools/gpg-mail-tube.c
@@ -371,7 +371,7 @@ main (int argc, char **argv)
}
-/* Return true if TSRING has only ascii chacrterst or is NULL. */
+/* Return true if STRING has only ascii characters or is NULL. */
static int
only_ascii (const char *string)
{
@@ -571,7 +571,8 @@ mail_tube_encrypt (estream_t fpin, strlist_t recipients)
ct_is_text? "encrypted-file-attached"
: "encrypted-mail-attached",
(GET_TEMPLATE_SUBST_ENVVARS
- | GET_TEMPLATE_CRLF));
+ | GET_TEMPLATE_CRLF),
+ NULL);
if (templ && !only_ascii (templ))
{
charset = "utf-8";
diff --git a/tools/gpg-wks-client.c b/tools/gpg-wks-client.c
index ef11a4e3e..2d037faef 100644
--- a/tools/gpg-wks-client.c
+++ b/tools/gpg-wks-client.c
@@ -155,6 +155,8 @@ const char *fake_submission_addr;
static char **blacklist_array;
static size_t blacklist_array_len;
+/* The current locale in the short form (e.g. "de" instead of "de_DE") */
+static char *short_locale;
static void wrong_args (const char *t1, const char *t2) GPGRT_ATTR_NORETURN;
static void add_blacklist (const char *fname);
@@ -168,7 +170,9 @@ static gpg_error_t encrypt_response (estream_t *r_output, estream_t input,
const char *fingerprint);
static gpg_error_t read_confirmation_request (estream_t msg);
static gpg_error_t command_receive_cb (void *opaque,
- const char *mediatype, estream_t fp,
+ const char *mediatype,
+ const char *language,
+ estream_t fp,
unsigned int flags);
static gpg_error_t command_mirror (char *domain[]);
@@ -374,6 +378,20 @@ main (int argc, char **argv)
!opt.no_autostart);
+ /* Get the short form of the current locale. */
+ {
+ const char *locname = gnupg_messages_locale_name ();
+ char *p;
+
+ if (locname && *locname && strcmp (locname, "C"))
+ {
+ short_locale = xstrdup (locname);
+ if ((p = strpbrk (short_locale, "_.@/")))
+ *p = 0;
+ gpgrt_annotate_leaked_object (short_locale);
+ }
+ }
+
/* Check that the top directory exists. */
if (cmd == aInstallKey || cmd == aRemoveKey || cmd == aMirror)
{
@@ -1338,16 +1356,32 @@ command_create (const char *fingerprint, const char *userid)
}
}
+ /* Hack to support posteo but let them disable this by setting the
+ * new policy-version flag. */
+ if (policy->protocol_version < 3
+ && !ascii_strcasecmp (domain, "posteo.de"))
+ {
+ log_info ("Warning: Using draft-1 method for domain '%s'\n", domain);
+ no_encrypt = 1;
+ posteo_hack = 1;
+ }
+
/* Now put the armor around the key. */
{
estream_t newkey;
+ char *prefix;
+
+ prefix = xstrconcat
+ ("Content-Type: application/pgp-keys\n",
+ short_locale && *short_locale? "Content-Language: " : "",
+ short_locale && *short_locale? short_locale : "",
+ short_locale && *short_locale? "\n" : "",
+ "\n", NULL);
es_rewind (key);
- err = wks_armor_key (&newkey, key,
- no_encrypt? NULL
- /* */ : ("Content-Type: application/pgp-keys\n"
- "\n"));
+ err = wks_armor_key (&newkey, key, no_encrypt? NULL : prefix);
+ xfree (prefix);
if (err)
{
log_error ("error armoring key: %s\n", gpg_strerror (err));
@@ -1357,16 +1391,6 @@ command_create (const char *fingerprint, const char *userid)
key = newkey;
}
- /* Hack to support posteo but let them disable this by setting the
- * new policy-version flag. */
- if (policy->protocol_version < 3
- && !ascii_strcasecmp (domain, "posteo.de"))
- {
- log_info ("Warning: Using draft-1 method for domain '%s'\n", domain);
- no_encrypt = 1;
- posteo_hack = 1;
- }
-
/* Encrypt the key part. */
if (!no_encrypt)
{
@@ -1415,8 +1439,10 @@ command_create (const char *fingerprint, const char *userid)
goto leave;
}
- err = mime_maker_add_header (mime, "Content-type",
+ err = mime_maker_add_header (mime, "Content-Type",
"application/pgp-keys");
+ if (!err && short_locale && *short_locale)
+ err = mime_maker_add_header (mime, "Content-Language", short_locale);
if (err)
goto leave;
@@ -1596,10 +1622,11 @@ send_confirmation_response (const char *sender, const char *address,
* only our client will see it. */
if (encrypt)
{
- es_fputs ("Content-Type: application/vnd.gnupg.wks\n"
- "Content-Transfer-Encoding: 8bit\n"
- "\n",
- body);
+ es_fprintf (body,
+ "Content-Type: application/vnd.gnupg.wks\n"
+ "Content-Transfer-Encoding: 8bit\n"
+ "Content-Language: %s\n"
+ "\n", (short_locale && *short_locale)? short_locale : "en");
}
es_fprintf (body, ("type: confirmation-response\n"
@@ -1858,12 +1885,13 @@ read_confirmation_request (estream_t msg)
/* Called from the MIME receiver to process the plain text data in MSG. */
static gpg_error_t
-command_receive_cb (void *opaque, const char *mediatype,
+command_receive_cb (void *opaque, const char *mediatype, const char *language,
estream_t msg, unsigned int flags)
{
gpg_error_t err;
(void)opaque;
+ (void)language;
(void)flags;
if (!strcmp (mediatype, "application/vnd.gnupg.wks"))
diff --git a/tools/gpg-wks-server.c b/tools/gpg-wks-server.c
index 31de67618..0e216cec6 100644
--- a/tools/gpg-wks-server.c
+++ b/tools/gpg-wks-server.c
@@ -142,6 +142,7 @@ struct server_ctx_s
{
char *fpr;
uidinfo_list_t mboxes; /* List with addr-specs taken from the UIDs. */
+ char *language; /* Requested language. */
unsigned int draft_version_2:1; /* Client supports the draft 2. */
};
typedef struct server_ctx_s *server_ctx_t;
@@ -157,7 +158,9 @@ static int opt_with_file;
static gpg_error_t get_domain_list (strlist_t *r_list);
static gpg_error_t command_receive_cb (void *opaque,
- const char *mediatype, estream_t fp,
+ const char *mediatype,
+ const char *language,
+ estream_t fp,
unsigned int flags);
static gpg_error_t command_list_domains (void);
static gpg_error_t command_revoke_key (const char *mailaddr);
@@ -432,6 +435,54 @@ main (int argc, char **argv)
}
+/* Return true if STRING has only ascii characters or is NULL. */
+static int
+only_ascii (const char *string)
+{
+ if (string)
+ for ( ; *string; string++)
+ if ((*string & 0x80))
+ return 0;
+ return 1;
+}
+
+
+struct my_subst_vars_s
+{
+ const char *address;
+};
+
+
+/* Helper for my_subst_vars. */
+static const char *
+my_subst_vars_cb (void *cookie, const char *name)
+{
+ struct my_subst_vars_s *parm = cookie;
+ const char *s;
+
+ if (name && !strcmp (name, "sigdelim"))
+ s = "-- ";
+ else if (name && !strcmp (name, "address"))
+ s = parm->address;
+ else /* Assume envvar. */
+ s = getenv (name);
+ return s? s : "";
+}
+
+
+/* Substitute all envvars in TEMPL and the var $address my the value
+ * of MBOX. Return a a new malloced string or NULL. */
+static char *
+my_subst_vars (server_ctx_t ctx, const char *templ, const char *mbox)
+{
+ struct my_subst_vars_s parm;
+
+ (void)ctx;
+ parm.address = mbox;
+ return substitute_vars (templ, my_subst_vars_cb, &parm);
+}
+
+
/* Take the key in KEYFILE and write it to OUTFILE in binary encoding.
* If ADDRSPEC is given only matching user IDs are included in the
* output. */
@@ -991,6 +1042,9 @@ send_confirmation_request (server_ctx_t ctx,
char *from_buffer = NULL;
const char *from;
strlist_t sl;
+ char *templ = NULL;
+ char *p;
+ const char *cttype, *ctencode;
from = from_buffer = get_submission_address (mbox);
if (!from)
@@ -1123,25 +1177,48 @@ send_confirmation_request (server_ctx_t ctx,
goto leave;
partid = mime_maker_get_partid (mime);
- err = mime_maker_add_header (mime, "Content-Type", "text/plain");
+ templ = gnupg_get_template ("wks-utils", "server.confirm.body",
+ 0, ctx->language);
+ if (templ)
+ {
+ p = my_subst_vars (ctx, templ, mbox);
+ xfree (templ);
+ templ = p;
+ }
+
+ if (templ && !only_ascii (templ))
+ {
+ cttype = "text/plain; charset=utf-8";
+ ctencode = "quoted-printable";
+ p = mime_maker_qp_encode (templ);
+ if (!p)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("QP encoding failed: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+ xfree (templ);
+ templ = p;
+ }
+ else
+ {
+ cttype = "text/plain";
+ ctencode = NULL;
+ }
+
+ err = mime_maker_add_header (mime, "Content-Type", cttype);
+ if (err)
+ goto leave;
+ if (ctencode)
+ err = mime_maker_add_header (mime,
+ "Content-Transfer-Encoding", ctencode);
if (err)
goto leave;
- err = mime_maker_add_body
- (mime,
+ err = mime_maker_add_body (mime, templ? templ :
"This message has been send to confirm your request\n"
"to publish your key. If you did not request a key\n"
- "publication, simply ignore this message.\n"
- "\n"
- "Most mail software can handle this kind of message\n"
- "automatically and thus you would not have seen this\n"
- "message. It seems that your client does not fully\n"
- "support this service. The web page\n"
- "\n"
- " https://gnupg.org/faq/wkd.html\n"
- "\n"
- "explains how you can process this message anyway in\n"
- "a few manual steps.\n");
+ "publication, simply ignore this message.\n");
if (err)
goto leave;
@@ -1180,6 +1257,7 @@ send_confirmation_request (server_ctx_t ctx,
err = wks_send_mime (mime);
leave:
+ xfree (templ);
mime_maker_release (mime);
es_fclose (signature);
es_fclose (signeddata);
@@ -1285,7 +1363,8 @@ process_new_key (server_ctx_t ctx, estream_t key)
/* Send a message to tell the user at MBOX that their key has been
* published. FNAME the name of the file with the key. */
static gpg_error_t
-send_congratulation_message (const char *mbox, const char *keyfile)
+send_congratulation_message (server_ctx_t ctx,
+ const char *mbox, const char *keyfile)
{
gpg_error_t err;
estream_t body = NULL;
@@ -1294,6 +1373,9 @@ send_congratulation_message (const char *mbox, const char *keyfile)
char *from_buffer = NULL;
const char *from;
strlist_t sl;
+ char *templ = NULL;
+ char *p;
+ const char *charset, *ctencode;
from = from_buffer = get_submission_address (mbox);
if (!from)
@@ -1315,28 +1397,47 @@ send_congratulation_message (const char *mbox, const char *keyfile)
log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
goto leave;
}
- /* It is fine to use 8 bit encoding because that is encrypted and
- * only our client will see it. */
- es_fputs ("Content-Type: text/plain; charset=utf-8\n"
- "Content-Transfer-Encoding: 8bit\n"
- "\n",
- body);
-
- es_fprintf (body,
- "Hello!\n\n"
- "The key for your address '%s' has been published\n"
- "and can now be retrieved from the Web Key Directory.\n"
- "\n"
- "For more information on this system see:\n"
- "\n"
- " https://gnupg.org/faq/wkd.html\n"
- "\n"
- "Best regards\n"
- "\n"
- " GnuPG Key Publisher\n\n\n"
- "-- \n"
- "For information on GnuPG see: %s\n",
- mbox, "https://gnupg.org");
+
+ templ = gnupg_get_template ("wks-utils", "server.publish.congrats",
+ 0, ctx->language);
+ if (templ)
+ {
+ p = my_subst_vars (ctx, templ, mbox);
+ xfree (templ);
+ templ = p;
+ }
+
+ if (templ && !only_ascii (templ))
+ {
+ charset = "utf-8";
+ ctencode = "Content-Transfer-Encoding: quoted-printable\n";
+ p = mime_maker_qp_encode (templ);
+ if (!p)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("QP encoding failed: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+ xfree (templ);
+ templ = p;
+ }
+ else
+ {
+ charset = "us-ascii";
+ ctencode = "";
+ }
+
+
+ es_fprintf (body, "Content-Type: text/plain; charset=%s\n%s\n",
+ charset, ctencode);
+
+ if (templ)
+ es_fputs (templ, body);
+ else
+ es_fprintf (body, "Hello!\n\n"
+ "The key for your address '%s' has been published\n"
+ "and can now be retrieved from the Web Key Directory.\n",
+ mbox);
es_rewind (body);
err = encrypt_stream (&bodyenc, body, keyfile);
@@ -1399,6 +1500,7 @@ send_congratulation_message (const char *mbox, const char *keyfile)
err = wks_send_mime (mime);
leave:
+ xfree (templ);
mime_maker_release (mime);
es_fclose (bodyenc);
es_fclose (body);
@@ -1516,7 +1618,7 @@ check_and_publish (server_ctx_t ctx, const char *address, const char *nonce)
fnewname, gpg_strerror (gpg_err_code_from_syserror()));
log_info ("key %s published for '%s'\n", ctx->fpr, address);
- send_congratulation_message (address, fnewname);
+ send_congratulation_message (ctx, address, fnewname);
/* Try to publish as DANE record if the DANE directory exists. */
xfree (fname);
@@ -1645,7 +1747,7 @@ process_confirmation_response (server_ctx_t ctx, estream_t msg)
/* Called from the MIME receiver to process the plain text data in MSG . */
static gpg_error_t
-command_receive_cb (void *opaque, const char *mediatype,
+command_receive_cb (void *opaque, const char *mediatype, const char *language,
estream_t msg, unsigned int flags)
{
gpg_error_t err;
@@ -1656,6 +1758,8 @@ command_receive_cb (void *opaque, const char *mediatype,
memset (&ctx, 0, sizeof ctx);
if ((flags & WKS_RECEIVE_DRAFT2))
ctx.draft_version_2 = 1;
+ if (language)
+ ctx.language = xtrystrdup (language);
if (!strcmp (mediatype, "application/pgp-keys"))
err = process_new_key (&ctx, msg);
diff --git a/tools/gpg-wks.h b/tools/gpg-wks.h
index 0601d48fe..3049aaa9d 100644
--- a/tools/gpg-wks.h
+++ b/tools/gpg-wks.h
@@ -127,6 +127,7 @@ gpg_error_t wks_cmd_print_wkd_url (const char *userid);
gpg_error_t wks_receive (estream_t fp,
gpg_error_t (*result_cb)(void *opaque,
const char *mediatype,
+ const char *language,
estream_t data,
unsigned int flags),
void *cb_data);
diff --git a/tools/wks-receive.c b/tools/wks-receive.c
index ecd8cafe3..6bc1318cc 100644
--- a/tools/wks-receive.c
+++ b/tools/wks-receive.c
@@ -50,6 +50,7 @@ struct receive_ctx_s
estream_t signature;
estream_t key_data;
estream_t wkd_data;
+ char *ct_language; /* The short locale of the conent or NULL. */
unsigned int collect_key_data:1;
unsigned int collect_wkd_data:1;
unsigned int draft_version_2:1; /* This is a draft version 2 request. */
@@ -288,6 +289,34 @@ t2body (void *cookie, int level)
}
+/* Get the Content-Language from the curent MIME header and store it
+ * in CTX. */
+static void
+get_language (receive_ctx_t ctx)
+{
+ rfc822parse_t msg;
+ char *value, *p;
+ size_t valueoff;
+
+ msg = mime_parser_rfc822parser (ctx->parser);
+ if (msg)
+ {
+ value = rfc822parse_get_field (msg, "Content-Language",
+ -1, &valueoff);
+ if (value)
+ {
+ xfree (ctx->ct_language);
+ ctx->ct_language = xtrystrdup (value+valueoff);
+ /* Take only the first short language. */
+ if (ctx->ct_language
+ && (p = strpbrk (ctx->ct_language, " \t,_.@/")))
+ *p = 0;
+ rfc822_free (value);
+ }
+ }
+}
+
+
static gpg_error_t
new_part (void *cookie, const char *mediatype, const char *mediasubtype)
{
@@ -308,6 +337,7 @@ new_part (void *cookie, const char *mediatype, const char *mediasubtype)
}
else
{
+ get_language (ctx);
ctx->key_data = es_fopenmem (0, "w+b");
if (!ctx->key_data)
{
@@ -333,6 +363,7 @@ new_part (void *cookie, const char *mediatype, const char *mediasubtype)
}
else
{
+ get_language (ctx);
ctx->wkd_data = es_fopenmem (0, "w+b");
if (!ctx->wkd_data)
{
@@ -410,6 +441,7 @@ gpg_error_t
wks_receive (estream_t fp,
gpg_error_t (*result_cb)(void *opaque,
const char *mediatype,
+ const char *language,
estream_t data,
unsigned int flags),
void *cb_data)
@@ -482,6 +514,8 @@ wks_receive (estream_t fp,
if (DBG_MIME)
{
es_rewind (ctx->key_data);
+ if (ctx->ct_language)
+ log_debug ("Language: '%s'\n", ctx->ct_language);
log_debug ("Key: '");
log_printf ("\n");
while ((c = es_getc (ctx->key_data)) != EOF)
@@ -492,7 +526,7 @@ wks_receive (estream_t fp,
{
es_rewind (ctx->key_data);
err = result_cb (cb_data, "application/pgp-keys",
- ctx->key_data, flags);
+ ctx->ct_language, ctx->key_data, flags);
if (err)
goto leave;
}
@@ -512,7 +546,7 @@ wks_receive (estream_t fp,
{
es_rewind (ctx->wkd_data);
err = result_cb (cb_data, "application/vnd.gnupg.wks",
- ctx->wkd_data, flags);
+ ctx->ct_language, ctx->wkd_data, flags);
if (err)
goto leave;
}
@@ -529,6 +563,7 @@ wks_receive (estream_t fp,
es_fclose (ctx->signature);
es_fclose (ctx->key_data);
es_fclose (ctx->wkd_data);
+ xfree (ctx->ct_language);
xfree (ctx);
return err;
}