/***************************************************************************** * gyach.c * * 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. * * Copyright (C) 2000-2002 Chris Pinkham * Released under the terms of the GPL. * *NO WARRANTY* * * cpinkham@corp.infi.net, cpinkham@bc2va.org * http://www4.infi.net/~cpinkham/gyach/ *****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #ifdef USE_PTHREAD_CREATE #include #endif #include "gyach.h" #include "aliases.h" #include "commands.h" #include "callbacks.h" #include "friends.h" #include "gyach_int.h" #include "history.h" #include "ignore.h" #include "images.h" #include "interface.h" #include "main.h" #include "parsecfg.h" #include "roomlist.h" #include "support.h" #include "users.h" #include "util.h" #include "yahoochat.h" YMSG9_SESSION ymsg_session, *ymsg_sess = &ymsg_session; #define URL_REGEX "\\w{1,}\\.\\w{1,}/|\\w{1,}\\.\\w{1,}\\.\\w{1,}" int debug_packets = 0; GtkWidget * login_window; GtkWidget * chat_window = NULL; GtkWidget * chat_status; GtkWidget * chat_entry; GtkWidget * setup_menu; GtkWidget * chat_username; GtkWidget * chat_password; GtkWidget * chat_roomname; GtkWidget * about_box = NULL; GtkWidget * setup_window = NULL; GtkWidget * find_window = NULL; guint st_cid; /* statusbar context_id */ time_t connect_time = 0; char *capture_filename = NULL; FILE *capture_fp = NULL; int my_status = 0; time_t auto_away_time; time_t child_timeout; char *search_text = NULL; int search_pos = -1; int search_case_sensitive = 0; int logged_in = 0; int def_user = -1; int auto_away = 15; int auto_login = 0; int buffer_size = 20; int use_bold = 0; int use_underline = 0; int use_italics = 0; int font_size = 14; int force_lowercase = 0; int ping_minutes = 10; int chat_window_x = 0; int chat_window_y = 0; int chat_window_width = 630; int chat_window_height = 450; int chat_text_width = 480; int chat_user_height = 200; int remember_position = 0; int remember_password = 1; int limit_lfs = 15; int chat_timestamp = 0; int chat_timestamp_pm = 0; int auto_reconnect = 1; int use_proxy = 0; int custom_color_red = 0; int custom_color_green = 0; int custom_color_blue = 0; int proxy_port = 80; char *auto_reply_msg = NULL; char *browser_command = NULL; char *filter_command = NULL; char *font_name = NULL; char *font_family = NULL; char *use_color = NULL; char *custom_away_message = NULL; char *password = NULL; char *def_room = NULL; char *proxy_host = NULL; char cfg_filename[256] = ""; char GYACH_CFG_DIR[256] = ""; char GYACH_CFG_COMMON_DIR[256] = ""; char username[41] = ""; char roomname[129] = ""; int connected = 0; int valid_user = 0; GList *favroom_list = NULL; GList *login_list = NULL; GList *tab_user = NULL; cfgList *username_list = NULL; cfgList *fav_room_list = NULL; cfgStruct cfg[] = { /* parameter type address of variable */ { "username", CFG_STRING_LIST, &username_list }, { "fav_room", CFG_STRING_LIST, &fav_room_list }, { "show_blended_colors", CFG_INT, &show_blended_colors }, { "show_colors", CFG_INT, &show_colors }, { "show_fonts", CFG_INT, &show_fonts }, { "debug_packets", CFG_INT, &debug_packets }, { "def_user", CFG_INT, &def_user }, { "auto_away", CFG_INT, &auto_away }, { "auto_login", CFG_INT, &auto_login }, { "pm_brings_back", CFG_INT, &pm_brings_back }, { "buffer_size", CFG_INT, &buffer_size }, { "show_html", CFG_INT, &show_html }, { "show_statuses", CFG_INT, &show_statuses }, { "show_enters", CFG_INT, &show_enters }, { "profile_viewer", CFG_INT, &profile_viewer }, { "ignore_on_mults", CFG_INT, &ignore_on_mults }, { "mute_on_mults", CFG_INT, &mute_on_mults }, { "use_bold", CFG_INT, &use_bold }, { "use_italics", CFG_INT, &use_italics }, { "use_underline", CFG_INT, &use_underline }, { "force_lowercase", CFG_INT, &force_lowercase }, { "suppress_mult", CFG_INT, &suppress_mult }, { "ping_minutes", CFG_INT, &ping_minutes }, { "chat_window_x", CFG_INT, &chat_window_x }, { "chat_window_y", CFG_INT, &chat_window_y }, { "chat_window_width", CFG_INT, &chat_window_width }, { "chat_window_height", CFG_INT, &chat_window_height }, { "chat_text_width", CFG_INT, &chat_text_width }, { "chat_user_height", CFG_INT, &chat_user_height }, { "disp_auto_ignored", CFG_INT, &disp_auto_ignored }, { "remember_position", CFG_INT, &remember_position }, { "remember_password", CFG_INT, &remember_password }, { "limit_lfs", CFG_INT, &limit_lfs }, { "auto_reply_when_away", CFG_INT, &auto_reply_when_away }, { "pm_in_sep_windows", CFG_INT, &pm_in_sep_windows }, { "pm_from_friends", CFG_INT, &pm_from_friends }, { "pm_from_users", CFG_INT, &pm_from_users }, { "pm_from_all", CFG_INT, &pm_from_all }, { "auto_raise_pm", CFG_INT, &auto_raise_pm }, { "auto_close_roomlist", CFG_INT, &auto_close_roomlist }, { "cache_room_list", CFG_INT, &cache_room_list }, { "chat_timestamp", CFG_INT, &chat_timestamp }, { "chat_timestamp_pm", CFG_INT, &chat_timestamp_pm }, { "ignore_guests", CFG_INT, &ignore_guests }, { "auto_reconnect", CFG_INT, &auto_reconnect }, { "history_limit", CFG_INT, &history_limit }, { "custom_color_red", CFG_INT, &custom_color_red }, { "custom_color_blue", CFG_INT, &custom_color_blue }, { "custom_color_green", CFG_INT, &custom_color_green }, { "auto_reply_msg", CFG_STRING, &auto_reply_msg }, { "use_color", CFG_STRING, &use_color }, { "custom_away_message", CFG_STRING, &custom_away_message }, { "browser_command", CFG_STRING, &browser_command }, { "filter_command", CFG_STRING, &filter_command }, { "font_name", CFG_STRING, &font_name }, { "font_family", CFG_STRING, &font_family }, { "font_size", CFG_INT, &font_size }, { "indent_wrapped", CFG_INT, &indent_wrapped }, { "password", CFG_STRING, &password }, { "def_room", CFG_STRING, &def_room }, { "show_adult", CFG_INT, &show_adult }, { "show_avatars", CFG_INT, &show_avatars }, { "send_avatar", CFG_STRING, &send_avatar }, { "show_emoticons", CFG_INT, &show_emoticons }, { "use_proxy", CFG_INT, &use_proxy }, { "proxy_host", CFG_STRING, &proxy_host }, { "proxy_port", CFG_INT, &proxy_port }, { "first_post_is_url", CFG_INT, &first_post_is_url }, { "first_post_is_pm", CFG_INT, &first_post_is_pm }, { "url_from_nonroom_user", CFG_INT, &url_from_nonroom_user }, { "highlight_friends", CFG_INT, &highlight_friends }, #ifdef USE_GTK2 { "transparent_bg", CFG_INT, &transparent_bg }, { "shading_r", CFG_INT, &shading_r }, { "shading_g", CFG_INT, &shading_g }, { "shading_b", CFG_INT, &shading_b }, #endif { NULL, CFG_END, NULL } }; /* * this function serves the purpose of upgrading Gyach user's config files * to the new *nix client chat standard at: * http://www.edge-op.org/files/chatstandard * * this standard should be supported by curphoo and hopefully curfloo in * due time */ void upgrade_config_to_standard() { char filename[256]; char filename2[256]; #ifndef OS_WINDOWS char filename3[256]; struct stat sbuf; struct stat sbuf2; struct stat sbuf3; #endif #ifdef OS_WINDOWS strcpy( filename, "./ignore.list" ); strcpy( filename2, "./ignore" ); rename( filename, filename2 ); strcpy( filename, "./aliases" ); strcpy( filename2, "./commands" ); rename( filename, filename2 ); return; #else snprintf( filename, sizeof(filename), "%s/.gyach/gyachrc", getenv( "HOME" )); snprintf( filename2, sizeof(filename2), "%s/.yahoorc/gyach/gyachrc", getenv( "HOME" )); if (( ! stat( filename, &sbuf )) && ( stat( filename2, &sbuf2 ))) { /* Convert from ~/.gyach directory to ~/.yahoorc format */ /* make ~/.yahoorc dir if doesn't exist */ snprintf( filename3, sizeof(filename3), "%s/.yahoorc", getenv( "HOME" )); if ( stat( filename3, &sbuf3 )) { mkdir( filename3, 0700 ); } /* remove ~/.yahoorc/gyach dir if exists */ /* only do this since we know no ~/.yahoorc/gyach/gyachrc file exists */ snprintf( filename3, sizeof(filename3), "%s/.yahoorc/gyach", getenv( "HOME" )); if ( ! stat( filename3, &sbuf3 )) { rmdir( filename3 ); } /* move gyachrc file */ rename( filename, filename2 ); /* move aliases/commands file */ snprintf( filename, sizeof(filename), "%s/.gyach/aliases", getenv( "HOME" )); snprintf( filename2, sizeof(filename2), "%s/.gyach/commands", getenv( "HOME" )); /* waiting on this for fan to implement standard snprintf( filename2, sizeof(filename2), "%s/.yahoorc/gyach/commands", getenv( "HOME" )); */ if ( stat( filename2, &sbuf2 )) { /* if commands doesn't exist then move aliases file */ rename( filename, filename2 ); } /* move ignore.list file */ snprintf( filename, sizeof(filename), "%s/.gyach/ignore.list", getenv( "HOME" )); snprintf( filename2, sizeof(filename2), "%s/.gyach/ignore", getenv( "HOME" )); /* waiting on this for fan to implement standard snprintf( filename2, sizeof(filename2), "%s/.yahoorc/ignore", getenv( "HOME" )); */ if ( stat( filename2, &sbuf2 )) { /* if ignore list doesn't exist them move ignore.list file */ rename( filename, filename2 ); } /* finally move the gyach directory itself */ snprintf( filename, sizeof(filename), "%s/.gyach", getenv( "HOME" )); snprintf( filename2, sizeof(filename2), "%s/.yahoorc/gyach", getenv( "HOME" )); rename( filename, filename2 ); } /* convert from 0.9.1-pre3 */ snprintf( filename, sizeof(filename), "%s/.yahoorc/ignore", getenv( "HOME" )); snprintf( filename2, sizeof(filename2), "%s/.yahoorc/gyach/ignore", getenv( "HOME" )); if (( ! stat( filename, &sbuf )) && ( stat( filename2, &sbuf2 ))) { rename( filename, filename2 ); } snprintf( filename, sizeof(filename), "%s/.yahoorc/commands", getenv( "HOME" )); snprintf( filename2, sizeof(filename2), "%s/.yahoorc/gyach/commands", getenv( "HOME" )); if (( ! stat( filename, &sbuf )) && ( stat( filename2, &sbuf2 ))) { rename( filename, filename2 ); } #endif } void gyach_copy( char *src, char *dest ) { FILE *in, *out; int ch; char fn_src[256]; char fn_dest[256]; snprintf( fn_src, sizeof(fn_src), "%s/.yahoorc/%s", getenv( "HOME" ), src ); snprintf( fn_dest, sizeof(fn_dest), "%s/.yahoorc/%s", getenv( "HOME" ), dest ); in = fopen( fn_src, "r" ); if ( in ) { out = fopen( fn_dest, "w" ); if ( out ) { while(( ch = fgetc( in )) != EOF ) { fputc( ch, out ); } fclose( out ); } fclose( in ); } } void gyach_backup() { char filename[256]; char filename2[256]; char backup_dir[256]; struct stat sbuf; int i; snprintf( backup_dir, sizeof(backup_dir), "%s/backups", GYACH_CFG_DIR ); if ( stat( backup_dir, &sbuf )) { mkdir( backup_dir, 0700 ); } for( i = 8; i >= 0; i-- ) { snprintf( filename, sizeof(filename), "%s/gyachrc.%d", backup_dir, i ); snprintf( filename2, sizeof(filename2), "%s/gyachrc.%d", backup_dir, i + 1 ); rename( filename, filename2 ); snprintf( filename, sizeof(filename), "%s/ignore.%d", backup_dir, i ); snprintf( filename2, sizeof(filename2), "%s/ignore.%d", backup_dir, i + 1 ); rename( filename, filename2 ); snprintf( filename, sizeof(filename), "%s/commands.%d", backup_dir, i ); snprintf( filename2, sizeof(filename2), "%s/commands.%d", backup_dir, i + 1 ); rename( filename, filename2 ); } /* now copy originals to *.0 in backups dir */ gyach_copy( "gyach/gyachrc", "gyach/backups/gyachrc.0" ); gyach_copy( "gyach/ignore", "gyach/backups/ignore.0" ); gyach_copy( "gyach/commands", "gyach/backups/commands.0" ); } void gyach_init() { char pixmap_dir[512]; #ifndef OS_WINDOWS struct stat sbuf; #endif DBG( 1, "gyach_init()\n" ); upgrade_config_to_standard(); #ifdef OS_WINDOWS sprintf( GYACH_CFG_DIR, "." ); sprintf( GYACH_CFG_COMMON_DIR, "." ); #else snprintf( GYACH_CFG_COMMON_DIR, sizeof(GYACH_CFG_COMMON_DIR), "%s/.yahoorc", getenv( "HOME" )); if ( stat( GYACH_CFG_COMMON_DIR, &sbuf )) mkdir( GYACH_CFG_COMMON_DIR, 0700 ); snprintf( GYACH_CFG_DIR, sizeof(GYACH_CFG_DIR), "%s/.yahoorc/gyach", getenv( "HOME" )); if ( stat( GYACH_CFG_DIR, &sbuf )) mkdir( GYACH_CFG_DIR, 0700 ); /* overrides until we get the standard in place */ snprintf( GYACH_CFG_COMMON_DIR, sizeof(GYACH_CFG_COMMON_DIR), "%s/.yahoorc/gyach", getenv( "HOME" )); gyach_backup(); #endif snprintf( pixmap_dir, sizeof(pixmap_dir), "%s/pixmaps", PACKAGE_DATA_DIR ); add_pixmap_directory( pixmap_dir ); read_config(); ignore_load(); history_load(); smileys_load(); if ( ! history_list ) { history_add( "" ); } auto_away_time = time(NULL) + ( auto_away * 60 ); if ( ! font_name ) { #ifdef USE_GTK2 font_name = strdup( "times 10" ); #else font_name = strdup( "-*-times-medium-r-normal-*-14-*-*-*-*-*-iso8859-1" ); #endif } else { #ifdef USE_GTK2 if ( strstr( font_name, "-*-" )) { free( font_name ); font_name = strdup( "times 10" ); if ( font_family) free( font_family ); font_family = strdup( "times" ); font_size = 10; use_bold = 0; use_italics = 0; } #else free( font_name ); font_name = strdup( "-*-times-medium-r-normal-*-14-*-*-*-*-*-iso8859-1" ); #endif } if ( ! font_family ) font_family = strdup( "times" ); if ( font_size < 0 ) font_size = 14; if ( ! browser_command ) { browser_command = strdup( "netscape -noraise -remote openURL\\(%s\\)" ); } } void process_gtk_events() { DBG( 251, "process_gtk_events()\n" ); return; while( gtk_events_pending()) gtk_main_iteration(); } void read_config() { struct stat sbuf; cfgList *this_item; DBG( 1, "read_config()\n" ); if ( ! getenv( "HOME" )) { return; } snprintf( cfg_filename, sizeof(cfg_filename), "%s/gyachrc", GYACH_CFG_DIR ); if ( stat( cfg_filename, &sbuf )) write_config(); cfgParse( cfg_filename, cfg, CFG_SIMPLE ); if ( ! use_color ) use_color = strdup( "black" ); ymsg_sess->debug_packets = debug_packets; this_item = username_list; while( this_item ) { login_list = g_list_append( login_list, strdup( this_item->str )); this_item = this_item->next; } this_item = fav_room_list; while( this_item ) { favroom_list = g_list_append( favroom_list, strdup( this_item->str )); this_item = this_item->next; } } void write_config() { GtkWidget *tmp_widget; GList *this_item = NULL; FILE *gyachrc; DBG( 1, "write_config()\n" ); snprintf( cfg_filename, sizeof(cfg_filename), "%s/gyachrc", GYACH_CFG_DIR ); gyachrc = fopen( cfg_filename, "wb" ); if ( gyachrc ) { if ( chat_window ) { tmp_widget = chat_window; if ( remember_position ) { gdk_window_get_position( tmp_widget->window, &chat_window_x, &chat_window_y ); } /* get current sizes */ chat_window_width = tmp_widget->allocation.width; chat_window_height = tmp_widget->allocation.height; tmp_widget = lookup_widget( chat_window, "hbox2" ); chat_text_width = tmp_widget->allocation.width; tmp_widget = lookup_widget( chat_window, "scrolledwindow3" ); chat_user_height = tmp_widget->allocation.height; } /* FIXME, put while loop here to save "username" */ this_item = login_list; while( this_item ) { fprintf( gyachrc, "username = \"%s\"\n", (char *)this_item->data ); this_item = g_list_next( this_item ); } if (password) fprintf( gyachrc, "password = \"%s\"\n", password ); this_item = favroom_list; while( this_item ) { fprintf( gyachrc, "fav_room = \"%s\"\n", (char *)this_item->data ); this_item = g_list_next( this_item ); } if (auto_reply_msg) fprintf( gyachrc, "auto_reply_msg = \"%s\"\n", auto_reply_msg ); if (custom_away_message) fprintf( gyachrc, "custom_away_message = \"%s\"\n", custom_away_message ); if (use_color) fprintf( gyachrc, "use_color = \"%s\"\n", use_color ); if (font_name) fprintf( gyachrc, "font_name = \"%s\"\n", font_name ); if (font_family) fprintf( gyachrc, "font_family = \"%s\"\n", font_family ); if (browser_command) fprintf( gyachrc, "browser_command = \"%s\"\n", browser_command ); if (filter_command) fprintf( gyachrc, "filter_command = \"%s\"\n", filter_command ); fprintf( gyachrc, "show_blended_colors = %d\n", show_blended_colors ); fprintf( gyachrc, "show_colors = %d\n", show_colors ); fprintf( gyachrc, "show_fonts = %d\n", show_fonts ); fprintf( gyachrc, "debug_packets = %d\n", ymsg_sess->debug_packets ); fprintf( gyachrc, "auto_away = %d\n", auto_away ); fprintf( gyachrc, "auto_login = %d\n", auto_login ); fprintf( gyachrc, "pm_brings_back = %d\n", pm_brings_back ); fprintf( gyachrc, "buffer_size = %d\n", buffer_size ); fprintf( gyachrc, "show_html = %d\n", show_html ); fprintf( gyachrc, "show_statuses = %d\n", show_statuses ); fprintf( gyachrc, "show_enters = %d\n", show_enters ); fprintf( gyachrc, "profile_viewer = %d\n", profile_viewer ); fprintf( gyachrc, "ignore_on_mults = %d\n", ignore_on_mults ); fprintf( gyachrc, "mute_on_mults = %d\n", mute_on_mults ); fprintf( gyachrc, "use_bold = %d\n", use_bold ); fprintf( gyachrc, "use_italics = %d\n", use_italics ); fprintf( gyachrc, "use_underline = %d\n", use_underline ); fprintf( gyachrc, "force_lowercase = %d\n", force_lowercase ); fprintf( gyachrc, "suppress_mult = %d\n", suppress_mult ); fprintf( gyachrc, "ping_minutes = %d\n", ping_minutes ); fprintf( gyachrc, "font_size = %d\n", font_size ); fprintf( gyachrc, "indent_wrapped = %d\n", indent_wrapped ); #ifdef USE_GTK2 fprintf( gyachrc, "transparent_bg = %d\n", transparent_bg ); fprintf( gyachrc, "shading_r = %d\n", shading_r ); fprintf( gyachrc, "shading_g = %d\n", shading_g ); fprintf( gyachrc, "shading_b = %d\n", shading_b ); #endif if ( remember_position ) { fprintf( gyachrc, "chat_window_x = %d\n", chat_window_x ); fprintf( gyachrc, "chat_window_y = %d\n", chat_window_y ); } fprintf( gyachrc, "chat_window_width = %d\n", chat_window_width ); fprintf( gyachrc, "chat_window_height = %d\n", chat_window_height ); fprintf( gyachrc, "chat_text_width = %d\n", chat_text_width ); fprintf( gyachrc, "chat_user_height = %d\n", chat_user_height ); fprintf( gyachrc, "disp_auto_ignored = %d\n", disp_auto_ignored ); fprintf( gyachrc, "remember_position = %d\n", remember_position ); fprintf( gyachrc, "remember_password = %d\n", remember_password ); fprintf( gyachrc, "limit_lfs = %d\n", limit_lfs ); fprintf( gyachrc, "auto_reply_when_away = %d\n", auto_reply_when_away ); fprintf( gyachrc, "pm_in_sep_windows = %d\n", pm_in_sep_windows ); fprintf( gyachrc, "pm_from_friends = %d\n", pm_from_friends ); fprintf( gyachrc, "pm_from_users = %d\n", pm_from_users ); fprintf( gyachrc, "pm_from_all = %d\n", pm_from_all ); fprintf( gyachrc, "auto_raise_pm = %d\n", auto_raise_pm ); fprintf( gyachrc, "auto_close_roomlist = %d\n", auto_close_roomlist ); fprintf( gyachrc, "cache_room_list = %d\n", cache_room_list ); fprintf( gyachrc, "chat_timestamp = %d\n", chat_timestamp ); fprintf( gyachrc, "chat_timestamp_pm = %d\n", chat_timestamp_pm ); fprintf( gyachrc, "ignore_guests = %d\n", ignore_guests ); fprintf( gyachrc, "auto_reconnect = %d\n", auto_reconnect ); fprintf( gyachrc, "history_limit = %d\n", history_limit ); fprintf( gyachrc, "show_adult = %d\n", show_adult ); fprintf( gyachrc, "use_proxy = %d\n", use_proxy ); fprintf( gyachrc, "show_avatars = %d\n", show_avatars ); fprintf( gyachrc, "show_emoticons = %d\n", show_emoticons ); fprintf( gyachrc, "custom_color_red = %d\n", custom_color_red ); fprintf( gyachrc, "custom_color_blue = %d\n", custom_color_blue ); fprintf( gyachrc, "custom_color_green = %d\n", custom_color_green ); if ( proxy_host ) { fprintf( gyachrc, "proxy_host = \"%s\"\n", proxy_host ); } fprintf( gyachrc, "proxy_port = %d\n", proxy_port ); if ( send_avatar ) { fprintf( gyachrc, "send_avatar = \"%s\"\n", send_avatar ); } fprintf( gyachrc, "first_post_is_url = %d\n", first_post_is_url ); fprintf( gyachrc, "first_post_is_pm = %d\n", first_post_is_pm ); fprintf( gyachrc, "url_from_nonroom_user = %d\n", url_from_nonroom_user ); fprintf( gyachrc, "highlight_friends = %d\n", highlight_friends ); fclose( gyachrc ); } } int chars_match( char *str1, char *str2 ) { int i = 0; DBG( 21, "chars_match( '%s', '%s' )\n", str1, str2 ); /* if the 2nd string is longer then it's can't be the same word */ if ( strlen( str2 ) > strlen( str1 ) ) return( 0 ); while(( *str1 ) && ( *str2 ) && ( *str1 == *str2 )) { i++; str1++; str2++; } DBG( 22, "RETURN chars_match() == %d\n", i ); return( i ); } char *find_file_best_match( char *partial ) { static char best_match[513] = ""; int matches = 0; char dir[513] = ""; char *ptr; char *lptr; char *dptr; char lcp[513] = ""; /* longest common part */ DIR *d; struct dirent *dp; struct stat sbuf; my_strncpy( dir, partial, sizeof(dir)); ptr = dir + strlen( dir ) - 1; while(( ptr >= dir ) && ( *ptr != '/' )) { ptr--; } if ( ptr < dir ) { /* didn't find any / chars, so search current directory */ d = opendir( "./" ); ptr = dir; } else { /* found a / so set to zero and search that directory */ *ptr = '\0'; ptr++; if ( dir[0] ) { d = opendir( dir ); } else { d = opendir( "/" ); } } /* return if couldn't open directory */ if ( d == NULL ) { my_strncpy( best_match, partial, sizeof(best_match)); return( best_match ); } /* do the search */ while( 1 ) { dp = readdir( d ); if ( ! dp ) { break; } if ( ! strncmp( dp->d_name, ptr, strlen( ptr ))) { /* this file matches */ matches++; if ( lcp[0] ) { /* this isn't our first match */ lptr = lcp; dptr = dp->d_name; while(( *lptr ) && ( *dptr ) && ( *lptr == *dptr )) { lptr++; dptr++; } if ( *lptr ) { /* truncate longest at this point */ *lptr = '\0'; } } else { /* this is our first match so it's our longest */ my_strncpy( lcp, dp->d_name, sizeof(lcp)); } } } closedir( d ); if ( strchr( partial, '/' )) { if ( dir[0] ) { my_strncpy( best_match, dir, sizeof(best_match)); } else { strcpy( best_match, "/" ); } } else { strcpy( best_match, "./" ); } if ( best_match[ strlen( best_match ) - 1 ] != '/' ) { strcat( best_match, "/" ); } strcat( best_match, lcp ); /* check if this is a dir and if so and matches == 1, append a / */ if ( ! stat( best_match, &sbuf )) { if ( S_ISDIR(sbuf.st_mode)) { strcat( best_match, "/" ); } } return( best_match ); } /* * fixme, this function needs cleaning up probably */ char *find_tab_best_match( char *partial ) { static char best_match[513] = ""; static char last_part[513] = ""; char part[513] = ""; GList *last_user = tab_user; gchar *tmp_str; int row; DBG( 11, "find_tab_best_match( '%s' )\n", partial ); if ( ! partial ) { return( "" ); } /* bounds checking */ if ( ! partial[0] ) { best_match[0] = '\0'; last_part[0] = '\0'; return( best_match ); } lower_str( best_match ); my_strncpy( part, partial, sizeof(part)); /* IF we haven't been in here before */ /* OR this part isn't equal to either the last part or the best_match */ if (( ! last_part[0] ) || (( strcmp( part, last_part )) && ( strcmp( part, best_match )))) { best_match[0] = '\0'; my_strncpy( last_part, part, sizeof(last_part)); tab_user = full_list; last_user = full_list; } else { /* we are still trying to find a match for the last part so revert */ my_strncpy( part, last_part, sizeof(part)); if (( tab_user ) && ( ! strcmp( tab_user->data, best_match ))) { tab_user = g_list_next( tab_user ); } } /* IF we're past the end start over */ if ( ! tab_user ) tab_user = full_list; /* go through the chatter list looking for the next match */ while(( tab_user ) && ( strncmp( tab_user->data, part, strlen( part )))) { tab_user = g_list_next( tab_user ); } /* did we hit the end of the list without finding any more matches */ /* after starting at the beginning. if so, return */ if (( ! tab_user ) && ( last_user == full_list )) { my_strncpy( best_match, part, sizeof(best_match)); tab_user = full_list; return( best_match ); } if ( ! tab_user ) { /* if we didn't find a match, then start from the beginning again */ tab_user = full_list; while(( tab_user ) && ( tab_user != last_user ) && ( strncmp( tab_user->data, part, strlen( part )))) { tab_user = g_list_next( tab_user ); } } if ((( tab_user ) && ( strncmp( tab_user->data, part, strlen( part )))) || ( ! tab_user )) { my_strncpy( best_match, part, sizeof(best_match)); tab_user = full_list; } else { my_strncpy( best_match, tab_user->data, sizeof(best_match)); } if (( row = find_user_row( best_match )) > -1 ) { gtk_clist_get_text(GTK_CLIST(chat_users), row, 1, &tmp_str ); my_strncpy( best_match, tmp_str, sizeof(best_match)); } if ( ! best_match[0] ) { my_strncpy( best_match, partial, sizeof(best_match)); } return( best_match ); } void build_tab_complete_list() { /* free the list before we rebuild */ if ( full_list ) { full_list = gyach_g_list_free( full_list ); } /* add the room users, people that just left, and friends to the list */ full_list = gyach_g_list_copy( user_list ); full_list = gyach_g_list_merge( full_list, gyach_g_list_copy( left_list )); full_list = gyach_g_list_merge( full_list, gyach_g_list_copy( friend_list)); /* add things like aliases, built-in commands, etc. */ append_aliases_to_list( full_list ); append_commands_to_list( full_list ); /* sort the list before we're done */ full_list = g_list_sort( full_list, (GCompareFunc)gstrcmp ); full_list = g_list_first( full_list ); tab_user = NULL; } void tab_complete() { const char *text = gtk_entry_get_text( GTK_ENTRY(chat_entry)); char new[1025]; char *ptr; char *dest; DBG( 11, "tab_complete()\n" ); if ( ! strlen( text )) return; my_strncpy( new, (char *)text, sizeof(new)); ptr = new + strlen( new ) - 1; while(( ptr > new ) && ( ! isspace( *ptr ))) ptr--; if ( isspace( *ptr )) ptr++; if ( *ptr == '!' ) { /* tab completion of filename */ ptr++; if ( *ptr == '!' ) { ptr++; } dest = ptr; my_strncpy( dest, find_file_best_match( ptr ), sizeof(new) - (dest - new)); } else if (( ptr != new ) && ( *ptr == '/' )) { dest = ptr; my_strncpy( dest, find_file_best_match( ptr ), sizeof(new) - (dest - new)); } else { /* tab completion of username/alias/command/etc. */ dest = ptr; lower_str( ptr ); my_strncpy( dest, find_tab_best_match( ptr ), sizeof(new) - (dest - new)); } gtk_entry_set_text( GTK_ENTRY(chat_entry), new ); gtk_entry_set_position( GTK_ENTRY(chat_entry), strlen(new)); } void io_callback(gpointer data, gint source, GdkInputCondition condition) { /* Tell the library to retrieve any newly available data */ if (condition == GDK_INPUT_READ) { if ( ymsg9_recv_data( ymsg_sess )) { show_yahoo_packet(); } } else { /* invalid/unknown condition, so disconnect */ on_disconnect_activate( NULL, NULL ); } } int ping_callback( guint data ) { if ( logged_in ) ymsg9_ping( ymsg_sess ); return( 1 ); } int login_to_yahoo_chat( void ) { int result; gtk_statusbar_pop( GTK_STATUSBAR(chat_status), st_cid ); gtk_statusbar_push( GTK_STATUSBAR(chat_status), st_cid, "Opening Connection to chat server." ); ymsg_sess->port = YMSG9_CHAT_PORT; if ( use_proxy ) { my_strncpy( ymsg_sess->proxy_host, proxy_host, YMSG9_HOST_SIZE ); ymsg_sess->proxy_port = proxy_port; } else { /* make sure this is empty */ ymsg_sess->proxy_host[0] = '\0'; ymsg_sess->proxy_port = 0; } result = ymsg9_open_socket( ymsg_sess ); /* if we connected */ if ( result ) { gtk_statusbar_pop( GTK_STATUSBAR(chat_status), st_cid ); gtk_statusbar_push( GTK_STATUSBAR(chat_status), st_cid, "Requesting Login Key." ); #ifndef OS_WINDOWS ymsg_sess->io_callback_tag = gdk_input_add( ymsg_sess->sock, GDK_INPUT_READ, io_callback, (gpointer)0 ); #endif ymsg9_request_key( ymsg_sess ); gtk_statusbar_pop( GTK_STATUSBAR(chat_status), st_cid ); logged_in = 1; sleep( 1 ); ymsg_sess->ping_callback_tag = gtk_timeout_add( ping_minutes * 60 * 1000, (void *)ping_callback, NULL ); set_menu_connected( 1 ); return( 1 ); } else { gtk_statusbar_pop( GTK_STATUSBAR(chat_status), st_cid ); gtk_statusbar_push( GTK_STATUSBAR(chat_status), st_cid, ymsg_sess->error_msg ); return( 0 ); } return( 0 ); } void set_status_room_counts( void ) { int users; int ig_users; int mu_users; char buf[128]; char tmp[21]; gtk_statusbar_pop( GTK_STATUSBAR(chat_status), st_cid ); users = GTK_CLIST(chat_users)->rows; snprintf(buf, sizeof(buf), "Current Room: %s (%d users", ymsg_sess->room, users ); ig_users = ignore_count_in_room(); if ( ig_users ) { snprintf( tmp, sizeof(tmp), ", %d ignored", ig_users ); my_strncat( buf, tmp, sizeof(buf)); } mu_users = mute_count_in_room(); if ( mu_users ) { snprintf( tmp, sizeof(tmp), ", %d muted", mu_users ); my_strncat( buf, tmp, sizeof(buf)); } my_strncat( buf, ")", sizeof(buf)); gtk_statusbar_push( GTK_STATUSBAR(chat_status), st_cid, buf ); } int check_clicked_text( char *text ) { char beginning[513]; char url[257]; char *ptr; regex_t url_regex; #ifdef USE_PTHREAD_CREATE pthread_t prof_thread; #endif if ( ! text ) return( 0 ); my_strncpy( url, text, sizeof(url)); g_free( text ); ptr = NULL; /* match anything with 3 strings separated by periods */ regcomp( &url_regex, URL_REGEX, REG_EXTENDED | REG_ICASE | REG_NOSUB ); if (( strstr( url, "http://" )) || ( strstr( url, "ftp://" )) || ( ! regexec( &url_regex, url, 0, NULL, 0 ))) { ptr = strdup( url ); } else if ( ! strcasecmp( url, "gyach" )) { ptr = strdup( GYACH_URL ); } else if ( ! strcasecmp( url, "curphoo" )) { ptr = strdup( "http://www.waduck.com/~curphoo/" ); } else if (( ! strcasecmp( url, "curfloo" )) || ( ! strcasecmp( url, "mybitch" )) || ( ! strcasecmp( url, "ncchat" )) || ( ! strcasecmp( url, "qtchat" )) || ( ! strcasecmp( url, "yahoonix" )) || ( ! strcasecmp( url, "yahscrewed" )) || ( ! strcasecmp( url, "ychat" ))) { /* curfloo links tak's patched copy http://bard.sytes.net/miscellaneous_code/curfloo-0.76c.tar.gz */ ptr = strdup( "http://www.edge-op.org/links1.html#chat" ); } else { /* if ends in a : then strip it */ if (( url[strlen(url)-1] == ':' ) || ( url[strlen(url)-1] == ',' )) { url[strlen(url)-1] = '\0'; } /* check to see if highlighted text is actually a username */ if ( find_user_row( url ) >= 0 ) { my_strncpy( beginning, url, sizeof(beginning)); snprintf( url, sizeof(url), "http://profiles.yahoo.com/%s", beginning ); ptr = strdup( url ); } } regfree( &url_regex ); if ( ptr ) { if (strstr( ptr, "profiles.yahoo.com") && strstr( ptr, beginning )) { free( ptr ); ptr = strdup( beginning ); gtk_timeout_add( 5, (void *)view_profile, (gpointer)ptr ); return( 0 ); } #ifdef USE_PTHREAD_CREATE pthread_create( &prof_thread, NULL, display_url, (void *)ptr ); #else display_url( (void *)ptr ); #endif } return( 0 ); } int search_chat_text( int case_sensitive ) { #ifdef USE_GTK2 GtkTextBuffer *text_buffer; GtkTextIter start; GtkTextIter end; #else GtkEditable *editable; #endif GtkWidget *tmp_widget; int pos; int len; int max_pos; char *str; int started_at = search_pos; tmp_widget = ct_widget(); if (( search_pos == -1 ) && ( ! search_text )) { /* initial search so popup window */ } else { /* search again starting at search_pos + 1 */ pos = search_pos + 1; len = strlen( search_text ); #ifdef USE_GTK2 /* if they have both panes visible, then scroll the top one */ if ( gtk_paned_get_position( GTK_PANED( vpaned ))) { tmp_widget = top_text; } text_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(tmp_widget)); max_pos = gtk_text_buffer_get_char_count( text_buffer ) - len; #else editable = >K_TEXT(tmp_widget)->editable; max_pos = gtk_text_get_length( GTK_TEXT(tmp_widget)) - len; #endif if ( pos > max_pos ) { return( 0 ); } #ifdef USE_GTK2 gtk_text_buffer_get_iter_at_offset( text_buffer, &start, pos ); gtk_text_buffer_get_iter_at_offset( text_buffer, &end, pos + len ); str = gtk_text_buffer_get_text( text_buffer, &start, &end, 0 ); #else str = gtk_editable_get_chars( editable, pos, pos + len ); #endif if ( case_sensitive ) { while(( strcmp( search_text, str )) && ( pos < max_pos )) { g_free( str ); pos++; #ifdef USE_GTK2 gtk_text_iter_forward_char( &start ); gtk_text_iter_forward_char( &end ); str = gtk_text_buffer_get_text( text_buffer, &start, &end, 0 ); #else str = gtk_editable_get_chars( editable, pos, pos + len ); #endif } if ( ! strcmp( search_text, str )) { search_pos = pos; #ifdef USE_GTK2 gtk_text_buffer_move_mark_by_name( text_buffer, "insert", &start ); gtk_text_buffer_move_mark_by_name( text_buffer, "selection_bound", &end ); gtk_text_view_scroll_to_iter( GTK_TEXT_VIEW(tmp_widget), &start, 0.0, 0, 0.0, 0.0 ); #else /* found, so select */ gtk_editable_select_region( editable, pos, pos + len ); /* scroll window to found position */ gtk_text_set_point( GTK_TEXT(tmp_widget), pos ); gtk_text_insert(GTK_TEXT(tmp_widget), NULL,NULL,NULL, " ", 1 ); gtk_text_backward_delete( GTK_TEXT(tmp_widget), 1 ); #endif return( 1 ); } else if ( started_at != -1 ) { /* loop around if we didn't start at the top of the window */ search_pos = -1; search_chat_text( case_sensitive ); } } else { while(( strcasecmp( search_text, str )) && ( pos < max_pos )) { g_free( str ); pos++; #ifdef USE_GTK2 gtk_text_iter_forward_char( &start ); gtk_text_iter_forward_char( &end ); str = gtk_text_buffer_get_text( text_buffer, &start, &end, 0 ); #else str = gtk_editable_get_chars( editable, pos, pos + len ); #endif } if ( ! strcasecmp( search_text, str )) { search_pos = pos; #ifdef USE_GTK2 gtk_text_buffer_move_mark_by_name( text_buffer, "insert", &start ); gtk_text_buffer_move_mark_by_name( text_buffer, "selection_bound", &end ); gtk_text_view_scroll_to_iter( GTK_TEXT_VIEW(tmp_widget), &start, 0.0, 0, 0.0, 0.0 ); #else /* found, so select */ gtk_editable_select_region( editable, pos, pos + len ); /* scroll window to found position */ gtk_text_set_point( GTK_TEXT(tmp_widget), pos ); gtk_text_insert(GTK_TEXT(tmp_widget), NULL,NULL,NULL, " ", 1 ); gtk_text_backward_delete( GTK_TEXT(tmp_widget), 1 ); #endif return( 1 ); } else if ( started_at != -1 ) { /* loop around if we didn't start at the top of the window */ search_pos = -1; search_chat_text( case_sensitive ); } } g_free( str ); } return( 0 ); } #define URL_CHAR_LIST "abcdefghijklmnopqrstuvwxyzzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\\.?!/%&_:~=-#" #ifdef USE_GTK2 gchar *get_word_at_pos( GtkTextView *text_view, gint x, gint y ) { GtkTextBuffer *text_buffer; GtkTextIter start; GtkTextIter iter; GtkTextIter end; gchar *ch; gchar *result; int not_at_end = 1; text_buffer = gtk_text_view_get_buffer( text_view ); gtk_text_view_get_iter_at_location( text_view, &start, x, y ); gtk_text_view_get_iter_at_location( text_view, &iter, x, y ); if ( gtk_text_iter_backward_char( &start )) { not_at_end = 1; ch = gtk_text_buffer_get_text( text_buffer, &start, &iter, 0 ); while(( strchr( URL_CHAR_LIST, *ch )) && ( not_at_end )) { if ( gtk_text_iter_backward_char( &start )) { g_free( ch ); gtk_text_iter_backward_char( &iter ); ch = gtk_text_buffer_get_text( text_buffer, &start, &iter, 0 ); } else { not_at_end = 0; } } g_free( ch ); if ( not_at_end ) { gtk_text_iter_forward_char( &start ); } } gtk_text_view_get_iter_at_location( text_view, &iter, x, y ); gtk_text_view_get_iter_at_location( text_view, &end, x, y ); if ( gtk_text_iter_forward_char( &end )) { not_at_end = 1; ch = gtk_text_buffer_get_text( text_buffer, &iter, &end, 0 ); while(( strchr( URL_CHAR_LIST, *ch )) && ( not_at_end )) { if ( gtk_text_iter_forward_char( &end )) { g_free( ch ); gtk_text_iter_forward_char( &iter ); ch = gtk_text_buffer_get_text( text_buffer, &iter, &end, 0 ); } else { not_at_end = 0; } } g_free( ch ); if ( not_at_end ) { gtk_text_iter_backward_char( &end ); } } result = gtk_text_buffer_get_text( text_buffer, &start, &end, 0 ); /* strip off any CR or LF at the end of the result */ while( result[strlen(result)-1] == '\n' ) { result[strlen(result)-1] = '\0'; } return( result ); } #else char *get_word_at_cursor( GtkText *text ) { GtkEditable *editable; int start, end; char *ch; start = end = text->cursor_mark.index; editable = >K_TEXT(text)->editable; start--; ch = gtk_editable_get_chars( editable, start, start+1 ); if ( ch ) { while(( strchr( URL_CHAR_LIST, *ch )) && ( start >= 1 )) { g_free( ch ); start--; ch = gtk_editable_get_chars( editable, start, start+1 ); } g_free( ch ); if ( start >= 1 ) { start++; } } ch = gtk_editable_get_chars( editable, end, end+1 ); if ( ch ) { while(( strchr( URL_CHAR_LIST, *ch )) && (( end +1 ) < gtk_text_get_length( text ))) { g_free( ch ); end++; ch = gtk_editable_get_chars( editable, end, end+1 ); } g_free( ch ); } end--; if ( start != end ) { ch = gtk_editable_get_chars( editable, start, end+1 ); } else { ch = NULL; } return( ch ); } #include "gtktext_parts.h" #endif /* #ifdef USE_GTK2 */ gint chat_motion_notify(GtkWidget * widget, GdkEventMotion * event) { GdkCursor *cursor = NULL; char *ch; regex_t url_regex; int x, y; #ifdef USE_GTK2 GdkWindow *win; gint buf_x, buf_y; #endif GdkModifierType state; int valid_link = 0; if (event->is_hint) { gdk_window_get_pointer(event->window, &x, &y, &state); } else { x = event->x; y = event->y; state = event->state; } /* don't do anything if there is a mouse button pressed */ if ((state & GDK_BUTTON1_MASK) || (state & GDK_BUTTON3_MASK)) { return( FALSE ); } #ifdef USE_GTK2 gtk_text_view_window_to_buffer_coords( GTK_TEXT_VIEW(widget), GTK_TEXT_WINDOW_TEXT, x, y, &buf_x, &buf_y ); ch = get_word_at_pos( GTK_TEXT_VIEW(widget), buf_x, buf_y ); #else find_mouse_cursor( GTK_TEXT(widget), x, y ); ch = get_word_at_cursor( GTK_TEXT(widget) ); #endif if (( ch ) && ( ch[0] )) { regcomp( &url_regex, URL_REGEX, REG_EXTENDED | REG_ICASE | REG_NOSUB ); if (( strstr( ch, "http://" )) || ( strstr( ch, "ftp://" )) || ( ! regexec( &url_regex, ch, 0, NULL, 0 ))) { valid_link = 1; } else if (( ! strcasecmp( ch, "gyach" )) || ( ! strcasecmp( ch, "curphoo" )) || ( ! strcasecmp( ch, "curfloo" )) || ( ! strcasecmp( ch, "mybitch" )) || ( ! strcasecmp( ch, "ncchat" )) || ( ! strcasecmp( ch, "qtchat" )) || ( ! strcasecmp( ch, "yahoonix" )) || ( ! strcasecmp( ch, "yahscrewed" )) || ( ! strcasecmp( ch, "ychat" ))) { valid_link = 1; } else { /* check if it's a username */ /* if ends in a : then strip it */ if (( ch[strlen(ch)-1] == ':' ) || ( ch[strlen(ch)-1] == ',' )) { ch[strlen(ch)-1] = '\0'; } /* check to see if highlighted text is actually a username */ if ( find_user_row( ch ) >= 0 ) { valid_link = 1; } } regfree( &url_regex ); g_free( ch ); } if ( valid_link ) { cursor = gdk_cursor_new(GDK_HAND2); #ifdef USE_GTK2 win = gtk_text_view_get_window( GTK_TEXT_VIEW(widget), GTK_TEXT_WINDOW_TEXT ); gdk_window_set_cursor( win, cursor ); #else gdk_window_set_cursor( GTK_TEXT(widget)->text_area, cursor ); #endif gdk_cursor_destroy(cursor); } else { #ifdef USE_GTK2 win = gtk_text_view_get_window( GTK_TEXT_VIEW(widget), GTK_TEXT_WINDOW_TEXT ); gdk_window_set_cursor( win, NULL ); #else gdk_window_set_cursor( GTK_TEXT(widget)->text_area, NULL ); #endif } return( TRUE ); } void gyach_comment( YMSG9_SESSION *session, char *text ) { char buf[2096]; if (( my_status ) && ( my_status != 12 )) { cmd_away( "0" ); } ymsg9_comment( session, text ); /* now print it to our window */ snprintf( buf, sizeof(buf), "%s%s%s%s%s: %s%s%s\n", YAHOO_STYLE_BOLDON, YAHOO_COLOR_BLUE, session->user, YAHOO_COLOR_BLACK, YAHOO_STYLE_BOLDOFF, YAHOO_COLOR_BLACK, text, YAHOO_COLOR_BLACK ); append_to_textbox( chat_window, NULL, buf ); auto_away_time = time(NULL) + ( auto_away * 60 ); } void gyach_emote( YMSG9_SESSION *session, char *text ) { char buf[2096]; ymsg9_emote( session, text ); snprintf( buf, sizeof(buf), "%s%s%s%s%s %s%s%s\n", YAHOO_STYLE_BOLDON, YAHOO_COLOR_BLUE, session->user, YAHOO_COLOR_BLACK, YAHOO_STYLE_BOLDOFF, YAHOO_COLOR_PURPLE, text, YAHOO_COLOR_BLACK ); append_to_textbox( chat_window, NULL, buf ); auto_away_time = time(NULL) + ( auto_away * 60 ); } void gyach_think( YMSG9_SESSION *session, char *text ) { char buf[2096]; if (( my_status ) && ( my_status != 12 )) { cmd_away( "0" ); } ymsg9_think( session, text ); snprintf( buf, sizeof(buf), "%s%s%s%s%s %s. o O ( %s )%s\n", YAHOO_STYLE_BOLDON, YAHOO_COLOR_BLUE, session->user, YAHOO_COLOR_BLACK, YAHOO_STYLE_BOLDOFF, YAHOO_COLOR_PURPLE, text, YAHOO_COLOR_BLACK ); append_to_textbox( chat_window, NULL, buf ); auto_away_time = time(NULL) + ( auto_away * 60 ); } void gyach_away( YMSG9_SESSION *session, char *text ) { char buf[2096]; char *mptr; ymsg9_away( session, text ); /* now tell people we're away */ mptr = strchr( text, ':' ); if ( ! mptr ) return; mptr++; if ( atoi( text ) == 10 ) { snprintf( buf, sizeof(buf), "is away ( %s ).", mptr ); } else { snprintf( buf, sizeof(buf), "is away ( %s ).", away_msgs[ atoi( text ) ] ); } gyach_emote( session, buf ); } void gyach_back( YMSG9_SESSION *session ) { ymsg9_back( session ); gyach_emote( session, "is back." ); }