Outbound call restriction

Hi,
If it's could be usefull for someone, here is a little AGI script I wrote in order to grant extension's outbound dialing, without aut.
I used the "account" field, so it would be improved as billing capabilities are disabled.
Be careful as this is not a standard @ pattern matching. We only watch the begenning of the dialed number.
All explanations and user guide inside the code.
[code:1]
//
// RESTRICTION D'ACCES SUR ROUTES SORTANTES PROGRAMMABLE PAR EXTENSION
//
// Written By: Bruno MOLTO (makko at wanadoo.fr)
//
//
//
//
// Contr̮̫le d'acc̮̬s aux routes sortantes pour chaque extension.
// Permet de contr̮̫ler poste per poste l'acc̮̬s aux routes sortantes, sans avoir ÃÆÃ saisir le mot de passe syst̮̩matiquement.
// Si l'acc̮̬s est interdit, le fonctionnement standard est conserv̮̩, et un mot de passe est demand̮̩ ÃÆÃ l'utilisateur.
// Ceci permet ÃÆÃ un administrateur de pouvoir tout de m̮̻me utiliser un poste restreint.
// Utilise le champ account code :
// - Le billing est donc inutilisable.
//
//
//
// Version 1.0 - le 05/05/2006
// todo:
// Impl̮̩menter correctement dans un champ sp̮̩cifique de la base asterisk
// Impl̮̩menter les couples route/pw route quand on aura de la place...
// G̮̩rer les connexions d'agent afin de bloquer/d̮̩bloquer provisoirement le poste
//
// Arguments d'appel :
// 1er argument : mot de passe affect̮̩ ÃÆÃ la route
// 2e argument : le num̮̩ro que l'utilisateur a compos̮̩
// 3e argument : le num̮̩ro de priorit̮̩ oÃÆÃ¹ on jumpe si appel OK
// 4e argument : le num̮̩ro de priorit̮̩ oÃÆÃ¹ on jumpe si appel interdit
//
//
//
// Fonctionnement :
//
// On utilise le mot de passe param̮̩trable sur les routes sortantes.
// et le code account code param̮̩trable sur chaque extension, et normalement utilis̮̩ par le billing.
//
// Pour prot̮̩ger l'acc̮̬s d'une route, il faut commencer par lui affecter un mot de passe.
//
// Pour le r̮̩glage de l'acc̮̬s de chaque poste (extension), param̮̩trer le account code comme suit:
//
// p=xxxx,y,ab,cd,ef,p=tttt,y,gh,ij
// xxxx et tttt sont les passwords correspondants aux routes trait̮̩es
// y est la strat̮̩gie appliqu̮̩e :
// - Si y=a, (strat̮̩gie "accept") on accepte par d̮̩faut touts les num̮̩ros, sauf ceux qui commencent par les exceptions qui suivent
// - Si y=d, (strat̮̩gie "deny") on rejette par d̮̩faut touts les num̮̩ros, sauf ceux qui commencent par les exceptions qui suivent
// ab,cd et ef d̮̩crivent la liste d'exceptions pour le premier mot de passe (donc la premi̮̬re route)
// gh et ij d̮̩crivent la liste d'exceptions pour le second mot de passe (donc la deuxi̮̬me route)
//
// Il n'y a pas de limitation concernant le nbre de routes ou de pr̮̩fixes d'exception trait̮̩s.
// La seule limitation est la longueur totale de la chaine qui ne peut exc̮̩der 150 caract̮̬res (taille du champ "data" ds MySQL)
//
// Exemple :
// p=123,a,06,08,p=5678,d,0466,0467,0468
// La route qui a le pw 123 va accepter tous les num̮̩ros, sauf ceux qui commencent par 06 ou 08
// La route qui a le pw 5678 va refuser tous les num̮̩ros, sauf ceux qui commencent par 0466,0467 ou 0468.
//
// Note : la recheche d'exception est effectu̮̩e de gauche ÃÆÃ droite dans la liste.
// La premi̮̬re exception qui correspond est valid̮̩e.
//
// Attention : afin de simplifier la chaine d'acc̮̬s, on ne tient compte que du mot de passe de la route,
// pas du couple (route/mot de passe). Ceci signifie qu'il faut imp̮̩rativement attribuer un mot de passe diff̮̩rent pour
// chaque route pour que la gestion soit correcte.
//
//
// Mise en service :
// Il faut simplement modifier la macro [macro-dialout-trunk] de extensions.conf comme suit :
// (insersion appel agi et d̮̩calage des lignes suivantes (et des destinations de saut bien sÃÆÃ»r)
//
//
/********************************MODIFICATION MACRO DIALOUT-TRUNK (Freepbx 2.0.1)**************************
;BEFORE
; dialout using a trunk, using pattern matching (don't strip any prefix)
; arg1 = trunk number, arg2 = number, arg3 = route password
;[macro-dialout-trunk]
;exten => s,1,GotoIf($["${ARG3}" = ""]?3:2) ; arg3 is pattern password
;exten => s,2,Authenticate(${ARG3})
;exten => s,3,Macro(user-callerid)
;exten => s,4,Macro(record-enable,${CALLERID(number)},OUT)
;exten => s,5,Macro(outbound-callerid,${ARG1})
;exten => s,6,Set(GROUP()=OUT_${ARG1})
;exten => s,7,GotoIf($[ ${GROUP_COUNT()} > ${OUTMAXCHANS_${ARG1}} ]?108)
; if we've used up the max channels, continue at (n+101)
;exten => s,8,Set(DIAL_NUMBER=${ARG2})
;exten => s,9,Set(DIAL_TRUNK=${ARG1})
;exten => s,10,AGI(fixlocalprefix) ; this sets DIAL_NUMBER to the proper dial string for this trunk
;exten => s,11,Set(OUTNUM=${OUTPREFIX_${ARG1}}${DIAL_NUMBER}) ; OUTNUM is the final dial number
;exten => s,12,Set(custom=${CUT(OUT_${ARG1},:,1)}) ; Custom trunks are prefixed with "AMP:"
;exten => s,13,GotoIf($["${custom}" = "AMP"]?16)
;exten => s,14,Dial(${OUT_${ARG1}}/${OUTNUM}|120|WT) ; Regular Trunk Dial, allow recording.Modifi̮̩ BM ajout̮̩ T
;exten => s,15,Goto(s-${DIALSTATUS},1)
; This is a custom trunk. Substitute $OUTNUM$ with the actual number and rebuild the dialstring
; example trunks: "AMP:CAPI/XXXXXXXX:b$OUTNUM$,30,r", "AMP:OH323/$OUTNUM$@XX.XX.XX.XX:XXXX"
;exten => s,16,Set(pre_num=${CUT(OUT_${ARG1},$,1)})
;exten => s,17,Set(the_num=${CUT(OUT_${ARG1},$,2)}) ; this is where we expect to find string OUTNUM
;exten => s,18,Set(post_num=${CUT(OUT_${ARG1},$,3)})
;exten => s,19,GotoIf($["${the_num}" = "OUTNUM"]?20:21) ; if we didn't find "OUTNUM", then skip to Dial
;exten => s,20,Set(the_num=${OUTNUM}) ; replace "OUTNUM" with the actual number to dial
;exten => s,21,Dial(${pre_num:4}${the_num}${post_num})
;exten => s,22,Goto(s-${DIALSTATUS},1)
;exten => s,108,Noop(max channels used up)
;exten => s-BUSY,1,NoOp(Trunk is reporting BUSY)
;exten => s-BUSY,2,Busy()
;exten => s-BUSY,3,Wait(60)
;exten => s-BUSY,4,NoOp()
;exten => _s-.,1,NoOp(Dial failed due to ${DIALSTATUS})
;AFTER
; dialout using a trunk, using pattern matching (don't strip any prefix)
; arg1 = trunk number, arg2 = number, arg3 = route password
[macro-dialout-trunk]
exten => s,1,GotoIf($["${ARG3}" = ""]?4:2) ; arg3 is pattern password
exten => s,2,AGI(checktrunkrights,${ARG3}|${ARG2}|4|3);Verification autorisation acc̮̬s du poste sur la route
exten => s,3,Authenticate(${ARG3})
exten => s,4,Macro(user-callerid)
exten => s,5,Macro(record-enable,${CALLERID(number)},OUT)
exten => s,6,Macro(outbound-callerid,${ARG1})
exten => s,7,Set(GROUP()=OUT_${ARG1})
exten => s,8,GotoIf($[ ${GROUP_COUNT()} > ${OUTMAXCHANS_${ARG1}} ]?109)
; if we've used up the max channels, continue at (n+101)
exten => s,9,Set(DIAL_NUMBER=${ARG2})
exten => s,10,Set(DIAL_TRUNK=${ARG1})
exten => s,11,AGI(fixlocalprefix) ; this sets DIAL_NUMBER to the proper dial string for this trunk
exten => s,12,Set(OUTNUM=${OUTPREFIX_${ARG1}}${DIAL_NUMBER}) ; OUTNUM is the final dial number
exten => s,13,Set(custom=${CUT(OUT_${ARG1},:,1)}) ; Custom trunks are prefixed with "AMP:"
exten => s,14,GotoIf($["${custom}" = "AMP"]?17)
exten => s,15,Dial(${OUT_${ARG1}}/${OUTNUM}|120|WT) ; Regular Trunk Dial, allow recording.Modifi̮̩ BM ajout̮̩ T
exten => s,16,Goto(s-${DIALSTATUS},1)
; This is a custom trunk. Substitute $OUTNUM$ with the actual number and rebuild the dialstring
; example trunks: "AMP:CAPI/XXXXXXXX:b$OUTNUM$,30,r", "AMP:OH323/$OUTNUM$@XX.XX.XX.XX:XXXX"
exten => s,17,Set(pre_num=${CUT(OUT_${ARG1},$,1)})
exten => s,18,Set(the_num=${CUT(OUT_${ARG1},$,2)}) ; this is where we expect to find string OUTNUM
exten => s,19,Set(post_num=${CUT(OUT_${ARG1},$,3)})
exten => s,20,GotoIf($["${the_num}" = "OUTNUM"]?21:22) ; if we didn't find "OUTNUM", then skip to Dial
exten => s,21,Set(the_num=${OUTNUM}) ; replace "OUTNUM" with the actual number to dial
exten => s,22,Dial(${pre_num:4}${the_num}${post_num})
exten => s,23,Goto(s-${DIALSTATUS},1)
exten => s,109,Noop(max channels used up)
exten => s-BUSY,1,NoOp(Trunk is reporting BUSY)
exten => s-BUSY,2,Busy()
exten => s-BUSY,3,Wait(60)
exten => s-BUSY,4,NoOp()
exten => _s-.,1,NoOp(Dial failed due to ${DIALSTATUS})
********************************MODIFICATION MACRO DIALOUT-TRUNK**************************/
#include
#include
#include
#include
//Prototypes bibliotheque cagi
#include "cagi.h"
//Param̮̬tres de connexion ÃÆÃ la base
#define MYSQL_HOST "localhost"
#define MYSQL_DB "asterisk"
#define MYSQL_LOGIN "asteriskuser"
#define MYSQL_PASSWD "amp109"
int main(int argc, char *argv[])
{
//argc contient le nombre d'arguments pass̮̩s lors de l'appel
//argv est un tableau de chaines contenant les arguments
//ATTENTION : le premier argument est le nom de l'executable
AGI_TOOLS agi;//Objet agi
AGI_CMD_RESULT agires;
char texte [1024];//pour messages debug
int i,j;
char routepw[100];//Mot de passe de la route pass̮̩ en argument au script agi
char dialnumber[100];//Num̮̩ro compos̮̩ pass̮̩ en argument au script agi
char account[1024];//Chaine account code d̮̩finie dans les extensions et pass̮̩e dans les variables agi
int priok,prinok;//Priorit̮̩s cibles extraites le la ligne de commande
//Variables temporaires pour extraction des chaines account
char temppassword[100];
char strat;//Strat̮̩gie d'autorisation a=accepte tout par d̮̩faut/d=refuse tout par d̮̩faut
int ok;//sort de la recherche. ok=0 =>appel interdit. ok=1 =>appel ok
int debutex;//flag extraction exception
char except[100];//Chaine d'extraction des exceptions
int outsearch;//Fin de scrutation des exceptions
//Variables pour connexion mySQL
MYSQL mysql;
MYSQL_RES *result;
MYSQL_ROW row;
int baseconnectee=1;
char query[512];
char table[10];//La table varie en fct de la technologie du channel
char extension[10];//Le num̮̩ro d'"extension" (la cl̮̩ dans la table)
//Init agitools
AGITool_Init(&agi);
agi_verbose("Beginning check route rigths",4);
//lecture de la chaine channel dans les variables agi
strcpy(table,AGITool_ListGetVal(agi.agi_vars,"agi_type"));
//On passe en minuscules car c'est ainsi que sont nomm̮̩es les tables ds la base
for(i=0;i


