/* MemoServ functions.
*
* (C) 2003-2008 Anope Team
* Contact us at info@anope.org
*
* Please read COPYING and README for further details.
*
* Based on the original code of Epona by Lara.
* Based on the original code of Services by Andy Church. 
* 
* $Id: memoserv.c 1345 2008-01-13 12:54:14Z geniusdex $
*
*/

#include "services.h"
#include "pseudo.h"

/*************************************************************************/
/* *INDENT-OFF* */

NickCore *nclists[1024];
E void moduleAddMemoServCmds(void);
static void new_memo_mail(NickCore *nc, Memo *m);
E void rsend_notify(User *u, Memo *m, const char *chan);

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

void moduleAddMemoServCmds(void) {
    modules_core_init(MemoServCoreNumber, MemoServCoreModules);
}

/*************************************************************************/
/*************************************************************************/
/* *INDENT-ON* */

/**
 * MemoServ initialization.
 * @return void
 */
void ms_init(void)
{
    moduleAddMemoServCmds();
}

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

/**
 * memoserv:  Main MemoServ routine.
 *            Note that the User structure passed to the do_* routines will
 *            always be valid (non-NULL) and will always have a valid
 *            NickInfo pointer in the `ni' field.
 * @param u User Struct
 * @param buf Buffer containing the privmsg
 * @return void
 */
void memoserv(User * u, char *buf)
{
    char *cmd, *s;

    cmd = strtok(buf, " ");
    if (!cmd) {
        return;
    } else if (stricmp(cmd, "\1PING") == 0) {
        if (!(s = strtok(NULL, ""))) {
            s = "";
        }
        anope_cmd_ctcp(s_MemoServ, u->nick, "PING %s", s);
    } else if (skeleton) {
        notice_lang(s_MemoServ, u, SERVICE_OFFLINE, s_MemoServ);
    } else {
        if (!u->na && stricmp(cmd, "HELP") != 0)
            notice_lang(s_MemoServ, u, NICK_NOT_REGISTERED_HELP,
                        s_NickServ);
        else
            mod_run_cmd(s_MemoServ, u, MEMOSERV, cmd);
    }
}

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

/**
 * check_memos:  See if the given user has any unread memos, and send a
 *               NOTICE to that user if so (and if the appropriate flag is
 *               set).
 * @param u User Struct
 * @return void
 */
void check_memos(User * u)
{
    NickCore *nc;
    int i, newcnt = 0;

    if (!u) {
        if (debug) {
            alog("debug: check_memos called with NULL values");
        }
        return;
    }

    if (!(nc = (u->na ? u->na->nc : NULL)) || !nick_recognized(u) ||
        !(nc->flags & NI_MEMO_SIGNON)) {
        return;
    }

    for (i = 0; i < nc->memos.memocount; i++) {
        if (nc->memos.memos[i].flags & MF_UNREAD)
            newcnt++;
    }
    if (newcnt > 0) {
        notice_lang(s_MemoServ, u,
                    newcnt == 1 ? MEMO_HAVE_NEW_MEMO : MEMO_HAVE_NEW_MEMOS,
                    newcnt);
        if (newcnt == 1 && (nc->memos.memos[i - 1].flags & MF_UNREAD)) {
            notice_lang(s_MemoServ, u, MEMO_TYPE_READ_LAST, s_MemoServ);
        } else if (newcnt == 1) {
            for (i = 0; i < nc->memos.memocount; i++) {
                if (nc->memos.memos[i].flags & MF_UNREAD)
                    break;
            }
            notice_lang(s_MemoServ, u, MEMO_TYPE_READ_NUM, s_MemoServ,
                        nc->memos.memos[i].number);
        } else {
            notice_lang(s_MemoServ, u, MEMO_TYPE_LIST_NEW, s_MemoServ);
        }
    }
    if (nc->memos.memomax > 0 && nc->memos.memocount >= nc->memos.memomax) {
        if (nc->memos.memocount > nc->memos.memomax)
            notice_lang(s_MemoServ, u, MEMO_OVER_LIMIT, nc->memos.memomax);
        else
            notice_lang(s_MemoServ, u, MEMO_AT_LIMIT, nc->memos.memomax);
    }
}

/*************************************************************************/
/*********************** MemoServ private routines ***********************/
/*************************************************************************/

/**
 * Return the MemoInfo corresponding to the given nick or channel name.
 * @param name Name to check
 * @param ischan - the result its a channel will be stored in here
 * @param isforbid - the result if its forbidden will be stored in here
 * @return `ischan' 1 if the name was a channel name, else 0.
 * @return `isforbid' 1 if the name is forbidden, else 0.
 */
MemoInfo *getmemoinfo(const char *name, int *ischan, int *isforbid)
{
    if (*name == '#') {
        ChannelInfo *ci;
        if (ischan)
            *ischan = 1;
        ci = cs_findchan(name);
        if (ci) {
            if (!(ci->flags & CI_VERBOTEN)) {
                *isforbid = 0;
                return &ci->memos;
            } else {
                *isforbid = 1;
                return NULL;
            }
        } else {
            *isforbid = 0;
            return NULL;
        }
    } else {
        NickAlias *na;
        if (ischan)
            *ischan = 0;
        na = findnick(name);
        if (na) {
            if (!(na->status & NS_VERBOTEN)) {
                *isforbid = 0;
                return &na->nc->memos;
            } else {
                *isforbid = 1;
                return NULL;
            }
        } else {
            *isforbid = 0;
            return NULL;
        }
    }
}

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

/**
 * Split from do_send, this way we can easily send a memo from any point
 * @param u User Struct
 * @param name Target of the memo
 * @param text Memo Text
 * @param z type see info
 *	0 - reply to user
 *	1 - silent
 *	2 - silent with no delay timer
 *	3 - reply to user and request read receipt
 * @return void
 */
void memo_send(User * u, char *name, char *text, int z)
{
    int ischan;
    int isforbid;
    Memo *m;
    MemoInfo *mi;
    time_t now = time(NULL);
    char *source = u->na->nc->display;
    int is_servoper = is_services_oper(u);

    if (readonly) {
        notice_lang(s_MemoServ, u, MEMO_SEND_DISABLED);
    } else if (checkDefCon(DEFCON_NO_NEW_MEMOS)) {
        notice_lang(s_MemoServ, u, OPER_DEFCON_DENIED);
        return;
    } else if (!text) {
        if (z == 0)
            syntax_error(s_MemoServ, u, "SEND", MEMO_SEND_SYNTAX);

        if (z == 3)
            syntax_error(s_MemoServ, u, "RSEND", MEMO_RSEND_SYNTAX);

    } else if (!nick_recognized(u)) {
        if (z == 0 || z == 3)
            notice_lang(s_MemoServ, u, NICK_IDENTIFY_REQUIRED, s_NickServ);

    } else if (!(mi = getmemoinfo(name, &ischan, &isforbid))) {
        if (z == 0 || z == 3) {
            if (isforbid) {
                notice_lang(s_MemoServ, u,
                            ischan ? CHAN_X_FORBIDDEN :
                            NICK_X_FORBIDDEN, name);
            } else {
                notice_lang(s_MemoServ, u,
                            ischan ? CHAN_X_NOT_REGISTERED :
                            NICK_X_NOT_REGISTERED, name);
            }
        }
    } else if (z != 2 && MSSendDelay > 0 &&
               u && u->lastmemosend + MSSendDelay > now && !is_servoper) {
        u->lastmemosend = now;
        if (z == 0)
            notice_lang(s_MemoServ, u, MEMO_SEND_PLEASE_WAIT, MSSendDelay);

        if (z == 3)
            notice_lang(s_MemoServ, u, MEMO_RSEND_PLEASE_WAIT,
                        MSSendDelay);

    } else if (mi->memomax == 0 && !is_servoper) {
        if (z == 0 || z == 3)
            notice_lang(s_MemoServ, u, MEMO_X_GETS_NO_MEMOS, name);

    } else if (mi->memomax > 0 && mi->memocount >= mi->memomax
               && !is_servoper) {
        if (z == 0 || z == 3)
            notice_lang(s_MemoServ, u, MEMO_X_HAS_TOO_MANY_MEMOS, name);

    } else {
        u->lastmemosend = now;
        mi->memocount++;
        mi->memos = srealloc(mi->memos, sizeof(Memo) * mi->memocount);
        m = &mi->memos[mi->memocount - 1];
        strscpy(m->sender, source, NICKMAX);
        m->moduleData = NULL;
        if (mi->memocount > 1) {
            m->number = m[-1].number + 1;
            if (m->number < 1) {
                int i;
                for (i = 0; i < mi->memocount; i++) {
                    mi->memos[i].number = i + 1;
                }
            }
        } else {
            m->number = 1;
        }
        m->time = time(NULL);
        m->text = sstrdup(text);
        m->flags = MF_UNREAD;
        /* Set notify sent flag - DrStein */
        if (z == 2) {
            m->flags |= MF_NOTIFYS;
        }
        /* Set receipt request flag */
        if (z == 3)
            m->flags |= MF_RECEIPT;
        if (z == 0 || z == 3)
            notice_lang(s_MemoServ, u, MEMO_SENT, name);
        if (!ischan) {
            NickAlias *na;
            NickCore *nc = (findnick(name))->nc;

            if (MSNotifyAll) {
                if ((nc->flags & NI_MEMO_RECEIVE)
                    && get_ignore(name) == NULL) {
                    int i;

                    for (i = 0; i < nc->aliases.count; i++) {
                        na = nc->aliases.list[i];
                        if (na->u && nick_identified(na->u))
                            notice_lang(s_MemoServ, na->u,
                                        MEMO_NEW_MEMO_ARRIVED, source,
                                        s_MemoServ, m->number);
                    }
                } else {
                    if ((u = finduser(name)) && nick_identified(u)
                        && (nc->flags & NI_MEMO_RECEIVE))
                        notice_lang(s_MemoServ, u, MEMO_NEW_MEMO_ARRIVED,
                                    source, s_MemoServ, m->number);
                }               /* if (flags & MEMO_RECEIVE) */
            }
            /* if (MSNotifyAll) */
            /* let's get out the mail if set in the nickcore - certus */
            if (nc->flags & NI_MEMO_MAIL)
                new_memo_mail(nc, m);
        } else {
            struct c_userlist *cu, *next;
            Channel *c;

            if (MSNotifyAll && (c = findchan(name))) {
                for (cu = c->users; cu; cu = next) {
                    next = cu->next;
                    if (check_access(cu->user, c->ci, CA_MEMO)) {
                        if (cu->user->na
                            && (cu->user->na->nc->flags & NI_MEMO_RECEIVE)
                            && get_ignore(cu->user->nick) == NULL) {
                            notice_lang(s_MemoServ, cu->user,
                                        MEMO_NEW_X_MEMO_ARRIVED,
                                        c->ci->name, s_MemoServ,
                                        c->ci->name, m->number);
                        }
                    }
                }
            }                   /* MSNotifyAll */
        }                       /* if (!ischan) */
    }                           /* if command is valid */
}

/*************************************************************************/
/**
 * Delete a memo by number.
 * @param mi Memoinfo struct
 * @param num Memo number to delete
 * @return int 1 if the memo was found, else 0.
 */
int delmemo(MemoInfo * mi, int num)
{
    int i;

    for (i = 0; i < mi->memocount; i++) {
        if (mi->memos[i].number == num)
            break;
    }
    if (i < mi->memocount) {
        moduleCleanStruct(&mi->memos[i].moduleData);
        free(mi->memos[i].text);        /* Deallocate memo text memory */
        mi->memocount--;        /* One less memo now */
        if (i < mi->memocount)  /* Move remaining memos down a slot */
            memmove(mi->memos + i, mi->memos + i + 1,
                    sizeof(Memo) * (mi->memocount - i));
        if (mi->memocount == 0) {       /* If no more memos, free array */
            free(mi->memos);
            mi->memos = NULL;
        }
        return 1;
    } else {
        return 0;
    }
}

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

static void new_memo_mail(NickCore * nc, Memo * m)
{
    MailInfo *mail = NULL;

    if (!nc || !m)
        return;

    mail = MailMemoBegin(nc);
    if (!mail) {
        return;
    }
    fprintf(mail->pipe, getstring2(NULL, MEMO_MAIL_TEXT1), nc->display);
    fprintf(mail->pipe, "\n");
    fprintf(mail->pipe, getstring2(NULL, MEMO_MAIL_TEXT2), m->sender,
            m->number);
    fprintf(mail->pipe, "\n\n");
    fprintf(mail->pipe, getstring2(NULL, MEMO_MAIL_TEXT3));
    fprintf(mail->pipe, "\n\n");
    fprintf(mail->pipe, "%s", m->text);
    fprintf(mail->pipe, "\n");
    MailEnd(mail);
    return;
}


/*************************************************************************/
/* Send receipt notification to sender. */

void rsend_notify(User * u, Memo * m, const char *chan)
{
    NickAlias *na;
    NickCore *nc;
    char text[256];
    const char *fmt;

    /* Only send receipt if memos are allowed */
    if ((!readonly) && (!checkDefCon(DEFCON_NO_NEW_MEMOS))) {

        /* Get nick alias for sender */
        na = findnick(m->sender);

        if (!na) {
            return;
        }

        /* Get nick core for sender */
        nc = na->nc;

        if (!nc) {
            return;
        }

        /* Text of the memo varies if the recepient was a
           nick or channel */
        if (chan) {
            fmt = getstring(na, MEMO_RSEND_CHAN_MEMO_TEXT);
            sprintf(text, fmt, chan);
        } else {
            fmt = getstring(na, MEMO_RSEND_NICK_MEMO_TEXT);
            sprintf(text, fmt);
        }

        /* Send notification */
        memo_send(u, m->sender, text, 2);

        /* Notify recepient of the memo that a notification has
           been sent to the sender */
        notice_lang(s_MemoServ, u, MEMO_RSEND_USER_NOTIFICATION,
                    nc->display);
    }

    /* Remove receipt flag from the original memo */
    m->flags &= ~MF_RECEIPT;

    return;
}
