common: New function log_debug_with_string.
[gnupg.git] / common / t-name-value.c
1 /* t-name-value.c - Module test for name-value.c
2  *      Copyright (C) 2016 g10 Code GmbH
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <https://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <errno.h>
24 #include <assert.h>
25 #include <unistd.h>
26 #include <sys/stat.h>
27
28 #include "util.h"
29 #include "name-value.h"
30
31 static int verbose;
32 static int private_key_mode;
33
34
35 static nvc_t
36 my_nvc_new (void)
37 {
38   if (private_key_mode)
39     return nvc_new_private_key ();
40   else
41     return nvc_new ();
42 }
43
44
45 void
46 test_getting_values (nvc_t pk)
47 {
48   nve_t e;
49
50   e = nvc_lookup (pk, "Comment:");
51   assert (e);
52
53   /* Names are case-insensitive.  */
54   e = nvc_lookup (pk, "comment:");
55   assert (e);
56   e = nvc_lookup (pk, "COMMENT:");
57   assert (e);
58
59   e = nvc_lookup (pk, "SomeOtherName:");
60   assert (e);
61 }
62
63
64 void
65 test_key_extraction (nvc_t pk)
66 {
67   gpg_error_t err;
68   gcry_sexp_t key;
69
70   if (private_key_mode)
71     {
72       err = nvc_get_private_key (pk, &key);
73       assert (err == 0);
74       assert (key);
75
76       if (verbose)
77         gcry_sexp_dump (key);
78
79       gcry_sexp_release (key);
80     }
81   else
82     {
83       err = nvc_get_private_key (pk, &key);
84       assert (gpg_err_code (err) == GPG_ERR_MISSING_KEY);
85     }
86 }
87
88
89 void
90 test_iteration (nvc_t pk)
91 {
92   int i;
93   nve_t e;
94
95   i = 0;
96   for (e = nvc_first (pk); e; e = nve_next (e))
97     i++;
98   assert (i == 4);
99
100   i = 0;
101   for (e = nvc_lookup (pk, "Comment:");
102        e;
103        e = nve_next_value (e, "Comment:"))
104     i++;
105   assert (i == 3);
106 }
107
108
109 void
110 test_whitespace (nvc_t pk)
111 {
112   nve_t e;
113
114   e = nvc_lookup (pk, "One:");
115   assert (e);
116   assert (strcmp (nve_value (e), "WithoutWhitespace") == 0);
117
118   e = nvc_lookup (pk, "Two:");
119   assert (e);
120   assert (strcmp (nve_value (e), "With Whitespace") == 0);
121
122   e = nvc_lookup (pk, "Three:");
123   assert (e);
124   assert (strcmp (nve_value (e),
125                   "Blank lines in continuations encode newlines.\n"
126                   "Next paragraph.") == 0);
127 }
128
129
130 struct
131 {
132   char *value;
133   void (*test_func) (nvc_t);
134 } tests[] =
135   {
136     {
137       "# This is a comment followed by an empty line\n"
138       "\n",
139       NULL,
140     },
141     {
142       "# This is a comment followed by two empty lines, Windows style\r\n"
143       "\r\n"
144       "\r\n",
145       NULL,
146     },
147     {
148       "# Some name,value pairs\n"
149       "Comment: Some comment.\n"
150       "SomeOtherName: Some value.\n",
151       test_getting_values,
152     },
153     {
154       "  # Whitespace is preserved as much as possible\r\n"
155       "Comment:Some comment.\n"
156       "SomeOtherName: Some value.   \n",
157       test_getting_values,
158     },
159     {
160       "# Values may be continued in the next line as indicated by leading\n"
161       "# space\n"
162       "Comment: Some rather long\n"
163       "  comment that is continued in the next line.\n"
164       "\n"
165       "  Blank lines with or without whitespace are allowed within\n"
166       "  continuations to allow paragraphs.\n"
167       "SomeOtherName: Some value.\n",
168       test_getting_values,
169     },
170     {
171       "# Names may be given multiple times forming an array of values\n"
172       "Comment: Some comment, element 0.\n"
173       "Comment: Some comment, element 1.\n"
174       "Comment: Some comment, element 2.\n"
175       "SomeOtherName: Some value.\n",
176       test_iteration,
177     },
178     {
179       "# One whitespace at the beginning of a continuation is swallowed.\n"
180       "One: Without\n"
181       " Whitespace\n"
182       "Two: With\n"
183       "  Whitespace\n"
184       "Three: Blank lines in continuations encode newlines.\n"
185       "\n"
186       "  Next paragraph.\n",
187       test_whitespace,
188     },
189     {
190       "Description: Key to sign all GnuPG released tarballs.\n"
191       "  The key is actually stored on a smart card.\n"
192       "Use-for-ssh: yes\n"
193       "OpenSSH-cert: long base64 encoded string wrapped so that this\n"
194       "  key file can be easily edited with a standard editor.\n"
195       "Key: (shadowed-private-key\n"
196       "  (rsa\n"
197       "  (n #00AA1AD2A55FD8C8FDE9E1941772D9CC903FA43B268CB1B5A1BAFDC900\n"
198       "  2961D8AEA153424DC851EF13B83AC64FBE365C59DC1BD3E83017C90D4365B4\n"
199       "  83E02859FC13DB5842A00E969480DB96CE6F7D1C03600392B8E08EF0C01FC7\n"
200       "  19F9F9086B25AD39B4F1C2A2DF3E2BE317110CFFF21D4A11455508FE407997\n"
201       "  601260816C8422297C0637BB291C3A079B9CB38A92CE9E551F80AA0EBF4F0E\n"
202       "  72C3F250461E4D31F23A7087857FC8438324A013634563D34EFDDCBF2EA80D\n"
203       "  F9662C9CCD4BEF2522D8BDFED24CEF78DC6B309317407EAC576D889F88ADA0\n"
204       "  8C4FFB480981FB68C5C6CA27503381D41018E6CDC52AAAE46B166BDC10637A\n"
205       "  E186A02BA2497FDC5D1221#)\n"
206       "  (e #00010001#)\n"
207       "  (shadowed t1-v1\n"
208       "   (#D2760001240102000005000011730000# OPENPGP.1)\n"
209       "    )))\n",
210       test_key_extraction,
211     },
212   };
213
214
215 static char *
216 nvc_to_string (nvc_t pk)
217 {
218   gpg_error_t err;
219   char *buf;
220   size_t len;
221   estream_t sink;
222
223   sink = es_fopenmem (0, "rw");
224   assert (sink);
225
226   err = nvc_write (pk, sink);
227   assert (err == 0);
228
229   len = es_ftell (sink);
230   buf = xmalloc (len+1);
231   assert (buf);
232
233   es_fseek (sink, 0, SEEK_SET);
234   es_read (sink, buf, len, NULL);
235   buf[len] = 0;
236
237   es_fclose (sink);
238   return buf;
239 }
240
241
242 void dummy_free (void *p) { (void) p; }
243 void *dummy_realloc (void *p, size_t s) { (void) s; return p; }
244
245 void
246 run_tests (void)
247 {
248   gpg_error_t err;
249   nvc_t pk;
250
251   int i;
252   for (i = 0; i < DIM (tests); i++)
253     {
254       estream_t source;
255       char *buf;
256       size_t len;
257
258       len = strlen (tests[i].value);
259       source = es_mopen (tests[i].value, len, len,
260                          0, dummy_realloc, dummy_free, "r");
261       assert (source);
262
263       if (private_key_mode)
264         err = nvc_parse_private_key (&pk, NULL, source);
265       else
266         err = nvc_parse (&pk, NULL, source);
267       assert (err == 0);
268       assert (pk);
269
270       if (verbose)
271         {
272           err = nvc_write (pk, es_stderr);
273           assert (err == 0);
274         }
275
276       buf = nvc_to_string (pk);
277       assert (memcmp (tests[i].value, buf, len) == 0);
278
279       es_fclose (source);
280       xfree (buf);
281
282       if (tests[i].test_func)
283         tests[i].test_func (pk);
284
285       nvc_release (pk);
286     }
287 }
288
289
290 void
291 run_modification_tests (void)
292 {
293   gpg_error_t err;
294   nvc_t pk;
295   gcry_sexp_t key;
296   char *buf;
297
298   pk = my_nvc_new ();
299   assert (pk);
300
301   nvc_set (pk, "Foo:", "Bar");
302   buf = nvc_to_string (pk);
303   assert (strcmp (buf, "Foo: Bar\n") == 0);
304   xfree (buf);
305
306   nvc_set (pk, "Foo:", "Baz");
307   buf = nvc_to_string (pk);
308   assert (strcmp (buf, "Foo: Baz\n") == 0);
309   xfree (buf);
310
311   nvc_set (pk, "Bar:", "Bazzel");
312   buf = nvc_to_string (pk);
313   assert (strcmp (buf, "Foo: Baz\nBar: Bazzel\n") == 0);
314   xfree (buf);
315
316   nvc_add (pk, "Foo:", "Bar");
317   buf = nvc_to_string (pk);
318   assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\n") == 0);
319   xfree (buf);
320
321   nvc_add (pk, "DontExistYet:", "Bar");
322   buf = nvc_to_string (pk);
323   assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\nDontExistYet: Bar\n")
324           == 0);
325   xfree (buf);
326
327   nvc_delete (pk, nvc_lookup (pk, "DontExistYet:"));
328   buf = nvc_to_string (pk);
329   assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\n") == 0);
330   xfree (buf);
331
332   nvc_delete (pk, nve_next_value (nvc_lookup (pk, "Foo:"), "Foo:"));
333   buf = nvc_to_string (pk);
334   assert (strcmp (buf, "Foo: Baz\nBar: Bazzel\n") == 0);
335   xfree (buf);
336
337   nvc_delete (pk, nvc_lookup (pk, "Foo:"));
338   buf = nvc_to_string (pk);
339   assert (strcmp (buf, "Bar: Bazzel\n") == 0);
340   xfree (buf);
341
342   nvc_delete (pk, nvc_first (pk));
343   buf = nvc_to_string (pk);
344   assert (strcmp (buf, "") == 0);
345   xfree (buf);
346
347   nvc_set (pk, "Foo:", "A really long value spanning across multiple lines"
348            " that has to be wrapped at a convenient space.");
349   buf = nvc_to_string (pk);
350   assert (strcmp (buf, "Foo: A really long value spanning across multiple"
351                   " lines that has to be\n  wrapped at a convenient space.\n")
352           == 0);
353   xfree (buf);
354
355   nvc_set (pk, "Foo:", "XA really long value spanning across multiple lines"
356            " that has to be wrapped at a convenient space.");
357   buf = nvc_to_string (pk);
358   assert (strcmp (buf, "Foo: XA really long value spanning across multiple"
359                   " lines that has to\n  be wrapped at a convenient space.\n")
360           == 0);
361   xfree (buf);
362
363   nvc_set (pk, "Foo:", "XXXXA really long value spanning across multiple lines"
364            " that has to be wrapped at a convenient space.");
365   buf = nvc_to_string (pk);
366   assert (strcmp (buf, "Foo: XXXXA really long value spanning across multiple"
367                   " lines that has\n  to be wrapped at a convenient space.\n")
368           == 0);
369   xfree (buf);
370
371   nvc_set (pk, "Foo:", "Areallylongvaluespanningacrossmultiplelines"
372            "thathastobewrappedataconvenientspacethatisnotthere.");
373   buf = nvc_to_string (pk);
374   assert (strcmp (buf, "Foo: Areallylongvaluespanningacrossmultiplelinesthat"
375                   "hastobewrappedataco\n nvenientspacethatisnotthere.\n")
376           == 0);
377   xfree (buf);
378   nvc_release (pk);
379
380   pk = my_nvc_new ();
381   assert (pk);
382
383   err = gcry_sexp_build (&key, NULL, "(hello world)");
384   assert (err == 0);
385   assert (key);
386
387   if (private_key_mode)
388     {
389       err = nvc_set_private_key (pk, key);
390       assert (err == 0);
391
392       buf = nvc_to_string (pk);
393       assert (strcmp (buf, "Key: (hello world)\n") == 0);
394       xfree (buf);
395     }
396   else
397     {
398       err = nvc_set_private_key (pk, key);
399       assert (gpg_err_code (err) == GPG_ERR_MISSING_KEY);
400     }
401   gcry_sexp_release (key);
402   nvc_release (pk);
403 }
404
405
406 void
407 convert (const char *fname)
408 {
409   gpg_error_t err;
410   estream_t source;
411   gcry_sexp_t key;
412   char *buf;
413   size_t buflen;
414   struct stat st;
415   nvc_t pk;
416
417   source = es_fopen (fname, "rb");
418   if (source == NULL)
419     goto leave;
420
421   if (fstat (es_fileno (source), &st))
422     goto leave;
423
424   buflen = st.st_size;
425   buf = xtrymalloc (buflen+1);
426   assert (buf);
427
428   if (es_fread (buf, buflen, 1, source) != 1)
429     goto leave;
430
431   err = gcry_sexp_sscan (&key, NULL, buf, buflen);
432   if (err)
433     {
434       fprintf (stderr, "malformed s-expression in %s\n", fname);
435       exit (1);
436     }
437
438   pk = my_nvc_new ();
439   assert (pk);
440
441   err = nvc_set_private_key (pk, key);
442   assert (err == 0);
443
444   err = nvc_write (pk, es_stdout);
445   assert (err == 0);
446
447   return;
448
449  leave:
450   perror (fname);
451   exit (1);
452 }
453
454
455 void
456 parse (const char *fname)
457 {
458   gpg_error_t err;
459   estream_t source;
460   char *buf;
461   nvc_t pk_a, pk_b;
462   nve_t e;
463   int line;
464
465   source = es_fopen (fname, "rb");
466   if (source == NULL)
467     {
468       perror (fname);
469       exit (1);
470     }
471
472   if (private_key_mode)
473     err = nvc_parse_private_key (&pk_a, &line, source);
474   else
475     err = nvc_parse (&pk_a, &line, source);
476   if (err)
477     {
478       fprintf (stderr, "failed to parse %s line %d: %s\n",
479                fname, line, gpg_strerror (err));
480       exit (1);
481     }
482
483   buf = nvc_to_string (pk_a);
484   xfree (buf);
485
486   pk_b = my_nvc_new ();
487   assert (pk_b);
488
489   for (e = nvc_first (pk_a); e; e = nve_next (e))
490     {
491       gcry_sexp_t key = NULL;
492
493       if (private_key_mode && !strcasecmp (nve_name (e), "Key:"))
494         {
495           err = nvc_get_private_key (pk_a, &key);
496           if (err)
497             key = NULL;
498         }
499
500       if (key)
501         {
502           err = nvc_set_private_key (pk_b, key);
503           assert (err == 0);
504         }
505       else
506         {
507           err = nvc_add (pk_b, nve_name (e), nve_value (e));
508           assert (err == 0);
509         }
510     }
511
512     buf = nvc_to_string (pk_b);
513     if (verbose)
514       fprintf (stdout, "%s", buf);
515     xfree (buf);
516 }
517
518
519 void
520 print_usage (void)
521 {
522   fprintf (stderr,
523            "usage: t-private-keys [--verbose]"
524            " [--convert <private-key-file>"
525            " || --parse-key <extended-private-key-file>"
526            " || --parse <file> ]\n");
527   exit (2);
528 }
529
530
531 int
532 main (int argc, char **argv)
533 {
534   enum { TEST, CONVERT, PARSE, PARSEKEY } command = TEST;
535
536   if (argc)
537     { argc--; argv++; }
538   if (argc && !strcmp (argv[0], "--verbose"))
539     {
540       verbose = 1;
541       argc--; argv++;
542     }
543
544   if (argc && !strcmp (argv[0], "--convert"))
545     {
546       command = CONVERT;
547       argc--; argv++;
548       if (argc != 1)
549         print_usage ();
550     }
551
552   if (argc && !strcmp (argv[0], "--parse-key"))
553     {
554       command = PARSEKEY;
555       argc--; argv++;
556       if (argc != 1)
557         print_usage ();
558     }
559
560   if (argc && !strcmp (argv[0], "--parse"))
561     {
562       command = PARSE;
563       argc--; argv++;
564       if (argc != 1)
565         print_usage ();
566     }
567
568   switch (command)
569     {
570     case TEST:
571       run_tests ();
572       run_modification_tests ();
573       private_key_mode = 1;
574       run_tests ();
575       run_modification_tests ();
576       break;
577
578     case CONVERT:
579       convert (*argv);
580       break;
581
582     case PARSEKEY:
583       private_key_mode = 1;
584       parse (*argv);
585       break;
586
587     case PARSE:
588       parse (*argv);
589       break;
590     }
591
592   return 0;
593 }