comparison libpurple/core.c @ 16341:786edf5e2144

The core portion of the migration code. This is implemented as I described in my e-mail a while back. If we have ~/.gaim and not ~/.purple (or we have ~/.purple, but the "migrating" file exists, indicating an incomplete previous migration), we copy all the files from ~/.gaim to ~/.purple and move and symlink directories, except for plugins and icons. For icons, we move and symlink any subdirectories, as a special case for the Album plugin (written by me). For this to work, it needs to be called by the UIs. Also, the icon migration code is not included in this commit, so I highly recommend you not test this on your main .gaim directory if you want a clean migration.
author Richard Laager <rlaager@wiktel.com>
date Mon, 23 Apr 2007 16:57:12 +0000
parents fa8aeab4ca5a
children 391a79778f89
comparison
equal deleted inserted replaced
16326:08557e94840c 16341:786edf5e2144
42 #include "signals.h" 42 #include "signals.h"
43 #include "sound.h" 43 #include "sound.h"
44 #include "sslconn.h" 44 #include "sslconn.h"
45 #include "status.h" 45 #include "status.h"
46 #include "stun.h" 46 #include "stun.h"
47 #include "util.h"
47 48
48 #ifdef HAVE_DBUS 49 #ifdef HAVE_DBUS
49 # include "dbus-server.h" 50 # include "dbus-server.h"
50 #endif 51 #endif
51 52
266 PurpleCoreUiOps * 267 PurpleCoreUiOps *
267 purple_core_get_ui_ops(void) 268 purple_core_get_ui_ops(void)
268 { 269 {
269 return _ops; 270 return _ops;
270 } 271 }
272
273 static gboolean
274 move_and_symlink_dir(const char *path, const char *basename, const char *old_base, const char *new_base, const char *relative)
275 {
276 char *new_name = g_build_filename(new_base, basename, NULL);
277 #ifndef _WIN32
278 char *old_name;
279 #endif
280 if (g_rename(path, new_name))
281 {
282 purple_debug_error("core", "Error renaming %s to %s: %s\n",
283 path, new_name, strerror(errno));
284 g_free(new_name);
285 return FALSE;
286 }
287 g_free(new_name);
288
289 #ifndef _WIN32
290 /* NOTE: This new_name is relative. */
291 new_name = g_build_filename(relative, basename, NULL);
292 old_name = g_build_filename(old_base, basename, NULL);
293 if (symlink(new_name, old_name))
294 {
295 purple_debug_warning("core", "Error symlinking %s to %s: %s\n",
296 old_name, new_name, strerror(errno));
297 }
298 g_free(old_name);
299 g_free(new_name);
300 #endif
301
302 return TRUE;
303 }
304
305 gboolean
306 purple_core_migrate(void)
307 {
308 const char *user_dir = purple_user_dir();
309 char *old_user_dir = g_strconcat(purple_home_dir(),
310 G_DIR_SEPARATOR_S ".gaim", NULL);
311 char *status_file;
312 FILE *fp;
313 GDir *dir;
314 GError *err;
315 const char *entry;
316 #ifndef _WIN32
317 char *logs_dir;
318 #endif
319 char *old_icons_dir;
320
321 if (!g_file_test(old_user_dir, G_FILE_TEST_EXISTS))
322 {
323 /* ~/.gaim doesn't exist, so there's nothing to migrate. */
324 g_free(old_user_dir);
325 return TRUE;
326 }
327
328 status_file = g_strconcat(user_dir, G_DIR_SEPARATOR_S "migrating", NULL);
329
330 if (g_file_test(user_dir, G_FILE_TEST_EXISTS))
331 {
332 /* If we're here, we have both ~/.gaim and .purple. */
333
334 if (!g_file_test(status_file, G_FILE_TEST_EXISTS))
335 {
336 /* There's no "migrating" status file,
337 * so ~/.purple is all up to date. */
338 g_free(status_file);
339 g_free(old_user_dir);
340 return TRUE;
341 }
342 }
343
344 /* If we're here, it's time to migrate from ~/.gaim to ~/.purple. */
345
346 /* Ensure the user directory exists */
347 if (!g_file_test(user_dir, G_FILE_TEST_IS_DIR))
348 {
349 if (g_mkdir(user_dir, S_IRUSR | S_IWUSR | S_IXUSR) == -1)
350 {
351 purple_debug_error("core", "Error creating directory %s: %s\n",
352 user_dir, strerror(errno));
353 g_free(status_file);
354 g_free(old_user_dir);
355 return FALSE;
356 }
357 }
358
359 /* This writes ~/.purple/migrating, which allows us to detect
360 * incomplete migrations and properly retry. */
361 if (!(fp = g_fopen(status_file, "w")))
362 {
363 purple_debug_error("core", "Error opening file %s for writing: %s\n",
364 status_file, strerror(errno));
365 g_free(status_file);
366 g_free(old_user_dir);
367 return FALSE;
368 }
369 fclose(fp);
370
371 /* Open ~/.gaim so we can loop over its contents. */
372 err = NULL;
373 if (!(dir = g_dir_open(old_user_dir, 0, &err)))
374 {
375 purple_debug_error("core", "Error opening directory %s: %s\n",
376 status_file,
377 (err ? err->message : "Unknown error"));
378 if (err)
379 g_error_free(err);
380 g_free(status_file);
381 g_free(old_user_dir);
382 return FALSE;
383 }
384
385 /* Loop over the contents of ~/.gaim */
386 while ((entry = g_dir_read_name(dir)))
387 {
388 char *name = g_build_filename(old_user_dir, entry, NULL);
389
390 #ifndef _WIN32
391 /* Deal with symlinks... */
392 if (g_file_test(name, G_FILE_TEST_IS_SYMLINK))
393 {
394 /* We're only going to duplicate a logs symlink. */
395 if (!strcmp(entry, "logs"))
396 {
397 char buf[MAXPATHLEN];
398
399 if (readlink(name, buf, sizeof(buf) - 1) == -1)
400 {
401 purple_debug_error("core", "Error reading symlink %s: %s\n",
402 name, strerror(errno));
403 g_free(name);
404 g_dir_close(dir);
405 g_free(status_file);
406 g_free(old_user_dir);
407 return FALSE;
408 }
409 buf[sizeof(buf) - 1] = '\0';
410
411 logs_dir = g_strconcat(user_dir, G_DIR_SEPARATOR_S "logs", NULL);
412
413 if (!strcmp(buf, "../.purple/logs") || !strcmp(buf, logs_dir))
414 {
415 /* If the symlink points to the new directory, we're
416 * likely just trying again after a failed migration,
417 * so there's no need to fail here. */
418 g_free(logs_dir);
419 continue;
420 }
421
422 /* In case we are trying again after a failed migration, we need
423 * to unlink any existing symlink. If it's a directory, this
424 * will fail, and so will the symlink below, which is good
425 * because the user should sort things out. */
426 g_unlink(logs_dir);
427
428 /* Relative links will most likely still be
429 * valid from ~/.purple, though not it's not
430 * guaranteed. Oh well. */
431 if (symlink(buf, logs_dir))
432 {
433 purple_debug_error("core", "Error symlinking %s to %s: %s\n",
434 logs_dir, buf, strerror(errno));
435 g_free(name);
436 g_free(logs_dir);
437 g_dir_close(dir);
438 g_free(status_file);
439 g_free(old_user_dir);
440 return FALSE;
441 }
442
443 g_free(logs_dir);
444 continue;
445 }
446
447 /* Ignore all other symlinks. */
448 continue;
449 }
450 #endif
451
452 /* Deal with directories... */
453 if (g_file_test(name, G_FILE_TEST_IS_DIR))
454 {
455 if (!strcmp(entry, "icons"))
456 {
457 /* This is a special case for the Album plugin, which
458 * stores data in the icons folder. We're not copying
459 * the icons directory over because previous bugs
460 * meant that it filled up with junk for many users.
461 * This is a great time to purge it. */
462
463 GDir *icons_dir;
464 char *new_icons_dir;
465 const char *icons_entry;
466
467 err = NULL;
468 if (!(icons_dir = g_dir_open(name, 0, &err)))
469 {
470 purple_debug_error("core", "Error opening directory %s: %s\n",
471 name,
472 (err ? err->message : "Unknown error"));
473 if (err)
474 g_error_free(err);
475 g_free(name);
476 g_dir_close(dir);
477 g_free(status_file);
478 g_free(old_user_dir);
479 return FALSE;
480 }
481
482 new_icons_dir = g_build_filename(user_dir, "icons", NULL);
483 /* Ensure the new icon directory exists */
484 if (!g_file_test(new_icons_dir, G_FILE_TEST_IS_DIR))
485 {
486 if (g_mkdir(new_icons_dir, S_IRUSR | S_IWUSR | S_IXUSR) == -1)
487 {
488 purple_debug_error("core", "Error creating directory %s: %s\n",
489 new_icons_dir, strerror(errno));
490 g_free(new_icons_dir);
491 g_dir_close(icons_dir);
492 g_free(name);
493 g_dir_close(dir);
494 g_free(status_file);
495 g_free(old_user_dir);
496 return FALSE;
497 }
498 }
499
500 while ((icons_entry = g_dir_read_name(icons_dir)))
501 {
502 char *icons_name = g_build_filename(name, icons_entry, NULL);
503
504 if (g_file_test(icons_name, G_FILE_TEST_IS_DIR))
505 {
506 if (!move_and_symlink_dir(icons_name, icons_entry,
507 name, new_icons_dir, "../../.purple/icons"))
508 {
509 g_free(icons_name);
510 g_free(new_icons_dir);
511 g_dir_close(icons_dir);
512 g_free(name);
513 g_dir_close(dir);
514 g_free(status_file);
515 g_free(old_user_dir);
516 return FALSE;
517 }
518 }
519 g_free(icons_name);
520 }
521
522 g_dir_close(icons_dir);
523 }
524 else if (!strcmp(entry, "plugins"))
525 {
526 /* Do nothing, because we broke plugin compatibility.
527 * This means that the plugins directory gets left behind. */
528 }
529 else
530 {
531 /* All other directories are moved and symlinked. */
532 if (!move_and_symlink_dir(name, entry, old_user_dir, user_dir, "../.purple"))
533 {
534 g_free(name);
535 g_dir_close(dir);
536 g_free(status_file);
537 g_free(old_user_dir);
538 return FALSE;
539 }
540 }
541 }
542 else if (g_file_test(name, G_FILE_TEST_IS_REGULAR))
543 {
544 /* Regular files are copied. */
545
546 char *new_name;
547 FILE *new_file;
548
549 if (!(fp = g_fopen(name, "rb")))
550 {
551 purple_debug_error("core", "Error opening file %s for reading: %s\n",
552 name, strerror(errno));
553 g_free(name);
554 g_dir_close(dir);
555 g_free(status_file);
556 g_free(old_user_dir);
557 return FALSE;
558 }
559
560 new_name = g_build_filename(user_dir, entry, NULL);
561 if (!(new_file = g_fopen(new_name, "wb")))
562 {
563 purple_debug_error("core", "Error opening file %s for writing: %s\n",
564 new_name, strerror(errno));
565 fclose(fp);
566 g_free(new_name);
567 g_free(name);
568 g_dir_close(dir);
569 g_free(status_file);
570 g_free(old_user_dir);
571 return FALSE;
572 }
573
574 while (!feof(fp))
575 {
576 unsigned char buf[256];
577 size_t size;
578
579 size = fread(buf, 1, sizeof(buf), fp);
580 if (size != sizeof(buf) && !feof(fp))
581 {
582 purple_debug_error("core", "Error reading %s: %s\n",
583 name, strerror(errno));
584 fclose(new_file);
585 fclose(fp);
586 g_free(new_name);
587 g_free(name);
588 g_dir_close(dir);
589 g_free(status_file);
590 g_free(old_user_dir);
591 return FALSE;
592 }
593
594 if (!fwrite(buf, size, 1, new_file))
595 {
596 purple_debug_error("core", "Error writing %s: %s\n",
597 new_name, strerror(errno));
598 fclose(new_file);
599 fclose(fp);
600 g_free(new_name);
601 g_free(name);
602 g_dir_close(dir);
603 g_free(status_file);
604 g_free(old_user_dir);
605 return FALSE;
606 }
607 }
608
609 if (fclose(new_file))
610 {
611 purple_debug_error("core", "Error writing: %s: %s\n",
612 new_name, strerror(errno));
613 }
614 if (fclose(fp))
615 {
616 purple_debug_warning("core", "Error closing %s: %s\n",
617 name, strerror(errno));
618 }
619 g_free(new_name);
620 }
621 else
622 purple_debug_warning("core", "Not a regular file or directory: %s\n", name);
623
624 g_free(name);
625 }
626
627 /* The migration was successful, so delete the status file. */
628 if (g_unlink(status_file))
629 {
630 purple_debug_error("core", "Error unlinking file %s: %s\n",
631 status_file, strerror(errno));
632 g_free(status_file);
633 return FALSE;
634 }
635
636 old_icons_dir = g_build_filename(old_user_dir, "icons", NULL);
637 purple_buddy_icon_set_old_icons_dir(old_icons_dir);
638 g_free(old_icons_dir);
639
640 g_free(old_user_dir);
641
642 g_free(status_file);
643 return TRUE;
644 }