Tuesday, April 8, 2008

Push XML to my Cisco IP Phone in .NET

Or "the Expect-100 story", or the "CiscoIPPhoneError 6 quest"...

I was trying to do a simple thing, i.e., push XML to my Cisco IP Phone, in order to make it display a message and/or display a sound.
Theory is simple : POST an XML message to the IP Phone's /CGI/Execute by means of a simple HTTP connection.
I decided to use the C# HttpWebRequest object, thinking it's exactly what I needed.

Note : this is a story of a good workaround, not of the solution, I'm still workin' on it, stay tuned.

What I wanted was to post this to my IP Phone :
"XML=<ciscoipphoneexecute><executeitem priority="0" url="Play:chime.raw"><executeitem priority="0" url="http://10.10.10.10:8080/textmessage.xml"></executeitem>"

that means : play chime.raw and display the xml returned by a GET from the phone on http://10.10.10.10:8080/textmessage.xml

Here's my simple HttpWebRequest code (NOT working...)

string sXML = "XML=<ciscoipphoneexecute><executeitem priority="0" url="Play:chime.raw"><executeitem priority="0" url="http://10.10.10.10:8080/textmessage.xml"></executeitem>"
HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create("http://10.1.1.1/CGI/Execute");
webRequest.Method = "POST";
webRequest.Credentials =
new NetworkCredential("test", "test");
byte[] bArr = Encoding.UTF8.GetBytes(sXML);
webRequest.ContentType = "
application/x-www-form-urlencoded";
webRequest.ContentLength = bArr.Length;
Stream dataStream = webRequest.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();

All I obtained was always a very annoying
<CiscoIPPhoneError Number="6" />

I found nothing on this error, as Cisco docs say :

CiscoIPPhoneError

The following list gives possible CiscoIPPhoneError codes:

  • Error 1 = Error parsing CiscoIPPhoneExecute object

  • Error 2 = Error framing CiscoIPPhoneResponse object

  • Error 3 = Internal file error

  • Error 4 = Authentication error


    So I started sniffing the POST from my dev box to the phone observing some strange traffic before sending the POST message.
    The reason is .NET implementation of the HTTP request, that add an Expect 100-Continue header on the very first packet, and then send the real POST after receiving the 100-Continue from the server (the IP Phone in this case).

    You can find a more exhaustive explanation on this here.
    Note that implementing the solution indicated (set the System.Net.ServicePointManager.Expect100Continue to false) worked not on my code, I'm still workin' on it.

    My solution, or workaround if you wish, was to use a simpler object, the MSXML2.XMLHTTPClass class.

    Here's the working code :

    string sXML = "XML=<ciscoipphoneexecute><executeitem priority="0" url="Play:chime.raw"><executeitem priority="0" url="http://10.10.10.10:8080/textmessage.xml"></executeitem>"
    byte[] bb = System.Text.ASCIIEncoding.UTF8.GetBytes("test" + ":" + "test");
    MSXML2.
    XMLHTTPClass xmlhttp = new MSXML2.XMLHTTPClass();

    xmlhttp.open("
    POST", "http://10.1.1.1/CGI/Execute", true, "", "");
    xmlhttp.setRequestHeader("
    Authorization", "Basic " + Convert.ToBase64String(bb));
    xmlhttp.setRequestHeader("
    Connection", "close");
    xmlhttp.setRequestHeader("
    Content-type", "application/x-www-form-urlencoded");
    xmlhttp.send(sXML);

    17 comments:

    Stephen Sinclair said...

    Hi, thanks for the info. I get a strange response on my phone for the exact request (minus the message, only doing the chime) : Object Not Found. Object Not Found
    The requested URL \'/CGI/Execute\' was not found on the RomPager server. This was an HTML document from the responseText. Any ideas? I am able to ping the ip address on the phone, so I know its available...

    Al said...

    Maybe you could post your code ? This should be a simple encoding issue.

    David J said...
    This comment has been removed by the author.
    David J said...

    I was having the same problem, and it turned out to be encoding.

    Here is some working code (PowerShell). These are .NET objects, so you can use them.

    [System.Text.Encoding]::ASCII.GetBytes("XML=" + [System.Web.HttpUtility]::UrlEncode($xmlstring)

    Adrián said...

    I have yet to try your code but it looks pretty good to me! However, my testing environment consists of a CM Simulator and a Cisco 7940G IP Phone. Since I don't have a production CM, what would be the best option for me to authenticate the PUSH request when the IP Phone server requests it?

    Do you have a modified authentication.asp sample that always returns "AUTHORIZED" value?

    Thank you very much in advance! My best regards!
    Adrian

    Al said...

    Here's a simple example :

    <%@ Language=JavaScript %>

    <%Response.ContentType = "text/html;charset=ISO-8859-1";%>

    <%Response.Write("AUTHORIZED")%>

    Hope it helps,
    Alberto

    Hans said...

    Hi. Thanks for your post! It corroborates my considerations about messaging with cisco ip phones.
    My problem is, that i have to implement these in c++ with quite a strenous compiler (old borland stuff..)...
    When I send my request the phone says "Not Available". Trying to send it again works fine. Resetting the phone to original state (without "not av." message) the same thing happens again. Any ideas / suggesitions?
    I can send you my code by mail if its allright with you. THANKS!

    Al said...

    Feel free to send me your code, I'll have a look at it. A great help in troubleshooting this type of issues comes from an ethereal capture, can you take one while tryin' your code and send it to me too ?

    bye

    Hans said...

    Hi Al! Thanks for your friendly offer, but happily I found the error.. I tried to append some params to the URL I was sending to the phone (http://../?foo=1&bar=2..). The "&" char was the problem. Replacing it with a ";" and URLEncoding this was the solution. Thanks anyway!

    StefaniaO said...

    Hi all, I have the same problem of Stephen Sinclair, using MSXML2.XMLHTTPClass.
    Any ideas ?
    If I push only the sound (chime.raw) using HttpWebRequest it works, but If I add the message I still obtain the CiscoIPPhoneError Number="6"
    Here is my code using MSXML2.XMLHTTPClass:
    public string P2P(string ipddress, string url, string uid, string pwd)
    {
    byte[] bb = System.Text.ASCIIEncoding.UTF8.GetBytes(uid + ":" + pwd);
    string pushxml = "XML=" + HttpUtility.UrlEncode(url);
    MSXML2.XMLHTTPClass xmlhttp = new MSXML2.XMLHTTPClass();
    string uri = "http://" + ipddress + "/CGI/Execute";
    xmlhttp.open("POST", uri, false,"", "");
    xmlhttp.setRequestHeader("Authorization", "Basic " + Convert.ToBase64String(bb));
    xmlhttp.setRequestHeader("Connection", "close");
    xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    xmlhttp.send(url);


    return xmlhttp.responseText;
    }

    Thank you, Stefania.

    Al said...

    Hi Stefania,
    could you post the XML you're trying to send ?
    The fact that the chimes.wav pushing works make me think that you should be experiencing an encoding problem.
    Have a look also at the IP Phone log :

    http://#ipphone#/CGI/Java/Serviceability?adapter=device.statistics.consolelog

    Hope it helps,
    Al

    Daniel said...

    Hi Alberto...

    Thanks for this. it has really helped me!

    I'm having a bit of an issue with my script (VBS)... the same as stephen (The requested URL \'/CGI/Execute\' was not found on the RomPager server)

    my code is below... i've removed the < symbols so it lets me post the xml bit

    the authentication seems fine...

    the only thing is im testing with ip communicator at the moment...

    Option Explicit

    Dim FSO, Mac, UserID, Password, objSvrHTTP, objFSO, XML
    SET FSO=CreateObject("Scripting.FileSystemObject")
    SET objSvrHTTP = CreateObject("Msxml2.XMLHTTP.4.0")

    UserID=inputbox("Enter User ID")
    Password=inputbox("Enter Pin")

    XML = "XML>CiscoIPPhoneText>Title>Title text goes here/Title>Prompt>The prompt text goes here/Prompt>Text>The text to be displayed as the message body goes here/Text>/CiscoIPPhoneText>/XML>"

    objsvrHTTP.open "POST", "http://192.168.1.5/CGI/Execute", false, UserID, Password
    objSvrHTTP.send(XML)
    Wscript.Echo objSvrHTTP.responseText

    beton said...

    I had some strange COMException when trying to get responseText of XmlHttpClass, so I decided to try HttpWebRequest method. I managed to get it working by setting HTTP Version to 1.0 (as "Expect: 100-Continue" is added only on HTTP 1.1 call). So the solution is:
    HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create("http://"+ipAddress+"/CGI/Execute");
    webRequest.ProtocolVersion = new Version("1.0");

    Robertico said...

    & in the uri needs to be escaped to &

    Jijo said...

    Thanks a lot really helped me.
    Jee
    Jee

    jose manuel said...

    This is Technical error code. You can deliver me your rule, I'll have a look at it. A great help in problem solving this kind of problems comes from a heavenly catch.
    Cisco ip phone

    Saul Benavides said...

    Hi there. We are currently in a similar project in which we need to push info to cisco ip phones. We cannot see any message in the ip phone display. We already checked the cisco configuration, the web server (iis) and also scan the network traffic with a sniffer. We don't catch any error message, however the service functionality is not working.

    Can you share some advice for troubleshooting?

    Best regards.