Evolution 数据迁移(更新时)遗漏了除第一个“帐户”(邮件、地址簿、日历......)之外的所有帐户

Evolution 数据迁移(更新时)遗漏了除第一个“帐户”(邮件、地址簿、日历......)之外的所有帐户

由于系统崩溃,我不得不在新电脑上重新安装。幸运的是,我的硬盘没有受到影响——但不幸的是,“老旧的 8.04”不想在新系统上从它启动……

因此,在将 12.04 安装在新硬盘上后,我将旧磁盘上的所有 evolution 文件夹复制到相应位置(~/.evolution 加上 ~/.gconf/apps/evolution——8.04 上的旧 Evo 没有 ~/.local/share/evolution),然后启动该应用程序。迁移助手弹出,我进行了操作——但最终,每个类别中只有一个“帐户”被迁移并可用:本地邮件、4 个 IMAP 帐户中的一个(尽管所有 4 个帐户的数据都移动到了 ~/.local/share/evolution)、4 个地址簿中的一个,等等。

对于 IMAP 来说没什么大不了的,因为我只需要配置它(在这种情况下,数据存储在服务器上)。但我如何才能找回我的通讯录?如上所述,启动“旧安装”来导出这些数据不是一个选项,因为旧系统无法再启动。有没有办法为给定的数据集启动迁移助手(在我的情况下:告诉它只迁移和导入单个指定的通讯录)?

答案1

上面的 PHP 帮助我入门。但我遇到了卡片中的垃圾问题。

我是一名 Python 爱好者,因此我用 Python 编写了一个新程序。我使用 Python 2.x 来运行它。

它使用 Python 字典来跟踪地址卡中已经看到的电子邮件地址,并且只存储一次给定的地址。这解决了我遇到的另一个问题,即重复的卡片记录。

我认为 Evolution 会使用卡片之间的二进制垃圾来判断哪些卡片是有效的。该程序只使用了几条经验法则:如果卡片中有垃圾二进制字符,或者没有正确终止,或者没有电子邮件地址,则该卡片是坏卡片,并会进入“坏”输出文件。

转换完成后,您可以检查“坏”输出文件,看看其中是否有 .vcf 输出文件中没有的内容。就我而言,没有;这个程序为我找到了所有好的卡片。

#!/usr/bin/python

import re
import sys

def bad_chars():
    for n in range(0, 32):
        if n not in (9, 10, 13):
            yield chr(n)
    for n in range(128, 256):
        yield chr(n)

def has_bad(s):
    return any(ch in s for ch in bad_chars())

def get_email(card):
    lst = card.split('\n')
    for line in lst:
        if "EMAIL" in line:
            _, _, email = line.partition(':')
            return email.strip()
    else:
        return ''

if len(sys.argv) != 4:
    print("Usage: cvt.py <input_old_address_book> <output.vcf> <bad.txt>")
    sys.exit(1)


with open(sys.argv[1], "rb") as in_f:
    s = in_f.read()

s_start = "BEGIN:VCARD"
s_end = "END:VCARD"

cards = {}
lst_bad = []

while s:
    i_start = s.find(s_start)
    if i_start == -1:
        break

    i_next = s.find(s_start, i_start + len(s_start))
    if i_next == -1:
        i_next = len(s) - 1

    i_end = s.find(s_end, i_start + len(s_start))
    if i_end == -1:
        i_end = len(s) - 1
    else:
        i_end += len(s_end)

    if i_next < i_end:
        i_end = i_next

    card = s[i_start:i_end+1].strip()
    s = s[i_end:]

    card = card.replace('\r', '')
    card = card.replace('\0', '')
    if not card:
        continue

    key = get_email(card)
    if has_bad(card) or s_end not in card or not key:
        lst_bad.append(card)
        continue

    if key not in cards or len(card) > len(cards[key]):
        cards[key] = card

with open(sys.argv[2], "w") as out_f:
    for key in sorted(cards.keys()):
        out_f.write(cards[key] + "\n\n")

with open(sys.argv[3], "w") as bad_f:
    for s in lst_bad:
        bad_f.write(s + "\n\n")

答案2

深入研究后,我发现了两件事:虽然新的数据库格式是 SQLite3,但旧格式是一些二进制存储的 VCARD,这些 VCARD 清晰易读。所以我想出了一个小的 PHP 代码片段:

#!/usr/bin/php
<?php
# -=[ UserConf ]=-
$evodb = 'addressbook.db'; // (path and) name to the old address database
$name  = 'business';       // name of the address book (used as filename)
$separate_vcards = FALSE;  // if TRUE, each VCARD will go to a separate file

#-=[ Do not touch below lines ]=-
$re_vcard = '!(BEGIN:VCARD.*?END:VCARD)!ims';

$evo = file_get_contents($evodb);
preg_match_all($re_vcard,$evo,$vcards);
$cards = count($vcards[1]);
$new = '';
for ($i=0;$i<$cards;++$i) {
  if ($separate_vcards) {
    file_put_contents("${name}_${i}.vcf",$vcards[1][$i]);
  } else {
    $new .= $vcards[1][$i] . "\n\n";
  }
}
if (!$separate_vcards) file_put_contents("${name}.vcf",$new);
?>

这不是 100% 完美,但比丢失所有数据要好。脚本会遍历旧式数据库,并尝试获取存储在那里的所有 VCARD。这些 VCARD 要么导出到单独的文件(如果 $separate_vcards = TRUE,则每个 VCARD 都放在一个文件中),要么导出到一个集合(否则)。

由于这些应该是纯文本文件,因此可以轻松地检查和更正它们(换行符、二进制垃圾)——最后使用 Evolutions 导入功能(在“文件”菜单中找到)将数据(“单个文件”)导入到现有的地址簿中(如果想要一个新的地址簿,请先创建一个空的地址簿)。

对我来说,这可以很好地恢复大约 90% 的地址。对于剩余的 10%,有些地址是混乱的或损坏的(请记住,即使旧格式也是二进制的 - 并且代码片段只在“ASCII 模式”下有效)。

希望这对其他人有帮助(如果是这样,请不要忘记对该解决方案投赞成票)。

相关内容