/* This file is part of Mailfromd.
   Copyright (C) 2005-2025 Sergey Poznyakoff

   This program 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, or (at your option)
   any later version.

   This program 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
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */

#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <gettext.h>
#include <limits.h>
#include <mailutils/types.h>
#include <mailutils/cfg.h>
#include <mailutils/stdstream.h>
#include <mailutils/cli.h>
#include <pwd.h>
#include <grp.h>

#include "dns.h"

#define _(String) gettext(String)
#define N_(String) String

#if defined HAVE_SYSCONF && defined _SC_OPEN_MAX
# define getmaxfd() sysconf(_SC_OPEN_MAX)
#elif defined (HAVE_GETDTABLESIZE)
# define getmaxfd() getdtablesize()
#else
# define getmaxfd() 256
#endif


/* The following defines are borrowed from intprops.h (gnulib) */

/* True if the arithmetic type T is signed.  */
#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))

/* Return zero if T can be determined to be an unsigned type.
   Otherwise, return 1.
   When compiling with GCC, INT_STRLEN_BOUND uses this macro to obtain a
   tighter bound.  Otherwise, it overestimates the true bound by one byte
   when applied to unsigned types of size 2, 4, 16, ... bytes.
   The symbol signed_type_or_expr__ is private to this header file.  */
#if __GNUC__ >= 2
# define signed_type_or_expr__(t) TYPE_SIGNED (__typeof__ (t))
#else
# define signed_type_or_expr__(t) 1
#endif

/* Bound on length of the string representing an integer type or expression T.
   Subtract 1 for the sign bit if T is signed; log10 (2.0) < 146/485;
   add 1 for integer division truncation; add 1 more for a minus sign
   if needed.  */
#define INT_STRLEN_BOUND(t) \
  ((sizeof (t) * CHAR_BIT - signed_type_or_expr__ (t)) * 146 / 485 \
   + signed_type_or_expr__ (t) + 1)

/* Bound on buffer size needed to represent an integer type or expression T,
   including the terminating null.  */
#define INT_BUFSIZE_BOUND(t) (INT_STRLEN_BOUND (t) + 1)

/* Size of the buffer able to accomodate mailfromd numeric type */
#define NUMERIC_BUFSIZE_BOUND INT_BUFSIZE_BOUND(long)


/* Status values used throughout the program */
typedef enum mf_status_code {
	mf_success,
	mf_not_found,
	mf_failure,
	mf_temp_failure,
	mf_timeout,

#define	mf_status_count (mf_smtp_temp_failure+1)
} mf_status;

#define mf_resolved(c) ((c) == mf_success || (c) == mf_not_found)

const char *mf_status_str(mf_status s);


enum smtp_timeout {
	smtp_timeout_connect,
	smtp_timeout_initial,
	smtp_timeout_helo,
	smtp_timeout_mail,
	smtp_timeout_rcpt,
	smtp_timeout_rset,
	smtp_timeout_quit
};

#define SMTP_NUM_TIMEOUT (smtp_timeout_quit+1)


#define MF_GETOPT_DEFAULT   0x00
/* Process options in order; don't permute */
#define MF_GETOPT_IN_ORDER  0x01
/* Don't parse configuration files */
#define MF_GETOPT_NO_CONFIG 0x02

void mf_getopt(struct mu_cli_setup *cli, int *pargc, char ***pargv, char **capa,
	       int flags);
void mailfromd_version(struct mu_parseopt *po, mu_stream_t stream);
int mf_vercmp(const char *a, const char *b, int *pres);
void mf_init_nls (void);
int parse_time_interval(const char *str, time_t *pint, const char **endp);

void dict_init(mu_assoc_t *dict);
char *dict_install(mu_assoc_t dict, const char *name, const char *value);
void dict_destroy(mu_assoc_t *dict);
char *dict_getsym(void *data, const char *str);


struct mf_privs {
	char *user;
	int allgroups;
	mu_list_t groups;
};

struct mf_gid_list;

struct mf_gid_list *mf_gid_list_alloc(void);
void mf_gid_list_free(struct mf_gid_list *gl);
struct mf_gid_list *mf_gid_list_dup(struct mf_gid_list *src);
void mf_gid_list_add(struct mf_gid_list *gl, gid_t gid);
void mf_gid_list_array(struct mf_gid_list *gl, size_t *gc, gid_t **gv);

int switch_to_privs (uid_t uid, gid_t gid, struct mf_gid_list *);
void get_user_groups (struct mf_gid_list *grouplist, const char *user);

void mf_priv_setup (struct mf_privs *);

extern void (*mf_server_startup_hook)(void);
extern void (*mf_server_shutdown_hook)(void);


char **config_array_to_argv (mu_config_value_t *val);
char *config_array_to_string (mu_config_value_t *val);

int config_cb_timeout (struct timeval *pt, mu_config_value_t *val);
int config_cb_time_t(void *data, mu_config_value_t *arg);
int config_cb_ignore(void *data, mu_config_value_t *val);

void mf_proctitle_init (int argc, char *argv[], char *env[]);
void mf_proctitle_format (const char *fmt, ...);
int stderr_closed_p(void);
int mf_list_compare_string(const void *item, const void *value);


struct symtab;
struct syment {
	char *name;
	unsigned refcnt;
};

typedef int (*symtab_enumerator_t)(void *sym, void *data);

#define SYMTAB_COPY_KEY 0x1
#define SYMTAB_ICASE    0x2

const char * symtab_strerror(int rc);

int symtab_lookup_or_install(struct syment **ent,
			     struct symtab *st, const char *name,
			     int *install);
int symtab_lookup_or_install_entry(struct syment **elp, 
				   struct symtab *st, struct syment *ent,
				   int *install);
void symtab_clear(struct symtab *st);
struct symtab *symtab_create(size_t elsize, int flags,
			     void *(*alloc_fun)(size_t),
			     void (*free_fun)(void *));
void symtab_destroy(struct symtab **pst);
int symtab_remove(struct symtab *st, const char *name);
int symtab_replace(struct symtab *st, struct syment *ent,
		   struct syment **old_ent);
int symtab_enumerate(struct symtab *st, symtab_enumerator_t fun, void *data);


typedef int (*symtab_selfun)(const struct syment *ent, void *closure);
typedef int (*symtab_errfun)(int rc, struct symtab *tab, const char *name,
			     void *closure);
typedef int (*symtab_confun)(struct symtab *dst,
			     struct syment *dstent, struct syment *srcent,
			     void *closure);
typedef int (*symtab_cpyfun)(struct syment **pdst, struct syment *src,
			     size_t len, void *closure);

int symtab_import(struct symtab *dst, struct symtab *src,
		  symtab_selfun selfun, symtab_errfun errfun,
		  symtab_confun confun, symtab_cpyfun cpyfun,
		  void *closure);

size_t symtab_count_entries(struct symtab *st);


typedef struct transform *transform_t;

const char *transform_error_string (void);
transform_t transform_compile(const char *expr, int cflags);
void transform_free(transform_t);
char *transform_string (transform_t tf, const char *input);
transform_t transform_join (transform_t dst, transform_t src);


void close_fds_above(int fd);
void close_fds_except(fd_set *exfd);

/* logger.c */
#ifdef DEFAULT_SYSLOG_ASYNC
# define DEFAULT_LOG_STREAM "syslog:async"
#else
# define DEFAULT_LOG_STREAM "syslog"
#endif

#define LOGF_STDERR 0x01 /* Logger uses stderr */

struct logger {
	char *name;
	int flags;
	void (*log_open)(void);
	void (*log_close)(void);
	void (*log_fdset)(fd_set *set);
	void (*log_text)(int, const char *);
};

int logger_flags(int mask);
int logger_select(const char *name);
void logger_open(void);
void logger_close(void);
void logger_fdset(fd_set *set);
void logger_text(int prio, const char *text);
void logger_set_print_severity(int enable);

#define vlogmsg mu_diag_voutput
#define logmsg mu_diag_output


/* strmwait.c */

struct timeout_ctl {
	time_t start;
	time_t timeout;
};

#define UPDATE_TTW(t) do {                                           \
        time_t now = time(NULL);                                     \
	time_t delta = now - (t).start;                              \
	if ((t).timeout > delta)                                     \
		(t).timeout -= delta;                                \
	else                                                         \
		(t).timeout = 0;                                     \
        (t).start = now;                                             \
} while (0)

void init_timeout_ctl(struct timeout_ctl *tctl, time_t timeout);
int mf_stream_wait(mu_stream_t stream, int flags, struct timeout_ctl *tctl);


/* ftimestr.c */
extern char *time_format_string;

size_t format_time_str(FILE *fp, time_t timestamp);


/* dns.c */
extern size_t dns_max_cname_chain;

void dnsbase_init(void);
mf_status dns_to_mf_status(dns_status stat);
dns_status mf_to_dns_status(mf_status stat);
mf_status resolve_ipstr_domain(const char *ipstr, const char *domain, char **phbuf);
mf_status resolve_ipstr(const char *ipstr, char **phbuf);	
	

/* server.c */

#define MF_SERVER_FOREGROUND 0x01
#define MF_SERVER_NORESTART  0x02

extern void mf_server_log_setup(void);

extern mode_t mf_server_umask;
extern char *mf_server_user;
extern struct mf_gid_list *mf_server_retain_groups;
extern char *mf_server_lint_option;

void mf_server_check_pidfile(const char *name);
void mf_server_save_cmdline(int argc, char **argv);
void mf_server_start(const char *program, const char *dir,
		     const char *pidfile, int flags);


/* namefixup.c */
void mf_namefixup_register(char **ptr, const char *initval);
void mf_file_name_ptr_fixup(char **ptr, char *dir, size_t dirlen);
void mf_namefixup_run(char *dir);
void mf_namefixup_free(void);

int mfl_filter_pipe_create(
	mu_stream_t *pret,
	mu_stream_t transport,
	int mode,
	int flags,
	char const *cmdline,
	int (*fltfunc)(mu_stream_t *, mu_stream_t, int, int, size_t, char **, void *),
	
	void (*errfunc)(int, char const *, char const *, int, int, void *),
	void *closure);

int sendmail_address_normalize(mu_message_t imsg, mu_message_t *ret_msg);
