From: <the...@us...> - 2006-08-15 08:22:33
|
Revision: 16766 Author: thekingant Date: 2006-08-15 01:22:29 -0700 (Tue, 15 Aug 2006) ViewCVS: http://svn.sourceforge.net/gaim/?rev=16766&view=rev Log Message: ----------- I'm hoping this will fix a lot of the remaining idle-away problems, like not correctly returning from idle-away. This change also causes the gtkstatusbox to show your idle-away status when you are idle-away. Please test everything and let me know if there are any problems. Modified Paths: -------------- trunk/src/connection.h trunk/src/gtkstatusbox.c trunk/src/idle.c trunk/src/savedstatuses.c trunk/src/savedstatuses.h Modified: trunk/src/connection.h =================================================================== --- trunk/src/connection.h 2006-08-15 07:23:13 UTC (rev 16765) +++ trunk/src/connection.h 2006-08-15 08:22:29 UTC (rev 16766) @@ -89,8 +89,6 @@ guint keepalive; /**< Keep-alive. */ - gboolean is_auto_away; /**< Whether or not it's auto-away. */ - gboolean wants_to_die; /**< Wants to Die state. This is set when the user chooses to log out, or when the protocol is Modified: trunk/src/gtkstatusbox.c =================================================================== --- trunk/src/gtkstatusbox.c 2006-08-15 07:23:13 UTC (rev 16765) +++ trunk/src/gtkstatusbox.c 2006-08-15 08:22:29 UTC (rev 16766) @@ -257,6 +257,7 @@ statusbox, GAIM_CALLBACK(account_status_changed_cb)); statusbox->status_changed_signal = 0; } + gaim_signals_disconnect_by_handle(statusbox); gaim_prefs_disconnect_by_handle(statusbox); G_OBJECT_CLASS(parent_class)->finalize(obj); @@ -829,8 +830,7 @@ } static void -current_status_pref_changed_cb(const char *name, GaimPrefType type, - gconstpointer val, gpointer data) +current_savedstatus_changed_cb(GaimSavedStatus *now, GaimSavedStatus *old, gpointer data) { GtkGaimStatusBox *status_box = data; @@ -1000,8 +1000,10 @@ gtk_gaim_status_box_regenerate(status_box); gtk_gaim_status_box_refresh(status_box); - gaim_prefs_connect_callback(status_box, "/core/savedstatus/current", - current_status_pref_changed_cb, status_box); + gaim_signal_connect(gaim_savedstatuses_get_handle(), "savedstatus-changed", + status_box, + GAIM_CALLBACK(current_savedstatus_changed_cb), + status_box); gaim_prefs_connect_callback(status_box, "/gaim/gtk/blist/show_buddy_icons", buddy_list_details_pref_changed_cb, status_box); gaim_prefs_connect_callback(status_box, "/gaim/gtk/conversations/spellcheck", @@ -1253,9 +1255,8 @@ * "Saved..." or a popular status then do nothing. * Popular statuses are * activated elsewhere, and we update the status_box - * accordingly by monitoring the preference - * "/core/savedstatus/current" and then calling - * status_menu_refresh_iter() + * accordingly by connecting to the savedstatus-changed + * signal and then calling status_menu_refresh_iter() */ if (type != GTK_GAIM_STATUS_BOX_TYPE_PRIMITIVE) return; Modified: trunk/src/idle.c =================================================================== --- trunk/src/idle.c 2006-08-15 07:23:13 UTC (rev 16765) +++ trunk/src/idle.c 2006-08-15 08:22:29 UTC (rev 16766) @@ -58,63 +58,6 @@ static time_t last_active_time = 0; static void -set_account_autoaway(GaimConnection *gc) -{ - GaimAccount *account; - GaimPresence *presence; - GaimStatus *status; - - if (gc->is_auto_away) - /* This account is already auto-away! */ - return; - - account = gaim_connection_get_account(gc); - presence = gaim_account_get_presence(account); - status = gaim_presence_get_active_status(presence); - - if (gaim_status_is_available(status)) - { - GaimSavedStatus *saved_status; - - gaim_debug_info("idle", "Making %s auto-away\n", - gaim_account_get_username(account)); - - saved_status = gaim_savedstatus_get_idleaway(); - gaim_savedstatus_activate_for_account(saved_status, account); - - gc->is_auto_away = GAIM_IDLE_AUTO_AWAY; - } else { - gc->is_auto_away = GAIM_IDLE_AWAY_BUT_NOT_AUTO_AWAY; - } -} - -static void -unset_account_autoaway(GaimConnection *gc) -{ - GaimAccount *account; - GaimSavedStatus *saved_status; - - account = gaim_connection_get_account(gc); - - if (!gc->is_auto_away) - /* This account is already not auto-away! */ - return; - - if (gc->is_auto_away == GAIM_IDLE_AWAY_BUT_NOT_AUTO_AWAY) { - gc->is_auto_away = GAIM_IDLE_NOT_AWAY; - } else { - gc->is_auto_away = GAIM_IDLE_NOT_AWAY; - - gaim_debug_info("idle", "%s returning from auto-away\n", - gaim_account_get_username(account)); - - /* Return our account to its previous status */ - saved_status = gaim_savedstatus_get_current(); - gaim_savedstatus_activate_for_account(saved_status, account); - } -} - -static void set_account_idle(GaimAccount *account, int time_idle) { GaimPresence *presence; @@ -203,13 +146,11 @@ if (auto_away && (time_idle > (60 * gaim_prefs_get_int("/core/away/mins_before_away")))) { - for (l = gaim_connections_get_all(); l != NULL; l = l->next) - set_account_autoaway(l->data); + gaim_savedstatus_set_idleaway(TRUE); } else if (time_idle < 60 * gaim_prefs_get_int("/core/away/mins_before_away")) { - for (l = gaim_connections_get_all(); l != NULL; l = l->next) - unset_account_autoaway(l->data); + gaim_savedstatus_set_idleaway(FALSE); } /* Idle reporting stuff */ @@ -252,7 +193,6 @@ account = gaim_connection_get_account(gc); set_account_unidle(account); - unset_account_autoaway(gc); } void Modified: trunk/src/savedstatuses.c =================================================================== --- trunk/src/savedstatuses.c 2006-08-15 07:23:13 UTC (rev 16765) +++ trunk/src/savedstatuses.c 2006-08-15 08:22:29 UTC (rev 16766) @@ -25,6 +25,7 @@ #include "internal.h" #include "debug.h" +#include "idle.h" #include "notify.h" #include "savedstatuses.h" #include "dbus-maybe.h" @@ -676,9 +677,9 @@ * If we just deleted our current status or our idleaway status, * then set the appropriate pref back to 0. */ - current = gaim_prefs_get_int("/core/savedstatus/current"); + current = gaim_prefs_get_int("/core/savedstatus/default"); if (current == creation_time) - gaim_prefs_set_int("/core/savedstatus/current", 0); + gaim_prefs_set_int("/core/savedstatus/default", 0); idleaway = gaim_prefs_get_int("/core/savedstatus/idleaway"); if (idleaway == creation_time) @@ -725,12 +726,21 @@ } GaimSavedStatus * -gaim_savedstatus_get_current() +gaim_savedstatus_get_current(void) { + if (gaim_savedstatus_is_idleaway()) + return gaim_savedstatus_get_idleaway(); + else + return gaim_savedstatus_get_default(); +} + +GaimSavedStatus * +gaim_savedstatus_get_default() +{ int creation_time; GaimSavedStatus *saved_status = NULL; - creation_time = gaim_prefs_get_int("/core/savedstatus/current"); + creation_time = gaim_prefs_get_int("/core/savedstatus/default"); if (creation_time != 0) saved_status = g_hash_table_lookup(creation_times, &creation_time); @@ -744,7 +754,7 @@ * using? In any case, add a default status. */ saved_status = gaim_savedstatus_new(NULL, GAIM_STATUS_AVAILABLE); - gaim_prefs_set_int("/core/savedstatus/current", + gaim_prefs_set_int("/core/savedstatus/default", gaim_savedstatus_get_creation_time(saved_status)); } @@ -780,6 +790,51 @@ return saved_status; } +gboolean +gaim_savedstatus_is_idleaway() +{ + return gaim_prefs_get_bool("/core/savedstatus/isidleaway"); +} + +void +gaim_savedstatus_set_idleaway(gboolean idleaway) +{ + GList *accounts, *node; + GaimSavedStatus *old, *saved_status; + + if (gaim_savedstatus_is_idleaway() == idleaway) + /* Don't need to do anything */ + return; + + /* Changing our status makes us un-idle */ + if (!idleaway) + gaim_idle_touch(); + + old = gaim_savedstatus_get_current(); + gaim_prefs_set_bool("/core/savedstatus/isidleaway", idleaway); + saved_status = gaim_savedstatus_get_current(); + + accounts = gaim_accounts_get_all_active(); + for (node = accounts; node != NULL; node = node->next) + { + GaimAccount *account; + GaimPresence *presence; + GaimStatus *status; + + account = node->data; + presence = gaim_account_get_presence(account); + status = gaim_presence_get_active_status(presence); + + if (!idleaway || gaim_status_is_available(status)) + gaim_savedstatus_activate_for_account(saved_status, account); + } + + g_list_free(accounts); + + gaim_signal_emit(gaim_savedstatuses_get_handle(), "savedstatus-changed", + saved_status, old); +} + GaimSavedStatus * gaim_savedstatus_get_startup() { @@ -1001,12 +1056,12 @@ g_list_free(accounts); - gaim_prefs_set_int("/core/savedstatus/current", + gaim_prefs_set_int("/core/savedstatus/default", gaim_savedstatus_get_creation_time(saved_status)); + gaim_savedstatus_set_idleaway(FALSE); gaim_signal_emit(gaim_savedstatuses_get_handle(), "savedstatus-changed", saved_status, old); - } void @@ -1070,10 +1125,11 @@ * saved status and return that to the user. */ gaim_prefs_add_none("/core/savedstatus"); - gaim_prefs_add_int("/core/savedstatus/current", 0); + gaim_prefs_add_int("/core/savedstatus/default", 0); gaim_prefs_add_int("/core/savedstatus/startup", 0); gaim_prefs_add_bool("/core/savedstatus/startup_current_status", TRUE); gaim_prefs_add_int("/core/savedstatus/idleaway", 0); + gaim_prefs_add_bool("/core/savedstatus/isidleaway", FALSE); load_statuses(); Modified: trunk/src/savedstatuses.h =================================================================== --- trunk/src/savedstatuses.h 2006-08-15 07:23:13 UTC (rev 16765) +++ trunk/src/savedstatuses.h 2006-08-15 08:22:29 UTC (rev 16766) @@ -171,7 +171,9 @@ GList *gaim_savedstatuses_get_popular(unsigned int how_many); /** - * Returns the currently selected saved status. + * Returns the currently selected saved status. If we are idle + * then this returns gaim_savedstatus_get_idleaway(). Otherwise + * it returns gaim_savedstatus_get_default(). * * @return A pointer to the in-use GaimSavedStatus. * This function never returns NULL. @@ -179,7 +181,16 @@ GaimSavedStatus *gaim_savedstatus_get_current(void); /** - * Returns the saved status that gets used when your + * Returns the default saved status that is used when our + * accounts are not idle-away. + * + * @return A pointer to the in-use GaimSavedStatus. + * This function never returns NULL. + */ +GaimSavedStatus *gaim_savedstatus_get_default(void); + +/** + * Returns the saved status that is used when your * accounts become idle-away. * * @return A pointer to the idle-away GaimSavedStatus. @@ -188,6 +199,23 @@ GaimSavedStatus *gaim_savedstatus_get_idleaway(void); /** + * Return TRUE if we are currently idle-away. Otherwise + * returns FALSE. + * + * @return TRUE if our accounts have been set to idle-away. + */ +gboolean gaim_savedstatus_is_idleaway(void); + +/** + * Set whether accounts in Gaim are idle-away or not. + * + * @param TRUE if accounts should be switched to use the + * idle-away saved status. FALSE if they should + * be switched to use the default status. + */ +void gaim_savedstatus_set_idleaway(gboolean idleaway); + +/** * Returns the status to be used when gaim is starting up * * @return A pointer to the startup GaimSavedStatus. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <sea...@us...> - 2006-08-15 23:25:40
|
Revision: 16775 Author: seanegan Date: 2006-08-15 16:25:29 -0700 (Tue, 15 Aug 2006) ViewCVS: http://svn.sourceforge.net/gaim/?rev=16775&view=rev Log Message: ----------- A global buddy icon selector in the statusbox. This is done totally in the GTK+ UI; the core still sees a buddy icon as belonging to an account. Per-account icons can override the global one in Modify Account. There are some caching issues to work out, still. Modified Paths: -------------- trunk/src/gtkaccount.c trunk/src/gtkblist.c trunk/src/gtkstatusbox.c trunk/src/gtkstatusbox.h trunk/src/gtkutils.c trunk/src/gtkutils.h Modified: trunk/src/gtkaccount.c =================================================================== --- trunk/src/gtkaccount.c 2006-08-15 20:23:58 UTC (rev 16774) +++ trunk/src/gtkaccount.c 2006-08-15 23:25:29 UTC (rev 16775) @@ -115,6 +115,7 @@ GtkWidget *user_frame; GtkWidget *new_mail_check; GtkWidget *icon_hbox; + GtkWidget *icon_check; GtkWidget *icon_entry; char *icon_path; GtkWidget *icon_filesel; @@ -153,8 +154,6 @@ static void add_account_to_liststore(GaimAccount *account, gpointer user_data); static void set_account(GtkListStore *store, GtkTreeIter *iter, GaimAccount *account); -static char* -convert_buddy_icon(GaimPlugin *plugin, const char *path); /************************************************************************** * Add/Modify Account dialog @@ -271,210 +270,24 @@ } } -#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ static void -icon_filesel_choose_cb(GtkWidget *widget, gint response, AccountPrefsDialog *dialog) +icon_filesel_choose_cb(const char *filename, AccountPrefsDialog *dialog) { - char *filename, *current_folder; - - if (response != GTK_RESPONSE_ACCEPT) { - if (response == GTK_RESPONSE_CANCEL) - gtk_widget_destroy(dialog->icon_filesel); - dialog->icon_filesel = NULL; - return; + if (filename) { + g_free(dialog->icon_path); + dialog->icon_path = gaim_gtk_convert_buddy_icon(dialog->plugin, filename); + set_dialog_icon(dialog); + gtk_widget_show(dialog->icon_entry); } - - filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog->icon_filesel)); - current_folder = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog->icon_filesel)); - if (current_folder != NULL) { - gaim_prefs_set_string("/gaim/gtk/filelocations/last_icon_folder", current_folder); - g_free(current_folder); - } - -#else /* FILECHOOSER */ -static void -icon_filesel_choose_cb(GtkWidget *w, AccountPrefsDialog *dialog) -{ - char *filename, *current_folder; - - filename = g_strdup(gtk_file_selection_get_filename( - GTK_FILE_SELECTION(dialog->icon_filesel))); - - /* If they typed in a directory, change there */ - if (gaim_gtk_check_if_dir(filename, - GTK_FILE_SELECTION(dialog->icon_filesel))) - { - g_free(filename); - return; - } - - current_folder = g_path_get_dirname(filename); - if (current_folder != NULL) { - gaim_prefs_set_string("/gaim/gtk/filelocations/last_icon_folder", current_folder); - g_free(current_folder); - } - -#endif /* FILECHOOSER */ - - g_free(dialog->icon_path); - dialog->icon_path = convert_buddy_icon(dialog->plugin, filename); - set_dialog_icon(dialog); - gtk_widget_show(dialog->icon_entry); - - gtk_widget_destroy(dialog->icon_filesel); + dialog->icon_filesel = NULL; - g_free(filename); - } - -static void -#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ -icon_preview_change_cb(GtkFileChooser *widget, AccountPrefsDialog *dialog) -#else /* FILECHOOSER */ -icon_preview_change_cb(GtkTreeSelection *sel, AccountPrefsDialog *dialog) -#endif /* FILECHOOSER */ -{ - GdkPixbuf *pixbuf, *scale; - int height, width; - char *basename, *markup, *size; - struct stat st; - char *filename; - -#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ - filename = gtk_file_chooser_get_preview_filename( - GTK_FILE_CHOOSER(dialog->icon_filesel)); -#else /* FILECHOOSER */ - filename = g_strdup(gtk_file_selection_get_filename( - GTK_FILE_SELECTION(dialog->icon_filesel))); -#endif /* FILECHOOSER */ - - if (!filename || g_stat(filename, &st)) - { - g_free(filename); - return; - } - - pixbuf = gdk_pixbuf_new_from_file(filename, NULL); - if (!pixbuf) { - gtk_image_set_from_pixbuf(GTK_IMAGE(dialog->icon_preview), NULL); - gtk_label_set_markup(GTK_LABEL(dialog->icon_text), ""); -#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ - gtk_file_chooser_set_preview_widget_active( - GTK_FILE_CHOOSER(dialog->icon_filesel), FALSE); -#endif /* FILECHOOSER */ - g_free(filename); - return; - } - - width = gdk_pixbuf_get_width(pixbuf); - height = gdk_pixbuf_get_height(pixbuf); - basename = g_path_get_basename(filename); - size = gaim_str_size_to_units(st.st_size); - markup = g_strdup_printf(_("<b>File:</b> %s\n" - "<b>File size:</b> %s\n" - "<b>Image size:</b> %dx%d"), - basename, size, width, height); - - scale = gdk_pixbuf_scale_simple(pixbuf, width * 50 / height, - 50, GDK_INTERP_BILINEAR); - gtk_image_set_from_pixbuf(GTK_IMAGE(dialog->icon_preview), scale); -#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ - gtk_file_chooser_set_preview_widget_active( - GTK_FILE_CHOOSER(dialog->icon_filesel), TRUE); -#endif /* FILECHOOSER */ - gtk_label_set_markup(GTK_LABEL(dialog->icon_text), markup); - - g_object_unref(G_OBJECT(pixbuf)); - g_object_unref(G_OBJECT(scale)); - g_free(filename); - g_free(basename); - g_free(size); - g_free(markup); } -#if !GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ static void -icon_filesel_delete_cb(GtkWidget *w, AccountPrefsDialog *dialog) -{ - if (dialog->icon_filesel != NULL) - gtk_widget_destroy(dialog->icon_filesel); - - dialog->icon_filesel = NULL; -} -#endif /* FILECHOOSER */ - -static void icon_select_cb(GtkWidget *button, AccountPrefsDialog *dialog) { -#if !GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ - GtkWidget *hbox; - GtkWidget *tv; - GtkTreeSelection *sel; -#endif /* FILECHOOSER */ - const char *current_folder; - - if (dialog->icon_filesel != NULL) { - gtk_window_present(GTK_WINDOW(dialog->icon_filesel)); - return; - } - - current_folder = gaim_prefs_get_string("/gaim/gtk/filelocations/last_icon_folder"); -#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ - dialog->icon_filesel = gtk_file_chooser_dialog_new(_("Buddy Icon"), - GTK_WINDOW(dialog->window), - GTK_FILE_CHOOSER_ACTION_OPEN, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, - NULL); - gtk_dialog_set_default_response(GTK_DIALOG(dialog->icon_filesel), GTK_RESPONSE_ACCEPT); - if ((current_folder != NULL) && (*current_folder != '\0')) - gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog->icon_filesel), - current_folder); - - dialog->icon_preview = gtk_image_new(); - dialog->icon_text = gtk_label_new(NULL); - gtk_widget_set_size_request(GTK_WIDGET(dialog->icon_preview), -1, 50); - gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(dialog->icon_filesel), - GTK_WIDGET(dialog->icon_preview)); - g_signal_connect(G_OBJECT(dialog->icon_filesel), "update-preview", - G_CALLBACK(icon_preview_change_cb), dialog); - g_signal_connect(G_OBJECT(dialog->icon_filesel), "response", - G_CALLBACK(icon_filesel_choose_cb), dialog); - icon_preview_change_cb(NULL, dialog); -#else /* FILECHOOSER */ - dialog->icon_filesel = gtk_file_selection_new(_("Buddy Icon")); - dialog->icon_preview = gtk_image_new(); - dialog->icon_text = gtk_label_new(NULL); - if ((current_folder != NULL) && (*current_folder != '\0')) - gtk_file_selection_set_filename(GTK_FILE_SELECTION(dialog->icon_filesel), - current_folder); - - gtk_widget_set_size_request(GTK_WIDGET(dialog->icon_preview), -1, 50); - hbox = gtk_hbox_new(FALSE, GAIM_HIG_BOX_SPACE); - gtk_box_pack_start( - GTK_BOX(GTK_FILE_SELECTION(dialog->icon_filesel)->main_vbox), - hbox, FALSE, FALSE, 0); - gtk_box_pack_end(GTK_BOX(hbox), dialog->icon_preview, - FALSE, FALSE, 0); - gtk_box_pack_end(GTK_BOX(hbox), dialog->icon_text, FALSE, FALSE, 0); - - tv = GTK_FILE_SELECTION(dialog->icon_filesel)->file_list; - sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv)); - - g_signal_connect(G_OBJECT(sel), "changed", - G_CALLBACK(icon_preview_change_cb), dialog); - g_signal_connect( - G_OBJECT(GTK_FILE_SELECTION(dialog->icon_filesel)->ok_button), - "clicked", - G_CALLBACK(icon_filesel_choose_cb), dialog); - g_signal_connect( - G_OBJECT(GTK_FILE_SELECTION(dialog->icon_filesel)->cancel_button), - "clicked", - G_CALLBACK(icon_filesel_delete_cb), dialog); - g_signal_connect(G_OBJECT(dialog->icon_filesel), "destroy", - G_CALLBACK(icon_filesel_delete_cb), dialog); -#endif /* FILECHOOSER */ - - gtk_widget_show_all(GTK_WIDGET(dialog->icon_filesel)); + dialog->icon_filesel = gaim_gtk_buddy_icon_chooser_new(dialog->window, icon_filesel_choose_cb, dialog); + gtk_widget_show_all(dialog->icon_filesel); } static void @@ -510,7 +323,8 @@ if ((rtmp = strchr(tmp, '\r')) || (rtmp = strchr(tmp, '\n'))) *rtmp = '\0'; g_free(dialog->icon_path); - dialog->icon_path = convert_buddy_icon(dialog->plugin, tmp); + + dialog->icon_path = gaim_gtk_convert_buddy_icon(dialog->plugin, tmp); set_dialog_icon(dialog); gtk_widget_show(dialog->icon_entry); g_free(tmp); @@ -520,207 +334,6 @@ gtk_drag_finish(dc, FALSE, FALSE, t); } - -#if GTK_CHECK_VERSION(2,2,0) -static gboolean -str_array_match(char **a, char **b) -{ - int i, j; - - if (!a || !b) - return FALSE; - for (i = 0; a[i] != NULL; i++) - for (j = 0; b[j] != NULL; j++) - if (!g_ascii_strcasecmp(a[i], b[j])) - return TRUE; - return FALSE; -} -#endif - -static char* -convert_buddy_icon(GaimPlugin *plugin, const char *path) -{ -#if GTK_CHECK_VERSION(2,2,0) - int width, height; - char **pixbuf_formats = NULL; - GdkPixbufFormat *format; - GdkPixbuf *pixbuf; - GaimPluginProtocolInfo *prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin); - char **prpl_formats = g_strsplit (prpl_info->icon_spec.format,",",0); -#if !GTK_CHECK_VERSION(2,4,0) - GdkPixbufLoader *loader; - FILE *file; - struct stat st; - void *data = NULL; -#endif -#endif - const char *dirname = gaim_buddy_icons_get_cache_dir(); - char *random = g_strdup_printf("%x", g_random_int()); - char *filename = g_build_filename(dirname, random, NULL); - - if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) { - gaim_debug_info("buddyicon", "Creating icon cache directory.\n"); - - if (g_mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR) < 0) { - gaim_debug_error("buddyicon", - "Unable to create directory %s: %s\n", - dirname, strerror(errno)); -#if GTK_CHECK_VERSION(2,2,0) - g_strfreev(prpl_formats); -#endif - g_free(random); - g_free(filename); - return NULL; - } - } - -#if GTK_CHECK_VERSION(2,2,0) -#if GTK_CHECK_VERSION(2,4,0) - format = gdk_pixbuf_get_file_info (path, &width, &height); -#else - loader = gdk_pixbuf_loader_new(); - if (!g_stat(path, &st) && (file = g_fopen(path, "rb")) != NULL) { - data = g_malloc(st.st_size); - fread(data, 1, st.st_size, file); - fclose(file); - gdk_pixbuf_loader_write(loader, data, st.st_size, NULL); - g_free(data); - } - gdk_pixbuf_loader_close(loader, NULL); - pixbuf = gdk_pixbuf_loader_get_pixbuf(loader); - width = gdk_pixbuf_get_width(pixbuf); - height = gdk_pixbuf_get_height(pixbuf); - format = gdk_pixbuf_loader_get_format(loader); - g_object_unref(G_OBJECT(loader)); -#endif - pixbuf_formats = gdk_pixbuf_format_get_extensions(format); - - if (str_array_match(pixbuf_formats, prpl_formats) && /* This is an acceptable format AND */ - (!(prpl_info->icon_spec.scale_rules & GAIM_ICON_SCALE_SEND) || /* The prpl doesn't scale before it sends OR */ - (prpl_info->icon_spec.min_width <= width && - prpl_info->icon_spec.max_width >= width && - prpl_info->icon_spec.min_height <= height && - prpl_info->icon_spec.max_height >= height))) /* The icon is the correct size */ -#endif - { - gchar *contents; - gsize length; - FILE *image; - -#if GTK_CHECK_VERSION(2,2,0) - g_strfreev(prpl_formats); - g_strfreev(pixbuf_formats); -#endif - - /* Copy the image to the cache folder as "filename". */ - - if (!g_file_get_contents(path, &contents, &length, NULL) || - (image = g_fopen(filename, "wb")) == NULL) - { - g_free(random); - g_free(filename); -#if GTK_CHECK_VERSION(2,2,0) && !GTK_CHECK_VERSION(2,4,0) - g_object_unref(G_OBJECT(pixbuf)); -#endif - return NULL; - } - - if (fwrite(contents, 1, length, image) != length) - { - fclose(image); - g_unlink(filename); - - g_free(random); - g_free(filename); -#if GTK_CHECK_VERSION(2,2,0) && !GTK_CHECK_VERSION(2,4,0) - g_object_unref(G_OBJECT(pixbuf)); -#endif - return NULL; - } - fclose(image); - -#if GTK_CHECK_VERSION(2,2,0) && !GTK_CHECK_VERSION(2,4,0) - g_object_unref(G_OBJECT(pixbuf)); -#endif - - g_free(filename); - return random; - } -#if GTK_CHECK_VERSION(2,2,0) - else - { - int i; - GError *error = NULL; - GdkPixbuf *scale; - pixbuf = gdk_pixbuf_new_from_file(path, &error); - g_strfreev(pixbuf_formats); - if (!error && (prpl_info->icon_spec.scale_rules & GAIM_ICON_SCALE_SEND) && - (width < prpl_info->icon_spec.min_width || - width > prpl_info->icon_spec.max_width || - height < prpl_info->icon_spec.min_height || - height > prpl_info->icon_spec.max_height)) - { - int new_width = width; - int new_height = height; - - if(new_width > prpl_info->icon_spec.max_width) - new_width = prpl_info->icon_spec.max_width; - else if(new_width < prpl_info->icon_spec.min_width) - new_width = prpl_info->icon_spec.min_width; - if(new_height > prpl_info->icon_spec.max_height) - new_height = prpl_info->icon_spec.max_height; - else if(new_height < prpl_info->icon_spec.min_height) - new_height = prpl_info->icon_spec.min_height; - - /* preserve aspect ratio */ - if ((double)height * (double)new_width > - (double)width * (double)new_height) { - new_width = 0.5 + (double)width * (double)new_height / (double)height; - } else { - new_height = 0.5 + (double)height * (double)new_width / (double)width; - } - - scale = gdk_pixbuf_scale_simple (pixbuf, new_width, new_height, - GDK_INTERP_HYPER); - g_object_unref(G_OBJECT(pixbuf)); - pixbuf = scale; - } - if (error) { - g_free(random); - g_free(filename); - gaim_debug_error("buddyicon", "Could not open icon for conversion: %s\n", error->message); - g_error_free(error); - g_strfreev(prpl_formats); - return NULL; - } - - for (i = 0; prpl_formats[i]; i++) { - gaim_debug_info("buddyicon", "Converting buddy icon to %s as %s\n", prpl_formats[i], filename); - /* The gdk-pixbuf documentation is wrong. gdk_pixbuf_save returns TRUE if it was successful, - * FALSE if an error was set. */ - if (gdk_pixbuf_save (pixbuf, filename, prpl_formats[i], &error, NULL) == TRUE) - break; - gaim_debug_warning("buddyicon", "Could not convert to %s: %s\n", prpl_formats[i], error->message); - g_error_free(error); - error = NULL; - } - g_strfreev(prpl_formats); - if (!error) { - g_object_unref(G_OBJECT(pixbuf)); - g_free(filename); - return random; - } else { - gaim_debug_error("buddyicon", "Could not convert icon to usable format: %s\n", error->message); - g_error_free(error); - } - g_free(random); - g_free(filename); - g_object_unref(G_OBJECT(pixbuf)); - } - return NULL; -#endif -} - static void update_editable(GaimConnection *gc, AccountPrefsDialog *dialog) { @@ -898,6 +511,12 @@ } static void +icon_check_cb(GtkWidget *checkbox, AccountPrefsDialog *dialog) +{ + gtk_widget_set_sensitive(dialog->icon_hbox, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->icon_check))); +} + +static void add_user_options(AccountPrefsDialog *dialog, GtkWidget *parent) { GtkWidget *frame; @@ -930,12 +549,17 @@ gtk_widget_show(dialog->new_mail_check); /* Buddy icon */ + dialog->icon_check = gtk_check_button_new_with_label(_("Use this buddy icon for this account:")); + g_signal_connect(G_OBJECT(dialog->icon_check), "toggled", G_CALLBACK(icon_check_cb), dialog); + gtk_widget_show(dialog->icon_check); + gtk_box_pack_start(GTK_BOX(vbox), dialog->icon_check, FALSE, FALSE, 0); + dialog->icon_hbox = hbox = gtk_hbox_new(FALSE, GAIM_HIG_BOX_SPACE); + gtk_widget_set_sensitive(hbox, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->icon_check))); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); gtk_widget_show(hbox); - label = gtk_label_new(_("Buddy icon:")); - gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0); + label = gtk_label_new(" "); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); gtk_widget_show(label); @@ -976,12 +600,14 @@ if (dialog->account != NULL) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->new_mail_check), - gaim_account_get_check_mail(dialog->account)); + gaim_account_get_check_mail(dialog->account)); + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->icon_check), + !gaim_account_get_ui_bool(dialog->account, GAIM_GTK_UI, "use-global-buddyicon", + TRUE)); - if (gaim_account_get_buddy_icon(dialog->account) != NULL) { - dialog->icon_path = g_strdup(gaim_account_get_buddy_icon(dialog->account)); - set_dialog_icon(dialog); - } + dialog->icon_path = g_strdup(gaim_account_get_ui_string(dialog->account, GAIM_GTK_UI, "non-global-buddyicon", NULL)); + set_dialog_icon(dialog); } if (!dialog->prpl_info || @@ -1425,12 +1051,13 @@ if (dialog->icon_path != NULL) { - const char *icon = gaim_account_get_buddy_icon(dialog->account); + const char *icon = gaim_account_get_ui_string(dialog->account, GAIM_GTK_UI, "non-global-buddyicon", NULL); if (dialog->icon_path != NULL && (icon == NULL || strcmp(dialog->icon_path, icon))) { /* The user set an icon, which would've been cached by convert_buddy_icon, * but didn't save the changes. Delete the cache file. */ char *filename = g_build_filename(gaim_buddy_icons_get_cache_dir(), dialog->icon_path, NULL); + printf("Deleting\n"); g_unlink(filename); g_free(filename); } @@ -1460,7 +1087,7 @@ const char *value; char *username; char *tmp; - gboolean new = FALSE; + gboolean new = FALSE, icon_change = FALSE; GaimAccount *account; if (dialog->account == NULL) @@ -1488,7 +1115,20 @@ gaim_account_set_alias(account, NULL); /* Buddy Icon */ - gaim_account_set_buddy_icon(account, dialog->icon_path); + if (gaim_account_get_ui_bool(account, GAIM_GTK_UI, "use-global-buddyicon", TRUE) == + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->icon_check))) { + icon_change = TRUE; + } + gaim_account_set_ui_bool(account, GAIM_GTK_UI, "use-global-buddyicon", !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->icon_check))); + gaim_account_set_ui_string(account, GAIM_GTK_UI, "non-global-buddyicon", dialog->icon_path); + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->icon_check))) { + gaim_account_set_buddy_icon(account, dialog->icon_path); + } else if (gaim_prefs_get_string("/gaim/gtk/accounts/buddyicon") && icon_change) { + char *icon = gaim_gtk_convert_buddy_icon(dialog->plugin, gaim_prefs_get_string("/gaim/gtk/accounts/buddyicon")); + gaim_account_set_buddy_icon(account, icon); + g_free(icon); + } + /* Remember Password */ gaim_account_set_remember_password(account, @@ -2672,6 +2312,7 @@ gaim_prefs_add_none("/gaim/gtk/accounts/dialog"); gaim_prefs_add_int("/gaim/gtk/accounts/dialog/width", 520); gaim_prefs_add_int("/gaim/gtk/accounts/dialog/height", 321); + gaim_prefs_add_string("/gaim/gtk/accounts/buddyicon", NULL); gaim_signal_register(gaim_gtk_account_get_handle(), "account-modified", gaim_marshal_VOID__POINTER, NULL, 1, Modified: trunk/src/gtkblist.c =================================================================== --- trunk/src/gtkblist.c 2006-08-15 20:23:58 UTC (rev 16774) +++ trunk/src/gtkblist.c 2006-08-15 23:25:29 UTC (rev 16775) @@ -3814,12 +3814,11 @@ /* Add the statusbox */ gtkblist->statusbox = gtk_gaim_status_box_new(); + gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->statusbox, FALSE, TRUE, 0); gtk_widget_set_name(gtkblist->statusbox, "gaim_gtkblist_statusbox"); - + gtk_gaim_status_box_set_buddy_icon(gtkblist->statusbox, gaim_prefs_get_string("/gaim/gtk/accounts/buddyicon")); gtk_widget_show(gtkblist->statusbox); - gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->statusbox, FALSE, TRUE, 0); - - + /* set the Show Offline Buddies option. must be done * after the treeview or faceprint gets mad. -Robot101 */ Modified: trunk/src/gtkstatusbox.c =================================================================== --- trunk/src/gtkstatusbox.c 2006-08-15 20:23:58 UTC (rev 16774) +++ trunk/src/gtkstatusbox.c 2006-08-15 23:25:29 UTC (rev 16775) @@ -77,6 +77,8 @@ static gboolean gtk_gaim_status_box_expose_event (GtkWidget *widget, GdkEventExpose *event); static void gtk_gaim_status_box_forall (GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data); +static void do_colorshift (GdkPixbuf *dest, GdkPixbuf *src, int shift); + static void (*combo_box_size_request)(GtkWidget *widget, GtkRequisition *requisition); static void (*combo_box_size_allocate)(GtkWidget *widget, GtkAllocation *allocation); static void (*combo_box_forall) (GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data); @@ -260,6 +262,17 @@ gaim_signals_disconnect_by_handle(statusbox); gaim_prefs_disconnect_by_handle(statusbox); + gdk_cursor_unref(statusbox->hand_cursor); + gdk_cursor_unref(statusbox->arrow_cursor); + + g_object_unref(G_OBJECT(statusbox->buddy_icon)); + g_object_unref(G_OBJECT(statusbox->buddy_icon_hover)); + + if (statusbox->buddy_icon_sel) + gtk_widget_destroy(statusbox->buddy_icon_sel); + + g_free(statusbox->buddy_icon_path); + G_OBJECT_CLASS(parent_class)->finalize(obj); } @@ -907,6 +920,55 @@ } static void +icon_choose_cb(const char *filename, GtkGaimStatusBox *box) +{ + if (filename) { + GList *accounts; + for (accounts = gaim_accounts_get_all(); accounts != NULL; accounts = accounts->next) { + GaimAccount *account = accounts->data; + if (gaim_account_get_ui_bool(account, GAIM_GTK_UI, "use-global-buddy-icon", TRUE)) { + char *icon = gaim_gtk_convert_buddy_icon(gaim_find_prpl(gaim_account_get_protocol_id(account)), + filename); + gaim_account_set_buddy_icon(account, icon); + g_free(icon); + } + } + gtk_gaim_status_box_set_buddy_icon(box, filename); + } + + box->buddy_icon_sel = NULL; +} + +static gboolean +icon_box_press_cb(GtkWidget *widget, GdkEventButton *event, GtkGaimStatusBox *box) +{ + if (box->buddy_icon_sel) { + gtk_window_present(GTK_WINDOW(box->buddy_icon_sel)); + return FALSE; + } + + GtkWidget *filesel = gaim_gtk_buddy_icon_chooser_new(NULL, icon_choose_cb, box); + gtk_widget_show_all(filesel); + return FALSE; +} + +static gboolean +icon_box_enter_cb(GtkWidget *widget, GdkEventCrossing *event, GtkGaimStatusBox *box) +{ + gdk_window_set_cursor(widget->window, box->hand_cursor); + gtk_image_set_from_pixbuf(box->icon, box->buddy_icon_hover); + return FALSE; +} + +static gboolean +icon_box_leave_cb(GtkWidget *widget, GdkEventCrossing *event, GtkGaimStatusBox *box) +{ + gdk_window_set_cursor(widget->window, box->arrow_cursor); + gtk_image_set_from_pixbuf(box->icon, box->buddy_icon) ; + return FALSE; +} + +static void gtk_gaim_status_box_init (GtkGaimStatusBox *status_box) { GtkCellRenderer *text_rend; @@ -922,6 +984,18 @@ status_box->vsep = gtk_vseparator_new(); status_box->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE); + status_box->buddy_icon = gdk_pixbuf_new_from_file("/home/seanegan/p1120233.jpg", NULL); + status_box->icon = gtk_image_new_from_pixbuf(status_box->buddy_icon); + status_box->icon_box = gtk_event_box_new(); + status_box->hand_cursor = gdk_cursor_new (GDK_HAND2); + status_box->arrow_cursor = gdk_cursor_new (GDK_LEFT_PTR); + + g_signal_connect(G_OBJECT(status_box->icon_box), "enter-notify-event", G_CALLBACK(icon_box_enter_cb), status_box); + g_signal_connect(G_OBJECT(status_box->icon_box), "leave-notify-event", G_CALLBACK(icon_box_leave_cb), status_box); + g_signal_connect(G_OBJECT(status_box->icon_box), "button-press-event", G_CALLBACK(icon_box_press_cb), status_box); + + gtk_container_add(GTK_CONTAINER(status_box->icon_box), status_box->icon); + status_box->store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_INT, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER); status_box->dropdown_store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_INT, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER); gtk_combo_box_set_model(GTK_COMBO_BOX(status_box), GTK_TREE_MODEL(status_box->dropdown_store)); @@ -934,6 +1008,7 @@ gtk_box_pack_start(GTK_BOX(status_box->hbox), status_box->vsep, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(status_box->hbox), status_box->arrow, FALSE, FALSE, 0); gtk_widget_show_all(status_box->toggle_button); + gtk_widget_show_all(status_box->icon_box); #if GTK_CHECK_VERSION(2,4,0) gtk_button_set_focus_on_click(GTK_BUTTON(status_box->toggle_button), FALSE); #endif @@ -984,6 +1059,7 @@ #endif gtk_widget_set_parent(status_box->vbox, GTK_WIDGET(status_box)); gtk_widget_set_parent(status_box->toggle_button, GTK_WIDGET(status_box)); + gtk_widget_set_parent(status_box->icon_box, GTK_WIDGET(status_box)); GTK_BIN(status_box)->child = status_box->toggle_button; gtk_box_pack_start(GTK_BOX(status_box->vbox), status_box->sw, TRUE, TRUE, 0); @@ -1024,14 +1100,58 @@ requisition->height += box_req.height + 3; requisition->width = 1; + + } +/* From gnome-panel */ static void +do_colorshift (GdkPixbuf *dest, GdkPixbuf *src, int shift) +{ + gint i, j; + gint width, height, has_alpha, srcrowstride, destrowstride; + guchar *target_pixels; + guchar *original_pixels; + guchar *pixsrc; + guchar *pixdest; + int val; + guchar r,g,b; + + has_alpha = gdk_pixbuf_get_has_alpha (src); + width = gdk_pixbuf_get_width (src); + height = gdk_pixbuf_get_height (src); + srcrowstride = gdk_pixbuf_get_rowstride (src); + destrowstride = gdk_pixbuf_get_rowstride (dest); + target_pixels = gdk_pixbuf_get_pixels (dest); + original_pixels = gdk_pixbuf_get_pixels (src); + + for (i = 0; i < height; i++) { + pixdest = target_pixels + i*destrowstride; + pixsrc = original_pixels + i*srcrowstride; + for (j = 0; j < width; j++) { + r = *(pixsrc++); + g = *(pixsrc++); + b = *(pixsrc++); + val = r + shift; + *(pixdest++) = CLAMP(val, 0, 255); + val = g + shift; + *(pixdest++) = CLAMP(val, 0, 255); + val = b + shift; + *(pixdest++) = CLAMP(val, 0, 255); + if (has_alpha) + *(pixdest++) = *(pixsrc++); + } + } +} + +static void gtk_gaim_status_box_size_allocate(GtkWidget *widget, - GtkAllocation *allocation) + GtkAllocation *allocation) { + GtkGaimStatusBox *status_box = GTK_GAIM_STATUS_BOX(widget); GtkRequisition req = {0,0}; - GtkAllocation parent_alc, box_alc; + GtkAllocation parent_alc, box_alc, icon_alc; + GdkPixbuf *scaled; combo_box_size_request(widget, &req); @@ -1043,7 +1163,28 @@ parent_alc = *allocation; parent_alc.height = MAX(1,req.height); parent_alc.y += 3; + parent_alc.width -= (parent_alc.height + 3); combo_box_size_allocate(widget, &parent_alc); + + icon_alc = *allocation; + icon_alc.height = MAX(1,req.height); + icon_alc.width = icon_alc.height; + icon_alc.x = allocation->width - icon_alc.width; + icon_alc.y += 3; + + if (status_box->icon_size != icon_alc.height) { + scaled = gdk_pixbuf_new_from_file_at_scale(status_box->buddy_icon_path, + icon_alc.height, icon_alc.width, FALSE, NULL); + status_box->buddy_icon_hover = gdk_pixbuf_copy(scaled); + do_colorshift(status_box->buddy_icon_hover, status_box->buddy_icon_hover, 30); + g_object_unref(status_box->buddy_icon); + status_box->buddy_icon = scaled; + gtk_image_set_from_pixbuf(status_box->icon, status_box->buddy_icon); + status_box->icon_size = icon_alc.height; + } + gtk_widget_size_allocate((GTK_GAIM_STATUS_BOX(widget))->icon_box, &icon_alc); + + gtk_widget_size_allocate((GTK_GAIM_STATUS_BOX(widget))->toggle_button, &parent_alc); widget->allocation = *allocation; } @@ -1055,6 +1196,7 @@ GtkGaimStatusBox *status_box = GTK_GAIM_STATUS_BOX(widget); gtk_container_propagate_expose(GTK_CONTAINER(widget), status_box->vbox, event); gtk_container_propagate_expose(GTK_CONTAINER(widget), status_box->toggle_button, event); + gtk_container_propagate_expose(GTK_CONTAINER(widget), status_box->icon_box, event); return FALSE; } @@ -1071,6 +1213,7 @@ (* callback) (status_box->vbox, callback_data); (* callback) (status_box->toggle_button, callback_data); (* callback) (status_box->arrow, callback_data); + (* callback) (status_box->icon_box, callback_data); } combo_box_forall(container, include_internals, callback, callback_data); @@ -1179,6 +1322,30 @@ } void +gtk_gaim_status_box_set_buddy_icon(GtkGaimStatusBox *box, const char *filename) +{ + GdkPixbuf *scaled; + g_free(box->buddy_icon_path); + box->buddy_icon_path = g_strdup(filename); + + scaled = gdk_pixbuf_new_from_file_at_scale(filename, + box->icon_size, box->icon_size, FALSE, NULL); + box->buddy_icon_hover = gdk_pixbuf_copy(scaled); + do_colorshift(box->buddy_icon_hover, box->buddy_icon_hover, 30); + g_object_unref(box->buddy_icon); + box->buddy_icon = scaled; + gtk_image_set_from_pixbuf(box->icon, box->buddy_icon); + + gaim_prefs_set_string("/gaim/gtk/accounts/buddyicon", filename); +} + +const char* +gtk_gaim_status_box_get_buddy_icon(GtkGaimStatusBox *box) +{ + return box->buddy_icon_path; +} + +void gtk_gaim_status_box_pulse_connecting(GtkGaimStatusBox *status_box) { if (!status_box) Modified: trunk/src/gtkstatusbox.h =================================================================== --- trunk/src/gtkstatusbox.h 2006-08-15 20:23:58 UTC (rev 16774) +++ trunk/src/gtkstatusbox.h 2006-08-15 23:25:29 UTC (rev 16775) @@ -83,6 +83,17 @@ GtkWidget *vbox, *sw; GtkWidget *imhtml; + + char *buddy_icon_path; + GdkPixbuf *buddy_icon; + GdkPixbuf *buddy_icon_hover; + GtkWidget *buddy_icon_sel; + GtkWidget *icon; + GtkWidget *icon_box; + GdkCursor *hand_cursor; + GdkCursor *arrow_cursor; + int icon_size; + gboolean imhtml_visible; GtkWidget *cell_view; @@ -146,6 +157,12 @@ void gtk_gaim_status_box_pulse_connecting(GtkGaimStatusBox *status_box); +void +gtk_gaim_status_box_set_buddy_icon(GtkGaimStatusBox *status_box, const char *filename); + +const char * +gtk_gaim_status_box_get_buddy_icon(GtkGaimStatusBox *status_box); + char *gtk_gaim_status_box_get_message(GtkGaimStatusBox *status_box); G_END_DECLS Modified: trunk/src/gtkutils.c =================================================================== --- trunk/src/gtkutils.c 2006-08-15 20:23:58 UTC (rev 16774) +++ trunk/src/gtkutils.c 2006-08-15 23:25:29 UTC (rev 16775) @@ -2211,3 +2211,429 @@ gdk_window_set_cursor(widget->window, NULL); } +struct _icon_chooser { + GtkWidget *icon_filesel; + GtkWidget *icon_preview; + GtkWidget *icon_text; + + void (*callback)(const char*,gpointer); + gpointer data; +}; + +#if !GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ +static void +icon_filesel_delete_cb(GtkWidget *w, struct _icon_chooser *dialog) +{ + if (dialog->icon_filesel != NULL) + gtk_widget_destroy(dialog->icon_filesel); + + if (dialog->callback) + dialog->callback(NULL, data); + + g_free(dialog); +} +#endif /* FILECHOOSER */ + + + +#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ +static void +icon_filesel_choose_cb(GtkWidget *widget, gint response, struct _icon_chooser *dialog) +{ + char *filename, *current_folder; + + if (response != GTK_RESPONSE_ACCEPT) { + if (response == GTK_RESPONSE_CANCEL) { + gtk_widget_destroy(dialog->icon_filesel); + } + dialog->icon_filesel = NULL; + if (dialog->callback) + dialog->callback(NULL, dialog->data); + g_free(dialog); + return; + } + + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog->icon_filesel)); + current_folder = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog->icon_filesel)); + if (current_folder != NULL) { + gaim_prefs_set_string("/gaim/gtk/filelocations/last_icon_folder", current_folder); + g_free(current_folder); + } + +#else /* FILECHOOSER */ +static void +icon_filesel_choose_cb(GtkWidget *w, AccountPrefsDialog *dialog) +{ + char *filename, *current_folder; + + filename = g_strdup(gtk_file_selection_get_filename( + GTK_FILE_SELECTION(dialog->icon_filesel))); + + /* If they typed in a directory, change there */ + if (gaim_gtk_check_if_dir(filename, + GTK_FILE_SELECTION(dialog->icon_filesel))) + { + g_free(filename); + return; + } + + current_folder = g_path_get_dirname(filename); + if (current_folder != NULL) { + gaim_prefs_set_string("/gaim/gtk/filelocations/last_icon_folder", current_folder); + g_free(current_folder); + } + +#endif /* FILECHOOSER */ + if (dialog->callback) + dialog->callback(filename, dialog->data); + gtk_widget_destroy(dialog->icon_filesel); + g_free(filename); + g_free(dialog); + } + + +static void +#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ +icon_preview_change_cb(GtkFileChooser *widget, struct _icon_chooser *dialog) +#else /* FILECHOOSER */ +icon_preview_change_cb(GtkTreeSelection *sel, struct _icon_chooser *dialog) +#endif /* FILECHOOSER */ +{ + GdkPixbuf *pixbuf, *scale; + int height, width; + char *basename, *markup, *size; + struct stat st; + char *filename; + +#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ + filename = gtk_file_chooser_get_preview_filename( + GTK_FILE_CHOOSER(dialog->icon_filesel)); +#else /* FILECHOOSER */ + filename = g_strdup(gtk_file_selection_get_filename( + GTK_FILE_SELECTION(dialog->icon_filesel))); +#endif /* FILECHOOSER */ + + if (!filename || g_stat(filename, &st)) + { + g_free(filename); + return; + } + + pixbuf = gdk_pixbuf_new_from_file(filename, NULL); + if (!pixbuf) { + gtk_image_set_from_pixbuf(GTK_IMAGE(dialog->icon_preview), NULL); + gtk_label_set_markup(GTK_LABEL(dialog->icon_text), ""); +#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ + gtk_file_chooser_set_preview_widget_active( + GTK_FILE_CHOOSER(dialog->icon_filesel), FALSE); +#endif /* FILECHOOSER */ + g_free(filename); + return; + } + + width = gdk_pixbuf_get_width(pixbuf); + height = gdk_pixbuf_get_height(pixbuf); + basename = g_path_get_basename(filename); + size = gaim_str_size_to_units(st.st_size); + markup = g_strdup_printf(_("<b>File:</b> %s\n" + "<b>File size:</b> %s\n" + "<b>Image size:</b> %dx%d"), + basename, size, width, height); + + scale = gdk_pixbuf_scale_simple(pixbuf, width * 50 / height, + 50, GDK_INTERP_BILINEAR); + gtk_image_set_from_pixbuf(GTK_IMAGE(dialog->icon_preview), scale); +#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ + gtk_file_chooser_set_preview_widget_active( + GTK_FILE_CHOOSER(dialog->icon_filesel), TRUE); +#endif /* FILECHOOSER */ + gtk_label_set_markup(GTK_LABEL(dialog->icon_text), markup); + + g_object_unref(G_OBJECT(pixbuf)); + g_object_unref(G_OBJECT(scale)); + g_free(filename); + g_free(basename); + g_free(size); + g_free(markup); +} + + +GtkWidget *gaim_gtk_buddy_icon_chooser_new(GtkWindow *parent, void(*callback)(const char*,gpointer), gpointer data) { + struct _icon_chooser *dialog = g_new0(struct _icon_chooser, 1); + +#if !GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ + GtkWidget *hbox; + GtkWidget *tv; + GtkTreeSelection *sel; + +#endif /* FILECHOOSER */ + const char *current_folder; + + dialog->callback = callback; + dialog->data = data; + + if (dialog->icon_filesel != NULL) { + gtk_window_present(GTK_WINDOW(dialog->icon_filesel)); + return NULL; + } + + current_folder = gaim_prefs_get_string("/gaim/gtk/filelocations/last_icon_folder"); +#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ + + dialog->icon_filesel = gtk_file_chooser_dialog_new(_("Buddy Icon"), + parent, + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + NULL); + gtk_dialog_set_default_response(GTK_DIALOG(dialog->icon_filesel), GTK_RESPONSE_ACCEPT); + if ((current_folder != NULL) && (*current_folder != '\0')) + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog->icon_filesel), + current_folder); + + dialog->icon_preview = gtk_image_new(); + dialog->icon_text = gtk_label_new(NULL); + gtk_widget_set_size_request(GTK_WIDGET(dialog->icon_preview), -1, 50); + gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(dialog->icon_filesel), + GTK_WIDGET(dialog->icon_preview)); + g_signal_connect(G_OBJECT(dialog->icon_filesel), "update-preview", + G_CALLBACK(icon_preview_change_cb), dialog); + g_signal_connect(G_OBJECT(dialog->icon_filesel), "response", + G_CALLBACK(icon_filesel_choose_cb), dialog); + icon_preview_change_cb(NULL, dialog); +#else /* FILECHOOSER */ + dialog->icon_filesel = gtk_file_selection_new(_("Buddy Icon")); + dialog->icon_preview = gtk_image_new(); + dialog->icon_text = gtk_label_new(NULL); + if ((current_folder != NULL) && (*current_folder != '\0')) + gtk_file_selection_set_filename(GTK_FILE_SELECTION(dialog->icon_filesel), + current_folder); + + gtk_widget_set_size_request(GTK_WIDGET(dialog->icon_preview), -1, 50); + hbox = gtk_hbox_new(FALSE, GAIM_HIG_BOX_SPACE); + gtk_box_pack_start( + GTK_BOX(GTK_FILE_SELECTION(dialog->icon_filesel)->main_vbox), + hbox, FALSE, FALSE, 0); + gtk_box_pack_end(GTK_BOX(hbox), dialog->icon_preview, + FALSE, FALSE, 0); + gtk_box_pack_end(GTK_BOX(hbox), dialog->icon_text, FALSE, FALSE, 0); + + tv = GTK_FILE_SELECTION(dialog->icon_filesel)->file_list; + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv)); + + g_signal_connect(G_OBJECT(sel), "changed", + G_CALLBACK(icon_preview_change_cb), dialog); + g_signal_connect( + G_OBJECT(GTK_FILE_SELECTION(dialog->icon_filesel)->ok_button), + "clicked", + G_CALLBACK(icon_filesel_choose_cb), dialog); + g_signal_connect( + G_OBJECT(GTK_FILE_SELECTION(dialog->icon_filesel)->cancel_button), + "clicked", + G_CALLBACK(icon_filesel_delete_cb), dialog); + g_signal_connect(G_OBJECT(dialog->icon_filesel), "destroy", + G_CALLBACK(icon_filesel_delete_cb), dialog); +#endif /* FILECHOOSER */ + return dialog->icon_filesel; +} + + +#if GTK_CHECK_VERSION(2,2,0) +static gboolean +str_array_match(char **a, char **b) +{ + int i, j; + + if (!a || !b) + return FALSE; + for (i = 0; a[i] != NULL; i++) + for (j = 0; b[j] != NULL; j++) + if (!g_ascii_strcasecmp(a[i], b[j])) + return TRUE; + return FALSE; +} +#endif + +char* +gaim_gtk_convert_buddy_icon(GaimPlugin *plugin, const char *path) +{ +#if GTK_CHECK_VERSION(2,2,0) + int width, height; + char **pixbuf_formats = NULL; + GdkPixbufFormat *format; + GdkPixbuf *pixbuf; + GaimPluginProtocolInfo *prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin); + char **prpl_formats = g_strsplit (prpl_info->icon_spec.format,",",0); +#if !GTK_CHECK_VERSION(2,4,0) + GdkPixbufLoader *loader; + FILE *file; + struct stat st; + void *data = NULL; +#endif +#endif + const char *dirname = gaim_buddy_icons_get_cache_dir(); + char *random = g_strdup_printf("%x", g_random_int()); + char *filename = g_build_filename(dirname, random, NULL); + + if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) { + gaim_debug_info("buddyicon", "Creating icon cache directory.\n"); + + if (g_mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR) < 0) { + gaim_debug_error("buddyicon", + "Unable to create directory %s: %s\n", + dirname, strerror(errno)); +#if GTK_CHECK_VERSION(2,2,0) + g_strfreev(prpl_formats); +#endif + g_free(random); + g_free(filename); + return NULL; + } + } + +#if GTK_CHECK_VERSION(2,2,0) +#if GTK_CHECK_VERSION(2,4,0) + format = gdk_pixbuf_get_file_info (path, &width, &height); +#else + loader = gdk_pixbuf_loader_new(); + if (!g_stat(path, &st) && (file = g_fopen(path, "rb")) != NULL) { + data = g_malloc(st.st_size); + fread(data, 1, st.st_size, file); + fclose(file); + gdk_pixbuf_loader_write(loader, data, st.st_size, NULL); + g_free(data); + } + gdk_pixbuf_loader_close(loader, NULL); + pixbuf = gdk_pixbuf_loader_get_pixbuf(loader); + width = gdk_pixbuf_get_width(pixbuf); + height = gdk_pixbuf_get_height(pixbuf); + format = gdk_pixbuf_loader_get_format(loader); + g_object_unref(G_OBJECT(loader)); +#endif + pixbuf_formats = gdk_pixbuf_format_get_extensions(format); + + if (str_array_match(pixbuf_formats, prpl_formats) && /* This is an acceptable format AND */ + (!(prpl_info->icon_spec.scale_rules & GAIM_ICON_SCALE_SEND) || /* The prpl doesn't scale before it sends OR */ + (prpl_info->icon_spec.min_width <= width && + prpl_info->icon_spec.max_width >= width && + prpl_info->icon_spec.min_height <= height && + prpl_info->icon_spec.max_height >= height))) /* The icon is the correct size */ +#endif + { + gchar *contents; + gsize length; + FILE *image; + +#if GTK_CHECK_VERSION(2,2,0) + g_strfreev(prpl_formats); + g_strfreev(pixbuf_formats); +#endif + + /* Copy the image to the cache folder as "filename". */ + + if (!g_file_get_contents(path, &contents, &length, NULL) || + (image = g_fopen(filename, "wb")) == NULL) + { + g_free(random); + g_free(filename); +#if GTK_CHECK_VERSION(2,2,0) && !GTK_CHECK_VERSION(2,4,0) + g_object_unref(G_OBJECT(pixbuf)); +#endif + return NULL; + } + + if (fwrite(contents, 1, length, image) != length) + { + fclose(image); + g_unlink(filename); + + g_free(random); + g_free(filename); +#if GTK_CHECK_VERSION(2,2,0) && !GTK_CHECK_VERSION(2,4,0) + g_object_unref(G_OBJECT(pixbuf)); +#endif + return NULL; + } + fclose(image); + +#if GTK_CHECK_VERSION(2,2,0) && !GTK_CHECK_VERSION(2,4,0) + g_object_unref(G_OBJECT(pixbuf)); +#endif + + g_free(filename); + return random; + } +#if GTK_CHECK_VERSION(2,2,0) + else + { + int i; + GError *error = NULL; + GdkPixbuf *scale; + pixbuf = gdk_pixbuf_new_from_file(path, &error); + g_strfreev(pixbuf_formats); + if (!error && (prpl_info->icon_spec.scale_rules & GAIM_ICON_SCALE_SEND) && + (width < prpl_info->icon_spec.min_width || + width > prpl_info->icon_spec.max_width || + height < prpl_info->icon_spec.min_height || + height > prpl_info->icon_spec.max_height)) + { + int new_width = width; + int new_height = height; + + if(new_width > prpl_info->icon_spec.max_width) + new_width = prpl_info->icon_spec.max_width; + else if(new_width < prpl_info->icon_spec.min_width) + new_width = prpl_info->icon_spec.min_width; + if(new_height > prpl_info->icon_spec.max_height) + new_height = prpl_info->icon_spec.max_height; + else if(new_height < prpl_info->icon_spec.min_height) + new_height = prpl_info->icon_spec.min_height; + + /* preserve aspect ratio */ + if ((double)height * (double)new_width > + (double)width * (double)new_height) { + new_width = 0.5 + (double)width * (double)new_height / (double)height; + } else { + new_height = 0.5 + (double)height * (double)new_width / (double)width; + } + + scale = gdk_pixbuf_scale_simple (pixbuf, new_width, new_height, + GDK_INTERP_HYPER); + g_object_unref(G_OBJECT(pixbuf)); + pixbuf = scale; + } + if (error) { + g_free(random); + g_free(filename); + gaim_debug_error("buddyicon", "Could not open icon for conversion: %s\n", error->message); + g_error_free(error); + g_strfreev(prpl_formats); + return NULL; + } + + for (i = 0; prpl_formats[i]; i++) { + gaim_debug_info("buddyicon", "Converting buddy icon to %s as %s\n", prpl_formats[i], filename); + /* The gdk-pixbuf documentation is wrong. gdk_pixbuf_save returns TRUE if it was successful, + * FALSE if an error was set. */ + if (gdk_pixbuf_save (pixbuf, filename, prpl_formats[i], &error, NULL) == TRUE) + break; + gaim_debug_warning("buddyicon", "Could not convert to %s: %s\n", prpl_formats[i], error->message); + g_error_free(error); + error = NULL; + } + g_strfreev(prpl_formats); + if (!error) { + g_object_unref(G_OBJECT(pixbuf)); + g_free(filename); + return random; + } else { + gaim_debug_error("buddyicon", "Could not convert icon to usable format: %s\n", error->message); + g_error_free(error); + } + g_free(random); + g_free(filename); + g_object_unref(G_OBJECT(pixbuf)); + } + return NULL; +#endif +} Modified: trunk/src/gtkutils.h =================================================================== --- trunk/src/gtkutils.h 2006-08-15 20:23:58 UTC (rev 16774) +++ trunk/src/gtkutils.h 2006-08-15 23:25:29 UTC (rev 16775) @@ -460,4 +460,23 @@ */ void gaim_gtk_clear_cursor(GtkWidget *widget); +/** + * Creates a File Selection widget for choosing a buddy icon + * + * @param parent The parent window + * @param callback The callback to call when the window is closed. If the user chose an icon, the char* argument will point to its path + * @param data Data to pass to @callback + * @return The file dialog + */ +GtkWidget *gaim_gtk_buddy_icon_chooser_new(GtkWindow *parent, void(*callback)(const char*,gpointer), gpointer data); + +/** + * Converts a buddy icon to the required size and format + * + * @param plugin The prpl to conver the icon + * @param path The path of a buddy icon to convert + * @return The path of a new buddy icon + */ +char* gaim_gtk_convert_buddy_icon(GaimPlugin *plugin, const char *path); + #endif /* _GAIM_GTKUTILS_H_ */ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <sea...@us...> - 2006-08-16 01:32:18
|
Revision: 16776 Author: seanegan Date: 2006-08-15 18:32:12 -0700 (Tue, 15 Aug 2006) ViewCVS: http://svn.sourceforge.net/gaim/?rev=16776&view=rev Log Message: ----------- Fix segfault when using non-buddy-icon'ed accounts Modified Paths: -------------- trunk/src/gtkstatusbox.c trunk/src/gtkutils.c Modified: trunk/src/gtkstatusbox.c =================================================================== --- trunk/src/gtkstatusbox.c 2006-08-15 23:25:29 UTC (rev 16775) +++ trunk/src/gtkstatusbox.c 2006-08-16 01:32:12 UTC (rev 16776) @@ -926,7 +926,8 @@ GList *accounts; for (accounts = gaim_accounts_get_all(); accounts != NULL; accounts = accounts->next) { GaimAccount *account = accounts->data; - if (gaim_account_get_ui_bool(account, GAIM_GTK_UI, "use-global-buddy-icon", TRUE)) { + if (gaim_account_get_ui_bool(account, GAIM_GTK_UI, "use-global-buddy-icon", TRUE) && + GAIM_PLUGIN_PROTOCOL_INFO(gaim_find_prpl(gaim_account_get_protocol_id(account)))->icon_spec.format) { char *icon = gaim_gtk_convert_buddy_icon(gaim_find_prpl(gaim_account_get_protocol_id(account)), filename); gaim_account_set_buddy_icon(account, icon); Modified: trunk/src/gtkutils.c =================================================================== --- trunk/src/gtkutils.c 2006-08-15 23:25:29 UTC (rev 16775) +++ trunk/src/gtkutils.c 2006-08-16 01:32:12 UTC (rev 16776) @@ -2457,6 +2457,8 @@ char* gaim_gtk_convert_buddy_icon(GaimPlugin *plugin, const char *path) { + g_return_val_if_fail(GAIM_PLUGIN_PROTOCOL_INFO(plugin)->icon_spec.format != NULL, NULL); + #if GTK_CHECK_VERSION(2,2,0) int width, height; char **pixbuf_formats = NULL; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <aar...@us...> - 2006-08-16 04:50:49
|
Revision: 16780 Author: aaronsheldon Date: 2006-08-15 21:50:27 -0700 (Tue, 15 Aug 2006) ViewCVS: http://svn.sourceforge.net/gaim/?rev=16780&view=rev Log Message: ----------- Merges r16472-16568 from blist-efficiency into trunk. Modified Paths: -------------- trunk/src/blist.c trunk/src/gtkblist.c trunk/src/gtkblist.h trunk/src/gtkconv.c trunk/src/privacy.c Modified: trunk/src/blist.c =================================================================== --- trunk/src/blist.c 2006-08-16 03:18:43 UTC (rev 16779) +++ trunk/src/blist.c 2006-08-16 04:50:27 UTC (rev 16780) @@ -2636,6 +2636,11 @@ GAIM_SUBTYPE_STATUS), gaim_value_new(GAIM_TYPE_SUBTYPE, GAIM_SUBTYPE_STATUS)); + gaim_signal_register(handle, "buddy-privacy-changed", + gaim_marshal_VOID__POINTER, NULL, + 1, + gaim_value_new(GAIM_TYPE_SUBTYPE, + GAIM_SUBTYPE_BLIST_BUDDY)); gaim_signal_register(handle, "buddy-idle-changed", gaim_marshal_VOID__POINTER_INT_INT, NULL, Modified: trunk/src/gtkblist.c =================================================================== --- trunk/src/gtkblist.c 2006-08-16 03:18:43 UTC (rev 16779) +++ trunk/src/gtkblist.c 2006-08-16 04:50:27 UTC (rev 16780) @@ -104,6 +104,7 @@ static guint visibility_manager_count = 0; static gboolean gtk_blist_obscured = FALSE; +GHashTable* status_icon_hash_table = NULL; static GList *gaim_gtk_blist_sort_methods = NULL; static struct gaim_gtk_blist_sort_method *current_sort_method = NULL; @@ -119,14 +120,15 @@ static GaimGtkBuddyList *gtkblist = NULL; static gboolean gaim_gtk_blist_refresh_timer(GaimBuddyList *list); -static void gaim_gtk_blist_update_buddy(GaimBuddyList *list, GaimBlistNode *node); +static void gaim_gtk_blist_update_buddy_status_icon_key(GaimBlistNode *node, GaimStatusIconSize size); +static void gaim_gtk_blist_update_buddy(GaimBuddyList *list, GaimBlistNode *node, gboolean statusChange); static void gaim_gtk_blist_selection_changed(GtkTreeSelection *selection, gpointer data); static void gaim_gtk_blist_update(GaimBuddyList *list, GaimBlistNode *node); static void gaim_gtk_blist_update_contact(GaimBuddyList *list, GaimBlistNode *node); static char *gaim_get_tooltip_text(GaimBlistNode *node, gboolean full); static const char *item_factory_translate_func (const char *path, gpointer func_data); static gboolean get_iter_from_node(GaimBlistNode *node, GtkTreeIter *iter); -static void redo_buddy_list(GaimBuddyList *list, gboolean remove); +static void redo_buddy_list(GaimBuddyList *list, gboolean remove, gboolean rerender); static void gaim_gtk_blist_collapse_contact_cb(GtkWidget *w, GaimBlistNode *node); static void gaim_gtk_blist_tooltip_destroy(void); @@ -136,6 +138,7 @@ gboolean contact_expanded; gboolean recent_signonoff; gint recent_signonoff_timer; + GString *status_icon_key; }; @@ -535,6 +538,12 @@ } static void +gaim_gtk_blist_update_privacy_cb(GaimBuddy *buddy) +{ + gaim_gtk_blist_update_buddy(gaim_get_blist(), (GaimBlistNode*)(buddy), TRUE); +} + +static void rebuild_joinchat_entries(GaimGtkJoinChatData *data) { GaimConnection *gc; @@ -2692,12 +2701,112 @@ int y; }; +static void g_string_destroy(GString *destroyable) +{ + g_string_free(destroyable, TRUE); + return; +} + +static void +gaim_gtk_blist_update_buddy_status_icon_key(GaimBlistNode *node, GaimStatusIconSize size) +{ + int i; + GaimAccount *account; + GaimPlugin *prpl; + GaimPluginProtocolInfo *prpl_info; + GString *key = g_string_sized_new(16); + GaimBuddy *buddy; + const char *protoname = NULL; + struct _gaim_gtk_blist_node *gtknode = node->ui_data; + struct _gaim_gtk_blist_node *gtkbuddynode = NULL; + struct _emblem_data emblems[4] = {{NULL, 15, 15}, {NULL, 0, 15}, + {NULL, 0, 0}, {NULL, 15, 0}}; + + buddy = (GaimBuddy*)node; + gtkbuddynode = node->ui_data; + + if (gtkbuddynode && gtkbuddynode->recent_signonoff) { + if (GAIM_BUDDY_IS_ONLINE(buddy)) + g_string_printf(key, "login"); + else + g_string_printf(key, "logout"); + } else { + GaimConversation *conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, + gaim_buddy_get_name(buddy), + gaim_buddy_get_account(buddy)); + + account = buddy->account; + prpl = gaim_find_prpl(gaim_account_get_protocol_id(account)); + + if(!prpl) + return; + + prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); + + if(prpl_info && prpl_info->list_icon) { + protoname = prpl_info->list_icon(account, buddy); + } + if(prpl_info && prpl_info->list_emblems) { + if(gtknode) + prpl_info->list_emblems(buddy, &emblems[0].filename, + &emblems[1].filename, &emblems[2].filename, + &emblems[3].filename); + } + + g_string_assign(key, protoname); + + if(conv != NULL) { + GaimGtkConversation *gtkconv = GAIM_GTK_CONVERSATION(conv); + if(gtkconv != NULL && gaim_gtkconv_is_hidden(gtkconv)) { + /* add pending emblem */ + if(size == GAIM_STATUS_ICON_SMALL) { + emblems[0].filename="pending"; + } + else { + emblems[3].filename=emblems[2].filename; + emblems[2].filename="pending"; + } + } + } + + if(size == GAIM_STATUS_ICON_SMALL) { + /* So that only the se icon will composite */ + emblems[1].filename = emblems[2].filename = emblems[3].filename = NULL; + } + + for(i=0; i<4; i++) { + if(emblems[i].filename) { + g_string_append_printf(key, "/%s", emblems[i].filename); + } + } + } + + if (!GAIM_BUDDY_IS_ONLINE(buddy)) { + key = g_string_append(key, "/off"); + } else if (gaim_presence_is_idle(gaim_buddy_get_presence(buddy))) { + key = g_string_append(key, "/idle"); + } + if (!gaim_privacy_check(buddy->account, gaim_buddy_get_name(buddy))) { + key = g_string_append(key, "/priv"); + } + + if (gtkbuddynode) { + if (gtkbuddynode->status_icon_key) + g_string_free(gtkbuddynode->status_icon_key, TRUE); + gtkbuddynode->status_icon_key = g_string_new(key->str); + } + + g_string_free(key, TRUE); + return; +} + GdkPixbuf * gaim_gtk_blist_get_status_icon(GaimBlistNode *node, GaimStatusIconSize size) { GdkPixbuf *scale, *status = NULL; int i, scalesize = 30; char *filename; + GString *key = g_string_sized_new(16); const char *protoname = NULL; struct _gaim_gtk_blist_node *gtknode = node->ui_data; struct _gaim_gtk_blist_node *gtkbuddynode = NULL; @@ -2721,6 +2830,28 @@ return NULL; } + if (!status_icon_hash_table) { + status_icon_hash_table = g_hash_table_new_full((GHashFunc)g_string_hash, + (GEqualFunc)g_string_equal, + (GDestroyNotify)g_string_destroy, + (GDestroyNotify)gdk_pixbuf_unref); + + } else if (buddy && gtkbuddynode->status_icon_key && gtkbuddynode->status_icon_key->str) { + key = g_string_new(gtkbuddynode->status_icon_key->str); + + /* Respect the size request given */ + if (size == GAIM_STATUS_ICON_SMALL) { + key = g_string_append(key, "/tiny"); + } + + scale = g_hash_table_lookup(status_icon_hash_table, key); + if (scale) { + gdk_pixbuf_ref(scale); + g_string_free(key, TRUE); + return scale; + } + } + if(buddy || chat) { GaimAccount *account; GaimPlugin *prpl; @@ -2748,24 +2879,93 @@ } } - if(buddy) { - GaimConversation *conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, +/* Begin Generating Lookup Key */ + if (buddy) { + gaim_gtk_blist_update_buddy_status_icon_key(node, size); + g_string_printf(key, "%s", strdup(gtkbuddynode->status_icon_key->str)); + } + /* There are only two options for chat or gaimdude - big or small */ + else if (chat) { + GaimAccount *account; + GaimPlugin *prpl; + GaimPluginProtocolInfo *prpl_info; + + account = chat->account; + + prpl = gaim_find_prpl(gaim_account_get_protocol_id(account)); + if(!prpl) + return NULL; + + prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); + + if(prpl_info && prpl_info->list_icon) { + protoname = prpl_info->list_icon(account, NULL); + } + g_string_append_printf(key, "%s-chat", protoname); + } + else + key = g_string_append(key, "gaimdude"); + + /* If the icon is small, we do not store this into the status_icon_key + * in the gtkbuddynode. This way we can respect the size value on cache + * lookup. Otherwise, different sized icons could not be stored easily. + */ + if (size == GAIM_STATUS_ICON_SMALL) { + key = g_string_append(key, "/tiny"); + } + +/* End Generating Lookup Key */ + +/* If we already know this icon, just return it */ + scale = g_hash_table_lookup(status_icon_hash_table, key); + if (scale) { + gdk_pixbuf_ref(scale); + g_string_free(key, TRUE); + return scale; + } + +/* Create a new composite icon */ + + if(buddy) { + GaimAccount *account; + GaimPlugin *prpl; + GaimPluginProtocolInfo *prpl_info; + GaimConversation *conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, gaim_buddy_get_name(buddy), gaim_buddy_get_account(buddy)); - if(conv != NULL) { - GaimGtkConversation *gtkconv = GAIM_GTK_CONVERSATION(conv); - if(gtkconv != NULL && gaim_gtkconv_is_hidden(gtkconv)) { - /* add pending emblem */ - if(size == GAIM_STATUS_ICON_SMALL) { - emblems[0].filename="pending"; + + account = buddy->account; + + prpl = gaim_find_prpl(gaim_account_get_protocol_id(account)); + if(!prpl) + return NULL; + + prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); + + if(prpl_info && prpl_info->list_icon) { + protoname = prpl_info->list_icon(account, buddy); + } + if(prpl_info && prpl_info->list_emblems) { + if(gtknode && !gtknode->recent_signonoff) + prpl_info->list_emblems(buddy, &emblems[0].filename, + &emblems[1].filename, &emblems[2].filename, + &emblems[3].filename); + } + + if(conv != NULL) { + GaimGtkConversation *gtkconv = GAIM_GTK_CONVERSATION(conv); + if(gtkconv != NULL && gaim_gtkconv_is_hidden(gtkconv)) { + /* add pending emblem */ + if(size == GAIM_STATUS_ICON_SMALL) { + emblems[0].filename="pending"; + } + else { + emblems[3].filename=emblems[2].filename; + emblems[2].filename="pending"; + } } - else { - emblems[3].filename=emblems[2].filename; - emblems[2].filename="pending"; - } } } - } if(size == GAIM_STATUS_ICON_SMALL) { scalesize = 15; @@ -2773,6 +2973,8 @@ emblems[1].filename = emblems[2].filename = emblems[3].filename = NULL; } + + if(buddy && GAIM_BUDDY_IS_ONLINE(buddy) && gtkbuddynode && gtkbuddynode->recent_signonoff) { filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "login.png", NULL); } else if(buddy && !GAIM_BUDDY_IS_ONLINE(buddy) && gtkbuddynode && gtkbuddynode->recent_signonoff) { @@ -2829,7 +3031,6 @@ if(buddy) { presence = gaim_buddy_get_presence(buddy); - if (!GAIM_BUDDY_IS_ONLINE(buddy)) gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.0, FALSE); else if (gaim_presence_is_idle(presence)) @@ -2859,6 +3060,10 @@ } } + /* Insert the new icon into the status icon hash table */ + g_hash_table_insert (status_icon_hash_table, key, scale); + gdk_pixbuf_ref(scale); + return scale; } @@ -3082,7 +3287,7 @@ return TRUE; } -static void gaim_gtk_blist_hide_node(GaimBuddyList *list, GaimBlistNode *node) +static void gaim_gtk_blist_hide_node(GaimBuddyList *list, GaimBlistNode *node, gboolean update) { struct _gaim_gtk_blist_node *gtknode = (struct _gaim_gtk_blist_node *)node->ui_data; GtkTreeIter iter; @@ -3092,11 +3297,10 @@ if(gtkblist->selected_node == node) gtkblist->selected_node = NULL; - if (get_iter_from_node(node, &iter)) { gtk_tree_store_remove(gtkblist->treemodel, &iter); - if(GAIM_BLIST_NODE_IS_CONTACT(node) || GAIM_BLIST_NODE_IS_BUDDY(node) - || GAIM_BLIST_NODE_IS_CHAT(node)) { + if(update && (GAIM_BLIST_NODE_IS_CONTACT(node) || + GAIM_BLIST_NODE_IS_BUDDY(node) || GAIM_BLIST_NODE_IS_CHAT(node))) { gaim_gtk_blist_update(list, node->parent); } } @@ -3228,7 +3432,7 @@ if(conv->account != NULL && conv->name != NULL) { GaimBuddy *buddy = gaim_find_buddy(conv->account, conv->name); if(buddy != NULL) - gaim_gtk_blist_update_buddy(NULL, (GaimBlistNode *)buddy); + gaim_gtk_blist_update_buddy(NULL, (GaimBlistNode *)buddy, TRUE); } if (gtkblist->menutrayicon) { @@ -3367,7 +3571,7 @@ gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); } - redo_buddy_list(gaim_get_blist(), TRUE); + redo_buddy_list(gaim_get_blist(), FALSE, FALSE); #if GTK_CHECK_VERSION(2,6,0) gtk_tree_view_columns_autosize(GTK_TREE_VIEW(gtkblist->treeview)); #endif @@ -3914,23 +4118,36 @@ gaim_signal_emit(handle, "gtkblist-created", list); } -static void redo_buddy_list(GaimBuddyList *list, gboolean remove) +static void redo_buddy_list(GaimBuddyList *list, gboolean remove, gboolean rerender) { GaimBlistNode *node = list->root; while (node) { + /* This is only needed when we're reverting to a non-GTK+ sorted + * status. We shouldn't need to remove otherwise. + */ if (remove && !GAIM_BLIST_NODE_IS_GROUP(node)) - gaim_gtk_blist_hide_node(list, node); + gaim_gtk_blist_hide_node(list, node, FALSE); - gaim_gtk_blist_update(list, node); + if (GAIM_BLIST_NODE_IS_BUDDY(node)) + gaim_gtk_blist_update_buddy(list, node, rerender); + else if (GAIM_BLIST_NODE_IS_CHAT(node)) + gaim_gtk_blist_update(list, node); node = gaim_blist_node_next(node, FALSE); } + + /* There is no hash table if there is nothing in the buddy list to update */ + if (status_icon_hash_table) { + g_hash_table_destroy(status_icon_hash_table); + status_icon_hash_table = NULL; + } + } void gaim_gtk_blist_refresh(GaimBuddyList *list) { - redo_buddy_list(list, FALSE); + redo_buddy_list(list, FALSE, TRUE); } void @@ -3979,7 +4196,7 @@ gaim_request_close_with_handle(node); - gaim_gtk_blist_hide_node(list, node); + gaim_gtk_blist_hide_node(list, node, TRUE); if(node->parent) gaim_gtk_blist_update(list, node->parent); @@ -4147,7 +4364,7 @@ -1); g_free(mark); } else { - gaim_gtk_blist_hide_node(list, gnode); + gaim_gtk_blist_hide_node(list, gnode, TRUE); } } @@ -4269,13 +4486,13 @@ buddy_node(buddy, &iter, cnode); } } else { - gaim_gtk_blist_hide_node(list, cnode); + gaim_gtk_blist_hide_node(list, cnode, TRUE); } } -static void gaim_gtk_blist_update_buddy(GaimBuddyList *list, GaimBlistNode *node) +static void gaim_gtk_blist_update_buddy(GaimBuddyList *list, GaimBlistNode *node, gboolean statusChange) { GaimBuddy *buddy; struct _gaim_gtk_blist_node *gtkparentnode; @@ -4287,6 +4504,10 @@ buddy = (GaimBuddy*)node; + if (statusChange) + gaim_gtk_blist_update_buddy_status_icon_key(node, (gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons") + ? GAIM_STATUS_ICON_LARGE : GAIM_STATUS_ICON_SMALL)); + /* First things first, update the contact */ gaim_gtk_blist_update_contact(list, node); @@ -4302,7 +4523,7 @@ buddy_node(buddy, &iter, node); } else { - gaim_gtk_blist_hide_node(list, node); + gaim_gtk_blist_hide_node(list, node, TRUE); } } @@ -4342,7 +4563,7 @@ if(status) g_object_unref(status); } else { - gaim_gtk_blist_hide_node(list, node); + gaim_gtk_blist_hide_node(list, node, TRUE); } } @@ -4362,7 +4583,7 @@ gaim_gtk_blist_update_contact(list, node); break; case GAIM_BLIST_BUDDY_NODE: - gaim_gtk_blist_update_buddy(list, node); + gaim_gtk_blist_update_buddy(list, node, TRUE); break; case GAIM_BLIST_CHAT_NODE: gaim_gtk_blist_update_chat(list, node); @@ -5214,6 +5435,7 @@ gaim_signal_connect(gaim_blist_get_handle(), "buddy-signed-on", gtk_blist_handle, GAIM_CALLBACK(buddy_signonoff_cb), NULL); gaim_signal_connect(gaim_blist_get_handle(), "buddy-signed-off", gtk_blist_handle, GAIM_CALLBACK(buddy_signonoff_cb), NULL); + gaim_signal_connect(gaim_blist_get_handle(), "buddy-privacy-changed", gtk_blist_handle, GAIM_CALLBACK(gaim_gtk_blist_update_privacy_cb), NULL); } void @@ -5271,8 +5493,11 @@ gaim_gtk_blist_sort_method_set("none"); return; } - redo_buddy_list(gaim_get_blist(), TRUE); - + if (!strcmp(id, "none")) { + redo_buddy_list(gaim_get_blist(), TRUE, FALSE); + } else { + redo_buddy_list(gaim_get_blist(), FALSE, FALSE); + } } /****************************************** @@ -5837,8 +6062,8 @@ if (gtk_check_menu_item_get_active(checkmenuitem)) { gaim_gtk_set_cursor(gtkblist->window, GDK_WATCH); - - gaim_gtk_blist_sort_method_set(id); + /* This is redundant. I think. */ + /* gaim_gtk_blist_sort_method_set(id); */ gaim_prefs_set_string("/gaim/gtk/blist/sort_type", id); gaim_gtk_clear_cursor(gtkblist->window); Modified: trunk/src/gtkblist.h =================================================================== --- trunk/src/gtkblist.h 2006-08-16 03:18:43 UTC (rev 16779) +++ trunk/src/gtkblist.h 2006-08-16 04:50:27 UTC (rev 16780) @@ -143,7 +143,7 @@ * Populates a menu with the items shown on the buddy list for a buddy. * * @param menu The menu to populate - * @param buddy The buddy who's menu to get + * @param buddy The buddy whose menu to get * @param sub TRUE if this is a sub-menu, FALSE otherwise */ void gaim_gtk_blist_make_buddy_menu(GtkWidget *menu, GaimBuddy *buddy, gboolean sub); Modified: trunk/src/gtkconv.c =================================================================== --- trunk/src/gtkconv.c 2006-08-16 03:18:43 UTC (rev 16779) +++ trunk/src/gtkconv.c 2006-08-16 04:50:27 UTC (rev 16780) @@ -2185,6 +2185,7 @@ GaimAccount *account = NULL; const char *name = NULL; GdkPixbuf *status = NULL; + GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); g_return_val_if_fail(conv != NULL, NULL); @@ -2198,6 +2199,12 @@ if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) { GaimBuddy *b = gaim_find_buddy(account, name); if (b != NULL) { + /* I hate this hack. It fixes a bug where the pending message icon + * displays in the conv tab even though it shouldn't. + * A better solution would be great. */ + if (ops && ops->update) + ops->update(NULL, (GaimBlistNode*)b); + status = gaim_gtk_blist_get_status_icon((GaimBlistNode*)b, (small_icon ? GAIM_STATUS_ICON_SMALL : GAIM_STATUS_ICON_LARGE)); } @@ -6341,6 +6348,20 @@ } static void +update_buddy_privacy_changed(GaimBuddy *buddy) +{ + GaimGtkConversation *gtkconv; + GaimConversation *conv; + + gtkconv = get_gtkconv_with_contact(gaim_buddy_get_contact(buddy)); + if (gtkconv) + { + conv = gtkconv->active_conv; + gaim_gtkconv_update_fields(conv, GAIM_GTKCONV_TAB_ICON); + } +} + +static void update_buddy_idle_changed(GaimBuddy *buddy, gboolean old, gboolean newidle) { GaimConversation *conv; @@ -6628,6 +6649,8 @@ handle, GAIM_CALLBACK(update_buddy_sign), "off"); gaim_signal_connect(blist_handle, "buddy-status-changed", handle, GAIM_CALLBACK(update_buddy_status_changed), NULL); + gaim_signal_connect(blist_handle, "buddy-privacy-changed", + handle, GAIM_CALLBACK(update_buddy_privacy_changed), NULL); gaim_signal_connect(blist_handle, "buddy-idle-changed", handle, GAIM_CALLBACK(update_buddy_idle_changed), NULL); gaim_signal_connect(blist_handle, "buddy-icon-changed", Modified: trunk/src/privacy.c =================================================================== --- trunk/src/privacy.c 2006-08-16 03:18:43 UTC (rev 16779) +++ trunk/src/privacy.c 2006-08-16 04:50:27 UTC (rev 16780) @@ -34,6 +34,7 @@ { GSList *l; char *name; + GaimBuddy *buddy; g_return_val_if_fail(account != NULL, FALSE); g_return_val_if_fail(who != NULL, FALSE); @@ -41,17 +42,18 @@ name = g_strdup(gaim_normalize(account, who)); for (l = account->permit; l != NULL; l = l->next) { - if (!gaim_utf8_strcasecmp(name, gaim_normalize(account, (char *)l->data))) + if (!gaim_utf8_strcasecmp(name, (char *)l->data)) break; } - g_free(name); + if (l != NULL) + { + g_free(name); + return FALSE; + } - if (l != NULL) - return FALSE; + account->permit = g_slist_append(account->permit, name); - account->permit = g_slist_append(account->permit, g_strdup(who)); - if (!local_only && gaim_account_is_connected(account)) serv_add_permit(gaim_account_get_connection(account), who); @@ -60,6 +62,12 @@ gaim_blist_schedule_save(); + /* This lets the UI know a buddy has had its privacy setting changed */ + buddy = gaim_find_buddy(account, name); + if (buddy != NULL) { + gaim_signal_emit(gaim_blist_get_handle(), + "buddy-privacy-changed", buddy); + } return TRUE; } @@ -69,19 +77,18 @@ { GSList *l; char *name; + GaimBuddy *buddy; g_return_val_if_fail(account != NULL, FALSE); g_return_val_if_fail(who != NULL, FALSE); - name = g_strdup(gaim_normalize(account, who)); + name = gaim_normalize(account, who); for (l = account->permit; l != NULL; l = l->next) { - if (!gaim_utf8_strcasecmp(name, gaim_normalize(account, (char *)l->data))) + if (!gaim_utf8_strcasecmp(name, (char *)l->data)) break; } - g_free(name); - if (l == NULL) return FALSE; @@ -96,6 +103,11 @@ gaim_blist_schedule_save(); + buddy = gaim_find_buddy(account, name); + if (buddy != NULL) { + gaim_signal_emit(gaim_blist_get_handle(), + "buddy-privacy-changed", buddy); + } return TRUE; } @@ -105,6 +117,7 @@ { GSList *l; char *name; + GaimBuddy *buddy; g_return_val_if_fail(account != NULL, FALSE); g_return_val_if_fail(who != NULL, FALSE); @@ -116,12 +129,13 @@ break; } - g_free(name); - if (l != NULL) + { + g_free(name); return FALSE; + } - account->deny = g_slist_append(account->deny, g_strdup(who)); + account->deny = g_slist_append(account->deny, name); if (!local_only && gaim_account_is_connected(account)) serv_add_deny(gaim_account_get_connection(account), who); @@ -131,6 +145,11 @@ gaim_blist_schedule_save(); + buddy = gaim_find_buddy(account, name); + if (buddy != NULL) { + gaim_signal_emit(gaim_blist_get_handle(), + "buddy-privacy-changed", buddy); + } return TRUE; } @@ -140,18 +159,19 @@ { GSList *l; char *name; + GaimBuddy *buddy; g_return_val_if_fail(account != NULL, FALSE); g_return_val_if_fail(who != NULL, FALSE); - name = g_strdup(gaim_normalize(account, who)); + name = gaim_normalize(account, who); for (l = account->deny; l != NULL; l = l->next) { - if (!gaim_utf8_strcasecmp(name, gaim_normalize(account, (char *)l->data))) + if (!gaim_utf8_strcasecmp(name, (char *)l->data)) break; } - g_free(name); + buddy = gaim_find_buddy(account, name); if (l == NULL) return FALSE; @@ -163,8 +183,13 @@ serv_rem_deny(gaim_account_get_connection(account), name); if (privacy_ops != NULL && privacy_ops->deny_removed != NULL) - privacy_ops->deny_removed(account, name); + privacy_ops->deny_removed(account, who); + if (buddy != NULL) { + gaim_signal_emit(gaim_blist_get_handle(), + "buddy-privacy-changed", buddy); + } + g_free(name); gaim_blist_schedule_save(); @@ -184,15 +209,17 @@ return FALSE; case GAIM_PRIVACY_ALLOW_USERS: + who = gaim_normalize(account, who); for (list=account->permit; list!=NULL; list=list->next) { - if (!gaim_utf8_strcasecmp(who, gaim_normalize(account, (char *)list->data))) + if (!gaim_utf8_strcasecmp(who, (char *)list->data)) return TRUE; } return FALSE; case GAIM_PRIVACY_DENY_USERS: + who = gaim_normalize(account, who); for (list=account->deny; list!=NULL; list=list->next) { - if (!gaim_utf8_strcasecmp(who, gaim_normalize( account, (char *)list->data ))) + if (!gaim_utf8_strcasecmp(who, (char *)list->data )) return FALSE; } return TRUE; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <the...@us...> - 2006-08-16 06:32:08
|
Revision: 16783 Author: thekingant Date: 2006-08-15 23:31:59 -0700 (Tue, 15 Aug 2006) ViewCVS: http://svn.sourceforge.net/gaim/?rev=16783&view=rev Log Message: ----------- Whitespace and warning fixes: gtkaccount.c: In function ?\226?\128?\152icon_select_cb?\226?\128?\153: gtkaccount.c:289: warning: passing argument 1 of ?\226?\128?\152gaim_gtk_buddy_icon_chooser_new?\226?\128?\153 from incompatible pointer type gtkaccount.c:289: warning: passing argument 2 of ?\226?\128?\152gaim_gtk_buddy_icon_chooser_new?\226?\128?\153 from incompatible pointer type gtkstatusbox.c: In function ?\226?\128?\152icon_box_press_cb?\226?\128?\153: gtkstatusbox.c:951: warning: passing argument 2 of ?\226?\128?\152gaim_gtk_buddy_icon_chooser_new?\226?\128?\153 from incompatible pointer type gtkstatusbox.c:951: warning: ISO C90 forbids mixed declarations and code gtkstatusbox.c: In function ?\226?\128?\152icon_box_enter_cb?\226?\128?\153: gtkstatusbox.c:960: warning: passing argument 1 of ?\226?\128?\152gtk_image_set_from_pixbuf?\226?\128?\153 from incompatible pointer type gtkstatusbox.c: In function ?\226?\128?\152icon_box_leave_cb?\226?\128?\153: gtkstatusbox.c:968: warning: passing argument 1 of ?\226?\128?\152gtk_image_set_from_pixbuf?\226?\128?\153 from incompatible pointer type gtkstatusbox.c: In function ?\226?\128?\152gtk_gaim_status_box_size_allocate?\226?\128?\153: gtkstatusbox.c:1183: warning: passing argument 1 of ?\226?\128?\152gtk_image_set_from_pixbuf?\226?\128?\153 from incompatible pointer type gtkstatusbox.c: In function ?\226?\128?\152gtk_gaim_status_box_set_buddy_icon?\226?\128?\153: gtkstatusbox.c:1338: warning: passing argument 1 of ?\226?\128?\152gtk_image_set_from_pixbuf?\226?\128?\153 from incompatible pointer type gtkutils.c: In function ?\226?\128?\152gaim_gtk_convert_buddy_icon?\226?\128?\153: gtkutils.c:2463: warning: ISO C90 forbids mixed declarations and code Modified Paths: -------------- trunk/src/gtkaccount.c trunk/src/gtkstatusbox.c trunk/src/gtkstatusbox.h trunk/src/gtkutils.c Modified: trunk/src/gtkaccount.c =================================================================== --- trunk/src/gtkaccount.c 2006-08-16 06:11:10 UTC (rev 16782) +++ trunk/src/gtkaccount.c 2006-08-16 06:31:59 UTC (rev 16783) @@ -271,22 +271,26 @@ } static void -icon_filesel_choose_cb(const char *filename, AccountPrefsDialog *dialog) +icon_filesel_choose_cb(const char *filename, gpointer data) { + AccountPrefsDialog *dialog; + + dialog = data; + if (filename) { g_free(dialog->icon_path); dialog->icon_path = gaim_gtk_convert_buddy_icon(dialog->plugin, filename); set_dialog_icon(dialog); gtk_widget_show(dialog->icon_entry); } - + dialog->icon_filesel = NULL; } static void icon_select_cb(GtkWidget *button, AccountPrefsDialog *dialog) { - dialog->icon_filesel = gaim_gtk_buddy_icon_chooser_new(dialog->window, icon_filesel_choose_cb, dialog); + dialog->icon_filesel = gaim_gtk_buddy_icon_chooser_new(GTK_WINDOW(dialog->window), icon_filesel_choose_cb, dialog); gtk_widget_show_all(dialog->icon_filesel); } Modified: trunk/src/gtkstatusbox.c =================================================================== --- trunk/src/gtkstatusbox.c 2006-08-16 06:11:10 UTC (rev 16782) +++ trunk/src/gtkstatusbox.c 2006-08-16 06:31:59 UTC (rev 16783) @@ -267,10 +267,10 @@ g_object_unref(G_OBJECT(statusbox->buddy_icon)); g_object_unref(G_OBJECT(statusbox->buddy_icon_hover)); - + if (statusbox->buddy_icon_sel) gtk_widget_destroy(statusbox->buddy_icon_sel); - + g_free(statusbox->buddy_icon_path); G_OBJECT_CLASS(parent_class)->finalize(obj); @@ -920,8 +920,12 @@ } static void -icon_choose_cb(const char *filename, GtkGaimStatusBox *box) +icon_choose_cb(const char *filename, gpointer data) { + GtkGaimStatusBox *box; + + box = data; + if (filename) { GList *accounts; for (accounts = gaim_accounts_get_all(); accounts != NULL; accounts = accounts->next) { @@ -936,19 +940,21 @@ } gtk_gaim_status_box_set_buddy_icon(box, filename); } - + box->buddy_icon_sel = NULL; } static gboolean icon_box_press_cb(GtkWidget *widget, GdkEventButton *event, GtkGaimStatusBox *box) { + GtkWidget *filesel; + if (box->buddy_icon_sel) { gtk_window_present(GTK_WINDOW(box->buddy_icon_sel)); return FALSE; } - GtkWidget *filesel = gaim_gtk_buddy_icon_chooser_new(NULL, icon_choose_cb, box); + filesel = gaim_gtk_buddy_icon_chooser_new(NULL, icon_choose_cb, box); gtk_widget_show_all(filesel); return FALSE; } @@ -957,7 +963,7 @@ icon_box_enter_cb(GtkWidget *widget, GdkEventCrossing *event, GtkGaimStatusBox *box) { gdk_window_set_cursor(widget->window, box->hand_cursor); - gtk_image_set_from_pixbuf(box->icon, box->buddy_icon_hover); + gtk_image_set_from_pixbuf(GTK_IMAGE(box->icon), box->buddy_icon_hover); return FALSE; } @@ -965,7 +971,7 @@ icon_box_leave_cb(GtkWidget *widget, GdkEventCrossing *event, GtkGaimStatusBox *box) { gdk_window_set_cursor(widget->window, box->arrow_cursor); - gtk_image_set_from_pixbuf(box->icon, box->buddy_icon) ; + gtk_image_set_from_pixbuf(GTK_IMAGE(box->icon), box->buddy_icon) ; return FALSE; } @@ -1172,15 +1178,15 @@ icon_alc.width = icon_alc.height; icon_alc.x = allocation->width - icon_alc.width; icon_alc.y += 3; - + if (status_box->icon_size != icon_alc.height) { - scaled = gdk_pixbuf_new_from_file_at_scale(status_box->buddy_icon_path, + scaled = gdk_pixbuf_new_from_file_at_scale(status_box->buddy_icon_path, icon_alc.height, icon_alc.width, FALSE, NULL); status_box->buddy_icon_hover = gdk_pixbuf_copy(scaled); do_colorshift(status_box->buddy_icon_hover, status_box->buddy_icon_hover, 30); g_object_unref(status_box->buddy_icon); status_box->buddy_icon = scaled; - gtk_image_set_from_pixbuf(status_box->icon, status_box->buddy_icon); + gtk_image_set_from_pixbuf(GTK_IMAGE(status_box->icon), status_box->buddy_icon); status_box->icon_size = icon_alc.height; } gtk_widget_size_allocate((GTK_GAIM_STATUS_BOX(widget))->icon_box, &icon_alc); @@ -1329,14 +1335,14 @@ g_free(box->buddy_icon_path); box->buddy_icon_path = g_strdup(filename); - scaled = gdk_pixbuf_new_from_file_at_scale(filename, + scaled = gdk_pixbuf_new_from_file_at_scale(filename, box->icon_size, box->icon_size, FALSE, NULL); box->buddy_icon_hover = gdk_pixbuf_copy(scaled); do_colorshift(box->buddy_icon_hover, box->buddy_icon_hover, 30); g_object_unref(box->buddy_icon); box->buddy_icon = scaled; - gtk_image_set_from_pixbuf(box->icon, box->buddy_icon); - + gtk_image_set_from_pixbuf(GTK_IMAGE(box->icon), box->buddy_icon); + gaim_prefs_set_string("/gaim/gtk/accounts/buddyicon", filename); } Modified: trunk/src/gtkstatusbox.h =================================================================== --- trunk/src/gtkstatusbox.h 2006-08-16 06:11:10 UTC (rev 16782) +++ trunk/src/gtkstatusbox.h 2006-08-16 06:31:59 UTC (rev 16783) @@ -85,14 +85,14 @@ GtkWidget *imhtml; char *buddy_icon_path; - GdkPixbuf *buddy_icon; - GdkPixbuf *buddy_icon_hover; + GdkPixbuf *buddy_icon; + GdkPixbuf *buddy_icon_hover; GtkWidget *buddy_icon_sel; - GtkWidget *icon; + GtkWidget *icon; GtkWidget *icon_box; GdkCursor *hand_cursor; GdkCursor *arrow_cursor; - int icon_size; + int icon_size; gboolean imhtml_visible; @@ -128,16 +128,16 @@ struct _GtkGaimStatusBoxClass { - GtkComboBoxClass parent_class; + GtkComboBoxClass parent_class; - /* signals */ - void (* changed) (GtkComboBox *combo_box); + /* signals */ + void (* changed) (GtkComboBox *combo_box); - /* Padding for future expansion */ - void (*_gtk_reserved0) (void); - void (*_gtk_reserved1) (void); - void (*_gtk_reserved2) (void); - void (*_gtk_reserved3) (void); + /* Padding for future expansion */ + void (*_gtk_reserved0) (void); + void (*_gtk_reserved1) (void); + void (*_gtk_reserved2) (void); + void (*_gtk_reserved3) (void); }; Modified: trunk/src/gtkutils.c =================================================================== --- trunk/src/gtkutils.c 2006-08-16 06:11:10 UTC (rev 16782) +++ trunk/src/gtkutils.c 2006-08-16 06:31:59 UTC (rev 16783) @@ -2358,14 +2358,13 @@ } -GtkWidget *gaim_gtk_buddy_icon_chooser_new(GtkWindow *parent, void(*callback)(const char*,gpointer), gpointer data) { +GtkWidget *gaim_gtk_buddy_icon_chooser_new(GtkWindow *parent, void(*callback)(const char *, gpointer), gpointer data) { struct _icon_chooser *dialog = g_new0(struct _icon_chooser, 1); #if !GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ GtkWidget *hbox; GtkWidget *tv; GtkTreeSelection *sel; - #endif /* FILECHOOSER */ const char *current_folder; @@ -2457,15 +2456,13 @@ char* gaim_gtk_convert_buddy_icon(GaimPlugin *plugin, const char *path) { - g_return_val_if_fail(GAIM_PLUGIN_PROTOCOL_INFO(plugin)->icon_spec.format != NULL, NULL); - #if GTK_CHECK_VERSION(2,2,0) int width, height; char **pixbuf_formats = NULL; GdkPixbufFormat *format; GdkPixbuf *pixbuf; - GaimPluginProtocolInfo *prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin); - char **prpl_formats = g_strsplit (prpl_info->icon_spec.format,",",0); + GaimPluginProtocolInfo *prpl_info; + char **prpl_formats; #if !GTK_CHECK_VERSION(2,4,0) GdkPixbufLoader *loader; FILE *file; @@ -2473,10 +2470,19 @@ void *data = NULL; #endif #endif - const char *dirname = gaim_buddy_icons_get_cache_dir(); - char *random = g_strdup_printf("%x", g_random_int()); - char *filename = g_build_filename(dirname, random, NULL); + const char *dirname; + char *random; + char *filename; + prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(plugin); + + g_return_val_if_fail(prpl_info->icon_spec.format != NULL, NULL); + + prpl_formats = g_strsplit(prpl_info->icon_spec.format,",",0); + dirname = gaim_buddy_icons_get_cache_dir(); + random = g_strdup_printf("%x", g_random_int()); + filename = g_build_filename(dirname, random, NULL); + if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) { gaim_debug_info("buddyicon", "Creating icon cache directory.\n"); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <the...@us...> - 2006-08-16 07:02:30
|
Revision: 16785 Author: thekingant Date: 2006-08-16 00:02:26 -0700 (Wed, 16 Aug 2006) ViewCVS: http://svn.sourceforge.net/gaim/?rev=16785&view=rev Log Message: ----------- Fix some assertion failures Modified Paths: -------------- trunk/src/gtkstatusbox.c trunk/src/prefs.c Modified: trunk/src/gtkstatusbox.c =================================================================== --- trunk/src/gtkstatusbox.c 2006-08-16 06:58:59 UTC (rev 16784) +++ trunk/src/gtkstatusbox.c 2006-08-16 07:02:26 UTC (rev 16785) @@ -1179,7 +1179,9 @@ icon_alc.x = allocation->width - icon_alc.width; icon_alc.y += 3; - if (status_box->icon_size != icon_alc.height) { + if ((status_box->buddy_icon_path != NULL) && + (status_box->icon_size != icon_alc.height)) + { scaled = gdk_pixbuf_new_from_file_at_scale(status_box->buddy_icon_path, icon_alc.height, icon_alc.width, FALSE, NULL); status_box->buddy_icon_hover = gdk_pixbuf_copy(scaled); @@ -1335,13 +1337,16 @@ g_free(box->buddy_icon_path); box->buddy_icon_path = g_strdup(filename); - scaled = gdk_pixbuf_new_from_file_at_scale(filename, - box->icon_size, box->icon_size, FALSE, NULL); - box->buddy_icon_hover = gdk_pixbuf_copy(scaled); - do_colorshift(box->buddy_icon_hover, box->buddy_icon_hover, 30); - g_object_unref(box->buddy_icon); - box->buddy_icon = scaled; - gtk_image_set_from_pixbuf(GTK_IMAGE(box->icon), box->buddy_icon); + if (filename != NULL) + { + scaled = gdk_pixbuf_new_from_file_at_scale(filename, + box->icon_size, box->icon_size, FALSE, NULL); + box->buddy_icon_hover = gdk_pixbuf_copy(scaled); + do_colorshift(box->buddy_icon_hover, box->buddy_icon_hover, 30); + g_object_unref(box->buddy_icon); + box->buddy_icon = scaled; + gtk_image_set_from_pixbuf(GTK_IMAGE(box->icon), box->buddy_icon); + } gaim_prefs_set_string("/gaim/gtk/accounts/buddyicon", filename); } Modified: trunk/src/prefs.c =================================================================== --- trunk/src/prefs.c 2006-08-16 06:58:59 UTC (rev 16784) +++ trunk/src/prefs.c 2006-08-16 07:02:26 UTC (rev 16785) @@ -124,14 +124,14 @@ } else if (pref->type == GAIM_PREF_STRING) { xmlnode_set_attrib(node, "type", "string"); - xmlnode_set_attrib(node, "value", pref->value.string); + xmlnode_set_attrib(node, "value", pref->value.string ? pref->value.string : ""); } else if (pref->type == GAIM_PREF_STRING_LIST) { xmlnode_set_attrib(node, "type", "stringlist"); for (cur = pref->value.stringlist; cur != NULL; cur = cur->next) { childnode = xmlnode_new_child(node, "item"); - xmlnode_set_attrib(childnode, "value", cur->data); + xmlnode_set_attrib(childnode, "value", cur->data ? cur->data : ""); } } else if (pref->type == GAIM_PREF_BOOLEAN) { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <the...@us...> - 2006-08-16 07:19:46
|
Revision: 16786 Author: thekingant Date: 2006-08-16 00:19:41 -0700 (Wed, 16 Aug 2006) ViewCVS: http://svn.sourceforge.net/gaim/?rev=16786&view=rev Log Message: ----------- More/better assertion fixes. Especially when starting Gaim with an account that doesn't have the global buddy icon preference set, or when trying to set an icon using a file that is not an image. Modified Paths: -------------- trunk/src/gtkstatusbox.c trunk/src/gtkutils.c Modified: trunk/src/gtkstatusbox.c =================================================================== --- trunk/src/gtkstatusbox.c 2006-08-16 07:02:26 UTC (rev 16785) +++ trunk/src/gtkstatusbox.c 2006-08-16 07:19:41 UTC (rev 16786) @@ -1179,16 +1179,19 @@ icon_alc.x = allocation->width - icon_alc.width; icon_alc.y += 3; - if ((status_box->buddy_icon_path != NULL) && - (status_box->icon_size != icon_alc.height)) + if (status_box->icon_size != icon_alc.height) { - scaled = gdk_pixbuf_new_from_file_at_scale(status_box->buddy_icon_path, - icon_alc.height, icon_alc.width, FALSE, NULL); - status_box->buddy_icon_hover = gdk_pixbuf_copy(scaled); - do_colorshift(status_box->buddy_icon_hover, status_box->buddy_icon_hover, 30); - g_object_unref(status_box->buddy_icon); - status_box->buddy_icon = scaled; - gtk_image_set_from_pixbuf(GTK_IMAGE(status_box->icon), status_box->buddy_icon); + if ((status_box->buddy_icon_path != NULL) && + (*status_box->buddy_icon_path != '\0')) + { + scaled = gdk_pixbuf_new_from_file_at_scale(status_box->buddy_icon_path, + icon_alc.height, icon_alc.width, FALSE, NULL); + status_box->buddy_icon_hover = gdk_pixbuf_copy(scaled); + do_colorshift(status_box->buddy_icon_hover, status_box->buddy_icon_hover, 30); + g_object_unref(status_box->buddy_icon); + status_box->buddy_icon = scaled; + gtk_image_set_from_pixbuf(GTK_IMAGE(status_box->icon), status_box->buddy_icon); + } status_box->icon_size = icon_alc.height; } gtk_widget_size_allocate((GTK_GAIM_STATUS_BOX(widget))->icon_box, &icon_alc); @@ -1337,15 +1340,19 @@ g_free(box->buddy_icon_path); box->buddy_icon_path = g_strdup(filename); - if (filename != NULL) + if ((filename != NULL) && (*filename != '\0')) { + if (box->buddy_icon != NULL) + g_object_unref(box->buddy_icon); scaled = gdk_pixbuf_new_from_file_at_scale(filename, box->icon_size, box->icon_size, FALSE, NULL); - box->buddy_icon_hover = gdk_pixbuf_copy(scaled); - do_colorshift(box->buddy_icon_hover, box->buddy_icon_hover, 30); - g_object_unref(box->buddy_icon); - box->buddy_icon = scaled; - gtk_image_set_from_pixbuf(GTK_IMAGE(box->icon), box->buddy_icon); + if (scaled != NULL) + { + box->buddy_icon_hover = gdk_pixbuf_copy(scaled); + do_colorshift(box->buddy_icon_hover, box->buddy_icon_hover, 30); + box->buddy_icon = scaled; + gtk_image_set_from_pixbuf(GTK_IMAGE(box->icon), box->buddy_icon); + } } gaim_prefs_set_string("/gaim/gtk/accounts/buddyicon", filename); Modified: trunk/src/gtkutils.c =================================================================== --- trunk/src/gtkutils.c 2006-08-16 07:02:26 UTC (rev 16785) +++ trunk/src/gtkutils.c 2006-08-16 07:19:41 UTC (rev 16786) @@ -2453,7 +2453,7 @@ } #endif -char* +char * gaim_gtk_convert_buddy_icon(GaimPlugin *plugin, const char *path) { #if GTK_CHECK_VERSION(2,2,0) @@ -2518,6 +2518,8 @@ format = gdk_pixbuf_loader_get_format(loader); g_object_unref(G_OBJECT(loader)); #endif + if (format == NULL) + return NULL; pixbuf_formats = gdk_pixbuf_format_get_extensions(format); if (str_array_match(pixbuf_formats, prpl_formats) && /* This is an acceptable format AND */ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <sa...@us...> - 2006-08-16 19:21:08
|
Revision: 16798 Author: sadrul Date: 2006-08-16 12:21:04 -0700 (Wed, 16 Aug 2006) ViewCVS: http://svn.sourceforge.net/gaim/?rev=16798&view=rev Log Message: ----------- Do not show the icon-box for an account if it doesn't support buddyicons in the per-account statusboxes. Allow the peraccount boxes to change the icon only for that account. Modified Paths: -------------- trunk/src/gtkblist.c trunk/src/gtkstatusbox.c Modified: trunk/src/gtkblist.c =================================================================== --- trunk/src/gtkblist.c 2006-08-16 19:05:07 UTC (rev 16797) +++ trunk/src/gtkblist.c 2006-08-16 19:21:04 UTC (rev 16798) @@ -4018,7 +4018,6 @@ gtkblist->statusbox = gtk_gaim_status_box_new(); gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->statusbox, FALSE, TRUE, 0); gtk_widget_set_name(gtkblist->statusbox, "gaim_gtkblist_statusbox"); - gtk_gaim_status_box_set_buddy_icon(gtkblist->statusbox, gaim_prefs_get_string("/gaim/gtk/accounts/buddyicon")); gtk_widget_show(gtkblist->statusbox); /* set the Show Offline Buddies option. must be done Modified: trunk/src/gtkstatusbox.c =================================================================== --- trunk/src/gtkstatusbox.c 2006-08-16 19:05:07 UTC (rev 16797) +++ trunk/src/gtkstatusbox.c 2006-08-16 19:21:04 UTC (rev 16798) @@ -78,6 +78,7 @@ static void gtk_gaim_status_box_forall (GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data); static void do_colorshift (GdkPixbuf *dest, GdkPixbuf *src, int shift); +static void icon_choose_cb(const char *filename, gpointer data); static void (*combo_box_size_request)(GtkWidget *widget, GtkRequisition *requisition); static void (*combo_box_size_allocate)(GtkWidget *widget, GtkAllocation *allocation); @@ -220,7 +221,64 @@ update_to_reflect_account_status(status_box, account, newstatus); } +static gboolean +icon_box_press_cb(GtkWidget *widget, GdkEventButton *event, GtkGaimStatusBox *box) +{ + if (box->buddy_icon_sel) { + gtk_window_present(GTK_WINDOW(box->buddy_icon_sel)); + return FALSE; + } + + box->buddy_icon_sel = gaim_gtk_buddy_icon_chooser_new(NULL, icon_choose_cb, box); + gtk_widget_show_all(box->buddy_icon_sel); + return FALSE; +} + +static gboolean +icon_box_enter_cb(GtkWidget *widget, GdkEventCrossing *event, GtkGaimStatusBox *box) +{ + gdk_window_set_cursor(widget->window, box->hand_cursor); + gtk_image_set_from_pixbuf(GTK_IMAGE(box->icon), box->buddy_icon_hover); + return FALSE; +} + +static gboolean +icon_box_leave_cb(GtkWidget *widget, GdkEventCrossing *event, GtkGaimStatusBox *box) +{ + gdk_window_set_cursor(widget->window, box->arrow_cursor); + gtk_image_set_from_pixbuf(GTK_IMAGE(box->icon), box->buddy_icon) ; + return FALSE; +} + static void +setup_icon_box(GtkGaimStatusBox *status_box) +{ + if (status_box->account) + { + char *string = gaim_buddy_icons_get_full_path(gaim_account_get_buddy_icon(status_box->account)); + gtk_gaim_status_box_set_buddy_icon(status_box, string); + g_free(string); + } + else + { + gtk_gaim_status_box_set_buddy_icon(status_box, gaim_prefs_get_string("/gaim/gtk/accounts/buddyicon")); + } + status_box->icon = gtk_image_new_from_pixbuf(status_box->buddy_icon); + status_box->icon_box = gtk_event_box_new(); + status_box->hand_cursor = gdk_cursor_new (GDK_HAND2); + status_box->arrow_cursor = gdk_cursor_new (GDK_LEFT_PTR); + + g_signal_connect(G_OBJECT(status_box->icon_box), "enter-notify-event", G_CALLBACK(icon_box_enter_cb), status_box); + g_signal_connect(G_OBJECT(status_box->icon_box), "leave-notify-event", G_CALLBACK(icon_box_leave_cb), status_box); + g_signal_connect(G_OBJECT(status_box->icon_box), "button-press-event", G_CALLBACK(icon_box_press_cb), status_box); + + gtk_container_add(GTK_CONTAINER(status_box->icon_box), status_box->icon); + + gtk_widget_show_all(status_box->icon_box); + gtk_widget_set_parent(status_box->icon_box, GTK_WIDGET(status_box)); +} + +static void gtk_gaim_status_box_set_property(GObject *object, guint param_id, const GValue *value, GParamSpec *pspec) { @@ -236,10 +294,18 @@ statusbox->status_changed_signal = 0; } - if (statusbox->account) + if (statusbox->account) { + GaimPlugin *plug = gaim_plugins_find_with_id(gaim_account_get_protocol_id(statusbox->account)); + GaimPluginProtocolInfo *prplinfo = GAIM_PLUGIN_PROTOCOL_INFO(plug); + if (prplinfo && prplinfo->icon_spec.format != NULL) { + setup_icon_box(statusbox); + } statusbox->status_changed_signal = gaim_signal_connect(gaim_accounts_get_handle(), "account-status-changed", statusbox, GAIM_CALLBACK(account_status_changed_cb), statusbox); + } else { + setup_icon_box(statusbox); + } gtk_gaim_status_box_regenerate(statusbox); break; @@ -928,15 +994,27 @@ if (filename) { GList *accounts; - for (accounts = gaim_accounts_get_all(); accounts != NULL; accounts = accounts->next) { - GaimAccount *account = accounts->data; - if (gaim_account_get_ui_bool(account, GAIM_GTK_UI, "use-global-buddy-icon", TRUE) && - GAIM_PLUGIN_PROTOCOL_INFO(gaim_find_prpl(gaim_account_get_protocol_id(account)))->icon_spec.format) { - char *icon = gaim_gtk_convert_buddy_icon(gaim_find_prpl(gaim_account_get_protocol_id(account)), - filename); - gaim_account_set_buddy_icon(account, icon); + + if (box->account) { + GaimPlugin *plug = gaim_find_prpl(gaim_account_get_protocol_id(box->account)); + GaimPluginProtocolInfo *prplinfo = GAIM_PLUGIN_PROTOCOL_INFO(plug); + if (prplinfo->icon_spec.format) { + char *icon = gaim_gtk_convert_buddy_icon(plug, filename); + gaim_account_set_buddy_icon(box->account, icon); g_free(icon); } + } else { + for (accounts = gaim_accounts_get_all(); accounts != NULL; accounts = accounts->next) { + GaimAccount *account = accounts->data; + GaimPlugin *plug = gaim_find_prpl(gaim_account_get_protocol_id(account)); + GaimPluginProtocolInfo *prplinfo = GAIM_PLUGIN_PROTOCOL_INFO(plug); + if (gaim_account_get_ui_bool(account, GAIM_GTK_UI, "use-global-buddy-icon", TRUE) && + prplinfo->icon_spec.format) { + char *icon = gaim_gtk_convert_buddy_icon(plug, filename); + gaim_account_set_buddy_icon(account, icon); + g_free(icon); + } + } } gtk_gaim_status_box_set_buddy_icon(box, filename); } @@ -944,35 +1022,6 @@ box->buddy_icon_sel = NULL; } -static gboolean -icon_box_press_cb(GtkWidget *widget, GdkEventButton *event, GtkGaimStatusBox *box) -{ - if (box->buddy_icon_sel) { - gtk_window_present(GTK_WINDOW(box->buddy_icon_sel)); - return FALSE; - } - - box->buddy_icon_sel = gaim_gtk_buddy_icon_chooser_new(NULL, icon_choose_cb, box); - gtk_widget_show_all(box->buddy_icon_sel); - return FALSE; -} - -static gboolean -icon_box_enter_cb(GtkWidget *widget, GdkEventCrossing *event, GtkGaimStatusBox *box) -{ - gdk_window_set_cursor(widget->window, box->hand_cursor); - gtk_image_set_from_pixbuf(GTK_IMAGE(box->icon), box->buddy_icon_hover); - return FALSE; -} - -static gboolean -icon_box_leave_cb(GtkWidget *widget, GdkEventCrossing *event, GtkGaimStatusBox *box) -{ - gdk_window_set_cursor(widget->window, box->arrow_cursor); - gtk_image_set_from_pixbuf(GTK_IMAGE(box->icon), box->buddy_icon) ; - return FALSE; -} - static void gtk_gaim_status_box_init (GtkGaimStatusBox *status_box) { @@ -989,17 +1038,6 @@ status_box->vsep = gtk_vseparator_new(); status_box->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE); - status_box->icon = gtk_image_new_from_pixbuf(status_box->buddy_icon); - status_box->icon_box = gtk_event_box_new(); - status_box->hand_cursor = gdk_cursor_new (GDK_HAND2); - status_box->arrow_cursor = gdk_cursor_new (GDK_LEFT_PTR); - - g_signal_connect(G_OBJECT(status_box->icon_box), "enter-notify-event", G_CALLBACK(icon_box_enter_cb), status_box); - g_signal_connect(G_OBJECT(status_box->icon_box), "leave-notify-event", G_CALLBACK(icon_box_leave_cb), status_box); - g_signal_connect(G_OBJECT(status_box->icon_box), "button-press-event", G_CALLBACK(icon_box_press_cb), status_box); - - gtk_container_add(GTK_CONTAINER(status_box->icon_box), status_box->icon); - status_box->store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_INT, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER); status_box->dropdown_store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_INT, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER); gtk_combo_box_set_model(GTK_COMBO_BOX(status_box), GTK_TREE_MODEL(status_box->dropdown_store)); @@ -1012,7 +1050,6 @@ gtk_box_pack_start(GTK_BOX(status_box->hbox), status_box->vsep, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(status_box->hbox), status_box->arrow, FALSE, FALSE, 0); gtk_widget_show_all(status_box->toggle_button); - gtk_widget_show_all(status_box->icon_box); #if GTK_CHECK_VERSION(2,4,0) gtk_button_set_focus_on_click(GTK_BUTTON(status_box->toggle_button), FALSE); #endif @@ -1063,7 +1100,6 @@ #endif gtk_widget_set_parent(status_box->vbox, GTK_WIDGET(status_box)); gtk_widget_set_parent(status_box->toggle_button, GTK_WIDGET(status_box)); - gtk_widget_set_parent(status_box->icon_box, GTK_WIDGET(status_box)); GTK_BIN(status_box)->child = status_box->toggle_button; gtk_box_pack_start(GTK_BOX(status_box->vbox), status_box->sw, TRUE, TRUE, 0); @@ -1167,35 +1203,37 @@ parent_alc = *allocation; parent_alc.height = MAX(1,req.height); parent_alc.y += 3; - parent_alc.width -= (parent_alc.height + 3); - combo_box_size_allocate(widget, &parent_alc); - icon_alc = *allocation; - icon_alc.height = MAX(1,req.height); - icon_alc.width = icon_alc.height; - icon_alc.x = allocation->width - icon_alc.width; - icon_alc.y += 3; + if (status_box->icon_box) + { + parent_alc.width -= (parent_alc.height + 3); + icon_alc = *allocation; + icon_alc.height = MAX(1,req.height); + icon_alc.width = icon_alc.height; + icon_alc.x = allocation->width - icon_alc.width; + icon_alc.y += 3; - if (status_box->icon_size != icon_alc.height) - { - if ((status_box->buddy_icon_path != NULL) && - (*status_box->buddy_icon_path != '\0')) + if (status_box->icon_size != icon_alc.height) { - scaled = gdk_pixbuf_new_from_file_at_scale(status_box->buddy_icon_path, - icon_alc.height, icon_alc.width, FALSE, NULL); - g_object_unref(status_box->buddy_icon_hover); - status_box->buddy_icon_hover = gdk_pixbuf_copy(scaled); - do_colorshift(status_box->buddy_icon_hover, status_box->buddy_icon_hover, 30); - g_object_unref(status_box->buddy_icon); - status_box->buddy_icon = scaled; - gtk_image_set_from_pixbuf(GTK_IMAGE(status_box->icon), status_box->buddy_icon); + if ((status_box->buddy_icon_path != NULL) && + (*status_box->buddy_icon_path != '\0')) + { + scaled = gdk_pixbuf_new_from_file_at_scale(status_box->buddy_icon_path, + icon_alc.height, icon_alc.width, FALSE, NULL); + g_object_unref(status_box->buddy_icon_hover); + status_box->buddy_icon_hover = gdk_pixbuf_copy(scaled); + do_colorshift(status_box->buddy_icon_hover, status_box->buddy_icon_hover, 30); + g_object_unref(status_box->buddy_icon); + status_box->buddy_icon = scaled; + gtk_image_set_from_pixbuf(GTK_IMAGE(status_box->icon), status_box->buddy_icon); + } + status_box->icon_size = icon_alc.height; } - status_box->icon_size = icon_alc.height; + gtk_widget_size_allocate(status_box->icon_box, &icon_alc); } - gtk_widget_size_allocate((GTK_GAIM_STATUS_BOX(widget))->icon_box, &icon_alc); - - gtk_widget_size_allocate((GTK_GAIM_STATUS_BOX(widget))->toggle_button, &parent_alc); + combo_box_size_allocate(widget, &parent_alc); + gtk_widget_size_allocate(status_box->toggle_button, &parent_alc); widget->allocation = *allocation; } @@ -1223,7 +1261,8 @@ (* callback) (status_box->vbox, callback_data); (* callback) (status_box->toggle_button, callback_data); (* callback) (status_box->arrow, callback_data); - (* callback) (status_box->icon_box, callback_data); + if (status_box->icon_box) + (* callback) (status_box->icon_box, callback_data); } combo_box_forall(container, include_internals, callback, callback_data); @@ -1232,7 +1271,7 @@ GtkWidget * gtk_gaim_status_box_new() { - return g_object_new(GTK_GAIM_TYPE_STATUS_BOX, NULL); + return g_object_new(GTK_GAIM_TYPE_STATUS_BOX, "account", NULL, NULL); } GtkWidget * @@ -1353,7 +1392,8 @@ } } - gaim_prefs_set_string("/gaim/gtk/accounts/buddyicon", filename); + if (box->account == NULL) + gaim_prefs_set_string("/gaim/gtk/accounts/buddyicon", filename); } const char* This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <the...@us...> - 2006-08-17 05:47:17
|
Revision: 16808 Author: thekingant Date: 2006-08-16 22:47:10 -0700 (Wed, 16 Aug 2006) ViewCVS: http://svn.sourceforge.net/gaim/?rev=16808&view=rev Log Message: ----------- gaim_ssl_connect's are now cancelable (without crashing, anyway) This was relatively easy, because the PRPLs already keep a reference to the GaimSslConnection. I just needed to update the core ssl code to keep track of the GaimProxyConnectInfo, and to call gaim_proxy_connect_cancel() when gaim_ssl_close() is called Modified Paths: -------------- trunk/plugins/ssl/ssl-nss.c trunk/src/protocols/irc/irc.c trunk/src/sslconn.c trunk/src/sslconn.h Modified: trunk/plugins/ssl/ssl-nss.c =================================================================== --- trunk/plugins/ssl/ssl-nss.c 2006-08-17 04:45:11 UTC (rev 16807) +++ trunk/plugins/ssl/ssl-nss.c 2006-08-17 05:47:10 UTC (rev 16808) @@ -237,16 +237,13 @@ } static void -ssl_nss_connect_cb(gpointer data, gint source, GaimInputCondition cond) +ssl_nss_connect(GaimSslConnection *gsc) { - GaimSslConnection *gsc = (GaimSslConnection *)data; GaimSslNssData *nss_data = g_new0(GaimSslNssData, 1); PRSocketOptionData socket_opt; gsc->private_data = nss_data; - gsc->fd = source; - nss_data->fd = PR_ImportTCPSocket(gsc->fd); if (nss_data->fd == NULL) @@ -359,7 +356,7 @@ { ssl_nss_init, ssl_nss_uninit, - ssl_nss_connect_cb, + ssl_nss_connect, ssl_nss_close, ssl_nss_read, ssl_nss_write Modified: trunk/src/protocols/irc/irc.c =================================================================== --- trunk/src/protocols/irc/irc.c 2006-08-17 04:45:11 UTC (rev 16807) +++ trunk/src/protocols/irc/irc.c 2006-08-17 05:47:10 UTC (rev 16808) @@ -450,7 +450,7 @@ g_free(irc->inbuf); if (irc->gsc) { gaim_ssl_close(irc->gsc); - } else if (irc->fd > 0) { + } else if (irc->fd >= 0) { close(irc->fd); } if (irc->timer) Modified: trunk/src/sslconn.c =================================================================== --- trunk/src/sslconn.c 2006-08-17 04:45:11 UTC (rev 16807) +++ trunk/src/sslconn.c 2006-08-17 05:47:10 UTC (rev 16808) @@ -45,10 +45,14 @@ gaim_plugin_load(plugin); ops = gaim_ssl_get_ops(); - if (ops != NULL && ops->init != NULL) - return ops->init(); - else + if ((ops == NULL) || (ops->init == NULL) || (ops->uninit == NULL) || + (ops->connect == NULL) || (ops->close == NULL) || + (ops->read == NULL) || (ops->write == NULL)) + { return FALSE; + } + + return ops->init(); } gboolean @@ -62,25 +66,42 @@ #endif } +static void +gaim_ssl_connect_cb(gpointer data, gint source, const gchar *error_message) +{ + GaimSslConnection *gsc; + GaimSslOps *ops; + + gsc = data; + gsc->connect_info = NULL; + + if (source < 0) + { + if (gsc->error_cb != NULL) + gsc->error_cb(gsc, GAIM_SSL_CONNECT_FAILED, gsc->connect_cb_data); + + gaim_ssl_close(gsc); + return; + } + + gsc->fd = source; + + ops = gaim_ssl_get_ops(); + ops->connect(gsc); +} + GaimSslConnection * gaim_ssl_connect(GaimAccount *account, const char *host, int port, GaimSslInputFunction func, GaimSslErrorFunction error_func, void *data) { GaimSslConnection *gsc; - GaimSslOps *ops; - GaimProxyConnectInfo *connect_info; g_return_val_if_fail(host != NULL, NULL); g_return_val_if_fail(port != 0 && port != -1, NULL); g_return_val_if_fail(func != NULL, NULL); g_return_val_if_fail(gaim_ssl_is_supported(), NULL); - ops = gaim_ssl_get_ops(); - - g_return_val_if_fail(ops != NULL, NULL); - g_return_val_if_fail(ops->connect_cb != NULL, NULL); - if (!_ssl_initialized) { if (!ssl_init()) @@ -89,15 +110,16 @@ gsc = g_new0(GaimSslConnection, 1); + gsc->fd = -1; gsc->host = g_strdup(host); gsc->port = port; gsc->connect_cb_data = data; gsc->connect_cb = func; gsc->error_cb = error_func; - connect_info = gaim_proxy_connect(account, host, port, ops->connect_cb, gsc); + gsc->connect_info = gaim_proxy_connect(account, host, port, gaim_ssl_connect_cb, gsc); - if (connect_info == NULL) + if (gsc->connect_info == NULL) { g_free(gsc->host); g_free(gsc); @@ -141,11 +163,6 @@ g_return_val_if_fail(func != NULL, NULL); g_return_val_if_fail(gaim_ssl_is_supported(), NULL); - ops = gaim_ssl_get_ops(); - - g_return_val_if_fail(ops != NULL, NULL); - g_return_val_if_fail(ops->connect_cb != NULL, NULL); - if (!_ssl_initialized) { if (!ssl_init()) @@ -157,8 +174,10 @@ gsc->connect_cb_data = data; gsc->connect_cb = func; gsc->error_cb = error_func; + gsc->fd = fd; - ops->connect_cb(gsc, fd, GAIM_INPUT_READ); + ops = gaim_ssl_get_ops(); + ops->connect(gsc); return (GaimSslConnection *)gsc; } @@ -171,14 +190,15 @@ g_return_if_fail(gsc != NULL); ops = gaim_ssl_get_ops(); + (ops->close)(gsc); - if (gsc->inpa) + if (gsc->connect_info != NULL) + gaim_proxy_connect_cancel(gsc->connect_info); + + if (gsc->inpa > 0) gaim_input_remove(gsc->inpa); - if (ops != NULL && ops->close != NULL) - (ops->close)(gsc); - - if (gsc->fd != -1) + if (gsc->fd >= 0) close(gsc->fd); g_free(gsc->host); @@ -195,11 +215,7 @@ g_return_val_if_fail(len > 0, 0); ops = gaim_ssl_get_ops(); - - if (ops != NULL && (ops->read) != NULL) - return (ops->read)(gsc, data, len); - - return 0; + return (ops->read)(gsc, data, len); } size_t @@ -212,11 +228,7 @@ g_return_val_if_fail(len > 0, 0); ops = gaim_ssl_get_ops(); - - if (ops != NULL && (ops->write) != NULL) - return (ops->write)(gsc, data, len); - - return 0; + return (ops->write)(gsc, data, len); } void @@ -245,9 +257,7 @@ return; ops = gaim_ssl_get_ops(); + ops->uninit(); - if (ops != NULL && ops->uninit != NULL) - ops->uninit(); - _ssl_initialized = FALSE; } Modified: trunk/src/sslconn.h =================================================================== --- trunk/src/sslconn.h 2006-08-17 04:45:11 UTC (rev 16807) +++ trunk/src/sslconn.h 2006-08-17 05:47:10 UTC (rev 16808) @@ -54,6 +54,7 @@ int fd; int inpa; + GaimProxyConnectInfo *connect_info; void *private_data; }; @@ -61,13 +62,13 @@ /** * SSL implementation operations structure. * - * Every SSL implementation must provide one of these and register it. + * Every SSL implementation must provide all of these and register it. */ typedef struct { gboolean (*init)(void); void (*uninit)(void); - GaimInputFunction connect_cb; + void (*connect)(GaimSslConnection *gsc); void (*close)(GaimSslConnection *gsc); size_t (*read)(GaimSslConnection *gsc, void *data, size_t len); size_t (*write)(GaimSslConnection *gsc, const void *data, size_t len); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <the...@us...> - 2006-08-17 07:45:07
|
Revision: 16811 Author: thekingant Date: 2006-08-17 00:44:52 -0700 (Thu, 17 Aug 2006) ViewCVS: http://svn.sourceforge.net/gaim/?rev=16811&view=rev Log Message: ----------- Split the DNS query stuff out into it's own file. Eventually we should move the dnssrv code into this same file. Maybe even share some code? Also the first steps toward cancelable DNS queries. Modified Paths: -------------- trunk/src/Makefile.am trunk/src/Makefile.mingw trunk/src/protocols/simple/simple.c trunk/src/proxy.c trunk/src/proxy.h trunk/src/stun.c Added Paths: ----------- trunk/src/dnsquery.c trunk/src/dnsquery.h Modified: trunk/src/Makefile.am =================================================================== --- trunk/src/Makefile.am 2006-08-17 06:21:28 UTC (rev 16810) +++ trunk/src/Makefile.am 2006-08-17 07:44:52 UTC (rev 16811) @@ -77,6 +77,7 @@ core.c \ debug.c \ desktopitem.c \ + dnsquery.c \ eventloop.c \ ft.c \ idle.c \ @@ -125,6 +126,7 @@ dbus-maybe.h \ debug.h \ desktopitem.h \ + dnsquery.h \ eventloop.h \ ft.h \ idle.h \ Modified: trunk/src/Makefile.mingw =================================================================== --- trunk/src/Makefile.mingw 2006-08-17 06:21:28 UTC (rev 16810) +++ trunk/src/Makefile.mingw 2006-08-17 07:44:52 UTC (rev 16811) @@ -95,6 +95,7 @@ conversation.c \ core.c \ debug.c \ + dnsquery.c \ dnssrv.c \ eventloop.c \ ft.c \ Added: trunk/src/dnsquery.c =================================================================== --- trunk/src/dnsquery.c (rev 0) +++ trunk/src/dnsquery.c 2006-08-17 07:44:52 UTC (rev 16811) @@ -0,0 +1,774 @@ +/** + * @file dnsquery.c DNS query API + * @ingroup core + * + * gaim + * + * Gaim is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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 + * + */ + +#include "internal.h" +#include "debug.h" +#include "ntlm.h" +#include "util.h" + +struct _GaimDnsqueryData { +}; + +/************************************************************************** + * Global DNS query API + **************************************************************************/ + +#if defined(__unix__) || defined(__APPLE__) + +/* + * This structure represents both a pending DNS request and + * a free child process. + */ +typedef struct _DnsqueryResolverprocess DnsqueryResolverprocess; +struct _DnsqueryResolverprocess { + char *host; + int port; + GaimProxyDnsConnectFunction callback; + gpointer data; + guint inpa; + int fd_in, fd_out; + pid_t dns_pid; +}; + +//static GSList *all_dns_children = NULL; +static GSList *free_dns_children = NULL; +static GQueue *queued_requests = NULL; + +static int number_of_dns_children = 0; + +static const int MAX_DNS_CHILDREN = 2; + +typedef struct { + char hostname[512]; + int port; +} dns_params_t; + +typedef struct { + dns_params_t params; + GaimProxyDnsConnectFunction callback; + gpointer data; +} queued_dns_request_t; + +/* + * Begin the DNS resolver child process functions. + */ +#ifdef HAVE_SIGNAL_H +static void +trap_gdb_bug() +{ + const char *message = + "Gaim's DNS child got a SIGTRAP signal.\n" + "This can be caused by trying to run gaim inside gdb.\n" + "There is a known gdb bug which prevents this. Supposedly gaim\n" + "should have detected you were using gdb and used an ugly hack,\n" + "check cope_with_gdb_brokenness() in dnsquery.c.\n\n" + "For more info about this bug, see http://sources.redhat.com/ml/gdb/2001-07/msg00349.html\n"; + fputs("\n* * *\n",stderr); + fputs(message,stderr); + fputs("* * *\n\n",stderr); + execlp("xmessage","xmessage","-center", message, NULL); + _exit(1); +} +#endif + +static void +cope_with_gdb_brokenness() +{ +#ifdef __linux__ + static gboolean already_done = FALSE; + char s[256], e[512]; + int n; + pid_t ppid; + + if (already_done) + return; + + already_done = TRUE; + ppid = getppid(); + snprintf(s, sizeof(s), "/proc/%d/exe", ppid); + n = readlink(s, e, sizeof(e)); + if (n < 0) + return; + + e[MIN(n,sizeof(e)-1)] = '\0'; + + if (strstr(e,"gdb")) + { + gaim_debug_info("dnsquery", + "Debugger detected, performing useless query...\n"); + gethostbyname("x.x.x.x.x"); + } +#endif +} + +/** + * When doing DNS queries on Unix and OS-X, we fork off a separate + * process and communicate with it using pipes. This function is + * called shortly after the child is forked, and the function exits + * the process after it's no longer needed. + */ +static void +gaim_dns_resolverprocess(int child_out, int child_in, gboolean show_debug) +{ + dns_params_t dns_params; + const size_t zero = 0; + int rc; +#ifdef HAVE_GETADDRINFO + struct addrinfo hints, *res, *tmp; + char servname[20]; +#else + struct sockaddr_in sin; + const size_t addrlen = sizeof(sin); +#endif + +#ifdef HAVE_SIGNAL_H + signal(SIGHUP, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGCHLD, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGTRAP, trap_gdb_bug); +#endif + + /* + * We resolve 1 host name for each iteration of this + * while loop. + * + * The top half of this reads in the hostname and port + * number from the socket with our parent. The bottom + * half of this resolves the IP (blocking) and sends + * the result back to our parent, when finished. + */ + while (1) + { + const char ch = 'Y'; + fd_set fds; + struct timeval tv = { .tv_sec = 40 , .tv_usec = 0 }; + + FD_ZERO(&fds); + FD_SET(child_in, &fds); + rc = select(child_in + 1, &fds, NULL, NULL, &tv); + if (rc < 0) { + if (show_debug) + printf("dns[%d]: select failed\n", getpid()); + close(child_out); + close(child_in); + _exit(1); + } + if (rc == 0) { + if (show_debug) + printf("dns[%d]: nobody needs me... =(\n", getpid()); + break; + } + + rc = read(child_in, &dns_params, sizeof(dns_params_t)); + if (rc < 0) { + if (show_debug) + printf("dns[%d]: read failed\n", getpid()); + close(child_out); + close(child_in); + _exit(1); + } + if (rc == 0) { + if (show_debug) + printf("dns[%d]: Oops, father has gone, wait for me, wait...!\n", getpid()); + break; + } + if (dns_params.hostname[0] == '\0') { + printf("dns[%d]: hostname = \"\" (port = %d)!!!\n", getpid(), dns_params.port); + close(child_out); + close(child_in); + _exit(1); + } + /* Tell our parent that we read the data successfully */ + write(child_out, &ch, sizeof(ch)); + + /* We have the hostname and port, now resolve the IP */ + +#ifdef HAVE_GETADDRINFO + g_snprintf(servname, sizeof(servname), "%d", dns_params.port); + memset(&hints, 0, sizeof(hints)); + + /* + * This is only used to convert a service + * name to a port number. As we know we are + * passing a number already, we know this + * value will not be really used by the C + * library. + */ + hints.ai_socktype = SOCK_STREAM; + rc = getaddrinfo(dns_params.hostname, servname, &hints, &res); + write(child_out, &rc, sizeof(rc)); + if (rc != 0) { + close(child_out); + if (show_debug) + printf("dns[%d] Error: getaddrinfo returned %d\n", + getpid(), rc); + dns_params.hostname[0] = '\0'; + continue; + } + for (tmp = res; tmp != NULL; tmp = tmp->ai_next) + { + size_t ai_addrlen = tmp->ai_addrlen; + write(child_out, &ai_addrlen, sizeof(ai_addrlen)); + write(child_out, tmp->ai_addr, tmp->ai_addrlen); + } + freeaddrinfo(res); + write(child_out, &zero, sizeof(zero)); +#else + if (!inet_aton(dns_params.hostname, &sin.sin_addr)) + { + struct hostent *hp; + + hp = gethostbyname(dns_params.hostname); + if (hp == NULL) + { + if (show_debug) + printf("DNS Error: %d\n", h_errno); + write(child_out, &h_errno, sizeof(int)); + close(child_out); + close(child_in); + _exit(1); + } + memset(&sin, 0, sizeof(struct sockaddr_in)); + memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); + sin.sin_family = hp->h_addrtype; + } else + sin.sin_family = AF_INET; + + sin.sin_port = htons(dns_params.port); + write(child_out, &addrlen, sizeof(addrlen)); + write(child_out, &sin, addrlen); + write(child_out, &zero, sizeof(zero)); +#endif + dns_params.hostname[0] = '\0'; + } + + close(child_out); + close(child_in); + + _exit(0); +} + +static DnsqueryResolverprocess * +gaim_dns_new_resolverprocess(gboolean show_debug) +{ + DnsqueryResolverprocess *req; + int child_out[2], child_in[2]; + + /* Create pipes for communicating with the child process */ + if (pipe(child_out) || pipe(child_in)) { + gaim_debug_error("dnsquery", + "Could not create pipes: %s\n", strerror(errno)); + return NULL; + } + + req = g_new(DnsqueryResolverprocess, 1); + + cope_with_gdb_brokenness(); + + /* Fork! */ + req->dns_pid = fork(); + + /* If we are the child process... */ + if (req->dns_pid == 0) { + /* We should not access the parent's side of the pipes, so close them */ + close(child_out[0]); + close(child_in[1]); + + gaim_dns_resolverprocess(child_out[1], child_in[0], show_debug); + /* The thread calls _exit() rather than returning, so we never get here */ + } + + /* We should not access the child's side of the pipes, so close them */ + close(child_out[1]); + close(child_in[0]); + if (req->dns_pid == -1) { + gaim_debug_error("dnsquery", + "Could not create child process for DNS: %s\n", + strerror(errno)); + g_free(req); + return NULL; + } + + req->fd_out = child_out[0]; + req->fd_in = child_in[1]; + number_of_dns_children++; + gaim_debug_info("dnsquery", + "Created new DNS child %d, there are now %d children.\n", + req->dns_pid, number_of_dns_children); + + return req; +} +/* + * End the DNS resolver child process functions. + */ + +/* + * Begin the functions for dealing with the DNS child processes. + */ +static void +req_free(DnsqueryResolverprocess *req) +{ + g_return_if_fail(req != NULL); + + close(req->fd_in); + close(req->fd_out); + + g_free(req->host); + g_free(req); + + number_of_dns_children--; +} + +static int +send_dns_request_to_child(DnsqueryResolverprocess *req, dns_params_t *dns_params) +{ + char ch; + int rc; + pid_t pid; + + /* This waitpid might return the child's PID if it has recently + * exited, or it might return an error if it exited "long + * enough" ago that it has already been reaped; in either + * instance, we can't use it. */ + if ((pid = waitpid (req->dns_pid, NULL, WNOHANG)) > 0) { + gaim_debug_warning("dnsquery", + "DNS child %d no longer exists\n", req->dns_pid); + return -1; + } else if (pid < 0) { + gaim_debug_warning("dnsquery", + "Wait for DNS child %d failed: %s\n", + req->dns_pid, strerror(errno)); + return -1; + } + + /* Let's contact this lost child! */ + rc = write(req->fd_in, dns_params, sizeof(*dns_params)); + if (rc < 0) { + gaim_debug_error("dnsquery", + "Unable to write to DNS child %d: %d\n", + req->dns_pid, strerror(errno)); + close(req->fd_in); + return -1; + } + + g_return_val_if_fail(rc == sizeof(*dns_params), -1); + + /* Did you hear me? (This avoids some race conditions) */ + rc = read(req->fd_out, &ch, sizeof(ch)); + if (rc != 1 || ch != 'Y') + { + gaim_debug_warning("dnsquery", + "DNS child %d not responding. Killing it!\n", + req->dns_pid); + kill(req->dns_pid, SIGKILL); + return -1; + } + + gaim_debug_info("dnsquery", + "Successfully sent DNS request to child %d\n", req->dns_pid); + + return 0; +} + +static void +host_resolved(gpointer data, gint source, GaimInputCondition cond); + +static void +release_dns_child(DnsqueryResolverprocess *req) +{ + g_free(req->host); + req->host = NULL; + + if (queued_requests && !g_queue_is_empty(queued_requests)) { + queued_dns_request_t *r = g_queue_pop_head(queued_requests); + req->host = g_strdup(r->params.hostname); + req->port = r->params.port; + req->callback = r->callback; + req->data = r->data; + + gaim_debug_info("dnsquery", + "Processing queued DNS query for '%s' with child %d\n", + req->host, req->dns_pid); + + if (send_dns_request_to_child(req, &(r->params)) != 0) { + req_free(req); + req = NULL; + + gaim_debug_warning("dnsquery", + "Intent of process queued query of '%s' failed, " + "requeueing...\n", r->params.hostname); + g_queue_push_head(queued_requests, r); + } else { + req->inpa = gaim_input_add(req->fd_out, GAIM_INPUT_READ, host_resolved, req); + g_free(r); + } + + } else { + req->host = NULL; + req->callback = NULL; + req->data = NULL; + free_dns_children = g_slist_append(free_dns_children, req); + } +} + +static void +host_resolved(gpointer data, gint source, GaimInputCondition cond) +{ + DnsqueryResolverprocess *req = (DnsqueryResolverprocess*)data; + int rc, err; + GSList *hosts = NULL; + struct sockaddr *addr = NULL; + size_t addrlen; + + gaim_debug_info("dnsquery", "Got response for '%s'\n", req->host); + gaim_input_remove(req->inpa); + + rc = read(req->fd_out, &err, sizeof(err)); + if ((rc == 4) && (err != 0)) + { + gchar *message; +#ifdef HAVE_GETADDRINFO + message = g_strdup_printf("DNS error: %s (pid=%d)", + gai_strerror(err), req->dns_pid); +#else + message = g_strdup_printf("DNS error: %d (pid=%d)", + err, req->dns_pid); +#endif + gaim_debug_error("dnsquery", "%s\n", message); + req->callback(NULL, req->data, message); + g_free(message); + release_dns_child(req); + return; + } + + if (rc > 0) + { + while (rc > 0) { + rc = read(req->fd_out, &addrlen, sizeof(addrlen)); + if (rc > 0 && addrlen > 0) { + addr = g_malloc(addrlen); + rc = read(req->fd_out, addr, addrlen); + hosts = g_slist_append(hosts, GINT_TO_POINTER(addrlen)); + hosts = g_slist_append(hosts, addr); + } else { + break; + } + } + } else if (rc == -1) { + gchar *message; + message = g_strdup_printf("Error reading from DNS child: %s", strerror(errno)); + gaim_debug_error("dnsquery", "%s\n", message); + req->callback(NULL, req->data, message); + g_free(message); + req_free(req); + return; + } else if (rc == 0) { + gchar *message; + close(req->fd_out); + message = g_strdup_printf("EOF reading from DNS child"); + gaim_debug_error("dnsquery", "%s\n", message); + req->callback(NULL, req->data, message); + g_free(message); + req_free(req); + return; + } + +/* wait4(req->dns_pid, NULL, WNOHANG, NULL); */ + + req->callback(hosts, req->data, NULL); + + release_dns_child(req); +} +/* + * End the functions for dealing with the DNS child processes. + */ + +GaimDnsqueryData * +gaim_dnsquery_a(const char *hostname, int port, GaimDnsqueryConnectFunction callback, gpointer data) +{ + GaimDnsqueryData *query_data; + DnsqueryResolverprocess *req = NULL; + dns_params_t dns_params; + gchar *host_temp; + gboolean show_debug; + + show_debug = gaim_debug_is_enabled(); + + host_temp = g_strstrip(g_strdup(hostname)); + strncpy(dns_params.hostname, host_temp, sizeof(dns_params.hostname) - 1); + g_free(host_temp); + dns_params.hostname[sizeof(dns_params.hostname) - 1] = '\0'; + dns_params.port = port; + + /* + * If we have any children, attempt to have them perform the DNS + * query. If we're able to send the query to a child, then req + * will be set to the DnsqueryResolverprocess. Otherwise, req will + * be NULL and we'll need to create a new DNS request child. + */ + while (free_dns_children != NULL) + { + req = free_dns_children->data; + free_dns_children = g_slist_remove(free_dns_children, req); + + if (send_dns_request_to_child(req, &dns_params) == 0) + /* We found an acceptable child, yay */ + break; + + req_free(req); + req = NULL; + } + + /* We need to create a new DNS request child */ + if (req == NULL) + { + if (number_of_dns_children >= MAX_DNS_CHILDREN) + { + queued_dns_request_t *r = g_new(queued_dns_request_t, 1); + memcpy(&(r->params), &dns_params, sizeof(dns_params)); + r->callback = callback; + r->data = data; + if (!queued_requests) + queued_requests = g_queue_new(); + g_queue_push_tail(queued_requests, r); + + gaim_debug_info("dnsquery", + "DNS query for '%s' queued\n", dns_params.hostname); + + return query_data; + } + + req = gaim_dns_new_resolverprocess(show_debug); + if (req == NULL) + { + gaim_debug_error("dnsquery", "oh dear, this is going to explode, I give up\n"); + return NULL; + } + send_dns_request_to_child(req, &dns_params); + } + + req->host = g_strdup(hostname); + req->port = port; + req->callback = callback; + req->data = data; + req->inpa = gaim_input_add(req->fd_out, GAIM_INPUT_READ, host_resolved, req); + + return query_data; +} + +#elif defined _WIN32 /* end __unix__ || __APPLE__ */ + +typedef struct _dns_tdata { + char *hostname; + int port; + GaimProxyDnsConnectFunction callback; + gpointer data; + GSList *hosts; + char *errmsg; +} dns_tdata; + +static gboolean dns_main_thread_cb(gpointer data) +{ + dns_tdata *td = (dns_tdata*)data; + if (td->errmsg != NULL) { + gaim_debug_info("dnsquery", "%s\n", td->errmsg); + } + td->callback(td->hosts, td->data, td->errmsg); + g_free(td->hostname); + g_free(td->errmsg); + g_free(td); + return FALSE; +} + +static gpointer +dns_thread(gpointer data) +{ + +#ifdef HAVE_GETADDRINFO + int rc; + struct addrinfo hints, *res, *tmp; + char servname[20]; +#else + struct sockaddr_in sin; + struct hostent *hp; +#endif + dns_tdata *td = (dns_tdata*)data; + +#ifdef HAVE_GETADDRINFO + g_snprintf(servname, sizeof(servname), "%d", td->port); + memset(&hints, 0, sizeof(hints)); + + /* This is only used to convert a service + * name to a port number. As we know we are + * passing a number already, we know this + * value will not be really used by the C + * library. + */ + hints.ai_socktype = SOCK_STREAM; + rc = getaddrinfo(td->hostname, servname, &hints, &res); + if (rc == 0) + { + tmp = res; + while(res) { + td->hosts = g_slist_append(td->hosts, + GSIZE_TO_POINTER(res->ai_addrlen)); + td->hosts = g_slist_append(td->hosts, + g_memdup(res->ai_addr, res->ai_addrlen)); + res = res->ai_next; + } + freeaddrinfo(tmp); + } + else + { + td->errmsg = g_strdup_printf("DNS getaddrinfo(\"%s\", \"%s\") error: %d", td->hostname, servname, rc); + } +#else + hp = gethostbyname(td->hostname); + if (hp != NULL) + { + memset(&sin, 0, sizeof(struct sockaddr_in)); + memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); + sin.sin_family = hp->h_addrtype; + sin.sin_port = htons(td->port); + + td->hosts = g_slist_append(td->hosts, + GSIZE_TO_POINTER(sizeof(sin))); + td->hosts = g_slist_append(td->hosts, + g_memdup(&sin, sizeof(sin))); + } else { + td->errmsg = g_strdup_printf("DNS gethostbyname(\"%s\") error: %d", td->hostname, h_errno); + } +#endif + /* back to main thread */ + g_idle_add(dns_main_thread_cb, td); + + return 0; +} + +GaimDnsqueryData * +gaim_dnsquery_a(const char *hostname, int port, + GaimProxyDnsConnectFunction callback, gpointer data) +{ + GaimDnsqueryData *query_data; + dns_tdata *td; + struct sockaddr_in sin; + GError* err = NULL; + + if (inet_aton(hostname, &sin.sin_addr)) + { + GSList *hosts = NULL; + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + hosts = g_slist_append(hosts, GINT_TO_POINTER(sizeof(sin))); + hosts = g_slist_append(hosts, g_memdup(&sin, sizeof(sin))); + callback(hosts, data, NULL); + return query_data; + } + + gaim_debug_info("dnsquery", "DNS Lookup for: %s\n", hostname); + td = g_new0(dns_tdata, 1); + td->hostname = g_strdup(hostname); + td->port = port; + td->callback = callback; + td->data = data; + + if (!g_thread_create(dns_thread, td, FALSE, &err)) + { + gaim_debug_error("dnsquery", "DNS thread create failure: %s\n", err?err->message:""); + g_error_free(err); + g_free(td->hostname); + g_free(td); + return NULL; + } + + return query_data; +} + +#else /* not __unix__ or __APPLE__ or _WIN32 */ + +typedef struct { + gpointer data; + size_t addrlen; + struct sockaddr *addr; + GaimProxyDnsConnectFunction callback; +} DnsqueryResolverprocess; + +static gboolean host_resolved(gpointer data) +{ + DnsqueryResolverprocess *req = (DnsqueryResolverprocess*)data; + GSList *hosts = NULL; + hosts = g_slist_append(hosts, GINT_TO_POINTER(req->addrlen)); + hosts = g_slist_append(hosts, req->addr); + req->callback(hosts, req->data, NULL); + g_free(req); + return FALSE; +} + +GaimDnsqueryData * +gaim_dnsquery_a(const char *hostname, int port, + GaimProxyDnsConnectFunction callback, gpointer data) +{ + GaimDnsqueryData *query_data; + struct sockaddr_in sin; + DnsqueryResolverprocess *req; + + if (!inet_aton(hostname, &sin.sin_addr)) + { + struct hostent *hp; + hp = gethostbyname(hostname); + if (hp == NULL) + { + gaim_debug_error("dnsquery", + "gaim_gethostbyname(\"%s\", %d) failed: %d\n", + hostname, port, h_errno); + return NULL; + } + memset(&sin, 0, sizeof(struct sockaddr_in)); + memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); + sin.sin_family = hp->h_addrtype; + } else + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + + req = g_new(DnsqueryResolverprocess, 1); + req->addr = (struct sockaddr*) g_memdup(&sin, sizeof(sin)); + req->addrlen = sizeof(sin); + req->data = data; + req->callback = callback; + gaim_timeout_add(10, host_resolved, req); + + return query_data; +} + +#endif /* not __unix__ or __APPLE__ or _WIN32 */ + +void +gaim_dnsquery_destroy(GaimDnsqueryData *query_data) +{ + g_free(query_data); +} Property changes on: trunk/src/dnsquery.c ___________________________________________________________________ Name: svn:mime-type + text/plain Name: svn:eol-style + native Added: trunk/src/dnsquery.h =================================================================== --- trunk/src/dnsquery.h (rev 0) +++ trunk/src/dnsquery.h 2006-08-17 07:44:52 UTC (rev 16811) @@ -0,0 +1,76 @@ +/** + * @file dnsquery.h DNS query API + * @ingroup core + * + * gaim + * + * Gaim is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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 + */ +#ifndef _GAIM_DNSQUERY_H_ +#define _GAIM_DNSQUERY_H_ + +#include <glib.h> + +typedef struct _GaimDnsqueryData GaimDnsqueryData; + +/** + * The "hosts" parameter is a linked list containing pairs of + * one size_t addrlen and one struct sockaddr *addr. + */ +typedef void (*GaimDnsqueryConnectFunction)(GSList *hosts, gpointer data, const char *error_message); + + +#ifdef __cplusplus +extern "C" { +#endif + +/**************************************************************************/ +/** @name DNS query API */ +/**************************************************************************/ +/*@{*/ + +/** + * Do an asynchronous DNS query. + * + * @param hostname The hostname to resolve + * @param port A portnumber which is stored in the struct sockaddr + * @param callback Callback to call after resolving + * @param data Extra data for the callback function + * + * @return NULL if there was an error, or a reference to a data + * structure that can be used to cancel the pending + * connection, if needed. + */ +GaimDnsqueryData *gaim_dnsquery_a(const char *hostname, int port, GaimDnsqueryConnectFunction callback, gpointer data); + +/** + * Cancel a DNS query. + * + * @param query_data A pointer to the DNS query data that you want + * to cancel. + */ +void gaim_dnsquery_destroy(GaimDnsqueryData *query_data); + +/*@}*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _GAIM_DNSQUERY_H_ */ Property changes on: trunk/src/dnsquery.h ___________________________________________________________________ Name: svn:mime-type + text/plain Name: svn:eol-style + native Modified: trunk/src/protocols/simple/simple.c =================================================================== --- trunk/src/protocols/simple/simple.c 2006-08-17 06:21:28 UTC (rev 16810) +++ trunk/src/protocols/simple/simple.c 2006-08-17 07:44:52 UTC (rev 16811) @@ -30,6 +30,7 @@ #include "blist.h" #include "conversation.h" #include "debug.h" +#include "dnsquery.h" #include "notify.h" #include "privacy.h" #include "prpl.h" @@ -1621,7 +1622,7 @@ } else { /* UDP */ gaim_debug_info("simple", "using udp with server %s and port %d\n", hostname, port); - gaim_gethostbyname_async(hostname, port, simple_udp_host_resolved, sip); + gaim_dnsquery_a(hostname, port, simple_udp_host_resolved, sip); } } Modified: trunk/src/proxy.c =================================================================== --- trunk/src/proxy.c 2006-08-17 06:21:28 UTC (rev 16810) +++ trunk/src/proxy.c 2006-08-17 07:44:52 UTC (rev 16811) @@ -31,6 +31,7 @@ #include "internal.h" #include "cipher.h" +#include "dnsquery.h" #include "debug.h" #include "notify.h" #include "ntlm.h" @@ -46,6 +47,7 @@ int fd; guint inpa; GaimProxyInfo *gpi; + GaimDnsqueryData *query_data; /** * This contains alternating length/char* values. The char* @@ -307,6 +309,11 @@ connect_infos = g_slist_remove(connect_infos, connect_info); + /* + if (connect_info->query_data != NULL) + gaim_dnsquery_destroy(connect_info->query_data); + */ + while (connect_info->hosts != NULL) { /* Discard the length... */ @@ -351,681 +358,7 @@ gaim_proxy_connect_info_destroy(connect_info); } -#if defined(__unix__) || defined(__APPLE__) - -/* - * This structure represents both a pending DNS request and - * a free child process. - */ -typedef struct { - char *host; - int port; - GaimProxyDnsConnectFunction callback; - gpointer data; - guint inpa; - int fd_in, fd_out; - pid_t dns_pid; -} pending_dns_request_t; - -static GSList *free_dns_children = NULL; -static GQueue *queued_requests = NULL; - -static int number_of_dns_children = 0; - -static const int MAX_DNS_CHILDREN = 2; - -typedef struct { - char hostname[512]; - int port; -} dns_params_t; - -typedef struct { - dns_params_t params; - GaimProxyDnsConnectFunction callback; - gpointer data; -} queued_dns_request_t; - -/* - * Begin the DNS resolver child process functions. - */ -#ifdef HAVE_SIGNAL_H static void -trap_gdb_bug() -{ - const char *message = - "Gaim's DNS child got a SIGTRAP signal.\n" - "This can be caused by trying to run gaim inside gdb.\n" - "There is a known gdb bug which prevents this. Supposedly gaim\n" - "should have detected you were using gdb and used an ugly hack,\n" - "check cope_with_gdb_brokenness() in proxy.c.\n\n" - "For more info about this bug, see http://sources.redhat.com/ml/gdb/2001-07/msg00349.html\n"; - fputs("\n* * *\n",stderr); - fputs(message,stderr); - fputs("* * *\n\n",stderr); - execlp("xmessage","xmessage","-center", message, NULL); - _exit(1); -} -#endif - -static void -cope_with_gdb_brokenness() -{ -#ifdef __linux__ - static gboolean already_done = FALSE; - char s[256], e[512]; - int n; - pid_t ppid; - - if(already_done) - return; - already_done = TRUE; - ppid = getppid(); - snprintf(s, sizeof(s), "/proc/%d/exe", ppid); - n = readlink(s, e, sizeof(e)); - if(n < 0) - return; - - e[MIN(n,sizeof(e)-1)] = '\0'; - - if(strstr(e,"gdb")) { - gaim_debug_info("dns", - "Debugger detected, performing useless query...\n"); - gethostbyname("x.x.x.x.x"); - } -#endif -} - -static void -gaim_dns_resolverthread(int child_out, int child_in, gboolean show_debug) -{ - dns_params_t dns_params; - const size_t zero = 0; - int rc; -#ifdef HAVE_GETADDRINFO - struct addrinfo hints, *res, *tmp; - char servname[20]; -#else - struct sockaddr_in sin; - const size_t addrlen = sizeof(sin); -#endif - -#ifdef HAVE_SIGNAL_H - signal(SIGHUP, SIG_DFL); - signal(SIGINT, SIG_DFL); - signal(SIGQUIT, SIG_DFL); - signal(SIGCHLD, SIG_DFL); - signal(SIGTERM, SIG_DFL); - signal(SIGTRAP, trap_gdb_bug); -#endif - - /* - * We resolve 1 host name for each iteration of this - * while loop. - * - * The top half of this reads in the hostname and port - * number from the socket with our parent. The bottom - * half of this resolves the IP (blocking) and sends - * the result back to our parent, when finished. - */ - while (1) { - const char ch = 'Y'; - fd_set fds; - struct timeval tv = { .tv_sec = 40 , .tv_usec = 0 }; - FD_ZERO(&fds); - FD_SET(child_in, &fds); - rc = select(child_in + 1, &fds, NULL, NULL, &tv); - if (!rc) { - if (show_debug) - printf("dns[%d]: nobody needs me... =(\n", getpid()); - break; - } - rc = read(child_in, &dns_params, sizeof(dns_params_t)); - if (rc < 0) { - perror("read()"); - break; - } - if (rc == 0) { - if (show_debug) - printf("dns[%d]: Oops, father has gone, wait for me, wait...!\n", getpid()); - _exit(0); - } - if (dns_params.hostname[0] == '\0') { - printf("dns[%d]: hostname = \"\" (port = %d)!!!\n", getpid(), dns_params.port); - _exit(1); - } - /* Tell our parent that we read the data successfully */ - write(child_out, &ch, sizeof(ch)); - - /* We have the hostname and port, now resolve the IP */ - -#ifdef HAVE_GETADDRINFO - g_snprintf(servname, sizeof(servname), "%d", dns_params.port); - memset(&hints, 0, sizeof(hints)); - - /* This is only used to convert a service - * name to a port number. As we know we are - * passing a number already, we know this - * value will not be really used by the C - * library. - */ - hints.ai_socktype = SOCK_STREAM; - rc = getaddrinfo(dns_params.hostname, servname, &hints, &res); - write(child_out, &rc, sizeof(rc)); - if (rc != 0) { - close(child_out); - if (show_debug) - printf("dns[%d] Error: getaddrinfo returned %d\n", - getpid(), rc); - dns_params.hostname[0] = '\0'; - continue; - } - tmp = res; - while (res) { - size_t ai_addrlen = res->ai_addrlen; - write(child_out, &ai_addrlen, sizeof(ai_addrlen)); - write(child_out, res->ai_addr, res->ai_addrlen); - res = res->ai_next; - } - freeaddrinfo(tmp); - write(child_out, &zero, sizeof(zero)); -#else - if (!inet_aton(dns_params.hostname, &sin.sin_addr)) { - struct hostent *hp; - if (!(hp = gethostbyname(dns_params.hostname))) { - write(child_out, &h_errno, sizeof(int)); - close(child_out); - if (show_debug) - printf("DNS Error: %d\n", h_errno); - _exit(0); - } - memset(&sin, 0, sizeof(struct sockaddr_in)); - memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); - sin.sin_family = hp->h_addrtype; - } else - sin.sin_family = AF_INET; - - sin.sin_port = htons(dns_params.port); - write(child_out, &addrlen, sizeof(addrlen)); - write(child_out, &sin, addrlen); - write(child_out, &zero, sizeof(zero)); -#endif - dns_params.hostname[0] = '\0'; - } - - close(child_out); - close(child_in); - - _exit(0); -} - -static pending_dns_request_t * -gaim_dns_new_resolverthread(gboolean show_debug) -{ - pending_dns_request_t *req; - int child_out[2], child_in[2]; - - /* Create pipes for communicating with the child process */ - if (pipe(child_out) || pipe(child_in)) { - gaim_debug_error("dns", - "Could not create pipes: %s\n", strerror(errno)); - return NULL; - } - - req = g_new(pending_dns_request_t, 1); - - cope_with_gdb_brokenness(); - - /* Fork! */ - req->dns_pid = fork(); - - /* If we are the child process... */ - if (req->dns_pid == 0) { - /* We should not access the parent's side of the pipes, so close them */ - close(child_out[0]); - close(child_in[1]); - - gaim_dns_resolverthread(child_out[1], child_in[0], show_debug); - /* The thread calls _exit() rather than returning, so we never get here */ - } - - /* We should not access the child's side of the pipes, so close them */ - close(child_out[1]); - close(child_in[0]); - if (req->dns_pid == -1) { - gaim_debug_error("dns", - "Could not create child process for DNS: %s\n", - strerror(errno)); - g_free(req); - return NULL; - } - - req->fd_out = child_out[0]; - req->fd_in = child_in[1]; - number_of_dns_children++; - gaim_debug_info("dns", - "Created new DNS child %d, there are now %d children.\n", - req->dns_pid, number_of_dns_children); - - return req; -} -/* - * End the DNS resolver child process functions. - */ - -/* - * Begin the functions for dealing with the DNS child processes. - */ -static void -req_free(pending_dns_request_t *req) -{ - g_return_if_fail(req != NULL); - - close(req->fd_in); - close(req->fd_out); - - g_free(req->host); - g_free(req); - - number_of_dns_children--; -} - -static int -send_dns_request_to_child(pending_dns_request_t *req, dns_params_t *dns_params) -{ - char ch; - int rc; - pid_t pid; - - /* This waitpid might return the child's PID if it has recently - * exited, or it might return an error if it exited "long - * enough" ago that it has already been reaped; in either - * instance, we can't use it. */ - if ((pid = waitpid (req->dns_pid, NULL, WNOHANG)) > 0) { - gaim_debug_warning("dns", - "DNS child %d no longer exists\n", req->dns_pid); - return -1; - } else if (pid < 0) { - gaim_debug_warning("dns", - "Wait for DNS child %d failed: %s\n", - req->dns_pid, strerror(errno)); - return -1; - } - - /* Let's contact this lost child! */ - rc = write(req->fd_in, dns_params, sizeof(*dns_params)); - if (rc < 0) { - gaim_debug_error("dns", - "Unable to write to DNS child %d: %d\n", - req->dns_pid, strerror(errno)); - close(req->fd_in); - return -1; - } - - g_return_val_if_fail(rc == sizeof(*dns_params), -1); - - /* Did you hear me? (This avoids some race conditions) */ - rc = read(req->fd_out, &ch, sizeof(ch)); - if (rc != 1 || ch != 'Y') - { - gaim_debug_warning("dns", - "DNS child %d not responding. Killing it!\n", - req->dns_pid); - kill(req->dns_pid, SIGKILL); - return -1; - } - - gaim_debug_info("dns", - "Successfully sent DNS request to child %d\n", req->dns_pid); - - return 0; -} - -static void -host_resolved(gpointer data, gint source, GaimInputCondition cond); - -static void -release_dns_child(pending_dns_request_t *req) -{ - g_free(req->host); - req->host = NULL; - - if (queued_requests && !g_queue_is_empty(queued_requests)) { - queued_dns_request_t *r = g_queue_pop_head(queued_requests); - req->host = g_strdup(r->params.hostname); - req->port = r->params.port; - req->callback = r->callback; - req->data = r->data; - - gaim_debug_info("dns", - "Processing queued DNS query for '%s' with child %d\n", - req->host, req->dns_pid); - - if (send_dns_request_to_child(req, &(r->params)) != 0) { - req_free(req); - req = NULL; - - gaim_debug_warning("dns", - "Intent of process queued query of '%s' failed, " - "requeueing...\n", r->params.hostname); - g_queue_push_head(queued_requests, r); - } else { - req->inpa = gaim_input_add(req->fd_out, GAIM_INPUT_READ, host_resolved, req); - g_free(r); - } - - } else { - req->host = NULL; - req->callback = NULL; - req->data = NULL; - free_dns_children = g_slist_append(free_dns_children, req); - } -} - -static void -host_resolved(gpointer data, gint source, GaimInputCondition cond) -{ - pending_dns_request_t *req = (pending_dns_request_t*)data; - int rc, err; - GSList *hosts = NULL; - struct sockaddr *addr = NULL; - size_t addrlen; - - gaim_debug_info("dns", "Got response for '%s'\n", req->host); - gaim_input_remove(req->inpa); - - rc = read(req->fd_out, &err, sizeof(err)); - if ((rc == 4) && (err != 0)) - { - char message[1024]; -#ifdef HAVE_GETADDRINFO - g_snprintf(message, sizeof(message), "DNS error: %s (pid=%d)", - gai_strerror(err), req->dns_pid); -#else - g_snprintf(message, sizeof(message), "DNS error: %d (pid=%d)", - err, req->dns_pid); -#endif - gaim_debug_error("dns", "%s\n", message); - req->callback(NULL, req->data, message); - release_dns_child(req); - return; - } - if (rc > 0) - { - while (rc > 0) { - rc = read(req->fd_out, &addrlen, sizeof(addrlen)); - if (rc > 0 && addrlen > 0) { - addr = g_malloc(addrlen); - rc = read(req->fd_out, addr, addrlen); - hosts = g_slist_append(hosts, GINT_TO_POINTER(addrlen)); - hosts = g_slist_append(hosts, addr); - } else { - break; - } - } - } else if (rc == -1) { - char message[1024]; - g_snprintf(message, sizeof(message), "Error reading from DNS child: %s",strerror(errno)); - gaim_debug_error("dns", "%s\n", message); - req->callback(NULL, req->data, message); - req_free(req); - return; - } else if (rc == 0) { - char message[1024]; - g_snprintf(message, sizeof(message), "EOF reading from DNS child"); - close(req->fd_out); - gaim_debug_error("dns", "%s\n", message); - req->callback(NULL, req->data, message); - req_free(req); - return; - } - -/* wait4(req->dns_pid, NULL, WNOHANG, NULL); */ - - req->callback(hosts, req->data, NULL); - - release_dns_child(req); -} -/* - * End the functions for dealing with the DNS child processes. - */ - -int -gaim_gethostbyname_async(const char *hostname, int port, GaimProxyDnsConnectFunction callback, gpointer data) -{ - pending_dns_request_t *req = NULL; - dns_params_t dns_params; - gchar *host_temp; - gboolean show_debug; - - show_debug = gaim_debug_is_enabled(); - - host_temp = g_strstrip(g_strdup(hostname)); - strncpy(dns_params.hostname, host_temp, sizeof(dns_params.hostname) - 1); - g_free(host_temp); - dns_params.hostname[sizeof(dns_params.hostname) - 1] = '\0'; - dns_params.port = port; - - /* - * If we have any children, attempt to have them perform the DNS - * query. If we're able to send the query to a child, then req - * will be set to the pending_dns_request_t. Otherwise, req will - * be NULL and we'll need to create a new DNS request child. - */ - while (free_dns_children != NULL) { - req = free_dns_children->data; - free_dns_children = g_slist_remove(free_dns_children, req); - - if (send_dns_request_to_child(req, &dns_params) == 0) - /* We found an acceptable child, yay */ - break; - - req_free(req); - req = NULL; - } - - /* We need to create a new DNS request child */ - if (req == NULL) { - if (number_of_dns_children >= MAX_DNS_CHILDREN) { - queued_dns_request_t *r = g_new(queued_dns_request_t, 1); - memcpy(&(r->params), &dns_params, sizeof(dns_params)); - r->callback = callback; - r->data = data; - if (!queued_requests) - queued_requests = g_queue_new(); - g_queue_push_tail(queued_requests, r); - - gaim_debug_info("dns", - "DNS query for '%s' queued\n", dns_params.hostname); - - return 0; - } - - req = gaim_dns_new_resolverthread(show_debug); - if (req == NULL) - { - gaim_debug_error("proxy", "oh dear, this is going to explode, I give up\n"); - return -1; - } - send_dns_request_to_child(req, &dns_params); - } - - req->host = g_strdup(hostname); - req->port = port; - req->callback = callback; - req->data = data; - req->inpa = gaim_input_add(req->fd_out, GAIM_INPUT_READ, host_resolved, req); - - return 0; -} - -#elif defined _WIN32 /* end __unix__ || __APPLE__ */ - -typedef struct _dns_tdata { - char *hostname; - int port; - GaimProxyDnsConnectFunction callback; - gpointer data; - GSList *hosts; - char *errmsg; -} dns_tdata; - -static gboolean dns_main_thread_cb(gpointer data) { - dns_tdata *td = (dns_tdata*)data; - if (td->errmsg != NULL) { - gaim_debug_info("dns", "%s\n", td->errmsg); - } - td->callback(td->hosts, td->data, td->errmsg); - g_free(td->hostname); - g_free(td->errmsg); - g_free(td); - return FALSE; -} - -static gpointer dns_thread(gpointer data) { - -#ifdef HAVE_GETADDRINFO - int rc; - struct addrinfo hints, *res, *tmp; - char servname[20]; -#else - struct sockaddr_in sin; - struct hostent *hp; -#endif - dns_tdata *td = (dns_tdata*)data; - -#ifdef HAVE_GETADDRINFO - g_snprintf(servname, sizeof(servname), "%d", td->port); - memset(&hints,0,sizeof(hints)); - - /* This is only used to convert a service - * name to a port number. As we know we are - * passing a number already, we know this - * value will not be really used by the C - * library. - */ - hints.ai_socktype = SOCK_STREAM; - if ((rc = getaddrinfo(td->hostname, servname, &hints, &res)) == 0) { - tmp = res; - while(res) { - td->hosts = g_slist_append(td->hosts, - GSIZE_TO_POINTER(res->ai_addrlen)); - td->hosts = g_slist_append(td->hosts, - g_memdup(res->ai_addr, res->ai_addrlen)); - res = res->ai_next; - } - freeaddrinfo(tmp); - } else { - td->errmsg = g_strdup_printf("DNS getaddrinfo(\"%s\", \"%s\") error: %d", td->hostname, servname, rc); - } -#else - if ((hp = gethostbyname(td->hostname))) { - memset(&sin, 0, sizeof(struct sockaddr_in)); - memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); - sin.sin_family = hp->h_addrtype; - sin.sin_port = htons(td->port); - - td->hosts = g_slist_append(td->hosts, - GSIZE_TO_POINTER(sizeof(sin))); - td->hosts = g_slist_append(td->hosts, - g_memdup(&sin, sizeof(sin))); - } else { - td->errmsg = g_strdup_printf("DNS gethostbyname(\"%s\") error: %d", td->hostname, h_errno); - } -#endif - /* back to main thread */ - g_idle_add(dns_main_thread_cb, td); - return 0; -} - -int -gaim_gethostbyname_async(const char *hostname, int port, - GaimProxyDnsConnectFunction callback, gpointer data) -{ - dns_tdata *td; - struct sockaddr_in sin; - GError* err = NULL; - - if(inet_aton(hostname, &sin.sin_addr)) { - GSList *hosts = NULL; - sin.sin_family = AF_INET; - sin.sin_port = htons(port); - hosts = g_slist_append(hosts, GINT_TO_POINTER(sizeof(sin))); - hosts = g_slist_append(hosts, g_memdup(&sin, sizeof(sin))); - callback(hosts, data, NULL); - return 0; - } - - gaim_debug_info("dns", "DNS Lookup for: %s\n", hostname); - td = g_new0(dns_tdata, 1); - td->hostname = g_strdup(hostname); - td->port = port; - td->callback = callback; - td->data = data; - - if(!g_thread_create(dns_thread, td, FALSE, &err)) { - gaim_debug_error("dns", "DNS thread create failure: %s\n", err?err->message:""); - g_error_free(err); - g_free(td->hostname); - g_free(td); - return -1; - } - return 0; -} - -#else /* not __unix__ or __APPLE__ or _WIN32 */ - -typedef struct { - gpointer data; - size_t addrlen; - struct sockaddr *addr; - GaimProxyDnsConnectFunction callback; -} pending_dns_request_t; - -static gboolean host_resolved(gpointer data) -{ - pending_dns_request_t *req = (pending_dns_request_t*)data; - GSList *hosts = NULL; - hosts = g_slist_append(hosts, GINT_TO_POINTER(req->addrlen)); - hosts = g_slist_append(hosts, req->addr); - req->callback(hosts, req->data, NULL); - g_free(req); - return FALSE; -} - -int -gaim_gethostbyname_async(const char *hostname, int port, - GaimProxyDnsConnectFunction callback, gpointer data) -{ - struct sockaddr_in sin; - pending_dns_request_t *req; - - if (!inet_aton(hostname, &sin.sin_addr)) { - struct hostent *hp; - if(!(hp = gethostbyname(hostname))) { - gaim_debug_error("dns", - "gaim_gethostbyname(\"%s\", %d) failed: %d\n", - hostname, port, h_errno); - return -1; - } - memset(&sin, 0, sizeof(struct sockaddr_in)); - memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); - sin.sin_family = hp->h_addrtype; - } else - sin.sin_family = AF_INET; - sin.sin_port = htons(port); - - req = g_new(pending_dns_request_t, 1); - req->addr = (struct sockaddr*) g_memdup(&sin, sizeof(sin)); - req->addrlen = sizeof(sin); - req->data = data; - req->callback = callback; - gaim_timeout_add(10, host_resolved, req); - return 0; -} - -#endif /* not __unix__ or __APPLE__ or _WIN32 */ - -static void no_one_calls(gpointer data, gint source, GaimInputCondition cond) { GaimProxyConnectInfo *connect_info = data; @@ -2368,8 +1701,9 @@ return NULL; } - if (gaim_gethostbyname_async(connecthost, - connectport, connection_host_resolved, connect_info) != 0) + connect_info->query_data = gaim_dnsquery_a(connecthost, + connectport, connection_host_resolved, connect_info); + if (connect_info->query_data == NULL) { gaim_proxy_connect_info_destroy(connect_info); return NULL; @@ -2401,8 +1735,9 @@ connect_info->port = port; connect_info->gpi = gpi; - if (gaim_gethostbyname_async(gaim_proxy_info_get_host(gpi), - gaim_proxy_info_get_port(gpi), connection_host_resolved, connect_info) != 0) + connect_info->query_data = gaim_dnsquery_a(gaim_proxy_info_get_host(gpi), + gaim_proxy_info_get_port(gpi), connection_host_resolved, connect_info); + if (connect_info->query_data == NULL) { gaim_proxy_connect_info_destroy(connect_info); return NULL; Modified: trunk/src/proxy.h =================================================================== --- trunk/src/proxy.h 2006-08-17 06:21:28 UTC (rev 16810) +++ trunk/src/proxy.h 2006-08-17 07:44:52 UTC (rev 16811) @@ -26,6 +26,7 @@ #define _GAIM_PROXY_H_ #include <glib.h> +#include "dnsquery.h" #include "eventloop.h" /** @@ -56,6 +57,7 @@ } GaimProxyInfo; +typedef struct _GaimDnsQueryData GaimDnsQueryData; typedef struct _GaimProxyConnectInfo GaimProxyConnectInfo; typedef void (*GaimProxyConnectFunction)(gpointer data, gint source, const gchar *error_message); @@ -278,18 +280,6 @@ */ void gaim_proxy_connect_cancel(GaimProxyConnectInfo *connect_info); -/** - * Do an async dns query - * - * @param hostname The hostname to resolve - * @param port A portnumber which is stored in the struct sockaddr - * @param callback Callback to call after resolving - * @param data Extra data for the callback function - * - * @return Zero indicates the connection is pending. Any other value indicates failure. - */ -int gaim_gethostbyname_async(const char *hostname, int port, GaimProxyDnsConnectFunction callback, gpointer data); - /*@}*/ #ifdef __cplusplus Modified: trunk/src/stun.c =================================================================== --- trunk/src/stun.c 2006-08-17 06:21:28 UTC (rev 16810) +++ trunk/src/stun.c 2006-08-17 07:44:52 UTC (rev 16811) @@ -40,6 +40,7 @@ #include "debug.h" #include "account.h" +#include "dnsquery.h" #include "dnssrv.h" #include "network.h" #include "proxy.h" @@ -359,7 +360,7 @@ gaim_debug_info("stun", "got %d SRV responses, server: %s, port: %d\n", results, servername, port); - gaim_gethostbyname_async(servername, port, hbn_cb, NULL); + gaim_dnsquery_a(servername, port, hbn_cb, NULL); g_free(resp); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <the...@us...> - 2006-08-17 10:04:31
|
Revision: 16814 Author: thekingant Date: 2006-08-17 03:04:21 -0700 (Thu, 17 Aug 2006) ViewCVS: http://svn.sourceforge.net/gaim/?rev=16814&view=rev Log Message: ----------- Revert SVN revision 16811. Kevin says it's crashing for him and I'm tired. ViewCVS Links: ------------- http://svn.sourceforge.net/gaim/?rev=16811&view=rev Modified Paths: -------------- trunk/src/Makefile.am trunk/src/Makefile.mingw trunk/src/protocols/simple/simple.c trunk/src/proxy.c trunk/src/proxy.h trunk/src/stun.c Removed Paths: ------------- trunk/src/dnsquery.c trunk/src/dnsquery.h Modified: trunk/src/Makefile.am =================================================================== --- trunk/src/Makefile.am 2006-08-17 10:04:19 UTC (rev 16813) +++ trunk/src/Makefile.am 2006-08-17 10:04:21 UTC (rev 16814) @@ -77,7 +77,6 @@ core.c \ debug.c \ desktopitem.c \ - dnsquery.c \ eventloop.c \ ft.c \ idle.c \ @@ -126,7 +125,6 @@ dbus-maybe.h \ debug.h \ desktopitem.h \ - dnsquery.h \ eventloop.h \ ft.h \ idle.h \ Modified: trunk/src/Makefile.mingw =================================================================== --- trunk/src/Makefile.mingw 2006-08-17 10:04:19 UTC (rev 16813) +++ trunk/src/Makefile.mingw 2006-08-17 10:04:21 UTC (rev 16814) @@ -95,7 +95,6 @@ conversation.c \ core.c \ debug.c \ - dnsquery.c \ dnssrv.c \ eventloop.c \ ft.c \ Deleted: trunk/src/dnsquery.c =================================================================== --- trunk/src/dnsquery.c 2006-08-17 10:04:19 UTC (rev 16813) +++ trunk/src/dnsquery.c 2006-08-17 10:04:21 UTC (rev 16814) @@ -1,774 +0,0 @@ -/** - * @file dnsquery.c DNS query API - * @ingroup core - * - * gaim - * - * Gaim is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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 - * - */ - -#include "internal.h" -#include "debug.h" -#include "ntlm.h" -#include "util.h" - -struct _GaimDnsqueryData { -}; - -/************************************************************************** - * Global DNS query API - **************************************************************************/ - -#if defined(__unix__) || defined(__APPLE__) - -/* - * This structure represents both a pending DNS request and - * a free child process. - */ -typedef struct _DnsqueryResolverprocess DnsqueryResolverprocess; -struct _DnsqueryResolverprocess { - char *host; - int port; - GaimProxyDnsConnectFunction callback; - gpointer data; - guint inpa; - int fd_in, fd_out; - pid_t dns_pid; -}; - -//static GSList *all_dns_children = NULL; -static GSList *free_dns_children = NULL; -static GQueue *queued_requests = NULL; - -static int number_of_dns_children = 0; - -static const int MAX_DNS_CHILDREN = 2; - -typedef struct { - char hostname[512]; - int port; -} dns_params_t; - -typedef struct { - dns_params_t params; - GaimProxyDnsConnectFunction callback; - gpointer data; -} queued_dns_request_t; - -/* - * Begin the DNS resolver child process functions. - */ -#ifdef HAVE_SIGNAL_H -static void -trap_gdb_bug() -{ - const char *message = - "Gaim's DNS child got a SIGTRAP signal.\n" - "This can be caused by trying to run gaim inside gdb.\n" - "There is a known gdb bug which prevents this. Supposedly gaim\n" - "should have detected you were using gdb and used an ugly hack,\n" - "check cope_with_gdb_brokenness() in dnsquery.c.\n\n" - "For more info about this bug, see http://sources.redhat.com/ml/gdb/2001-07/msg00349.html\n"; - fputs("\n* * *\n",stderr); - fputs(message,stderr); - fputs("* * *\n\n",stderr); - execlp("xmessage","xmessage","-center", message, NULL); - _exit(1); -} -#endif - -static void -cope_with_gdb_brokenness() -{ -#ifdef __linux__ - static gboolean already_done = FALSE; - char s[256], e[512]; - int n; - pid_t ppid; - - if (already_done) - return; - - already_done = TRUE; - ppid = getppid(); - snprintf(s, sizeof(s), "/proc/%d/exe", ppid); - n = readlink(s, e, sizeof(e)); - if (n < 0) - return; - - e[MIN(n,sizeof(e)-1)] = '\0'; - - if (strstr(e,"gdb")) - { - gaim_debug_info("dnsquery", - "Debugger detected, performing useless query...\n"); - gethostbyname("x.x.x.x.x"); - } -#endif -} - -/** - * When doing DNS queries on Unix and OS-X, we fork off a separate - * process and communicate with it using pipes. This function is - * called shortly after the child is forked, and the function exits - * the process after it's no longer needed. - */ -static void -gaim_dns_resolverprocess(int child_out, int child_in, gboolean show_debug) -{ - dns_params_t dns_params; - const size_t zero = 0; - int rc; -#ifdef HAVE_GETADDRINFO - struct addrinfo hints, *res, *tmp; - char servname[20]; -#else - struct sockaddr_in sin; - const size_t addrlen = sizeof(sin); -#endif - -#ifdef HAVE_SIGNAL_H - signal(SIGHUP, SIG_DFL); - signal(SIGINT, SIG_DFL); - signal(SIGQUIT, SIG_DFL); - signal(SIGCHLD, SIG_DFL); - signal(SIGTERM, SIG_DFL); - signal(SIGTRAP, trap_gdb_bug); -#endif - - /* - * We resolve 1 host name for each iteration of this - * while loop. - * - * The top half of this reads in the hostname and port - * number from the socket with our parent. The bottom - * half of this resolves the IP (blocking) and sends - * the result back to our parent, when finished. - */ - while (1) - { - const char ch = 'Y'; - fd_set fds; - struct timeval tv = { .tv_sec = 40 , .tv_usec = 0 }; - - FD_ZERO(&fds); - FD_SET(child_in, &fds); - rc = select(child_in + 1, &fds, NULL, NULL, &tv); - if (rc < 0) { - if (show_debug) - printf("dns[%d]: select failed\n", getpid()); - close(child_out); - close(child_in); - _exit(1); - } - if (rc == 0) { - if (show_debug) - printf("dns[%d]: nobody needs me... =(\n", getpid()); - break; - } - - rc = read(child_in, &dns_params, sizeof(dns_params_t)); - if (rc < 0) { - if (show_debug) - printf("dns[%d]: read failed\n", getpid()); - close(child_out); - close(child_in); - _exit(1); - } - if (rc == 0) { - if (show_debug) - printf("dns[%d]: Oops, father has gone, wait for me, wait...!\n", getpid()); - break; - } - if (dns_params.hostname[0] == '\0') { - printf("dns[%d]: hostname = \"\" (port = %d)!!!\n", getpid(), dns_params.port); - close(child_out); - close(child_in); - _exit(1); - } - /* Tell our parent that we read the data successfully */ - write(child_out, &ch, sizeof(ch)); - - /* We have the hostname and port, now resolve the IP */ - -#ifdef HAVE_GETADDRINFO - g_snprintf(servname, sizeof(servname), "%d", dns_params.port); - memset(&hints, 0, sizeof(hints)); - - /* - * This is only used to convert a service - * name to a port number. As we know we are - * passing a number already, we know this - * value will not be really used by the C - * library. - */ - hints.ai_socktype = SOCK_STREAM; - rc = getaddrinfo(dns_params.hostname, servname, &hints, &res); - write(child_out, &rc, sizeof(rc)); - if (rc != 0) { - close(child_out); - if (show_debug) - printf("dns[%d] Error: getaddrinfo returned %d\n", - getpid(), rc); - dns_params.hostname[0] = '\0'; - continue; - } - for (tmp = res; tmp != NULL; tmp = tmp->ai_next) - { - size_t ai_addrlen = tmp->ai_addrlen; - write(child_out, &ai_addrlen, sizeof(ai_addrlen)); - write(child_out, tmp->ai_addr, tmp->ai_addrlen); - } - freeaddrinfo(res); - write(child_out, &zero, sizeof(zero)); -#else - if (!inet_aton(dns_params.hostname, &sin.sin_addr)) - { - struct hostent *hp; - - hp = gethostbyname(dns_params.hostname); - if (hp == NULL) - { - if (show_debug) - printf("DNS Error: %d\n", h_errno); - write(child_out, &h_errno, sizeof(int)); - close(child_out); - close(child_in); - _exit(1); - } - memset(&sin, 0, sizeof(struct sockaddr_in)); - memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); - sin.sin_family = hp->h_addrtype; - } else - sin.sin_family = AF_INET; - - sin.sin_port = htons(dns_params.port); - write(child_out, &addrlen, sizeof(addrlen)); - write(child_out, &sin, addrlen); - write(child_out, &zero, sizeof(zero)); -#endif - dns_params.hostname[0] = '\0'; - } - - close(child_out); - close(child_in); - - _exit(0); -} - -static DnsqueryResolverprocess * -gaim_dns_new_resolverprocess(gboolean show_debug) -{ - DnsqueryResolverprocess *req; - int child_out[2], child_in[2]; - - /* Create pipes for communicating with the child process */ - if (pipe(child_out) || pipe(child_in)) { - gaim_debug_error("dnsquery", - "Could not create pipes: %s\n", strerror(errno)); - return NULL; - } - - req = g_new(DnsqueryResolverprocess, 1); - - cope_with_gdb_brokenness(); - - /* Fork! */ - req->dns_pid = fork(); - - /* If we are the child process... */ - if (req->dns_pid == 0) { - /* We should not access the parent's side of the pipes, so close them */ - close(child_out[0]); - close(child_in[1]); - - gaim_dns_resolverprocess(child_out[1], child_in[0], show_debug); - /* The thread calls _exit() rather than returning, so we never get here */ - } - - /* We should not access the child's side of the pipes, so close them */ - close(child_out[1]); - close(child_in[0]); - if (req->dns_pid == -1) { - gaim_debug_error("dnsquery", - "Could not create child process for DNS: %s\n", - strerror(errno)); - g_free(req); - return NULL; - } - - req->fd_out = child_out[0]; - req->fd_in = child_in[1]; - number_of_dns_children++; - gaim_debug_info("dnsquery", - "Created new DNS child %d, there are now %d children.\n", - req->dns_pid, number_of_dns_children); - - return req; -} -/* - * End the DNS resolver child process functions. - */ - -/* - * Begin the functions for dealing with the DNS child processes. - */ -static void -req_free(DnsqueryResolverprocess *req) -{ - g_return_if_fail(req != NULL); - - close(req->fd_in); - close(req->fd_out); - - g_free(req->host); - g_free(req); - - number_of_dns_children--; -} - -static int -send_dns_request_to_child(DnsqueryResolverprocess *req, dns_params_t *dns_params) -{ - char ch; - int rc; - pid_t pid; - - /* This waitpid might return the child's PID if it has recently - * exited, or it might return an error if it exited "long - * enough" ago that it has already been reaped; in either - * instance, we can't use it. */ - if ((pid = waitpid (req->dns_pid, NULL, WNOHANG)) > 0) { - gaim_debug_warning("dnsquery", - "DNS child %d no longer exists\n", req->dns_pid); - return -1; - } else if (pid < 0) { - gaim_debug_warning("dnsquery", - "Wait for DNS child %d failed: %s\n", - req->dns_pid, strerror(errno)); - return -1; - } - - /* Let's contact this lost child! */ - rc = write(req->fd_in, dns_params, sizeof(*dns_params)); - if (rc < 0) { - gaim_debug_error("dnsquery", - "Unable to write to DNS child %d: %d\n", - req->dns_pid, strerror(errno)); - close(req->fd_in); - return -1; - } - - g_return_val_if_fail(rc == sizeof(*dns_params), -1); - - /* Did you hear me? (This avoids some race conditions) */ - rc = read(req->fd_out, &ch, sizeof(ch)); - if (rc != 1 || ch != 'Y') - { - gaim_debug_warning("dnsquery", - "DNS child %d not responding. Killing it!\n", - req->dns_pid); - kill(req->dns_pid, SIGKILL); - return -1; - } - - gaim_debug_info("dnsquery", - "Successfully sent DNS request to child %d\n", req->dns_pid); - - return 0; -} - -static void -host_resolved(gpointer data, gint source, GaimInputCondition cond); - -static void -release_dns_child(DnsqueryResolverprocess *req) -{ - g_free(req->host); - req->host = NULL; - - if (queued_requests && !g_queue_is_empty(queued_requests)) { - queued_dns_request_t *r = g_queue_pop_head(queued_requests); - req->host = g_strdup(r->params.hostname); - req->port = r->params.port; - req->callback = r->callback; - req->data = r->data; - - gaim_debug_info("dnsquery", - "Processing queued DNS query for '%s' with child %d\n", - req->host, req->dns_pid); - - if (send_dns_request_to_child(req, &(r->params)) != 0) { - req_free(req); - req = NULL; - - gaim_debug_warning("dnsquery", - "Intent of process queued query of '%s' failed, " - "requeueing...\n", r->params.hostname); - g_queue_push_head(queued_requests, r); - } else { - req->inpa = gaim_input_add(req->fd_out, GAIM_INPUT_READ, host_resolved, req); - g_free(r); - } - - } else { - req->host = NULL; - req->callback = NULL; - req->data = NULL; - free_dns_children = g_slist_append(free_dns_children, req); - } -} - -static void -host_resolved(gpointer data, gint source, GaimInputCondition cond) -{ - DnsqueryResolverprocess *req = (DnsqueryResolverprocess*)data; - int rc, err; - GSList *hosts = NULL; - struct sockaddr *addr = NULL; - size_t addrlen; - - gaim_debug_info("dnsquery", "Got response for '%s'\n", req->host); - gaim_input_remove(req->inpa); - - rc = read(req->fd_out, &err, sizeof(err)); - if ((rc == 4) && (err != 0)) - { - gchar *message; -#ifdef HAVE_GETADDRINFO - message = g_strdup_printf("DNS error: %s (pid=%d)", - gai_strerror(err), req->dns_pid); -#else - message = g_strdup_printf("DNS error: %d (pid=%d)", - err, req->dns_pid); -#endif - gaim_debug_error("dnsquery", "%s\n", message); - req->callback(NULL, req->data, message); - g_free(message); - release_dns_child(req); - return; - } - - if (rc > 0) - { - while (rc > 0) { - rc = read(req->fd_out, &addrlen, sizeof(addrlen)); - if (rc > 0 && addrlen > 0) { - addr = g_malloc(addrlen); - rc = read(req->fd_out, addr, addrlen); - hosts = g_slist_append(hosts, GINT_TO_POINTER(addrlen)); - hosts = g_slist_append(hosts, addr); - } else { - break; - } - } - } else if (rc == -1) { - gchar *message; - message = g_strdup_printf("Error reading from DNS child: %s", strerror(errno)); - gaim_debug_error("dnsquery", "%s\n", message); - req->callback(NULL, req->data, message); - g_free(message); - req_free(req); - return; - } else if (rc == 0) { - gchar *message; - close(req->fd_out); - message = g_strdup_printf("EOF reading from DNS child"); - gaim_debug_error("dnsquery", "%s\n", message); - req->callback(NULL, req->data, message); - g_free(message); - req_free(req); - return; - } - -/* wait4(req->dns_pid, NULL, WNOHANG, NULL); */ - - req->callback(hosts, req->data, NULL); - - release_dns_child(req); -} -/* - * End the functions for dealing with the DNS child processes. - */ - -GaimDnsqueryData * -gaim_dnsquery_a(const char *hostname, int port, GaimDnsqueryConnectFunction callback, gpointer data) -{ - GaimDnsqueryData *query_data; - DnsqueryResolverprocess *req = NULL; - dns_params_t dns_params; - gchar *host_temp; - gboolean show_debug; - - show_debug = gaim_debug_is_enabled(); - - host_temp = g_strstrip(g_strdup(hostname)); - strncpy(dns_params.hostname, host_temp, sizeof(dns_params.hostname) - 1); - g_free(host_temp); - dns_params.hostname[sizeof(dns_params.hostname) - 1] = '\0'; - dns_params.port = port; - - /* - * If we have any children, attempt to have them perform the DNS - * query. If we're able to send the query to a child, then req - * will be set to the DnsqueryResolverprocess. Otherwise, req will - * be NULL and we'll need to create a new DNS request child. - */ - while (free_dns_children != NULL) - { - req = free_dns_children->data; - free_dns_children = g_slist_remove(free_dns_children, req); - - if (send_dns_request_to_child(req, &dns_params) == 0) - /* We found an acceptable child, yay */ - break; - - req_free(req); - req = NULL; - } - - /* We need to create a new DNS request child */ - if (req == NULL) - { - if (number_of_dns_children >= MAX_DNS_CHILDREN) - { - queued_dns_request_t *r = g_new(queued_dns_request_t, 1); - memcpy(&(r->params), &dns_params, sizeof(dns_params)); - r->callback = callback; - r->data = data; - if (!queued_requests) - queued_requests = g_queue_new(); - g_queue_push_tail(queued_requests, r); - - gaim_debug_info("dnsquery", - "DNS query for '%s' queued\n", dns_params.hostname); - - return query_data; - } - - req = gaim_dns_new_resolverprocess(show_debug); - if (req == NULL) - { - gaim_debug_error("dnsquery", "oh dear, this is going to explode, I give up\n"); - return NULL; - } - send_dns_request_to_child(req, &dns_params); - } - - req->host = g_strdup(hostname); - req->port = port; - req->callback = callback; - req->data = data; - req->inpa = gaim_input_add(req->fd_out, GAIM_INPUT_READ, host_resolved, req); - - return query_data; -} - -#elif defined _WIN32 /* end __unix__ || __APPLE__ */ - -typedef struct _dns_tdata { - char *hostname; - int port; - GaimProxyDnsConnectFunction callback; - gpointer data; - GSList *hosts; - char *errmsg; -} dns_tdata; - -static gboolean dns_main_thread_cb(gpointer data) -{ - dns_tdata *td = (dns_tdata*)data; - if (td->errmsg != NULL) { - gaim_debug_info("dnsquery", "%s\n", td->errmsg); - } - td->callback(td->hosts, td->data, td->errmsg); - g_free(td->hostname); - g_free(td->errmsg); - g_free(td); - return FALSE; -} - -static gpointer -dns_thread(gpointer data) -{ - -#ifdef HAVE_GETADDRINFO - int rc; - struct addrinfo hints, *res, *tmp; - char servname[20]; -#else - struct sockaddr_in sin; - struct hostent *hp; -#endif - dns_tdata *td = (dns_tdata*)data; - -#ifdef HAVE_GETADDRINFO - g_snprintf(servname, sizeof(servname), "%d", td->port); - memset(&hints, 0, sizeof(hints)); - - /* This is only used to convert a service - * name to a port number. As we know we are - * passing a number already, we know this - * value will not be really used by the C - * library. - */ - hints.ai_socktype = SOCK_STREAM; - rc = getaddrinfo(td->hostname, servname, &hints, &res); - if (rc == 0) - { - tmp = res; - while(res) { - td->hosts = g_slist_append(td->hosts, - GSIZE_TO_POINTER(res->ai_addrlen)); - td->hosts = g_slist_append(td->hosts, - g_memdup(res->ai_addr, res->ai_addrlen)); - res = res->ai_next; - } - freeaddrinfo(tmp); - } - else - { - td->errmsg = g_strdup_printf("DNS getaddrinfo(\"%s\", \"%s\") error: %d", td->hostname, servname, rc); - } -#else - hp = gethostbyname(td->hostname); - if (hp != NULL) - { - memset(&sin, 0, sizeof(struct sockaddr_in)); - memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); - sin.sin_family = hp->h_addrtype; - sin.sin_port = htons(td->port); - - td->hosts = g_slist_append(td->hosts, - GSIZE_TO_POINTER(sizeof(sin))); - td->hosts = g_slist_append(td->hosts, - g_memdup(&sin, sizeof(sin))); - } else { - td->errmsg = g_strdup_printf("DNS gethostbyname(\"%s\") error: %d", td->hostname, h_errno); - } -#endif - /* back to main thread */ - g_idle_add(dns_main_thread_cb, td); - - return 0; -} - -GaimDnsqueryData * -gaim_dnsquery_a(const char *hostname, int port, - GaimProxyDnsConnectFunction callback, gpointer data) -{ - GaimDnsqueryData *query_data; - dns_tdata *td; - struct sockaddr_in sin; - GError* err = NULL; - - if (inet_aton(hostname, &sin.sin_addr)) - { - GSList *hosts = NULL; - sin.sin_family = AF_INET; - sin.sin_port = htons(port); - hosts = g_slist_append(hosts, GINT_TO_POINTER(sizeof(sin))); - hosts = g_slist_append(hosts, g_memdup(&sin, sizeof(sin))); - callback(hosts, data, NULL); - return query_data; - } - - gaim_debug_info("dnsquery", "DNS Lookup for: %s\n", hostname); - td = g_new0(dns_tdata, 1); - td->hostname = g_strdup(hostname); - td->port = port; - td->callback = callback; - td->data = data; - - if (!g_thread_create(dns_thread, td, FALSE, &err)) - { - gaim_debug_error("dnsquery", "DNS thread create failure: %s\n", err?err->message:""); - g_error_free(err); - g_free(td->hostname); - g_free(td); - return NULL; - } - - return query_data; -} - -#else /* not __unix__ or __APPLE__ or _WIN32 */ - -typedef struct { - gpointer data; - size_t addrlen; - struct sockaddr *addr; - GaimProxyDnsConnectFunction callback; -} DnsqueryResolverprocess; - -static gboolean host_resolved(gpointer data) -{ - DnsqueryResolverprocess *req = (DnsqueryResolverprocess*)data; - GSList *hosts = NULL; - hosts = g_slist_append(hosts, GINT_TO_POINTER(req->addrlen)); - hosts = g_slist_append(hosts, req->addr); - req->callback(hosts, req->data, NULL); - g_free(req); - return FALSE; -} - -GaimDnsqueryData * -gaim_dnsquery_a(const char *hostname, int port, - GaimProxyDnsConnectFunction callback, gpointer data) -{ - GaimDnsqueryData *query_data; - struct sockaddr_in sin; - DnsqueryResolverprocess *req; - - if (!inet_aton(hostname, &sin.sin_addr)) - { - struct hostent *hp; - hp = gethostbyname(hostname); - if (hp == NULL) - { - gaim_debug_error("dnsquery", - "gaim_gethostbyname(\"%s\", %d) failed: %d\n", - hostname, port, h_errno); - return NULL; - } - memset(&sin, 0, sizeof(struct sockaddr_in)); - memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); - sin.sin_family = hp->h_addrtype; - } else - sin.sin_family = AF_INET; - sin.sin_port = htons(port); - - req = g_new(DnsqueryResolverprocess, 1); - req->addr = (struct sockaddr*) g_memdup(&sin, sizeof(sin)); - req->addrlen = sizeof(sin); - req->data = data; - req->callback = callback; - gaim_timeout_add(10, host_resolved, req); - - return query_data; -} - -#endif /* not __unix__ or __APPLE__ or _WIN32 */ - -void -gaim_dnsquery_destroy(GaimDnsqueryData *query_data) -{ - g_free(query_data); -} Deleted: trunk/src/dnsquery.h =================================================================== --- trunk/src/dnsquery.h 2006-08-17 10:04:19 UTC (rev 16813) +++ trunk/src/dnsquery.h 2006-08-17 10:04:21 UTC (rev 16814) @@ -1,76 +0,0 @@ -/** - * @file dnsquery.h DNS query API - * @ingroup core - * - * gaim - * - * Gaim is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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 - */ -#ifndef _GAIM_DNSQUERY_H_ -#define _GAIM_DNSQUERY_H_ - -#include <glib.h> - -typedef struct _GaimDnsqueryData GaimDnsqueryData; - -/** - * The "hosts" parameter is a linked list containing pairs of - * one size_t addrlen and one struct sockaddr *addr. - */ -typedef void (*GaimDnsqueryConnectFunction)(GSList *hosts, gpointer data, const char *error_message); - - -#ifdef __cplusplus -extern "C" { -#endif - -/**************************************************************************/ -/** @name DNS query API */ -/**************************************************************************/ -/*@{*/ - -/** - * Do an asynchronous DNS query. - * - * @param hostname The hostname to resolve - * @param port A portnumber which is stored in the struct sockaddr - * @param callback Callback to call after resolving - * @param data Extra data for the callback function - * - * @return NULL if there was an error, or a reference to a data - * structure that can be used to cancel the pending - * connection, if needed. - */ -GaimDnsqueryData *gaim_dnsquery_a(const char *hostname, int port, GaimDnsqueryConnectFunction callback, gpointer data); - -/** - * Cancel a DNS query. - * - * @param query_data A pointer to the DNS query data that you want - * to cancel. - */ -void gaim_dnsquery_destroy(GaimDnsqueryData *query_data); - -/*@}*/ - -#ifdef __cplusplus -} -#endif - -#endif /* _GAIM_DNSQUERY_H_ */ Modified: trunk/src/protocols/simple/simple.c =================================================================== --- trunk/src/protocols/simple/simple.c 2006-08-17 10:04:19 UTC (rev 16813) +++ trunk/src/protocols/simple/simple.c 2006-08-17 10:04:21 UTC (rev 16814) @@ -30,7 +30,6 @@ #include "blist.h" #include "conversation.h" #include "debug.h" -#include "dnsquery.h" #include "notify.h" #include "privacy.h" #include "prpl.h" @@ -1622,7 +1621,7 @@ } else { /* UDP */ gaim_debug_info("simple", "using udp with server %s and port %d\n", hostname, port); - gaim_dnsquery_a(hostname, port, simple_udp_host_resolved, sip); + gaim_gethostbyname_async(hostname, port, simple_udp_host_resolved, sip); } } Modified: trunk/src/proxy.c =================================================================== --- trunk/src/proxy.c 2006-08-17 10:04:19 UTC (rev 16813) +++ trunk/src/proxy.c 2006-08-17 10:04:21 UTC (rev 16814) @@ -31,7 +31,6 @@ #include "internal.h" #include "cipher.h" -#include "dnsquery.h" #include "debug.h" #include "notify.h" #include "ntlm.h" @@ -47,7 +46,6 @@ int fd; guint inpa; GaimProxyInfo *gpi; - GaimDnsqueryData *query_data; /** * This contains alternating length/char* values. The char* @@ -309,11 +307,6 @@ connect_infos = g_slist_remove(connect_infos, connect_info); - /* - if (connect_info->query_data != NULL) - gaim_dnsquery_destroy(connect_info->query_data); - */ - while (connect_info->hosts != NULL) { /* Discard the length... */ @@ -358,7 +351,681 @@ gaim_proxy_connect_info_destroy(connect_info); } +#if defined(__unix__) || defined(__APPLE__) + +/* + * This structure represents both a pending DNS request and + * a free child process. + */ +typedef struct { + char *host; + int port; + GaimProxyDnsConnectFunction callback; + gpointer data; + guint inpa; + int fd_in, fd_out; + pid_t dns_pid; +} pending_dns_request_t; + +static GSList *free_dns_children = NULL; +static GQueue *queued_requests = NULL; + +static int number_of_dns_children = 0; + +static const int MAX_DNS_CHILDREN = 2; + +typedef struct { + char hostname[512]; + int port; +} dns_params_t; + +typedef struct { + dns_params_t params; + GaimProxyDnsConnectFunction callback; + gpointer data; +} queued_dns_request_t; + +/* + * Begin the DNS resolver child process functions. + */ +#ifdef HAVE_SIGNAL_H static void +trap_gdb_bug() +{ + const char *message = + "Gaim's DNS child got a SIGTRAP signal.\n" + "This can be caused by trying to run gaim inside gdb.\n" + "There is a known gdb bug which prevents this. Supposedly gaim\n" + "should have detected you were using gdb and used an ugly hack,\n" + "check cope_with_gdb_brokenness() in proxy.c.\n\n" + "For more info about this bug, see http://sources.redhat.com/ml/gdb/2001-07/msg00349.html\n"; + fputs("\n* * *\n",stderr); + fputs(message,stderr); + fputs("* * *\n\n",stderr); + execlp("xmessage","xmessage","-center", message, NULL); + _exit(1); +} +#endif + +static void +cope_with_gdb_brokenness() +{ +#ifdef __linux__ + static gboolean already_done = FALSE; + char s[256], e[512]; + int n; + pid_t ppid; + + if(already_done) + return; + already_done = TRUE; + ppid = getppid(); + snprintf(s, sizeof(s), "/proc/%d/exe", ppid); + n = readlink(s, e, sizeof(e)); + if(n < 0) + return; + + e[MIN(n,sizeof(e)-1)] = '\0'; + + if(strstr(e,"gdb")) { + gaim_debug_info("dns", + "Debugger detected, performing useless query...\n"); + gethostbyname("x.x.x.x.x"); + } +#endif +} + +static void +gaim_dns_resolverthread(int child_out, int child_in, gboolean show_debug) +{ + dns_params_t dns_params; + const size_t zero = 0; + int rc; +#ifdef HAVE_GETADDRINFO + struct addrinfo hints, *res, *tmp; + char servname[20]; +#else + struct sockaddr_in sin; + const size_t addrlen = sizeof(sin); +#endif + +#ifdef HAVE_SIGNAL_H + signal(SIGHUP, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGCHLD, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGTRAP, trap_gdb_bug); +#endif + + /* + * We resolve 1 host name for each iteration of this + * while loop. + * + * The top half of this reads in the hostname and port + * number from the socket with our parent. The bottom + * half of this resolves the IP (blocking) and sends + * the result back to our parent, when finished. + */ + while (1) { + const char ch = 'Y'; + fd_set fds; + struct timeval tv = { .tv_sec = 40 , .tv_usec = 0 }; + FD_ZERO(&fds); + FD_SET(child_in, &fds); + rc = select(child_in + 1, &fds, NULL, NULL, &tv); + if (!rc) { + if (show_debug) + printf("dns[%d]: nobody needs me... =(\n", getpid()); + break; + } + rc = read(child_in, &dns_params, sizeof(dns_params_t)); + if (rc < 0) { + perror("read()"); + break; + } + if (rc == 0) { + if (show_debug) + printf("dns[%d]: Oops, father has gone, wait for me, wait...!\n", getpid()); + _exit(0); + } + if (dns_params.hostname[0] == '\0') { + printf("dns[%d]: hostname = \"\" (port = %d)!!!\n", getpid(), dns_params.port); + _exit(1); + } + /* Tell our parent that we read the data successfully */ + write(child_out, &ch, sizeof(ch)); + + /* We have the hostname and port, now resolve the IP */ + +#ifdef HAVE_GETADDRINFO + g_snprintf(servname, sizeof(servname), "%d", dns_params.port); + memset(&hints, 0, sizeof(hints)); + + /* This is only used to convert a service + * name to a port number. As we know we are + * passing a number already, we know this + * value will not be really used by the C + * library. + */ + hints.ai_socktype = SOCK_STREAM; + rc = getaddrinfo(dns_params.hostname, servname, &hints, &res); + write(child_out, &rc, sizeof(rc)); + if (rc != 0) { + close(child_out); + if (show_debug) + printf("dns[%d] Error: getaddrinfo returned %d\n", + getpid(), rc); + dns_params.hostname[0] = '\0'; + continue; + } + tmp = res; + while (res) { + size_t ai_addrlen = res->ai_addrlen; + write(child_out, &ai_addrlen, sizeof(ai_addrlen)); + write(child_out, res->ai_addr, res->ai_addrlen); + res = res->ai_next; + } + freeaddrinfo(tmp); + write(child_out, &zero, sizeof(zero)); +#else + if (!inet_aton(dns_params.hostname, &sin.sin_addr)) { + struct hostent *hp; + if (!(hp = gethostbyname(dns_params.hostname))) { + write(child_out, &h_errno, sizeof(int)); + close(child_out); + if (show_debug) + printf("DNS Error: %d\n", h_errno); + _exit(0); + } + memset(&sin, 0, sizeof(struct sockaddr_in)); + memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); + sin.sin_family = hp->h_addrtype; + } else + sin.sin_family = AF_INET; + + sin.sin_port = htons(dns_params.port); + write(child_out, &addrlen, sizeof(addrlen)); + write(child_out, &sin, addrlen); + write(child_out, &zero, sizeof(zero)); +#endif + dns_params.hostname[0] = '\0'; + } + + close(child_out); + close(child_in); + + _exit(0); +} + +static pending_dns_request_t * +gaim_dns_new_resolverthread(gboolean show_debug) +{ + pending_dns_request_t *req; + int child_out[2], child_in[2]; + + /* Create pipes for communicating with the child process */ + if (pipe(child_out) || pipe(child_in)) { + gaim_debug_error("dns", + "Could not create pipes: %s\n", strerror(errno)); + return NULL; + } + + req = g_new(pending_dns_request_t, 1); + + cope_with_gdb_brokenness(); + + /* Fork! */ + req->dns_pid = fork(); + + /* If we are the child process... */ + if (req->dns_pid == 0) { + /* We should not access the parent's side of the pipes, so close them */ + close(child_out[0]); + close(child_in[1]); + + gaim_dns_resolverthread(child_out[1], child_in[0], show_debug); + /* The thread calls _exit() rather than returning, so we never get here */ + } + + /* We should not access the child's side of the pipes, so close them */ + close(child_out[1]); + close(child_in[0]); + if (req->dns_pid == -1) { + gaim_debug_error("dns", + "Could not create child process for DNS: %s\n", + strerror(errno)); + g_free(req); + return NULL; + } + + req->fd_out = child_out[0]; + req->fd_in = child_in[1]; + number_of_dns_children++; + gaim_debug_info("dns", + "Created new DNS child %d, there are now %d children.\n", + req->dns_pid, number_of_dns_children); + + return req; +} +/* + * End the DNS resolver child process functions. + */ + +/* + * Begin the functions for dealing with the DNS child processes. + */ +static void +req_free(pending_dns_request_t *req) +{ + g_return_if_fail(req != NULL); + + close(req->fd_in); + close(req->fd_out); + + g_free(req->host); + g_free(req); + + number_of_dns_children--; +} + +static int +send_dns_request_to_child(pending_dns_request_t *req, dns_params_t *dns_params) +{ + char ch; + int rc; + pid_t pid; + + /* This waitpid might return the child's PID if it has recently + * exited, or it might return an error if it exited "long + * enough" ago that it has already been reaped; in either + * instance, we can't use it. */ + if ((pid = waitpid (req->dns_pid, NULL, WNOHANG)) > 0) { + gaim_debug_warning("dns", + "DNS child %d no longer exists\n", req->dns_pid); + return -1; + } else if (pid < 0) { + gaim_debug_warning("dns", + "Wait for DNS child %d failed: %s\n", + req->dns_pid, strerror(errno)); + return -1; + } + + /* Let's contact this lost child! */ + rc = write(req->fd_in, dns_params, sizeof(*dns_params)); + if (rc < 0) { + gaim_debug_error("dns", + "Unable to write to DNS child %d: %d\n", + req->dns_pid, strerror(errno)); + close(req->fd_in); + return -1; + } + + g_return_val_if_fail(rc == sizeof(*dns_params), -1); + + /* Did you hear me? (This avoids some race conditions) */ + rc = read(req->fd_out, &ch, sizeof(ch)); + if (rc != 1 || ch != 'Y') + { + gaim_debug_warning("dns", + "DNS child %d not responding. Killing it!\n", + req->dns_pid); + kill(req->dns_pid, SIGKILL); + return -1; + } + + gaim_debug_info("dns", + "Successfully sent DNS request to child %d\n", req->dns_pid); + + return 0; +} + +static void +host_resolved(gpointer data, gint source, GaimInputCondition cond); + +static void +release_dns_child(pending_dns_request_t *req) +{ + g_free(req->host); + req->host = NULL; + + if (queued_requests && !g_queue_is_empty(queued_requests)) { + queued_dns_request_t *r = g_queue_pop_head(queued_requests); + req->host = g_strdup(r->params.hostname); + req->port = r->params.port; + req->callback = r->callback; + req->data = r->data; + + gaim_debug_info("dns", + "Processing queued DNS query for '%s' with child %d\n", + req->host, req->dns_pid); + + if (send_dns_request_to_child(req, &(r->params)) != 0) { + req_free(req); + req = NULL; + + gaim_debug_warning("dns", + "Intent of process queued query of '%s' failed, " + "requeueing...\n", r->params.hostname); + g_queue_push_head(queued_requests, r); + } else { + req->inpa = gaim_input_add(req->fd_out, GAIM_INPUT_READ, host_resolved, req); + g_free(r); + } + + } else { + req->host = NULL; + req->callback = NULL; + req->data = NULL; + free_dns_children = g_slist_append(free_dns_children, req); + } +} + +static void +host_resolved(gpointer data, gint source, GaimInputCondition cond) +{ + pending_dns_request_t *req = (pending_dns_request_t*)data; + int rc, err; + GSList *hosts = NULL; + struct sockaddr *addr = NULL; + size_t addrlen; + + gaim_debug_info("dns", "Got response for '%s'\n", req->host); + gaim_input_remove(req->inpa); + + rc = read(req->fd_out, &err, sizeof(err)); + if ((rc == 4) && (err != 0)) + { + char message[1024]; +#ifdef HAVE_GETADDRINFO + g_snprintf(message, sizeof(message), "DNS error: %s (pid=%d)", + gai_strerror(err), req->dns_pid); +#else + g_snprintf(message, sizeof(message), "DNS error: %d (pid=%d)", + err, req->dns_pid); +#endif + gaim_debug_error("dns", "%s\n", message); + req->callback(NULL, req->data, message); + release_dns_child(req); + return; + } + if (rc > 0) + { + while (rc > 0) { + rc = read(req->fd_out, &addrlen, sizeof(addrlen)); + if (rc > 0 && addrlen > 0) { + addr = g_malloc(addrlen); + rc = read(req->fd_out, addr, addrlen); + hosts = g_slist_append(hosts, GINT_TO_POINTER(addrlen)); + hosts = g_slist_append(hosts, addr); + } else { + break; + } + } + } else if (rc == -1) { + char message[1024]; + g_snprintf(message, sizeof(message), "Error reading from DNS child: %s",strerror(errno)); + gaim_debug_error("dns", "%s\n", message); + req->callback(NULL, req->data, message); + req_free(req); + return; + } else if (rc == 0) { + char message[1024]; + g_snprintf(message, sizeof(message), "EOF reading from DNS child"); + close(req->fd_out); + gaim_debug_error("dns", "%s\n", message); + req->callback(NULL, req->data, message); + req_free(req); + return; + } + +/* wait4(req->dns_pid, NULL, WNOHANG, NULL); */ + + req->callback(hosts, req->data, NULL); + + release_dns_child(req); +} +/* + * End the functions for dealing with the DNS child processes. + */ + +int +gaim_gethostbyname_async(const char *hostname, int port, GaimProxyDnsConnectFunction callback, gpointer data) +{ + pending_dns_request_t *req = NULL; + dns_params_t dns_params; + gchar *host_temp; + gboolean show_debug; + + show_debug = gaim_debug_is_enabled(); + + host_temp = g_strstrip(g_strdup(hostname)); + strncpy(dns_params.hostname, host_temp, sizeof(dns_params.hostname) - 1); + g_free(host_temp); + dns_params.hostname[sizeof(dns_params.hostname) - 1] = '\0'; + dns_params.port = port; + + /* + * If we have any children, attempt to have them perform the DNS + * query. If we're able to send the query to a child, then req + * will be set to the pending_dns_request_t. Otherwise, req will + * be NULL and we'll need to create a new DNS request child. + */ + while (free_dns_children != NULL) { + req = free_dns_children->data; + free_dns_children = g_slist_remove(free_dns_children, req); + + if (send_dns_request_to_child(req, &dns_params) == 0) + /* We found an acceptable child, yay */ + break; + + req_free(req); + req = NULL; + } + + /* We need to create a new DNS request child */ + if (req == NULL) { + if (number_of_dns_children >= MAX_DNS_CHILDREN) { + queued_dns_request_t *r = g_new(queued_dns_request_t, 1); + memcpy(&(r->params), &dns_params, sizeof(dns_params)); + r->callback = callback; + r->data = data; + if (!queued_requests) + queued_requests = g_queue_new(); + g_queue_push_tail(queued_requests, r); + + gaim_debug_info("dns", + "DNS query for '%s' queued\n", dns_params.hostname); + + return 0; + } + + req = gaim_dns_new_resolverthread(show_debug); + if (req == NULL) + { + gaim_debug_error("proxy", "oh dear, this is going to explode, I give up\n"); + return -1; + } + send_dns_request_to_child(req, &dns_params); + } + + req->host = g_strdup(hostname); + req->port = port; + req->callback = callback; + req->data = data; + req->inpa = gaim_input_add(req->fd_out, GAIM_INPUT_READ, host_resolved, req); + + return 0; +} + +#elif defined _WIN32 /* end __unix__ || __APPLE__ */ + +typedef struct _dns_tdata { + char *hostname; + int port; + GaimProxyDnsConnectFunction callback; + gpointer data; + GSList *hosts; + char *errmsg; +} dns_tdata; + +static gboolean dns_main_thread_cb(gpointer data) { + dns_tdata *td = (dns_tdata*)data; + if (td->errmsg != NULL) { + gaim_debug_info("dns", "%s\n", td->errmsg); + } + td->callback(td->hosts, td->data, td->errmsg); + g_free(td->hostname); + g_free(td->errmsg); + g_free(td); + return FALSE; +} + +static gpointer dns_thread(gpointer data) { + +#ifdef HAVE_GETADDRINFO + int rc; + struct addrinfo hints, *res, *tmp; + char servname[20]; +#else + struct sockaddr_in sin; + struct hostent *hp; +#endif + dns_tdata *td = (dns_tdata*)data; + +#ifdef HAVE_GETADDRINFO + g_snprintf(servname, sizeof(servname), "%d", td->port); + memset(&hints,0,sizeof(hints)); + + /* This is only used to convert a service + * name to a port number. As we know we are + * passing a number already, we know this + * value will not be really used by the C + * library. + */ + hints.ai_socktype = SOCK_STREAM; + if ((rc = getaddrinfo(td->hostname, servname, &hints, &res)) == 0) { + tmp = res; + while(res) { + td->hosts = g_slist_append(td->hosts, + GSIZE_TO_POINTER(res->ai_addrlen)); + td->hosts = g_slist_append(td->hosts, + g_memdup(res->ai_addr, res->ai_addrlen)); + res = res->ai_next; + } + freeaddrinfo(tmp); + } else { + td->errmsg = g_strdup_printf("DNS getaddrinfo(\"%s\", \"%s\") error: %d", td->hostname, servname, rc); + } +#else + if ((hp = gethostbyname(td->hostname))) { + memset(&sin, 0, sizeof(struct sockaddr_in)); + memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); + sin.sin_family = hp->h_addrtype; + sin.sin_port = htons(td->port); + + td->hosts = g_slist_append(td->hosts, + GSIZE_TO_POINTER(sizeof(sin))); + td->hosts = g_slist_append(td->hosts, + g_memdup(&sin, sizeof(sin))); + } else { + td->errmsg = g_strdup_printf("DNS gethostbyname(\"%s\") error: %d", td->hostname, h_errno); + } +#endif + /* back to main thread */ + g_idle_add(dns_main_thread_cb, td); + return 0; +} + +int +gaim_gethostbyname_async(const char *hostname, int port, + GaimProxyDnsConnectFunction callback, gpointer data) +{ + dns_tdata *td; + struct sockaddr_in sin; + GError* err = NULL; + + if(inet_aton(hostname, &sin.sin_addr)) { + GSList *hosts = NULL; + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + hosts = g_slist_append(hosts, GINT_TO_POINTER(sizeof(sin))); + hosts = g_slist_append(hosts, g_memdup(&sin, sizeof(sin))); + callback(hosts, data, NULL); + return 0; + } + + gaim_debug_info("dns", "DNS Lookup for: %s\n", hostname); + td = g_new0(dns_tdata, 1); + td->hostname = g_strdup(hostname); + td->port = port; + td->callback = callback; + td->data = data; + + if(!g_thread_create(dns_thread, td, FALSE, &err)) { + gaim_debug_error("dns", "DNS thread create failure: %s\n", err?err->message:""); + g_error_free(err); + g_free(td->hostname); + g_free(td); + return -1; + } + return 0; +} + +#else /* not __unix__ or __APPLE__ or _WIN32 */ + +typedef struct { + gpointer data; + size_t addrlen; + struct sockaddr *addr; + GaimProxyDnsConnectFunction callback; +} pending_dns_request_t; + +static gboolean host_resolved(gpointer data) +{ + pending_dns_request_t *req = (pending_dns_request_t*)data; + GSList *hosts = NULL; + hosts = g_slist_append(hosts, GINT_TO_POINTER(req->addrlen)); + hosts = g_slist_append(hosts, req->addr); + req->callback(hosts, req->data, NULL); + g_free(req); + return FALSE; +} + +int +gaim_gethostbyname_async(const char *hostname, int port, + GaimProxyDnsConnectFunction callback, gpointer data) +{ + struct sockaddr_in sin; + pending_dns_request_t *req; + + if (!inet_aton(hostname, &sin.sin_addr)) { + struct hostent *hp; + if(!(hp = gethostbyname(hostname))) { + gaim_debug_error("dns", + "gaim_gethostbyname(\"%s\", %d) failed: %d\n", + hostname, port, h_errno); + return -1; + } + memset(&sin, 0, sizeof(struct sockaddr_in)); + memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); + sin.sin_family = hp->h_addrtype; + } else + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + + req = g_new(pending_dns_request_t, 1); + req->addr = (struct sockaddr*) g_memdup(&sin, sizeof(sin)); + req->addrlen = sizeof(sin); + req->data = data; + req->callback = callback; + gaim_timeout_add(10, host_resolved, req); + return 0; +} + +#endif /* not __unix__ or __APPLE__ or _WIN32 */ + +static void no_one_calls(gpointer data, gint source, GaimInputCondition cond) { GaimProxyConnectInfo *connect_info = data; @@ -1701,9 +2368,8 @@ return NULL; } - connect_info->query_data = gaim_dnsquery_a(connecthost, - connectport, connection_host_resolved, connect_info); - if (connect_info->query_data == NULL) + if (gaim_gethostbyname_async(connecthost, + connectport, connection_host_resolved, connect_info) != 0) { gaim_proxy_connect_info_destroy(connect_info); return NULL; @@ -1735,9 +2401,8 @@ connect_info->port = port; connect_info->gpi = gpi; - connect_info->query_data = gaim_dnsquery_a(gaim_proxy_info_get_host(gpi), - gaim_proxy_info_get_port(gpi), connection_host_resolved, connect_info); - if (connect_info->query_data == NULL) + if (gaim_gethostbyname_async(gaim_proxy_info_get_host(gpi), + gaim_proxy_info_get_port(gpi), connection_host_resolved, connect_info) != 0) { gaim_proxy_connect_info_destroy(connect_info); return NULL; Modified: trunk/src/proxy.h =================================================================== --- trunk/src/proxy.h 2006-08-17 10:04:19 UTC (rev 16813) +++ trunk/src/proxy.h 2006-08-17 10:04:21 UTC (rev 16814) @@ -26,7 +26,6 @@ #define _GAIM_PROXY_H_ #include <glib.h> -#include "dnsquery.h" #include "eventloop.h" /** @@ -57,7 +56,6 @@ } GaimProxyInfo; -typedef struct _GaimDnsQueryData GaimDnsQueryData; typedef struct _GaimProxyConnectInfo GaimProxyConnectInfo; typedef void (*GaimProxyConnectFunction)(gpointer data, gint source, const gchar *error_message); @@ -280,6 +278,18 @@ */ void gaim_proxy_connect_cancel(GaimProxyConnectInfo *connect_info); +/** + * Do an async dns query + * + * @param hostname The hostname to resolve + * @param port A portnumber which is stored in the struct sockaddr + * @param callback Callback to call after resolving + * @param data Extra data for the callback function + * + * @return Zero indicates the connection is pending. Any other value indicates failure. + */ +int gaim_gethostbyname_async(const char *hostname, int port, GaimProxyDnsConnectFunction callback, gpointer data); + /*@}*/ #ifdef __cplusplus Modified: trunk/src/stun.c =================================================================== --- trunk/src/stun.c 2006-08-17 10:04:19 UTC (rev 16813) +++ trunk/src/stun.c 2006-08-17 10:04:21 UTC (rev 16814) @@ -40,7 +40,6 @@ #include "debug.h" #include "account.h" -#include "dnsquery.h" #include "dnssrv.h" #include "network.h" #include "proxy.h" @@ -360,7 +359,7 @@ gaim_debug_info("stun", "got %d SRV responses, server: %s, port: %d\n", results, servername, port); - gaim_dnsquery_a(servername, port, hbn_cb, NULL); + gaim_gethostbyname_async(servername, port, hbn_cb, NULL); g_free(resp); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <sa...@us...> - 2006-08-18 00:15:29
|
Revision: 16821 Author: sadrul Date: 2006-08-17 17:15:25 -0700 (Thu, 17 Aug 2006) ViewCVS: http://svn.sourceforge.net/gaim/?rev=16821&view=rev Log Message: ----------- Patch #1523103 ("Improved markup processing"): "This patch tries to handle all the html-entities (including stuff like ® and &#xx;) in gaim_markup_strip_html and gaim_unescape_html. This also allows fixing the issue with libxml2 where & was being converted to &." Modified Paths: -------------- trunk/src/util.c trunk/src/xmlnode.c Modified: trunk/src/util.c =================================================================== --- trunk/src/util.c 2006-08-17 23:58:58 UTC (rev 16820) +++ trunk/src/util.c 2006-08-18 00:15:25 UTC (rev 16821) @@ -854,6 +854,55 @@ /************************************************************************** * Markup Functions **************************************************************************/ + +/* Returns a NULL-terminated string after unescaping an entity + * (eg. &, < & etc.) starting at s. Returns NULL on failure.*/ +static const char * +detect_entity(const char *text, int *length) +{ + const char *pln; + int len, pound; + + if (!text || *text != '&') + return NULL; + +#define IS_ENTITY(s) (!g_ascii_strncasecmp(text, s, (len = sizeof(s) - 1))) + + if(IS_ENTITY("&")) + pln = "&"; + else if(IS_ENTITY("<")) + pln = "<"; + else if(IS_ENTITY(">")) + pln = ">"; + else if(IS_ENTITY(" ")) + pln = " "; + else if(IS_ENTITY("©")) + pln = "\251"; + else if(IS_ENTITY(""")) + pln = "\""; + else if(IS_ENTITY("®")) + pln = "\256"; + else if(IS_ENTITY("'")) + pln = "\'"; + else if(*(text+1) == '#' && (sscanf(text, "&#%u;", £) == 1) && + pound != 0 && *(text+3+(gint)log10(pound)) == ';') { + static char buf[7]; + int buflen = g_unichar_to_utf8((gunichar)pound, buf); + buf[buflen] = '\0'; + pln = buf; + + len = 2; + while(isdigit((gint) text[len])) len++; + if(text[len] == ';') len++; + } + else + return NULL; + + if (length) + *length = len; + return pln; +} + gboolean gaim_markup_find_tag(const char *needle, const char *haystack, const char **start, const char **end, GData **attributes) @@ -1443,44 +1492,10 @@ } } else if(*c == '&') { char buf[7]; - char *pln; - int len = 1; - guint pound; - if(!g_ascii_strncasecmp(c, "&", 5)) { - pln = "&"; - len = 5; - } else if(!g_ascii_strncasecmp(c, "<", 4)) { - pln = "<"; - len = 4; - } else if(!g_ascii_strncasecmp(c, ">", 4)) { - pln = ">"; - len = 4; - } else if(!g_ascii_strncasecmp(c, " ", 6)) { - pln = " "; - len = 6; - } else if(!g_ascii_strncasecmp(c, "©", 6)) { - pln = "©"; - len = 6; - } else if(!g_ascii_strncasecmp(c, """, 6)) { - pln = "\""; - len = 6; - } else if(!g_ascii_strncasecmp(c, "®", 5)) { - pln = "®"; - len = 5; - } else if(!g_ascii_strncasecmp(c, "'", 6)) { - pln = "\'"; - len = 6; - } else if(*(c+1) == '#' && (sscanf(c, "&#%u;", £) == 1) && - pound != 0 && *(c+3+(gint)log10(pound)) == ';') { - int buflen = g_unichar_to_utf8((gunichar)pound, buf); - buf[buflen] = '\0'; - pln = buf; + const char *pln; + int len; - - len = 2; - while(isdigit((gint) c [len])) len++; - if(c [len] == ';') len++; - } else { + if ((pln = detect_entity(c, &len)) == NULL) { len = 1; g_snprintf(buf, sizeof(buf), "%c", *c); pln = buf; @@ -1522,11 +1537,11 @@ char * gaim_markup_strip_html(const char *str) { - int i, j, k; + int i, j, k, entlen; gboolean visible = TRUE; gboolean closing_td_p = FALSE; gchar *str2; - const gchar *cdata_close_tag = NULL; + const gchar *cdata_close_tag = NULL, *ent; gchar *href = NULL; int href_st = 0; @@ -1685,44 +1700,14 @@ visible = TRUE; } - /* XXX: This sucks. We need to be un-escaping all entities, which - * includes these, as well as the &#num; ones */ - - if (str2[i] == '&' && strncasecmp(str2 + i, """, 6) == 0) + if (str2[i] == '&' && (ent = detect_entity(str2 + i, &entlen)) != NULL) { - str2[j++] = '\"'; - i = i + 5; - continue; - } - - if (str2[i] == '&' && strncasecmp(str2 + i, "&", 5) == 0) - { - str2[j++] = '&'; - i = i + 4; + while (*ent) + str2[j++] = *ent++; + i += entlen - 1; continue; } - if (str2[i] == '&' && strncasecmp(str2 + i, "<", 4) == 0) - { - str2[j++] = '<'; - i = i + 3; - continue; - } - - if (str2[i] == '&' && strncasecmp(str2 + i, ">", 4) == 0) - { - str2[j++] = '>'; - i = i + 3; - continue; - } - - if (str2[i] == '&' && strncasecmp(str2 + i, "'", 6) == 0) - { - str2[j++] = '\''; - i = i + 5; - continue; - } - if (visible) str2[j++] = g_ascii_isspace(str2[i])? ' ': str2[i]; } @@ -2026,41 +2011,28 @@ char * gaim_unescape_html(const char *html) { - const char *c; - GString *ret; + if (html != NULL) { + const char *c = html; + GString *ret = g_string_new(""); + while (*c) { + int len; + const char *ent; - if (html == NULL) - return NULL; - - c = html; - ret = g_string_new(""); - while (*c) { - if (!strncmp(c, "&", 5)) { - ret = g_string_append_c(ret, '&'); - c += 5; - } else if (!strncmp(c, "<", 4)) { - ret = g_string_append_c(ret, '<'); - c += 4; - } else if (!strncmp(c, ">", 4)) { - ret = g_string_append_c(ret, '>'); - c += 4; - } else if (!strncmp(c, """, 6)) { - ret = g_string_append_c(ret, '"'); - c += 6; - } else if (!strncmp(c, "'", 6)) { - ret = g_string_append_c(ret, '\''); - c += 6; - } else if (!strncmp(c, "<br>", 4)) { - ret = g_string_append_c(ret, '\n'); - c += 4; - } else { - ret = g_string_append_c(ret, *c); - c++; + if ((ent = detect_entity(c, &len)) != NULL) { + ret = g_string_append(ret, ent); + c += len; + } else if (!strncmp(c, "<br>", 4)) { + ret = g_string_append_c(ret, '\n'); + c += 4; + } else { + ret = g_string_append_c(ret, *c); + c++; + } } + return g_string_free(ret, FALSE); } - return g_string_free(ret, FALSE); - + return NULL; } char * @@ -3998,4 +3970,3 @@ return buf; } - Modified: trunk/src/xmlnode.c =================================================================== --- trunk/src/xmlnode.c 2006-08-17 23:58:58 UTC (rev 16820) +++ trunk/src/xmlnode.c 2006-08-18 00:15:25 UTC (rev 16821) @@ -35,6 +35,7 @@ #include <string.h> #include <glib.h> +#include "util.h" #include "xmlnode.h" #ifdef _WIN32 @@ -406,6 +407,11 @@ char *attrib = g_malloc(attrib_len + 1); memcpy(attrib, attributes[i+3], attrib_len); attrib[attrib_len] = '\0'; +#ifdef HAVE_LIBXML + char *txt = attrib; + attrib = gaim_unescape_html(txt); + g_free(txt); +#endif xmlnode_set_attrib(node, attributes[i], attrib); g_free(attrib); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <the...@us...> - 2006-08-18 05:58:27
|
Revision: 16829 Author: thekingant Date: 2006-08-17 22:58:17 -0700 (Thu, 17 Aug 2006) ViewCVS: http://svn.sourceforge.net/gaim/?rev=16829&view=rev Log Message: ----------- Just rename connect to connectfunc. This is what it was like before my changes. Now I know why! Modified Paths: -------------- trunk/src/sslconn.c trunk/src/sslconn.h Modified: trunk/src/sslconn.c =================================================================== --- trunk/src/sslconn.c 2006-08-18 05:56:10 UTC (rev 16828) +++ trunk/src/sslconn.c 2006-08-18 05:58:17 UTC (rev 16829) @@ -46,7 +46,7 @@ ops = gaim_ssl_get_ops(); if ((ops == NULL) || (ops->init == NULL) || (ops->uninit == NULL) || - (ops->connect == NULL) || (ops->close == NULL) || + (ops->connectfunc == NULL) || (ops->close == NULL) || (ops->read == NULL) || (ops->write == NULL)) { return FALSE; @@ -87,7 +87,7 @@ gsc->fd = source; ops = gaim_ssl_get_ops(); - (ops->connect)(gsc); + ops->connectfunc(gsc); } GaimSslConnection * @@ -177,7 +177,7 @@ gsc->fd = fd; ops = gaim_ssl_get_ops(); - (ops->connect)(gsc); + ops->connectfunc(gsc); return (GaimSslConnection *)gsc; } Modified: trunk/src/sslconn.h =================================================================== --- trunk/src/sslconn.h 2006-08-18 05:56:10 UTC (rev 16828) +++ trunk/src/sslconn.h 2006-08-18 05:58:17 UTC (rev 16829) @@ -68,7 +68,7 @@ { gboolean (*init)(void); void (*uninit)(void); - void (*connect)(GaimSslConnection *gsc); + void (*connectfunc)(GaimSslConnection *gsc); void (*close)(GaimSslConnection *gsc); size_t (*read)(GaimSslConnection *gsc, void *data, size_t len); size_t (*write)(GaimSslConnection *gsc, const void *data, size_t len); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <the...@us...> - 2006-08-18 07:20:42
|
Revision: 16833 Author: thekingant Date: 2006-08-18 00:20:31 -0700 (Fri, 18 Aug 2006) ViewCVS: http://svn.sourceforge.net/gaim/?rev=16833&view=rev Log Message: ----------- Split the DNS query code out into its own file without actually changing anything. Modified Paths: -------------- trunk/src/Makefile.am trunk/src/Makefile.mingw trunk/src/protocols/simple/simple.c trunk/src/proxy.c trunk/src/proxy.h trunk/src/stun.c Added Paths: ----------- trunk/src/dnsquery.c trunk/src/dnsquery.h Modified: trunk/src/Makefile.am =================================================================== --- trunk/src/Makefile.am 2006-08-18 06:40:16 UTC (rev 16832) +++ trunk/src/Makefile.am 2006-08-18 07:20:31 UTC (rev 16833) @@ -77,6 +77,7 @@ core.c \ debug.c \ desktopitem.c \ + dnsquery.c \ eventloop.c \ ft.c \ idle.c \ @@ -125,6 +126,7 @@ dbus-maybe.h \ debug.h \ desktopitem.h \ + dnsquery.h \ eventloop.h \ ft.h \ idle.h \ Modified: trunk/src/Makefile.mingw =================================================================== --- trunk/src/Makefile.mingw 2006-08-18 06:40:16 UTC (rev 16832) +++ trunk/src/Makefile.mingw 2006-08-18 07:20:31 UTC (rev 16833) @@ -95,6 +95,7 @@ conversation.c \ core.c \ debug.c \ + dnsquery.c \ dnssrv.c \ eventloop.c \ ft.c \ Added: trunk/src/dnsquery.c =================================================================== --- trunk/src/dnsquery.c (rev 0) +++ trunk/src/dnsquery.c 2006-08-18 07:20:31 UTC (rev 16833) @@ -0,0 +1,710 @@ +/** + * @file dnsquery.c DNS query API + * @ingroup core + * + * gaim + * + * Gaim is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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 + * + */ + +#include "internal.h" +#include "debug.h" +#include "notify.h" +#include "prefs.h" +#include "dnsquery.h" +#include "util.h" + +/************************************************************************** + * DNS query API + **************************************************************************/ + +#if defined(__unix__) || defined(__APPLE__) + +/* + * This structure represents both a pending DNS request and + * a free child process. + */ +typedef struct { + char *host; + int port; + GaimProxyDnsConnectFunction callback; + gpointer data; + guint inpa; + int fd_in, fd_out; + pid_t dns_pid; +} pending_dns_request_t; + +static GSList *free_dns_children = NULL; +static GQueue *queued_requests = NULL; + +static int number_of_dns_children = 0; + +static const int MAX_DNS_CHILDREN = 2; + +typedef struct { + char hostname[512]; + int port; +} dns_params_t; + +typedef struct { + dns_params_t params; + GaimProxyDnsConnectFunction callback; + gpointer data; +} queued_dns_request_t; + +/* + * Begin the DNS resolver child process functions. + */ +#ifdef HAVE_SIGNAL_H +static void +trap_gdb_bug() +{ + const char *message = + "Gaim's DNS child got a SIGTRAP signal.\n" + "This can be caused by trying to run gaim inside gdb.\n" + "There is a known gdb bug which prevents this. Supposedly gaim\n" + "should have detected you were using gdb and used an ugly hack,\n" + "check cope_with_gdb_brokenness() in dnsquery.c.\n\n" + "For more info about this bug, see http://sources.redhat.com/ml/gdb/2001-07/msg00349.html\n"; + fputs("\n* * *\n",stderr); + fputs(message,stderr); + fputs("* * *\n\n",stderr); + execlp("xmessage","xmessage","-center", message, NULL); + _exit(1); +} +#endif + +static void +cope_with_gdb_brokenness() +{ +#ifdef __linux__ + static gboolean already_done = FALSE; + char s[256], e[512]; + int n; + pid_t ppid; + + if(already_done) + return; + already_done = TRUE; + ppid = getppid(); + snprintf(s, sizeof(s), "/proc/%d/exe", ppid); + n = readlink(s, e, sizeof(e)); + if(n < 0) + return; + + e[MIN(n,sizeof(e)-1)] = '\0'; + + if(strstr(e,"gdb")) { + gaim_debug_info("dns", + "Debugger detected, performing useless query...\n"); + gethostbyname("x.x.x.x.x"); + } +#endif +} + +static void +gaim_dns_resolverthread(int child_out, int child_in, gboolean show_debug) +{ + dns_params_t dns_params; + const size_t zero = 0; + int rc; +#ifdef HAVE_GETADDRINFO + struct addrinfo hints, *res, *tmp; + char servname[20]; +#else + struct sockaddr_in sin; + const size_t addrlen = sizeof(sin); +#endif + +#ifdef HAVE_SIGNAL_H + signal(SIGHUP, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGCHLD, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGTRAP, trap_gdb_bug); +#endif + + /* + * We resolve 1 host name for each iteration of this + * while loop. + * + * The top half of this reads in the hostname and port + * number from the socket with our parent. The bottom + * half of this resolves the IP (blocking) and sends + * the result back to our parent, when finished. + */ + while (1) { + const char ch = 'Y'; + fd_set fds; + struct timeval tv = { .tv_sec = 40 , .tv_usec = 0 }; + FD_ZERO(&fds); + FD_SET(child_in, &fds); + rc = select(child_in + 1, &fds, NULL, NULL, &tv); + if (!rc) { + if (show_debug) + printf("dns[%d]: nobody needs me... =(\n", getpid()); + break; + } + rc = read(child_in, &dns_params, sizeof(dns_params_t)); + if (rc < 0) { + perror("read()"); + break; + } + if (rc == 0) { + if (show_debug) + printf("dns[%d]: Oops, father has gone, wait for me, wait...!\n", getpid()); + _exit(0); + } + if (dns_params.hostname[0] == '\0') { + printf("dns[%d]: hostname = \"\" (port = %d)!!!\n", getpid(), dns_params.port); + _exit(1); + } + /* Tell our parent that we read the data successfully */ + write(child_out, &ch, sizeof(ch)); + + /* We have the hostname and port, now resolve the IP */ + +#ifdef HAVE_GETADDRINFO + g_snprintf(servname, sizeof(servname), "%d", dns_params.port); + memset(&hints, 0, sizeof(hints)); + + /* This is only used to convert a service + * name to a port number. As we know we are + * passing a number already, we know this + * value will not be really used by the C + * library. + */ + hints.ai_socktype = SOCK_STREAM; + rc = getaddrinfo(dns_params.hostname, servname, &hints, &res); + write(child_out, &rc, sizeof(rc)); + if (rc != 0) { + close(child_out); + if (show_debug) + printf("dns[%d] Error: getaddrinfo returned %d\n", + getpid(), rc); + dns_params.hostname[0] = '\0'; + continue; + } + tmp = res; + while (res) { + size_t ai_addrlen = res->ai_addrlen; + write(child_out, &ai_addrlen, sizeof(ai_addrlen)); + write(child_out, res->ai_addr, res->ai_addrlen); + res = res->ai_next; + } + freeaddrinfo(tmp); + write(child_out, &zero, sizeof(zero)); +#else + if (!inet_aton(dns_params.hostname, &sin.sin_addr)) { + struct hostent *hp; + if (!(hp = gethostbyname(dns_params.hostname))) { + write(child_out, &h_errno, sizeof(int)); + close(child_out); + if (show_debug) + printf("DNS Error: %d\n", h_errno); + _exit(0); + } + memset(&sin, 0, sizeof(struct sockaddr_in)); + memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); + sin.sin_family = hp->h_addrtype; + } else + sin.sin_family = AF_INET; + + sin.sin_port = htons(dns_params.port); + write(child_out, &addrlen, sizeof(addrlen)); + write(child_out, &sin, addrlen); + write(child_out, &zero, sizeof(zero)); +#endif + dns_params.hostname[0] = '\0'; + } + + close(child_out); + close(child_in); + + _exit(0); +} + +static pending_dns_request_t * +gaim_dns_new_resolverthread(gboolean show_debug) +{ + pending_dns_request_t *req; + int child_out[2], child_in[2]; + + /* Create pipes for communicating with the child process */ + if (pipe(child_out) || pipe(child_in)) { + gaim_debug_error("dns", + "Could not create pipes: %s\n", strerror(errno)); + return NULL; + } + + req = g_new(pending_dns_request_t, 1); + + cope_with_gdb_brokenness(); + + /* Fork! */ + req->dns_pid = fork(); + + /* If we are the child process... */ + if (req->dns_pid == 0) { + /* We should not access the parent's side of the pipes, so close them */ + close(child_out[0]); + close(child_in[1]); + + gaim_dns_resolverthread(child_out[1], child_in[0], show_debug); + /* The thread calls _exit() rather than returning, so we never get here */ + } + + /* We should not access the child's side of the pipes, so close them */ + close(child_out[1]); + close(child_in[0]); + if (req->dns_pid == -1) { + gaim_debug_error("dns", + "Could not create child process for DNS: %s\n", + strerror(errno)); + g_free(req); + return NULL; + } + + req->fd_out = child_out[0]; + req->fd_in = child_in[1]; + number_of_dns_children++; + gaim_debug_info("dns", + "Created new DNS child %d, there are now %d children.\n", + req->dns_pid, number_of_dns_children); + + return req; +} +/* + * End the DNS resolver child process functions. + */ + +/* + * Begin the functions for dealing with the DNS child processes. + */ +static void +req_free(pending_dns_request_t *req) +{ + g_return_if_fail(req != NULL); + + close(req->fd_in); + close(req->fd_out); + + g_free(req->host); + g_free(req); + + number_of_dns_children--; +} + +static int +send_dns_request_to_child(pending_dns_request_t *req, dns_params_t *dns_params) +{ + char ch; + int rc; + pid_t pid; + + /* This waitpid might return the child's PID if it has recently + * exited, or it might return an error if it exited "long + * enough" ago that it has already been reaped; in either + * instance, we can't use it. */ + if ((pid = waitpid (req->dns_pid, NULL, WNOHANG)) > 0) { + gaim_debug_warning("dns", + "DNS child %d no longer exists\n", req->dns_pid); + return -1; + } else if (pid < 0) { + gaim_debug_warning("dns", + "Wait for DNS child %d failed: %s\n", + req->dns_pid, strerror(errno)); + return -1; + } + + /* Let's contact this lost child! */ + rc = write(req->fd_in, dns_params, sizeof(*dns_params)); + if (rc < 0) { + gaim_debug_error("dns", + "Unable to write to DNS child %d: %d\n", + req->dns_pid, strerror(errno)); + close(req->fd_in); + return -1; + } + + g_return_val_if_fail(rc == sizeof(*dns_params), -1); + + /* Did you hear me? (This avoids some race conditions) */ + rc = read(req->fd_out, &ch, sizeof(ch)); + if (rc != 1 || ch != 'Y') + { + gaim_debug_warning("dns", + "DNS child %d not responding. Killing it!\n", + req->dns_pid); + kill(req->dns_pid, SIGKILL); + return -1; + } + + gaim_debug_info("dns", + "Successfully sent DNS request to child %d\n", req->dns_pid); + + return 0; +} + +static void +host_resolved(gpointer data, gint source, GaimInputCondition cond); + +static void +release_dns_child(pending_dns_request_t *req) +{ + g_free(req->host); + req->host = NULL; + + if (queued_requests && !g_queue_is_empty(queued_requests)) { + queued_dns_request_t *r = g_queue_pop_head(queued_requests); + req->host = g_strdup(r->params.hostname); + req->port = r->params.port; + req->callback = r->callback; + req->data = r->data; + + gaim_debug_info("dns", + "Processing queued DNS query for '%s' with child %d\n", + req->host, req->dns_pid); + + if (send_dns_request_to_child(req, &(r->params)) != 0) { + req_free(req); + req = NULL; + + gaim_debug_warning("dns", + "Intent of process queued query of '%s' failed, " + "requeueing...\n", r->params.hostname); + g_queue_push_head(queued_requests, r); + } else { + req->inpa = gaim_input_add(req->fd_out, GAIM_INPUT_READ, host_resolved, req); + g_free(r); + } + + } else { + req->host = NULL; + req->callback = NULL; + req->data = NULL; + free_dns_children = g_slist_append(free_dns_children, req); + } +} + +static void +host_resolved(gpointer data, gint source, GaimInputCondition cond) +{ + pending_dns_request_t *req = (pending_dns_request_t*)data; + int rc, err; + GSList *hosts = NULL; + struct sockaddr *addr = NULL; + size_t addrlen; + + gaim_debug_info("dns", "Got response for '%s'\n", req->host); + gaim_input_remove(req->inpa); + + rc = read(req->fd_out, &err, sizeof(err)); + if ((rc == 4) && (err != 0)) + { + char message[1024]; +#ifdef HAVE_GETADDRINFO + g_snprintf(message, sizeof(message), "DNS error: %s (pid=%d)", + gai_strerror(err), req->dns_pid); +#else + g_snprintf(message, sizeof(message), "DNS error: %d (pid=%d)", + err, req->dns_pid); +#endif + gaim_debug_error("dns", "%s\n", message); + req->callback(NULL, req->data, message); + release_dns_child(req); + return; + } + if (rc > 0) + { + while (rc > 0) { + rc = read(req->fd_out, &addrlen, sizeof(addrlen)); + if (rc > 0 && addrlen > 0) { + addr = g_malloc(addrlen); + rc = read(req->fd_out, addr, addrlen); + hosts = g_slist_append(hosts, GINT_TO_POINTER(addrlen)); + hosts = g_slist_append(hosts, addr); + } else { + break; + } + } + } else if (rc == -1) { + char message[1024]; + g_snprintf(message, sizeof(message), "Error reading from DNS child: %s",strerror(errno)); + gaim_debug_error("dns", "%s\n", message); + req->callback(NULL, req->data, message); + req_free(req); + return; + } else if (rc == 0) { + char message[1024]; + g_snprintf(message, sizeof(message), "EOF reading from DNS child"); + close(req->fd_out); + gaim_debug_error("dns", "%s\n", message); + req->callback(NULL, req->data, message); + req_free(req); + return; + } + +/* wait4(req->dns_pid, NULL, WNOHANG, NULL); */ + + req->callback(hosts, req->data, NULL); + + release_dns_child(req); +} +/* + * End the functions for dealing with the DNS child processes. + */ + +int +gaim_gethostbyname_async(const char *hostname, int port, GaimProxyDnsConnectFunction callback, gpointer data) +{ + pending_dns_request_t *req = NULL; + dns_params_t dns_params; + gchar *host_temp; + gboolean show_debug; + + show_debug = gaim_debug_is_enabled(); + + host_temp = g_strstrip(g_strdup(hostname)); + strncpy(dns_params.hostname, host_temp, sizeof(dns_params.hostname) - 1); + g_free(host_temp); + dns_params.hostname[sizeof(dns_params.hostname) - 1] = '\0'; + dns_params.port = port; + + /* + * If we have any children, attempt to have them perform the DNS + * query. If we're able to send the query to a child, then req + * will be set to the pending_dns_request_t. Otherwise, req will + * be NULL and we'll need to create a new DNS request child. + */ + while (free_dns_children != NULL) { + req = free_dns_children->data; + free_dns_children = g_slist_remove(free_dns_children, req); + + if (send_dns_request_to_child(req, &dns_params) == 0) + /* We found an acceptable child, yay */ + break; + + req_free(req); + req = NULL; + } + + /* We need to create a new DNS request child */ + if (req == NULL) { + if (number_of_dns_children >= MAX_DNS_CHILDREN) { + queued_dns_request_t *r = g_new(queued_dns_request_t, 1); + memcpy(&(r->params), &dns_params, sizeof(dns_params)); + r->callback = callback; + r->data = data; + if (!queued_requests) + queued_requests = g_queue_new(); + g_queue_push_tail(queued_requests, r); + + gaim_debug_info("dns", + "DNS query for '%s' queued\n", dns_params.hostname); + + return 0; + } + + req = gaim_dns_new_resolverthread(show_debug); + if (req == NULL) + { + gaim_debug_error("proxy", "oh dear, this is going to explode, I give up\n"); + return -1; + } + send_dns_request_to_child(req, &dns_params); + } + + req->host = g_strdup(hostname); + req->port = port; + req->callback = callback; + req->data = data; + req->inpa = gaim_input_add(req->fd_out, GAIM_INPUT_READ, host_resolved, req); + + return 0; +} + +#elif defined _WIN32 /* end __unix__ || __APPLE__ */ + +typedef struct _dns_tdata { + char *hostname; + int port; + GaimProxyDnsConnectFunction callback; + gpointer data; + GSList *hosts; + char *errmsg; +} dns_tdata; + +static gboolean dns_main_thread_cb(gpointer data) { + dns_tdata *td = (dns_tdata*)data; + if (td->errmsg != NULL) { + gaim_debug_info("dns", "%s\n", td->errmsg); + } + td->callback(td->hosts, td->data, td->errmsg); + g_free(td->hostname); + g_free(td->errmsg); + g_free(td); + return FALSE; +} + +static gpointer dns_thread(gpointer data) { + +#ifdef HAVE_GETADDRINFO + int rc; + struct addrinfo hints, *res, *tmp; + char servname[20]; +#else + struct sockaddr_in sin; + struct hostent *hp; +#endif + dns_tdata *td = (dns_tdata*)data; + +#ifdef HAVE_GETADDRINFO + g_snprintf(servname, sizeof(servname), "%d", td->port); + memset(&hints,0,sizeof(hints)); + + /* This is only used to convert a service + * name to a port number. As we know we are + * passing a number already, we know this + * value will not be really used by the C + * library. + */ + hints.ai_socktype = SOCK_STREAM; + if ((rc = getaddrinfo(td->hostname, servname, &hints, &res)) == 0) { + tmp = res; + while(res) { + td->hosts = g_slist_append(td->hosts, + GSIZE_TO_POINTER(res->ai_addrlen)); + td->hosts = g_slist_append(td->hosts, + g_memdup(res->ai_addr, res->ai_addrlen)); + res = res->ai_next; + } + freeaddrinfo(tmp); + } else { + td->errmsg = g_strdup_printf("DNS getaddrinfo(\"%s\", \"%s\") error: %d", td->hostname, servname, rc); + } +#else + if ((hp = gethostbyname(td->hostname))) { + memset(&sin, 0, sizeof(struct sockaddr_in)); + memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); + sin.sin_family = hp->h_addrtype; + sin.sin_port = htons(td->port); + + td->hosts = g_slist_append(td->hosts, + GSIZE_TO_POINTER(sizeof(sin))); + td->hosts = g_slist_append(td->hosts, + g_memdup(&sin, sizeof(sin))); + } else { + td->errmsg = g_strdup_printf("DNS gethostbyname(\"%s\") error: %d", td->hostname, h_errno); + } +#endif + /* back to main thread */ + g_idle_add(dns_main_thread_cb, td); + return 0; +} + +int +gaim_gethostbyname_async(const char *hostname, int port, + GaimProxyDnsConnectFunction callback, gpointer data) +{ + dns_tdata *td; + struct sockaddr_in sin; + GError* err = NULL; + + if(inet_aton(hostname, &sin.sin_addr)) { + GSList *hosts = NULL; + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + hosts = g_slist_append(hosts, GINT_TO_POINTER(sizeof(sin))); + hosts = g_slist_append(hosts, g_memdup(&sin, sizeof(sin))); + callback(hosts, data, NULL); + return 0; + } + + gaim_debug_info("dns", "DNS Lookup for: %s\n", hostname); + td = g_new0(dns_tdata, 1); + td->hostname = g_strdup(hostname); + td->port = port; + td->callback = callback; + td->data = data; + + if(!g_thread_create(dns_thread, td, FALSE, &err)) { + gaim_debug_error("dns", "DNS thread create failure: %s\n", err?err->message:""); + g_error_free(err); + g_free(td->hostname); + g_free(td); + return -1; + } + return 0; +} + +#else /* not __unix__ or __APPLE__ or _WIN32 */ + +typedef struct { + gpointer data; + size_t addrlen; + struct sockaddr *addr; + GaimProxyDnsConnectFunction callback; +} pending_dns_request_t; + +static gboolean host_resolved(gpointer data) +{ + pending_dns_request_t *req = (pending_dns_request_t*)data; + GSList *hosts = NULL; + hosts = g_slist_append(hosts, GINT_TO_POINTER(req->addrlen)); + hosts = g_slist_append(hosts, req->addr); + req->callback(hosts, req->data, NULL); + g_free(req); + return FALSE; +} + +int +gaim_gethostbyname_async(const char *hostname, int port, + GaimProxyDnsConnectFunction callback, gpointer data) +{ + struct sockaddr_in sin; + pending_dns_request_t *req; + + if (!inet_aton(hostname, &sin.sin_addr)) { + struct hostent *hp; + if(!(hp = gethostbyname(hostname))) { + gaim_debug_error("dns", + "gaim_gethostbyname(\"%s\", %d) failed: %d\n", + hostname, port, h_errno); + return -1; + } + memset(&sin, 0, sizeof(struct sockaddr_in)); + memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); + sin.sin_family = hp->h_addrtype; + } else + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + + req = g_new(pending_dns_request_t, 1); + req->addr = (struct sockaddr*) g_memdup(&sin, sizeof(sin)); + req->addrlen = sizeof(sin); + req->data = data; + req->callback = callback; + gaim_timeout_add(10, host_resolved, req); + return 0; +} + +#endif /* not __unix__ or __APPLE__ or _WIN32 */ Property changes on: trunk/src/dnsquery.c ___________________________________________________________________ Name: svn:mime-type + text/plain Name: svn:eol-style + native Added: trunk/src/dnsquery.h =================================================================== --- trunk/src/dnsquery.h (rev 0) +++ trunk/src/dnsquery.h 2006-08-18 07:20:31 UTC (rev 16833) @@ -0,0 +1,67 @@ +/** + * @file dnsquery.h DNS query API + * @ingroup core + * + * gaim + * + * Gaim is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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 + */ +#ifndef _GAIM_DNSQUERY_H_ +#define _GAIM_DNSQUERY_H_ + +#include <glib.h> +#include "eventloop.h" + +/** + * The "hosts" parameter is a linked list containing pairs of + * one size_t addrlen and one struct sockaddr *addr. + */ +typedef void (*GaimProxyDnsConnectFunction)(GSList *hosts, gpointer data, const char *error_message); + + +#include "account.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**************************************************************************/ +/** @name DNS query API */ +/**************************************************************************/ +/*@{*/ + +/** + * Do an async dns query + * + * @param hostname The hostname to resolve + * @param port A portnumber which is stored in the struct sockaddr + * @param callback Callback to call after resolving + * @param data Extra data for the callback function + * + * @return Zero indicates the connection is pending. Any other value indicates failure. + */ +int gaim_gethostbyname_async(const char *hostname, int port, GaimProxyDnsConnectFunction callback, gpointer data); + +/*@}*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _GAIM_DNSQUERY_H_ */ Property changes on: trunk/src/dnsquery.h ___________________________________________________________________ Name: svn:mime-type + text/plain Name: svn:eol-style + native Modified: trunk/src/protocols/simple/simple.c =================================================================== --- trunk/src/protocols/simple/simple.c 2006-08-18 06:40:16 UTC (rev 16832) +++ trunk/src/protocols/simple/simple.c 2006-08-18 07:20:31 UTC (rev 16833) @@ -29,6 +29,7 @@ #include "accountopt.h" #include "blist.h" #include "conversation.h" +#include "dnsquery.h" #include "debug.h" #include "notify.h" #include "privacy.h" Modified: trunk/src/proxy.c =================================================================== --- trunk/src/proxy.c 2006-08-18 06:40:16 UTC (rev 16832) +++ trunk/src/proxy.c 2006-08-18 07:20:31 UTC (rev 16833) @@ -32,6 +32,7 @@ #include "internal.h" #include "cipher.h" #include "debug.h" +#include "dnsquery.h" #include "notify.h" #include "ntlm.h" #include "prefs.h" @@ -351,681 +352,7 @@ gaim_proxy_connect_info_destroy(connect_info); } -#if defined(__unix__) || defined(__APPLE__) - -/* - * This structure represents both a pending DNS request and - * a free child process. - */ -typedef struct { - char *host; - int port; - GaimProxyDnsConnectFunction callback; - gpointer data; - guint inpa; - int fd_in, fd_out; - pid_t dns_pid; -} pending_dns_request_t; - -static GSList *free_dns_children = NULL; -static GQueue *queued_requests = NULL; - -static int number_of_dns_children = 0; - -static const int MAX_DNS_CHILDREN = 2; - -typedef struct { - char hostname[512]; - int port; -} dns_params_t; - -typedef struct { - dns_params_t params; - GaimProxyDnsConnectFunction callback; - gpointer data; -} queued_dns_request_t; - -/* - * Begin the DNS resolver child process functions. - */ -#ifdef HAVE_SIGNAL_H static void -trap_gdb_bug() -{ - const char *message = - "Gaim's DNS child got a SIGTRAP signal.\n" - "This can be caused by trying to run gaim inside gdb.\n" - "There is a known gdb bug which prevents this. Supposedly gaim\n" - "should have detected you were using gdb and used an ugly hack,\n" - "check cope_with_gdb_brokenness() in proxy.c.\n\n" - "For more info about this bug, see http://sources.redhat.com/ml/gdb/2001-07/msg00349.html\n"; - fputs("\n* * *\n",stderr); - fputs(message,stderr); - fputs("* * *\n\n",stderr); - execlp("xmessage","xmessage","-center", message, NULL); - _exit(1); -} -#endif - -static void -cope_with_gdb_brokenness() -{ -#ifdef __linux__ - static gboolean already_done = FALSE; - char s[256], e[512]; - int n; - pid_t ppid; - - if(already_done) - return; - already_done = TRUE; - ppid = getppid(); - snprintf(s, sizeof(s), "/proc/%d/exe", ppid); - n = readlink(s, e, sizeof(e)); - if(n < 0) - return; - - e[MIN(n,sizeof(e)-1)] = '\0'; - - if(strstr(e,"gdb")) { - gaim_debug_info("dns", - "Debugger detected, performing useless query...\n"); - gethostbyname("x.x.x.x.x"); - } -#endif -} - -static void -gaim_dns_resolverthread(int child_out, int child_in, gboolean show_debug) -{ - dns_params_t dns_params; - const size_t zero = 0; - int rc; -#ifdef HAVE_GETADDRINFO - struct addrinfo hints, *res, *tmp; - char servname[20]; -#else - struct sockaddr_in sin; - const size_t addrlen = sizeof(sin); -#endif - -#ifdef HAVE_SIGNAL_H - signal(SIGHUP, SIG_DFL); - signal(SIGINT, SIG_DFL); - signal(SIGQUIT, SIG_DFL); - signal(SIGCHLD, SIG_DFL); - signal(SIGTERM, SIG_DFL); - signal(SIGTRAP, trap_gdb_bug); -#endif - - /* - * We resolve 1 host name for each iteration of this - * while loop. - * - * The top half of this reads in the hostname and port - * number from the socket with our parent. The bottom - * half of this resolves the IP (blocking) and sends - * the result back to our parent, when finished. - */ - while (1) { - const char ch = 'Y'; - fd_set fds; - struct timeval tv = { .tv_sec = 40 , .tv_usec = 0 }; - FD_ZERO(&fds); - FD_SET(child_in, &fds); - rc = select(child_in + 1, &fds, NULL, NULL, &tv); - if (!rc) { - if (show_debug) - printf("dns[%d]: nobody needs me... =(\n", getpid()); - break; - } - rc = read(child_in, &dns_params, sizeof(dns_params_t)); - if (rc < 0) { - perror("read()"); - break; - } - if (rc == 0) { - if (show_debug) - printf("dns[%d]: Oops, father has gone, wait for me, wait...!\n", getpid()); - _exit(0); - } - if (dns_params.hostname[0] == '\0') { - printf("dns[%d]: hostname = \"\" (port = %d)!!!\n", getpid(), dns_params.port); - _exit(1); - } - /* Tell our parent that we read the data successfully */ - write(child_out, &ch, sizeof(ch)); - - /* We have the hostname and port, now resolve the IP */ - -#ifdef HAVE_GETADDRINFO - g_snprintf(servname, sizeof(servname), "%d", dns_params.port); - memset(&hints, 0, sizeof(hints)); - - /* This is only used to convert a service - * name to a port number. As we know we are - * passing a number already, we know this - * value will not be really used by the C - * library. - */ - hints.ai_socktype = SOCK_STREAM; - rc = getaddrinfo(dns_params.hostname, servname, &hints, &res); - write(child_out, &rc, sizeof(rc)); - if (rc != 0) { - close(child_out); - if (show_debug) - printf("dns[%d] Error: getaddrinfo returned %d\n", - getpid(), rc); - dns_params.hostname[0] = '\0'; - continue; - } - tmp = res; - while (res) { - size_t ai_addrlen = res->ai_addrlen; - write(child_out, &ai_addrlen, sizeof(ai_addrlen)); - write(child_out, res->ai_addr, res->ai_addrlen); - res = res->ai_next; - } - freeaddrinfo(tmp); - write(child_out, &zero, sizeof(zero)); -#else - if (!inet_aton(dns_params.hostname, &sin.sin_addr)) { - struct hostent *hp; - if (!(hp = gethostbyname(dns_params.hostname))) { - write(child_out, &h_errno, sizeof(int)); - close(child_out); - if (show_debug) - printf("DNS Error: %d\n", h_errno); - _exit(0); - } - memset(&sin, 0, sizeof(struct sockaddr_in)); - memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); - sin.sin_family = hp->h_addrtype; - } else - sin.sin_family = AF_INET; - - sin.sin_port = htons(dns_params.port); - write(child_out, &addrlen, sizeof(addrlen)); - write(child_out, &sin, addrlen); - write(child_out, &zero, sizeof(zero)); -#endif - dns_params.hostname[0] = '\0'; - } - - close(child_out); - close(child_in); - - _exit(0); -} - -static pending_dns_request_t * -gaim_dns_new_resolverthread(gboolean show_debug) -{ - pending_dns_request_t *req; - int child_out[2], child_in[2]; - - /* Create pipes for communicating with the child process */ - if (pipe(child_out) || pipe(child_in)) { - gaim_debug_error("dns", - "Could not create pipes: %s\n", strerror(errno)); - return NULL; - } - - req = g_new(pending_dns_request_t, 1); - - cope_with_gdb_brokenness(); - - /* Fork! */ - req->dns_pid = fork(); - - /* If we are the child process... */ - if (req->dns_pid == 0) { - /* We should not access the parent's side of the pipes, so close them */ - close(child_out[0]); - close(child_in[1]); - - gaim_dns_resolverthread(child_out[1], child_in[0], show_debug); - /* The thread calls _exit() rather than returning, so we never get here */ - } - - /* We should not access the child's side of the pipes, so close them */ - close(child_out[1]); - close(child_in[0]); - if (req->dns_pid == -1) { - gaim_debug_error("dns", - "Could not create child process for DNS: %s\n", - strerror(errno)); - g_free(req); - return NULL; - } - - req->fd_out = child_out[0]; - req->fd_in = child_in[1]; - number_of_dns_children++; - gaim_debug_info("dns", - "Created new DNS child %d, there are now %d children.\n", - req->dns_pid, number_of_dns_children); - - return req; -} -/* - * End the DNS resolver child process functions. - */ - -/* - * Begin the functions for dealing with the DNS child processes. - */ -static void -req_free(pending_dns_request_t *req) -{ - g_return_if_fail(req != NULL); - - close(req->fd_in); - close(req->fd_out); - - g_free(req->host); - g_free(req); - - number_of_dns_children--; -} - -static int -send_dns_request_to_child(pending_dns_request_t *req, dns_params_t *dns_params) -{ - char ch; - int rc; - pid_t pid; - - /* This waitpid might return the child's PID if it has recently - * exited, or it might return an error if it exited "long - * enough" ago that it has already been reaped; in either - * instance, we can't use it. */ - if ((pid = waitpid (req->dns_pid, NULL, WNOHANG)) > 0) { - gaim_debug_warning("dns", - "DNS child %d no longer exists\n", req->dns_pid); - return -1; - } else if (pid < 0) { - gaim_debug_warning("dns", - "Wait for DNS child %d failed: %s\n", - req->dns_pid, strerror(errno)); - return -1; - } - - /* Let's contact this lost child! */ - rc = write(req->fd_in, dns_params, sizeof(*dns_params)); - if (rc < 0) { - gaim_debug_error("dns", - "Unable to write to DNS child %d: %d\n", - req->dns_pid, strerror(errno)); - close(req->fd_in); - return -1; - } - - g_return_val_if_fail(rc == sizeof(*dns_params), -1); - - /* Did you hear me? (This avoids some race conditions) */ - rc = read(req->fd_out, &ch, sizeof(ch)); - if (rc != 1 || ch != 'Y') - { - gaim_debug_warning("dns", - "DNS child %d not responding. Killing it!\n", - req->dns_pid); - kill(req->dns_pid, SIGKILL); - return -1; - } - - gaim_debug_info("dns", - "Successfully sent DNS request to child %d\n", req->dns_pid); - - return 0; -} - -static void -host_resolved(gpointer data, gint source, GaimInputCondition cond); - -static void -release_dns_child(pending_dns_request_t *req) -{ - g_free(req->host); - req->host = NULL; - - if (queued_requests && !g_queue_is_empty(queued_requests)) { - queued_dns_request_t *r = g_queue_pop_head(queued_requests); - req->host = g_strdup(r->params.hostname); - req->port = r->params.port; - req->callback = r->callback; - req->data = r->data; - - gaim_debug_info("dns", - "Processing queued DNS query for '%s' with child %d\n", - req->host, req->dns_pid); - - if (send_dns_request_to_child(req, &(r->params)) != 0) { - req_free(req); - req = NULL; - - gaim_debug_warning("dns", - "Intent of process queued query of '%s' failed, " - "requeueing...\n", r->params.hostname); - g_queue_push_head(queued_requests, r); - } else { - req->inpa = gaim_input_add(req->fd_out, GAIM_INPUT_READ, host_resolved, req); - g_free(r); - } - - } else { - req->host = NULL; - req->callback = NULL; - req->data = NULL; - free_dns_children = g_slist_append(free_dns_children, req); - } -} - -static void -host_resolved(gpointer data, gint source, GaimInputCondition cond) -{ - pending_dns_request_t *req = (pending_dns_request_t*)data; - int rc, err; - GSList *hosts = NULL; - struct sockaddr *addr = NULL; - size_t addrlen; - - gaim_debug_info("dns", "Got response for '%s'\n", req->host); - gaim_input_remove(req->inpa); - - rc = read(req->fd_out, &err, sizeof(err)); - if ((rc == 4) && (err != 0)) - { - char message[1024]; -#ifdef HAVE_GETADDRINFO - g_snprintf(message, sizeof(message), "DNS error: %s (pid=%d)", - gai_strerror(err), req->dns_pid); -#else - g_snprintf(message, sizeof(message), "DNS error: %d (pid=%d)", - err, req->dns_pid); -#endif - gaim_debug_error("dns", "%s\n", message); - req->callback(NULL, req->data, message); - release_dns_child(req); - return; - } - if (rc > 0) - { - while (rc > 0) { - rc = read(req->fd_out, &addrlen, sizeof(addrlen)); - if (rc > 0 && addrlen > 0) { - addr = g_malloc(addrlen); - rc = read(req->fd_out, addr, addrlen); - hosts = g_slist_append(hosts, GINT_TO_POINTER(addrlen)); - hosts = g_slist_append(hosts, addr); - } else { - break; - } - } - } else if (rc == -1) { - char message[1024]; - g_snprintf(message, sizeof(message), "Error reading from DNS child: %s",strerror(errno)); - gaim_debug_error("dns", "%s\n", message); - req->callback(NULL, req->data, message); - req_free(req); - return; - } else if (rc == 0) { - char message[1024]; - g_snprintf(message, sizeof(message), "EOF reading from DNS child"); - close(req->fd_out); - gaim_debug_error("dns", "%s\n", message); - req->callback(NULL, req->data, message); - req_free(req); - return; - } - -/* wait4(req->dns_pid, NULL, WNOHANG, NULL); */ - - req->callback(hosts, req->data, NULL); - - release_dns_child(req); -} -/* - * End the functions for dealing with the DNS child processes. - */ - -int -gaim_gethostbyname_async(const char *hostname, int port, GaimProxyDnsConnectFunction callback, gpointer data) -{ - pending_dns_request_t *req = NULL; - dns_params_t dns_params; - gchar *host_temp; - gboolean show_debug; - - show_debug = gaim_debug_is_enabled(); - - host_temp = g_strstrip(g_strdup(hostname)); - strncpy(dns_params.hostname, host_temp, sizeof(dns_params.hostname) - 1); - g_free(host_temp); - dns_params.hostname[sizeof(dns_params.hostname) - 1] = '\0'; - dns_params.port = port; - - /* - * If we have any children, attempt to have them perform the DNS - * query. If we're able to send the query to a child, then req - * will be set to the pending_dns_request_t. Otherwise, req will - * be NULL and we'll need to create a new DNS request child. - */ - while (free_dns_children != NULL) { - req = free_dns_children->data; - free_dns_children = g_slist_remove(free_dns_children, req); - - if (send_dns_request_to_child(req, &dns_params) == 0) - /* We found an acceptable child, yay */ - break; - - req_free(req); - req = NULL; - } - - /* We need to create a new DNS request child */ - if (req == NULL) { - if (number_of_dns_children >= MAX_DNS_CHILDREN) { - queued_dns_request_t *r = g_new(queued_dns_request_t, 1); - memcpy(&(r->params), &dns_params, sizeof(dns_params)); - r->callback = callback; - r->data = data; - if (!queued_requests) - queued_requests = g_queue_new(); - g_queue_push_tail(queued_requests, r); - - gaim_debug_info("dns", - "DNS query for '%s' queued\n", dns_params.hostname); - - return 0; - } - - req = gaim_dns_new_resolverthread(show_debug); - if (req == NULL) - { - gaim_debug_error("proxy", "oh dear, this is going to explode, I give up\n"); - return -1; - } - send_dns_request_to_child(req, &dns_params); - } - - req->host = g_strdup(hostname); - req->port = port; - req->callback = callback; - req->data = data; - req->inpa = gaim_input_add(req->fd_out, GAIM_INPUT_READ, host_resolved, req); - - return 0; -} - -#elif defined _WIN32 /* end __unix__ || __APPLE__ */ - -typedef struct _dns_tdata { - char *hostname; - int port; - GaimProxyDnsConnectFunction callback; - gpointer data; - GSList *hosts; - char *errmsg; -} dns_tdata; - -static gboolean dns_main_thread_cb(gpointer data) { - dns_tdata *td = (dns_tdata*)data; - if (td->errmsg != NULL) { - gaim_debug_info("dns", "%s\n", td->errmsg); - } - td->callback(td->hosts, td->data, td->errmsg); - g_free(td->hostname); - g_free(td->errmsg); - g_free(td); - return FALSE; -} - -static gpointer dns_thread(gpointer data) { - -#ifdef HAVE_GETADDRINFO - int rc; - struct addrinfo hints, *res, *tmp; - char servname[20]; -#else - struct sockaddr_in sin; - struct hostent *hp; -#endif - dns_tdata *td = (dns_tdata*)data; - -#ifdef HAVE_GETADDRINFO - g_snprintf(servname, sizeof(servname), "%d", td->port); - memset(&hints,0,sizeof(hints)); - - /* This is only used to convert a service - * name to a port number. As we know we are - * passing a number already, we know this - * value will not be really used by the C - * library. - */ - hints.ai_socktype = SOCK_STREAM; - if ((rc = getaddrinfo(td->hostname, servname, &hints, &res)) == 0) { - tmp = res; - while(res) { - td->hosts = g_slist_append(td->hosts, - GSIZE_TO_POINTER(res->ai_addrlen)); - td->hosts = g_slist_append(td->hosts, - g_memdup(res->ai_addr, res->ai_addrlen)); - res = res->ai_next; - } - freeaddrinfo(tmp); - } else { - td->errmsg = g_strdup_printf("DNS getaddrinfo(\"%s\", \"%s\") error: %d", td->hostname, servname, rc); - } -#else - if ((hp = gethostbyname(td->hostname))) { - memset(&sin, 0, sizeof(struct sockaddr_in)); - memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); - sin.sin_family = hp->h_addrtype; - sin.sin_port = htons(td->port); - - td->hosts = g_slist_append(td->hosts, - GSIZE_TO_POINTER(sizeof(sin))); - td->hosts = g_slist_append(td->hosts, - g_memdup(&sin, sizeof(sin))); - } else { - td->errmsg = g_strdup_printf("DNS gethostbyname(\"%s\") error: %d", td->hostname, h_errno); - } -#endif - /* back to main thread */ - g_idle_add(dns_main_thread_cb, td); - return 0; -} - -int -gaim_gethostbyname_async(const char *hostname, int port, - GaimProxyDnsConnectFunction callback, gpointer data) -{ - dns_tdata *td; - struct sockaddr_in sin; - GError* err = NULL; - - if(inet_aton(hostname, &sin.sin_addr)) { - GSList *hosts = NULL; - sin.sin_family = AF_INET; - sin.sin_port = htons(port); - hosts = g_slist_append(hosts, GINT_TO_POINTER(sizeof(sin))); - hosts = g_slist_append(hosts, g_memdup(&sin, sizeof(sin))); - callback(hosts, data, NULL); - return 0; - } - - gaim_debug_info("dns", "DNS Lookup for: %s\n", hostname); - td = g_new0(dns_tdata, 1); - td->hostname = g_strdup(hostname); - td->port = port; - td->callback = callback; - td->data = data; - - if(!g_thread_create(dns_thread, td, FALSE, &err)) { - gaim_debug_error("dns", "DNS thread create failure: %s\n", err?err->message:""); - g_error_free(err); - g_free(td->hostname); - g_free(td); - return -1; - } - return 0; -} - -#else /* not __unix__ or __APPLE__ or _WIN32 */ - -typedef struct { - gpointer data; - size_t addrlen; - struct sockaddr *addr; - GaimProxyDnsConnectFunction callback; -} pending_dns_request_t; - -static gboolean host_resolved(gpointer data) -{ - pending_dns_request_t *req = (pending_dns_request_t*)data; - GSList *hosts = NULL; - hosts = g_slist_append(hosts, GINT_TO_POINTER(req->addrlen)); - hosts = g_slist_append(hosts, req->addr); - req->callback(hosts, req->data, NULL); - g_free(req); - return FALSE; -} - -int -gaim_gethostbyname_async(const char *hostname, int port, - GaimProxyDnsConnectFunction callback, gpointer data) -{ - struct sockaddr_in sin; - pending_dns_request_t *req; - - if (!inet_aton(hostname, &sin.sin_addr)) { - struct hostent *hp; - if(!(hp = gethostbyname(hostname))) { - gaim_debug_error("dns", - "gaim_gethostbyname(\"%s\", %d) failed: %d\n", - hostname, port, h_errno); - return -1; - } - memset(&sin, 0, sizeof(struct sockaddr_in)); - memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); - sin.sin_family = hp->h_addrtype; - } else - sin.sin_family = AF_INET; - sin.sin_port = htons(port); - - req = g_new(pending_dns_request_t, 1); - req->addr = (struct sockaddr*) g_memdup(&sin, sizeof(sin)); - req->addrlen = sizeof(sin); - req->data = data; - req->callback = callback; - gaim_timeout_add(10, host_resolved, req); - return 0; -} - -#endif /* not __unix__ or __APPLE__ or _WIN32 */ - -static void no_one_calls(gpointer data, gint source, GaimInputCondition cond) { GaimProxyConnectInfo *connect_info = data; Modified: trunk/src/proxy.h =================================================================== --- trunk/src/proxy.h 2006-08-18 06:40:16 UTC (rev 16832) +++ trunk/src/proxy.h 2006-08-18 07:20:31 UTC (rev 16833) @@ -60,13 +60,7 @@ typedef void (*GaimProxyConnectFunction)(gpointer data, gint source, const gchar *error_message); -/** - * The "hosts" parameter is a linked list containing pairs of - * one size_t addrlen and one struct sockaddr *addr. - */ -typedef void (*GaimProxyDnsConnectFunction)(GSList *hosts, gpointer data, const char *error_message); - #include "account.h" #ifdef __cplusplus @@ -278,18 +272,6 @@ */ void gaim_proxy_connect_cancel(GaimProxyConnectInfo *connect_info); -/** - * Do an async dns query - * - * @param hostname The hostname to resolve - * @param port A portnumber which is stored in the struct sockaddr - * @param callback Callback to call after resolving - * @param data Extra data for the callback function - * - * @return Zero indicates the connection is pending. Any other value indicates failure. - */ -int gaim_gethostbyname_async(const char *hostname, int port, GaimProxyDnsConnectFunction callback, gpointer data); - /*@}*/ #ifdef __cplusplus Modified: trunk/src/stun.c =================================================================== --- trunk/src/stun.c 2006-08-18 06:40:16 UTC (rev 16832) +++ trunk/src/stun.c 2006-08-18 07:20:31 UTC (rev 16833) @@ -40,6 +40,7 @@ #include "debug.h" #include "account.h" +#include "dnsquery.h" #include "dnssrv.h" #include "network.h" #include "proxy.h" This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <the...@us...> - 2006-08-18 07:40:28
|
Revision: 16835 Author: thekingant Date: 2006-08-18 00:40:17 -0700 (Fri, 18 Aug 2006) ViewCVS: http://svn.sourceforge.net/gaim/?rev=16835&view=rev Log Message: ----------- Change the DNS query function to return a struct that will eventually allow the query to be canceled. Also rename two of the DNS query things Modified Paths: -------------- trunk/src/dnsquery.c trunk/src/dnsquery.h trunk/src/protocols/simple/simple.c trunk/src/proxy.c trunk/src/stun.c Modified: trunk/src/dnsquery.c =================================================================== --- trunk/src/dnsquery.c 2006-08-18 07:22:55 UTC (rev 16834) +++ trunk/src/dnsquery.c 2006-08-18 07:40:17 UTC (rev 16835) @@ -35,6 +35,9 @@ * DNS query API **************************************************************************/ +struct _GaimDnsQueryData { +}; + #if defined(__unix__) || defined(__APPLE__) #define MAX_DNS_CHILDREN 4 @@ -46,7 +49,7 @@ typedef struct { char *host; int port; - GaimProxyDnsConnectFunction callback; + GaimDnsQueryConnectFunction callback; gpointer data; guint inpa; int fd_in, fd_out; @@ -65,7 +68,7 @@ typedef struct { dns_params_t params; - GaimProxyDnsConnectFunction callback; + GaimDnsQueryConnectFunction callback; gpointer data; } queued_dns_request_t; @@ -473,8 +476,8 @@ * End the functions for dealing with the DNS child processes. */ -int -gaim_gethostbyname_async(const char *hostname, int port, GaimProxyDnsConnectFunction callback, gpointer data) +GaimDnsQueryData * +gaim_dnsquery_a(const char *hostname, int port, GaimDnsQueryConnectFunction callback, gpointer data) { pending_dns_request_t *req = NULL; dns_params_t dns_params; @@ -521,14 +524,14 @@ gaim_debug_info("dns", "DNS query for '%s' queued\n", dns_params.hostname); - return 0; + return (GaimDnsQueryData *)1; } req = gaim_dns_new_resolverthread(show_debug); if (req == NULL) { gaim_debug_error("proxy", "oh dear, this is going to explode, I give up\n"); - return -1; + return NULL; } send_dns_request_to_child(req, &dns_params); } @@ -539,7 +542,7 @@ req->data = data; req->inpa = gaim_input_add(req->fd_out, GAIM_INPUT_READ, host_resolved, req); - return 0; + return (GaimDnsQueryData *)1; } #elif defined _WIN32 /* end __unix__ || __APPLE__ */ @@ -547,7 +550,7 @@ typedef struct _dns_tdata { char *hostname; int port; - GaimProxyDnsConnectFunction callback; + GaimDnsQueryConnectFunction callback; gpointer data; GSList *hosts; char *errmsg; @@ -621,9 +624,9 @@ return 0; } -int -gaim_gethostbyname_async(const char *hostname, int port, - GaimProxyDnsConnectFunction callback, gpointer data) +GaimDnsQueryData * +gaim_dnsquery_a(const char *hostname, int port, + GaimDnsQueryConnectFunction callback, gpointer data) { dns_tdata *td; struct sockaddr_in sin; @@ -636,7 +639,7 @@ hosts = g_slist_append(hosts, GINT_TO_POINTER(sizeof(sin))); hosts = g_slist_append(hosts, g_memdup(&sin, sizeof(sin))); callback(hosts, data, NULL); - return 0; + return (GaimDnsQueryData *)1; } gaim_debug_info("dns", "DNS Lookup for: %s\n", hostname); @@ -651,9 +654,9 @@ g_error_free(err); g_free(td->hostname); g_free(td); - return -1; + return NULL; } - return 0; + return (GaimDnsQueryData *)1; } #else /* not __unix__ or __APPLE__ or _WIN32 */ @@ -662,7 +665,7 @@ gpointer data; size_t addrlen; struct sockaddr *addr; - GaimProxyDnsConnectFunction callback; + GaimDnsQueryConnectFunction callback; } pending_dns_request_t; static gboolean host_resolved(gpointer data) @@ -676,9 +679,9 @@ return FALSE; } -int -gaim_gethostbyname_async(const char *hostname, int port, - GaimProxyDnsConnectFunction callback, gpointer data) +GaimDnsQueryData * +gaim_dnsquery_a(const char *hostname, int port, + GaimDnsQueryConnectFunction callback, gpointer data) { struct sockaddr_in sin; pending_dns_request_t *req; @@ -689,7 +692,7 @@ gaim_debug_error("dns", "gaim_gethostbyname(\"%s\", %d) failed: %d\n", hostname, port, h_errno); - return -1; + return NULL; } memset(&sin, 0, sizeof(struct sockaddr_in)); memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); @@ -704,7 +707,7 @@ req->data = data; req->callback = callback; gaim_timeout_add(10, host_resolved, req); - return 0; + return (GaimDnsQueryData *)1; } #endif /* not __unix__ or __APPLE__ or _WIN32 */ Modified: trunk/src/dnsquery.h =================================================================== --- trunk/src/dnsquery.h 2006-08-18 07:22:55 UTC (rev 16834) +++ trunk/src/dnsquery.h 2006-08-18 07:40:17 UTC (rev 16835) @@ -28,11 +28,13 @@ #include <glib.h> #include "eventloop.h" +typedef struct _GaimDnsQueryData GaimDnsQueryData; + /** * The "hosts" parameter is a linked list containing pairs of * one size_t addrlen and one struct sockaddr *addr. */ -typedef void (*GaimProxyDnsConnectFunction)(GSList *hosts, gpointer data, const char *error_message); +typedef void (*GaimDnsQueryConnectFunction)(GSList *hosts, gpointer data, const char *error_message); #include "account.h" @@ -54,9 +56,11 @@ * @param callback Callback to call after resolving * @param data Extra data for the callback function * - * @return Zero indicates the connection is pending. Any other value indicates failure. + * @return NULL if there was an error, otherwise return a reference to + * a data structure that can be used to cancel the pending + * DNS query, if needed. */ -int gaim_gethostbyname_async(const char *hostname, int port, GaimProxyDnsConnectFunction callback, gpointer data); +GaimDnsQueryData *gaim_dnsquery_a(const char *hostname, int port, GaimDnsQueryConnectFunction callback, gpointer data); /*@}*/ Modified: trunk/src/protocols/simple/simple.c =================================================================== --- trunk/src/protocols/simple/simple.c 2006-08-18 07:22:55 UTC (rev 16834) +++ trunk/src/protocols/simple/simple.c 2006-08-18 07:40:17 UTC (rev 16835) @@ -1622,7 +1622,7 @@ } else { /* UDP */ gaim_debug_info("simple", "using udp with server %s and port %d\n", hostname, port); - gaim_gethostbyname_async(hostname, port, simple_udp_host_resolved, sip); + gaim_dnsquery_a(hostname, port, simple_udp_host_resolved, sip); } } Modified: trunk/src/proxy.c =================================================================== --- trunk/src/proxy.c 2006-08-18 07:22:55 UTC (rev 16834) +++ trunk/src/proxy.c 2006-08-18 07:40:17 UTC (rev 16835) @@ -47,6 +47,7 @@ int fd; guint inpa; GaimProxyInfo *gpi; + GaimDnsQueryData *query_data; /** * This contains alternating length/char* values. The char* @@ -1695,8 +1696,9 @@ return NULL; } - if (gaim_gethostbyname_async(connecthost, - connectport, connection_host_resolved, connect_info) != 0) + connect_info->query_data = gaim_dnsquery_a(connecthost, + connectport, connection_host_resolved, connect_info); + if (connect_info->query_data == NULL) { gaim_proxy_connect_info_destroy(connect_info); return NULL; @@ -1728,8 +1730,11 @@ connect_info->port = port; connect_info->gpi = gpi; - if (gaim_gethostbyname_async(gaim_proxy_info_get_host(gpi), - gaim_proxy_info_get_port(gpi), connection_host_resolved, connect_info) != 0) + connect_info->query_data = + gaim_dnsquery_a(gaim_proxy_info_get_host(gpi), + gaim_proxy_info_get_port(gpi), + connection_host_resolved, connect_info); + if (connect_info->query_data == NULL) { gaim_proxy_connect_info_destroy(connect_info); return NULL; Modified: trunk/src/stun.c =================================================================== --- trunk/src/stun.c 2006-08-18 07:22:55 UTC (rev 16834) +++ trunk/src/stun.c 2006-08-18 07:40:17 UTC (rev 16835) @@ -360,7 +360,7 @@ gaim_debug_info("stun", "got %d SRV responses, server: %s, port: %d\n", results, servername, port); - gaim_gethostbyname_async(servername, port, hbn_cb, NULL); + gaim_dnsquery_a(servername, port, hbn_cb, NULL); g_free(resp); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <aar...@us...> - 2006-08-19 00:24:17
|
Revision: 16854 Author: aaronsheldon Date: 2006-08-18 17:24:14 -0700 (Fri, 18 Aug 2006) ViewCVS: http://svn.sourceforge.net/gaim/?rev=16854&view=rev Log Message: ----------- Generates the image for the about dialog on demand instead of making it a stock image which remains in memory forever. Modified Paths: -------------- trunk/src/gtkdialogs.c trunk/src/gtkstock.c trunk/src/gtkstock.h Modified: trunk/src/gtkdialogs.c =================================================================== --- trunk/src/gtkdialogs.c 2006-08-19 00:07:40 UTC (rev 16853) +++ trunk/src/gtkdialogs.c 2006-08-19 00:24:14 UTC (rev 16854) @@ -223,6 +223,44 @@ about = NULL; } +/* This function puts the version number onto the pixmap we use in the 'about' + * screen in Gaim. */ +static void +gaim_gtk_logo_versionize(GdkPixbuf **original, GtkWidget *widget) { + GdkPixmap *pixmap; + GtkStyle *style; + PangoContext *context; + PangoLayout *layout; + gchar *markup; + gint width, height; + gint lwidth = 0, lheight = 0; + + style = gtk_widget_get_style(widget); + + gdk_pixbuf_render_pixmap_and_mask(*original, &pixmap, NULL, 255); + width = gdk_pixbuf_get_width(*original); + height = gdk_pixbuf_get_height(*original); + g_object_unref(G_OBJECT(*original)); + + context = gtk_widget_get_pango_context(widget); + layout = pango_layout_new(context); + + markup = g_strdup_printf("<span foreground=\"#FFFFFF\" size=\"larger\">%s</span>", VERSION); + pango_layout_set_font_description(layout, style->font_desc); + pango_layout_set_markup(layout, markup, strlen(markup)); + g_free(markup); + + pango_layout_get_pixel_size(layout, &lwidth, &lheight); + gdk_draw_layout(GDK_DRAWABLE(pixmap), style->bg_gc[GTK_STATE_NORMAL], + width - (lwidth + 3), height - (lheight + 1), layout); + g_object_unref(G_OBJECT(layout)); + + *original = gdk_pixbuf_get_from_drawable(NULL, pixmap, NULL, + 0, 0, 0, 0, + width, height); + g_object_unref(G_OBJECT(pixmap)); +} + void gaim_gtkdialogs_about() { GtkWidget *hbox; @@ -236,6 +274,8 @@ GString *str; int i; AtkObject *obj; + char* filename; + GdkPixbuf *pixbuf; if (about != NULL) { gtk_window_present(GTK_WINDOW(about)); @@ -256,7 +296,17 @@ vbox = gtk_vbox_new(FALSE, GAIM_HIG_BORDER); gtk_container_add(GTK_CONTAINER(hbox), vbox); - logo = gtk_image_new_from_stock(GAIM_STOCK_LOGO, gtk_icon_size_from_name(GAIM_ICON_SIZE_LOGO)); + /* Generate a logo with a version number */ + logo = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_realize(logo); + filename = g_build_filename(DATADIR, "pixmaps", "gaim", "logo.png", NULL); + pixbuf = gdk_pixbuf_new_from_file(filename, NULL); + g_free(filename); + gaim_gtk_logo_versionize(&pixbuf, logo); + gtk_widget_destroy(logo); + logo = gtk_image_new_from_pixbuf(pixbuf); + gdk_pixbuf_unref(pixbuf); + /* Insert the logo */ obj = gtk_widget_get_accessible(logo); atk_object_set_description(obj, "Gaim " VERSION); gtk_box_pack_start(GTK_BOX(vbox), logo, FALSE, FALSE, 0); Modified: trunk/src/gtkstock.c =================================================================== --- trunk/src/gtkstock.c 2006-08-19 00:07:40 UTC (rev 16853) +++ trunk/src/gtkstock.c 2006-08-19 00:24:14 UTC (rev 16854) @@ -93,7 +93,6 @@ { GAIM_STOCK_INVITE, NULL, GTK_STOCK_JUMP_TO }, { GAIM_STOCK_LINK, "buttons", "insert-link-small.png" }, { GAIM_STOCK_LOG, NULL, GTK_STOCK_DND_MULTIPLE }, - { GAIM_STOCK_LOGO, "gaim", "logo.png" }, { GAIM_STOCK_MODIFY, NULL, GTK_STOCK_PREFERENCES }, #if GTK_CHECK_VERSION(2,6,0) { GAIM_STOCK_PAUSE, NULL, GTK_STOCK_MEDIA_PAUSE }, @@ -174,42 +173,6 @@ return filename; } -static void -gaim_gtk_stock_versionize(GdkPixbuf **original, GtkWidget *widget) { - GdkPixmap *pixmap; - GtkStyle *style; - PangoContext *context; - PangoLayout *layout; - gchar *markup; - gint width, height; - gint lwidth = 0, lheight = 0; - - style = gtk_widget_get_style(widget); - - gdk_pixbuf_render_pixmap_and_mask(*original, &pixmap, NULL, 255); - width = gdk_pixbuf_get_width(*original); - height = gdk_pixbuf_get_height(*original); - g_object_unref(G_OBJECT(*original)); - - context = gtk_widget_get_pango_context(widget); - layout = pango_layout_new(context); - - markup = g_strdup_printf("<span foreground=\"#FFFFFF\" size=\"larger\">%s</span>", VERSION); - pango_layout_set_font_description(layout, style->font_desc); - pango_layout_set_markup(layout, markup, strlen(markup)); - g_free(markup); - - pango_layout_get_pixel_size(layout, &lwidth, &lheight); - gdk_draw_layout(GDK_DRAWABLE(pixmap), style->bg_gc[GTK_STATE_NORMAL], - width - (lwidth + 3), height - (lheight + 1), layout); - g_object_unref(G_OBJECT(layout)); - - *original = gdk_pixbuf_get_from_drawable(NULL, pixmap, NULL, - 0, 0, 0, 0, - width, height); - g_object_unref(G_OBJECT(pixmap)); -} - void gaim_gtk_stock_init(void) { @@ -255,9 +218,6 @@ g_free(filename); - if (!strcmp(stock_icons[i].name, GAIM_STOCK_LOGO)) - gaim_gtk_stock_versionize(&pixbuf, win); - iconset = gtk_icon_set_new_from_pixbuf(pixbuf); g_object_unref(G_OBJECT(pixbuf)); Modified: trunk/src/gtkstock.h =================================================================== --- trunk/src/gtkstock.h 2006-08-19 00:07:40 UTC (rev 16853) +++ trunk/src/gtkstock.h 2006-08-19 00:24:14 UTC (rev 16854) @@ -67,7 +67,6 @@ #define GAIM_STOCK_INVITE "gaim-invite" #define GAIM_STOCK_LINK "gaim-link" #define GAIM_STOCK_LOG "gaim-log" -#define GAIM_STOCK_LOGO "gaim-logo" #define GAIM_STOCK_MODIFY "gaim-modify" #define GAIM_STOCK_OPEN_MAIL "gaim-stock-open-mail" #define GAIM_STOCK_PAUSE "gaim-pause" This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |