Sunday, October 17, 2010

Yealink IP Phone corporate directory integrated into FreePBX




Newer firmware of Yealink IP phones allows you to perform searches within the “Remote Directory” setup screen. Instead of just having a static file, users can search just about any parts of the name field and voila, the results are shown on the display.
Here, we take this functionality and add it to the FreePBX directory meaning, it is truly realtime to the adding and removal of users in FreePBX as it uses the asterisk.users directory in FreePBX MySQL table.
Test on:
1) FreePBX 2.x
2) Debian 5
3) Asterisk 1.6 (versions of Asterisk doesn’t really matter)
NOTE: If the codes appear truncated, simply copy the whole table and paste into a text editor
Features
  • Wildcard searching capability using backend PHP engine
  • Searches directly from FreePBX no need to maintain separate DB/files, thus making it dynamic
  • Some security tips plus Database access is limited with very low privileges for the user that connects to FreePBX / MySQL
  • May work for just about any type of phones that support the search function
Security Notice
Do not allow this file to be exposed in the public domain it can disclose your corporate directory and users/people

- Enforce ACL on this website/page so that only internal users can enumerate
- Try to use a complex name for the php file so that it’s difficult for someone to guess it.
Platforms
Tested on Debian5, FreePBX2.8, Asterisk1.6, MySQL5,Apache2 (PHP5)
Assumptions/notes:
1) You have a running apache server which support PHP5 and does not force SSL.
2) Apache runs as asterisk in group asterisk
3) There's no other service listening on port 80
4) Use freepbx asterisk database with users table found (default)
5) Using IP 10.10.10.1 as example web server
6) Newer Yealink firmware allows searching functionality, get that to use with this function
IMPORTANT: YOU MUST USE NON SSL BASED WEBSITE AS THE PHONES MAY NOT KNOW HOW TO INTERPRET SSL TRANSACTIONS
We will use a very low privileged user for this requests.
Create DB user with very low privileges (change the username and password accordingly to connect to your MySQL box, here, its a local box). This below is give access to the database asterisk, table user and column name.
mysql -h localhost -u root -ppassword  mysql --execute="GRANT SELECT (name) ON asterisk.users TO directory@localhost IDENTIFIED BY ‘p@ssword1';"

mysql -h localhost -u root -ppassword  mysql --execute="GRANT SELECT (extension) ON asterisk.users TO directory@localhost IDENTIFIED BY 'p@ssword1';"

Create PHP to XML transformation file that will retrieve info directly from FreePBX's user table in the database asterisk (created by FreePBX)

SECURITY TIP: Use an odd name only you would know. Common names can be guessed and then information enumerated.
nano /var/www/directory/search.php
Paste into file, read the file headers and change where appropriate.
<?
// with credit to JOYCE CR, s.r.o. http://www.joyce.cz/produkt-soubory/searching_remote_phonebook_manual.pdf
// Make sure you configure the allowable settings only
// This script directly integrates with FreePBX and picksup the asterisk.users table
// Should work for both device-user mode or extensions mode
// Works by searching from anywhere of the person's name
// feedback to sanjay@astiostech.com
// Change here to match the webaddress absolute path
$URL = 'http://10.10.10.1/directory/';
// Choose how many results to return if search term produces a lot of output
$per_page = '10';
// Change here to match your own passwords
$mysql_conn = mysql_connect('localhost', 'directory', 'p@ssword1');
// Dont change anything from here unless you know what you are doing
mysql_select_db('asterisk', $mysql_conn );
$NAME=$_GET["name"];
$FROM=$_GET["FROM"];
$TO=$_GET["TO"];
if ( ($FROM=='') and ($TO=='') )
{
   //check to see how many
   $result= mysql_query("SELECT count(users.name) as total
                         FROM users
                         WHERE users.name LIKE '%$NAME%' ", $mysql_conn);
   $howmany = mysql_fetch_row($result);
   if ($howmany[0] > $per_page)
   {
    $start = 0;
    $index = 0;
    $total = $howmany[0];
    $remain = $per_page;
    print("\n");
    print("<YealinkIPPhoneDirectory>\n"); 
    while ($start < ($total + 1))
    {
      $limitstart = 'LIMIT '.$start.','.$per_page;
      $result = mysql_query("SELECT name,extension
                             FROM users
                             WHERE name LIKE '%$NAME%' ORDER BY name $limitstart", $mysql_conn);
      $row = mysql_fetch_row($result);
      $from = $row[0];
      if (($total - $start) < $per_page) { $remain = $total - $start; }
      for ($i = 1; $i < $remain; ++$i) { $row = mysql_fetch_row($result); }
      $to = $row[0];
      print("<SoftKeyItem>\n");
      print("\t<Name>");
      print($index);         
      print("</Name>\n");
      print("\t<URL>");
      print($URL."search.php?FROM=".$from."&TO=".$to);
      print("</URL>\n");
      print("</SoftKeyItem>\n");
      $start = $start + $per_page;
      $index = $index+1;
    }
    print("</YealinkIPPhoneDirectory>\n");
   } else {
    $result = mysql_query("SELECT name,extension,extension
                           FROM users
                           WHERE users.name LIKE '%$NAME%'
                           ORDER BY name ", $mysql_conn);
    print("\n");
    print("<YealinkIPPhoneDirectory>\n"); 
    while($row = mysql_fetch_row($result))
    {
      print("<DirectoryEntry>\n");
      print("\t<Name>");
      print($row[0]."- ".$row[1] );
      print("</Name>\n");
      print("\t<Telephone>");
      print($row[2]);
      print("</Telephone>\n");
      print("</DirectoryEntry>\n");
    }
    print("</YealinkIPPhoneDirectory>\n");
   }
} else {
  $result = mysql_query("SELECT name,extension,extension
                         FROM users
                         WHERE name>='$FROM' AND name<='$TO'
                         ORDER BY name", $mysql_conn);
   print("\n");
   print("<YealinkIPPhoneDirectory>\n"); 
   while($row = mysql_fetch_row($result))
   {
     print("<DirectoryEntry>\n");
     print("\t<Name>");
     print($row[0]."- ".$row[1] );
     print("</Name>\n");
     print("\t<Telephone>");
     print($row[2]);
     print("</Telephone>\n");
     print("</DirectoryEntry>\n");
   }
   print("</YealinkIPPhoneDirectory>\n");
}
?>
Save and close. Give proper permissions to the file
chown asterisk:asterisk /var/www/directory/search.php
Try on browser, assuming a user Sanjay is being searched for, you can also use ja, nj, as long as it's within the correct sequence
http://10.10.10.1/directory/search.php?name=sa
An output should look something like this:
Sanjay WS- 1000 1000
Now, on your Yealink:
Go to yealink admin page | Contacts | Remote Phone Book
http://10.10.10.1/directory/search.php?name=#SEARCH
Name this entry like “Corporate Directory” and what not.
Now, save, hit Dir, scrolls to the newly created “Corporate Directory” and a search function should show up. IF you search, enter the relevent text or if you don’t it will enumerate all list of directory entries limited by the number of pages specified in the php script above.
Do test it out and let us know.

Asterisk + FreePBX - Removing queue members with logout *12

Some organizations probably need this, so we thought of sharing.
The requirements form one of client is to “auto” logout someone from an Asterisk/FreePBX queue module when he/she logout from the user/device mode.
This script uses AGI. I have tested this on;
1) Debian 5
2) FreePBX 2.8 and below
3) Asterisk 1.6 (may work for 1.4- need to probably tweak the script)
NOTE: If the codes appear truncated, simply copy the whole table and paste into a text editor
Firstly, we use the override function in FreePBX to override the macro-user-logoff directive
nano /etc/asterisk/extensions_override_freepbx.conf
Enter the following code;
[macro-user-logoff]
; check device type
;
exten => s,1,Set(DEVICETYPE=${DB(DEVICE/${CALLERID(number)}/type)})
exten => s,n,GotoIf($["${DEVICETYPE}" = "fixed"]?s-FIXED,1)
exten => s,n,Set(dev=${DB(DEVICE/${CALLERID(num)}/user})
exten => s,n,AGI(kick.sh,${dev})
exten => s,n,AGI(user_login_out.agi,logout,${CALLERID(number)})
exten => s,n(done),Playback(vm-goodbye)
exten => s-FIXED,1,NoOp(Device is FIXED and cannot be logged out of)
exten => s-FIXED,n,Playback(an-error-has-occured&vm-goodbye)
exten => s-FIXED,n,Hangup
The two items highlighted in bold are basically the ones we add to an existing macro-user-logoff function in FreePBX
Now, create the AGI / bash script that will do the trick
nano /var/lib/asterisk/agi-bin/kick.sh
Paste the following code in the editor, MAKE SURE YOU MODIFY YOUR QUEUE NUMBERS show in “queuenumbers”
#!/bin/bash
# script by Sanjay Willie
# v0.1-beta
# tested on Asterisk 1.6/FreePBX 2.8/Debian 5.06
# maintainer: sanjay@astiostech.com
# the directory /tmp should exist and can be accessible by user asterisk and group asterisk
# NOTE: FILES IN /tmp normally gets flushed on restarts
# On grabbing agent number, should look exactly like "Local/1000@from-queue" eventually and should not contain any preceding or trailing characters
# the extraction of agents in the script in the line "for agents..." may or not yield output, so test it out.
# The sed part is to remove brackets from the output in the front and back (seen in FBX 2.8)
# if the sed isn't required (like in FreePBX 2.7 and below) you can remove it, also lookout for the print $3, in FreePBX 2.7 and below its print $1, so test it out first
stdin="0"
datenano=`date +%N-%F`
regulardate=`date +%F-%T`
gennum=$RANDOM-$datenano.int-tmp
queuenumbers="4001 4002 4003 5001 5002 5003 4021 4022 4023 5021 5022 5023"
###########
while [ "$stdin" != "" ]
        do
                read stdin
                if [ "$stdin" != EOF ]
                then
                        echo $stdin | grep agi_arg_1 | awk {'print $2'} >> $gennum
                fi
        done
############
include=`cat $gennum`
#include=2006
############
cleanup=`rm -v /tmp/$gennum`
#echo $cleanup >> /tmp/cleanup
############
if [[ "$include" == "" ]] ; then
        echo Nothing to do, quitting
        exit
else
for queue in $queuenumbers; do
        #echo $queue >> /tmp/logfile.txt
        # see NOTES above
        #for agents in `asterisk -rx "queue show $queue" | grep from-queue | awk '{print $3}' | grep -w "$include" | sed 's/.\(.*\)/\1/' | sed 's/\(.*\)./\1/'`; do # freePBX 2.8 or higher
        for agents in `asterisk -rx "queue show $queue" | grep from-queue | awk '{print $1}' | grep -w "$include"`; do
                #echo $agents >> /tmp/logfile.txt
                asterisk -rx "queue remove member $agents from $queue"
                echo "we remove $agents in $queue"
                echo $regulardate - Attempt removing agent $agents from queue $queue >> /tmp/agentremover.log
        done
done
fi
############
exit 0
This code does this;
1) Once the Asterisk dial plan calls the script, it will parse the user into bash and we will remove that user with a script that walks through all queues you have specified looking for this user and does a removal using asterisk –rx
2) Stores a simple log in /tmp
Make it executable, chmod +x /var/lib/asterisk/agi-bin/kick.sh and make sure the asterisk user can read/run the file, chown asterisk:asterisk /var/lib/asterisk/agi-bin/kick.sh
Reload the dialplan
asterisk -rx "dialplan reload"
Go ahead and test it…
Let us know if this worked out. For help, write to sanjay@astiostech.com