Blame tests/json/t-json.c

Packit Service 30b792
/* t-json.c - Regression test.
Packit Service 30b792
 * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
Packit Service 30b792
 *                    Software engineering by Intevation GmbH
Packit Service 30b792
 *
Packit Service 30b792
 * This file is part of GPGME.
Packit Service 30b792
 *
Packit Service 30b792
 * GPGME is free software; you can redistribute it and/or modify it
Packit Service 30b792
 * under the terms of the GNU Lesser General Public License as
Packit Service 30b792
 * published by the Free Software Foundation; either version 2.1 of
Packit Service 30b792
 * the License, or (at your option) any later version.
Packit Service 30b792
 *
Packit Service 30b792
 * GPGME is distributed in the hope that it will be useful, but
Packit Service 30b792
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 30b792
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 30b792
 * Lesser General Public License for more details.
Packit Service 30b792
 *
Packit Service 30b792
 * You should have received a copy of the GNU Lesser General Public
Packit Service 30b792
 * License along with this program; if not, see <https://gnu.org/licenses/>.
Packit Service 30b792
 * SPDX-License-Identifier: LGPL-2.1-or-later
Packit Service 30b792
 */
Packit Service 30b792
Packit Service 30b792
#ifdef HAVE_CONFIG_H
Packit Service 30b792
# include <config.h>
Packit Service 30b792
#endif
Packit Service 30b792
Packit Service 30b792
#include <stdlib.h>
Packit Service 30b792
#include <stdio.h>
Packit Service 30b792
#include <string.h>
Packit Service 30b792
#include <errno.h>
Packit Service 30b792
#include <sys/stat.h>
Packit Service 30b792
Packit Service 30b792
#include <gpgme.h>
Packit Service 30b792
#include <gpg-error.h>
Packit Service 30b792
Packit Service 30b792
#include "../gpg/t-support.h"
Packit Service 30b792
#include "../../src/cJSON.h"
Packit Service 30b792
Packit Service 30b792
/* Register tests here */
Packit Service 30b792
static const char*tests[] = { "t-config", "t-version",
Packit Service 30b792
    "t-keylist", "t-keylist-secret", "t-decrypt", "t-config-opt",
Packit Service 30b792
    "t-encrypt", "t-encrypt-sign", "t-sign", "t-verify",
Packit Service 30b792
    "t-decrypt-verify", "t-export", "t-createkey",
Packit Service 30b792
    "t-export-secret-info", "t-chunking", "t-sig-notations",
Packit Service 30b792
    /* For these two the order is important
Packit Service 30b792
     * as t-import imports the deleted key from t-delete */
Packit Service 30b792
    "t-delete", "t-import",
Packit Service 30b792
    NULL };
Packit Service 30b792
Packit Service 30b792
static int verbose = 0;
Packit Service 30b792
Packit Service 30b792
Packit Service 30b792
/* Read the next number in the version string STR and return it in
Packit Service 30b792
   *NUMBER.  Return a pointer to the tail of STR after parsing, or
Packit Service 30b792
   *NULL if the version string was invalid.  */
Packit Service 30b792
static const char *
Packit Service 30b792
parse_version_number (const char *str, int *number)
Packit Service 30b792
{
Packit Service 30b792
#define MAXVAL ((INT_MAX - 10) / 10)
Packit Service 30b792
  int val = 0;
Packit Service 30b792
Packit Service 30b792
  /* Leading zeros are not allowed.  */
Packit Service 30b792
  if (*str == '0' && isdigit(str[1]))
Packit Service 30b792
    return NULL;
Packit Service 30b792
Packit Service 30b792
  while (isdigit (*str) && val <= MAXVAL)
Packit Service 30b792
    {
Packit Service 30b792
      val *= 10;
Packit Service 30b792
      val += *(str++) - '0';
Packit Service 30b792
    }
Packit Service 30b792
  *number = val;
Packit Service 30b792
  return val > MAXVAL ? NULL : str;
Packit Service 30b792
}
Packit Service 30b792
Packit Service 30b792
Packit Service 30b792
/* Parse the version string STR in the format MAJOR.MINOR.MICRO (for
Packit Service 30b792
   example, 9.3.2) and return the components in MAJOR, MINOR and MICRO
Packit Service 30b792
   as integers.  The function returns the tail of the string that
Packit Service 30b792
   follows the version number.  This might be the empty string if there
Packit Service 30b792
   is nothing following the version number, or a patchlevel.  The
Packit Service 30b792
   function returns NULL if the version string is not valid.  */
Packit Service 30b792
static const char *
Packit Service 30b792
parse_version_string (const char *str, int *major, int *minor, int *micro)
Packit Service 30b792
{
Packit Service 30b792
  str = parse_version_number (str, major);
Packit Service 30b792
  if (!str || *str != '.')
Packit Service 30b792
    return NULL;
Packit Service 30b792
  str++;
Packit Service 30b792
Packit Service 30b792
  str = parse_version_number (str, minor);
Packit Service 30b792
  if (!str || *str != '.')
Packit Service 30b792
    return NULL;
Packit Service 30b792
  str++;
Packit Service 30b792
Packit Service 30b792
  str = parse_version_number (str, micro);
Packit Service 30b792
  if (!str)
Packit Service 30b792
    return NULL;
Packit Service 30b792
Packit Service 30b792
  /* A patchlevel might follow.  */
Packit Service 30b792
  return str;
Packit Service 30b792
}
Packit Service 30b792
Packit Service 30b792
Packit Service 30b792
/* Return true if MY_VERSION is at least REQ_VERSION, and false
Packit Service 30b792
   otherwise.  */
Packit Service 30b792
static int
Packit Service 30b792
compare_versions (const char *my_version,
Packit Service 30b792
                  const char *rq_version)
Packit Service 30b792
{
Packit Service 30b792
  int my_major, my_minor, my_micro;
Packit Service 30b792
  int rq_major, rq_minor, rq_micro;
Packit Service 30b792
  const char *my_plvl, *rq_plvl;
Packit Service 30b792
Packit Service 30b792
  if (!rq_version)
Packit Service 30b792
    return 1;
Packit Service 30b792
  if (!my_version)
Packit Service 30b792
    return 0;
Packit Service 30b792
Packit Service 30b792
  my_plvl = parse_version_string (my_version, &my_major, &my_minor, &my_micro);
Packit Service 30b792
  if (!my_plvl)
Packit Service 30b792
    return 0;
Packit Service 30b792
Packit Service 30b792
  rq_plvl = parse_version_string (rq_version, &rq_major, &rq_minor, &rq_micro);
Packit Service 30b792
  if (!rq_plvl)
Packit Service 30b792
    return 0;
Packit Service 30b792
Packit Service 30b792
  if (my_major > rq_major
Packit Service 30b792
      || (my_major == rq_major && my_minor > rq_minor)
Packit Service 30b792
      || (my_major == rq_major && my_minor == rq_minor
Packit Service 30b792
	  && my_micro > rq_micro)
Packit Service 30b792
      || (my_major == rq_major && my_minor == rq_minor
Packit Service 30b792
	  && my_micro == rq_micro && strcmp (my_plvl, rq_plvl) >= 0))
Packit Service 30b792
    return 1;
Packit Service 30b792
Packit Service 30b792
  return 0;
Packit Service 30b792
}
Packit Service 30b792
Packit Service 30b792
/* Return true if we have the required gpg version.
Packit Service 30b792
Packit Service 30b792
   This should probably go into gpgrt or gpgme proper.
Packit Service 30b792
*/
Packit Service 30b792
static int
Packit Service 30b792
check_gpg_version (const char *req_version)
Packit Service 30b792
{
Packit Service 30b792
  gpgme_engine_info_t engine_info;
Packit Service 30b792
  init_gpgme (GPGME_PROTOCOL_OpenPGP);
Packit Service 30b792
Packit Service 30b792
  fail_if_err (gpgme_get_engine_info (&engine_info));
Packit Service 30b792
  for (; engine_info; engine_info = engine_info->next)
Packit Service 30b792
    if (engine_info->protocol == GPGME_PROTOCOL_OpenPGP)
Packit Service 30b792
      break;
Packit Service 30b792
Packit Service 30b792
  test (engine_info);
Packit Service 30b792
Packit Service 30b792
  return compare_versions (engine_info->version, req_version);
Packit Service 30b792
}
Packit Service 30b792
Packit Service 30b792
static char *
Packit Service 30b792
get_file (const char *fname)
Packit Service 30b792
{
Packit Service 30b792
  gpg_error_t err;
Packit Service 30b792
  gpgrt_stream_t fp;
Packit Service 30b792
  struct stat st;
Packit Service 30b792
  char *buf;
Packit Service 30b792
  size_t buflen;
Packit Service 30b792
Packit Service 30b792
  fp = gpgrt_fopen (fname, "r");
Packit Service 30b792
  if (!fp)
Packit Service 30b792
    {
Packit Service 30b792
      err = gpg_error_from_syserror ();
Packit Service 30b792
      fprintf (stderr, "Error: can't open '%s': %s\n", fname,
Packit Service 30b792
               gpg_strerror (err));
Packit Service 30b792
      return NULL;
Packit Service 30b792
    }
Packit Service 30b792
Packit Service 30b792
  if (fstat (gpgrt_fileno(fp), &st))
Packit Service 30b792
    {
Packit Service 30b792
      err = gpg_error_from_syserror ();
Packit Service 30b792
      fprintf (stderr, "Error: can't stat '%s': %s\n", fname,
Packit Service 30b792
               gpg_strerror (err));
Packit Service 30b792
      gpgrt_fclose (fp);
Packit Service 30b792
      return NULL;
Packit Service 30b792
    }
Packit Service 30b792
Packit Service 30b792
  buflen = st.st_size;
Packit Service 30b792
  buf = malloc (buflen+1);
Packit Service 30b792
  if (!buf)
Packit Service 30b792
    {
Packit Service 30b792
      fprintf (stderr, "Error: no mem\n");
Packit Service 30b792
      gpgrt_fclose (fp);
Packit Service 30b792
      return NULL;
Packit Service 30b792
    }
Packit Service 30b792
Packit Service 30b792
  if (gpgrt_fread (buf, buflen, 1, fp) != 1)
Packit Service 30b792
    {
Packit Service 30b792
      err = gpg_error_from_syserror ();
Packit Service 30b792
      fprintf (stderr, "error reading '%s': %s\n", fname, gpg_strerror (err));
Packit Service 30b792
      gpgrt_fclose (fp);
Packit Service 30b792
      free (buf);
Packit Service 30b792
      return NULL;
Packit Service 30b792
    }
Packit Service 30b792
  buf[buflen] = 0;
Packit Service 30b792
  gpgrt_fclose (fp);
Packit Service 30b792
Packit Service 30b792
  return buf;
Packit Service 30b792
}
Packit Service 30b792
Packit Service 30b792
/* Check that the element needle exists in hay. Returns 0 if
Packit Service 30b792
   the needle was found. */
Packit Service 30b792
int
Packit Service 30b792
test_contains (cjson_t needle, cjson_t hay)
Packit Service 30b792
{
Packit Service 30b792
  if (verbose == 2)
Packit Service 30b792
    fprintf (stderr, "%s: -------checking-------- "
Packit Service 30b792
                     "\n%s\n -------against-------- \n%s\n",
Packit Service 30b792
             nonnull (needle->string), cJSON_Print (needle),
Packit Service 30b792
             cJSON_Print (hay));
Packit Service 30b792
Packit Service 30b792
  /* Type check. This automatically checks bool vals and NULL */
Packit Service 30b792
  if (needle->type != hay->type)
Packit Service 30b792
    {
Packit Service 30b792
      if (verbose)
Packit Service 30b792
        fprintf (stderr, "%s: type mismatch expected %i got %i\n",
Packit Service 30b792
                 nonnull (needle->string), needle->type,
Packit Service 30b792
                 hay->type);
Packit Service 30b792
      return 1;
Packit Service 30b792
    }
Packit Service 30b792
Packit Service 30b792
  /* First the simple types */
Packit Service 30b792
  if (cjson_is_number (needle))
Packit Service 30b792
    {
Packit Service 30b792
      if (needle->valueint != hay->valueint)
Packit Service 30b792
        {
Packit Service 30b792
          if (verbose)
Packit Service 30b792
            fprintf (stderr, "%s: value mismatch. Expected %i got %i\n",
Packit Service 30b792
                     nonnull (needle->string), needle->valueint,
Packit Service 30b792
                     hay->valueint);
Packit Service 30b792
          return 1;
Packit Service 30b792
        }
Packit Service 30b792
    }
Packit Service 30b792
  if (cjson_is_string (needle))
Packit Service 30b792
    {
Packit Service 30b792
      if (strcmp (needle->valuestring, hay->valuestring) &&
Packit Service 30b792
          /* Use * as a general don't care placeholder */
Packit Service 30b792
          strcmp (needle->valuestring, "*"))
Packit Service 30b792
        {
Packit Service 30b792
          if (verbose)
Packit Service 30b792
            fprintf (stderr, "%s: string mismatch Expected '%s' got '%s'\n",
Packit Service 30b792
                     needle->string, needle->valuestring, hay->valuestring);
Packit Service 30b792
          return 1;
Packit Service 30b792
        }
Packit Service 30b792
    }
Packit Service 30b792
Packit Service 30b792
  /* Now the complex types */
Packit Service 30b792
  if (needle->child)
Packit Service 30b792
    {
Packit Service 30b792
      if (!hay->child)
Packit Service 30b792
        {
Packit Service 30b792
          fprintf (stderr, "Depth mismatch. Expected child for %s\n",
Packit Service 30b792
                   nonnull (needle->string));
Packit Service 30b792
        }
Packit Service 30b792
      if (test_contains (needle->child, hay->child))
Packit Service 30b792
        {
Packit Service 30b792
          int found = 0;
Packit Service 30b792
          for (cjson_t hit = hay->child; hit; hit = hit->next)
Packit Service 30b792
            {
Packit Service 30b792
              found |= !test_contains (needle->child, hit);
Packit Service 30b792
              if (found)
Packit Service 30b792
                {
Packit Service 30b792
                  break;
Packit Service 30b792
                }
Packit Service 30b792
            }
Packit Service 30b792
          if (!found)
Packit Service 30b792
            {
Packit Service 30b792
              return 1;
Packit Service 30b792
            }
Packit Service 30b792
        }
Packit Service 30b792
    }
Packit Service 30b792
Packit Service 30b792
  if (needle->prev)
Packit Service 30b792
    {
Packit Service 30b792
      return 0;
Packit Service 30b792
    }
Packit Service 30b792
Packit Service 30b792
  /* Walk elements of an array */
Packit Service 30b792
  for (cjson_t it = needle->next; it; it = it->next)
Packit Service 30b792
    {
Packit Service 30b792
      int found = 0;
Packit Service 30b792
      if (!it->string && it->child)
Packit Service 30b792
        {
Packit Service 30b792
          /* Try out all other anonymous children on the same level */
Packit Service 30b792
          cjson_t hit = hay;
Packit Service 30b792
          /* Return to the beginning */
Packit Service 30b792
          while (hit->prev)
Packit Service 30b792
            {
Packit Service 30b792
              hit = hit->prev;
Packit Service 30b792
            }
Packit Service 30b792
          for (; hit && hit->child; hit = hit->next)
Packit Service 30b792
            {
Packit Service 30b792
              found |= !test_contains (it->child, hit->child);
Packit Service 30b792
              if (found)
Packit Service 30b792
                {
Packit Service 30b792
                  break;
Packit Service 30b792
                }
Packit Service 30b792
            }
Packit Service 30b792
          if (!found)
Packit Service 30b792
            {
Packit Service 30b792
              return 1;
Packit Service 30b792
            }
Packit Service 30b792
          continue;
Packit Service 30b792
        }
Packit Service 30b792
Packit Service 30b792
      /* Try the children in the haystack */
Packit Service 30b792
      for (cjson_t hit = hay; hit; hit = hit->next)
Packit Service 30b792
        {
Packit Service 30b792
          if (hit->string && it->string &&
Packit Service 30b792
              !strcmp (hit->string, it->string))
Packit Service 30b792
            {
Packit Service 30b792
              found = 1;
Packit Service 30b792
              if (test_contains (it, hit))
Packit Service 30b792
                {
Packit Service 30b792
                  return 1;
Packit Service 30b792
                }
Packit Service 30b792
            }
Packit Service 30b792
        }
Packit Service 30b792
      if (!found)
Packit Service 30b792
        {
Packit Service 30b792
          if (verbose)
Packit Service 30b792
            fprintf (stderr, "Failed to find '%s' in list\n",
Packit Service 30b792
                     nonnull (it->string));
Packit Service 30b792
          return 1;
Packit Service 30b792
        }
Packit Service 30b792
    }
Packit Service 30b792
  return 0;
Packit Service 30b792
}
Packit Service 30b792
Packit Service 30b792
Packit Service 30b792
int
Packit Service 30b792
check_response (const char *response, const char *expected)
Packit Service 30b792
{
Packit Service 30b792
  cjson_t hay;
Packit Service 30b792
  cjson_t needle;
Packit Service 30b792
  int rc;
Packit Service 30b792
  size_t erroff;
Packit Service 30b792
Packit Service 30b792
  hay = cJSON_Parse (response, &erroff);
Packit Service 30b792
Packit Service 30b792
  if (!hay)
Packit Service 30b792
    {
Packit Service 30b792
      fprintf (stderr, "Failed to parse json at %i:\n%s\n", (int) erroff,
Packit Service 30b792
               response);
Packit Service 30b792
      return 1;
Packit Service 30b792
    }
Packit Service 30b792
  needle = cJSON_Parse (expected, &erroff);
Packit Service 30b792
  if (!needle)
Packit Service 30b792
    {
Packit Service 30b792
      fprintf (stderr, "Failed to parse json at %i:\n%s\n", (int) erroff,
Packit Service 30b792
               expected);
Packit Service 30b792
      cJSON_Delete (hay);
Packit Service 30b792
      return 1;
Packit Service 30b792
    }
Packit Service 30b792
Packit Service 30b792
  rc = test_contains (needle, hay);
Packit Service 30b792
Packit Service 30b792
  cJSON_Delete (needle);
Packit Service 30b792
  cJSON_Delete (hay);
Packit Service 30b792
  return rc;
Packit Service 30b792
}
Packit Service 30b792
Packit Service 30b792
Packit Service 30b792
int
Packit Service 30b792
run_test (const char *test, const char *gpgme_json)
Packit Service 30b792
{
Packit Service 30b792
  gpgme_ctx_t ctx;
Packit Service 30b792
  gpgme_data_t json_stdin = NULL;
Packit Service 30b792
  gpgme_data_t json_stdout = NULL;
Packit Service 30b792
  gpgme_data_t json_stderr = NULL;
Packit Service 30b792
  char *test_in;
Packit Service 30b792
  char *test_out;
Packit Service 30b792
  const char *argv[3];
Packit Service 30b792
  char *response;
Packit Service 30b792
  char *expected = NULL;
Packit Service 30b792
  size_t response_size;
Packit Service 30b792
  int rc = 0;
Packit Service 30b792
  const char *top_srcdir = getenv ("top_srcdir");
Packit Service 30b792
Packit Service 30b792
  if (!top_srcdir)
Packit Service 30b792
    {
Packit Service 30b792
      fprintf (stderr, "Error top_srcdir environment variable not set\n");
Packit Service 30b792
      exit(1);
Packit Service 30b792
    }
Packit Service 30b792
Packit Service 30b792
  gpgrt_asprintf (&test_in, "%s/tests/json/%s.in.json",
Packit Service 30b792
                  top_srcdir, test);
Packit Service 30b792
  gpgrt_asprintf (&test_out, "%s/tests/json/%s.out.json",
Packit Service 30b792
                  top_srcdir, test);
Packit Service 30b792
Packit Service 30b792
  printf ("Running %s...\n", test);
Packit Service 30b792
Packit Service 30b792
  fail_if_err (gpgme_new (&ctx));
Packit Service 30b792
Packit Service 30b792
  gpgme_set_protocol (ctx, GPGME_PROTOCOL_SPAWN);
Packit Service 30b792
Packit Service 30b792
  fail_if_err (gpgme_data_new_from_file (&json_stdin, test_in, 1));
Packit Service 30b792
  fail_if_err (gpgme_data_new (&json_stdout));
Packit Service 30b792
  fail_if_err (gpgme_data_new (&json_stderr));
Packit Service 30b792
Packit Service 30b792
  argv[0] = gpgme_json;
Packit Service 30b792
  argv[1] = "-s";
Packit Service 30b792
  argv[2] = NULL;
Packit Service 30b792
Packit Service 30b792
  fail_if_err (gpgme_op_spawn (ctx, gpgme_json, argv,
Packit Service 30b792
                               json_stdin,
Packit Service 30b792
                               json_stdout,
Packit Service 30b792
                               json_stderr,
Packit Service 30b792
                               0));
Packit Service 30b792
  response = gpgme_data_release_and_get_mem (json_stdout,
Packit Service 30b792
                                             &response_size);
Packit Service 30b792
  if (response_size)
Packit Service 30b792
    {
Packit Service 30b792
      expected = get_file (test_out);
Packit Service 30b792
Packit Service 30b792
      test (expected);
Packit Service 30b792
Packit Service 30b792
      rc = check_response (response, expected);
Packit Service 30b792
    }
Packit Service 30b792
  else
Packit Service 30b792
    {
Packit Service 30b792
      rc = 1;
Packit Service 30b792
    }
Packit Service 30b792
Packit Service 30b792
  if (!rc)
Packit Service 30b792
    {
Packit Service 30b792
      printf (" success\n");
Packit Service 30b792
      gpgme_data_release (json_stderr);
Packit Service 30b792
    }
Packit Service 30b792
  else
Packit Service 30b792
    {
Packit Service 30b792
      char *buf;
Packit Service 30b792
      size_t size;
Packit Service 30b792
Packit Service 30b792
      buf = gpgme_data_release_and_get_mem (json_stderr, &size);
Packit Service 30b792
      printf (" failed%s\n", response_size ? "" :
Packit Service 30b792
                             ", no response from gpgme-json");
Packit Service 30b792
      if (size)
Packit Service 30b792
        {
Packit Service 30b792
          printf ("gpgme-json stderr:\n%.*s\n", (int)size, buf);
Packit Service 30b792
        }
Packit Service 30b792
      free (buf);
Packit Service 30b792
    }
Packit Service 30b792
Packit Service 30b792
  free (test_out);
Packit Service 30b792
  free (test_in);
Packit Service 30b792
  free (response);
Packit Service 30b792
  free (expected);
Packit Service 30b792
  gpgme_data_release (json_stdin);
Packit Service 30b792
  gpgme_release (ctx);
Packit Service 30b792
Packit Service 30b792
  return rc;
Packit Service 30b792
}
Packit Service 30b792
Packit Service 30b792
int
Packit Service 30b792
main (int argc, char *argv[])
Packit Service 30b792
{
Packit Service 30b792
  const char *gpgme_json = getenv ("gpgme_json");
Packit Service 30b792
  int last_argc = -1;
Packit Service 30b792
Packit Service 30b792
  if (argc)
Packit Service 30b792
    { argc--; argv++; }
Packit Service 30b792
Packit Service 30b792
Packit Service 30b792
  while (argc && last_argc != argc )
Packit Service 30b792
    {
Packit Service 30b792
      last_argc = argc;
Packit Service 30b792
      if (!strcmp (*argv, "--verbose"))
Packit Service 30b792
        {
Packit Service 30b792
          verbose++;
Packit Service 30b792
          argc--; argv++;
Packit Service 30b792
        }
Packit Service 30b792
    }
Packit Service 30b792
Packit Service 30b792
  if (!check_gpg_version ("2.1.18"))
Packit Service 30b792
    {
Packit Service 30b792
      /* Lets not break too much or have to test all combinations */
Packit Service 30b792
      printf ("Testsuite skipped. Minimum GnuPG version (2.1.18) "
Packit Service 30b792
              "not found.\n");
Packit Service 30b792
      exit(0);
Packit Service 30b792
    }
Packit Service 30b792
Packit Service 30b792
  init_gpgme (GPGME_PROTOCOL_SPAWN);
Packit Service 30b792
Packit Service 30b792
  for (const char **test = tests; *test; test++)
Packit Service 30b792
    {
Packit Service 30b792
      if (run_test (*test, gpgme_json))
Packit Service 30b792
        {
Packit Service 30b792
          exit(1);
Packit Service 30b792
        }
Packit Service 30b792
    }
Packit Service 30b792
  return 0;
Packit Service 30b792
}