diff -r 198222e01a7d -r 46933dc62880 pidgin/gtkmain.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtkmain.c Sun Apr 15 03:43:17 2007 +0000 @@ -0,0 +1,788 @@ +/* + * pidgin + * + * Pidgin 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 "pidgin.h" + +#include "account.h" +#include "conversation.h" +#include "core.h" +#include "debug.h" +#include "eventloop.h" +#include "ft.h" +#include "log.h" +#include "notify.h" +#include "prefs.h" +#include "prpl.h" +#include "pounce.h" +#include "sound.h" +#include "status.h" +#include "util.h" +#include "whiteboard.h" + +#include "gtkaccount.h" +#include "gtkblist.h" +#include "gtkconn.h" +#include "gtkconv.h" +#include "gtkdebug.h" +#include "gtkdialogs.h" +#include "gtkdocklet.h" +#include "gtkeventloop.h" +#include "gtkft.h" +#include "gtkidle.h" +#include "gtklog.h" +#include "gtknotify.h" +#include "gtkplugin.h" +#include "gtkpounce.h" +#include "gtkprefs.h" +#include "gtkprivacy.h" +#include "gtkrequest.h" +#include "gtkroomlist.h" +#include "gtksavedstatuses.h" +#include "gtksession.h" +#include "gtksound.h" +#include "gtkthemes.h" +#include "gtkutils.h" +#include "pidginstock.h" +#include "gtkwhiteboard.h" + +#ifdef HAVE_SIGNAL_H +# include +#endif + +#include + +#ifdef HAVE_STARTUP_NOTIFICATION +# define SN_API_NOT_YET_FROZEN +# include +# include +#endif + + + +#ifdef HAVE_STARTUP_NOTIFICATION +static SnLauncheeContext *sn_context = NULL; +static SnDisplay *sn_display = NULL; +#endif + +#ifdef HAVE_SIGNAL_H + +/* + * Lists of signals we wish to catch and those we wish to ignore. + * Each list terminated with -1 + */ +static int catch_sig_list[] = { + SIGSEGV, + SIGHUP, + SIGINT, + SIGTERM, + SIGQUIT, + SIGCHLD, + SIGALRM, + -1 +}; + +static int ignore_sig_list[] = { + SIGPIPE, + -1 +}; +#endif + +static int +dologin_named(const char *name) +{ + PurpleAccount *account; + char **names; + int i; + int ret = -1; + + if (name != NULL) { /* list of names given */ + names = g_strsplit(name, ",", 64); + for (i = 0; names[i] != NULL; i++) { + account = purple_accounts_find(names[i], NULL); + if (account != NULL) { /* found a user */ + ret = 0; + purple_account_connect(account); + } + } + g_strfreev(names); + } else { /* no name given, use the first account */ + GList *accounts; + + accounts = purple_accounts_get_all(); + if (accounts != NULL) + { + account = (PurpleAccount *)accounts->data; + ret = 0; + purple_account_connect(account); + } + } + + return ret; +} + +#ifdef HAVE_SIGNAL_H +static void sighandler(int sig); + +/** + * Reap all our dead children. Sometimes Purple forks off a separate + * process to do some stuff. When that process exits we are + * informed about it so that we can call waitpid() and let it + * stop being a zombie. + * + * We used to do this immediately when our signal handler was + * called, but because of GStreamer we now wait one second before + * reaping anything. Why? For some reason GStreamer fork()s + * during their initialization process. I don't understand why... + * but they do it, and there's nothing we can do about it. + * + * Anyway, so then GStreamer waits for its child to die and then + * it continues with the initialization process. This means that + * we have a race condition where GStreamer is waitpid()ing for its + * child to die and we're catching the SIGCHLD signal. If GStreamer + * is awarded the zombied process then everything is ok. But if Purple + * reaps the zombie process then the GStreamer initialization sequence + * fails. + * + * So the ugly solution is to wait a second to give GStreamer time to + * reap that bad boy. + * + * GStreamer 0.10.10 and newer have a gst_register_fork_set_enabled() + * function that can be called by applications to disable forking + * during initialization. But it's not in 0.10.0, so we shouldn't + * use it. + * + * All of this child process reaping stuff is currently only used for + * processes that were forked to play sounds. It's not needed for + * forked DNS child, which have their own waitpid() call. It might + * be wise to move this code into gtksound.c. + */ +static void +clean_pid() +{ + int status; + pid_t pid; + + do { + pid = waitpid(-1, &status, WNOHANG); + } while (pid != 0 && pid != (pid_t)-1); + + if ((pid == (pid_t) - 1) && (errno != ECHILD)) { + char errmsg[BUFSIZ]; + snprintf(errmsg, BUFSIZ, "Warning: waitpid() returned %d", pid); + perror(errmsg); + } + + /* Restore signal catching */ + signal(SIGALRM, sighandler); +} + +char *segfault_message; + +static void +sighandler(int sig) +{ + switch (sig) { + case SIGHUP: + purple_debug_warning("sighandler", "Caught signal %d\n", sig); + purple_connections_disconnect_all(); + break; + case SIGSEGV: + fprintf(stderr, "%s", segfault_message); + abort(); + break; + case SIGCHLD: + /* Restore signal catching */ + signal(SIGCHLD, sighandler); + alarm(1); + break; + case SIGALRM: + clean_pid(); + break; + default: + purple_debug_warning("sighandler", "Caught signal %d\n", sig); + purple_connections_disconnect_all(); + + purple_plugins_unload_all(); + + if (gtk_main_level()) + gtk_main_quit(); + exit(0); + } +} +#endif + +static int +ui_main() +{ +#ifndef _WIN32 + GList *icons = NULL; + GdkPixbuf *icon = NULL; + char *icon_path; + int i; + const char *icon_sizes[] = { + "16", + "24", + "32", + "48" + }; + +#endif + + pidginthemes_init(); + + pidgin_blist_setup_sort_methods(); + +#ifndef _WIN32 + /* use the nice PNG icon for all the windows */ + for(i=0; imessage); + g_error_free(error); + segfault_message = segfault_message_tmp; + } +#else + /* Don't mark this for translation. */ + segfault_message = g_strdup( + "Hi, user. We need to talk.\n" + "I think something's gone wrong here. It's probably my fault.\n" + "No, really, it's not you... it's me... no no no, I think we get along well\n" + "it's just that.... well, I want to see other people. I... what?!? NO! I \n" + "haven't been cheating on you!! How many times do you want me to tell you?! And\n" + "for the last time, it's just a rash!\n" + ); +#endif + + /* Let's not violate any PLA's!!!! */ + /* jseymour: whatever the fsck that means */ + /* Robot101: for some reason things like gdm like to block * + * useful signals like SIGCHLD, so we unblock all the ones we * + * declare a handler for. thanks JSeymour and Vann. */ + if (sigemptyset(&sigset)) { + snprintf(errmsg, BUFSIZ, "Warning: couldn't initialise empty signal set"); + perror(errmsg); + } + for(sig_indx = 0; catch_sig_list[sig_indx] != -1; ++sig_indx) { + if((prev_sig_disp = signal(catch_sig_list[sig_indx], sighandler)) == SIG_ERR) { + snprintf(errmsg, BUFSIZ, "Warning: couldn't set signal %d for catching", + catch_sig_list[sig_indx]); + perror(errmsg); + } + if(sigaddset(&sigset, catch_sig_list[sig_indx])) { + snprintf(errmsg, BUFSIZ, "Warning: couldn't include signal %d for unblocking", + catch_sig_list[sig_indx]); + perror(errmsg); + } + } + for(sig_indx = 0; ignore_sig_list[sig_indx] != -1; ++sig_indx) { + if((prev_sig_disp = signal(ignore_sig_list[sig_indx], SIG_IGN)) == SIG_ERR) { + snprintf(errmsg, BUFSIZ, "Warning: couldn't set signal %d to ignore", + ignore_sig_list[sig_indx]); + perror(errmsg); + } + } + + if (sigprocmask(SIG_UNBLOCK, &sigset, NULL)) { + snprintf(errmsg, BUFSIZ, "Warning: couldn't unblock signals"); + perror(errmsg); + } +#endif + + /* scan command-line options */ + opterr = 1; + while ((opt = getopt_long(argc, argv, +#ifndef _WIN32 + "c:dhnl::s:v", +#else + "c:dhnl::v", +#endif + long_options, NULL)) != -1) { + switch (opt) { + case 'c': /* config dir */ + g_free(opt_config_dir_arg); + opt_config_dir_arg = g_strdup(optarg); + break; + case 'd': /* debug */ + debug_enabled = TRUE; + break; + case 'h': /* help */ + opt_help = TRUE; + break; + case 'n': /* no autologin */ + opt_nologin = TRUE; + break; + case 'l': /* login, option username */ + opt_login = TRUE; + g_free(opt_login_arg); + if (optarg != NULL) + opt_login_arg = g_strdup(optarg); + break; + case 's': /* use existing session ID */ + g_free(opt_session_arg); + opt_session_arg = g_strdup(optarg); + break; + case 'v': /* version */ + opt_version = TRUE; + break; + case '?': /* show terse help */ + default: + show_usage(argv[0], TRUE); + return 0; + break; + } + } + + /* show help message */ + if (opt_help) { + show_usage(argv[0], FALSE); + return 0; + } + /* show version message */ + if (opt_version) { + printf(PIDGIN_NAME " %s\n", VERSION); + return 0; + } + + /* set a user-specified config directory */ + if (opt_config_dir_arg != NULL) { + purple_util_set_user_dir(opt_config_dir_arg); + } + + /* + * We're done piddling around with command line arguments. + * Fire up this baby. + */ + + purple_debug_set_enabled(debug_enabled); + + + search_path = g_build_filename(purple_user_dir(), "gtkrc-2.0", NULL); + gtk_rc_add_default_file(search_path); + g_free(search_path); + + gui_check = gtk_init_check(&argc, &argv); + if (!gui_check) { + char *display = gdk_get_display(); + + printf(PIDGIN_NAME " %s\n", VERSION); + + g_warning("cannot open display: %s", display ? display : "unset"); + g_free(display); + + return 1; + } + +#ifdef _WIN32 + winpidgin_init(hint); +#endif + + purple_core_set_ui_ops(pidgin_core_get_ui_ops()); + purple_eventloop_set_ui_ops(pidgin_eventloop_get_ui_ops()); + + /* + * Set plugin search directories. Give priority to the plugins + * in user's home directory. + */ + search_path = g_build_filename(purple_user_dir(), "plugins", NULL); + purple_plugins_add_search_path(search_path); + g_free(search_path); + purple_plugins_add_search_path(LIBDIR); + + if (!purple_core_init(PIDGIN_UI)) { + fprintf(stderr, + "Initialization of the " PIDGIN_NAME " core failed. Dumping core.\n" + "Please report this!\n"); + abort(); + } + + /* TODO: Move blist loading into purple_blist_init() */ + purple_set_blist(purple_blist_new()); + purple_blist_load(); + + /* TODO: Move prefs loading into purple_prefs_init() */ + purple_prefs_load(); + purple_prefs_update_old(); + pidgin_prefs_update_old(); + + /* load plugins we had when we quit */ + purple_plugins_load_saved(PIDGIN_PREFS_ROOT "/plugins/loaded"); + pidgin_docklet_init(); + + /* TODO: Move pounces loading into purple_pounces_init() */ + purple_pounces_load(); + + + /* HACK BY SEANEGAN: + * We've renamed prpl-oscar to prpl-aim and prpl-icq, accordingly. + * Let's do that change right here... after everything's loaded, but + * before anything has happened + */ + for (accounts = purple_accounts_get_all(); accounts != NULL; accounts = accounts->next) { + PurpleAccount *account = accounts->data; + if (!strcmp(purple_account_get_protocol_id(account), "prpl-oscar")) { + if (isdigit(*purple_account_get_username(account))) + purple_account_set_protocol_id(account, "prpl-icq"); + else + purple_account_set_protocol_id(account, "prpl-aim"); + } + } + + ui_main(); + +#ifdef USE_SM + pidgin_session_init(argv[0], opt_session_arg, opt_config_dir_arg); +#endif + if (opt_session_arg != NULL) { + g_free(opt_session_arg); + opt_session_arg = NULL; + } + if (opt_config_dir_arg != NULL) { + g_free(opt_config_dir_arg); + opt_config_dir_arg = NULL; + } + + /* + * We want to show the blist early in the init process so the + * user feels warm and fuzzy (not cold and prickley). + */ + purple_blist_show(); + + if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/enabled")) + pidgin_debug_window_show(); + + if (opt_login) { + dologin_ret = dologin_named(opt_login_arg); + if (opt_login_arg != NULL) { + g_free(opt_login_arg); + opt_login_arg = NULL; + } + } + + if (opt_nologin) + { + /* Set all accounts to "offline" */ + PurpleSavedStatus *saved_status; + + /* If we've used this type+message before, lookup the transient status */ + saved_status = purple_savedstatus_find_transient_by_type_and_message( + PURPLE_STATUS_OFFLINE, NULL); + + /* If this type+message is unique then create a new transient saved status */ + if (saved_status == NULL) + saved_status = purple_savedstatus_new(NULL, PURPLE_STATUS_OFFLINE); + + /* Set the status for each account */ + purple_savedstatus_activate(saved_status); + } + else + { + /* Everything is good to go--sign on already */ + if (!purple_prefs_get_bool("/core/savedstatus/startup_current_status")) + purple_savedstatus_activate(purple_savedstatus_get_startup()); + purple_accounts_restore_current_statuses(); + } + + if ((accounts = purple_accounts_get_all_active()) == NULL) + { + pidgin_accounts_window_show(); + } + else + { + g_list_free(accounts); + } + +#ifdef HAVE_STARTUP_NOTIFICATION + startup_notification_complete(); +#endif + +#ifdef _WIN32 + winpidgin_post_init(); +#endif + + gtk_main(); + +#ifdef HAVE_SIGNAL_H + g_free(segfault_message); +#endif + +#ifdef _WIN32 + winpidgin_cleanup(); +#endif + + return 0; +}