Mercurial > pidgin.yaz
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 } |