====== Calendari e contatti su Android, senza Google ====== Su uno smartphone Android è possibile avere la rubrica dei contatti e dei calendari con gli eventi senza passare per i servizi Google? Sembra di sì, anche se è tutt'altro che semplice! In pratica è necessario installare un server che fornisca il servizio tramite i protocolli DAV, nel nostro caso abbiamo utilizzato **DAViCal** su Debian. Sul terminale Android si deve installare un client opportuno, la nostra scelta è caduta su **DAVdroid**. Infine è necessario adottare diversi accorgimenti per poter **esportare** i calendari e i contatti esistenti e verificare che la **sincronizzazione** col server remoto avvenga correttamente. ===== Installazione del server DAViCal ===== In Debian 9 Stretch esiste il pacchetto **DAViCal** versione 1.1.5, per convenienza esiste anche il **[[https://packages.debian.org/stretch-backports/|backport]]** della **versione 1.1.7**, che abbiamo preferito. Per soddisfare le dipendenze è necessario prendere dai backports anche il pacchetto **libawl-php** versione **0.59**. È necessario un **database di supporto**, nel nostro caso abbiamo utilizzato **PostgreSQL**. La procedura di installazione è abbastanza //sui generis//, in quanto richiede che venga predisposto l'accesso al database senza password (per fortuna è possibile revocarlo al termine dell'installazione). Vedere gli appunti nel paragrafo **[[https://www.rigacci.org/wiki/doku.php/doc/appunti/linux/sa/davical?s[]=caldav#installazione_del_server_davical|Installazione del server DAViCal]]**. In **Apache 2.4** abbiamo definito un **VirtualHost** solo su HTTPS. Chi fa accesso usando HTTP viene rediretto per evitare che trasmetta password in chiaro sulla rete. Questa la configurazione da aggiungere: DirectoryIndex index.php AllowOverride None Require all granted ServerName dav.server.org DocumentRoot /var/www/html/default # Redirect everything to https, except /.well-known/ directory. RedirectMatch permanent ^/((?!\.well-known).*)$ https://dav.server.org/$1 SSLEngine on ServerName dav.server.org SSLCertificateFile /etc/letsencrypt/live/dav.server.org/fullchain.pem SSLCertificateKeyFile /etc/letsencrypt/live/dav.server.org/privkey.pem DocumentRoot /var/www/html/default ServerAdmin webmaster@server.org ErrorLog ${APACHE_LOG_DIR}/dav.server.org/error.log CustomLog ${APACHE_LOG_DIR}/dav.server.org/access.log combined Alias /dav /usr/share/davical/htdocs RewriteEngine On # PT is important if you are using an alias, it implies L # Redirect /.well-known URLs RewriteRule ^/\.well-known/(.*)$ /dav/caldav.php/.well-known/$1 [NC,PT] # Optionally: redirect /principals/users/ as well RewriteRule ^/principals/users/(.*)$ /dav/caldav.php/$1 [NC,PT] RewriteRule ^/principals/resources/(.*)$ /dav/caldav.php/$1 [NC,PT] RewriteRule ^/calendars/__uids__/(.*)$ /dav/caldav.php/$1 [NC,PT] RewriteRule ^/addressbooks/__uids__/(.*)$ /dav/caldav.php/$1 [NC,PT] # Redirect / requests to web login page RedirectMatch permanent ^/$ https://dav.server.org/dav/ L'URL base del server sarà **%%https://dav.server.org/dav/%%** e sarà accessibile sia con il protocollo WebDAV che con il protocollo HTTPS. L'accesso in HTTPS presenta un'interfaccia minimale di amministrazione web: sarà possibile creare utenti (//principals//), creare address book e calendar (//collections//), ma per le normali operazioni di gestione contatti ed eventi si dovrà utilizzare un client dedicato. Nel nostro caso il client è rappresentato dalla app **DAVdroid** installata su uno smartphone Android, che darà accesso ai contatti e agli eventi di calendario a tutte le app relative. ===== Creazione di un utente con address book e calendar ===== L'amministratore del server DAViCal si collega all'URL di gestione **%%https://dav.server.org/dav/%%**, quindi accede alla pagina **User Functions** => **Create Principal**. I parametri da inserire sono: ^ Username | È prassi abbastanza diffusa utilizzare l'indirizzo di posta elettronica come nome utente. | ^ Password | Immettere una password. | ^ Fullname | Normalmente si inserisce nome e cognome. Gli altri utenti del server DAViCal potranno vedere questa informazione. | ^ Email Address | Informazione di base richiesta per la creazione di un account, di solito coincide con lo Username. Anche questa informazione sarà pubblica per tutti gli utenti del server DAViCal. | ^ Principal Type | Selezionare **Person** per la creazione di un utente. | Al termine dell'operazione sarà stato attivato un utente DAViCal; automaticamente sarà stato creato anche un **address book** (CardDAV) e un **calendario** (CalDAV) a lui assegnati. Se il nome di login è //%%user@server.org%%//, gli URL dei rispettivi oggetti saranno del tipo: ^ Address book | %%https://dav.server.org/dav/caldav.php/user@server.org/addresses/%% | ^ Calendar | %%https://dav.server.org/dav/caldav.php/user@server.org/calendar/%% | In generale non sarà necessario ricordare il percorso completo dei due oggetti, con l'**autodiscover** dovrebbe essere sempre sufficiente indicare solo l'**URL base del server** e gli oggetti disponibili dovrebbero essere automaticamente mostrati. ===== Installazione di DAVx5 ===== Esistono diverse app per Android per la **sincronizzazione** di **contatti** ed **eventi** calendario verso server **DAV**. Purtroppo si tratta spesso di app che offrono solo una delle due funzionalità e spesso le versioni free hanno limitazioni o problemi (ad esempio continui alert se la connessione con il server fallisce). Pare invece che l'app **[[https://play.google.com/store/apps/details?id=at.bitfire.davdroid|DAVx5]]** (prima conosciuto come **DAVdroid**) abbia tutte le carte in regola per essere scelta: si tratta di **software open source** disponibile anche su **[[https://f-droid.org/en/packages/at.bitfire.davdroid/|F-Droid]]** e sembra sufficientemente stabile e maturo. {{ .:davdroid:davdroid-account.png?direct&180|DAVx5 Account}} Una volta scaricata e installata l'app, si deve **creare un nuovo account** sullo smartphone. Nel nostro caso si procede con l'opzione **Login with URL and user name**. Con l'installazione di DAViCal vista in precedenza si inserisce qualcosa del genere: ^ Base URL | %%https://dav.server.org/dav/%% | ^ User name | %%email@server.org%% | ^ Password | %%******%% | Lasciare attiva l'opzione predefinita **Groups are separate VCards**. Il server DAViCal support il formato [[wpit>VCard|vCard]] v.3.0 e quindi DAVx5 gestirà i gruppi come vCard separate (i gruppi sono usati ad esempio per creare le etichette da assegnare ai contatti). Vedere il paragrafo //Contact group method// nella [[https://www.davdroid.com/manual/accounts/|documentazione sugli Account]]. DAVx5 si collega al server e tramite l'**autodiscovery** sul percorso **%%/.well-known/%%** si accorge che esistono già due //collection// collegate all'account: un **addressbook** e un **calendar**. Per impostazione predefinita infatti DAViCal crea queste due collection per ogni //principal// di tipo //person// (cioè per ogni account) che viene creato. Nella schermata che segue (vedi figura) bisogna attivare la sincronizzazione per queste due //collection//. Questo è ciò che compare nello smartphone: * Strumenti => **Utenti e account** * Compare un nuovo account di tipo **DAVx5**, identificato con l'indirizzo email. Da questo account dipendono tutti i calendari e le rubriche che verranno sincronizzate col server. * Compare anche un nuovo account di tipo **Rubrica DAVx5**, identificato con il //Displayname// definito da DAViCal e l'indirizzo email più due lettere casuali. Questo account //figlio// è necessario per una **limitazione di Android**; la rubrica contatti infatti deve essere direttamente collegata ad uno specifico account, quindi l'account **DAVx5** (padre) deve esporre un account **Rubrica DAVx5** (figlio) per ogni rubrica contatti disponibile sul server. Questo non accade per i calendari: il singolo account padre è sufficiente per accederli tutti. Maggiori dettagli nella pagina **[[https://www.davdroid.com/manual/accounts/|Accounts del manuale di DAVdroid]]**. ==== Problema di sincronizzazione ==== Il sistema Android usa **tecniche aggressive per il risparmi di batteria**, questo affligge soprattutto le app non di sistema tipo DAVdroid. In pratica accade che **viene inibita la sincronizzazione** con il server: quando l'app è in background non viene mai svegliata. Avviando l'app DAVx5 si viene avvisati di questo potenziale problema da tre pop-up: * Battery ... ? * Automatic synchronization * OpenTasks non installata Ci si può accorgere che qualcosa non va dal menu **Strumenti** => //Utenti e account// => //DAVdroid// => **Sincronizzazione account**. Sebbene l'opzione sincronizzazione sia attiva e si scelga l'azione **Sincronizza ora** dal menu in alto a destra, **l'orario di sincronizzazione non si aggiorna**! Nel nostro caso Android 8.1.0 Oreo su Xiaomi Mi A1, è stato necessario ... FIXME ===== Creazione di un calendario aggiuntivo ===== {{ .:davdroid:davdroid-multi-calendar.png?direct&200|DAVdroid: calendari}} In genere la via più comoda per aggiungere un calendario è utilizzare il client CalDAV preferito. Anche DAVdroid ha questa funzione agendo dalla app **DAVdroid**, //Account// => //CalDAV// => //Hamburger menu// => **Crea nuovo calendario**. Il calendario può essere di tipo **Calendario** oppure **Elenco attività** o meglio ancora la combinazione dei due. Tale impostazione viene registrata nel database nella colonna **property_value** della tabella **property**. Le attività (**task** in inglese), a differenza degli eventi di calendario, di solito sono associati ad una data senza l'orario e sono pensati per potersi sovrapporre gli uni con gli altri (molte attività vengono portate avanti in parallelo. L'app calendario stock di Android non consente di visualizzare i task. In alternativa l'utente (anche non amministratore) può effettuare login alla **pagina di amministrazione web** di DAViCal, quindi sceglie l'opzione //User Functions// => //View My Details// => //Principal Collections// => **Create Collection**. Nella pagina di creazione si deve attivare l'opzione **Is a Calendar**. Il calendario creato in questo modo sarà di tipo combinato //Calendario// ed //Elenco attività//. Per attivare il nuovo calendario nello smartphone si accede alla app DAVdroid, dopo aver selezionato l'account opportuno, si effettua tap sull'Hamburger menu di CalDAV, quindi si sceglie l'azione **Aggiorna lista calendari**. Per poter disporre del nuovo calendario sullo smartphone è necessario: * Dalla app **DAVdroid** si sceglie l'account relativo, quidni dall'hamburger menu si sceglie l'azione **Aggiorna lista calendari**. * Dalla app **Sistema** => //Utenti e account// => //Account DAVdroid// => //Sincronizzazione account// => //Menu// => **Sincronizza ora**. * Dalla app **Calendario** si inserisce un **nuovo evento**, che generalmente provoca la **sincronizzazione** con il server. ===== Esportazione dei contatti da Google ===== Per fortuna è possibile esportare in formato aperto tutti i contatti che abbiamo associato al nostro account Google. È sufficiente autenticarsi su Gmail all'url [[https://mail.google.com/]], quindi aprire l'url **[[https://contacts.google.com/]]**. Aprendo il menù **Di più** è possibile scegliere l'opzione **Esporta**. Nel dialog-box è possibile indicare **Esporta come vCard**. Al termine sul PC avremo il file **contacts.vcf** che deve essere trasferito sullo smartphone, ad esempio con il tool **adb push** e il cavetto USB. L'importazione va fatta dall'app **Contatti**, noi abbiamo usato quella stock di Android Oreo 8.1.0. Anzitutto dal menu **Impostazioni** abbiamo indicato come **Account predefinito per i nuovi contatti** quello aggiunto con DAVdroid, quindi, sempre da //Impostazioni//, si è scelto la voce **Importa** => **File .vcf**. **Non ha funzionato** l'import delle **foto associate** ai contatti, infatti Google le include nel formato vCard come link ad un indirizzo web esterno (eventualmente da scaricare a parte), non come allegato JPEG. L'app Contatti può mostrare i contatti provenienti dall'account Google, dall'account DAVdroid, oppure da tutti e due; dall'hamburger menu vi è un menu a tendina per la scelta. **Fare attenzione** a quando si rimuove un account, perché **non è chiaro se lo stiamo rimuovendo** anche dall'account Google e/o DAVdroid. Per sicurezza conviene rimuovere gli account Google facendo accesso con il browser all'url [[https://contacts.google.com/]]. ===== Esportazione degli eventi calendario da Google ===== Per l'esportazione è necessario fare accesso con un PC e un browser alla pagina **[[https://calendar.google.com/]]**, ovviamente dopo aver effettuato il login ad esempio alla casella [[https://mail.google.com/|Gmail]]. Dall'icona **ingranaggio** si sceglie **Impostazioni** => //Importazione ed esportazione// => **Esporta**. Viene fatto scaricare un file zip che contiene un **file .ics** per ogni calendario esistente nell'account Google. Nei file .ics esportati è opportuno modificare il tag **X-WR-CALNAME**, impostando la descrizione breve che identificherà il calendario nell'interfaccia di DAVdroid. È possibile caricare ogni singolo file .ics in un calendario DAViCal dall'interfaccia web. Dopo aver fatto login come utente proprietario del calendario, si clicca **View My Details** => //Principal Collections// => //ID// => **Load From File**. L'opzione //Append// consente di aggiungere gli eventi a quelli già esistenti, altrimenti il file caricato sostituisce del tutto il contenuto esistente. Per altri dettagli vedere la pagina [[https://wiki.davical.org/index.php/How_can_I_migrate_to_DAViCal_using_already_generated_.ics_files%3F|How can I migrate to DAViCal using already generated .ics files?]]. ===== Impostazione del colore predefinito di un calendario ===== Dall'interfaccia di amministrazione web di DAViCal non è possibile impostare il colore predefinito di un calendario. Né la app calendario di AOSP stock (Android Open Source Project) né DAVdroid consentono di impostare il colore di un calendario esistente. È possibile tuttavia **agire direttamente sul database** di DAViCal per impostare il colore predefinito. Anzitutto verificate nella **app DAVdroid**, aprendo l'account interessato, che nelle **Impostazioni** (icona ingranaggio) l'opzione **Cambia il colore del calendario** sia attiva; in questo modo DAVdroid gestirà i colori dei calendari forniti dal server. Anche l'opzione **Supporto colore dell'evento** può essere attivata; in questo modo ogni singolo evento potrà essere colorato in modo diverso rispetto al calendario di appartenenza (funzione [[https://www.davdroid.com/faq/setting-event-colors-crash/|con alcuni problemi]]: non funziona con la app Calendario stock, manda in crash l'app calendario **Etar**, ecc.). Dopo essersi **collegati al database** come amministratore, si cerca l'**utente proprietario** del calendario: davical=# SELECT principal_id, user_no, displayname FROM principal; principal_id | user_no | displayname --------------+---------+----------------------- 1 | 1 | DAViCal Administrator 1001 | 1001 | Niccolo Rigacci quindi si cerca il **dav_name** del calendario desiderato: davical=# SELECT user_no, dav_name, is_calendar FROM collection WHERE user_no = 1001; user_no | dav_name | is_calendar ---------+---------------------------------+------------- 1001 | /niccolo@rigacci.org/addresses/ | f 1001 | /niccolo@rigacci.org/calendar/ | t 1001 | /niccolo@rigacci.org/famiglia/ | t Infine si aggiunge una riga alla **tabella property**: INSERT INTO property (dav_name, property_name, property_value, changed_by) VALUES ('/niccolo@rigacci.org/famiglia/', 'http://apple.com/ns/ical/:calendar-color', '#F8BE14', 1); Sullo smartphone è necessaria una sincronizzazione per vedere l'effetto: * Dalla app **DAVx5** => Tap su account => CalDAV => [[wp>Hamburger button]] => **Refresh calendar list**. * **Chiudere** l'app **Calendario**. * Da **Impostazioni** => //Utenti e account// => //DAVx5// => //Sincronizzazione account// => **Sincronizza ora**. ===== Disattivazione App Calendario Google ===== A margine di questa esperienza vorrei segnalare un comportamento **poco evidente e rischioso** della **app Calendario di Google**. Su uno smartphone con CyanogenMod v.12.1 (basato on Android 5.1 Lollipop) era stata installata la app **[[https://f-droid.org/en/packages/ws.xsoh.etar/|Etar]]**; poiché il **Calendario di Google** non veniva più utilizzato, la app era stata **disattivata** dal menu //Impostazioni// => //App//. La app Etar e le altre app di tipo calendario continuavano a mostrare gli eventi di calendario come associati all'account Google, permettendo anche di inserirne di nuovi. In realtà la sincronizzazione con l'account Google non funzionava più: tutti gli eventi erano salvati solo in locale sul device Android e non erano presenti sui server Google. Il tutto accadeva in modo silente, senza alcun messaggio di errore o di warning. Due circostanze potevano evidenziare il problema: * Gli eventi aggiunti dalla app Android **non comparivano nel calendario web** [[https://calendar.google.com/]] e viceversa. * Dal menu //Impostazioni// => //Account// => //Google// => **Sincronizzazione** era sparita la voce Calendario. Per effettuare la corretta esportazione dei calendari è stato necessario riattivare la App Google e forzare la sincronizzazione. ===== Creazione di un calendario di gruppo ===== Scenario: si vuole creare un calendario e condividerlo con due o più utenti. === Creazione di un Principal di tipo Group === Il //principal// di tipo //group// avrà il suo login e la sua password, ma le credenziali non verranno condivise con gli utenti che avranno accesso al calendario. Il calendario (in effetti una //collection//) sarà creato come appartenente a questo gruppo. * User Functions => Create Principal * Username: caldavtest@domain.tld * Password: %%******%% * Fullname: Gruppo per calendario condiviso * Email Address: caldavtest@domain.tld * Principal Type: Group === Aggiunta degli User al Group === FIXME Serve davvero? Oppure è sufficiente creare il Ticket (vedi avanti)? * User Functions => List Groups => (click su ID) * Group Members => (select the user) => Add * Group Members => (select the user) => Add === Creazione di un Calendar Collection nel Group === * User Functions => List Groups => (click su ID) * Principal Collections => Create Collection * DAV Path: /dav/caldav.php/caldavtest@domain.tld/calendar/ * Displayname: Calendario condiviso * Is a Calendar: [x] * Default Privileges: [x] * alendar Timezone: Europe/Rome * Collection Grants: * (select the user): ALL (FIXME Serve davvero? O basta il ticket?) * (select the user): ALL (FIXME Serve davvero? O basta il ticket?) L'URL completo del calendario è **%%https://dav.domain.tld/dav/caldav.php/caldavtest@domain.tld/calendar/%%**, ma non si userà questo per l'accesso. La risorsa verrà ricollocata (**bind**) come risorsa di ciascun utente. === Creazione di un Access Ticket per la Collection === * User Functions => List Groups => (click su ID) * Access Tickets * Ticket ID: Nfe5wq0Y * Target: /caldavtest@domain.tld/calendar/ * Expires: 2029-12-29 * Privileges: ALL === Binding della Calendar Collection allo User === Con questa operazione, il calendario condiviso (che ha URL **%%/caldavtest@domain.tld/calendar/%%**) verrà mappato come risorsa dell'utente (con URL **%%/user1@domain.tld/boundcalendar/%%**): * User Functions => List Users => (click su ID) * Bindings to other collections * ID Bounds As: /user1@domain.tld/boundcalendar/ * Display Name: Calendario condiviso * To Collection: /caldavtest@domain.tld/calendar/ * Ticket ID: Nfe5wq0Y L'URL completo del calendario diventa quindi **%%https://dav.domain.tld/dav/caldav.php/user1@domain.tld/boundcalendar/%%**. In generale non si dovrà indicare l'URL per esteso, perché il client (es DAVx5 su Android) elencherà il nuovo calendario nell'elenco di quelli disponibili, con l'etichetta impostata in //Displayname//. ===== Aggiornamento del database di backend ===== Dopo l'aggiornamento da Debian 9 Stretch a **Debian 10 Buster**, quando ci si collega all'interfaccia web di Davical (nella nostra installazione è qualcosa del tipo **%%https://dav.host.tld/dav/%%**), si legge il seguente warning: Database schema needs upgrading. Current: 1.3.2, Desired: 1.3.3 Si è deciso di agire da riga di comando. Anzitutto si crea un file **/etc/davical/administration.yml** con le credenziali per accedere al database (proteggere il file con **mode 600**): admin_db_user: davical_dba admin_db_pass: MySecret admin_db_host: 127.0.0.1 Quindi si esegue lo script **/usr/share/davical/dba/update-davical-database** che provvede ad applicare tutte le patch incrementali allo schema del database. Ovviamente si consiglia di fare un backup del database **prima** di eseguire la procedura.