/* * Copyright (C) 1998 Nathan Neulinger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "config.h" #include #include "gtkyahoo.h" #include "window-start-conference.h" #include "window-conference-invitation.h" #include "window-conference.h" #include "window-login.h" #include typedef enum bool_e { false = 0, true = 1 } bool; /* Globals */ char *current_user; char *current_pass; char *current_config = NULL; struct gtkyahoo_images images = { {0} }; struct yahoo_context *context; struct gtkyahoo_main_window *mw; GHashTable *cwhash = NULL; struct yahoo_buddy *user_selected = NULL; gboolean initial_warmup = TRUE; int initial_warmup_time = 5000; /* in thousanths of a second */ int online_count = 0; int show_online_only = TRUE; int invisible_login = FALSE; int show_status_time = FALSE; int friendlymsgsonly = FALSE; int myfontsonly = FALSE; int show_notify_messages = TRUE; int show_timestamp = TRUE; int friends_list_height = 5; /* show 5 friends by default */ int chat_window_sizew = 50; int chat_window_sizeh = 6; int chat_window_wrap = 0; int new_mail_count = 0; int new_personal_mail_count = 0; int reaper_timeout_tag = 0; int ping_timeout_tag = 0; int last_user_status = 0; int last_sent_status = 0; struct yahoo_options yahoo_options; FILE *logfile = NULL; char *logfilename = NULL; char *chat_log_fileformat = NULL; int connected = 0; int io_callback_tag = 0; int show_idle_messages = TRUE; int display_real_names = FALSE; char *last_custom_msg = NULL; int quiet = 1; int show_status = TRUE; int show_chat_button = TRUE; /* Proxy mode */ int connect_mode = YAHOO_CONNECT_NORMAL; int connect_retries = 0; int connect_delay = 1; char *proxy_host = NULL; int proxy_port = 80; /* Auto Reply Facility */ char *auto_reply_message = NULL; int auto_reply = 0; int auto_forward = 0; char *auto_forward_address = NULL; /* Handler routines */ char *url_handler = NULL; char *email_handler = NULL; char *sound_handler = NULL; /* Banner text for startup and about window */ char *gtkyahoo_gnu_banner = "\nGTKYahoo " VERSION " Copyright (C) 1998, 1999, 2000, 2001 Nathan Neulinger\n\n" "This software comes with ABSOLUTELY NO WARRANTY.\n" "\n" "This is free software, and you are welcome to redistribute it under\n" "certain conditions. Refer to the file COPYING for more details.\n\n"; int res; int connect_attempt; GtkWidget *login; GtkWidget *entry; void handle_sigpipe(int x) { printf("Lost connection to server (SIGPIPE).\n"); printf("This will be handled better in a future version.\n"); gtk_main_quit(); } void append_notify_msg(char *msg) { char *timestamp = make_timestamp(); if (show_notify_messages) { /* do it in front, eliminates last blank line */ /* need to only do this once */ if (gtk_text_get_length(GTK_TEXT(mw->notify_textbox)) != 0) { append_to_textbox(mw->window, mw->notify_textbox, "\n"); } append_to_textbox(mw->window, mw->notify_textbox, timestamp); append_to_textbox(mw->window, mw->notify_textbox, msg); } if (logfile) { fprintf(logfile, "%s%s\n", timestamp, msg); fflush(logfile); } } void append_notify_message(char *format, ...) { static char message[1000] = ""; va_list args; if (!format) { fprintf(stderr, "append_notify_message Error: format is null.\n"); return; } va_start(args, format); vsnprintf(message, 1000, format, args); va_end(args); append_notify_msg(message); } void handle_button_startchat(GtkWidget * widget, gpointer * data) { create_startchat_window(); } void io_callback(gpointer data, gint source, GdkInputCondition condition) { /* Tell the library to retrieve any newly available data */ if (condition == GDK_INPUT_READ) { if (!yahoo_getdata(context)) { return; printf("Error: Couldn't get more pager data.\n"); } } else { DBG_Print("io", "io_callback: condition == %d\n", condition); exit(1); } /* immmediately attempt to process packets */ process_packets(); } int process_packets_callback(gpointer data) { static int lasttime = 0; int thistime; /* it it's been long enough, trigger a status update */ if (connect_mode == YAHOO_CONNECT_HTTP || connect_mode == YAHOO_CONNECT_HTTPPROXY) { thistime = time(0); if (thistime - lasttime > 15) { yahoo_cmd_idle(context); lasttime = thistime; } } /* Process any available packets */ process_packets(); /* keep processing this timeout */ return (1); } void launch_browser_cb(GtkWidget * w, gpointer data) { char *url = (char *) data; launch_browser(url); } static int id_is_sys_id (const char *id) { int isit = 0; if (0 == strcmp ("YMessengerSystem", id)) { isit = 1; } if (0 == strcmp ("SYSTEM", id)) { isit = 1; } return isit; } void process_packet_message(struct yahoo_packet *pkt) { struct gtkyahoo_chat_window *cw; DBG_Print("packets", "\tMessage ID = %s\n", pkt->msg_id); if (pkt->msgtype == YAHOO_MSGTYPE_STATUS) { DBG_Print("packets", "\tMessage Status Update = %d\n", pkt->msg_status); status_set_status(pkt->msg_id, pkt->msg_status); } if (pkt->msg) { DBG_Print("packets", "\tMessage = %s\n", pkt->msg); if (pkt->msg_timestamp) { DBG_Print("packets", "\tTimestamp = %s\n", pkt->msg_timestamp); } if (pkt->imvironment_name) { DBG_Print("packets", "\tIMvironment Name = %s\n", pkt->imvironment_name); } DBG_Print("packets", "\tIMvironment Enabled = %d\n", pkt->imvironment_enabled); DBG_Print("packets", "\tIMvironment Unknown 1 = %d\n", pkt->imvironment_unkn1); DBG_Print("packets", "\tIMvironment Unknown 2 = %d\n", pkt->imvironment_unkn2); if (NULL != pkt->msg_id && '\0' == *(pkt->msg_id)) { return; /* ignoring messages from user "" because for some * reasone we get these when using HTTP only mode */ } if (friendlymsgsonly && pkt->service != YAHOO_SERVICE_NEWCONTACT) { if (!id_is_sys_id (pkt->msg_id) && !yahoo_isbuddy(context, pkt->msg_id) && NULL == find_chat_window (current_user, pkt->msg_id)) { char *msg = malloc(30 + strlen(pkt->msg_id)); sprintf(msg, "Ignoring message from \"%s\"", pkt->msg_id); DBG_Print("packets", "%s\n", msg); append_notify_msg(msg); free(msg); return; } } cw = get_chat_window(pkt->active_id, pkt->msg_id); if (pkt->msgtype == YAHOO_MSGTYPE_BOUNCE) { append_chat_msg(cw, NULL, YAHOO_COLOR_RED ""); event_clearinfo(); event_info->id = pkt->msg_id; event_info->msg = pkt->msg; event_execute(GTKYAHOO_EVENT_BOUNCE); /* give the user an easy way to send it offline */ create_sendoffline_window(pkt->msg_id, pkt->active_id, pkt->msg); } else { if (pkt->msg_timestamp && pkt->msg_timestamp[0] != 0) { char *tmpmsg; /* mark this chat session as offline */ /* cw->offline_chat = 1; */ tmpmsg = (char *) malloc(strlen(pkt->msg) + 50); strcpy(tmpmsg, pkt->msg); strcat(tmpmsg, YAHOO_COLOR_RED " [Offline at: "); strcat(tmpmsg, pkt->msg_timestamp); strcat(tmpmsg, "]"); append_chat_msg(cw, pkt->msg_id, tmpmsg); event_clearinfo(); event_info->id = pkt->msg_id; event_info->msg = tmpmsg; event_execute(GTKYAHOO_EVENT_MESSAGE); if (cw->hasFocus) { event_execute(GTKYAHOO_EVENT_MESSAGE_FOCUS); } else { event_execute(GTKYAHOO_EVENT_MESSAGE_NO_FOCUS); } free(tmpmsg); } else { if (YAHOO_SERVICE_NEWCONTACT == pkt->service) { char msg[1000]; snprintf(msg, 1000, "%s%s has made you a friend and said...%s", YAHOO_COLOR_RED, pkt->msg_id, YAHOO_COLOR_BLACK); append_chat_msg(cw, NULL, msg); } append_chat_msg(cw, pkt->msg_id, pkt->msg); event_clearinfo(); event_info->id = pkt->msg_id; event_info->msg = pkt->msg; event_execute(GTKYAHOO_EVENT_MESSAGE); if (cw->hasFocus) { event_execute(GTKYAHOO_EVENT_MESSAGE_FOCUS); } else { event_execute(GTKYAHOO_EVENT_MESSAGE_NO_FOCUS); } } handle_autoforward(pkt); handle_autoreply(pkt); } } else { DBG_Print("packets", "\tMessage was null.\n"); } } void process_packet_ping(struct yahoo_packet *pkt) { if (pkt->msg) DBG_Print("packets", "\tMessage = %s\n", pkt->msg); } void process_packet_filetransfer(struct yahoo_packet *pkt) { if (pkt->file_from) DBG_Print("packets", "\tFile From = %s\n", pkt->file_from); if (pkt->file_flag) DBG_Print("packets", "\tFile Flag = %s\n", pkt->file_flag); if (pkt->file_url) DBG_Print("packets", "\tFile URL = %s\n", pkt->file_url); if (pkt->file_expires) DBG_Print("packets", "\tFile Expires = %d\n", pkt->file_expires); if (pkt->file_description) DBG_Print("packets", "\tFile Description = %s\n", pkt->file_description); create_recvfile_window(pkt); } void process_packet_conference(struct yahoo_packet *pkt) { if (pkt->conf_id) DBG_Print("packets", "\tConference ID = %s\n", pkt->conf_id); if (pkt->conf_host) DBG_Print("packets", "\tConference Host = %s\n", pkt->conf_host); if (pkt->conf_user) DBG_Print("packets", "\tConference User = %s\n", pkt->conf_user); if (pkt->conf_msg) DBG_Print("packets", "\tConference Message = %s\n", pkt->conf_msg); if (pkt->conf_userlist) { char *user = NULL; int i = 0; for (i = 0; NULL != (user = (pkt->conf_userlist)[i]); ++i) { DBG_Print("packets", "\tConference User #%d = %s\n", i + 1, user); user = NULL; }; } else { DBG_Print("packets", "\tConference Userlist = NULL!\n"); } switch (pkt->service) { case YAHOO_SERVICE_CONFINVITE: case YAHOO_SERVICE_CONFADDINVITE: conference_field_invitation(pkt); break; case YAHOO_SERVICE_CONFLOGON: conference_join_existing(pkt->conf_host, pkt->conf_userlist, pkt->conf_msg); break; case YAHOO_SERVICE_CONFLOGOFF: break; case YAHOO_SERVICE_CONFMSG: conference_incomming(pkt->conf_id, pkt->conf_user, pkt->conf_msg); break; default: break; } } void set_status_line(const struct yahoo_idstatus *rec) { char *id = rec->id; int pagable = rec->in_pager || rec->in_game || rec->in_chat; int status = rec->status; int set_the_status = FALSE; int len = 250; char *statusString; char *label_name = NULL; if (FALSE == display_real_names) { label_name = id; } else { char *real_name = yahoo_get_real_name (id, context); if (NULL != real_name) { label_name = real_name; } else { label_name = id; } } if (rec->status_msg) { len += strlen(rec->status_msg); } statusString = (char *) malloc(len); if (!pagable) { snprintf(statusString, len, "%s has logged off", label_name); set_the_status = TRUE; } else if (status == YAHOO_STATUS_CUSTOM) { if (rec->status_msg) { snprintf(statusString, len, "%s is now '%s'", label_name, rec->status_msg); } else { snprintf(statusString, len, "%s is now ''", label_name); } set_the_status = TRUE; } else { char *append = yahoo_get_status_append(status); if (append) { snprintf(statusString, len, "%s %s", label_name, append); set_the_status = TRUE; } } if (set_the_status) { if (!initial_warmup) append_notify_msg(statusString); append_chat_notification(id, statusString); } free(statusString); } void duplicate_login(void) { char *msgs[] = { "You have been logged off because\nyou have logged on elsewhere!", NULL }; if (io_callback_tag != 0) { gdk_input_remove(io_callback_tag); } create_alert_window("GTKYahoo: Duplicate Logon", msgs, "Okay", TRUE); gtk_main_quit(); } void process_packet_status(struct yahoo_packet *pkt) { int i; if (pkt->service == YAHOO_SERVICE_LOGOFF && !strcmp(pkt->active_id, current_user)) { if (pkt->msgtype == YAHOO_MSGTYPE_ERROR && (context->connect_mode != YAHOO_CONNECT_HTTP && context->connect_mode != YAHOO_CONNECT_HTTPPROXY)) { printf("You have been disconnected due to duplicate logon.\n"); duplicate_login(); } return; } DBG_Print("packets", "\tFlag = %d\n", pkt->flag); for (i = 0; i < pkt->idstatus_count; i++) { struct yahoo_idstatus *rec; int oldstatus, oldinpager, oldavail, avail; char *id; bool do_status_line = false; rec = pkt->idstatus[i]; id = rec->id; oldstatus = status_get_status(id); oldinpager = status_get_in_pager(id); oldavail = status_get_avail(id); status_set_in_pager(id, rec->in_pager); status_set_in_chat(id, rec->in_chat); status_set_in_game(id, rec->in_game); avail = status_get_avail(id); DBG_Print("packets", "\tIDStatus ID (%d) = %s\n", i, id); DBG_Print("packets", "\t\tPageable = %d\n", rec->in_pager); DBG_Print("packets", "\t\tChatting = %d\n", rec->in_chat); DBG_Print("packets", "\t\tGaming = %d\n", rec->in_game); /* handle events for logon/logoff */ event_clearinfo(); event_info->id = id; event_info->newstatus = rec->status; if (avail && !oldavail) /* user logged on */ { event_execute(GTKYAHOO_EVENT_LOGON); do_status_line = true; } else if (!avail && oldavail) /* user logged off */ { event_execute(GTKYAHOO_EVENT_LOGOFF); do_status_line = true; } if (rec->status_msg) { DBG_Print("packets", "\t\tStatus Message = %s\n", rec->status_msg); status_set_msg(id, rec->status_msg); } if (rec->connection_id) { DBG_Print("packets", "\t\tConnection ID = %s\n", rec->connection_id); } /* added 5/3/1999 cause the chat logoff messages seem to have a bogus status value in them */ /* probably need to find a better way to do this */ if (pkt->service != YAHOO_SERVICE_CHATLOGOFF && pkt->service != YAHOO_SERVICE_CHATLOGON && pkt->service != YAHOO_SERVICE_GAMELOGON && pkt->service != YAHOO_SERVICE_GAMELOGOFF) { DBG_Print("packets", "\t\tStatus = %d\n", rec->status); status_set_status(id, rec->status); event_clearinfo(); event_info->id = id; event_info->newstatus = rec->status; event_info->oldstatus = oldstatus; /* ignore using going to idle and back, it's annoying */ /* only trigger if different than old status */ /* this eliminates some of the duplicated events */ /* always do for custom status, since they may have changed msg */ if (oldstatus != rec->status || oldstatus == YAHOO_STATUS_CUSTOM) { if (oldstatus != YAHOO_STATUS_IDLE && rec->status != YAHOO_STATUS_IDLE) { event_execute(GTKYAHOO_EVENT_STATUS); } else { event_execute(GTKYAHOO_EVENT_STATUS_IDLE); } do_status_line = true; } } else { DBG_Print("packets", "\t\tStatus = %d (Ignored)\n", rec->status); } /* Update display to indicate status */ gtkyahoo_userlist_update(id); /* Add notification of status update */ if (do_status_line) { set_status_line(rec); } } } void process_packet_newmail(struct yahoo_packet *pkt) { char toolmsg[500] = "\0"; int len; char *title; if (pkt->service == YAHOO_SERVICE_NEWMAIL) { DBG_Print("packets", "\tMail Status = %d\n", pkt->mail_status); new_mail_count = pkt->mail_status; } else { DBG_Print("packets", "\tPersonal Mail Status = %d\n", pkt->mail_status); new_personal_mail_count = pkt->mail_status; } if (new_mail_count || new_personal_mail_count) { len = strlen(current_user) + 70; title = (char *) malloc(len); snprintf(title, len, "GTKYahoo (%s) : New Mail", current_user); gtk_window_set_title(GTK_WINDOW(mw->window), title); free(title); gtk_pixmap_set(GTK_PIXMAP(mw->pmwid_mailstatus), mw->pm_mail_full, mw->mask_mail_full); if (new_mail_count && new_personal_mail_count) { snprintf(toolmsg, 500, "%d new personal message%s\n" "%d new mail message%s", new_personal_mail_count, new_personal_mail_count > 1 ? "s" : "", new_mail_count, new_mail_count > 1 ? "s" : ""); event_clearinfo(); append_notify_message("You now have %d new personal message%s", new_personal_mail_count, new_personal_mail_count > 1 ? "s" : ""); append_notify_message("You now have %d new mail message%s", new_mail_count, new_mail_count > 1 ? "s" : ""); event_execute(GTKYAHOO_EVENT_MAIL_NEW); } else if (new_mail_count) { snprintf(toolmsg, 500, "%d new mail message%s", new_mail_count, new_mail_count > 1 ? "s" : ""); event_clearinfo(); append_notify_message("You now have %d new mail message%s", new_mail_count, new_mail_count > 1 ? "s" : ""); event_execute(GTKYAHOO_EVENT_MAIL_NEW); } else if (new_personal_mail_count) { snprintf(toolmsg, 500, "%d new personal message%s", new_personal_mail_count, new_personal_mail_count > 1 ? "s" : ""); event_clearinfo(); append_notify_message("You now have %d new personal message%s", new_personal_mail_count, new_personal_mail_count > 1 ? "s" : ""); event_execute(GTKYAHOO_EVENT_MAIL_NEW); } else { snprintf(toolmsg, 500, "no new messages"); event_clearinfo(); append_notify_msg("You no longer have any new messages"); event_execute(GTKYAHOO_EVENT_MAIL_NONE); } gtk_tooltips_set_tip(mw->tooltips, mw->button_mailstatus, toolmsg, NULL); } else { append_notify_msg("You no longer have any new messages"); len = strlen(current_user) + 70; title = (char *) malloc(len); snprintf(title, len, "GTKYahoo (%s) : No New Mail", current_user); gtk_window_set_title(GTK_WINDOW(mw->window), title); free(title); gtk_pixmap_set(GTK_PIXMAP(mw->pmwid_mailstatus), mw->pm_mail_empty, mw->mask_mail_empty); gtk_tooltips_set_tip(mw->tooltips, mw->button_mailstatus, NULL, NULL); event_clearinfo(); event_execute(GTKYAHOO_EVENT_MAIL_NONE); } } void process_packet_chatinvite(struct yahoo_packet *pkt) { create_chatinvite_window(pkt->chat_invite_content); } /* process yahoo packets */ void process_packets(void) { struct yahoo_rawpacket *rawpkt; struct yahoo_packet *pkt; /* process each packet in turn */ fflush(stdout); while ((rawpkt = yahoo_getpacket(context))) { static unsigned int pkt_count = 0; char *un_raw = NULL; pkt = yahoo_parsepacket(context, rawpkt); /* indicate we are now connected if this is the first packet */ if (!connected) { append_notify_msg("Connected."); connected = 1; } DBG_Print("packets", "\n"); DBG_Print("packets", "Received packet #%u at %s:\n", ++pkt_count, make_timestamp ()); DBG_Print("packets", "\tService = %s\n", yahoo_get_service_string(pkt->service)); DBG_Print("packets", "\tReal ID = %s\n", pkt->real_id); DBG_Print("packets", "\tActive ID = %s\n", pkt->active_id); DBG_Print("packets", "\tConnection ID = 0x%.8X\n", pkt->connection_id); DBG_Print("packets", "\tMagic ID = %X\n", pkt->magic_id); DBG_Print("packets", "\tUnknown Flag 1 = 0x%.8X\n", pkt->unknown1); DBG_Print("packets", "\tMessage Type = 0x%.8X\n", pkt->msgtype); un_raw = yahoo_unraw_buffer(rawpkt->content, yahoo_makeint(rawpkt->len)); DBG_Print("packets", "\tRaw Content = %s\n", un_raw); free(un_raw); un_raw = NULL; 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: case YAHOO_SERVICE_GAMELOGON: case YAHOO_SERVICE_GAMELOGOFF: 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_CALENDAR: create_calendar_window(pkt); break; case YAHOO_SERVICE_CHATINVITE: process_packet_chatinvite(pkt); break; case YAHOO_SERVICE_NEWMAIL: case YAHOO_SERVICE_NEWPERSONALMAIL: process_packet_newmail(pkt); break; case YAHOO_SERVICE_NEWCONTACT: if (pkt->msg) { process_packet_message(pkt); } else { process_packet_status(pkt); } if (NULL != pkt->msg_id && !yahoo_isbuddy(context, pkt->msg_id)) { char msg[1000]; snprintf(msg, 1000, "%s has made you their friend", pkt->msg_id); append_notify_msg(msg); create_addfriend_window_with_name(pkt->msg_id); } yahoo_get_config(context); gtkyahoo_userlist_refresh(); break; case YAHOO_SERVICE_GROUPRENAME: yahoo_get_config(context); gtkyahoo_userlist_refresh(); break; case YAHOO_SERVICE_CONFINVITE: case YAHOO_SERVICE_CONFADDINVITE: case YAHOO_SERVICE_CONFLOGON: case YAHOO_SERVICE_CONFLOGOFF: case YAHOO_SERVICE_CONFMSG: process_packet_conference(pkt); break; case YAHOO_SERVICE_PING: process_packet_ping(pkt); break; case YAHOO_SERVICE_FILETRANSFER: process_packet_filetransfer(pkt); break; default: DBG_Print("packets", "Unknown service type (%d) packet received!\n", pkt->service); break; } fflush(stdout); yahoo_free_packet(pkt); yahoo_free_rawpacket(rawpkt); } } /* another callback */ void delete_event(GtkWidget * widget, GdkEvent * event, gpointer * data) { if (context != NULL) yahoo_cmd_logoff(context); gtk_main_quit(); } void password_entered(GtkEditable * editable, gpointer user_data) { gchar *entered = NULL; GtkWidget *login = NULL; if (NULL != (entered = gtk_editable_get_chars(editable, 0, -1))) { current_pass = strdup(entered); } login = gtk_object_get_data(GTK_OBJECT(editable), "login"); gtk_widget_destroy(login); got_password(); } void prompt_for_password(void) { login = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_object_set_data(GTK_OBJECT(login), "login", login); gtk_window_set_title(GTK_WINDOW(login), "Please Supply your password"); gtk_window_set_modal(GTK_WINDOW(login), TRUE); gtk_window_set_default_size(GTK_WINDOW(login), 250, 15); entry = gtk_entry_new(); gtk_widget_ref(entry); gtk_object_set_data(GTK_OBJECT(entry), "login", login); gtk_object_set_data_full(GTK_OBJECT(login), "entry", entry, (GtkDestroyNotify) gtk_widget_unref); gtk_widget_show(entry); gtk_widget_show(login); gtk_container_add(GTK_CONTAINER(login), entry); gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); gtk_widget_grab_focus(entry); gtk_signal_connect(GTK_OBJECT(entry), "activate", GTK_SIGNAL_FUNC(password_entered), NULL); } void got_password(void) { int len; int statuscode; char *title; const char title_str[] = "GTKYahoo (%s) : No New Mail"; /* Check to make sure we got a reasonable parse */ if (!current_user || !current_pass) { printf("Error: UserID or Password not specified in config.\n"); exit(1); } DBG_Print("general", "initializing yahoolib\n"); context = yahoo_init(current_user, current_pass, &yahoo_options); if (!context) { char *msgs[] = { "Invalid password.", "Correct ~/.gtkyahoo/gtkyahoorc.", NULL }; append_notify_msg("Invalid password!"); create_alert_window("GtkYahoo Error!", msgs, "Oops!", TRUE); printf("\nGTKYahoo has quit: You have an INVALID password.\n"); printf ("Please edit ~/.gtkyahoo/gtkyahoorc and enter the correct password\n\n"); context = NULL; current_pass = NULL; return; } gtk_widget_set_sensitive(mw->menuitem_file_login, FALSE); gtk_widget_set_sensitive(mw->menuitem_file_credentials, FALSE); gtk_widget_set_sensitive(mw->menuitem_tools_startchat, TRUE); gtk_widget_set_sensitive(mw->menuitem_tools_sendraw, TRUE); gtk_widget_set_sensitive(mw->menuitem_tools_sendfile, TRUE); gtk_widget_set_sensitive(mw->menuitem_tools_sendoffline, TRUE); gtk_widget_set_sensitive(mw->menuitem_tools_addfriend, TRUE); gtk_widget_set_sensitive(mw->menuitem_tools_removefriend, TRUE); gtk_widget_set_sensitive(mw->menuitem_tools_addignore, TRUE); /* indicate we are connecting */ append_notify_msg("Connecting..."); while (gtk_events_pending()) gtk_main_iteration(); /* establish connection to server */ /* if it is actually a http style connection, this won't do anything */ res = yahoo_connect(context); connect_attempt = 1; while (!res && (connect_attempt++ <= connect_retries)) { append_notify_msg("Retrying connection."); while (gtk_events_pending()) gtk_main_iteration(); printf("Connection attempt failed. Retrying. (%d of %d)\n", connect_attempt, connect_retries); sleep(connect_delay); res = yahoo_connect(context); } if (!res) { char *msgs[] = { "Connect failed.", NULL }; append_notify_msg("Connect failed."); create_alert_window("GtkYahoo Error!", msgs, "Exit", TRUE); printf("Error: Exiting due to failure to connect to pager server.\n"); exit(1); } len = strlen (current_user) + sizeof (title_str); title = (char *) malloc(len); snprintf(title, len, title_str, current_user); gtk_window_set_title (GTK_WINDOW (mw->window), title); /* retrieve configuration details */ yahoo_get_config(context); append_notify_msg("Fetching Addressbook..."); yahoo_fetchaddressbook (context); /* update the user list now so it looks like something happened */ gtkyahoo_userlist_refresh(); while (gtk_events_pending()) gtk_main_iteration(); /* log in */ /* the status option won't work until YPNS2.0 support works */ DBG_Print("general", "sending logon command\n"); statuscode = invisible_login ? YAHOO_STATUS_INVISIBLE : YAHOO_STATUS_AVAILABLE; res = yahoo_cmd_logon(context, statuscode); last_user_status = statuscode; last_sent_status = statuscode; gtkyahoo_userlist_refresh(); select_menu_status (statuscode); /* need an alert here for failure */ DBG_Print("general", "setting up timeouts\n"); have_user_activity(); setup_reaper_timeout(); setup_ping_timeout(); setup_process_packets_timeout(); /* set up IO callback - http mode connections won't set the socket, so this won't get called */ /* I tried moving this up but it causes some sort of timing problem that needs to be looking into */ if (context->sockfd) { DBG_Print("general", "adding I/O handler\n"); io_callback_tag = gdk_input_add(context->sockfd, GDK_INPUT_READ, io_callback, (gpointer) 0); } } /* callback for updating the user selection in the main window */ void callback_user_selected(GtkCTree * ctree, GList * node, gint column, gpointer data) { if (user_selected) { user_selected = NULL; } user_selected = gtk_ctree_node_get_row_data(ctree, GTK_CTREE_NODE(node)); } void click_in_notify_textbox(GtkWidget * w, GdkEventButton * event, gpointer data) { switch (event->button) { case 3: popup_notify_menu(); break; case 4: case 5: { GtkAdjustment *adjust = GTK_ADJUSTMENT(data); const gfloat step = adjust->page_increment / 2.0; const gfloat value = adjust->value; gtk_adjustment_set_value (adjust, (event->button == 5) ? MIN (value+step, adjust->upper - adjust->page_size) : MAX (value-step, adjust->lower)); gtk_adjustment_value_changed(adjust); } break; } } void callback_user_clicked(GtkWidget * w, GdkEventButton * event, gpointer data) { GtkCTreeNode *ctn; gint row, column; char *user = NULL; gint ret; /* button 1 doubl-click or button 3 single click */ if ((event->button == 1 && event->type == GDK_2BUTTON_PRESS) || (event->button == 3 && event->type == GDK_BUTTON_PRESS)) { if (!(ret = gtk_clist_get_selection_info(GTK_CLIST(w), (gint) event->x, (gint) event->y, &row, &column))) { DBG_Print ("gtkyahoo", "callback_user_clicked: gtk_clist_get_selection returned %d\n", ret); return; } ctn = gtk_ctree_node_nth(GTK_CTREE(w), row); if (!ctn) { DBG_Print ("gtkyahoo", "callback_user_clicked: gtk_ctree_node_nth returned NULL\n"); return; } if (row > 0) { user_selected = gtk_ctree_node_get_row_data(GTK_CTREE(w), ctn); if (!user_selected) { return; } user = user_selected->id; if (NULL != user) { if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) { /* if nothing known or in chat or in pager */ /* more natural to assume a regular chat by default */ if (status_get_in_pager(user) || status_get_in_chat(user) || status_get_in_game(user)) { create_startchat_window(); } else { create_sendoffline_window(user, NULL, NULL); } } else { gtk_ctree_select(GTK_CTREE(w), ctn); popup_user_menu(); } } } else { if (event->button == 3 && event->type == GDK_BUTTON_PRESS) { gtk_ctree_select(GTK_CTREE(w), ctn); popup_your_menu(); } } } } void callback_user_unselected(GtkCTree * ctree, GList * node, gint column, gpointer data) { if (user_selected) { user_selected = NULL; } } /* Callback to start browser to check mail */ void callback_readmail(GtkWidget * widget, gpointer data) { /* if you have yahoo mail, opens to it else if you have personals mail, opens to it else opens to yahoo mail */ if (new_mail_count || !new_personal_mail_count) { launch_browser("http://mail.yahoo.com"); } else { launch_browser("http://mbox.yahoo.com/51/mail/read"); } } static int clear_warmup() { initial_warmup = FALSE; return FALSE; /* don't want to repeat */ } /* Create the main userlist window */ struct gtkyahoo_main_window *create_main_window(void) { int len; char *title; const char title_str[] = "GTKYahoo (%s) : Not Logged On"; mw = (struct gtkyahoo_main_window *) calloc(1, sizeof(*mw)); /* Create main window */ len = strlen(current_user) + sizeof (title_str); title = (char *) malloc(len); snprintf(title, len, title_str, current_user); mw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(mw->window), title); free(title); gtk_container_border_width(GTK_CONTAINER(mw->window), 5); gtk_widget_realize(mw->window); gtk_signal_connect(GTK_OBJECT(mw->window), "focus_in_event", GTK_SIGNAL_FUNC(have_user_activity), NULL); /* get the windowstyle for when loading pixmaps */ mw->windowstyle = gtk_widget_get_style(mw->window); /* Load the icon pixmap */ mw->pm_icon = gdk_pixmap_create_from_xpm_d(mw->window->window, &mw->mask_icon, &mw->windowstyle->bg[GTK_STATE_NORMAL], (gchar **) pixmapdata_gtkyahoo_icon); gdk_window_set_icon(mw->window->window, NULL, mw->pm_icon, mw->mask_icon); /* Create the tool tips */ mw->tooltips = gtk_tooltips_new(); /* Trap delete_event signal */ gtk_signal_connect(GTK_OBJECT(mw->window), "delete_event", GTK_SIGNAL_FUNC(delete_event), NULL); /* Create the boxes */ /* 1) whole window (menu_box,image,text_box) */ /* 2) menu bar box */ /* 3) text area box */ mw->box_window = gtk_vbox_new(FALSE, 0); mw->box_status = gtk_hbox_new(FALSE, 0); mw->box_userlist = gtk_hbox_new(FALSE, 0); mw->notify_hbox = gtk_handle_box_new(); mw->box_notify = gtk_hbox_new(FALSE, 0); mw->box_buttons = gtk_hbox_new(FALSE, 0); gtk_widget_show(mw->box_window); if (show_status) gtk_widget_show(mw->box_status); gtk_widget_show(mw->box_userlist); if (show_notify_messages) gtk_widget_show(mw->notify_hbox); gtk_widget_show(mw->box_notify); if (show_chat_button) gtk_widget_show(mw->box_buttons); mw->toolbar = create_main_menus(mw); gtk_widget_show(mw->toolbar); /* build the full window box */ gtk_container_add(GTK_CONTAINER(mw->window), mw->box_window); gtk_box_pack_start(GTK_BOX(mw->box_window), mw->toolbar, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(mw->box_window), mw->box_status, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(mw->box_window), mw->box_userlist, TRUE, TRUE, 5); gtk_box_pack_start(GTK_BOX(mw->box_window), mw->notify_hbox, FALSE, TRUE, 0); gtk_container_add(GTK_CONTAINER(mw->notify_hbox), mw->box_notify); gtk_box_pack_start(GTK_BOX(mw->box_window), mw->box_buttons, FALSE, TRUE, 0); /* Load the pager pixmap and pack into button box */ mw->pm_pager = gdk_pixmap_create_from_xpm_d(mw->window->window, &mw->mask_pager, &mw->windowstyle->bg[GTK_STATE_NORMAL], (gchar **) pixmapdata_logo); mw->pmwid_pager = gtk_pixmap_new(mw->pm_pager, mw->mask_pager); gtk_box_pack_end(GTK_BOX(mw->box_buttons), mw->pmwid_pager, FALSE, TRUE, 5); gtk_widget_show(mw->pmwid_pager); /* do these same steps again to create a second button */ mw->button = gtk_button_new(); gtk_box_pack_start(GTK_BOX(mw->box_buttons), mw->button, FALSE, TRUE, 0); gtk_widget_show(mw->button); gtk_signal_connect(GTK_OBJECT(mw->button), "clicked", GTK_SIGNAL_FUNC(handle_button_startchat), NULL); /* This is the trouble maker!!! Giving more elder parent to xpm_label_box seems to work */ gtk_container_add(GTK_CONTAINER(mw->button), xpm_label_box(mw->window, pixmapdata_send, "Start Chat")); /* create a text box */ mw->notify_textbox = gtk_text_new(NULL, NULL); gtk_box_pack_start(GTK_BOX(mw->box_notify), mw->notify_textbox, TRUE, TRUE, 0); gtk_widget_set_usize(mw->notify_textbox, 275, 60); gtk_widget_show(mw->notify_textbox); /* Make the text box scrollable */ mw->notify_scroll = gtk_vscrollbar_new(GTK_TEXT(mw->notify_textbox)->vadj); gtk_box_pack_start(GTK_BOX(mw->box_notify), mw->notify_scroll, FALSE, FALSE, 0); gtk_widget_show(mw->notify_scroll); gtk_signal_connect(GTK_OBJECT(mw->notify_textbox), "button_press_event", GTK_SIGNAL_FUNC(click_in_notify_textbox), GTK_TEXT(mw->notify_textbox)->vadj); /* Load the other pixmaps - try from file first, else fallback to compile time data */ if (images.status.here) mw->pm_here = gdk_pixmap_create_from_xpm(mw->window->window, &mw->mask_here, &mw->windowstyle->bg[GTK_STATE_NORMAL], images.status.here); if (!mw->pm_here) mw->pm_here = gdk_pixmap_create_from_xpm_d(mw->window->window, &mw->mask_here, &mw->windowstyle->bg[GTK_STATE_NORMAL], (gchar **) pixmapdata_status_here); if (images.status.away) mw->pm_away = gdk_pixmap_create_from_xpm(mw->window->window, &mw->mask_away, &mw->windowstyle->bg[GTK_STATE_NORMAL], images.status.away); if (!mw->pm_away) mw->pm_away = gdk_pixmap_create_from_xpm_d(mw->window->window, &mw->mask_away, &mw->windowstyle->bg[GTK_STATE_NORMAL], (gchar **) pixmapdata_status_away); if (images.status.offline) mw->pm_offline = gdk_pixmap_create_from_xpm(mw->window->window, &mw->mask_offline, &mw->windowstyle->bg[GTK_STATE_NORMAL], images.status.offline); if (!mw->pm_offline) mw->pm_offline = gdk_pixmap_create_from_xpm_d(mw->window->window, &mw->mask_offline, &mw->windowstyle->bg[GTK_STATE_NORMAL], (gchar **) pixmapdata_status_offline); if (images.status.idle) mw->pm_offline = gdk_pixmap_create_from_xpm(mw->window->window, &mw->mask_idle, &mw->windowstyle->bg[GTK_STATE_NORMAL], images.status.idle); if (!mw->pm_idle) mw->pm_idle = gdk_pixmap_create_from_xpm_d(mw->window->window, &mw->mask_idle, &mw->windowstyle->bg[GTK_STATE_NORMAL], (gchar **) pixmapdata_status_idle); mw->pm_mail_full = gdk_pixmap_create_from_xpm_d(mw->window->window, &mw->mask_mail_full, &mw->windowstyle->bg[GTK_STATE_NORMAL], (gchar **) pixmapdata_mail_full); mw->pm_mail_empty = gdk_pixmap_create_from_xpm_d(mw->window->window, &mw->mask_mail_empty, &mw->windowstyle->bg[GTK_STATE_NORMAL], (gchar **) pixmapdata_mail_empty); /* Scrollable user list */ mw->userlist = GTK_CTREE(gtk_ctree_new(1, 0)); gtk_clist_set_column_auto_resize(GTK_CLIST(mw->userlist), 0, TRUE); gtk_clist_set_column_width(GTK_CLIST(mw->userlist), 1, 200); gtk_clist_set_selection_mode(GTK_CLIST(mw->userlist), GTK_SELECTION_SINGLE); gtk_clist_set_auto_sort(GTK_CLIST(mw->userlist), TRUE); gtk_ctree_set_line_style(mw->userlist, GTK_CTREE_LINES_NONE); gtk_ctree_set_expander_style(mw->userlist, GTK_CTREE_EXPANDER_TRIANGLE); gtk_ctree_set_indent(GTK_CTREE(mw->userlist), 0); gtk_signal_connect(GTK_OBJECT(mw->userlist), "button_press_event", GTK_SIGNAL_FUNC(callback_user_clicked), NULL); mw->scrolled_userlist = gtk_scrolled_window_new(NULL, NULL); gtk_container_border_width(GTK_CONTAINER(mw->scrolled_userlist), 0); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(mw->scrolled_userlist), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); gtk_box_pack_start(GTK_BOX(mw->box_userlist), mw->scrolled_userlist, TRUE, TRUE, 0); gtk_container_add(GTK_CONTAINER(mw->scrolled_userlist), GTK_WIDGET(mw->userlist)); if (friends_list_height > -1) { /* Start by assuming height dictated by font */ gint h, w, mh = GTK_CLIST(mw->userlist)->row_height, oldmh = mh; /* Check height of all status pixmaps */ gdk_window_get_size(mw->pm_away, &w, &h); if (h > mh) mh = h; gdk_window_get_size(mw->pm_here, &w, &h); if (h > mh) mh = h; gdk_window_get_size(mw->pm_offline, &w, &h); if (h > mh) mh = h; gdk_window_get_size(mw->pm_idle, &w, &h); if (h > mh) mh = h; if (mh != oldmh) gtk_clist_set_row_height(GTK_CLIST(mw->userlist), mh); gtk_widget_set_usize(mw->scrolled_userlist, -1, mh * friends_list_height); } gtk_widget_show(mw->scrolled_userlist); gtk_widget_show(GTK_WIDGET(mw->userlist)); gtk_signal_connect(GTK_OBJECT(mw->userlist), "tree-select-row", GTK_SIGNAL_FUNC(callback_user_selected), NULL); gtk_signal_connect(GTK_OBJECT(mw->userlist), "tree-unselect-row", GTK_SIGNAL_FUNC(callback_user_unselected), NULL); /* online status option menu */ mw->optionmenu_status = gtk_option_menu_new(); get_status_menu(mw->window, &mw->menu_status); gtk_option_menu_set_menu(GTK_OPTION_MENU(mw->optionmenu_status), mw->menu_status); gtk_widget_show(mw->menu_status); gtk_widget_show(mw->optionmenu_status); gtk_box_pack_start(GTK_BOX(mw->box_status), mw->optionmenu_status, FALSE, TRUE, 0); /* mail status pixmap */ mw->pmwid_mailstatus = gtk_pixmap_new(mw->pm_mail_empty, mw->mask_mail_empty); gtk_widget_show(mw->pmwid_mailstatus); mw->button_mailstatus = gtk_button_new(); gtk_signal_connect(GTK_OBJECT(mw->button_mailstatus), "clicked", GTK_SIGNAL_FUNC(callback_readmail), NULL); gtk_container_add(GTK_CONTAINER(mw->button_mailstatus), mw->pmwid_mailstatus); gtk_widget_show(mw->button_mailstatus); gtk_tooltips_set_tip(mw->tooltips, mw->button_mailstatus, "no new messages", NULL); gtk_box_pack_end(GTK_BOX(mw->box_status), mw->button_mailstatus, FALSE, TRUE, 0); /* now show the main window - should prevent resizing */ gtk_widget_show(mw->window); return mw; } void gtkyahoo_userlist_refresh(void) { static const char title_str[] = "GTKYahoo (%s) : %d online"; char title[256]; make_friends_ctree(mw->userlist, !show_online_only); snprintf(title, sizeof(title)-1, title_str, current_user, online_count); gtk_window_set_title(GTK_WINDOW(mw->window), title); } void gtkyahoo_userlist_update(char *user_to_update) { /* for now be dumb and inefficient, in the future be smarter */ gtkyahoo_userlist_refresh(); update_start_conference_window(); } int main(int argc, char *argv[]) { int setgeom = 0; int curarg = 1; GtkAllocation initial_geometry; initial_geometry.x = initial_geometry.y = initial_geometry.width = initial_geometry.height = 0; DBG_Open(NULL); /* gmg */ while (argc > 1) { if (strcmp(argv[curarg], "-geometry") == 0) { int width, height, x, y; curarg++; argc--; /* we should do more error checking on this arg, and we throw half away, just using this format so that it looks like X's */ sscanf(argv[curarg], "%dx%d+%d+%d", &width, &height, &x, &y); curarg++; argc--; initial_geometry.x = x; initial_geometry.y = y; initial_geometry.width = width; initial_geometry.height = height; setgeom = 1; continue; } if (strcmp(argv[curarg], "-verbose") == 0) { quiet = 0; continue; } /* If it starts with - assume parameters */ if (argv[curarg][0] == '-') break; if (strcmp(argv[curarg], "")) { current_config = malloc(strlen(argv[curarg]) + 1); strcpy(current_config, argv[curarg]); DBG_Print("gtkyahoo", "Current Config: %s\n", current_config); } break; } /* Print out a nice GNU banner */ if (!quiet) printf("%s", gtkyahoo_gnu_banner); if (getenv("GTKYAHOO_DEBUG")) { DBG_Enable(getenv("GTKYAHOO_DEBUG")); } /* This will only print if using GTKYAHOO_DEBUG */ DBG_Print("general", "Current config set to: '%s'\n", current_config ? current_config : "first in file"); /* Set the email handler */ #ifdef PATH_PROG_SENDMAIL email_handler = strdup(PATH_PROG_SENDMAIL " -t"); DBG_Print("general", "Fallback Email handler set to: '%s'\n", email_handler); #endif /* Set the URL handler */ #ifdef PATH_PROG_URLHANDLER url_handler = strdup(PATH_PROG_URLHANDLER); DBG_Print("general", "Fallback URL handler set to: '%s'\n", url_handler); #endif /* Set the sound handler */ #ifdef LIBEXECDIR sound_handler = strdup(LIBEXECDIR "/gtkyahoo-sound-handler"); DBG_Print("general", "Fallback sound handler set to: '%s'\n", sound_handler); #endif /* set up handler for SIGPIPE */ signal(SIGPIPE, handle_sigpipe); /* Initialize GTK */ gtk_init(&argc, &argv); /* Load the user and password */ rcfile_init(); /* Open the log file */ if (logfilename) { if ((logfile = fopen(logfilename, "a+")) == NULL) { printf("Error: Failed to open log file.\n"); exit(1); } fprintf(logfile, "%s%s\n", make_timestamp(), "GTKYahoo started."); fflush(logfile); } /* Create the main window */ create_main_window(); /* set the position of window, this should happen in create_main_window, but did not want to pollute global variables, or change call interface */ if (setgeom) { gtk_widget_set_uposition(mw->window, initial_geometry.x, initial_geometry.y); gtk_widget_set_usize(mw->window, initial_geometry.width, initial_geometry.height); } /* make sure the window/home/craig/dev/gnome/gtk/gtkyahoo/patch.text is available */ while (gtk_events_pending()) gtk_main_iteration(); /* Initialize the yahoo library */ yahoo_options.connect_mode = connect_mode; yahoo_options.is_proxy_present = 0; if (yahoo_options.connect_mode != YAHOO_CONNECT_HTTPPROXY) { char *proxy_info; char *s1, *s2; if ((proxy_info = getenv("HTTP_PROXY")) != NULL || (proxy_info = getenv("http_proxy")) != NULL) { yahoo_options.is_proxy_present = 1; s1 = strchr(proxy_info, ':'); s2 = strchr(s1 + 1, ':'); if (s2 == NULL) { proxy_port = atoi(s1 + 1); proxy_host = malloc(s1 - proxy_info + 1); strncpy(proxy_host, proxy_info, s1 - proxy_info); proxy_host[s1 - proxy_info] = '\0'; } else { s1 += 3; proxy_port = atoi(s2 + 1); proxy_host = malloc(s2 - s1 + 1); strncpy(proxy_host, s1, s2 - s1); proxy_host[s2 - s1] = '\0'; } } } yahoo_options.proxy_port = proxy_port; yahoo_options.proxy_host = proxy_host; if (current_pass != NULL) got_password(); else gtk_idle_add (get_login, NULL); /* start event loop */ DBG_Print("general", "initializing event loop\n"); gtk_timeout_add(initial_warmup_time, clear_warmup, NULL); start_idle_check(); gtk_main(); if (!quiet) printf("\n\nThanks for using GTKYahoo.\n"); if (logfile) { fprintf(logfile, "%s%s\n", make_timestamp(), "GTKYahoo exited."); fclose(logfile); } exit(0); } int reaper_timeout_callback(void) { int status; pid_t pid; while ((pid = wait3(&status, WNOHANG, NULL)) > 0); return (1); } int ping_timeout_callback(guint data) { /* Only handle if we're connected */ if (!connected) { append_notify_msg("Ping skipped, not connected."); return 1; } yahoo_cmd_ping(context); return (1); } /* setup timeout for processing packets */ void setup_process_packets_timeout(void) { /* check if we have any new data twice a second */ gtk_timeout_add(500, (void *) process_packets_callback, NULL); } /* setup reaper timeout */ void setup_reaper_timeout(void) { if (reaper_timeout_tag) { gtk_timeout_remove(reaper_timeout_tag); } reaper_timeout_tag = gtk_timeout_add(30 * 1000, (void *) reaper_timeout_callback, NULL); } /* setup or reset ping timeout */ void setup_ping_timeout(void) { if (ping_timeout_tag) { gtk_timeout_remove(ping_timeout_tag); } ping_timeout_tag = gtk_timeout_add(2 * 60 * 1000, (void *) ping_timeout_callback, NULL); } /* vim:ts=8:sw=8 */