/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the Netscape security libraries.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1994-2000
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

/*
 *  JAREVIL
 *
 *  Wrappers to callback in the mozilla thread
 *
 *  Certificate code is unsafe when called outside the
 *  mozilla thread. These functions push an event on the
 *  queue to cause the cert function to run in that thread. 
 *
 */

#include "nssrenam.h"
#include "jar.h"
#include "jarint.h"

#include "jarevil.h"
#include "certdb.h"

/* from libevent.h */
#ifdef MOZILLA_CLIENT_OLD
typedef void (*ETVoidPtrFunc) (void * data);
extern void ET_moz_CallFunction (ETVoidPtrFunc fn, void *data);

extern void *mozilla_event_queue;
#endif


/* Special macros facilitate running on Win 16 */
#if defined(XP_WIN16)

  /* 
   * Allocate the data passed to the callback functions from the heap...
   *
   * This inter-thread structure cannot reside on a thread stack since the 
   * thread's stack is swapped away with the thread under Win16...
   */

 #define ALLOC_OR_DEFINE(type, pointer_var_name, out_of_memory_return_value) \
         type * pointer_var_name = PORT_ZAlloc (sizeof(type));               \
         do {                                                                \
           if (!pointer_var_name)                                            \
             return (out_of_memory_return_value);                            \
         } while (0)   /* and now a semicolon can follow :-) */

 #define FREE_IF_ALLOC_IS_USED(pointer_var_name) PORT_Free(pointer_var_name)

#else /* not win 16... so we can alloc via auto variables */

 #define ALLOC_OR_DEFINE(type, pointer_var_name, out_of_memory_return_value) \
         type actual_structure_allocated_in_macro;                           \
         type * pointer_var_name = &actual_structure_allocated_in_macro;     \
         PORT_Memset (pointer_var_name, 0, sizeof (*pointer_var_name));      \
         ((void) 0) /* and now a semicolon can follow  */

 #define FREE_IF_ALLOC_IS_USED(pointer_var_name) ((void) 0)

#endif /* not Win 16 */

/* --- --- --- --- --- --- --- --- --- --- --- --- --- */

/*
 *  JAR_MOZ_encode
 *
 *  Call SEC_PKCS7Encode inside
 *  the mozilla thread
 *
 */

struct EVIL_encode
  {
  int error;
  SECStatus status;
  SEC_PKCS7ContentInfo *cinfo;
  SEC_PKCS7EncoderOutputCallback outputfn;
  void *outputarg;
  PK11SymKey *bulkkey;
  SECKEYGetPasswordKey pwfn;
  void *pwfnarg;
  };


/* This is called inside the mozilla thread */

PR_STATIC_CALLBACK(void) jar_moz_encode_fn (void *data)
  {
  SECStatus status;
  struct EVIL_encode *encode_data = (struct EVIL_encode *)data;

  PORT_SetError (encode_data->error);

  status = SEC_PKCS7Encode (encode_data->cinfo, encode_data->outputfn, 
                            encode_data->outputarg, encode_data->bulkkey, 
                            encode_data->pwfn, encode_data->pwfnarg);

  encode_data->status = status;
  encode_data->error = PORT_GetError();
  }


/* Wrapper for the ET_MOZ call */
 
SECStatus jar_moz_encode
      (
      SEC_PKCS7ContentInfo *cinfo,
      SEC_PKCS7EncoderOutputCallback  outputfn,
      void *outputarg,
      PK11SymKey *bulkkey,
      SECKEYGetPasswordKey pwfn,
      void *pwfnarg
      )
  {
  SECStatus ret;
  ALLOC_OR_DEFINE(struct EVIL_encode, encode_data, SECFailure);

  encode_data->error     = PORT_GetError();
  encode_data->cinfo     = cinfo;
  encode_data->outputfn  = outputfn;
  encode_data->outputarg = outputarg;
  encode_data->bulkkey   = bulkkey;
  encode_data->pwfn      = pwfn;
  encode_data->pwfnarg   = pwfnarg;

  /* Synchronously invoke the callback function on the mozilla thread. */
#ifdef MOZILLA_CLIENT_OLD
  if (mozilla_event_queue)
    ET_moz_CallFunction (jar_moz_encode_fn, encode_data);
  else
    jar_moz_encode_fn (encode_data);
#else
  jar_moz_encode_fn (encode_data);
#endif

  PORT_SetError (encode_data->error);
  ret = encode_data->status;

  /* Free the data passed to the callback function... */
  FREE_IF_ALLOC_IS_USED(encode_data);
  return ret;
  }

/* --- --- --- --- --- --- --- --- --- --- --- --- --- */

/*
 *  JAR_MOZ_verify
 *
 *  Call SEC_PKCS7VerifyDetachedSignature inside
 *  the mozilla thread
 *
 */

struct EVIL_verify
  {
  int error;
  SECStatus status;
  SEC_PKCS7ContentInfo *cinfo;
  SECCertUsage certusage;
  SECItem *detached_digest;
  HASH_HashType digest_type;
  PRBool keepcerts;
  };

/* This is called inside the mozilla thread */

PR_STATIC_CALLBACK(void) jar_moz_verify_fn (void *data)
  {
	PRBool result;
  struct EVIL_verify *verify_data = (struct EVIL_verify *)data;

  PORT_SetError (verify_data->error);

  result = SEC_PKCS7VerifyDetachedSignature
        (verify_data->cinfo, verify_data->certusage, verify_data->detached_digest, 
         verify_data->digest_type, verify_data->keepcerts);
	

  verify_data->status = result==PR_TRUE ? SECSuccess : SECFailure;
  verify_data->error = PORT_GetError();
  }


/* Wrapper for the ET_MOZ call */
 
SECStatus jar_moz_verify
      (
      SEC_PKCS7ContentInfo *cinfo,
      SECCertUsage certusage,
      SECItem *detached_digest,
      HASH_HashType digest_type,
      PRBool keepcerts
      )
  {
  SECStatus ret;
  ALLOC_OR_DEFINE(struct EVIL_verify, verify_data, SECFailure);

  verify_data->error           = PORT_GetError();
  verify_data->cinfo           = cinfo;
  verify_data->certusage       = certusage;
  verify_data->detached_digest = detached_digest;
  verify_data->digest_type     = digest_type;
  verify_data->keepcerts       = keepcerts;

  /* Synchronously invoke the callback function on the mozilla thread. */
#ifdef MOZILLA_CLIENT_OLD
  if (mozilla_event_queue)
    ET_moz_CallFunction (jar_moz_verify_fn, verify_data);
  else
    jar_moz_verify_fn (verify_data);
#else
  jar_moz_verify_fn (verify_data);
#endif

  PORT_SetError (verify_data->error);
  ret = verify_data->status;

  /* Free the data passed to the callback function... */
  FREE_IF_ALLOC_IS_USED(verify_data);
  return ret;
  }

/* --- --- --- --- --- --- --- --- --- --- --- --- --- */

/*
 *  JAR_MOZ_nickname
 *
 *  Call CERT_FindCertByNickname inside
 *  the mozilla thread
 *
 */

struct EVIL_nickname
  {
  int error;
  CERTCertDBHandle *certdb;
  char *nickname;
  CERTCertificate *cert;
  };


/* This is called inside the mozilla thread */

PR_STATIC_CALLBACK(void) jar_moz_nickname_fn (void *data)
  {
  CERTCertificate *cert;
  struct EVIL_nickname *nickname_data = (struct EVIL_nickname *)data;

  PORT_SetError (nickname_data->error);

  cert = CERT_FindCertByNickname (nickname_data->certdb, nickname_data->nickname);

  nickname_data->cert  = cert;
  nickname_data->error = PORT_GetError();
  }


/* Wrapper for the ET_MOZ call */
 
CERTCertificate *jar_moz_nickname (CERTCertDBHandle *certdb, char *nickname)
  {
  CERTCertificate *cert;
  ALLOC_OR_DEFINE(struct EVIL_nickname, nickname_data, NULL );

  nickname_data->error    = PORT_GetError();
  nickname_data->certdb   = certdb;
  nickname_data->nickname = nickname;

  /* Synchronously invoke the callback function on the mozilla thread. */
#ifdef MOZILLA_CLIENT_OLD
  if (mozilla_event_queue)
    ET_moz_CallFunction (jar_moz_nickname_fn, nickname_data);
  else
    jar_moz_nickname_fn (nickname_data);
#else
  jar_moz_nickname_fn (nickname_data);
#endif

  PORT_SetError (nickname_data->error);
  cert = nickname_data->cert;

  /* Free the data passed to the callback function... */
  FREE_IF_ALLOC_IS_USED(nickname_data);
  return cert;
  }

/* --- --- --- --- --- --- --- --- --- --- --- --- --- */

/*
 *  JAR_MOZ_perm
 *
 *  Call CERT_AddTempCertToPerm inside
 *  the mozilla thread
 *
 */

struct EVIL_perm
  {
  int error;
  SECStatus status;
  CERTCertificate *cert;
  char *nickname;
  CERTCertTrust *trust;
  };


/* This is called inside the mozilla thread */

PR_STATIC_CALLBACK(void) jar_moz_perm_fn (void *data)
  {
  SECStatus status;
  struct EVIL_perm *perm_data = (struct EVIL_perm *)data;

  PORT_SetError (perm_data->error);

  status = CERT_AddTempCertToPerm (perm_data->cert, perm_data->nickname, perm_data->trust);

  perm_data->status = status;
  perm_data->error = PORT_GetError();
  }


/* Wrapper for the ET_MOZ call */
 
SECStatus jar_moz_perm 
    (CERTCertificate *cert, char *nickname, CERTCertTrust *trust)
  {
  SECStatus ret;
  ALLOC_OR_DEFINE(struct EVIL_perm, perm_data, SECFailure);

  perm_data->error    = PORT_GetError();
  perm_data->cert     = cert;
  perm_data->nickname = nickname;
  perm_data->trust    = trust;

  /* Synchronously invoke the callback function on the mozilla thread. */
#ifdef MOZILLA_CLIENT_OLD
  if (mozilla_event_queue)
    ET_moz_CallFunction (jar_moz_perm_fn, perm_data);
  else
    jar_moz_perm_fn (perm_data);
#else
  jar_moz_perm_fn (perm_data);
#endif

  PORT_SetError (perm_data->error);
  ret = perm_data->status;

  /* Free the data passed to the callback function... */
  FREE_IF_ALLOC_IS_USED(perm_data);
  return ret;
  }

/* --- --- --- --- --- --- --- --- --- --- --- --- --- */

/*
 *  JAR_MOZ_certkey
 *
 *  Call CERT_FindCertByKey inside
 *  the mozilla thread
 *
 */

struct EVIL_certkey
  {
  int error;
  CERTCertificate *cert;
  CERTCertDBHandle *certdb;
  CERTIssuerAndSN *seckey;
  };


/* This is called inside the mozilla thread */

PR_STATIC_CALLBACK(void) jar_moz_certkey_fn (void *data)
  {
  CERTCertificate *cert;
  struct EVIL_certkey *certkey_data = (struct EVIL_certkey *)data;

  PORT_SetError (certkey_data->error);

  cert=CERT_FindCertByIssuerAndSN(certkey_data->certdb, certkey_data->seckey);

  certkey_data->cert = cert;
  certkey_data->error = PORT_GetError();
  }


/* Wrapper for the ET_MOZ call */
 
CERTCertificate *jar_moz_certkey (CERTCertDBHandle *certdb, 
						CERTIssuerAndSN *seckey)
  {
  CERTCertificate *cert;
  ALLOC_OR_DEFINE(struct EVIL_certkey, certkey_data, NULL);

  certkey_data->error  = PORT_GetError();
  certkey_data->certdb = certdb;
  certkey_data->seckey = seckey;

  /* Synchronously invoke the callback function on the mozilla thread. */
#ifdef MOZILLA_CLIENT_OLD
  if (mozilla_event_queue)
    ET_moz_CallFunction (jar_moz_certkey_fn, certkey_data);
  else
    jar_moz_certkey_fn (certkey_data);
#else
  jar_moz_certkey_fn (certkey_data);
#endif

  PORT_SetError (certkey_data->error);
  cert = certkey_data->cert;

  /* Free the data passed to the callback function... */
  FREE_IF_ALLOC_IS_USED(certkey_data);
  return cert;
  }

/* --- --- --- --- --- --- --- --- --- --- --- --- --- */

/*
 *  JAR_MOZ_issuer
 *
 *  Call CERT_FindCertIssuer inside
 *  the mozilla thread
 *
 */

struct EVIL_issuer
  {
  int error;
  CERTCertificate *cert;
  CERTCertificate *issuer;
  };


/* This is called inside the mozilla thread */

PR_STATIC_CALLBACK(void) jar_moz_issuer_fn (void *data)
  {
  CERTCertificate *issuer;
  struct EVIL_issuer *issuer_data = (struct EVIL_issuer *)data;

  PORT_SetError (issuer_data->error);

  issuer = CERT_FindCertIssuer (issuer_data->cert, PR_Now(),
				certUsageObjectSigner);

  issuer_data->issuer = issuer;
  issuer_data->error = PORT_GetError();
  }


/* Wrapper for the ET_MOZ call */
 
CERTCertificate *jar_moz_issuer (CERTCertificate *cert)
  {
  CERTCertificate *issuer_cert;
  ALLOC_OR_DEFINE(struct EVIL_issuer, issuer_data, NULL);

  issuer_data->error = PORT_GetError();
  issuer_data->cert  = cert;

  /* Synchronously invoke the callback function on the mozilla thread. */
#ifdef MOZILLA_CLIENT_OLD
  if (mozilla_event_queue)
    ET_moz_CallFunction (jar_moz_issuer_fn, issuer_data);
  else
    jar_moz_issuer_fn (issuer_data);
#else
  jar_moz_issuer_fn (issuer_data);
#endif

  PORT_SetError (issuer_data->error);
  issuer_cert = issuer_data->issuer;

  /* Free the data passed to the callback function... */
  FREE_IF_ALLOC_IS_USED(issuer_data);
  return issuer_cert;
  }

/* --- --- --- --- --- --- --- --- --- --- --- --- --- */

/*
 *  JAR_MOZ_dup
 *
 *  Call CERT_DupCertificate inside
 *  the mozilla thread
 *
 */

struct EVIL_dup
  {
  int error;
  CERTCertificate *cert;
  CERTCertificate *return_cert;
  };


/* This is called inside the mozilla thread */

PR_STATIC_CALLBACK(void) jar_moz_dup_fn (void *data)
  {
  CERTCertificate *return_cert;
  struct EVIL_dup *dup_data = (struct EVIL_dup *)data;

  PORT_SetError (dup_data->error);

  return_cert = CERT_DupCertificate (dup_data->cert);

  dup_data->return_cert = return_cert;
  dup_data->error = PORT_GetError();
  }


/* Wrapper for the ET_MOZ call */
 
CERTCertificate *jar_moz_dup (CERTCertificate *cert)
  {
  CERTCertificate *dup_cert;
  ALLOC_OR_DEFINE(struct EVIL_dup, dup_data, NULL);

  dup_data->error = PORT_GetError();
  dup_data->cert  = cert;

  /* Synchronously invoke the callback function on the mozilla thread. */
#ifdef MOZILLA_CLIENT_OLD
  if (mozilla_event_queue)
    ET_moz_CallFunction (jar_moz_dup_fn, dup_data);
  else
    jar_moz_dup_fn (dup_data);
#else
  jar_moz_dup_fn (dup_data);
#endif

  PORT_SetError (dup_data->error);
  dup_cert = dup_data->return_cert;

  /* Free the data passed to the callback function... */
  FREE_IF_ALLOC_IS_USED(dup_data);
  return dup_cert;
  }

/* --- --- --- --- --- --- --- --- --- --- --- --- --- */
