Update: Spurred on by pagemen’s comment I looked into the character encoding of the POST requests and I WAS making a mistake by not escaping the data fields. The reason I didn’t twig to that previously was that some accounts would work without the need to escape the data. It will come down to whether the unescaped data contains an illegal character sequence such as \t. Since correcting that bug I have not seen any 500 errors from sipsorcery.

Some people are getting a (500) Internal Server Error when attempting to place a Google Voice Call using the sipsorcery dialplan application. One person has passed along their account details so I could take a look and I’m sorry to say that 4 hours later I’m still none the wiser. As far as I can see the failing account is identical to my working account. Mine can place calls and works every time. The identical account can login and retrieve the key but fails every time with the Internal Server Error when placing the call request.

There are people around with more perserverance than me for this kind of thing (I’m generally happier creating my own bugs than reverse engineering or fixing other people’s, no suprise there) so below is the relevant C# source code that the sipsorcery dialplan application uses to place the call. Running the console application with my own account details works everytime.

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;

namespace GoogleVoiceCall {
    class Program {

        private const string LOGIN_URL = "https://www.google.com/accounts/ServiceLoginAuth?service=grandcentral";
        private const string GOOGLE_VOICE_HOME_URL = "https://www.google.com/voice";
        private const string CALL_URL = "https://www.google.com/voice/call/connect";

        private static string m_emailAddress = "your email address";
        private static string m_password = "your password";
        private static string m_gizmoNumber = "your gizmo number"; 
        private static string m_destinationNumber = "your destination number";

        static void Main(string[] args) {
            try {
                Console.WriteLine("Attempting Google Voice Call");

                CookieContainer cookies = new CookieContainer();

                // First send a login request to get the necessary cookies.
                string loginData = "Email=" + Uri.EscapeDataString(m_emailAddress)
                      + "&Passwd=" + Uri.EscapeDataString(m_password);
                HttpWebRequest loginRequest = (HttpWebRequest)WebRequest.Create(LOGIN_URL);
                loginRequest.CookieContainer = cookies;
                loginRequest.AllowAutoRedirect = true;
                loginRequest.Method = "POST";
                loginRequest.ContentType = "application/x-www-form-urlencoded;charset=utf-8";
                loginRequest.ContentLength = loginData.Length;
                loginRequest.GetRequestStream().Write(Encoding.UTF8.GetBytes(loginData), 0, loginData.Length);

                HttpWebResponse loginResponse = (HttpWebResponse)loginRequest.GetResponse();
                if (loginResponse.StatusCode != HttpStatusCode.OK) {
                    throw new ApplicationException("Login failed.");
                }
                else {
                    Console.WriteLine("Login request was successful.");
                }

                // Second send a request to the Google Voice home page to get a string key needed when placing a callback.
                HttpWebRequest keyRequest = (HttpWebRequest)WebRequest.Create(GOOGLE_VOICE_HOME_URL);
                keyRequest.CookieContainer = cookies;

                HttpWebResponse keyResponse = (HttpWebResponse)keyRequest.GetResponse();
                if (keyResponse.StatusCode != HttpStatusCode.OK) {
                    throw new ApplicationException("_rnr_se key request failed.");
                }
                else {
                    Console.WriteLine("Key request was successful.");
                }

                StreamReader reader = new StreamReader(keyResponse.GetResponseStream());
                string keyResponseHTML = reader.ReadToEnd();
                Match rnrMatch = Regex.Match(keyResponseHTML, @"name=""_rnr_se"".*?value=""(?<rnrvalue>.*?)""");
                if (!rnrMatch.Success) {
                    throw new ApplicationException("_rnr_se key was not found on your Google Voice home page.");
                }
                string rnr = rnrMatch.Result("${rnrvalue}");
                Console.WriteLine("_rnr_se key=" + rnr);

                // Thirdly (and lastly) submit the request to initiate the callback.
                string callData = "outgoingNumber=" + Uri.EscapeDataString(m_destinationNumber) + 
             "&forwardingNumber=" + Uri.EscapeDataString(m_gizmoNumber) +
             "&subscriberNumber=undefined&remember=0&_rnr_se=" + Uri.EscapeDataString(rnr);
                HttpWebRequest callRequest = (HttpWebRequest)WebRequest.Create(CALL_URL);
                callRequest.CookieContainer = cookies;
                callRequest.Method = "POST";
                callRequest.ContentType = "application/x-www-form-urlencoded;charset=utf-8";
                callRequest.ContentLength = callData.Length;
                callRequest.GetRequestStream().Write(Encoding.UTF8.GetBytes(callData), 0, callData.Length);

                HttpWebResponse callResponse = (HttpWebResponse)callRequest.GetResponse();
                if (callResponse.StatusCode != HttpStatusCode.OK) {
                    Console.WriteLine("Call request failed.");
                }
                else {
                    Console.WriteLine("Call request was successful.");
                }
            }
            catch (Exception excp) {
                Console.WriteLine("Exception Main. " + excp.Message);
            }
            finally {
                Console.WriteLine("finished, press any key to exit...");
                Console.ReadLine();
            }
        }
    }
}
About these ads