České vysoké učení technické v Praze Fakulta elektrotechnická Katedra počítačů
Diplomová práce
Synchronizační modul pro Mozilla Thunderbird Bc. David Kalivoda
Vedoucí práce: Ing. Tomáš Novotný
Studijní program: Elektrotechnika a informatika, strukturovaný, Navazující magisterský Obor: Výpočetní technika květen 2012
iv
v
Prohlášení Prohlašuji, že jsem práci vypracoval samostatně a použil jsem pouze podklady uvedené v přiloženém seznamu. Nemám závažný důvod proti užití tohoto školního díla ve smyslu §60 Zákona č. 121/2000 Sb., o právu autorském, o právech souvisejících s právem autorským a o změně některých zákonů (autorský zákon).
Ve Starém Hradišti dne 1. 5. 2012
.............................................................
vi
Abstract Content of this thesis is designing and implementing information synchronization module for Mozilla Thunderbird over remote storage as IMAP server. Besides common branches of software engineering, thesis deals with universal principles of synchronization and versioning. There is ExtBrain Sync Protocol which takes control of the synchronization process described in this thesis.
Abstrakt Náplní této práce je návrh a implementace synchronizačního modulu informací pro Mozilla Thunderbird přes vzdálené úložiště v podobě IMAP serveru. Práce se kromě obvyklých disciplín softwarového inženýrství zabývá obecnými principy synchronizace a verzováním informací. V práci je popsán obecný ExtBrain Sync Protokol, podle kterého synchronizace probíhá.
vii
viii
Obsah 1 Úvod 1.1 Motivace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2 Struktura práce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1 1 1
2 Popis problému, specifikace 2.1 Popis problému . . . . . . 2.2 Specifikace cíle . . . . . . 2.3 Zadání projektu . . . . . . 2.4 Rešerše existujících řešení
. . . .
3 3 3 3 4
cíle . . . . . . . . . . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
3 Analýza a návrh implementace 3.1 Analýza . . . . . . . . . . . . . . . . . . . . . . . 3.1.1 Specifikace požadavků . . . . . . . . . . . 3.1.2 Princip synchronizace . . . . . . . . . . . 3.1.3 Vzdálené úložiště - IMAP protokol . . . . 3.1.4 GUI Thunderbirdu s ohledem na kontakty 3.1.5 Mozilla Application Framework . . . . . . 3.2 Návrh implementace . . . . . . . . . . . . . . . . 3.2.1 Struktura aplikace . . . . . . . . . . . . . 3.2.2 Řešení kolizí . . . . . . . . . . . . . . . . 3.2.3 Formát přenášených objektů . . . . . . . 3.2.4 Optimalita datového přenosu . . . . . . . 3.2.5 Rozšíření atributů kontaktů . . . . . . . . 3.2.6 Použité nástroje . . . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
9 9 9 9 12 15 15 16 16 18 18 18 19 20
4 ExtBrain Sync Protokol 4.1 Definice pojmů . . . . . . . . . . . . . . 4.2 Označení položky . . . . . . . . . . . . . 4.3 Lokální a databázové úložiště . . . . . . 4.4 Vzdálené úložiště . . . . . . . . . . . . . 4.5 Fáze synchronizace . . . . . . . . . . . . 4.6 Fáze import . . . . . . . . . . . . . . . . 4.6.1 Získání UIDNEXT a EXISTS . . 4.6.2 Načtení hlaviček IMAP zpráv . . 4.6.3 Načtení těl a příloh nových zpráv
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
23 23 23 24 24 24 25 26 27 28
ix
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
x
OBSAH
4.7
4.8
4.9
Synchronizační jádro . . . . . . . . . . . . 4.7.1 Vytvoření synchronizační tabulky . 4.7.2 Procházení synchronizační tabulky Fáze export . . . . . . . . . . . . . . . . . 4.8.1 Krok zápisu . . . . . . . . . . . . . 4.8.2 Krok přesouvání . . . . . . . . . . Paralelní přístup . . . . . . . . . . . . . . 4.9.1 Zamykání vzdáleného úložiště . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
5 Realizace 5.1 Obecný synchronizační modul . . . . . . . . . . . . . . . . 5.1.1 Průběh volání v jednotlivých fázích synchronizace 5.2 Databázové úložiště . . . . . . . . . . . . . . . . . . . . . 5.3 Obsluha IMAP úložiště . . . . . . . . . . . . . . . . . . . 5.3.1 Posloupnost IMAP příkazů . . . . . . . . . . . . . 5.3.2 Struktura obsluhy volání IMAP příkazu . . . . . . 5.3.3 Zamykání vzdáleného úložiště . . . . . . . . . . . . 5.3.4 IMAP knihovna . . . . . . . . . . . . . . . . . . . 5.4 Synchronizační modul kontaktů . . . . . . . . . . . . . . . 5.4.1 Implementace . . . . . . . . . . . . . . . . . . . . . 5.4.2 Definice formátu obsahu přenášených zpráv . . . . 5.4.3 Serializace a deserializace kontaktů . . . . . . . . . 5.4.4 Chyba v jádru Thunderbirdu . . . . . . . . . . . . 5.4.5 Řešení kolizních situací . . . . . . . . . . . . . . . 5.5 Grafické uživatelské rozhraní . . . . . . . . . . . . . . . . 5.5.1 Rozšíření atributů kontaktu . . . . . . . . . . . . . 5.5.2 Wizard na tvorbu účtů . . . . . . . . . . . . . . . . 5.5.3 Obsluha synchronizace . . . . . . . . . . . . . . . . 6 Testování
. . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . .
28 29 31 36 37 38 39 39
. . . . . . . . . . . . . . . . . .
41 41 41 43 44 44 45 46 47 47 47 47 48 48 50 50 50 50 51 53
7 Závěr 55 7.1 Možná další rozšíření . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 Literatura
57
A Seznam použitých zkratek
59
B Obrázky a diagramy
61
C Instalační příručka 67 C.1 Instalace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 C.2 Vytvoření účtu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 C.3 Spuštění synchronizace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 D Obsah přiloženého CD
71
Seznam obrázků 2.1 2.2 2.3
Okno se zobrazením průběhu synchronizace (SyncKolab) . . . . . . . . . . . . Kolizní okno doplňku SyncKolab . . . . . . . . . . . . . . . . . . . . . . . . . Rozšíření položek v kontaktech v doplňku gContactSync . . . . . . . . . . . .
3.1 3.2 3.3
Grafické rozhraní editace kontaktu v Thunderbirdu 11.0.1 . . . . . . . . . . . 15 Návrh třívrstvé architektury . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 Návrh okna pro řešení kolizních situací . . . . . . . . . . . . . . . . . . . . . . 18
4.1 4.2 4.3 4.4 4.5 4.6
Fáze procesu synchronizace . . . . . . . . . . . . . . . Stavový diagram fáze import . . . . . . . . . . . . . . Stavový diagram jádra synchronizace . . . . . . . . . . Poslední podmínka průchodu synchronizační tabulkou Stavový diagram fáze export . . . . . . . . . . . . . . Stavový diagram zamykání vzdáleného úložiště . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
25 26 29 32 36 40
5.1 5.2 5.3 5.4 5.5 5.6
Class diagram synchronizačního modulu . . . . . Sekvenční diagram fáze import . . . . . . . . . . Sekvenční diagram fáze export . . . . . . . . . . Relační databázový model . . . . . . . . . . . . . Okno pro editaci kontaktů s rozšířenými atributy Okno informující o průběhu synchronizace . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
42 43 43 44 51 52
B.1 B.2 B.3 B.4 B.5
Návrh okna pro editaci kontaktů . . . . . . Sekvenční diagram všech fází synchronizace Volání IMAP příkazů v initialSequence . . . Volání IMAP příkazů v importSequence . . Volání IMAP příkazů v exportSequence . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
61 62 63 64 65
. . . . .
. . . . .
. . . . .
4 5 7
C.1 Úvodní okno průvodce pro vytváření účtů . . . . . . . . . . . . . . . . . . . . 68 C.2 Okno se seznamem složek na serveru . . . . . . . . . . . . . . . . . . . . . . . 68 C.3 Spuštění synchronizace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
xi
xii
SEZNAM OBRÁZKŮ
Seznam tabulek 3.1 3.2
Tabulka synchronizace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 Trojcestné slučování - příklad . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4.1 4.2 4.3 4.4 4.5 4.6
Načtení hlaviček IMAP zpráv . . . . . . . . Načtení těl IMAP zpráv . . . . . . . . . . . Synchronizační tabulka - příklad . . . . . . Možné situace v synchronizační tabulce . . Příklad struktury objektu objectsToExport Příklad struktury objektu objectsToDelete .
5.1
JSON atributy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
27 29 30 31 35 35
B.1 Kontakt v JSON podobě . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
xiii
xiv
SEZNAM TABULEK
Kapitola 1
Úvod 1.1
Motivace
Při současném trendu velkého rozvoje chytrých telefonů a dalších jiných mobilních zařízení je kladen velký důraz jednak na zabezpečení takových zařízení, ale také na přenášení souborů, kontaktů, záložek a dalších informací, které je výhodné mít ve stejných verzích na všech zařízeních. Při více než dvou používaných zařízeních pro služby jako volání, psaní e-mailů atd. již nestačí synchronizovat tato zařízení pouze mezi sebou, ale při dodržení obvyklých bezpečnostních pravidel je lepší využívat centrální úložiště, na kterém jsou data uchovávána. Pomocí klientských aplikací na jednotlivých zařízeních jsou na toto centrální úložiště data odesílána, při synchronizačním procesu data z úložiště získávána, při výskytu kolizních situací musí aplikace zajistit její vyřešení. Nejde jen o to přesunovat soubory a informace z jednoho zařízení na druhé, nýbrž je přenášet obousměrně, pokud možno v reálném čase a po každé změně na jedné ze stran. Tento trend se velmi přibližuje modelu využívání IT technologií zvaný jako „cloud computing“. V každém případě může služby „v cloudu“ velmi dobře doplňovat. Pro přiblížení motivace pro tuto práci můžeme vzít běžnou konkrétní situaci: máme mobilní telefon s kontakty a na jiném zařízení e-mailového klienta. V tomto případě je samozřejmě účinné mít na všech zařízeních kontakty sdílené tak, abychom v případě změny např. e-mailu u jednoho kontaktu editaci nemuseli provádět několikrát. Změníme-li kontakt na jednom zařízení, chceme, aby se kontakt v této verzi uložil „někam na server“, odkud si jeho změny na jiném zařízení nahrajeme. Cílem této práce je návrh a implementace aplikace, která bude umožňovat synchronizaci informací v e-mailovém klientu Mozilla Thunderbird přímo v jeho prostředí, bude se jednat o zásuvný modul. Řešení poskytující synchronizaci například kontaktních údajů již samozřejmě existují, žádné z nich ovšem není nijak obecné a většinou nesplňují požadavky na dlouhodobé bezproblémové nasazení.
1.2
Struktura práce
V následující kapitole se zaměřím na konkrétní popis cílů práce a podrobný rozbor existujících řešení. Díky inspiraci a poučení se z nedostatků konkurence v kapitole Analýza a
1
2
KAPITOLA 1. ÚVOD
návrh popíšu jaké konkrétní postupy jsem se rozhodl zvolit. Zaměřím se zde i na obecné principy synchronizace. V kapitole ExtBrain Sync Protokol bude místo pro podrobný popis protokolu, podle kterého se synchronizace řídí. Pomocí stavových diagramů a konkrétního příkladu provázejícího celou tuto kapitolu bude vysvětlen flow dat při synchronizaci. Kapitola Realizace se bude týkat konkrétních tříd výsledné implementace. Kapitola Testování bude věnována popisu testů, které byly na implementovanou aplikaci provedeny. V závěru se pokusím shrnout výsledek projektu s ohledem na zadání diplomové práce, praktickou použitelnost a nastínit možnosti budoucích rozšíření.
Kapitola 2
Popis problému, specifikace cíle 2.1
Popis problému
E-mailový klient Mozilla Thunderbird, tak jak je to u podobných aplikací běžné, obsahuje adresář s kontakty. Vstup kontaktů do adresáře je možný přes ruční zadávání v grafickém uživatelském rozhraní, nebo přes funkci import. Ta ve verzi Thunderbird 11.0 podporuje následující formáty: vCard, LDIF, textové formáty s daty oddělenými tabelátorem nebo čárkou (.csv) a přímý import adresářových souborů z aplikací Outlook, Outlook Express a Eudora. V jádru aplikace chybí možnost synchronizace či uložení na sdílené úložiště. Synchronizací se zabývá několik doplňků Thunderbirdu (jejich seznam a stručná rešerše je uvedena dále v této kapitole). Tyto doplňky synchronizují kontakty z adresáře do složky na lokálním úložišti, několik z nich podporuje synchronizaci se službou Google Contacts a některé umožňují jako vzdálené úložiště zvolit IMAP server. Toto je užitečné například pro korporátní uživatele, kde s ohledem na bezpečnost chceme využít například firemní IMAP server. Užitečnost synchronizace v e-mailovém klientu se však netýká pouze kontaktů, ale i událostí v kalendáři, nebo například poznámek k e-mailovým zprávám.
2.2
Specifikace cíle
Cílem této práce je návrh a implementace zásuvného modulu do Mozilla Thunderbird rozšiřujícího jej o možnosti synchronizace informací pomocí IMAP protokolu. S využitím tohoto modulu bude pak implementováno rozšíření pro synchronizaci kontaktů. Atributy kontaktů v Mozilla Thunderbird budou rozšířeny na formát odpovídající atributům v OS Android. Zásuvný modul bude napsán v jazyce JavaScript obvyklém pro implementaci rozšíření Thunderbirdu.
2.3
Zadání projektu
Diplomová práce byla zadána v rámci projektu ExtBrain [1], který se zaměřuje na vývoj aplikací usnadňujících každodenní práci na PC. Projekt ExtBrain již zahrnuje některé aplikace pro Mozilla Application Framework, například e-mailového klienta a IM komunikátor.
3
4
KAPITOLA 2. POPIS PROBLÉMU, SPECIFIKACE CÍLE
Synchronizační modul bude po dokončení integrován do většího celku sdružujícího všechna ExtBrain rozšíření pro Mozilla Thunderbird.
2.4
Rešerše existujících řešení
V následujících odstavcích jsou shrnuty výsledky zkoušení několika zásuvných modulů do Thunderbirdu včetně heslovitého zhodnocení kladných a záporných vlastností, které tyto moduly vykazují.
2.4.1
SyncKolab
Obrázek 2.1: Okno se zobrazením průběhu synchronizace (SyncKolab)
Doplněk Thunderbirdu přináší synchronizaci kontaktů přes IMAP server. Ke každé složce kontaktů, kterou chce uživatel synchronizovat, musí v rozšíření vytvořit samostatný účet. Verze kontaktů jsou ukládány lokálně ve formátu xml nebo vcard v adresáři profilu, tyto soubory jsou při synchronizaci přílohou zprávy na serveru, stejně jako fotky, pokud jsou ke kontaktu přiřazeny. Při smazání fotky v jiném profilu nedojde ke smazání souboru s fotkou v aktuálním profilu. Fotky jsou uloženy ve složce profilu ve Photos. Autor píše, že na verzi Thunderbird 3 by měl uživatel používat noční sestavení, ani s ním však v době psaní
2.4. REŠERŠE EXISTUJÍCÍCH ŘEŠENÍ
5
této práce nefunguje GUI umožňující výběr verze při kolizi. Dialog upozorňující na kolizi se uživateli zobrazí (viz obr. 2.2), tlačítka označující, zda uživatel chce zachovat lokální verzi, nebo verzi ze serveru, však nereagují. Především ale chybí zobrazení kolidujících položek. Jedinou možností je tedy zvolit Skip, což vyvolá přeskočení této kolize a kontakt tak není v konzistentním stavu. SyncKolab nijak nerozšiřuje položky kontaktů, hodí se tak pro použití mezi více klienty používajícími pouze Thunderbird. Nezotaví se při změně názvu adresáře (addressBook v Thunderbirdu je uložen jako název, ne URI). Všechny informace o účtech jsou uloženy v preferencích v about:config. Synchronizované kontakty: jakékoli Úložiště: IMAP server, složka v profilu a TH Fotky: ano Kolize: ano, ale nefunkční GUI + + + −
více složek k synchronizaci použití jakéhokoli IMAP serveru podpora fotek nefungující podpora kolizních situací
Obrázek 2.2: Kolizní okno doplňku SyncKolab
2.4.2
Zindus
Doplněk Thunderbirdu přináší synchronizaci kontaktů s Google kontakty nebo Zimbra Server (open-source server pro Linux a Mac umožňující správu e-mailů, kontaktů a kalendářů [2]). Možnost synchronizace pouze kontaktů v účtu Google (ne libovolné kontakty do složky na IMAP serveru). Podpora Google Groups v kontaktech. Zindus nijak nerozšiřuje položky kontaktů. Synchronizace probíhá pouze na úrovni vybraných položek (např. adresa není zahrnuta vůbec). V doplňku neexistuje okno s kolizemi, bez uživatelova potvrzení přepisuje verzi na serveru lokálními změnami. Nemá žádné interní úložiště pro verze kontaktů. Nepodporuje mail listy ani fotky.
6
KAPITOLA 2. POPIS PROBLÉMU, SPECIFIKACE CÍLE Synchronizované kontakty: google contacts Úložiště: pouze server a TH Fotky: ne Kolize: ne − − −
2.4.3
synchronizace pouze kontaktů v účtu Google chybí podpora kolizních situací (serverová verze je při kolizi přepsána lokální) nepodporuje fotky u kontaktů
ThunderSync
Doplněk Thunderbirdu přináší synchronizaci přes úložiště na lokálním disku ve formě vCard souborů. Lze synchronizovat jakýkoli adresář v Thunderbirdu, pro každý je možné zvolit složku na disku, do které budou soubory zapisovány. Soubory jsou zapisovány jako vCard 2.1. ThunderSync nijak nerozšiřuje položky kontaktů, hodí se tak pro použití mezi více klienty používajícími pouze Thunderbird. Všechny informace o účtech jsou uloženy v preferencích v about:config. Zotaví se ze změny názvu adresáře v Thunderbirdu, addressBook je uložen jako URI, ne jako jeho název. Podporuje fotky, ty jsou uloženy přímo ve vCard souboru jako base64. Při kolizi není žádná verze původní, pouze aktuální z adresáře a vzdálená ze souboru. Synchronizované kontakty: jakékoli Úložiště: pouze složka a TH Fotky: ano Kolize: ano + + −
2.4.4
synchronizace jakéhokoli adresáře podpora fotek úložiště pouze na lokálním disku
Google Contacts
Doplněk Thunderbirdu umožňuje synchronizaci s Google kontakty. Podporovány jsou tak pouze kontakty v účtu Google. Synchronizaci nelze ručně vyvolat, lze pouze zvolit, zda zahájovat synchronizaci při startu Thunderbirdu. Změny v kontaktech jsou pak zapisovány na server ihned po uložení změny v kontaktu, nekontroluje se ale jejich změna na serveru. Verze na serveru je vždy přepsána lokální. Podporuje fotky u kontaktů, lokálně je ukládá do adresáře GoogleContacts/Photos v profilu. Synchronizované kontakty: google contacts Úložiště: pouze server a TH Fotky: ano Kolize: ne (vždy přepis serverové verze) + − −
podpora fotek synchronizace pouze kontaktů v účtu Google chybí podpora kolizních situací (serverová verze je při kolizi přepsána lokální)
2.4. REŠERŠE EXISTUJÍCÍCH ŘEŠENÍ
7
Obrázek 2.3: Rozšíření položek v kontaktech v doplňku gContactSync
2.4.5
gContactSync
Doplněk Thunderbirdu umožňuje synchronizaci s Google kontakty. Lze vytvořit více účtů, každý však bude spolupracovat pouze s Google kontakty. Toto lze tedy prakticky použít pouze v situaci, máme-li více Google účtů. Složky s kontakty v Thunderbirdu pak budou odpovídat kontaktům uloženým v jednotlivých účtech. Neobsahuje okno na řešení kolizí, směr přepisu při konfliktu uživatel volí v nastavení předem. Podporuje fotky, jsou uloženy ve standardním adresáři pro fotky v Thunderbirdu (ProfD/Photos). Doplněk rozšiřuje položky kontaktů tak, aby bylo možné je pohodlně namapovat na základní položky použité v Google kontaktech (viz obr. 2.3). Zotaví se ze změny názvu adresáře v Thunderbirdu, addressBook je uložen jako URI, ne jako jeho název. Synchronizované kontakty: google contacts Úložiště: google server, soubor se zálohou v profilu a TH Fotky: ano Kolize: ne (směr přepisu lze měnit v nastavení rozšíření) + − −
2.4.6
podpora fotek synchronizace pouze kontaktů v účtu Google chybí podpora kolizních situací
Addressbook Synchronizer
V průběhu implementační fáze se objevilo ještě jedno rozšíření Thunderbirdu s názvem Addressbook Synchronizer. Podle popisu umožňuje synchronizovat kontakty přes lokální adresář, FTP, WebDAV a IMAP server. Při testování jsem však došel k závěru, že synchronizace zde není úplná a funkční. Program je založen na výměně souborů ukládaných do
8
KAPITOLA 2. POPIS PROBLÉMU, SPECIFIKACE CÍLE
jednotlivých úložišť (složka v případě lokálního disku, FTP a WebDAVu, příloha e-mailu v případě IMAP serveru). Soubory však nereprezentují jednotlivé kontakty, nýbrž jsou to soubory s příponou .mab obsahující celý adresář. Ačkoli nastavení programu se zmiňuje o možnostech nastavení chování při kolizích, vždy dochází pouze k přepisu celého adresáře buď ve vzdáleném úložišti, nebo v Thunderbirdu. Pro práci s takovým rozšířením bychom museli pečlivě dbát na rozlišování akcí, které vyvoláme. Upload striktně přepíše vzdálenou verzi, download přepíše lokální změny.
Kapitola 3
Analýza a návrh implementace 3.1 3.1.1
Analýza Specifikace požadavků
Soustředím se na vytvoření aplikace, která bude vykazovat následující vlastnosti: ∙ obecný synchronizační modul bude mít charakter knihovny, ∙ synchronizovány budou nejen textové položky, ale také přílohy v binárním formátu, ∙ při synchronizaci se budou kompletně řešit kolizní situace, ∙ obecný synchronizační modul bude využit pro implementaci synchronizace kontaktů, ∙ položky kontaktů v Thunderbirdu budou rozšířeny tak, aby lépe odpovídaly kontaktům ve službě Gmail a v OS Android, ∙ položky budou ukládány každá zvlášť (jedna položka = jeden soubor/e-mail), ∙ synchronizace bude podporovat všechny běžně dostupné IMAP servery, ∙ synchronizace bude co nejoptimálnější ohledně datového přenosu, ∙ aplikace bude podporovat více adresářů a synchronizaci na více IMAP serverech.
3.1.2
Princip synchronizace
Synchronizací je pro účel této práce myšlena jednorázová akce vyvolaná uživatelem či automaticky vedoucí k dosažení stavu, kdy si obsahem a strukturou odpovídají data v lokálním adresáři a ve vzdáleném úložišti. Na straně lokálního adresáře synchronizaci obsluhuje klientská aplikace, vzdálené úložiště je obsluhováno serverem. Za položky považujeme jednotlivé entity nacházející se v adresáři, mohou jimi být soubory nebo např. kontakty. Položky jsou tvořeny z atributů.
9
10
KAPITOLA 3. ANALÝZA A NÁVRH IMPLEMENTACE
3.1.2.1
Základní princip
Základní princip synchronizace lze vysvětlit jednoduchým způsobem: 1. zjistíme, jaké změny jsou ve vzdáleném úložišti (rozhodně se nechceme zabývat srovnáváním všech položek, pokud nenastala žádná změna), 2. zjistíme, k jakým změnám došlo v lokálním adresáři od poslední synchronizace, 3. ze změněných položek si vytvoříme tabulku, kde jeden sloupec označuje lokální změny, druhý změny ve vzdáleném adresáři, tuto tabulku řádek po řádku procházíme, 4. je-li změna na lokální straně, přepíšeme verzi ve vzdáleném úložišti a naopak. 3.1.2.2
Kolizní situace
Problém nastává v situaci, kdy se v jednom řádku tabulky vyskytuje hodnota v obou sloupcích. V takové chvíli nejsme schopni jednoduše určit, která verze má být upřednostněna. Tuto situaci lze označit jako kolizní. Kolize tedy nastává v případě, že se klientská aplikace snaží do vzdáleného úložiště vložit změněnou verzi položky z lokálního adresáře, odpovídající položka na vzdáleném úložišti však byla od předchozí sychronizace změněna. (Tato změna musela být provedena jinou klientskou aplikací.) Aby se předešlo takovýmto kolizím v případě současných změn na obou stranách, je možné na klientské straně synchronizaci podporovat databází, sloužící k uložení předchozích verzí dat. V tomto ohledu se pak synchronizace podobá procesu verzování, kdy se ukládá historie provedených změn nad daty tak, aby bylo možné rozpoznat, kdy byla jaká verze vytvořena. Díky tomu lze například poznat novější verzi a tu při synchronizaci automaticky upřednostňovat. I přesto ale mohou nastávat situace, kdy není aplikace sama schopna rozhodnout, která verze dat by měla být ponechána. Za pomoci znalosti předchozích změn při synchronizaci tedy klientská aplikace vyřeší některé situace sama, na některé vyžaduje zásah uživatele a jeho rozhodnutí. Při verzování změn do synchronizační tabulky přibude jeden sloupec s označením jako původní (databázová) verze. Verzování změn nad daty má ještě jednu výhodu, změny lze využít jako zálohu. V případě verzovacích systémů, které se v současné době používají především na podporu vývoje softwaru při verzování zdrojových kódu, ke kolizím dochází v případě, že například více vývojářů mění na svých lokálních počítačích jeden soubor zdrojového kódu, který poté odešlou do repozitáře1 . Verzovací nástroje v tomto případě většinou nabízí možnost sloučení souboru, což je proces, při kterém se řádky z jednotlivých verzí zdrojových souborů vkládají za sebe, až vytvoří jeden výsledný. Slučování (merge) probíhá částečně automaticky, ale vyžaduje rozhodnutí uživatele v případě nesrovnalostí. Při rešerši existujících synchronizačních aplikací jsem se často setkal s přístupem, že kolize jsou řešeny sice automaticky, ale podle předem daného scénáře, např. tak, že je vždy přepsána lokální verze verzí ze vzdáleného úložiště. 1
ve verzovacích nástrojích se vzdálené úložiště nazývá obvykle repozitář
3.1. ANALÝZA
3.1.2.3
11
Identifikátory
Položky jsou od sebe odlišovány pomocí unikátního atributu označeného jako UUID (beremeli v úvahu např. možnou změnu jména a příjmení u kontaktu je UUID nezbytností), jejich konkrétní verze jsou pak označovány atributem RevId (celé číslo). RevId v průběhu života položky roste o 1 s každou změnou. Pomocí dvojice RevId a UUID jsme schopni naplnit tabulku synchronizace, rychle skrze ní procházet a rozhodovat o akcích, které mají být vykonány. 3.1.2.4
Příklad procházení synchronizační tabulkou
Ukázka toho, jak může synchronizační tabulka vypadat včetně akcí, které je nutné při synchronizaci vykonat, je v tabulce 3.1. Protože není nutné vždy porovnávat přímo hodnoty položek, ale pouze jejich čísla revizí, jsou v tabulce uvedena pouze tato čísla.
UUID 53 54 55 58 60 62 67 68 71 83
local 3 1 20 15 15
32 1
RevId remote 2 2 28 15 20 1 3 10
database 2 1 20 14 14
10 32
akce zápis lokální verze do vzdáleného úložiště přepis lokální verze vzdálenou přepis lokální verze vzdálenou kolize kolize vytvoření nové položky v lokálním adresáři vytvoření nové položky v lokálním adresáři smazání položky ve vzdáleném úložišti smazání položky v lokálním adresáři nová lokální položka, zápis do vzdáleného úl.
Tabulka 3.1: Tabulka synchronizace
Ve chvíli vyvolání synchronizace je každá lokální položka v jednom ze stavů: buď je identická s verzí v databázi (pak local.RevId==database.RevId), nebo je změněná a její RevId je oproti databázovému o 1 vyšší. RevId vzdálené verze se může od RevId lokální verze lišit o libovolné celé číslo, nesmí ale být menší než RevId v databázi. Je-li vzdálené RevId vyšší než lokální o 2 a více, položka byla v době od poslední synchronizace několikrát změněna jiným klientem. Tuto situaci ukazují položky s UUID 55 a 60. První vyvolá přepis lokální verze, druhá vyvolá kolizi (všimněme si, že lokální položka je oproti databázi změněna). Zápis verze do databáze je realizován při každém zápisu/přepisu do vzdáleného nebo lokálního úložiště. Absence RevId je považována za absenci položky s daným UUID v daném úložišti. Tak je možné pro položku s UUID 71 rozhodnout, že byla ve vzdáleném úložišti smazána a odstranit ji i lokálně. Není-li UUID nalezeno v databázi ani ve vzdáleném úložišti,
12
KAPITOLA 3. ANALÝZA A NÁVRH IMPLEMENTACE
je to známka toho, že položka byla nově vytvořena – položka s UUID 83. Položky s UUID 62 a 67 naopak existují pouze ve vzdáleném úložišti a budou synchronizovány stažením do lokálního adresáře. Z tabulky lze také pozorovat, že číslo RevId z lokálního adresáře a z databáze se liší vždy maximálně o 1. Je to dáno tím, že číslo verze se zvyšuje jen při vyvolání synchronizace, ne při každé editaci položky a jejím uložení. 3.1.2.5
Trojcestné slučování
Změnu položky (souboru, kontaktu) v lokálním adresáři poznáme buď pomocí příznaku označujícího čas poslední změny, nebo přímým porovnáním obsahu položky s poslední verzí v databázi. Nejsou-li shodné, uživatel položku změnil a měla by tedy být označena vyšším RevId a následně distribuována na vzdálené úložiště a do databáze. Pokud se ve vzdáleném úložišti mezitím objevila položka s vyšším RevId, než tím, které máme pro položku naposledy registrované v databázi, nastává kolize. Díky verzování jsme schopni uživateli zobrazit tabulku s jednotlivými atributy položky a nabídnout předpokládané vyřešení. Kolize se tak může řešit tzv. trojcestným slučováním (3-way merge), kde jedna verze je lokální, druhá vzdálená a třetí z databáze (lze označit jako původní). Slučování se neprovádí nad celou položkou jako nedělitelnou množinou, nýbrž nad jednotlivými atributy, ze kterých se výsledná položka složí. Výsledná verze položky tak může obsahovat jednotlivé atributy pocházející každý z jiné verze. Ukázka trojcestného slučování je v tabulce 3.2. Je-li lokální a vzdálená verze stejná, řešení je snadné, výsledkem bude jedna z těchto verzí. Jsou-li jakékoli jiné dvě verze shodné, výsledkem je verze třetí. Nerovnají-li se hodnoty žádné dvojice odpovídajících atributů je o vyřešení této kolize nutné požádat uživatele. V tabulce jsou zvýrazněny vždy dvojice shodných hodnot atributů. Případ shodnosti celé trojice hodnot (jméno, příjmení) je při slučování vyhodnocen jako shoda lokální a vzdálené verze. atribute jméno příjmení telefon zaměstnání web bydliště
local Karel Poláček 504-111 reportér www.polacek.cz Praha
remote Karel Poláček 504-222 reportér www.ln.cz Hradec Králové
database Karel Poláček 504-111 spisovatel www.ln.cz Rychnov n. Kněžnou
result Karel Poláček 504-222 reportér www.polacek.cz ???
Tabulka 3.2: Trojcestné slučování - příklad Algoritmus slučování je možné v pseudo kódu napsat způsobem viz algoritmus 3.1. Kvalitní článek zabývající se trojcestným slučováním a algoritmem diff3, který je jeho implementací, byl vydán na University of Pennsylvania ve spolupráci s Yahoo [3].
3.1.3
Vzdálené úložiště - IMAP protokol
IMAP (Internet Message Access Protocol) je protokol umožňující vzdálený přístup k elektronickým zprávám na serveru a manipulaci s nimi (publikováno v RFC 3501[4]). Protokol
3.1. ANALÝZA
13
Algoritmus 3.1 trojcestné slučování for all atribute in item do if ( local.atribute == remote.atribute ) then result.atribute = local.atribute; continue; end if if ( local.atribute == database.atribute ) then result.atribute = remote.atribute; continue; end if if ( remote.atribute == database.atribute ) then result.atribute = local.atribute; continue; end if result.atribute = askUser(atribute); ◁ kolize nemůže být automaticky vyřešena end for
pracuje na klasickém modelu klient-server, klient odesílá příkazy serveru, který na ně reaguje a odpovídá. Protokol neslouží k odesílání e-mailů (jako SMTP), odchozí zprávy tak nebudou e-maily v pravém slova smyslu. Odchozí zprávy od klienta na server jsou pouze uloženy do e-mailové schránky, odkud mohou být později staženy jiným klientem. Většina dnešních běžně dostupných serverů vyžaduje zabezpečení komunikace, nejčastěji SSL. Jelikož se jednotlivé servery v přesných implementacích liší, budeme při vývoji brát za důležitou funkčnost na serveru imap.gmail.com, imap.aol.com a imap.feld.cvut.cz. 3.1.3.1
IMAP zprávy
Protokol byl vytvořen kompatibilní s RFC 822 [5], které definuje strukturu internetových textových zpráv. V tomto RFC je popsána zpráva jako text členěný na jednotlivé řádky složený z hlavičky a nepovinně těla zprávy [6] . Tyto části jsou od sebe odděleny prázdným řádkem. Každá položka hlavičky musí začínat na novém řádku a doporučení umožňuje přidávat i hlavičky vlastní. Toho bude využito pro uchování UUID jednotlivých kontaktů. Příklad zprávy včetně standardních hlaviček: 1 2 3 4 5 6 7 8 9
MIME-Version: 1.0 Received: by 10.112.23.8 with HTTP; Wed, 4 Feb 2012 14:14:07 -0800 (PDT) Date: Wed, 4 Feb 2012 23:14:07 +0100 Delivered-To:
[email protected] Message-ID:
Subject: Greeting From: Imap Imap <[email protected]> To: Imap Imap <[email protected]> Content-Type: text/plain; charset=UTF-8
10 11
Hello Mike!
14
KAPITOLA 3. ANALÝZA A NÁVRH IMPLEMENTACE
Obsah zpráv včetně hlaviček smí obsahovat pouze ASCII znaky, pro přenos znaků mimo ASCII je tedy nutné znaky převést například pomocí quoted printable, což je způsob přenosu 8 bitových znaků přes 7 bitový kanál. Souhrnně se rozšířením elektronických zpráv o znaky mimo ASCII zabývá technologie Multipurpose Internet Mail Extensions (MIME) popsaná v RFC 2045 a 2046. Quoted printable je jedním z možných způsobů tohoto rozšíření. Ke zprávám je možné přikládat přílohy zakódované ve formátu base64 (taktéž součástí standardu MIME) toho bude využito pro synchronizaci fotek, které jsou součástí kontaktů. 3.1.3.2
Přidělování RevId
Pro ukládání zpráv na server budeme používat složku v existující e-mailové schránce. Dle definice protokolu je každé zprávě na serveru přiřazen jednoznačný číselný identifikátor UID o délce 32 bitů. Protokol zaručuje, že žádná jiná zpráva v dané složce nebude mít UID se stejnou hodnotou. Při vložení zprávy na server je přiřazen identifikátor (číslo) vyšší, než předchozí zprávě. Tato vlastnost přesně odpovídá potřebám pro verzování položek, přiřazení atributu RevId jednotlivým verzím. Položky nebudou sice moci se svým RevId začínat na 1, jako je tomu v tabulce 3.1, odpadne však starost o udržování konzistentního stavu RevId. Každá položka ve vzdáleném úložišti tedy bude identifikována buď pomocí UUID (identifikátor položky) nebo RevId (UID zprávy dle IMAP protokolu, identifikátor verze). V obou případech jednoznačně. 3.1.3.3
IMAP příkazy
Protokol IMAP v základní verzi obsahuje na 20 příkazů, klíčovými pro přenos zpráv jsou: FETCH – pro načtení zpráv ze složky na serveru. Podporuje celou řadu parametrů pro získání konkrétních částí zpráv (bude využito pro minimalizaci přenesených dat). APPEND – pro uložení zprávy do složky na server. Zprávě je po uložení přiřazeno jednoznačné UID, které bude využito na verzování kontaktů (viz dále). Všechny příkazy pro manipulaci se zprávami na serveru vyžadují parametr identifikující zprávu nebo sekvenci zpráv. Tímto parametrem je standardně pořadí zprávy ve složce; toto pořadí se logicky při odstranění zprávy mění. Druhou možností, kterou IMAP příkazy podporují, je identifikace zprávy pomocí UID. Toto UID zůstává po celou dobu existence zprávy v dané složce neměnné. (RFC definuje ještě atribut UID validity, které připouští možnost změny UID, zpráva by tak měla být identifikována dvojicí UID a UID validity. Kromě situace, kdy dojde k přetečení UID a reorganizace identifikátorů zpráv je tak nezbytná, však běžně dostupné servery UID validity nemění.) Protože verze položek budou nést unikátní UID zpráv, musí být složka na IMAP serveru po celou dobu vyhrazena pouze pro synchronizování jednoho lokálního adresáře (ke složce samozřejmě přistupují klienti z různých zařízení, není však možné dovolit, aby byly do jedné složky vkládány položky z více lokálních adresářů jednoho klienta, případně aby se do složky ukládaly jiné zprávy než synchronizační).
3.1. ANALÝZA
3.1.3.4
15
Přesouvání starých verzí položek
Vlastností IMAP protokolu je i nemožnost zprávy na serveru editovat. Podporováno je pouze jejich ukládání, odstranění, případně přesunutí. Změny v kontaktech tedy budeme ukládat tak, že původní zpráva se smaže a do složky na server bude odeslána zpráva s novou verzí. Tato zpráva získá unikátní UID, které se pro naše potřeby stane RevId. Pro uchovávání historie je možné místo odstraňování starých zpráv přesouvání do složky např. Deleted. Tuto složku bude uživatel moci později kdykoli vymazat.
3.1.4
GUI Thunderbirdu s ohledem na kontakty
Grafické rozhraní pro editaci kontaktů v programu Thunderbird je ukázáno na obrázku 3.1. Protože součástí práce je rozšíření struktury kontaktů o další položky (atributy kontaktu), je nutné upravit také toto rozhraní. Přidáné atributy se budou nejvíce týkat typování stávajících atributů (např. přidání možnosti specifikovat typ telefonu – mobilní, pracovní, domů atd.). Jako inspirace v tomto ohledu poslouží grafické rozhraní zásuvného modulu gContactSync (viz obr. 2.3). Návrh takového okna je na obrázku B.1 v příloze tohoto dokumentu.
Obrázek 3.1: Grafické rozhraní editace kontaktu v Thunderbirdu 11.0.1
3.1.5
Mozilla Application Framework
Zásuvné moduly pro Mozilla Thunderbird stejně jako pro Mozilla Firefox mohou být vyvíjeny v prostředí Mozilla Application Framework. Tento framework postkytuje vývojářům několik služeb pro jednodušší implementaci. Logika aplikace je psána v jazyce Javascript,
16
KAPITOLA 3. ANALÝZA A NÁVRH IMPLEMENTACE
pro tvorbu grafického uživatelského rozhraní se využívá XUL technologie, je zde i možnost využití cross-platformních objektových komponent přes technologii XPCOM. K Frameworku existuje poměrně bohatá dokumentace, ač často se vztahuje již ke starším verzím. Je jí tedy nutné brát často s rezervou a všechny implementační detaily ověřovat v nových verzích. Při vývoji byla využita obecná příručka vývoje aplikací pro Mozilla Framework [7], článek o vývoji zásuvných modulů do Mozilla a Thunderbird aplikací [8] a dále pak dokumentace z oficiálních stránek pro podporu vývoje [9].
3.2
Návrh implementace
Pro pochopení následujícího návrhu architektury aplikace si dovolím odkázat nejprve na následující kapitolu 4 věnovanou výhradně podrobnému popisu synchronizačního protokolu.
3.2.1
Struktura aplikace
Aplikace se bude skládat z následujících prvků: obecný synchronizační modul, rozšiřující modul pro synchronizaci kontaktů, IMAP klient pro komunikaci se vzdáleným úložištěm a grafické uživatelské rozhraní pro podporu rozšířených atributů kontaktů a tvorbu synchronizačních účtů. 3.2.1.1
Vrstvy synchronizačního modulu
Obecný synchronizační modul je navržen ve třech vrstvách, kde nejnižší vrstva (remote storage) obstarává komunikaci se vzdáleným úložištěm (získávání a ukládání zpráv), prostřední vrstva (common sync) řídí synchronizační fáze a horní vrstva (items) slouží pro obsluhu položek v lokálním úložišti. Tyto vrstvy jsou naznačeny také v obrázku 3.2.
Obrázek 3.2: Návrh třívrstvé architektury
3.2. NÁVRH IMPLEMENTACE
3.2.1.2
17
Význam a úkoly jednotlivých vrstev
Horní vrstva je od obecné synchronizace oddělena, protože každý typ synchronizovaných položek vyžaduje jiný přístup. Kontakty v e-mailovém klientu jsou uloženy jinde a za pomocí jiných objektů než události z kalendáře. Obecná obsluha synchronizace však na těchto podrobnostech ohledně konkrétních položek nesmí záležet. Spodní vrstva je obecné synchronizace oddělena proto, abychom umožnili synchronizačnímu modulu jednoduše přidat i jiné vzdálené úložiště než jen IMAP server. Po vrstvě items je vyžadováno umožňovat načítání položek z lokálního úložiště (tvorba synchronizační tabulky), manipulaci s položkami (jejich vytváření, editaci a odstraňování), dále serializaci a deserializaci položek do zpráv. Pro různé druhy položek budou sloužit vždy samostatné třídy vykazující zmiňované funkcionality. Po remote storage vrstvě se vyžaduje, aby uměla procházet vzdálené úložiště, získávat z něj a zapisovat do něho zprávy (fáze import, export a zamykání úložiště). 3.2.1.3
Implementace vrstev
Vstupní bod synchronizace bude v horní vrstvě. Voláme vždy synchronizaci konkrétního typu položek (konkrétního adresáře s kontakty atd.). Z této vrstvy se bude volat třída implementující vrstvu obecné synchronizace, která bude již dále řídit jednotlivé fáze synchronizace. Vrstva items bude implementována pro různé druhy položek samostatnými třídami. Metody, které musí tyto třídy implementovat, budou popsány dále. V aplikaci k synchronizaci kontaktů bude konkrétně implementována třídou ContactSynchronization. Vrstva common sync bude implementována pomocí třídy CommonSync. Instance této třídy bude obsahovat objekty jako ukazatele na instance tříd implementujících nadřazenou a podřazenou vrstvu, přes tyto objekty bude docházet k volání obslužných metod synchronizace. Vrstva remote storage bude implementována pomocí třídy ImapSyncController s IMAP klientem a třídy ImapSync, která tohoto klienta obsluhuje. 3.2.1.4
IMAP klient
Klient bude přes socket komunikovat se serverem, starat se o načtení aktuálních verzí položek, zápis nových verzí, přesouvání a odstraňování starých verzí. Při vývoji bude použita a rozšířena IMAP knihovna Bc. Petra Macha, kterou implementoval v rámci bakalářské práce. Protože IMAP knihovna je asynchronní, bude celý modul implementován jako flow mezi metodami jednotlivých tříd. Objekty budou tedy pomocí scope vědět o objektech jejich metodách, které mají po ukončení svého běhu volat. Přes scope se například do obsluhy IMAP úložiště dostane odkaz na metodu, která se má zavolat po úspěšném načtení zpráv. 3.2.1.5
Zodpovědnost vrstev v jednotlivých fázích synchronizace
Za importní a exportní fázi je zodpovědná vrstva remote storage. Tato vrstva taktéž zodpovídá za bezchybné zamykání a odemykání vzdáleného úložiště. Odpovědnost za fázi synchronizačního jádra je rozdělena mezi zbývající dvě vrstvy. Common sync má na starost pouze obecnou synchronizaci a to procházení synchronizační tabulky a verzování položek do databáze. Pro vytvoření synchronizační tabulky a editaci položek vyžaduje funkce vrstvy items.
18
3.2.2
KAPITOLA 3. ANALÝZA A NÁVRH IMPLEMENTACE
Řešení kolizí
Aby uživatel mohl při kolizi pohodlně vybírat hodnoty položek, je navrženo zobrazování těchto kolizí v samostatném okně aplikace. V okně budou v tabulce srovnány hodnoty jednotlivých subatributů. Existuje-li rovnost alespoň ve dvou hodnotách, je do sloupce s výsledkem rovnou předvybrána hodnota dle algoritmu slučování, takové řádky jsou pak podbarveny zeleně. Neexistuje-li řešení podle algoritmu slučování, řádky jsou podbarveny červeně. Návrh takového okna je v obrázku 3.3.
Obrázek 3.3: Návrh okna pro řešení kolizních situací
3.2.3
Formát přenášených objektů
Pro synchronizaci s ostatními klienty je nutné vytvořit jednotný formát přenášených zpráv (serializovaných kontaktů). Po dohodě s vedoucím práce jsem se inspiroval projektem Portable Contacts [11], který modeluje kontakty jako JSON elementy [12]. Toto bude pro serializaci kontaktů velmi výhodné, protože mezi JSONy a objekty v Javascriptu je přímý vztah – lze je mezi sebou nativně převádět. Serializace a deserializace kontaktů je tak elegantně vyřešena.
3.2.4
Optimalita datového přenosu
Pro dosažení minimálního datového přenosu při synchronizaci bude každý klient ukládat stav vzdáleného úložiště po každé sychronizaci. Jedná se o dvojici existingItems a nextUID,
3.2. NÁVRH IMPLEMENTACE
19
tedy počet uložených zpráv a UID příští zprávy. Díky znalosti této dvojice můžeme lehce předejít stahování objemných dat v případě, že se ve vzdáleném úložišti neprovedla žádná změna. Pokud byla v době od poslední synchronizace nějaká položka ze vzdáleného úložiště odstraněna, musíme sáhnout ke stahování zpráv, abychom zjistili, o kterou položku se jednalo. I tak je však cílem nestahovat verze položek celé, nýbrž jen jejich hlavičky. Pro tento účel bude v hlavičce každé zprávy uloženo UUID sloužící k identifikaci kontaktů. Porovnáním stažených UUID z hlaviček s lokální verzí adresáře jednoduše zjistíme například, který kontakt byl smazán. Teprve při zjištění, že na serveru se nachází nová verze položky se přistoupí načtení jejího obsahu a přílohy.
3.2.5
Rozšíření atributů kontaktů
Následující odstavce se týkají návrhu rozšíření kontaktů o typy používané v OS Android. Tento návrh je rozdělen na backend (ukládání těchto rozšířených typů) a frontend (zobrazování rozšířených typů v grafickém uživatelském rozhraní). 3.2.5.1
Backend
Vlastnosti kontaktu jsou v Thunderbirdu ukládány v instancích třídy nsIAbCard [9], která bohužel postrádá možnost vylistování všech atributů kontaktu. Pro získání hodnoty atributu kontaktu případně pro jeho editaci nabízí třída pouze funkce getProperty a setProperty. Všechny atributy jsou typu string a tak změna se provádí například voláním aCard.setProperty("FirstName","Karel");. Aplikace si tedy bude muset pamatovat všechny přidané atributy pro jejich další používání (jejich změnu, či vyčištění kontaktu). Druhou možností by bylo vytvořit nad touto instancí wrapper, který by ukládal atributy kontaktu do seznamu, přes který bychom mohli listovat. Při tomto řešení by však bylo nutné přepsat kompletně funkce jádra Thunderbirdu starající se o zobrazování položek kontaktů v grafickém rozhraní a jejich ukládání po editaci. Vzhledem k tomu, že rozšíření atributů v kontaktu není velké, navrhuji použít první variantu. Z instancí nsIAbCard budeme při každé synchronizaci vytvářet JSON (export kontaktu) a opačně (import kontaktu). Toto budou mít na starost 2 funkce: importContactProperties a exportContactProperties. Pro funkční serializaci aplikace bude muset znát i všechny defaultní atributy třídy nsIAbCard (kontakt neumí odstranit všechny svoje atributy). Editace kontaktu se provede tak, že se nejdříve všechny jeho atributy odstraní (nastaví na prázdný řetězec), pak teprve jsou nastaveny jeho atributy na nové hodnoty. Atributy (defaultní i přidané) budou aplikaci dostupné přes globální objekt rozšíření. Změny v atributech kontaktů budou následující: ∙ přidání typů k telefonním číslům, ∙ rozšíření počtu e-mailových adres, ∙ přidání typů k e-mailovým adresám,
20
KAPITOLA 3. ANALÝZA A NÁVRH IMPLEMENTACE
∙ přidání typů k webovým stránkám, ∙ přidání IM účtů (uživatelské jméno, protokol, typ), ∙ přidání SIP účtů (uživatelské jméno, typ). Hodnoty konkrétních typů budou převzaty z dokumentace k OS Android [10]. 3.2.5.2
Frontend – GUI
Po rozšíření atributů kontaktu je nutné změnit i grafické rozhraní starající se o zobrazování kontaktů tak, aby k těmto přidaným atributům měl uživatel přístup. K novým atributům budou namapovány ovládací prvky podle návrhu v obrázku B.1. O grafické uživatelské rozhraní kontaktů se v Thunderbirdu starají XUL objekty abEditCardDialog.xul a abNewCardDialog.xul. Jejich volání bude překryto pomocí overlay tak, aby se při otevírání okna s kontaktem otevřel současně i soubor cardDialogOverlay.xul, který bude zajišťovat přidání elementů DOM stromu tohoto okna. Ovládací elementy se nejprve v .xul souboru nadefinují, následně bude implementována jejich obsluha (ukládání, přípustné hodnoty). Overlay se vloží do souboru chrome.manifest, což je zaváděcí soubor zásuvného modulu do hostitelské aplikace. Tím je zajištěn vstup souborů s objekty starajícími se o zobrazování a ukládání přidaných kontaktních atributů do jádra Thunderbirdu. Toto překrytí implicitních souborů je ukázáno zde: overlay chrome://messenger/content/addressbook/abEditCardDialog.xul chrome://sync.extbrain.thunderbird/content/gui/cardDialogOverlay.xul overlay chrome://messenger/content/addressbook/abNewCardDialog.xul chrome://sync.extbrain.thunderbird/content/gui/cardDialogOverlay.xul
3.2.6
Použité nástroje
Pro vývoj zásuvného modulu budou použity následující nástroje: IntelliJ IDEA Pro psaní javascriptového kódu lze použít samozřejmě jakýkoli textový editor. Idea však poskytuje dobré zvýrazňování syntaxe, podporu refaktoringu a našeptávání ze standardních knihoven, tak z kódu vlastního projektu. Venkman Debugovací nástroj přímo jako zásuvný modul v Mozilla Thunderbird umožňuje pozastavit vykonávání kódu přímo v aplikaci. Dobře použitelný při zobrazování obsahu proměnných, také pro prozkoumávání zdrojových souborů jádra Thunderbirdu.
3.2. NÁVRH IMPLEMENTACE
21
DOM Inspector Umožňuje prozkoumávání hierarchického stromu Document Object Model elementů v grafickém rozhraní. Užitečný hlavně při potřebě navázat na defaultní GUI novými ovládacími prvky. SQLite Manager Nástroj pro kompletní management databáze SQLite, která je součástí Mozilla Thunderbird. Zobrazení struktury jednotlivých tabulek včetně jejich obsahu usnadňuje ladění a optimalizaci databázových dotazů.
22
KAPITOLA 3. ANALÝZA A NÁVRH IMPLEMENTACE
Kapitola 4
ExtBrain Sync Protokol Pro bezchybné realizování synchronizace je nutné definovat nejen formát zpráv, které budou přenášeny, a typ úložiště, ale obecněji i protokol, podle kterého se bude řídit jednak komunikace klientské aplikace se vzdáleným úložištěm, ale i sled kroků prováděných nad lokálním úložištěm. Na podrobný popis protokolu se zaměřuje následující kapitola.
4.1
Definice pojmů
Označme entitu určenou k synchronizaci jako položku (item). Položkou může být například množina kontaktních informací, událost v kalendáři apod. Především při synchronizačním přenosu lze místo pojmu položka užívat označení zpráva. Zpráva se skládá povinně z těla (content) a nepovinně přílohy (attachment). Content je serializovaná položka v určitém formátu (např. XML, JSON), má podobu textových dat. Attachment je soubor, který se k položce váže. Může mít podobu textového nebo binárního souboru. Jedná se například obrázek přiložený ke kontaktu. Účtem označujeme souhrn informací o synchronizovaném lokálním adresáři, uživatelské jméno, adresu serveru, port, typ zabezpečení a název složky na serveru. Synchronizace je jednorázová akce vyvolaná uživatelem či automaticky vedoucí k dosažení stavu, kdy sobě obsahem a strukturou odpovídají položky v lokálním adresáři a ve vzdáleném úložišti.
4.2
Označení položky
Položka je identifikována pomocí UUID (Universally Unique IDentifier) obsaženého v těle zprávy. Synchronizačním protokolem je zajištěno, že se v úložišti v jednu chvíli nevyskytují dvě zprávy se stejným UUID. Protokol přikládá každé verzi položky také unikátní RevId, které slouží k rychlému odlišení verzí. Unikátnost tohoto identifikátoru je zaručena pomocí IMAP protokolu.
23
24
KAPITOLA 4. EXTBRAIN SYNC PROTOKOL
4.3
Lokální a databázové úložiště
Lokální úložiště (adresář) je místo na lokálním zařízení (počítači, mobilním telefonu), které má být synchronizováno se vzdáleným úložištěm. Pro podporu synchronizace slouží databázové úložiště uchovávájící informace o verzích položek. Databáze zajišťuje uchovávání těchto informací o každé položce: UUID, RevId, tělo, hash přílohy, entryPresent (zdali položka v lokálním úložišti existuje, nebo byla smazána) a účet, ke kterému se položka váže.
4.4
Vzdálené úložiště
Jako vzdálené úložiště synchronizace slouží složka (mailbox) v e-mailovém účtu na IMAP serveru. Zprávy jsou do úložiště posílány pomocí IMAP protokolu dle RFC 3501. Zpráva je pro tento přenos zabalena do IMAP zprávy dle RFC 822 (viz odstavec 3.1.3), ta je pomocí příkazu APPEND uložena na server. RevId je v ExtBrain Sync protokolu označení pro UID IMAP zprávy, které je IMAP protokolem vygenerováno vždy při vložení zprávy do složky. IMAP zpráva obsahuje kromě jiných standardních hlaviček i řádek s UUID položky. ExtBrain Sync protokol vyžaduje v hlavičce pouze tuto UUID položku, pro přehlednost jsou však v IMAP zprávách ponechány i standardní hlavičky. Ukázka hlavičky IMAP zprávy rozšířené o UUID záznam: 1 2 3 4 5 6 7 8 9
From: Archimedes Thunderbird testProfile Date: Tue, 03 Apr 2012 10:02:40 GMT Subject: =?UTF-8?Q?Karel Jan=C3=A1k?= To: [email protected] uuid: d10a7f70-3a97-4ac9-8293-b0de38c95a3d Message-Id: <ed4f2786b2284cbfb037291584f49393@libimap-client> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable
V následujících podkapitolách jsou podrobně popsány jednotlivé fáze synchronizace. Začneme popisem výměny synchronizačních zpráv, na konci kapitoly je pak popsán způsob zajištění unikátního přístupu do vzdáleného úložiště.
4.5
Fáze synchronizace
Proces synchronizace je možné rozdělit do tří hlavních fází (viz obr. 4.1). Ve fázi import se kontaktuje vzdálené úložiště, zjišťuje se jeho aktuální stav a podle potřeby se z něj získají aktuální/nová data. Druhou fází (označenou jako core) je porovnávání příchozích změn se stavem lokálních položek, jejich editace a případné vybrání pro závěrečnou fázi – export. V té se do vzdáleného úložiště ukládají změny, případně se odstraňují staré verze položek. Není-li třeba měnit obsah vzdáleného úložiště, je fáze export vynechána.
4.6. FÁZE IMPORT
25
Obrázek 4.1: Fáze procesu synchronizace
4.6
Fáze import
Cílem této fáze je zjištění obsahu složky v e-mailové schránce. Data jsou předána dále ke zpracování synchronizačnímu jádru. Získávání obsahu složky se děje ve třech krocích tak, aby se minimalizoval datový přenos mezi klientskou aplikací a serverem. Postup v této fázi je možné popsat jako sled těchto kroků: 1. získání UIDNEXT a EXISTS, 2. načtení hlaviček IMAP zpráv, 3. načtení těl a příloh nových zpráv. Průběh kroků ve fázi import ukazuje také obrázek 4.2. IMAP protokol udržuje nad složkou několik atributů, pro ExtBrain Sync protokol jsou důležité následující dva: UIDNEXT a EXISTS. První označuje předpokládané UID příští zprávy, která bude do složky uložena. Je-li UIDNEXT vyšší než bylo při poslední synchronizaci, do složky byla uložena nová zpráva. Změna tohoto atributu však označuje pouze ten fakt, že zpráva s daným UID byla do složky přidána. Z pouhé změny UIDNEXT nelze rozhodnout, zda tato zpráva ve složce stále existuje. Počet zpráv ve složce označuje druhý parametr – EXISTS. Klientská aplikace musí zajistit lokální uložení obou těchto atributů při každé synchronizaci. Tato dvojice se souhrnně označuje jako „stav účtu“. Aby se nemusel získávat obsah všech zpráv ve složce, není-li to nezbytně nutné, v prvním kroku se zjistí hodnoty UIDNEXT a EXISTS. Pokud se obě tyto hodnoty rovnají hodnotám z předchozí synchronizace, ve vzdálené složce nedošlo k žádným změnám a není proto nutné obsah úložiště dále načítat. (Protože IMAP zprávy na serveru není možné editovat, je zajištěno, že zprávy se stávajícím UID mají stejný tvar, jako při odesílání klientem. Z této situace
26
KAPITOLA 4. EXTBRAIN SYNC PROTOKOL
Obrázek 4.2: Stavový diagram fáze import
lze vyvodit závěr, že aktuální klientská aplikace byla poslední, která ve vzdáleném úložišti prováděla změny.) Liší-li se alespoň jedna z hodnot, je nutné přistoupit ke druhému kroku, kterým je načtení hlaviček IMAP zpráv. Pokud se stav účtu liší v UIDNEXT a zprávy s vyšším nebo rovným UID než je lokální UIDNEXT ve složce na serveru existují, je nezbytné získat jejich kompletní obsah, což je zároveň třetím a záverečným krokem této fáze. V následujících odstavcích je podrobnější popis kroků prováděných ve fázi import.
4.6.1
Získání UIDNEXT a EXISTS
Atributy složky UIDNEXT a EXISTS se vyskytují v odpovědi IMAP serveru na příkaz SELECT (vybrání složky pro přístup ke zprávám uvnitř). Požadované atributy se tedy získají jednorázovým zavoláním příkazu SELECT s parametrem názvu složky a vyparsováním hodnot pomocí regulárních výrazů. Ukázka získání atributů UIDNEXT a EXISTS je v následujícím výpisu. Řádek 1 obsahuje příkaz klientské aplikace, další řádky odpověď serveru. Řetězec a003 před příkazem je tzv. tag umožňující identifikovat odpověď serveru na daný příkaz při asynchronních voláních. V tomto případě je vybrána složka „extbrainContacts“, hodnota UIDNEXT je rovna 988 a ve složce existuje 5 zpráv. 1 2 3 4 5
a003 SELECT extbrainContacts * FLAGS (\Answered \Flagged \Draft \Deleted \Seen, Seen Test lock) * OK [PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen, Seen Test lock \*)] Flags permitted. * OK [UIDVALIDITY 660858846] UIDs valid.
4.6. FÁZE IMPORT
6 7 8 9
27
* 5 EXISTS * 0 RECENT * OK [UIDNEXT 988] Predicted next UID. a003 OK [READ-WRITE] extbrainContacts selected. (Success)
4.6.2
Načtení hlaviček IMAP zpráv
Příkaz FETCH slouží v protokolu IMAP k získávání zpráv. Příkaz vyžaduje dva parametry: číselné označení sekvence požadovaných zpráv a vyžadované prvky zprávy (např. jen některé položky hlavičky, tělo, přílohu). UID FETCH má stejné použití, jen se jako parametr označující sekvenci získávaných zpráv udává UID zprávy namísto pořadí zprávy ve složce. Hlavičky zpráv získáme jednorázovým odesláním příkazu UID FETCH s parametrem pro získání všech zpráv (UID zpráv 1 až *) a BODY[HEADER.FIELDS (UUID)] pro získání UUID řádku z hlavičky IMAP zprávy. Ukázka komunikace klienta se serverem pro získání hlaviček IMAP zpráv.
1 2 3 4 5 6 7 8 9 10 11 12
a004 UID FETCH 1:* BODY[HEADER.FIELDS (UUID)] * 1 FETCH (UID 892 BODY[HEADER.FIELDS (UUID)] uuid: e0be92ae-1a96-4d74-a4f3-1aca8936fdf6) * 2 FETCH (UID 910 BODY[HEADER.FIELDS (UUID)] uuid: 2906f0f4-4958-4be4-904a-b80f4af3ceeb) * 3 FETCH (UID 915 BODY[HEADER.FIELDS (UUID)] uuid: d86d2e79-0f4b-4504-822f-b7529d7d8bf6) * 4 FETCH (UID 923 BODY[HEADER.FIELDS (UUID)] uuid: c6d7c601-a11c-46fb-b96c-733f9c6a9dd9) * 5 FETCH (UID 987 BODY[HEADER.FIELDS (UUID)] uuid: 6ace5e74-97a1-4502-9b56-069caa33485c) a004 OK Success
{46} {46} {46} {46} {46}
Z odpovědi serveru (řádky 2 – 12) je vytvořen seznam položek ve tvaru (RevId=UID zprávy). Příklad takové struktury vytvořené z předchozí odpovědi serveru je v tabulce 4.1. UUID e0be92ae-1a96-4d74-a4f3-1aca8936fdf6 2906f0f4-4958-4be4-904a-b80f4af3ceeb d86d2e79-0f4b-4504-822f-b7529d7d8bf6 c6d7c601-a11c-46fb-b96c-733f9c6a9dd9 6ace5e74-97a1-4502-9b56-069caa33485c
RevId 892 910 915 923 987
Tabulka 4.1: Načtení hlaviček IMAP zpráv - výsledek
28
KAPITOLA 4. EXTBRAIN SYNC PROTOKOL
4.6.3
Načtení těl a příloh nových zpráv
Nové zprávy (nově přidané od poslední synchronizace) jsou ty, které mají v tabulce hlaviček RevId vyšší nebo rovno lokálnímu UIDNEXT. Tyto zprávy je na rozdíl od ostatních nutné získat celé, k tomu se využije opět příkazu UID FETCH. Po serveru požadujeme celé tělo zprávy, proto jako druhý parametr použijeme BODY[TEXT]. Příkaz na získání zprávy s RevId 923 z tabulky 4.1 tak bude vypadat takto: UID FETCH 923:923 BODY[TEXT]. Ukázka komunikace nutné k získání této zprávy je zde: 1 2 3 4 5
a005 UID FETCH 923:923 BODYTEXT * 5 FETCH (UID 65 FLAGS (\Seen) BODYTEXT {451} --e0cb4efe319222759c04bd6bef7a Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable
6 7 8
{"name":"Karel Pol=C3=A1=C5=A1ek","uuid": "c6d7c601-a11c-46fb-b96c-733f9= c6a9dd9"}
9 10 11 12 13 14
--e0cb4efe319222759c04bd6bef7a Content-Type: image/png; name="karel.png" Content-Disposition: attachment; filename="karel.png" Content-Transfer-Encoding: base64 X-Attachment-Id: f_h0wr2yuw0
15 16 17 18
b2JyYXplaw== --e0cb4efe319222759c04bd6bef7a--) a005 OK Success
V řádku 1 je příkaz klientské aplikace včetně úvodního tagu. Řádky 7 a 8 obsahují tělo zprávy, řádek 16 base64 reprezentaci souboru s názvem karel.png v příloze. Tělo zprávy obsahuje serializovanou podobu synchronizované položky. Tělo zprávy a příloha jsou přidány do odpovídajícího řádku seznamu z předchozí sekce. Tento seznam položek nyní již ve tvaru je výstupem tohoto kroku a je předán dále synchronizačnímu jádru. Ukázka této struktury je v tabulce 4.2. V řádku s RevId 923 vidíme tělo zprávy a přílohu tak, jak byly přijaty v tomto kroku. Poté byla získána ještě zpráva s RevId 987, ta přílohu neobsahuje.
4.7
Synchronizační jádro
Úkolem této ústřední fáze procesu synchronizace je učinit všechny „lokální“ kroky potřebné pro nastolení konzistentního stavu mezi vzdáleným úložištěm a místním adresářem. To zahrnuje práci s lokálními položkami (vyhledání změn, serializace), vytvoření synchronizační tabulky, její procházení a vykonávání editací položek, přípravu položek pro exportní fázi (odeslání položek, či jejich odstraňění). Kroky synchronizačního jádra ukazuje obrázek 4.3.
4.7. SYNCHRONIZAČNÍ JÁDRO UUID e0be92ae... 2906f0f4... d86d2e79... c6d7c601...
RevId 892 910 915 923
6ace5e74...
987
content
{"name":"Karel Polášek","uuid": "c6d7c601-a11c-46fb-b96c733f9c6a9dd9"} {"name":"Jakub Nový","uuid": "6ace5e7497a1-4502-9b56-069caa33485c"}
29 attachment
karel.png
Tabulka 4.2: Načtení těl IMAP zpráv - výsledek
Obrázek 4.3: Stavový diagram jádra synchronizace
V následujících dvou podkapitolách se zaměříme na popis kroků v synchronizačním jádru. Ty jsou dva: vytvoření synchronizační tabulky a její následné procházení.
4.7.1
Vytvoření synchronizační tabulky
Základem synchronizační tabulky jsou data získaná z předchozí fáze import. Tento krok má za úkol přidat verze položek v lokálním adresáři a verze z databáze. Cílem je vytvoření struktury podobné tabulce 3.1. Místo pouhých čísel revizí budou však v této struktuře textové reprezentace verzí, toho je zapotřebí pro řešení kolizí v dalším kroku. Protože číslo RevId je lokálním položkám přidělováno až prostřednictvím IMAP protokolu při uložení na server, jsou lokální položky zastoupeny pouze textovou reprezentací verze. Hotovou synchronizační tabulku pro příklad z předchozí fáze ukazuje tabulka 4.3. Attachment je místo názvu souboru převeden na hash. UUID a content jsou pro účel této tabulky zkráceny. Inicializace seznamu položek z databáze Skončila-li fáze import po kroku 1 (získání UIDNEXT a EXISTS) bez vytvoření seznamu položek a není tudíž seznam položek inicializován, je nutné jej vytvořit nyní. Pokud seznam
30
KAPITOLA 4. EXTBRAIN SYNC PROTOKOL
row 1
remote UUID RevId content e0be 892
2
2906
910
3
d86d
915
4
c6d7
923
5
6ace
987
6
186a
Karel Polášek Jakub Nový
hash
1231
database RevId content 892 Pavel Stejný 910 Tomáš Změna 915 Jan Starý 914 Karel Poláczek
913
hash
1231
Josef Smazaný
local content hash Pavel Stejný Tomáš 7323 Změna
Karel Poláček
1231
Josef Smazaný
Tabulka 4.3: Synchronizační tabulka - hodnoty mají ilustrační charakter (UUID a content jsou zkráceny, hash je výsledek hashovací funkce zavolané na soubor v příloze)
nebyl vytvořen, znamená to, že se ve vzdáleném úložišti nevyskytují změny, a tak je možné vytvořit obraz tohoto seznamu lokálně pomocí výběru položek z databáze. Pro tento účel slouží atribut entryPresent, který je v databázi uložen pro každou položku. Při vytváření obrazu položek ve vzdáleném úložišti vybíráme z databáze právě ty položky, které mají atribut entryPresent nastaven na true. S tímto seznamem položek pak lze pracovat stejně jako by byl získán ze vzdáleného úložiště ve fázi import.
Přidávání verzí do tabulky Následující popis přidávání verzí do tabulky se vztahuje k tabulce 4.3. Při vytváření tabulky je nejprve nutné k položkám získaným ze serveru přidat verze z lokálního adresáře a z databáze (řádky 1, 2, 4). Za databázovou verzi je považována verze s nejvyšším RevId pro danou položku. Poté se přidávají všechny lokální položky, které ve struktuře z fáze import chybí - byly ze serveru odstraněny (řádek 6). Nakonec se porovnává stav lokálního adresáře s databází přes atribut entryPresent a přidávají se položky, které byly lokálně od poslední synchronizace smazány (hledají se takové, které mají v databázi entryPresent==true, ale v adresáři neexistují, řádek 3). Do řádku 5 nebyly přidány žádné údaje, položka není v lokálním adresáři ani v databázi zastoupena. Absence těla zprávy a hashe ze vzdáleného úložiště je v řádcích 1 až 3 dána tím, že nebyly stahovány celé zprávy, ale pouze hlavičky obsahující UUID a RevId. Pomocí parametrů UIDNEXT a EXISTS je zaručeno, že obsah zprávy není nutné stahovat ze serveru, protože jej lze jednodušším způsobem získat z databáze. Protokolem je zajištěno, že databáze pro tyto položky verze s danými RevId obsahuje, jak lze vidět ve sloupci database u těchto řádků.
4.7. SYNCHRONIZAČNÍ JÁDRO
4.7.2
31
Procházení synchronizační tabulky
V tomto kroku synchronizace dochází k průchodu synchronizační tabulkou, manipulaci s položkami a přípravou pro zapsání nových verzí do vzdáleného úložiště. Před vyhodnocováním změn v hodnotách jednotlivých verzí se podívejme na každý řádek z globálního pohledu a rozlišujme mezi situacemi, kdy položka v jednotlivých úložištích existuje, či nikoli. Situace, které mohou z tohoto úhlu pohledu nastat, lze popsat pomocí binárních řetězců. Máme tři možná úložiště, verze v daném úložišti je zastoupena (označme 1), nebo chybí (0). Z toho plyne, že nastává celkem 23 = 8 možných situací, které můžeme nejprve rozlišit. Seřadíme-li úložiště například v pořadí remote, database, local, lze situace označit pomocí binárních řetězců vzestupně od 000 po 111. Některé ze situací však nejsou „platné“. Například situace označená jako 000, kdy položka není v žádném z úložišť, nemůže nastat, neboť by se tak neměla jak dostat do synchronizační tabulky. Obdobně je tomu se situací označenou jako 010, kdy je položka zastoupena pouze v databázi. To by znamenalo, že položka existovala ve vzdáleném a lokálním úložišti a byla odtud odstraněna. (Při odstranění se však příznak položky entryPresent v databázi nastaví na false a položka se při dalším vytváření synchronizační tabulky ignoruje.) Všechny ostatní situace označené binárními řetězci jsou platné, nejkomplexnější je samozřejmě situace 111, kdy jsou verze dané položky zastoupeny ve všech úložištích. Touto situací, pro jejíž vyřešení potřebujeme znát již konkrétní hodnoty verzí se budeme zabývat později. Všechny situace, které mohou nastat jsou včetně popisu v tabulce 4.4. remote 0 0 0 0 1 1 1 1
database 0 0 1 1 0 0 1 1
local 0 1 0 1 0 1 0 1
popis situace neplatné nová lokální položka neplatné položka ve vzdáleném úložišti smazána nová položka ve vzdáleném úložišti účet odstraněn a vytvořen nový, položka ponechána položka v lokálním úložišti smazána nelze takto rozhodnout
Tabulka 4.4: Možné situace v synchronizační tabulce (1 označuje prezenci verze, 0 absenci, popis označuje, co se stalo od poslední synhronizace)
Popis algoritmu průchodu tabulkou Průchod tabulkou lze nejlépe popsat přímo pseudokódem, jeho zápis je v algoritmu 4.1. Z důvodů zjednodušení je v algoritmu vynecháno řešení specifického případu, který bychom mohli označit jako situace 101. Ta znamená, že položka chybí pouze v databázi. Toto se může při spoléhání na fakt, že neexistují dvě různé položky se stejným UUID, stát pouze v situaci, kdy jsou databázová data odstraněna bez odstranění položek v lokálním a vzdáleném úložišti.
32
KAPITOLA 4. EXTBRAIN SYNC PROTOKOL
Algoritmus je realizován jako for cyklus, v němž se prochází přes všechny položky v tabulce. Uvnitř for cyklu jsou nejprve umístěny 4 podmínky zjišťující prezenci a absenci položek v jednotlivých lokacích – vzdálené, lokální úložiště a databáze (řádky 2 až 17). Splňuje-li položka určitou podmínku, je vyvolána akce přidružená k dané situaci a pomocí continue se ukončí běh for cyklu pro tuto položku. Tato úvodní sada podmínek zahrnuje situace, kdy položka je na lokální nebo vzdálené straně nově vytvořena, případně odstraněna. Nesplňuje-li položka ani jednu z podmínek, znamená to, že v tabulce existují verze pro všechna úložiště. Manipulace s položkou už tedy nebude pouze v mezích „vytvoř novou“, případně „smaž“, bude se jednat o přepis verze položky. Pro další postup je nutné zjistit, zdali byla položka v lokálním adresáři změněna. K tomu slouží podmínka na porovnání databázové a lokální verze (od řádku 18 po 33). Tato podmínka se dále větví a je rozkreslena na obrázku 4.4.
Obrázek 4.4: Poslední krok průchodu synchronizační tabulkou (v algoritmu 4.1 řádky 18 až 33)
Neliší-li se databázová a lokální verze, položka změněna nebyla a můžeme tedy přijmout změnu. Pokud se liší (položka byla změněna), porovnají se ještě RevId z databáze a ze vzdáleného úložiště. Rovnají-li se RevId, bude lokální verze (změna) zapsána na server, nerovnají-li se, došlo k situaci, kdy byla změna položky provedena jednak v lokálním, tak ve vzdáleném úložišti a dochází ke kolizi. Při obsluze kolize je doporučeno zkontrolovat, zdali není lokální a vzdálená verze obsahem stejná. Více o kolizích a jejich řešení je v podkapitole Princip synchronizace.
4.7. SYNCHRONIZAČNÍ JÁDRO
33
Algoritmus 4.1 procházení synchronizační tabulky 1: for all item in syncTable do 000 neplatné 001: 2: if ( !item.remote && !item.database && item.local ) then 3: objectsToExport.push(item); ◁ položka vybrána pro export 4: continue; 5: end if 010 neplatné 011: 6: if ( !item.remote && item.database && item.local ) then 7: deleteLocal(item); ◁ smazat položku lokálně 8: continue; 9: end if 100: 10: if ( item.remote && !item.database && !item.local ) then 11: createLocal(item); ◁ vytvoření nové lokální položky 12: continue; 13: end if 101: vynechána 110: 14: if ( item.remote && item.database && !item.local ) then 15: objectsToDelete.push(item); ◁ položka vybrána pro smazání 16: continue; 17: end if 111: 18: if ( equals( item.local, item.database ) ) then ◁ verze se sobě rovnají 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34:
if ( item.remote.RevId > item.database.RevId ) then editLocalEntry(item); ◁ změň lokální položku continue; end if continue; ◁ všechny verze se rovnají, nedělej nic else ◁ verze se sobě nerovnají if ( item.remote.RevId == item.database.RevId ) then objectsToExport.push(item); ◁ položka vybrána pro export continue; end if if ( item.remote.RevId > item.database.RevId ) then handleCollision(item); continue; end if end if end for
◁ kolize
34
KAPITOLA 4. EXTBRAIN SYNC PROTOKOL
Všimněme si také řádku 23 a příkazu continue hned za předchozí podmínkou. K vykonání tohoto příkazu dojde tehdy, není-li položka lokálně ani vzdáleně změněna (remote.RevId == database.RevId) a není proto potřeba s ní nijak manipulovat. Za připomenutí stojí, že protokolem je zajištěno, že nedojde k situaci, kdy remote.RevId < database.RevId. Podmínky na tyto situace jsou tedy vypuštěny. Příklad průchodu konkrétní tabulkou Pro příklad z předchozích odstavců by průchod tabulkou 4.3 vypadal následovně: 1. řádek: UUID e0be má zastoupení ve všech lokalitách, ve všech ve stejné verzi, tudíž splní podmínku algoritmu průchodu tabulkou z řádku 18. I RevId se rovnají, proto se podle řádku 23 s touto položkou manipulovat nebude. 2. řádek: UUID 2906 má taktéž zastoupení ve všech lokalitách, lokální však obsahuje kromě těla položky i hash přílohy a tím se liší od verze v databázi. Tato položka tedy nesplní podmínku v řádku 18. RevId vzdálené a databázové verze se shodují, proto bude v řádku 26 vybrána pro export do vzdáleného úložiště. 3. řádek: UUID d86d má zastoupení pouze ve sloupci remote a database, v lokálním úložišti tedy neexistuje a to je známka toho, že odsud byla odstraněna. Po splnění podmínky v řádku 14 bude vybrána pro smazání ve vzdáleném úložišti. 4. řádek: UUID c6d7 je zastoupeno ve všech třech lokalitách. Všimněme si, že ale pokaždé v jiné verzi. Původní jméno „Karel Poláczek“ bylo lokálně upraveno na „Karel Poláček“, zatímco ve vzdáleném úložišti na „Karel Polášek“. Hashe příloh se neliší, příloha změněna nebyla. Položka tedy byla od poslední synchronizace lokálně změněna, navíc byla načtena v nové verzi ze vzdáleného úložiště, nesplní tedy porovnávací podmínku v řádku 18. RevId ze vzdáleného úložiště je vyšší než z databáze a proto nastává kolize. 5. řádek: UUID 6ace je obsaženo pouze ve sloupci remote, což znamená, že nebylo klientem ještě registrováno. Položka splní podmínku v řádku 10 a bude tak lokálně nově vytvořena. Zároveň s tím bude do databáze přidán tento záznam (hodnoty jsou opět v plném tvaru, content a UUID vzato z tabulky 4.2): UUID RevId content
: : :
hash entryPresent
: :
6ace5e74-97a1-4502-9b56-069caa33485c 987 {"name":"Jakub Nový","uuid": "6ace5e74-97a1-4502-9b56069caa33485c"} true
6. řádek: UUID 186a je zastoupeno pouze ve sloupcích local a database. Ze vzdáleného úložiště tedy musela být tato položka jiným klientem odstraněna. Položka splní podmínku v řádku 6 a bude lokálně odstraněna. Po odstranění se u databázové verze položky (RevId 913) nastaví atribut entryPresent na false.
4.7. SYNCHRONIZAČNÍ JÁDRO
35
Zápis verzí do databáze Při průchodu se provádějí také zápisy verzí do databáze, ale pouze u položek, jejichž lokální verze je přepisována verzí ze vzdáleného úložiště. U těchto verzí je totiž již znám atribut verze RevId. U položek, které byly změněny lokálně a budou exportovány se zápis do databáze provádí až po zapsání do vzdáleného úložiště – po přidělení RevId IMAP protokolem. Při vkládání nové verze do databáze se musí zabezpečit nastavení atributu entryPresent na false u předchozí verze. Taktéž musí být tento atribut změněn při odstranění položky. Příprava pro exportní fázi Vybrání položky pro exportní fázi procesu synchronizace je realizováno jako přidání do struktury objectsToExport případně objectsToDelete. Výstupem synchronizačního jádra jsou tedy tyto dva objekty. Jsou-li oba tyto objekty prázdné, synchronizace je ukončena a k fázi export se nepřistupuje. Ukázka výstupu pro náš příklad je v tabulkách 4.5 a 4.6. Všimněme si druhého řádku objectsToExport s položkou, která vyvolala kolizi. Uživatel ji vyřešil tak, že dal v atributu jméno přednost lokální verzi (Karel Poláček) a od položky odstranil přílohu. objectsToExport: remote UUID RevId content 2906 910 c6d7
923
Karel Polášek
hash
1231
database RevId content 910 Tomáš Změna 914 Karel Poláczek
hash
1231
local content hash Tomáš 7323 Změna Karel Poláček
Tabulka 4.5: Příklad struktury objektu objectsToExport
objectsToDelete: remote UUID RevId content d86d 915
hash
database RevId content 915 Jan Starý
hash
local content hash
Tabulka 4.6: Příklad struktury objektu objectsToDelete
36
4.8
KAPITOLA 4. EXTBRAIN SYNC PROTOKOL
Fáze export
Fáze export má za úkol využít protokol IMAP k zapsání nových verzí položek na vzdálené úložiště a odstranění (resp. přesunutí) starých, pokud je toho při synchronizaci zapotřebí. Využívá k tomu struktur přicházejících od synchronizačního jádra, těmi jsou verze položek srovnané v objektech objectsToExport a objectsToDelete. Protože IMAP protokol neumožňuje zprávy na serveru editovat, musí se nové verze položek na server uložit a staré verze přesunout do jiné složky. Přesouvat se budou nejen položky z objectsToDelete, ale i položky z objectsToExport, které obsahují záznam ve sloupci remote. V našem příkladu v tabulce 4.5 to jsou všechny položky. Je však nutné mít na paměti, že pokud by některá z položek byla v lokálním úložišti nově vytvořena, byla by na server pouze zapsána bez následného přesouvání předchozí verze, ta ostatně ani neexistuje. Pro uchovávání historie nejsou zprávy ze serveru trvale odstraňovány, jsou pouze přesunuty do podsložky „Deleted“. Pro další synchronizace však nejsou tyto verze potřebné a tak při nutnosti uvolnit místo na serveru mohou být z této složky kdykoli odstraněny. Fázi export lze rozdělit do dvou navazujících kroků: krok zápisu a krok přesouvání. V obrázku 4.5 označeno jako APPEND a MOVE. Je-li objectsToExport prázdný, přikračuje se rovnou ke kroku přesování.
Obrázek 4.5: Stavový diagram fáze export
4.8. FÁZE EXPORT
4.8.1
37
Krok zápisu
Zápis položky na server je realizován pomocí IMAP příkazu APPEND se dvěma povinnými parametry: složkou a obsahem zprávy (hlavička, tělo a příloha). K tělu a příloze položky tedy musí být vygenerována standardní IMAP hlavička rozšířená o UUID záznam. Ukázka této rozšířené hlavičky je v podkapitole Vzdálené úložiště. Do těla IMAP zprávy je vložena serializovaná podoba těla položky, případná příloha je ve formátu base64. Jako tělo položky je vždy brána lokální verze, to je ostatně přesně účelem tohoto kroku, exportovat lokální verze. Ukládání zpráv probíhá postupně po jedné položce procházením přes pole objectsToExport. Ve stavovém diagramu je to naznačeno cyklem kolem stavu APPEND. Odpověď serveru na příkaz APPEND obsahuje UID nově přidané zprávy. UID je od této chvíle RevId této verze položky a je spolu s tělem položky a hashem přílohy zapsáno do databáze. Příklad Komunikace mezi klientem a serverem během zápisu jedné IMAP zprávy je ukázána v následujícím výpisu. V něm se zapisuje položka z druhého řádku objectsToExport (tabulka 4.5). Řádek 1 obsahuje příkaz klienta, číslo 817 označuje délku zprávy ve znacích. Následuje hlavička zprávy (řádky 2 až 9). Na řádcích 15 a 16 je vidět serializované tělo položky (tentokrát opět v nezkráceném tvaru, je uveden celý JSON včetně UUID položky), přílohu položka neobsahuje, proto je vynechána. Poslední řádek ukazuje standardní odpověď klienta na takový příkaz. Za APPENDUID je nejdříve uvedeno UIDVALIDITY a pak UID zprávy. Toto UID se stane novým RevId této položky. 1 2 3 4 5 6 7 8 9
a006 APPEND extbrainContacts {817} From: Archimedes Thunderbird testProfile Date: Fri, 13 Apr 2012 18:56:37 GMT Subject: =?UTF-8?Q?Karel Pol=C3=A1=C4=8Dek?= To: Imap Test uuid: 18c34b9c-d4fb-4ff3-b4d4-3d7aaf64c522 Message-Id: MIME-Version: 1.0 Content-Type: multipart/mixed; boundary=a5a5fd7524ac42f5a61b434e568f7deb
10 11 12 13
--a5a5fd7524ac42f5a61b434e568f7deb Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable
14 15 16 17 18
{"name":"Karel Pol=C3=A1=C4=8Dek","uuid":"c6d7c601-a11c-46fb-b96c-73=\r\n 3f9c6a9dd9"} --a5a5fd7524ac42f5a61b434e568f7deb-a006 OK [APPENDUID 660858846 988] (Success)
Verzi položky bylo tedy přiděleno RevId s hodnotou 988, ihned po zapsání zprávy do vzdáleného úložiště je do databáze přidán záznam o nové verzi položky.
38
KAPITOLA 4. EXTBRAIN SYNC PROTOKOL UUID RevId content
: : :
hash entryPresent
: :
18c34b9c-d4fb-4ff3-b4d4-3d7aaf64c522 988 {"name":"Karel Poláček", "uuid": "18c34b9c-d4fb-4ff3-b4d4-3d7aaf64c522"} true
Zároveň je u předchozí verze položky (RevId 914 viz tabulka 4.3) nastaven atribut entryPresent na false.
4.8.2
Krok přesouvání
K přesunutí jsou určeny remote verze položek ze struktury objectsToDelete a objectsToExport. Položky v objectsToExport neobsahující remote verzi nejsou určeny k přesouvání. Protože IMAP protokol neobsahuje příkaz pro přesunutí zprávy (MOVE), ale pouze COPY pro zkopírování, přesunutí je realizováno jako kopírování zprávy do cílové složky a její následné odstranění ze složky původní. Pro odstraňování zpráv je v protokolu vyčleněn příkaz EXPUNGE, který jednorázově odstraní všechny zprávy ve složce označené příznakem \Deleted, přičemž příznaky se zprávám přidávají pomocí příkazu STORE. Přesouvání je tedy realizováno operacemi v tomto sledu: 1. zkopírování přesouvaných zpráv do podsložky „Deleted“, 2. nastavení příznaku \Deleted k originálům zpráv, 3. trvalé odstranění zpráv s tímto příznakem. První dvě operace jsou realizovány postupně po jedné zprávě, voláním příkazů UID COPY resp. UID STORE. UID COPY vyžaduje jako parametr sekvenci zpráv a název složky, do které se mají zprávy zkopírovat. UID STORE sekvenci zpráv a příznak, který má být zprávě přidán. Po každém volání UID STORE se pro dané číslo RevId v databázi změní hodnota atributu entryPresent na false. Po nastavení příznaku ∖Deleted poslední zprávě je zavolán příkaz EXPUNGE na vyčištění složky. Tak jsou zkopírované zprávy odstraněny. Tím je dosaženo stavu, kdy si obsah složky vzdáleného úložiště s lokální složkou odpovídají, synchronizace je tím ukončena. Příklad Příkazy volané ve fázi export pro náš příklad ukazuje následující výpis komunikace klientserver. Pro přehlednost jsou volání klienta zvýrazněna. Z tabulek struktur objectsToExport a objectsToDelete je vidět, že zprávy k přesunutí jsou celkem 3 (RevId 915 z objectsToDelete, 910 a 923 z objectsToExport). Příkazy UID COPY a UID STORE jsou tudíž volány každý třikrát. V odpovědi na příkaz UID COPY jsou vidět parametry zprávy nově přidané do podsložky, v odpovědi na UID STORE aktuální stav příznaků u zprávy a počet zbývajících zpráv ve složce (pro ExtBrain Sync není důležité).
4.9. PARALELNÍ PŘÍSTUP
1 2
39
A004 UID COPY 915:915 extbrainContacts/Deleted A004 OK [COPYUID 660858847 915 43] (Success)
3 4 5
A005 UID COPY 910:910 extbrainContacts/Deleted A005 OK [COPYUID 660858847 910 44] (Success)
6 7 8
A006 UID COPY 923:923 extbrainContacts/Deleted A006 OK [COPYUID 660858847 923 45] (Success)
9 10 11 12 13 14
A007 UID STORE 915:915 +FLAGS (\Deleted) * 6 FETCH (FLAGS (\Seen \Deleted) UID 915) * 6 EXPUNGE * 5 EXIST A007 OK Success
15 16 17 18
A008 UID STORE 910:910 +FLAGS (\Deleted) ... A008 OK Success
19 20 21 22
A009 UID STORE 923:923 +FLAGS (\Deleted) ... A009 OK Success
23 24 25
A010 EXPUNGE A010 OK Success
4.9
Paralelní přístup
Protokol je navržen tak, že podporuje současný přístup pouze jedné klientské aplikace. Během synchronizačního procesu nastávají chvíle, kdy jsou data ve vzdáleném úložišti v nekonzistentním stavu (typicky mezi uložením nové verze položky a odstraněním staré). Pokud by v tuto chvíli jiný klient načítal ze vzdáleného úložiště, budou jím získané údaje neúplné. Pro zamezení současného přístupu více klientů najednou jsou v protokolu synchronizační zámky.
4.9.1
Zamykání vzdáleného úložiště
Klient se před zahájením importní fáze synchronizace pokusí zamknout nad vzdáleným úložištěm zámek. Jako zámky slouží IMAP zprávy ukládané do podsložky „lock“. Při pokusu o zamčení úložiště klient do této složky vloží zprávu, které je přiděleno serverem jednoznačné UID. Žádá-li o zamčení více klientů najednou, jejich zprávy se do složky zařadí postupně jako do fronty. Pokračovat v synchronizaci však bude pouze jeden z nich; ten, jenož zpráva reprezentující zámek byla do složky vložena nejdříve. Klientská aplikace tedy ověří, zda je jím odeslaná zpráva na prvním místě. To je implementováno jako získání nejstarší zprávy ve složce. Shoduje-li se UID této zprávy a zprávy,
40
KAPITOLA 4. EXTBRAIN SYNC PROTOKOL
kterou klientská aplikace na server odeslala, úložiště je úspěšně uzamčeno pro danou aplikaci a ta může pokračovat v synchronizaci. Pokud se tato UID neshodují, složka byla uzamčena jiným klientem a aktuální klient svůj zámek ze složky odstraní. Stejně tak i klient, který pro svoji potřebu úložiště úspěšně zamkl, po dokončení synchronizace odemkne úložiště odstraněním svého zámku. Stavový diagram zamykání vzdáleného úložiště je na obrázku 4.6.
Obrázek 4.6: Stavový diagram zamykání vzdáleného úložiště
Kapitola 5
Realizace 5.1
Obecný synchronizační modul
Obecný synchronizační modul informací je implementován několika třídami, prostřední vrstvu tvoří třída CommonSync. Diagram tříd synchronizačního modulu je na obrázku 5.1. Třídy ItemSync a RemoteStorageHandler jsou abstraktní. V class diagramu ukazují, jaké metody musí mít jejich konkrétní implementace, aby s nimi dokázala CommonSync komunikovat. Konkrétními implementacemi jsou ContactSync (rozšíření modulu o synchronizaci kontaktů), ImapSync a ImapSyncController (realizace spodní vrstvy architektury pro podporu IMAP úložiště). Pomocí atributů this.itemsAPI a this.communicationAPI jsou v CommonSync uchovány reference na instance těchto konkrétních tříd. Struktura položek a synchronizační tabulky byla uvedena již v definici protokolu, není tedy třeba ji zde znovu uvádět.
5.1.1
Průběh volání v jednotlivých fázích synchronizace
Sekvenční diagram celého procesu synchronizace je na obrázku B.2 v příloze této práce. V následujících odstavcích je rozebrán flow programu po jednotlivých fázích.
Fáze import První fáze synchronizace je celá realizována pomocí vrstvy remote storage. V případě vzdáleného úložiště IMAP konkrétně objektem třídy ImapSync a ImapSyncController. Objekt commonSync volá this.communicationAPI.import(this) pro zahájení importu. Metodě je v parametru předáno scope, aby po sérii asynchronních voláních v IMAP knihovně dokázal vrátit běh synchronizace k druhé fázi – jádru synchronizace. Po načtení položek ze vzdáleného úložiště tedy obsluha IMAP knihovny (ImapSyncController) vrací běh programu pomocí scope.callMakeSyncTable(incommingItems) zpět do třídy CommonSync. Sekvenční diagram volání při importní fázi je na obrázku 5.2.
41
42
KAPITOLA 5. REALIZACE
Obrázek 5.1: Class diagram synchronizačního modulu
Fáze synchronizační jádro První krok synchronizačního jádra – vytvoření synchronizační tabulky – obstarává horní vrstva synchronizace, volá se proto this.itemsAPI.makeSyncTable(incommingItems). Třída implementující toto items API musí zajistit vytvoření synchronizační tabulky dle definice v kapitole ExtBrain Sync Protokol. Volání v horní vrstvě nejsou asynchronní, proto může být synchronizační tabulka předána zpět do CommonSync přes návratovou hodnotu. Průchod synchronizační tabulkou je implementován v metodě handleSyncTable třídy CommonSync. Zde jsou uvnitř cyklu implementovány podmínky pro nastalé situace přesně podle definice v protokolu. Obsluhu situací podporují metody initialItemHandling, createItem, deleteItem, acceptChange a handleCollision, (tyto nejsou pro zjednodušení uvedeny v class diagramu). Uvnitř těchto metod totiž neprobíhá fyzická manipulace s položkami (třída Common sync toto ani neumí), pouze se zde volají příslušné metody z items API, probíhá update verzí v databázi, případně se položky vkládají do objectsToExport a objectsToDelete. Fáze export Exportní fáze je opět celá v režii vrstvy remote storage. Třídě implementující tuto vrstvu jsou předány objekty objectsToExport, objectsToDelete a scope, aby po uložení položek na server mohla ohlásit konec synchronizačního procesu do třídy CommonSync pomocí metody terminate s parametrem true – synchronizace se zdařila, nebo false – během exportní fáze se vyskytla chyba. Volání při fázi export jsou podrobně rozkreslena v obrázku 5.3. Všimněme si, že v diagramu není vůbec zachycen objekt třídy ContactSync, který se na této fázi nepodílí.
5.2. DATABÁZOVÉ ÚLOŽIŠTĚ
43
Obrázek 5.2: Sekvenční diagram fáze import
Obrázek 5.3: Sekvenční diagram fáze export
5.2
Databázové úložiště
Pro ukládání databázových verzí položek slouží databázový stroj SQLite, který je součástí jádra Mozilla Thunderbird [13]. Informace o účtech jsou dekomponovány do dvou tabulek. Tabulka loginInfo obsahuje obecné informace o vzdáleném úložišti, tabulka account zahrnuje informace o konkrétních účtech namapovaných na složku ve vzdáleném úložišti a na lokální úložiště. Ke konkrétnímu účtu se pak váží řádky v tabulce sync, která obsahuje informace o verzích položek. Primárním klíčem této tabulky je dvojice . Pro jeden účet totiž nesmí existovat více položek se stejným číslem revize. Relační schéma databáze je na obrázku 5.4. Heslo k účtu na serveru není uloženo v databázi, v Mozilla Application Frameworku existuje pro hesla speciální podpora skrze password manager. Odtud se heslo získává při každé synchronizaci. Lokální adresář je v případě synchronizace kontaktů uložen jako URI objektu nsIAbDirectory – kontejneru pro objekty kontaktů. Při změne názvu adresáře zůstává URI stejná.
44
KAPITOLA 5. REALIZACE
Obrázek 5.4: Relační databázový model
Nejčastějším dotazem nad databází je při synchronizaci získání poslední verze položky ve tvaru: SELECT * FROM sync WHERE itemId = ’abc’ AND accountUID = ’def’ ORDER BY revId DESC LIMIT 1 Proto je nad tabulkou sync vytvořen index přes sloupce . Chování databázového stroje SQLite při zpracovávání tohoto dotazu bylo ověřeno nad stovkami řádků a index uchovávající revId v sestupném řazení vyšel z možných variant jako nejvýhodnější.
5.3
Obsluha IMAP úložiště
Obecný synchronizační modul je navržen tak, aby nezávisel na vrstvě obsluhující vzdálené úložiště. V této práci je vzdáleným úložištěm IMAP server, takže implementace této vrstvy je spojovníkem mezi common sync vrstvou a IMAP knihovnou. Třída ImapSync přebírá od CommonSync parametry (účet, položky k zápisu) a předává je do třídy ImapSyncController, která zajišťuje obsluhu IMAP klienta uvnitř knihovny. ImapSync obsahuje metody lock, import a export, všechny slouží pouze k vyvolání příslušných částí obsluhy IMAP klienta. ImapSyncController obsahuje řadu atributů (vnořených objektů), které vyvolávají akce v klientovi. Pomocí callbacků, které jsou součástí atributů se běh aplikace vrátí zpět do místa, odkud se příkaz klientovi posílal.
5.3.1
Posloupnost IMAP příkazů
Požadovaného sledu příkazů při importní a exportní fázi je dosaženo přes sekvenci callbacků. Pro obecné použití je vytvořena initialSequence, která obsahuje navázání spojení se serverem a příkaz LOGIN, a disconnectSequence obsahující příkazy EXAMINE a LOGOUT. Díky příkazu EXAMINE ještě před odpojením je možné modifikovat záznam o účtu v databázi tak,
5.3. OBSLUHA IMAP ÚLOŽIŠTĚ
45
aby zde byly aktuální údaje o počtu existujících položek ve vzdáleném úložišti (EXISTING) a NEXTUID. Dále jsou pak vytvořeny importSequence a exportSequence. Sled příkazů v jednotlivých sekvencích je následující. initialSequence importSequence exportSequence disconnectSequence
connect, LOGIN SELECT, UID FETCH (hlavičky zpráv), UID FETCH (celé zprávy) APPEND, UID COPY, UID STORE, EXPUNGE EXAMINE, LOGOUT
Sekvenční diagramy znázorňující postup volání příkazů do IMAP knihovny v initial, import a export sekvenci jsou na obrázcích B.3, B.4 a B.5 v příloze této práce. Všechna volání jsou asynchronní, proto v diagramech nezachycuji životnost objektu. Metodám uvnitř IMAP knihovny je znázorněno předání prostředí přes scope.
5.3.2
Struktura obsluhy volání IMAP příkazu
Struktura objektu pro volání konkrétního příkazu vypadá následovně. V metodě command se vždy zavolá příkaz v IMAP klientu s patřičnými parametry (zde název složky, která má být vybrána), metody okay a fail jsou callbacky, které jsou zavolány z klienta po příchodu a vyhodnocení odpovědi od serveru. Všimněme si přiřazení scope.current = this; v řádku 3. V případě, že se vybrání složky podaří, běh aplikace se vrátí do callbacku okay a rozhoduje se, kam se bude běh programu odebírat dál (popsáno v dalším odstavci), v případě neúspěchu se vrátí do callbacku fail. ImapSyncController se tak odpojí od serveru a zavolá ukončení synchronizace (řádky 19 a 20). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
select : { command : function(scope) { scope.current = this; scope.client.select(scope.account.folderStr); }, okay : function(scope, response) { if (response.nextUID == scope.account.nextUID && response.existingItems == scope.account.existingItems ) { scope.runDisconnect(); var incommingItems = {changed: "false", items: new Object()}; scope.syncScope.callMakeSyncTable(scope.syncScope, incommingItems); } else { scope.importSequence.fetchUuids.command(scope); } }, fail : function(scope, response) { printOutput(’Select failed’); scope.runDisconnect(); scope.syncScope.termination(false); } }
46
KAPITOLA 5. REALIZACE
Při fázi import je volána nejprve úvodní sekvence příkazů (initialSequence), na níž naváže importSequence. Z importSequence lze po každém příkazu vystoupit (a ukončit fázi import), pakliže není nutné načítat další podrobnosti o zprávách. Toto je implementováno dle obrázku 4.2. Ukončení importSequence před vykonáním celého sledu příkazů je realizováno pomocí podmínek v okay callbacích jednotlivých příkazů. Opuštění fáze import Předčasné ukončení sekvence příkazů je ilustrováno také v předchozí ukázce objektu select. V okay callbacku na řádku 7 je podmínka na rovnost parametrů UIDNEXT a EXISTING z odpovědi serveru (aktuální) a z databáze (předchozí synchronizace). V případě, že se tyto parametry rovnají, ukončíme běh importSequence. IMAPSyncController má přes atribut syncScope k dispozici metody z CommonSync, takže vystoupení ze sledu příkazů se děje zavoláním scope.syncScope.callMakeSyncTable (řádek 11). V parametrech volání je uvedeno scope.syncScope. To proto, aby instance CommonSync měla opět k dispozici všechny svoje atributy. Druhým parametrem je nově vytvořený objekt incommingItems, který obsahuje atribut changed nastavený na false. Pomocí incommingItems se do obecného jádra vrací seznam načtených položek. Atribut changed slouží k rozlišení situace, kdy ve vzdáleném úložišti nebyly vykonány změny (incommingItems.items je prázdný, protože položky nebyly získávány) a kdy byly vykonány změny a incommingItems.items je prázdný proto, že byly všechny položky smazány. Obsahuje-li přijímaná položka přílohu, je tato uložena do profilového adresáře do podsložky „ExtBrainSync“ a odkaz na tento soubor je předán společně s obsahem položky dále do synchronizačního jádra. Export fáze Při fázi export sled sekvencí příkazů závisí na obsahu polí objectsToExport a objectsToDelete. Pro zjednodušení obsluh volání příkazů se položky, jejichž verze mají být zapsány, a pro které zároveň mají být předchozí verze přesunuty, kopírují z objectsToExport do objectsToDelete ve třídě IMAPSync v metodě export. Jsou-li obě pole prázdná, exportní fáze je ukončena bez volání obsluhy IMAP klienta. Je-li objectsToExport neprázdné pole, je vyvolána nejprve initialSequence, poté exportSequence. Mezi sekvencemi se přechází pomocí konstruktů v callbacích podobných těm, které jsou popsány v předchozím odstavci. Sled příkazů je přesnou implementací stavového diagramu v obrázku 4.5. Po úspěšném vykonání všech potřebných IMAP příkazů se běh programu vrací zpět do instance třídy CommonSync zavoláním scope.syncScope.termination(true).
5.3.3
Zamykání vzdáleného úložiště
Zamykání vzdáleného úložiště před provedením synchronizace je také v režii tříd IMAPSync a IMAPSyncController. Volání požadované sekvence příkazů (APPEND zámku a následný FETCH) je provedeno pomocí sekvencí s callbacky stejně jako je tomu ve fázích import a export. Pokud se během synchronizace vyskytne závažná chyba, například se ztratí připojení k serveru, zámek není možné z úložiště odstranit a to tak zůstává trvale zamčené. Proto pokud
5.4. SYNCHRONIZAČNÍ MODUL KONTAKTŮ
47
při další synchronizaci klient zjistí, že je zamčeno, nabídne uživateli možnost výběru. Buď může tento fakt přijmout a přerušit synchronizaci, nebo, pokud například víme, že zámek ve složce zůstal díky chybě, zamčení obejít a pokračovat v synchronizaci dále. V obou případech aplikace před ukončením ze složky odstraní pouze svůj zámek. Zámek obsahuje informace o datu, času a v předmětu název počítače a profilu v Thunderbirdu, ze kterého byl odeslán. „Zaseknutý“ zámek může uživatel odstranit v e-mailovém klientu jednoduše odstraněním zprávy se zámkem.
5.3.4
IMAP knihovna
V IMAP knihovně implementované Petrem Machem bylo provedeno několik změn. Do klienta byly přidány příkazy EXAMINE, UID FETCH, UID COPY, UID STORE a EXPUNGE. Třída odesílající data do socketu MozExtbrainToolkit byla upravena tak, aby podporovala i znaky mimo ASCII. Dále byla rozšířena třída IMAPParser tak, aby dokázala převádět a parsovat příchozí data zakódovaná v quoted printable formátu.
5.4 5.4.1
Synchronizační modul kontaktů Implementace
Rozšiřující modul k synchronizaci kontaktů je implementován třídou ContactSynchronization, která tvoří items layer. Tato třída obsahuje metody pro manipulaci s položkami makeSyncTable, initialItemsHandling, createItem, deleteItem, acceptChange, handleCollision a další 2 metody na serializaci a deserializaci položek – serialize, deserialize. Třída je implementací rozhraní ItemSync. Stejně je to zachyceno i v class diagramu na obrázku 5.1. Za zvláštní zmínku stojí metoda initialItemsHandling, jejíž význam není možná zcela zřejmý. Metoda je volána v situaci, kdy byla vytvořena nová lokální položka. Během serializace je položce přiřazeno UUID, tento identifikátor je ale nutné uložit přímo do objektu položky, abychom ji rozlišili při další synchronizaci. Toto zpětné uložení už ale serializace nezajišťuje a proto musíme před exportem nových položek serializovaná data včetně nově vytvořeného identifikátoru deserializovat zpět do objektu položky.
5.4.2
Definice formátu obsahu přenášených zpráv
Těla přenášených zpráv při synchronizaci kontaktů mají podobu textové reprezentace JSON objektů [14]. Pro bezchybnou synchronizaci je nutné definovat přesný tvar objektů v tomto kontejneru. JSON obsahuje objekty ve tvaru , kde element může mít tři různé typy: string, pole objektů, objekt. Je-li element typu objekt, skládá se ze seznamu subatributů ve tvaru <subatribut : string>. Kontaktní synchronizační modul zná atributy uvedené v tabulce 5.1. Kromě uvedených subatributů obsahuje každý element typu objekt v JSONu ještě pomocné subatributy sync a primary. Primary je použito v OS Android pro vyznačení primární položky daného atributu, je typu boolean. Existuje-li například pro jeden
48
KAPITOLA 5. REALIZACE
kontakt více telefonních čísel, můžeme zvolit, které bude upřednostňováno. Subatribut sync má formát UUID a slouží jako identifikátor elementu. Elementy mohou být do JSONu skládány v různém pořadí a právě sync slouží k tomu, abychom při synchronizaci mohli srovnat odpovídající si elementy k sobě a zjistit jejich změny. Při řešení kolizí je pomocí subatributu sync možné identifikovat elementy a odlišit tak situaci, kdy byl jeden element změněn od situace, kdy byl vytvořen nový. (V prvním případě má řešením kolize být změna elementu, ve druhé zřetězení těchto elementů.) Ukázka, jak vypadá kontakt se všemi podporovanými atributy v JSON podobě je v tabulce B.1. V tabulce 5.1 jsou uvedené i podporované řetězce v subatributech type. Tato množina je omezená, jedná se o řetězce nativně podporované v OS Android. Je-li jako typ elementu zvoleno „custom“ znamená to, že v elementu je obsažen subatribut „label“, který obsahuje uživatelem zadaný vlastní typ elementu. Takto lze vytvořit například událost typu svátek: event": [{"type":"custom", "label": "svátek", "value": "0000-11-04", "sync": "ccb515f3-2d80-498c-9ae6-b256e9ee93e8", "primary": "false"}
5.4.3
Serializace a deserializace kontaktů
Přeměna položek z objektů nsIAbCard do podoby JSON se provádí v metodě exportContactProperties procházením přes všechny defaultní a přidané atributy kontaktů. Kromě přidaných atributů přímo přístupných uživateli skrze grafické uživatelské rozhraní je ale i několik atributů „skrytých“. Synchronizace totiž musí pasivně podporovat i atributy, které Thunderbird ani s nově přidanými atributy nezná. Představit si to lze například na údajích o společnosti, kde osoba v kontaktu pracuje. Tyto údaje jsou v Thunderbirdu a v OS Android shrnuty jen na tyto tři: název společnosti, oddělení, funkce. Některá jiná platforma, která bude v budoucnu do synchronizace zapojena, ale může tyto informace rozšiřovat například o číslo kanceláře apod. O takovéto informace nesmíme během synchronizace přijít, jak by se to stávalo pouhým uložením „známých“ atributů do objektu nsIAbCard a jejich pozdějším vytažení zpět do JSONu. I tyto pro synchronizační modul neznámé atributy musejí být uloženy společně s ostatními tak, abychom je při dalším exportu z objektu, dokázali složit do původní podoby. Uživatel k těmto atributům logicky má přístup jen v platformě, která tyto typy „aktivně“ podporuje, tzv. zobrazuje jejich hodnoty v GUI a umožňuje jejich editaci. Při každém importu (zápisu obsahu JSONu do objektu nsIAbCard) se do speciálních properties uloží v textové podobě i všechny elementy z JSONu. Při dalším exportu (serializaci) se tyto původní elementy vytáhnou a provede se sloučení původních elementů s nově exportovanými atributy. Tím je zajištěno, že v nově vyexportovaných elementech nebudou chybět i subatributy, které nejsou podporovány. Toto slučování probíhá s využitím sync subatributů v každém elementu.
5.4.4
Chyba v jádru Thunderbirdu
Během vývoje byla objevena a reportována chyba v jádru aplikace Thunderbird. Je-li ke kontaktu přiřazena fotografie, vytvoří se její kopie v profilovém adresáři aplikace. Po odstranění kontaktu ale soubor s fotografií v adresáři zůstává a zbytečně tak zabírá místo na
5.4. SYNCHRONIZAČNÍ MODUL KONTAKTŮ atribut uuid displayName name
typ string string object
phone
array
email
array
urls
array
event
array
organization
array
im
array
subatribut
givenName familyName displayName type
value type value type value type value company department title type type value protocol
sipAddress
array
addresses
array
nickname
array
type value type streetAddress neighborhood city region postalCode country type
array array
value photoName note
photo note
typ možnosti
home, mobile, work, fax_work, fax_home, pager, other, callback, car, company_main, isdn, main, other_fax, radio, telex, tty_ttd, work mobile, work_pager, assistant, mms, custom home, work, mobile, other, custom home, work, mobile, other, custom birthday, anniversary, custom
work, other, custom home, work, other, custom aim, msn, yahoo, skype, qq, googleTalk, icq, jabber, netmeeting, custom home, work, other, custom home, work, other, custom
default, other_name, maiden_name, short_name, initials, custom
Tabulka 5.1: JSON atributy
49
50
KAPITOLA 5. REALIZACE
disku. Fotografie, které jsou přenášeny v rámci synchronizace jsou uloženy také v profilovém adresáři aplikace ve složce „ExtBrainSync“. Aby nedocházelo k plýtvání místem, je v možnostech zásuvného modulu přidáno tlačítko na vyčištění této složky, které odstraní fotografie navázané na již neexistující kontakty. Při změně fotografie u kontaktu je případná stará verze z toho adresáře odstraněna ihned.
5.4.5
Řešení kolizních situací
Kolizní situace jsou v případě kontaktů řešeny zobrazením okna shrnujícího všechny verze kontaktu. V tomto okně uživatel vybere podobu atributů tak, jak je chce zahrnout do výsledné položky této kolize. Při zobrazování atributů je aplikován algoritmus trojcestného slučování (viz. jeho popis v podkapitole Princip synchronizace). Algoritmus prochází přes hodnoty v atributech položky, zvýrazňuje shodné verze a na základě těchto shod předvybírá hodnoty do výsledné verze. Samotný výběr hodnot je ale na uživateli. Zeleným zvýrazněním je ukázáno, že kolize v atributu byla vyřešena, červeně pak situace, kdy jsou všechny tři verze rozdílné a verzi položky musí zvolit uživatel.
5.5 5.5.1
Grafické uživatelské rozhraní Rozšíření atributů kontaktu
Okno pro editaci kontaktů se skládá z několika karet obsahujících vždy ovládací prvky k editaci podobných typů položek. V tomto okně je nově karta s názvem „Instant messaging“ obsahující informace o IM a SIP účtech k danému kontaktu. V případě přidání typů k atributům je před textbox pro zadání hodnoty atributu vytvořen menulist, což je vstupní prvek typu „roleta“, ve kterém uživatel vybere z předem definovaných řetězců typů. Zde je místo pro možný další rozvoj aplikace. U typu atributu „custom“ je skutečný typ jako uživatelem zadaný vlastní řetězec uložen v atributu „label“. Tento atribut není v této verzi synchronizačního modulu uživateli přístupný. Pro jeho zpřístupnění je nutný větší zásah do GUI okna pro editaci kontaktů (je možné přeuspořádání ovládacích prvků, zobrazení vyskakovacího okna pro zadání konkrétné hodnoty atd.). Ukládání položek po uzavření editačního okna se i v jádru Thunderbirdu děje průchodem přes předem definované pole známých atributů a uložení jejich hodnot do objektu nsIAbCard. Uložení přidaných atributů je provedeno stejně. Názvy přidaných atributů jsou uloženy v souboru attributes.js. Při tvorbě grafického rozhraní byla použita příručka jazyka XUL z [15]. Okno pro editaci kontaktů je ukázáno na obrázku 5.5.
5.5.2
Wizard na tvorbu účtů
Před samotnou synchronizací je nutné vytvořit účet se všemi potřebnými údaji o serveru, lokální a vzdálené složce. Protože je důležité mít jistotu, že bylo zadáno správné uživatelské jméno a heslo k serveru, zda tento server existuje atd., byl tento účel vytvořen průvodce vytvořením účtu. Průvodce je spuštěn pravým kliknutím na složku kontaktů v adresáři a
5.5. GRAFICKÉ UŽIVATELSKÉ ROZHRANÍ
51
Obrázek 5.5: Okno pro editaci kontaktů s rozšířenými atributy
zvolením „Add ExtBrain Sync account“. Průvodci pak uživatel zadává přihlašovací údaje k serveru, tyto informace jsou uloženy v databázi v tabulce loginInfo, a tak je při vytváření více účtů namapovaných na složky na jednom serveru nemusí zadávat opakovaně. Průvodce přidáním účtu se pak pokusí k serveru přihlásit a získat odtud seznam všech složek. Z nich uživatel vybírá, nebo může zadat název nové složku, která je po potvrzení vytvořena (včetně podsložky „Deleted“). Tím je ošetřeno, že složka v nově zadaném účtu skutečně existuje a účet má platná přihlašovací data.
5.5.3
Obsluha synchronizace
Kontextové menu na složku kontaktů v adresáři má dvě různé podoby. Je-li na složku zaregistrován synchronizační účet, zobrazují se volby „Start sync“ a „Remove ExtBrain Sync account“, není-li složka v seznamu účtů, zobrazuje se „Add ExtBrain Sync account“. Pro spolehlivou synchronizaci je nutné zajistit, aby složka, na níž je namapován synchronizační účet existovala. Již bylo zmíněno, že v databázi je lokální složka uložena jako URI nsIAbDirectory, změna názvu složky tedy synchronizaci nerozbije. Při odstranění složky se odstraní účet i všechna historická data o synchronizacích tohoto účtu. Synchronizace složky kontaktů se spouští z kontextového menu příkazem „Start sync“ (viz obrázek C.3). Po jeho vyvolání se zobrazí okno syncLog.xul (viz obrázek 5.6) obsahující informace o složce a serveru, kam se lokální adresář sychronizuje, je zde i místo pro výpis aktuálně prováděných operací včetně případných chyb. Při vyvolání synchronizace se pro lepší user experience (např. při dlouhých prodlevách při načítání zpráv) v tomto okně deaktivuje tlačítko „OK“, které je aktivováno po skončení synchronizace, nebo při závažné chybě vedoucí k nemožnosti synchronizaci dokončit.
52
KAPITOLA 5. REALIZACE
Obrázek 5.6: Okno informující o průběhu synchronizace
Kapitola 6
Testování V průběhu vývoje bylo autorem této práce prováděno funkční testování. Na počátku vývoje byla funkčnost serializace a deserializace rozšířených kontaktních atributů ověřována přes modul pro synchronizaci kontaktů přes složku na lokálním disku. Tento modul byl posléze pro komplexnost synchronizačních procesů z aplikace odstraněn. Funkčnost obecného synchronizačního modulu (knihovny) byla ověřena přes modul k synchronizaci kontaktů. Ten lze testovat jednak posíláním dat z několika profilů aplikace Mozilla Thunderbird a proti ExtBrain synchronizační aplikaci pro operační systém Android, kterou vyvinul v rámci diplomové práce Ing. Luboš Hilgert [16]. Po dokončení vývoje byl synchronizační modul testován vedoucím práce a okruhem testerů sestávajího se z bývalých vývojářů projektu ExtBrain. Testování synchronizačního modulu bylo prováděno v aplikaci Mozilla Thunderbird ve verzích 10, 11 a 12. Přestože zásuvný modul závisí pouze na hostitelské aplikaci a měl by se v různých operačních systémech chovat stejně, byl otestován v operačních systémech Microsoft Windows XP, Vista a 7 a v Linux Ubuntu 11. Synchronizace byla ověřena pro následující IMAP servery: ∙ imap.feld.cvut.cz, ∙ imap.gmail.com, ∙ imap.aol.com, ∙ imap.mail.yahoo.com. Některé servery bohužel nejsou implementované podle RFC 3501 a tak například nepodporují vícenásobné vnořování složek (imap.centrum.cz, imap.seznam.cz) nebo před název uživatelem vytvořené složky přidávají vlastní řetězec (imap.seznam.cz). Podpora pro tyto servery je tedy omezená. Funkčnost závisí na tom, zdali se podaří vytvořit synchronizační složku a její podsložku s názvem „Deleted“. Charakter vytvořené práce se během vývoje změnil ze samostatné aplikace spíše ke knihovní aplikaci, která je v současné době portována do většího projektu ExtBrain Communicator kolegou Bc. Davidem Jirovcem. Po dohodě s vedoucím práce tedy nebyly prováděny
53
54
KAPITOLA 6. TESTOVÁNÍ
speciální testy zaměřující se na uživatelské rozhraní synchronizačního modulu. Funkčnost modulu je nyní ověřována již během testů při vývoji výše zmiňované aplikace. Uživatelské rozhraní, které je v aplikaci obsaženo slouží k vytváření účtů a k demonstraci funkčnosti synchronizace.
Kapitola 7
Závěr Výsledkem této práce je synchronizační modul v aplikaci Mozilla Thunderbird, který je možné využít pro synchronizaci jakýchkoli textových informací včetně příloh v binárním tvaru. Synchronizační modul je obecný a je nezávislý na vzdáleném úložišti, konkrétně je implementována podpora pro vzdálené úložiště v podobě IMAP serveru. Funkčnost tohoto modulu je ukázána na vytvořeném zásuvném modulu pro synchronizaci kontaktů ve stejné hostitelské aplikaci. V práci byl popsán ExtBrain Sync protokol, podle kterého se celá synchronizace řídí. Synchronizační aplikaci podle stejného protokolu implementoval pro OS Android ve své diplomové práci Ing. Luboš Hilgert. Díky těmto dvěma modulům je tedy možné synchronizovat kontaktní informace z Thunderbirdu s telefonem. Na práci navazuje projekt Bc. Davida Jirovce s názvem ExtBrain Communicator, která modul přebírá a implementuje synchronizaci kalendářových dat a poznámek. Práce naplnila všechny požadavky, po dohodě s vedoucím došlo k jediné úpravě v zadání a to k ustoupení od rozsáhlejších testů uživatelského rozhraní z důvodu integrace modulu do většího projektu, které bude mít svoje vlastní uživatelské rozhraní.
7.1
Možná další rozšíření
Na projekt je možné kdykoli navázat implementací modulů pro synchronizaci dalších typů položek. Dále je možné zabývat se vylepšeními speciálně v těchto ohledech:
Grafické uživatelské rozhraní pro editaci kontaktů V okně pro editaci kontaktů je staticky zobrazeno několik polí pro zadávání hodnot konkrétních atributů. Ovládací prvky v tomto okně by mohly být přeuspořádány tak, aby se zobrazovaly vždy jen zadané hodnoty. Další řádek pro přidávání hodnot by se zobrazil kliknutím na tlačítko „+“. Synchronizační modul je na toto připraven, počet hodnot pro jeden atribut není omezen. Dále je zde možnost vytvořit podporu zadávání vlastních štítků pro atributy, jako je tomu v OS Android. (Např. možnost přidat typ adresy „přechodné bydliště“.)
55
56
KAPITOLA 7. ZÁVĚR
Automatická synchronizace pomocí IMAP push Pro IMAP protokol existuje rozšíření s názvem push-email, který zajišťuje odeslání zpráv z IMAP serveru na klienta ihned po přijetí zprávy serverem, bez nutnosti opakovaného dotazování klientem na stav serveru. Tuto technologii by bylo možné přidat do synchronizačního modulu tak, aby se informace ihned po změně a uložení na server replikovaly i na další připojené klienty. S touto technologií pracuje synchronizační standard SyncML.
Literatura [1] NOVOTNÝ, Tomáš. ExtBrain – to simplify everyday tasks [online]. [cit. 18. 11. 2011]. Dostupné z: http://extbrain.felk.cvut.cz [2] Zimbra Email and Collaboration Server [online]. 2012 [cit. 5. 11. 2011]. Dostupné z: http://www.zimbra.com/products/email-server.html [3] KHANNA, Sanjeev, Keshav KUNAL, a Benjamin PIERCE: A Formal Investigation of Diff3. [online]. [2007] [cit. 14. 1. 2012]. Dostupné z: http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf [4] CRISPIN, M. RFC 3501 - INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1 [online]. 2003 [cit. 10. 12. 2011]. Dostupné z: http://www.faqs.org/rfcs/rfc3501.html [5] CROCKER, David H. RFC 822 - STANDARD FOR THE FORMAT OF ARPA INTERNET TEXT MESSAGES [online]. August 13, 1982 [cit. 10. 12. 2011]. Dostupné z: http://www.ietf.org/rfc/rfc0822.txt c 2011 [6] PETERKA, Jiří. RFC 822. Jiří Peterka: archiv článků a přednášek [online]. ○ [cit. 14. 1. 2012]. Dostupné z: http://www.earchiv.cz/a94/a435c110.php3 [7] BOSSWELL, David, Brian KING, Ian OESCHGER aj. Creating Applications with Mozilla. First edition. Sebastopol, CA, USA: O’Reilly Media, 2002. [8] NYMAN, Robert. How to develop a Firefox extension [online]. 2009 [cit. 7. 10. 2011]. Dostupné z: http://blog.mozilla.org/addons/2009/01/28/ how-to-develop-a-firefox-extension/ c 2005-2012. [cit. 5. 3. 2012]. [9] Mozilla. Mozilla Developer Network [online]. ○ Dostupné z: https://developer.mozilla.org/en/ [10] ContactsContract.CommonDataKinds. Android Developers [online]. 2012 [cit. 14. 1. 2012]. Dostupné z: http://developer.android.com/reference/android/provider/ ContactsContract.CommonDataKinds.html [11] SMARR, Joseph. Portable Contacts 1.0 Draft C [online]. August 5, 2008 [cit. 12. 2. 2012]. Dostupné z: http://portablecontacts.net/draft-spec.html
57
58
LITERATURA
[12] Introducing JSON. [online] [cit. 12. 2. 2012]. Dostupné z: http://www.json.org [13] SQLite - Documentation [online]. [cit. 12. 2. 2012]. Dostupné z: http://www.sqlite.org/docs.html [14] CROCKFORD, Douglas. The application/json Media Type for JavaScript Object Notation (JSON) [online]. July 2006 [cit. 12. 2. 2012]. Dostupné z: http://www.ietf.org/rfc/rfc4627 [15] Mozilla Developer Network. XUL reference [online]. 26 April 2010 [cit. 5. 3. 2012]. Dostupné z: https://developer.mozilla.org/en/XUL_Reference [16] HILGERT, Luboš. Modul pro správu informací pro Android. Praha, 2012. Diplomová práce. České vysoké učení technické v Praze.
Příloha A
Seznam použitých zkratek API Application Programming Interface DOM Document Object Model IM Instant Messaging IMAP Internet Message Access Protocol JSON JavaScript Object Notation OS Operating System SIP Session Initiation Protocol TH Thunderbird URI Uniform Resource Identifier UUID Universally Unique IDentifier XUL XML User Interface Language
59
60
PŘÍLOHA A. SEZNAM POUŽITÝCH ZKRATEK
Příloha B
Obrázky a diagramy
Obrázek B.1: Návrh okna pro editaci kontaktů
61
62
PŘÍLOHA B. OBRÁZKY A DIAGRAMY
Obrázek B.2: Sekvenční diagram všech fází synchronizace
63
Obrázek B.3: Volání IMAP příkazů v initialSequence
64
PŘÍLOHA B. OBRÁZKY A DIAGRAMY
Obrázek B.4: Volání IMAP příkazů v importSequence
65
Obrázek B.5: Volání IMAP příkazů v exportSequence
66
PŘÍLOHA B. OBRÁZKY A DIAGRAMY
{"displayName": "Karel Poláček", "uuid": "456af2dd-0783-44f6-9984-2d9d09baf41b", "name": {"familyName": "Poláček", "givenName": "Karel", "displayName": "Karel Poláček", "sync": "7497ada0-ffa2-42b9-be96-38fba9d56d88", "primary": "false"}, "event": [{"sync": "ccb515f3-2d80-498c-9ae6-b256e9ee93e8", "type": "birthday", "value": "1893-03-22", "primary": "false"}], "email": [{"sync": "9fee2628-76f5-43f4-b7d7-b11bbe9c01cf", "type": "home", "value": "[email protected]", "primary": "false"}], "phone": [{"sync": "9c258986-ef2f-4ef2-a80d-401107267ab8", "type": "work", "primary": "false"}],
"value":
"222-323-983",
"urls": [{"sync": "752921fb-ced8-4563-95b0-bd91c7dcf3d9", "type": "profile", "value": "http://cs.wikipedia.org/wiki/Karel_Polacek", "primary": "false"}], "im": [{"sync": "adccda29-cf68-4712-bae3-3749ab2dea99", "value": "[email protected]", "protocol": "googleTalk", "type": "home", "primary": "false"}], "sipAddress": [{"sync": "0d7211cd-5be2-4c0f-ad53-ec3586f20ce4", [email protected]", "type": "home", "primary": "false"}],
"value":
"ka-
"addresses": [{"sync": "4dafb46f-8ba4-4852-b945-ec938f166595", "region": "Praha", "streetAddress": "Koněvova 343", "postalCode": "11022", "primary": "false", "neighborhood": "Žižkov", "type": "home", "city": "Praha", "country": "Česká republika"}], "organization": [{"sync": "03800363-8964-44c9-b419-bc29cd4787f2", "title": "sloupkař", "type": "work", "department": "denní tisk", "primary": "false", "company": "Lidové noviny"}], "note": [{"sync": "99972fd1-7e27-4308-9375-f51b76f42a00", "note": "Má 4 bratry: Arnošta, Kamila, Ludvíka a Zdeňka", "primary": "false"}], "nickname": [{"sync": "f42e1dbc-73a3-4dac-9eff-716f445c8178", "value": "Polič", "type": "default", "primary": "false"}], "photo": [{"sync": "1d3ff9db-3b4e-4c9b-ac85-9c96fe07667d", "photoName": "karel.png", "primary": "true"}]} Tabulka B.1: Kontakt v JSON podobě se všemi podporovanými atributy
Příloha C
Instalační příručka Pro správnou činnost doplňku je doporučováno používat Mozilla Thunderbird ve verzi 11.0.1 (v době psaní této příručky nejnovější verze). Zásuvný modul se instaluje ze souboru ExtBrainSync.xpi, který najdeme na přiloženém CD.
C.1
Instalace
1. Otevření Nástroje -> Správce doplňků v aplikaci Mozilla Thunderbird. 2. Přetažení (drag&drop) instalačního souboru ExtBrainSync.xpi do tohoto správce. 3. Potvrzení instalace. 4. Restart aplikace Thunderbird.
C.2
Vytvoření účtu
1. Kliknutím pravým tlačítkem na složku kontaktů v adresáři otevřete kontextové menu a zvolte „Add ExtBrain Sync accout“, otevře se průvodce vytvořením účtu. 2. Klikněte na „Create new“. Zadejte přihlašovací údaje k IMAP serveru, jeho adresu a typ zabezpečení (SSL nebo TLS) a potvrďte kliknutím na „Další“. 3. Vyberte složku, kam chcete kontakty synchronizovat, nebo zadejte název nové složky. 4. Účet se vytvoří kliknutím na „Další“. V případě, že jste již zadali přihlašovací údaje k IMAP serveru, nemusíte je při vytváření účtu k jiné složce zadávat znovu, ale ve druhém kroku lze vybrat ze seznamu již registrovaných serverů (viz obrázek C.1). Seznam všech vytvořených účtů můžete zkontrolovat v okně „Možnosti ExtBrain Sync“. Toto okno se otevírá ve Správci doplňků. V možnostech najdete také tlačítko na vyčištění složky s fotografiemi kontaktů.
67
68
PŘÍLOHA C. INSTALAČNÍ PŘÍRUČKA
Obrázek C.1: Úvodní okno průvodce pro vytváření účtů
Obrázek C.2: Okno se seznamem složek na serveru
C.3. SPUŠTĚNÍ SYNCHRONIZACE
C.3
69
Spuštění synchronizace
Synchronizace se spouští kliknutím na „Start sync“ v kontextovém menu složky kontaktů. Po spuštění se otevře okno s informacemi o synchronizovaném účtu a probíhajících operacích. Po dokončení synchronizace zavřete toto okno tlačítkem „OK“.
Obrázek C.3: Spuštění synchronizace
70
PŘÍLOHA C. INSTALAČNÍ PŘÍRUČKA
Příloha D
Obsah přiloženého CD ExtBrainSync text ExtBrainSync.xpi readme.txt
projektový adresář se zdrojovými soubory adresář s textem této práce (zdrojové kódy LATEXu i pdf) instalační soubor zásuvného modulu instalační informace
71