(500) Internal Server Error on Google Voice Call

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();
            }
        }
    }
}
  1. mazilo’s avatar

    Thank you for posting your GVOut source code. Hopefully, others will be able to taylor some changes to their needs and/or help to redevelop the source to make it works better.

    Reply

  2. emoci’s avatar

    I just have 2 suggestions:

    1. Check with the user having the problem. When Gizmo released this feature at first I noticed that accounts that had just been converted from GC to GV in the last 24-48 hours would not work … they did work after 48 hours though

    2. (this speaks more to the feature below) It seems like the Gizmo number you use for the callback doesn’t have to be one already verified with that GV acct.

    I created a new Gizmo acct. not verified with wither GV account … I have no problem… in fact I can change the GV user/pass set in the dial plan and use the same Gizmo number to place calls via two different GV accts.

    Reply

  3. pagemen’s avatar

    Try to encode the _rnr_se value with HttpServerUtility.UrlEncode before posting since it may contain special chars(like + / =), its also safer to encode the password in the first login http post.

    Reply

    1. sipsorcery’s avatar

      I did play around with the escaping just in case but found it didn’t make any difference. My own _rnr_se has an ‘=’ in it and the call request works fine whether it’s sent as ‘=’ or %3D. The escaping shouldn’t be needed for a POST where the data is sent in the HTTP body which is the case here.

      Reply

    2. sipsorcery’s avatar

      I dug a bit deeper into the encoding issue and the Content-Type being set is application/x-www-form-urlencoded;charset=utf-8 which means the data in the POST request should be escaped, as you pointed out. I’ve made the update to do that (using Uri.EscapeDataString(string)) and it looks like it could be helping. I haven’t seen any 500 errors yet. Thanks for making the suggestion!

      Reply

    3. djon’s avatar

      Re: emoci Item 1.
      The number I am getting “500 error code” for was a GC number converted months ago when $1.00 credit was issued for conversion.

      Other 5 numbers, that are successfully employing the GoogleVoiceCall routine are a mixture of a) Original GC to GV $1.00 credit, b) GC to GV $0.10 credit, and c) GV issued $0.00 credit.

      Reply

    4. Alex’s avatar

      I have the same (or different) kind of problem. When I use my email@gmail.com – for GV credentials – calls don’ go through, but when I use another account me@mydomain.com – it works! Hope this will be useful.

      Reply

    5. djon’s avatar

      Any way to compare Chad’s Firefox GV AddOn approach? The problem GV account works there including for SMS.
      http://thatsmith.com/2009/03/google-voice-add-on-for-firefox

      Reply

      1. sipsorcery’s avatar

        If I could find te source I probaly could. Anyone know if it’s available or where it lives>

        Reply

        1. anonymous’s avatar

          The source code is in the extension itself. The current file is http://thatsmith.com/google-voice-addon-0.5.0.xpi. You can rename it as .zip and open it up with a zip file reader. In the /chrome directory, you will find google-voice.jar. Again, you can rename this file as .zip and open it up. In the /content directory there is google-voice.js, which is the Javascript source code. It is licensed as MPL 1.1/GPL 2.0/LGPL 2.1.

          Reply

        2. sipsorcery’s avatar

          Thanks for the source code tip on the FireFox plugin.

          Reply

        3. Mike’s avatar

          Seems like the encoding trick helped, I’m not getting 500 errors anymore! THANK YOU!!!

          Reply

        4. Brian’s avatar

          How can I can i set this up to check my gv mail?

          Reply

          1. sipsorcery’s avatar

            Not sure, sorry. I don’t know the GV voicemail procedures.

            Reply

Reply

Your email address will not be published. Required fields are marked *