/**
 * @file message.h
 * Message processing functions and structures
 */

#ifndef RSPAMD_MESSAGE_H
#define RSPAMD_MESSAGE_H

#include "config.h"

#include "libmime/email_addr.h"
#include "libutil/addr.h"
#include "libcryptobox/cryptobox.h"
#include "libmime/mime_headers.h"
#include "libmime/content_type.h"
#include "libserver/url.h"
#include "libutil/ref.h"
#include "libutil/str_util.h"
#include "libutil/heap.h"
#include "libserver/word.h"

#include <unicode/uchar.h>
#include <unicode/utext.h>

#ifdef __cplusplus
extern "C" {
#endif

struct rspamd_task;
struct controller_session;
struct rspamd_image;
struct rspamd_archive;
struct rspamd_html_features;

enum rspamd_mime_part_flags {
	RSPAMD_MIME_PART_ATTACHEMENT = (1u << 1u),
	RSPAMD_MIME_PART_BAD_CTE = (1u << 4u),
	RSPAMD_MIME_PART_MISSING_CTE = (1u << 5u),
	RSPAMD_MIME_PART_NO_TEXT_EXTRACTION = (1u << 6u),
	RSPAMD_MIME_PART_COMPUTED = (1u << 7u),
};

enum rspamd_mime_part_type {
	RSPAMD_MIME_PART_UNDEFINED = 0,
	RSPAMD_MIME_PART_MULTIPART,
	RSPAMD_MIME_PART_MESSAGE,
	RSPAMD_MIME_PART_TEXT,
	RSPAMD_MIME_PART_ARCHIVE,
	RSPAMD_MIME_PART_IMAGE,
	RSPAMD_MIME_PART_CUSTOM_LUA
};

#define IS_PART_MULTIPART(part) ((part) && ((part)->part_type == RSPAMD_MIME_PART_MULTIPART))
#define IS_PART_TEXT(part) ((part) && ((part)->part_type == RSPAMD_MIME_PART_TEXT))
#define IS_PART_MESSAGE(part) ((part) && ((part)->part_type == RSPAMD_MIME_PART_MESSAGE))

enum rspamd_cte {
	RSPAMD_CTE_UNKNOWN = 0,
	RSPAMD_CTE_7BIT = 1,
	RSPAMD_CTE_8BIT = 2,
	RSPAMD_CTE_QP = 3,
	RSPAMD_CTE_B64 = 4,
	RSPAMD_CTE_UUE = 5,
};

struct rspamd_mime_text_part;

struct rspamd_mime_multipart {
	GPtrArray *children;
	rspamd_ftok_t boundary;
};

enum rspamd_lua_specific_type {
	RSPAMD_LUA_PART_TEXT,
	RSPAMD_LUA_PART_STRING,
	RSPAMD_LUA_PART_TABLE,
	RSPAMD_LUA_PART_FUNCTION,
	RSPAMD_LUA_PART_UNKNOWN,
};

struct rspamd_lua_specific_part {
	int cbref;
	enum rspamd_lua_specific_type type;
};

struct rspamd_mime_part {
	struct rspamd_content_type *ct;
	struct rspamd_content_type *detected_ct;
	char *detected_type;
	char *detected_ext;
	struct rspamd_content_disposition *cd;
	rspamd_ftok_t raw_data;
	rspamd_ftok_t parsed_data;
	struct rspamd_mime_part *parent_part;

	struct rspamd_mime_header *headers_order;
	struct rspamd_mime_headers_table *raw_headers;
	GPtrArray *urls;

	char *raw_headers_str;
	gsize raw_headers_len;

	enum rspamd_cte cte;
	unsigned int flags;
	enum rspamd_mime_part_type part_type;
	unsigned int part_number;

	union {
		struct rspamd_mime_multipart *mp;
		struct rspamd_mime_text_part *txt;
		struct rspamd_image *img;
		struct rspamd_archive *arch;
		struct rspamd_lua_specific_part lua_specific;
	} specific;

	unsigned char digest[rspamd_cryptobox_HASHBYTES];
};

#define RSPAMD_MIME_TEXT_PART_FLAG_UTF (1 << 0)
#define RSPAMD_MIME_TEXT_PART_FLAG_EMPTY (1 << 1)
#define RSPAMD_MIME_TEXT_PART_FLAG_HTML (1 << 2)
#define RSPAMD_MIME_TEXT_PART_FLAG_8BIT_RAW (1 << 3)
#define RSPAMD_MIME_TEXT_PART_FLAG_8BIT_ENCODED (1 << 4)
#define RSPAMD_MIME_TEXT_PART_ATTACHMENT (1 << 5)
#define RSPAMD_MIME_TEXT_PART_FLAG_NEWLINES_TRUNCATED (1 << 6)
#define RSPAMD_MIME_TEXT_PART_FLAG_WORDS_TRUNCATED (1 << 7)

#define IS_TEXT_PART_EMPTY(part) ((part)->flags & RSPAMD_MIME_TEXT_PART_FLAG_EMPTY)
#define IS_TEXT_PART_UTF(part) ((part)->flags & RSPAMD_MIME_TEXT_PART_FLAG_UTF)
#define IS_TEXT_PART_HTML(part) ((part)->flags & RSPAMD_MIME_TEXT_PART_FLAG_HTML)
#define IS_TEXT_PART_ATTACHMENT(part) ((part)->flags & RSPAMD_MIME_TEXT_PART_ATTACHMENT)

/* CTA (call-to-action) URL heap entry structure */
struct rspamd_html_cta_entry {
	unsigned int pri;       /* Priority for heap (weight * scale) */
	unsigned int idx;       /* Heap index (managed by heap) */
	struct rspamd_url *url; /* URL pointer */
	float weight;           /* Original button weight */
};

RSPAMD_HEAP_DECLARE(rspamd_html_heap_storage, struct rspamd_html_cta_entry);

struct rspamd_mime_text_part {
	const char *language;
	GPtrArray *languages;
	const char *real_charset;

	/* Raw data in native encoding */
	rspamd_ftok_t raw;
	rspamd_ftok_t parsed; /* decoded from mime encodings */

	/* UTF8 content */
	rspamd_ftok_t utf_content;        /* utf8 encoded processed content */
	GByteArray *utf_raw_content;      /* utf raw content */
	GByteArray *utf_stripped_content; /* utf content with no newlines */
	GArray *normalized_hashes;        /* Array of uint64_t */
	rspamd_words_t utf_words;         /* kvec of rspamd_word_t */
	UText utf_stripped_text;          /* Used by libicu to represent the utf8 content */

	GPtrArray *newlines; /**< positions of newlines in text, relative to content*/
	void *html;
	/* Optional HTML features collected during parsing */
	struct rspamd_html_features *html_features;
	/* CTA (call-to-action) URLs extracted from HTML with weights */
	rspamd_html_heap_storage_t *cta_urls; /**< cta_heap_t* for HTML parts, NULL for plain text */
	GList *exceptions;                    /**< list of offsets of urls						*/
	struct rspamd_mime_part *mime_part;
	struct rspamd_mime_text_part *alt_text_part; /**< alternative text part (text for html, html for text) */

	unsigned int flags;
	unsigned int nlines;
	unsigned int spaces;
	unsigned int nwords;
	unsigned int non_ascii_chars;
	unsigned int ascii_chars;
	unsigned int double_spaces;
	unsigned int non_spaces;
	unsigned int empty_lines;
	unsigned int capital_letters;
	unsigned int numeric_characters;
	unsigned int unicode_scripts;
};

struct rspamd_message_raw_headers_content {
	const char *begin;
	gsize len;
	const char *body_start;
};

struct rspamd_message {
	const char *message_id;
	char *subject;

	GPtrArray *parts;      /**< list of parsed parts							*/
	GPtrArray *text_parts; /**< list of text parts								*/
	struct rspamd_message_raw_headers_content raw_headers_content;
	void *received_headers; /**< list of received headers						*/
	khash_t(rspamd_url_hash) * urls;
	struct rspamd_mime_headers_table *raw_headers; /**< list of raw headers						*/
	struct rspamd_mime_header *headers_order;      /**< order of raw headers							*/
	unsigned int nheaders;                         /**< total parsed headers across all MIME parts */
	gboolean headers_limit_reached;
	unsigned int narchive_files; /**< total archive file metadata entries */
	gboolean archive_files_limit_reached;
	unsigned int ntext_newlines; /**< total tracked text newline positions */
	gboolean text_newlines_limit_reached;
	struct rspamd_tokenize_budget words_budget; /**< message wide words budget */
	gboolean text_words_limit_reached;
	struct rspamd_task *task;
	GPtrArray *rcpt_mime;
	GPtrArray *from_mime;
	unsigned char digest[16];
	enum rspamd_newlines_type nlines_type; /**< type of newlines (detected on most of headers 	*/
	ref_entry_t ref;
};

#define MESSAGE_FIELD(task, field) ((task)->message->field)
#define MESSAGE_FIELD_CHECK(task, field) ((task)->message ? (task)->message->field : (__typeof__((task)->message->field)) NULL)

/**
 * Parse and pre-process mime message
 * @param task worker_task object
 * @return
 */
gboolean rspamd_message_parse(struct rspamd_task *task);

/**
 * Process content in task (e.g. HTML parsing)
 * @param task
 */
void rspamd_message_process(struct rspamd_task *task);

/**
 * Process an injected text part (URL extraction, words, normalization)
 * @param task
 * @param text_part the injected text part to process
 * @param cur_url_order pointer to current URL order counter
 */
void rspamd_message_process_injected_text_part(struct rspamd_task *task,
											   struct rspamd_mime_text_part *text_part,
											   uint16_t *cur_url_order);

/**
 * Converts string to cte
 * @param str
 * @return
 */
enum rspamd_cte rspamd_cte_from_string(const char *str);

/**
 * Converts cte to string
 * @param ct
 * @return
 */
const char *rspamd_cte_to_string(enum rspamd_cte ct);

struct rspamd_message *rspamd_message_new(struct rspamd_task *task);

struct rspamd_message *rspamd_message_ref(struct rspamd_message *msg);

void rspamd_message_unref(struct rspamd_message *msg);

/**
 * Updates digest of the message if modified
 * @param msg
 * @param input
 * @param len
 */
void rspamd_message_update_digest(struct rspamd_message *msg,
								  const void *input, gsize len);

#ifdef __cplusplus
}
#endif

#endif
