/* This is part of Ari's Yahoo Client. * This file and Ari's Yahoo Client are copyright (C) 1999 Ari Johnson. * For more copyright and licensing information, see the file * 'COPYING', which should have been distributed with Ari's Yahoo Client. * * Yahoo Messenger interface code provided by yahoolib, as provided * with GTKYahoo. */ #include "config.h" #include #include #include #include #include #include #ifdef USE_CURSES #include #endif #include "libyahoo.h" #include "libyahoo-proto.h" struct statusrec { char *id; int status; int in_pager; int in_chat; struct statusrec *next; }; int debug_packets = 0; int quit_flag; struct statusrec *statushead = NULL; struct yahoo_options options; struct yahoo_context *context; char *username; char *password; char *cur_target = NULL; char *prev_target = NULL; char *reply_target = NULL; int cur_status = YAHOO_STATUS_AVAILABLE; int cur_target_changed = 1; int cur_status_changed = 1; #ifdef USE_CURSES WINDOW *win; WINDOW *displaywin; WINDOW *sepwin; WINDOW *inputwin; #endif void main_loop(void); void process_input(void); void handle_io(void); int is_integer(char *); void myprintf(char *, ...); #ifdef USE_CURSES void separatewin(char *, char *); void init_curses(void); #else void show_prompt(void); #endif int main(int argc, char *argv[]) { int i; struct yahoo_buddy *tmpbuddy; struct statusrec *tmprec; char *id; char *tmp; if(argc > 3) { fprintf(stderr, "Usage: %s [] []\n", argv[0]); return 1; } if(argc == 3) { username = (char *) malloc(strlen(argv[1]) + 1); strncpy(username, argv[1], strlen(argv[1])); password = (char *) malloc(strlen(argv[2]) + 1); strncpy(password, argv[2], strlen(argv[2])); } else if(argc == 2) { username = (char *) malloc(strlen(argv[1]) + 1); strncpy(username, argv[1], strlen(argv[1])); tmp = getpass("Password: "); password = (char *) malloc(strlen(tmp) + 1); strncpy(password, tmp, strlen(tmp)); } else if(argc == 1) { username = (char *) malloc(100); printf("Yahoo ID: "); fflush(stdout); scanf("%s", username); tmp = getpass("Password: "); password = (char *) malloc(strlen(tmp) + 1); strncpy(password, tmp, strlen(tmp)); } options.connect_mode = YAHOO_CONNECT_NORMAL; context = yahoo_init(username, password, &options); if(!context) { fprintf(stderr, "Connection failed.\n"); return 1; } if(!yahoo_connect(context)) { fprintf(stderr, "Connection failed.\n"); return 1; } yahoo_get_config(context); yahoo_cmd_logon(context, cur_status); i = 0; while(context->buddies && context->buddies[i]) i++; i--; while(i >= 0) { tmpbuddy = context->buddies[i]; id = tmpbuddy->id; tmprec = (struct statusrec *) malloc(sizeof(struct statusrec)); tmprec->id = id; tmprec->status = cur_status; tmprec->in_pager = 0; tmprec->in_chat = 0; tmprec->next = statushead; statushead = tmprec; i--; } #ifdef USE_CURSES init_curses(); #endif myprintf("Thank you for using Ari's Yahoo Client.\n"); myprintf("Ari's Yahoo Client is copyright (C) 2000 Ari Johnson\n"); myprintf("and is covered by the GNU General Public License.\n\n"); myprintf("Yahoo Messenger interface provided by yahoolib,\n"); myprintf("as distributed with GTKYahoo.\n\n"); main_loop(); free(username); free(password); return 0; } struct statusrec *get_statusrec(char *id) { struct statusrec *ptr; ptr = statushead; while(ptr && strcasecmp(id, ptr->id)) ptr = ptr->next; return ptr; } int get_in_pager(char *id) { struct statusrec *rec; rec = get_statusrec(id); if(!rec) return 0; return rec->in_pager; } int get_in_chat(char *id) { struct statusrec *rec; rec = get_statusrec(id); if(!rec) return 0; return rec->in_chat; } int get_status(char *id) { struct statusrec *rec; rec = get_statusrec(id); if(!rec) return YAHOO_STATUS_AVAILABLE; return rec->status; } void set_in_pager(char *id, int in) { struct statusrec *rec; rec = get_statusrec(id); if(!rec) return; rec->in_pager = in; } void set_in_chat(char *id, int in) { struct statusrec *rec; rec = get_statusrec(id); if(!rec) return; rec->in_chat = in; } void set_status(char *id, int status) { struct statusrec *rec; rec = get_statusrec(id); if(!rec) return; rec->status = status; } void show_userlist(void) { int i; struct yahoo_buddy *tmpbuddy; char *id; struct statusrec *rec; int status, in_pager, in_chat; i = 0; while(context->buddies && context->buddies[i]) { tmpbuddy = context->buddies[i]; id = tmpbuddy->id; status = get_status(id); in_pager = get_in_pager(id); in_chat = get_in_chat(id); if(!in_pager && !in_chat) myprintf(" %s\n", id); i++; } i = 0; while(context->buddies && context->buddies[i]) { tmpbuddy = context->buddies[i]; id = tmpbuddy->id; status = get_status(id); in_pager = get_in_pager(id); in_chat = get_in_chat(id); if((in_pager || in_chat) && status != YAHOO_STATUS_AVAILABLE) myprintf("* %s [%s]\n", id, yahoo_get_status_string(status)); i++; } i = 0; while(context->buddies && context->buddies[i]) { tmpbuddy = context->buddies[i]; id = tmpbuddy->id; status = get_status(id); in_pager = get_in_pager(id); in_chat = get_in_chat(id); if((in_pager || in_chat) && status == YAHOO_STATUS_AVAILABLE) myprintf("* %s\n", id); i++; } } int do_add(char *id) { int i; struct yahoo_buddy *tmpbuddy; char *group; group = NULL; if(context->buddies && context->buddies[0]) { tmpbuddy = context->buddies[0]; group = tmpbuddy->group; } if(group) { yahoo_add_buddy(context, id, username, group, ""); return 0; } return 1; } int do_remove(char *id) { int i; struct yahoo_buddy *tmpbuddy; char *group; group = NULL; i = 0; while(context->buddies && context->buddies[i]) { tmpbuddy = context->buddies[i]; if(!strcmp(tmpbuddy->id, id)) { group = tmpbuddy->group; } i++; } if(group) { yahoo_remove_buddy(context, id, username, group, ""); return 0; } return 1; } #ifdef USE_CURSES void init_curses(void) { int maxx, maxy; win = initscr(); cbreak(); noecho(); getmaxyx(win, maxy, maxx); inputwin = derwin(win, 3, maxx, maxy - 3, 0); sepwin = derwin(win, 1, maxx, maxy - 4, 0); displaywin = derwin(win, maxy - 4, maxx, 0, 0); scrollok(inputwin, TRUE); keypad(inputwin, TRUE); nodelay(inputwin, TRUE); scrollok(displaywin, TRUE); separatewin(NULL, NULL); refresh(); wrefresh(inputwin); } void separatewin(char *s, char *t) { int i, j, maxx, maxy, slen; char *ptr; char buff[1024]; getmaxyx(sepwin, maxy, maxx); for(i = 0; i < 5; i++) buff[i] = '-'; slen = 5; if(s) { slen = 7 + strlen(s); ptr = s; buff[i++] = '<'; for(; i < maxx && *ptr; i++) buff[i] = *ptr++; buff[i++] = '>'; } for(j = 0; j < (29 - slen); j++) buff[i++] = '-'; if(t) { ptr = t; buff[i++] = '<'; for(; i < maxx && *ptr; i++) buff[i] = *ptr++; buff[i++] = '>'; } for(; i < (maxx - 6); i++) buff[i] = '-'; if(i <= (maxx - 6)) { buff[i++] = '1'; buff[i++] = '.'; buff[i++] = '1'; buff[i++] = '0'; buff[i++] = '-'; buff[i++] = '-'; } buff[i] = '\0'; wmove(sepwin, 0, 0); waddstr(sepwin, buff); wrefresh(sepwin); wrefresh(inputwin); } #else void show_prompt(void) { if(cur_status != YAHOO_STATUS_AVAILABLE) printf("[%s] ", yahoo_get_status_string(cur_status)); printf("%s: ", cur_target ? cur_target : ""); fflush(stdout); } #endif void main_loop(void) { fd_set inset; quit_flag = 0; myprintf("Connected.\n"); #ifndef USE_CURSES show_prompt(); #endif while(!quit_flag) { FD_ZERO(&inset); FD_SET(fileno(stdin), &inset); FD_SET(context->sockfd, &inset); if(select(context->sockfd + 1, &inset, NULL, NULL, NULL) < 0) { perror("select"); return; } if(FD_ISSET(fileno(stdin), &inset)) { process_input(); #ifdef USE_CURSES wrefresh(inputwin); #endif } if(FD_ISSET(context->sockfd, &inset)) { handle_io(); #ifdef USE_CURSES wrefresh(displaywin); wrefresh(inputwin); #endif } if(cur_status_changed || cur_target_changed) { #ifdef USE_CURSES separatewin((cur_status != YAHOO_STATUS_AVAILABLE) ? yahoo_get_status_string(cur_status) : NULL, cur_target); #endif cur_status_changed = 0; cur_target_changed = 0; } } yahoo_cmd_logoff(context); #ifdef USE_CURSES endwin(); #endif } void process_command(char *orig) { char *cmd; char *args; if(!orig || !*orig) return; if(orig[0] == '/') { cmd = &orig[1]; args = strchr(cmd, ' '); if(args) while(*args == ' ') *args++ = '\0'; if(!strcasecmp(cmd, "help")) { myprintf("Ari's Yahoo Client Help\n"); myprintf("--------------------------------------\n"); myprintf("Commands:\n"); myprintf(" /help Display this screen.\n"); #ifdef USE_CURSES myprintf(" /clear Clear the screen.\n"); myprintf(" /c Alias for /clear.\n"); #endif myprintf(" /who Display friends list.\n"); myprintf(" /w Alias for /who.\n"); myprintf(" /msg Send to .\n"); myprintf(" /m Alias for /msg.\n"); myprintf(" /query Select as target.\n"); myprintf(" /query Unselect target.\n"); myprintf(" /q [] Alias for /query.\n"); myprintf(" /prev Select previous target.\n"); myprintf(" /p Alias for /prev.\n"); myprintf(" /reply Reply to last message.\n"); myprintf(" /r Alias for /reply.\n"); myprintf(" /statuslist Lists possible statuses.\n"); myprintf(" /status Set status to .\n"); myprintf(" /add Add a friend.\n"); myprintf(" /remove Remove a friend.\n"); myprintf(" /quit Exit the program.\n"); myprintf("--------------------------------------\n"); #ifdef USE_CURSES } else if(!strcasecmp(cmd, "clear") || !strcasecmp(cmd, "c")) { wclear(displaywin); wmove(displaywin, 0, 0); wrefresh(displaywin); #endif } else if(!strcasecmp(cmd, "who") || !strcasecmp(cmd, "w")) { show_userlist(); } else if(!strcasecmp(cmd, "msg") || !strcasecmp(cmd, "m")) { char *target; char *message; target = args; if(!target) { myprintf("You must supply a target and a message.\n"); return; } while(*target == ' ') *target++ = '\0'; message = strchr(target, ' '); if(!message) { myprintf("You must supply a target and a message.\n"); return; } while(*message == ' ') *message++ = '\0'; yahoo_cmd_msg(context, username, target, message); myprintf("-> %s: %s\n", target, message); } else if(!strcasecmp(cmd, "query") || !strcasecmp(cmd, "q")) { if(cur_target) { if(prev_target) free(prev_target); prev_target = (char *) malloc(strlen(cur_target) + 1); strncpy(prev_target, cur_target, strlen(cur_target)); prev_target[strlen(cur_target)] = '\0'; free(cur_target); cur_target = NULL; cur_target_changed = 1; } if(args && *args) { cur_target = (char *) malloc(strlen(args) + 1); strncpy(cur_target, args, strlen(args)); cur_target[strlen(args)] = '\0'; cur_target_changed = 1; } } else if(!strcasecmp(cmd, "prev") || !strcasecmp(cmd, "p")) { if(!prev_target) { myprintf("No previous target.\n"); } else { char *tmp; tmp = prev_target; prev_target = cur_target; cur_target = tmp; cur_target_changed = 1; } } else if(!strcasecmp(cmd, "reply") || !strcasecmp(cmd, "r")) { if(!reply_target) { myprintf("No target to which to reply.\n"); } else if(!args || !*args) { myprintf("No message.\n"); } else { yahoo_cmd_msg(context, username, reply_target, args); myprintf("-> %s: %s\n", reply_target, args); } } else if(!strcasecmp(cmd, "statuslist")) { int i; for(i = 0; i <= YAHOO_STATUS_IDLE; i++) if(i != YAHOO_STATUS_CUSTOM && yahoo_get_status_string(i)) myprintf("%8d: %s\n", i, yahoo_get_status_string(i)); myprintf(": Custom message\n"); } else if(!strcasecmp(cmd, "status")) { if(args && *args) { int newstatus; if(!is_integer(args)) { yahoo_cmd_set_away_mode(context, YAHOO_STATUS_CUSTOM, args); cur_status = YAHOO_STATUS_CUSTOM; cur_status_changed = 1; } else { newstatus = atoi(args); if(yahoo_get_status_string(newstatus)) { if(newstatus == YAHOO_STATUS_AVAILABLE) yahoo_cmd_set_back_mode(context, newstatus, NULL); else yahoo_cmd_set_away_mode(context, newstatus, NULL); cur_status = newstatus; cur_status_changed = 1; } else { myprintf("Invalid status number.\n"); } } } else { cur_status = YAHOO_STATUS_AVAILABLE; yahoo_cmd_set_back_mode(context, cur_status, NULL); cur_status_changed = 1; } } else if(!strcasecmp(cmd, "add")) { if(args && *args) { if(do_add(args)) myprintf("Could not add friend.\n"); else myprintf("Added `%s' to friends list.\n", args); } else { myprintf("You must give a Yahoo ID to add to your list.\n"); } } else if(!strcasecmp(cmd, "remove")) { if(args && *args) { if(do_remove(args)) myprintf("Could not remove friend.\n"); else myprintf("Removed `%s' from friends list.\n", args); } else { myprintf("You must give a Yahoo ID to remove from your list.\n"); } } else if(!strcasecmp(cmd, "quit")) { quit_flag = 1; } else { myprintf("Invalid command.\n"); } } else { if(!cur_target) { myprintf("No target selected.\n"); return; } yahoo_cmd_msg(context, username, cur_target, orig); myprintf("-> %s: %s\n", cur_target, orig); } } #ifdef USE_CURSES void process_input(void) { static char buff[1024]; static char *ptr = buff; int c; c = wgetch(inputwin); while(c != ERR) { switch(c) { case '\n': case '\r': *ptr = '\0'; ptr = buff; waddch(inputwin, '\n'); wrefresh(inputwin); process_command(buff); wrefresh(displaywin); break; case '\b': case 127: if(ptr > buff) { ptr--; waddch(inputwin, '\b'); waddch(inputwin, ' '); waddch(inputwin, '\b'); } break; default: *ptr++ = c; waddch(inputwin, c); } c = wgetch(inputwin); } } #else void process_input(void) { char buff[1024]; char *ptr; fgets(buff, 1024, stdin); ptr = strchr(buff, '\n'); if(ptr) *ptr = '\0'; ptr = strchr(buff, '\r'); if(ptr) *ptr = '\0'; process_command(buff); show_prompt(); } #endif void process_packet_status(struct yahoo_packet *pkt) { int i; if(pkt->service == YAHOO_SERVICE_LOGOFF && !strcmp(pkt->active_id, username)) { myprintf("Disconnected.\n"); quit_flag = 1; } for(i = 0; i < pkt->idstatus_count; i++) { struct yahoo_idstatus *rec; char *id; rec = pkt->idstatus[i]; id = rec->id; set_in_pager(id, rec->in_pager); set_in_chat(id, rec->in_chat); if(pkt->service != YAHOO_SERVICE_CHATLOGOFF && pkt->service != YAHOO_SERVICE_CHATLOGON) { set_status(id, rec->status); } if(pkt->service == YAHOO_SERVICE_LOGON) myprintf("%s has connected.\n", id); else if(pkt->service == YAHOO_SERVICE_LOGOFF) myprintf("%s has disconnected.\n", id); else myprintf("%s is now %s.\n", id, yahoo_get_status_string(get_status(id))); #ifndef USE_CURSES show_prompt(); #endif } } void process_packet_message(struct yahoo_packet *pkt) { if(pkt->msgtype == YAHOO_MSGTYPE_STATUS) set_status(pkt->msg_id, pkt->msg_status); if(pkt->msg) { if(pkt->msgtype == YAHOO_MSGTYPE_BOUNCE) { myprintf("%s -> %s: \n", pkt->active_id, pkt->msg_id); } else { myprintf("%s -> %s: %s\n", pkt->msg_id, pkt->active_id, pkt->msg); if(reply_target) free(reply_target); reply_target = (char *) malloc(strlen(pkt->msg_id) + 1); strncpy(reply_target, pkt->msg_id, strlen(pkt->msg_id)); reply_target[strlen(pkt->msg_id)] = '\0'; } #ifndef USE_CURSES show_prompt(); #endif } } void process_packet_ping(struct yahoo_packet *pkt) { myprintf("Ping.\n"); #ifndef USE_CURSES show_prompt(); #endif } void handle_io(void) { struct yahoo_rawpacket *rawpkt; struct yahoo_packet *pkt; yahoo_getdata(context); if(context->io_buf_curlen <= 103) return; while((rawpkt = yahoo_getpacket(context))) { pkt = yahoo_parsepacket(context, rawpkt); if(debug_packets) { myprintf("Received packet:\n"); myprintf("\tService = %s\n", yahoo_get_service_string(pkt->service)); myprintf("\tReal ID = %s\n", pkt->real_id); myprintf("\tActive ID = %s\n", pkt->active_id); myprintf("\tConnection ID = %X\n", pkt->connection_id); myprintf("\tMagic ID = %X\n", pkt->magic_id); myprintf("\tUnknown Flag 1 = %X\n", pkt->unknown1); myprintf("\tMessage Type = %X\n", pkt->msgtype); myprintf("\tRaw Content = %s\n", rawpkt->content); } switch(pkt->service) { case YAHOO_SERVICE_USERSTAT: case YAHOO_SERVICE_CHATLOGON: case YAHOO_SERVICE_CHATLOGOFF: case YAHOO_SERVICE_LOGON: case YAHOO_SERVICE_LOGOFF: case YAHOO_SERVICE_ISAWAY: case YAHOO_SERVICE_ISBACK: process_packet_status(pkt); break; case YAHOO_SERVICE_MESSAGE: case YAHOO_SERVICE_CHATMSG: case YAHOO_SERVICE_SYSMESSAGE: process_packet_message(pkt); break; case YAHOO_SERVICE_NEWCONTACT: if(pkt->msg) process_packet_message(pkt); else process_packet_status(pkt); yahoo_get_config(context); break; case YAHOO_SERVICE_PING: process_packet_ping(pkt); break; } yahoo_free_packet(pkt); yahoo_free_rawpacket(rawpkt); } } int is_integer(char *s) { char *ptr; if(!s || !*s) return 0; ptr = s; if(((*ptr == '-') || (*ptr == '+')) && ptr[1]) ptr++; while(isdigit(*ptr)) ptr++; if(*ptr) return 0; return 1; } #ifdef USE_CURSES void myprintf(char *fmt, ...) { va_list ap; va_start(ap, fmt); vwprintw(displaywin, fmt, ap); va_end(ap); } #else void myprintf(char *fmt, ...) { va_list ap; va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); } #endif