![Evolution 数据迁移(更新时)遗漏了除第一个“帐户”(邮件、地址簿、日历......)之外的所有帐户](https://linux22.com/image/819333/Evolution%20%E6%95%B0%E6%8D%AE%E8%BF%81%E7%A7%BB%EF%BC%88%E6%9B%B4%E6%96%B0%E6%97%B6%EF%BC%89%E9%81%97%E6%BC%8F%E4%BA%86%E9%99%A4%E7%AC%AC%E4%B8%80%E4%B8%AA%E2%80%9C%E5%B8%90%E6%88%B7%E2%80%9D%EF%BC%88%E9%82%AE%E4%BB%B6%E3%80%81%E5%9C%B0%E5%9D%80%E7%B0%BF%E3%80%81%E6%97%A5%E5%8E%86......%EF%BC%89%E4%B9%8B%E5%A4%96%E7%9A%84%E6%89%80%E6%9C%89%E5%B8%90%E6%88%B7.png)
由于系统崩溃,我不得不在新电脑上重新安装。幸运的是,我的硬盘没有受到影响——但不幸的是,“老旧的 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 模式”下有效)。
希望这对其他人有帮助(如果是这样,请不要忘记对该解决方案投赞成票)。