/* MemoServ routines

   Cygnus IRC Services - Copyright (c) 2001-2002 Darcy Grexton
   Contact: skold@habber.net, skold @ HabberNet

   See doc/LICENSE for licensing details.
 */

#include "../inc/services.h"

static void do_help (User *u);
static void do_send (User *u);
static void do_list (User *u);
static void do_read (User *u);
static void do_del (User *u);
static void do_recall (User *u);
static void do_set (User *u);
static void do_news (User *u);

/* Go through the specified users memos, and see if
   they have any new ones.
 */
void check_memos (User *u)
{
    NickInfo *ni = get_nick (u);
    MemoInfo *mi;
    int i, new = 0;

    if (ni)
    {
	/* Did we tell them about new memos in the last 5 minutes? */
	if (time (NULL) - u->lastnotify < 300)
	    return;

	mi = &ni->memos;

	/* If their memobox is full or nearly full, let them know. */
	if (mi->memocnt >= .90 * memobox_size)
	{
	    notice (s_MemoServ, u->nick, MS_NEAR_FULL, mi->memocnt == memobox_size ? "" : "nearly ");
	    notice (s_MemoServ, u->nick, MS_IF_FULL, mi->memocnt == memobox_size ? "" : "becomes ");

	    u->lastnotify = time (NULL);
	}

	for (i = 0; i < mi->memocnt; i++)
	    if (mi->memos[i].flags & (MF_UNREAD | MF_EMAILED))
		new++;

	if (new)
	{
	    notice (s_MemoServ, u->nick, MS_NEW_MEMOS, new,
		new == 1 ? "" : "s");
	    notice (s_MemoServ, u->nick, MS_TO_LIST,
		haveserv_on == TRUE ? "" : "MSG ", s_MemoServ,
		haveserv_on == TRUE ? "" : securitysetting == 1 ? "@" : "",
		haveserv_on == TRUE ? "" : securitysetting == 1 ? me.name : "");

	    u->lastnotify = time (NULL);
	}
    }
}

/* Tell them about new news */
void check_news (User *u)
{
    NickInfo *ni = get_nick (u);

    if (ni->flags & NF_NEWS)
    {
	FILE *f;

	/* Make sure there still IS news to read */
	f = fopen (NEWSFILE, "r");

	if (f)
	{
	    notice (s_MemoServ, u->nick, MS_NEW_NEWS);
	    notice (s_MemoServ, u->nick, MS_TO_READ_NEWS,
		haveserv_on == TRUE ? "" : "MSG ", s_MemoServ,
		haveserv_on == TRUE ? "" : securitysetting == 1 ? "@" : "",
		haveserv_on == TRUE ? "" : securitysetting == 1 ?
		me.name : "");
	    fclose (f);
	}
	else
	{
	    /* The news file is gone now .. Turn the flag off. */
	    ni->flags &= ~NF_NEWS;
	}
    }
}

/* Send a memo. This function does no error checking, it assumes this
   was done beforehand. This stops memos from services itself from
   being rejected for some reason.
 */
void send_memo (NickInfo *ni, const char *from, const char *text)
{
    NickInfo *hni, *dni = hni;
    MemoInfo *mi;
    Memo *m;
    int i, emailed = 0;

    /* First off, get the host nickname if there is one. */
    if (ni->host)
	hni = findnick (ni->host);
    else
	hni = ni;

    /* Check for a FORWARD setting */
    if (hni->forward)
	dni = findnick (hni->forward);
    else
	dni = hni;

    mi = &dni->memos;

    /* See how many emailed memos they have */
    for (i = 0, m = mi->memos; i < mi->memocnt; i++, m++)
	if (m->flags & MF_EMAILED)
	    emailed++;

    mi->memocnt++;
    mi->memos = srealloc (mi->memos, sizeof (Memo) * mi->memocnt);
    m = &mi->memos[mi->memocnt - 1];

    strscpy (m->sender, from, NICKLEN);

    if (mi->memocnt > 1)
    {
	m->number = m[-1].number + 1;

	if (m->number < 1)
	    for (i = 0; i < mi->memocnt; i++)
		mi->memos[i].number = i + 1;
    }
    else
	m->number = 1;

    m->time = time (NULL);
    m->text = sstrdup (text);

    /* Send memomail as needed */
    if (dni->email && memomail_on == TRUE && (dni->flags & NF_MEMOMAIL) && emailed < memomailmax)
    {
	/* Is this email being abused? */
	if (check_email (dni->email))
	{
	    /* Used too much.. The memo is already stored, so we'll
	       just skip the email.
	     */
	    m->flags = MF_UNREAD;
	    return;
	}

	sendemail (dni->nick, itoa (m->number), 5);
	m->flags = MF_EMAILED;
    }
    else
	m->flags = MF_UNREAD;

    /* Tell anyone identified to this nick they have a new memo. */
    notify_new_memo (dni, m);
}

/* Main MemoServ routine. */
void memoserv (User *u, char *buf)  
{
    char *cmd, *s;

    cmd = strtok (buf, " ");

    if (!cmd)
	return;
    
    if (!stricmp (cmd, "\1PING"))
    {
	if (!(s = strtok (NULL, "")))
	    s = "0";
	strip (s);
	ctcpreply (s_MemoServ, u->nick, "PING %s", s);
	return;
    }
    else if (!stricmp (cmd, "\1VERSION\1"))
    {
	ctcpreply (s_MemoServ, u->nick, "VERSION Cygnus IRC Services v%s",
	    version);
	return;
    }

    /* A CTCP we don't recognize.. Ignore it. */
    else if (*cmd == '\1')
	return;

    else
    {
	Hash *command, hash_table[] =
	{
	    {"HELP",		H_NONE,		do_help},
	    {"SEND",		H_NONE,		do_send},
	    {"LIST",		H_NONE,		do_list},
	    {"READ",		H_NONE,		do_read},
	    {"DEL",		H_NONE,		do_del},
	    {"RECALL",		H_NONE,		do_recall},
	    {"NEWS",		H_NONE,		do_news},
	    {"SET",		H_NONE,		do_set},
	    {NULL}
	};
            
	if ((command = get_hash (s_MemoServ, u, strupper (cmd), hash_table)))
	    (*command->process) (u);
    }
}

/* Return a help message. */
static void do_help (User *u) 
{
    char *cmd = strtok (NULL, "");
            
    if (!cmd)
	memoserv_help_index (u);
    else
    {
	Hash *command, hash_table[] =
	{
	    {"SEND",		H_NONE,		memoserv_help_send},
	    {"DEL",		H_NONE,		memoserv_help_del},
	    {"LIST",		H_NONE,		memoserv_help_list},
	    {"READ",		H_NONE,		memoserv_help_read},
	    {"RECALL",		H_NONE,		memoserv_help_recall},
	    {"NEWS",		H_NONE,		memoserv_help_news},
	    {"SET",		H_NONE,		memoserv_help_set},
	    {"SET RECEIPTS",	H_NONE,		memoserv_help_set_receipts},
	    {"SET MEMOBOX",	H_NONE,		memoserv_help_set_memobox},
	    {"SET NOMEMO",	H_NONE,		memoserv_help_set_nomemo},
	    {"SET MEMOMAIL",	H_NONE,		memoserv_help_set_memomail},
	    {"SET FORWARD",	H_NONE,		memoserv_help_set_forward},
	    {NULL}
	};

	if ((command = get_help_hash (s_MemoServ, u, strupper (cmd), hash_table)))
	    (*command->process) (u);
    }
}

/* Send a memo to someone. */
static void do_send (User *u)
{
    NickInfo *sni, *dni, *hdni, *tni;
    MemoInfo *mi;
    Memo *m;
    char *dest = strtok (NULL, " ");
    char *text = strtok (NULL, "");
    char mbuf[BUFSIZE];
    time_t now = time (NULL);
    int i, found = 0;

    if (!u)
	return;

    if (!dest || !text)
    {
	notice (s_MemoServ, u->nick, RPL_SYNTAX, "SEND Nickname Text");
	return;
    }

    /* Make sure they're authed for a nick */
    sni = get_nick (u);

    if (!sni)
    {
	notice (s_MemoServ, u->nick, RPL_NEED_IDENT, "SEND");
	return;
    }

    if (!check_ns_auth (s_MemoServ, u->nick, sni))
	return;

    /* No memobombs! */
    if ((memo_delay && (now - u->lastmemo < memo_delay)) && !is_oper (u))
    {
	notice (s_MemoServ, u->nick, MS_WAIT_SEND,
	    duration (u->lastmemo - now + memo_delay, 2));
	return;
    }

    /* Find the person they want to memo */
    dni = findnick (dest);

    if (!dni)
    {
	notice (s_MemoServ, u->nick, NS_NOT_REGISTERED, dest);
	return;
    }

    if (!check_ns_auth (s_MemoServ, u->nick, dni))
	return;

    /* Set up the nick we reference. If theres a host, this will
       be it. We'll reference hdni for checks, and dni for replies.
     */
    if (dni->host)
	hdni = findnick (dni->host);
    else
	hdni = dni;

    /* Make sure they WANT memos.. */
    if (hdni->flags & NF_NOMEMO)
    {
	notice (s_MemoServ, u->nick, MS_NOMEMO, dni->nick);
	return;
    }

    /* Get the nick to forward to, if set */
    if (hdni->forward)
    {
	tni = findnick (hdni->forward);

	/* If tni is null, then the nick this one's set to forward to
	   isn't registered anymore. We'll free that and continue
	   normally.
	 */
	if (!tni)
	{
	    free (hdni->forward);
	    hdni->forward = NULL;
	    tni = hdni;
	}
    }
    else
	tni = hdni;

    /* Find their memolist */
    mi = &tni->memos;

    if ((mi->memocnt >= mi->memolimit) && !is_sra (u))
    {
	notice (s_MemoServ, u->nick, MS_FULL_MEMOBOX, dni->nick);
	return;
    }

    /* Go through their memos and see if they already have enough
       from this user.
     */
    for (i = 0, m = mi->memos; i < mi->memocnt; i++, m++)
	if (!stricmp (m->sender, sni->nick))
	    found++;

    /* Tell them if they have too many */
    if ((memosfromuser && found >= memosfromuser) && !is_sra (u))
    {
	notice (s_MemoServ, u->nick, MS_TOO_MANY_FROM_YOU, dni->nick);
	return;
    }

    /* Update their lastmemo time. */
    u->lastmemo = now;

    /* Send the memo */
    snprintf (mbuf, sizeof (mbuf), "%s", text);
    send_memo (dni, sni->nick, mbuf);

    /* Success! */
    notice (s_MemoServ, u->nick, MS_SENT, dni->nick);
}

/* Delete one or more memos */
static void do_del (User *u)
{
    NickInfo *ni;
    MemoInfo *mi;
    char *param = strtok (NULL, " ");
    int i, deleted = 0;

    if (!u)
	return;

    if (!param)
    {
	notice (s_MemoServ, u->nick, RPL_SYNTAX, "DEL Number|ALL|READ");
	return;
    }

    /* Make sure they're identified */
    ni = get_nick (u);

    if (!ni)
    {
	notice (s_MemoServ, u->nick, RPL_NEED_IDENT, "DEL");
	return;
    }

    if (!check_ns_auth (s_MemoServ, u->nick, ni))
	return;

    mi = &ni->memos;

    /* Make sure they have some memos to delete */
    if (!mi->memocnt)
    {
	notice (s_MemoServ, u->nick, MS_NO_MEMOS);
	return;
    }

    if (!stricmp (param, "ALL"))
    {
	for (i = 0; i < mi->memocnt; i++)
	    free (mi->memos[i].text);

	free (mi->memos);
	mi->memos = NULL;
	mi->memocnt = 0;

	notice (s_MemoServ, u->nick, MS_ALL_DELETED);
	return;
    }
    else if (!stricmp (param, "READ"))
    {
	int searching = 1;

	while (searching)
	{
	    searching = 0;

	    for (i = 0; i < mi->memocnt; i++)
	    {
		if (!(mi->memos[i].flags & MF_UNREAD))
		{
		    searching = 1;
		    break;
		}
	    }

	    if (i < mi->memocnt)
	    {
		free (mi->memos[i].text);
		mi->memocnt--;
		deleted++;

		if (i < mi->memocnt)
		    memmove (mi->memos + i, mi->memos + i + 1, sizeof (Memo) *
			(mi->memocnt - i));

		if (!mi->memocnt)
		{
		    free (mi->memos);
		    mi->memos = NULL;
		}
	    }

	    if (!searching)
		break;
	}

	notice (s_MemoServ, u->nick, MS_READ_DELETED, deleted);

	return;
    }
    else
    {
	int searching = 1;

	while (searching)
	{
	    searching = 0;

	    for (i = 0; i < mi->memocnt; i++)
	    {
		if ((atoi (param) == mi->memos[i].number) ||
		    (!stricmp (param, mi->memos[i].sender)))
		{
		    searching = 1;
		    break;
		}
	    }

	    if (i < mi->memocnt)
	    {
		free (mi->memos[i].text);
		mi->memocnt--;
		deleted++;

		if (i < mi->memocnt)
		    memmove (mi->memos + i, mi->memos + i + 1, sizeof (Memo) *
			(mi->memocnt - i));

		if (!mi->memocnt)
		{
		    free (mi->memos);
		    mi->memos = NULL;
		}
	    }

	    if (!searching)
		break;
	}
    }

    if (deleted)
    {
	if (strlen (param) > 1)
	    notice (s_MemoServ, u->nick, MS_FROM_DELETED, deleted, param);
	else
	    notice (s_MemoServ, u->nick, MS_NUM_DELETED, param);
    }
    else
	notice (s_MemoServ, u->nick, MS_NO_MEMO_NUM, atoi (param));
}

/* Recall a memo */
static void do_recall (User *u)
{
    NickInfo *sni, *dni;
    MemoInfo *mi;
    char *nick = strtok (NULL, " ");
    char *num = strtok (NULL, " ");
    int i, deleted = 0;

    if (!u)
	return;

    if (!nick || !num)
    {
	notice (s_MemoServ, u->nick, RPL_SYNTAX, "RECALL Nickname Number|ALL");
	return;
    }

    /* Make sure they're identified */
    sni = get_nick (u);

    if (!sni)
    {
	notice (s_MemoServ, u->nick, RPL_NEED_IDENT, "RECALL");
	return;
    }

    if (!check_ns_auth (s_MemoServ, u->nick, sni))
	return;

    dni = findnick (nick);

    if (!dni)
    {
	notice (s_MemoServ, u->nick, NS_NOT_REGISTERED, nick);
	return;
    }

    if (!check_ns_auth (s_MemoServ, u->nick, dni))
	return;

    /* Get their memolist */
    mi = &dni->memos;

    /* Check here if they have memos, saves us going through the list..
       This reply says 'has no memos from you', where in fact this would
       only be true if they had no memos at ALL. But they don't need to
       know that :)
     */
    if (!mi->memocnt)
    {
	notice (s_MemoServ, u->nick, MS_NO_MEMOS_OTHER, dni->nick);
	return;
    }

    /* Now go through their memo list, and look for the memo they want. */
    for (i = 0; i < mi->memocnt; i++)
    {
	if (atoi (num) == mi->memos[i].number)
	{
	    /* Found the memo. Did they send it? */
	    if (!stricmp (mi->memos[i].sender, sni->nick))
	    {
		/* Check if it's been read already. */
		if (!mi->memos[i].flags & MF_UNREAD)
		{
		    notice (s_MemoServ, u->nick, MS_ALREADY_READ, dni->nick);
		    return;
		}

		/* They can't recall E-Mailed memos. */
		if (mi->memos[i].flags & MF_EMAILED)
		{
		    notice (s_MemoServ, u->nick, MS_CANT_RECALL_EMAILED,
			dni->nick);
		    return;
		}

		/* They sent it, lets delete it. */
		free (mi->memos[i].text);

		mi->memocnt--;

		if (i < mi->memocnt)
		    memmove (mi->memos + i, mi->memos + i + 1, sizeof (Memo) *
			(mi->memocnt - i));

		if (!mi->memocnt)
		{
		    free (mi->memos);
		    mi->memos = NULL;
		}

		deleted++;
	    }
	    else
	    {
		/* It's not their memo. */
		notice (s_MemoServ, u->nick, MS_NOT_YOURS, num, dni->nick);
		return;
	    }
	}
    }

    if (deleted)
	notice (s_MemoServ, u->nick, MS_MEMO_RECALLED, dni->nick);
    else
	notice (s_MemoServ, u->nick, MS_NO_MEMO_NUM_OTHER, dni->nick, num);
}

/* Display one or more memos */
static void do_read (User *u)
{
    NickInfo *ni, *sni;
    MemoInfo *mi;
    Memo *m;
    char *param = strtok (NULL, " ");
    int i, shown = 0, new = 0, all = 0;

    if (!u)
	return;

    if (!param)
    {
	notice (s_MemoServ, u->nick, RPL_SYNTAX, "READ Number|ALL|NEW");
	return;
    }

    /* Make sure they're identified */
    ni = get_nick (u);

    if (!ni)
    {
	notice (s_MemoServ, u->nick, RPL_NEED_IDENT, "READ");
	return;
    }

    if (!check_ns_auth (s_MemoServ, u->nick, ni))
	return;

    mi = &ni->memos;

    /* Make sure they have some memos to read.. */
    if (!mi->memocnt)
    {
	notice (s_MemoServ, u->nick, MS_NO_MEMOS);
	return;
    }

    /* Find these here */
    if (!stricmp (param, "NEW"))
	new = 1;

    if (!stricmp (param, "ALL"))
	all = 1;

    for (i = 0; i < mi->memocnt; i++)
    {
	if (all || (atoi (param) == mi->memos[i].number) ||
	    (new && mi->memos[i].flags & MF_UNREAD))
	{
	    m = &mi->memos[i];

	    notice (s_MemoServ, u->nick, MS_MEMO_FROM, m->number, m->sender,
		time_ago (m->time));
	    notice (s_MemoServ, u->nick, MS_MEMO, m->text);

	    /* Send a receipt to the sender if they want one */
	    sni = findnick (m->sender);

	    /* Don't want receipts for our own memos */
	    if (sni && stricmp (sni->nick, ni->nick) &&
		sni->flags & NF_RECEIPTS && m->flags & MF_UNREAD)
	    {
		char buf[256], textbuf[26];

		/* They want receipts, so we'll send them one. A receipt
		   is just a normal memo, from MemoServ, saying they read
		   the memo. The user can look at the 'waiting for'
		   if they want to know how long ago they read it.
		 */
		snprintf (textbuf, sizeof (textbuf), "%s", m->text);

		snprintf (buf, sizeof (buf), MS_RECEIPT_MEMO, ni->nick,
		    textbuf, strlen (textbuf) == 25 ? "..." : "");

		send_memo (sni, s_MemoServ, buf);
	    }

	    /* It's read now. */
	    m->flags &= ~(MF_UNREAD | MF_EMAILED);

	    /* How many memos we've shown */
	    shown++;
	}
    }

    /* Tell them we didn't find any matching memos if needed. */
    if (!shown)
    {
	if (!stricmp (param, "NEW"))
	    notice (s_MemoServ, u->nick, MS_NO_NEW);
	else
	    notice (s_MemoServ, u->nick, MS_NO_MEMO_NUM, atoi (param));
    }
}

/* List the memos the user has. */
static void do_list (User *u)
{
    NickInfo *ni, *dni;
    MemoInfo *mi;
    Memo *m;
    char *param = strtok (NULL, " "), textbuf[11];
    int i;

    if (!u)
	return;

    /* Make sure they're identified */
    ni = get_nick (u);

    if (!ni)
    {
	notice (s_MemoServ, u->nick, RPL_NEED_IDENT, "LIST");
	return;
    }

    if (!check_ns_auth (s_MemoServ, u->nick, ni))
	return;

    if (param)
    {
	int found = 0;

	dni = findnick (param);

	if (!dni)
	{
	    notice (s_MemoServ, u->nick, NS_NOT_REGISTERED, param);
	    return;
	}

	if (!check_ns_auth (s_MemoServ, u->nick, dni))
	    return;

	mi = &dni->memos;

	if (!mi->memocnt)
	{
	    notice (s_MemoServ, u->nick, MS_NO_MEMOS_OTHER, dni->nick);
	    return;
	}

	/* We go through their memos here to see if they have any from
	   the querying user. If not, we don't want to show the headers.
	 */
	for (i = 0, m = mi->memos; i < mi->memocnt; i++, m++)
	    if (!stricmp (m->sender, ni->nick))
		found++;

	if (!found)
	{
	    notice (s_MemoServ, u->nick, MS_NO_MEMOS_OTHER, dni->nick);
	    return;
	}

	notice (s_MemoServ, u->nick, MS_LIST_OTHER_HEADER, dni->nick);
	notice (s_MemoServ, u->nick, MS_LIST_OTHER);

	for (i = 0, m = mi->memos; i < mi->memocnt; i++, m++)
	{
	    if (!stricmp (m->sender, ni->nick))
	    {
		snprintf (textbuf, sizeof (textbuf), "%s", m->text);

		notice (s_MemoServ, u->nick, MS_LIST_MEMO_OTHER, m->number,
		    m->flags & MF_UNREAD ? FLAG_UNREAD : m->flags & MF_EMAILED ?
		    FLAG_EMAILED : FLAG_READ, time_ago (m->time), textbuf,
		    strlen (textbuf) == 10 ? "..." : "");
	    }
	}

	return;
    }

    /* We only get here if theres no param. */
    mi = &ni->memos;

    if (!mi->memocnt)
    {
	notice (s_MemoServ, u->nick, MS_NO_MEMOS);
	return;
    }

    notice (s_MemoServ, u->nick, MS_LIST);

    for (i = 0, m = mi->memos; i < mi->memocnt; i++, m++)
    {
	snprintf (textbuf, sizeof (textbuf), "%s", m->text);

	notice (s_MemoServ, u->nick, MS_LIST_MEMO, m->number,
	    m->flags & MF_UNREAD ? FLAG_UNREAD : m->flags & MF_EMAILED ?
	    FLAG_EMAILED : FLAG_READ, m->sender, time_ago (m->time), textbuf,
	    strlen (textbuf) == 10 ? "..." : "");
    }
}

/* Toggle various MemoServ settings */
static void do_set (User *u)
{
    NickInfo *ni;
    char *option = strtok (NULL, " ");
    char *param = strtok (NULL, "");

    if (!u)
	return;

    /* Do we have all needed info? */
    if (!option || !param)
    {
	notice (s_MemoServ, u->nick, RPL_SYNTAX, "SET Option Parameters");
	errmoreinfo (s_MemoServ, u->nick, "SET");
	return;
    }

    /* Make sure they're authed for a nick */
    ni = get_nick (u);

    if (!ni)
    {
	notice (s_NickServ, u->nick, RPL_NEED_IDENT, "SET");
	return;
    }

    if (!check_ns_auth (s_MemoServ, u->nick, ni))
	return;

    /* Toggle RECEIPTS? */
    if (!stricmp (option, "RECEIPTS"))
    {
	if (!stricmp (param, "ON"))
	{
	    if (ni->flags & NF_RECEIPTS)
	    {
		notice (s_MemoServ, u->nick, NS_OPTION_ALREADY, "Receipts", "on");
		return;
	    }

	    ni->flags |= NF_RECEIPTS;
	    notice (s_MemoServ, u->nick, NS_OPTION_NOW, "Receipts", "on");
	    return;
	}

	if (!stricmp (param, "OFF"))
	{
	    if (!(ni->flags & NF_RECEIPTS))
	    {
		notice (s_MemoServ, u->nick, NS_OPTION_ALREADY, "Receipts", "off");
		return;
	    }

	    ni->flags &= ~NF_RECEIPTS;
	    notice (s_MemoServ, u->nick, NS_OPTION_NOW, "Receipts", "off");
	    return;
	}

	else
	{
	    notice (s_MemoServ, u->nick, RPL_UNKNOWN_OPTION, param,
		haveserv_on == TRUE ? "" : "MSG ", s_MemoServ,
		haveserv_on == TRUE ? "" : securitysetting == 1 ? "@" : "",
		haveserv_on == TRUE ? "" : securitysetting == 1 ? me.name : "");
	    return;
	}
    }

    /* Change the memobox limit? */
    if (!stricmp (option, "MEMOBOX"))
    {
	if (atoi (param) > memobox_size || atoi (param) < 1)
	{
	    notice (s_MemoServ, u->nick, MS_LIMIT_SIZE, memobox_size);
	    return;
	}

	ni->memos.memolimit = atoi (param);

	notice (s_MemoServ, u->nick, NS_OPTION_NOW, "MemoBox", param);
	return;
    }

    /* Toggle NOMEMO? */
    if (!stricmp (option, "NOMEMO"))
    {
	if (!stricmp (param, "ON"))
	{
	    if (ni->flags & NF_NOMEMO)
	    {
		notice (s_MemoServ, u->nick, NS_OPTION_ALREADY, "NoMemo", "on");
		return;
	    }

	    ni->flags |= NF_NOMEMO;
	    notice (s_MemoServ, u->nick, NS_OPTION_NOW, "NoMemo", "on");
	    return;
	}

	if (!stricmp (param, "OFF"))
	{
	    if (!(ni->flags & NF_NOMEMO))
	    {
		notice (s_MemoServ, u->nick, NS_OPTION_ALREADY, "NoMemo", "off");
		return;
	    }

	    ni->flags &= ~NF_NOMEMO;
	    notice (s_MemoServ, u->nick, NS_OPTION_NOW, "NoMemo", "off");
	    return;
	}

	else
	{
	    notice (s_MemoServ, u->nick, RPL_UNKNOWN_OPTION, param,
		haveserv_on == TRUE ? "" : "MSG ", s_MemoServ,
		haveserv_on == TRUE ? "" : securitysetting == 1 ? "@" : "",
		haveserv_on == TRUE ? "" : securitysetting == 1 ? me.name : "");
	    return;
	}
    }

    if (memomail_on == TRUE)
    {
	/* Toggle MEMOMAIL? */
	if (!stricmp (option, "MEMOMAIL"))
	{
	    if (!stricmp (param, "ON"))
	    {
		if (ni->flags & NF_MEMOMAIL)
		{
		    notice (s_MemoServ, u->nick, NS_OPTION_ALREADY, "MemoMail", "on");
		    return;
		}

		ni->flags |= NF_MEMOMAIL;
		notice (s_MemoServ, u->nick, NS_OPTION_NOW, "MemoMail", "on");
		return;
	    }

	    if (!stricmp (param, "OFF"))
	    {
		if (!(ni->flags & NF_MEMOMAIL))
		{
		    notice (s_MemoServ, u->nick, NS_OPTION_ALREADY, "MemoMail", "off");
		    return;
		}

		ni->flags &= ~NF_MEMOMAIL;
		notice (s_MemoServ, u->nick, NS_OPTION_NOW, "MemoMail", "off");
		return;
	    }

	    else
	    {
		notice (s_MemoServ, u->nick, RPL_UNKNOWN_OPTION, param,
		    haveserv_on == TRUE ? "" : "MSG ", s_MemoServ,
		    haveserv_on == TRUE ? "" : securitysetting == 1 ? "@" : "",
		    haveserv_on == TRUE ? "" : securitysetting == 1 ?
		    me.name : "");
		return;
	    }
	}
    }

    /* Change FORWARD Nick? */
    if (!stricmp (option, "FORWARD"))
    {
	/* If there's no forward, we'll treat 'none' or 'off' like a nickname. */
	if (ni->forward && (!stricmp (param, "NONE") || !stricmp (param, "OFF")))
	{
	    if (ni->forward)
		free (ni->forward);
	    ni->forward = NULL;
	    notice (s_MemoServ, u->nick, RPL_REMOVED, "Your \2Forward\2 setting");
	    return;
	}
	else
	{
	    NickInfo *tni;
	    char *nick = strtok (param, " ");
	    char *pass = strtok (NULL, " ");

	    if (!nick || !pass)
	    {
		notice (s_MemoServ, u->nick, RPL_SYNTAX,
		    "SET FORWARD Nickname Password");
		errmoreinfo (s_MemoServ, u->nick, "SET FORWARD");
		return;
	    }

	    tni = findnick (nick);

	    if (!tni)
	    {
		notice (s_MemoServ, u->nick, NS_NOT_REGISTERED, param);
		return;
	    }

	    if (!check_ns_auth (s_MemoServ, u->nick, tni))
		return;

	    /* We don't let people set FORWARD for nicks that have FORWARD
	       themselves. This stops 'bugs' where memos don't forward
	       properly. Rather then spend a lot of cpu time finding out
	       where a memo is supposed to go, (and have people waste cpu
	       by setting circular forwarding), we just dont let people
	       forward to nicks with forward. :P
	     */
	    if (tni->forward)
	    {
		notice (s_MemoServ, u->nick, MS_NO_FORWARD, tni->nick,
		    tni->nick);
		return;
	    }

	    if (tni->flags & NF_NOMEMO)
	    {
		notice (s_MemoServ, u->nick, MS_NOMEMO, tni->nick);
		return;
	    }

	    /* Check the pass for the nick to forward to. We don't want people
	       forwarding their memos to a nick that doesn't want them.
	     */
	    if (strcmp (tni->pass, pass))
	    {
		notice (s_MemoServ, u->nick, RPL_WRONG_PASS, tni->nick);
		return;
	    }

	    if (ni->forward)
		free (ni->forward);

	    ni->forward = sstrdup (tni->nick);
	    notice (s_MemoServ, u->nick, RPL_ADDED, "Your \2Forward\2 setting", tni->nick);
	    return;
	}
    }

    /* Don't know what they want .. */
    notice (s_MemoServ, u->nick, RPL_UNKNOWN_OPTION, strupper (option),
	haveserv_on == TRUE ? "" : "MSG ", s_MemoServ,
	haveserv_on == TRUE ? "" : securitysetting == 1 ? "@" : "",
	haveserv_on == TRUE ? "" : securitysetting == 1 ? me.name : "");
}

/* Show them the news. The news file is just a regular text file
   on the disk, so this is very similar to the m_motd function in
   process.c. In fact, I copied it directly over and changed a few
   words. Lazy, eh? Anyhow, they can always read news, but they're
   only told about news if they havent read it. We only support one
   news article at a time, (this should be enough..) so when new news
   is added, an SRA has to reset the NF_NEWS flags to on, so users
   get told about it. But they can always read it.
 */
static void do_news (User *u)
{
    NickInfo *ni, *sni = get_nick (u);
    FILE *f;
    char buf[BUFSIZE];

    f = fopen (NEWSFILE, "r");

    if (!f)
    {
	notice (s_MemoServ, u->nick, MS_NO_NEWS);
	return;
    }

    if (f)
    {
	notice (s_MemoServ, u->nick, MS_NEWS_START, network_name, sni ?
	    zone_time (sni, news_time, 0) : get_time (news_time, 0, 0, "GMT"));
	notice (s_MemoServ, u->nick, " ");

	while (fgets (buf, sizeof (buf), f))
	{
	    buf[strlen (buf) - 1] = 0;

	    notice (s_MemoServ, u->nick, " %s", buf);
	}

	fclose (f);

	notice (s_MemoServ, u->nick, " ");
	notice (s_MemoServ, u->nick, MS_NEWS_END);

	/* They've read the news now, turn the flag off.. */
	ni = get_nick (u);

	/* This should be true :P */
	if (ni)
	    ni->flags &= ~NF_NEWS;
    }
}
