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