/* * copyright David J. Binette * ALL RIGHTS RESERVED */ /* ------------------------------------------------------------------------ */ /* tab stops are set to 4 */ /* ------------------------------------------------------------------------ */ /* yahmini is an small yahoo chat client as of July 8 2000 * written in portable C for win32 and linux * Its main grace is its small size partially attributable * to consideration of the segment boundarys created in win32 applications. * yahmini has no global volatile variables ,hence * there is only a code segment and a const data segment. * *your* C runtime however may add a whole pile of crap * making it significantly larger. * */ /* choose your flavour */ #if WIN32 #include "dos.h" #else #include "nix.h" #endif /* ------------------------------------------------------------------------ */ /* we dont default to showing ansi ,because it * looks terrible when the user has no ansi support */ /* DEFAULTSHOWANSI 1=ANSI color ,0=plain */ #define DEFAULTSHOWANSI 0 /* DEFAULTSHOWMOVE 1= show enter/leave ,0= dont */ #define DEFAULTSHOWMOVE 0 /* DEFAULTSHOWAWAY 1= show away/back ,0= dont */ #define DEFAULTSHOWAWAY 0 /* DEFAULTPINGTIME 0= no ping ,>0 is # minutes */ #define DEFAULTPINGTIME 10 /* ------------------------------------------------------------------------ */ /* the 'help' text */ static const char Gusagestr[] = { "\nyahmini copyright 2000 David J. Binette, ALL RIGHTS RESERVED\n" "usage: yahmini name pass room\n" "chat commands:\n" "/quit to quit\n" "/cls\n" "/color{+-} (needs ANSI.SYS)\n" "/showaway{+-}\n" "/showenter{+-}\n" "/away\treason\n" "/call\twho\n" "/goto\twho\n" "/join\troom\n" "/ping\t#minutes\n" "/roll\t#\n" "/tell\twho text\n" "/think\ttext\n" }; /* the http request to get cookie ,with username ,password and host */ static const char HTTPGet_argU_argP_argH[] = { "GET /config/ncclogin?.src=bl&login=%s&passwd=%s&n=1 HTTP/1.0\r\n" "Host: %s\r\n" "Accept: */*\r\n" "User-Agent: Mozilla/4.0\r\n" "\r\n" }; /* ------------------------------------------------------------------------ */ static BOOL chatOut (SESSION *sess ,char *cmd); static BOOL yahooPing(SESSION *sess); /* ------------------------------------------------------------------------ */ /* state: TRUe=skip white ,FALSE=skip nonwhite */ static char * skipwhite(char *p ,BOOL state) { while(*p && (isspace(*p) == state)) p++; return p; } /* ------------------------------------------------------------------------ */ static void showSockError(SESSION *sess ,const char * arg1 ,const char *arg2 ,const char *arg3 ,int errcode) { if (!sess->quit) fprintf(sess->o ,"%s%s %s failed %s, error %d\n" ,"*** " ,arg1 ,arg2 ,arg3 ,errcode); } /* ------------------------------------------------------------------------ */ #if WIN32 #pragma warning(push) #pragma warning(disable : 4127) /* VC6 winsock warning on FD_SET */ #endif /* WIN32 */ /* ------------------------------------------------------------------------ */ /* * wait for up to 250mS for a packet * RETURNS * <0 error ,socket is closed * =0 timeout ,no data available * >0 data waiting */ /* ------------------------------------------------------------------------ */ static int sockDataAvailable(SOCKET sock) { int rv; struct timeval tv; fd_set fds; tv.tv_sec = 0; tv.tv_usec = 250000; FD_ZERO(&fds); FD_SET(sock ,&fds); rv = select(sock+1 ,&fds ,NULL ,NULL ,&tv); if (rv > 0) { char x[2]; if (recv(sock ,x ,1 ,MSG_PEEK) < 1) rv = -1; } return rv; } /* ------------------------------------------------------------------------ */ #if WIN32 #pragma warning(pop) #endif /* WIN32 */ /* ------------------------------------------------------------------------ */ static int selectRecv(SESSION *sess ,char *buf ,int siz ,int flags) { static int n = 0; while(!sess->quit) { if (KeybDataAvailable(sess)) { char cmd[YAHOOMAXTEXTOUT+1]; Ask(sess ,CHATPROMPT ,cmd ,YAHOOMAXTEXTOUT ,2); if (SessionHook(sess ,SHOOK_INPUT ,0 ,cmd)) if (!chatOut(sess ,cmd)) break; } if (sockDataAvailable(sess->sock)) return recv(sess->sock ,buf ,siz ,flags); /* our select sleeps for 250mS ,do a ping approx. every 10 minutes */ if ((sess->ping) &&(++n >( 4 * 60 * sess->ping))) { yahooPing(sess); if (sess->ansi) fprintf(sess->o ,"%s%s%s%s" ,ATTR_CYAN ,"*ping*" ,ATTR_RESET ,"\b\b\b\b\n"); else fprintf(sess->o ,"%s%s" ,"*ping*" ,"\b\b\b\b\n"); n = 0; } } return FALSE; } /* ------------------------------------------------------------------------ */ /* General purpose socket read * sock socket * buf pointer to data buffer * siz size of buffer * info text for error message * * RETURN * >= 0 number bytes received * < 0 on error */ static int myRecv(SESSION *sess ,char *buf ,int siz ,const char *info) { int cnt = 0; while (siz) { int b = selectRecv(sess ,buf ,siz ,0); if (b == -1) { showSockError(sess ,info,"incomplete" ,"recv()" ,SockErrNum()); return -1; } if (b == 0) break; cnt += b; buf += b; siz -= b; } return cnt; } /* ------------------------------------------------------------------------ */ /* read exactly n bytes * Function will return TRUE if success */ static BOOL recvN(SESSION *sess ,char *buf ,int cnt ,const char *info) { while (cnt) { int b = myRecv(sess ,buf ,cnt ,info); if (b < 1) { showSockError(sess ,info ,"incomplete" ,"recv()" ,SockErrNum()); break; } buf += b; cnt -= b; } return cnt==0; } /* ------------------------------------------------------------------------ */ /* send exactly n bytes * Function will return TRUE if success */ static BOOL sendN(SESSION *sess ,const u_char *buf ,int cnt ,const char *info) { while (cnt) { int b = send(sess->sock ,(char*)buf ,cnt ,0); if (b < 1) { showSockError(sess ,info ,"incomplete" ,"send()" ,SockErrNum()); break; } buf += b; cnt -= b; } return cnt==0; } /* ------------------------------------------------------------------------ */ /* function to copy from a NULL terminated string * to a NON null terminated string * * Function will return pointer to last byte copied +1 */ static u_char *appendString(u_char *dst ,const u_char *src) { while (*src) *dst++ = *src++; return dst; } /* ------------------------------------------------------------------------ */ /* A function to create a yahoo packet header * * Function will return the next available cell for packet contents */ static u_char *initYahooHeader(u_char *buf ,u_long code) { memmove(buf ,"YCHT" ,4); *((u_long*)(buf+ 4))=htonl(0x100); *((u_long*)(buf+ 8))=htonl(code); /* *((u_long*)(buf+12))=htonl(length of packet filled inlater);*/ return buf + 16; } /* ------------------------------------------------------------------------ */ /* function to send the yahoo packet * the header is updated to contain the accumulated length * * Function will return TRUE if success */ static BOOL sendYahooPacket(SESSION *sess ,const u_char *bufp ,const u_char *endp ,const char *info) { int n = endp - bufp; /* update the packet length count */ *((u_long*)(bufp+12)) = htonl(n-16); /* send the packet */ return sendN(sess ,bufp ,n ,info); } /* ------------------------------------------------------------------------ */ /* A function to connect to the chat server * Function will return TRUE if success */ static BOOL yahooChatConnect(SESSION *sess ,const char *cook) { u_char buf[YAHOOBUFOUTSIZE]; u_char *p = initYahooHeader(buf ,0x01); p = appendString(p ,(const u_char*)sess->user); *p++ = 0x01; p = appendString(p ,(const u_char*)cook); return sendYahooPacket(sess ,buf ,p ,"yahooChatConnect"); } /* ------------------------------------------------------------------------ */ /* A function that keeps the Yahoo session open. * yahoo will disconnect if this packet is not received within 30 minutes * Function will return TRUE if success */ static int yahooPing(SESSION *sess) { u_char buf[YAHOOBUFOUTSIZE]; u_char *p = initYahooHeader(buf ,0x62); return sendYahooPacket(sess ,buf ,p ,"ping"); } /* ------------------------------------------------------------------------ */ BOOL YahooTextCommand(SESSION *sess ,const char *cmd ,const char *txt) { u_char buf[YAHOOBUFOUTSIZE]; u_char *p = initYahooHeader(buf ,0x71); p = appendString(p ,(const u_char*)cmd); p = appendString(p ,(const u_char*)txt); return sendYahooPacket(sess ,buf ,p ,*cmd ? cmd : ( *txt ? txt : "send")); } /* ------------------------------------------------------------------------ */ /* A function to receive a yahoo chat packet * SESSION *sess = contains a socket connected to the yahoo chat server * * Function will return TRUE if success */ static BOOL yahooRecvData(SESSION *sess) { /* read the yahooheader ,expect 16 bytes * bytes 0- 3 will always be 59 43 48 54 - or "YCHT" in ASCII. * bytes 4- 7 are 00 00 01 00 ,perhaps a Yahoo version number. * bytes 8-11 bytes will be the packet type. * bytes 12-13 are flag message. generally only needed for errors. * bytes 14-15 are data length: TCP payload minus 16-byte YCHT header. */ int synccnt = 0; sess->yin.zero = EOS; { char buf[16] ,*p; int cnt; HEADSTART: p = buf; cnt = sizeof(buf); HEADAGAIN: if (!recvN(sess ,buf ,cnt ,"Read header")) return FALSE; /* check for a faulty packet */ if (memcmp(buf ,"YCHT\0\0\1\0\0\0\0" ,11)) { if (!synccnt++) fprintf(sess->o ,"%sResynch\n" ,"*** "); p = buf+1; while (p < (buf+16)) { if (*p == 'Y') { cnt = p-buf; memmove(buf ,p ,16-cnt); goto HEADAGAIN; } p++; } goto HEADSTART; } if (synccnt) { fprintf(sess->o ,"%sResynch after %d bytes.\n" ,"*** " ,synccnt); synccnt = 0; } sess->yin.type = buf[11]; sess->yin.errs = ntohs((u_short)*((u_short*)(buf+12))); sess->yin.size = ntohs((u_short)*((u_short*)(buf+14))); if (sess->yin.size >= YAHOODATAINSIZE) { fprintf(sess->o ,"%shuge (%u bytes) packet rejected.\n" ,"*** " ,sess->yin.size); goto HEADSTART; } } /* add a null terminator ,just in case */ sess->yin.data[sess->yin.size] = EOS; /* read the expected amount of data */ return recvN(sess ,sess->yin.data ,sess->yin.size ,"Read data"); } /* ------------------------------------------------------------------------ */ /* A function to resolve and connect to a TCP/IP port * return INVALID_SOCKET on error * otherwise return a valid socket handle */ static SOCKET setupConnection(SESSION *sess ,const char *host ,const u_short port ,const int proto ,const char *info) { SOCKET sock; struct sockaddr_in serv_addr; fprintf(sess->o ,"Finding %s\n" ,host); memset(&serv_addr ,0 ,sizeof(serv_addr)); serv_addr.sin_addr.s_addr = inet_addr(host); if (serv_addr.sin_addr.s_addr == (u_long)INADDR_NONE) { /* resolve hostname to ipaddr */ struct hostent *hostInfo = gethostbyname(host); if (!hostInfo || (hostInfo->h_addrtype != AF_INET)) { showSockError(sess ,info ,host ,"gethostbyname()" ,SockErrNum()); return INVALID_SOCKET; } memset(&serv_addr ,0 ,sizeof(serv_addr)); memmove((void*)&serv_addr.sin_addr.s_addr ,hostInfo->h_addr ,hostInfo->h_length); } serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(port); sock = socket(AF_INET ,SOCK_STREAM ,proto); if (sock == INVALID_SOCKET) showSockError(sess ,info ,host ,"socket()" ,SockErrNum()); else if (connect(sock ,(struct sockaddr*)&serv_addr ,sizeof(serv_addr)) == -1) { showSockError(sess ,info ,host ,"connect()" ,SockErrNum()); closesocket(sock); sock = INVALID_SOCKET; } return sock; } /* ------------------------------------------------------------------------ */ /* login to yahoo authentication server and get cookie * return TRUE if success */ static BOOL subLoginToYahoo(SESSION *sess ,char *buf ,int siz) { int n; char *p; char *hostname = SERVERAUTHHOST; #if USETINYCRT char *np; #define strnext(a ,b) strtoken(a ,b ,&np) #else #define strnext(a ,b) strtok(a ,b) #endif sess->cook[0] = EOS; sess->sock = setupConnection(sess ,hostname ,SERVERAUTHPORT ,IPPROTO_TCP ,"Login"); if (sess->sock != INVALID_SOCKET) { n = sprintf(buf ,HTTPGet_argU_argP_argH ,sess->user ,sess->pass ,hostname); n = sendN(sess ,(const u_char*)buf ,n ,"Login"); if (n) n = myRecv(sess ,buf ,siz-1 ,"Login"); closesocket(sess->sock); sess->sock = INVALID_SOCKET; if (n < 1) fprintf(sess->o ,"%sno login data.\n" ,"*** "); else { buf[n] = EOS; /* we have to be careful what we output * because some terminals allow keys to be remapped * we dont want to every allow unstripped data to be * sent to the terminal */ /* fprintf(sess->o ,"%s\n" ,buf); */ for(p=strnext(buf ,"\r\n"); p; p=strnext(NULL ,"\r\n")) { if (!strcmp(p ,"ERROR: Invalid NCC Login")) { sess->cook[0] = EOS; fprintf(sess->o ,"%s\n" ,p); break; } if (!strncmp(p ,"Set-Cookie:" ,11)) { char *q = strchr(p ,';'); if (q) *q = EOS; q = p+11; q = skipwhite(q ,TRUE); if (*q) { if ((strlen(sess->cook)+strlen(q)+3) >= CHATCOOKSIZE) fprintf(sess->o ,"%sBad cookie\n" ,"*** "); else { if (sess->cook[0]) strcat(sess->cook ,"; "); strcat(sess->cook ,q); } } } } } } return sess->cook[0] ? TRUE : FALSE; } /* wrap the call to handle the malloc()/free() * I dont like big stack frames * return TRUE if success */ static BOOL LoginToYahoo(SESSION *sess) { BOOL r = FALSE; char *buf = malloc(65534); if (buf) { r = subLoginToYahoo(sess ,buf ,65533); free(buf); } else fprintf(sess->o ,"no mem\n"); return r; } /* ------------------------------------------------------------------------ */ /* login to yahoo chat server and send the login packet * return a TRUE if login ok */ static BOOL loginToChat(SESSION *sess) { char tCook[CHATCOOKSIZE+1]; sess->sock = setupConnection(sess ,SERVERCHATHOST ,SERVERCHATPORT ,IPPROTO_TCP ,"Chat"); if (sess->sock != INVALID_SOCKET) { /* the chat connect function requires a subset of the cookie data * we only want the v= portion so build a temporary cookie */ { char *p = strstr(sess->cook ,"v="); strcpy(tCook ,p?p:sess->cook); if ((p=strstr(tCook ,"; "))!=NULL) *p = EOS; } if (!yahooChatConnect(sess ,tCook)) { closesocket(sess->sock); sess->sock = INVALID_SOCKET; } } return (sess->sock == INVALID_SOCKET) ? (fprintf(sess->o ,"loginToChat failed\n") ,FALSE) : TRUE ; } /* ------------------------------------------------------------------------ */ static BOOL chatopt(SESSION *sess ,char *cmd ,char *pat ,short *opt) { BOOL r = FALSE; int n = strlen(pat); if (!strncmp(cmd ,pat ,n)) { char *s = skipwhite(cmd+n ,TRUE); if ((*s>='0')&&(*s<='9')) *opt = (short)(*s-'0'); else if (*s=='-') *opt = 0; else if (*s=='+') *opt = 1; else *opt = (short) (*opt ? 0 : 1); fprintf(sess->o ,">%s is now %d\n" ,pat ,*opt); r = TRUE; } return r; } /* ------------------------------------------------------------------------ */ /* send a chat command * Note: it modifies the string in place * return FALSE if we should quit */ static BOOL chatOut(SESSION *sess ,char *cmd) { BOOL r = TRUE; char *pre = ""; char *txt = ""; switch(cmd[0]) { case EOS: break; case '/': if (chatopt(sess ,cmd ,"/showenter" ,&sess->move) || chatopt(sess ,cmd ,"/showaway" ,&sess->away) || chatopt(sess ,cmd ,"/color" ,&sess->ansi) || chatopt(sess ,cmd ,"/ping" ,&sess->ping) ) break; if (!strcmp (cmd ,"/quit")) { sess->quit=TRUE; r=FALSE; } else if (!strcmp(cmd ,"/cls")) ClearScreen(sess); else if (!strcmp(cmd ,"/help")) fprintf(sess->o ,"%s\n" ,Gusagestr); else if (!strncmp(cmd ,"/away" ,5)) { pre = "away away" "\300\200" "12" "\300\200"; txt = cmd+5; } else if (!strncmp(cmd ,"/tell " ,6)) { char *p = cmd; /* tell dummytext\001user\text */ p = skipwhite(p ,FALSE); /* skip cmd ,it will be a dummy arg */ *p++='\001'; /* add separator */ p = skipwhite(p ,TRUE); /* skip to name */ p = skipwhite(p ,FALSE); /* skip over name */ if (*p) { *p = '\001'; pre = "tell "; txt = skipwhite(cmd+1 ,TRUE); } /* else could put a usage string here */ } else /* send it directly ,who knows ,maybe its cool */ pre = cmd+1; break; default: pre = "say say\001"; txt = cmd; } if (*pre || *txt) r = YahooTextCommand(sess ,pre ,txt); return r; } /* ------------------------------------------------------------------------ */ /* strip the formatting codes from the string * after stripping, if there are ans ESC (\033) chars left in the buffer * convert them to the letter 'E' so that an escape sequence is not * allowed to be sent to the console. * return the addr of the string */ static char *stripVT100(char *s) { char *p = s; char *q = s; /* strip the standard yahoo color codes */ while (*p) { if ((*p == '\033') && (*(p+1) == '[')) { p += 2; while (*p && (*p++ != 'm')) ; } else *q++ = *p++; } *q = EOS; /* convert any remaining ESC chars to the letter 'E' */ for(p = s; *p; p++) { if(isprint(*p)) continue; if(isspace(*p)) continue; *p = '?'; } *p = EOS; return s; } /* ------------------------------------------------------------------------ */ /* the mask controls which fields are assigned to the * four pointers passed on the invocation to parse(). * Not all of the pointers to parse() are populated. * that is controlled by the special chars in mask. * args is only used as a late test to validate that * the right number of assignments has occured. */ typedef struct YPARSE_DEF { /* fields controlling how the incoming packet is scanned */ const short type; /* packet type */ const short args; /* # of data fields */ const char * const mask; /* variable mask */ /* fields controlling how the packet is displayed */ const char * const show; /* a format string for fprintf */ } YPARSE; /* ------------------------------------------------------------------------ */ /* * this routines scans the sess->yin.data and populates a series of pointers. * the fmt string tokens direct us to scan for and optionally stores * pointers to data. The data in sess->yin->dta is modified and terminated * with ASCII NULL. The variable args are pointers that will receive the * a pointer to the scanned text withing the yahoo packet. * * we honour the following special characters * $ expect text followed by \300\200 ,store the pointer * * expect text followed by \300\200 ,do not assign it * * returns the number of fields scanned and assigned * * assumes the data is null terminated */ static short __cdecl parse(SESSION *sess ,const YPARSE *yp ,char **endp ,...) { short assigned = 0; char *pdata = sess->yin.data; const char *limit = sess->yin.data + sess->yin.size; const char *fmt = yp->mask; va_list argptr; va_start(argptr ,endp); while (*fmt) { if (*fmt=='$') /* assign a string or number */ { assigned++; *va_arg( argptr ,char**) = pdata; } while (pdata < limit) /* skip to the next field */ { if ((*pdata == '\300') && (*(pdata+1) == '\200')) { *pdata = EOS; pdata+=2; break; } pdata++; } fmt++; } va_end(argptr); *endp = pdata; /* store the position of the next data*/ return assigned; } /* ------------------------------------------------------------------------ */ static void showErrUnknown(SESSION *sess ,const char *msg) { fprintf(sess->o ,"%s type=0x%02x ,errs=0x%04x ,size=%u\n%s\n" ,msg ,sess->yin.type ,sess->yin.errs ,sess->yin.size ,stripVT100(sess->yin.data) ); } /* ------------------------------------------------------------------------ */ static char * show(char *fielddata[] ,char c ,short ansi) { char *p = ""; if ((c>='0') && (c<='3')) p = fielddata[c-'0']; else if (ansi) switch(c) { case '+' : p = ATTR_BOLD ; break; case '-' : p = ATTR_RESET ; break; case 'b' : p = ATTR_BLUE ; break; case 'c' : p = ATTR_CYAN ; break; case 'g' : p = ATTR_HGREEN ; break; case 'G' : p = ATTR_LGREEN ; break; case 'p' : p = ATTR_PURPLE ; break; case 'r' : p = ATTR_RED ; break; case 'y' : p = ATTR_YELLOW ; break; } return p; } /* ------------------------------------------------------------------------ */ #if 0 line wrap makes it difficult to read on 80 col terminals so the packet number ,type and fields of interest are shown here 0x01 "login" name,-,-,-,-,mail 0x02 "logout" text 0x11 "enters" room,text,spam,list 0x12 "leaves" room,name 0x17 "invite" text,room,name 0x21 "move" name,room,away,text 0x25 "notuser" text 0x41 "says" -room,name,text 0x42 "thinks" -room,name,text 0x43 "emotes" -room,name,text 0x44 "spam" text 0x45 "private" name,user,text 0x46 "away" text 0x47 "dice" name,text 0x64 "help" text 0x68 "buddy" mode 0x69 "mail" mail 0x70 "yahoo" text 0x71 "err71" text 0x80 "grafiti" codes 0xff "mutant" ??? #endif /* ------------------------------------------------------------------------ */ #define FIELD(n) show(f ,yp->show[n] ,sess->ansi) /* note that our table does not include packet 0x80 ,we ignore it */ static void showYahooPacket(SESSION *sess) { short r; char *endp = sess->yin.data; char *f[4] = { "" ,"" ,"" ,""}; /* receives pointers to packet fields */ static const YPARSE yparse[] = {{0x01 ,2 ,"$****$" ,"r0-+1- Welcome %s%s%s, you have %s%s%s email\n"} ,{0x02 ,1 ,"$" ,"0 %s\n" } ,{0x11 ,4 ,"$$$*$" ,"3g-012 %s %senters%s %s %s %s\n" } ,{0x12 ,2 ,"$$" ,"1G-0 %s %sleaves%s %s\n" } ,{0x17 ,3 ,"$$$" ,"r2-y1- %s%s%s invites you to %s%s%s\n" } ,{0x21 ,4 ,"$$#$" ,"032 %s %s(%s)\n" } ,{0x25 ,1 ,"$" ,"0 unknown user %s\n" } ,{0x41 ,2 ,"*$$" ,"r0-+1- %s%s%s: %s%s%s\n" } ,{0x42 ,2 ,"*$$" ,"r0-p1- %s%s%s: %s%s%s\n" } ,{0x43 ,2 ,"*$$" ,"r0-b1- %s%s%s: %s%s%s\n" } ,{0x44 ,1 ,"$" ,"0 spam > %s\n" } ,{0x45 ,3 ,"$$$" ,"r0-1+2-%s%s%s %s%s%s\n" } ,{0x46 ,1 ,"$" ,"G0- %s%s%s\n" } ,{0x47 ,2 ,"$$" ,"r0-1 %s%s%s %s\n" } ,{0x64 ,1 ,"$" ,"0 help %s\n" } ,{0x68 ,1 ,"$" ,"0 buddy %s\n" } ,{0x69 ,1 ,"$" ,"+0- mail %s\n" } ,{0x70 ,1 ,"$" ,"0 yahoo %s\n" } ,{0x71 ,1 ,"$" ,"0 err71 %s\n" } ,{0xff ,0 ,"" ," " } /* pkt #arg mask order outputText */ }; const YPARSE *yp = yparse; /* we ignore a graffiti packet */ if (sess->yin.type == 0x80) return; if (sess->yin.errs < 0) { showErrUnknown(sess ,"error"); return; } /* scan the parse table ,stopping at the found item ,or the last entry */ while((yp->type != 0xff) && (yp->type != sess->yin.type)) yp++; if (yp->type == 0xff) { showErrUnknown(sess ,"unknown"); return; } /* assign pointers according to the format mask */ r = parse(sess ,yp ,&endp ,&f[0] ,&f[1] ,&f[2] ,&f[3]); if (r != yp->args) /* not all variables populated */ { #if 0 /* this is fine ,if you want it */ fprintf(sess->o ,"bad packet (%d/%d args) type=%x ,errs=0x%04x ,size=%u\n%s\n" ,r ,yp->args ,sess->yin.type ,sess->yin.errs ,sess->yin.size ,stripVT100(sess->yin.data) ); #endif return; } /* strip vt100 codes from all displayable packet data */ if (r>0) stripVT100(f[0]); if (r>1) stripVT100(f[1]); if (r>2) stripVT100(f[2]); if (r>3) stripVT100(f[3]); /* we dont use this, but someone might * r=the number of fields decoded * f=the decoded fields * * if(SessionHook(sess,SHOOK_DECODE,r,f) * return; */ /* special handling for a room enter */ if (sess->yin.type == 0x11) { if (strchr(f[3] ,',')||strcmp(f[0] ,sess->room)) { strncpy(sess->room ,f[0] ,CHATROOMSIZE); sess->room[CHATROOMSIZE] = EOS; fprintf(sess->o ,"%sYou are now in %s%s\n%s%s%s\nYou see here %s\n" ,show(f ,'y' ,sess->ansi) /* room name in yellow */ ,show(f ,'0' ,sess->ansi) ,show(f ,'-' ,sess->ansi) ,show(f ,'+' ,sess->ansi) /* room message in bold */ ,show(f ,'1' ,sess->ansi) ,show(f ,'-' ,sess->ansi) ,f[3]); /* list of guests */ return; } } /* maybe we dont show buddy or enter/leave messages */ if (!sess->move &&((sess->yin.type == 0x11) ||(sess->yin.type == 0x12) ||(sess->yin.type == 0x68))) return; /* maybe we dont away/back messages */ if (!sess->away && (sess->yin.type == 0x46)) return; /* display the packet ,formatted nicely */ fprintf(sess->o ,yp->show+7 ,FIELD(0),FIELD(1),FIELD(2),FIELD(3),FIELD(4),FIELD(5),FIELD(6)); } #undef FIELD /* ------------------------------------------------------------------------ */ static BOOL fetcharg(SESSION *sess ,const char *prompt ,char *dst ,u_int siz ,const char *src ,int srcvalid ,short hide) { BOOL r = FALSE; /* return value */ const char *p; /* pointer to input */ char buf[1024]; /* buffer for interactive input */ dst[0] = EOS; if(!sess->quit) { p = (srcvalid && *src) /* valid number of command line args */ ? src : Ask(sess ,prompt ,buf ,sizeof(buf)-1 ,hide) ; if(!sess->quit) { if (!*p) /* input is empty */ sprintf(buf ,"%smissing." ,prompt); else if (strlen(p) > siz) /* input is too long */ sprintf(buf ,"%s too long (%u chars max)\n" ,prompt ,siz); else /* input is valid */ { strcpy(dst ,p); r = TRUE; } if (r == FALSE) /* complain */ fprintf(sess->o ,"%s: %s\n" ,"Yahmini",buf); } } return r; } /* ------------------------------------------------------------------------ */ int __cdecl main(int argc ,const char **argv) { SESSION session ,*sess = &session; /* for consistency */ memset(sess ,0 ,sizeof(SESSION)); sess->rval = 1; if (SessionHook(sess ,SHOOK_PROG ,0 ,NULL)) /* set io handels */ { /* get args from cmd line ,or ask for them interactively */ if (fetcharg(sess,"Username: ",sess->user,CHATNAMESIZE,argv[1],argc>1,0) && fetcharg(sess,"Password: ",sess->pass,CHATPASSSIZE,argv[2],argc>2,1) && fetcharg(sess,"Roomname: ",sess->room,CHATROOMSIZE,argv[3],argc>3,0) ) { sess->ansi = (short)((argc > 4) ? (*argv[4]=='y') : DEFAULTSHOWANSI); sess->move = (short)((argc > 5) ? (*argv[5]=='y') : DEFAULTSHOWMOVE); sess->away = (short)((argc > 6) ? (*argv[6]=='y') : DEFAULTSHOWAWAY); sess->ping = (short)((argc > 7) ? (*argv[7]- '0') : DEFAULTPINGTIME); sess->sock = INVALID_SOCKET; /* proceed through the steps to manage a chat session * at strategic places we make calls to SessionHook() * so that any special terminal handling or user preferances * can be localized and customized to the programmers desire */ if (SessionHook(sess ,SHOOK_NET ,0 ,NULL)) { if (LoginToYahoo(sess)) { if (SessionHook(sess ,SHOOK_CHAT ,0 ,NULL)) { if (loginToChat(sess)) { if (SessionHook(sess ,SHOOK_RECV ,0 ,NULL)) { while (yahooRecvData(sess)) { if (SessionHook(sess ,SHOOK_DATA ,0 ,NULL)) { showYahooPacket(sess); } } SessionHook(sess ,SHOOK_CHATEND ,0 ,NULL); closesocket(sess->sock); sess->sock = INVALID_SOCKET; sess->quit = 0; } } } } SessionHook(sess ,SHOOK_NETEND ,0 ,NULL); } } } return SessionHook(sess ,SHOOK_EXIT ,0 ,NULL); } /* ------------------------------------------------------------------------ */ /* end */