Advertisement
Guest User

Could not find _rnr_se key fixed

a guest
Sep 11th, 2011
694
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 21.39 KB | None | 0 0
  1. //-----------------------------------------------------------------------------
  2. // Filename: GoogleVoiceCall.cs
  3. //
  4. // Description: A dial plan command that places HTTP request to initiate a call
  5. // through the Google Voice service and bridges the callback with the original caller.
  6. //
  7. // History:
  8. // 11 Aug 2009      Aaron Clauson       Created.
  9. //
  10. // License:
  11. // This software is licensed under the BSD License http://www.opensource.org/licenses/bsd-license.php
  12. //
  13. // Copyright (c) 2008 Aaron Clauson (aaronc@blueface.ie), Blue Face Ltd, Dublin, Ireland (www.blueface.ie)
  14. // All rights reserved.
  15. //
  16. // Redistribution and use in source and binary forms, with or without modification, are permitted provided that
  17. // the following conditions are met:
  18. //
  19. // Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  20. // Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
  21. // disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of Blue Face Ltd.
  22. // nor the names of its contributors may be used to endorse or promote products derived from this software without specific
  23. // prior written permission.
  24. //
  25. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
  26. // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  27. // IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
  28. // OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
  29. // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  30. // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  31. // POSSIBILITY OF SUCH DAMAGE.
  32. //-----------------------------------------------------------------------------
  33.  
  34. using System;
  35. using System.Collections.Generic;
  36. using System.IO;
  37. using System.Linq;
  38. using System.Net;
  39. using System.Security;
  40. using System.Text;
  41. using System.Text.RegularExpressions;
  42. using System.Threading;
  43. using System.Web;
  44. using SIPSorcery.SIP;
  45. using SIPSorcery.SIP.App;
  46. using SIPSorcery.Sys;
  47. using log4net;
  48.  
  49. namespace SIPSorcery.AppServer.DialPlan
  50. {
  51.     public class GoogleVoiceCall
  52.     {
  53.         private const string PRE_LOGIN_URL = "https://www.google.com/accounts/ServiceLogin";
  54.         private const string LOGIN_URL = "https://www.google.com/accounts/ServiceLoginAuth?service=grandcentral";
  55.         private const string VOICE_HOME_URL = "https://www.google.com/voice";
  56.         private const string VOICE_CALL_URL = "https://www.google.com/voice/call/connect";
  57.         private const string CANCEL_CALL_URL = "https://www.google.com/voice/call/cancel";
  58.         private const int MIN_CALLBACK_TIMEOUT = 3;
  59.         private const int MAX_CALLBACK_TIMEOUT = 60;
  60.         private const int WAIT_FOR_CALLBACK_TIMEOUT = 30;
  61.         private const int HTTP_REQUEST_TIMEOUT = 5;
  62.  
  63.         private static ILog logger = AppState.logger;
  64.         private SIPMonitorLogDelegate Log_External;
  65.  
  66.         private SIPTransport m_sipTransport;
  67.         private ISIPCallManager m_callManager;
  68.         private string m_username;
  69.         private string m_adminMemberId;
  70.         private SIPEndPoint m_outboundProxy;
  71.  
  72.         private string m_forwardingNumber;
  73.         private string m_fromURIUserRegexMatch;
  74.         private string m_destinationNumber;
  75.         private ManualResetEvent m_waitForCallback = new ManualResetEvent(false);
  76.         private ISIPServerUserAgent m_callbackCall;
  77.         private bool m_clientCallCancelled;
  78.         private bool m_hasBeenCancelled;
  79.         private CookieContainer m_cookies;
  80.         private string m_rnrKey;
  81.  
  82.         internal event CallProgressDelegate CallProgress;
  83.  
  84.         public GoogleVoiceCall(
  85.             SIPTransport sipTransport,
  86.             ISIPCallManager callManager,
  87.             SIPMonitorLogDelegate logDelegate,
  88.             string username,
  89.             string adminMemberId,
  90.             SIPEndPoint outboundProxy)
  91.         {
  92.             m_sipTransport = sipTransport;
  93.             m_callManager = callManager;
  94.             Log_External = logDelegate;
  95.             m_username = username;
  96.             m_adminMemberId = adminMemberId;
  97.             m_outboundProxy = outboundProxy;
  98.         }
  99.  
  100.         /// <summary>
  101.         /// Initiates a Google Voice callback by sending 3 HTTP requests and then waiting for the incoming SIP call.
  102.         /// </summary>
  103.         /// <param name="emailAddress">The Google Voice email address to login with.</param>
  104.         /// <param name="password">The Google Voice password to login with.</param>
  105.         /// <param name="forwardingNumber">The number to request Google Voice to do the intial callback on.</param>
  106.         /// <param name="destinationNumber">The number to request Google Voice to dial out on. This is what Google will attempt to
  107.         /// call once the callback on the forwardingNumber is answered.</param>
  108.         /// <param name="fromUserToMatch">The FromURI user to match to recognise the incoming call. If null it will be assumed that
  109.         /// Gizmo is being used and the X-GoogleVoice header will be used.</param>
  110.         /// <param name="contentType">The content type of the SIP call into sipsorcery that created the Google Voice call. It is
  111.         /// what will be sent in the Ok response to the initial incoming callback.</param>
  112.         /// <param name="body">The content of the SIP call into sipsorcery that created the Google Voice call. It is
  113.         /// what will be sent in the Ok response to the initial incoming callback.</param>
  114.         /// <returns>If successful the dialogue of the established call otherwsie null.</returns>
  115.         public SIPDialogue InitiateCall(string emailAddress, string password, string forwardingNumber, string destinationNumber, string fromUserRegexMatch, int phoneType, int waitForCallbackTimeout, string contentType, string body)
  116.         {
  117.             try
  118.             {
  119.                 m_forwardingNumber = forwardingNumber;
  120.                 m_destinationNumber = destinationNumber;
  121.                 m_fromURIUserRegexMatch = fromUserRegexMatch;
  122.  
  123.                 if (CallProgress != null)
  124.                 {
  125.                     //CallProgress(SIPResponseStatusCodesEnum.Ringing, "Initiating Google Voice call", null, null, null);
  126.                     CallProgress(SIPResponseStatusCodesEnum.Ringing, null, null, null, null, null);
  127.                 }
  128.  
  129.                 m_cookies = new CookieContainer();
  130.                 m_rnrKey = Login(emailAddress, password);
  131.                 if (!m_rnrKey.IsNullOrBlank())
  132.                 {
  133.                     Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Call key " + m_rnrKey + " successfully retrieved for " + emailAddress + ", proceeding with callback.", m_username));
  134.                     return SendCallRequest(forwardingNumber, destinationNumber, phoneType, waitForCallbackTimeout, contentType, body);
  135.                 }
  136.                 else
  137.                 {
  138.                     Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Call key was not retrieved for " + emailAddress + " callback cannot proceed.", m_username));
  139.                     return null;
  140.                 }
  141.             }
  142.             catch (Exception excp)
  143.             {
  144.                 logger.Error("Exception GoogleVoiceCall InitiateCall. " + excp.Message);
  145.                 throw;
  146.             }
  147.         }
  148.  
  149.         private string Login(string emailAddress, string password)
  150.         {
  151.             try
  152.             {
  153.                 Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Logging into google.com for " + emailAddress + ".", m_username));
  154.  
  155.                 // Fetch GALX
  156.                 HttpWebRequest galxRequest = (HttpWebRequest)WebRequest.Create(PRE_LOGIN_URL);
  157.                 galxRequest.ConnectionGroupName = "prelogin";
  158.                 galxRequest.CookieContainer = m_cookies;
  159.  
  160.                 HttpWebResponse galxResponse = (HttpWebResponse)galxRequest.GetResponse();
  161.                 if (galxResponse.StatusCode != HttpStatusCode.OK)
  162.                 {
  163.                     galxResponse.Close();
  164.                     throw new ApplicationException("Load of the Google Voice pre-login page failed with response " + galxResponse.StatusCode + ".");
  165.                 }
  166.                 else
  167.                 {
  168.                     Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Google Voice pre-login page loaded successfully.", m_username));
  169.                 }
  170.  
  171.                 StreamReader galxReader = new StreamReader(galxResponse.GetResponseStream());
  172.                 string galxResponseFromServer = galxReader.ReadToEnd();
  173.                 galxResponse.Close();
  174.  
  175.                 Match galxMatch = Regex.Match(galxResponseFromServer, @"name=""GALX""\s+?value=""(?<galxvalue>.*?)""");
  176.                 if (galxMatch.Success)
  177.                 {
  178.                     Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "GALX key " + galxMatch.Result("${galxvalue}") + " successfully retrieved.", m_username));
  179.                 }
  180.                 else
  181.                 {
  182.                     throw new ApplicationException("Could not find GALX key on your Google Voice pre-login page, callback cannot proceed.");
  183.                 }
  184.  
  185.                // WebProxy proxy = new WebProxy("http:g0000000000.no-ip.org:81", false);
  186.                 // Build login request.
  187.                 m_cookies.Add(new Uri(PRE_LOGIN_URL), galxResponse.Cookies);
  188.                 string loginData = "Email=" + Uri.EscapeDataString(emailAddress) + "&Passwd=" + Uri.EscapeDataString(password) + "&GALX=" + Uri.EscapeDataString(galxMatch.Result("${galxvalue}"));
  189.                 //string loginData = "Email=" + emailAddress + "&Passwd=" + password + "&GALX=" + Uri.EscapeDataString(galxMatch.Result("${galxvalue}"));
  190.                 HttpWebRequest loginRequest = (HttpWebRequest)WebRequest.Create(LOGIN_URL);
  191.                 //loginRequest.Proxy = proxy;
  192.                 loginRequest.CookieContainer = m_cookies;
  193.                 loginRequest.ConnectionGroupName = "login";
  194.                 loginRequest.AllowAutoRedirect = true;
  195.                 loginRequest.Method = "POST";
  196.  
  197.                 loginRequest.ContentType = "application/x-www-form-urlencoded;charset=utf-8";
  198.                 loginRequest.ContentLength = loginData.Length;
  199.                 byte[] loginDataBytes = Encoding.UTF8.GetBytes(loginData);
  200.                 int tmp1 = loginDataBytes.Length;
  201.                 int tmp2 = loginData.Length;
  202.                 loginRequest.GetRequestStream().Write(loginDataBytes, 0, loginDataBytes.Length);
  203.                 loginRequest.Timeout = HTTP_REQUEST_TIMEOUT * 1000;
  204.                 loginRequest.KeepAlive = true;
  205.  
  206.                 // Send login request and read response stream.
  207.                 HttpWebResponse response = (HttpWebResponse)loginRequest.GetResponse();
  208.                 if (response.StatusCode != HttpStatusCode.OK)
  209.                 {
  210.                     response.Close();
  211.                     throw new ApplicationException("Login to google.com failed for " + emailAddress + " with response " + response.StatusCode + ".");
  212.                 }
  213.  
  214.                 StreamReader readertmp = new StreamReader(response.GetResponseStream());
  215.                 string responseFromServerlogin = readertmp.ReadToEnd();
  216.                 response.Close();
  217.                
  218.  
  219.                 // We're now logged in. Need to load up the Google Voice page to get the rnr hidden input value which is needed for
  220.                 // the HTTP call requests.
  221.                 HttpWebRequest rnrRequest = (HttpWebRequest)WebRequest.Create(VOICE_HOME_URL);
  222.                 rnrRequest.ConnectionGroupName = "call";
  223.                 rnrRequest.CookieContainer = m_cookies;
  224.  
  225.                 // Send the Google Voice account page request and read response stream.
  226.                 response = (HttpWebResponse)rnrRequest.GetResponse();
  227.                 if (response.StatusCode != HttpStatusCode.OK)
  228.                 {
  229.                     response.Close();
  230.                     throw new ApplicationException("Load of the Google Voice account page failed for " + emailAddress + " with response " + response.StatusCode + ".");
  231.                 }
  232.                 else
  233.                 {
  234.                     Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Google Voice home page loaded successfully.", m_username));
  235.                 }
  236.  
  237.  
  238.  
  239.                 StreamReader reader = new StreamReader(response.GetResponseStream());
  240.                 string responseFromServer = reader.ReadToEnd();
  241.                 response.Close();
  242.  
  243.                 // Extract the rnr field from the HTML.
  244.                 Match rnrMatch = Regex.Match(responseFromServer, @"name=""_rnr_se"".*?value=""(?<rnrvalue>.*?)""");
  245.                 if (rnrMatch.Success)
  246.                 {
  247.                     return rnrMatch.Result("${rnrvalue}");
  248.                 }
  249.                 else
  250.                 {
  251.                     throw new ApplicationException("Could not find _rnr_se key on your Google Voice account page, callback cannot proceed.");
  252.                 }
  253.             }
  254.             catch (Exception excp)
  255.             {
  256.                 logger.Error("Exception GoogleVoiceCall Login. " + excp.Message);
  257.                 throw;
  258.             }
  259.         }
  260.  
  261.         private SIPDialogue SendCallRequest(string forwardingNumber, string destinationNumber, int phoneType, int waitForCallbackTimeout, string contentType, string body)
  262.         {
  263.             try
  264.             {
  265.                 int callbackTimeout = (waitForCallbackTimeout < MIN_CALLBACK_TIMEOUT || waitForCallbackTimeout > MAX_CALLBACK_TIMEOUT) ? WAIT_FOR_CALLBACK_TIMEOUT : waitForCallbackTimeout;
  266.  
  267.                 CallbackWaiter callbackWaiter = new CallbackWaiter(m_username, CallbackWaiterEnum.GoogleVoice, forwardingNumber, MatchIncomingCall);
  268.                 m_callManager.AddWaitingApplication(callbackWaiter);
  269.  
  270.                 string callData = "outgoingNumber=" + Uri.EscapeDataString(destinationNumber) + "&forwardingNumber=" + Uri.EscapeDataString(forwardingNumber) +
  271.                     "&subscriberNumber=undefined&remember=0&_rnr_se=" + Uri.EscapeDataString(m_rnrKey) + "&phoneType=" + phoneType;
  272.                 //logger.Debug("call data=" + callData + ".");
  273.  
  274.                 // Build the call request.
  275.                 HttpWebRequest callRequest = (HttpWebRequest)WebRequest.Create(VOICE_CALL_URL);
  276.                 callRequest.ConnectionGroupName = "call";
  277.                 callRequest.CookieContainer = m_cookies;
  278.                 callRequest.Method = "POST";
  279.                 callRequest.ContentType = "application/x-www-form-urlencoded;charset=utf-8";
  280.                 callRequest.ContentLength = callData.Length;
  281.                 callRequest.GetRequestStream().Write(Encoding.UTF8.GetBytes(callData), 0, callData.Length);
  282.                 callRequest.Timeout = HTTP_REQUEST_TIMEOUT * 1000;
  283.  
  284.                 HttpWebResponse response = (HttpWebResponse)callRequest.GetResponse();
  285.                 HttpStatusCode responseStatus = response.StatusCode;
  286.                 response.Close();
  287.                 if (responseStatus != HttpStatusCode.OK)
  288.                 {
  289.                     throw new ApplicationException("The call request failed with a " + responseStatus + " response.");
  290.                 }
  291.                 else
  292.                 {
  293.                     Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Google Voice Call to " + destinationNumber + " initiated, callback #" + forwardingNumber + ", phone type " + phoneType + ", timeout " + callbackTimeout + "s.", m_username));
  294.                 }
  295.  
  296.                 if (m_waitForCallback.WaitOne(callbackTimeout * 1000))
  297.                 {
  298.                     if (!m_hasBeenCancelled)
  299.                     {
  300.                         Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Google Voice Call callback received.", m_username));
  301.                         return m_callbackCall.Answer(contentType, body, null, SIPDialogueTransferModesEnum.Default);
  302.                     }
  303.                     else
  304.                     {
  305.                         return null;
  306.                     }
  307.                 }
  308.                 else
  309.                 {
  310.                     Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Google Voice Call timed out waiting for callback.", m_username));
  311.                     CancelCall();
  312.                     return null;
  313.                 }
  314.             }
  315.             catch (Exception excp)
  316.             {
  317.                 logger.Error("Exception GoogleVoiceCall SendCallRequest. " + excp.Message);
  318.                 throw;
  319.             }
  320.         }
  321.  
  322.         private void CancelCall()
  323.         {
  324.             try
  325.             {
  326.                 if (!m_hasBeenCancelled)
  327.                 {
  328.                     m_hasBeenCancelled = true;
  329.                     m_waitForCallback.Set();
  330.  
  331.                     string callData = "outgoingNumber=undefined&forwardingNumber=undefined&_rnr_se=" + Uri.EscapeDataString(m_rnrKey);
  332.  
  333.                     // Build the call request.
  334.                     HttpWebRequest cancelRequest = (HttpWebRequest)WebRequest.Create(CANCEL_CALL_URL);
  335.                     cancelRequest.ConnectionGroupName = "cancel";
  336.                     cancelRequest.CookieContainer = m_cookies;
  337.                     cancelRequest.Method = "POST";
  338.                     cancelRequest.ContentType = "application/x-www-form-urlencoded;charset=utf-8";
  339.                     cancelRequest.ContentLength = callData.Length;
  340.                     cancelRequest.GetRequestStream().Write(Encoding.UTF8.GetBytes(callData), 0, callData.Length);
  341.                     cancelRequest.Timeout = HTTP_REQUEST_TIMEOUT * 1000;
  342.  
  343.                     HttpWebResponse response = (HttpWebResponse)cancelRequest.GetResponse();
  344.                     HttpStatusCode responseStatus = response.StatusCode;
  345.                     response.Close();
  346.                     if (responseStatus != HttpStatusCode.OK)
  347.                     {
  348.                        logger.Warn("The GoogleVoiceCall cancel request failed with a " + responseStatus + " response.");
  349.                     }
  350.                     else
  351.                     {
  352.                         Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "Google Voice Call to " + m_destinationNumber + " was successfully cancelled.", m_username));
  353.                     }
  354.                 }
  355.             }
  356.             catch (Exception excp)
  357.             {
  358.                 logger.Error("Exception GoogleVoiceCall CancelCall. " + excp.Message);
  359.                 throw;
  360.             }
  361.         }
  362.  
  363.         private bool MatchIncomingCall(ISIPServerUserAgent incomingCall)
  364.         {
  365.             try
  366.             {
  367.                 if (incomingCall.SIPAccount.Owner != m_username)
  368.                 {
  369.                     return false;
  370.                 }
  371.                 else if (m_clientCallCancelled)
  372.                 {
  373.                     // If the call has been cancelled then don't match to avoid chance of a new incoming call matching a dead Google Voice call.
  374.                     return false;
  375.                 }
  376.  
  377.                 SIPHeader callHeader = incomingCall.CallRequest.Header;
  378.                 bool matchedCall = false;
  379.  
  380.                 if (!m_fromURIUserRegexMatch.IsNullOrBlank())
  381.                 {
  382.                     if (Regex.Match(callHeader.From.FromURI.User, m_fromURIUserRegexMatch).Success)
  383.                     {
  384.                         matchedCall = true;
  385.                     }
  386.                 }
  387.                 else if (callHeader.UnknownHeaders.Contains("X-GoogleVoice: true") && callHeader.To.ToURI.User == m_forwardingNumber.Substring(1))
  388.                 {
  389.                     matchedCall = true;
  390.                 }
  391.  
  392.                 if (matchedCall)
  393.                 {
  394.                     m_callbackCall = incomingCall;
  395.                     m_callbackCall.SetOwner(m_username, m_adminMemberId);
  396.                     m_waitForCallback.Set();
  397.                     return true;
  398.                 }
  399.                 else
  400.                 {
  401.                     return false;
  402.                 }
  403.             }
  404.             catch (Exception excp)
  405.             {
  406.                 logger.Error("Exception GoogleVoiceCall MatchIncomingCall. " + excp.Message);
  407.                 return false;
  408.             }
  409.         }
  410.  
  411.         public void ClientCallTerminated(CallCancelCause cancelCause)
  412.         {
  413.             Log_External(new SIPMonitorConsoleEvent(SIPMonitorServerTypesEnum.AppServer, SIPMonitorEventTypesEnum.DialPlan, "GoogleVoiceCall client call cancelled, " + cancelCause + ".", m_username));
  414.             m_clientCallCancelled = true;
  415.             CancelCall();
  416.         }
  417.     }
  418. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement