Add ChangeLog entries for the two security fixes and a few other things.
[pidgin-git.git] / libpurple / core.c
blobc07fc35c9ed03508b42b7a167bba3b530357518b
1 /**
2 * @file core.c Purple Core API
3 * @ingroup core
4 */
6 /* purple
8 * Purple is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
26 #include "internal.h"
27 #include "cipher.h"
28 #include "certificate.h"
29 #include "cmds.h"
30 #include "connection.h"
31 #include "conversation.h"
32 #include "core.h"
33 #include "debug.h"
34 #include "dnsquery.h"
35 #include "ft.h"
36 #include "idle.h"
37 #include "imgstore.h"
38 #include "network.h"
39 #include "notify.h"
40 #include "plugin.h"
41 #include "pounce.h"
42 #include "prefs.h"
43 #include "privacy.h"
44 #include "proxy.h"
45 #include "savedstatuses.h"
46 #include "signals.h"
47 #include "smiley.h"
48 #include "sound.h"
49 #include "sound-theme-loader.h"
50 #include "sslconn.h"
51 #include "status.h"
52 #include "stun.h"
53 #include "theme-manager.h"
54 #include "util.h"
56 #ifdef HAVE_DBUS
57 # ifndef DBUS_API_SUBJECT_TO_CHANGE
58 # define DBUS_API_SUBJECT_TO_CHANGE
59 # endif
60 # include <dbus/dbus.h>
61 # include "dbus-purple.h"
62 # include "dbus-server.h"
63 # include "dbus-bindings.h"
64 #endif
66 struct PurpleCore
68 char *ui;
70 void *reserved;
73 static PurpleCoreUiOps *_ops = NULL;
74 static PurpleCore *_core = NULL;
76 STATIC_PROTO_INIT
78 gboolean
79 purple_core_init(const char *ui)
81 PurpleCoreUiOps *ops;
82 PurpleCore *core;
84 g_return_val_if_fail(ui != NULL, FALSE);
85 g_return_val_if_fail(purple_get_core() == NULL, FALSE);
87 #ifdef ENABLE_NLS
88 bindtextdomain(PACKAGE, LOCALEDIR);
89 #endif
90 #ifdef _WIN32
91 wpurple_init();
92 #endif
94 g_type_init();
96 _core = core = g_new0(PurpleCore, 1);
97 core->ui = g_strdup(ui);
98 core->reserved = NULL;
100 ops = purple_core_get_ui_ops();
102 /* The signals subsystem is important and should be first. */
103 purple_signals_init();
105 purple_util_init();
107 purple_signal_register(core, "uri-handler",
108 purple_marshal_BOOLEAN__POINTER_POINTER_POINTER,
109 purple_value_new(PURPLE_TYPE_BOOLEAN), 3,
110 purple_value_new(PURPLE_TYPE_STRING), /* Protocol */
111 purple_value_new(PURPLE_TYPE_STRING), /* Command */
112 purple_value_new(PURPLE_TYPE_BOXED, "GHashTable *")); /* Parameters */
114 purple_signal_register(core, "quitting", purple_marshal_VOID, NULL, 0);
116 /* The prefs subsystem needs to be initialized before static protocols
117 * for protocol prefs to work. */
118 purple_prefs_init();
120 purple_debug_init();
122 if (ops != NULL)
124 if (ops->ui_prefs_init != NULL)
125 ops->ui_prefs_init();
127 if (ops->debug_ui_init != NULL)
128 ops->debug_ui_init();
131 #ifdef HAVE_DBUS
132 purple_dbus_init();
133 #endif
135 purple_ciphers_init();
136 purple_cmds_init();
138 /* Since plugins get probed so early we should probably initialize their
139 * subsystem right away too.
141 purple_plugins_init();
143 /* Initialize all static protocols. */
144 static_proto_init();
146 purple_plugins_probe(G_MODULE_SUFFIX);
148 purple_theme_manager_init();
150 /* The buddy icon code uses the imgstore, so init it early. */
151 purple_imgstore_init();
153 /* Accounts use status, buddy icons and connection signals, so
154 * initialize these before accounts
156 purple_status_init();
157 purple_buddy_icons_init();
158 purple_connections_init();
160 purple_accounts_init();
161 purple_savedstatuses_init();
162 purple_notify_init();
163 purple_certificate_init();
164 purple_conversations_init();
165 purple_blist_init();
166 purple_log_init();
167 purple_network_init();
168 purple_privacy_init();
169 purple_pounces_init();
170 purple_proxy_init();
171 purple_dnsquery_init();
172 purple_sound_init();
173 purple_ssl_init();
174 purple_stun_init();
175 purple_xfers_init();
176 purple_idle_init();
177 purple_smileys_init();
179 * Call this early on to try to auto-detect our IP address and
180 * hopefully save some time later.
182 purple_network_get_my_ip(-1);
184 if (ops != NULL && ops->ui_init != NULL)
185 ops->ui_init();
187 /* The UI may have registered some theme types, so refresh them */
188 purple_theme_manager_refresh();
190 return TRUE;
193 void
194 purple_core_quit(void)
196 PurpleCoreUiOps *ops;
197 PurpleCore *core = purple_get_core();
199 g_return_if_fail(core != NULL);
201 /* The self destruct sequence has been initiated */
202 purple_signal_emit(purple_get_core(), "quitting");
204 /* Transmission ends */
205 purple_connections_disconnect_all();
208 * Certificates must be destroyed before the SSL plugins, because
209 * PurpleCertificates contain pointers to PurpleCertificateSchemes,
210 * and the PurpleCertificateSchemes will be unregistered when the
211 * SSL plugin is uninit.
213 purple_certificate_uninit();
215 /* The SSL plugins must be uninit before they're unloaded */
216 purple_ssl_uninit();
218 /* Unload all non-loader, non-prpl plugins before shutting down
219 * subsystems. */
220 purple_debug_info("main", "Unloading normal plugins\n");
221 purple_plugins_unload(PURPLE_PLUGIN_STANDARD);
223 /* Save .xml files, remove signals, etc. */
224 purple_smileys_uninit();
225 purple_idle_uninit();
226 purple_pounces_uninit();
227 purple_blist_uninit();
228 purple_ciphers_uninit();
229 purple_notify_uninit();
230 purple_conversations_uninit();
231 purple_connections_uninit();
232 purple_buddy_icons_uninit();
233 purple_savedstatuses_uninit();
234 purple_status_uninit();
235 purple_accounts_uninit();
236 purple_sound_uninit();
237 purple_theme_manager_uninit();
238 purple_xfers_uninit();
239 purple_proxy_uninit();
240 purple_dnsquery_uninit();
241 purple_imgstore_uninit();
242 purple_network_uninit();
244 /* Everything after unloading all plugins must not fail if prpls aren't
245 * around */
246 purple_debug_info("main", "Unloading all plugins\n");
247 purple_plugins_destroy_all();
249 ops = purple_core_get_ui_ops();
250 if (ops != NULL && ops->quit != NULL)
251 ops->quit();
253 /* Everything after prefs_uninit must not try to read any prefs */
254 purple_prefs_uninit();
255 purple_plugins_uninit();
256 #ifdef HAVE_DBUS
257 purple_dbus_uninit();
258 #endif
260 purple_cmds_uninit();
261 /* Everything after util_uninit cannot try to write things to the confdir */
262 purple_util_uninit();
263 purple_log_uninit();
265 purple_signals_uninit();
267 g_free(core->ui);
268 g_free(core);
270 #ifdef _WIN32
271 wpurple_cleanup();
272 #endif
274 _core = NULL;
277 gboolean
278 purple_core_quit_cb(gpointer unused)
280 purple_core_quit();
282 return FALSE;
285 const char *
286 purple_core_get_version(void)
288 return VERSION;
291 const char *
292 purple_core_get_ui(void)
294 PurpleCore *core = purple_get_core();
296 g_return_val_if_fail(core != NULL, NULL);
298 return core->ui;
301 PurpleCore *
302 purple_get_core(void)
304 return _core;
307 void
308 purple_core_set_ui_ops(PurpleCoreUiOps *ops)
310 _ops = ops;
313 PurpleCoreUiOps *
314 purple_core_get_ui_ops(void)
316 return _ops;
319 #ifdef HAVE_DBUS
320 static char *purple_dbus_owner_user_dir(void)
322 DBusMessage *msg = NULL, *reply = NULL;
323 DBusConnection *dbus_connection = NULL;
324 DBusError dbus_error;
325 char *remote_user_dir = NULL;
327 if ((dbus_connection = purple_dbus_get_connection()) == NULL)
328 return NULL;
330 if ((msg = dbus_message_new_method_call(DBUS_SERVICE_PURPLE, DBUS_PATH_PURPLE, DBUS_INTERFACE_PURPLE, "PurpleUserDir")) == NULL)
331 return NULL;
333 dbus_error_init(&dbus_error);
334 reply = dbus_connection_send_with_reply_and_block(dbus_connection, msg, 5000, &dbus_error);
335 dbus_message_unref(msg);
336 dbus_error_free(&dbus_error);
338 if (reply)
340 dbus_error_init(&dbus_error);
341 dbus_message_get_args(reply, &dbus_error, DBUS_TYPE_STRING, &remote_user_dir, DBUS_TYPE_INVALID);
342 remote_user_dir = g_strdup(remote_user_dir);
343 dbus_error_free(&dbus_error);
344 dbus_message_unref(reply);
347 return remote_user_dir;
350 #endif /* HAVE_DBUS */
352 gboolean
353 purple_core_ensure_single_instance()
355 gboolean is_single_instance = TRUE;
356 #ifdef HAVE_DBUS
357 /* in the future, other mechanisms might have already set this to FALSE */
358 if (is_single_instance)
360 if (!purple_dbus_is_owner())
362 const char *user_dir = purple_user_dir();
363 char *dbus_owner_user_dir = purple_dbus_owner_user_dir();
365 is_single_instance = !purple_strequal(dbus_owner_user_dir, user_dir);
366 g_free(dbus_owner_user_dir);
369 #endif /* HAVE_DBUS */
371 return is_single_instance;
374 static gboolean
375 move_and_symlink_dir(const char *path, const char *basename, const char *old_base, const char *new_base, const char *relative)
377 char *new_name = g_build_filename(new_base, basename, NULL);
378 #ifndef _WIN32
379 char *old_name;
380 #endif
381 if (g_rename(path, new_name))
383 purple_debug_error("core", "Error renaming %s to %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
384 path, new_name, g_strerror(errno));
385 g_free(new_name);
386 return FALSE;
388 g_free(new_name);
390 #ifndef _WIN32
391 /* NOTE: This new_name is relative. */
392 new_name = g_build_filename(relative, basename, NULL);
393 old_name = g_build_filename(old_base, basename, NULL);
394 if (symlink(new_name, old_name))
396 purple_debug_warning("core", "Error symlinking %s to %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
397 old_name, new_name, g_strerror(errno));
399 g_free(old_name);
400 g_free(new_name);
401 #endif
403 return TRUE;
406 gboolean
407 purple_core_migrate(void)
409 const char *user_dir = purple_user_dir();
410 char *old_user_dir = g_strconcat(purple_home_dir(),
411 G_DIR_SEPARATOR_S ".gaim", NULL);
412 char *status_file;
413 FILE *fp;
414 GDir *dir;
415 GError *err;
416 const char *entry;
417 #ifndef _WIN32
418 char *logs_dir;
419 #endif
420 char *old_icons_dir;
422 if (!g_file_test(old_user_dir, G_FILE_TEST_EXISTS))
424 /* ~/.gaim doesn't exist, so there's nothing to migrate. */
425 g_free(old_user_dir);
426 return TRUE;
429 status_file = g_strconcat(user_dir, G_DIR_SEPARATOR_S "migrating", NULL);
431 if (g_file_test(user_dir, G_FILE_TEST_EXISTS))
433 /* If we're here, we have both ~/.gaim and .purple. */
435 if (!g_file_test(status_file, G_FILE_TEST_EXISTS))
437 /* There's no "migrating" status file,
438 * so ~/.purple is all up to date. */
439 g_free(status_file);
440 g_free(old_user_dir);
441 return TRUE;
445 /* If we're here, it's time to migrate from ~/.gaim to ~/.purple. */
447 /* Ensure the user directory exists */
448 if (!g_file_test(user_dir, G_FILE_TEST_IS_DIR))
450 if (g_mkdir(user_dir, S_IRUSR | S_IWUSR | S_IXUSR) == -1)
452 purple_debug_error("core", "Error creating directory %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
453 user_dir, g_strerror(errno));
454 g_free(status_file);
455 g_free(old_user_dir);
456 return FALSE;
460 /* This writes ~/.purple/migrating, which allows us to detect
461 * incomplete migrations and properly retry. */
462 if (!(fp = g_fopen(status_file, "w")))
464 purple_debug_error("core", "Error opening file %s for writing: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
465 status_file, g_strerror(errno));
466 g_free(status_file);
467 g_free(old_user_dir);
468 return FALSE;
470 fclose(fp);
472 /* Open ~/.gaim so we can loop over its contents. */
473 err = NULL;
474 if (!(dir = g_dir_open(old_user_dir, 0, &err)))
476 purple_debug_error("core", "Error opening directory %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
477 status_file,
478 (err ? err->message : "Unknown error"));
479 if (err)
480 g_error_free(err);
481 g_free(status_file);
482 g_free(old_user_dir);
483 return FALSE;
486 /* Loop over the contents of ~/.gaim */
487 while ((entry = g_dir_read_name(dir)))
489 char *name = g_build_filename(old_user_dir, entry, NULL);
491 #ifndef _WIN32
492 /* Deal with symlinks... */
493 if (g_file_test(name, G_FILE_TEST_IS_SYMLINK))
495 /* We're only going to duplicate a logs symlink. */
496 if (purple_strequal(entry, "logs"))
498 char *link;
499 err = NULL;
501 if ((link = g_file_read_link(name, &err)) == NULL)
503 char *name_utf8 = g_filename_to_utf8(name, -1, NULL, NULL, NULL);
504 purple_debug_error("core", "Error reading symlink %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
505 name_utf8 ? name_utf8 : name, err->message);
506 g_free(name_utf8);
507 g_error_free(err);
508 g_free(name);
509 g_dir_close(dir);
510 g_free(status_file);
511 g_free(old_user_dir);
512 return FALSE;
515 logs_dir = g_build_filename(user_dir, "logs", NULL);
517 if (purple_strequal(link, "../.purple/logs") ||
518 purple_strequal(link, logs_dir))
520 /* If the symlink points to the new directory, we're
521 * likely just trying again after a failed migration,
522 * so there's no need to fail here. */
523 g_free(link);
524 g_free(logs_dir);
525 continue;
528 /* In case we are trying again after a failed migration, we need
529 * to unlink any existing symlink. If it's a directory, this
530 * will fail, and so will the symlink below, which is good
531 * because the user should sort things out. */
532 g_unlink(logs_dir);
534 /* Relative links will most likely still be
535 * valid from ~/.purple, though it's not
536 * guaranteed. Oh well. */
537 if (symlink(link, logs_dir))
539 purple_debug_error("core", "Error symlinking %s to %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
540 logs_dir, link, g_strerror(errno));
541 g_free(link);
542 g_free(name);
543 g_free(logs_dir);
544 g_dir_close(dir);
545 g_free(status_file);
546 g_free(old_user_dir);
547 return FALSE;
550 g_free(link);
551 g_free(logs_dir);
552 continue;
555 /* Ignore all other symlinks. */
556 continue;
558 #endif
560 /* Deal with directories... */
561 if (g_file_test(name, G_FILE_TEST_IS_DIR))
563 if (purple_strequal(entry, "icons"))
565 /* This is a special case for the Album plugin, which
566 * stores data in the icons folder. We're not copying
567 * the icons directory over because previous bugs
568 * meant that it filled up with junk for many users.
569 * This is a great time to purge it. */
571 GDir *icons_dir;
572 char *new_icons_dir;
573 const char *icons_entry;
575 err = NULL;
576 if (!(icons_dir = g_dir_open(name, 0, &err)))
578 purple_debug_error("core", "Error opening directory %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
579 name,
580 (err ? err->message : "Unknown error"));
581 if (err)
582 g_error_free(err);
583 g_free(name);
584 g_dir_close(dir);
585 g_free(status_file);
586 g_free(old_user_dir);
587 return FALSE;
590 new_icons_dir = g_build_filename(user_dir, "icons", NULL);
591 /* Ensure the new icon directory exists */
592 if (!g_file_test(new_icons_dir, G_FILE_TEST_IS_DIR))
594 if (g_mkdir(new_icons_dir, S_IRUSR | S_IWUSR | S_IXUSR) == -1)
596 purple_debug_error("core", "Error creating directory %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
597 new_icons_dir, g_strerror(errno));
598 g_free(new_icons_dir);
599 g_dir_close(icons_dir);
600 g_free(name);
601 g_dir_close(dir);
602 g_free(status_file);
603 g_free(old_user_dir);
604 return FALSE;
608 while ((icons_entry = g_dir_read_name(icons_dir)))
610 char *icons_name = g_build_filename(name, icons_entry, NULL);
612 if (g_file_test(icons_name, G_FILE_TEST_IS_DIR))
614 if (!move_and_symlink_dir(icons_name, icons_entry,
615 name, new_icons_dir, "../../.purple/icons"))
617 g_free(icons_name);
618 g_free(new_icons_dir);
619 g_dir_close(icons_dir);
620 g_free(name);
621 g_dir_close(dir);
622 g_free(status_file);
623 g_free(old_user_dir);
624 return FALSE;
627 g_free(icons_name);
630 g_dir_close(icons_dir);
632 else if (purple_strequal(entry, "plugins"))
634 /* Do nothing, because we broke plugin compatibility.
635 * This means that the plugins directory gets left behind. */
637 else
639 /* All other directories are moved and symlinked. */
640 if (!move_and_symlink_dir(name, entry, old_user_dir, user_dir, "../.purple"))
642 g_free(name);
643 g_dir_close(dir);
644 g_free(status_file);
645 g_free(old_user_dir);
646 return FALSE;
650 else if (g_file_test(name, G_FILE_TEST_IS_REGULAR))
652 /* Regular files are copied. */
654 char *new_name;
655 FILE *new_file;
657 if (!(fp = g_fopen(name, "rb")))
659 purple_debug_error("core", "Error opening file %s for reading: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
660 name, g_strerror(errno));
661 g_free(name);
662 g_dir_close(dir);
663 g_free(status_file);
664 g_free(old_user_dir);
665 return FALSE;
668 new_name = g_build_filename(user_dir, entry, NULL);
669 if (!(new_file = g_fopen(new_name, "wb")))
671 purple_debug_error("core", "Error opening file %s for writing: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
672 new_name, g_strerror(errno));
673 fclose(fp);
674 g_free(new_name);
675 g_free(name);
676 g_dir_close(dir);
677 g_free(status_file);
678 g_free(old_user_dir);
679 return FALSE;
682 while (!feof(fp))
684 unsigned char buf[256];
685 size_t size;
687 size = fread(buf, 1, sizeof(buf), fp);
688 if (size != sizeof(buf) && !feof(fp))
690 purple_debug_error("core", "Error reading %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
691 name, g_strerror(errno));
692 fclose(new_file);
693 fclose(fp);
694 g_free(new_name);
695 g_free(name);
696 g_dir_close(dir);
697 g_free(status_file);
698 g_free(old_user_dir);
699 return FALSE;
702 if (!fwrite(buf, size, 1, new_file) && ferror(new_file) != 0)
704 purple_debug_error("core", "Error writing %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
705 new_name, g_strerror(errno));
706 fclose(new_file);
707 fclose(fp);
708 g_free(new_name);
709 g_free(name);
710 g_dir_close(dir);
711 g_free(status_file);
712 g_free(old_user_dir);
713 return FALSE;
717 if (fclose(new_file))
719 purple_debug_error("core", "Error writing: %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
720 new_name, g_strerror(errno));
722 if (fclose(fp))
724 purple_debug_warning("core", "Error closing %s: %s\n",
725 name, g_strerror(errno));
727 g_free(new_name);
729 else
730 purple_debug_warning("core", "Not a regular file or directory: %s\n", name);
732 g_free(name);
735 /* The migration was successful, so delete the status file. */
736 if (g_unlink(status_file))
738 purple_debug_error("core", "Error unlinking file %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
739 status_file, g_strerror(errno));
740 g_free(status_file);
741 return FALSE;
744 old_icons_dir = g_build_filename(old_user_dir, "icons", NULL);
745 _purple_buddy_icon_set_old_icons_dir(old_icons_dir);
746 g_free(old_icons_dir);
748 g_free(old_user_dir);
750 g_free(status_file);
751 return TRUE;
754 GHashTable* purple_core_get_ui_info() {
755 PurpleCoreUiOps *ops = purple_core_get_ui_ops();
757 if(NULL == ops || NULL == ops->get_ui_info)
758 return NULL;
760 return ops->get_ui_info();