public void Update()
        {
            try
            {
                List <Contact> OldContacts = new List <Contact>(Contacts);

                HttpResult ret = http.Get("https://www.google.com/voice?ui=desktop");
                var        m   = Regex.Match(ret.Page, @"_gcData.*?\=(.*?)_gvRun", RegexOptions.Singleline);
                if (m.Success)
                {
                    // hack: :(
                    var json = m.Groups[1].Value.Replace('\'', '"');
                    json = Regex.Replace(json, "\"flags\":\\s*{\\s*};", "", RegexOptions.Singleline);

                    var o = JObject.Parse(json);

                    var contacts = o["contacts"];

                    string BasePhotoUrl = "http://www.google.com/s2/b/0"; // (Body["UserData"]["PhotoUrl"] as JValue).Value.ToString();
                    Trace.WriteLine("Base Photo URL: " + BasePhotoUrl);

                    // We'll fail on any bad contact, because we want to FailFast here
                    // that way, the user will not get any contacts, and hopefully report
                    // the bug to us.
                    foreach (var cx in contacts)
                    {
                        var contact = cx.First;

                        Trace.WriteLine(contact);

                        string ID   = (contact["contactId"] as JValue).Value.ToString();
                        string Name = (contact["name"] as JValue).Value.ToString();


                        bool    shouldAdd = false;
                        Contact c         = Contacts.SingleOrDefault(x => x.ID == ID);
                        if (c == null)
                        {
                            c         = new Contact();
                            shouldAdd = true;
                        }
                        else
                        {
                            OldContacts.Remove(c);
                        }

                        c.ID   = ID;
                        c.Name = WebUtil.HtmlDecode(Name);

                        if (c.Name.Contains("Microsoft"))
                        {
                            // Debugger.Break();
                        }

                        c.Phones.Clear(); // kill old phones.
                        foreach (var ph in (JArray)contact["numbers"])
                        {
                            try
                            {
                                if (ph.ToString().Contains("phoneType"))
                                {
                                    c.Phones.Add(new Phone
                                    {
                                        Number = (ph["phoneNumber"] as JValue).Value.ToString(),
                                        Type   = (ph["phoneType"] as JValue).Value.ToString()
                                    });
                                }
                                else
                                {
                                    // NOTE: 5/5/2012
                                    // Contacts with 'Custom' label don't have a phoneType
                                    c.Phones.Add(new Phone
                                    {
                                        Number = (ph["phoneNumber"] as JValue).Value.ToString(),
                                        Type   = "Unknown",
                                    });
                                }
                            }
                            catch (NullReferenceException)
                            {
                                Debugger.Break();
                                // no phoneType = GV number we should ignore
                            }
                        }

                        if (LoadImages)
                        {
                            try
                            {
                                string photoUrl = (contact["photoUrl"] as JValue).Value.ToString();
                                var    r_ImgID  = Regex.Match(photoUrl, ".*/(.*?)$", RegexOptions.Singleline);
                                string ImgID    = "";
                                if (!r_ImgID.Success)
                                {
                                    ImgID = Util.SafeFileName(photoUrl);
                                }
                                else
                                {
                                    ImgID = Util.SafeFileName(r_ImgID.Groups[1].Value);
                                }
                                if (!string.IsNullOrEmpty(ImgID))
                                {
                                    string save = ImageDir + ImgID + ".jpg";
                                    try
                                    {
                                        if (!File.Exists(save))
                                        {
                                            var imageBytes = http.GetFile(BasePhotoUrl + photoUrl);
                                            Trace.WriteLine("Saving: " + save);
                                            File.WriteAllBytes(save, imageBytes);
                                        }
                                    }
                                    catch (Exception ex)
                                    {
                                        // if we fail to save, we don't want to attempt it every time the contacts sync
                                        // so we'll just save anyway.
                                        // TODO consider failed=true property on contact download
                                        Trace.WriteLine("GoogleVoice/ContactsManager/Update/Photo Save Error: " + ex.Message);
                                    }
                                    c.ImageLocation = save;
                                    c.ImageETag     = photoUrl;
                                }
                            }
                            catch (Exception ex)
                            {
                                Trace.WriteLine("Photo: " + ex.Message);
                            }
                        }

                        if (shouldAdd)
                        {
                            Contacts.Add(c);
                        }
                    }

                    foreach (Contact deletedContact in OldContacts)
                    {
                        Trace.WriteLine("Removing orphaned contact: " + deletedContact);
                        Contacts.Remove(deletedContact);
                    }

                    Contacts.Sort(new ContactComparer());

                    if (OnContactsUpdated != null)
                    {
                        OnContactsUpdated();
                    }
                }
            }
            catch (Exception ex)
            {
                Trace.WriteLine("Error updating contacts: " + ex);
            }
        }
Exemple #2
0
        public void Login()
        {
            // fiddler2
            // GlobalProxySelection.Select = new WebProxy("127.0.0.1", 8888);
            HttpResult ret = null;

            Trace.WriteLine("GoogleVoice/Account/Login UserName: "******"Connecting...");
                ret = http.Get("https://www.google.com/voice/m");
                if (ret.Uri.LocalPath == "/ServiceLogin")
                {
                    string Token_GALX = http.GetCookieString("GALX", "https://accounts.google.com/");
                    if (string.IsNullOrEmpty(Token_GALX))
                    {
                        // I'm pretty confident that we can't login without a GALX.
                        throw new GVLoginException("GALX missing");
                    }

                    OnLoginMessage_Internal("Logging in...");
                    ret = http.Post("https://accounts.google.com/ServiceLoginAuth?service=grandcentral",
                                    new Dictionary <string, string> {
                        { "ltmpl", "mobile" },      // Probably don't need one of these
                        { "btmpl", "mobile" },
                        { "followup", "https://www.google.com/voice/m?initialauth" },
                        { "continue", "https://www.google.com/voice/m?initialauth" },
                        { "service", "grandcentral" },
                        { "bgresponse", "js_disabled" },
                        { "PersistentCookie", "yes" },
                        { "GALX", Token_GALX },
                        { "Email", UserName },
                        { "Passwd", Password },
                    });
                    if (ret.Uri.ToString() == "https://www.google.com/voice/m")
                    {
                        // we're logged in!
                        Token_GVX = http.GetCookieString("gvx", "https://www.google.com/voice/m");
                        if (string.IsNullOrEmpty(Token_GVX))
                        {
                            throw new GVLoginException("GVX is missing after primary redirect");
                        }
                    }
                    else if (ret.Uri.ToString().StartsWith("https://accounts.google.com/SecondFactor"))
                    {
                        // 2-step verification - SMS verification required.
                        Match sms = Regex.Match(ret.Page, "name=\"secTok\".*?value='(.*?)'", RegexOptions.Singleline);
                        if (sms.Success)
                        {
                            string Timestamp = "";
                            Match  timeStmp  = Regex.Match(ret.Page, "name=\"timeStmp\".*?value='(.*?)'", RegexOptions.Singleline);
                            if (timeStmp.Success)
                            {
                                Timestamp = timeStmp.Groups[1].Value;
                            }

                            string secTok           = sms.Groups[1].Value;
                            string Token_UserSMSPin = GetSMSPinFromUser();

                            Trace.WriteLine("secTok: " + secTok);
                            Trace.WriteLine("User-Provided PIN: " + Token_UserSMSPin);

                            ret = http.Post("https://accounts.google.com/SecondFactor",
                                            new Dictionary <string, string> {
                                { "continue", "https://www.google.com/voice/m?initialauth" },
                                { "service", "grandcentral" },
                                { "smsToken", "" },
                                { "secTok", secTok },
                                { "timeStmp", Timestamp },
                                { "smsUserPin", Token_UserSMSPin },
                                { "PersistentCookie", "true" },
                                { "smsVerifyPin", "Verify" },
                                { "PersistentOptionSelection", "1" },
                            });

                            Cookie Token_SMSV = http.GetCookie("SMSV", "https://accounts.google.com/");
                            SMSVCookieUpdated_Internal(new GVCookie(Token_SMSV));

                            if (ret.Uri.ToString() == "https://www.google.com/voice/m")
                            {
                                // Logged in, find the cookie.
                                Token_GVX = http.GetCookieString("gvx", "https://www.google.com/voice/m");
                                if (string.IsNullOrEmpty(Token_GVX))
                                {
                                    throw new GVLoginException("GVX is missing after validation response");
                                }
                            }
                            else
                            {
                                Trace.WriteLine("Didn't get expected redirect: " + ret.Page);
                                throw new GVLoginException("Didn't get expected redirect");
                            }
                        }
                        else
                        {
                            Trace.WriteLine("Can't find smsToken in page: " + ret.Page);
                            throw new GVLoginException("Can't find smsToken in page");
                        }
                    }
                    else
                    {
                        // CheckCookie ... this is a redirect.
                        // TEST: Canadian Proxy: 199.185.95.42 8080
                        Match m = Regex.Match(ret.Page, "href=\"(.*?)\"");
                        if (m.Success)
                        {
                            // NOTE:  this page has URLs that must be HtmlDecoded!
                            OnLoginMessage_Internal("Redirecting...");
                            ret       = http.Get(WebUtil.HtmlDecode(m.Groups[1].Value));
                            Token_GVX = http.GetCookieString("gvx", "https://www.google.com/voice/m");
                            if (string.IsNullOrEmpty(Token_GVX))
                            {
                                throw new GVLoginException("GVX is missing after redirect", ((ret != null) ? ret.Uri : null));
                            }
                        }
                        else
                        {
                            Trace.WriteLine("Couldn't find continue: " + ret.Page);
                            throw new GVLoginException("Couldn't find continue", ((ret != null) ? ret.Uri : null));
                        }
                    }
                }
                else
                {
                    Trace.WriteLine("Couldn't find ServiceLogin: "******"ServiceLogin not found", ((ret != null) ? ret.Uri : null));
                }

                try
                {
                    Match m = Regex.Match(ret.Page, @"appVersion: (.\d*)", RegexOptions.Singleline);
                    if (m.Success)
                    {
                        AppVersion = int.Parse(m.Groups[1].Value);
                    }
                }
                catch (FormatException ex)
                {
                    Trace.WriteLine("Can't get AppVersion: " + ex);
                }

                // We're going to fetch the lite mobile page, and pull out an RNR_SE, so we can make calls
                // the HTML5 calling interface is unsuitable for non-phone devices
                ret = http.Get("https://www.google.com/voice/m/");
                Match rnr = Regex.Match(ret.Page, "name=\"_rnr_se\" value=\"(.*?)\"");
                if (rnr.Success)
                {
                    // NOTE: we won't encode here, because Post'ing will encode it.
                    // but this value MUST be encoded before going out!
                    Token_RNR_SE_MustEncode = rnr.Groups[1].Value;
                }
            }
            catch (WebException ex)
            {
                Trace.WriteLine("Login Failed: " + ex);
                // We want to turn HTTP 401 and 403 into a login exception, otherwise the UI will
                // assume we don't have a network connection.
                if ((ex.Response as HttpWebResponse).StatusCode == HttpStatusCode.Unauthorized ||
                    (ex.Response as HttpWebResponse).StatusCode == HttpStatusCode.Forbidden)
                {
                    throw new GVLoginException("HTTP 401 or 403 - not authorized");
                }
                else
                {
                    throw;
                }
            }
            catch (GVLoginException ex)
            {
                Trace.WriteLine("Cannot login: "******"RNR_SE: " + Token_RNR_SE_MustEncode);
            Trace.WriteLine("GVX: " + Token_GVX);
            Trace.WriteLine("GV App Version: " + AppVersion);

            new Thread(() => LoadPhones()).Start();

            OnLoginMessage_Internal("Loading Contacts...");

            AddFeeds();
        }