Mon CV

Synchroniser plusieurs fichiers ics (iCal) avec des calendriers Google Agenda

31 octobre 2013

Il y a quelques temps je vous ai expliqué la stratégie que j’ai mise en place pour synchroniser un fichier ICS (iCal) avec un calendrier sous Google Agenda. La méthode est simple, elle s’appuie sur un script Perl (auquel j’ai apporté depuis quelques petites modifications) et des modules que vous pouvez installer via CPAN.

Maintenons compliquons le problème avec plusieurs calendriers à synchroniser. La méthode la plus simple est de programmer dans notre Crontab (au via LaunchDaemon sur OS X) autant de tâches que de calendriers à synchroniser, en passant lançant une commande par calendrier comme indiqué mon article précédent.

J’ai préféré de mon côté mettre en place un script Bash “parent” qui se chargera de lire un fichier de configuration spécifique contenant la liste de calendriers à synchroniser. L’avantage est d’avoir un fichier texte avec votre configuration que vous pourrez modifier à tout moment. J’en ai profité pour fiabiliser un peu le processus en ajoutant quelques tests et en utilisant également un autre sous-script chargé de nettoyer le fichier ICS des rappels (alarmes) et des tâches pour que cela soit plus propre dans Google Calendar.

 

Le principe

Le principe que j’ai retenu est simple. Je rassemble mes scripts dans un dossier /usr/local/bin/kerio-ics-to-gcal.

A l’intérieur je vais déposer un script “parent” en Bash : ics-sync.sh. Ce script va lire un fichier ics-sync-script.conf dans le dossier /etc. Ce dernier fichier de configuration va contenir une ligne par calendrier à synchroniser avec :

  • l’URL du calendrier à synchroniser,
  • le nom du fichier temporaire local qui sera créé,
  • la référence au compte Google Calendar (telle que stockée dans votre fichier ~./netrc),
  • le nom du Calendrier Google Agenda avec lequel synchroniser.

Notre script ics-sync.sh va effectuer, pour chaque ligne de ce fichier de configuration, les actions suivantes :

  • quelques tests pour vérifier que l’URL du fichier ICS à synchroniser est joignable et qu’elle renvoie un fichier ICS valide,
  • le fichier ICS est ensuite téléchargé dans un dossier (variable $PATH_FILES). Notez que ce dossier doit être joignable depuis une url pour la suite du processus. Je ne détaillerai pas ici la configuration nécessaire (mais vous trouverez sans souci avec votre ami au grand G).
  • le fichier ICS est ensuite “nettoyé” grâce à un script Python (trouvé ici) : le fichier est nettoyé des éventuelles alarmes (rappels) et des tâches.
  • le fichier est ensuite synchronisé avec votre calendrier Google, selon la méthode que j’avais décrite.

C’est un peu tordu et protéiforme car, n’étant pas informaticien et peu fainéant, j’utilise ici des scripts d’autres auteurs, mais ça marche.

 

Etape 1 : mise en place du dossier et du fichier ~/.netrc

Pour commencer il nous faut préparer le dossier où nous allons déposer nos trois scripts, ici /usr/local/bin/kerio-ics-to-gcal. Dans votre terminal, faites sudo mkdir -p /usr/local/bin/kerio-ics-to-gcal pour créer ce dossier.

Nous allons maintenant stocker les différentes informations de connexion dans le fichier ~./netrc du compte root. Depuis votre terminal, faites :

sudo su # uniquement si vous n'êtes pas connecté en root
nano ~./netrc

Pour chaque compte à configurer, il vous faut entrer trois lignes, commençant par machineloginpassword.

  • machine nomducompte
  • login monadresse@gcalendar.com
  • password monsupermotdepasse

Pour configurer deux comptes votre fichier devrait ressembler à ceci :

machine moncompte_googlecalendar_1
login monadresse@gcalendar.com
password monsupermotdepasse

machine moncompte_googlecalendar_2
login monadresse@moi.me
password mondeuxiememotdepassesupersolide

Une fois que vous avez entré les informations faites un petit [CTRL] + X pour sortir, et n’oubliez pas de valider l’enregistrement par Yes puis [Enter]. Ensuite, pour sécuriser un peu ce fichier sensible, n’oubliez pas de faire sudo chmod 600 ~/.netrc.

 

Etape 2 : installer et lancer le sous-script Perl

La deuxième chose à faire est d’installer le sous-script Perl qui va gérer la synchronisation à proprement parler. Avant de passer à la suite, je vous invite donc à vous reporter à mon billet précédent et le suivre en totalité. Assurez-vous d’arriver à synchroniser un calendrier avant de passer à la suite.

 

Etape 3 : récupérons le script de nettoyage

Une fois que vous arrivez à obtenir une synchronisation, il va vous falloir récupérer le script Python https://github.com/emeidi/ical-to-gcal.

Pour cela, depuis votre terminal :

cd /usr/local/bin/kerio-ics-to-gcal
sudo wget https://raw.github.com/emeidi/ical-to-gcal/master/ical-to-gcal.py
sudo chmod 755 ical-to-gcal.py

 

Etape 4 : préparer le fichier de configuration /etc/ics-sync-script.conf

Créez avec l’éditeur de votre choix ce fichier de configuration. Il contiendra une ligne par calendrier à synchronisation. Sur cette ligne vous devez entrer quatre paramètres, séparés par des tabulations dans l’ordre suivant :

  1. URL du fichier ICS
  2. Nom de fichier en local
  3. Compte Google Calendar à utiliser, tel qu’il apparait dans le fichier ~/.netrc (exemple moncompte_googlecalendar_2 pour utiliser nos deuxièmes identifiants de connexion dans l’exemple de l’étape 1)
  4. Nom du Calendrier Google, attention c’est sensible à la casse et vous devez vérifier que votre calendrier a bien été créé sur le compte Google correspondant avant de commencer.

Cela peut donner par exemple :

http://media.education.gouv.fr/ics/Calendrier_Scolaire_Zone_A.ics        ZONEA        moncompte_googlecalendar_2       VacancesZoneA

 

Etape 5 : mettre en place le script parent

Pour terminer, il nous reste à mettre en place le script qui va orchestrer l’ensemble. Lancer dans votre terminal, puis faites  sudo nano /usr/local/bin/kerio-ics-to-gcal/ics-sync.sh.

Collez le contenu suivant, en adaptant les variables à votre configuration :

  • $CONFIG_FILE  contiendra l’emplacement de votre fichier de configuration. Si vous ne le déposez pas, comme convenu à l’étape 4, à l’emplacement /etc/ics-sync-script.conf, il faudra modifier cette variable ;
  • $PATH_FILES contiendra l’emplacement des fichiers ICS téléchargés en local, ce dossier devant être joignable en HTTP ;
  • $WEB_PATH_ICS contiendra l’URL permettant de joindre sur le web le dossier $PATH_FILES ;
  • $LOGS_GEN contiendra l’emplacement des journaux du script, par défaut ici /var/log/ics_sync.log
  • $MAIL_ADMIN contiendra l’adresse email pour les rapports d’exécution ;
  • $MAIL_SENDER contiendra l’adresse email d’expéditeur pour les rapports d’exécution.

Vous n’avez normalement pas à modifier les autres variables.

#!/bin/bash

SCRIPT_NAME=$(basename $0)
SCRIPT_DIR=$(dirname $0)
# Thanks to https://github.com/emeidi/ical-to-gcal | cf. Etape 3 https://www.yvangodard.fr/synchroniser-plusieurs-fichiers-ics-ical-avec-des-calendriers-google-agenda
PYTHON_ICS_CLEANER=${SCRIPT_DIR}/cleaner-ics-to-gcal.py
# Thanks to https://github.com/bigpresh/ical-to-google-calendar | need some modifications => https://github.com/yvangodard/ical-to-google-calendar
# Cf. cf. Etape 2 https://www.yvangodard.fr/synchroniser-plusieurs-fichiers-ics-ical-avec-des-calendriers-google-agenda
PERL_SYNC_SCRIPT=${SCRIPT_DIR}/ical-to-gcal.pl
SCR_VERS="1.0"
# cf. Etape 4 https://www.yvangodard.fr/synchroniser-plusieurs-fichiers-ics-ical-avec-des-calendriers-google-agenda
CONFIG_FILE=/etc/ics-sync-script.conf
# Répertoire où seront stockés les fichiers ICS en "transit". Ce répertoire doit être joignable via le procoloe HTTP.
PATH_FILES=/home/webserver
OWNER_PATH_ICS=$(stat -c %U $PATH_FILES)
GWNER_PATH_ICS=$(stat -c %G $PATH_FILES)
PATH_ICS=$PATH_FILES/ical
# Emplacement sur le web de votre dossier $PATH_ICS 
WEB_PATH_ICS=http://monserveur.web/ical
DATE_DU_JOUR=$(date)
LOGS_TMP=$(mktemp /tmp/tmp_log_ics_sync.XXXXX)
LOGS_MAIL=$(mktemp /tmp/tmp_log_ics_sync_mail.XXXXX)
LOGS_GEN=/var/log/ics_sync.log
MAIL_ADMIN="monmail@alerte.com"
MAIL_SENDER="homer@thesimpsons.net"
ERROR_GEN=0
ERROR_FILE=$(mktemp /tmp/tmp_log_ics_sync_error.XXXXX)
ERROR=0
FORCEMAIL=0

[ -z "$1" ] || {
   case $1 in
     "-forcemail" )
       FORCEMAIL=1
       ;;
     "-version" )
       echo "${SCRIPT_NAME} version ${SCR_VERS}"
       ;;
     * )
   esac
}

## Vérifions que le script soit exécuté par le compte root
if [ `whoami` != 'root' ] 
then
	echo "Ce script doit être utilisé par le compte root. Utilisez SUDO." >&2
exit 1
fi

[ ! -e $LOGS_GEN ] && echo " " >> $LOGS_GEN

# Ouvrons une ligne dans le log temporaire
echo " " >> $LOGS_TMP
echo "****************************** $DATE_DU_JOUR ******************************" >> $LOGS_TMP
echo " " >> $LOGS_TMP

# Par sécurité créons le dossier 
[ ! -d $PATH_ICS ] && mkdir -p $PATH_ICS

# Testons si les éléments de configuration nécessaires sont présents
if [ ! -e $CONFIG_FILE ]; then
	echo "ERREUR : Le fichier de configuration $CONFIG_FILE n'existe pas !!!" >> $LOGS_TMP
	echo  >> $LOGS_TMP
	echo "Voici un exemple de configuration :" >> $LOGS_TMP
	echo "# 	Format des lignes :" >> $LOGS_TMP
	echo "# 	URL_du_calendrier	Nom_fichier		Entree_Config_netrc		Calendar_GCAL" >> $LOGS_TMP
	echo "http://mon.adresse/mon/fichier/ics 	fichier_test	calendar.google.com 	MonCalendrierGooglePro" >> $LOGS_TMP
	echo "https://webmail.reseauenscene.fr/ical/reseauenscene.fr/t.grospiron/Projet%20CultiZer	CultiZer 	calendar.google.com 	CultiZer_Reunions" >> $LOGS_TMP
	echo "https://serveur.reseauenscene.fr/ical/reseauenscene.fr/t.grospiron/Calendrier	TGPro 	calendar.google.com 	Thomas_Grospiron_Pro" >> $LOGS_TMP
    ERROR_GEN=1
elif [ ! -e ~/.netrc ]; then
	echo "ERREUR : Le fichier de configuration ~/.netrc n'existe pas !!!" >> $LOGS_TMP
	echo  >> $LOGS_TMP
	echo "Voici un exemple de configuration :" >> $LOGS_TMP
	echo "machine ftp.freebsd.org" >> $LOGS_TMP
	echo "	login anonymous" >> $LOGS_TMP
	echo "	password edwin@mavetju.org" >> $LOGS_TMP
	echo  >> $LOGS_TMP
	echo "machine myownmachine" >> $LOGS_TMP
	echo "	login myusername" >> $LOGS_TMP
	echo "	password mypassword" >> $LOGS_TMP
    ERROR_GEN=1
# Testons si les sous-scripts sont présents
elif [ ! -e $PYTHON_ICS_CLEANER ]; then
	echo "ERREUR : Le sous-script $PYTHON_ICS_CLEANER n'existe pas !!!" >> $LOGS_TMP
	echo "Installez-le à partir de https://github.com/emeidi/ical-to-gcal et renommez-le correctement." >> $LOGS_TMP
	ERROR_GEN=1
elif [ ! -e $PERL_SYNC_SCRIPT ]; then
	echo "ERREUR : Le sous-script $PERL_SYNC_SCRIPT n'existe pas !!!" >> $LOGS_TMP
	echo "Installez-le à partir de https://github.com/yvangodard/ical-to-google-calendar." >> $LOGS_TMP
	ERROR_GEN=1
else

	cat $CONFIG_FILE | \
	while read URL_CALENDAR LOCAL_FILE NETRC_CONFIG	CALENDAR_GCAL
	do
		ERROR_PART=0
		echo "	--> Traitement de l'URL : $URL_CALENDAR" >> $LOGS_TMP
		echo "" >> $LOGS_TMP

		## Testons si les quatre variables nécessaires sont présentes
		VAR_OK=0
		if [ "1${CALENDAR_GCAL}" = "1" ]; then
			VAR_OK=1
			echo "Erreur : les quatre variables nécessaires ne sont pas présentes." >> $LOGS_TMP && ERROR=1
			echo "Voici un exemple de configuration :" >> $LOGS_TMP
			echo "# 	Format des lignes :" >> $LOGS_TMP
			echo "# 	URL_du_calendrier	Nom_fichier		Entree_Config_netrc		Calendar_GCAL" >> $LOGS_TMP
			echo "http://media.education.gouv.fr/ics/Calendrier_Scolaire_Zone_A.ics        ZONEA        moncompte_googlecalendar_2       VacancesZoneA" >> $LOGS_TMP
			echo "http://media.education.gouv.fr/ics/Calendrier_Scolaire_Zone_B.ics        ZONEB        moncompte_googlecalendar_1       VacancesZoneB" >> $LOGS_TMP
			echo "http://media.education.gouv.fr/ics/Calendrier_Scolaire_Zone_C.ics        ZONE_C_Vacances        moncompte_googlecalendar_1       VacancesZoneC" >> $LOGS_TMP
		fi
		if [ $VAR_OK -eq 0 ]; then
			## Testons si l'URL est correcte
			curl -k -s --head $URL_CALENDAR | head -n 1 | grep "HTTP/1.1 200 OK" > /dev/null 
			URL_OK=$?
			[ $URL_OK -eq 0 ] && echo "URL du calendrier semble OK : $URL_CALENDAR." >> $LOGS_TMP
			[ $URL_OK -ne 0 ] && echo "Problème possible pour accéder à l'URL du calendrier : $URL_CALENDAR." >> $LOGS_TMP

			## Testons si l'entrée dans le fichier ~/.netrc semble correcte
			grep $NETRC_CONFIG ~/.netrc > /dev/null
			GREP_NETRC=$?
			[ $GREP_NETRC -eq 0 ] && echo "Entrée $NETRC_CONFIG du fichier ~/.netrc semble correcte." >> $LOGS_TMP
			[ $GREP_NETRC -ne 0 ] && echo "Entrée $NETRC_CONFIG manquante dans le fichier ~/.netrc." >> $LOGS_TMP && ERROR_PART=1
		else
			ERROR_PART=1
		fi

		## Continuons notre processus si nous n'avons pas rencontré d'erreur
		if [ $ERROR_PART -ne 0 ]; then
			echo "	--> Impossible de continuer le traitement de l'URL : $URL_CALENDAR." >> $LOGS_TMP && ERROR=1
		else
			[ -e ${PATH_ICS}/${LOCAL_FILE}.ics ] && rm ${PATH_ICS}/${LOCAL_FILE}.ics
			[ -e ${PATH_ICS}/${LOCAL_FILE}.gcal.ics ] && rm ${PATH_ICS}/${LOCAL_FILE}.gcal.ics
			curl -k --silent $URL_CALENDAR -o ${PATH_ICS}/${LOCAL_FILE}.ics && chown -R $OWNER_PATH_ICS:$GWNER_PATH_ICS $PATH_ICS
			## Testons si le fichier semble correct (contient BEGIN:VCALENDAR)
			if [ -e ${PATH_ICS}/${LOCAL_FILE}.ics ]; then
				cat ${PATH_ICS}/${LOCAL_FILE}.ics | head -n 10 | grep "BEGIN:VCALENDAR" > /dev/null
				ICS_OK=$?
			else
				ICS_OK=1
			fi
			if [ $ICS_OK -ne 0 ]; then
				echo "Le fichier ${PATH_ICS}/${LOCAL_FILE}.ics ne semble pas être un ICS valide." >> $LOGS_TMP
				echo "	--> Impossible de continuer le traitement de l'URL : $URL_CALENDAR." >> $LOGS_TMP && ERROR=1
			else
				echo "Le fichier ${PATH_ICS}/${LOCAL_FILE}.ics semble correct." >> $LOGS_TMP

				## Préparons le fichier pour la synchro GCAL
				echo "" >> $LOGS_TMP
				echo "Envoi de la commande $PYTHON_ICS_CLEANER ${PATH_ICS}/${LOCAL_FILE}.ics." >> $LOGS_TMP
				echo "" >> $LOGS_TMP
				echo "***********" >> $LOGS_TMP
				$PYTHON_ICS_CLEANER ${PATH_ICS}/${LOCAL_FILE}.ics >> $LOGS_TMP 2>&1
				ERROR_PART2=$?
				echo "***********" >> $LOGS_TMP
				echo "" >> $LOGS_TMP
				if [ $ERROR_PART2 -ne 0 ]; then
					echo "Le fichier ${PATH_ICS}/${LOCAL_FILE}.ics n'a pas été traité correctement par $PYTHON_ICS_CLEANER." >> $LOGS_TMP
					echo "	--> Impossible de continuer le traitement de l'URL : $URL_CALENDAR." >> $LOGS_TMP && ERROR=1
				else
					echo "Le fichier ${PATH_ICS}/${LOCAL_FILE}.ics a été traité correctement par $PYTHON_ICS_CLEANER." >> $LOGS_TMP

					## Continuons notre processus si nous n'avons pas rencontré d'erreur
					echo "" >> $LOGS_TMP
					echo "Envoi de la commande $PERL_SYNC_SCRIPT --calendar=${CALENDAR_GCAL} --ical_url=${WEB_PATH_ICS}/${LOCAL_FILE}.gcal.ics --configmachine=${NETRC_CONFIG}." >> $LOGS_TMP
					echo "" >> $LOGS_TMP
					echo "***********" >> $LOGS_TMP
					/usr/bin/perl $PERL_SYNC_SCRIPT --calendar=${CALENDAR_GCAL} --ical_url=${WEB_PATH_ICS}/${LOCAL_FILE}.gcal.ics --configmachine=${NETRC_CONFIG} >> $LOGS_TMP 2>&1 
					ERROR_PART3=$?
					echo "***********" >> $LOGS_TMP
					echo "" >> $LOGS_TMP
					if [ ${ERROR_PART3} -eq 0 ]; then
						echo "Le fichier ${PATH_ICS}/${LOCAL_FILE}.gcal.ics a été traité correctement par la commande $PERL_SYNC_SCRIPT." >> $LOGS_TMP
					else
						echo "Erreur(s) lors de l'envoi de la commande $PERL_SYNC_SCRIPT." >> $LOGS_TMP
						echo "	--> Impossible de terminer le traitement de l'URL : $URL_CALENDAR." >> $LOGS_TMP && ERROR=1
					fi
				fi
			fi
      	 fi  	
      	 echo "" >> $LOGS_TMP
      	 [ $ERROR -ne 0 ] && echo $ERROR > $ERROR_FILE
	done < $CONFIG_FILE
fi

grep 1 $ERROR_FILE > /dev/null
GREP_ERROR=$?

if [ $ERROR_GEN -ne 0 ]; then
	# envoi du mail
	echo "To: $MAIL_ADMIN" > $LOGS_MAIL
	echo "From: Server ICS SYNC Report <$MAIL_SENDER>" >> $LOGS_MAIL
	echo "Subject: [ERREUR TOTALE] Synchronisation fichiers ICS" - `date` >> $LOGS_MAIL
	cat $LOGS_TMP >> $LOGS_MAIL
	cat $LOGS_MAIL | /usr/sbin/sendmail -f $MAIL_ADMIN -t
	echo "Problème(s) important(s) rencontré(s) lors de l'éxécution de $0" >&2
	cat $LOGS_TMP >> $LOGS_GEN
	rm $LOGS_TMP
	rm $LOGS_MAIL
	rm $ERROR_FILE
	exit 1
elif [ $GREP_ERROR -eq 0 ]; then
	# envoi du mail
	echo "To: $MAIL_ADMIN" > $LOGS_MAIL
	echo "From: Server ICS SYNC Report <$MAIL_SENDER>" >> $LOGS_MAIL
	echo "Subject: [ERREUR PARTIELLE] Synchronisation fichiers ICS" - `date` >> $LOGS_MAIL
	cat $LOGS_TMP >> $LOGS_MAIL
	cat $LOGS_MAIL | /usr/sbin/sendmail -f $MAIL_ADMIN -t
	echo "Problème(s) rencontré(s) lors de l'éxécution de $0" >&2
	cat $LOGS_TMP >> $LOGS_GEN
	rm $LOGS_TMP
	rm $LOGS_MAIL
	rm $ERROR_FILE
	exit 1
elif [ $FORCEMAIL -eq 1 ]; then
	# envoi du mail
	echo "To: $MAIL_ADMIN" > $LOGS_MAIL
	echo "From: Server ICS SYNC Report <$MAIL_SENDER>" >> $LOGS_MAIL
	echo "Subject: Synchronisation fichiers ICS" - `date` >> $LOGS_MAIL
	cat $LOGS_TMP >> $LOGS_MAIL
	cat $LOGS_MAIL | /usr/sbin/sendmail -f $MAIL_ADMIN -t
	cat $LOGS_TMP >> $LOGS_GEN
	rm $LOGS_TMP
	rm $LOGS_MAIL
	rm $ERROR_FILE
	exit 0
fi

cat $LOGS_TMP >> $LOGS_GEN
rm $LOGS_TMP
rm $LOGS_MAIL
rm $ERROR_FILE
exit 0

Une fois que vous avez entré les informations faites un petit [CTRL] + X pour sortir, et n’oubliez pas de valider l’enregistrement par Yes puis [Enter].

Puis rendez le script exécutable : sudo chmod 750 /usr/local/bin/kerio-ics-to-gcal/ics-sync.sh.

 

Etape 6 : testez, testez, testez !

Faites un test depuis votre terminal : sudo /usr/local/bin/kerio-ics-to-gcal/ics-sync.sh -forcemail.
En passant l’option -forcemail, vous allez recevoir, même si le script s’exécute correctement, un rapport par email. Cela vous permettra de vérifier que tout fonctionne correctement.

Si tout est OK, il ne vous reste plus qu’à programmer le lancement du script /usr/local/bin/kerio-ics-to-gcal/ics-sync.sh depuis votre Crontab (au via LaunchDaemon sur OS X).

 

N’hésitez pas à me faire part de vos commentaires, retours ou améliorations !

Posted in Unix et LinuxTags: