CGI Execute

The phone can be commanded to execute an internal URI handler or fetch an external URL by sending a POST 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.


ExecuteItem link

Specifies a URL to fetch or a URI to execute. Up to 3 <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)
0 Execute immediately (default) 1 Execute when idle 2 Execute only if idle

<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>