~ubuntu-branches/ubuntu/raring/pidgin/raring

1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
1
/**
2
 * @file core.c Purple Core API
3
 * @ingroup core
1.1.5 by Sebastien Bacher
Import upstream version 2.2.1
4
 */
5
6
/* purple
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
7
 *
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.
11
 *
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.
16
 *
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.
21
 *
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
1.1.4 by Sebastien Bacher
Import upstream version 2.2.0
24
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
25
 */
26
#include "internal.h"
27
#include "cipher.h"
1.1.4 by Sebastien Bacher
Import upstream version 2.2.0
28
#include "certificate.h"
1.1.12 by Sebastien Bacher
Import upstream version 2.5.0
29
#include "cmds.h"
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
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"
1.1.12 by Sebastien Bacher
Import upstream version 2.5.0
47
#include "smiley.h"
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
48
#include "sound.h"
49
#include "sslconn.h"
50
#include "status.h"
51
#include "stun.h"
52
#include "util.h"
53
54
#ifdef HAVE_DBUS
1.1.9 by Pedro Fragoso
Import upstream version 2.4.1
55
#  ifndef DBUS_API_SUBJECT_TO_CHANGE
56
#    define DBUS_API_SUBJECT_TO_CHANGE
57
#  endif
1.1.2 by Sebastien Bacher
Import upstream version 2.1.0
58
#  include <dbus/dbus.h>
59
#  include "dbus-purple.h"
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
60
#  include "dbus-server.h"
1.1.2 by Sebastien Bacher
Import upstream version 2.1.0
61
#  include "dbus-bindings.h"
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
62
#endif
63
64
struct PurpleCore
65
{
66
	char *ui;
67
68
	void *reserved;
69
};
70
71
static PurpleCoreUiOps *_ops  = NULL;
72
static PurpleCore      *_core = NULL;
73
74
STATIC_PROTO_INIT
75
76
gboolean
77
purple_core_init(const char *ui)
78
{
79
	PurpleCoreUiOps *ops;
80
	PurpleCore *core;
81
82
	g_return_val_if_fail(ui != NULL, FALSE);
83
	g_return_val_if_fail(purple_get_core() == NULL, FALSE);
84
85
#ifdef ENABLE_NLS
86
	bindtextdomain(PACKAGE, LOCALEDIR);
87
#endif
88
#ifdef _WIN32
89
	wpurple_init();
90
#endif
91
1.1.10 by Pedro Fragoso
Import upstream version 2.4.2
92
	g_type_init();
93
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
94
	_core = core = g_new0(PurpleCore, 1);
95
	core->ui = g_strdup(ui);
96
	core->reserved = NULL;
97
98
	ops = purple_core_get_ui_ops();
99
100
	/* The signals subsystem is important and should be first. */
101
	purple_signals_init();
102
1.1.7 by Pedro Fragoso
Import upstream version 2.3.1
103
	purple_util_init();
104
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
105
	purple_signal_register(core, "uri-handler",
106
		purple_marshal_BOOLEAN__POINTER_POINTER_POINTER,
107
		purple_value_new(PURPLE_TYPE_BOOLEAN), 3,
108
		purple_value_new(PURPLE_TYPE_STRING), /* Protocol */
109
		purple_value_new(PURPLE_TYPE_STRING), /* Command */
110
		purple_value_new(PURPLE_TYPE_BOXED, "GHashTable *")); /* Parameters */
111
112
	purple_signal_register(core, "quitting", purple_marshal_VOID, NULL, 0);
113
114
	/* The prefs subsystem needs to be initialized before static protocols
115
	 * for protocol prefs to work. */
116
	purple_prefs_init();
117
118
	purple_debug_init();
119
120
	if (ops != NULL)
121
	{
122
		if (ops->ui_prefs_init != NULL)
123
			ops->ui_prefs_init();
124
125
		if (ops->debug_ui_init != NULL)
126
			ops->debug_ui_init();
127
	}
128
129
#ifdef HAVE_DBUS
130
	purple_dbus_init();
131
#endif
132
133
	purple_ciphers_init();
1.1.12 by Sebastien Bacher
Import upstream version 2.5.0
134
	purple_cmds_init();
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
135
136
	/* Since plugins get probed so early we should probably initialize their
137
	 * subsystem right away too.
138
	 */
139
	purple_plugins_init();
1.1.12 by Sebastien Bacher
Import upstream version 2.5.0
140
	
141
	/* Initialize all static protocols. */
142
	static_proto_init();
143
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
144
	purple_plugins_probe(G_MODULE_SUFFIX);
145
146
	/* The buddy icon code uses the imgstore, so init it early. */
147
	purple_imgstore_init();
148
1.1.7 by Pedro Fragoso
Import upstream version 2.3.1
149
	/* Accounts use status, buddy icons and connection signals, so
150
	 * initialize these before accounts
151
	 */
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
152
	purple_status_init();
153
	purple_buddy_icons_init();
1.1.7 by Pedro Fragoso
Import upstream version 2.3.1
154
	purple_connections_init();
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
155
156
	purple_accounts_init();
157
	purple_savedstatuses_init();
158
	purple_notify_init();
1.1.4 by Sebastien Bacher
Import upstream version 2.2.0
159
	purple_certificate_init();
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
160
	purple_conversations_init();
161
	purple_blist_init();
162
	purple_log_init();
163
	purple_network_init();
164
	purple_privacy_init();
165
	purple_pounces_init();
166
	purple_proxy_init();
167
	purple_dnsquery_init();
168
	purple_sound_init();
169
	purple_ssl_init();
170
	purple_stun_init();
171
	purple_xfers_init();
172
	purple_idle_init();
1.1.12 by Sebastien Bacher
Import upstream version 2.5.0
173
	purple_smileys_init();
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
174
175
	/*
176
	 * Call this early on to try to auto-detect our IP address and
177
	 * hopefully save some time later.
178
	 */
1.1.4 by Sebastien Bacher
Import upstream version 2.2.0
179
	purple_network_get_my_ip(-1);
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
180
181
	if (ops != NULL && ops->ui_init != NULL)
182
		ops->ui_init();
183
184
	return TRUE;
185
}
186
187
void
188
purple_core_quit(void)
189
{
190
	PurpleCoreUiOps *ops;
191
	PurpleCore *core = purple_get_core();
192
193
	g_return_if_fail(core != NULL);
194
195
	/* The self destruct sequence has been initiated */
196
	purple_signal_emit(purple_get_core(), "quitting");
197
198
	/* Transmission ends */
199
	purple_connections_disconnect_all();
200
201
	/* Save .xml files, remove signals, etc. */
1.1.12 by Sebastien Bacher
Import upstream version 2.5.0
202
	purple_smileys_uninit();
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
203
	purple_idle_uninit();
204
	purple_ssl_uninit();
205
	purple_pounces_uninit();
206
	purple_blist_uninit();
207
	purple_ciphers_uninit();
208
	purple_notify_uninit();
209
	purple_conversations_uninit();
210
	purple_connections_uninit();
1.1.4 by Sebastien Bacher
Import upstream version 2.2.0
211
	purple_certificate_uninit();
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
212
	purple_buddy_icons_uninit();
213
	purple_accounts_uninit();
214
	purple_savedstatuses_uninit();
215
	purple_status_uninit();
216
	purple_prefs_uninit();
1.1.12 by Sebastien Bacher
Import upstream version 2.5.0
217
	purple_sound_uninit();
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
218
	purple_xfers_uninit();
219
	purple_proxy_uninit();
220
	purple_dnsquery_uninit();
221
	purple_imgstore_uninit();
222
223
	purple_debug_info("main", "Unloading all plugins\n");
224
	purple_plugins_destroy_all();
225
226
	ops = purple_core_get_ui_ops();
227
	if (ops != NULL && ops->quit != NULL)
228
		ops->quit();
229
230
	purple_plugins_uninit();
1.1.2 by Sebastien Bacher
Import upstream version 2.1.0
231
#ifdef HAVE_DBUS
232
	purple_dbus_uninit();
233
#endif
1.1.7 by Pedro Fragoso
Import upstream version 2.3.1
234
1.1.12 by Sebastien Bacher
Import upstream version 2.5.0
235
	purple_cmds_uninit();
1.1.7 by Pedro Fragoso
Import upstream version 2.3.1
236
	purple_util_uninit();
237
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
238
	purple_signals_uninit();
239
240
	g_free(core->ui);
241
	g_free(core);
242
243
#ifdef _WIN32
244
	wpurple_cleanup();
245
#endif
246
247
	_core = NULL;
248
}
249
250
gboolean
251
purple_core_quit_cb(gpointer unused)
252
{
253
	purple_core_quit();
254
255
	return FALSE;
256
}
257
258
const char *
259
purple_core_get_version(void)
260
{
261
	return VERSION;
262
}
263
264
const char *
265
purple_core_get_ui(void)
266
{
267
	PurpleCore *core = purple_get_core();
268
269
	g_return_val_if_fail(core != NULL, NULL);
270
271
	return core->ui;
272
}
273
274
PurpleCore *
275
purple_get_core(void)
276
{
277
	return _core;
278
}
279
280
void
281
purple_core_set_ui_ops(PurpleCoreUiOps *ops)
282
{
283
	_ops = ops;
284
}
285
286
PurpleCoreUiOps *
287
purple_core_get_ui_ops(void)
288
{
289
	return _ops;
290
}
291
1.1.2 by Sebastien Bacher
Import upstream version 2.1.0
292
#ifdef HAVE_DBUS
293
static char *purple_dbus_owner_user_dir(void)
294
{
295
	DBusMessage *msg = NULL, *reply = NULL;
296
	DBusConnection *dbus_connection = NULL;
297
	DBusError dbus_error;
298
	char *remote_user_dir = NULL;
299
300
	if ((dbus_connection = purple_dbus_get_connection()) == NULL)
301
		return NULL;
302
303
	if ((msg = dbus_message_new_method_call(DBUS_SERVICE_PURPLE, DBUS_PATH_PURPLE, DBUS_INTERFACE_PURPLE, "PurpleUserDir")) == NULL)
304
		return NULL;
305
306
	dbus_error_init(&dbus_error);
307
	reply = dbus_connection_send_with_reply_and_block(dbus_connection, msg, 5000, &dbus_error);
308
	dbus_message_unref(msg);
309
	dbus_error_free(&dbus_error);
310
311
	if (reply)
312
	{
313
		dbus_error_init(&dbus_error);
314
		dbus_message_get_args(reply, &dbus_error, DBUS_TYPE_STRING, &remote_user_dir, DBUS_TYPE_INVALID);
315
		remote_user_dir = g_strdup(remote_user_dir);
316
		dbus_error_free(&dbus_error);
317
		dbus_message_unref(reply);
318
	}
319
320
	return remote_user_dir;
321
}
322
323
#endif /* HAVE_DBUS */
324
325
gboolean
326
purple_core_ensure_single_instance()
327
{
328
	gboolean is_single_instance = TRUE;
329
#ifdef HAVE_DBUS
330
	/* in the future, other mechanisms might have already set this to FALSE */
331
	if (is_single_instance)
332
	{
333
		if (!purple_dbus_is_owner())
334
		{
335
			const char *user_dir = purple_user_dir();
336
			char *dbus_owner_user_dir = purple_dbus_owner_user_dir();
337
338
			if (NULL == user_dir && NULL != dbus_owner_user_dir)
339
				is_single_instance = TRUE;
340
			else if (NULL != user_dir && NULL == dbus_owner_user_dir)
341
				is_single_instance = TRUE;
342
			else if (NULL == user_dir && NULL == dbus_owner_user_dir)
343
				is_single_instance = FALSE;
344
			else
345
				is_single_instance = strcmp(dbus_owner_user_dir, user_dir);
346
347
			g_free(dbus_owner_user_dir);
348
		}
349
	}
350
#endif /* HAVE_DBUS */
351
352
	return is_single_instance;
353
}
354
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
355
static gboolean
356
move_and_symlink_dir(const char *path, const char *basename, const char *old_base, const char *new_base, const char *relative)
357
{
358
	char *new_name = g_build_filename(new_base, basename, NULL);
359
#ifndef _WIN32
360
	char *old_name;
361
#endif
362
	if (g_rename(path, new_name))
363
	{
364
		purple_debug_error("core", "Error renaming %s to %s: %s. Please report this at http://developer.pidgin.im\n",
1.1.7 by Pedro Fragoso
Import upstream version 2.3.1
365
		                   path, new_name, g_strerror(errno));
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
366
		g_free(new_name);
367
		return FALSE;
368
	}
369
	g_free(new_name);
370
371
#ifndef _WIN32
372
	/* NOTE: This new_name is relative. */
373
	new_name = g_build_filename(relative, basename, NULL);
374
	old_name = g_build_filename(old_base, basename, NULL);
375
	if (symlink(new_name, old_name))
376
	{
377
		purple_debug_warning("core", "Error symlinking %s to %s: %s. Please report this at http://developer.pidgin.im\n",
1.1.7 by Pedro Fragoso
Import upstream version 2.3.1
378
		                     old_name, new_name, g_strerror(errno));
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
379
	}
380
	g_free(old_name);
381
	g_free(new_name);
382
#endif
383
384
	return TRUE;
385
}
386
387
gboolean
388
purple_core_migrate(void)
389
{
390
	const char *user_dir = purple_user_dir();
391
	char *old_user_dir = g_strconcat(purple_home_dir(),
392
	                                 G_DIR_SEPARATOR_S ".gaim", NULL);
393
	char *status_file;
394
	FILE *fp;
395
	GDir *dir;
396
	GError *err;
397
	const char *entry;
398
#ifndef _WIN32
399
	char *logs_dir;
400
#endif
401
	char *old_icons_dir;
402
403
	if (!g_file_test(old_user_dir, G_FILE_TEST_EXISTS))
404
	{
405
		/* ~/.gaim doesn't exist, so there's nothing to migrate. */
406
		g_free(old_user_dir);
407
		return TRUE;
408
	}
409
410
	status_file = g_strconcat(user_dir, G_DIR_SEPARATOR_S "migrating", NULL);
411
412
	if (g_file_test(user_dir, G_FILE_TEST_EXISTS))
413
	{
414
		/* If we're here, we have both ~/.gaim and .purple. */
415
416
		if (!g_file_test(status_file, G_FILE_TEST_EXISTS))
417
		{
418
			/* There's no "migrating" status file,
419
			 * so ~/.purple is all up to date. */
420
			g_free(status_file);
421
			g_free(old_user_dir);
422
			return TRUE;
423
		}
424
	}
425
426
	/* If we're here, it's time to migrate from ~/.gaim to ~/.purple. */
427
428
        /* Ensure the user directory exists */
429
	if (!g_file_test(user_dir, G_FILE_TEST_IS_DIR))
430
	{
431
		if (g_mkdir(user_dir, S_IRUSR | S_IWUSR | S_IXUSR) == -1)
432
		{
433
			purple_debug_error("core", "Error creating directory %s: %s. Please report this at http://developer.pidgin.im\n",
1.1.7 by Pedro Fragoso
Import upstream version 2.3.1
434
			                   user_dir, g_strerror(errno));
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
435
			g_free(status_file);
436
			g_free(old_user_dir);
437
			return FALSE;
438
		}
439
	}
440
441
	/* This writes ~/.purple/migrating, which allows us to detect
442
	 * incomplete migrations and properly retry. */
443
	if (!(fp = g_fopen(status_file, "w")))
444
	{
445
		purple_debug_error("core", "Error opening file %s for writing: %s. Please report this at http://developer.pidgin.im\n",
1.1.7 by Pedro Fragoso
Import upstream version 2.3.1
446
		                   status_file, g_strerror(errno));
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
447
		g_free(status_file);
448
		g_free(old_user_dir);
449
		return FALSE;
450
	}
451
	fclose(fp);
452
453
	/* Open ~/.gaim so we can loop over its contents. */
454
	err = NULL;
455
	if (!(dir = g_dir_open(old_user_dir, 0, &err)))
456
	{
457
		purple_debug_error("core", "Error opening directory %s: %s. Please report this at http://developer.pidgin.im\n",
458
		                   status_file,
459
		                   (err ? err->message : "Unknown error"));
460
		if (err)
461
			g_error_free(err);
462
		g_free(status_file);
463
		g_free(old_user_dir);
464
		return FALSE;
465
	}
466
467
	/* Loop over the contents of ~/.gaim */
468
	while ((entry = g_dir_read_name(dir)))
469
	{
470
		char *name = g_build_filename(old_user_dir, entry, NULL);
471
472
#ifndef _WIN32
473
		/* Deal with symlinks... */
474
		if (g_file_test(name, G_FILE_TEST_IS_SYMLINK))
475
		{
476
			/* We're only going to duplicate a logs symlink. */
477
			if (!strcmp(entry, "logs"))
478
			{
1.1.2 by Sebastien Bacher
Import upstream version 2.1.0
479
				char *link;
480
#if GLIB_CHECK_VERSION(2,4,0)
481
				GError *err = NULL;
482
483
				if ((link = g_file_read_link(name, &err)) == NULL)
484
				{
485
					char *name_utf8 = g_filename_to_utf8(name, -1, NULL, NULL, NULL);
486
					purple_debug_error("core", "Error reading symlink %s: %s. Please report this at http://developer.pidgin.im\n",
487
					                   name_utf8 ? name_utf8 : name, err->message);
488
					g_free(name_utf8);
489
					g_error_free(err);
490
					g_free(name);
491
					g_dir_close(dir);
492
					g_free(status_file);
493
					g_free(old_user_dir);
494
					return FALSE;
495
				}
496
#else
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
497
				char buf[MAXPATHLEN];
498
				size_t linklen;
499
500
				if ((linklen = readlink(name, buf, sizeof(buf) - 1) == -1))
501
				{
1.1.2 by Sebastien Bacher
Import upstream version 2.1.0
502
					char *name_utf8 = g_filename_to_utf8(name, -1, NULL, NULL, NULL);
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
503
					purple_debug_error("core", "Error reading symlink %s: %s. Please report this at http://developer.pidgin.im\n",
1.1.7 by Pedro Fragoso
Import upstream version 2.3.1
504
					                   name_utf8, g_strerror(errno));
1.1.2 by Sebastien Bacher
Import upstream version 2.1.0
505
					g_free(name_utf8);
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
506
					g_free(name);
507
					g_dir_close(dir);
508
					g_free(status_file);
509
					g_free(old_user_dir);
510
					return FALSE;
511
				}
512
				buf[linklen] = '\0';
513
1.1.2 by Sebastien Bacher
Import upstream version 2.1.0
514
				/* This way we don't have to GLIB_VERSION_CHECK every g_free(link) below. */
515
				link = g_strdup(buf);
516
#endif
517
518
				logs_dir = g_build_filename(user_dir, "logs", NULL);
519
520
				if (!strcmp(link, "../.purple/logs") || !strcmp(link, logs_dir))
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
521
				{
522
					/* If the symlink points to the new directory, we're
523
					 * likely just trying again after a failed migration,
524
					 * so there's no need to fail here. */
1.1.2 by Sebastien Bacher
Import upstream version 2.1.0
525
					g_free(link);
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
526
					g_free(logs_dir);
527
					continue;
528
				}
529
530
				/* In case we are trying again after a failed migration, we need
531
				 * to unlink any existing symlink.  If it's a directory, this
532
				 * will fail, and so will the symlink below, which is good
533
				 * because the user should sort things out. */
534
				g_unlink(logs_dir);
535
536
				/* Relative links will most likely still be
1.1.2 by Sebastien Bacher
Import upstream version 2.1.0
537
				 * valid from ~/.purple, though it's not
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
538
				 * guaranteed.  Oh well. */
1.1.2 by Sebastien Bacher
Import upstream version 2.1.0
539
				if (symlink(link, logs_dir))
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
540
				{
541
					purple_debug_error("core", "Error symlinking %s to %s: %s. Please report this at http://developer.pidgin.im\n",
1.1.7 by Pedro Fragoso
Import upstream version 2.3.1
542
					                   logs_dir, link, g_strerror(errno));
1.1.2 by Sebastien Bacher
Import upstream version 2.1.0
543
					g_free(link);
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
544
					g_free(name);
545
					g_free(logs_dir);
546
					g_dir_close(dir);
547
					g_free(status_file);
548
					g_free(old_user_dir);
549
					return FALSE;
550
				}
551
1.1.2 by Sebastien Bacher
Import upstream version 2.1.0
552
				g_free(link);
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
553
				g_free(logs_dir);
554
				continue;
555
			}
556
557
			/* Ignore all other symlinks. */
558
			continue;
559
		}
560
#endif
561
562
		/* Deal with directories... */
563
		if (g_file_test(name, G_FILE_TEST_IS_DIR))
564
		{
565
			if (!strcmp(entry, "icons"))
566
			{
567
				/* This is a special case for the Album plugin, which
568
				 * stores data in the icons folder.  We're not copying
569
				 * the icons directory over because previous bugs
570
				 * meant that it filled up with junk for many users.
571
				 * This is a great time to purge it. */
572
573
				GDir *icons_dir;
574
				char *new_icons_dir;
575
				const char *icons_entry;
576
577
				err = NULL;
578
				if (!(icons_dir = g_dir_open(name, 0, &err)))
579
				{
580
					purple_debug_error("core", "Error opening directory %s: %s. Please report this at http://developer.pidgin.im\n",
581
					                   name,
582
					                   (err ? err->message : "Unknown error"));
583
					if (err)
584
						g_error_free(err);
585
					g_free(name);
586
					g_dir_close(dir);
587
					g_free(status_file);
588
					g_free(old_user_dir);
589
					return FALSE;
590
				}
591
592
				new_icons_dir = g_build_filename(user_dir, "icons", NULL);
593
			        /* Ensure the new icon directory exists */
594
				if (!g_file_test(new_icons_dir, G_FILE_TEST_IS_DIR))
595
				{
596
					if (g_mkdir(new_icons_dir, S_IRUSR | S_IWUSR | S_IXUSR) == -1)
597
					{
598
						purple_debug_error("core", "Error creating directory %s: %s. Please report this at http://developer.pidgin.im\n",
1.1.7 by Pedro Fragoso
Import upstream version 2.3.1
599
						                   new_icons_dir, g_strerror(errno));
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
600
						g_free(new_icons_dir);
601
						g_dir_close(icons_dir);
602
						g_free(name);
603
						g_dir_close(dir);
604
						g_free(status_file);
605
						g_free(old_user_dir);
606
						return FALSE;
607
					}
608
				}
609
610
				while ((icons_entry = g_dir_read_name(icons_dir)))
611
				{
612
					char *icons_name = g_build_filename(name, icons_entry, NULL);
613
614
					if (g_file_test(icons_name, G_FILE_TEST_IS_DIR))
615
					{
616
						if (!move_and_symlink_dir(icons_name, icons_entry,
617
						                          name, new_icons_dir, "../../.purple/icons"))
618
						{
619
							g_free(icons_name);
620
							g_free(new_icons_dir);
621
							g_dir_close(icons_dir);
622
							g_free(name);
623
							g_dir_close(dir);
624
							g_free(status_file);
625
							g_free(old_user_dir);
626
							return FALSE;
627
						}
628
					}
629
					g_free(icons_name);
630
				}
631
632
				g_dir_close(icons_dir);
633
			}
634
			else if (!strcmp(entry, "plugins"))
635
			{
636
				/* Do nothing, because we broke plugin compatibility.
637
				 * This means that the plugins directory gets left behind. */
638
			}
639
			else
640
			{
641
				/* All other directories are moved and symlinked. */
642
				if (!move_and_symlink_dir(name, entry, old_user_dir, user_dir, "../.purple"))
643
				{
644
					g_free(name);
645
					g_dir_close(dir);
646
					g_free(status_file);
647
					g_free(old_user_dir);
648
					return FALSE;
649
				}
650
			}
651
		}
652
		else if (g_file_test(name, G_FILE_TEST_IS_REGULAR))
653
		{
654
			/* Regular files are copied. */
655
656
			char *new_name;
657
			FILE *new_file;
658
659
			if (!(fp = g_fopen(name, "rb")))
660
			{
661
				purple_debug_error("core", "Error opening file %s for reading: %s. Please report this at http://developer.pidgin.im\n",
1.1.7 by Pedro Fragoso
Import upstream version 2.3.1
662
				                   name, g_strerror(errno));
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
663
				g_free(name);
664
				g_dir_close(dir);
665
				g_free(status_file);
666
				g_free(old_user_dir);
667
				return FALSE;
668
			}
669
670
			new_name = g_build_filename(user_dir, entry, NULL);
671
			if (!(new_file = g_fopen(new_name, "wb")))
672
			{
673
				purple_debug_error("core", "Error opening file %s for writing: %s. Please report this at http://developer.pidgin.im\n",
1.1.7 by Pedro Fragoso
Import upstream version 2.3.1
674
				                   new_name, g_strerror(errno));
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
675
				fclose(fp);
676
				g_free(new_name);
677
				g_free(name);
678
				g_dir_close(dir);
679
				g_free(status_file);
680
				g_free(old_user_dir);
681
				return FALSE;
682
			}
683
684
			while (!feof(fp))
685
			{
686
				unsigned char buf[256];
687
				size_t size;
688
689
				size = fread(buf, 1, sizeof(buf), fp);
690
				if (size != sizeof(buf) && !feof(fp))
691
				{
692
					purple_debug_error("core", "Error reading %s: %s. Please report this at http://developer.pidgin.im\n",
1.1.7 by Pedro Fragoso
Import upstream version 2.3.1
693
					                   name, g_strerror(errno));
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
694
					fclose(new_file);
695
					fclose(fp);
696
					g_free(new_name);
697
					g_free(name);
698
					g_dir_close(dir);
699
					g_free(status_file);
700
					g_free(old_user_dir);
701
					return FALSE;
702
				}
703
704
				if (!fwrite(buf, size, 1, new_file) && ferror(new_file) != 0)
705
				{
706
					purple_debug_error("core", "Error writing %s: %s. Please report this at http://developer.pidgin.im\n",
1.1.7 by Pedro Fragoso
Import upstream version 2.3.1
707
					                   new_name, g_strerror(errno));
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
708
					fclose(new_file);
709
					fclose(fp);
710
					g_free(new_name);
711
					g_free(name);
712
					g_dir_close(dir);
713
					g_free(status_file);
714
					g_free(old_user_dir);
715
					return FALSE;
716
				}
717
			}
718
719
			if (fclose(new_file))
720
			{
721
				purple_debug_error("core", "Error writing: %s: %s. Please report this at http://developer.pidgin.im\n",
1.1.7 by Pedro Fragoso
Import upstream version 2.3.1
722
				                   new_name, g_strerror(errno));
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
723
			}
724
			if (fclose(fp))
725
			{
726
				purple_debug_warning("core", "Error closing %s: %s\n",
1.1.7 by Pedro Fragoso
Import upstream version 2.3.1
727
				                     name, g_strerror(errno));
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
728
			}
729
			g_free(new_name);
730
		}
731
		else
732
			purple_debug_warning("core", "Not a regular file or directory: %s\n", name);
733
734
		g_free(name);
735
	}
736
737
	/* The migration was successful, so delete the status file. */
738
	if (g_unlink(status_file))
739
	{
740
		purple_debug_error("core", "Error unlinking file %s: %s. Please report this at http://developer.pidgin.im\n",
1.1.7 by Pedro Fragoso
Import upstream version 2.3.1
741
		                   status_file, g_strerror(errno));
1 by Ari Pollak
Import upstream version 2.0.0+dfsg.1
742
		g_free(status_file);
743
		return FALSE;
744
	}
745
746
	old_icons_dir = g_build_filename(old_user_dir, "icons", NULL);
747
	_purple_buddy_icon_set_old_icons_dir(old_icons_dir);
748
	g_free(old_icons_dir);
749
750
	g_free(old_user_dir);
751
752
	g_free(status_file);
753
	return TRUE;
754
}
1.1.2 by Sebastien Bacher
Import upstream version 2.1.0
755
756
GHashTable* purple_core_get_ui_info() {
757
	PurpleCoreUiOps *ops = purple_core_get_ui_ops();
758
759
	if(NULL == ops || NULL == ops->get_ui_info)
760
		return NULL;
761
762
	return ops->get_ui_info();
763
}