/* * sample yahoo client based on libyahoo2 * * libyahoo2 is available at http://libyahoo2.sourceforge.net/ * * $Revision: 373 $ * $Date: 2010-06-02 13:55:25 -0700 (Wed, 02 Jun 2010) $ * * Copyright (C) 2002-2004, Philip S Tellis * Copyright (C) 2009, Siddhesh Poyarekar * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #if HAVE_CONFIG_H # include #endif #ifndef _WIN32 #include #include #endif #include #ifndef _WIN32 #include #include #include #include #endif #include #include #include #include #include #include #include #include #include #include #if HAVE_UNISTD_H #include #endif /* Get these from http://libyahoo2.sourceforge.net/ */ #include #include #include "yahoo_util.h" #ifndef _WIN32 int fileno(FILE * stream); #endif #define MAX_PREF_LEN 255 static char *local_host = NULL; static int do_mail_notify = 0; static int do_yahoo_debug = 0; static int ignore_system = 0; static int do_typing_notify = 1; static int accept_webcam_viewers = 1; static int send_webcam_images = 0; static int webcam_direction = YAHOO_WEBCAM_DOWNLOAD; static time_t curTime = 0; static time_t pingTimer = 0; static time_t webcamTimer = 0; static double webcamStart = 0; /* id of the webcam connection (needed for uploading) */ static int webcam_id = 0; static int poll_loop=1; static void register_callbacks(); typedef struct { char yahoo_id[255]; char password[255]; int id; int fd; int status; char *msg; } yahoo_local_account; typedef struct { char yahoo_id[255]; char name[255]; int status; int away; char *msg; char group[255]; } yahoo_account; typedef struct { int id; char *label; } yahoo_idlabel; typedef struct { int id; char *who; } yahoo_authorize_data; yahoo_idlabel yahoo_status_codes[] = { {YAHOO_STATUS_AVAILABLE, "Available"}, {YAHOO_STATUS_BRB, "BRB"}, {YAHOO_STATUS_BUSY, "Busy"}, {YAHOO_STATUS_NOTATHOME, "Not Home"}, {YAHOO_STATUS_NOTATDESK, "Not at Desk"}, {YAHOO_STATUS_NOTINOFFICE, "Not in Office"}, {YAHOO_STATUS_ONPHONE, "On Phone"}, {YAHOO_STATUS_ONVACATION, "On Vacation"}, {YAHOO_STATUS_OUTTOLUNCH, "Out to Lunch"}, {YAHOO_STATUS_STEPPEDOUT, "Stepped Out"}, {YAHOO_STATUS_INVISIBLE, "Invisible"}, {YAHOO_STATUS_IDLE, "Idle"}, {YAHOO_STATUS_OFFLINE, "Offline"}, {YAHOO_STATUS_CUSTOM, "[Custom]"}, {YPACKET_STATUS_NOTIFY, "Notify"}, {0, NULL} }; static void ext_yahoo_close(void *fd); char * yahoo_status_code(enum yahoo_status s) { int i; for(i=0; yahoo_status_codes[i].label; i++) if(yahoo_status_codes[i].id == s) return yahoo_status_codes[i].label; return "Unknown"; } #define YAHOO_DEBUGLOG ext_yahoo_log static int ext_yahoo_log(const char *fmt,...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); fflush(stderr); va_end(ap); return 0; } #define LOG(x) if(do_yahoo_debug) { YAHOO_DEBUGLOG("%s:%d: ", __FILE__, __LINE__); \ YAHOO_DEBUGLOG x; \ YAHOO_DEBUGLOG("\n"); } #define WARNING(x) if(do_yahoo_debug) { YAHOO_DEBUGLOG("%s:%d: warning: ", __FILE__, __LINE__); \ YAHOO_DEBUGLOG x; \ YAHOO_DEBUGLOG("\n"); } #define print_message(x) { printf x; printf("\n"); } static yahoo_local_account * ylad = NULL; static YList * buddies = NULL; static int expired(time_t timer) { if (timer && curTime >= timer) return 1; return 0; } static void rearm(time_t *timer, int seconds) { time(timer); *timer += seconds; } #ifndef _WIN32 FILE *popen(const char *command, const char *type); int pclose(FILE *stream); int gethostname(char *name, size_t len); #endif static char * get_local_addresses() { static char addresses[1024]; char buff[1024]; struct hostent * hn; #ifndef _WIN32 char gateway[16]; char * c; int i; FILE * f; f = popen("netstat -nr", "r"); if(!f) goto IP_TEST_2; while( fgets(buff, sizeof(buff), f) != NULL ) { c = strtok( buff, " " ); if( (strstr(c, "default") || strstr(c,"0.0.0.0") ) && !strstr(c, "127.0.0" ) ) break; } c = strtok( NULL, " " ); pclose(f); strncpy(gateway,c, 16); for(i = strlen(gateway); gateway[i] != '.'; i-- ) gateway[i] = 0; gateway[i] = 0; for(i = strlen(gateway); gateway[i] != '.'; i-- ) gateway[i] = 0; f = popen("/sbin/ifconfig -a", "r"); if(!f) goto IP_TEST_2; while( fgets(buff, sizeof(buff), f) != NULL ) { if( strstr(buff, "inet") && strstr(buff,gateway) ) break; } pclose(f); c = strtok( buff, " " ); c = strtok( NULL, " " ); strncpy ( addresses, c, sizeof(addresses) ); c = strtok(addresses, ":" ); strncpy ( buff, c, sizeof(buff) ); if((c=strtok(NULL, ":"))) strncpy( buff, c, sizeof(buff) ); strncpy(addresses, buff, sizeof(addresses)); return addresses; IP_TEST_2: #endif /* _WIN32 */ gethostname(buff,sizeof(buff)); hn = gethostbyname(buff); if(hn) strncpy(addresses, inet_ntoa( *((struct in_addr*)hn->h_addr)), sizeof(addresses) ); else addresses[0] = 0; return addresses; } static double get_time() { #ifndef _WIN32 struct timeval ct; gettimeofday(&ct, 0); /* return time in milliseconds */ return (ct.tv_sec * 1E3 + ct.tv_usec / 1E3); #else return timeGetTime(); #endif } static int yahoo_ping_timeout_callback() { print_message(("Sending a keep alive message")); yahoo_keepalive(ylad->id); rearm(&pingTimer, 600); return 1; } static int yahoo_webcam_timeout_callback(int id) { static unsigned image_num = 0; unsigned char *image = NULL; unsigned int length = 0; unsigned int timestamp = get_time() - webcamStart; char fname[1024]; FILE *f_image = NULL; struct stat s_image; if (send_webcam_images) { sprintf(fname, "images/image_%.3d.jpc", image_num++); if (image_num > 999) image_num = 0; if (stat(fname, &s_image) == -1) return -1; length = s_image.st_size; image = y_new0(unsigned char, length); if ((f_image = fopen(fname, "r")) != NULL) { fread(image, length, 1, f_image); fclose(f_image); } else { printf("Error reading from %s\n", fname); } } print_message(("Sending a webcam image (%d bytes)", length)); yahoo_webcam_send_image(id, image, length, timestamp); FREE(image); rearm(&webcamTimer, 2); return 1; } YList * conferences = NULL; typedef struct { int id; char * me; char * room_name; char * host; YList * members; int joined; } conf_room; static const char * get_buddy_name(const char * yid) { YList * b; for (b = buddies; b; b = b->next) { yahoo_account * ya = b->data; if(!strcmp(yid, ya->yahoo_id)) return ya->name&&*ya->name?ya->name:ya->yahoo_id; } return yid; } static conf_room * find_conf_room_by_name_and_id(int id, const char * me, const char * name) { YList * l; for(l = conferences; l; l=l->next) { conf_room * cr = l->data; if(cr->id == id && !strcmp(name, cr->room_name) && (me == NULL || cr->me == NULL || !strcmp(me, cr->me))) { return cr; } } return NULL; } static void ext_yahoo_got_conf_invite(int id, const char *me, const char *who, const char *room, const char *msg, YList *members) { conf_room * cr = y_new0(conf_room, 1); cr->room_name = strdup(room); cr->me = strdup(me); cr->host = strdup(who); cr->members = members; cr->id = id; conferences = y_list_append(conferences, cr); print_message(("%s has invited you [%s] to a conference: %s\n" "with the message: %s", who, me, room, msg)); for(; members; members=members->next) print_message(("\t%s", (char *)members->data)); } static void ext_yahoo_conf_userdecline(int id, const char *me, const char *who, const char *room, const char *msg) { YList * l; /* TODO: probably have to use the me arg to find the room */ conf_room * cr = find_conf_room_by_name_and_id(id, me, room); if(cr) for(l = cr->members; l; l=l->next) { char * w = l->data; if(!strcmp(w, who)) { FREE(l->data); cr->members = y_list_remove_link(cr->members, l); y_list_free_1(l); break; } } print_message(("%s declined the invitation from %s to %s\n" "with the message: %s", who, me, room, msg)); } static void ext_yahoo_conf_userjoin(int id, const char *me, const char *who, const char *room) { conf_room * cr = find_conf_room_by_name_and_id(id, me, room); if(cr) { YList * l = NULL; for(l = cr->members; l; l=l->next) { char * w = l->data; if(!strcmp(w, who)) break; } if(!l) cr->members = y_list_append(cr->members, strdup(who)); } print_message(("%s joined the conference %s [%s]", who, room, me)); } static void ext_yahoo_conf_userleave(int id, const char *me, const char *who, const char *room) { YList * l; conf_room * cr = find_conf_room_by_name_and_id(id, me, room); if(cr) for(l = cr->members; l; l=l->next) { char * w = l->data; if(!strcmp(w, who)) { FREE(l->data); cr->members = y_list_remove_link(cr->members, l); y_list_free_1(l); break; } } print_message(("%s left the conference %s [%s]", who, room, me)); } static void ext_yahoo_conf_message(int id, const char *me, const char *who, const char *room, const char *msg, int utf8) { char * umsg = (char *)msg; if(utf8) umsg = y_utf8_to_str(msg); who = get_buddy_name(who); print_message(("%s (in %s [%s]): %s", who, room, me, umsg)); if(utf8) FREE(umsg); } static void print_chat_member(struct yahoo_chat_member *ycm) { printf("%s (%s) ", ycm->id, ycm->alias); printf(" Age: %d Sex: ", ycm->age); if (ycm->attribs & YAHOO_CHAT_MALE) { printf("Male"); } else if (ycm->attribs & YAHOO_CHAT_FEMALE) { printf("Female"); } else { printf("Unknown"); } if (ycm->attribs & YAHOO_CHAT_WEBCAM) { printf(" with webcam"); } printf(" Location: %s", ycm->location); } static void ext_yahoo_chat_cat_xml(int id, const char *xml) { print_message(("%s", xml)); } static void ext_yahoo_chat_join(int id, const char *me, const char *room, const char * topic, YList *members, void *fd) { print_message(("You [%s] have joined the chatroom %s with topic %s", me, room, topic)); while(members) { YList *n = members->next; printf("\t"); print_chat_member(members->data); printf("\n"); FREE(((struct yahoo_chat_member *)members->data)->id); FREE(((struct yahoo_chat_member *)members->data)->alias); FREE(((struct yahoo_chat_member *)members->data)->location); FREE(members->data); FREE(members); members=n; } } static void ext_yahoo_chat_userjoin(int id, const char *me, const char *room, struct yahoo_chat_member *who) { print_chat_member(who); print_message((" joined the chatroom %s [%s]", room, me)); FREE(who->id); FREE(who->alias); FREE(who->location); FREE(who); } static void ext_yahoo_chat_userleave(int id, const char *me, const char *room, const char *who) { print_message(("%s left the chatroom %s [%s]", who, room, me)); } static void ext_yahoo_chat_message(int id, const char *me, const char *who, const char *room, const char *msg, int msgtype, int utf8) { char * umsg = (char *)msg; char * charpos; if(utf8) umsg = y_utf8_to_str(msg); /* Remove escapes */ charpos = umsg; while(*charpos) { if (*charpos == 0x1b) { *charpos = ' '; } charpos++; } if (msgtype == 2) { print_message(("(in %s [%s]) %s %s", room, me, who, umsg)); } else { print_message(("(in %s [%s]) %s: %s", room, me, who, umsg)); } if(utf8) FREE(umsg); } static void ext_yahoo_status_changed(int id, const char *who, int stat, const char *msg, int away, int idle, int mobile) { yahoo_account * ya=NULL; YList * b; char buf[1024]; for(b = buddies; b; b = b->next) { if(!strcmp(((yahoo_account *)b->data)->yahoo_id, who)) { ya = b->data; break; } } if (msg == NULL) { sprintf(buf, "%s", yahoo_status_code(stat)); } else if (stat == YAHOO_STATUS_CUSTOM) { sprintf(buf, "%s", msg); } else { sprintf(buf, "%s: %s", yahoo_status_code(stat), msg); } if (away > 0) { char away_buf[32]; sprintf(away_buf, " away[%d]", away); strcat(buf, away_buf); } if (mobile > 0) { char mobile_buf[32]; sprintf(mobile_buf, " mobile[%d]", mobile); strcat(buf, mobile_buf); } if (idle > 0) { char time_buf[32]; sprintf(time_buf, " idle for %d:%02d:%02d", idle/3600, (idle/60)%60, idle%60); strcat(buf, time_buf); } print_message(("%s (%s) is now %s", ya?ya->name:who, who, buf)) if(ya) { ya->status = stat; ya->away = away; if(msg) { FREE(ya->msg); ya->msg = strdup(msg); } } } static void ext_yahoo_got_buddies(int id, YList * buds) { while(buddies) { FREE(buddies->data); buddies = buddies->next; if(buddies) FREE(buddies->prev); } for(; buds; buds = buds->next) { yahoo_account *ya = y_new0(yahoo_account, 1); struct yahoo_buddy *bud = buds->data; strncpy(ya->yahoo_id, bud->id, 255); if(bud->real_name) strncpy(ya->name, bud->real_name, 255); strncpy(ya->group, bud->group, 255); ya->status = YAHOO_STATUS_OFFLINE; buddies = y_list_append(buddies, ya); /* print_message(("%s is %s", bud->id, bud->real_name));*/ } } static void ext_yahoo_got_ignore(int id, YList * igns) { } static void ext_yahoo_got_buzz(int id, const char *me, const char *who, long tm) { who = get_buddy_name(who); printf("\a"); if(tm) { char timestr[255]; strncpy(timestr, ctime((time_t *)&tm), sizeof(timestr)); timestr[strlen(timestr) - 1] = '\0'; print_message(("[Offline message at %s to %s from %s]: **DING**", timestr, me, who)) } else print_message(("[%s]%s: **DING**", me, who)) } static void ext_yahoo_got_im(int id, const char *me, const char *who, const char *msg, long tm, int stat, int utf8) { char *umsg = (char *)msg; if(stat == 2) { LOG(("Error sending message from %s to %s", me, who)); return; } if(!msg) return; if(utf8) umsg = y_utf8_to_str(msg); who = get_buddy_name(who); if(tm) { char timestr[255]; strncpy(timestr, ctime((time_t *)&tm), sizeof(timestr)); timestr[strlen(timestr) - 1] = '\0'; print_message(("[Offline message at %s to %s from %s]: %s", timestr, me, who, umsg)) } else print_message(("[%s]%s: %s", me, who, umsg)) if(utf8) FREE(umsg); } static void ext_yahoo_rejected(int id, const char *who, const char *msg) { print_message(("%s has rejected you%s%s", who, (msg?" with the message:\n":"."), (msg?msg:""))); } static void ext_yahoo_contact_added(int id, const char *myid, const char *who, const char *msg) { char buff[1024]; snprintf(buff, sizeof(buff), "%s, the yahoo user %s has added you to their contact list", myid, who); if(msg) { strcat(buff, " with the following message:\n"); strcat(buff, msg); strcat(buff, "\n"); } else { strcat(buff, ". "); } strcat(buff, "Do you want to allow this [Y/N]?"); /* print_message((buff)); scanf("%c", &choice); if(choice != 'y' && choice != 'Y') yahoo_reject_buddy(id, who, "Thanks, but no thanks."); */ } static void ext_yahoo_typing_notify(int id, const char* me, const char *who, int stat) { if(stat && do_typing_notify) print_message(("[%s]%s is typing...", me, who)); } static void ext_yahoo_game_notify(int id, const char *me, const char *who, int stat, const char *msg) { } static void ext_yahoo_mail_notify(int id, const char *from, const char *subj, int cnt) { char buff[1024] = {0}; if(!do_mail_notify) return; if(from && subj) snprintf(buff, sizeof(buff), "You have new mail from %s about %s\n", from, subj); if(cnt) { char buff2[100]; snprintf(buff2, sizeof(buff2), "You have %d message%s\n", cnt, cnt==1?"":"s"); strcat(buff, buff2); } if(buff[0]) print_message((buff)); } static void ext_yahoo_got_webcam_image(int id, const char *who, const unsigned char *image, unsigned int image_size, unsigned int real_size, unsigned int timestamp) { static unsigned char *cur_image = NULL; static unsigned int cur_image_len = 0; static unsigned int image_num = 0; FILE* f_image; char fname[1024]; /* copy image part to cur_image */ if (real_size) { if (!cur_image) cur_image = y_new0(unsigned char, image_size); memcpy(cur_image + cur_image_len, image, real_size); cur_image_len += real_size; } if (image_size == cur_image_len) { print_message(("Received a image update at %d (%d bytes)", timestamp, image_size)); /* if we recieved an image then write it to file */ if (image_size) { sprintf(fname, "images/%s_%.3d.jpc", who, image_num++); if ((f_image = fopen(fname, "w")) != NULL) { fwrite(cur_image, image_size, 1, f_image); fclose(f_image); } else { printf("Error writing to %s\n", fname); } FREE(cur_image); cur_image_len = 0; if (image_num > 999) image_num = 0; } } } static void ext_yahoo_webcam_viewer(int id, const char *who, int connect) { switch (connect) { case 0: print_message(("%s has stopped viewing your webcam", who)); break; case 1: print_message(("%s has started viewing your webcam", who)); break; case 2: print_message(("%s is trying to view your webcam", who)); yahoo_webcam_accept_viewer(id, who, accept_webcam_viewers); break; } } static void ext_yahoo_webcam_closed(int id, const char *who, int reason) { switch(reason) { case 1: print_message(("%s stopped broadcasting", who)); break; case 2: print_message(("%s cancelled viewing permission", who)); break; case 3: print_message(("%s declines permission to view his/her webcam", who)); break; case 4: print_message(("%s does not have his/her webcam online", who)); break; } } static void ext_yahoo_webcam_data_request(int id, int send) { webcam_id = id; if (send) { print_message(("Got request to start sending images")); if (!webcamTimer) rearm(&webcamTimer, 2); } else { print_message(("Got request to stop sending images")); } send_webcam_images = send; } static void ext_yahoo_webcam_invite(int id, const char *me, const char *from) { print_message(("Got a webcam invitation to %s from %s", me, from)); } static void ext_yahoo_webcam_invite_reply(int id, const char *me, const char *from, int accept) { if(accept) { print_message(("[%s]%s accepted webcam invitation...", me, from)); } else { print_message(("[%s]%s declined webcam invitation...", me, from)); } } static void ext_yahoo_system_message(int id, const char *me, const char *who, const char *msg) { if(ignore_system) return; print_message(("Yahoo System Message: %s", msg)); } void yahoo_logout() { if (ylad->id <= 0) { return; } pingTimer=0; while(conferences) { YList * n = conferences->next; conf_room * cr = conferences->data; if(cr->joined) yahoo_conference_logoff(ylad->id, NULL, cr->members, cr->room_name); FREE(cr->me); FREE(cr->room_name); FREE(cr->host); while(cr->members) { YList *n = cr->members->next; FREE(cr->members->data); FREE(cr->members); cr->members=n; } FREE(cr); FREE(conferences); conferences = n; } yahoo_logoff(ylad->id); yahoo_close(ylad->id); ylad->status = YAHOO_STATUS_OFFLINE; ylad->id = 0; poll_loop=0; print_message(("logged_out")); } static void ext_yahoo_login(yahoo_local_account * ylad, int login_mode) { LOG(("ext_yahoo_login")); ylad->id = yahoo_init_with_attributes(ylad->yahoo_id, ylad->password, "local_host", local_host, "pager_port", 5050, NULL); ylad->status = YAHOO_STATUS_OFFLINE; yahoo_login(ylad->id, login_mode); /* if (ylad->id <= 0) { print_message(("Could not connect to Yahoo server. Please verify that you are connected to the net and the pager host and port are correctly entered.")); return; } */ rearm(&pingTimer, 600); } static void ext_yahoo_got_cookies(int id) { /*yahoo_get_yab(id);*/ } static void ext_yahoo_login_response(int id, int succ, const char *url) { char buff[1024]; if(succ == YAHOO_LOGIN_OK) { ylad->status = yahoo_current_status(id); print_message(("logged in")); return; } else if(succ == YAHOO_LOGIN_UNAME) { snprintf(buff, sizeof(buff), "Could not log into Yahoo service - username not recognised. Please verify that your username is correctly typed."); } else if(succ == YAHOO_LOGIN_PASSWD) { snprintf(buff, sizeof(buff), "Could not log into Yahoo service - password incorrect. Please verify that your password is correctly typed."); } else if(succ == YAHOO_LOGIN_LOCK) { snprintf(buff, sizeof(buff), "Could not log into Yahoo service. Your account has been locked.\nVisit %s to reactivate it.", url); } else if(succ == YAHOO_LOGIN_DUPL) { snprintf(buff, sizeof(buff), "You have been logged out of the yahoo service, possibly due to a duplicate login."); } else if(succ == YAHOO_LOGIN_SOCK) { snprintf(buff, sizeof(buff), "The server closed the socket."); } else { snprintf(buff, sizeof(buff), "Could not log in, unknown reason: %d.", succ); } ylad->status = YAHOO_STATUS_OFFLINE; print_message((buff)); yahoo_logout(); poll_loop=0; } static void ext_yahoo_error(int id, const char *err, int fatal, int num) { fprintf(stdout, "Yahoo Error: "); fprintf(stdout, "%s", err); switch(num) { case E_UNKNOWN: fprintf(stdout, "unknown error %s", err); break; case E_CUSTOM: fprintf(stdout, "custom error %s", err); break; case E_CONFNOTAVAIL: fprintf(stdout, "%s is not available for the conference", err); break; case E_IGNOREDUP: fprintf(stdout, "%s is already ignored", err); break; case E_IGNORENONE: fprintf(stdout, "%s is not in the ignore list", err); break; case E_IGNORECONF: fprintf(stdout, "%s is in buddy list - cannot ignore ", err); break; case E_SYSTEM: fprintf(stdout, "system error %s", err); break; case E_CONNECTION: fprintf(stdout, "server connection error %s", err); break; } fprintf(stdout, "\n"); if(fatal) yahoo_logout(); } void yahoo_set_current_state(int yahoo_state) { if (ylad->status == YAHOO_STATUS_OFFLINE && yahoo_state != YAHOO_STATUS_OFFLINE) { ext_yahoo_login(ylad, yahoo_state); return; } else if (ylad->status != YAHOO_STATUS_OFFLINE && yahoo_state == YAHOO_STATUS_OFFLINE) { yahoo_logout(); return; } ylad->status = yahoo_state; if(yahoo_state == YAHOO_STATUS_CUSTOM) { if(ylad->msg) yahoo_set_away(ylad->id, yahoo_state, ylad->msg, 1); else yahoo_set_away(ylad->id, yahoo_state, "delta p * delta x too large", 1); } else yahoo_set_away(ylad->id, yahoo_state, NULL, 1); } static int ext_yahoo_connect(const char *host, int port) { WARNING(("This should not be used anymore. File a bug report.")); return -1; } /************************************* * Callback handling code starts here */ YList *connections = NULL; struct _conn { int fd; SSL *ssl; int use_ssl; int remove; }; struct conn_handler { struct _conn *con; int id; int tag; yahoo_input_condition cond; int remove; void *data; }; static int connection_tags=0; static int ext_yahoo_add_handler(int id, void *d, yahoo_input_condition cond, void *data) { struct conn_handler *h = y_new0(struct conn_handler, 1); h->id = id; h->tag = ++connection_tags; h->con = d; h->cond = cond; h->data = data; LOG(("Add %d(%d) for %d, tag %d", h->con->fd, cond, id, h->tag)); connections = y_list_prepend(connections, h); return h->tag; } static void ext_yahoo_remove_handler(int id, int tag) { YList *l; if (!tag) return; for(l = connections; l; l = y_list_next(l)) { struct conn_handler *c = l->data; if(c->tag == tag) { /* don't actually remove it, just mark it for removal */ /* we'll remove when we start the next poll cycle */ LOG(("Marking id:%d fd:%p tag:%d for removal", c->id, c->con, c->tag)); c->remove = 1; return; } } } static SSL *do_ssl_connect(int fd) { SSL *ssl; SSL_CTX *ctx; LOG(("SSL Handshake")); SSL_library_init (); ctx = SSL_CTX_new(SSLv23_client_method()); ssl = SSL_new(ctx); SSL_CTX_free(ctx); SSL_set_fd(ssl, fd); if (SSL_connect(ssl) == 1) return ssl; return NULL; } struct connect_callback_data { yahoo_connect_callback callback; void * callback_data; int id; int tag; }; static void connect_complete(void *data, struct _conn *source, yahoo_input_condition condition) { struct connect_callback_data *ccd = data; int error, err_size = sizeof(error); ext_yahoo_remove_handler(0, ccd->tag); getsockopt(source->fd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&err_size); if(error) goto err; LOG(("Connected fd: %d, error: %d", source->fd, error)); if (source->use_ssl) { source->ssl = do_ssl_connect(source->fd); if (!source->ssl) { err: LOG(("SSL Handshake Failed!")); ext_yahoo_close(source); ccd->callback(NULL, 0, ccd->callback_data); FREE(ccd); return; } } fcntl(source->fd, F_SETFL, O_NONBLOCK); ccd->callback(source, error, ccd->callback_data); FREE(ccd); } void yahoo_callback(struct conn_handler *c, yahoo_input_condition cond) { int ret=1; char buff[1024]={0}; if(c->id < 0) { connect_complete(c->data, c->con, cond); } else { if(cond & YAHOO_INPUT_READ) ret = yahoo_read_ready(c->id, c->con, c->data); if(ret>0 && cond & YAHOO_INPUT_WRITE) ret = yahoo_write_ready(c->id, c->con, c->data); if(ret == -1) snprintf(buff, sizeof(buff), "Yahoo read error (%d): %s", errno, strerror(errno)); else if(ret == 0) snprintf(buff, sizeof(buff), "Yahoo read error: Server closed socket"); if(buff[0]) print_message((buff)); } } static int ext_yahoo_write(void *fd, char *buf, int len) { struct _conn *c = fd; if (c->use_ssl) return SSL_write(c->ssl, buf, len); else return write(c->fd, buf, len); } static int ext_yahoo_read(void *fd, char *buf, int len) { struct _conn *c = fd; if (c->use_ssl) return SSL_read(c->ssl, buf, len); else return read(c->fd, buf, len); } static void ext_yahoo_close(void *fd) { struct _conn *c = fd; YList *l; if (c->use_ssl) SSL_free(c->ssl); close(c->fd); c->fd = 0; /* Remove all handlers */ for (l = connections; l; l = y_list_next(l)) { struct conn_handler *h = l->data; if (h->con == c) h->remove = 1; } c->remove = 1; } static int ext_yahoo_connect_async(int id, const char *host, int port, yahoo_connect_callback callback, void *data, int use_ssl) { struct sockaddr_in serv_addr; static struct hostent *server; int servfd; struct connect_callback_data * ccd; int error; SSL *ssl = NULL; struct _conn *c; LOG(("Connecting to %s:%d", host, port)); if(!(server = gethostbyname(host))) { errno=h_errno; return -1; } if((servfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { return -1; } memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; memcpy(&serv_addr.sin_addr.s_addr, *server->h_addr_list, server->h_length); serv_addr.sin_port = htons(port); c = y_new0(struct _conn, 1); c->fd = servfd; c->use_ssl = use_ssl; error = connect(servfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)); LOG(("Trying to connect: fd:%d error:%d", servfd, error)); if(!error) { LOG(("Connected")); if (use_ssl) { ssl = do_ssl_connect(servfd); if (!ssl) { LOG(("SSL Handshake Failed!")); ext_yahoo_close(c); callback(NULL, 0, data); return -1; } } c->ssl = ssl; fcntl(c->fd, F_SETFL, O_NONBLOCK); callback(c, 0, data); return 0; } else if(error == -1 && errno == EINPROGRESS) { ccd = calloc(1, sizeof(struct connect_callback_data)); ccd->callback = callback; ccd->callback_data = data; ccd->id = id; ccd->tag = ext_yahoo_add_handler(-1, c, YAHOO_INPUT_WRITE, ccd); return ccd->tag; } else { if(error == -1) LOG(("Connection failure: %s", strerror(errno))); ext_yahoo_close(c); callback(NULL, errno, data); return -1; } } /* * Callback handling code ends here ***********************************/ static void process_commands(char *line) { char *cmd, *to, *msg; char *tmp, *start; char *copy = strdup(line); int yid = 0; enum yahoo_status state; start = cmd = copy; tmp = strchr(copy, ' '); if(tmp) { *tmp = '\0'; copy = tmp+1; } else { copy = NULL; } if(!strncasecmp(cmd, "MSG", strlen("MSG"))) { /* send a message */ to = copy; tmp = strchr(copy, ' '); if(tmp) { *tmp = '\0'; copy = tmp+1; } msg = copy; if(to && msg) { if(!strcmp(msg, "\a")) yahoo_send_im(ylad->id, NULL, to, "", 0,0); else { msg = y_str_to_utf8(msg); yahoo_send_im(ylad->id, NULL, to, msg, 1,0); FREE(msg) } } } else if(!strncasecmp(cmd, "CMS", strlen("CMS"))) { /* send a message */ conf_room * cr; to = copy; tmp = strchr(copy, ' '); if(tmp) { *tmp = '\0'; copy = tmp+1; } msg = copy; cr = find_conf_room_by_name_and_id(ylad->id, NULL, to); if(!cr) { print_message(("no such room: %s", copy)); goto end_parse; } if(msg) yahoo_conference_message(ylad->id, NULL, cr->members, to, msg, 0); } else if(!strncasecmp(cmd, "CLS", strlen("CLS"))) { YList * l; if(copy) { conf_room * cr = find_conf_room_by_name_and_id(ylad->id, NULL, copy); if(!cr) { print_message(("no such room: %s", copy)); goto end_parse; } print_message(("Room: %s", copy)); for(l = cr->members; l; l=l->next) { print_message(("%s", (char *)l->data)) } } else { print_message(("All Rooms:")); for(l = conferences; l; l=l->next) { conf_room * cr = l->data; print_message(("%s", cr->room_name)); } } } else if(!strncasecmp(cmd, "CCR", strlen("CCR"))) { conf_room * cr = y_new0(conf_room, 1); while((tmp = strchr(copy, ' ')) != NULL) { *tmp = '\0'; if(!cr->room_name) cr->room_name = strdup(copy); else cr->members = y_list_append(cr->members, strdup(copy)); copy = tmp+1; } cr->members = y_list_append(cr->members, strdup(copy)); if(!cr->room_name || !cr->members) { FREE(cr); } else { cr->id = ylad->id; cr->joined = 1; conferences = y_list_append(conferences, cr); yahoo_conference_invite(ylad->id, NULL, cr->members, cr->room_name, "Join my conference"); cr->members = y_list_append(cr->members,strdup(ylad->yahoo_id)); } } else if(!strncasecmp(cmd, "CIN", strlen("CIN"))) { conf_room * cr; char * room=copy; YList * l1, *l = NULL; while((tmp = strchr(copy, ' ')) != NULL) { *tmp = '\0'; copy = tmp+1; l = y_list_append(l, copy); } cr = find_conf_room_by_name_and_id(ylad->id, NULL, room); if(!cr) { print_message(("no such room: %s", room)); y_list_free(l); goto end_parse; } for(l1 = l; l1; l1=l1->next) { char * w = l1->data; yahoo_conference_addinvite(ylad->id, NULL, w, room, cr->members, "Join my conference"); cr->members = y_list_append(cr->members, strdup(w)); } y_list_free(l); } else if(!strncasecmp(cmd, "CLN", strlen("CLN"))) { conf_room * cr = find_conf_room_by_name_and_id(ylad->id, NULL, copy); YList * l; if(!cr) { print_message(("no such room: %s", copy)); goto end_parse; } cr->joined = 1; for(l = cr->members; l; l=l->next) { char * w = l->data; if(!strcmp(w, ylad->yahoo_id)) break; } if(!l) cr->members = y_list_append(cr->members, strdup(ylad->yahoo_id)); yahoo_conference_logon(ylad->id, NULL, cr->members, copy); } else if(!strncasecmp(cmd, "CLF", strlen("CLF"))) { conf_room * cr = find_conf_room_by_name_and_id(ylad->id, NULL, copy); if(!cr) { print_message(("no such room: %s", copy)); goto end_parse; } yahoo_conference_logoff(ylad->id, NULL, cr->members, copy); conferences = y_list_remove(conferences, cr); FREE(cr->room_name); FREE(cr->host); while(cr->members) { YList *n = cr->members->next; FREE(cr->members->data); FREE(cr->members); cr->members=n; } FREE(cr); } else if(!strncasecmp(cmd, "CDC", strlen("CDC"))) { conf_room * cr; char * room = copy; tmp = strchr(copy, ' '); if(tmp) { *tmp = '\0'; copy = tmp+1; msg = copy; } else { msg = "Thanks, but no thanks!"; } cr = find_conf_room_by_name_and_id(ylad->id, NULL, room); if(!cr) { print_message(("no such room: %s", room)); goto end_parse; } yahoo_conference_decline(ylad->id, NULL, cr->members, room,msg); conferences = y_list_remove(conferences, cr); FREE(cr->room_name); FREE(cr->host); while(cr->members) { YList *n = cr->members->next; FREE(cr->members->data); FREE(cr->members); cr->members=n; } FREE(cr); } else if(!strncasecmp(cmd, "CHL", strlen("CHL"))) { int roomid; roomid = atoi(copy); yahoo_get_chatrooms(ylad->id, roomid); } else if(!strncasecmp(cmd, "CHJ", strlen("CHJ"))) { char *roomid, *roomname; /* Linux, FreeBSD, Solaris:1 */ /* 1600326591 */ roomid = copy; tmp = strchr(copy, ' '); if(tmp) { *tmp = '\0'; copy = tmp+1; } roomname = copy; if(roomid && roomname) { yahoo_chat_logon(ylad->id, NULL, roomname, roomid); } } else if(!strncasecmp(cmd, "CHM", strlen("CHM"))) { char *msg, *roomname; roomname = copy; tmp = strstr(copy, " "); if(tmp) { *tmp = '\0'; copy = tmp+2; } msg = copy; if(roomname && msg) { yahoo_chat_message(ylad->id, NULL, roomname, msg, 1, 0); } } else if(!strncasecmp(cmd, "CHX", strlen("CHX"))) { yahoo_chat_logoff(ylad->id, NULL); } else if(!strncasecmp(cmd, "STA", strlen("STA"))) { if(isdigit(copy[0])) { state = (enum yahoo_status)atoi(copy); copy = strchr(copy, ' '); if(state == 99) { if(copy) msg = copy; else msg = "delta x * delta p too large"; } else msg = NULL; } else { state = YAHOO_STATUS_CUSTOM; msg = copy; } yahoo_set_away(ylad->id, state, msg, 1); } else if(!strncasecmp(cmd, "OFF", strlen("OFF"))) { /* go offline */ printf("Going offline\n"); poll_loop=0; } else if(!strncasecmp(cmd, "IDS", strlen("IDS"))) { /* print identities */ const YList * ids = yahoo_get_identities(ylad->id); printf("Identities: "); for(; ids; ids = ids->next) printf("%s, ", (char *)ids->data); printf("\n"); } else if(!strncasecmp(cmd, "AID", strlen("AID"))) { /* activate identity */ yahoo_set_identity_status(ylad->id, copy, 1); } else if(!strncasecmp(cmd, "DID", strlen("DID"))) { /* deactivate identity */ yahoo_set_identity_status(ylad->id, copy, 0); } else if(!strncasecmp(cmd, "LST", strlen("LST"))) { YList * b = buddies; for(; b; b=b->next) { yahoo_account * ya = b->data; if(ya->status == YAHOO_STATUS_OFFLINE) continue; if(ya->msg) print_message(("%s (%s) is now %s", ya->name, ya->yahoo_id, ya->msg)) else print_message(("%s (%s) is now %s", ya->name, ya->yahoo_id, yahoo_status_code(ya->status))) } } else if(!strncasecmp(cmd, "NAM", strlen("NAM"))) { struct yab * yab; to = copy; tmp = strchr(copy, ' '); if(tmp) { *tmp = '\0'; copy = tmp+1; } yid = atoi(copy); tmp = strchr(copy, ' '); if(tmp) { *tmp = '\0'; copy = tmp+1; } msg = copy; if(to && msg) { yab = y_new0(struct yab, 1); yab->id = to; yab->yid = yid; /* Only do this if you have got it from the server */ yab->nname = msg; yahoo_set_yab(ylad->id, yab); FREE(yab); } } else if(!strncasecmp(cmd, "WCAM", strlen("WCAM"))) { if (copy) { printf("Viewing webcam (%s)\n", copy); webcam_direction = YAHOO_WEBCAM_DOWNLOAD; yahoo_webcam_get_feed(ylad->id, copy); } else { printf("Starting webcam\n"); webcam_direction = YAHOO_WEBCAM_UPLOAD; yahoo_webcam_get_feed(ylad->id, NULL); } } else if(!strncasecmp(cmd, "WINV", strlen("WINV"))) { printf("Inviting %s to view webcam\n", copy); yahoo_webcam_invite(ylad->id, copy); } else { fprintf(stderr, "Unknown command: %s\n", cmd); } end_parse: FREE(start); } #ifndef _WIN32 static void local_input_callback(int source) { char line[1024] = {0}; int i; char c; i=0; c=0; do { if(read(source, &c, 1) <= 0) c='\0'; if(c == '\r') continue; if(c == '\n') break; if(c == '\b') { if(!i) continue; c = '\0'; i--; } if(c) { line[i++] = c; line[i]='\0'; } } while(i<1023 && c != '\n'); if(line[0]) process_commands(line); } #else #include static void local_input_callback(char c) { static char line[1024] = {0}; static int line_length = 0; if (c == '\b' || (int)c == 127) { if (line_length > 0) { _cputs("\b \b"); line_length--; } return; } if (c == '\n' || c == '\r' || c == 3) { _cputs("\n"); line[line_length] = 0; process_commands(line); line_length = 0; line[0] = 0; return; } _putch(c); line[line_length++] = c; } #endif int main(int argc, char * argv[]) { int status; int log_level; int lfd=0; fd_set inp, outp; struct timeval tv; #ifndef _WIN32 int fd_stdin = fileno(stdin); #endif YList *l=connections; #ifdef _WIN32 WSADATA wsa; if (WSAStartup(MAKEWORD(2,2), &wsa)) return -1; #endif ylad = y_new0(yahoo_local_account, 1); local_host = strdup(get_local_addresses()); printf("Yahoo Id: "); scanf("%s", ylad->yahoo_id); printf("Password: "); #ifdef _WIN32 scanf("%s", ylad->password); #else { tcflag_t oflags; struct termios term; tcgetattr(fd_stdin, &term); oflags = term.c_lflag; term.c_lflag = oflags & ~(ECHO | ECHOK | ICANON); term.c_cc[VTIME] = 1; tcsetattr(fd_stdin, TCSANOW, &term); scanf("%s", ylad->password); term.c_lflag = oflags; term.c_cc[VTIME] = 0; tcsetattr(fd_stdin, TCSANOW, &term); } printf("\n"); #endif printf("Initial Status: "); scanf("%d", &status); printf("Log Level: "); scanf("%d", &log_level); do_yahoo_debug=log_level; register_callbacks(); yahoo_set_log_level(log_level); ext_yahoo_login(ylad, status); while(poll_loop) { FD_ZERO(&inp); FD_ZERO(&outp); #ifndef _WIN32 FD_SET(fd_stdin, &inp); tv.tv_sec=1; tv.tv_usec=0; #else tv.tv_sec=0; tv.tv_usec=1E4; #endif lfd=0; for(l=connections; l; ) { struct conn_handler *c = l->data; if(c->remove) { YList *n = y_list_next(l); LOG(("Removing id:%d fd:%d", c->id, c->con->fd)); connections = y_list_remove_link(connections, l); y_list_free_1(l); FREE(c); l=n; } else { if(c->cond & YAHOO_INPUT_READ) FD_SET(c->con->fd, &inp); if(c->cond & YAHOO_INPUT_WRITE) FD_SET(c->con->fd, &outp); if(lfd < c->con->fd) lfd = c->con->fd; l = y_list_next(l); } } select(lfd + 1, &inp, &outp, NULL, &tv); time(&curTime); #ifndef _WIN32 if(FD_ISSET(fd_stdin, &inp)) local_input_callback(0); #else if (_kbhit()) local_input_callback(_getch()); #endif for(l = connections; l; l = y_list_next(l)) { struct conn_handler *c = l->data; if(c->con->remove) { FREE(c->con); c->con = NULL; continue; } if(c->remove) continue; if(FD_ISSET(c->con->fd, &inp)) yahoo_callback(c, YAHOO_INPUT_READ); if(FD_ISSET(c->con->fd, &outp)) yahoo_callback(c, YAHOO_INPUT_WRITE); } if(expired(pingTimer)) yahoo_ping_timeout_callback(); if(expired(webcamTimer)) yahoo_webcam_timeout_callback(webcam_id); } LOG(("Exited loop")); while(connections) { YList *tmp = connections; struct conn_handler *c = connections->data; FREE(c); connections = y_list_remove_link(connections, connections); y_list_free_1(tmp); } yahoo_logout(); FREE(ylad); #ifdef _WIN32 WSACleanup(); #endif return 0; } static void ext_yahoo_got_file(int id, const char *me, const char *who, const char *msg, const char *fname, unsigned long fesize, char *trid) { LOG(("Got a File transfer request (%s, %ld bytes) from %s", fname, fesize, who)); } static void ext_yahoo_file_transfer_done(int id, int response, void *data) { } static char *ext_yahoo_get_ip_addr(const char *domain) { return NULL; } static void ext_yahoo_got_ft_data(int id, const unsigned char *in, int count, void *data) { } static void ext_yahoo_got_identities(int id, YList * ids) { } static void ext_yahoo_chat_yahoologout(int id, const char *me) { LOG(("got chat logout for %s", me)); } static void ext_yahoo_chat_yahooerror(int id, const char *me) { LOG(("got chat error for %s", me)); } static void ext_yahoo_got_ping(int id, const char *errormsg) { LOG(("got ping errormsg %s", errormsg)); } static void ext_yahoo_got_search_result(int id, int found, int start, int total, YList *contacts) { LOG(("got search result")); } static void ext_yahoo_got_buddyicon_checksum(int id, const char *a, const char *b, int checksum) { LOG(("got buddy icon checksum")); } static void ext_yahoo_got_buddy_change_group(int id, const char *me, const char *who, const char *old_group, const char *new_group) { } static void ext_yahoo_got_buddyicon(int id, const char *a, const char *b, const char *c, int checksum) { LOG(("got buddy icon")); } static void ext_yahoo_buddyicon_uploaded(int id, const char *url) { LOG(("buddy icon uploaded")); } static void ext_yahoo_got_buddyicon_request(int id, const char *me, const char *who) { LOG(("got buddy icon request from %s",who)); } static void register_callbacks() { static struct yahoo_callbacks yc; yc.ext_yahoo_login_response = ext_yahoo_login_response; yc.ext_yahoo_got_buddies = ext_yahoo_got_buddies; yc.ext_yahoo_got_ignore = ext_yahoo_got_ignore; yc.ext_yahoo_got_identities = ext_yahoo_got_identities; yc.ext_yahoo_got_cookies = ext_yahoo_got_cookies; yc.ext_yahoo_status_changed = ext_yahoo_status_changed; yc.ext_yahoo_got_im = ext_yahoo_got_im; yc.ext_yahoo_got_buzz = ext_yahoo_got_buzz; yc.ext_yahoo_got_conf_invite = ext_yahoo_got_conf_invite; yc.ext_yahoo_conf_userdecline = ext_yahoo_conf_userdecline; yc.ext_yahoo_conf_userjoin = ext_yahoo_conf_userjoin; yc.ext_yahoo_conf_userleave = ext_yahoo_conf_userleave; yc.ext_yahoo_conf_message = ext_yahoo_conf_message; yc.ext_yahoo_chat_cat_xml = ext_yahoo_chat_cat_xml; yc.ext_yahoo_chat_join = ext_yahoo_chat_join; yc.ext_yahoo_chat_userjoin = ext_yahoo_chat_userjoin; yc.ext_yahoo_chat_userleave = ext_yahoo_chat_userleave; yc.ext_yahoo_chat_message = ext_yahoo_chat_message; yc.ext_yahoo_chat_yahoologout = ext_yahoo_chat_yahoologout; yc.ext_yahoo_chat_yahooerror = ext_yahoo_chat_yahooerror; yc.ext_yahoo_got_webcam_image = ext_yahoo_got_webcam_image; yc.ext_yahoo_webcam_invite = ext_yahoo_webcam_invite; yc.ext_yahoo_webcam_invite_reply = ext_yahoo_webcam_invite_reply; yc.ext_yahoo_webcam_closed = ext_yahoo_webcam_closed; yc.ext_yahoo_webcam_viewer = ext_yahoo_webcam_viewer; yc.ext_yahoo_webcam_data_request = ext_yahoo_webcam_data_request; yc.ext_yahoo_got_file = ext_yahoo_got_file; yc.ext_yahoo_got_ft_data = ext_yahoo_got_ft_data; yc.ext_yahoo_get_ip_addr = ext_yahoo_get_ip_addr; yc.ext_yahoo_file_transfer_done = ext_yahoo_file_transfer_done; yc.ext_yahoo_contact_added = ext_yahoo_contact_added; yc.ext_yahoo_rejected = ext_yahoo_rejected; yc.ext_yahoo_typing_notify = ext_yahoo_typing_notify; yc.ext_yahoo_game_notify = ext_yahoo_game_notify; yc.ext_yahoo_mail_notify = ext_yahoo_mail_notify; yc.ext_yahoo_got_search_result = ext_yahoo_got_search_result; yc.ext_yahoo_system_message = ext_yahoo_system_message; yc.ext_yahoo_error = ext_yahoo_error; yc.ext_yahoo_log = ext_yahoo_log; yc.ext_yahoo_add_handler = ext_yahoo_add_handler; yc.ext_yahoo_remove_handler = ext_yahoo_remove_handler; yc.ext_yahoo_connect = ext_yahoo_connect; yc.ext_yahoo_connect_async = ext_yahoo_connect_async; yc.ext_yahoo_read = ext_yahoo_read; yc.ext_yahoo_write = ext_yahoo_write; yc.ext_yahoo_close = ext_yahoo_close; yc.ext_yahoo_got_buddyicon = ext_yahoo_got_buddyicon; yc.ext_yahoo_got_buddyicon_checksum = ext_yahoo_got_buddyicon_checksum; yc.ext_yahoo_buddyicon_uploaded = ext_yahoo_buddyicon_uploaded; yc.ext_yahoo_got_buddyicon_request = ext_yahoo_got_buddyicon_request; yc.ext_yahoo_got_ping = ext_yahoo_got_ping; yc.ext_yahoo_got_buddy_change_group = ext_yahoo_got_buddy_change_group; yahoo_register_callbacks(&yc); }