diff options
author | Werner Koch <[email protected]> | 2025-04-07 07:56:39 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2025-04-07 07:56:39 +0000 |
commit | 7ec1f27b60eec95297c5267ab5ebbdb73b263c3f (patch) | |
tree | 5924edb001bd93cbc770549106a7a9dbe403af24 /tests | |
parent | Update the copyright notice. (diff) | |
download | libgpg-error-7ec1f27b60eec95297c5267ab5ebbdb73b263c3f.tar.gz libgpg-error-7ec1f27b60eec95297c5267ab5ebbdb73b263c3f.zip |
New public API gpgrt_nvc_* and gpgrt_nve_*
* src/name-value.c: New.
* tests/t-name-value.c: New.
* src/gpg-error.def.in: Add new functions.
* src/gpg-error.h.in: Ditto.
* src/gpg-error.vers: Ditto.
* src/visibility.c: Ditto.
* src/visibility.h: Ditto.
--
These new functions were originally added to GnuPG taken from current
GnuPG master. The license has been changed from (LGPL-3.0-or-later OR
GPL-2.0-or-later) to LGPL-2.1-or-later: Everything was written by me
or employees/contractors of g10 Code GmbH. The private key handling
has been removed because that would require Libgcrypt functions. Some
functions are changed.
Diffstat (limited to 'tests')
-rw-r--r-- | tests/Makefile.am | 3 | ||||
-rw-r--r-- | tests/t-name-value.c | 581 |
2 files changed, 583 insertions, 1 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am index e96f60e..abe57b3 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -24,7 +24,8 @@ EXTRA_DIST = t-argparse.conf etc/t-argparse.conf gpg_error_lib = ../src/libgpg-error.la TESTS = t-version t-strerror t-syserror t-lock t-printf t-poll t-b64 \ - t-argparse t-logging t-stringutils t-malloc t-spawn t-strlist + t-argparse t-logging t-stringutils t-malloc t-spawn t-strlist \ + t-name-value if HAVE_LOCK_OPTIMIZATION TESTS += t-lock-single-posix diff --git a/tests/t-name-value.c b/tests/t-name-value.c new file mode 100644 index 0000000..7a28bf4 --- /dev/null +++ b/tests/t-name-value.c @@ -0,0 +1,581 @@ +/* t-name-value.c - Check name-value functions + * Copyright (C) 2015, 2025 g10 Code GmbH + * + * This file is part of Libgpg-error. + * + * Libgpg-error is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * Libgpg-error is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define PGM "t-name-value" +#include "t-common.h" + + +static void test_getting_values (gpgrt_nvc_t cont); +static void test_key_extraction (gpgrt_nvc_t pk); +static void test_iteration (gpgrt_nvc_t pk); +static void test_whitespace (gpgrt_nvc_t pk); + +static int private_key_mode; + +static struct +{ + char *value; + void (*test_func) (gpgrt_nvc_t); +} tests[] = + { + { + "# This is a comment followed by an empty line\n" + "\n", + NULL + }, + { + "# This is a comment followed by two empty lines, Windows style\r\n" + "\r\n" + "\r\n", + NULL + }, + { + "# Some name,value pairs\n" + "Comment: Some comment.\n" + "SomeOtherName: Some value.\n", + test_getting_values + }, + { + " # Whitespace is preserved as much as possible\r\n" + "Comment:Some comment.\n" + "SomeOtherName: Some value. \n", + test_getting_values + }, + { + "# Values may be continued in the next line as indicated by leading\n" + "# space\n" + "Comment: Some rather long\n" + " comment that is continued in the next line.\n" + "\n" + " Blank lines with or without whitespace are allowed within\n" + " continuations to allow paragraphs.\n" + "SomeOtherName: Some value.\n", + test_getting_values + }, + { + "# Names may be given multiple times forming an array of values\n" + "Comment: Some comment, element 0.\n" + "Comment: Some comment, element 1.\n" + "Comment: Some comment, element 2.\n" + "SomeOtherName: Some value.\n", + test_iteration + }, + { + "# One whitespace at the beginning of a continuation is swallowed.\n" + "One: Without\n" + " Whitespace\n" + "Two: With\n" + " Whitespace\n" + "Three: Blank lines in continuations encode newlines.\n" + "\n" + " Next paragraph.\n", + test_whitespace + }, + { + "Description: Key to sign all GnuPG released tarballs.\n" + " The key is actually stored on a smart card.\n" + "Use-for-ssh: yes\n" + "OpenSSH-cert: long base64 encoded string wrapped so that this\n" + " key file can be easily edited with a standard editor.\n" + "Key: (shadowed-private-key\n" + " (rsa\n" + " (n #00AA1AD2A55FD8C8FDE9E1941772D9CC903FA43B268CB1B5A1BAFDC900\n" + " 2961D8AEA153424DC851EF13B83AC64FBE365C59DC1BD3E83017C90D4365B4\n" + " 83E02859FC13DB5842A00E969480DB96CE6F7D1C03600392B8E08EF0C01FC7\n" + " 19F9F9086B25AD39B4F1C2A2DF3E2BE317110CFFF21D4A11455508FE407997\n" + " 601260816C8422297C0637BB291C3A079B9CB38A92CE9E551F80AA0EBF4F0E\n" + " 72C3F250461E4D31F23A7087857FC8438324A013634563D34EFDDCBF2EA80D\n" + " F9662C9CCD4BEF2522D8BDFED24CEF78DC6B309317407EAC576D889F88ADA0\n" + " 8C4FFB480981FB68C5C6CA27503381D41018E6CDC52AAAE46B166BDC10637A\n" + " E186A02BA2497FDC5D1221#)\n" + " (e #00010001#)\n" + " (shadowed t1-v1\n" + " (#D2760001240102000005000011730000# OPENPGP.1)\n" + " )))\n", + test_key_extraction + } + }; + + + +static void +test_getting_values (gpgrt_nvc_t pk) +{ + gpgrt_nve_t e; + + e = gpgrt_nvc_lookup (pk, "Comment:"); + gpgrt_assert (e); + + /* Names are case-insensitive. */ + e = gpgrt_nvc_lookup (pk, "comment:"); + gpgrt_assert (e); + e = gpgrt_nvc_lookup (pk, "COMMENT:"); + gpgrt_assert (e); + + e = gpgrt_nvc_lookup (pk, "SomeOtherName:"); + gpgrt_assert (e); +} + + +static void +test_key_extraction (gpgrt_nvc_t pk) +{ + const char *key; + + /* Note that the original test in gnupg used the gnupg specific + * private key extraction function which works only in + * private_key_mode. We can't do this here so we do a mere + * extraction test. */ + key = gpgrt_nve_value (gpgrt_nvc_lookup (pk, "Key:")); + gpgrt_assert (key); + + if (verbose) + fprintf (stdout, "->%s<-\n", key); +} + + +static void +test_iteration (gpgrt_nvc_t pk) +{ + int i; + gpgrt_nve_t e; + + i = 0; + for (e = gpgrt_nvc_lookup (pk, NULL); e; e = gpgrt_nve_next (e, NULL)) + i++; + gpgrt_assert (i == 4); + + i = 0; + for (e = gpgrt_nvc_lookup (pk, "Comment:"); + e; + e = gpgrt_nve_next (e, "Comment:")) + i++; + gpgrt_assert (i == 3); +} + + +static void +test_whitespace (gpgrt_nvc_t pk) +{ + gpgrt_nve_t e; + + e = gpgrt_nvc_lookup (pk, "One:"); + gpgrt_assert (e); + gpgrt_assert (strcmp (gpgrt_nve_value (e), "WithoutWhitespace") == 0); + + e = gpgrt_nvc_lookup (pk, "Two:"); + gpgrt_assert (e); + gpgrt_assert (strcmp (gpgrt_nve_value (e), "With Whitespace") == 0); + + e = gpgrt_nvc_lookup (pk, "Three:"); + gpgrt_assert (e); + gpgrt_assert (strcmp (gpgrt_nve_value (e), + "Blank lines in continuations encode newlines.\n" + "Next paragraph.") == 0); +} + + +/* Convert container PK to a string. Terminates on error. */ +static char * +nvc_to_string (gpgrt_nvc_t pk) +{ + gpg_error_t err; + char *buf; + size_t len; + estream_t sink; + + sink = es_fopenmem (0, "rw"); + gpgrt_assert (sink); + + err = gpgrt_nvc_write (pk, sink); + gpgrt_assert (err == 0); + + len = es_ftell (sink); + buf = xmalloc (len+1); + gpgrt_assert (buf); + + es_fseek (sink, 0, SEEK_SET); + es_read (sink, buf, len, NULL); + buf[len] = 0; + + es_fclose (sink); + return buf; +} + + +static void dummy_free (void *p) { (void) p; } +static void *dummy_realloc (void *p, size_t s) { (void) s; return p; } + +/* + * Run the standard test cases. + */ +static void +run_tests (void) +{ + gpg_error_t err; + gpgrt_nvc_t pk; + int i; + + for (i = 0; i < DIM (tests); i++) + { + estream_t source; + char *buf; + size_t len; + + len = strlen (tests[i].value); + source = es_mopen (tests[i].value, len, len, + 0, dummy_realloc, dummy_free, "r"); + gpgrt_assert (source); + + if (private_key_mode) + err = gpgrt_nvc_parse (&pk, NULL, source, GPGRT_NVC_PRIVKEY); + else + err = gpgrt_nvc_parse (&pk, NULL, source, 0); + gpgrt_assert (err == 0); + gpgrt_assert (pk); + + if (verbose) + { + err = gpgrt_nvc_write (pk, es_stderr); + gpgrt_assert (err == 0); + } + + buf = nvc_to_string (pk); + gpgrt_assert (memcmp (tests[i].value, buf, len) == 0); + + es_fclose (source); + xfree (buf); + + if (tests[i].test_func) + tests[i].test_func (pk); + + gpgrt_nvc_release (pk); + } +} + + +/* + * Run tests checking the modification functions. + */ +static void +run_modification_tests (void) +{ + gpg_error_t err; + gpgrt_nvc_t pk; + gpgrt_nve_t e; + char *buf; + + pk = gpgrt_nvc_new (private_key_mode? GPGRT_NVC_PRIVKEY : 0); + gpgrt_assert (pk); + + err = gpgrt_nvc_set (pk, "Foo:", "Bar"); + gpgrt_assert (!err); + buf = nvc_to_string (pk); + gpgrt_assert (strcmp (buf, "Foo: Bar\n") == 0); + xfree (buf); + + err = gpgrt_nvc_set (pk, "Foo:", "Baz"); + if (err) + fail ("gpgrt_nvc_set failed at line %d: %s\n",__LINE__,gpg_strerror (err)); + buf = nvc_to_string (pk); + gpgrt_assert (strcmp (buf, "Foo: Baz\n") == 0); + xfree (buf); + + err = gpgrt_nvc_set (pk, "Bar:", "Bazzel"); + if (err) + fail ("gpgrt_nvc_set failed at line %d: %s\n",__LINE__,gpg_strerror (err)); + buf = nvc_to_string (pk); + gpgrt_assert (strcmp (buf, "Foo: Baz\nBar: Bazzel\n") == 0); + xfree (buf); + + err = gpgrt_nvc_add (pk, "Foo:", "Bar"); + if (err) + fail ("gpgrt_nvc_add failed at line %d: %s\n",__LINE__,gpg_strerror (err)); + buf = nvc_to_string (pk); + gpgrt_assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\n") == 0); + xfree (buf); + + err = gpgrt_nvc_add (pk, "DontExistYet:", "Bar"); + if (err) + fail ("gpgrt_nvc_set failed at line %d: %s\n",__LINE__,gpg_strerror (err)); + buf = nvc_to_string (pk); + gpgrt_assert (strcmp (buf, ("Foo: Baz\nFoo: Bar\n" + "Bar: Bazzel\nDontExistYet: Bar\n")) == 0); + xfree (buf); + + gpgrt_nvc_delete (pk, gpgrt_nvc_lookup (pk, "DontExistYet:"), NULL); + buf = nvc_to_string (pk); + gpgrt_assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\n") == 0); + xfree (buf); + + gpgrt_nvc_delete (pk, gpgrt_nve_next (gpgrt_nvc_lookup (pk, "Foo:"), + "Foo:"), NULL); + buf = nvc_to_string (pk); + gpgrt_assert (strcmp (buf, "Foo: Baz\nBar: Bazzel\n") == 0); + xfree (buf); + + gpgrt_nvc_delete (pk, gpgrt_nvc_lookup (pk, "Foo:"), NULL); + buf = nvc_to_string (pk); + gpgrt_assert (strcmp (buf, "Bar: Bazzel\n") == 0); + xfree (buf); + + gpgrt_nvc_delete (pk, gpgrt_nvc_lookup (pk, NULL), NULL); + buf = nvc_to_string (pk); + gpgrt_assert (strcmp (buf, "") == 0); + xfree (buf); + + /* Test whether we can delete an entry by name. */ + err = gpgrt_nvc_add (pk, "Key:", "(3:foo)"); + if (err) + fail ("gpgrt_nvc_add failed at line %d: %s\n",__LINE__,gpg_strerror (err)); + e = gpgrt_nvc_lookup (pk, "Key:"); + gpgrt_assert (e); + gpgrt_nvc_delete (pk, NULL, "Kez:"); /* Delete an nonexistent name. */ + e = gpgrt_nvc_lookup (pk, "Key:"); + gpgrt_assert (e); + gpgrt_nvc_delete (pk, NULL, "Key:"); + e = gpgrt_nvc_lookup (pk, "Key:"); + gpgrt_assert (!e); + + /* Ditto but now whether it deletes all entries with that name. We + * don't use "Key" because that name is special in private key mode. */ + err = gpgrt_nvc_add (pk, "AKey:", "A-value"); + if (err) + fail ("gpgrt_nvc_add failed at line %d: %s\n",__LINE__,gpg_strerror (err)); + + err = gpgrt_nvc_add (pk, "AKey:", "B-value"); + if (err) + fail ("gpgrt_nvc_add failed at line %d: %s\n",__LINE__,gpg_strerror (err)); + + e = gpgrt_nvc_lookup (pk, "AKey:"); + gpgrt_assert (e); + + gpgrt_nvc_delete (pk, NULL, "AKey:"); + e = gpgrt_nvc_lookup (pk, "AKey:"); + gpgrt_assert (!e); + + err = gpgrt_nvc_set (pk, "Foo:", + "A really long value spanning across multiple lines" + " that has to be wrapped at a convenient space."); + if (err) + fail ("gpgrt_nvc_set failed at line %d: %s\n",__LINE__,gpg_strerror (err)); + buf = nvc_to_string (pk); + gpgrt_assert (strcmp (buf, "Foo: A really long value spanning across multiple" + " lines that has to be\n wrapped at a convenient space.\n") + == 0); + xfree (buf); + + err = gpgrt_nvc_set (pk, "Foo:", + "XA really long value spanning across multiple lines" + " that has to be wrapped at a convenient space."); + if (err) + fail ("gpgrt_nvc_set failed at line %d: %s\n",__LINE__,gpg_strerror (err)); + buf = nvc_to_string (pk); + gpgrt_assert (strcmp (buf, + "Foo: XA really long value spanning across multiple" + " lines that has to\n be wrapped at a convenient space.\n") + == 0); + xfree (buf); + + err = gpgrt_nvc_set (pk, "Foo:", + "XXXXA really long value spanning across multiple lines" + " that has to be wrapped at a convenient space."); + if (err) + fail ("gpgrt_nvc_set failed at line %d: %s\n",__LINE__,gpg_strerror (err)); + buf = nvc_to_string (pk); + gpgrt_assert (strcmp (buf, + "Foo: XXXXA really long value spanning across multiple" + " lines that has\n to be wrapped at a convenient space.\n") + == 0); + xfree (buf); + + err = gpgrt_nvc_set (pk, "Foo:", + "Areallylongvaluespanningacrossmultiplelines" + "thathastobewrappedataconvenientspacethatisnotthere."); + if (err) + fail ("gpgrt_nvc_set failed at line %d: %s\n",__LINE__,gpg_strerror (err)); + buf = nvc_to_string (pk); + gpgrt_assert (strcmp (buf, + "Foo: Areallylongvaluespanningacrossmultiplelinesthat" + "hastobewrappedataco\n nvenientspacethatisnotthere.\n") + == 0); + xfree (buf); + gpgrt_nvc_release (pk); + + pk = gpgrt_nvc_new (private_key_mode? GPGRT_NVC_PRIVKEY : 0); + gpgrt_assert (pk); + + err = gpgrt_nvc_set (pk, "Key:", "(hello world)"); + gpgrt_assert (err == 0); + buf = nvc_to_string (pk); + gpgrt_assert (strcmp (buf, "Key: (hello world)\n") == 0); + xfree (buf); + gpgrt_nvc_release (pk); +} + + + +static void +parse (const char *fname) +{ + gpg_error_t err; + estream_t source; + char *buf; + gpgrt_nvc_t pk_a, pk_b; + gpgrt_nve_t e; + int line; + + source = es_fopen (fname, "rb"); + if (source == NULL) + { + perror (fname); + exit (1); + } + + if (private_key_mode) + err = gpgrt_nvc_parse (&pk_a, &line, source, GPGRT_NVC_PRIVKEY); + else + err = gpgrt_nvc_parse (&pk_a, &line, source, 0); + if (err) + { + fail ("failed to parse %s line %d: %s\n", + fname, line, gpg_strerror (err)); + exit (1); + } + + buf = nvc_to_string (pk_a); + xfree (buf); + + pk_b = gpgrt_nvc_new (private_key_mode? GPGRT_NVC_PRIVKEY : 0); + gpgrt_assert (pk_b); + + for (e = gpgrt_nvc_lookup (pk_a, NULL); e; e = gpgrt_nve_next (e, NULL)) + { + const char *key = NULL; + + if (private_key_mode && !strcasecmp (gpgrt_nve_name (e), "Key:")) + { + key = gpgrt_nve_value (e); + } + + if (key) + { + err = gpgrt_nve_set (pk_a, e, key); + gpgrt_assert (err == 0); + } + else + { + err = gpgrt_nvc_add (pk_b, gpgrt_nve_name (e), gpgrt_nve_value (e)); + gpgrt_assert (err == 0); + } + } + + buf = nvc_to_string (pk_b); + if (verbose) + fprintf (stdout, "%s", buf); + xfree (buf); +} + + + +static const char * +my_strusage (int level) +{ + const char *p; + + switch (level) + { + case 9: p = "LGPL-2.1-or-later"; break; + case 11: p = PGM; break; + case 12: p = PACKAGE_NAME; break; + case 13: p = PACKAGE_VERSION; break; + case 14: p = GPGRT_STD_COPYRIGHT_LINE; break; + case 19: p = "Please report bugs to <https://bugs.gnupg.org>.\n"; break; + default: p = NULL; + } + return p; +} + + +int +main (int argc, char **argv) +{ + enum { CMD_TEST = 500, CMD_PARSE } command = CMD_TEST; + gpgrt_opt_t opts[] = { + ARGPARSE_x ('v', "verbose", NONE, 0, "Print more diagnostics"), + ARGPARSE_s_n('d', "debug", "Flyswatter"), + ARGPARSE_c (CMD_TEST, "test", "Test mode (default)"), + ARGPARSE_c (CMD_PARSE, "parse", "Parse a file"), + ARGPARSE_end() + }; + gpgrt_argparse_t pargs = { &argc, &argv, 0 }; + + gpgrt_set_strusage (my_strusage); + gpgrt_log_set_prefix (gpgrt_strusage (11), GPGRT_LOG_WITH_PREFIX); + + while (gpgrt_argparse (NULL, &pargs, opts)) + { + switch (pargs.r_opt) + { + case 'v': verbose++; break; + case 'd': debug++; break; + + case CMD_TEST: + case CMD_PARSE: + command = pargs.r_opt; + break; + + default : pargs.err = ARGPARSE_PRINT_ERROR; break; + } + } + gpgrt_argparse (NULL, &pargs, NULL); + + if (command == CMD_TEST) + { + show ("testing name-value functions\n"); + + run_tests (); + run_modification_tests (); + + show ("again in private key mode\n"); + /* Now again in rivate key mode */ + private_key_mode = 1; + run_tests (); + run_modification_tests (); + + show ("testing name-value functions finished\n"); + } + else if (command == CMD_PARSE) + { + if (argc != 1) + gpgrt_usage (1); + parse (*argv); + } + + return !!errorcount; +} |