CGI Execute
The phone can be commanded to execute an internal URI handler or fetch an external URL by sending aPOST
request to the phone's web-server using the URL http://x.x.x.x/CGI/Execute with a parameter named XML
. An archive containing sample command line utilties can be downloaded from the URL below.file_download commands-2.4.tar.gz (15K) event 23/05/2022 security SHA256:eaa8aeb0addceb41a6636dc8523f0c0695565c68c777a10efb2fb295a4881b1f.
If an <
authenticationURL
> has been defined, requests need to include a Authorization
header encoded using the basic
method. The username and password will be passed on to the authentication URL for checking. See Phone Services for more information.See the open_in_browser Cisco Unified IP Phone Services Application Development Notes a list of URIs the each phone model supports.
<CiscoIPPhoneExecute>
ExecuteItem link
Specifies a URL to fetch or a URI to execute. Up to3
<ExecuteItem
> tags can be specified.URL | URL to fetch or execute. Must be ether one of the internal URIs (Dial , Key , SoftKey , Init , Play etc.), an http:// URL or an https:// URL. |
||||||
Priority | Priority of the request (optional) | ||||||
|
<ExecuteItem URL="URL" Priority="PRIORITY" />
</CiscoIPPhoneExecute>
An example script that can send up to three CGI execute requests to a phone is below. Available as the
cgiexecute
script from the commands archive above.#!/usr/bin/python3
import sys
import os.path
import re
import getopt
import traceback
from html import escape
import requests
import requests.auth
import requests.adapters
from lxml import etree
class ProgramError(Exception):
pass
class HTTPSAdapter(requests.adapters.HTTPAdapter):
def init_poolmanager(self, *args, **kwargs):
kwargs['assert_hostname'] = False
super().init_poolmanager(*args, **kwargs)
cgi_errors = {
'1': 'Error parsing CiscoIPPhone object',
'2': 'Error framing CiscoIPPhone object',
'3': 'Internal file error',
'4': 'Authentication error'
}
def cgi_execute(hostname, port, timeout, username, password, certificate_file, urls):
xml = '<?xml version="1.0" encoding="UTF-8"?>' \
'<CiscoIPPhoneExecute>'
for url, priority in urls:
xml += '<ExecuteItem URL="' + escape(url) + '" Priority="' + str(priority) + '" />'
xml += '</CiscoIPPhoneExecute>'
if username != '':
auth = requests.auth.HTTPBasicAuth(username, password)
else:
auth = None
scheme = 'https' if certificate_file else 'http'
try:
session = requests.Session()
session.mount('https://', HTTPSAdapter())
response = session.post(f'{scheme}://{hostname}:{port}/CGI/Execute',
timeout = timeout, auth = auth, verify = certificate_file, data = {'XML': xml})
except requests.RequestException as error:
raise ProgramError(error)
if response.headers['Content-Type'][0:8] != 'text/xml':
raise ProgramError('Unexpected Content-Type: ' + response.headers['Content-Type'])
try:
document = etree.fromstring(response.content)
except etree.XMLSyntaxError as error:
raise ProgramError(error)
if document.tag == 'CiscoIPPhoneError':
number = document.get('Number', '')
raise ProgramError('Error: ' + cgi_errors.get(number, number))
for element in document.findall('ResponseItem'):
url = element.get('URL')
data = element.get('Data', '')
print(f'{url}: {data}')
def main():
try:
short_options = 'h:t:u:p:c:H'
long_options = ['host=', 'timeout=', 'username=', 'password=', 'certificate=', 'help']
try:
options, arguments = getopt.gnu_getopt(sys.argv[1:], short_options, long_options)
except getopt.GetoptError as error:
raise ProgramError(error.msg[0].upper() + error.msg[1:] +
'. Try \'' + os.path.basename(sys.argv[0]) + ' --help\' for more information')
hostname = None
port = None
timeout = 10
username = ''
password = ''
certificate_file = None
help = False
for option, argument in options:
if option in ('-h', '--host'):
hostname = argument
if ':' in hostname:
hostname, port = hostname.rsplit(':', maxsplit = 1)
try:
port = int(port)
if port < 1 or port > 65535:
raise ValueError
except ValueError:
raise ProgramError(f'Invalid port: {port}')
if not re.search(r'(?xi) ^ (?: [a-z0-9\-]+ \.)* [a-z0-9\-]+ $', hostname):
raise ProgramError(f'Invalid host: {hostname}')
elif option in ('-t', '--timeout'):
timeout = argument
try:
timeout = int(timeout)
except ValueError:
raise ProgramError(f'Invalid timeout: {timeout}')
elif option in ('-u', '--username'):
username = argument
elif option in ('-p', '--password'):
password = argument
elif option in ('-c', '--certificate'):
certificate_file = argument
elif option in ('-H', '--help'):
help = True
if help:
print('Usage: ' + os.path.basename(sys.argv[0]) + ' [OPTIONS] URL[@PRIORITY]...\n'
'Send CGI Execute URLs to a Cisco IP Phone.\n'
'\n'
' -h, --host HOST[:PORT] host name or IP address and port of the phone\n'
' -t, --timeout TIMEOUT connection timeout in seconds (default 10)\n'
' -u, --username USERNAME authentication username\n'
' -p, --password PASSWORD authentication password\n'
' -c, --certificate CERT-FILE connect using SSL and verify using certificate\n'
' -H, --help print this help and exit\n'
'\n'
'Up to 3 URLs may be specified.\n'
'URL is one of Dial:, EditDial:, Key:, SoftKey:, Init:, Play:, Display:, http: or https:\n'
'Optional PRIORITY is either 0 (immediately), 1 (when idle) or 2 (only if idle).\n')
return
if not len(arguments):
raise ProgramError('No URLs specified')
urls = []
for argument in arguments:
url = argument
if '@' in url:
url, priority = argument.rsplit('@', maxsplit = 1)
try:
priority = int(priority)
if priority < 0 or priority > 2:
raise ValueError
except ValueError:
raise ProgramError(f'Invalid priority: {priority}')
else:
priority = 0
if not re.search(r'(?x) ^ (?: (?: Dial | EditDial) : [0-9#*]+'
r' | (?: Key | SoftKey | Init) : [a-zA-Z0-9]+'
r' | Play : [a-zA-Z0-9._\-]+'
r' | Display : (?: Off | On | Default) (: [0-9]+)?'
r' | https? :// [^ ]+) $', url):
raise ProgramError(f'Invalid URL: {url}')
if len(urls) == 3:
raise ProgramError('A maximum of 3 URLs can be specified')
urls.append((url, priority))
if hostname is None:
raise ProgramError('No host specified')
if port is None:
port = 443 if certificate_file else 80
cgi_execute(hostname, port, timeout, username, password, certificate_file, urls)
except ProgramError as error:
print(str(error), file = sys.stderr)
exit(1)
except Exception:
traceback.print_exc(file = sys.stderr)
exit(1)
exit(0)
if __name__ == '__main__':
main()
setBackground link
Sets the background on the phone, both URLs must be http://. Send to the phone using the same method as <CiscoIPPhoneExecute
> with the XML
parameter as follows. An example setbackground
script can be found in the archive above.See Background Images for more information.
<setBackground>
<background>
<icon>ICON URL</icon>
<image>IMAGE URL</image>
</background>
</setBackground>
setRingTone link
Sets the ring-tone on the phone, the URLs must be http://. Send to the phone using the same method as <CiscoIPPhoneExecute
> with the XML
parameter as follows. An example setringtone
script can be found in the archive above.See Ring Tones for more information.
<setRingTone>
<ringTone>RINGTONE URL</ringTone>
</setRingTone>