/*
* transform.h - written by ale in milano on 09 Sep 2020
* functions to undo MLM transformations

Copyright (C) 2020 Alessandro Vesely

This file is part of zdkimfilter

zdkimfilter 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 3 of the License, or
(at your option) any later version.

zdkimfilter 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 version 3
along with zdkimfilter.  If not, see <http://www.gnu.org/licenses/>.

Additional permission under GNU GPLv3 section 7:

If you modify zdkimfilter, or any covered part of it, by linking or combining
it with OpenSSL, OpenDKIM, Sendmail, or any software developed by The Trusted
Domain Project or Sendmail Inc., containing parts covered by the applicable
licence, the licensor of zdkimfilter grants you additional permission to convey
the resulting work.
*/
#include <libopendkim/dkim.h>

#include "myadsp.h" // for dmarc_rec, dmarc_domains
#include "database.h" // for domain_prescreen
#include "treewalk.h"

#include <stdio.h> // for FILE

#if !defined TRANSFORM_H_INCLUDED
typedef struct original_header
{
	struct original_header *next;
	unsigned name_length; // sort key
	unsigned colon_ndx;   // different from name_length if there are spaces
	unsigned is_empty: 1;
	unsigned is_replaced: 1;
	unsigned only_if_signed: 1;
	unsigned is_author: 1;
	char field[]; // header field, with CRLF if folded
} original_header;

typedef enum original_h_flag
{
	original_h_only_if_signed = 1,
	original_h_is_author = 2,

	original_h_set = 0x80
} original_h_flag;

void clear_original_header(original_header *oh);

// #define MAX_FROM_CANDIDATES 6 // From, Author, {,X-}Original-From, Reply-To, Cc
// candidate from cf[i] indexes defined in verify_headers()

typedef enum candidate_is
{
	candidate_is_from,
	candidate_is_author,
	candidate_is_original_from,
	candidate_is_x_original_from,
	candidate_is_reply_to,
	candidate_is_cc,
	MAX_FROM_CANDIDATES
} candidate_is;

typedef struct candidate_from
{
	char *mailbox, *domain;
	domain_prescreen *dps;
	unsigned short substring_length; // matching part of display phrase
	unsigned short part; // index where the match started
	candidate_is is;
} candidate_from;
#define MIN_SUBSTRING_LENGTH 4

void clear_candidate_from(candidate_from **dest);
int add_candidate_from(candidate_from **dest, char const*mailbox, candidate_is);

struct transform_stack;
typedef struct transform_stack transform_stack;

typedef enum cte_transform
{
	cte_transform_identity,
	cte_transform_base64,
	cte_transform_quoted_printable,
	cte_transform_identity_multipart
} cte_transform;

char const *cte_transform_string(cte_transform cte);

typedef struct base64_encode_parm
{
	unsigned column_width: 12;
	unsigned nocr: 1;
	unsigned nooutcr: 1;
} base64_encode_parm;

typedef enum transform_retry_mode
{
	transform_retry_none,
	transform_retry_header,
	transform_retry_body,
	transform_retry_all
} transform_retry_mode;

char const *retry_mode_string(transform_retry_mode mode);

#define MAX_SAVE_SUBJECT_ALTERNATIVES 3 // Subject, Thread-Topic, X-ASG-Orig-Subj
typedef struct dkim_transform
{
	DKIM *dkim;
	original_header *oh_base;
	candidate_from *cf[MAX_FROM_CANDIDATES];
	transform_stack *vc;
	char *boundary; // top boundary, if multipart
	char *first_content_type;
	char *sender_domain, *list_domain;
	char *save_subject[MAX_SAVE_SUBJECT_ALTERNATIVES];

	char *save_file;  // zdkimverify's -o
	FILE *save_fp;    // open it

	tree_walk *tw;

	long body_offset;
	DKIM_STAT status;

	// flags
	unsigned cleared: 1;
	unsigned footer_found: 1;   // can try a new body hash
	unsigned seen_list_post: 1; // and set list_domain
	unsigned is_reply: 1;       // seen References: / In-Reply-To:
	unsigned could_pass: 1;     // found a signed field in oh_base

	unsigned add_part: 1;  // given if a mime entity was added
	unsigned mime_wrap: 1; // given if a mime entity was wrapped
	unsigned footer: 1;    // length possibly defined for this value

	unsigned original_from_is_candidate: 1;
	unsigned original_from_in_header: 1;
	unsigned original_from_remove: 1;

	unsigned author_in_header: 1;
	unsigned author_is_candidate: 1;

	cte_transform original_cte, cte;
	transform_retry_mode retry_mode;

	/*
	* Column width and nocr are needed by the encode_*() functions,
	* in order to rebuild the original content.
	*/
	base64_encode_parm b64;

	/*
	* Only the first is needed by the limit_size() function.
	*/
	unsigned long length[2];

} dkim_transform;

void clear_dkim_transform(dkim_transform *dt);
void parse_content_transfer_encoding(dkim_transform *dt, char const *s, int is_original);
cte_transform parse_content_transfer_encoding_simple(char const *s);
int content_type_is_text_plain(char const *s);
int content_type_is_multipart(char const *s);
void parse_content_type(dkim_transform *dt, char const *s, int is_original);
char *replace_original_header(dkim_transform *dt, char const *hdr);
char *peek_original_header(dkim_transform *dt, char const *hdr);
int add_to_original(dkim_transform *dt, char const *hdr);
int add_to_original_signed(original_header **oh_base, char const *hdr, original_h_flag);
int is_any_original_signed(original_header *oh, DKIM_SIGINFO *sig);
int check_original_subject(dkim_transform *dt);

typedef int (*transform_fn)(char *, unsigned, transform_stack *);

struct transform_stack
{
	transform_fn fn;
	transform_stack *next;
	void *fn_parm;
};

void clear_transform_stack(transform_stack *vc);
transform_stack *append_transform_stack(transform_stack *, transform_fn, void *);

int skip_one_entity(char *in, unsigned len, transform_stack *vc);
int decode_base64(char *in, unsigned len, transform_stack *vc);

int encode_base64(char *in, unsigned len, transform_stack *vc);
int encode_quoted_printable(char *in, unsigned len, transform_stack *vc);
int limit_size(char *in, unsigned len, transform_stack *vc);
int decode_quoted_printable(char *in, unsigned len, transform_stack *vc);
int rectify_lines(char *in, unsigned len, transform_stack *vc);
int skip_last_entity(char *in, unsigned len, transform_stack *vc);

#define TRANSFORM_H_INCLUDED
#endif
