/*
 * this file is a part of yabbs - yet another bulletin board system.
 * Copyright (C) 1993, 1994 Alex Wetmore.  
 * email: alex@phred.org
 * address: 6 rech ave
 *          oreland pa 19075
 *
 * 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 2 of the License, 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, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 * yalk.c - part of the yabbs bulletin board package by alex wetmore
 *          see bbs.c for information on the system and copying.
 */

#include "bbsdefs.h"

/* possible return codes from talk commands                             */
#define TRC_FAILED 0                    /* command failed               */
#define TRC_OKAY 1                      /* command ran okay             */
#define TRC_QUIT 2                      /* quit command run             */

#define TST_BADUSAGE "*** invalid usage; type /help for help\n"

functable_t talkfuncs[] = {
    { 
        "help", 
        NULL,
        "List all talk commands.", 
        t_help 
    }, { 
        "?", 
        NULL,
        NULL, 
        t_help 
    }, { 
        "who", 
        NULL,
        "List all logged in users on yabbs.", 
        t_who
    }, { 
        "channels", 
        NULL,
        "List all talk channels and users.", 
        t_channels
    }, { 
        "join", 
        "<channel name>",
        "Join (or create) new channel.  Surround the channel name by *'s to create a hidden channel.", 
        t_join
    }, { 
        "leave", 
        "<channel name>",
        "Leave a channel.", 
        t_leave
    }, { 
        "quit", 
        NULL,
        "Exit from talk.", 
        t_quit
    }, { 
        "hide", 
        NULL,
        "Hide the current channel (private).", 
        t_hide
    }, { 
        "show",
        NULL,
        "Make the current channel visable (public).", 
        t_show
    }, { 
        "page",
        "<user>",
        "Page a user to talk with them.", 
        t_page
    }, { 
        "msg",
        "<user|:channel> <message>",
        "Send a private message to a user or a channel.  to send a message to a channel precede its name with a colon (:).", 
        t_msg
    }, { 
        "ignore",
        "[<user>]",
        "Ignore or unignore a user or list all ignored users.", 
        t_managelist
    }, { 
        "volume",
        "[<user>]",
        "Lowercase text from a user or list lowercased users.", 
        t_managelist
    }, { 
        "notify",
        "[<user>]",
        "Print a notification message when a user logs on or list the notify list.", 
        t_managelist
    }, { 
        "info",
        "<user>",
        "Read a users plan file.", 
        t_info
    }, { 
        "clear",
        NULL,
        "Clear the screen.", 
        t_clear
    }, { 
        "alias",
        "[<alias>] [<command>]",
        "Add a new alias, delete an alias or list current aliases.", 
        t_manageenv
    }, {
        "set",
        "[<variable> <value>]",
        "Add environment variables or list the current environment",
        t_manageenv
    }, {
        "elite",
        NULL,
        NULL,
        t_elite
    }, {
        "yeck",
        NULL,
        NULL,
        t_yeck
    }, { 
        NULL, 
        NULL, 
        NULL, 
        NULL 
    }
};

/*
 * thanks to dmonger, pw1r@andrew.cmu.edu, for the volume hacks
 */

#define MAXCHANNELNAMELEN 10
#define MAXJOINS 3

/*
 * ---- helper functions ----
 */

char *fixcname(char *str) {
    int i, j, l;

    l = strlen(str);
    for (i = j = 0; i < l; i++) {
        if ((str[i] != ' ') && (str[i] != '*')) str[j++] = lower(str[i]);
    }
    str[j] = 0;

    return str;
}

void setcurchan(char *cname) {
    char buf[255];

    strcpy(curchan, cname);
    sprintf(buf, "*** current channel is %s\n", curchan);
    sputs(buf);
}

/*
 * ---- yabbslib callbacks ---
 */

/*
 * a line from a channel listing.  cname is channel name, users is a comma
 * delimited list of users.
 */
void cli_channelline(char *cname, char *users, void *null) {
    char buf[255], newusers[300];
    int i, l, j;
    
    for (i = j = 0, l = strlen(users); i < l; i++) {
        if (users[i] == ',') { newusers[j++] = ','; newusers[j++] = ' '; }
        else newusers[j++] = users[i];
    }
    newusers[j] = 0;
    if ((!getfromenv("RealChannelNames", buf)) && 
        (strncmp(cname, "*priv", 5) == 0))
        sprintf(buf, "%12s : %s\n", "*private*", newusers);
    else
        sprintf(buf, "%12s : %s\n", cname, newusers);
    sputs_process = FALSE; sputs_wr(buf); sputs_process = TRUE;
}

/*
 * an announce message from the server.  antype tells what kind of announce it
 * is, the two arguments change depending on the type.
 */
void cli_announce(int antype, char *arg1, char *arg2) {
    char buf[255];
    int base;

    if ((antype == AN_LOGIN) || (antype == AN_LOGOUT)) {
        if (!getfromenv("NotifyAll", buf)) {
            if (!inlist(notify, arg1, stringptrcomp)) return;
        }
    }

    sputs("\r"); 
    clearline();
    switch (antype) {
        case AN_LOGIN:  
            sprintf(buf, "*** login by \016%s\017\n", arg1); 
            break;
        case AN_LOGOUT: 
            sprintf(buf, "*** logout by \016%s\017\n", arg1); 
            break;
        case AN_NEWMSG: 
            base = atoi(arg1);

            if (base == 0) 
                sprintf(buf, "*** you have new yabbs email\n"); 
            else if (getfromenv("NotifyOnNewMessages", buf)) 
                sprintf(buf, "*** new message on base %c\n", BASEOF + base);
            break;
        default: 
            return;
    }
    sputs(buf);
    line = 1;
    redraw = TRUE;
}

/*
 * incoming talk commands from the server.
 */
void cli_talkline(char *channel, char *name, char *msg, int type) {
    char buf[1024];
    int i, l;

    strcpy(buf, name);

    /* check to see if we should be ignoring this person                */
    if (isignored(buf)) return;

    /* check to see if we are lowercasing this persons text             */
    if (isnocaps(buf)) 
        for (i = 0, l = strlen(msg); i < l; i++) msg[i] = lower(msg[i]);

    if (intalk) { sputs("\r"); clearline(); }
    switch (type) {
        case TL_PUBLIC:
            if (strcmp(channel, curchan) == 0) 
                sprintf(buf, "<\016%s\017> %s\n", name, msg); 
            else
                sprintf(buf, "<\016%s:%s\017> %s\n", name, channel, msg); 
            break;
        case TL_PRIVATE:
            sprintf(buf, "*\016%s\017* %s\n", name, msg); 
            break;
        case TL_ACTION:
            if (strcmp(channel, curchan) == 0) 
                sprintf(buf, "\016%s\017 %s\n", name, msg); 
            else
                sprintf(buf, "\016%s:%s\017 %s\n", name, channel, msg); 
            break;
        case TL_HIDE: 
            sprintf(buf, "*** \016%s\017 made hidden by %s\n", channel, name); 
            break;
        case TL_SHOW: 
            sprintf(buf, "*** \016%s\017 made visible by %s\n", channel, name); 
            break;
        case TL_JOIN: 
            sprintf(buf,    
                "*** \016%s\017 has joined channel %s\n", name, channel); 
            break;
        case TL_LEAVE: 
            sprintf(buf, 
                "*** \016%s\017 has left channel %s\n", name, channel); 
            break;
    }
    if (intalk) sputs_wr(buf);
    line = 1;
    redraw = TRUE;
}

/*
 * incoming page from the server.
 */
void cli_page(char *from) {
    char buf[255];

    if (isignored(from)) return;

    sprintf(buf, "\nPage Request: [%s] would like to talk to you\n\n", from);
    sputs(buf);
    redraw = TRUE;

    if (getfromenv("BeepOnPage", buf)) {
        sputs("\007");
    }
}

/*
 * compare two env entries
 */
int envptrcomp(void *p1, void *p2) {
    char lp1[256];
    char lp2[256];

    strncpy(lp1, ((env_t *) p1)->name, 256); lp1[255] = 0; strlwr(lp1);
    strncpy(lp2, ((env_t *) p2)->name, 256); lp2[255] = 0; strlwr(lp2);
    return (strcmp(lp1, lp2) == 0);
}

/*
 * ---- client commands ----
 */

/* 
 * list online users
 */
int t_who(char *st, int argc, char **argv) {
    who();
    return TRC_OKAY;
}

/*
 * list channels
 */
int t_channels(char *st, int argc, char **argv) {
    char buf[255];
    
    sprintf(buf, "\016%12s : users\017\n", "name");
    sputs_wr(buf);
    bbs_channels(NULL);
    return TRC_OKAY;
}

/*
 * join a channel
 */
int t_join(char *st, int argc, char **argv) {
    char    *name = argv[1], *cname;
    int     hide = FALSE;

    if ((name[0] == '*') && (name[strlen(name) - 1] == '*')) hide = TRUE;
    fixcname(name);
    
    if (argc == 2) {
        cname = (char *) malloc(sizeof(char) * (strlen(name) + 1));
        strcpy(cname, name);
        if (!inlist(channels, (void *) cname, stringptrcomp)) {
            if (numchan == MAXJOINS) {
                sputs("*** you can only join 3 channels.\n");
                return TRC_OKAY;
            } else {
                channels = addtolist(channels, (void *) cname);
                numchan++;
            }
        }
        setcurchan(cname);
        if (hide) bbs_talkjoinpriv(cname);
        else bbs_talkjoin(cname);
    } else sputs(TST_BADUSAGE);
    return TRC_OKAY;
}

/*
 * leave a channel
 */
int t_leave(char *st, int argc, char **argv) {
    char    *name = argv[1];

    fixcname(name);

    if (argc == 2) {
        numchan--;
        bbs_talkleave(name);
        channels = deletefromlist(channels, (void *) name, stringptrcomp);
        if (channels != NULL) setcurchan((char *) channels->ptr);
        else sputs("*** you are in limbo, use /j to join a channel\n");
    } else sputs(TST_BADUSAGE);
    return TRC_OKAY;
}

/*
 * show current channels
 */
int t_show(char *st, int argc, char **argv) {
    bbs_talkshow(curchan);
    return TRC_OKAY;
}

/*
 * hide the current channel
 */
int t_hide(char *st, int argc, char **argv) {
    bbs_talkhide(curchan);
    return TRC_OKAY;
}

/*
 * quit talk
 */
int t_quit(char *st, int argc, char **argv) {
    return TRC_QUIT;
}

/*
 * page a user
 */
int t_page(char *st, int argc, char **argv) {
    char    buf[1024], *name = argv[1];

    bbs_page(name);
    sprintf(buf, "*** sent page to \016%s\017\n", name);
    sputs(buf);
    return TRC_OKAY;
}

/*
 * send a private message to a user
 */
int t_msg(char *st, int argc, char **argv) {
    int i;
    char *to = argv[1], outgoing[255], toscreen[255];

    if (argc >= 3) {
        /* build the outgoing message                                       */
        if (elite) strcpy(outgoing, "d00d, ");
        else if (yeck) strcpy(outgoing, "dude, ");
        else *outgoing = 0;
        for (i = 2; i < argc; i++) {
            strcat(outgoing, argv[i]);
            strcat(outgoing, " ");
        }
        if (yeck) strcat(outgoing, ", dude!");

        if (*to == ':') bbs_talkpublic(&(to[1]), outgoing);
        else bbs_talkprivate(to, outgoing);
    
        /* build the message to go to the users screen                      */
        sprintf(toscreen, "=> *\016%s\017* %s\n", to, outgoing);
        sputs_wr(toscreen);
    } else sputs(TST_BADUSAGE); 
    return TRC_OKAY;
}

/*
 * manage a user list.  which list (volume or ignore) is chosen based on
 * argv[0].
 */
int t_managelist(char *st, int argc, char **argv) {
    char    buf[1024], *name = argv[1], *namep;
    char    listname[20];
    llist   p, *l;

    if (argv[0][0] == 'i') {
        strcpy(listname, "ignore");
        l = &ignores;
    } else if (argv[0][0] == 'n') {
        strcpy(listname, "notify");
        l = &notify;
    } else if (argv[0][0] == 'l') {
        strcpy(listname, "locked");
        l = &locked;
    } else {
        strcpy(listname, "volume");
        l = &nocaps;
    }

    if (argc == 2) {
        if (inlist(*l, name, stringptrcomp)) {
            *l = deletefromlist(*l, (void *) name, stringptrcomp);
            sprintf(buf, "*** removed <Bold>%s</Bold> from %s list<nl>", 
                name, listname);
        } else {
            namep = (char *) malloc(sizeof(char) * (strlen(name) + 1));
            strcpy(namep, name);
            *l = addtolist(*l, (void *) namep);
            sprintf(buf, "*** added <Bold>%s</Bold> to %s list<nl>", 
                name, listname);
        }
        if (!inrc) sputs_mime(buf);
    } else if (argc == 1) {
        char buf[80];

        p = *l;
        sprintf(buf, "*** the %s list contains\n", listname);
        sputs(buf);
        while (p != NULL) {
            sprintf(buf, "<Bold>%s</Bold><nl>", (char *) p->ptr);
            sputs_mime(buf);
            p = p->next;
        }
        sputs("*** end of list\n");
    } else {
        if (inrc) {
            char buf[80];

            sprintf("*** invalid %s command usage; should be %s <user>\n",
                listname, listname);
            sputs(buf);
        } else sputs(TST_BADUSAGE);
    }
    return TRC_OKAY;
}

/*
 * read a users planfile
 */
int t_info(char *st, int argc, char **argv) {
    char    buf[1024], *name = argv[1];

    if (argc == 2) {
        if (!finduser(name)) {
            sprintf(buf, "*** no such user %s\n", name);
            sputs(buf);
        } else {
            sprintf(buf, "*** ---- plan file for %s ----\n", name);
            sputs(buf);
            bbs_getplanfile(name, NULL);
            sputs("*** ---- end of plan ----\n");
        }
    } else sputs(TST_BADUSAGE);
    return TRC_OKAY;
}

/*
 * clear the screen
 */
int t_clear(char *st, int argc, char **argv) { 
    bbs_clrscr();
    return TRC_OKAY;
}

/* 
 * list all commands.  arguments are ignored.
 */
int t_help(char *st, int argc, char **argv) {
    int i;
    char buf[255];

    sputs("---- talk help ----\n");
    for (i = 0; (talkfuncs[i].commandst != NULL); i++) {
        if (talkfuncs[i].helpst != NULL) {
            if (talkfuncs[i].commandst[0] == ':') {
                sprintf(buf, "\016%s%s\017 - %s\n", talkfuncs[i].commandst,
                    talkfuncs[i].paramst, talkfuncs[i].helpst);
            } else if (talkfuncs[i].paramst == NULL) {
                sprintf(buf, "\016/%s\017 - %s\n", talkfuncs[i].commandst,
                    talkfuncs[i].helpst);
            } else {
                sprintf(buf, "\016/%s %s\017 - %s\n", talkfuncs[i].commandst,
                    talkfuncs[i].paramst, talkfuncs[i].helpst);
            }
            sputs_wr(buf);
        }
    }
    sputs_mime(H_TALK);
    sputs("-------------------\n");
    return TRC_OKAY;
}

/*
 * go into yeck mode
 */
int t_yeck(char *st, int argc, char **argv) {
    if (yeck) sputs("*** dude, you suck!\n");
    else sputs("*** welcome to yeck mode, dude!\n");
    yeck = !yeck;
    return TRC_OKAY;
}

/* 
 * go into elite mode
 */
int t_elite(char *st, int argc, char **argv) {
    if (elite) sputs("*** lam3r!\n");
    else sputs("*** w3lc0me t0 th3 rank$ 0f th3 |<-rad 3l33t, d00d!\n");
    elite = !elite; 
    return TRC_OKAY;
}

/*
 * manage the list of talk aliases
 */
int t_manageenv(char *st, int argc, char **argv) {
    char buf[1024], listname[20];
    llist *l;

    if (argv[0][0] == 'a') {
        strcpy(listname, "talk alias");
        l = &aliases;
    } else {
        strcpy(listname, "environment");
        l = &env;

        /* make sure this variable isn't locked */
        if (argc > 1) {
            strncpy(buf, argv[1], 1024);
            if (inlist(locked, buf, stringptrcomp)) {
                sputs("*** that is a locked variable and can not be changed\n");
                return TRUE;
            }
        }
    }

    if (argc == 1) {
        /* list all entries */  
        llist p;

        sprintf(buf, "*** %s list:\n", listname);
        sputs(buf);
        p = *l;
        while (p != NULL) {
            env_t *e = (env_t *) p->ptr;
            sprintf(buf, "%s = %s\n", e->name, e->value);
            sputs(buf);
            p = p->next;
        }
        sprintf(buf, "*** end of %s list\n", listname);
        sputs(buf);
    } else if (argc == 2) {
        /* remove an entry */
        env_t *e;

        e = (env_t *) malloc(sizeof(env_t));
        e->name = (char *) malloc(sizeof(char) * (strlen(argv[1]) + 1));
        strcpy(e->name, argv[1]);

        if (inlist(*l, e, envptrcomp)) {
            *l = deletefromlist(*l, e, envptrcomp);
            sprintf(buf, "*** removed entry %s from %s list\n", argv[1], 
                listname);

        } else {
            sprintf(buf, "*** alias %s not found in %s list\n", argv[1],
                listname);
        }
        free(e->name);
        free(e);
        if (!inrc) sputs(buf);
    } else {
        /* add an entry */
        env_t *e;
        int i, len;

        e = (env_t *) malloc(sizeof(env_t));
        e->name = (char *) malloc(sizeof(char) * (strlen(argv[1]) + 1));
        strcpy(e->name, argv[1]);

        for (len = 0, i = 2; i < argc; len += (strlen(argv[i]) + 1), i++);
        e->value = (char *) malloc(sizeof(char) * (len + 1));
        e->value[0] = 0;
        for (i = 2; i < argc; i++) {
            strcat(e->value, argv[i]);
            if (i != (argc - 1)) strcat(e->value, " ");
        }

        if (inlist(*l, e, envptrcomp))
            *l = deletefromlist(*l, e, envptrcomp);

        *l = addtolist(*l, e);

        sprintf(buf, "*** added %s = %s to %s\n", e->name, e->value,
            listname);
        if (!inrc) sputs(buf);
    }
    return TRC_OKAY;
}

/*
 * send a message to all of the users in the talk section
 */
void dowrite(char *tmsg) {
    char buf[1024];

    if (elite) {
        sprintf(buf, "d00d, %s", tmsg);
    } else if (yeck) {
        sprintf(buf, "dude, %s, dude!", tmsg);
    } else {
        sprintf(buf, "%s", tmsg);
    }
    bbs_talkpublic(curchan, buf);
}

/*
 * talk with other users (just a simple chat section).
 */
void dotalk(void) {
    char    tmsg[256], buf[11];
    int     donetalk;

    bbs_announce(AN_LOGIN);
    bbs_announce(AN_LOGOUT);
    bbs_announce(AN_NEWMSG);

    channels = NULL;
    intalk = TRUE;
    numchan = 0;
    sputs("welcome to yalk : /help = help : /quit = quit\n\n");
    t_channels(NULL, 0, NULL);
    sputs("\nWhat channel would you like to join? [yabbs] ");
    sgets(buf, 10);
    if (*buf == 0) strcpy(buf, "yabbs");
    sprintf(tmsg, "join %s", buf);
    parseandrun(talkfuncs, NULL, tmsg, tmsg);
    donetalk = FALSE;
    do {
        /* read a command from the user                                 */
        sgets(tmsg, 200); 
        /* move up one line, clear it, and return the to front of it    */
        up(); sputs("\r"); clearline(); sputs("\r");

        /* handle the command                                           */
        if (tmsg[0] == '/') {
            int rc;
            char errorstring[256];

            rc = parseandrun(talkfuncs, aliases, &(tmsg[1]), errorstring);
            switch (rc) {
                case TRC_OKAY: break;
                case TRC_QUIT: donetalk = TRUE; break;
                case TRC_FAILED: 
                    sputs("*** "); sputs(errorstring); 
                    sputs(".  type /help for help\n");
                    break;
                default: ASSERT(FALSE);
            }
        } else if ((tmsg[0] == ':') && (strlen(tmsg) > 3)) {
            bbs_talkaction(curchan, &(tmsg[1]));
        } else if (strlen(tmsg) != 0) {
            dowrite(tmsg);  
        }
    } while (!donetalk);

    intalk = FALSE;
    numchan = 0;
    while (channels != NULL) {
        bbs_talkleave((char *) channels->ptr);
        channels = deletefromlist(channels, channels->ptr, stringptrcomp);
    }
    bbs_unannounce(AN_LOGIN);
    bbs_unannounce(AN_LOGOUT);
    bbs_unannounce(AN_NEWMSG);
}
