Envoyer des SMS automatiquement à une date définie

Il y a quelques temps, je me demandais, par simple curiosité, comment on pouvait envoyer un SMS automatiquement à une heure définie sur les "vieux" téléphones. Nan parce que sur les smartphones, je suis sûr qu'il doit y avoir 20 applis par store qui font ça mais quid pour un bête Nokia 6300, par exemple ?

À l'époque, j'avais trouvé quelques logiciels comme Automsg mais, n'étant pas signé, il ne pouvait envoyer de SMS sans me demander une confirmation avant ... Ce qui est pas trop l'objectif d'un envoi automatique. Je me rappelle avoir tenté de bidouiller de ce côté là : tenter de signer le logiciel, tenter de forcer l'autorisation (car elle était grisée dans le menu de mon téléphoe). Rien n'avait fonctionné.

Aujourd'hui, je me suis à nouveau intéressé à ce sujet. J'ai décidé d'interfacer mon téléphone avec mon ordinateur avec gammu (logiciel libre de gestion de téléphone portable/modem compatible avec un large panel de téléphones). Je me suis dit que couplé à cron (pour un envoi régulier) ou at (pour un envoi ponctuel), ça devait le faire.

Alors certes, il faut une "proximité" avec un ordinateur mais avec le bluetooth, ça ne devrait pas poser de problèmes. Alors certes, il faut que l'ordinateur associé reste allumé mais avec la "démocratisation" des machines à faible consommation, ça ne devrait pas être un problème et puis, ce n'est que pour tester et pour le fun qu'on fait ça 😛 .

Gammu s'installe sur une Debian de manière classique :

sudo apt-get install gammu

Ensuite, il faut raccorder votre téléphone à votre machine. Dans mon cas, cela se fera en Bluetooth. J'utilise donc gnome-bluetooth (installé par défaut normalement) pour appairer mon téléphone et ma machine. Basiquement "Configurer un nouveau périphérique ..." puis suivre l'assistant. Ne pas cocher les cases concernant l'utilisation du téléphone comme modem/périphérique réseau.

Avant de commencer la configuration de gammu, il vous faut l'adresse matérielle bluetooth de votre téléphone (Bluetooth Device Address). Pour l'obtenir :

hcitool scan

Vous obtiendrez une sortie comme celle-là :

00:1D:98:3E:24:12	Nokia 6300

La configuration de gammu se fait facilement :

gammu-config

Une interface semi-graphique s'affiche. Dans "Port", il faut saisir la Bluetooth Device Address obtenue à l'étape précédente. Dans "Connection", il faut choisir "bluephonet", module dédié aux Nokia over Bluetooth. On ne touchera pas au reste. On choisit "Save" et on valide par "OK".

Je vous laisse découvrir les commandes de gammu avec gammu help ... Il y'en a des inutiles donc indispensables comme "nokiavibratest", des intéressantes comme getlocation, des non fiables comme "battery", ... Bref, à vous de découvrir.

Pour tester l'envoi d'un SMS :

echo "sms de test" | gammu sendsms TEXT +336xxxxxxxx OU gammu sendsms TEXT +336xxxxxxxx -text "sms de test"

Pour demander un accusé de réception, il faut ajouter l'option "-report" Il y a aussi l'option -validity pour définir la période de validité si non-remise 😉

Pour envoyer un SMS à une date donnée, il suffit d'utiliser la commande at. Exemple :

$ at 06:00
warning: commands will be executed using /bin/sh
at> /usr/bin/gammu sendsms TEXT +336xxxxxxxx -text "Bonjour !"    
at> <EOT> (Pressez CTRL+D)
job 8 at Thu Nov 29 06:00:00 2012

Ici nous envoyons un SMS à 6h le jour même.

Pour envoyer un mail régulièrement, il faut utiliser cron. crontab -e puis :

00 06 * * * /usr/bin/gammu sendsms TEXT +336xxxxxxxx -text "Bonjour !"

Ici, nous envoyons le SMS tous les matins à 6h.

Pour l'intérêt d'une telle manip, je ne trouve rien de sérieux donc ... être le premier à souhaiter un anniversaire (envoi d'un SMS à minuit pile), gagner un pari stupide du genre "je pari que je m'endors après toi" (envoi d'un sms toutes les 5 minutes pour dire qu'on ne dort pas encore alors qu'en vrai si) ... Désolé.

En dehors de cron/at, gammu doit pouvoir s'interfacer avec les logiciels de monitoring pour envoyer des alertes SMS si l'on ne souhaite pas utiliser les API des opérateurs ou autres API. Gammu doit aussi pouvoir s'utiliser dans un script de spam par SMS même si il existe des logiciels plus performants pour ce genre de choses.

DDNS indépendant sur OpenWRT

Table des matières

But

D'un côté, j'ai mon WRT54GL sous OpenWRT derrière une ligne ADSL (chez un FAI classique) avec une IP dynamique. Pour maintenir une correspondance entre un nom et l'adresse IP changeante, j'utilise les services de dyn.com.

De l'autre, j'ai un nom de domaine dont je gère moi-même le serveur de nom primaire. J'utilise BIND comme logiciel serveur.

Il serait bien que je prenne moi-même en charge la correspondance entre le nom de mon OpenWRT et son adresse IP. Pour ceux qui se demandent encore l'intérêt de la manip' : indépendance et tout ce que ça implique.

Contraintes

On parle de mon WRT54GL donc :

  • La méthode traditionnelle est de faire du DDNS (Dynamic DNS) dont l'échange est sécurisé grâce à une paire de clés grâce à un client comme nsupdate. Néanmoins, je n'ai pas la place pour installer un client sur mon OpenWRT.
  • Je n'ai pas la place pour installer les libs nécessaires au fonctionnement d'HTTPS avec curl ou wget. Or, si je fais mon propre système, je veux que tout soit sécurisé (de la récupération de l'IP jusqu'à sa mise à jour sur le serveur de nom.
  • Mon modem donne une IP RFC 1918 à mon WRT54GL donc le seul moyen de récupérer mon IP externe est de faire appel à un service externe. Or, je ne veux pas.

Ce que l'on va faire

OpenWRT dispose d'un client SSH ... donc toute la partie sécurité sera gérée par SSH lui-même.

Pour la périodicité de la vérification, OpenWRT dispose, par défaut, du classique cron.

Pour obtenir l'adresse IP de l'OpenWRT, pas besoin d'aller la récupérer sur un site web : on l'a récupérera depuis la variable d'environnement "SSH_CLIENT".

Pour mettre à jour la zone, on utilisera nsupdate qui sera déporté sur le serveur. Pas besoin de sécuriser l'échange avec une paire de clé vu que l'échange se fera sur localhost.

Pour résumer : Sur l'OpenWRT, cron lancera, de manière répétée, le client ssh. Le client SSH présent sur l'OpenWRT ira se connecter au serveur de nom en utilisant sa clé privée. Il lancera un script présent sur le serveur de nom. Ce script prendra la nouvelle IP en paramètre, fera quelques vérifications et utilisera nsupdate pour mettre à jour la zone.

Mise en œuvre

Dans la suite, "serveur" désignera le serveur de nom, "OpenWRT" désignera mon WRT54GL sous OpenWRT et "openwrt.guiguishow.info" sera le nom de domaine attribué à mon OpenWRT.

Script

#!/bin/bash
 
if [ $# -ne 1 ]
then
        echo "Trop ou pas assez d'arguments"
        logger "DDNS openwrt - Trop ou pas assez d'arguments"
        exit 2
fi
 

echo $1 | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" > /dev/null
if [ $? -ne 0 ]
then
        echo "La paramètre \"IP\" n'est pas au bon format."
        logger "DDNS openwrt - Paramètre IP pas au bon format."
        exit 2
fi
 
# Un petit délire pas utile : vérifier rapidement l'AS à qui a
# été distribuée l'IP passée en paramètre. Si ce n'est pas l'AS
# de mon FAI, alors soit il y a une erreur, soit le script
# n'est pas lancé depuis l'OpenWRT ... de là à parler d'une
# usurpation ... 
as=`whois $1 | grep origin | grep -oE "AS[0-9]{1,5}"`
if [ $? -eq 0 ] && [ $as != "AS5410" ]
then
        logger "DDNS openwrt - Usurpation"
        echo "." | mail -s "DDNS openwrt - USURPATION" guigui chez guiguishow pointinfo
        exit 1
fi
 
# Si l'IP passée en paramètre est la même que celle 
# actuellement dans la zone, on ne fait rien.
ipActuelle=`dig +short @127.0.0.1 openwrt.guiguishow.info`
if [ $ipActuelle !=  $1 ]
then
        cat > ./majDNSopenwrt << EOF
server 127.0.0.1
zone guiguishow.info
update delete openwrt.guiguishow.info. A
update add openwrt.guiguishow.info. 60 A $1
show
send
EOF

        nsupdate -v ./majDNSopenwrt > /dev/null 2>&1
 
        if [ $? -eq 0 ]
        then
                logger "DDNS openwrt - MAJ OK"
                echo "." | mail -s "DDNS openwrt - MAJ OK" guigui chez guiguishow pointinfo
        else
                logger "DDNS openwrt - MAJ NEEDED BUT FAIL"
                echo "." | mail -s "DDNS openwrt - MAJ NEEDED BUT FAIL" guigui chez guiguishow pointinfo
        fi
fi

ÉDIT du 06/08/2014 à 16h20 : b4n propose une version mieux écrite de ce script :

#!/bin/bash
 
EMAIL="guigui chez guiguishow pointinfo"
 
if [ $# -ne 1 ]
then
  echo "Trop ou pas assez d'arguments" >&2
  logger "DDNS openwrt - Trop ou pas assez d'arguments"
  exit 2
fi
 
if ! grep -qxE '([0-9]{1,3}\.){3}[0-9]{1,3}' <<<"$1"
then
  echo "Le paramètre \"IP\" n'est pas au bon format." >&2
  logger "DDNS openwrt - Paramètre IP pas au bon format."
  exit 2
fi
 
# Un petit délire pas utile : vérifier rapidement l'AS à qui a
# été distribuée l'IP passée en paramètre. Si ce n'est pas l'AS
# de mon FAI, alors soit il y a une erreur, soit le script
# n'est pas lancé depuis l'OpenWRT ... de là à parler d'une
# usurpation ...
if [ "$(whois "$1" | grep origin | grep -oE 'AS[0-9]{1,5}')" != "AS5410" ]
then
  logger "DDNS openwrt - Usurpation"
  mail -s "DDNS openwrt - USURPATION" "$EMAIL" <<<"."
  exit 1
fi
 
# Si l'IP passée en paramètre est la même que celle
# actuellement dans la zone, on ne fait rien.
if [ "$(dig +short @127.0.0.1 openwrt.guiguishow.info)" != "$1" ]
then
  if nsupdate -v /dev/stdin >/dev/null 2>&1 <<EOF
server 127.0.0.1
zone guiguishow.info
update delete openwrt.guiguishow.info. A
update add openwrt.guiguishow.info. 60 A $1
show
send
EOF
  then
    logger "DDNS openwrt - MAJ OK"
    mail -s "DDNS openwrt - MAJ OK" "$EMAIL" <<<"."
  else
    logger "DDNS openwrt - MAJ NEEDED BUT FAIL"
    mail -s "DDNS openwrt - MAJ NEEDED BUT FAIL" "$EMAIL" <<<"."
  fi
fi

Fin de l'édit

Chez moi, ce script s'appelle "DDNSOpenWRT.sh".

Installer nsupdate sur le serveur

Sous Debian, nsupdate se trouve dans le package dnsutils :

apt-get install nsupdate

Réglage de BIND

Il faut autoriser le DDNS sur la zone depuis localhost. Cela se passe dans la déclaration de la zone, donc normalement dans le fichier /etc/bind/named.conf.local. Il faut ajouter la directive "allow-update { 127.0.0.1; };". Exemple :

zone "guiguishow.info." IN {
        type master;
        [...]
        allow-update { 127.0.0.1; };
};

Au niveau des droits, BIND doit avoir le droit d'écrire (w) sur le fichier qui défini la zone ainsi que sur le dossier contenant ce fichier car BIND écrit des journaux (fichiers avec une extension .jnl).

Utilisateur dédié et paire de clés

Sur le serveur, on va créer un utilisateur (openwrt) qui sera dédié uniquement à l'activité DDNS.

adduser openwrt

Mettre un mot de passe.

On met le script dans le home directory de cet utilisateur (/home/openwrt).

On génére une paire de clé pour l'utiliser avec SSH :

ssh-keygen -t rsa

La partie publique va sur le serveur, dans /home/openwrt/.ssh/authorized_keys, comme d'habitude.

La clé privée doit aller sur l'OpenWRT. Néanmoins, dropbear accepte les clés uniquement sous un certain format (sinon erreur "ssh: Exited: String too long"). On installe dropbear sur notre machine de travail et on converti la clé openwrt.rsa -> openwrt.dropb) :

sudo apt-get install dropbear
/usr/lib/dropbear/dropbearconvert openssh dropbear openwrt.rsa openwrt.dropb

On transfère la clé sur l'OpenWRT :

scp openwrt.dropb root@openwrt:.

Un peu de sécurité

L'utilisateur openwrt ne doit rien pouvoir faire sur le serveur à part lancer le script. On va donc restreindre les commandes possibles à une seule avec la directive de configuration ForceCommand de SSH. On ajoute donc ceci au fichier /etc/ssh/sshd_config :

Match User openwrt
ForceCommand ~/DDNSOpenWRT.sh `env | grep SSH_CLIENT | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}"`

Créer un cron sur OpenWRT

Par défaut, crond n'est pas lancé s'il n'y a aucun fichier dans /etc/crontabs/ (voir le script dans /etc/init/ pour vous en convaincre).

Il suffit de créer un fichier /etc/crontabs/root avec le contenu suivant :

*/5 * * * * /usr/bin/ssh -i /path/to/cle/privee openwrt@serveur_de_nom '~/DDNSOpenWRT.sh `env | grep SSH_CLIENT | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}"`'

Ainsi, toutes les 5 minutes, une vérification sera faite.

Au prochain reboot, crond démarrera tout seul mais cette fois-ci, c'est à vous de le lancer :

/etc/init.d/cron start

Pour terminer, il ne reste plus qu'à supprimer les ddns-scripts. Pour cela, je commence à killer les processus (ps aux + kill -9). Ensuite je supprime le package (opkg remove --autoremove). Pensez aussi à supprimer luci-app-ddns si jamais vous l'avez. Puis je supprime les paramètres conservés par uci (uci delete ddns.myddns) . Enfin : reboot.

Inspiration

L'idée d'utiliser nsupdate côté serveur, dans un script, me vient de : Mise à jour dynamique d’entrée DNS chez GuiguiABloc.

Impression de 4 pages format A6 sur une feuille format A4

Ce billet parle d'impression de flyers (tracts) qui sont généralement au format A6 sur une feuille A4.

On ne parlera pas de la création de ce flyer (Inkscape, GIMP, ...) : on suppose simplement que l'on a 2 fichiers PDF qui contiennent chacun un côté du flyer sur une page au format A6 (le recto dans un fichier et le verso dans un autre fichier). On supposera aussi que vous êtes sous un GNU/Linux ou un BSD. On supposera enfin que la configuration pour imprimer dans un PDF est OK.

Il peut arriver que l'imprimeur demande 2 PDF (un ayant le recto au format A6 et l'autre ayant le verso au format A6) quand la quantité est faible (pas d'utilisation d'offset) ou pour un bout d'essai. Pour la première fois, un imprimeur m'a demandé 1 PDF un peu spécial qui contient 2 pages : la première contenant 4 recto et la deuxième contenant 4 verso le tout sans aucune marge blanche.

La première difficulté est d'imprimer 4 pages sur une même feuille quand le PDF source n'en a qu'une ... On ouvre le PDF qui contient le recto avec la visionneuse habituelle et on demande l'impression :

On lance l'impression. Et là, c'est quitte ou double :

  • Soit le PDF généré est impeccable, chacun des "flyers" prenant précisément 1/4 de la feuille A4. Dans ce cas, vous imprimez le recto de la même manière et passez à la fin de ce billet pour la fusion de 2 PDF.
  • Soit il reste du blanc entre les flyers et de grosses marges de chaque côtés de la feuille. Nous allons y remédier.

On reouvre le PDF contenant le recto et on l'imprime. Ce coup-là, on va imprimer 4 fois la première page sur 4 feuilles différentes. Les paramètres sont donc ceux par défaut :

  • Général -> Plage - Pages : 1,1,1,1
  • Mise en page -> Pages par côtés : 1
  • Mise en page -> Échelle : 100%
  • Taille du papier : A6

On installe le paquet pdfjam :

apt-get install pdfjam

On exécute la commande :

pdfnup  --nup 2x2 --paper a4paper --noautoscale true --no-landscape recto.pdf

Explications :

  • --nup : 2 lignes et 2 colonnes par page
  • --paper a4paper : vous ne croyez quand même pas que je vais expliquer ça ? 😀
  • --noautoscale true : pareil ...
  • --no-landscape : on veut le mode portrait, pas paysage

On obtient un pdf, recto-nup.pdf, qui contient bien 4 fois le recto de notre flyer sur une seule feuille sans aucun marge. On réalise la même manipulation (impression en "4 pages sur 4 feuilles puis utilisation de pdfnup") avec le PDF qui contient le verso.

On a donc deux fichiers : recto-nup.pdf et verso-nup.pdf. Mais l'imprimeur veut le recto et le verso dans un même PDF. Qu'à cela ne tienne, utilisons gs (source : [CUPS-PDF] : assemblage de document) :

gs -dNOPAUSE -sDEVICE=pdfwrite -sOUTPUTFILE=recto-verso.pdf -dBATCH recto-nup.pdf verso-nup.pdf

On obtient bien un PDF qui contient 2 pages : la première contient 4 recto et la deuxième 4 verso, le tout sans grosses marges abominables.

P.-S. : Pour regrouper plusieurs pages sur une même feuille, il existe aussi mpage ou psnup. Le premier n'a jamais fonctionné chez moi. Le deuxième bloque sur certains PDF, en fonction de leurs contenus, sans doute.

Tout est dans le titre : l'installation de cups-pdf avec apt-get sous Debian Wheezy se bloque/gèle/freeze/hang sur la ligne "Reloading Common Unix Printing System: cupsd.". Le même problème peut survenir lors de la désinstallation de cups-pdf.

Un ps aux | grep dpkg permet de constater qu'il y a un processus "/bin/sh /var/lib/dpkg/info/cups-pdf.postinst" qui tourne. Comme par hasard, quand on kill se processus, l'installation continue. Certes elle échoue mais elle continue.

En décortiquant ce script, on observe l'utilisation de lpstat. OK, on se documente sur le bouzin et on essaye nous même, en ligne de commande, "lpstat -h localhost -r" : aucun retour ...

Hum ... que donne "lpstat -h 127.0.0.1 -r" ? "scheduler is running" ...

Hum ... problème de résolution des noms locaux ... On vérifie le contenu de /etc/hosts : il contient bien une association localhost <=> 127.0.0.1. Il contient évidement le même type d'association pour IPv6 ...

Hum ... donc s'il ne résout pas les noms locaux, un "lpstat -h nom_de_la_machine -r" va échouer ... Même pas ... Donc c'est bien un soucis interne à lpstat.

La solution est évidente (mais pas forcement propre, je n'ai aucun avis sur la question) : on modifie /var/lib/dpkg/info/cups-pdf.postinst pour remplacer toutes les occurrences de localhost (même celles qui ne sont pas utilisées comme paramètre d'un lpstat).

Tant qu'à faire, pour s'éviter des problèmes lors de la déinstallation/suppression de cups-pdf, on modifie les scripts /var/lib/dpkg/info/cups-pdf.postprerm et /var/lib/dpkg/info/cups-pdf.postpostrm de la même manière.

Ensuite si ce n'est pas déjà fait, on kill le processus /bin/sh /var/lib/dpkg/info/cups-pdf.* qui bloque l'installation/désinstallation de cups-pdf.

Enfin, on lance juste dpkg --configure -a et l'installation/déinstallation de cups-pdf se termine sans problèmes.

Adversity bloque le CSS de piwik.org

Si vous avez remarqué que, depuis quelques temps, le CSS du site officiel de Piwik (piwik.org) n'est plus chargé par votre navigateur et si vous utilisez la liste Adversity pour Adblock Plus (ou Adblock Edge), alors ce billet est fait pour vous. Dans le cas contraire, je n'ai pas de solution pour vous.

Il s'agit bien de deux règles de la liste Adversity qui opèrent ce blocage involontaire (on comprend que le but initial de ces règles est de bloquer piwik.js/piwik.php, les deux parties du traqueur JS sur une installation Piwik et que ce blocage est un effet de bord) :

  • /piwik.
  • ||piwik.

Au rayon des "solutions", on a :

  • Désactiver Adblock Plus sur piwik.org.
  • Désactiver les deux règles sus-citées. Cela se passe dans les Préférences de filtre d'Adblock, sélectionnez la liste Adversity, recherchez les deux règles et décochez-les. La prise en compte est immédiate. Les règles désactivées ne seront pas reactivées durant une MAJ de la liste. Attention : si vous n'utilisez pas d'autres plugins, alors le traqueur Piwik fonctionnera à nouveau sur tous les sites que vous visitez (ce qui n'est pas dramatique ( beaucoup moins que Google Analytics quoi) mais je préfère prévenir les esprits sensibles).
  • Désactiver/supprimer Adversity/Adblock Plus ;). Juste pour troller.

J'ai fait remonter l'information via les adresses mail publiées sur la page Google Code de la liste car pour moi il s'agit d'un comportement non désiré : bloquer le traqueur Piwik (piwik.js/piwik.php) sur tous les sites (y compris sur piwik.org) est bien un comportement désiré d'une telle liste. Bloquer le CSS du site officiel d'une solution de stats me semble être plus un effet de bord des règles qu'un comportement désiré car le CSS du site officiel d'un traqueur n'est pas nuisible ...

Il ressort de la conversation que le but de la liste Adversity est de supprimer la publicité et les traqueurs statistiques (jusque là, je suis d'accord). Piwik.org étant le site officiel d'un traqueur statistiques (je suis toujours OK 😛 ), ce sur-blocage doit être attendu (je ne suis plus d'accord, pour les raisons indiquées dans le précédent paragraphe). Aucun effort n'est fait ni ne sera fait pour s'assurer que ce genre de pages s'affichent comme attendu. Au moins, c'est clair 🙂 .