summaryrefslogblamecommitdiffstats
path: root/mbbsd/chat.c
blob: 9ce7d6c39f46076032b37adaf59d2e3ca3b14b88 (plain) (tree)
1
2
3
4
5
6
7
8
9
          
                
 
                 


                    

                             
                     
           
                              
 
                      
                                            
               
                                      

                   

                                           



               

             


                                   
           
                                   
 
                                                          








                          
           

              
                           



                                                                            
                                
 
                         
               

                 
                   

 
          
                                  


                                
 
                                                        


                                             




                  
          
                                                                                       
 

                           
 

                                                            
                  
                      
 
                   
                   
                               
                                                             

                  

                              
                     
                                 

                      
                                                       
                                     

                      
                                                              



                           
                                                                                                       
                                           







                                
                

                                                               
          
                         


             
           
                                           


                                
                                                         


                       
           


                             






                                                           

                                                                                  
            
                                                      
                                                  







                                                          

                                               
                                                 


                                                                    


     
           
                                  

                                
 

                                                        


                          
           
                                   

                                
 




                                                 
                                                          
                                                                          


                          
           



                         
                          
                     
 
                      

                                                                                          
                                               

                           

                                                                                      
                                              
                                                     
                           
 
                                  
                                       
                                                                            

                                                                 
 
                                                 
                                     
                     
                                                                            
                                              








                                   
                               
                                                             
                                                                         
                               
 
                                             




                          


                
          
                                                
 
                                               
                                      



                     
          
                                      

                      
 
                      

                 

                                                          






                                        





                            
                    

              
          
                                                                   
 
                                             

























                                                                                 
 














                                          




                                                                        
                             
                                 
                               
                                  
                                  



                                        



                       
                                
 
   
            
 



                                                          

                                   
                        
                                   

                                    
 
                                    



                                         
 


                                








                                                              
 
                   
                                                            
     
                                                                     

                                                  



                                         
                                                                  
 

                                 
                               
                                                                

                             
                  
     

               
                                                                               
                      
                                                          
                         
          
                                               
           
                                                     
                                                    
                              

                                          
                              
                     
         
                                          
                  
                                                   
                                         
                                                    
                                     
                                                  
                                              
 




                             

                    
 
                   
 

                            
                                                                
 

                 
 
                       
                        

                                   


                         
                                    
 

                                       
 







                                           
                               
 
                      



                                      
                         

                              
                           
         
                             
                                    
 

                                                                      
 
                                                    
                      
                  
 







                                             


                                         
 




                                        
 






















                                                                           
             
         







                                              









                                                       
     
 


                                                



                               


                                                                  
                                          
                          




                                                     
     

             
/* $Id$ */
#include "bbs.h"

#ifndef DBCSAWARE
#define dbcs_off (1)
#endif

#define STOP_LINE (t_lines-3)
static int      chatline;
static FILE    *flog;
static void
printchatline(const char *str)
{
    move(chatline, 0);
    if (*str == '>' && !PERM_HIDE(currutmp))
    return;
    else if (chatline < STOP_LINE - 1)
    chatline++;
    else {
    region_scroll_up(2, STOP_LINE - 2);
    move(STOP_LINE - 2, 0);
    }
    outs(str);
    outc('\n');
    outs("→");

    if (flog)
    fprintf(flog, "%s\n", str);
}

static void
chat_clear(char *unused GCC_UNUSED)
{
    for (chatline = 2; chatline < STOP_LINE; chatline++) {
    move(chatline, 0);
    clrtoeol();
    }
    move(b_lines, 0);
    clrtoeol();
    move(chatline = 2, 0);
    outs("→");
}

static void
print_footer()
{
    vs_footer("【談天室】",
        " (PgUp/PgDn)回顧聊天記錄 (Ctrl-Z)快速切換 (Ctrl-C)離開聊天室");
}

static void
print_chatid(const char *chatid)
{
    move(b_lines - 1, 0);
    clrtobot();
    outs(chatid);
    outc(':');
    print_footer();
}

static int
chat_send(int fd, const char *buf)
{
    int             len;
    char            genbuf[200];

    len = snprintf(genbuf, sizeof(genbuf), "%s\n", buf);
    return (send(fd, genbuf, len, 0) == len);
}

struct ChatBuf {
    char buf[128];
    int bufstart;
};

static int
chat_recv(struct ChatBuf *cb, int fd, char *chatroom, char *chatid, size_t chatid_size)
{
    int             c, len;
    char           *bptr;

    len = sizeof(cb->buf) - cb->bufstart - 1;
    if ((c = recv(fd, cb->buf + cb->bufstart, len, 0)) <= 0)
    return -1;
    c += cb->bufstart;

    bptr = cb->buf;
    while (c > 0) {
    len = strlen(bptr) + 1;
    if (len > c && (unsigned)len < (sizeof(cb->buf)/ 2) )
        break;

    if (*bptr == '/') {
        switch (bptr[1]) {
        case 'c':
        chat_clear(NULL);
        break;
        case 'n':
        strlcpy(chatid, bptr + 2, chatid_size);
        print_chatid(chatid);
        break;
        case 'r':
        strlcpy(chatroom, bptr + 2, sizeof(chatroom));
        break;
        case 't':
        move(0, 0);
        clrtoeol();
        prints(ANSI_COLOR(1;37;46) " 談天室 [%-12s] " ANSI_COLOR(45) " 話題:%-48s" ANSI_RESET,
               chatroom, bptr + 2);
        }
    } else
        printchatline(bptr);

    c -= len;
    bptr += len;
    }

    if (c > 0) {
    memmove(cb->buf, bptr, sizeof(cb->buf)-(bptr-cb->buf));
    cb->bufstart = len - 1;
    } else
    cb->bufstart = 0;
    return 0;
}

static void
chathelp(const char *cmd, const char *desc)
{
    char            buf[STRLEN];

    snprintf(buf, sizeof(buf), "  %-20s- %s", cmd, desc);
    printchatline(buf);
}

static void
chat_help(char *arg)
{
    if (strstr(arg, " op")) {
    printchatline("談天室管理員專用指令");
    chathelp("[/f]lag [+-][ls]", "設定鎖定、秘密狀態");
    chathelp("[/i]nvite <id>", "邀請 <id> 加入談天室");
    chathelp("[/k]ick <id>", "將 <id> 踢出談天室");
    chathelp("[/o]p <id>", "將 Op 的權力轉移給 <id>");
    chathelp("[/t]opic <text>", "換個話題");
    chathelp("[/w]all", "廣播 (站長專用)");
    chathelp(" /ban <userid>", "拒絕 <userid> 再次進入此聊天室 (加入黑名單)");
    chathelp(" /unban <userid>", "把 <userid> 移出黑名單");
    } else {
    chathelp(" /help op", "談天室管理員專用指令");
    chathelp("[//]help", "MUD-like 社交動詞");
    chathelp("[/a]ct <msg>", "做一個動作");
    chathelp("[/b]ye [msg]", "道別");
    chathelp("[/c]lear", "清除螢幕");
    chathelp("[/j]oin <room>", "建立或加入談天室");
    chathelp("[/l]ist [room]", "列出談天室使用者");
    chathelp("[/m]sg <id> <msg>", "跟 <id> 說悄悄話");
    chathelp("[/n]ick <id>", "將談天代號換成 <id>");
    chathelp("[/p]ager", "切換呼叫器");
    chathelp("[/q]uery <id>", "查詢網友");
    chathelp("[/r]oom ", "列出一般談天室");
    chathelp("[/w]ho", "列出本談天室使用者");
    chathelp(" /whoin <room>", "列出談天室<room> 的使用者");
    chathelp(" /ignore <userid>", "忽略指定使用者的訊息");
    chathelp(" /unignore <userid>", "停止忽略指定使用者的訊息");
    }
}

static void
chat_date(char *unused GCC_UNUSED)
{
    char            genbuf[200];

    snprintf(genbuf, sizeof(genbuf),
         "◆ " BBSNAME "標準時間: %s", Cdate(&now));
    printchatline(genbuf);
}

static void
chat_pager(char *unused GCC_UNUSED)
{
    char            genbuf[200];

    char           *msgs[PAGER_MODES] = {
    /* Ref: please match PAGER* in modes.h */
    "關閉", "打開", "拔掉", "防水", "好友"
    };

    snprintf(genbuf, sizeof(genbuf), "◆ 您的呼叫器:[%s]",
         msgs[currutmp->pager = (currutmp->pager + 1) % PAGER_MODES]);
    printchatline(genbuf);
}

static void
chat_query(char *arg)
{
    char           *uid;
    int             tuid;
    userec_t        xuser;
    char *strtok_pos;

    printchatline("");
    strtok_r(arg, str_space, &strtok_pos);
    if ((uid = strtok_r(NULL, str_space, &strtok_pos)) && (tuid = getuser(uid, &xuser))) {
    char            buf[ANSILINELEN], *ptr;
    FILE           *fp;

    snprintf(buf, sizeof(buf), 
        "%s(%s) " STR_LOGINDAYS " %d " STR_LOGINDAYS_QTY ",發表過 %d 篇文章",
         xuser.userid, xuser.nickname,
         xuser.numlogindays, xuser.numposts);
    printchatline(buf);

    snprintf(buf, sizeof(buf),
         "最近(%s)從[%s]上站", 
         Cdate(xuser.lastseen ? &xuser.lastseen : &xuser.lastlogin),
        (xuser.lasthost[0] ? xuser.lasthost : "(不詳)"));
    printchatline(buf);

    sethomefile(buf, xuser.userid, fn_plans);
    if ((fp = fopen(buf, "r"))) {
        tuid = 0;
        while (tuid++ < MAX_QUERYLINES && fgets(buf, sizeof(buf), fp)) {
        if ((ptr = strchr(buf, '\n')))
            ptr[0] = '\0';
        printchatline(buf);
        }
        fclose(fp);
    }
    } else
    printchatline(err_uid);
}

typedef struct chat_command_t {
    char           *cmdname;    /* Chatroom command length */
    void            (*cmdfunc) (char *);    /* Pointer to function */
}               chat_command_t;

static const chat_command_t chat_cmdtbl[] = {
    {"help", chat_help},
    {"clear", chat_clear},
    {"date", chat_date},
    {"pager", chat_pager},
    {"query", chat_query},
    {NULL, NULL}
};

static int
chat_cmd_match(const char *buf, const char *str)
{
    while (*str && *buf && !isspace((int)*buf))
    if (tolower(*buf++) != *str++)
        return 0;
    return 1;
}

static int
chat_cmd(char *buf, int fd GCC_UNUSED)
{
    int             i;

    if (*buf++ != '/')
    return 0;

    for (i = 0; chat_cmdtbl[i].cmdname; i++) {
    if (chat_cmd_match(buf, chat_cmdtbl[i].cmdname)) {
        chat_cmdtbl[i].cmdfunc(buf);
        return 1;
    }
    }
    return 0;
}

typedef struct {
    struct ChatBuf *chatbuf;
    int    cfd;
    char  *chatroom;
    char  *chatid;
    int   *chatting;
    char  *logfpath;
} ChatCbParam;

static int
_vgetcb_peek(int key, VGET_RUNTIME *prt GCC_UNUSED, void *instance)
{
    ChatCbParam *p = (ChatCbParam*) instance;
    assert(p);

    switch (key) {
    case I_OTHERDATA: // incoming
        // XXX why 9? I don't know... just copied from old code.
        if (chat_recv(p->chatbuf, p->cfd, p->chatroom, p->chatid, 9) == -1) {
        chat_send(p->cfd, "/b");
        *(p->chatting) = 0;
        return VGETCB_ABORT;
        }
        return VGETCB_NEXT;

    case Ctrl('C'):
        chat_send(p->cfd, "/b");
        *(p->chatting) = 0;
        return VGETCB_ABORT;

    case Ctrl('I'):
        {
        VREFSCR scr = vscr_save();
        add_io(0, 0);
        t_idle();
        vscr_restore(scr);
        add_io(p->cfd, 0);
        }
        return VGETCB_NEXT;

    case KEY_PGUP:
    case KEY_PGDN:
        if (flog)
        {
        VREFSCR scr = vscr_save();
        add_io(0, 0);

        fflush(flog);
        more(p->logfpath, YEA);

        vscr_restore(scr);
        add_io(p->cfd, 0);
        }
        return VGETCB_NEXT;

        // Support ZA because chat is mostly independent and secure.
    case Ctrl('Z'):
        {
        int za = 0;
        VREFCUR cur = vcur_save();
        add_io(0, 0);
        za = ZA_Select();
        print_footer();
        vcur_restore(cur);
        add_io(p->cfd, 0);
        if (za)
            return VGETCB_ABORT;
        return VGETCB_NEXT;
        }
    }
    return VGETCB_NONE;
}

static int      chatid_len = 10;

int
t_chat(void)
{
    static time4_t lastEnter = 0;

    char     chatroom[IDLEN+1] = "";/* Chat-Room Name */
    char            inbuf[80], chatid[20] = "", *ptr = "";
    char        hasnewmail = 0;
    char            fpath[PATHLEN];
    int             cfd;
    int             chatting = YEA;
    struct ChatBuf  chatbuf;
    ChatCbParam     vgetparam = {0};

    if(HasUserPerm(PERM_VIOLATELAW))
    {
       vmsg("請先繳罰單才能使用聊天室!");
       return -1;
    }

    if (!HasUserPerm(PERM_CHAT))
    return -1;

    syncnow();

#ifdef CHAT_GAPMINS
    if ((now - lastEnter)/60 < CHAT_GAPMINS)
    {
       vmsg("您才剛離開聊天室,裡面正在整理中。請稍後再試。");
       return 0;
    }
#endif

#ifdef CHAT_REGDAYS
    if ((now - cuser.firstlogin)/DAY_SECONDS < CHAT_REGDAYS)
    {
    int i = CHAT_REGDAYS - (now-cuser.firstlogin)/DAY_SECONDS +1;
    vmsgf("您還不夠資深喔 (再等 %d 天吧)", i);
    return 0;
    }
#endif

    memset(&chatbuf, 0, sizeof(chatbuf));
    outs("                     驅車前往 請梢候........         ");

    cfd = toconnect(XCHATD_ADDR);
    if (cfd < 0) {
    outs("\n              "
         "哇! 沒人在那邊耶...要有那地方的人先去開門啦!...");
    system("bin/xchatd");
    pressanykey();
    return -1;
    }

    while (1) {
    getdata(b_lines - 1, 0, "請輸入想使用的聊天暱稱:", chatid, 9, DOECHO);
    if(!chatid[0])
        strlcpy(chatid, cuser.userid, sizeof(chatid));
    chatid[8] = '\0';
    /*
     * 新格式:    /! UserID ChatID Password
     */
    snprintf(inbuf, sizeof(inbuf), "/! %s %s %s",
        cuser.userid, chatid, cuser.passwd);
    chat_send(cfd, inbuf);
    if (recv(cfd, inbuf, 3, 0) != 3) {
        close(cfd);
           vmsg("系統錯誤。");
        return 0;
    }
    if (!strcmp(inbuf, CHAT_LOGIN_OK))
        break;
    else if (!strcmp(inbuf, CHAT_LOGIN_EXISTS))
        ptr = "這個代號已經有人用了";
    else if (!strcmp(inbuf, CHAT_LOGIN_INVALID))
        ptr = "這個代號是錯誤的";
    else if (!strcmp(inbuf, CHAT_LOGIN_BOGUS))
        ptr = "請勿派遣分身進入聊天室 !!";

    move(b_lines - 2, 0);
    outs(ptr);
    clrtoeol();
    bell();
    }
    syncnow();
    lastEnter = now;

    add_io(cfd, 0);

    setutmpmode(CHATING);
    currutmp->in_chat = YEA;
    strlcpy(currutmp->chatid, chatid, sizeof(currutmp->chatid));

    clear();
    chatline = 2;

    move(STOP_LINE, 0);
    outs(msg_seperator);
    move(STOP_LINE, 56);
    outs(" /h 查詢指令  /b 離開 ");
    move(1, 0);
    outs(msg_seperator);
    print_chatid(chatid);
    memset(inbuf, 0, sizeof(inbuf));

    setuserfile(fpath, "chat_XXXXXX");
    flog = fdopen(mkstemp(fpath), "w");

    // set up vgetstring callback parameter
    VGET_CALLBACKS vge = { _vgetcb_peek };

    vgetparam.chatbuf = &chatbuf;
    vgetparam.cfd = cfd;
    vgetparam.chatid = chatid;
    vgetparam.chatroom = chatroom;
    vgetparam.chatting = &chatting;
    vgetparam.logfpath = fpath;

    while (chatting) {
    if (ZA_Waiting())
    {
        // process ZA
        VREFSCR scr = vscr_save();
        add_io(0, 0);
        ZA_Enter();
        vscr_restore(scr);
        add_io(cfd, 0);
    }
    print_chatid(chatid);
    move(b_lines-1, chatid_len);

    // chatid_len = 10, quote(:) occupies 1, so 79-11=68
    vgetstring(inbuf, 68, VGET_TRANSPARENT, "", &vge, &vgetparam);

    // quick check for end flag or exit command.
    if (!chatting)
        break;

    if (strncasecmp(inbuf, "/b", 2) == 0)
    {
        // cases: /b, /bye, "/b "
        // !cases: /ban
        if (tolower(inbuf[2]) != 'a')
        break;
    }

    // quick continue for empty input
    if (!*inbuf)
        continue;

#ifdef EXP_ANTIFLOOD
    {
        // prevent flooding */
        static time4_t lasttime = 0;
        static int flood = 0;

        syncnow();
        if (now - lasttime < 3 )
        {
        // 3 秒內洗半面是不行的 ((25-5)/2)
        if( ++flood > 10 ){
            // flush all input!
            drop_input();
            while (wait_input(1, 0))
            {
            if (num_in_buf())
                drop_input();
            else
                tty_read((unsigned char*)inbuf, sizeof(inbuf));
            }
            drop_input();
            vmsg("請勿大量剪貼或造成洗板面的效果。");
            // log?
            sleep(2);
            continue;
        }
        } else {
        lasttime = now;
        flood = 0;
        }
    }
#endif // anti-flood

    // send message to server if possible.
    if (!chat_cmd(inbuf, cfd))
        chatting = chat_send(cfd, inbuf);

    // print mail message if possible.
    if (ISNEWMAIL(currutmp))
    {
        if (!hasnewmail)
        {
        printchatline("◆ 您有未讀的新信件。");
        hasnewmail = 1;
        }
    } else {
        if (hasnewmail)
        hasnewmail = 0;
    }
    }

    close(cfd);
    add_io(0, 0);
    currutmp->in_chat = currutmp->chatid[0] = 0;

    if (flog) {
    char            ans[4];

    fclose(flog);
    more(fpath, NA);
    getdata(b_lines - 1, 0, "清除(C) 移至備忘錄(M) (C/M)?[C]",
        ans, sizeof(ans), LCECHO);
    if (*ans == 'm') {
        if (mail_log2id(cuser.userid, "會議記錄",
            fpath, "[備.忘.錄]", 0, 1) < 0)
        vmsg("備忘錄儲存失敗。");
    }
    unlink(fpath);
    }
    return 0;
}