/* 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/>. */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <syslog.h>
#include "libmf.h"
#include <mailutils/mailutils.h>
#include <gacopyz.h>
#ifdef USE_SYSLOG_ASYNC
# include "syslog_async.h"
#endif


static void
syslog_default_close()
{
	closelog();
}

static void
syslog_default_open()
{
	mu_stdstream_strerr_setup(MU_STRERR_SYSLOG);
}

static void
syslog_default_logtext(int prio, const char *text)
{
	syslog(prio, "%s", text);
}


#ifdef USE_SYSLOG_ASYNC
static void
syslog_async_close()
{
	closelog_async();
}

static void
syslog_async_open()
{
	int rc;
	
	mu_stdstream_strerr_setup(MU_STRERR_SYSLOG);
	rc = mu_stream_ioctl(mu_strerr, MU_IOCTL_SYSLOGSTREAM,
			     MU_IOCTL_SYSLOGSTREAM_SET_LOGGER, syslog_async);
	if (rc)
		mu_error(_("cannot configure asynchronous syslog: %s; "
			   "falling back to system one"), mu_strerror (rc));
	closelog(); /* we won't use it */
	/* LOG_NDELAY ensures that the log descriptor is ready.
	   It is needed for logger_fdset to work (see mf_srvcfg_log_setup). */
	openlog_async(mu_log_tag, LOG_NDELAY|LOG_PID, mu_log_facility);
	
	mu_onexit(syslog_async_close, NULL);
}

static void
syslog_async_fdset(fd_set *fds)
{
	FD_SET(log_fd_async(), fds);
}

static void
syslog_async_logtext(int prio, const char *text)
{
	syslog_async(prio, "%s", text);
}
#endif


static void
stderr_open()
{
	mu_stdstream_strerr_setup(MU_STRERR_STDERR);
}

static void
stderr_fdset(fd_set *fds)
{
	FD_SET(fileno(stderr), fds);
}

static void
mf_gacopyz_log_printer(int level, char *fmt, va_list ap)
{
	switch (level) {
	case SMI_LOG_PROTO:
	case SMI_LOG_DEBUG:
		level = MU_LOG_DEBUG;
		break;
	case SMI_LOG_INFO:
		level = MU_LOG_INFO;
		break;
	case SMI_LOG_WARN:
		level = MU_LOG_WARNING;
		break;
	case SMI_LOG_ERR:
		level = MU_LOG_ERROR;
		break;

	case SMI_LOG_FATAL:
	default:
		level = MU_LOG_EMERG;
	}
	mu_diag_voutput(level, fmt, ap); 
}


static struct logger loggertab[] = {
	{ "stderr",
	  LOGF_STDERR,
	  stderr_open,
	  NULL,
	  stderr_fdset,
	  NULL },
	{ "syslog",
	  0,
	  syslog_default_open,
	  syslog_default_close,
	  NULL,
	  syslog_default_logtext },
#ifdef USE_SYSLOG_ASYNC
	{ "syslog:async",
	  0,
	  syslog_async_open,
	  syslog_async_close,
	  syslog_async_fdset,
	  syslog_async_logtext },
#endif
	{ NULL }
};
static int current_logger;

int
logger_flags(int mask)
{
	return loggertab[current_logger].flags & mask;
}

int
logger_select(const char *name)
{
	int i;
	for (i = 0; loggertab[i].name; i++) {
		if (strcmp(loggertab[i].name, name) == 0) {
			current_logger = i;
			return 0;
		}
	}
	return -1;
}

void
logger_open()
{
	int mode;
	struct logger *lp = loggertab + current_logger;
	
	if (lp->log_open)
		lp->log_open();
	mode = MU_DEBUG_LEVEL_MASK(MU_LOG_ERROR);
	mu_stream_ioctl(mu_strerr, MU_IOCTL_LOGSTREAM,
			MU_IOCTL_LOGSTREAM_SET_SEVERITY_MASK, &mode);
	gacopyz_set_logger(mf_gacopyz_log_printer);
}

void
logger_close()
{
	struct logger *lp = loggertab + current_logger;
	if (lp->log_close)
		lp->log_close();
}

void
logger_fdset(fd_set *set)
{
	struct logger *lp = loggertab + current_logger;
	if (lp->log_fdset)
		lp->log_fdset(set);
}

void
logger_text(int prio, const char *text)
{
	struct logger *lp = loggertab + current_logger;
	(lp->log_text ? lp->log_text : syslog_default_logtext)(prio, text);
}

void
logger_set_print_severity(int enable)
{
	mu_log_print_severity = enable;
	mu_stream_ioctl(mu_strerr, MU_IOCTL_LOGSTREAM,
			MU_IOCTL_LOGSTREAM_SET_MODE, &enable);
}
