/* t-stream.c
 *        Copyright (C) 2002 Timo Schulz
 *
 * This file is part of OpenCDK.
 *
 * OpenCDK is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * OpenCDK 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 OpenCDK; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include <stdio.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <stdarg.h>

#include "opencdk.h"

/* we need this for _cdk_stream_get_opaque */
#include "../src/main.h"
#include "../src/filters.h"
#include "../src/stream.h"


int
stream_test_filters (int literal, int compress, int armor, int encrypt)
{
  char buf[128];
  CDK_STREAM s;
  CDK_DEK dek;
  int rc, i;

  rc = cdk_stream_new ("test.asc", &s);
  if (rc)
    {
      printf ("cdk_stream_new: `%s'\n", cdk_strerror (rc));
      return -1;
    }
  
  if (armor)
    cdk_stream_set_armor_flag (s, CDK_ARMOR_MESSAGE);
  if (encrypt)
    {
      rc = cdk_dek_new (&dek);
      if (rc)
        {
          cdk_stream_close (s);
          return rc;
        }
      cdk_dek_set_cipher( dek, GCRY_CIPHER_TWOFISH );
      cdk_stream_set_cipher_flag (s, dek, 0);
    }
  if (compress)
    cdk_stream_set_compress_flag (s, CDK_COMPRESS_ZLIB, 5);
  if (literal)
    cdk_stream_set_literal_flag (s, 0, "test.asc");

  cdk_stream_set_cache (s, 1);
  /* write a marker packet we won't filter! */
  buf[0] = (0x80 | (10<<2));
  buf[1] = 3;
  buf[2] = 0x50;
  buf[3] = 0x47;
  buf[4] = 0x50;
  cdk_stream_write (s, buf, 5);
  cdk_stream_set_cache (s, 0);

  for (i = 0; i < 512; i++)
    {
      switch (i % 4)
        {
        case 0: strcpy (buf, "Hello World!\n"); break;
        case 1: strcpy (buf, "You won't believe it.\n"); break;
        case 2: strcpy (buf, "Haha, chicks will laugh about it?\n"); break;
        case 3: strcpy (buf, "1..2..3 the police is coming.\n"); break;
        }
      cdk_stream_write (s, buf, strlen (buf));
    }

  rc = cdk_stream_close (s);
  if (rc)
    printf ("cdk_stream_close: `%s'\n", cdk_strerror (rc));
  cdk_free (dek);
  return rc;
}


static char *
read_ascii_data (const char *file, int *r_nbytes)
{
  struct stat statbuf;
  FILE *fp;
  char *p;

  fp = fopen (file, "r");
  if (!fp)
    return NULL;
  if (fstat (fileno (fp), &statbuf))
    {
      fclose (fp);
      return NULL;
    }
  *r_nbytes = statbuf.st_size;
  p = cdk_calloc (1, *r_nbytes + 1);
  if (!p)
    {
      fclose (fp);
      return NULL;
    }
  *r_nbytes = fread (p, 1, *r_nbytes, fp);
  p[*r_nbytes] = '\0';
  fclose (fp);

  return p;
}

  

int
stream_test_filters_tmp_ascii (void)
{
  CDK_STREAM s;
  char *pk;
  size_t nbytes;
  int c, rc;

  pk = read_ascii_data ("ts.asc", &nbytes);
  if (!pk)
    {
      printf ("could not read armored key: %s\n", strerror (errno));
      return -1;
    }  
  
  s = cdk_stream_tmp_from_mem (pk, nbytes);
  if (!s)
    {
      printf ("stream_tmp: `%s'\n", cdk_strerror (CDK_Out_Of_Core));
      return -1;
    }

  #if 0
  c = cdk_stream_get_length (s);
  printf ("length of stream `%d'\n", c);
  #endif

  cdk_stream_set_armor_flag (s, 0);
  
  while ((c = cdk_stream_getc (s)) != -1)
    printf ("%c", c);

  rc = cdk_stream_close (s);
  if (rc)
    printf ("stream_close: `%s'\n", cdk_strerror (rc));
  cdk_free (pk);
  return 0;
}


int
stream_test_filters_tmp_both (void)
{
  CDK_STREAM s;
  char *p;
  size_t nbytes;
  int rc, c;

  p = read_ascii_data ("dat.lit", &nbytes);
  if (!p)
    {
      printf ("could not read armored data: `%s'\n", strerror (errno));
      return -1;
    }

  s = cdk_stream_tmp_from_mem (p, nbytes);
  if (!s)
    {
      printf ("stream_tmp: `%s'\n", cdk_strerror (CDK_Out_Of_Core));
      return -1;
    }

  cdk_stream_set_literal_flag (s, 0, NULL);
  cdk_stream_set_armor_flag (s, 0);

  while ((c = cdk_stream_getc (s)) != -1)
    printf ("%c", c);

  rc = cdk_stream_close (s);
  if (rc)
    printf ("stream_close: `%s'\n", cdk_strerror (rc));
  return 0;
}


int
stream_encrypt (char *file)
{
  int rc;
  CDK_HD hd;

  cdk_handle_new (&hd);
  rc = cdk_file_encrypt (hd, NULL, file, "out.gpg");
  if (rc)
    printf ("encrypt file %s: `%s'\n", file, cdk_strerror (rc));
  cdk_handle_free (hd);
  return rc;
}


int
stream_encrypt_pk (char *user, char *file)
{
  int rc;
  CDK_HD hd;
  CDK_STRLIST recp = NULL;
  CDK_KEYDB_HD db;
  
  cdk_handle_new (&hd);
  cdk_keydb_new (&db, CDK_DBTYPE_PK_KEYRING, "ts.gpg", 6);
  cdk_strlist_add (&recp, user);

  cdk_handle_set_keydb (hd, db);
  rc = cdk_file_encrypt (hd, recp, file, "out.gpg");
  if (rc)
    printf ("encrypt_file %s: `%s'\n", file, cdk_strerror (rc));
  cdk_handle_free (hd);
  cdk_strlist_free (recp);
  return rc;
}


int
stream_trustdb (char *db)
{
  CDK_STREAM s;
  cdkPKT_user_id *u;
  int rc, valid;

  rc = cdk_stream_open (db, &s);
  if (rc)
    {
      printf ("stream_open: `%s'\n", cdk_strerror (rc));
      return -1;
    }
  u = cdk_calloc (1, sizeof *u + 128);
  strcpy (u->name, "Timo Schulz <ts@winpt.org>");
  u->len = strlen (u->name);
  rc = cdk_trustdb_get_validity (s, u, &valid);
  if (!rc)
      printf ("Validity of `%s': %d\n", u->name, valid);

  cdk_stream_close (s);
  cdk_free (u);
  
  return 0;
}


const char *
get_sig_status (int id)
{
  const char *s = "unknown error";
  switch (id)
    {
    case CDK_SIGSTAT_NONE: s = "no signature"; break;
    case CDK_SIGSTAT_GOOD: s = "good signature"; break;
    case CDK_SIGSTAT_BAD : s = "bad signature"; break;
    case CDK_SIGSTAT_NOKEY: s = "no key for checking the signature"; break;
    }
  return s;
}


const char *
get_sig_time (unsigned long timestamp)
{
  static char buf[128];
  struct tm * tmbuf;
  
  tmbuf = localtime (&timestamp);
  sprintf (buf, "%04d-%02d-%02d",
           tmbuf->tm_year+1900, tmbuf->tm_mon+1, tmbuf->tm_mday);
  return buf;
}


const char *
get_sig_algo (int pubkey_algo)
{
  switch (pubkey_algo)
    {
    case GCRY_PK_RSA_E:
    case GCRY_PK_RSA_S:
    case GCRY_PK_RSA  : return "RSA";
    case GCRY_PK_ELG  :
    case GCRY_PK_ELG_E: return "ELG";
    case GCRY_PK_DSA  : return "DSA";
    default           : return "???";
    }
  return NULL;
}


int
stream_decrypt (char *file, int with_pass)
{
  int rc, sigstat, created, pkalgo;
  const unsigned long * keyid;
  CDK_HD hd;
  CDK_KEYDB_HD db, p_db;

  if (!with_pass)
    cdk_keydb_new (&db, CDK_DBTYPE_SK_KEYRING, "sec.gpg", 7);
  else
    cdk_keydb_new (&db, CDK_DBTYPE_SK_KEYRING, "sec-with-pwd.gpg", 16);
  cdk_keydb_new(&p_db, CDK_DBTYPE_PK_KEYRING, "pub.gpg", 7);
  cdk_handle_new (&hd);
  cdk_handle_set_keydb (hd, db);
  cdk_handle_set_keydb (hd, p_db);
  rc = cdk_file_decrypt (hd, file, "out.gpg");
  if (rc)
    printf ("decrypt file %s: `%s'\n", file, cdk_strerror (rc));
  sigstat = cdk_sig_get_ulong_attr (hd, 0, CDK_ATTR_STATUS);
  if (sigstat)
    {
      keyid = cdk_sig_get_data_attr (hd, 0, CDK_ATTR_KEYID);
      created = cdk_sig_get_ulong_attr (hd, 0, CDK_ATTR_CREATED);
      pkalgo = cdk_sig_get_ulong_attr (hd, 0, CDK_ATTR_ALGO_PK);
      printf ("(%s) keyid %08lX%08lX algo %s created %s\n",
              get_sig_status (sigstat), keyid[0], keyid[1],
              get_sig_algo (pkalgo), get_sig_time (created));
    }
  cdk_handle_free (hd);
  return rc;
}


int
stream_hash (char * file, char * algo)
{
  md_filter_s * mfx;
  CDK_STREAM s;
  int rc;

  rc = cdk_stream_open (file, &s);
  if (rc)
    {
      printf ("stream_open %s: `%s'\n", file, cdk_strerror (rc));
      return -1;
    }
  rc = cdk_stream_set_hash_flag (s, atol (algo));
  cdk_stream_read (s, NULL, 0);
  mfx = _cdk_stream_get_opaque (s, fHASH);
  printf ("hash %s...\n", file);
  if (mfx && mfx->md)
    {
      unsigned char *md = gcry_md_read (mfx->md, 0);
      size_t dlen = gcry_md_get_algo_dlen (mfx->digest_algo), i;
      for (i = 0; i < dlen; i++)
        printf ("%02X ", md[i]);
      printf ("\n");
      gcry_md_close (mfx->md);
    }
  cdk_stream_close (s);
  return 0;
}

int
stream_test_armor (char *file)
{
  CDK_STREAM s;
  int rc;
    
  rc = cdk_stream_open (file, &s);
  if (rc)
    {
      printf ("stream_open %s: `%s'\n", file, cdk_strerror (rc));
      return -1;
    }
  printf ("use_armor_filter=%d\n", cdk_armor_filter_use (s));
  cdk_stream_close (s);
  return 0;
}


int
stream_test_keyblock (char *file)
{
  CDK_STREAM s;
  CDK_KBNODE knode;
  int rc;

  rc = cdk_stream_open (file, &s);
  if (rc)
    {
      printf ("stream_open %s: `%s'\n", file, cdk_strerror (rc));
      return -1;
    }
  if (cdk_armor_filter_use (s))
    cdk_stream_set_armor_flag (s, 0);;
  rc = cdk_keydb_get_keyblock (s, &knode);
  printf ("rc=%d knode=%p\n", rc, knode);

  cdk_stream_close (s);
  cdk_kbnode_release (knode);
  return 0;
}


static int
stream_verify (char *file)
{
  CDK_HD hd;
  CDK_KEYDB_HD db;
  int rc;
  
  cdk_handle_new (&hd);
  cdk_keydb_new (&db, CDK_DBTYPE_PK_KEYRING, "pub.gpg", 7);
  cdk_handle_set_keydb (hd, db);
  rc = cdk_file_verify (hd, file, "clear.txt");
  if (rc)
    printf ("verify_file %s: `%s'\n", file, cdk_strerror (rc));
  cdk_handle_free (hd);
  return 0;
}


static int
stream_clearsign (char *file)
{
  CDK_HD hd;
  CDK_STRLIST locusr = NULL;
  CDK_KEYDB_HD db;
  int rc;
  
  cdk_handle_new (&hd);
  cdk_keydb_new (&db, CDK_DBTYPE_SK_KEYRING, "sec.gpg", 7);
  cdk_strlist_add (&locusr, "twoaday@daredevil");
      //rc = cdk_file_clearsign (hd, locusr, file, "out.asc");
  if (rc)
    printf ("clearsign_file %s: `%s'\n", file, cdk_strerror (rc));
  cdk_handle_free (hd);
  cdk_strlist_free (locusr);
  return 0;
}


int
stream_test_textmode (void)
{
  CDK_STREAM s;
  int rc;

  rc = cdk_stream_new ("literal.txm", &s);
  if (rc)
    {
      printf ("stream_new: `%s'\n", cdk_strerror (rc));
      return -1;
    }
  cdk_stream_set_text_flag (s, NULL);
  cdk_stream_write (s, "hallo\n", 6);
  rc = cdk_stream_close (s);
  if (rc)
    printf ("stream_close `%s'\n", cdk_strerror (rc));
  return 0;
}


int
test_stream_kbnode (const char * file)
{
  CDK_KBNODE node = NULL, n, ctx = NULL;
  CDK_STREAM inp;
  unsigned char buf[8192];
  int rc, nread;

  rc = cdk_stream_open (file, &inp);
  if (rc)
    {
      fprintf (stderr, "stream open `%s': %s\n", file, cdk_strerror (rc));
      return -1;
    }

  nread = cdk_stream_read (inp, buf, sizeof buf-1);
  cdk_stream_close (inp);

  rc = cdk_kbnode_read_from_mem (&node, buf, nread);
  if (rc)
    fprintf (stderr, "read_from_mem: %s\n", cdk_strerror (rc));

  while ((n = cdk_kbnode_walk (node, &ctx, 0)))
    {
      CDK_PACKET * pkt = cdk_kbnode_get_packet (n);
      fprintf (stderr, "pkttype=%d\n", pkt->pkttype);
    }

  cdk_kbnode_write_to_mem (node, buf, &nread);
  cdk_kbnode_release (node);

  /*fwrite (buf, 1, nread, stdout);*/
  return 0;
}


#if TEST
int classify_data (const byte * buf, size_t len);
     
void
test_classify_function (void)
{
  int i;
  const char * s;
  const char * kid [] =
  {
    "0xBF3DF9B4",
    "BF3DF9B4",
    "bf3df9b4",
    "ED4681C9BF3DF9B4",
    "0xED4681C9BF3DF9B4",
    "timo",
    "ts@winpt.org",
    NULL
  };
  for (i = 0, s = kid[i]; kid[i]; i++)
    printf ("classify_data `%s' = %d\n", kid[i],
            classify_data (kid[i], strlen (kid[i])));
  
} 
#endif


int
main (int argc, char **argv)
{
  cdk_set_log_level (CDK_LOG_DEBUG);
  return test_stream_kbnode (argc>1? argv[1] : "foo");
  /*stream_test_filters (1, 1, 1, 1);*/
  /*stream_test_filters_tmp_ascii ();*/
  /*stream_test_filters_tmp_both ();*/
  /*stream_trustdb ("trustdb.gpg");*/
  #if 0
  if (argc > 1)
    stream_clearsign (argv[1]);
  #endif
  if (argc > 1)
    stream_verify (argv[1]);
  #if 0
  if (argc > 1)
    stream_decrypt (argv[1], argc==3);
  if (argc > 1)
    stream_encrypt (argv[1]);
  if (argc == 2)
    stream_encrypt_pk ("ts@winpt.org", argv[1]);
  if (argc == 3)
    stream_hash (argv[1], argv[2]);
#endif
  
  return 0;
}



