/*
 * $Id: pattern.h 942 2008-03-04 18:21:33Z hubert@u.washington.edu $
 *
 * ========================================================================
 * Copyright 2013-2020 Eduardo Chappa
 * Copyright 2006-2008 University of Washington
 *
 * Licensed 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
 *
 * ========================================================================
 */

#ifndef PITH_PATTERN_INCLUDED
#define PITH_PATTERN_INCLUDED


#include "../pith/msgno.h"
#include "../pith/sorttype.h"
#include "../pith/string.h"
#include "../pith/indxtype.h"


/*
 * This structure is used to contain strings which are matched against
 * header fields. The match is a simple substring match. The match is
 * an OR of all the patterns in the PATTERN_S list. That is,
 * substring1_matches OR substring2_matches OR substring3_matches.
 * If not is set in the _head_ of the PATTERN_S, it is a NOT of the
 * whole pattern, that is,
 * NOT (substring1_matches OR substring2_matches OR substring3_matches).
 * The not variable is not meaningful except in the head member of the
 * PATTERN_S list.
 */
typedef struct pattern_s {
    int               not;		/* NOT of whole pattern */
    char             *substring;
    struct pattern_s *next;
} PATTERN_S;

/*
 * List of these is a list of arbitrary freetext headers and patterns.
 * This may be part of a pattern group.
 * The isemptyval bit is to keep track of the difference between an arb
 * header with no value set and one with the empty value "" set. For the
 * other builtin headers this difference is kept track of by whether or
 * not the header is in the config file at all or not. Here we want to
 * be able to add a header to the config file without necessarily giving
 * it a value.
 */
typedef struct arbhdr_s {
    char            *field;
    PATTERN_S       *p;
    int              isemptyval;
    struct arbhdr_s *next;
} ARBHDR_S;

/*
 * A list of intervals of integers.
 */
typedef struct intvl_s {
    long            imin, imax;
    struct intvl_s *next;
} INTVL_S;

/*
 * A Pattern group gives characteristics of an envelope to match against. Any of
 * the characteristics (to, from, ...) which is non-null must match for the
 * whole thing to be considered a match. That is, it is an AND of all the
 * non-null members.
 */
typedef struct patgrp_s {
    char      *nick;
    char      *comment;		/* for user, not used for anything */
    PATTERN_S *to,
	      *from,
	      *sender,
	      *cc,
	      *recip,
	      *partic,
	      *news,
	      *subj,
	      *alltext,
	      *bodytext,
	      *keyword,
	      *charsets;
    STRLIST_S *charsets_list;	/* used for efficiency, computed from charset */
    ARBHDR_S  *arbhdr;		/* list of arbitrary hdrnames and patterns */
    int        fldr_type;	/* see FLDR_* below			*/
    PATTERN_S *folder;		/* folder if type FLDR_SPECIFIC		*/
    int        inabook;		/* see IAB_* below			*/
    PATTERN_S *abooks;
    int        do_score;
    INTVL_S   *score;
    int        do_age;
    INTVL_S   *age;		/* ages are in days			*/
    int        do_size;
    INTVL_S   *size;
    int        age_uses_sentdate; /* on or off				*/
    int        do_cat;
    char     **category_cmd;
    INTVL_S   *cat;
    long       cat_lim;		/* -1 no limit  0 only headers		*/
    int        bogus;		/* patgrp contains unknown stuff	*/
    int        stat_new,	/* msg status is New (Unseen)		*/
	       stat_rec,	/* msg status is Recent			*/
	       stat_del,	/* msg status is Deleted		*/
	       stat_imp,	/* msg is flagged Important		*/
	       stat_ans,	/* msg is flagged Answered		*/
	       stat_8bitsubj,	/* subject contains 8bit chars		*/
	       stat_bom,	/* this is first pine run of the month	*/
	       stat_boy;	/* this is first pine run of the year	*/
} PATGRP_S;

#define	FLDR_ANY	0
#define	FLDR_NEWS	1
#define	FLDR_EMAIL	2
#define	FLDR_SPECIFIC	3

#define	FLDR_DEFL	FLDR_EMAIL


#define	IAB_EITHER		0x0	/* don't care if in or not */

#define IAB_TYPE_MASK		0xf
#define	IAB_YES			0x1	/* addresses in any abook */
#define	IAB_NO			0x2	/*   "      not  " */
#define	IAB_SPEC_YES		0x3	/* addresses in specific abooks */
#define	IAB_SPEC_NO		0x4

/*
 * Warning about reply-to. We're using the c-client envelope reply-to which
 * means if there isn't a real reply-to it uses the From!
 */
#define IAB_ADDR_MASK		0xff0
#define	IAB_FROM		0x10	/* from address included in list */
#define	IAB_REPLYTO		0x20	/* reply-to address included in list */
#define	IAB_SENDER		0x40	/* sender address included in list */
#define	IAB_TO			0x80	/* to address included in list */
#define	IAB_CC			0x100	/* cc address included in list */

#define	IAB_DEFL		IAB_EITHER


#define	FILTER_STATE	0
#define	FILTER_KILL	1
#define	FILTER_FOLDER	2

/*
 * For the Status parts of a PATGRP_S. For example, stat_del is Deleted
 * status. User sets EITHER means they don't care, it always matches.
 * YES means it must be deleted to match. NO means it must not be deleted.
 */
#define	PAT_STAT_EITHER		0  /* we don't care which, yes or no	*/
#define	PAT_STAT_YES		1  /* yes, this status is true		*/
#define	PAT_STAT_NO		2  /* no, this status is not true	*/

/*
 * For the State setting part of a filter action
 */
#define	ACT_STAT_LEAVE		0  /* leave msg state alone		*/
#define	ACT_STAT_SET		1  /* set this part of msg state	*/
#define	ACT_STAT_CLEAR		2  /* clear this part of msg state	*/

typedef struct action_s {
    unsigned	 is_a_role:1;	/* this is a role action		*/
    unsigned	 is_a_incol:1;	/* this is an index color action	*/
    unsigned	 is_a_score:1;	/* this is a score setting action	*/
    unsigned	 is_a_filter:1;	/* this is a filter action		*/
    unsigned	 is_a_other:1;	/* this is a miscellaneous action	*/
    unsigned	 is_a_srch:1;	/* this is for Select cmd, no action	*/
    unsigned	 bogus:1;	/* action contains unknown stuff	*/
    unsigned	 been_here_before:1;  /* inheritance loop prevention	*/
	    /* --- These are for roles --- */
    ADDRESS	*from;		/* value to set for From		*/
    ADDRESS	*replyto;	/* value to set for Reply-To		*/
    char       **cstm;		/* custom headers			*/
    char       **smtp;		/* custom SMTP server for this role	*/
    char       **nntp;		/* custom NNTP server for this role	*/
    char	*fcc;		/* value to set for Fcc			*/
    char	*litsig;	/* value to set Literal Signature	*/
    char	*sig;		/* value to set for Sig File		*/
    char	*template;	/* value to set for Template		*/
    char	*nick;		/* value to set for Nickname		*/
    int		 repl_type;	/* see ROLE_REPL_* below		*/
    int		 forw_type;	/* see ROLE_FORW_* below		*/
    int		 comp_type;	/* see ROLE_COMP_* below		*/
    char	*inherit_nick;	/* pattern we inherit actions from	*/
	    /* --- This is for indexcoloring --- */
    COLOR_PAIR  *incol;		/* colors for index line		*/
	    /* --- This is for scoring --- */
    long         scoreval;
    HEADER_TOK_S *scorevalhdrtok;
	    /* --- These are for filtering --- */
    int	 	 kill;
    long	 state_setting_bits;
    PATTERN_S	*keyword_set;	/* set these keywords			*/
    PATTERN_S	*keyword_clr;	/* clear these keywords			*/
    PATTERN_S	*folder;	/* folders to recv. filtered mail	*/
    int          move_only_if_not_deleted;	/* on or off		*/
    int          non_terminating;		/* on or off		*/
	    /* --- These are for other --- */
	      /* sort order of folder */
    unsigned     sort_is_set:1;
    SortOrder	 sortorder;	/* sorting order			*/
    int          revsort;	/* whether or not to reverse sort	*/
	      /* Index format of folder */
    char	*index_format;
    unsigned     startup_rule;
} ACTION_S;

/* flags for first_pattern..., set_role_from_msg, and confirm_role() */
#define	PAT_CLOSED	0x00000000	/* closed                            */
#define	PAT_OPENED	0x00000001	/* opened successfully		     */
#define	PAT_OPEN_FAILED	0x00000002
#define	PAT_USE_CURRENT	0x00000010	/* use current_val to set up pattern */
#define	PAT_USE_CHANGED	0x00000020	/* use changed_val to set up pattern */
#define	PAT_USE_MAIN	0x00000040	/* use main_user_val		     */
#define	PAT_USE_POST	0x00000080	/* use post_user_val		     */
#define ROLE_COMPOSE	0x00000100	/* roles with compose value != NO   */
#define ROLE_REPLY	0x00000200	/* roles with reply value != NO	    */
#define ROLE_FORWARD	0x00000400	/* roles with forward value != NO   */
#define ROLE_INCOL	0x00000800	/* patterns with non-Normal colors  */
#define ROLE_SCORE	0x00001000	/* patterns with non-zero scorevals */
#define ROLE_DO_ROLES	0x00010000	/* role patterns		    */
#define ROLE_DO_INCOLS	0x00020000	/* index line color patterns	    */
#define ROLE_DO_SCORES	0x00040000	/* set score patterns		    */
#define	ROLE_DO_FILTER	0x00080000	/* filter patterns		    */
#define	ROLE_DO_OTHER	0x00100000	/* miscellaneous patterns	    */
#define ROLE_DO_SRCH	0x00200000	/* index line color patterns	    */
#define	ROLE_OLD_PAT	0x00400000	/* old patterns variable            */
#define	ROLE_OLD_FILT	0x00800000	/* old patterns-filters variable    */
#define	ROLE_OLD_SCORE	0x01000000	/* old patterns-scores variable     */
#define ROLE_CHANGES	0x02000000	/* start editing with changes
					   already registered */

#define PAT_OPEN_MASK	0x0000000f
#define PAT_USE_MASK	0x000000f0
#define ROLE_MASK	0x00ffff00

#define	ROLE_REPL_NO		0  /* never use for reply		 */
#define	ROLE_REPL_YES		1  /* use for reply with confirmation	 */
#define	ROLE_REPL_NOCONF	2  /* use for reply without confirmation */
#define	ROLE_FORW_NO		0  /* ... forward ...			 */
#define	ROLE_FORW_YES		1
#define	ROLE_FORW_NOCONF	2
#define	ROLE_COMP_NO		0  /* ... compose ...			 */
#define	ROLE_COMP_YES		1
#define	ROLE_COMP_NOCONF	2

#define	ROLE_REPL_DEFL		ROLE_REPL_YES	/* default reply value */
#define	ROLE_FORW_DEFL		ROLE_FORW_YES	/* default forward value */
#define	ROLE_COMP_DEFL		ROLE_COMP_NO	/* default compose value */
#define	ROLE_NOTAROLE_DEFL	ROLE_COMP_NO

#define INTVL_INF	  (2147483646L)
#define INTVL_UNDEF	  (INTVL_INF + 1L)
#define SCORE_UNDEF	  INTVL_UNDEF
#define SCORE_MIN	  (-100)
#define SCORE_MAX	  (100)
#define SCOREUSE_GET	  0x000
#define SCOREUSE_INVALID  0x001	/* will recalculate scores_in_use next time */
#define SCOREUSE_ROLES    0x010	/* scores are used for roles                */
#define SCOREUSE_INCOLS   0x020	/* scores are used for index line colors    */
#define SCOREUSE_FILTERS  0x040	/* scores are used for filters              */
#define SCOREUSE_OTHER    0x080	/* scores are used for miscellaneous stuff  */
#define SCOREUSE_INDEX    0x100	/* scores are used in index-format          */
#define SCOREUSE_STATEDEP 0x200	/* scores depend on message state           */

/*
 * A message is compared with a pattern group to see if it matches.
 * If it does match, then there are actions which are taken.
 */
typedef struct pat_s {
    PATGRP_S          *patgrp;
    ACTION_S          *action;
    struct pat_line_s *patline;		/* pat_line that goes with this pat */
    char              *raw;
    unsigned           inherit:1;
    struct pat_s      *next;
    struct pat_s      *prev;
} PAT_S;

typedef	enum {TypeNotSet = 0, Literal, File, Inherit} PAT_TYPE;

/*
 * There's one of these for each line in the pinerc variable.
 * Normal type=Literal patterns have a patline with both first and last
 * pointing to the pattern. Type File has one patline for the file and first
 * and last point to the first and last patterns in the file.
 * The patterns aren't linked into one giant list, the patlines are.
 * To traverse all the patterns you have to go through the patline list
 * and then for each patline go from first to last through the patterns.
 * That's what next_pattern and friends do.
 */
typedef struct pat_line_s {
    PAT_TYPE           type;
    PAT_S             *first;	/* 1st pattern in list belonging to this line */
    PAT_S             *last;
    char              *filename; /* If type File, the filename */
    char              *filepath;
    unsigned	       readonly:1;
    unsigned	       dirty:1;	/* needs to be written back to storage */
    struct pat_line_s *next;
    struct pat_line_s *prev;
} PAT_LINE_S;

typedef struct pat_handle {
    PAT_LINE_S *patlinehead;	/* list of in-core, parsed pat lines */
    unsigned    dirtypinerc:1;	/* needs to be written */
} PAT_HANDLE;

typedef struct pat_state {
    long        rflags;
    int         cur_rflag_num;
    PAT_LINE_S *patlinecurrent;
    PAT_S      *patcurrent;	/* current pat within patline */
} PAT_STATE;

#define PATTERN_MAGIC     "P#Pats"
#define PATTERN_FILE_VERS "01"


/*
 * This is a little dangerous. We're passing flags to match_pattern and
 * peeling some of them off for our own use while passing the rest on
 * to mail_search_full. So we need to define ours so they don't overlap
 * with the c-client flags that can be passed to mail_search_full.
 * We could formalize it with mrc.
 */
#define MP_IN_CCLIENT_CB	0x10000   /* we're in a c-client callback! */
#define MP_NOT			0x20000   /* use ! of patgrp for search    */


/* match_pattern_folder_specific flags */
#define FOR_PATTERN		0x01
#define FOR_FILTER		0x02
#define FOR_OPTIONSCREEN	0x04


extern PAT_HANDLE **cur_pat_h;


/* exported prototypes */
void	    role_process_filters(void);
int         add_to_pattern(PAT_S *, long);
char       *add_pat_escapes(char *);
char       *remove_pat_escapes(char *);
char	   *add_roletake_escapes(char *);
char	   *add_comma_escapes(char *);
void        set_pathandle(long);
void        close_every_pattern(void);
void        close_patterns(long);
int         nonempty_patterns(long, PAT_STATE *);
int         any_patterns(long, PAT_STATE *);
int         edit_pattern(PAT_S *, int, long);
int         add_pattern(PAT_S *, long);
int         delete_pattern(int, long);
int         shuffle_pattern(int, int, long);
PAT_LINE_S *parse_pat_file(char *);
INTVL_S    *parse_intvl(char *);
char       *stringform_of_intvl(INTVL_S *);
char       *hdrtok_to_stringform(HEADER_TOK_S *);
HEADER_TOK_S *stringform_to_hdrtok(char *);
char       *hdrtok_to_config(HEADER_TOK_S *);
HEADER_TOK_S *config_to_hdrtok(char *);
int         scores_are_used(int);
int         patgrp_depends_on_state(PATGRP_S *);
int         patgrp_depends_on_active_state(PATGRP_S *);
PATTERN_S  *parse_pattern(char *, char *, int);
PATTERN_S  *string_to_pattern(char *);
char	   *pattern_to_string(PATTERN_S *);
char       *pattern_to_config(PATTERN_S *);
PATTERN_S  *config_to_pattern(char *);
PATTERN_S  *editlist_to_pattern(char **);
char      **pattern_to_editlist(PATTERN_S *);
PATGRP_S   *nick_to_patgrp(char *, int);
PAT_S      *first_pattern(PAT_STATE *);
PAT_S      *last_pattern(PAT_STATE *);
PAT_S      *prev_pattern(PAT_STATE *);
PAT_S      *next_pattern(PAT_STATE *);
int         write_patterns(long);
void        convert_statebits_to_vals(long, int *, int *, int *, int *);
int         match_pattern(PATGRP_S *, MAILSTREAM *, SEARCHSET *,char *,
			  long (*)(MAILSTREAM *, long), long);
void        find_8bitsubj_in_messages(MAILSTREAM *, SEARCHSET *, int, int);
void        find_charsets_in_messages(MAILSTREAM *, SEARCHSET *, PATGRP_S *, int);
int         compare_strlists_for_match(STRLIST_S *, STRLIST_S *);
int	    match_pattern_folder(PATGRP_S *, MAILSTREAM *);
int	    match_pattern_folder_specific(PATTERN_S *, MAILSTREAM *, int);
SEARCHPGM  *match_pattern_srchpgm(PATGRP_S   *, MAILSTREAM *, SEARCHSET *);
void        calc_extra_hdrs(void);
char       *get_extra_hdrs(void);
void        free_extra_hdrs(void);
void        free_pat(PAT_S **);
void        free_pattern(PATTERN_S **);
void        free_action(ACTION_S **);
PAT_S      *copy_pat(PAT_S *);
PATGRP_S   *copy_patgrp(PATGRP_S *);
ACTION_S   *copy_action(ACTION_S *);
ACTION_S   *combine_inherited_role(ACTION_S *);
void        mail_expunge_prefilter(MAILSTREAM *, int);
void	    process_filter_patterns(MAILSTREAM *, MSGNO_S *, long);
void	    reprocess_filter_patterns(MAILSTREAM *, MSGNO_S *, int);
int         trivial_patgrp(PATGRP_S *);
void        free_patgrp(PATGRP_S **);
void        free_patline(PAT_LINE_S **patline);
int         some_filter_depends_on_active_state(void);
void	    delete_filtered_msgs(MAILSTREAM *);
void        free_intvl(INTVL_S **);
void        free_arbhdr(ARBHDR_S **);


#endif /* PITH_PATTERN_INCLUDED */
