/*
 * Copyright (c) 1993, 1994, 1995, 1996, 1998
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that: (1) source code distributions
 * retain the above copyright notice and this paragraph in its entirety, (2)
 * distributions including binary code include the above copyright notice and
 * this paragraph in its entirety in the documentation or other materials
 * provided with the distribution, and (3) all advertising materials mentioning
 * features or use of this software display the following acknowledgement:
 * ``This product includes software developed by the University of California,
 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
 * the University nor the names of its contributors may be used to endorse
 * or promote products derived from this software without specific prior
 * written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

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

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>

#ifdef _WIN32
#include <windows.h>
#else
#include <syslog.h>
#endif

#include "portability.h"

#include "log.h"

static int log_to_systemlog;
static int log_debug_messages;

static void rpcapd_vlog_stderr(log_priority,
    PCAP_FORMAT_STRING(const char *), va_list) PCAP_PRINTFLIKE(2, 0);

static void rpcapd_vlog_stderr(log_priority priority, const char *message, va_list ap)
{
	const char *tag;

	/*
	 * Squelch warnings from compilers that *don't* assume that
	 * priority always has a valid enum value and therefore don't
	 * assume that we'll always go through one of the case arms.
	 *
	 * If we have a default case, compilers that *do* assume that
	 * will then complain about the default case code being
	 * unreachable.
	 *
	 * Damned if you do, damned if you don't.
	 */
	tag = "";

	switch (priority) {

	case LOGPRIO_DEBUG:
		tag = "DEBUG: ";
		break;

	case LOGPRIO_INFO:
		tag = "";
		break;

	case LOGPRIO_WARNING:
		tag = "warning: ";
		break;

	case LOGPRIO_ERROR:
		tag = "error: ";
		break;
	}

	fprintf(stderr, "rpcapd: %s", tag);
	vfprintf(stderr, message, ap);
	putc('\n', stderr);
}

static void rpcapd_vlog_systemlog(log_priority,
    PCAP_FORMAT_STRING(const char *), va_list) PCAP_PRINTFLIKE(2, 0);

#ifdef _WIN32
#define MESSAGE_SUBKEY \
    "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\rpcapd"

static void rpcapd_vlog_systemlog(log_priority priority, const char *message,
    va_list ap)
{
#if 0
	static int initialized = 0;
	HKEY hey_handle;
	static HANDLE log_handle;
	WORD eventlog_type;
	DWORD event_id;
	char msgbuf[1024];
	char *strings[1];

	if (!initialized) {
		/*
		 * Register our message stuff in the Registry.
		 *
		 * First, create the registry key for us.  If the key
		 * already exists, this succeeds and returns a handle
		 * for it.
		 */
		if (RegCreateKey(HKEY_LOCAL_MACHINE, MESSAGE_SUBKEY,
		    &key_handle) != ERROR_SUCCESS) {
			/*
			 * Failed - give up and just log this message,
			 * and all subsequent messages, to the
			 * standard error.
			 */
			log_to_systemlog = 0;
			initialized = 1;
			rpcapd_vlog_stderr(priority, message, ap);
			return;
		}
		log_handle = RegisterEventSource(NULL, "rpcapd");
		initialized = 1;
	}

	switch (priority) {

	case LOGPRIO_DEBUG:
		//
		// XXX - what *should* we do about debug messages?
		//
		eventlog_type = EVENTLOG_INFORMATION_TYPE;
		event_id = RPCAPD_INFO_ID;
		break;

	case LOGPRIO_INFO:
		eventlog_type = EVENTLOG_INFORMATION_TYPE;
		event_id = RPCAPD_INFO_ID;
		break;

	case LOGPRIO_WARNING:
		eventlog_type = EVENTLOG_WARNING_TYPE;
		event_id = RPCAPD_WARNING_ID;
		break;

	case LOGPRIO_ERROR:
		eventlog_type = EVENTLOG_ERROR_TYPE;
		event_id = RPCAPD_ERROR_ID;
		break;

	default:
		/* Don't do this. */
		return;
	}

	vsprintf(msgbuf, message, ap);

	strings[0] = msgbuf;
	/*
	 * If this fails, how are we going to report it?
	 */
	(void) ReportEvent(log_handle, eventlog_type, 0, event_id, NULL, 1, 0,
	    strings, NULL);
#else
	rpcapd_vlog_stderr(priority, message, ap);
#endif
}
#else
static void rpcapd_vlog_systemlog(log_priority priority, const char *message,
    va_list ap)
{
	static int initialized = 0;
	int syslog_priority;

	if (!initialized) {
		//
		// Open the log.
		//
		openlog("rpcapd", LOG_PID, LOG_DAEMON);
		initialized = 1;
	}

	switch (priority) {

	case LOGPRIO_DEBUG:
		syslog_priority = LOG_DEBUG;
		break;

	case LOGPRIO_INFO:
		syslog_priority = LOG_INFO;
		break;

	case LOGPRIO_WARNING:
		syslog_priority = LOG_WARNING;
		break;

	case LOGPRIO_ERROR:
		syslog_priority = LOG_ERR;
		break;

	default:
		/* Don't do this. */
		return;
	}

#ifdef HAVE_VSYSLOG
	vsyslog(syslog_priority, message, ap);
#else
	/*
	 * Thanks, IBM, for not providing vsyslog() in AIX!
	 *
	 * They also warn that the syslog functions shouldn't
	 * be used in multithreaded programs, but the only thing
	 * obvious that seems to make the syslog_r functions
	 * better is that they have an additional argument
	 * that points to the information that's static to
	 * the syslog code in non-thread-safe versions.  Most
	 * of that data is set by openlog(); since we already
	 * do an openlog before doing logging, and don't
	 * change that data afterwards, I suspect that, in
	 * practice, the regular syslog routines are OK for
	 * us (especially given that we'd end up having one
	 * static struct syslog_data anyway, which means we'd
	 * just be like the non-thread-safe version).
	 */
	char logbuf[1024+1];

	vsnprintf(logbuf, sizeof logbuf, message, ap);
	syslog(syslog_priority, "%s", logbuf);
#endif
}
#endif

void rpcapd_log_set(int log_to_systemlog_arg, int log_debug_messages_arg)
{
	log_debug_messages = log_debug_messages_arg;
	log_to_systemlog = log_to_systemlog_arg;
}

void rpcapd_log(log_priority priority, const char *message, ...)
{
	va_list ap;

	if (priority != LOGPRIO_DEBUG || log_debug_messages) {
		va_start(ap, message);
		if (log_to_systemlog)
		{
			rpcapd_vlog_systemlog(priority, message, ap);
		}
		else
		{
			rpcapd_vlog_stderr(priority, message, ap);
		}
		va_end(ap);
	}
}
