Wazo Phonebook SnomDECT

De SLM - MediaWiki

Présentation

L'annuaire Wazo n'est pas pris en charge sur les bornes M900 en raison d'une URL Trop grande.

Wazo-m900snom-limitation.jpg

Après vérification, la raison communiqué par l'éditeur wazo est exact :).

J'ai tenté de contourner cette limitation via une redirection 301, mais sans succès.

La seconde approche, qui elle fonctionne, est de télécharger ce répertoire et de le mettre à disposition sur un autre serveur avec une URL de taille accepté par les bornes.

Environnement de test:

  • Wazo version: 23.1
  • Borne M900 version: 530.2
  • M85 version: 530.2

M900 - Phonebook

Les bornes M900 peuvent gérer le répertoire distant de 4manières.

  • Local
  • LDAP
  • XML Server
  • XML MiniBrowser Server

Local

Ce mode télécharge le répertoire à une intervalle prédéfini et le stock dans sa mémoire. les téléphones récupère le répertoire sur la borne.

Documentation: https://service.snom.com/display/wiki/How+to+use+the+Local+Central+Directory+on+M300%2C+M700%2C+M900+DECT+base

LDAP

L'annuaire central du serveur LDAP permet de configurer tous les paramètres spécifiques à LDAP afin d'effectuer des recherches sur un serveur LDAP

XML Server

Le choix XML server est spécifique à Broadsoft , ce type de carnet d'adresses utilise une API propriétaire Broadsoft nommée XSI pour accéder aux répertoires.

XML MiniBrowser Server

Dans ce mode, rien n'est stocké sur la borne,

Quand un téléphone souhaite accéder à l'annuaire, il fait une requête http vers le serveur distant et télécharge le répertoire.

Choix du type de serveur

De base, wazo est fait pour fonctionner avec XML Minibrowser Server, mais lors de mes différents test j'ai pu constater qu'un annuaire de plus de 160 contacts n'était pas accepté.

Le mode Local n'ayant pas cette limitation nous adapteront le fichier xml afin de lui convenir.

Configuration du serveur

VM

Dans mon cas, je suis parti avec une VM:

  • Stockage: 4Go
  • Rem: 1024Mo

Sur laquelle j'ai installé une Debian 11.6 minimal avec serveur web (apache2) + php

Page Web

Dans notre cas le repertoire apache racine est:

/var/www/html

Création des repertoires

mkdir /var/www/html/includes
mkdir /var/www/html/download

Mise en place des droits pour appaches chown www-data:www-data /var/www/html chown www-data:www-data /var/www/html/includes

Créer un fichier de redirection pour l'index vi /var/www/html/index.php

1 <?php
2 header("Status: 301 Moved Permanently", false, 301);
3 header("Location: https://www.oci.fr/oci-connect/");
4 exit();
5 ?>

Créer la page d'administration en remplaçant bien sur le mot de passe : VOTRE_MOT_DE_PASSE par le votre en ligne 8

vi /var/www/html/phonebook.php
  1 <?php
  2 session_start();
  3 
  4 // Vérifier si le mot de passe a été soumis
  5 if (isset($_POST['mot_de_passe'])) {
  6     $motDePasseSaisi = $_POST['mot_de_passe'];
  7     // Comparer le mot de passe saisi avec le mot de passe souhaité
  8     if ($motDePasseSaisi === 'VOTRE_MOT_DE_PASSE') {
  9         // Mot de passe correct, définir une variable de session pour indiquer que l'utilisateur est authentifié
 10         $_SESSION['authentifie'] = true;
 11     } else {
 12         // Mot de passe incorrect, afficher un message d'erreur
 13         echo "Mot de passe incorrect.";
 14     }
 15 }
 16 
 17 // Vérifier si l'utilisateur est authentifié
 18 if (!isset($_SESSION['authentifie']) || $_SESSION['authentifie'] !== true) {
 19     // Afficher le formulaire de saisie du mot de passe
 20     echo '<form method="post" action="">';
 21     echo 'Mot de passe : <input type="password" name="mot_de_passe">';
 22     echo '<input type="submit" value="Valider">';
 23     echo '</form>';
 24 } else {
 25     // Afficher le contenu protégé
 26     echo "Contenu protégé : les utilisateurs authentifiés peuvent voir ce contenu.";
 27 
 28         // Vérification si le formulaire a été soumis
 29         if ($_SERVER["REQUEST_METHOD"] == "POST") {
 30                 // Récupération des valeurs soumises
 31                 $valeurs1 = isset($_POST["valeur1"]) ? $_POST["valeur1"] : array();
 32                 $valeurs2 = isset($_POST["valeur2"]) ? $_POST["valeur2"] : array();
 33                 $valeurs3 = isset($_POST["valeur3"]) ? $_POST["valeur3"] : array();
 34                 $valeurs4 = isset($_POST["valeur4"]) ? $_POST["valeur4"] : array();
 35 
 36 				 // Formatage des valeurs en tableau associatif
 37 				$donnees = array();
 38 				for ($i = 0; $i < count($valeurs1); $i++) {
 39 					// Génération du hash en utilisant la valeur de la colonne 2
 40 					$hash = hash('sha256', $valeurs2[$i]);
 41 					// Encodage du hash en base64
 42 					$base64 = base64_encode($hash);
 43 					// Extraction des 16 premiers caractères de l'encodage en base64
 44 					$valeur4 = substr($base64, 0, 16);
 45 
 46 					$donnees[$i] = array(
 47 						"valeur1" => isset($valeurs1[$i]) ? "checked" : "",
 48 						"valeur2" => $valeurs2[$i],
 49 						"valeur3" => $valeurs3[$i],
 50 						"valeur4" => $valeur4
 51 					);
 52 				}
 53 
 54                 // Écriture des données dans le fichier variables.json
 55                 $contenu = json_encode($donnees);
 56                 file_put_contents("includes/variables.json", $contenu);
 57         } else {
 58                 // Si le formulaire n'a pas été soumis, vérifier si le fichier variables.json existe
 59                 if (file_exists("includes/variables.json")) {
 60                         // Charger les données depuis le fichier variables.json
 61                         $contenu = file_get_contents("includes/variables.json");
 62                         $donnees = json_decode($contenu, true);
 63                 }
 64         }
 65         ?>
 66 		
 67         <!DOCTYPE html>
 68         <html>
 69         <head>
 70                 <title>OCI - Phonebook</title>
 71                 <style>
 72                         /* Styles pour agrandir les champs de texte des colonnes 3 et 4 */
 73                         .large-input {
 74                                 width: 300px;
 75                         }
 76                         /* Styles pour le tableau */
 77                         body {
 78                                 font-family: Arial, sans-serif;
 79                                 background-color: #F8F8F8;
 80                         }
 81                         h1 {
 82                                 text-align: center;
 83                                 color: #333;
 84                         }
 85                         table {
 86                                 width: 100%;
 87                                 max-width: 800px;
 88                                 margin: 0 auto;
 89                                 border-collapse: collapse;
 90                                 background-color: #FFF;
 91                                 box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
 92                         }
 93                         th, td {
 94                                 padding: 10px;
 95                                 text-align: left;
 96                         }
 97                         th {
 98                                 background-color: #333;
 99                                 color: #FFF;
100                         }
101                         tr:nth-child(even) {
102                                 background-color: #F8F8F8;
103                         }
104                         tr:hover {
105                                 background-color: #EAEAEA;
106                         }
107                         input[type="submit"] {
108                                 display: block;
109                                 margin: 20px auto;
110                                 padding: 10px 20px;
111                                 font-size: 16px;
112                                 color: #FFF;
113                                 background-color: #333;
114                                 border: none;
115                                 cursor: pointer;
116                         }
117                         input[type="submit"]:hover {
118                                 background-color: #555;
119                         }
120 						.readonly {
121 								background-color: #F8F8F8;
122 								color: #999;
123 						}
124 						.copy-btn {
125 								display: inline-block;
126 								vertical-align: middle;
127 								margin-left: 10px;
128 								padding: 5px 10px;
129 								background-color: #333;
130 								color: #FFF;
131 								border: none;
132 								cursor: pointer;
133 						}
134 						.copy-btn:hover {
135 								background-color: #555;
136 }
137                 </style>
138         </head>
139         <body>
140                 <h1>Phonebook Wazo pour Snom</h1>
141                 <form method="post" action="<?php echo $_SERVER["PHP_SELF"]; ?>">
142                         <table>
143                                 <tr>
144                                         <th>#</th>
145                                         <th>Activer</th>
146                                         <th>Nom du client</th>
147                                         <th>URL Annuaire Wazo</th>
148                                         <th>ID nouvel annuaire</th>
149 										<th></th>
150                                 </tr>
151                                 <?php for ($i = 0; $i < 100; $i++) { ?>
152                                 <tr>
153                                         <td><?php echo $i + 1; ?></td>
154                                         <td><input type="checkbox" name="valeur1[]" <?php echo isset($donnees[$i]["valeur1"]) ? $donnees[$i]["valeur1"] : ''; ?>></td>
155                                         <td><input type="text" name="valeur2[]" value="<?php echo isset($donnees[$i]["valeur2"]) ? $donnees[$i]["valeur2"] : ''; ?>"></td>
156                                         <td><input type="text" name="valeur3[]" value="<?php echo isset($donnees[$i]["valeur3"]) ? $donnees[$i]["valeur3"] : ''; ?>" class="large-input"></td>
157                                         <td><input type="text" name="valeur4[]" value="<?php echo isset($donnees[$i]["valeur4"]) ? $donnees[$i]["valeur4"] : ''; ?>" class="large-input readonly" readonly></td>
158 						                <td><input type="button" value="Copier ID" class="copy-btn" onclick="copyToClipboard(this)"></td>
159 							   </tr>
160                                 <?php } ?>
161                         </table>
162                         <input type="submit" value="Enregistrer">
163                 </form>
164 				<script>
165 					// Fonction pour copier le contenu d'un champ de texte dans le presse-papier
166 					function copyToClipboard(button) {
167 						var input = button.parentNode.previousElementSibling.firstElementChild;
168 						input.select();
169 						input.setSelectionRange(0, 99999);
170 						document.execCommand("copy");
171 					}
172 				</script>
173         </body>
174         </html>
175 <!-- Fin du contenu HTML protégé -->
176 <?php
177 }
178 ?>

Script

Nous allons installer 2 scripts

  1. Télécharger les répertoires wazo
  2. Adapter les répertoires téléchargés
mkdir /etc/phonebook

Télécharger les répertoires

vi /etc/phonebook/download.sh
 1 #!/bin/bash
 2 
 3 # Charger les données depuis le fichier variables.json
 4 contenu=$(cat /var/www/html/includes/variables.json)
 5 donnees=($(echo "$contenu" | jq -r '.[] | "\(.valeur1),\(.valeur2),\(.valeur3),\(.valeur4)"'))
 6 
 7 # Parcourir les données et télécharger les fichiers
 8 for donnee in "${donnees[@]}"; do
 9     # Extraire les valeurs de chaque colonne
10     valeur1=$(echo "$donnee" | cut -d ',' -f 1)
11     valeur3=$(echo "$donnee" | cut -d ',' -f 3)
12     valeur4=$(echo "$donnee" | cut -d ',' -f 4)
13 
14     # Vérifier si la case de la colonne 1 est activée
15     if [ "$valeur1" == "checked" ]; then
16         # Télécharger le fichier avec wget
17         wget "$valeur3" -O "/var/www/html/phonebook/$valeur4"
18         echo "Le fichier $valeur4 a été téléchargé depuis $valeur3."
19     fi
20 done

Donner les droits d'exécution

chmod +x /etc/phonebook/download.sh

Adapter les répertoires

vi /etc/phonebook/custom.sh
 1 #!/bin/bash
 2 # Scrypt lancé par crontab
 3 
 4 sleep 5
 5 
 6 #Remplacer les caractères non apprecié
 7 sed -i 's/&#39;/ /' /var/www/html/phonebook/*
 8 sed -i 'y/_àçéèêëîïôöùüÂÇÉÈÊËÎÏÔÖÙÜ/ aceeeeiioouuACEEEEIIOOUU/' /var/www/html/phonebook/*
 9 
10 ## Adapter au format Tbook
11 sed -i 's#/SnomIPPhoneDirectory#/tbook#' /var/www/html/phonebook/*
12 sed -i 's/SnomIPPhoneDirectory/tbook complete="true"/' /var/www/html/phonebook/*
13 
14 sed -i 's#/DirectoryEntry#/item#' /var/www/html/phonebook/*
15 sed -i 's/DirectoryEntry/item context="active" type="colleagues"/' /var/www/html/phonebook/*
16 
17 sed -i 's#/Name>#/name>#' /var/www/html/phonebook/*
18 sed -i 's/Name>/name>/' /var/www/html/phonebook/*
19 
20 sed -i 's#/Telephone>#/number>#' /var/www/html/phonebook/*
21 sed -i 's/Telephone>/number>/' /var/www/html/phonebook/*

Donner les droits d'exécution

chmod +x /etc/phonebook/custom.sh

Planifier l'exécution des scripts

Dans l'exemple ci-dessous les répertoires seront mis à jours toute les 5 minutes

crontab -e
1 # Télécharger les répertoires
2 */5 * * * * sh /etc/phonebook/download.sh  > /dev/null
3 
4 # Adapter les répertoires
5 */5 * * * * sh /etc/phonebook/custom.sh  > /dev/null

Configuration page Web

Se connecter sur votre page web

https://phonebook.slemoal/phonebook.php

Renseigner votre mot de passe, valider.

Phonebook-admin.jpg

Renseigner:

  • Activer
  • Saisir URL de l'annuaire wazo
  • Enregistrer

L'ID est automatiquement généré par rapport au nom du client. Modifier celui ci obligera à modifier l'ID sur les bornes SNOM du client

Configuration des bornes snom

Méthode Manuel

Voici les paramètres à configurer sur la borne maitre afin que la borne à télécharger son annuaire toute les 5minutes

Coller l'id généré précedemment dans le champs "nom du fichier"

Attention, si le provisionning est activé avec une configuration différente, les paramètres seront écrasés.

PI: Le bouton "charger" permet de vider le répertoire actuel

Wazo-m900snom-manuel.jpg

Autoprovisionning Wazo

La méthode "propre" est de créer une template custom pour la borne M900

ci dessous un lien vers la documentation wazo:

https://wazo-platform.org/documentation/overview/provisioning-admin.html

Les champs nous intéressant sont: en ligne 56 le type de repertoire (O = Local, 1 LDAP, 2 XML Server, 3 XML browser server)

<phonebook_server_location perm="R">0</phonebook_server_location>

en ligne 57 l'adresse du serveur

<phonebook_location perm="R">https://phonebook.slemoal.fr/</phonebook_location>

le nouveau fichier M900.tpl devra ressembler à celui la:

 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <settings>
 3     <phone-settings>
 4     {%- if vlan_enabled %}
 5         <vlan_id>{{ vlan_id }}</vlan_id>
 6         <vlan_qos>{{ vlan_priority|d('0') }}</vlan_qos>
 7     {%- else %}
 8         <vlan_id></vlan_id>
 9         <vlan_qos></vlan_qos>
10     {%- endif %}
11 
12         <codec_tos>184</codec_tos>
13         <codec_priority_list perm="R">pcma,pcmu,g722,g729,telephone-event</codec_priority_list>
14         <signaling_tos>184</signaling_tos>
15 
16     {%- if admin_username %}
17         <http_user>{{ admin_username|e }}</http_user>
18     {%- else %}
19         <http_user>guest</http_user>
20     {%- endif %}
21 
22     {%- if admin_password %}
23         <admin_mode_password>{{ admin_password|e }}</admin_mode_password>
24         <http_pass>{{ admin_password|e }}</http_pass>
25     {%- else %}
26         <admin_mode_password>12345</admin_mode_password>
27         <http_pass>guest</http_pass>
28     {%- endif %}
29 
30     {%- if ntp_enabled %}
31         <ntp_server>{{ ntp_ip }}</ntp_server>
32     {%- else %}
33         <ntp_server></ntp_server>
34     {%- endif %}
35 
36     {%- for server in XX_servers.values() %}
37         <srv_sip_server_alias idx="{{ server['id'] }}">Wazo {{ server['id'] }}</srv_sip_server_alias>
38         <user_host idx="{{ server['id'] }}">{{ server['proxy_ip'] }}</user_host>
39         <srv_srtp_auth idx="{{ server['id'] }}">off</srv_srtp_auth>
40         <user_dtmf_info idx="{{ server['id'] }}">{{ server['dtmf_mode'] }}</user_dtmf_info>
41     {%- endfor %}
42 
43     {%- for line_no, line in sip_lines.items() %}
44         <user_active idx="{{ line_no }}">on</user_active>
45         <user_name idx="{{ line_no }}">{{ line['username']|e }}</user_name>
46         <user_pname idx="{{ line_no }}">{{ line['auth_username']|e }}</user_pname>
47         <user_pass idx="{{ line_no }}">{{ line['password']|e }}</user_pass>
48         <user_realname idx="{{ line_no }}">{{ line['display_name']|e }}</user_realname>
49         <user_mailbox idx="{{ line_no }}">{{ line['voicemail'] }}</user_mailbox>
50         <subscr_sip_hs_idx idx="{{ line_no }}">{{ line_no }}</subscr_sip_hs_idx>
51         <subscr_dect_ac_code idx="{{ line_no }}">{{ "{0:0>4}".format(line_no) }}</subscr_dect_ac_code>
52         <subscr_sip_ua_data_server_id idx="{{ line_no }}">{{ line['XX_server_id'] }}</subscr_sip_ua_data_server_id>
53         <codec_priority_list idx="{{ line_no }}" perm="R">pcma,pcmu,g722,g729,telephone-event</codec_priority_list>
54     {%- endfor %}
55 
56     {% if XX_xivo_phonebook_url -%}
57         <phonebook_server_location perm="R">0</phonebook_server_location>
58         <phonebook_location perm="R">https://phonebook.slemoal.fr/</phonebook_location>
59     {% endif -%}
60 
61     {%- if XX_lang %}
62         <language>{{ XX_lang[0] }}</language>
63         <web_language>{{ XX_lang[0] }}</web_language>
64         <tone_scheme>{{ XX_lang[1] }}</tone_scheme>
65     {%- endif %}
66 
67     {% block settings_suffix %}{% endblock %}
68     </phone-settings>
69 </settings>

Une fois la borne autoprovisionner, vous devrez vous connecter sur la borne maitre et renseigner le nom du fichier xml (phonebook-nomduclient.xml)

Wazo-m900snom-auto.jpg