/* <@LICENSE>
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to you under the Apache License, Version 2.0
 * (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.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * </@LICENSE>
 */
#ifndef LIBSPAMC_H
#define LIBSPAMC_H 1

#include <stdio.h>
#include <stdarg.h>
#include <sys/types.h>
#ifdef _WIN32
#ifdef _MSC_VER
/* ignore MSVC++ warnings that are annoying and hard to remove:
 4115 named type definition in parentheses
 4127 conditional expression is constant
 4514 unreferenced inline function removed
 4996 deprecated "unsafe" functions (bug 4855)
 */
#pragma warning( disable : 4115 4127 4514 )
#if (_MSC_VER >= 1400)  /* VC8+ */
#pragma warning( disable : 4996 )
#endif

#endif
#include <winsock.h>
#else
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
/* some platforms (Cygwin) don't implement getaddrinfo */
#ifdef EAI_AGAIN
#define SPAMC_HAS_ADDRINFO 1
#endif
#endif

#if (defined(_WIN32) || !defined(_SYSEXITS_H))
/* FIXME: This stuff has to go somewhere else */

#define EX_OK           0
#define EX_USAGE        64
#define EX_DATAERR      65
#define EX_NOINPUT      66
#define EX_NOUSER       67
#define EX_NOHOST       68
#define EX_UNAVAILABLE  69
#define EX_SOFTWARE     70
#define EX_OSERR        71
#define EX_OSFILE       72
#define EX_CANTCREAT    73
#define EX_IOERR        74
#define EX_TEMPFAIL     75
#define EX_PROTOCOL     76
#define EX_NOPERM       77
#define EX_CONFIG       78

#define STDIN_FILENO 0
#define STDOUT_FILENO 1

/* FIXME: This doesn't belong here either */
#define LOG_EMERG       0       /* system is unusable */
#define LOG_ALERT       1       /* action must be taken immediately */
#define LOG_CRIT        2       /* critical conditions */
#define LOG_ERR         3       /* error conditions */
#define LOG_WARNING     4       /* warning conditions */
#define LOG_NOTICE      5       /* normal but significant condition */
#define LOG_INFO        6       /* informational */
#define LOG_DEBUG       7       /* debug-level messages */

#endif

#define EX_NOTSPAM		  0
#define EX_ISSPAM		  1
#define EX_TOOBIG		866

/* Aug 14, 2002 bj: Bitflags instead of lots of bool parameters */
#define SPAMC_MODE_MASK      1
#define SPAMC_RAW_MODE       0
#define SPAMC_BSMTP_MODE     1

#define SPAMC_USE_INET6       (1<<31)
#define SPAMC_USE_INET4       (1<<30)

#define SPAMC_CHECK_ONLY      (1<<29)
#define SPAMC_SAFE_FALLBACK   (1<<28)
#define SPAMC_USE_SSL         (1<<27)

/* Jan 30, 2003 ym: added reporting options */
#define SPAMC_REPORT          (1<<26)
#define SPAMC_REPORT_IFSPAM   (1<<25)

/* Feb  1 2003 jm: might as well fix bug 191 as well */
#define SPAMC_SYMBOLS         (1<<24)

/* 2003/04/16 SJF: randomize hostname order (quasi load balancing) */
#define SPAMC_RANDOMIZE_HOSTS (1<<23)

/* log to stderr */
#define SPAMC_LOG_TO_STDERR   (1<<22)

/* Nov 24, 2004 NP: added learning support */
#define SPAMC_LEARN	      (1<<21)

/* May 5, 2005 NP: added list reporting support */
#define SPAMC_REPORT_MSG      (1<<20)

/* Oct 21, 2005 sidney: added ping test */
#define SPAMC_PING      (1<<19)

/* Jan 1, 2007 sidney: added SSL protocol versions */
/* no flags means use default of SSL_v23 */
/* Set both flags to specify TSL_v1 */
#define SPAMC_TLSV1 (1<<18)
#define SPAMC_SSLV3 (1<<17)

/* Nov 30, 2006 jm: add -z, zlib support */
#define SPAMC_USE_ZLIB        (1<<16)

/* Jan 16, 2007 jm: get markup headers from spamd */
#define SPAMC_HEADERS         (1<<15)

/* December 5, 2007 duncf: send log messages to callback */
#define SPAMC_LOG_TO_CALLBACK (1<<14)

/* December 6, 2011 Sebastian Wiesinger <sebastian@karotte.org>:
 * Turn EX_UNAVAILABLE into EX_TEMPFAIL - bug 6717
 * */
#define SPAMC_UNAVAIL_TEMPFAIL (1<<13)

/* April 2022, add SSL client certificate support, bug 7267 */
#define SPAMC_CLIENT_SSL_CERT (1<<12)

#define SPAMC_MESSAGE_CLASS_SPAM 1
#define SPAMC_MESSAGE_CLASS_HAM  2

#define SPAMC_SET_LOCAL          1
#define SPAMC_SET_REMOTE         2

#define SPAMC_REMOVE_LOCAL       4
#define SPAMC_REMOVE_REMOTE      8

#define SPAMC_MAX_MESSAGE_LEN     (256 * 1024 * 1024)     /* see bug 4928 */

/* Aug 14, 2002 bj: A struct for storing a message-in-progress */
typedef enum
{
    MESSAGE_NONE,
    MESSAGE_ERROR,
    MESSAGE_RAW,
    MESSAGE_BSMTP,
    MAX_MESSAGE_TYPE
} message_type_t;

struct libspamc_private_message;

struct message
{
    /* Set before passing the struct on! */
    unsigned int max_len; /* messages larger than this will return EX_TOOBIG */
    int timeout;		/* timeout for read() system calls */
    int connect_timeout;	/* Sep 8, 2008 mrgus: timeout for opening sockets */

    /* Filled in by message_read */
    message_type_t type;
    char *raw;
    int raw_len;		/* Raw message buffer */
    /* note: do not make "raw_len" in particular unsigned! see bug 4593 */
    char *pre;
    int pre_len;		/* Pre-message data (e.g. SMTP commands) */
    char *msg;
    int msg_len;		/* The message */
    char *post;
    int post_len;		/* Post-message data (e.g. SMTP commands) */
    int content_length;

    /* Filled in by filter_message */
    int is_spam;		/* EX_ISSPAM if the message is spam, EX_NOTSPAM
				   if not */
    float score, threshold;	/* score and threshold */
    char *outbuf;		/* Buffer for output from spamd */
    char *out;
    int out_len;		/* Output from spamd. Either the filtered
				   message, or the check-only response. Or else,
				   a pointer to msg above. */

    /* these members added in SpamAssassin version 2.60: */
    struct libspamc_private_message *priv;
};

/*------------------------------------------------------------------------
 * TRANSPORT (2004/04/16 - SJF)
 *
 * The code to connect with the daemon has gotten more complicated: support
 * for SSL, fallback to multiple hosts, and using UNIX domain sockets. The
 * code has gotten ugly with way too many parameters being passed all around.
 *
 * So we've created this object to hold all the info required to connect with
 * the remote site, including a self-contained list of all the IP addresses
 * in the event this is using TCP sockets. These multiple IPs can be obtained
 * only from DNS returning more than one A record for a single name, and
 * this allows for fallback.
 *
 * We also allow a kind of quasi-load balancing, where we take the list of
 * A records from DNS and randomize them before starting out - this lets
 * us spread the load out among multiple servers if desired. The idea for
 * load balancing goes to Jeremy Zawodny.
 *
 * By putting all our data here, we remove "fallback" from being a special
 * case. We may find ourselves with several IP addresses, but if the user
 * disables fallback, we set the IP address count to one. Now the connect
 * code just loops over that same address.
 */
#define TRANSPORT_LOCALHOST 0x01	/* TCP to localhost only */
#define	TRANSPORT_TCP	    0x02	/* standard TCP socket   */
#define TRANSPORT_UNIX	    0x03	/* UNIX domain socket    */

#define TRANSPORT_MAX_HOSTS 256	/* max hosts we can failover between */

struct transport
{
    int type;

    const char *socketpath;	/* for UNIX dommain socket      */
    const char *hostname;	/* for TCP sockets              */

    unsigned short port;	/* for TCP sockets              */

#ifdef SPAMC_HAS_ADDRINFO
    struct addrinfo *hosts[TRANSPORT_MAX_HOSTS];
#else
    struct in_addr hosts[TRANSPORT_MAX_HOSTS];
#endif
    int nhosts;
    int flags;

    /* added in SpamAssassin 3.2.0 */
    int connect_retries;
    int retry_sleep;

    /* Added for filterloop */
    int filter_retries;
    int filter_retry_sleep;

#ifdef SPAMC_SSL
    const char *ssl_cert_file;
    const char *ssl_key_file;
    const char *ssl_ca_file;
    const char *ssl_ca_path;
#endif
};

/* Initialise and setup transport-specific context for the connection
 * to spamd.  Note that this may leak a small amount of string data for
 * the remote hostname (bug 5531) if called repeatedly; use
 *  transport_cleanup() to clean this up. */
extern void transport_init(struct transport *tp);
extern int transport_setup(struct transport *tp, int flags);

/* Aug 14, 2002 bj: New interface functions */

/* Read in a message from the fd, with the mode specified in the flags.
 * Returns EX_OK on success, EX_otherwise on failure. On failure, m may be
 * either MESSAGE_NONE or MESSAGE_ERROR. */
int message_read(int in_fd, int flags, struct message *m);

/* Write out a message to the fd, as specified by m->type. Note that
 * MESSAGE_NONE messages have nothing to write. Also note that if you ran the
 * message through message_filter with SPAMC_CHECK_ONLY, it will only output
 * the "score/threshold" line. */
long message_write(int out_fd, struct message *m);

/* Process the message through the spamd filter, making as many connection
 * attempts as are implied by the transport structure. To make this do
 * failover, more than one host is defined, but if there is only one there,
 * no failover is done.
 */
int message_filter(struct transport *tp, const char *username,
		   int flags, struct message *m);

/* Process the message through the spamd tell command, making as many
 * connection attempts as are implied by the transport structure. To make
 * this do failover, more than one host is defined, but if there is only
 * one there, no failover is done.
 */
int message_tell(struct transport *tp, const char *username, int flags,
		 struct message *m, int msg_class,
		 unsigned int tellflags, unsigned int *didtellflags);

/* Dump the message. If there is any data in the message (typically, m->type
 * will be MESSAGE_ERROR) it will be message_writed. Then, fd_in will be piped
 * to fd_out intol EOF. This is particularly useful if you get back an
 * EX_TOOBIG. */
void message_dump(int in_fd, int out_fd, struct message *m, int flags);

/* Do a message_read->message_filter->message_write sequence, handling errors
 * appropriately with dump_message or appropriate CHECK_ONLY output. Returns
 * EX_OK or EX_ISSPAM/EX_NOTSPAM on success, some error EX on error. */
int message_process(struct transport *trans, char *username, int max_size,
		    int in_fd, int out_fd, const int flags);

/* Cleanup the resources we allocated for storing the message. Call after
 * you're done processing. */
void message_cleanup(struct message *m);

/* Aug 14, 2002 bj: This is now legacy, don't use it. */
int process_message(struct transport *tp, char *username,
		    int max_size, int in_fd, int out_fd,
		    const int check_only, const int safe_fallback);

void register_spamc_header_callback(const struct message *m, void (*func)(struct message *m, int flags, char *buf, int len));
void register_spamd_header_callback(const struct message *m, void (*func)(struct message *m, int flags, const char *buf, int len));

void register_libspamc_log_callback(void (*function)(int flags, int level, char *msg, va_list args));

void libspamc_log(int flags, int level, char *msg, ...);

/* Cleanup the resources allocated for storing details of the transport.
 * Added in SpamAssassin 3.3.0. */
void transport_cleanup(struct transport *tp);

/* define a preprocessor symbol so that calling code can tell if the
 * transport_cleanup() API function is available. */
#define SPAMC_HAS_TRANSPORT_CLEANUP

#endif
