Use g_strcmp0() for code simplification
[pidgin-git.git] / pidgin / gtkmain.c
blob13aa1de9c65adf2f4c2bde166ff6faf0f45ce6bc
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 "ft.h"
34 #include "log.h"
35 #include "network.h"
36 #include "notify.h"
37 #include "prefs.h"
38 #include "prpl.h"
39 #include "pounce.h"
40 #include "sound.h"
41 #include "status.h"
42 #include "util.h"
43 #include "whiteboard.h"
45 #include "gtkaccount.h"
46 #include "gtkblist.h"
47 #include "gtkconn.h"
48 #include "gtkconv.h"
49 #include "gtkdebug.h"
50 #include "gtkdialogs.h"
51 #include "gtkdocklet.h"
52 #include "gtkeventloop.h"
53 #include "gtkft.h"
54 #include "gtkidle.h"
55 #include "gtklog.h"
56 #include "gtkmedia.h"
57 #include "gtknotify.h"
58 #include "gtkplugin.h"
59 #include "gtkpounce.h"
60 #include "gtkprefs.h"
61 #include "gtkprivacy.h"
62 #include "gtkrequest.h"
63 #include "gtkroomlist.h"
64 #include "gtksavedstatuses.h"
65 #include "gtksession.h"
66 #include "gtksmiley.h"
67 #include "gtksound.h"
68 #include "gtkthemes.h"
69 #include "gtkutils.h"
70 #include "pidginstock.h"
71 #include "gtkwhiteboard.h"
73 #ifdef HAVE_SIGNAL_H
74 # include <signal.h>
75 #endif
77 #include <getopt.h>
80 #ifdef HAVE_SIGNAL_H
83 * Lists of signals we wish to catch and those we wish to ignore.
84 * Each list terminated with -1
86 static const int catch_sig_list[] = {
87 SIGSEGV,
88 SIGINT,
89 SIGTERM,
90 SIGQUIT,
91 SIGCHLD,
92 #if defined(USE_GSTREAMER) && !defined(GST_CAN_DISABLE_FORKING)
93 SIGALRM,
94 #endif
98 static const int ignore_sig_list[] = {
99 SIGPIPE,
102 #endif
104 static void
105 dologin_named(const char *name)
107 PurpleAccount *account;
108 char **names;
109 int i;
111 if (name != NULL) { /* list of names given */
112 names = g_strsplit(name, ",", 64);
113 for (i = 0; names[i] != NULL; i++) {
114 account = purple_accounts_find(names[i], NULL);
115 if (account != NULL) { /* found a user */
116 purple_account_set_enabled(account, PIDGIN_UI, TRUE);
119 g_strfreev(names);
120 } else { /* no name given, use the first account */
121 GList *accounts;
123 accounts = purple_accounts_get_all();
124 if (accounts != NULL)
126 account = (PurpleAccount *)accounts->data;
127 purple_account_set_enabled(account, PIDGIN_UI, TRUE);
132 #ifdef HAVE_SIGNAL_H
133 static char *segfault_message;
135 static int signal_sockets[2];
137 static void sighandler(int sig);
140 * This child process reaping stuff is currently only used for processes that
141 * were forked to play sounds. It's not needed for forked DNS child, which
142 * have their own waitpid() call. It might be wise to move this code into
143 * gtksound.c.
145 static void
146 clean_pid(void)
148 int status;
149 pid_t pid;
151 do {
152 pid = waitpid(-1, &status, WNOHANG);
153 } while (pid != 0 && pid != (pid_t)-1);
155 if ((pid == (pid_t) - 1) && (errno != ECHILD)) {
156 char errmsg[BUFSIZ];
157 snprintf(errmsg, sizeof(errmsg), "Warning: waitpid() returned %d", pid);
158 perror(errmsg);
162 static void sighandler(int sig)
164 ssize_t written;
167 * We won't do any of the heavy lifting for the signal handling here
168 * because we have no idea what was interrupted. Previously this signal
169 * handler could result in some calls to malloc/free, which can cause
170 * deadlock in libc when the signal handler was interrupting a previous
171 * malloc or free. So instead we'll do an ugly hack where we write the
172 * signal number to one end of a socket pair. The other half of the
173 * socket pair is watched by our main loop. When the main loop sees new
174 * data on the socket it reads in the signal and performs the appropriate
175 * action without fear of interrupting stuff.
177 if (sig == SIGSEGV) {
178 fprintf(stderr, "%s", segfault_message);
179 abort();
180 return;
183 written = write(signal_sockets[0], &sig, sizeof(int));
184 if (written < 0 || written != sizeof(int)) {
185 /* This should never happen */
186 purple_debug_error("sighandler", "Received signal %d but only "
187 "wrote %" G_GSSIZE_FORMAT " bytes out of %"
188 G_GSIZE_FORMAT ": %s\n",
189 sig, written, sizeof(int), g_strerror(errno));
190 exit(1);
194 static gboolean
195 mainloop_sighandler(GIOChannel *source, GIOCondition cond, gpointer data)
197 GIOStatus stat;
198 int sig;
199 gsize bytes_read;
200 GError *error = NULL;
202 /* read the signal number off of the io channel */
203 stat = g_io_channel_read_chars(source, (gchar *)&sig, sizeof(int),
204 &bytes_read, &error);
205 if (stat != G_IO_STATUS_NORMAL) {
206 purple_debug_error("sighandler", "Signal callback failed to read "
207 "from signal socket: %s", error->message);
208 purple_core_quit();
209 return FALSE;
212 switch (sig) {
213 #if defined(USE_GSTREAMER) && !defined(GST_CAN_DISABLE_FORKING)
214 /* By default, gstreamer forks when you initialize it, and waitpids for the
215 * child. But if libpurple reaps the child rather than leaving it to
216 * gstreamer, gstreamer's initialization fails. So, we wait a second before
217 * reaping child processes, to give gst a chance to reap it if it wants to.
219 * This is not needed in later gstreamers, which let us disable the forking.
220 * And, it breaks the world on some Real Unices.
222 case SIGCHLD:
223 /* Restore signal catching */
224 signal(SIGCHLD, sighandler);
225 alarm(1);
226 break;
227 case SIGALRM:
228 #else
229 case SIGCHLD:
230 #endif
231 clean_pid();
232 /* Restore signal catching */
233 signal(SIGCHLD, sighandler);
234 break;
235 default:
236 purple_debug_warning("sighandler", "Caught signal %d\n", sig);
237 purple_core_quit();
240 return TRUE;
242 #endif
244 static int
245 ui_main(void)
247 #ifndef _WIN32
248 GList *icons = NULL;
249 GdkPixbuf *icon = NULL;
250 char *icon_path;
251 gsize i;
252 struct {
253 const char *dir;
254 const char *filename;
255 } icon_sizes[] = {
256 {"16x16", "pidgin.png"},
257 {"24x24", "pidgin.png"},
258 {"32x32", "pidgin.png"},
259 {"48x48", "pidgin.png"},
260 {"scalable", "pidgin.svg"}
263 #endif
265 pidgin_themes_init();
267 pidgin_blist_setup_sort_methods();
269 #ifndef _WIN32
270 /* use the nice PNG icon for all the windows */
271 for(i=0; i<G_N_ELEMENTS(icon_sizes); i++) {
272 icon_path = g_build_filename(DATADIR, "icons", "hicolor", icon_sizes[i].dir, "apps", icon_sizes[i].filename, NULL);
273 icon = pidgin_pixbuf_new_from_file(icon_path);
274 g_free(icon_path);
275 if (icon) {
276 icons = g_list_append(icons,icon);
277 } else {
278 purple_debug_error("ui_main",
279 "Failed to load the default window icon (%spx version)!\n", icon_sizes[i].dir);
282 if(NULL == icons) {
283 purple_debug_error("ui_main", "Unable to load any size of default window icon!\n");
284 } else {
285 gtk_window_set_default_icon_list(icons);
287 g_list_foreach(icons, (GFunc)g_object_unref, NULL);
288 g_list_free(icons);
290 #endif
292 return 0;
295 static void
296 debug_init(void)
298 purple_debug_set_ui_ops(pidgin_debug_get_ui_ops());
299 pidgin_debug_init();
302 static void
303 pidgin_ui_init(void)
305 pidgin_stock_init();
307 /* Set the UI operation structures. */
308 purple_accounts_set_ui_ops(pidgin_accounts_get_ui_ops());
309 purple_xfers_set_ui_ops(pidgin_xfers_get_ui_ops());
310 purple_blist_set_ui_ops(pidgin_blist_get_ui_ops());
311 purple_notify_set_ui_ops(pidgin_notify_get_ui_ops());
312 purple_privacy_set_ui_ops(pidgin_privacy_get_ui_ops());
313 purple_request_set_ui_ops(pidgin_request_get_ui_ops());
314 purple_sound_set_ui_ops(pidgin_sound_get_ui_ops());
315 purple_connections_set_ui_ops(pidgin_connections_get_ui_ops());
316 purple_whiteboard_set_ui_ops(pidgin_whiteboard_get_ui_ops());
317 #if defined(USE_SCREENSAVER) || defined(HAVE_IOKIT)
318 purple_idle_set_ui_ops(pidgin_idle_get_ui_ops());
319 #endif
321 pidgin_account_init();
322 pidgin_connection_init();
323 pidgin_blist_init();
324 pidgin_status_init();
325 pidgin_conversations_init();
326 pidgin_pounces_init();
327 pidgin_privacy_init();
328 pidgin_xfers_init();
329 pidgin_roomlist_init();
330 pidgin_log_init();
331 pidgin_docklet_init();
332 pidgin_smileys_init();
333 pidgin_utils_init();
334 pidgin_medias_init();
335 pidgin_notify_init();
338 static GHashTable *ui_info = NULL;
340 static void
341 pidgin_quit(void)
343 #ifdef USE_SM
344 /* unplug */
345 pidgin_session_end();
346 #endif
348 /* Uninit */
349 pidgin_utils_uninit();
350 pidgin_notify_uninit();
351 pidgin_smileys_uninit();
352 pidgin_conversations_uninit();
353 pidgin_status_uninit();
354 pidgin_docklet_uninit();
355 pidgin_blist_uninit();
356 pidgin_connection_uninit();
357 pidgin_account_uninit();
358 pidgin_xfers_uninit();
359 pidgin_debug_uninit();
361 if(NULL != ui_info)
362 g_hash_table_destroy(ui_info);
364 /* and end it all... */
365 gtk_main_quit();
368 static GHashTable *pidgin_ui_get_info(void)
370 if(NULL == ui_info) {
371 ui_info = g_hash_table_new(g_str_hash, g_str_equal);
373 g_hash_table_insert(ui_info, "name", (char*)PIDGIN_NAME);
374 g_hash_table_insert(ui_info, "version", VERSION);
375 g_hash_table_insert(ui_info, "website", "http://pidgin.im");
376 g_hash_table_insert(ui_info, "dev_website", "http://developer.pidgin.im");
377 g_hash_table_insert(ui_info, "client_type", "pc");
380 * prpl-aim-clientkey is a DevID (or "client key") for Pidgin, given to
381 * us by AOL in September 2016. prpl-icq-clientkey is also a client key
382 * for Pidgin, owned by the AIM account "markdoliner." Please don't use
383 * either for other applications. Instead, you can either not specify a
384 * client key, in which case the default "libpurple" key will be used,
385 * or you can try to register your own at the AIM or ICQ web sites
386 * (although this functionality was removed at some point, it's possible
387 * it has been re-added).
389 g_hash_table_insert(ui_info, "prpl-aim-clientkey", "do1UCeb5gNqxB1S1");
390 g_hash_table_insert(ui_info, "prpl-icq-clientkey", "ma1cSASNCKFtrdv9");
393 * prpl-aim-distid is a distID for Pidgin, given to us by AOL in
394 * September 2016. prpl-icq-distid is also a distID for Pidgin, given
395 * to us by AOL. Please don't use either for other applications.
396 * Instead, you can just not specify a distID and libpurple will use a
397 * default.
399 g_hash_table_insert(ui_info, "prpl-aim-distid", GINT_TO_POINTER(1715));
400 g_hash_table_insert(ui_info, "prpl-icq-distid", GINT_TO_POINTER(1550));
403 return ui_info;
406 static PurpleCoreUiOps core_ops =
408 pidgin_prefs_init,
409 debug_init,
410 pidgin_ui_init,
411 pidgin_quit,
412 pidgin_ui_get_info,
413 NULL,
414 NULL,
415 NULL
418 static PurpleCoreUiOps *
419 pidgin_core_get_ui_ops(void)
421 return &core_ops;
424 static void
425 show_usage(const char *name, gboolean terse)
427 char *text;
429 if (terse) {
430 text = g_strdup_printf(_("%s %s. Try `%s -h' for more information.\n"), PIDGIN_NAME, DISPLAY_VERSION, name);
431 } else {
432 GString *str = g_string_new(NULL);
433 g_string_append_printf(str, "%s %s\n", PIDGIN_NAME, DISPLAY_VERSION);
434 g_string_append_printf(str, _("Usage: %s [OPTION]...\n\n"), name);
435 g_string_append_printf(str, " -c, --config=%s %s\n",
436 _("DIR"), _("use DIR for config files"));
437 g_string_append_printf(str, " -d, --debug %s\n",
438 _("print debugging messages to stdout"));
439 g_string_append_printf(str, " -f, --force-online %s\n",
440 _("force online, regardless of network status"));
441 g_string_append_printf(str, " -h, --help %s\n",
442 _("display this help and exit"));
443 g_string_append_printf(str, " -m, --multiple %s\n",
444 _("allow multiple instances"));
445 g_string_append_printf(str, " -n, --nologin %s\n",
446 _("don't automatically login"));
447 g_string_append_printf(str, " -l, --login[=%s] %s\n",
448 _("NAME"),
449 _("enable specified account(s) (optional argument NAME\n"
451 "specifies account(s) to use, separated by commas.\n"
453 "Without this only the first account will be enabled)."));
454 #ifndef WIN32
455 g_string_append_printf(str, " --display=DISPLAY %s\n",
456 _("X display to use"));
457 #endif /* !WIN32 */
458 g_string_append_printf(str, " -v, --version %s\n",
459 _("display the current version and exit"));
460 text = g_string_free(str, FALSE);
463 purple_print_utf8_to_console(stdout, text);
464 g_free(text);
467 /* FUCKING GET ME A TOWEL! */
468 #ifdef _WIN32
469 /* suppress gcc "no previous prototype" warning */
470 int __cdecl pidgin_main(HINSTANCE hint, int argc, char *argv[]);
471 int __cdecl pidgin_main(HINSTANCE hint, int argc, char *argv[])
472 #else
473 int main(int argc, char *argv[])
474 #endif
476 gboolean opt_force_online = FALSE;
477 gboolean opt_help = FALSE;
478 gboolean opt_login = FALSE;
479 gboolean opt_nologin = FALSE;
480 gboolean opt_version = FALSE;
481 gboolean opt_si = TRUE; /* Check for single instance? */
482 char *opt_config_dir_arg = NULL;
483 char *opt_login_arg = NULL;
484 char *opt_session_arg = NULL;
485 char *search_path;
486 GList *accounts;
487 #ifdef HAVE_SIGNAL_H
488 int sig_indx; /* for setting up signal catching */
489 sigset_t sigset;
490 char errmsg[BUFSIZ];
491 GIOChannel *signal_channel;
492 GIOStatus signal_status;
493 guint signal_channel_watcher;
494 #ifndef DEBUG
495 char *segfault_message_tmp;
496 #endif
497 GError *error;
498 #endif
499 int opt;
500 gboolean gui_check;
501 gboolean debug_enabled;
502 gboolean migration_failed = FALSE;
503 GList *active_accounts;
505 struct option long_options[] = {
506 {"config", required_argument, NULL, 'c'},
507 {"debug", no_argument, NULL, 'd'},
508 {"force-online", no_argument, NULL, 'f'},
509 {"help", no_argument, NULL, 'h'},
510 {"login", optional_argument, NULL, 'l'},
511 {"multiple", no_argument, NULL, 'm'},
512 {"nologin", no_argument, NULL, 'n'},
513 {"session", required_argument, NULL, 's'},
514 {"version", no_argument, NULL, 'v'},
515 {"display", required_argument, NULL, 'D'},
516 {"sync", no_argument, NULL, 'S'},
517 {0, 0, 0, 0}
520 #ifdef DEBUG
521 debug_enabled = TRUE;
522 #else
523 debug_enabled = FALSE;
524 #endif
526 #if !GLIB_CHECK_VERSION(2, 32, 0)
527 /* GLib threading system is automaticaly initialized since 2.32.
528 * For earlier versions, it have to be initialized before calling any
529 * Glib or GTK+ functions.
531 g_thread_init(NULL);
532 #endif
534 g_set_prgname("Pidgin");
536 #ifdef ENABLE_NLS
537 bindtextdomain(PACKAGE, LOCALEDIR);
538 bind_textdomain_codeset(PACKAGE, "UTF-8");
539 textdomain(PACKAGE);
540 #endif
542 #ifdef HAVE_SETLOCALE
543 /* Locale initialization is not complete here. See gtk_init_check() */
544 setlocale(LC_ALL, "");
545 #endif
547 #ifdef HAVE_SIGNAL_H
549 #ifndef DEBUG
550 /* We translate this here in case the crash breaks gettext. */
551 segfault_message_tmp = g_strdup_printf(_(
552 "%s %s has segfaulted and attempted to dump a core file.\n"
553 "This is a bug in the software and has happened through\n"
554 "no fault of your own.\n\n"
555 "If you can reproduce the crash, please notify the developers\n"
556 "by reporting a bug at:\n"
557 "%ssimpleticket/\n\n"
558 "Please make sure to specify what you were doing at the time\n"
559 "and post the backtrace from the core file. If you do not know\n"
560 "how to get the backtrace, please read the instructions at\n"
561 "%swiki/GetABacktrace\n"),
562 PIDGIN_NAME, DISPLAY_VERSION, PURPLE_DEVEL_WEBSITE, PURPLE_DEVEL_WEBSITE
565 /* we have to convert the message (UTF-8 to console
566 charset) early because after a segmentation fault
567 it's not a good practice to allocate memory */
568 error = NULL;
569 segfault_message = g_locale_from_utf8(segfault_message_tmp,
570 -1, NULL, NULL, &error);
571 if (segfault_message != NULL) {
572 g_free(segfault_message_tmp);
574 else {
575 /* use 'segfault_message_tmp' (UTF-8) as a fallback */
576 g_warning("%s\n", error->message);
577 g_error_free(error);
578 segfault_message = segfault_message_tmp;
580 #else
581 /* Don't mark this for translation. */
582 segfault_message = g_strdup(
583 "Hi, user. We need to talk.\n"
584 "I think something's gone wrong here. It's probably my fault.\n"
585 "No, really, it's not you... it's me... no no no, I think we get along well\n"
586 "it's just that.... well, I want to see other people. I... what?!? NO! I \n"
587 "haven't been cheating on you!! How many times do you want me to tell you?! And\n"
588 "for the last time, it's just a rash!\n"
590 #endif
593 * Create a socket pair for receiving unix signals from a signal
594 * handler.
596 if (socketpair(AF_UNIX, SOCK_STREAM, 0, signal_sockets) < 0) {
597 perror("Failed to create sockets for GLib signal handling");
598 exit(1);
600 signal_channel = g_io_channel_unix_new(signal_sockets[1]);
603 * Set the channel encoding to raw binary instead of the default of
604 * UTF-8, because we'll be sending integers across instead of strings.
606 error = NULL;
607 signal_status = g_io_channel_set_encoding(signal_channel, NULL, &error);
608 if (signal_status != G_IO_STATUS_NORMAL) {
609 fprintf(stderr, "Failed to set the signal channel to raw "
610 "binary: %s", error->message);
611 exit(1);
613 signal_channel_watcher = g_io_add_watch(signal_channel, G_IO_IN, mainloop_sighandler, NULL);
614 g_io_channel_unref(signal_channel);
616 /* Let's not violate any PLA's!!!! */
617 /* jseymour: whatever the fsck that means */
618 /* Robot101: for some reason things like gdm like to block *
619 * useful signals like SIGCHLD, so we unblock all the ones we *
620 * declare a handler for. thanks JSeymour and Vann. */
621 if (sigemptyset(&sigset)) {
622 snprintf(errmsg, sizeof(errmsg), "Warning: couldn't initialise empty signal set");
623 perror(errmsg);
625 for(sig_indx = 0; catch_sig_list[sig_indx] != -1; ++sig_indx) {
626 if(signal(catch_sig_list[sig_indx], sighandler) == SIG_ERR) {
627 snprintf(errmsg, sizeof(errmsg), "Warning: couldn't set signal %d for catching",
628 catch_sig_list[sig_indx]);
629 perror(errmsg);
631 if(sigaddset(&sigset, catch_sig_list[sig_indx])) {
632 snprintf(errmsg, sizeof(errmsg), "Warning: couldn't include signal %d for unblocking",
633 catch_sig_list[sig_indx]);
634 perror(errmsg);
637 for(sig_indx = 0; ignore_sig_list[sig_indx] != -1; ++sig_indx) {
638 if(signal(ignore_sig_list[sig_indx], SIG_IGN) == SIG_ERR) {
639 snprintf(errmsg, sizeof(errmsg), "Warning: couldn't set signal %d to ignore",
640 ignore_sig_list[sig_indx]);
641 perror(errmsg);
645 if (sigprocmask(SIG_UNBLOCK, &sigset, NULL)) {
646 snprintf(errmsg, sizeof(errmsg), "Warning: couldn't unblock signals");
647 perror(errmsg);
649 #endif
651 /* scan command-line options */
652 opterr = 1;
653 while ((opt = getopt_long(argc, argv,
654 #ifndef _WIN32
655 "c:dfhmnl::s:v",
656 #else
657 "c:dfhmnl::v",
658 #endif
659 long_options, NULL)) != -1) {
660 switch (opt) {
661 case 'c': /* config dir */
662 g_free(opt_config_dir_arg);
663 opt_config_dir_arg = g_strdup(optarg);
664 break;
665 case 'd': /* debug */
666 debug_enabled = TRUE;
667 break;
668 case 'f': /* force-online */
669 opt_force_online = TRUE;
670 break;
671 case 'h': /* help */
672 opt_help = TRUE;
673 break;
674 case 'n': /* no autologin */
675 opt_nologin = TRUE;
676 break;
677 case 'l': /* login, option username */
678 opt_login = TRUE;
679 g_free(opt_login_arg);
680 if (optarg != NULL)
681 opt_login_arg = g_strdup(optarg);
682 break;
683 case 's': /* use existing session ID */
684 g_free(opt_session_arg);
685 opt_session_arg = g_strdup(optarg);
686 break;
687 case 'v': /* version */
688 opt_version = TRUE;
689 break;
690 case 'm': /* do not ensure single instance. */
691 opt_si = FALSE;
692 break;
693 case 'D': /* --display */
694 case 'S': /* --sync */
695 /* handled by gtk_init_check below */
696 break;
697 case '?': /* show terse help */
698 default:
699 show_usage(argv[0], TRUE);
700 #ifdef HAVE_SIGNAL_H
701 g_free(segfault_message);
702 #endif
703 return 0;
704 break;
708 /* show help message */
709 if (opt_help) {
710 show_usage(argv[0], FALSE);
711 #ifdef HAVE_SIGNAL_H
712 g_free(segfault_message);
713 #endif
714 return 0;
716 /* show version message */
717 if (opt_version) {
718 printf("%s %s (libpurple %s)\n", PIDGIN_NAME, DISPLAY_VERSION,
719 purple_core_get_version());
720 #ifdef HAVE_SIGNAL_H
721 g_free(segfault_message);
722 #endif
723 return 0;
726 /* set a user-specified config directory */
727 if (opt_config_dir_arg != NULL) {
728 purple_util_set_user_dir(opt_config_dir_arg);
732 * We're done piddling around with command line arguments.
733 * Fire up this baby.
736 purple_debug_set_enabled(debug_enabled);
738 /* If we're using a custom configuration directory, we
739 * do NOT want to migrate, or weird things will happen. */
740 if (opt_config_dir_arg == NULL)
742 if (!purple_core_migrate())
744 migration_failed = TRUE;
748 search_path = g_build_filename(purple_user_dir(), "gtkrc-2.0", NULL);
749 gtk_rc_add_default_file(search_path);
750 g_free(search_path);
752 gui_check = gtk_init_check(&argc, &argv);
753 if (!gui_check) {
754 char *display = gdk_get_display();
756 printf("%s %s\n", PIDGIN_NAME, DISPLAY_VERSION);
758 g_warning("cannot open display: %s", display ? display : "unset");
759 g_free(display);
760 #ifdef HAVE_SIGNAL_H
761 g_free(segfault_message);
762 #endif
764 return 1;
767 g_set_application_name(PIDGIN_NAME);
769 #ifdef _WIN32
770 winpidgin_init(hint);
771 #endif
773 if (migration_failed)
775 char *old = g_strconcat(purple_home_dir(),
776 G_DIR_SEPARATOR_S ".gaim", NULL);
777 const char *text = _(
778 "%s encountered errors migrating your settings "
779 "from %s to %s. Please investigate and complete the "
780 "migration by hand. Please report this error at http://developer.pidgin.im");
781 GtkWidget *dialog;
783 dialog = gtk_message_dialog_new(NULL,
785 GTK_MESSAGE_ERROR,
786 GTK_BUTTONS_CLOSE,
787 text, PIDGIN_NAME,
788 old, purple_user_dir());
789 g_free(old);
791 g_signal_connect_swapped(dialog, "response",
792 G_CALLBACK(gtk_main_quit), NULL);
794 gtk_widget_show_all(dialog);
796 gtk_main();
798 #ifdef HAVE_SIGNAL_H
799 g_free(segfault_message);
800 #endif
801 return 0;
804 purple_core_set_ui_ops(pidgin_core_get_ui_ops());
805 purple_eventloop_set_ui_ops(pidgin_eventloop_get_ui_ops());
808 * Set plugin search directories. Give priority to the plugins
809 * in user's home directory.
811 search_path = g_build_filename(purple_user_dir(), "plugins", NULL);
812 if (g_mkdir(search_path, S_IRUSR | S_IWUSR | S_IXUSR) != 0 && errno != EEXIST)
813 fprintf(stderr, "Couldn't create plugins dir\n");
814 purple_plugins_add_search_path(search_path);
815 g_free(search_path);
816 purple_plugins_add_search_path(LIBDIR);
818 if (!purple_core_init(PIDGIN_UI)) {
819 fprintf(stderr,
820 "Initialization of the libpurple core failed. Dumping core.\n"
821 "Please report this!\n");
822 #ifdef HAVE_SIGNAL_H
823 g_free(segfault_message);
824 #endif
825 abort();
828 if (opt_si && !purple_core_ensure_single_instance()) {
829 #ifdef HAVE_DBUS
830 DBusConnection *conn = purple_dbus_get_connection();
831 DBusMessage *message = dbus_message_new_method_call(DBUS_SERVICE_PURPLE, DBUS_PATH_PURPLE,
832 DBUS_INTERFACE_PURPLE, "PurpleBlistSetVisible");
833 gboolean tr = TRUE;
834 dbus_message_append_args(message, DBUS_TYPE_INT32, &tr, DBUS_TYPE_INVALID);
835 dbus_connection_send_with_reply_and_block(conn, message, -1, NULL);
836 dbus_message_unref(message);
837 #endif
838 gdk_notify_startup_complete();
839 purple_core_quit();
840 g_printerr(_("Exiting because another libpurple client is already running.\n"));
841 #ifdef HAVE_SIGNAL_H
842 g_free(segfault_message);
843 #endif
844 return 0;
847 /* TODO: Move blist loading into purple_blist_init() */
848 purple_set_blist(purple_blist_new());
849 purple_blist_load();
851 /* load plugins we had when we quit */
852 purple_plugins_load_saved(PIDGIN_PREFS_ROOT "/plugins/loaded");
854 /* TODO: Move pounces loading into purple_pounces_init() */
855 purple_pounces_load();
857 ui_main();
859 #ifdef USE_SM
860 pidgin_session_init(argv[0], opt_session_arg, opt_config_dir_arg);
861 #endif
862 if (opt_session_arg != NULL) {
863 g_free(opt_session_arg);
864 opt_session_arg = NULL;
866 if (opt_config_dir_arg != NULL) {
867 g_free(opt_config_dir_arg);
868 opt_config_dir_arg = NULL;
871 /* This needs to be before purple_blist_show() so the
872 * statusbox gets the forced online status. */
873 if (opt_force_online)
874 purple_network_force_online();
877 * We want to show the blist early in the init process so the
878 * user feels warm and fuzzy (not cold and prickley).
880 purple_blist_show();
882 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/enabled"))
883 pidgin_debug_window_show();
885 if (opt_login) {
886 /* disable all accounts */
887 for (accounts = purple_accounts_get_all(); accounts != NULL; accounts = accounts->next) {
888 PurpleAccount *account = accounts->data;
889 purple_account_set_enabled(account, PIDGIN_UI, FALSE);
891 /* honor the startup status preference */
892 if (!purple_prefs_get_bool("/purple/savedstatus/startup_current_status"))
893 purple_savedstatus_activate(purple_savedstatus_get_startup());
894 /* now enable the requested ones */
895 dologin_named(opt_login_arg);
896 if (opt_login_arg != NULL) {
897 g_free(opt_login_arg);
898 opt_login_arg = NULL;
900 } else if (opt_nologin) {
901 /* Set all accounts to "offline" */
902 PurpleSavedStatus *saved_status;
904 /* If we've used this type+message before, lookup the transient status */
905 saved_status = purple_savedstatus_find_transient_by_type_and_message(
906 PURPLE_STATUS_OFFLINE, NULL);
908 /* If this type+message is unique then create a new transient saved status */
909 if (saved_status == NULL)
910 saved_status = purple_savedstatus_new(NULL, PURPLE_STATUS_OFFLINE);
912 /* Set the status for each account */
913 purple_savedstatus_activate(saved_status);
914 } else {
915 /* Everything is good to go--sign on already */
916 if (!purple_prefs_get_bool("/purple/savedstatus/startup_current_status"))
917 purple_savedstatus_activate(purple_savedstatus_get_startup());
918 purple_accounts_restore_current_statuses();
921 if ((active_accounts = purple_accounts_get_all_active()) == NULL)
923 pidgin_accounts_window_show();
925 else
927 g_list_free(active_accounts);
930 /* GTK clears the notification for us when opening the first window,
931 * but we may have launched with only a status icon, so clear the it
932 * just in case. */
933 gdk_notify_startup_complete();
935 #ifdef _WIN32
936 winpidgin_post_init();
937 #endif
939 gtk_main();
941 #ifdef HAVE_SIGNAL_H
942 g_free(segfault_message);
943 g_source_remove(signal_channel_watcher);
944 close(signal_sockets[0]);
945 close(signal_sockets[1]);
946 #endif
948 #ifdef _WIN32
949 winpidgin_cleanup();
950 #endif
952 return 0;