/*
 *
 *  Visual Voicemail Daemon
 *
 *  Copyright (C) 2010-2011  Intel Corporation.
 *                2021, Chris Talbot <chris@talbothome.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/mman.h>

#include <glib.h>
#include <glib/gprintf.h>

#include "vvmutil.h"

#define VVM_SHA1_UUID_LEN 20

struct vvm_test
{
  const char *pathname;
  const char *content_type;
  const char *sha;
};

static const char vvm_mbox_1[] = "./unit/Tmobile1.mbox";
static const char vvm_mbox_1_content_type[] = "multipart/mixed; boundary=\"_Part_922_1624281418\"";
static const char vvm_mbox_1_sha[] = "9D25A01C4D47C3F309D4C45B0C584DFF1B5CBE11";

static const struct vvm_test vvm_test_1 = {
  .pathname = vvm_mbox_1,
  .content_type = vvm_mbox_1_content_type,
  .sha = vvm_mbox_1_sha
};

static const char vvm_mbox_2[] = "./unit/ATT1.mbox";
static const char vvm_mbox_2_content_type[] = "multipart/mixed; Boundary=\"============>>AnyPath 1624551264<<============\"";
static const char vvm_mbox_2_sha[] = "6160CA0D43739ED268FCC59757D8758238D179AD";

static const struct vvm_test vvm_test_2 = {
  .pathname = vvm_mbox_2,
  .content_type = vvm_mbox_2_content_type,
  .sha = vvm_mbox_2_sha
};

static const char vvm_mbox_3[] = "./unit/MintMobile1.mbox";
static const char vvm_mbox_3_content_type[] = "multipart/mixed; boundary=\"_Part_740_1626995070\"";
static const char vvm_mbox_3_sha[] = "8C389E984DB96CA75C9ECEF7EB4A2A01EFB24727";


static const struct vvm_test vvm_test_3 = {
  .pathname = vvm_mbox_3,
  .content_type = vvm_mbox_3_content_type,
  .sha = vvm_mbox_3_sha
};

static const char vvm_mbox_4[] = "./unit/VZWUSA1.mbox";
static const char vvm_mbox_4_content_type[] = "multipart/voice-message;boundary=\"----=_Part_45040777_18760913.1634822783904\"";
static const char vvm_mbox_4_sha[] = "0D59618C89F509FD9860EB316B2B8366FD404E88";

static const struct vvm_test vvm_test_4 = {
  .pathname = vvm_mbox_4,
  .content_type = vvm_mbox_4_content_type,
  .sha = vvm_mbox_4_sha
};

static const char *
digest_to_str (const unsigned char *digest)
{
  static char buf[VVM_SHA1_UUID_LEN * 2 + 1];
  unsigned int i;

  for (i = 0; i < VVM_SHA1_UUID_LEN; i++)
    sprintf (&buf[i * 2], "%02X", digest[i]);

  buf[VVM_SHA1_UUID_LEN * 2] = 0;

  return buf;
}

static const char *
generate_sha_from_pdu (const unsigned char *pdu,
                       unsigned int         len)
{
  GChecksum *checksum;
  guint8 digest[VVM_SHA1_UUID_LEN];
  gsize digest_size = VVM_SHA1_UUID_LEN;

  checksum = g_checksum_new (G_CHECKSUM_SHA1);
  if (checksum == NULL)
    return NULL;

  g_checksum_update (checksum, pdu, len);

  g_checksum_get_digest (checksum, digest, &digest_size);

  g_checksum_free (checksum);

  return digest_to_str (digest);
}


static void
test_decode_vvm (gconstpointer data)
{
  const struct vvm_test *test = data;
  struct voicemail *vvm_msg;
  char *tmp_dir;
  const char *sha;
  unsigned int len;
  gboolean ret = TRUE;

  vvm_msg = g_try_new0 (struct voicemail, 1);
  if (vvm_msg == NULL)
    {
      g_printerr ("Failed allocate message");
      return;
    }

  struct stat st;
  unsigned char *pdu;
  int fd;

  fd = open (test->pathname, O_RDONLY);
  if (fd < 0)
    {
      g_printerr ("Failed to open path %s\n", test->pathname);
      goto out;
    }

  if (fstat (fd, &st) < 0)
    {
      g_printerr ("Failed to stat %s\n", test->pathname);
      close (fd);
      goto out;
    }

  len = st.st_size;

  pdu = mmap (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
  if (!pdu || pdu == MAP_FAILED)
    {
      g_printerr ("Failed to mmap %s\n", test->pathname);
      close (fd);
      goto out;
    }

  vvm_msg->contents = (char *) pdu;
  vvm_msg->content_type = g_strdup (test->content_type);
  vvm_msg->file_uuid = vvm_store_generate_uuid_objpath ();

  tmp_dir = g_strdup_printf ("%s/", g_get_home_dir ());

  ret = vvm_util_decode_vvm_all_email_attachments (vvm_msg, tmp_dir);

  if (ret != TRUE)
    g_printerr ("vvm_util_decode_vvm_all_email_attachments() Failed!\n");
  g_assert (ret == TRUE);

  g_free (tmp_dir);

  munmap (pdu, len);

  close (fd);

  fd = open (vvm_msg->attachments, O_RDONLY);
  if (fd < 0)
    {
      g_printerr ("Failed to open Attachment %s\n", vvm_msg->attachments);
      goto out;
    }

  if (fstat (fd, &st) < 0)
    {
      g_printerr ("Failed to stat %s\n", test->pathname);
      close (fd);
      goto out;
    }

  len = st.st_size;

  pdu = mmap (NULL, len, PROT_READ, MAP_SHARED, fd, 0);
  if (!pdu || pdu == MAP_FAILED)
    {
      g_printerr ("Failed to mmap %s\n", test->pathname);
      close (fd);
      goto out;
    }

  sha = generate_sha_from_pdu (pdu, len);
  if (g_strcmp0 (sha, test->sha) != 0)
    g_debug ("%s sha: %s is different! compare sha: %s\n",
             vvm_msg->attachments,
             test->sha,
             sha);
  g_assert (g_strcmp0 (sha, test->sha) == 0);

  munmap (pdu, len);

  close (fd);

  unlink (vvm_msg->attachments);

  // Contents are freed already
  vvm_msg->contents = NULL;

  vvm_util_delete_vvm_message (vvm_msg);
  return;

 out:
  vvm_util_delete_vvm_message (vvm_msg);
  g_assert (FALSE);
}

int
main (int    argc,
      char **argv)
{
  g_test_init (&argc, &argv, NULL);

  g_test_add_data_func ("/vvmutil/Decode T-Mobile USA 1 (One Attachment)",
                        &vvm_test_1, test_decode_vvm);

  g_test_add_data_func ("/vvmutil/Decode AT&T USA 1 (One Attachment)",
                        &vvm_test_2, test_decode_vvm);

  g_test_add_data_func ("/vvmutil/Decode Mint Mobile USA 1 (One Attachment)",
                        &vvm_test_3, test_decode_vvm);

  g_test_add_data_func ("/vvmutil/Decode VZW USA 1 (One Attachment)",
                        &vvm_test_4, test_decode_vvm);

  return g_test_run ();
}
