pidgin/libpidgin.c

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

mercurial