mirror of
git://git.gnupg.org/gnupg.git
synced 2025-07-02 22:46:30 +02:00
See ChangeLog: Thu Dec 10 20:15:36 CET 1998 Werner Koch
This commit is contained in:
parent
a45f824150
commit
af6e96e4f9
40 changed files with 1848 additions and 1444 deletions
|
@ -1,3 +1,35 @@
|
|||
Thu Dec 10 20:15:36 CET 1998 Werner Koch <wk@isil.d.shuttle.de>
|
||||
|
||||
* ringedit.c (gdbm_store): Fix for inserts
|
||||
|
||||
* g10.c (main): New option --export-all
|
||||
* export.c (export_pubkeys): New arg.
|
||||
(do_export): Now may skip old keys.
|
||||
|
||||
* status.c: Minor patches for Sun's cc
|
||||
|
||||
* keygen.c (ask_algo): Disabled v3 ElGamal choice, rearranged
|
||||
the numbers. Add a warning question when a sign+encrypt key
|
||||
is selected.
|
||||
|
||||
* g10.c (do_not_use_RSA): Removed.
|
||||
* misc.c (print_pubkey_algo_note): New as replacement for the
|
||||
do_not_use_RSA() and chnaged all callers.
|
||||
(print_cipher_algo_note): New.
|
||||
(print_hash_algo_note): New.
|
||||
|
||||
* cipher.c (write_header): Add a call to print_cipher_algo_note.
|
||||
* seckey-cert.c (protect_secret_key): Ditto
|
||||
* sign.c (do_sign): Add a call to print_digest_algo_note.
|
||||
|
||||
* getkey.c (get_long_user_id_string): New.
|
||||
* mainproc.c (check_sig_and_print): Changed the format of the
|
||||
status output.
|
||||
|
||||
* encrypt.c (write_pubkey_enc_from_list): print used symmetric cipher.
|
||||
|
||||
* pkclist.c (do_we_trust): Changed a message.
|
||||
|
||||
Wed Dec 9 13:41:06 CET 1998 Werner Koch <wk@isil.d.shuttle.de>
|
||||
|
||||
* misc.c (trap_unaligned) [ALPHA]: Only if UAC_SIGBUS is defined.
|
||||
|
|
|
@ -59,6 +59,7 @@ write_header( cipher_filter_context_t *cfx, IOBUF a )
|
|||
randomize_buffer( temp, blocksize, 1 );
|
||||
temp[blocksize] = temp[blocksize-2];
|
||||
temp[blocksize+1] = temp[blocksize-1];
|
||||
print_cipher_algo_note( cfx->dek->algo );
|
||||
cfx->cipher_hd = cipher_open( cfx->dek->algo, CIPHER_MODE_AUTO_CFB, 1 );
|
||||
cipher_setkey( cfx->cipher_hd, cfx->dek->key, cfx->dek->keylen );
|
||||
cipher_setiv( cfx->cipher_hd, NULL );
|
||||
|
|
|
@ -379,8 +379,8 @@ write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out )
|
|||
MPI frame;
|
||||
|
||||
pk = pk_list->pk;
|
||||
if( is_RSA(pk->pubkey_algo) )
|
||||
do_not_use_RSA();
|
||||
|
||||
print_pubkey_algo_note( pk->pubkey_algo );
|
||||
enc = m_alloc_clear( sizeof *enc );
|
||||
enc->pubkey_algo = pk->pubkey_algo;
|
||||
keyid_from_pk( pk, enc->keyid );
|
||||
|
@ -394,8 +394,9 @@ write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out )
|
|||
else {
|
||||
if( opt.verbose ) {
|
||||
char *ustr = get_user_id_string( enc->keyid );
|
||||
log_info(_("%s encrypted for: %s\n"),
|
||||
pubkey_algo_to_string(enc->pubkey_algo), ustr );
|
||||
log_info(_("%s/%s encrypted for: %s\n"),
|
||||
pubkey_algo_to_string(enc->pubkey_algo),
|
||||
cipher_algo_to_string(dek->algo), ustr );
|
||||
m_free(ustr);
|
||||
}
|
||||
/* and write it */
|
||||
|
|
26
g10/export.c
26
g10/export.c
|
@ -34,27 +34,28 @@
|
|||
#include "main.h"
|
||||
#include "i18n.h"
|
||||
|
||||
static int do_export( STRLIST users, int secret );
|
||||
static int do_export( STRLIST users, int secret, int onlyrfc );
|
||||
|
||||
/****************
|
||||
* Export the public keys (to standard out or --output).
|
||||
* Depending on opt.armor the output is armored.
|
||||
* If onlyrfc is True only RFC24404 compatible keys are exported.
|
||||
* If USERS is NULL, the complete ring will be exported.
|
||||
*/
|
||||
int
|
||||
export_pubkeys( STRLIST users )
|
||||
export_pubkeys( STRLIST users, int onlyrfc )
|
||||
{
|
||||
return do_export( users, 0 );
|
||||
return do_export( users, 0, onlyrfc );
|
||||
}
|
||||
|
||||
int
|
||||
export_seckeys( STRLIST users )
|
||||
{
|
||||
return do_export( users, 1 );
|
||||
return do_export( users, 1, 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
do_export( STRLIST users, int secret )
|
||||
do_export( STRLIST users, int secret, int onlyrfc )
|
||||
{
|
||||
int rc = 0;
|
||||
armor_filter_context_t afx;
|
||||
|
@ -110,7 +111,7 @@ do_export( STRLIST users, int secret )
|
|||
rc = secret? find_secret_keyblock_byname( &kbpos, sl->d )
|
||||
: find_keyblock_byname( &kbpos, sl->d );
|
||||
if( rc ) {
|
||||
log_error("%s: user not found: %s\n", sl->d, g10_errstr(rc) );
|
||||
log_error(_("%s: user not found: %s\n"), sl->d, g10_errstr(rc));
|
||||
rc = 0;
|
||||
continue;
|
||||
}
|
||||
|
@ -119,10 +120,21 @@ do_export( STRLIST users, int secret )
|
|||
}
|
||||
|
||||
if( rc ) {
|
||||
log_error("certificate read problem: %s\n", g10_errstr(rc));
|
||||
log_error(_("certificate read problem: %s\n"), g10_errstr(rc));
|
||||
goto leave;
|
||||
}
|
||||
|
||||
|
||||
/* do not export keys which are incompatible with rfc2440 */
|
||||
if( onlyrfc && (node = find_kbnode( keyblock, PKT_PUBLIC_KEY )) ) {
|
||||
PKT_public_key *pk = node->pkt->pkt.public_key;
|
||||
if( pk->version == 3 && pk->pubkey_algo > 3 ) {
|
||||
log_info(_("key %08lX: not a rfc2440 key - skipped\n"),
|
||||
(ulong)keyid_from_pk( pk, NULL) );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* and write it */
|
||||
for( kbctx=NULL; (node = walk_kbnode( keyblock, &kbctx, 0 )); ) {
|
||||
/* don't export any comment packets but those in the
|
||||
|
|
19
g10/g10.c
19
g10/g10.c
|
@ -84,6 +84,7 @@ enum cmd_and_opt_values { aNull = 0,
|
|||
aListSigs,
|
||||
aListSecretKeys,
|
||||
aExport,
|
||||
aExportAll,
|
||||
aExportSecret,
|
||||
aCheckKeys,
|
||||
aGenRevoke,
|
||||
|
@ -180,7 +181,8 @@ static ARGPARSE_OPTS opts[] = {
|
|||
{ aEditKey, "edit-key" ,256, N_("sign or edit a key")},
|
||||
{ aGenRevoke, "gen-revoke",256, N_("generate a revocation certificate")},
|
||||
#endif
|
||||
{ aExport, "export" , 256, N_("export keys") },
|
||||
{ aExport, "export" , 256, N_("export keys") },
|
||||
{ aExportAll, "export-all" , 256, "@" },
|
||||
{ aExportSecret, "export-secret-keys" , 256, "@" },
|
||||
{ aImport, "import", 256 , N_("import/merge keys")},
|
||||
{ aFastImport, "fast-import", 256 , "@"},
|
||||
|
@ -634,6 +636,7 @@ main( int argc, char **argv )
|
|||
case aImport: set_cmd( &cmd, aImport); break;
|
||||
case aFastImport: set_cmd( &cmd, aFastImport); break;
|
||||
case aExport: set_cmd( &cmd, aExport); break;
|
||||
case aExportAll: set_cmd( &cmd, aExportAll); break;
|
||||
case aListKeys: set_cmd( &cmd, aListKeys); break;
|
||||
case aListSigs: set_cmd( &cmd, aListSigs); break;
|
||||
case aExportSecret: set_cmd( &cmd, aExportSecret); break;
|
||||
|
@ -1084,10 +1087,11 @@ main( int argc, char **argv )
|
|||
break;
|
||||
|
||||
case aExport:
|
||||
case aExportAll:
|
||||
sl = NULL;
|
||||
for( ; argc; argc--, argv++ )
|
||||
add_to_strlist( &sl, *argv );
|
||||
export_pubkeys( sl );
|
||||
export_pubkeys( sl, (cmd == aExport) );
|
||||
free_strlist(sl);
|
||||
break;
|
||||
|
||||
|
@ -1315,17 +1319,6 @@ g10_exit( int rc )
|
|||
}
|
||||
|
||||
|
||||
void
|
||||
do_not_use_RSA()
|
||||
{
|
||||
static int did_rsa_note = 0;
|
||||
|
||||
if( !did_rsa_note ) {
|
||||
did_rsa_note = 1;
|
||||
log_info(_("RSA keys are deprecated; please consider "
|
||||
"creating a new key and use this key in the future\n"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef IS_G10MAINT
|
||||
|
|
21
g10/getkey.c
21
g10/getkey.c
|
@ -1413,6 +1413,27 @@ get_user_id_string( u32 *keyid )
|
|||
return p;
|
||||
}
|
||||
|
||||
char*
|
||||
get_long_user_id_string( u32 *keyid )
|
||||
{
|
||||
user_id_db_t r;
|
||||
char *p;
|
||||
int pass=0;
|
||||
/* try it two times; second pass reads from key resources */
|
||||
do {
|
||||
for(r=user_id_db; r; r = r->next )
|
||||
if( r->keyid[0] == keyid[0] && r->keyid[1] == keyid[1] ) {
|
||||
p = m_alloc( r->len + 20 );
|
||||
sprintf(p, "%08lX%08lX %.*s",
|
||||
(ulong)keyid[0], (ulong)keyid[1], r->len, r->name );
|
||||
return p;
|
||||
}
|
||||
} while( ++pass < 2 && !get_pubkey( NULL, keyid ) );
|
||||
p = m_alloc( 25 );
|
||||
sprintf(p, "%08lX%08lX [?]", (ulong)keyid[0], (ulong)keyid[1] );
|
||||
return p;
|
||||
}
|
||||
|
||||
char*
|
||||
get_user_id( u32 *keyid, size_t *rn )
|
||||
{
|
||||
|
|
12
g10/gpgd.c
12
g10/gpgd.c
|
@ -258,18 +258,6 @@ g10_exit( int rc )
|
|||
exit(rc );
|
||||
}
|
||||
|
||||
void
|
||||
do_not_use_RSA()
|
||||
{
|
||||
static int did_rsa_note = 0;
|
||||
|
||||
if( !did_rsa_note ) {
|
||||
did_rsa_note = 1;
|
||||
log_info("RSA keys are depreciated; please consider "
|
||||
"creating a new key and use this key in the future\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
become_daemon()
|
||||
|
|
|
@ -72,11 +72,17 @@ static struct helptexts { const char *key; const char *help; } helptexts[] = {
|
|||
"does this but other OpenPGP implemenations are not required to understand\n"
|
||||
"the signature+encryption flavor.\n"
|
||||
"The first (primary) key must always be a key which is capable of signing;\n"
|
||||
"this is the reason why the ecrytion only ElGamal key is disabled in this.\n"
|
||||
"You should not select the \"ElGamal in a v3 packet\", because that key is\n"
|
||||
"not compatible to other OpenPGP implementations."
|
||||
"this is the reason why the ecrytion only ElGamal key is disabled in this."
|
||||
},
|
||||
|
||||
|
||||
{ N_("keygen.algo.elg_se"),
|
||||
"Although these keys are defined in RFC2440 they are not suggested\n"
|
||||
"because they are not supported by all programs and signatures created\n"
|
||||
"with them are quite large and very slow to verify."
|
||||
},
|
||||
|
||||
|
||||
{ N_("keygen.size"),
|
||||
"Enter the size of the key"
|
||||
},
|
||||
|
|
|
@ -145,6 +145,7 @@ int get_seckey_byname( PKT_secret_key *sk, const char *name, int unlock );
|
|||
int enum_secret_keys( void **context, PKT_secret_key *sk, int with_subkeys );
|
||||
void merge_keys_and_selfsig( KBNODE keyblock );
|
||||
char*get_user_id_string( u32 *keyid );
|
||||
char*get_long_user_id_string( u32 *keyid );
|
||||
char*get_user_id( u32 *keyid, size_t *rn );
|
||||
|
||||
/*-- keyid.c --*/
|
||||
|
|
19
g10/keygen.c
19
g10/keygen.c
|
@ -388,11 +388,13 @@ ask_algo( int *ret_v4, int addmode )
|
|||
tty_printf(_("Please select what kind of key you want:\n"));
|
||||
if( !addmode )
|
||||
tty_printf(_(" (%d) DSA and ElGamal (default)\n"), 1 );
|
||||
tty_printf( _(" (%d) ElGamal (sign and encrypt)\n"), 2 );
|
||||
tty_printf( _(" (%d) DSA (sign only)\n"), 2 );
|
||||
if( addmode )
|
||||
tty_printf( _(" (%d) ElGamal (encrypt only)\n"), 3 );
|
||||
tty_printf( _(" (%d) DSA (sign only)\n"), 4 );
|
||||
tty_printf( _(" (%d) ElGamal (sign and encrypt)\n"), 4 );
|
||||
#if 0
|
||||
tty_printf( _(" (%d) ElGamal in a v3 packet\n"), 5 );
|
||||
#endif
|
||||
|
||||
*ret_v4 = 1;
|
||||
for(;;) {
|
||||
|
@ -404,23 +406,28 @@ ask_algo( int *ret_v4, int addmode )
|
|||
algo = 0; /* create both keys */
|
||||
break;
|
||||
}
|
||||
else if( algo == 2 ) {
|
||||
algo = PUBKEY_ALGO_ELGAMAL;
|
||||
break;
|
||||
else if( algo == 4 ) {
|
||||
if( cpr_get_answer_is_yes("keygen.algo.elg_se",_(
|
||||
"Do you really want to create a sign and encrypt key? "))) {
|
||||
algo = PUBKEY_ALGO_ELGAMAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if( algo == 3 && addmode ) {
|
||||
algo = PUBKEY_ALGO_ELGAMAL_E;
|
||||
break;
|
||||
}
|
||||
else if( algo == 4 ) {
|
||||
else if( algo == 2 ) {
|
||||
algo = PUBKEY_ALGO_DSA;
|
||||
break;
|
||||
}
|
||||
#if 0
|
||||
else if( algo == 5 ) {
|
||||
algo = PUBKEY_ALGO_ELGAMAL_E;
|
||||
*ret_v4 = 0;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
tty_printf(_("Invalid selection.\n"));
|
||||
}
|
||||
|
|
|
@ -45,7 +45,9 @@ extern int g10_errors_seen;
|
|||
#else
|
||||
void g10_exit(int rc);
|
||||
#endif
|
||||
void do_not_use_RSA(void);
|
||||
void print_pubkey_algo_note( int algo );
|
||||
void print_cipher_algo_note( int algo );
|
||||
void print_digest_algo_note( int algo );
|
||||
|
||||
/*-- misc.c --*/
|
||||
void trap_unaligned(void);
|
||||
|
@ -108,7 +110,7 @@ KBNODE make_mpi_comment_node( const char *s, MPI a );
|
|||
/*-- import.c --*/
|
||||
int import_keys( const char *filename, int fast );
|
||||
/*-- export.c --*/
|
||||
int export_pubkeys( STRLIST users );
|
||||
int export_pubkeys( STRLIST users, int onlyrfc );
|
||||
int export_seckeys( STRLIST users );
|
||||
|
||||
/* dearmor.c --*/
|
||||
|
|
|
@ -841,7 +841,7 @@ check_sig_and_print( CTX c, KBNODE node )
|
|||
|
||||
rc = do_check_sig(c, node, NULL );
|
||||
if( !rc || rc == G10ERR_BAD_SIGN ) {
|
||||
char *us = get_user_id_string( sig->keyid );
|
||||
char *us = get_long_user_id_string( sig->keyid );
|
||||
write_status_text( rc? STATUS_BADSIG : STATUS_GOODSIG, us );
|
||||
m_free(us);
|
||||
log_info(rc? _("BAD signature from \"")
|
||||
|
|
58
g10/misc.c
58
g10/misc.c
|
@ -187,3 +187,61 @@ buffer_to_u32( const byte *buffer )
|
|||
return a;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
no_exp_algo(void)
|
||||
{
|
||||
static int did_note = 0;
|
||||
|
||||
if( !did_note ) {
|
||||
did_note = 1;
|
||||
log_info(_("Experimental algorithms should not be used!\n"));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
print_pubkey_algo_note( int algo )
|
||||
{
|
||||
if( algo >= 100 && algo <= 110 )
|
||||
no_exp_algo();
|
||||
else if( is_RSA( algo ) ) {
|
||||
static int did_note = 0;
|
||||
|
||||
if( !did_note ) {
|
||||
did_note = 1;
|
||||
log_info(_("RSA keys are deprecated; please consider "
|
||||
"creating a new key and use this key in the future\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
print_cipher_algo_note( int algo )
|
||||
{
|
||||
if( algo >= 100 && algo <= 110 )
|
||||
no_exp_algo();
|
||||
else if( algo == CIPHER_ALGO_3DES
|
||||
|| algo == CIPHER_ALGO_CAST5
|
||||
|| algo == CIPHER_ALGO_BLOWFISH
|
||||
)
|
||||
;
|
||||
else {
|
||||
static int did_note = 0;
|
||||
|
||||
if( !did_note ) {
|
||||
did_note = 1;
|
||||
log_info(_("This cipher algorithm is depreciated; "
|
||||
"please use a more standard one!\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
print_digest_algo_note( int algo )
|
||||
{
|
||||
if( algo >= 100 && algo <= 110 )
|
||||
no_exp_algo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -302,7 +302,7 @@ do_we_trust( PKT_public_key *pk, int trustlevel )
|
|||
|
||||
case TRUST_ULTIMATE:
|
||||
if( opt.verbose )
|
||||
log_info(_("This key belongs to us (we have the secret key)\n"));
|
||||
log_info(_("This key belongs to us\n"));
|
||||
return 1; /* yes */
|
||||
|
||||
default: BUG();
|
||||
|
|
|
@ -249,7 +249,7 @@ add_keyblock_resource( const char *url, int force, int secret )
|
|||
goto leave;
|
||||
|
||||
case rt_RING:
|
||||
iobuf = iobuf_fopen( filename, "rb" );
|
||||
iobuf = iobuf_open( filename );
|
||||
if( !iobuf && !force ) {
|
||||
rc = G10ERR_OPEN_FILE;
|
||||
goto leave;
|
||||
|
@ -689,7 +689,7 @@ enum_keyblocks( int mode, KBPOS *kbpos, KBNODE *ret_root )
|
|||
kbpos->rt = resource_table[i].rt;
|
||||
switch( kbpos->rt ) {
|
||||
case rt_RING:
|
||||
kbpos->fp = iobuf_fopen( rentry->fname, "rb" );
|
||||
kbpos->fp = iobuf_open( rentry->fname );
|
||||
if( !kbpos->fp ) {
|
||||
log_error("can't open '%s'\n", rentry->fname );
|
||||
return G10ERR_OPEN_FILE;
|
||||
|
@ -1083,7 +1083,7 @@ keyring_read( KBPOS *kbpos, KBNODE *ret_root )
|
|||
if( !(rentry=check_pos(kbpos)) )
|
||||
return G10ERR_GENERAL;
|
||||
|
||||
a = iobuf_fopen( rentry->fname, "rb" );
|
||||
a = iobuf_open( rentry->fname );
|
||||
if( !a ) {
|
||||
log_error("can't open '%s'\n", rentry->fname );
|
||||
return G10ERR_OPEN_FILE;
|
||||
|
@ -1246,7 +1246,7 @@ keyring_copy( KBPOS *kbpos, int mode, KBNODE root )
|
|||
log_fatal("can't lock '%s'\n", rentry->fname );
|
||||
|
||||
/* open the source file */
|
||||
fp = iobuf_fopen( rentry->fname, "rb" );
|
||||
fp = iobuf_open( rentry->fname );
|
||||
if( mode == 1 && !fp && errno == ENOENT ) { /* no file yet */
|
||||
KBNODE kbctx, node;
|
||||
|
||||
|
@ -1526,6 +1526,9 @@ do_gdbm_store( KBPOS *kbpos, KBNODE root, int update )
|
|||
content.dsize = iobuf_get_temp_length( fp );
|
||||
rc = gdbm_store( rentry->dbf, key, content,
|
||||
update? GDBM_REPLACE : GDBM_INSERT );
|
||||
if( rc == 1 && !update )
|
||||
rc = gdbm_store( rentry->dbf, key, content, GDBM_REPLACE );
|
||||
|
||||
if( rc ) {
|
||||
log_error("%s: gdbm_store failed: %s\n", rentry->fname,
|
||||
rc == 1 ? "already stored"
|
||||
|
|
|
@ -209,6 +209,7 @@ protect_secret_key( PKT_secret_key *sk, DEK *dek )
|
|||
else if( cipher_get_blocksize( sk->protect.algo ) != 8 )
|
||||
rc = G10ERR_CIPHER_ALGO; /* unsupport protection algorithm */
|
||||
else {
|
||||
print_cipher_algo_note( sk->protect.algo );
|
||||
cipher_hd = cipher_open( sk->protect.algo,
|
||||
CIPHER_MODE_AUTO_CFB, 1 );
|
||||
if( cipher_setkey( cipher_hd, dek->key, dek->keylen ) )
|
||||
|
|
|
@ -47,12 +47,12 @@ do_sign( PKT_secret_key *sk, PKT_signature *sig,
|
|||
byte *dp;
|
||||
int rc;
|
||||
|
||||
if( is_RSA(sk->pubkey_algo) )
|
||||
do_not_use_RSA();
|
||||
print_pubkey_algo_note(sk->pubkey_algo);
|
||||
|
||||
if( !digest_algo )
|
||||
digest_algo = md_get_algo(md);
|
||||
|
||||
print_digest_algo_note( digest_algo );
|
||||
dp = md_read( md, digest_algo );
|
||||
sig->digest_algo = digest_algo;
|
||||
sig->digest_start[0] = dp[0];
|
||||
|
|
|
@ -152,7 +152,8 @@ init_shm_coprocessing ( ulong requested_shm_size, int lock_mem )
|
|||
else
|
||||
shm_is_locked = 1;
|
||||
#elif defined(HAVE_MLOCK) && !defined(HAVE_BROKEN_MLOCK)
|
||||
if ( mlock (shm_area, shm_size) )
|
||||
/* (need the cast for Solaris with Sun's workshop compilers) */
|
||||
if ( mlock ( (char*)shm_area, shm_size) )
|
||||
log_info("locking shared memory %d failed: %s\n",
|
||||
shm_id, strerror(errno));
|
||||
else
|
||||
|
@ -307,7 +308,8 @@ cpr_kill_prompt(void)
|
|||
if( opt.shm_coprocess )
|
||||
return;
|
||||
#endif
|
||||
return tty_kill_prompt();
|
||||
tty_kill_prompt();
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue