Merged pidgin/main into default
[pidgin-git.git] / pidgin / libpidgin.c
blob4604f84ab7dfad72adc3282701ef026d7a764947
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 #ifndef _WIN32
74 #include <signal.h>
75 #endif
77 #include <getopt.h>
79 #ifndef _WIN32
82 * Lists of signals we wish to catch and those we wish to ignore.
83 * Each list terminated with -1
85 static const int catch_sig_list[] = {
86 SIGSEGV,
87 SIGINT,
88 SIGTERM,
89 SIGQUIT,
90 SIGCHLD,
94 static const int ignore_sig_list[] = {
95 SIGPIPE,
98 #endif /* !_WIN32 */
100 static void
101 dologin_named(const char *name)
103 PurpleAccount *account;
104 char **names;
105 int i;
107 if (name != NULL) { /* list of names given */
108 names = g_strsplit(name, ",", 64);
109 for (i = 0; names[i] != NULL; i++) {
110 account = purple_accounts_find(names[i], NULL);
111 if (account != NULL) { /* found a user */
112 purple_account_set_enabled(account, PIDGIN_UI, TRUE);
115 g_strfreev(names);
116 } else { /* no name given, use the first account */
117 GList *accounts;
119 accounts = purple_accounts_get_all();
120 if (accounts != NULL)
122 account = (PurpleAccount *)accounts->data;
123 purple_account_set_enabled(account, PIDGIN_UI, TRUE);
128 #ifndef _WIN32
129 static char *segfault_message;
131 static int signal_sockets[2];
133 static void sighandler(int sig);
135 static void sighandler(int sig)
137 ssize_t written;
140 * We won't do any of the heavy lifting for the signal handling here
141 * because we have no idea what was interrupted. Previously this signal
142 * handler could result in some calls to malloc/free, which can cause
143 * deadlock in libc when the signal handler was interrupting a previous
144 * malloc or free. So instead we'll do an ugly hack where we write the
145 * signal number to one end of a socket pair. The other half of the
146 * socket pair is watched by our main loop. When the main loop sees new
147 * data on the socket it reads in the signal and performs the appropriate
148 * action without fear of interrupting stuff.
150 if (sig == SIGSEGV) {
151 fprintf(stderr, "%s", segfault_message);
152 abort();
153 return;
156 written = write(signal_sockets[0], &sig, sizeof(int));
157 if (written < 0 || written != sizeof(int)) {
158 /* This should never happen */
159 purple_debug_error("sighandler", "Received signal %d but only "
160 "wrote %" G_GSSIZE_FORMAT " bytes out of %"
161 G_GSIZE_FORMAT ": %s\n",
162 sig, written, sizeof(int), g_strerror(errno));
163 exit(1);
167 static gboolean
168 mainloop_sighandler(GIOChannel *source, GIOCondition cond, gpointer data)
170 GIOStatus stat;
171 int sig;
172 gsize bytes_read;
173 GError *error = NULL;
175 /* read the signal number off of the io channel */
176 stat = g_io_channel_read_chars(source, (gchar *)&sig, sizeof(int),
177 &bytes_read, &error);
178 if (stat != G_IO_STATUS_NORMAL) {
179 purple_debug_error("sighandler", "Signal callback failed to read "
180 "from signal socket: %s", error->message);
181 purple_core_quit();
182 return FALSE;
185 switch (sig) {
186 case SIGCHLD:
187 /* Restore signal catching */
188 signal(SIGCHLD, sighandler);
189 break;
190 default:
191 purple_debug_warning("sighandler", "Caught signal %d\n", sig);
192 purple_core_quit();
195 return TRUE;
197 #endif /* !_WIN32 */
199 static int
200 ui_main(void)
202 #ifndef _WIN32
203 GList *icons = NULL;
204 GdkPixbuf *icon = NULL;
205 char *icon_path;
206 gsize i;
207 struct {
208 const char *dir;
209 const char *filename;
210 } icon_sizes[] = {
211 {"16x16", "pidgin.png"},
212 {"24x24", "pidgin.png"},
213 {"32x32", "pidgin.png"},
214 {"48x48", "pidgin.png"},
215 {"scalable", "pidgin.svg"}
218 #endif
220 pidgin_blist_setup_sort_methods();
222 #ifndef _WIN32
223 /* use the nice PNG icon for all the windows */
224 for(i=0; i<G_N_ELEMENTS(icon_sizes); i++) {
225 icon_path = g_build_filename(PURPLE_DATADIR, "icons", "hicolor",
226 icon_sizes[i].dir, "apps", icon_sizes[i].filename, NULL);
227 icon = pidgin_pixbuf_new_from_file(icon_path);
228 g_free(icon_path);
229 if (icon) {
230 icons = g_list_append(icons,icon);
231 } else {
232 purple_debug_error("ui_main",
233 "Failed to load the default window icon (%spx version)!\n", icon_sizes[i].dir);
236 if(NULL == icons) {
237 purple_debug_error("ui_main", "Unable to load any size of default window icon!\n");
238 } else {
239 gtk_window_set_default_icon_list(icons);
241 g_list_foreach(icons, (GFunc)g_object_unref, NULL);
242 g_list_free(icons);
244 #endif
246 return 0;
249 static void
250 debug_init(void)
252 purple_debug_set_ui_ops(pidgin_debug_get_ui_ops());
253 pidgin_debug_init();
256 static void
257 pidgin_ui_init(void)
259 pidgin_stock_init();
261 /* Set the UI operation structures. */
262 purple_accounts_set_ui_ops(pidgin_accounts_get_ui_ops());
263 purple_xfers_set_ui_ops(pidgin_xfers_get_ui_ops());
264 purple_blist_set_ui_ops(pidgin_blist_get_ui_ops());
265 purple_notify_set_ui_ops(pidgin_notify_get_ui_ops());
266 purple_request_set_ui_ops(pidgin_request_get_ui_ops());
267 purple_sound_set_ui_ops(pidgin_sound_get_ui_ops());
268 purple_connections_set_ui_ops(pidgin_connections_get_ui_ops());
269 purple_whiteboard_set_ui_ops(pidgin_whiteboard_get_ui_ops());
270 #if defined(USE_SCREENSAVER) || defined(HAVE_IOKIT)
271 purple_idle_set_ui_ops(pidgin_idle_get_ui_ops());
272 #endif
274 pidgin_accounts_init();
275 pidgin_connection_init();
276 pidgin_request_init();
277 pidgin_blist_init();
278 pidgin_status_init();
279 pidgin_conversations_init();
280 pidgin_pounces_init();
281 pidgin_privacy_init();
282 pidgin_xfers_init();
283 pidgin_roomlist_init();
284 pidgin_log_init();
285 pidgin_docklet_init();
286 _pidgin_smiley_theme_init();
287 pidgin_utils_init();
288 pidgin_medias_init();
289 pidgin_notify_init();
292 static GHashTable *ui_info = NULL;
294 static void
295 pidgin_quit(void)
297 #ifdef USE_SM
298 /* unplug */
299 pidgin_session_end();
300 #endif
302 /* Uninit */
303 pidgin_utils_uninit();
304 pidgin_notify_uninit();
305 _pidgin_smiley_theme_uninit();
306 pidgin_conversations_uninit();
307 pidgin_status_uninit();
308 pidgin_docklet_uninit();
309 pidgin_blist_uninit();
310 pidgin_request_uninit();
311 pidgin_connection_uninit();
312 pidgin_accounts_uninit();
313 pidgin_xfers_uninit();
314 pidgin_debug_uninit();
316 if(NULL != ui_info)
317 g_hash_table_destroy(ui_info);
319 /* and end it all... */
320 gtk_main_quit();
323 static GHashTable *pidgin_ui_get_info(void)
325 if(NULL == ui_info) {
326 ui_info = g_hash_table_new(g_str_hash, g_str_equal);
328 g_hash_table_insert(ui_info, "name", (char*)PIDGIN_NAME);
329 g_hash_table_insert(ui_info, "version", VERSION);
330 g_hash_table_insert(ui_info, "website", "https://pidgin.im");
331 g_hash_table_insert(ui_info, "dev_website", "https://developer.pidgin.im");
332 g_hash_table_insert(ui_info, "client_type", "pc");
335 * This is the client key for "Pidgin." It is owned by the AIM
336 * account "markdoliner." Please don't use this key for other
337 * applications. You can either not specify a client key, in
338 * which case the default "libpurple" key will be used, or you
339 * can try to register your own at the AIM or ICQ web sites
340 * (although this functionality was removed at some point, it's
341 * possible it has been re-added). AOL's old key management
342 * page is http://developer.aim.com/manageKeys.jsp
344 g_hash_table_insert(ui_info, "prpl-aim-clientkey", "ma1cSASNCKFtrdv9");
345 g_hash_table_insert(ui_info, "prpl-icq-clientkey", "ma1cSASNCKFtrdv9");
348 * This is the distid for Pidgin, given to us by AOL. Please
349 * don't use this for other applications. You can just not
350 * specify a distid and libpurple will use a default.
352 g_hash_table_insert(ui_info, "prpl-aim-distid", GINT_TO_POINTER(1550));
353 g_hash_table_insert(ui_info, "prpl-icq-distid", GINT_TO_POINTER(1550));
356 return ui_info;
359 static PurpleCoreUiOps core_ops =
361 pidgin_prefs_init,
362 debug_init,
363 pidgin_ui_init,
364 pidgin_quit,
365 pidgin_ui_get_info,
366 NULL,
367 NULL,
368 NULL,
369 NULL
372 static PurpleCoreUiOps *
373 pidgin_core_get_ui_ops(void)
375 return &core_ops;
378 static void
379 show_usage(const char *name, gboolean terse)
381 char *text;
383 if (terse) {
384 text = g_strdup_printf(_("%s %s. Try `%s -h' for more information.\n"), PIDGIN_NAME, DISPLAY_VERSION, name);
385 } else {
386 GString *str = g_string_new(NULL);
387 g_string_append_printf(str, "%s %s\n", PIDGIN_NAME, DISPLAY_VERSION);
388 g_string_append_printf(str, _("Usage: %s [OPTION]...\n\n"), name);
389 g_string_append_printf(str, " -c, --config=%s %s\n",
390 _("DIR"), _("use DIR for config files"));
391 g_string_append_printf(str, " -d, --debug[=colored] %s\n",
392 _("print debugging messages to stdout"));
393 g_string_append_printf(str, " -f, --force-online %s\n",
394 _("force online, regardless of network status"));
395 g_string_append_printf(str, " -h, --help %s\n",
396 _("display this help and exit"));
397 g_string_append_printf(str, " -m, --multiple %s\n",
398 _("allow multiple instances"));
399 g_string_append_printf(str, " -n, --nologin %s\n",
400 _("don't automatically login"));
401 g_string_append_printf(str, " -l, --login[=%s] %s\n",
402 _("NAME"),
403 _("enable specified account(s) (optional argument NAME\n"
405 "specifies account(s) to use, separated by commas.\n"
407 "Without this only the first account will be enabled)."));
408 #ifndef WIN32
409 g_string_append_printf(str, " --display=DISPLAY %s\n",
410 _("X display to use"));
411 #endif /* !WIN32 */
412 g_string_append_printf(str, " -v, --version %s\n",
413 _("display the current version and exit"));
414 text = g_string_free(str, FALSE);
417 purple_print_utf8_to_console(stdout, text);
418 g_free(text);
421 int pidgin_start(int argc, char *argv[])
423 gboolean opt_force_online = FALSE;
424 gboolean opt_help = FALSE;
425 gboolean opt_login = FALSE;
426 gboolean opt_nologin = FALSE;
427 gboolean opt_version = FALSE;
428 gboolean opt_si = TRUE; /* Check for single instance? */
429 char *opt_config_dir_arg = NULL;
430 char *opt_login_arg = NULL;
431 char *opt_session_arg = NULL;
432 char *search_path;
433 GtkCssProvider *provider;
434 GdkScreen *screen;
435 GList *accounts;
436 #ifndef _WIN32
437 int sig_indx; /* for setting up signal catching */
438 sigset_t sigset;
439 char errmsg[BUFSIZ];
440 GIOChannel *signal_channel;
441 GIOStatus signal_status;
442 guint signal_channel_watcher;
443 #ifndef DEBUG
444 char *segfault_message_tmp;
445 #endif /* DEBUG */
446 #endif /* !_WIN32 */
447 int opt;
448 gboolean gui_check;
449 gboolean debug_enabled, debug_colored;
450 GList *active_accounts;
451 GStatBuf st;
452 GError *error;
454 struct option long_options[] = {
455 {"config", required_argument, NULL, 'c'},
456 {"debug", optional_argument, NULL, 'd'},
457 {"force-online", no_argument, NULL, 'f'},
458 {"help", no_argument, NULL, 'h'},
459 {"login", optional_argument, NULL, 'l'},
460 {"multiple", no_argument, NULL, 'm'},
461 {"nologin", no_argument, NULL, 'n'},
462 {"session", required_argument, NULL, 's'},
463 {"version", no_argument, NULL, 'v'},
464 {"display", required_argument, NULL, 'D'},
465 {"sync", no_argument, NULL, 'S'},
466 {0, 0, 0, 0}
469 debug_colored = FALSE;
470 #ifdef DEBUG
471 debug_enabled = TRUE;
472 #else
473 debug_enabled = FALSE;
474 #endif
476 #ifdef ENABLE_NLS
477 bindtextdomain(PACKAGE, PURPLE_LOCALEDIR);
478 bind_textdomain_codeset(PACKAGE, "UTF-8");
479 textdomain(PACKAGE);
480 #endif
482 /* Locale initialization is not complete here. See gtk_init_check() */
483 setlocale(LC_ALL, "");
485 #ifndef _WIN32
487 #ifndef DEBUG
488 /* We translate this here in case the crash breaks gettext. */
489 segfault_message_tmp = g_strdup_printf(_(
490 "%s %s has segfaulted and attempted to dump a core file.\n"
491 "This is a bug in the software and has happened through\n"
492 "no fault of your own.\n\n"
493 "If you can reproduce the crash, please notify the developers\n"
494 "by reporting a bug at:\n"
495 "%ssimpleticket/\n\n"
496 "Please make sure to specify what you were doing at the time\n"
497 "and post the backtrace from the core file. If you do not know\n"
498 "how to get the backtrace, please read the instructions at\n"
499 "%swiki/GetABacktrace\n"),
500 PIDGIN_NAME, DISPLAY_VERSION, PURPLE_DEVEL_WEBSITE, PURPLE_DEVEL_WEBSITE
503 /* we have to convert the message (UTF-8 to console
504 charset) early because after a segmentation fault
505 it's not a good practice to allocate memory */
506 error = NULL;
507 segfault_message = g_locale_from_utf8(segfault_message_tmp,
508 -1, NULL, NULL, &error);
509 if (segfault_message != NULL) {
510 g_free(segfault_message_tmp);
512 else {
513 /* use 'segfault_message_tmp' (UTF-8) as a fallback */
514 g_warning("%s\n", error->message);
515 g_error_free(error);
516 segfault_message = segfault_message_tmp;
518 #else
519 /* Don't mark this for translation. */
520 segfault_message = g_strdup(
521 "Hi, user. We need to talk.\n"
522 "I think something's gone wrong here. It's probably my fault.\n"
523 "No, really, it's not you... it's me... no no no, I think we get along well\n"
524 "it's just that.... well, I want to see other people. I... what?!? NO! I \n"
525 "haven't been cheating on you!! How many times do you want me to tell you?! And\n"
526 "for the last time, it's just a rash!\n"
528 #endif
531 * Create a socket pair for receiving unix signals from a signal
532 * handler.
534 if (socketpair(AF_UNIX, SOCK_STREAM, 0, signal_sockets) < 0) {
535 perror("Failed to create sockets for GLib signal handling");
536 exit(1);
538 signal_channel = g_io_channel_unix_new(signal_sockets[1]);
541 * Set the channel encoding to raw binary instead of the default of
542 * UTF-8, because we'll be sending integers across instead of strings.
544 error = NULL;
545 signal_status = g_io_channel_set_encoding(signal_channel, NULL, &error);
546 if (signal_status != G_IO_STATUS_NORMAL) {
547 fprintf(stderr, "Failed to set the signal channel to raw "
548 "binary: %s", error->message);
549 exit(1);
551 signal_channel_watcher = g_io_add_watch(signal_channel, G_IO_IN, mainloop_sighandler, NULL);
552 g_io_channel_unref(signal_channel);
554 /* Let's not violate any PLA's!!!! */
555 /* jseymour: whatever the fsck that means */
556 /* Robot101: for some reason things like gdm like to block *
557 * useful signals like SIGCHLD, so we unblock all the ones we *
558 * declare a handler for. thanks JSeymour and Vann. */
559 if (sigemptyset(&sigset)) {
560 snprintf(errmsg, sizeof(errmsg), "Warning: couldn't initialise empty signal set");
561 perror(errmsg);
563 for(sig_indx = 0; catch_sig_list[sig_indx] != -1; ++sig_indx) {
564 if(signal(catch_sig_list[sig_indx], sighandler) == SIG_ERR) {
565 snprintf(errmsg, sizeof(errmsg), "Warning: couldn't set signal %d for catching",
566 catch_sig_list[sig_indx]);
567 perror(errmsg);
569 if(sigaddset(&sigset, catch_sig_list[sig_indx])) {
570 snprintf(errmsg, sizeof(errmsg), "Warning: couldn't include signal %d for unblocking",
571 catch_sig_list[sig_indx]);
572 perror(errmsg);
575 for(sig_indx = 0; ignore_sig_list[sig_indx] != -1; ++sig_indx) {
576 if(signal(ignore_sig_list[sig_indx], SIG_IGN) == SIG_ERR) {
577 snprintf(errmsg, sizeof(errmsg), "Warning: couldn't set signal %d to ignore",
578 ignore_sig_list[sig_indx]);
579 perror(errmsg);
583 if (sigprocmask(SIG_UNBLOCK, &sigset, NULL)) {
584 snprintf(errmsg, sizeof(errmsg), "Warning: couldn't unblock signals");
585 perror(errmsg);
587 #endif /* !_WIN32 */
589 /* scan command-line options */
590 opterr = 1;
591 while ((opt = getopt_long(argc, argv,
592 #ifndef _WIN32
593 "c:dfhmnl::s:v",
594 #else
595 "c:dfhmnl::v",
596 #endif
597 long_options, NULL)) != -1) {
598 switch (opt) {
599 case 'c': /* config dir */
600 g_free(opt_config_dir_arg);
601 opt_config_dir_arg = g_strdup(optarg);
602 break;
603 case 'd': /* debug */
604 debug_enabled = TRUE;
605 if (g_strcmp0(optarg, "colored") == 0)
606 debug_colored = TRUE;
607 break;
608 case 'f': /* force-online */
609 opt_force_online = TRUE;
610 break;
611 case 'h': /* help */
612 opt_help = TRUE;
613 break;
614 case 'n': /* no autologin */
615 opt_nologin = TRUE;
616 break;
617 case 'l': /* login, option username */
618 opt_login = TRUE;
619 g_free(opt_login_arg);
620 if (optarg != NULL)
621 opt_login_arg = g_strdup(optarg);
622 break;
623 case 's': /* use existing session ID */
624 g_free(opt_session_arg);
625 opt_session_arg = g_strdup(optarg);
626 break;
627 case 'v': /* version */
628 opt_version = TRUE;
629 break;
630 case 'm': /* do not ensure single instance. */
631 opt_si = FALSE;
632 break;
633 case 'D': /* --display */
634 case 'S': /* --sync */
635 /* handled by gtk_init_check below */
636 break;
637 case '?': /* show terse help */
638 default:
639 show_usage(argv[0], TRUE);
640 #ifndef _WIN32
641 g_free(segfault_message);
642 #endif
643 return 0;
644 break;
648 /* show help message */
649 if (opt_help) {
650 show_usage(argv[0], FALSE);
651 #ifndef _WIN32
652 g_free(segfault_message);
653 #endif
654 return 0;
656 /* show version message */
657 if (opt_version) {
658 printf("%s %s (libpurple %s)\n", PIDGIN_NAME, DISPLAY_VERSION,
659 purple_core_get_version());
660 #ifndef _WIN32
661 g_free(segfault_message);
662 #endif
663 return 0;
666 /* set a user-specified config directory */
667 if (opt_config_dir_arg != NULL) {
668 if (g_path_is_absolute(opt_config_dir_arg)) {
669 purple_util_set_user_dir(opt_config_dir_arg);
670 } else {
671 /* Make an absolute (if not canonical) path */
672 char *cwd = g_get_current_dir();
673 char *path = g_build_path(G_DIR_SEPARATOR_S, cwd, opt_config_dir_arg, NULL);
674 purple_util_set_user_dir(path);
675 g_free(path);
676 g_free(cwd);
681 * We're done piddling around with command line arguments.
682 * Fire up this baby.
685 if (g_getenv("PIDGIN_DEBUG_COLORED") != NULL)
686 debug_colored = TRUE;
687 purple_debug_set_enabled(debug_enabled);
688 purple_debug_set_colored(debug_colored);
690 gui_check = gtk_init_check(&argc, &argv);
691 if (!gui_check) {
692 const char *display = gdk_display_get_name(gdk_display_get_default());
694 printf("%s %s\n", PIDGIN_NAME, DISPLAY_VERSION);
696 g_warning("cannot open display: %s", display ? display : "unset");
697 #ifndef _WIN32
698 g_free(segfault_message);
699 #endif
701 return 1;
704 search_path = g_build_filename(purple_user_dir(), "gtk-3.0.css", NULL);
706 error = NULL;
707 provider = gtk_css_provider_new();
708 gui_check = gtk_css_provider_load_from_path(provider, search_path, &error);
710 if (gui_check && !error) {
711 screen = gdk_screen_get_default();
712 gtk_style_context_add_provider_for_screen(screen,
713 GTK_STYLE_PROVIDER(provider),
714 GTK_STYLE_PROVIDER_PRIORITY_USER);
715 } else {
716 purple_debug_error("gtk", "Unable to load custom gtk-3.0.css: %s\n",
717 error ? error->message : "(unknown error)");
720 g_free(search_path);
722 #ifdef _WIN32
723 winpidgin_init();
724 #endif
726 purple_core_set_ui_ops(pidgin_core_get_ui_ops());
727 purple_eventloop_set_ui_ops(pidgin_eventloop_get_ui_ops());
729 if (!purple_core_init(PIDGIN_UI)) {
730 fprintf(stderr,
731 "Initialization of the libpurple core failed. Dumping core.\n"
732 "Please report this!\n");
733 #ifndef _WIN32
734 g_free(segfault_message);
735 #endif
736 abort();
739 search_path = g_build_filename(purple_user_dir(), "plugins", NULL);
740 if (!g_stat(search_path, &st))
741 g_mkdir(search_path, S_IRUSR | S_IWUSR | S_IXUSR);
742 purple_plugins_add_search_path(search_path);
743 g_free(search_path);
745 purple_plugins_add_search_path(PIDGIN_LIBDIR);
746 purple_plugins_refresh();
748 if (opt_si && !purple_core_ensure_single_instance()) {
749 #ifdef HAVE_DBUS
750 DBusConnection *conn = purple_dbus_get_connection();
751 DBusMessage *message = dbus_message_new_method_call(PURPLE_DBUS_SERVICE, PURPLE_DBUS_PATH,
752 PURPLE_DBUS_INTERFACE, "PurpleBlistSetVisible");
753 gboolean tr = TRUE;
754 dbus_message_append_args(message, DBUS_TYPE_INT32, &tr, DBUS_TYPE_INVALID);
755 dbus_connection_send_with_reply_and_block(conn, message, -1, NULL);
756 dbus_message_unref(message);
757 #endif
758 gdk_notify_startup_complete();
759 purple_core_quit();
760 g_printerr(_("Exiting because another libpurple client is already running.\n"));
761 #ifndef _WIN32
762 g_free(segfault_message);
763 #endif
764 return 0;
767 /* load plugins we had when we quit */
768 purple_plugins_load_saved(PIDGIN_PREFS_ROOT "/plugins/loaded");
770 ui_main();
772 #ifdef USE_SM
773 pidgin_session_init(argv[0], opt_session_arg, opt_config_dir_arg);
774 #endif
775 g_free(opt_session_arg);
776 opt_session_arg = NULL;
777 g_free(opt_config_dir_arg);
778 opt_config_dir_arg = NULL;
780 /* This needs to be before purple_blist_show() so the
781 * statusbox gets the forced online status. */
782 if (opt_force_online)
783 purple_network_force_online();
786 * We want to show the blist early in the init process so the
787 * user feels warm and fuzzy (not cold and prickley).
789 purple_blist_show();
791 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/enabled"))
792 pidgin_debug_window_show();
794 if (opt_login) {
795 /* disable all accounts */
796 for (accounts = purple_accounts_get_all(); accounts != NULL; accounts = accounts->next) {
797 PurpleAccount *account = accounts->data;
798 purple_account_set_enabled(account, PIDGIN_UI, FALSE);
800 /* honor the startup status preference */
801 if (!purple_prefs_get_bool("/purple/savedstatus/startup_current_status"))
802 purple_savedstatus_activate(purple_savedstatus_get_startup());
803 /* now enable the requested ones */
804 dologin_named(opt_login_arg);
805 g_free(opt_login_arg);
806 opt_login_arg = NULL;
807 } else if (opt_nologin) {
808 /* Set all accounts to "offline" */
809 PurpleSavedStatus *saved_status;
811 /* If we've used this type+message before, lookup the transient status */
812 saved_status = purple_savedstatus_find_transient_by_type_and_message(
813 PURPLE_STATUS_OFFLINE, NULL);
815 /* If this type+message is unique then create a new transient saved status */
816 if (saved_status == NULL)
817 saved_status = purple_savedstatus_new(NULL, PURPLE_STATUS_OFFLINE);
819 /* Set the status for each account */
820 purple_savedstatus_activate(saved_status);
821 } else {
822 /* Everything is good to go--sign on already */
823 if (!purple_prefs_get_bool("/purple/savedstatus/startup_current_status"))
824 purple_savedstatus_activate(purple_savedstatus_get_startup());
825 purple_accounts_restore_current_statuses();
828 if ((active_accounts = purple_accounts_get_all_active()) == NULL)
830 pidgin_accounts_window_show();
832 else
834 g_list_free(active_accounts);
837 /* GTK clears the notification for us when opening the first window,
838 * but we may have launched with only a status icon, so clear the it
839 * just in case. */
840 gdk_notify_startup_complete();
842 #ifdef _WIN32
843 winpidgin_post_init();
844 #endif
846 gtk_main();
848 #ifndef _WIN32
849 g_free(segfault_message);
850 g_source_remove(signal_channel_watcher);
851 close(signal_sockets[0]);
852 close(signal_sockets[1]);
853 #endif
855 #ifdef _WIN32
856 winpidgin_cleanup();
857 #endif
859 return 0;