#!/usr/bin/perl -wT # gsutil - GrandStream configuration dump/restore + reboot # Copyright (C) 2006 Charles Howes # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301, USA. # Version 2.4 - 2006-02-04 # Version 2.4 of GSutil, a GrandStream BudgeTone phone backup, restore # and reboot utility. Written by Charles Howes http://www.pkts.ca/gsutil.shtml # Usage: /usr/bin/gsutil -[b|d|e|h|r] [-n] [-o] [-p password] address.. # -b : reboot # -d : dump to stdout # -e : show phone firmware versions # -h, --help : print this help # -r : restore from stdin # -n : don't wait for reboot to finish # -o : don't translate configuration values # -p : password (default: admin) # --version : print the version of gsutil # The Grandstream BudgeTone 100 VOIP and GX2000 telephones are telephones # that does VOIP, from a company called Grandstream. # # The company is http://www.grandstream.com/ # # The company's java configuration tool (which creates tftp files) # is available at http://www.grandstream.com/y-configurationtool.htm # # GsUtil is a short program I wrote to dump and restore the data # from these phones. Since a reboot is required to make the # configuration change effective, this program does that too. # # It's written in Perl, tested on Fedora Core 4, and provided as # tarball and rpm files. Only perl and perl-libwww-perl were # used, and they came with the operating system. # # Version 1.x was done for the 1.0.5.11 firmware, and # Version 2.x is for the current 1.0.2.3 firmware on the GXP2000. # # Here's the help information: # # Version 2.4 of gsutil, a Grandstream BudgeTone phone backup, restore and # reboot utility. # # Usage: gsutil -[b|d|e|h|r] [-n] [-o] [-p password] address.. # -b : reboot # -d : dump to stdout # -e : show phone firmware versions # -h, --help : print this help # -r : restore from stdin # -n : don't wait for reboot to finish # -o : don't translate configuration values # -p : password (default: admin) # --version : print the version of gsutil # # gsutil -d 10.10.13.5 > phone1.conf - dump configuration # gsutil -do 10.10.13.5 > phone1.conf - dump in GS compatible style # gsutil -r 10.10.13.5 < phone1.conf - restore configuration # gsutil -b 10.10.13.5 - reboot phone and wait for it # gsutil -bn 10.10.13.5 - reboot phone and don't wait # gsutil -e 10.10.13.5 - show firmware version number # # gsutil -deorbn 10.10.13.5 < new.conf > old.conf - everything # # gsutil -d 10.10.13.2 10.10.13.3 10.10.13.4 > out.conf - dump phones # gsutil -r 10.10.13.2 10.10.13.3 10.10.13.4 < in.conf - restore phones # gsutil -bn 10.10.13.2 10.10.13.3 10.10.13.4 - boot phones now # # More than one phone can be specified at once; the configurations are # appended when dumping, and separated when restoring. # # Only the settings listed in the input file will be changed on the phone. # This allows you to change a single setting on all of your phones at # once without having to download, edit and upload each phone's # settings. Make a configuration file containing only the parameter # to change, and use that as input: # gsutil -rb phone1 < input # gsutil -rb phone2 < input # # Note: if restoring multiple phones at once, the command line ip # addresses should be in the same order as they were when they were # dumped, or else the configurations will go to different phones. # This may be desirable, or it might upset your users when # their phone number and voicemail get assigned to someone # else's phone. # # Charles Howes -- gsutil@ch.pkts.ca use strict; use Getopt::Std; use HTML::Form; use HTTP::Request; #use HTTP::Cookies; # They don't work for me, I had to kludge it. use LWP::UserAgent; use Socket; use POSIX qw(strftime); use Data::Dumper; # The configuration variables get translated into english with this table. # The translations themselves are stored in the __DATA__ block at the end. my %translate; while () { chomp; s/#.*//; # remove comments s/\s+$//; # remove trailing space # if no translation is available, use the key: /^\s*(P\d+):\s*(.*)/ and $translate{$1} = ($2 || $1); } # Backup the command line: my $cmdline=join(" ",@ARGV); # Stuff used by getopts: $main::VERSION="2.4"; $Getopt::Std::STANDARD_HELP_VERSION=1; # Get command line options: my %opts; my $r=getopts('bdehnop:r',\%opts); # If the user is asking for help: if (defined $opts{'h'}) { VERSION_MESSAGE(*STDOUT{IO}); HELP_MESSAGE(*STDOUT{IO}); exit 0; } # Version information, called by getopts if --version or --help given. sub VERSION_MESSAGE { my $fh=$_[0]; # Passed a file handle. print $fh "Version 2.4 of GSutil, a GrandStream BudgeTone phone backup, restore\n"; print $fh "and reboot utility. Written by Charles Howes. http://www.pkts.ca/gsutil.shtml\n"; } # Help information, called by getopts if --help given. sub HELP_MESSAGE { my $fh=$_[0]; # Passed a file handle. print $fh "Usage: $0 -[b|d|e|h|r] [-n] [-o] [-p password] address..\n"; print $fh " -b : reboot\n"; print $fh " -d : dump to stdout\n"; print $fh " -e : show phone firmware versions\n"; print $fh " -h, --help : print this help\n"; print $fh " -r : restore from stdin\n"; print $fh " -n : don't wait for reboot to finish\n"; print $fh " -o : don't translate configuration values\n"; print $fh " -p : password (default: admin)\n"; print $fh " --version : print the version of gsutil\n"; # getopts exits after calling HELP_MESSAGE() } if (!defined $opts{'b'} and !defined $opts{'d'} and !defined $opts{'e'} and !defined $opts{'r'}) { print STDERR "No command was given.\n\n"; print STDERR "Choose one of -b, -d, -e, or -r to do something.\n"; print STDERR "Try `$0 --help' for more information.\n"; exit 0; } # Get the password my $password=$opts{'p'}; if (!defined $password) {$password='admin'}; # The addresses are on the command line: if (!defined $ARGV[0]) { print STDERR "No addresses of phones were given on the command line.\n"; HELP_MESSAGE(*STDOUT{IO}); exit(3); } my $linenumber=0; # For each address on the command line: foreach my $add (@ARGV) { # Validate the address: if ($add !~ m/^([a-z0-9.-]{1,80})$/) { die("$add isn't a valid hostname."); } my $address=$1; if ($address=~m/[a-z]/) { if (!defined gethostbyname($address)) { warn("$0: unknown host $address\n"); next; } } # Make a url out of it: my $url="http://$address/"; # Set up the user agent: my $ua=LWP::UserAgent->new; # Cookie handling in LWP is messed up, so these parts are commented out: # $ua->cookie_jar( {} ); # Store cookies temporarily # my $cookie_jar = HTTP::Cookies->new( # file=>"/tmp/lwp_cookies.dat", # autosave=>1, # ); # $ua->cookie_jar($cookie_jar); # Store cookies permanently $ua->timeout(3); # Set timeout # Get the login page: my $request=HTTP::Request->new(GET => $url); my $response=$ua->request($request); # Page not found: if ($response->code == 403) { warn("$address couldn't find the login page, is it a GS phone?\n"); next; } # Error: if ($response->code == 500) { if ($response->content =~ m/Can't connect to \S+ \(connect: timeout\)/) { warn("$address was unreachable, was that the right address?\n"); next; } # Some other fatal error: print "A 500 error was received, but not for connection timeout.\n"; print "Here is a full dump of the response:\n"; print Dumper($response),"\n"; exit 0; } # Parse the form on the login page my $form=HTML::Form->parse($response->content,$url); if (!defined $form) { warn("$address didn't respond with a form; is it a GS phone?\n"); next; } # See if this is the right form my @names=$form->param; if (grep(/^P2$/,@names)==0) { warn("The form didn't have the expected 'P2' (password) field in it!\n". "The fields that were found were: ".join(", ",@names)."\n"); next; } # Stuff in the password $form->value('P2' => $password); # Submit it (log in) $response = $ua->request($form->click); # Check to see if we logged in successfully. if ($response->content =~ m/Your Login Password is not recognized/) { die("$address: Your login password was not recognized.\n". "Try using the '-p password' option:\n". " $0 -p secret-password $cmdline\n"); } # Parse the response (the main config form). # This is done rather early, in order to cache the info. $form=HTML::Form->parse($response->content,$form->action); # Figure out the version: my $r=$response->content; my $version=""; if ($r=~m/Grandstream Device Configuration/) { $version="1.0.6.x";} if ($r=~m/Grandstream IP Phone Configuration/) { $version="1.0.5.x";} if ($version eq "") {warn("$0: $address: Unknown firmware\n");next;} #print Dumper($response->headers); my $cookie=$response->headers->header("set-cookie"); #1.0.5.x:'set-cookie' => 'SessionId=3bf1ca965d26edb9"; Version=1; Path=/' #1.0.6.x:'set-cookie' => 'SessionId="965c22e1bd86fece"; Version=1; Path=/' if (!defined $cookie) { die("$address: No cookie was sent!\n"); } if ($cookie!~m/SessionId="?([^"]*)"/) { die("$address: The cookie didn't contain 'SessionId='!\n". " It said: $cookie\n"); } #if ($cookie!~m/SessionId="?([^"]*)"/) { die("No cookie found!"); } my $sessionid=$1; # Show the firmware release information: if (defined $opts{'e'}) { if ($version eq "1.0.5.x") { $r=~s/.*Software Version: <\/b><\/td>\s+//s; $r=~s/<\/td>.*//s; $r=~s///s; $r=~s/( |\s)+/ /sg; $r=~s/--/=/sg; $r=~s/^\s+//m; $r=~s/\s+$//mg; print "# $address versions: $r\n"; } if ($version eq "1.0.6.x") { # Get status page $request=HTTP::Request->new(GET => $url."index.htm"); $request->header("Cookie" => "SessionId=\"$sessionid\""); $response=$ua->request($request); if (!defined $response) {warn("$address didn't respond to request 2");next;} my $r2=$response->content; $r2=~s/.*Program--/Program--/s; $r2=~s/System Up Time:.*//s; $r2=~s/<\/td>.*//s; $r2=~s/( |\s)+/ /sg; $r2=~s/-- /=/sg; print "# $address versions: $r2\n"; } } # Dump the data: if (defined $opts{'d'}) { # Print the data my $stamp=strftime("%Y-%m-%d %H:%M:%S",localtime()); print "#-- start of phone $address ($stamp)\n"; # Mark the start # Dump form 1: dump_form($form, $version); if ($version eq "1.0.6.x") { # Dump the Basic Settings page as well: dump_url($ua, $url."config2.htm", $form->action, $sessionid, $version); # See if we have settings pages for account1, 2, 3, and 4. If so, # dump them. These are available on the GXP-2000, at least on # phones running firmware Program 1.0.1.2. The $version string # doesn't detect GXP-2000 firmware versions correctly: this # information is on the "Status" page. for my $a ("a1".."a4") { dump_url($ua, $url."config_$a.htm", $form->action, $sessionid, $version); } } print "#-- end of phone $address ($stamp)\n"; } # Restore the data: if (defined $opts{'r'}) { my %val; # Error checking: my %formlines; my %filled; # Prepare conversion back to P-values: my %untranslate; foreach (keys %translate) {$untranslate{uc($translate{$_})}=$_;} # Read the file: while() { chomp; $linenumber++; if (m/^#-- end of phone/) {last;} # Stop if (m/^#/) {next;} # Skip comment lines if (m/^$/) {next;} # Skip blank lines s/\s+#.*//; # Comment deletion (won't cut in the middle of a word) if (m/^([^=]+)=(.*?)$/) { # name = value... my $ls=uc($1); # ls=left side my $rs=$2; # rs=right side # Trim leading and trailing whitespace: $ls=~s/^\s*//; $ls=~s/\s*$//; $rs=~s/^\s*//; $rs=~s/\s*$//; # Optional quote removal: if ($rs=~m/^'(.*)'$/) {$rs=$1;} if ($rs=~m/^"(.*)"$/) {$rs=$1;} # Uppercase the name: $ls=uc($ls); # Stick it into %val: if (defined($translate{$ls})) { $val{$ls}=$rs;next;} if (defined($untranslate{$ls})) { $val{$untranslate{$ls}}=$rs;next;} die("$0: Unknown variable: '$_', stdin line $linenumber"); } die("$0: Unrecognized line: '$_', stdin line $linenumber"); } # Fill in the form: foreach my $n ($form->param) { $formlines{$n}=1; if (defined $val{$n}) { # Only put it in if it was present $form->param($n,$val{$n}); $filled{$n}=1; } } # Add cookie: $request=$form->make_request; $request->header("Cookie" => "SessionId=\"$sessionid\""); # Send it: $response=$ua->request($request); # There's a second config page in the new firmware. # It may be the case that we could stick everything into the first # form and it'd work, but I didn't feel like figuring that out. if ($version eq "1.0.6.x") { $request=HTTP::Request->new(GET => $url."config2.htm"); # $request=HTTP::Request->new(GET => $url."update.htm"); $request->header("Cookie" => "SessionId=\"$sessionid\""); # Add cookie $response=$ua->request($request); $form=HTML::Form->parse($response->content,$form->action); # Fill in the form foreach my $n ($form->param) { $formlines{$n}=1; if (defined $val{$n}) { $form->param($n,$val{$n}); $filled{$n}=1; } } # Add cookie: $request=$form->make_request; $request->header("Cookie" => "SessionId=\"$sessionid\""); # Send it: $response=$ua->request($request); } # do the loop for the # four sip accounts for my $a ("a1".."a4") { if ($version eq "1.0.6.x") { $request=HTTP::Request->new(GET => $url."config_$a.htm"); # $request=HTTP::Request->new(GET => $url."update.htm"); $request->header("Cookie" => "SessionId=\"$sessionid\""); # Add cookie $response=$ua->request($request); $form=HTML::Form->parse($response->content,$form->action); # Fill in the form foreach my $n ($form->param) { $formlines{$n}=1; if (defined $val{$n}) { $form->param($n,$val{$n}); $filled{$n}=1; } } # Add cookie: $request=$form->make_request; $request->header("Cookie" => "SessionId=\"$sessionid\""); # Send it: $response=$ua->request($request); } } # Check for missed data: foreach (sort keys %val) { if (!defined $formlines{$_}) { print "The config file had $_ ($translate{$_}), but the phone did not.\n"; } } # Check for missed form fields (undesirable): #foreach (sort keys %formlines) { # if (!defined $val{$_}) { # print "The form had $_ ($translate{$_}), but the config file did not.\n"; # } #} } # Reboot the phone: if (defined $opts{'b'}) { $request->uri($url."rs.htm"); $request->header("Cookie" => "SessionId=\"$sessionid\""); $response=$ua->request($request); # Wait for reboot to finish (skip it with the -n option) if (!defined $opts{'n'}) { my $now=time(); my $up=1; $ua->timeout(1); for (my $n=0;$n<30;$n++) { # Should be done in 21 seconds or so. $request=HTTP::Request->new(GET => $url); $response=$ua->request($request); #print time()-$now,": ",$response->code,"\n"; if ($response->code==200 and $up==1) {sleep(1);next;} if ($response->code==500) {$up=0;next;} last; # It's up again after being down (200 -> 500 -> 200) } } } # Done with this phone, on to the next. } sub dump_form { my ($form, $version) = @_; foreach my $n ($form->param) { if ($n =~ m/^(cancel|reboot|gnkey|update)$/) {next;} if ($version eq "1.0.6.x" && $n =~ m/^(P2|P196)$/) {next;} # Skip if (defined $translate{$n} && !defined $opts{'o'}) { print $translate{$n}," = ",$form->param($n),"\n"; } else { if (!defined $opts{'o'}) { die("$n not found in translate table"); } if (defined $translate{$n}) { print "# $translate{$n}:\n"; } print "$n = ",$form->param($n),"\n"; print "\n"; } } } sub dump_url { my ($ua, $url, $action, $session, $version) = @_; my $request = HTTP::Request->new(GET => $url); $request->header("Cookie" => "SessionId=\"$session\""); my $response = $ua->request($request); my $form = HTML::Form->parse($response->content, $action); # Dump the form dump_form($form, $version); } __DATA__ P2: admin_password P3: name P8: static_ipaddress_true P9: static_ipaddress_1 P10: static_ipaddress_2 P11: static_ipaddress_3 P12: static_ipaddress_4 P13: static_subnet_1 P14: static_subnet_2 P15: static_subnet_3 P16: static_subnet_4 P17: static_router_1 P18: static_router_2 P19: static_router_3 P20: static_router_4 P21: static_dns1_1 P22: static_dns1_2 P23: static_dns1_3 P24: static_dns1_4 P25: static_dns2_1 P26: static_dns2_2 P27: static_dns2_3 P28: static_dns2_4 P29: early_dial P30: ntp_server P31: sip_registration P32: register_expiration P33: voicemail_userid P34: authenticate_password P35: sip_userid P36: authenticate_id P37: voice_frames_per_tx P38: layer3_QoS P39: local_rtp_port P40: local_sip_port P41: tftp_server_1 P42: tftp_server_2 P43: tftp_server_3 P44: tftp_server_4 P46: vocoder_7 P47: sip_server P48: outbound_proxy P49: G723_rate P50: silence_suppression P51: layer2_QoS_vlan_tag P52: nat_traversal_true P57: vocoder_1 P58: vocoder_2 P59: vocoder_3 P60: vocoder_4 P61: vocoder_5 P62: vocoder_6 P63: userid_is_phone_number P64: time_zone P65: send_anonymous P66: dial_plan_prefix P71: offhook_autodial P72: use_hash_as_dial_key P73: send_dtmf_mode P74: send_flash_event P75: daylight_savings_time P76: nat_traversal_stun_server P78: use_random_port P79: dtmf_payload_type P81: unregister_on_reboot P82: PPPoE_id P83: PPPoE_password P84: keepalive_interval P85: no_key_entry_timeout P86: FXS_impedance P87: layer2_QoS_priority_value P88: lock_keypad_update P90: auto_answer P91: disable_call_waiting P92: dhcp_dns_server_1 P93: dhcp_dns_server_2 P94: dhcp_dns_server_3 P95: dhcp_dns_server_4 P96: iLBC_payload_type P97: iLBC_frame_size P98: vocoder_8 P99: subscribe_for_mwi P101: use_nat_ip P102: date_display_format P103: use_dns_srv P104: default_ring_tone P105: custom_ring_tone_1_caller_id P106: custom_ring_tone_2_caller_id P107: custom_ring_tone_3_caller_id P110: lan_subnet_mask P111: lan_dhcp_base_ip P112: dhcp_ip_lease_time P113: dmz_ip P115: cloned_wan_mac_addr_1 P116: cloned_wan_mac_addr_2 P117: cloned_wan_mac_addr_3 P118: cloned_wan_mac_addr_4 P119: cloned_wan_mac_addr_5 P120: cloned_wan_mac_addr_6 P122: time_display_format # 0 = 12 hour; 1 = 24 hour P123: display_clock_instead_of_date # 0 or 1 P143: dhcp_option_2_override_timezone #SRS P144: dhcp_option_42_override_ntp_server #SRS P145: dhcp_option_66_override_tftp_server #SRS P146: PPPoE_host_name #SRS P147: PPPoE_domain_name #SRS P148: PPPoE_vendor_class_id #SRS Default is Grandstream GXP2000 P150: port_fw_port_num_1 P151: port_fw_port_num_2 P152: port_fw_port_num_3 P153: port_fw_port_num_4 P154: port_fw_port_num_5 P155: port_fw_port_num_6 P156: port_fw_port_num_7 P157: port_fw_port_num_8 P158: port_fw_lan_ip_1 P159: port_fw_lan_ip_2 P160: port_fw_lan_ip_3 P161: port_fw_lan_ip_4 P162: port_fw_lan_ip_5 P163: port_fw_lan_ip_6 P164: port_fw_lan_ip_7 P165: port_fw_lan_ip_9 P166: port_fw_lan_port_1 P167: port_fw_lan_port_2 P168: port_fw_lan_port_3 P169: port_fw_lan_port_4 P170: port_fw_lan_port_5 P171: port_fw_lan_port_6 P172: port_fw_lan_port_7 P173: port_fw_lan_port_8 P174: port_fw_lan_proto_1 P175: port_fw_lan_proto_2 P176: port_fw_lan_proto_3 P177: port_fw_lan_proto_4 P178: port_fw_lan_proto_5 P179: port_fw_lan_proto_6 P180: port_fw_lan_proto_7 P181: port_fw_lan_proto_8 P182: disable_missed_call_1 #SRS P184: use_quick_ip_call_mode #SRS P189: replay_to_icmp_in_wan # 0 = no # 1 = yes P190: wan_side_http_access # 0 = no # 1 = yes P191: enable_call_features P192: http_upgrade_url P193: automatic_http_upgrade_days P194: automatic_http_upgrade P196: end_user_password P197: proxy_require P198: special_feature_1 # 100 = Standard # 101 = Nortel MCS # 102 = Broadsoft # 103 = Howdy # 104 = Sonus # 105 = 3COM P200: caller_id_scheme P205: polarity_reversal P206: onhook_voltage P207: syslog_server P208: syslog_level P209: sip_t1_timeout # 50 = 0_5_sek # 100 = 1_sek # 200 = 2_sek P212: firmware_upgrade P213: firmware_upgrade_tftp_1 P214: firmware_upgrade_tftp_2 P215: firmware_upgrade_tftp_3 P216: firmware_upgrade_tftp_4 P228: fax_mode P231: device_mode # 0 = switch # 1 = nat_router P232: firmware_file_prefix P233: firmware_file_postfix P234: config_file_prefix P235: config_file_postfix P237: config_server_path P238: always_check_for_new_firmware P240: auth_config_file # 0 = No # 1 = Yes P250: sip_t2_interval P260: session_expiration_1 # in seconds, default 180 P261: min_session_expiration_1 # seconds, minimum 90 P262: caller_request_timer_1 # 0 or 1 P263: callee_request_timer_1 # 0 or 1 P264: force_timer_1 # 0 or 1 P265: force_invite_1 # 0 or 1 P266: uac_specify_refresher_1 # 0=omit, 1=UAC, 2=UAS P267: uas_specify_refresher_1 1# 1=UAC, 2=UAS P270: account_name_1 P271: account_active_1 # 0 or 1 P272: enable_100rel_1 # 0 or 1 P298: allow_auto_answer_by_call_info P299: turn_off_speaker_on_remote_disconnect P301: speed_dial_1_account # 0, 1, 2, 3 P302: speed_dial_1_name P303: speed_dial_1_userid P304: speed_dial_2_account P305: speed_dial_2_name P306: speed_dial_2_userid P307: speed_dial_3_account P308: speed_dial_3_name P309: speed_dial_3_userid P310: speed_dial_4_account P311: speed_dial_4_name P312: speed_dial_4_userid P313: speed_dial_5_account P314: speed_dial_5_name P315: speed_dial_5_userid P316: speed_dial_6_account P317: speed_dial_6_name P318: speed_dial_6_userid P319: speed_dial_7_account P320: speed_dial_7_name P321: speed_dial_7_userid P322: lcd_backlight_always_on # 0 or 1 P323: multi_purpose_key_1 # 0 = speed_dial # 1 = asterisk_blf P324: multi_purpose_key_2 # 0 = speed_dial # 1 = asterisk_blf P325: multi_purpose_key_3 # 0 = speed_dial # 1 = asterisk_blf P326: multi_purpose_key_4 # 0 = speed_dial # 1 = asterisk_blf P327: multi_purpose_key_5 # 0 = speed_dial # 1 = asterisk_blf P328: multi_purpose_key_6 # 0 = speed_dial # 1 = asterisk_blf P329: multi_purpose_key_7 # 0 = speed_dial # 1 = asterisk_blf P401: account_active_2 # 0 or 1 P402: sip_server_2 P403: outbound_proxy_2 P404: sip_userid_2 P405: authenticate_id_2 P406: authenticate_password_2 P407: name_2 P408: use_dns_srv_2 P409: userid_is_phone_number_2 P410: sip_registration_2 P411: unregister_on_reboot_2 P412: register_expiration_2 P413: local_sip_port_2 P414: nat_traversal_true_2 P415: subscribe_for_mwi_2 P416: send_dtmf_2 # 0=in-audio, 1=RTP (RFC2833), 2=SIP INFO P417: account_name_2 P418: proxy_require_2 P419: dial_plan_prefix_2 P420: enable_call_features_2 P421: send_anonymous_2 P422: early_dial_2 P423: account_ring_tone_2 # 0, 1, 2, 3 P424: special_feature_2 # 100 = Standard # 101 = Nortel MCS # 102 = Broadsoft # 103 = Howdy # 104 = Sonus # 105 = 3COM P425: auto_answer_2 P426: voicemail_userid_2 P427: min_session_expiration_2 P428: caller_request_timer_2 P429: callee_request_timer_2 P430: force_timer_2 P431: force_invite_2 P432: uac_specify_refresher_2 P433: uas_specify_refresher_2 P434: session_expiration_2 P435: enable_100rel_2 P438: allow_auto_answer_by_caller_info P439: turn_off_speaker_on_remote_disconnect P440: sip_t1_imeout P441: sip_t2_interval P442: disable_missed_call_2 #SRS P451: vocoder_1_2 P452: vocoder_2_2 P453: vocoder_3_2 P454: vocoder_4_2 P455: vocoder_5_2 P456: vocoder_6_2 P457: vocoder_7_2 P458: vocoder_8_2 P501: account_active_3 # 0 or 1 P502: sip_server_3 P503: outbound_proxy_3 P504: sip_userid_3 P505: authenticate_id_3 P506: authenticate_password_3 P507: name_3 P508: use_dns_srv_3 P509: userid_is_phone_number_3 P510: sip_registration_3 P511: unregister_on_reboot_3 P512: register_expiration_3 P513: local_sip_port_3 P514: nat_traversal_true_3 P515: subscribe_for_mwi_3 P516: send_dtmf_3 # 0=in-audio, 1=RTP (RFC2833), 2=SIP INFO P517: account_name_3 P518: proxy_require_3 P519: dial_plan_prefix_3 P520: enable_call_features_3 P521: send_anonymous_3 P522: early_dial_3 P523: account_ring_tone_3 # 0, 1, 2, 3 P524: special_feature_3 # 100 = Standard # 101 = Nortel MCS # 102 = Broadsoft # 103 = Howdy # 104 = Sonus # 105 = 3COM P525: auto_answer_3 P526: voicemail_userid_3 P527: min_session_expiration_3 P528: caller_request_timer_3 P529: callee_request_timer_3 P530: force_timer_3 P531: force_invite_3 P532: uac_specify_refresher_3 P533: uas_specify_refresher_3 P534: session_expiration_3 P535: enable_100rel_3 P538: allow_auto_answer_by_caller_info P539: turn_off_speaker_on_remote_disconnect P540: sip_t1_imeout P541: sip_t2_interval P542: disable_missed_call_3 #SRS P551: vocoder_1_3 P552: vocoder_2_3 P553: vocoder_3_3 P554: vocoder_4_3 P555: vocoder_5_3 P556: vocoder_6_3 P557: vocoder_7_3 P558: vocoder_8_3 P601: account_active_4 # 0 or 1 P602: sip_server_4 P603: outbound_proxy_4 P604: sip_userid_4 P605: authenticate_id_4 P606: authenticate_password_4 P607: name_4 P608: use_dns_srv_4 P609: userid_is_phone_number_4 P610: sip_registration_4 P611: unregister_on_reboot_4 P612: register_expiration_4 P613: local_sip_port_4 P614: nat_traversal_true_4 P615: subscribe_for_mwi_4 P616: send_dtmf_4 # 0=in-audio, 1=RTP (RFC2833), 2=SIP INFO P617: account_name_4 P618: proxy_require_4 P619: dial_plan_prefix_4 P620: enable_call_features_4 P621: send_anonymous_4 P622: early_dial_4 P623: account_ring_tone_4 # 0, 1, 2, 3 P624: special_feature_4 # 100 = Standard # 101 = Nortel MCS # 102 = Broadsoft # 103 = Howdy # 104 = Sonus # 105 = 3COM P625: auto_answer_4 P626: voicemail_userid_4 P627: min_session_expiration_4 P628: caller_request_timer_4 P629: callee_request_timer_4 P630: force_timer_4 P631: force_invite_4 P632: uac_specify_refresher_4 P633: uas_specify_refresher_4 P634: session_expiration_4 P635: enable_100rel_4 P638: allow_auto_answer_by_caller_info P639: turn_off_speaker_on_remote_disconnect P640: sip_t1_imeout P641: sip_t2_interval P642: disable_missed_call_4 #SRS P651: vocoder_1_4 P652: vocoder_2_4 P653: vocoder_3_4 P654: vocoder_4_4 P655: vocoder_5_4 P656: vocoder_6_4 P657: vocoder_7_4 P658: vocoder_8_4