mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
Merge branch 'STABLE-BRANCH-2-2' into master
-- Signed-off-by: Werner Koch <wk@gnupg.org> Conflicts: NEWS - include release info from 2.2.1 configure.ac - keep master.
This commit is contained in:
commit
cd2d758f3f
27
NEWS
27
NEWS
@ -1,10 +1,29 @@
|
||||
Noteworthy changes in version 2.3.0 (unreleased)
|
||||
------------------------------------------------
|
||||
|
||||
Changes also found in 2.2.1:
|
||||
|
||||
* Release dates of 2.2.x versions:
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Version 2.2.1 (unreleased)
|
||||
* gpg: Fix formatting of the user id in batch mode key generation
|
||||
if only "name-email" is given.
|
||||
|
||||
* gpgv: Fix annoying "not suitable for" warnings.
|
||||
|
||||
* wks: Convey only the newest user id to the provider. This is the
|
||||
case if different names are used with the same addr-spec.
|
||||
|
||||
* wks: Create a complying user id for provider policy mailbox-only.
|
||||
|
||||
* wks: Add workaround for posteo.de.
|
||||
|
||||
* scd: Fix the use of large ECC keys with an OpenPGP card.
|
||||
|
||||
* dirmngr: Use system provided root certificates if no specific HKP
|
||||
certificates are configured. If build with GNUTLS, this was
|
||||
already the case.
|
||||
|
||||
Release dates of 2.2.x versions:
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Version 2.2.1 (2017-09-19)
|
||||
|
||||
|
||||
Noteworthy changes in version 2.2.0 (2017-08-28)
|
||||
@ -18,6 +37,8 @@ Noteworthy changes in version 2.2.0 (2017-08-28)
|
||||
|
||||
* Fixed a few minor bugs.
|
||||
|
||||
See-also: gnupg-announce/2017q3/000413.html
|
||||
|
||||
|
||||
Noteworthy changes in version 2.1.23 (2017-08-09)
|
||||
-------------------------------------------------
|
||||
|
@ -94,6 +94,10 @@ static int initialization_done;
|
||||
/* Total number of non-permanent certificates. */
|
||||
static unsigned int total_nonperm_certificates;
|
||||
|
||||
/* For each cert class the corresponding bit is set if at least one
|
||||
* certificate of that class is loaded permanetly. */
|
||||
static unsigned int any_cert_of_class;
|
||||
|
||||
|
||||
#ifdef HAVE_W32_SYSTEM
|
||||
/* We load some functions dynamically. Provide typedefs for tehse
|
||||
@ -343,7 +347,9 @@ put_cert (ksba_cert_t cert, int permanent, unsigned int trustclass,
|
||||
ci->permanent = !!permanent;
|
||||
ci->trustclasses = trustclass;
|
||||
|
||||
if (!permanent)
|
||||
if (permanent)
|
||||
any_cert_of_class |= trustclass;
|
||||
else
|
||||
total_nonperm_certificates++;
|
||||
|
||||
return 0;
|
||||
@ -758,6 +764,7 @@ cert_cache_deinit (int full)
|
||||
}
|
||||
|
||||
total_nonperm_certificates = 0;
|
||||
any_cert_of_class = 0;
|
||||
initialization_done = 0;
|
||||
release_cache_lock ();
|
||||
}
|
||||
@ -814,6 +821,15 @@ cert_cache_print_stats (void)
|
||||
}
|
||||
|
||||
|
||||
/* Return true if any cert of a class in MASK is permanently
|
||||
* loaded. */
|
||||
int
|
||||
cert_cache_any_in_class (unsigned int mask)
|
||||
{
|
||||
return !!(any_cert_of_class & mask);
|
||||
}
|
||||
|
||||
|
||||
/* Put CERT into the certificate cache. */
|
||||
gpg_error_t
|
||||
cache_cert (ksba_cert_t cert)
|
||||
|
@ -39,6 +39,9 @@ void cert_cache_deinit (int full);
|
||||
/* Print some statistics to the log file. */
|
||||
void cert_cache_print_stats (void);
|
||||
|
||||
/* Return true if any cert of a class in MASK is permanently loaded. */
|
||||
int cert_cache_any_in_class (unsigned int mask);
|
||||
|
||||
/* Compute the fingerprint of the certificate CERT and put it into
|
||||
the 20 bytes large buffer DIGEST. Return address of this buffer. */
|
||||
unsigned char *cert_compute_fpr (ksba_cert_t cert, unsigned char *digest);
|
||||
|
@ -91,6 +91,12 @@ gnupg_http_tls_verify_cb (void *opaque,
|
||||
validate_flags |= VALIDATE_FLAG_TRUST_HKP;
|
||||
if ((http_flags & HTTP_FLAG_TRUST_SYS))
|
||||
validate_flags |= VALIDATE_FLAG_TRUST_SYSTEM;
|
||||
|
||||
/* If HKP trust is requested and there are no HKP certificates
|
||||
* configured, also try thye standard system certificates. */
|
||||
if ((validate_flags & VALIDATE_FLAG_TRUST_HKP)
|
||||
&& !cert_cache_any_in_class (CERTTRUST_CLASS_HKP))
|
||||
validate_flags |= VALIDATE_FLAG_TRUST_SYSTEM;
|
||||
}
|
||||
|
||||
if ((http_flags & HTTP_FLAG_NO_CRL))
|
||||
|
@ -78,7 +78,9 @@ the command is a properly formatted mail with all standard headers.
|
||||
This mail can be fed to @command{sendmail(8)} or any other tool to
|
||||
actually send that mail. If @command{sendmail(8)} is installed the
|
||||
option @option{--send} can be used to directly send the created
|
||||
request.
|
||||
request. If the provider request a 'mailbox-only' user id and no such
|
||||
user id is found, @command{gpg-wks-client} will try an additional user
|
||||
id.
|
||||
|
||||
The @option{--receive} and @option{--read} commands are used to
|
||||
process confirmation mails as send from the service provider. The
|
||||
|
@ -202,6 +202,7 @@ main( int argc, char **argv )
|
||||
dotlock_disable ();
|
||||
gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
|
||||
additional_weak_digest("MD5");
|
||||
gnupg_initialize_compliance (GNUPG_MODULE_NAME_GPG);
|
||||
|
||||
pargs.argc = &argc;
|
||||
pargs.argv = &argv;
|
||||
|
@ -3529,7 +3529,14 @@ proc_parameter_file (ctrl_t ctrl, struct para_data_s *para, const char *fname,
|
||||
if( s2 )
|
||||
p = stpcpy(stpcpy(stpcpy(p," ("), s2 ),")");
|
||||
if( s3 )
|
||||
p = stpcpy(stpcpy(stpcpy(p," <"), s3 ),">");
|
||||
{
|
||||
/* If we have only the email part, do not add the space
|
||||
* and the angle brackets. */
|
||||
if (*r->u.value)
|
||||
p = stpcpy(stpcpy(stpcpy(p," <"), s3 ),">");
|
||||
else
|
||||
p = stpcpy (p, s3);
|
||||
}
|
||||
append_to_parameter (para, r);
|
||||
have_user_id=1;
|
||||
}
|
||||
|
4
po/de.po
4
po/de.po
@ -9,7 +9,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gnupg-2.1.0\n"
|
||||
"Report-Msgid-Bugs-To: translations@gnupg.org\n"
|
||||
"PO-Revision-Date: 2017-08-09 12:49+0200\n"
|
||||
"PO-Revision-Date: 2017-09-26 11:51+0200\n"
|
||||
"Last-Translator: Werner Koch <wk@gnupg.org>\n"
|
||||
"Language-Team: German <de@li.org>\n"
|
||||
"Language: de\n"
|
||||
@ -194,7 +194,7 @@ msgid "failed to create stream from socket: %s\n"
|
||||
msgstr "Das Erzeugen eines Datenstroms aus dem Socket schlug fehl: %s\n"
|
||||
|
||||
msgid "Please insert the card with serial number"
|
||||
msgstr "Die legen Sie die Karte mit der folgenden Seriennummer ein:"
|
||||
msgstr "Bitte legen Sie die Karte mit der folgenden Seriennummer ein"
|
||||
|
||||
msgid "Please remove the current card and insert the one with serial number"
|
||||
msgstr ""
|
||||
|
15
po/el.po
15
po/el.po
@ -1,20 +1,21 @@
|
||||
# Greek Translation of GnuPG.
|
||||
# Copyright (C) 2002 Free Software Foundation, Inc.
|
||||
# Dokianakis Theofanis <madf@hellug.gr>, 2002.
|
||||
# !-- psbl.surriel.com rejected (2011-01-11)
|
||||
# !-- psbl.surriel.com rejected (2011-01-11)
|
||||
# Designated-Translator: none
|
||||
#
|
||||
# Dimitris Maroulidis <dmaroulidis@dimitrismaroulidis.com>, 2017.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: gnupg-1.1.92\n"
|
||||
"Report-Msgid-Bugs-To: translations@gnupg.org\n"
|
||||
"PO-Revision-Date: 2003-06-27 12:00+0200\n"
|
||||
"Last-Translator: Dokianakis Theofanis <madf@hellug.gr>\n"
|
||||
"Language-Team: Greek <nls@tux.hellug.gr>\n"
|
||||
"PO-Revision-Date: 2017-09-14 21:14+0300\n"
|
||||
"Last-Translator: Dimitris Maroulidis <dmaroulidis@dimitrismaroulidis.com>\n"
|
||||
"Language-Team: team@gnome.gr\n"
|
||||
"Language: el\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#, fuzzy, c-format
|
||||
msgid "failed to acquire the pinentry lock: %s\n"
|
||||
@ -4423,7 +4424,7 @@ msgid "Keyring"
|
||||
msgstr "Κλειδοθήκη"
|
||||
|
||||
msgid "Primary key fingerprint:"
|
||||
msgstr "Αποτύπωμα πρωτεύων κλειδιού:"
|
||||
msgstr "Αποτύπωμα πρωτεύοντος κλειδιού:"
|
||||
|
||||
msgid " Subkey fingerprint:"
|
||||
msgstr " Αποτύπωμα υποκλειδιού:"
|
||||
@ -4431,7 +4432,7 @@ msgstr " Αποτύπωμα υποκλειδιού:"
|
||||
#. TRANSLATORS: this should fit into 24 bytes so that the
|
||||
#. * fingerprint data is properly aligned with the user ID
|
||||
msgid " Primary key fingerprint:"
|
||||
msgstr " Αποτύπωμα πρωτεύων κλειδιού:"
|
||||
msgstr " Αποτύπωμα πρωτ. κλειδιού:"
|
||||
|
||||
msgid " Subkey fingerprint:"
|
||||
msgstr " Αποτύπωμα υποκλειδιού:"
|
||||
|
@ -119,7 +119,7 @@ const char *fake_submission_addr;
|
||||
static void wrong_args (const char *text) GPGRT_ATTR_NORETURN;
|
||||
static gpg_error_t command_supported (char *userid);
|
||||
static gpg_error_t command_check (char *userid);
|
||||
static gpg_error_t command_send (const char *fingerprint, char *userid);
|
||||
static gpg_error_t command_send (const char *fingerprint, const char *userid);
|
||||
static gpg_error_t encrypt_response (estream_t *r_output, estream_t input,
|
||||
const char *addrspec,
|
||||
const char *fingerprint);
|
||||
@ -348,13 +348,13 @@ get_key_status_cb (void *opaque, const char *keyword, char *args)
|
||||
|
||||
|
||||
/* Get a key by fingerprint from gpg's keyring and make sure that the
|
||||
* mail address ADDRSPEC is included in the key. The key is returned
|
||||
* as a new memory stream at R_KEY.
|
||||
*
|
||||
* Fixme: After we have implemented import and export filters for gpg
|
||||
* this function shall only return a key with just this user id. */
|
||||
* mail address ADDRSPEC is included in the key. If EXACT is set the
|
||||
* returned user id must match Addrspec exactly and not just in the
|
||||
* addr-spec (mailbox) part. The key is returned as a new memory
|
||||
* stream at R_KEY. */
|
||||
static gpg_error_t
|
||||
get_key (estream_t *r_key, const char *fingerprint, const char *addrspec)
|
||||
get_key (estream_t *r_key, const char *fingerprint, const char *addrspec,
|
||||
int exact)
|
||||
{
|
||||
gpg_error_t err;
|
||||
ccparray_t ccp;
|
||||
@ -379,7 +379,7 @@ get_key (estream_t *r_key, const char *fingerprint, const char *addrspec)
|
||||
es_fputs ("Content-Type: application/pgp-keys\n"
|
||||
"\n", key);
|
||||
|
||||
filterexp = es_bsprintf ("keep-uid=mbox = %s", addrspec);
|
||||
filterexp = es_bsprintf ("keep-uid=%s=%s", exact? "uid":"mbox", addrspec);
|
||||
if (!filterexp)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
@ -438,6 +438,49 @@ get_key (estream_t *r_key, const char *fingerprint, const char *addrspec)
|
||||
}
|
||||
|
||||
|
||||
/* Add the user id UID to the key identified by FINGERPRINT. */
|
||||
static gpg_error_t
|
||||
add_user_id (const char *fingerprint, const char *uid)
|
||||
{
|
||||
gpg_error_t err;
|
||||
ccparray_t ccp;
|
||||
const char **argv = NULL;
|
||||
|
||||
ccparray_init (&ccp, 0);
|
||||
|
||||
ccparray_put (&ccp, "--no-options");
|
||||
if (!opt.verbose)
|
||||
ccparray_put (&ccp, "--quiet");
|
||||
else if (opt.verbose > 1)
|
||||
ccparray_put (&ccp, "--verbose");
|
||||
ccparray_put (&ccp, "--batch");
|
||||
ccparray_put (&ccp, "--always-trust");
|
||||
ccparray_put (&ccp, "--quick-add-uid");
|
||||
ccparray_put (&ccp, fingerprint);
|
||||
ccparray_put (&ccp, uid);
|
||||
|
||||
ccparray_put (&ccp, NULL);
|
||||
argv = ccparray_get (&ccp, NULL);
|
||||
if (!argv)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
err = gnupg_exec_tool_stream (opt.gpg_program, argv, NULL,
|
||||
NULL, NULL,
|
||||
NULL, NULL);
|
||||
if (err)
|
||||
{
|
||||
log_error ("adding user id failed: %s\n", gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
leave:
|
||||
xfree (argv);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct decrypt_stream_parm_s
|
||||
{
|
||||
@ -600,8 +643,8 @@ command_check (char *userid)
|
||||
char *addrspec = NULL;
|
||||
estream_t key = NULL;
|
||||
char *fpr = NULL;
|
||||
strlist_t mboxes = NULL;
|
||||
strlist_t sl;
|
||||
uidinfo_list_t mboxes = NULL;
|
||||
uidinfo_list_t sl;
|
||||
int found = 0;
|
||||
|
||||
addrspec = mailbox_from_userid (userid);
|
||||
@ -647,10 +690,9 @@ command_check (char *userid)
|
||||
|
||||
/* Look closer at the key. */
|
||||
err = wks_list_key (key, &fpr, &mboxes);
|
||||
if (err || !fpr)
|
||||
if (err)
|
||||
{
|
||||
log_error ("error parsing key: %s\n",
|
||||
err? gpg_strerror (err) : "no fingerprint found");
|
||||
log_error ("error parsing key: %s\n", gpg_strerror (err));
|
||||
err = gpg_error (GPG_ERR_NO_PUBKEY);
|
||||
goto leave;
|
||||
}
|
||||
@ -660,10 +702,15 @@ command_check (char *userid)
|
||||
|
||||
for (sl = mboxes; sl; sl = sl->next)
|
||||
{
|
||||
if (!strcmp (sl->d, addrspec))
|
||||
if (sl->mbox && !strcmp (sl->mbox, addrspec))
|
||||
found = 1;
|
||||
if (opt.verbose)
|
||||
log_info (" addr-spec: %s\n", sl->d);
|
||||
{
|
||||
log_info (" user-id: %s\n", sl->uid);
|
||||
log_info (" created: %s\n", asctimestamp (sl->created));
|
||||
if (sl->mbox)
|
||||
log_info (" addr-spec: %s\n", sl->mbox);
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
@ -674,7 +721,7 @@ command_check (char *userid)
|
||||
|
||||
leave:
|
||||
xfree (fpr);
|
||||
free_strlist (mboxes);
|
||||
free_uidinfo_list (mboxes);
|
||||
es_fclose (key);
|
||||
xfree (addrspec);
|
||||
return err;
|
||||
@ -685,7 +732,7 @@ command_check (char *userid)
|
||||
/* Locate the key by fingerprint and userid and send a publication
|
||||
* request. */
|
||||
static gpg_error_t
|
||||
command_send (const char *fingerprint, char *userid)
|
||||
command_send (const char *fingerprint, const char *userid)
|
||||
{
|
||||
gpg_error_t err;
|
||||
KEYDB_SEARCH_DESC desc;
|
||||
@ -695,6 +742,12 @@ command_send (const char *fingerprint, char *userid)
|
||||
char *submission_to = NULL;
|
||||
mime_maker_t mime = NULL;
|
||||
struct policy_flags_s policy;
|
||||
int no_encrypt = 0;
|
||||
int posteo_hack = 0;
|
||||
const char *domain;
|
||||
uidinfo_list_t uidlist = NULL;
|
||||
uidinfo_list_t uid, thisuid;
|
||||
time_t thistime;
|
||||
|
||||
memset (&policy, 0, sizeof policy);
|
||||
|
||||
@ -706,6 +759,7 @@ command_send (const char *fingerprint, char *userid)
|
||||
err = gpg_error (GPG_ERR_INV_NAME);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
addrspec = mailbox_from_userid (userid);
|
||||
if (!addrspec)
|
||||
{
|
||||
@ -713,10 +767,14 @@ command_send (const char *fingerprint, char *userid)
|
||||
err = gpg_error (GPG_ERR_INV_USER_ID);
|
||||
goto leave;
|
||||
}
|
||||
err = get_key (&key, fingerprint, addrspec);
|
||||
err = get_key (&key, fingerprint, addrspec, 0);
|
||||
if (err)
|
||||
goto leave;
|
||||
|
||||
domain = strchr (addrspec, '@');
|
||||
log_assert (domain);
|
||||
domain++;
|
||||
|
||||
/* Get the submission address. */
|
||||
if (fake_submission_addr)
|
||||
{
|
||||
@ -727,11 +785,8 @@ command_send (const char *fingerprint, char *userid)
|
||||
err = wkd_get_submission_address (addrspec, &submission_to);
|
||||
if (err)
|
||||
{
|
||||
char *domain = strchr (addrspec, '@');
|
||||
if (domain)
|
||||
domain = domain + 1;
|
||||
log_error (_("looking up WKS submission address for %s: %s\n"),
|
||||
domain ? domain : addrspec, gpg_strerror (err));
|
||||
log_error (_("error looking up submission address for domain '%s': %s\n"),
|
||||
domain, gpg_strerror (err));
|
||||
if (gpg_err_code (err) == GPG_ERR_NO_DATA)
|
||||
log_error (_("this domain probably doesn't support WKS.\n"));
|
||||
goto leave;
|
||||
@ -762,14 +817,92 @@ command_send (const char *fingerprint, char *userid)
|
||||
if (policy.auth_submit)
|
||||
log_info ("no confirmation required for '%s'\n", addrspec);
|
||||
|
||||
/* Encrypt the key part. */
|
||||
es_rewind (key);
|
||||
err = encrypt_response (&keyenc, key, submission_to, fingerprint);
|
||||
/* In case the key has several uids with the same addr-spec we will
|
||||
* use the newest one. */
|
||||
err = wks_list_key (key, NULL, &uidlist);
|
||||
if (err)
|
||||
goto leave;
|
||||
es_fclose (key);
|
||||
key = NULL;
|
||||
{
|
||||
log_error ("error parsing key: %s\n",gpg_strerror (err));
|
||||
err = gpg_error (GPG_ERR_NO_PUBKEY);
|
||||
goto leave;
|
||||
}
|
||||
thistime = 0;
|
||||
thisuid = NULL;
|
||||
for (uid = uidlist; uid; uid = uid->next)
|
||||
{
|
||||
if (!uid->mbox)
|
||||
continue; /* Should not happen anyway. */
|
||||
if (policy.mailbox_only
|
||||
&& ascii_strcasecmp (uid->uid, uid->mbox))
|
||||
continue; /* UID has more than just the mailbox. */
|
||||
if (uid->created > thistime)
|
||||
{
|
||||
thistime = uid->created;
|
||||
thisuid = uid;
|
||||
}
|
||||
}
|
||||
if (!thisuid)
|
||||
thisuid = uidlist; /* This is the case for a missing timestamp. */
|
||||
if (opt.verbose)
|
||||
log_info ("submitting key with user id '%s'\n", thisuid->uid);
|
||||
|
||||
/* If we have more than one user id we need to filter the key to
|
||||
* include only THISUID. */
|
||||
if (uidlist->next)
|
||||
{
|
||||
estream_t newkey;
|
||||
|
||||
es_rewind (key);
|
||||
err = wks_filter_uid (&newkey, key, thisuid->uid);
|
||||
if (err)
|
||||
{
|
||||
log_error ("error filtering key: %s\n", gpg_strerror (err));
|
||||
err = gpg_error (GPG_ERR_NO_PUBKEY);
|
||||
goto leave;
|
||||
}
|
||||
es_fclose (key);
|
||||
key = newkey;
|
||||
}
|
||||
|
||||
if (policy.mailbox_only
|
||||
&& (!thisuid->mbox || ascii_strcasecmp (thisuid->uid, thisuid->mbox)))
|
||||
{
|
||||
log_info ("Warning: policy requires 'mailbox-only'"
|
||||
" - adding user id '%s'\n", addrspec);
|
||||
err = add_user_id (fingerprint, addrspec);
|
||||
if (err)
|
||||
goto leave;
|
||||
|
||||
/* Need to get the key again. This time we request filtering
|
||||
* for the full user id, so that we do not need check and filter
|
||||
* the key again. */
|
||||
es_fclose (key);
|
||||
key = NULL;
|
||||
err = get_key (&key, fingerprint, addrspec, 1);
|
||||
if (err)
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
es_rewind (key);
|
||||
err = encrypt_response (&keyenc, key, submission_to, fingerprint);
|
||||
if (err)
|
||||
goto leave;
|
||||
es_fclose (key);
|
||||
key = NULL;
|
||||
}
|
||||
|
||||
/* Send the key. */
|
||||
err = mime_maker_new (&mime, NULL);
|
||||
@ -787,40 +920,86 @@ command_send (const char *fingerprint, char *userid)
|
||||
|
||||
/* Tell server which draft we support. */
|
||||
err = mime_maker_add_header (mime, "Wks-Draft-Version",
|
||||
STR2(WKS_DRAFT_VERSION));
|
||||
STR2(WKS_DRAFT_VERSION));
|
||||
if (err)
|
||||
goto leave;
|
||||
|
||||
err = mime_maker_add_header (mime, "Content-Type",
|
||||
"multipart/encrypted; "
|
||||
"protocol=\"application/pgp-encrypted\"");
|
||||
if (err)
|
||||
goto leave;
|
||||
err = mime_maker_add_container (mime);
|
||||
if (err)
|
||||
goto leave;
|
||||
if (no_encrypt)
|
||||
{
|
||||
void *data;
|
||||
size_t datalen, n;
|
||||
|
||||
err = mime_maker_add_header (mime, "Content-Type",
|
||||
"application/pgp-encrypted");
|
||||
if (err)
|
||||
goto leave;
|
||||
err = mime_maker_add_body (mime, "Version: 1\n");
|
||||
if (err)
|
||||
goto leave;
|
||||
err = mime_maker_add_header (mime, "Content-Type",
|
||||
"application/octet-stream");
|
||||
if (err)
|
||||
goto leave;
|
||||
if (posteo_hack)
|
||||
{
|
||||
/* Needs a multipart/mixed with one(!) attachment. It does
|
||||
* not grok a non-multipart mail. */
|
||||
err = mime_maker_add_header (mime, "Content-Type", "multipart/mixed");
|
||||
if (err)
|
||||
goto leave;
|
||||
err = mime_maker_add_container (mime);
|
||||
if (err)
|
||||
goto leave;
|
||||
}
|
||||
|
||||
err = mime_maker_add_stream (mime, &keyenc);
|
||||
if (err)
|
||||
goto leave;
|
||||
err = mime_maker_add_header (mime, "Content-type",
|
||||
"application/pgp-keys");
|
||||
if (err)
|
||||
goto leave;
|
||||
|
||||
if (es_fclose_snatch (key, &data, &datalen))
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
key = NULL;
|
||||
/* We need to skip over the first line which has a content-type
|
||||
* header not needed here. */
|
||||
for (n=0; n < datalen ; n++)
|
||||
if (((const char *)data)[n] == '\n')
|
||||
{
|
||||
n++;
|
||||
break;
|
||||
}
|
||||
|
||||
err = mime_maker_add_body_data (mime, (char*)data + n, datalen - n);
|
||||
xfree (data);
|
||||
if (err)
|
||||
goto leave;
|
||||
}
|
||||
else
|
||||
{
|
||||
err = mime_maker_add_header (mime, "Content-Type",
|
||||
"multipart/encrypted; "
|
||||
"protocol=\"application/pgp-encrypted\"");
|
||||
if (err)
|
||||
goto leave;
|
||||
err = mime_maker_add_container (mime);
|
||||
if (err)
|
||||
goto leave;
|
||||
|
||||
err = mime_maker_add_header (mime, "Content-Type",
|
||||
"application/pgp-encrypted");
|
||||
if (err)
|
||||
goto leave;
|
||||
err = mime_maker_add_body (mime, "Version: 1\n");
|
||||
if (err)
|
||||
goto leave;
|
||||
err = mime_maker_add_header (mime, "Content-Type",
|
||||
"application/octet-stream");
|
||||
if (err)
|
||||
goto leave;
|
||||
|
||||
err = mime_maker_add_stream (mime, &keyenc);
|
||||
if (err)
|
||||
goto leave;
|
||||
}
|
||||
|
||||
err = wks_send_mime (mime);
|
||||
|
||||
leave:
|
||||
mime_maker_release (mime);
|
||||
xfree (submission_to);
|
||||
free_uidinfo_list (uidlist);
|
||||
es_fclose (keyenc);
|
||||
es_fclose (key);
|
||||
xfree (addrspec);
|
||||
|
@ -127,7 +127,7 @@ static struct debug_flags_s debug_flags [] =
|
||||
struct server_ctx_s
|
||||
{
|
||||
char *fpr;
|
||||
strlist_t mboxes; /* List of addr-specs taken from the UIDs. */
|
||||
uidinfo_list_t mboxes; /* List with addr-specs taken from the UIDs. */
|
||||
unsigned int draft_version_2:1; /* Client supports the draft 2. */
|
||||
};
|
||||
typedef struct server_ctx_s *server_ctx_t;
|
||||
@ -1092,7 +1092,7 @@ static gpg_error_t
|
||||
process_new_key (server_ctx_t ctx, estream_t key)
|
||||
{
|
||||
gpg_error_t err;
|
||||
strlist_t sl;
|
||||
uidinfo_list_t sl;
|
||||
const char *s;
|
||||
char *dname = NULL;
|
||||
char *nonce = NULL;
|
||||
@ -1101,27 +1101,25 @@ process_new_key (server_ctx_t ctx, estream_t key)
|
||||
|
||||
/* First figure out the user id from the key. */
|
||||
xfree (ctx->fpr);
|
||||
free_strlist (ctx->mboxes);
|
||||
free_uidinfo_list (ctx->mboxes);
|
||||
err = wks_list_key (key, &ctx->fpr, &ctx->mboxes);
|
||||
if (err)
|
||||
goto leave;
|
||||
if (!ctx->fpr)
|
||||
{
|
||||
log_error ("error parsing key (no fingerprint)\n");
|
||||
err = gpg_error (GPG_ERR_NO_PUBKEY);
|
||||
goto leave;
|
||||
}
|
||||
log_assert (ctx->fpr);
|
||||
log_info ("fingerprint: %s\n", ctx->fpr);
|
||||
for (sl = ctx->mboxes; sl; sl = sl->next)
|
||||
{
|
||||
log_info (" addr-spec: %s\n", sl->d);
|
||||
if (sl->mbox)
|
||||
log_info (" addr-spec: %s\n", sl->mbox);
|
||||
}
|
||||
|
||||
/* Walk over all user ids and send confirmation requests for those
|
||||
* we support. */
|
||||
for (sl = ctx->mboxes; sl; sl = sl->next)
|
||||
{
|
||||
s = strchr (sl->d, '@');
|
||||
if (!sl->mbox)
|
||||
continue;
|
||||
s = strchr (sl->mbox, '@');
|
||||
log_assert (s && s[1]);
|
||||
xfree (dname);
|
||||
dname = make_filename_try (opt.directory, s+1, NULL);
|
||||
@ -1133,26 +1131,26 @@ process_new_key (server_ctx_t ctx, estream_t key)
|
||||
|
||||
if (access (dname, W_OK))
|
||||
{
|
||||
log_info ("skipping address '%s': Domain not configured\n", sl->d);
|
||||
log_info ("skipping address '%s': Domain not configured\n", sl->mbox);
|
||||
continue;
|
||||
}
|
||||
if (get_policy_flags (&policybuf, sl->d))
|
||||
if (get_policy_flags (&policybuf, sl->mbox))
|
||||
{
|
||||
log_info ("skipping address '%s': Bad policy flags\n", sl->d);
|
||||
log_info ("skipping address '%s': Bad policy flags\n", sl->mbox);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (policybuf.auth_submit)
|
||||
{
|
||||
/* Bypass the confirmation stuff and publish the key as is. */
|
||||
log_info ("publishing address '%s'\n", sl->d);
|
||||
log_info ("publishing address '%s'\n", sl->mbox);
|
||||
/* FIXME: We need to make sure that we do this only for the
|
||||
* address in the mail. */
|
||||
log_debug ("auth-submit not yet working!\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
log_info ("storing address '%s'\n", sl->d);
|
||||
log_info ("storing address '%s'\n", sl->mbox);
|
||||
|
||||
xfree (nonce);
|
||||
xfree (fname);
|
||||
@ -1160,7 +1158,7 @@ process_new_key (server_ctx_t ctx, estream_t key)
|
||||
if (err)
|
||||
goto leave;
|
||||
|
||||
err = send_confirmation_request (ctx, sl->d, nonce, fname);
|
||||
err = send_confirmation_request (ctx, sl->mbox, nonce, fname);
|
||||
if (err)
|
||||
goto leave;
|
||||
}
|
||||
@ -1313,7 +1311,7 @@ check_and_publish (server_ctx_t ctx, const char *address, const char *nonce)
|
||||
char *hash = NULL;
|
||||
const char *domain;
|
||||
const char *s;
|
||||
strlist_t sl;
|
||||
uidinfo_list_t sl;
|
||||
char shaxbuf[32]; /* Used for SHA-1 and SHA-256 */
|
||||
|
||||
/* FIXME: There is a bug in name-value.c which adds white space for
|
||||
@ -1351,25 +1349,21 @@ check_and_publish (server_ctx_t ctx, const char *address, const char *nonce)
|
||||
|
||||
/* We need to get the fingerprint from the key. */
|
||||
xfree (ctx->fpr);
|
||||
free_strlist (ctx->mboxes);
|
||||
free_uidinfo_list (ctx->mboxes);
|
||||
err = wks_list_key (key, &ctx->fpr, &ctx->mboxes);
|
||||
if (err)
|
||||
goto leave;
|
||||
if (!ctx->fpr)
|
||||
{
|
||||
log_error ("error parsing key (no fingerprint)\n");
|
||||
err = gpg_error (GPG_ERR_NO_PUBKEY);
|
||||
goto leave;
|
||||
}
|
||||
log_assert (ctx->fpr);
|
||||
log_info ("fingerprint: %s\n", ctx->fpr);
|
||||
for (sl = ctx->mboxes; sl; sl = sl->next)
|
||||
log_info (" addr-spec: %s\n", sl->d);
|
||||
if (sl->mbox)
|
||||
log_info (" addr-spec: %s\n", sl->mbox);
|
||||
|
||||
/* Check that the key has 'address' as a user id. We use
|
||||
* case-insensitive matching because the client is expected to
|
||||
* return the address verbatim. */
|
||||
for (sl = ctx->mboxes; sl; sl = sl->next)
|
||||
if (!strcmp (sl->d, address))
|
||||
if (sl->mbox && !strcmp (sl->mbox, address))
|
||||
break;
|
||||
if (!sl)
|
||||
{
|
||||
@ -1565,7 +1559,7 @@ command_receive_cb (void *opaque, const char *mediatype,
|
||||
}
|
||||
|
||||
xfree (ctx.fpr);
|
||||
free_strlist (ctx.mboxes);
|
||||
free_uidinfo_list (ctx.mboxes);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -63,16 +63,32 @@ struct policy_flags_s
|
||||
unsigned int mailbox_only : 1;
|
||||
unsigned int dane_only : 1;
|
||||
unsigned int auth_submit : 1;
|
||||
unsigned int protocol_version; /* The supported WKS_DRAFT_VERION or 0 */
|
||||
unsigned int max_pending; /* Seconds to wait for a confirmation. */
|
||||
};
|
||||
typedef struct policy_flags_s *policy_flags_t;
|
||||
|
||||
|
||||
/* An object to convey user ids of a key. */
|
||||
struct uidinfo_list_s
|
||||
{
|
||||
struct uidinfo_list_s *next;
|
||||
time_t created; /* Time the userid was created. */
|
||||
char *mbox; /* NULL or the malloced mailbox from UID. */
|
||||
char uid[1];
|
||||
};
|
||||
typedef struct uidinfo_list_s *uidinfo_list_t;
|
||||
|
||||
|
||||
|
||||
/*-- wks-util.c --*/
|
||||
void wks_set_status_fd (int fd);
|
||||
void wks_write_status (int no, const char *format, ...) GPGRT_ATTR_PRINTF(2,3);
|
||||
gpg_error_t wks_list_key (estream_t key, char **r_fpr, strlist_t *r_mboxes);
|
||||
void free_uidinfo_list (uidinfo_list_t list);
|
||||
gpg_error_t wks_list_key (estream_t key, char **r_fpr,
|
||||
uidinfo_list_t *r_mboxes);
|
||||
gpg_error_t wks_filter_uid (estream_t *r_newkey, estream_t key,
|
||||
const char *uid);
|
||||
gpg_error_t wks_send_mime (mime_maker_t mime);
|
||||
gpg_error_t wks_parse_policy (policy_flags_t flags, estream_t stream,
|
||||
int ignore_unknown);
|
||||
|
@ -478,7 +478,8 @@ add_body (mime_maker_t ctx, const void *data, size_t datalen)
|
||||
|
||||
|
||||
/* Add STRING as body to the mail or the current MIME container. A
|
||||
* second call to this function is not allowed.
|
||||
* second call to this function or mime_make_add_body_data is not
|
||||
* allowed.
|
||||
*
|
||||
* FIXME: We may want to have an append_body to add more data to a body.
|
||||
*/
|
||||
@ -489,6 +490,16 @@ mime_maker_add_body (mime_maker_t ctx, const char *string)
|
||||
}
|
||||
|
||||
|
||||
/* Add (DATA,DATALEN) as body to the mail or the current MIME
|
||||
* container. Note that a second call to this function or to
|
||||
* mime_make_add_body is not allowed. */
|
||||
gpg_error_t
|
||||
mime_maker_add_body_data (mime_maker_t ctx, const void *data, size_t datalen)
|
||||
{
|
||||
return add_body (ctx, data, datalen);
|
||||
}
|
||||
|
||||
|
||||
/* This is the same as mime_maker_add_body but takes a stream as
|
||||
* argument. As of now the stream is copied to the MIME object but
|
||||
* eventually we may delay that and read the stream only at the time
|
||||
|
@ -34,6 +34,8 @@ void mime_maker_dump_tree (mime_maker_t ctx);
|
||||
gpg_error_t mime_maker_add_header (mime_maker_t ctx,
|
||||
const char *name, const char *value);
|
||||
gpg_error_t mime_maker_add_body (mime_maker_t ctx, const char *string);
|
||||
gpg_error_t mime_maker_add_body_data (mime_maker_t ctx,
|
||||
const void *data, size_t datalen);
|
||||
gpg_error_t mime_maker_add_stream (mime_maker_t ctx, estream_t *stream_addr);
|
||||
gpg_error_t mime_maker_add_container (mime_maker_t ctx);
|
||||
gpg_error_t mime_maker_end_container (mime_maker_t ctx);
|
||||
|
174
tools/wks-util.c
174
tools/wks-util.c
@ -90,9 +90,52 @@ wks_write_status (int no, const char *format, ...)
|
||||
|
||||
|
||||
|
||||
/* Helper for wks_list_key. */
|
||||
|
||||
/* Append UID to LIST and return the new item. On success LIST is
|
||||
* updated. On error ERRNO is set and NULL returned. */
|
||||
static uidinfo_list_t
|
||||
append_to_uidinfo_list (uidinfo_list_t *list, const char *uid, time_t created)
|
||||
{
|
||||
uidinfo_list_t r, sl;
|
||||
|
||||
sl = xtrymalloc (sizeof *sl + strlen (uid));
|
||||
if (!sl)
|
||||
return NULL;
|
||||
|
||||
strcpy (sl->uid, uid);
|
||||
sl->created = created;
|
||||
sl->mbox = mailbox_from_userid (uid);
|
||||
sl->next = NULL;
|
||||
if (!*list)
|
||||
*list = sl;
|
||||
else
|
||||
{
|
||||
for (r = *list; r->next; r = r->next )
|
||||
;
|
||||
r->next = sl;
|
||||
}
|
||||
return sl;
|
||||
}
|
||||
|
||||
|
||||
/* Free the list of uid infos at LIST. */
|
||||
void
|
||||
free_uidinfo_list (uidinfo_list_t list)
|
||||
{
|
||||
while (list)
|
||||
{
|
||||
uidinfo_list_t tmp = list->next;
|
||||
xfree (list->mbox);
|
||||
xfree (list);
|
||||
list = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Helper for wks_list_key and wks_filter_uid. */
|
||||
static void
|
||||
list_key_status_cb (void *opaque, const char *keyword, char *args)
|
||||
key_status_cb (void *opaque, const char *keyword, char *args)
|
||||
{
|
||||
(void)opaque;
|
||||
|
||||
@ -103,9 +146,10 @@ list_key_status_cb (void *opaque, const char *keyword, char *args)
|
||||
|
||||
/* Run gpg on KEY and store the primary fingerprint at R_FPR and the
|
||||
* list of mailboxes at R_MBOXES. Returns 0 on success; on error NULL
|
||||
* is stored at R_FPR and R_MBOXES and an error code is returned. */
|
||||
* is stored at R_FPR and R_MBOXES and an error code is returned.
|
||||
* R_FPR may be NULL if the fingerprint is not needed. */
|
||||
gpg_error_t
|
||||
wks_list_key (estream_t key, char **r_fpr, strlist_t *r_mboxes)
|
||||
wks_list_key (estream_t key, char **r_fpr, uidinfo_list_t *r_mboxes)
|
||||
{
|
||||
gpg_error_t err;
|
||||
ccparray_t ccp;
|
||||
@ -118,11 +162,11 @@ wks_list_key (estream_t key, char **r_fpr, strlist_t *r_mboxes)
|
||||
char **fields = NULL;
|
||||
int nfields;
|
||||
int lnr;
|
||||
char *mbox = NULL;
|
||||
char *fpr = NULL;
|
||||
strlist_t mboxes = NULL;
|
||||
uidinfo_list_t mboxes = NULL;
|
||||
|
||||
*r_fpr = NULL;
|
||||
if (r_fpr)
|
||||
*r_fpr = NULL;
|
||||
*r_mboxes = NULL;
|
||||
|
||||
/* Open a memory stream. */
|
||||
@ -158,7 +202,7 @@ wks_list_key (estream_t key, char **r_fpr, strlist_t *r_mboxes)
|
||||
}
|
||||
err = gnupg_exec_tool_stream (opt.gpg_program, argv, key,
|
||||
NULL, listing,
|
||||
list_key_status_cb, NULL);
|
||||
key_status_cb, NULL);
|
||||
if (err)
|
||||
{
|
||||
log_error ("import failed: %s\n", gpg_strerror (err));
|
||||
@ -232,9 +276,8 @@ wks_list_key (estream_t key, char **r_fpr, strlist_t *r_mboxes)
|
||||
else if (!strcmp (fields[0], "uid") && nfields > 9)
|
||||
{
|
||||
/* Fixme: Unescape fields[9] */
|
||||
xfree (mbox);
|
||||
mbox = mailbox_from_userid (fields[9]);
|
||||
if (mbox && !append_to_strlist_try (&mboxes, mbox))
|
||||
if (!append_to_uidinfo_list (&mboxes, fields[9],
|
||||
parse_timestamp (fields[5], NULL)))
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
@ -248,15 +291,23 @@ wks_list_key (estream_t key, char **r_fpr, strlist_t *r_mboxes)
|
||||
goto leave;
|
||||
}
|
||||
|
||||
*r_fpr = fpr;
|
||||
fpr = NULL;
|
||||
if (!fpr)
|
||||
{
|
||||
err = gpg_error (GPG_ERR_NO_PUBKEY);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (r_fpr)
|
||||
{
|
||||
*r_fpr = fpr;
|
||||
fpr = NULL;
|
||||
}
|
||||
*r_mboxes = mboxes;
|
||||
mboxes = NULL;
|
||||
|
||||
leave:
|
||||
xfree (fpr);
|
||||
xfree (mboxes);
|
||||
xfree (mbox);
|
||||
free_uidinfo_list (mboxes);
|
||||
xfree (fields);
|
||||
es_free (line);
|
||||
xfree (argv);
|
||||
@ -265,6 +316,85 @@ wks_list_key (estream_t key, char **r_fpr, strlist_t *r_mboxes)
|
||||
}
|
||||
|
||||
|
||||
/* Run gpg as a filter on KEY and write the output to a new stream
|
||||
* stored at R_NEWKEY. The new key will containn only the user id
|
||||
* UID. Returns 0 on success. Only one key is expected in KEY. */
|
||||
gpg_error_t
|
||||
wks_filter_uid (estream_t *r_newkey, estream_t key, const char *uid)
|
||||
{
|
||||
gpg_error_t err;
|
||||
ccparray_t ccp;
|
||||
const char **argv = NULL;
|
||||
estream_t newkey;
|
||||
char *filterexp = NULL;
|
||||
|
||||
*r_newkey = NULL;
|
||||
|
||||
/* Open a memory stream. */
|
||||
newkey = es_fopenmem (0, "w+b");
|
||||
if (!newkey)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Prefix the key with the MIME content type. */
|
||||
es_fputs ("Content-Type: application/pgp-keys\n"
|
||||
"\n", newkey);
|
||||
|
||||
filterexp = es_bsprintf ("keep-uid=uid=%s", uid);
|
||||
if (!filterexp)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
ccparray_init (&ccp, 0);
|
||||
|
||||
ccparray_put (&ccp, "--no-options");
|
||||
if (!opt.verbose)
|
||||
ccparray_put (&ccp, "--quiet");
|
||||
else if (opt.verbose > 1)
|
||||
ccparray_put (&ccp, "--verbose");
|
||||
ccparray_put (&ccp, "--batch");
|
||||
ccparray_put (&ccp, "--status-fd=2");
|
||||
ccparray_put (&ccp, "--always-trust");
|
||||
ccparray_put (&ccp, "--armor");
|
||||
ccparray_put (&ccp, "--import-options=import-export");
|
||||
ccparray_put (&ccp, "--import-filter");
|
||||
ccparray_put (&ccp, filterexp);
|
||||
ccparray_put (&ccp, "--import");
|
||||
|
||||
ccparray_put (&ccp, NULL);
|
||||
argv = ccparray_get (&ccp, NULL);
|
||||
if (!argv)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
err = gnupg_exec_tool_stream (opt.gpg_program, argv, key,
|
||||
NULL, newkey,
|
||||
key_status_cb, NULL);
|
||||
if (err)
|
||||
{
|
||||
log_error ("import/export failed: %s\n", gpg_strerror (err));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
es_rewind (newkey);
|
||||
*r_newkey = newkey;
|
||||
newkey = NULL;
|
||||
|
||||
leave:
|
||||
xfree (filterexp);
|
||||
xfree (argv);
|
||||
es_fclose (newkey);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Helper to write mail to the output(s). */
|
||||
gpg_error_t
|
||||
wks_send_mime (mime_maker_t mime)
|
||||
@ -316,7 +446,8 @@ wks_parse_policy (policy_flags_t flags, estream_t stream, int ignore_unknown)
|
||||
TOK_MAILBOX_ONLY,
|
||||
TOK_DANE_ONLY,
|
||||
TOK_AUTH_SUBMIT,
|
||||
TOK_MAX_PENDING
|
||||
TOK_MAX_PENDING,
|
||||
TOK_PROTOCOL_VERSION
|
||||
};
|
||||
static struct {
|
||||
const char *name;
|
||||
@ -325,7 +456,8 @@ wks_parse_policy (policy_flags_t flags, estream_t stream, int ignore_unknown)
|
||||
{ "mailbox-only", TOK_MAILBOX_ONLY },
|
||||
{ "dane-only", TOK_DANE_ONLY },
|
||||
{ "auth-submit", TOK_AUTH_SUBMIT },
|
||||
{ "max-pending", TOK_MAX_PENDING }
|
||||
{ "max-pending", TOK_MAX_PENDING },
|
||||
{ "protocol-version", TOK_PROTOCOL_VERSION }
|
||||
};
|
||||
gpg_error_t err = 0;
|
||||
int lnr = 0;
|
||||
@ -400,6 +532,14 @@ wks_parse_policy (policy_flags_t flags, estream_t stream, int ignore_unknown)
|
||||
* and decide whether to allow other units. */
|
||||
flags->max_pending = atoi (value);
|
||||
break;
|
||||
case TOK_PROTOCOL_VERSION:
|
||||
if (!value)
|
||||
{
|
||||
err = gpg_error (GPG_ERR_SYNTAX);
|
||||
goto leave;
|
||||
}
|
||||
flags->protocol_version = atoi (value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user