Move migration to core, check for errors
[pidgin-git.git] / pidgin / libpidgin.c
blob4af54024067c98e36b616149cd53089e99d322c1
1 /*
2 * pidgin
4 * Pidgin is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
6 * source distribution.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
24 #include "internal.h"
25 #include "pidgin.h"
27 #include "account.h"
28 #include "conversation.h"
29 #include "core.h"
30 #include "dbus-maybe.h"
31 #include "debug.h"
32 #include "eventloop.h"
33 #include "glibcompat.h"
34 #include "log.h"
35 #include "network.h"
36 #include "notify.h"
37 #include "prefs.h"
38 #include "protocol.h"
39 #include "pounce.h"
40 #include "sound.h"
41 #include "status.h"
42 #include "util.h"
43 #include "whiteboard.h"
44 #include "xfer.h"
46 #include "gtkaccount.h"
47 #include "gtkblist.h"
48 #include "gtkconn.h"
49 #include "gtkconv.h"
50 #include "gtkdebug.h"
51 #include "gtkdialogs.h"
52 #include "gtkdocklet.h"
53 #include "gtkeventloop.h"
54 #include "gtkxfer.h"
55 #include "gtkidle.h"
56 #include "gtklog.h"
57 #include "gtkmedia.h"
58 #include "gtknotify.h"
59 #include "gtkplugin.h"
60 #include "gtkpounce.h"
61 #include "gtkprefs.h"
62 #include "gtkprivacy.h"
63 #include "gtkrequest.h"
64 #include "gtkroomlist.h"
65 #include "gtksavedstatuses.h"
66 #include "gtksession.h"
67 #include "gtksmiley-theme.h"
68 #include "gtksound.h"
69 #include "gtkutils.h"
70 #include "pidginstock.h"
71 #include "gtkwhiteboard.h"
73 #include <signal.h>
75 #include <getopt.h>
79 * Lists of signals we wish to catch and those we wish to ignore.
80 * Each list terminated with -1
82 static const int catch_sig_list[] = {
83 SIGSEGV,
84 SIGINT,
85 SIGTERM,
86 SIGQUIT,
87 SIGCHLD,
91 static const int ignore_sig_list[] = {
92 SIGPIPE,
96 static void
97 dologin_named(const char *name)
99 PurpleAccount *account;
100 char **names;
101 int i;
103 if (name != NULL) { /* list of names given */
104 names = g_strsplit(name, ",", 64);
105 for (i = 0; names[i] != NULL; i++) {
106 account = purple_accounts_find(names[i], NULL);
107 if (account != NULL) { /* found a user */
108 purple_account_set_enabled(account, PIDGIN_UI, TRUE);
111 g_strfreev(names);
112 } else { /* no name given, use the first account */
113 GList *accounts;
115 accounts = purple_accounts_get_all();
116 if (accounts != NULL)
118 account = (PurpleAccount *)accounts->data;
119 purple_account_set_enabled(account, PIDGIN_UI, TRUE);
124 static char *segfault_message;
126 static int signal_sockets[2];
128 static void sighandler(int sig);
130 static void sighandler(int sig)
132 ssize_t written;
135 * We won't do any of the heavy lifting for the signal handling here
136 * because we have no idea what was interrupted. Previously this signal
137 * handler could result in some calls to malloc/free, which can cause
138 * deadlock in libc when the signal handler was interrupting a previous
139 * malloc or free. So instead we'll do an ugly hack where we write the
140 * signal number to one end of a socket pair. The other half of the
141 * socket pair is watched by our main loop. When the main loop sees new
142 * data on the socket it reads in the signal and performs the appropriate
143 * action without fear of interrupting stuff.
145 if (sig == SIGSEGV) {
146 fprintf(stderr, "%s", segfault_message);
147 abort();
148 return;
151 written = write(signal_sockets[0], &sig, sizeof(int));
152 if (written < 0 || written != sizeof(int)) {
153 /* This should never happen */
154 purple_debug_error("sighandler", "Received signal %d but only "
155 "wrote %" G_GSSIZE_FORMAT " bytes out of %"
156 G_GSIZE_FORMAT ": %s\n",
157 sig, written, sizeof(int), g_strerror(errno));
158 exit(1);
162 static gboolean
163 mainloop_sighandler(GIOChannel *source, GIOCondition cond, gpointer data)
165 GIOStatus stat;
166 int sig;
167 gsize bytes_read;
168 GError *error = NULL;
170 /* read the signal number off of the io channel */
171 stat = g_io_channel_read_chars(source, (gchar *)&sig, sizeof(int),
172 &bytes_read, &error);
173 if (stat != G_IO_STATUS_NORMAL) {
174 purple_debug_error("sighandler", "Signal callback failed to read "
175 "from signal socket: %s", error->message);
176 purple_core_quit();
177 return FALSE;
180 switch (sig) {
181 case SIGCHLD:
182 /* Restore signal catching */
183 signal(SIGCHLD, sighandler);
184 break;
185 default:
186 purple_debug_warning("sighandler", "Caught signal %d\n", sig);
187 purple_core_quit();
190 return TRUE;
193 static int
194 ui_main(void)
196 #ifndef _WIN32
197 GList *icons = NULL;
198 GdkPixbuf *icon = NULL;
199 char *icon_path;
200 gsize i;
201 struct {
202 const char *dir;
203 const char *filename;
204 } icon_sizes[] = {
205 {"16x16", "pidgin.png"},
206 {"24x24", "pidgin.png"},
207 {"32x32", "pidgin.png"},
208 {"48x48", "pidgin.png"},
209 {"scalable", "pidgin.svg"}
212 #endif
214 pidgin_blist_setup_sort_methods();
216 #ifndef _WIN32
217 /* use the nice PNG icon for all the windows */
218 for(i=0; i<G_N_ELEMENTS(icon_sizes); i++) {
219 icon_path = g_build_filename(PURPLE_DATADIR, "icons", "hicolor",
220 icon_sizes[i].dir, "apps", icon_sizes[i].filename, NULL);
221 icon = pidgin_pixbuf_new_from_file(icon_path);
222 g_free(icon_path);
223 if (icon) {
224 icons = g_list_append(icons,icon);
225 } else {
226 purple_debug_error("ui_main",
227 "Failed to load the default window icon (%spx version)!\n", icon_sizes[i].dir);
230 if(NULL == icons) {
231 purple_debug_error("ui_main", "Unable to load any size of default window icon!\n");
232 } else {
233 gtk_window_set_default_icon_list(icons);
235 g_list_foreach(icons, (GFunc)g_object_unref, NULL);
236 g_list_free(icons);
238 #endif
240 return 0;
243 static void
244 debug_init(void)
246 purple_debug_set_ui_ops(pidgin_debug_get_ui_ops());
247 pidgin_debug_init();
250 static void
251 pidgin_ui_init(void)
253 pidgin_stock_init();
255 /* Set the UI operation structures. */
256 purple_accounts_set_ui_ops(pidgin_accounts_get_ui_ops());
257 purple_xfers_set_ui_ops(pidgin_xfers_get_ui_ops());
258 purple_blist_set_ui_ops(pidgin_blist_get_ui_ops());
259 purple_notify_set_ui_ops(pidgin_notify_get_ui_ops());
260 purple_request_set_ui_ops(pidgin_request_get_ui_ops());
261 purple_sound_set_ui_ops(pidgin_sound_get_ui_ops());
262 purple_connections_set_ui_ops(pidgin_connections_get_ui_ops());
263 purple_whiteboard_set_ui_ops(pidgin_whiteboard_get_ui_ops());
264 #if defined(USE_SCREENSAVER) || defined(HAVE_IOKIT)
265 purple_idle_set_ui_ops(pidgin_idle_get_ui_ops());
266 #endif
268 pidgin_accounts_init();
269 pidgin_connection_init();
270 pidgin_request_init();
271 pidgin_blist_init();
272 pidgin_status_init();
273 pidgin_conversations_init();
274 pidgin_pounces_init();
275 pidgin_privacy_init();
276 pidgin_xfers_init();
277 pidgin_roomlist_init();
278 pidgin_log_init();
279 pidgin_docklet_init();
280 _pidgin_smiley_theme_init();
281 pidgin_utils_init();
282 pidgin_medias_init();
283 pidgin_notify_init();
286 static GHashTable *ui_info = NULL;
288 static void
289 pidgin_quit(void)
291 #ifdef USE_SM
292 /* unplug */
293 pidgin_session_end();
294 #endif
296 /* Uninit */
297 pidgin_utils_uninit();
298 pidgin_notify_uninit();
299 _pidgin_smiley_theme_uninit();
300 pidgin_conversations_uninit();
301 pidgin_status_uninit();
302 pidgin_docklet_uninit();
303 pidgin_blist_uninit();
304 pidgin_request_uninit();
305 pidgin_connection_uninit();
306 pidgin_accounts_uninit();
307 pidgin_xfers_uninit();
308 pidgin_debug_uninit();
310 if(NULL != ui_info)
311 g_hash_table_destroy(ui_info);
313 /* and end it all... */
314 gtk_main_quit();
317 static GHashTable *pidgin_ui_get_info(void)
319 if(NULL == ui_info) {
320 ui_info = g_hash_table_new(g_str_hash, g_str_equal);
322 g_hash_table_insert(ui_info, "name", (char*)PIDGIN_NAME);
323 g_hash_table_insert(ui_info, "version", VERSION);
324 g_hash_table_insert(ui_info, "website", "https://pidgin.im");
325 g_hash_table_insert(ui_info, "dev_website", "https://developer.pidgin.im");
326 g_hash_table_insert(ui_info, "client_type", "pc");
329 * This is the client key for "Pidgin." It is owned by the AIM
330 * account "markdoliner." Please don't use this key for other
331 * applications. You can either not specify a client key, in
332 * which case the default "libpurple" key will be used, or you
333 * can try to register your own at the AIM or ICQ web sites
334 * (although this functionality was removed at some point, it's
335 * possible it has been re-added). AOL's old key management
336 * page is http://developer.aim.com/manageKeys.jsp
338 g_hash_table_insert(ui_info, "prpl-aim-clientkey", "ma1cSASNCKFtrdv9");
339 g_hash_table_insert(ui_info, "prpl-icq-clientkey", "ma1cSASNCKFtrdv9");
342 * This is the distid for Pidgin, given to us by AOL. Please
343 * don't use this for other applications. You can just not
344 * specify a distid and libpurple will use a default.
346 g_hash_table_insert(ui_info, "prpl-aim-distid", GINT_TO_POINTER(1550));
347 g_hash_table_insert(ui_info, "prpl-icq-distid", GINT_TO_POINTER(1550));
350 return ui_info;
353 static PurpleCoreUiOps core_ops =
355 pidgin_prefs_init,
356 debug_init,
357 pidgin_ui_init,
358 pidgin_quit,
359 pidgin_ui_get_info,
360 NULL,
361 NULL,
362 NULL,
363 NULL
366 static PurpleCoreUiOps *
367 pidgin_core_get_ui_ops(void)
369 return &core_ops;
372 static void
373 show_usage(const char *name, gboolean terse)
375 char *text;
377 if (terse) {
378 text = g_strdup_printf(_("%s %s. Try `%s -h' for more information.\n"), PIDGIN_NAME, DISPLAY_VERSION, name);
379 } else {
380 GString *str = g_string_new(NULL);
381 g_string_append_printf(str, "%s %s\n", PIDGIN_NAME, DISPLAY_VERSION);
382 g_string_append_printf(str, _("Usage: %s [OPTION]...\n\n"), name);
383 g_string_append_printf(str, " -c, --config=%s %s\n",
384 _("DIR"), _("use DIR for config files"));
385 g_string_append_printf(str, " -d, --debug[=colored] %s\n",
386 _("print debugging messages to stdout"));
387 g_string_append_printf(str, " -f, --force-online %s\n",
388 _("force online, regardless of network status"));
389 g_string_append_printf(str, " -h, --help %s\n",
390 _("display this help and exit"));
391 g_string_append_printf(str, " -m, --multiple %s\n",
392 _("allow multiple instances"));
393 g_string_append_printf(str, " -n, --nologin %s\n",
394 _("don't automatically login"));
395 g_string_append_printf(str, " -l, --login[=%s] %s\n",
396 _("NAME"),
397 _("enable specified account(s) (optional argument NAME\n"
399 "specifies account(s) to use, separated by commas.\n"
401 "Without this only the first account will be enabled)."));
402 #ifndef WIN32
403 g_string_append_printf(str, " --display=DISPLAY %s\n",
404 _("X display to use"));
405 #endif /* !WIN32 */
406 g_string_append_printf(str, " -v, --version %s\n",
407 _("display the current version and exit"));
408 text = g_string_free(str, FALSE);
411 purple_print_utf8_to_console(stdout, text);
412 g_free(text);
415 int pidgin_start(int argc, char *argv[])
417 gboolean opt_force_online = FALSE;
418 gboolean opt_help = FALSE;
419 gboolean opt_login = FALSE;
420 gboolean opt_nologin = FALSE;
421 gboolean opt_version = FALSE;
422 gboolean opt_si = TRUE; /* Check for single instance? */
423 char *opt_config_dir_arg = NULL;
424 char *opt_login_arg = NULL;
425 char *opt_session_arg = NULL;
426 char *search_path;
427 GtkCssProvider *provider;
428 GdkScreen *screen;
429 GList *accounts;
430 int sig_indx; /* for setting up signal catching */
431 sigset_t sigset;
432 char errmsg[BUFSIZ];
433 GIOChannel *signal_channel;
434 GIOStatus signal_status;
435 guint signal_channel_watcher;
436 GError *error;
437 #ifndef DEBUG
438 char *segfault_message_tmp;
439 #endif /* DEBUG */
440 int opt;
441 gboolean gui_check;
442 gboolean debug_enabled, debug_colored;
443 GList *active_accounts;
444 GStatBuf st;
446 struct option long_options[] = {
447 {"config", required_argument, NULL, 'c'},
448 {"debug", optional_argument, NULL, 'd'},
449 {"force-online", no_argument, NULL, 'f'},
450 {"help", no_argument, NULL, 'h'},
451 {"login", optional_argument, NULL, 'l'},
452 {"multiple", no_argument, NULL, 'm'},
453 {"nologin", no_argument, NULL, 'n'},
454 {"session", required_argument, NULL, 's'},
455 {"version", no_argument, NULL, 'v'},
456 {"display", required_argument, NULL, 'D'},
457 {"sync", no_argument, NULL, 'S'},
458 {0, 0, 0, 0}
461 debug_colored = FALSE;
462 #ifdef DEBUG
463 debug_enabled = TRUE;
464 #else
465 debug_enabled = FALSE;
466 #endif
468 #ifdef ENABLE_NLS
469 bindtextdomain(PACKAGE, PURPLE_LOCALEDIR);
470 bind_textdomain_codeset(PACKAGE, "UTF-8");
471 textdomain(PACKAGE);
472 #endif
474 /* Locale initialization is not complete here. See gtk_init_check() */
475 setlocale(LC_ALL, "");
477 #ifndef DEBUG
478 /* We translate this here in case the crash breaks gettext. */
479 segfault_message_tmp = g_strdup_printf(_(
480 "%s %s has segfaulted and attempted to dump a core file.\n"
481 "This is a bug in the software and has happened through\n"
482 "no fault of your own.\n\n"
483 "If you can reproduce the crash, please notify the developers\n"
484 "by reporting a bug at:\n"
485 "%ssimpleticket/\n\n"
486 "Please make sure to specify what you were doing at the time\n"
487 "and post the backtrace from the core file. If you do not know\n"
488 "how to get the backtrace, please read the instructions at\n"
489 "%swiki/GetABacktrace\n"),
490 PIDGIN_NAME, DISPLAY_VERSION, PURPLE_DEVEL_WEBSITE, PURPLE_DEVEL_WEBSITE
493 /* we have to convert the message (UTF-8 to console
494 charset) early because after a segmentation fault
495 it's not a good practice to allocate memory */
496 error = NULL;
497 segfault_message = g_locale_from_utf8(segfault_message_tmp,
498 -1, NULL, NULL, &error);
499 if (segfault_message != NULL) {
500 g_free(segfault_message_tmp);
502 else {
503 /* use 'segfault_message_tmp' (UTF-8) as a fallback */
504 g_warning("%s\n", error->message);
505 g_error_free(error);
506 segfault_message = segfault_message_tmp;
508 #else
509 /* Don't mark this for translation. */
510 segfault_message = g_strdup(
511 "Hi, user. We need to talk.\n"
512 "I think something's gone wrong here. It's probably my fault.\n"
513 "No, really, it's not you... it's me... no no no, I think we get along well\n"
514 "it's just that.... well, I want to see other people. I... what?!? NO! I \n"
515 "haven't been cheating on you!! How many times do you want me to tell you?! And\n"
516 "for the last time, it's just a rash!\n"
518 #endif
521 * Create a socket pair for receiving unix signals from a signal
522 * handler.
524 if (socketpair(AF_UNIX, SOCK_STREAM, 0, signal_sockets) < 0) {
525 perror("Failed to create sockets for GLib signal handling");
526 exit(1);
528 signal_channel = g_io_channel_unix_new(signal_sockets[1]);
531 * Set the channel encoding to raw binary instead of the default of
532 * UTF-8, because we'll be sending integers across instead of strings.
534 error = NULL;
535 signal_status = g_io_channel_set_encoding(signal_channel, NULL, &error);
536 if (signal_status != G_IO_STATUS_NORMAL) {
537 fprintf(stderr, "Failed to set the signal channel to raw "
538 "binary: %s", error->message);
539 exit(1);
541 signal_channel_watcher = g_io_add_watch(signal_channel, G_IO_IN, mainloop_sighandler, NULL);
542 g_io_channel_unref(signal_channel);
544 /* Let's not violate any PLA's!!!! */
545 /* jseymour: whatever the fsck that means */
546 /* Robot101: for some reason things like gdm like to block *
547 * useful signals like SIGCHLD, so we unblock all the ones we *
548 * declare a handler for. thanks JSeymour and Vann. */
549 if (sigemptyset(&sigset)) {
550 snprintf(errmsg, sizeof(errmsg), "Warning: couldn't initialise empty signal set");
551 perror(errmsg);
553 for(sig_indx = 0; catch_sig_list[sig_indx] != -1; ++sig_indx) {
554 if(signal(catch_sig_list[sig_indx], sighandler) == SIG_ERR) {
555 snprintf(errmsg, sizeof(errmsg), "Warning: couldn't set signal %d for catching",
556 catch_sig_list[sig_indx]);
557 perror(errmsg);
559 if(sigaddset(&sigset, catch_sig_list[sig_indx])) {
560 snprintf(errmsg, sizeof(errmsg), "Warning: couldn't include signal %d for unblocking",
561 catch_sig_list[sig_indx]);
562 perror(errmsg);
565 for(sig_indx = 0; ignore_sig_list[sig_indx] != -1; ++sig_indx) {
566 if(signal(ignore_sig_list[sig_indx], SIG_IGN) == SIG_ERR) {
567 snprintf(errmsg, sizeof(errmsg), "Warning: couldn't set signal %d to ignore",
568 ignore_sig_list[sig_indx]);
569 perror(errmsg);
573 if (sigprocmask(SIG_UNBLOCK, &sigset, NULL)) {
574 snprintf(errmsg, sizeof(errmsg), "Warning: couldn't unblock signals");
575 perror(errmsg);
578 /* scan command-line options */
579 opterr = 1;
580 while ((opt = getopt_long(argc, argv,
581 #ifndef _WIN32
582 "c:dfhmnl::s:v",
583 #else
584 "c:dfhmnl::v",
585 #endif
586 long_options, NULL)) != -1) {
587 switch (opt) {
588 case 'c': /* config dir */
589 g_free(opt_config_dir_arg);
590 opt_config_dir_arg = g_strdup(optarg);
591 break;
592 case 'd': /* debug */
593 debug_enabled = TRUE;
594 if (g_strcmp0(optarg, "colored") == 0)
595 debug_colored = TRUE;
596 break;
597 case 'f': /* force-online */
598 opt_force_online = TRUE;
599 break;
600 case 'h': /* help */
601 opt_help = TRUE;
602 break;
603 case 'n': /* no autologin */
604 opt_nologin = TRUE;
605 break;
606 case 'l': /* login, option username */
607 opt_login = TRUE;
608 g_free(opt_login_arg);
609 if (optarg != NULL)
610 opt_login_arg = g_strdup(optarg);
611 break;
612 case 's': /* use existing session ID */
613 g_free(opt_session_arg);
614 opt_session_arg = g_strdup(optarg);
615 break;
616 case 'v': /* version */
617 opt_version = TRUE;
618 break;
619 case 'm': /* do not ensure single instance. */
620 opt_si = FALSE;
621 break;
622 case 'D': /* --display */
623 case 'S': /* --sync */
624 /* handled by gtk_init_check below */
625 break;
626 case '?': /* show terse help */
627 default:
628 show_usage(argv[0], TRUE);
629 g_free(segfault_message);
630 return 0;
631 break;
635 /* show help message */
636 if (opt_help) {
637 show_usage(argv[0], FALSE);
638 g_free(segfault_message);
639 return 0;
641 /* show version message */
642 if (opt_version) {
643 printf("%s %s (libpurple %s)\n", PIDGIN_NAME, DISPLAY_VERSION,
644 purple_core_get_version());
645 g_free(segfault_message);
646 return 0;
649 /* set a user-specified config directory */
650 if (opt_config_dir_arg != NULL) {
651 if (g_path_is_absolute(opt_config_dir_arg)) {
652 purple_util_set_user_dir(opt_config_dir_arg);
653 } else {
654 /* Make an absolute (if not canonical) path */
655 char *cwd = g_get_current_dir();
656 char *path = g_build_path(G_DIR_SEPARATOR_S, cwd, opt_config_dir_arg, NULL);
657 purple_util_set_user_dir(path);
658 g_free(path);
659 g_free(cwd);
664 * We're done piddling around with command line arguments.
665 * Fire up this baby.
668 if (g_getenv("PIDGIN_DEBUG_COLORED") != NULL)
669 debug_colored = TRUE;
670 purple_debug_set_enabled(debug_enabled);
671 purple_debug_set_colored(debug_colored);
673 gui_check = gtk_init_check(&argc, &argv);
674 if (!gui_check) {
675 const char *display = gdk_display_get_name(gdk_display_get_default());
677 printf("%s %s\n", PIDGIN_NAME, DISPLAY_VERSION);
679 g_warning("cannot open display: %s", display ? display : "unset");
680 g_free(segfault_message);
682 return 1;
685 search_path = g_build_filename(purple_user_dir(), "gtk-3.0.css", NULL);
687 error = NULL;
688 provider = gtk_css_provider_new();
689 gui_check = gtk_css_provider_load_from_path(provider, search_path, &error);
691 if (gui_check && !error) {
692 screen = gdk_screen_get_default();
693 gtk_style_context_add_provider_for_screen(screen,
694 GTK_STYLE_PROVIDER(provider),
695 GTK_STYLE_PROVIDER_PRIORITY_USER);
696 } else {
697 purple_debug_error("gtk", "Unable to load custom gtk-3.0.css: %s\n",
698 error ? error->message : "(unknown error)");
701 g_free(search_path);
703 #ifdef _WIN32
704 winpidgin_init();
705 #endif
707 purple_core_set_ui_ops(pidgin_core_get_ui_ops());
708 purple_eventloop_set_ui_ops(pidgin_eventloop_get_ui_ops());
710 if (!purple_core_init(PIDGIN_UI)) {
711 fprintf(stderr,
712 "Initialization of the libpurple core failed. Dumping core.\n"
713 "Please report this!\n");
714 g_free(segfault_message);
715 abort();
718 search_path = g_build_filename(purple_user_dir(), "plugins", NULL);
719 if (!g_stat(search_path, &st))
720 g_mkdir(search_path, S_IRUSR | S_IWUSR | S_IXUSR);
721 purple_plugins_add_search_path(search_path);
722 g_free(search_path);
724 purple_plugins_add_search_path(PIDGIN_LIBDIR);
725 purple_plugins_refresh();
727 if (opt_si && !purple_core_ensure_single_instance()) {
728 #ifdef HAVE_DBUS
729 DBusConnection *conn = purple_dbus_get_connection();
730 DBusMessage *message = dbus_message_new_method_call(PURPLE_DBUS_SERVICE, PURPLE_DBUS_PATH,
731 PURPLE_DBUS_INTERFACE, "PurpleBlistSetVisible");
732 gboolean tr = TRUE;
733 dbus_message_append_args(message, DBUS_TYPE_INT32, &tr, DBUS_TYPE_INVALID);
734 dbus_connection_send_with_reply_and_block(conn, message, -1, NULL);
735 dbus_message_unref(message);
736 #endif
737 gdk_notify_startup_complete();
738 purple_core_quit();
739 g_printerr(_("Exiting because another libpurple client is already running.\n"));
740 g_free(segfault_message);
741 return 0;
744 /* load plugins we had when we quit */
745 purple_plugins_load_saved(PIDGIN_PREFS_ROOT "/plugins/loaded");
747 ui_main();
749 #ifdef USE_SM
750 pidgin_session_init(argv[0], opt_session_arg, opt_config_dir_arg);
751 #endif
752 g_free(opt_session_arg);
753 opt_session_arg = NULL;
754 g_free(opt_config_dir_arg);
755 opt_config_dir_arg = NULL;
757 /* This needs to be before purple_blist_show() so the
758 * statusbox gets the forced online status. */
759 if (opt_force_online)
760 purple_network_force_online();
763 * We want to show the blist early in the init process so the
764 * user feels warm and fuzzy (not cold and prickley).
766 purple_blist_show();
768 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/enabled"))
769 pidgin_debug_window_show();
771 if (opt_login) {
772 /* disable all accounts */
773 for (accounts = purple_accounts_get_all(); accounts != NULL; accounts = accounts->next) {
774 PurpleAccount *account = accounts->data;
775 purple_account_set_enabled(account, PIDGIN_UI, FALSE);
777 /* honor the startup status preference */
778 if (!purple_prefs_get_bool("/purple/savedstatus/startup_current_status"))
779 purple_savedstatus_activate(purple_savedstatus_get_startup());
780 /* now enable the requested ones */
781 dologin_named(opt_login_arg);
782 g_free(opt_login_arg);
783 opt_login_arg = NULL;
784 } else if (opt_nologin) {
785 /* Set all accounts to "offline" */
786 PurpleSavedStatus *saved_status;
788 /* If we've used this type+message before, lookup the transient status */
789 saved_status = purple_savedstatus_find_transient_by_type_and_message(
790 PURPLE_STATUS_OFFLINE, NULL);
792 /* If this type+message is unique then create a new transient saved status */
793 if (saved_status == NULL)
794 saved_status = purple_savedstatus_new(NULL, PURPLE_STATUS_OFFLINE);
796 /* Set the status for each account */
797 purple_savedstatus_activate(saved_status);
798 } else {
799 /* Everything is good to go--sign on already */
800 if (!purple_prefs_get_bool("/purple/savedstatus/startup_current_status"))
801 purple_savedstatus_activate(purple_savedstatus_get_startup());
802 purple_accounts_restore_current_statuses();
805 if ((active_accounts = purple_accounts_get_all_active()) == NULL)
807 pidgin_accounts_window_show();
809 else
811 g_list_free(active_accounts);
814 /* GTK clears the notification for us when opening the first window,
815 * but we may have launched with only a status icon, so clear the it
816 * just in case. */
817 gdk_notify_startup_complete();
819 #ifdef _WIN32
820 winpidgin_post_init();
821 #endif
823 gtk_main();
825 g_free(segfault_message);
826 g_source_remove(signal_channel_watcher);
827 close(signal_sockets[0]);
828 close(signal_sockets[1]);
830 #ifdef _WIN32
831 winpidgin_cleanup();
832 #endif
834 return 0;