/* Miscellaneous MaskData-related functionality.
 *
 * IRC Services is copyright (c) 1996-2009 Andrew Church.
 *     E-mail: <achurch@achurch.org>
 * Parts written by Andrew Kempe and others.
 * This program is free but copyrighted software; see the file COPYING for
 * details.
 */

#include "services.h"
#include "modules.h"
#include "language.h"
#include "commands.h"

#include "operserv.h"
#include "maskdata.h"

/*************************************************************************/
/**************** MaskData-related command helper routine ****************/
/*************************************************************************/

/* This routines is designed to be called from a command handler for a
 * command that deals with MaskData records (AKILL, EXCEPTION, etc.), to
 * help avoid code repetition in those command handlers.  The `info'
 * structure passed as the first parameter to each function contains
 * information about the command, such as command name and message numbers;
 * see maskdata.h for details.
 */

void do_maskdata_cmd(MaskDataCmdInfo *info, User *u)
{
    const char *cmd;
    char *mask, *reason, *expiry, *s;
    time_t expires;
    MaskData *md;

    cmd = strtok(NULL, " ");
    if (!cmd)
	cmd = "";
    expiry = NULL;
    s = strtok_remaining();
    if (stricmp(cmd,"ADD") == 0 && s && *s == '+') {
	expiry = strtok(s+1, " ");
	s = strtok_remaining();
    }
    if (s && *s == '"') {
	mask = s+1;
	s = strchr(mask, '"');
	if (!s) {
	    notice_lang(s_OperServ, u, MISSING_QUOTE);
	    return;
	}
	strtok(s, " ");         /* prime strtok() for later */
	*s = 0;                 /* null-terminate quoted string */
    } else {
	mask = strtok(s, " ");  /* this will still work if s is NULL: mask
				 * will end up NULL, which is what we want */
    }
    if (mask && info->mangle_mask)
	info->mangle_mask(mask);

    if (stricmp(cmd, "ADD") == 0) {
	time_t now = time(NULL);

	if (maskdata_count(info->md_type) >= MAX_MASKDATA) {
	    notice_lang(s_OperServ, u, info->msg_add_too_many, info->name);
	    return;
	}
	reason = strtok_remaining();
	if (!reason) {
	    syntax_error(s_OperServ, u, info->name, info->msg_add_syntax);
	    return;
	}

	expires = expiry ? dotime(expiry) : *info->def_expiry_ptr;
	if (expires < 0) {
	    notice_lang(s_OperServ, u, BAD_EXPIRY_TIME);
	    return;
	} else {
	    if (expires > 0)
		expires += now;	/* Make it into an absolute time */
	}

	/* Run command-specific checks. */
	if (info->check_add_mask
	 && !info->check_add_mask(u, info->md_type, mask, &expires)
	) {
	    return;
	}

	/* Make sure mask does not already exist on list. */
	if (get_maskdata(info->md_type, mask)) {
	    notice_lang(s_OperServ, u, info->msg_add_exists, mask, info->name);
	    return;
	}

	md = scalloc(1, sizeof(*md));
	md->mask = sstrdup(mask);
	md->reason = sstrdup(reason);
	md->time = time(NULL);
	md->expires = expires;
	strscpy(md->who, u->nick, NICKMAX);
	md = add_maskdata(info->md_type, md);
	if (info->do_add_mask)
	    info->do_add_mask(u, info->md_type, md);
	notice_lang(s_OperServ, u, info->msg_added, mask, info->name);
	if (readonly)
	    notice_lang(s_OperServ, u, READ_ONLY_MODE);

    } else if (stricmp(cmd, "DEL") == 0) {
	if (mask) {
	    md = get_maskdata(info->md_type, mask);
	    if (md) {
		if (info->do_del_mask)
		    info->do_del_mask(u, info->md_type, md);
		del_maskdata(info->md_type, md);
		notice_lang(s_OperServ, u, info->msg_deleted, mask,
			    info->name);
		if (readonly)
		    notice_lang(s_OperServ, u, READ_ONLY_MODE);
	    } else {
		notice_lang(s_OperServ, u, info->msg_del_not_found, mask,
			    info->name);
	    }
	} else {
	    syntax_error(s_OperServ, u, info->name, info->msg_del_syntax);
	}

    } else if (stricmp(cmd, "LIST") == 0 || stricmp(cmd, "VIEW") == 0) {
	int is_view = stricmp(cmd,"VIEW")==0;
	int count = 0;

	if (!mask) {
	    mask = (char *)"*";	/* Not modified in this block */
	    expires = -1;	/* Don't match on expiry time */
	} else {
	    /* This is a little longwinded for what it acheives - but we can
	     * extend it later to allow for user defined expiry times. */
	    expiry = strtok(NULL, " ");
	    if (expiry && stricmp(expiry, "NOEXPIRE") == 0)
		expires = 0;	/* Entries that never expire */
	    else
		expires = -1;
	}

	notice_lang(s_OperServ, u, info->msg_list_header, info->name);
	for (md = first_maskdata(info->md_type); md;
	     md = next_maskdata(info->md_type)
	) {
	    if (count < 500 &&
		(!mask || (match_wild_nocase(mask, md->mask) &&
			   (expires == -1 || md->expires == expires)))
	    ) {
		count++;
		if (is_view) {
		    char timebuf[BUFSIZE], usedbuf[BUFSIZE];
		    char expirebuf[BUFSIZE];

		    strftime_lang(timebuf, sizeof(timebuf), u->ngi,
				  STRFTIME_SHORT_DATE_FORMAT, md->time);
		    strftime_lang(usedbuf, sizeof(usedbuf), u->ngi,
				  STRFTIME_SHORT_DATE_FORMAT, md->lastused);
		    expires_in_lang(expirebuf, sizeof(expirebuf), u->ngi,
				    md->expires);
		    if (md->lastused) {
			notice_lang(s_OperServ, u, info->msg_list_view_format,
				    md->mask, *md->who ? md->who : "<unknown>",
				    timebuf, usedbuf, expirebuf, md->reason);
		    } else {
			notice_lang(s_OperServ, u, info->msg_list_view_unused,
				    md->mask, *md->who ? md->who : "<unknown>",
				    timebuf, expirebuf, md->reason);
		    }
		} else { /* !is_view */
		    notice_lang(s_OperServ, u, info->msg_list_list_format,
				md->mask, md->reason);
		}
	    }
	}

    } else if (stricmp(cmd, "COUNT") == 0) {
	notice_lang(s_OperServ, u, info->msg_count,
		    maskdata_count(info->md_type), info->name);

    } else if (!info->do_unknown_cmd || !info->do_unknown_cmd(u, cmd, mask)) {
	syntax_error(s_OperServ, u, info->name, info->msg_syntax);
    }
}

/*************************************************************************/
/************************ Miscellaneous functions ************************/
/*************************************************************************/

/* Create and return the reason string for the given MaskData and format
 * string.  The string will be truncated at BUFSIZE-1 bytes.  The string is
 * returned in a static buffer, and will be overwritten by subsequent calls.
 */

char *make_reason(const char *format, const MaskData *data)
{
    static char reason[BUFSIZE];
    char *s;
    int data_reason_len = -1;

    s = reason;
    while (*format && s-reason < sizeof(reason)-1) {
	if (*format == '%' && format[1] == 's') {
	    int left = (sizeof(reason)-1) - (s-reason);
	    if (data_reason_len < 0)
		data_reason_len = strlen(data->reason);
	    if (left > data_reason_len)
		left = data_reason_len;
	    memcpy(s, data->reason, left);
	    s += left;
	    format += 2;
	} else {
	    *s++ = *format++;
	}
    }
    *s = 0;
    return reason;
}

/*************************************************************************/
