OpenSIPS è uno strumento veramente fantastico che, con un po’ di studio e di fantasia, permette di realizzare anche configurazioni complesse come la funzionalità di direttore-segretaria tanto amata dai dirigenti.
In dettaglio, si tratta di configurare un certo interno (quello del direttore) non direttamente raggiungibile se non da alcuni interni (le segretarie): eventuali chiamate dirette a quell’interno saranno deviate alle segretarie che, una volta risposto, decideranno se concedere al chiamante la possibilità di interloquire con il direttore o meno.
La funzionalità è possibile se i terminali VoIP (volgarmente “i telefoni”) hanno il supporto per la segnalazione REFER descritta nell’RFC 3515:
The REFER method indicates that the recipient (identified by the Request-URI) should contact a third party using the contact information provided in the request.
Giusto per informazione, i terminali SNOM 7xx supportano egregiamente questa funzionalità.
Per realizzare questa funzione ho dovuto suddividerla in due parti, ovvero studiare un sistema per permettere le chiamate verso un certo interno solo da alcuni interni, chiamati successivamente “gatekeeper“. Ho utilizzato la ben nota funzionalità delle AVP (Attribute-Value Pair) caricabili dal database nella tabella usr_preferences per verificare se il numero chiamato (Request-URI, contenuto nella variabile $ru) ha la proprietà “in_filter” attiva.
Pertanto iniziamo con il popolare la tabella usr_preferences indicando il numero del BOSS e delle SEGRETARIE:
Il BOSS ha l’attributo “in_filter” ed in value l’URI della SEGRETARIA dove far rimbalzare la chiamata (in questo esempio è possibile indicare solo un interno). Le segretarie, da una a più di una, hanno l’attributo “gatekeeper” indicando in value l’interno del BOSS, così da identificare verso quale interno hanno potere di decidere chi far passare e chi no.
Nella sezione principale dello script di routing di OpenSIPS aggiungo:
route { [...] if(is_method("INVITE")) { if(avp_db_load("$ru","$avp(in_filter)")) { if(!cache_fetch("local","allow_$fU",$avp(i:55))) { # Il numero chiamante ha il "ticket di ingresso" ? if(avp_db_load("$fu","$avp(gatekeeper)")) { # Il chiamante è un GATEKEEPER ? xlog("L_INFO","$ci - FILTER Caller is a gatekeeper for $avp(gatekeeper) - $rU\n"); if(avp_check("$avp(gatekeeper)","eq/$rU")) { xlog("L_INFO","$ci - FILTER Call allowed for $rU from $fU\n"); } else { # Non è abilitato: fai rimbalzare la chiamata a $avp(in_filter) $ru = $avp(in_filter); set_dlg_flag("20"); # Assegna il TICKET xlog("L_INFO","$ci - FILTER Call diverted to $rU (from $fU)\n"); } } else { # Non è abilitato: fai rimbalzare la chiamata a $avp(in_filter) $ru = $avp(in_filter); set_dlg_flag("20"); # Assegna il TICKET xlog("L_INFO","$ci - FILTER Call diverted to $rU (from $fU)\n"); } } else { xlog("L_INFO","$ci - FILTER Call ALLOWED for $fU\n"); set_dlg_flag("21"); # Assegna il TICKET } } else { # Controlla se la chiamata proviene da un numero "gatekeeper" if(avp_db_load("$fu","$avp(gatekeeper)")) { set_dlg_flag("20"); # Assegna il TICKET } } } }
mentre nella route di relay:
route[relay] { [...] if (is_method("BYE")) { if(is_dlg_flag_set("21")) { # Se chiamata con TICKET, azzera i ticket ! cache_remove("local","allow_$tU"); # Cancella "ticket di ingresso" cache_remove("local","allow_$fU"); # Cancella "ticket di ingresso" xlog("L_INFO","$ci - FILTER Call REVOKE ALLOW for $tU and $fU\n"); } } if(is_method("REFER")) { if(is_dlg_flag_set("20")) { # Se devo assegnare il ticket.... xlog("L_INFO","$ci - FILTER Call ALLOW for $tU\n"); cache_store("local","allow_$tU","$ci",1200); # Emetti "ticket di ingresso" } } [...] }
Ok, è necessaria una breve spiegazione di come funziona il sistema. Partiamo da un riepilogo delle variabili di routing coinvolte:
$fu – reference to URI of ‘From’ header
$fU – reference to username in URI of ‘From’ header
$ru – reference to request’s URI
$rU – reference to username in request’s URI
$tu – reference to URI of ‘To’ header
$tU – reference to username in URI of ‘To’ header
Sintetizzando, con questo script ho realizzato un meccanismo basato su “ticket” per permettere alle chiamate esterne di passare direttamente al numero interno del BOSS: solamente le chiamate dotate di tale “ticket” possono passare il filtro di ingresso. L’emissione di tale “ticket”, attraverso l’inserimento in memoria dell’URI del chiamante ($fU) sfruttando il meccanismo del cache_store e cache_fetch (Memory Caching support from OpenSIPS) è gestito da due flag di dialogo, 20 e 21 (comando set_dlg_flag()), che rispettivamente servono le chiamate in ingresso e le chiamate eseguite dalle SEGRETARIE che successivamente dovranno essere inoltrate al BOSS.
Quindi, ricapitolando, le chiamate dirette al BOSS e risposte dalle segretarie avranno il flag 20 settato (set_dlg_flag(“20”)). In caso di redirezione ( if(is_method(“REFER”)) e if(is_dlg_flag_set(“20”))) sarà emesso un “ticket di ingresso” (cache_store(“local”,”allow_$tU”,”$ci”,1200)) con un timeout di 1200: tutte le chiamate che hanno tale ticket emesso (if(!cache_fetch(“local”,”allow_$fU”,$avp(i:55)))) sono abilitate a passare al numero del BOSS ed avranno il flag 21 settato (set_dlg_flag(“21”)).
Al termine della chiamata (if (is_method(“BYE”))) se il flag 21 è settato (if(is_dlg_flag_set(“21”))) si rimuovono i ticket di ingresso emessi (cache_remove(“local”,”allow_$tU”)).
Seguendo i messaggi di debug la sequenza è:
- FILTER Call diverted to SEGRETARIA (from UTENTE)
- FILTER Caller is a gatekeeper for BOSS – BOSS
- FILTER Call allowed for BOSS from SEGRETARIA
- FILTER Call ALLOW for UTENTE
- FILTER Call ALLOWED for UTENTE
- FILTER Call REVOKE ALLOW for UTENTE and BOSS
UTENTE, BOSS e SEGRETARIA saranno sostituiti dai rispettivi numeri interni. Tra i limiti del sistema, il primo è sicuramente il redirect della chiamata diretta al BOSS verso un solo numero di SEGRETARIA: andrebbe realizzato un sistema di call hunting. Il secondo è l’evidente dipendenza dal mantenimento del dialogo (l’ID univoco della chiamata è nella variabile $ci) tra le varie operazioni di redirect e forward.
Se conoscete un altro sistema per realizzare questa funzionalità o avete suggerimenti e migliorie da indicare, sono le benvenute !