mod_directory: New Search by Name Directory IVR (MODAPP-325)
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@14984 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
parent
94e7412db6
commit
54038e9975
|
@ -10,6 +10,7 @@ applications/mod_fifo
|
|||
#applications/mod_fax
|
||||
#applications/mod_curl
|
||||
applications/mod_voicemail
|
||||
#applications/mod_directory
|
||||
#applications/mod_lcr
|
||||
applications/mod_limit
|
||||
applications/mod_expr
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<configuration name="directory.conf" description="Directory">
|
||||
<settings>
|
||||
</settings>
|
||||
<profiles>
|
||||
<profile name="default">
|
||||
<param name="max-menu-attempts" value="3"/>
|
||||
<param name="min-search-digits" value="3"/>
|
||||
<param name="terminator-key" value="#"/>
|
||||
<param name="digit-timeout" value="3000"/>
|
||||
<param name="max-result" value="5"/>
|
||||
<param name="next-key" value="6"/>
|
||||
<param name="prev-key" value="4"/>
|
||||
<param name="switch-order-key" value="*"/>
|
||||
<param name="select-name-key" value="1"/>
|
||||
<param name="new-search-key" value="3"/>
|
||||
<param name="search-order" value="last_name"/>
|
||||
</profile>
|
||||
</profiles>
|
||||
</configuration>
|
|
@ -44,6 +44,7 @@
|
|||
<load module="mod_expr"/>
|
||||
<load module="mod_fifo"/>
|
||||
<load module="mod_voicemail"/>
|
||||
<load module="mod_directory"/>
|
||||
<!--<load module="mod_fax"/>-->
|
||||
<!--<load module="mod_lcr"/>-->
|
||||
<load module="mod_limit"/>
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
<include><!--This line will be ignored it's here to validate the xml and is optional -->
|
||||
<macro name="directory_intro">
|
||||
<input pattern="^(last_name)" break_on_match="false">
|
||||
<match>
|
||||
<action function="play-file" data="directory/dir-enter-person.wav"/>
|
||||
<action function="play-file" data="directory/dir-last_name.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
<input pattern="^(first_name)" break_on_match="false">
|
||||
<match>
|
||||
<action function="play-file" data="directory/dir-enter-person.wav"/>
|
||||
<action function="play-file" data="directory/dir-first_name.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
<input pattern="^(last_name):([0-9#*])$" break_on_match="false">
|
||||
<match>
|
||||
<action function="play-file" data="directory/dir-to_search_by.wav"/>
|
||||
<action function="play-file" data="directory/dir-first_name.wav"/>
|
||||
<action function="play-file" data="directory/dir-press.wav"/>
|
||||
<action function="say" data="$2" method="pronounced" type="name_spelled"/>
|
||||
</match>
|
||||
</input>
|
||||
<input pattern="^(first_name):([0-9#*])$" break_on_match="false">
|
||||
<match>
|
||||
<action function="play-file" data="directory/dir-to_search_by.wav"/>
|
||||
<action function="play-file" data="directory/dir-last_name.wav"/>
|
||||
<action function="play-file" data="directory/dir-press.wav"/>
|
||||
<action function="say" data="$2" method="pronounced" type="name_spelled"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="directory_min_search_digits">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="play-file" data="directory/dir-specify_mininum_first.wav"/>
|
||||
<action function="say" data="$1" method="pronounced" type="items"/>
|
||||
<action function="play-file" data="directory/dir-letters_of_person_name.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_count">
|
||||
<input pattern="^0$" break_on_match="true">
|
||||
<match>
|
||||
<action function="play-file" data="directory/dir-no_match_entry.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="say" data="$1" method="pronounced" type="items"/>
|
||||
<action function="play-file" data="directory/dir-result_match.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_count_too_large">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="play-file" data="directory/dir-to_many_result.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_last">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="play-file" data="directory/dir-no_more_result.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_item">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="play-file" data="directory/dir-result_number.wav"/>
|
||||
<action function="say" data="$1" method="pronounced" type="items"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_at">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="play-file" data="directory/dir-at_extension.wav"/>
|
||||
<action function="say" data="$1" method="pronounced" type="name_spelled"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_menu">
|
||||
<input pattern="^([0-9#*]),([0-9#*]),([0-9#*]),([0-9#*])$">
|
||||
<match>
|
||||
<action function="play-file" data="directory/dir-to_select_entry.wav"/>
|
||||
<action function="play-file" data="directory/dir-press.wav"/>
|
||||
<action function="say" data="$1" method="pronounced" type="name_spelled"/>
|
||||
<action function="play-file" data="directory/dir-for_next.wav"/>
|
||||
<action function="play-file" data="directory/dir-press.wav"/>
|
||||
<action function="say" data="$2" method="pronounced" type="name_spelled"/>
|
||||
<action function="play-file" data="directory/dir-for_prev.wav"/>
|
||||
<action function="play-file" data="directory/dir-press.wav"/>
|
||||
<action function="say" data="$3" method="pronounced" type="name_spelled"/>
|
||||
<action function="play-file" data="directory/dir-to_make_new_search.wav"/>
|
||||
<action function="play-file" data="directory/dir-press.wav"/>
|
||||
<action function="say" data="$4" method="pronounced" type="name_spelled"/>
|
||||
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_say_name">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="say" data="$1" method="pronounced" type="name_spelled"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
</include><!--This line will be ignored it's here to validate the xml and is optional -->
|
|
@ -0,0 +1,96 @@
|
|||
<include><!--This line will be ignored it's here to validate the xml and is optional -->
|
||||
|
||||
<macro name="directory_intro">
|
||||
<input pattern="^(last_name)" break_on_match="false">
|
||||
<match>
|
||||
<action function="speak-text" data="Please enter the first few digit of the person last name"/>
|
||||
</match>
|
||||
</input>
|
||||
<input pattern="^(first_name)" break_on_match="false">
|
||||
<match>
|
||||
<action function="speak-text" data="Please enter the first few digit of the person first name"/>
|
||||
</match>
|
||||
</input>
|
||||
<input pattern="^(last_name):([0-9#*])$" break_on_match="false">
|
||||
<match>
|
||||
<action function="speak-text" data="to search by first name, press $2"/>
|
||||
</match>
|
||||
</input>
|
||||
<input pattern="^(first_name):([0-9#*])$" break_on_match="false">
|
||||
<match>
|
||||
<action function="speak-text" data="to search by last name, press $2"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="directory_min_search_digits">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="speak-text" data="You need to specify a minimum the first $1 letters of the person name, try again."/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_count">
|
||||
<input pattern="^0$" break_on_match="true">
|
||||
<match>
|
||||
<action function="speak-text" data="Your search match no user on this system, try again."/>
|
||||
</match>
|
||||
</input>
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="speak-text" data="$1 result match your search"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_count_too_large">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="speak-text" data="Your search returned too many result, please try again"/>
|
||||
</match>
|
||||
</input>
|
||||
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_last">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="speak-text" data="No more result"/>
|
||||
</match>
|
||||
</input>
|
||||
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_item">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="speak-text" data="Result number $1"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_menu">
|
||||
<input pattern="^([0-9#*]),([0-9#*]),([0-9#*]),([0-9#*])$">
|
||||
<match>
|
||||
<action function="speak-text" data="To select this entry press $1, for the next entry press $2, for the previous entry press $3, to make a new search press $4"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_at">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="speak-text" data="at extension $1"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
<macro name="directory_result_say_name">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="speak-text" data="$1"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
</include><!--This line will be ignored it's here to validate the xml and is optional -->
|
|
@ -3,5 +3,6 @@
|
|||
<X-PRE-PROCESS cmd="include" data="demo/*.xml"/> <!-- Note: this now grabs whole subdir, previously grabbed only demo.xml -->
|
||||
<!--voicemail_en_tts is purely implemented with tts, we have the files based one that is the default. -->
|
||||
<X-PRE-PROCESS cmd="include" data="vm/sounds.xml"/> <!-- vm/tts.xml if you want to use tts and have cepstral -->
|
||||
<X-PRE-PROCESS cmd="include" data="dir/sounds.xml"/> <!-- dir/tts.xml if you want to use tts and have cepstral -->
|
||||
</language>
|
||||
</include>
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
<include><!--This line will be ignored it's here to validate the xml and is optional -->
|
||||
<macro name="directory_intro">
|
||||
<input pattern="^(last_name)" break_on_match="false">
|
||||
<match>
|
||||
<action function="play-file" data="directory/dir-enter-person.wav"/>
|
||||
<action function="play-file" data="directory/dir-last_name.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
<input pattern="^(first_name)" break_on_match="false">
|
||||
<match>
|
||||
<action function="play-file" data="directory/dir-enter-person.wav"/>
|
||||
<action function="play-file" data="directory/dir-first_name.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
<input pattern="^(last_name):([0-9#*])$" break_on_match="false">
|
||||
<match>
|
||||
<action function="play-file" data="directory/dir-to_search_by.wav"/>
|
||||
<action function="play-file" data="directory/dir-first_name.wav"/>
|
||||
<action function="play-file" data="directory/dir-press.wav"/>
|
||||
<action function="say" data="$2" method="pronounced" type="name_spelled"/>
|
||||
</match>
|
||||
</input>
|
||||
<input pattern="^(first_name):([0-9#*])$" break_on_match="false">
|
||||
<match>
|
||||
<action function="play-file" data="directory/dir-to_search_by.wav"/>
|
||||
<action function="play-file" data="directory/dir-last_name.wav"/>
|
||||
<action function="play-file" data="directory/dir-press.wav"/>
|
||||
<action function="say" data="$2" method="pronounced" type="name_spelled"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="directory_min_search_digits">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="play-file" data="directory/dir-specify_mininum_first.wav"/>
|
||||
<action function="say" data="$1" method="pronounced" type="items"/>
|
||||
<action function="play-file" data="directory/dir-letters_of_person_name.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_count">
|
||||
<input pattern="^0$" break_on_match="true">
|
||||
<match>
|
||||
<action function="play-file" data="directory/dir-no_match_entry.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="say" data="$1" method="pronounced" type="items"/>
|
||||
<action function="play-file" data="directory/dir-result_match.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_count_too_large">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="play-file" data="directory/dir-to_many_result.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_last">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="play-file" data="directory/dir-no_more_result.wav"/>
|
||||
</match>
|
||||
</input>
|
||||
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_item">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="play-file" data="directory/dir-result_number.wav"/>
|
||||
<action function="say" data="$1" method="pronounced" type="items"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_at">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="play-file" data="directory/dir-at_extension.wav"/>
|
||||
<action function="say" data="$1" method="pronounced" type="name_spelled"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_menu">
|
||||
<input pattern="^([0-9#*]),([0-9#*]),([0-9#*]),([0-9#*])$">
|
||||
<match>
|
||||
<action function="play-file" data="directory/dir-to_select_entry.wav"/>
|
||||
<action function="play-file" data="directory/dir-press.wav"/>
|
||||
<action function="say" data="$1" method="pronounced" type="name_spelled"/>
|
||||
<action function="play-file" data="directory/dir-for_next.wav"/>
|
||||
<action function="play-file" data="directory/dir-press.wav"/>
|
||||
<action function="say" data="$2" method="pronounced" type="name_spelled"/>
|
||||
<action function="play-file" data="directory/dir-for_prev.wav"/>
|
||||
<action function="play-file" data="directory/dir-press.wav"/>
|
||||
<action function="say" data="$3" method="pronounced" type="name_spelled"/>
|
||||
<action function="play-file" data="directory/dir-to_make_new_search.wav"/>
|
||||
<action function="play-file" data="directory/dir-press.wav"/>
|
||||
<action function="say" data="$4" method="pronounced" type="name_spelled"/>
|
||||
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_say_name">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="say" data="$1" method="pronounced" type="name_spelled"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
</include><!--This line will be ignored it's here to validate the xml and is optional -->
|
|
@ -0,0 +1,101 @@
|
|||
<include><!--This line will be ignored it's here to validate the xml and is optional -->
|
||||
|
||||
<macro name="directory_intro">
|
||||
<input pattern="^(last_name)" break_on_match="false">
|
||||
<match>
|
||||
<action function="speak-text" data="Veuillez entrer les premiere lettre du nom de famille"/>
|
||||
</match>
|
||||
</input>
|
||||
<input pattern="^(first_name)" break_on_match="false">
|
||||
<match>
|
||||
<action function="speak-text" data="Veuillez entrer les premiere lettre du prénom"/>
|
||||
</match>
|
||||
</input>
|
||||
<input pattern="^(last_name):([0-9#*])$" break_on_match="false">
|
||||
<match>
|
||||
<action function="speak-text" data="pour chercher par prénom, appuyer sur le $2"/>
|
||||
</match>
|
||||
</input>
|
||||
<input pattern="^(first_name):([0-9#*])$" break_on_match="false">
|
||||
<match>
|
||||
<action function="speak-text" data="pour chercher par nom de famille, appuyer sur le $2"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="directory_min_search_digits">
|
||||
<input pattern="^(1)$">
|
||||
<match>
|
||||
<action function="speak-text" data="Vous devez entré au minimum une lettre du nom de la personne, essayer de nouveaux"/>
|
||||
</match>
|
||||
</input>
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="speak-text" data="Vous devez entré au minimum $1 lettre du nom de la personne, essayer de nouveaux"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_count">
|
||||
<input pattern="^0$" break_on_match="true">
|
||||
<match>
|
||||
<action function="speak-text" data="Votre recherche a retourner aucune résultat, essayer de nouveaux"/>
|
||||
</match>
|
||||
</input>
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="speak-text" data="$1 résultats correspond a votre recherche"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_count_too_large">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="speak-text" data="Votre recherche retourne trop de résultat, essayer de nouveaux."/>
|
||||
</match>
|
||||
</input>
|
||||
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_last">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="speak-text" data="Fin des résultats."/>
|
||||
</match>
|
||||
</input>
|
||||
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_item">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="speak-text" data="Résultat numéro $1"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_menu">
|
||||
<input pattern="^([0-9#*]),([0-9#*]),([0-9#*]),([0-9#*])$">
|
||||
<match>
|
||||
<action function="speak-text" data="Pour selectionner ce nom appuyer sur le $1, pour le nom suivant appuyer sur le $2, pour le nom précédent appuyer sur le $3 ou pour faire une nouvelle recherche appuyer sur le $4"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
<macro name="directory_result_at">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="speak-text" data="aux poste $1"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
<macro name="directory_result_say_name">
|
||||
<input pattern="^(.*)$">
|
||||
<match>
|
||||
<action function="speak-text" data="$1"/>
|
||||
</match>
|
||||
</input>
|
||||
</macro>
|
||||
|
||||
</include><!--This line will be ignored it's here to validate the xml and is optional -->
|
|
@ -3,5 +3,6 @@
|
|||
<X-PRE-PROCESS cmd="include" data="demo/demo.xml"/>
|
||||
<!--voicemail_fr_tts is purely implemented with tts, we need a files based implementation too -->
|
||||
<X-PRE-PROCESS cmd="include" data="vm/sounds.xml"/>
|
||||
<X-PRE-PROCESS cmd="include" data="dir/sounds.xml"/> <!-- dir/tts.xml if you want to use tts and have cepstral -->
|
||||
</language>
|
||||
</include>
|
||||
|
|
|
@ -219,6 +219,25 @@
|
|||
<prompt phrase="urgent" filename="vm-urgent.wav"/>
|
||||
<prompt phrase="That mailbox is full. Please try your call again later." filename="vm-mailbox_full.wav"/>
|
||||
</voicemail>
|
||||
<directory>
|
||||
<prompt phrase="Please enter the first few letters of the persons" filename="dir-enter-person.wav"/>
|
||||
<prompt phrase="last name." filename="dir-last_name.wav"/>
|
||||
<prompt phrase="To search by" filename="dir-to_search_by.wav"/>
|
||||
<prompt phrase="first name." filename="dir-first_name.wav"/>
|
||||
<prompt phrase="Your search matched no results, try again." filename="dir-no_match_entry.wav"/>
|
||||
<prompt phrase="results matched your search." filename="dir-result_match.wav"/>
|
||||
<prompt phrase="Your search returned too many results, please try again." filename="dir-to_many_result.wav"/>
|
||||
<prompt phrase="No more results." filename="dir-no_more_result.wav"/>
|
||||
<prompt phrase="Result number." filename="dir-result_number.wav"/>
|
||||
<prompt phrase="at extension" filename="dir-at_extension.wav"/>
|
||||
<prompt phrase="To select this entry" filename="dir-to_select_entry.wav"/>
|
||||
<prompt phrase="For the next entry" filename="dir-for_next.wav"/>
|
||||
<prompt phrase="For the previous entry" filename="dir-for_prev.wav"/>
|
||||
<prompt phrase="To start a new search" filename="dir-to_make_new_search.wav"/>
|
||||
<prompt phrase="You need to specify a minimum of" filename="dir-specify_mininum_first.wav"/>
|
||||
<prompt phrase="letters of the person name, try again." filename="dir-letters_of_person_name.wav"/>
|
||||
<prompt phrase="press" filename="dir-press.wav"/>
|
||||
</directory>
|
||||
<conference>
|
||||
<prompt phrase="NULL" filename="conf-ack.wav" type="tone"/>
|
||||
<prompt phrase="NULL" filename="conf-nack.wav" type="tone"/>
|
||||
|
|
|
@ -218,6 +218,25 @@
|
|||
<prompt phrase="pour répondre à cet appel tout de suite" filename="vm-return_call.wav"/>
|
||||
<prompt phrase="urgent" filename="vm-urgent.wav"/>
|
||||
</voicemail>
|
||||
<directory>
|
||||
<prompt phrase="Veuillez entrer les premiere lettre du" filename="dir-enter-person.wav"/>
|
||||
<prompt phrase="nom de famille." filename="dir-last_name.wav"/>
|
||||
<prompt phrase="Pour chercher par" filename="dir-to_search_by.wav"/>
|
||||
<prompt phrase="prénom." filename="dir-first_name.wav"/>
|
||||
<prompt phrase="Votre recherche a retourner aucune résultat, essayer de nouveaux." filename="dir-no_match_entry.wav"/>
|
||||
<prompt phrase="résultats correspond a votre recherche." filename="dir-result_match.wav"/>
|
||||
<prompt phrase="Votre recherche retourne trop de résultat, essayer de nouveaux." filename="dir-to_many_result.wav"/>
|
||||
<prompt phrase="Fin des résultats." filename="dir-no_more_result.wav"/>
|
||||
<prompt phrase="Résultat numéro" filename="dir-result_number.wav"/>
|
||||
<prompt phrase="aux poste" filename="dir-at_extension.wav"/>
|
||||
<prompt phrase="Pour selectionner ce nom" filename="dir-to_select_entry.wav"/>
|
||||
<prompt phrase="Pour le nom suivant" filename="dir-for_next.wav"/>
|
||||
<prompt phrase="Pour le nom précédent" filename="dir-for_prev.wav"/>
|
||||
<prompt phrase="Pour faire une nouvelle recherche" filename="dir-to_make_new_search.wav"/>
|
||||
<prompt phrase="Vous devez entré au minimum" filename="dir-specify_mininum_first.wav"/>
|
||||
<prompt phrase="lettre du nom de la personne, essayer de nouveaux." filename="dir-letters_of_person_name.wav"/>
|
||||
<prompt phrase="Appuyez" filename="dir-press.wav"/>
|
||||
</directory>
|
||||
<conference>
|
||||
<prompt phrase="NULL" filename="conf-ack.wav" type="tone"/>
|
||||
<prompt phrase="NULL" filename="conf-nack.wav" type="tone"/>
|
||||
|
|
|
@ -314,7 +314,7 @@ export QA_RPATHS=$[ 0x0001|0x0002 ]
|
|||
|
||||
PASSTHRU_CODEC_MODULES="codecs/mod_g729 codecs/mod_g723_1 codecs/mod_amr codecs/mod_amrwb"
|
||||
SPIDERMONKEY_MODULES="languages/mod_spidermonkey languages/mod_spidermonkey_curl languages/mod_spidermonkey_core_db languages/mod_spidermonkey_odbc languages/mod_spidermonkey_socket languages/mod_spidermonkey_teletone"
|
||||
APPLICATIONS_MODULES="applications/mod_commands applications/mod_conference applications/mod_dptools applications/mod_enum applications/mod_esf applications/mod_expr applications/mod_fifo applications/mod_limit applications/mod_rss applications/mod_voicemail applications/mod_fsv applications/mod_lcr applications/mod_easyroute applications/mod_stress applications/mod_vmd applications/mod_limit applications/mod_soundtouch applications/mod_fax"
|
||||
APPLICATIONS_MODULES="applications/mod_commands applications/mod_conference applications/mod_dptools applications/mod_enum applications/mod_esf applications/mod_expr applications/mod_fifo applications/mod_limit applications/mod_rss applications/mod_voicemail applications/mod_directory applications/mod_fsv applications/mod_lcr applications/mod_easyroute applications/mod_stress applications/mod_vmd applications/mod_limit applications/mod_soundtouch applications/mod_fax"
|
||||
CODECS_MODULES="codecs/mod_ilbc codecs/mod_h26x codecs/mod_voipcodecs codecs/mod_speex codecs/mod_celt codecs/mod_siren"
|
||||
DIALPLANS_MODULES="dialplans/mod_dialplan_asterisk dialplans/mod_dialplan_directory dialplans/mod_dialplan_xml"
|
||||
DIRECTORIES_MODULES=""
|
||||
|
@ -541,6 +541,7 @@ userdel freeswitch
|
|||
%{prefix}/mod/mod_limit.so*
|
||||
%{prefix}/mod/mod_rss.so*
|
||||
%{prefix}/mod/mod_voicemail.so*
|
||||
%{prefix}/mod/mod_directory.so*
|
||||
%{prefix}/mod/mod_pocketsphinx.so*
|
||||
%{prefix}/mod/mod_flite.so*
|
||||
%{prefix}/mod/mod_ilbc.so*
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
BASE=../../../..
|
||||
include $(BASE)/build/modmake.rules
|
|
@ -0,0 +1,287 @@
|
|||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="9.00"
|
||||
Name="mod_directory"
|
||||
ProjectGUID="{11C9BC3D-45E9-46E3-BE84-B8CEE4685E39}"
|
||||
RootNamespace="mod_directory"
|
||||
Keyword="Win32Proj"
|
||||
TargetFrameworkVersion="131072"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
<Platform
|
||||
Name="x64"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
ConfigurationType="2"
|
||||
InheritedPropertySheets="..\..\..\..\w32\module_debug.vsprops"
|
||||
CharacterSet="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="0"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
RandomizedBaseAddress="1"
|
||||
DataExecutionPrevention="0"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Debug|x64"
|
||||
OutputDirectory="$(PlatformName)\$(ConfigurationName)"
|
||||
IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
|
||||
ConfigurationType="2"
|
||||
InheritedPropertySheets="..\..\..\..\w32\module_debug.vsprops"
|
||||
CharacterSet="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
TargetEnvironment="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="0"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
OutputFile="$(SolutionDir)$(PlatformName)\$(ConfigurationName)/mod/$(ProjectName).dll"
|
||||
RandomizedBaseAddress="1"
|
||||
DataExecutionPrevention="0"
|
||||
TargetMachine="17"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
ConfigurationType="2"
|
||||
InheritedPropertySheets="..\..\..\..\w32\module_release.vsprops"
|
||||
CharacterSet="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="0"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
RandomizedBaseAddress="1"
|
||||
DataExecutionPrevention="0"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|x64"
|
||||
OutputDirectory="$(PlatformName)\$(ConfigurationName)"
|
||||
IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
|
||||
ConfigurationType="2"
|
||||
InheritedPropertySheets="..\..\..\..\w32\module_release.vsprops"
|
||||
CharacterSet="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
TargetEnvironment="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="0"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
OutputFile="$(SolutionDir)$(PlatformName)\$(ConfigurationName)/mod/$(ProjectName).dll"
|
||||
RandomizedBaseAddress="1"
|
||||
DataExecutionPrevention="0"
|
||||
TargetMachine="17"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<File
|
||||
RelativePath=".\mod_directory.c"
|
||||
>
|
||||
</File>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
|
@ -0,0 +1,990 @@
|
|||
/*
|
||||
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
* Copyright (C) 2005-2009, Anthony Minessale II <anthm@freeswitch.org>
|
||||
*
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Anthony Minessale II <anthm@freeswitch.org>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Marc Olivier Chouinard <mochouinard at moctel dot com>
|
||||
*
|
||||
*
|
||||
* mod_directory.c -- Search by Name Directory IVR
|
||||
*
|
||||
*/
|
||||
#include <switch.h>
|
||||
|
||||
/* Prototypes */
|
||||
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_directory_shutdown);
|
||||
SWITCH_MODULE_LOAD_FUNCTION(mod_directory_load);
|
||||
|
||||
SWITCH_MODULE_DEFINITION(mod_directory, mod_directory_load, mod_directory_shutdown, NULL);
|
||||
|
||||
static const char *global_cf = "directory.conf";
|
||||
|
||||
static char dir_sql[] =
|
||||
"CREATE TABLE directory_search (\n"
|
||||
" hostname VARCHAR(255),\n"
|
||||
" uuid VARCHAR(255),\n"
|
||||
" extension VARCHAR(255),\n"
|
||||
" full_name VARCHAR(255),\n"
|
||||
" full_name_digit VARCHAR(255),\n"
|
||||
" first_name VARCHAR(255),\n"
|
||||
" first_name_digit VARCHAR(255),\n"
|
||||
" last_name VARCHAR(255),\n"
|
||||
" last_name_digit VARCHAR(255),\n"
|
||||
" name_visible INTEGER,\n"
|
||||
" exten_visible INTEGER\n"
|
||||
");\n";
|
||||
|
||||
#define DIR_RESULT_ITEM "directory_result_item"
|
||||
#define DIR_RESULT_SAY_NAME "directory_result_say_name"
|
||||
#define DIR_RESULT_AT "directory_result_at"
|
||||
#define DIR_RESULT_MENU "directory_result_menu"
|
||||
#define DIR_INTRO "directory_intro"
|
||||
#define DIR_MIN_SEARCH_DIGITS "directory_min_search_digits"
|
||||
#define DIR_RESULT_COUNT "directory_result_count"
|
||||
#define DIR_RESULT_COUNT_TOO_LARGE "directory_result_count_too_large"
|
||||
#define DIR_RESULT_LAST "directory_result_last"
|
||||
|
||||
static switch_xml_config_string_options_t config_dtmf = { NULL, 2, "[0-9#\\*]" };
|
||||
static switch_xml_config_int_options_t config_int_digit_timeout = { SWITCH_TRUE, 0, SWITCH_TRUE, 30000 };
|
||||
static switch_xml_config_int_options_t config_int_ht_0 = { SWITCH_TRUE, 0 };
|
||||
|
||||
static struct {
|
||||
switch_hash_t *profile_hash;
|
||||
char hostname[256];
|
||||
int integer;
|
||||
int debug;
|
||||
char *dbname;
|
||||
switch_mutex_t *mutex;
|
||||
switch_memory_pool_t *pool;
|
||||
} globals;
|
||||
|
||||
#define DIR_PROFILE_CONFIGITEM_COUNT 100
|
||||
|
||||
struct dir_profile {
|
||||
char *name;
|
||||
|
||||
char next_key[2];
|
||||
char prev_key[2];
|
||||
char select_name_key[2];
|
||||
char new_search_key[2];
|
||||
|
||||
char terminator_key[2];
|
||||
char switch_order_key[2];
|
||||
|
||||
char *search_order;
|
||||
|
||||
uint32_t min_search_digits;
|
||||
uint32_t max_menu_attempt;
|
||||
uint32_t digit_timeout;
|
||||
uint32_t max_result;
|
||||
switch_mutex_t *mutex;
|
||||
|
||||
switch_thread_rwlock_t *rwlock;
|
||||
switch_memory_pool_t *pool;
|
||||
|
||||
switch_xml_config_item_t config[DIR_PROFILE_CONFIGITEM_COUNT];
|
||||
switch_xml_config_string_options_t config_str_pool;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
typedef struct dir_profile dir_profile_t;
|
||||
|
||||
typedef enum {
|
||||
PFLAG_DESTROY = 1 << 0
|
||||
} dir_flags_t;
|
||||
|
||||
static int digit_matching_keypad(char letter) {
|
||||
int result = -1;
|
||||
switch(toupper(letter)) {
|
||||
case 'A':
|
||||
case 'B':
|
||||
case 'C':
|
||||
result = 2;
|
||||
break;
|
||||
case 'D':
|
||||
case 'E':
|
||||
case 'F':
|
||||
result = 3;
|
||||
break;
|
||||
case 'G':
|
||||
case 'H':
|
||||
case 'I':
|
||||
result = 4;
|
||||
break;
|
||||
case 'J':
|
||||
case 'K':
|
||||
case 'L':
|
||||
result = 5;
|
||||
break;
|
||||
case 'M':
|
||||
case 'N':
|
||||
case 'O':
|
||||
result = 6;
|
||||
break;
|
||||
case 'P':
|
||||
case 'Q':
|
||||
case 'R':
|
||||
case 'S':
|
||||
result = 7;
|
||||
break;
|
||||
case 'T':
|
||||
case 'U':
|
||||
case 'V':
|
||||
result = 8;
|
||||
break;
|
||||
case 'W':
|
||||
case 'X':
|
||||
case 'Y':
|
||||
case 'Z':
|
||||
result = 9;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
char *string_to_keypad_digit(const char *in)
|
||||
{
|
||||
const char *s = NULL;
|
||||
char *dst = NULL;
|
||||
char *d = NULL;
|
||||
|
||||
if (in) {
|
||||
s = in;
|
||||
dst = strdup(in);
|
||||
d = dst;
|
||||
|
||||
while (*s) {
|
||||
char c;
|
||||
if ((c = digit_matching_keypad(*s++)) > 0) {
|
||||
*d++ = c + 48;
|
||||
}
|
||||
}
|
||||
if (*d) {
|
||||
*d = '\0';
|
||||
}
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
static switch_status_t directory_execute_sql(char *sql, switch_mutex_t *mutex)
|
||||
{
|
||||
switch_core_db_t *db;
|
||||
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
||||
|
||||
if (mutex) {
|
||||
switch_mutex_lock(mutex);
|
||||
}
|
||||
|
||||
if (!(db = switch_core_db_open_file(globals.dbname))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB %s\n", globals.dbname);
|
||||
status = SWITCH_STATUS_FALSE;
|
||||
goto end;
|
||||
}
|
||||
status = switch_core_db_persistant_execute(db, sql, 1);
|
||||
switch_core_db_close(db);
|
||||
|
||||
end:
|
||||
if (mutex) {
|
||||
switch_mutex_unlock(mutex);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
ENTRY_MOVE_NEXT,
|
||||
ENTRY_MOVE_PREV
|
||||
} entry_move_t;
|
||||
|
||||
struct search_params {
|
||||
char digits[255];
|
||||
char transfer_to[255];
|
||||
char domain[255];
|
||||
char profile[255];
|
||||
int search_by_last_name;
|
||||
int timeout;
|
||||
int try_again;
|
||||
};
|
||||
typedef struct search_params search_params_t;
|
||||
|
||||
struct listing_callback {
|
||||
char extension[255];
|
||||
char fullname[255];
|
||||
char first_name[255];
|
||||
char last_name[255];
|
||||
char transfer_to[255];
|
||||
int name_visible;
|
||||
int exten_visible;
|
||||
int new_search;
|
||||
int index;
|
||||
int want;
|
||||
entry_move_t move;
|
||||
search_params_t *params;
|
||||
};
|
||||
typedef struct listing_callback listing_callback_t;
|
||||
|
||||
static int listing_callback(void *pArg, int argc, char **argv, char **columnNames)
|
||||
{
|
||||
listing_callback_t *cbt = (listing_callback_t *) pArg;
|
||||
if (cbt->index++ != cbt->want) {
|
||||
return 0;
|
||||
}
|
||||
switch_copy_string(cbt->extension, argv[0], 255);
|
||||
switch_copy_string(cbt->fullname, argv[1], 255);
|
||||
switch_copy_string(cbt->last_name, argv[2], 255);
|
||||
switch_copy_string(cbt->first_name, argv[3], 255);
|
||||
cbt->name_visible = atoi(argv[4]);
|
||||
cbt->exten_visible = atoi(argv[5]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
struct callback {
|
||||
char *buf;
|
||||
size_t len;
|
||||
int matches;
|
||||
};
|
||||
typedef struct callback callback_t;
|
||||
|
||||
static int sql2str_callback(void *pArg, int argc, char **argv, char **columnNames)
|
||||
{
|
||||
callback_t *cbt = (callback_t *) pArg;
|
||||
|
||||
switch_copy_string(cbt->buf, argv[0], cbt->len);
|
||||
cbt->matches++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static switch_bool_t directory_execute_sql_callback(switch_mutex_t *mutex, char *sql, switch_core_db_callback_func_t callback, void *pdata)
|
||||
{
|
||||
switch_bool_t ret = SWITCH_FALSE;
|
||||
switch_core_db_t *db;
|
||||
char *errmsg = NULL;
|
||||
|
||||
if (mutex) {
|
||||
switch_mutex_lock(mutex);
|
||||
}
|
||||
|
||||
if (!(db = switch_core_db_open_file(globals.dbname))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB %s\n", globals.dbname);
|
||||
goto end;
|
||||
}
|
||||
|
||||
switch_core_db_exec(db, sql, callback, pdata, &errmsg);
|
||||
|
||||
if (errmsg) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR: [%s] %s\n", sql, errmsg);
|
||||
free(errmsg);
|
||||
}
|
||||
|
||||
if (db) {
|
||||
switch_core_db_close(db);
|
||||
}
|
||||
|
||||
end:
|
||||
if (mutex) {
|
||||
switch_mutex_unlock(mutex);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define DIR_DESC "directory"
|
||||
#define DIR_USAGE "<profile_name> <domain_name>"
|
||||
|
||||
static void free_profile(dir_profile_t *profile)
|
||||
{
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Destroying Profile %s\n", profile->name);
|
||||
switch_core_destroy_memory_pool(&profile->pool);
|
||||
}
|
||||
|
||||
static void profile_rwunlock(dir_profile_t *profile)
|
||||
{
|
||||
switch_thread_rwlock_unlock(profile->rwlock);
|
||||
if (switch_test_flag(profile, PFLAG_DESTROY)) {
|
||||
if (switch_thread_rwlock_tryrdlock(profile->rwlock) == SWITCH_STATUS_SUCCESS) {
|
||||
free_profile(profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dir_profile_t *profile_set_config(dir_profile_t *profile)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
profile->config_str_pool.pool = profile->pool;
|
||||
|
||||
SWITCH_CONFIG_SET_ITEM(profile->config[i++], "next-key", SWITCH_CONFIG_STRING, CONFIG_RELOADABLE,
|
||||
&profile->next_key, "6", &config_dtmf, NULL, NULL);
|
||||
SWITCH_CONFIG_SET_ITEM(profile->config[i++], "prev-key", SWITCH_CONFIG_STRING, CONFIG_RELOADABLE,
|
||||
&profile->prev_key, "4", &config_dtmf, NULL, NULL);
|
||||
SWITCH_CONFIG_SET_ITEM(profile->config[i++], "terminator-key", SWITCH_CONFIG_STRING, CONFIG_RELOADABLE,
|
||||
&profile->terminator_key, "#", &config_dtmf, NULL, NULL);
|
||||
SWITCH_CONFIG_SET_ITEM(profile->config[i++], "switch-order-key", SWITCH_CONFIG_STRING, CONFIG_RELOADABLE,
|
||||
&profile->switch_order_key, "*", &config_dtmf, NULL, NULL);
|
||||
SWITCH_CONFIG_SET_ITEM(profile->config[i++], "select-name-key", SWITCH_CONFIG_STRING, CONFIG_RELOADABLE,
|
||||
&profile->select_name_key, "1", &config_dtmf, NULL, NULL);
|
||||
SWITCH_CONFIG_SET_ITEM(profile->config[i++], "new-search-key", SWITCH_CONFIG_STRING, CONFIG_RELOADABLE,
|
||||
&profile->new_search_key, "3", &config_dtmf, NULL, NULL);
|
||||
SWITCH_CONFIG_SET_ITEM(profile->config[i++], "search-order", SWITCH_CONFIG_STRING, CONFIG_RELOADABLE,
|
||||
&profile->search_order, "last_name", &profile->config_str_pool, NULL, NULL);
|
||||
SWITCH_CONFIG_SET_ITEM(profile->config[i++], "digit-timeout", SWITCH_CONFIG_INT, CONFIG_RELOADABLE,
|
||||
&profile->digit_timeout, 3000, &config_int_digit_timeout, NULL, NULL);
|
||||
SWITCH_CONFIG_SET_ITEM(profile->config[i++], "min-search-digits", SWITCH_CONFIG_INT, CONFIG_RELOADABLE,
|
||||
&profile->min_search_digits, 3, &config_int_ht_0, NULL, NULL);
|
||||
SWITCH_CONFIG_SET_ITEM(profile->config[i++], "max-menu-attempts", SWITCH_CONFIG_INT, CONFIG_RELOADABLE,
|
||||
&profile->max_menu_attempt, 3, &config_int_ht_0, NULL, NULL);
|
||||
SWITCH_CONFIG_SET_ITEM(profile->config[i++], "max-result", SWITCH_CONFIG_INT, CONFIG_RELOADABLE,
|
||||
&profile->max_result, 5, &config_int_ht_0, NULL, NULL);
|
||||
|
||||
return profile;
|
||||
|
||||
}
|
||||
|
||||
static dir_profile_t * load_profile(const char *profile_name)
|
||||
{
|
||||
dir_profile_t *profile = NULL;
|
||||
switch_xml_t x_profiles, x_profile, cfg, xml = NULL;
|
||||
switch_event_t *event = NULL;
|
||||
|
||||
if (!(xml = switch_xml_open_cfg(global_cf, &cfg, NULL))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", global_cf);
|
||||
return profile;
|
||||
}
|
||||
if (!(x_profiles = switch_xml_child(cfg, "profiles"))) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
if ((x_profile = switch_xml_find_child(x_profiles, "profile", "name", profile_name))) {
|
||||
switch_memory_pool_t *pool;
|
||||
int count;
|
||||
|
||||
if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Pool Failure\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!(profile = switch_core_alloc(pool, sizeof(dir_profile_t)))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Alloc Failure\n");
|
||||
switch_core_destroy_memory_pool(&pool);
|
||||
goto end;
|
||||
}
|
||||
|
||||
profile->pool = pool;
|
||||
profile_set_config(profile);
|
||||
|
||||
/* Add the params to the event structure */
|
||||
count = switch_event_import_xml(switch_xml_child(x_profile, "param"), "name", "value", &event);
|
||||
|
||||
if (switch_xml_config_parse_event(event, count, SWITCH_FALSE, profile->config) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to process configuration\n");
|
||||
switch_core_destroy_memory_pool(&pool);
|
||||
goto end;
|
||||
}
|
||||
|
||||
switch_thread_rwlock_create(&profile->rwlock, pool);
|
||||
profile->name = switch_core_strdup(pool, profile_name);
|
||||
|
||||
switch_mutex_init(&profile->mutex, SWITCH_MUTEX_NESTED, profile->pool);
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Added Profile %s\n", profile->name);
|
||||
switch_core_hash_insert(globals.profile_hash, profile->name, profile);
|
||||
}
|
||||
|
||||
end:
|
||||
switch_xml_free(xml);
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
static switch_status_t load_config(switch_bool_t reload)
|
||||
{
|
||||
switch_xml_t cfg, xml = NULL, settings, param, x_profiles, x_profile;
|
||||
|
||||
if (!(xml = switch_xml_open_cfg(global_cf, &cfg, NULL))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", global_cf);
|
||||
return SWITCH_STATUS_TERM;
|
||||
}
|
||||
|
||||
switch_mutex_lock(globals.mutex);
|
||||
if ((settings = switch_xml_child(cfg, "settings"))) {
|
||||
for (param = switch_xml_child(settings, "param"); param; param = param->next) {
|
||||
char *var = (char *) switch_xml_attr_soft(param, "name");
|
||||
char *val = (char *) switch_xml_attr_soft(param, "value");
|
||||
|
||||
if (!strcasecmp(var, "debug")) {
|
||||
globals.debug = atoi(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((x_profiles = switch_xml_child(cfg, "profiles"))) {
|
||||
for (x_profile = switch_xml_child(x_profiles, "profile"); x_profile; x_profile = x_profile->next) {
|
||||
load_profile(switch_xml_attr_soft(x_profile, "name"));
|
||||
}
|
||||
}
|
||||
switch_mutex_unlock(globals.mutex);
|
||||
|
||||
switch_xml_free(xml);
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static dir_profile_t * get_profile(const char *profile_name)
|
||||
{
|
||||
dir_profile_t *profile = NULL;
|
||||
|
||||
switch_mutex_lock(globals.mutex);
|
||||
if (!(profile = switch_core_hash_find(globals.profile_hash, profile_name))) {
|
||||
profile = load_profile(profile_name);
|
||||
}
|
||||
if (profile) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "[%s] rwlock\n", profile->name);
|
||||
|
||||
switch_thread_rwlock_rdlock(profile->rwlock);
|
||||
}
|
||||
switch_mutex_unlock(globals.mutex);
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
static switch_status_t populate_database(switch_core_session_t *session, dir_profile_t * profile, const char *domain_name) {
|
||||
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
||||
|
||||
char *sql = NULL;
|
||||
char *sqlvalues = NULL;
|
||||
char *sqltmp = NULL;
|
||||
|
||||
switch_xml_t xml_root = NULL, x_domain;
|
||||
switch_xml_t ut;
|
||||
|
||||
switch_event_t *xml_params = NULL;
|
||||
switch_xml_t group = NULL, groups = NULL, users = NULL, x_params = NULL, x_param = NULL, x_vars = NULL, x_var = NULL;
|
||||
switch_event_create(&xml_params, SWITCH_EVENT_REQUEST_PARAMS);
|
||||
switch_assert(xml_params);
|
||||
|
||||
if (switch_xml_locate_domain(domain_name, xml_params, &xml_root, &x_domain) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot locate domain %s\n", domain_name);
|
||||
status = SWITCH_STATUS_FALSE;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if ((groups = switch_xml_child(x_domain, "groups"))) {
|
||||
for (group = switch_xml_child(groups, "group"); group; group = group->next) {
|
||||
if ((users = switch_xml_child(group, "users"))) {
|
||||
for (ut = switch_xml_child(users, "user"); ut; ut = ut->next) {
|
||||
int name_visible = 1;
|
||||
int exten_visible = 1;
|
||||
const char *type = switch_xml_attr_soft(ut, "type");
|
||||
const char *id = switch_xml_attr_soft(ut, "id");
|
||||
char *fullName = NULL;
|
||||
char *caller_name = NULL;
|
||||
char *caller_name_override = NULL;
|
||||
char *firstName = NULL;
|
||||
char *lastName = NULL;
|
||||
char *fullNameDigit = NULL;
|
||||
char *firstNameDigit = NULL;
|
||||
char *lastNameDigit = NULL;
|
||||
|
||||
if (!strcasecmp(type, "pointer")) {
|
||||
continue;
|
||||
}
|
||||
/* Check all the user params */
|
||||
if ((x_params = switch_xml_child(ut, "params"))) {
|
||||
for (x_param = switch_xml_child(x_params, "param"); x_param; x_param = x_param->next) {
|
||||
const char *var = switch_xml_attr_soft(x_param, "name");
|
||||
const char *val = switch_xml_attr_soft(x_param, "value");
|
||||
if (!strcasecmp(var, "directory-visible")) {
|
||||
name_visible = switch_true(val);
|
||||
}
|
||||
if (!strcasecmp(var, "directory-exten-visible")) {
|
||||
exten_visible = switch_true(val);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
/* Check all the user variables */
|
||||
if ((x_vars = switch_xml_child(ut, "variables"))) {
|
||||
for (x_var = switch_xml_child(x_vars, "variable"); x_var; x_var = x_var->next) {
|
||||
const char *var = switch_xml_attr_soft(x_var, "name");
|
||||
const char *val = switch_xml_attr_soft(x_var, "value");
|
||||
if (!strcasecmp(var, "effective_caller_id_name")) {
|
||||
caller_name = switch_core_session_strdup(session, val);
|
||||
}
|
||||
if (!strcasecmp(var, "directory_full_name")) {
|
||||
caller_name_override = switch_core_session_strdup(session, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (caller_name_override) {
|
||||
fullName = caller_name_override;
|
||||
} else {
|
||||
fullName = caller_name;
|
||||
}
|
||||
if (switch_strlen_zero(fullName)) {
|
||||
continue;
|
||||
}
|
||||
firstName = switch_core_session_strdup(session, fullName);
|
||||
|
||||
if ((lastName = strrchr(firstName, ' '))) {
|
||||
*lastName++ = '\0';
|
||||
} else {
|
||||
lastName = switch_core_session_strdup(session, firstName);
|
||||
}
|
||||
|
||||
/* switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "FullName %s firstName [%s] lastName [%s]\n", fullName, firstName, lastName); */
|
||||
|
||||
/* Generate Digits key mapping */
|
||||
fullNameDigit = string_to_keypad_digit(fullName);
|
||||
lastNameDigit = string_to_keypad_digit(lastName);
|
||||
firstNameDigit = string_to_keypad_digit(firstName);
|
||||
|
||||
/* add user into DB */
|
||||
sql = switch_mprintf("insert into directory_search values('%q','%q','%q','%q','%q','%q','%q','%q','%q','%d','%d')",
|
||||
globals.hostname, switch_core_session_get_uuid(session), id, fullName, fullNameDigit, firstName, firstNameDigit, lastName, lastNameDigit, name_visible, exten_visible);
|
||||
|
||||
if (sqlvalues) {
|
||||
sqltmp = sqlvalues;
|
||||
sqlvalues = switch_mprintf("%s;%s", sqlvalues, sql);
|
||||
switch_safe_free(sqltmp);
|
||||
} else {
|
||||
sqlvalues = sql;
|
||||
sql = NULL;
|
||||
}
|
||||
switch_safe_free(sql);
|
||||
switch_safe_free(fullNameDigit);
|
||||
switch_safe_free(lastNameDigit);
|
||||
switch_safe_free(firstNameDigit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sql = switch_mprintf("BEGIN;%s;COMMIT;",sqlvalues);
|
||||
directory_execute_sql(sql, profile->mutex);
|
||||
|
||||
end:
|
||||
switch_safe_free(sql);
|
||||
switch_safe_free(sqlvalues);
|
||||
switch_event_destroy(&xml_params);
|
||||
switch_xml_free(xml_root);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
struct cb_result {
|
||||
char digits[255];
|
||||
char digit;
|
||||
dir_profile_t *profile;
|
||||
};
|
||||
|
||||
typedef struct cb_result cbr_t;
|
||||
|
||||
static switch_status_t on_dtmf(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen)
|
||||
{
|
||||
switch (itype) {
|
||||
case SWITCH_INPUT_TYPE_DTMF:
|
||||
{
|
||||
switch_dtmf_t *dtmf = (switch_dtmf_t *) input;
|
||||
cbr_t *cbr = (cbr_t *) buf;
|
||||
cbr->digit = dtmf->digit;
|
||||
if (dtmf->digit == *cbr->profile->terminator_key || dtmf->digit == *cbr->profile->switch_order_key) {
|
||||
return SWITCH_STATUS_BREAK;
|
||||
}
|
||||
|
||||
if (strlen(cbr->digits) < sizeof(cbr->digits) - 2) {
|
||||
int at = strlen(cbr->digits);
|
||||
cbr->digits[at++] = dtmf->digit;
|
||||
cbr->digits[at] = '\0';
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "DTMF buffer is full\n");
|
||||
return SWITCH_STATUS_BREAK;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return SWITCH_STATUS_BREAK;
|
||||
}
|
||||
|
||||
static switch_status_t listen_entry(switch_core_session_t *session, dir_profile_t *profile, listing_callback_t *cbt)
|
||||
{
|
||||
char buf[2] = "";
|
||||
char macro[256] = "";
|
||||
char recorded_name[256] = "";
|
||||
|
||||
/* Try to use the recorded name from voicemail if it exist */
|
||||
if (switch_loadable_module_exists("mod_voicemail") == SWITCH_STATUS_SUCCESS) {
|
||||
char *cmd = NULL;
|
||||
switch_stream_handle_t stream = { 0 };
|
||||
SWITCH_STANDARD_STREAM(stream);
|
||||
|
||||
cmd = switch_core_session_sprintf(session, "%s/%s@%s|name_path", cbt->params->profile, cbt->extension, cbt->params->domain);
|
||||
switch_api_execute("vm_prefs", cmd, session, &stream);
|
||||
if (strncmp("-ERR", stream.data, 4)) {
|
||||
switch_copy_string(recorded_name, (char *) stream.data, sizeof(recorded_name));
|
||||
}
|
||||
switch_safe_free(stream.data);
|
||||
}
|
||||
|
||||
if (switch_strlen_zero(buf)) {
|
||||
switch_snprintf(macro, sizeof(macro), "phrase:%s:%d", DIR_RESULT_ITEM, cbt->want+1);
|
||||
switch_ivr_read(session, 0, 1, macro, NULL, buf, sizeof(buf), 1, profile->terminator_key);
|
||||
}
|
||||
|
||||
if (!switch_strlen_zero(recorded_name) && switch_strlen_zero(buf)) {
|
||||
switch_ivr_read(session, 0, 1, recorded_name, NULL, buf, sizeof(buf), 1, profile->terminator_key);
|
||||
|
||||
}
|
||||
if (switch_strlen_zero(recorded_name) && switch_strlen_zero(buf)) {
|
||||
switch_snprintf(macro, sizeof(macro), "phrase:%s:%s", DIR_RESULT_SAY_NAME, cbt->fullname);
|
||||
switch_ivr_read(session, 0, 1, macro, NULL, buf, sizeof(buf), 1, profile->terminator_key);
|
||||
}
|
||||
if (cbt->exten_visible && switch_strlen_zero(buf)) {
|
||||
switch_snprintf(macro, sizeof(macro), "phrase:%s:%s", DIR_RESULT_AT, cbt->extension);
|
||||
switch_ivr_read(session, 0, 1, macro, NULL, buf, sizeof(buf), 1, profile->terminator_key);
|
||||
}
|
||||
if (switch_strlen_zero(buf)) {
|
||||
switch_snprintf(macro, sizeof(macro), "phrase:%s:%c,%c,%c,%c", DIR_RESULT_MENU, *profile->select_name_key, *profile->next_key, *profile->prev_key, *profile->new_search_key);
|
||||
switch_ivr_read(session, 0, 1, macro, NULL, buf, sizeof(buf), profile->digit_timeout, profile->terminator_key);
|
||||
}
|
||||
|
||||
if (!switch_strlen_zero(buf)) {
|
||||
if (buf[0] == *profile->select_name_key) {
|
||||
switch_copy_string(cbt->transfer_to, cbt->extension, 255);
|
||||
}
|
||||
if (buf[0] == *profile->new_search_key) {
|
||||
cbt->new_search = 1;
|
||||
}
|
||||
if (buf[0] == *profile->prev_key) {
|
||||
cbt->move = ENTRY_MOVE_PREV;
|
||||
}
|
||||
} else {
|
||||
return SWITCH_STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
switch_status_t gather_name_digit(switch_core_session_t *session, dir_profile_t *profile, search_params_t *params) {
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
||||
cbr_t cbr;
|
||||
int loop = 1;
|
||||
|
||||
switch_input_args_t args = { 0 };
|
||||
args.input_callback = on_dtmf;
|
||||
args.buf = &cbr;
|
||||
|
||||
while (switch_channel_ready(channel) && loop) {
|
||||
char macro[255];
|
||||
loop = 0;
|
||||
memset(&cbr, 0, sizeof(cbr));
|
||||
cbr.profile = profile;
|
||||
params->timeout = 0;
|
||||
|
||||
/* Gather the user Name */
|
||||
|
||||
switch_snprintf(macro, sizeof(macro), "%s:%c", (params->search_by_last_name?"last_name":"first_name"), *profile->switch_order_key);
|
||||
switch_ivr_phrase_macro(session, DIR_INTRO, macro, NULL, &args);
|
||||
|
||||
while (switch_channel_ready(channel)) {
|
||||
if (cbr.digit == *profile->terminator_key) {
|
||||
status = SWITCH_STATUS_BREAK;
|
||||
break;
|
||||
}
|
||||
if (cbr.digit == *profile->switch_order_key) {
|
||||
if (params->search_by_last_name) {
|
||||
params->search_by_last_name = 0;
|
||||
} else {
|
||||
params->search_by_last_name = 1;
|
||||
}
|
||||
loop = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (switch_ivr_collect_digits_callback(session, &args, profile->digit_timeout, 0) == SWITCH_STATUS_TIMEOUT) {
|
||||
params->timeout = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
switch_copy_string(params->digits, cbr.digits, 255);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
switch_status_t navigate_entrys(switch_core_session_t *session, dir_profile_t *profile, search_params_t *params) {
|
||||
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
||||
char *sql = NULL;
|
||||
char entry_count[80] = "";
|
||||
callback_t cbt = { 0 };
|
||||
int result_count;
|
||||
char macro[256] = "";
|
||||
listing_callback_t listing_cbt;
|
||||
int cur_entry = 0;
|
||||
cbt.buf = entry_count;
|
||||
cbt.len = sizeof(entry_count);
|
||||
|
||||
sql = switch_mprintf("select count(*) from directory_search where hostname = '%q' and uuid = '%q' and name_visible = 1 and %s like '%q%%'", globals.hostname, switch_core_session_get_uuid(session), (params->search_by_last_name?"last_name_digit":"first_name_digit"), params->digits);
|
||||
|
||||
directory_execute_sql_callback(profile->mutex, sql, sql2str_callback, &cbt);
|
||||
switch_safe_free(sql);
|
||||
|
||||
result_count = atoi(entry_count);
|
||||
|
||||
if (result_count == 0) {
|
||||
switch_snprintf(macro, sizeof(macro), "%d", result_count);
|
||||
switch_ivr_phrase_macro(session, DIR_RESULT_COUNT, macro, NULL, NULL);
|
||||
params->try_again = 1;
|
||||
return SWITCH_STATUS_BREAK;
|
||||
} else if (profile->max_result != 0 && result_count > profile->max_result) {
|
||||
switch_ivr_phrase_macro(session, DIR_RESULT_COUNT_TOO_LARGE, NULL, NULL, NULL);
|
||||
params->try_again = 1;
|
||||
return SWITCH_STATUS_BREAK;
|
||||
} else {
|
||||
switch_snprintf(macro, sizeof(macro), "%d", result_count);
|
||||
switch_ivr_phrase_macro(session, DIR_RESULT_COUNT, macro, NULL, NULL);
|
||||
}
|
||||
|
||||
memset(&listing_cbt, 0, sizeof(listing_cbt));
|
||||
listing_cbt.params = params;
|
||||
|
||||
sql = switch_mprintf("select extension, full_name, last_name, first_name, name_visible, exten_visible from directory_search where hostname = '%q' and uuid = '%q' and name_visible = 1 and %s like '%q%%' order by last_name, first_name", globals.hostname, switch_core_session_get_uuid(session), (params->search_by_last_name?"last_name_digit":"first_name_digit"), params->digits);
|
||||
|
||||
for (cur_entry = 0; cur_entry < result_count; cur_entry++) {
|
||||
listing_cbt.index = 0;
|
||||
listing_cbt.want = cur_entry;
|
||||
listing_cbt.move = ENTRY_MOVE_NEXT;
|
||||
directory_execute_sql_callback(profile->mutex, sql, listing_callback, &listing_cbt);
|
||||
status = listen_entry(session, profile, &listing_cbt);
|
||||
if (!switch_strlen_zero(listing_cbt.transfer_to)) {
|
||||
switch_copy_string(params->transfer_to, listing_cbt.transfer_to, 255);
|
||||
break;
|
||||
}
|
||||
if (listing_cbt.new_search) {
|
||||
params->try_again = 1;
|
||||
goto end;
|
||||
|
||||
}
|
||||
if (listing_cbt.move == ENTRY_MOVE_NEXT) {
|
||||
if (cur_entry == result_count - 1) {
|
||||
switch_snprintf(macro, sizeof(macro), "%d", result_count);
|
||||
switch_ivr_phrase_macro(session, DIR_RESULT_LAST, macro, NULL, NULL);
|
||||
cur_entry -= 1;
|
||||
}
|
||||
}
|
||||
if (listing_cbt.move == ENTRY_MOVE_PREV) {
|
||||
if (cur_entry <= 0) {
|
||||
cur_entry = -1;
|
||||
} else {
|
||||
cur_entry -= 2;
|
||||
}
|
||||
}
|
||||
if (status == SWITCH_STATUS_TIMEOUT) {
|
||||
goto end;
|
||||
}
|
||||
if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) {
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
switch_safe_free(sql);
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
SWITCH_STANDARD_APP(directory_function)
|
||||
{
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
int argc = 0;
|
||||
char *argv[6] = { 0 };
|
||||
char *mydata = NULL;
|
||||
const char *profile_name = NULL;
|
||||
const char *domain_name = NULL;
|
||||
dir_profile_t * profile = NULL;
|
||||
int x = 0;
|
||||
char *sql = NULL;
|
||||
search_params_t s_param;
|
||||
int attempts = 3;
|
||||
char macro[255];
|
||||
|
||||
if (!switch_strlen_zero(data)) {
|
||||
mydata = switch_core_session_strdup(session, data);
|
||||
argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
|
||||
}
|
||||
if (argv[x]) {
|
||||
profile_name = argv[x++];
|
||||
}
|
||||
|
||||
if (argv[x]) {
|
||||
domain_name = argv[x++];
|
||||
}
|
||||
|
||||
|
||||
if (!(profile = get_profile(profile_name))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error invalid profile %s\n", profile_name);
|
||||
goto end;
|
||||
}
|
||||
|
||||
populate_database(session, profile, domain_name);
|
||||
|
||||
memset(&s_param, 0, sizeof(s_param));
|
||||
s_param.search_by_last_name = 1;
|
||||
s_param.try_again = 1;
|
||||
switch_copy_string(s_param.profile, profile_name, 255);
|
||||
switch_copy_string(s_param.domain, domain_name, 255);
|
||||
|
||||
if (strcasecmp(profile->search_order, "last_name")) {
|
||||
s_param.search_by_last_name = 0;
|
||||
}
|
||||
attempts = profile->max_menu_attempt;
|
||||
s_param.try_again = 1;
|
||||
while (switch_channel_ready(channel) && (s_param.try_again && attempts-- > 0)) {
|
||||
s_param.try_again = 0;
|
||||
gather_name_digit(session, profile, &s_param);
|
||||
|
||||
if (switch_strlen_zero(s_param.digits)) {
|
||||
s_param.try_again = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strlen(s_param.digits) < profile->min_search_digits) {
|
||||
switch_snprintf(macro, sizeof(macro), "%d", profile->min_search_digits);
|
||||
switch_ivr_phrase_macro(session, DIR_MIN_SEARCH_DIGITS, macro, NULL, NULL);
|
||||
s_param.try_again = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
navigate_entrys(session, profile, &s_param);
|
||||
}
|
||||
|
||||
if (!switch_strlen_zero(s_param.transfer_to)) {
|
||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Directory transfering call to : %s\n", s_param.transfer_to);
|
||||
switch_ivr_session_transfer(session, s_param.transfer_to, "XML", domain_name);
|
||||
}
|
||||
|
||||
end:
|
||||
/* Delete all sql entry for this call */
|
||||
sql = switch_mprintf("delete from directory_search where hostname = '%q' and uuid = '%q'", globals.hostname, switch_core_session_get_uuid(session));
|
||||
directory_execute_sql(sql, profile->mutex);
|
||||
switch_safe_free(sql);
|
||||
|
||||
|
||||
profile_rwunlock(profile);
|
||||
|
||||
}
|
||||
|
||||
SWITCH_MODULE_LOAD_FUNCTION(mod_directory_load)
|
||||
{
|
||||
switch_application_interface_t *app_interface;
|
||||
switch_status_t status;
|
||||
switch_core_db_t *db = NULL;
|
||||
char *sql = NULL;
|
||||
|
||||
memset(&globals, 0, sizeof(globals));
|
||||
globals.pool = pool;
|
||||
|
||||
switch_core_hash_init(&globals.profile_hash, globals.pool);
|
||||
switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, globals.pool);
|
||||
|
||||
if ((status = load_config(SWITCH_FALSE)) != SWITCH_STATUS_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* connect my internal structure to the blank pointer passed to me */
|
||||
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
|
||||
|
||||
gethostname(globals.hostname, sizeof(globals.hostname));
|
||||
|
||||
globals.dbname = switch_core_sprintf(pool, "directory");
|
||||
|
||||
if ((db = switch_core_db_open_file(globals.dbname))) {
|
||||
switch_core_db_test_reactive(db, "select count(uuid),name_visible from directory_search", "drop table directory_search", dir_sql);
|
||||
switch_core_db_close(db);
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to open db name : %s\n", globals.dbname);
|
||||
return SWITCH_STATUS_FALSE;
|
||||
|
||||
}
|
||||
|
||||
sql = switch_mprintf("delete from directory_search where hostname = '%q'", globals.hostname);
|
||||
directory_execute_sql(sql, globals.mutex);
|
||||
switch_safe_free(sql);
|
||||
|
||||
SWITCH_ADD_APP(app_interface, "directory", "directory", DIR_DESC, directory_function, DIR_USAGE, SAF_NONE);
|
||||
|
||||
/* indicate that the module should continue to be loaded */
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
Called when the system shuts down
|
||||
Macro expands to: switch_status_t mod_directory_shutdown() */
|
||||
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_directory_shutdown)
|
||||
{
|
||||
switch_hash_index_t *hi;
|
||||
dir_profile_t *profile;
|
||||
void *val = NULL;
|
||||
const void *key;
|
||||
switch_ssize_t keylen;
|
||||
char *sql = NULL;
|
||||
|
||||
switch_mutex_lock(globals.mutex);
|
||||
|
||||
while((hi = switch_hash_first(NULL, globals.profile_hash))) {
|
||||
switch_hash_this(hi, &key, &keylen, &val);
|
||||
profile = (dir_profile_t *) val;
|
||||
|
||||
switch_core_hash_delete(globals.profile_hash, profile->name);
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Waiting for write lock (Profile %s)\n", profile->name);
|
||||
switch_thread_rwlock_wrlock(profile->rwlock);
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Destroying Profile %s\n", profile->name);
|
||||
switch_core_destroy_memory_pool(&profile->pool);
|
||||
profile = NULL;
|
||||
}
|
||||
|
||||
sql = switch_mprintf("delete from directory_search where hostname = '%q'", globals.hostname);
|
||||
directory_execute_sql(sql, globals.mutex);
|
||||
switch_safe_free(sql);
|
||||
|
||||
switch_mutex_unlock(globals.mutex);
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* For Emacs:
|
||||
* Local Variables:
|
||||
* mode:c
|
||||
* indent-tabs-mode:t
|
||||
* tab-width:4
|
||||
* c-basic-offset:4
|
||||
* End:
|
||||
* For VIM:
|
||||
* vim:set softtabstop=4 shiftwidth=4 tabstop=4
|
||||
*/
|
|
@ -0,0 +1,153 @@
|
|||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="8.00"
|
||||
Name="mod_directory"
|
||||
ProjectGUID="{75DF7F29-2FBF-47F7-B5AF-5B4952DC1ABD}"
|
||||
RootNamespace="mod_directory"
|
||||
Keyword="Win32Proj"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
ConfigurationType="2"
|
||||
InheritedPropertySheets="..\..\..\..\w32\module_debug.vsprops"
|
||||
CharacterSet="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="0"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
ConfigurationType="2"
|
||||
InheritedPropertySheets="..\..\..\..\w32\module_release.vsprops"
|
||||
CharacterSet="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
UsePrecompiledHeader="0"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<File
|
||||
RelativePath=".\mod_directory.c"
|
||||
>
|
||||
</File>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
Loading…
Reference in New Issue