I spent about 8 hours a few days ago hacking through FreePBX code to implement what was a show stopper for me. It seems the way FreePBX handles call pickup with **+EXT is to let anyone pick up any ringing extension. This is OK for very small organizations, but not for larger organizations with multiple departments.
What I did was to utilize the callgroup and pickupgroup parameters to determine whether the extension attempting to pickup someone else's call is eligible to do so.
I doubt this is complete, but it worked for me and will give a starting point for everyone to build on.
Here is what I modified:
/var/www/html/admin/modules/core/functions.inc.php
starting at line 675 of the original version 2.4.0.1 file:
This is the original code:
// Call pickup using app_pickup - Note that '**xtn' is hard-coded into the GXPs and SNOMs as a number to dial
// when a user pushes a flashing BLF.
if ($fc_pickup != '') {
$ext->addInclude('from-internal-additional', 'app-pickup');
$fclen = strlen($fc_pickup);
$ext->add('app-pickup', "_$fc_pickup.", '', new ext_NoOp('Attempt to Pickup ${EXTEN:'.$fclen.'} by ${CALLERID(num)}'));
if (strstr($version, 'BRI'))
$ext->add('app-pickup', "_$fc_pickup.", '', new ext_dpickup('${EXTEN:'.$fclen.'}'));
else
$ext->add('app-pickup', "_$fc_pickup.", '', new ext_pickup('${EXTEN:'.$fclen.'}'));
}
This is the modified code:
// Call pickup using app_pickup - Note that '**xtn' is hard-coded into the GXPs and SNOMs as a number to dial
// when a user pushes a flashing BLF.
if ($fc_pickup != '') {
$ext->addInclude('from-internal-additional', 'app-pickup');
$fclen = strlen($fc_pickup);
$ext->add('app-pickup', "_$fc_pickup.", '', new ext_NoOp('Attempt to Pickup ${EXTEN:'.$fclen.'} by ${CALLERID(num)}'));
//new code to do permission checking
$ext->add('app-pickup', "_$fc_pickup.", '', new ext_setvar('CALLG','${DB(AMPUSER/${EXTEN:'.$fclen.'}/callgroup)}'));
$ext->add('app-pickup', "_$fc_pickup.", '', new ext_setvar('PICKUPG','${DB(AMPUSER/${CALLERID(number)}/pickupgroup)}'));
$ext->add('app-pickup', "_$fc_pickup.", '', new ext_system('${ASTAGIDIR}/parse.py ${CALLG} ${PICKUPG}'));
$ext->add('app-pickup', "_$fc_pickup.", '', new ext_gotoif('$[${SYSTEMSTATUS} = APPERROR]','permden','permok'));
$ext->add('app-pickup', "_$fc_pickup.", 'permden', new ext_playback('sorry-cant-let-you-do-that2'));
$ext->add('app-pickup', "_$fc_pickup.", '', new ext_hangup(''));
//end new code
if (strstr($version, 'BRI')) {
//added from-did-direct and from-internal
$ext->add('app-pickup', "_$fc_pickup.", 'permok', new ext_dpickup('${EXTEN:'.$fclen.'}'));
$ext->add('app-pickup', "_$fc_pickup.", '', new ext_dpickup('${EXTEN:'.$fclen.'}@from-did-direct'));
$ext->add('app-pickup', "_$fc_pickup.", '', new ext_dpickup('${EXTEN:'.$fclen.'}@from-internal'));
}
else {
//added from-did-direct and from-internal
$ext->add('app-pickup', "_$fc_pickup.", 'permok', new ext_pickup('${EXTEN:'.$fclen.'}'));
$ext->add('app-pickup', "_$fc_pickup.", '', new ext_pickup('${EXTEN:'.$fclen.'}@from-did-direct'));
$ext->add('app-pickup', "_$fc_pickup.", '', new ext_pickup('${EXTEN:'.$fclen.'}@from-internal'));
}
}
also, at line 2439 of the original version 2.4.0.1 version of the file:
Original:
//write to astdb
if ($astman) {
$cid_masquerade = (isset($cid_masquerade) && trim($cid_masquerade) != "")?trim($cid_masquerade):$extension;
$astman->database_put("AMPUSER",$extension."/password",isset($password)?$password:'');
$astman->database_put("AMPUSER",$extension."/ringtimer",isset($ringtimer)?$ringtimer:'');
$astman->database_put("AMPUSER",$extension."/noanswer",isset($noanswer)?$noanswer:'');
$astman->database_put("AMPUSER",$extension."/recording",isset($recording)?$recording:'');
$astman->database_put("AMPUSER",$extension."/outboundcid",isset($outboundcid)?"\"".$outboundcid."\"":'');
$astman->database_put("AMPUSER",$extension."/cidname",isset($name)?"\"".$name."\"":'');
$astman->database_put("AMPUSER",$extension."/cidnum",$cid_masquerade);
$astman->database_put("AMPUSER",$extension."/voicemail","\"".isset($voicemail)?$voicemail:''."\"");
Modified:
//write to astdb
if ($astman) {
$cid_masquerade = (isset($cid_masquerade) && trim($cid_masquerade) != "")?trim($cid_masquerade):$extension;
$astman->database_put("AMPUSER",$extension."/password",isset($password)?$password:'');
$astman->database_put("AMPUSER",$extension."/ringtimer",isset($ringtimer)?$ringtimer:'');
$astman->database_put("AMPUSER",$extension."/noanswer",isset($noanswer)?$noanswer:'');
$astman->database_put("AMPUSER",$extension."/recording",isset($recording)?$recording:'');
$astman->database_put("AMPUSER",$extension."/outboundcid",isset($outboundcid)?"\"".$outboundcid."\"":'');
$astman->database_put("AMPUSER",$extension."/cidname",isset($name)?"\"".$name."\"":'');
$astman->database_put("AMPUSER",$extension."/cidnum",$cid_masquerade);
$astman->database_put("AMPUSER",$extension."/voicemail","\"".isset($voicemail)?$voicemail:''."\"");
//added code to insert callgroup and pickupgroup info to the built in DB
if (isset($devinfo_callgroup)) {
$astman->database_put("AMPUSER",$extension."/callgroup",$devinfo_callgroup);
}
else {
$astman->database_put("AMPUSER",$extension."/callgroup",'');
}
if (isset($devinfo_pickupgroup)) {
$astman->database_put("AMPUSER",$extension."/pickupgroup",$devinfo_pickupgroup);
}
else {
$astman->database_put("AMPUSER",$extension."/pickupgroup",'');
}
//end added code
I also had to write a helper script parse.py and place it in /var/lib/asterisk/agi-bin to sort out and match the callgroup and pickupgroup parameters. Since I don't know PHP too well I wrote it in python. I'm sure someone could probably easily convert this, as it's a pretty simple script.
parse.py:
#!/usr/bin/python
import string
import sys
# Get the values from asterisk
callgroup = sys.argv[1]
pickupgroup = sys.argv[2]
group_array = []
# split the string by commas
pgsplit = pickupgroup.split(',')
#hacky
j=0
i=0
while j < len(pgsplit):
#If the value has a dash in it, the first statement will reject it sending it to the except: statement.
try:
group_array.append(int(pgsplit[j]))
except:
split_range = pgsplit[j].split('-')
split_range[1] = int(split_range[1]) + 1
range_count = range(int(split_range[0]), split_range[1])
for num in range_count:
group_array.append(num)
j = j + 1
while i < len(group_array):
if int(callgroup) == group_array[i]:
sys.exit(0)
i = i + 1
sys.exit(1)
All this results in 2 more attributes being written per extension to the internal asterisk database and the [app-pickup] dialplan will look like this:
[app-pickup]
include => app-pickup-custom
exten => _**.,1,Noop(Attempt to Pickup ${EXTEN:2} by ${CALLERID(num)})
exten => _**.,n,Set(CALLG=${DB(AMPUSER/${EXTEN:2}/callgroup)})
exten => _**.,n,Set(PICKUPG=${DB(AMPUSER/${CALLERID(number)}/pickupgroup)})
exten => _**.,n,System(${ASTAGIDIR}/parse.py ${CALLG} ${PICKUPG})
exten => _**.,n,GotoIf($[${SYSTEMSTATUS} = APPERROR]?permden:permok)
exten => _**.,n(permden),Playback(sorry-cant-let-you-do-that2)
exten => _**.,n,Hangup
exten => _**.,n(permok),Pickup(${EXTEN:2})
exten => _**.,n,Pickup(${EXTEN:2}@from-did-direct)
exten => _**.,n,Pickup(${EXTEN:2}@from-internal)
; end of [app-pickup]
I realize this is a bit hacky, and some pieces are probably missing, but it worked for me, for now. Perhaps the community can improve upon it and implement it soon.