#ifndef __SOP_H__
#define __SOP_H__

#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <limits.h>

/* C API for Stateless OpenPGP */

/* Depends on C99 */


/* statically-defined, non-opaque definitions */

typedef enum {
  SOP_OK = 0,
  SOP_INTERNAL_ERROR = 1, /* Not part of sop CLI */
  SOP_INVALID_ARG = 2, /* Not part of sop CLI */
  SOP_NO_SIGNATURE = 3,
  SOP_OPERATION_ALREADY_EXECUTED = 4, /* Not part of sop CLI */
  SOP_UNSUPPORTED_ASYMMETRIC_ALGO = 13,
  SOP_CERT_CANNOT_ENCRYPT = 17,
  SOP_MISSING_ARG = 19,
  SOP_INCOMPLETE_VERIFICATION = 23,
  SOP_CANNOT_DECRYPT = 29,
  SOP_PASSWORD_NOT_HUMAN_READABLE = 31,
  SOP_UNSUPPORTED_OPTION = 37,
  SOP_BAD_DATA = 41,
  SOP_EXPECTED_TEXT = 53,
  SOP_OUTPUT_EXISTS = 59,
  SOP_MISSING_INPUT = 61,
  SOP_KEY_IS_PROTECTED = 67,
  SOP_UNSUPPORTED_SUBCOMMAND = 69,
  SOP_UNSUPPORTED_SPECIAL_PREFIX = 71,
  SOP_AMBIGUOUS_INPUT = 73,
  SOP_KEY_CANNOT_SIGN = 79,
  SOP_INCOMPATIBLE_OPTIONS = 83,
  SOP_UNSUPPORTED_PROFILE = 89,
  SOP_NO_HARDWARE_KEY_FOUND = 97,
  SOP_HARDWARE_KEY_FAILURE = 101,
  SOP_PRIMARY_KEY_BAD = 103,
  SOP_CERT_USERID_NO_MATCH = 107,
  SOP_KEY_CANNOT_CERTIFY = 109,

  /* ensures a stable size for the enum -- do not use! */
  SOP_MAX_ERR = INT_MAX,
} sop_err;
  

typedef enum {
  SOP_SIGN_AS_BINARY = 0,
  SOP_SIGN_AS_TEXT = 1,

  /* ensures a stable size for the enum -- do not use! */
  SOP_SIGN_AS_MAX = INT_MAX,
} sop_sign_as;

typedef enum {
  SOP_INLINE_SIGN_AS_BINARY = 0,
  SOP_INLINE_SIGN_AS_TEXT = 1,
  SOP_INLINE_SIGN_AS_CLEARSIGNED = 2,

  /* ensures a stable size for the enum -- do not use! */
  SOP_INLINE_SIGN_AS_MAX = INT_MAX,
} sop_inline_sign_as;

typedef enum {
  SOP_ENCRYPT_AS_BINARY = 0,
  SOP_ENCRYPT_AS_TEXT = 1,

  /* ensures a stable size for the enum -- do not use! */
  SOP_ENCRYPT_AS_MAX = INT_MAX,
} sop_encrypt_as;


/* FIXME: timestamps */
/* time_t is 32-bit on some architectures; we also want this to be
   able to represent a "none" value as well as a "now" value without
   removing some value from the range of time_t */
typedef time_t sop_time;
#define sop_time_none ((sop_time)0)
#define sop_time_now ((sop_time)-1)


/* Context object
 *
 * Each SOP object is bound back to a context object, and, when used
 * in combination with other SOP objects, all SOP objects should come
 * from the same context.
 *
 * A SOP context object need not be thread-safe; it should probably
 * not be used across multiple threads.  See "Zero global state" in
 * the README file in
 * https://git.kernel.org/pub/scm/linux/kernel/git/kay/libabc.git
 */

struct sop_ctx_st;
typedef struct sop_ctx_st sop_ctx;

sop_ctx*
sop_ctx_new ();
void
sop_ctx_free (sop_ctx *sop);

/* Logging: */

typedef enum {
  SOP_LOG_NEVER = 0,
  SOP_LOG_ERROR = 1,
  SOP_LOG_WARNING = 2,
  SOP_LOG_INFO = 3,
  SOP_LOG_DEBUG = 4,

  /* ensures a stable size for the enum -- do not use! */
  SOP_LOG_MAX = INT_MAX,
} sop_log_level;

static inline const char *
sop_log_level_name (sop_log_level log_level) {
#define rep(x) if (log_level == SOP_LOG_ ## x) return #x
  rep(ERROR);
  rep(WARNING);
  rep(INFO);
  rep(DEBUG);
#undef rep
  return "Unknown";
}

/* Handle warnings and other feedback.
 * 
 * A SOP implementation that is capable of producing log messages
 * will invoke the requested function with the log level of the
 * message, and a NULL-terminated UTF-8 human-readable string with
 * no trailing whitespace.
 *
 * the "passthrough" pointer is supplied by the library user via
 * sop_set_log_level.
 */
typedef void (*sop_log_func) (sop_log_level log_level,
                              void *passthrough, const char *);
sop_err
sop_set_log_function (sop_ctx *sop, sop_log_func func,
                      void *passthrough);
/* Set the logging verbosity.
 *
 * Only log warnings up to max_level. (by default, max_level is
 * SOP_LOG_WARNING, meaning SOP_LOG_INFO and SOP_LOG_DEBUG will be
 * suppressed).
 */
sop_err
sop_set_log_level (sop_ctx *sop, sop_log_level max_level);



/* Information about the library: */

/* The name and version of the implementation of the C API (simple
 * NUL-terminated string, no newlines), or NULL if there is an error
 * producing the version. */
const char *
sop_version (sop_ctx *sop);
/* The name and version of the primary underlying OpenPGP toolkit
 * (or NULL if there is no backend, or if there was an error
 * producing the backend version) */
const char *
sop_version_backend (sop_ctx *sop);
/* Any arbitrary extended version information other than
   sop_ctx_version.  Version info should be UTF-8 text, separated by
   newlines (a NUL-terminated string, no trailing newline).  Can
   return NULL if there is nothing more to report beyond
   sop_version. */
const char *
sop_version_extended (sop_ctx *sop);

/* note: there is nothing comparable to sop version --sop-spec
 * because that should be visible based on the exported symbols in
 * the shared object */



/* PROFILE objects: */

/* These describe a profile (e.g. for generate-key or encrypt).
 * This use used when the implementation might legitimately want to
 * offer the user some minimal amount of control over what is done.
 * The profile-listing functions return blocks of four profiles.  A
 * sop_profile value of NULL represents no profile at all.  In a
 * list of sop_profile objects, once a NULL profile appears, no
 * non-NULL profiles may follow.
 
 */
struct sop_profile_st;
typedef struct sop_profile_st sop_profile;
/* the NUL-terminated string returned by sop_profile_name MUST be a
   UTF-8 encoded string, and MUST NOT include any whitespace or
   colon (`:`) characters.  It MUST NOT vary depending on locale. */
const char *
sop_profile_name (const sop_profile *profile);
/* The NUL-terminated string returned by sop_profile_description
   cannot contain any newlines, and it MAY vary depending on
   locale(7) if the implementation is internationalized. */
const char *
sop_profile_description (const sop_profile *profile);


#define SOP_MAX_PROFILE_COUNT 4

typedef struct {
  sop_profile *profile[SOP_MAX_PROFILE_COUNT];
} sop_profiles;

static inline int
sop_profiles_count(const sop_profiles profiles) {
  for (int i = 0; i < SOP_MAX_PROFILE_COUNT; i++)
    if (profiles.profile[i] == NULL)
      return i;
  return SOP_MAX_PROFILE_COUNT;
}

/* Return a list of profiles supported by the library for generating
 * keys.
 */
sop_err
sop_list_profiles_generate_key (sop_ctx *sop, sop_profiles *out);


/* CLEARTEXT (and other raw data): */

/* This is a standard buffer for bytestrings produced by sop.  Users
   never create this kind of object, but it is sometimes returned
   from the library. */
struct sop_buf_st;
typedef struct sop_buf_st sop_buf;

void
sop_buf_free (sop_buf *buf);
size_t
sop_buf_size (const sop_buf *buf);
const uint8_t *
sop_buf_data (const sop_buf *buf);


/* KEYS objects: */
struct sop_keys_st;
typedef struct sop_keys_st sop_keys;

sop_err
sop_keys_from_bytes (sop_ctx *sop,
                     const uint8_t* data, size_t len,
                     sop_keys **out);
sop_err
sop_keys_to_bytes (const sop_keys *keys,
                   bool armor, sop_buf **out);
void
sop_keys_free (sop_keys *keys);



/* Generate a new, minimal OpenPGP Transferable secret key.
   `profile` can be NULL to mean the default profile. */
sop_err
sop_generate_key_with_profile (sop_ctx *sop,
                               sop_profile *profile,
                               bool sign_only,
                               sop_keys **out);

static inline sop_err
sop_generate_key (sop_ctx *sop, sop_keys **out) {
  return sop_generate_key_with_profile (sop, NULL, false, out);
}

/* For each key in the sop_keys object, add the given user ID, and
   return a new sop_keys object containing the updated keys.  If the
   supplied user ID is not valid UTF-8 text, this call will fail and
   return SOP_EXPECTED_TEXT.

   If the implementation rejects the user ID string by policy for
   any other reason, this call will fail and return SOP_BAD_DATA.
 */
sop_err
sop_keys_add_uid (const sop_keys *keys, const char *uid,
                  sop_keys **out);

/* returns true if any of the secret key material is currently
   locked with a password */
sop_err
sop_keys_locked (const sop_keys *keys, bool *out);

/* return a new sop_keys object with any secret key material
   encrypted with `password` unlocked, Returns SOP_OK if all keys
   have now been unlocked.

   If any locked key material could not be unlocked, return
   SOP_KEY_IS_PROTECTED, while also unlocking what key material can
   be unlocked.

   This allows the user to try an arbitrary bytestream as a
   password.  Most users will just invoke the inlined
   sop_keys_unlock, below.

   An implementation MUST NOT reject proposed passwords by policy
   during unlock, but rather should try them as requested.
*/
sop_err
sop_keys_unlock_raw (const sop_keys *keys,
                     const uint8_t *raw_password, size_t len,
                     sop_keys **out);


static inline sop_err
sop_keys_unlock (const sop_keys *keys, const char *password,
                 sop_keys **out) {
  return sop_keys_unlock_raw (keys,
                              (const uint8_t *)password,
                              strlen (password),
                              out);
}


/* return a new sop_keys object where all secret key material is
   locked with `password` where possible.

   During locking, a safety-oriented implementation MAY reject the
   supplied password by policy for any number of reasons.  This
   helps libsop ensure that the proposed password can be
   successfully re-supplied during some future unlock attempt.

   If the implementation requires passwords to be UTF-8 text and the
   supplied password is not valid UTF-8, the implementation will
   fail, returning SOP_EXPECTED_TEXT.  If an implementation rejects
   a supplied password for some other reason (for example, if it
   contains an NUL, unprintable, or otherwise forbidden character),
   this call will fail and return SOP_BAD_DATA.

   If any key material is already locked, it does nothing and
   returns SOP_KEY_IS_PROTECTED.

   Upon a successful locking, the user probably wants to use
   sop_keys_free to free the original keys object.
*/
sop_err
sop_keys_lock_raw (const sop_keys *keys,
                   const uint8_t *password, size_t len,
                   sop_keys **out);

static inline sop_err
sop_keys_lock (const sop_keys *keys, const char *password,
               sop_keys **out) {
  return sop_keys_lock_raw (keys,
                            (const uint8_t *)password,
                            strlen (password),
                            out);
}





/* CERTS objects: */
struct sop_certs_st;
typedef struct sop_certs_st sop_certs;

sop_err
sop_certs_from_bytes (sop_ctx *sop,
                      const uint8_t* data, size_t len,
                      sop_certs **out);
sop_err
sop_certs_to_bytes (const sop_certs *certs,
                    bool armor, sop_buf **out);
void
sop_certs_free (sop_certs *certs);

/* Return the OpenPGP certificates ("Transferable Public Keys") that
   correspond to the OpenPGP Transferable Secret Keys. */
sop_err
sop_keys_extract_certs (const sop_keys *keys, sop_certs **out);


/* Return an OpenPGP revocation certificate for each Transferable
   Secret Key found in the input. */
sop_err
sop_keys_revoke_keys (const sop_keys *keys, sop_certs **out);



/* SIGNATURES objects: */
struct sop_sigs_st;
typedef struct sop_sigs_st sop_sigs;

sop_err
sop_sigs_from_bytes (sop_ctx *sop,
                     const uint8_t* data, size_t len,
                     sop_sigs **out);
sop_err
sop_sigs_to_bytes (const sop_sigs *sigs,
                   bool armor, sop_buf **out);
void
sop_sigs_free (sop_sigs *sigs);



/* VERIFICATIONS (output only, describes valid, verified
   signatures): */
struct sop_verifications_st;
typedef struct sop_verifications_st sop_verifications;

void
sop_verifications_free (sop_verifications *verifs);
sop_err
sop_verifications_count (const sop_verifications *verifs, int *out);
/* textual representations of verifications, in the form described
   by VERIFICATIONS in the CLI */
sop_err
sop_verifications_to_text (const sop_verifications *verifs,
                           sop_buf **out);
/* returns SOP_INTERNAL_ERROR if index is out of bounds. */
sop_err
sop_verifications_get_time (const sop_verifications *verifs,
                            int index, sop_time *out);
/* returns SOP_INTERNAL_ERROR if index is out of bounds.  If the
   signature is neither type 0x00 nor 0x01, this should probably not
   be considered a valid, verified signature. */
sop_err
sop_verifications_get_mode (const sop_verifications *verifs,
                            int index, sop_sign_as *out);

/* returns SOP_INTERNAL_ERROR if index is out of bounds. */
sop_err
sop_verifications_get_signer_count (const sop_verifications *verifs,
                                    int index, int *out);

/* returns SOP_INTERNAL_ERROR if either verif_index or signer_index
   is out of bounds.  Yields a pointer to the sop_certs object that
   could have made the signature.
 */
sop_err
sop_verifications_get_signer (const sop_verifications *verifs,
                              int verif_index, int signer_index,
                              const sop_certs **out);

/* FIXME: (do we want to get more detailed info programmatically?
   each verification should also have an issuing key fingerprint, a
   primary key fingerprint, and a trailing text string) */






/* create detached signatures: */
struct sop_op_sign_st;
typedef struct sop_op_sign_st sop_op_sign;

sop_err
sop_op_sign_new (sop_ctx *sop, sop_op_sign** out);
void
sop_op_sign_free (sop_op_sign *sign);

sop_err
sop_op_sign_use_keys (sop_op_sign *sign, const sop_keys *keys);

sop_err
sop_op_sign_detached_execute (sop_op_sign *sign,
                              sop_sign_as sign_as,
                              const uint8_t *msg,
                              size_t sz,
                              sop_buf **micalg_out,
                              sop_sigs **out);


/* verify detached signatures: */
struct sop_op_verify_st;
typedef struct sop_op_verify_st sop_op_verify;

sop_err
sop_op_verify_new (sop_ctx *sop, sop_op_verify** out);
void
sop_op_verify_free (sop_op_verify *verify);

sop_err
sop_op_verify_not_before (sop_op_verify *verify, sop_time when);
sop_err
sop_op_verify_not_after (sop_op_verify *verify, sop_time when);
sop_err
sop_op_verify_add_signers (sop_op_verify *verify,
                           const sop_certs *signers);

/* if no verifications are possible with the set of signers, this
   returns SOP_NO_SIGNATURE, and *out is set to NULL */
sop_err
sop_op_verify_detached_execute (sop_op_verify *verify,
                                const sop_sigs *sigs,
                                const uint8_t *msg,
                                size_t sz,
                                sop_verifications **out);



/* INLINESIGNED object: */
struct sop_inlinesigned_st;
typedef struct sop_inlinesigned_st sop_inlinesigned;

sop_err
sop_inlinesigned_from_bytes (sop_ctx *sop,
                             const uint8_t* data, size_t len,
                             sop_inlinesigned **out);
/* if the inlinesigned object uses the Cleartext Signing framework,
 * the armor parameter is ignored.
 */
sop_err
sop_inlinesigned_to_bytes (const sop_inlinesigned *inlinesigned,
                           bool armor, sop_buf **out);
void
sop_inlinesigned_free (sop_inlinesigned *inlinesigned);


/* sop inline-sign */
sop_err
sop_op_sign_inline_execute (sop_op_sign *sign,
                            sop_inline_sign_as sign_as,
                            const uint8_t *msg,
                            size_t sz,
                            sop_inlinesigned **out);

/* sop inline-verify */
sop_err
sop_op_verify_inline_execute (sop_op_verify *verify,
                              const sop_inlinesigned *msg,
                              sop_verifications **verifications_out,
                              sop_buf **msg_out);

/* sop inline-detach */
sop_err
sop_inlinesigned_detach (const sop_inlinesigned *msg,
                         sop_sigs **sigs_out,
                         sop_buf **msg_out);

#endif // __SOP_H__
