diff options
author | Werner Koch <[email protected]> | 2025-06-02 10:42:59 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2025-06-02 10:42:59 +0000 |
commit | e6463d7fe097b39c9e8952ef9f5758fa0ee0e4bd (patch) | |
tree | 38222b8338276d8de0b92130dd43b003ec46754c /tools | |
parent | gpg-mail-tube: Support templates. (diff) | |
download | gnupg-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.c | 5 | ||||
-rw-r--r-- | tools/gpg-wks-client.c | 70 | ||||
-rw-r--r-- | tools/gpg-wks-server.c | 184 | ||||
-rw-r--r-- | tools/gpg-wks.h | 1 | ||||
-rw-r--r-- | tools/wks-receive.c | 39 |
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; } |