/// <summary> /// Asynchroniously invoke VK API method and get response as XML. /// </summary> /// <param name="xmlMethodName">API method name, '.xml' added automatically</param> /// <param name="methodCallArguments">Arguments to call method. May be empty.</param> /// <param name="sendToken">Send access_token, or not.</param> /// <returns>Response, if no error.</returns> /// <exception cref="VK9.VK.APIException">Server returned an error.</exception> public async Task<XmlElement> AsyncXmlMethodCall(string xmlMethodName, NameValueCollection methodCallArguments, bool sendToken = true) { // Request rate limit BeginAnotherRequest(); _pendingCaptcha: if (pendingCaptchaKey != null) { if (pendingCaptchaKey.IsSolved) { // Append args with solved captcha key methodCallArguments.Add("captcha_sid", pendingCaptchaKey.ID); methodCallArguments.Add("captcha_key", pendingCaptchaKey.SolveKey); } // HACK! It seems that server does not like token sent with captcha key at one time // But what about method call result without token? sendToken = false; pendingCaptchaKey = null; } // Https? string url = string.Format("{0}://{1}", HostSession.IsHttpsEnabled ? "https" : "http", ServerHost); // Do we need to add access_token to arguments? if (sendToken) methodCallArguments.Add("access_token", HostSession.Token.Value); // Append url with query string var query_s = ToQueryString(methodCallArguments, true); url += string.Format("/method/{0}.xml{1}", xmlMethodName, !string.IsNullOrEmpty(query_s) ? "?" + query_s : string.Empty); // Do we need to sign the request? if (!HostSession.IsHttpsEnabled) { // Build sign_hash argument query_s = ToQueryString(methodCallArguments, false); string sig_url_s = string.Format("/method/{0}.xml{1}{2}", xmlMethodName, !string.IsNullOrEmpty(query_s) ? "?" + query_s : string.Empty, HostSession.Secret); // Generate sign byte[] sig = null; using (var md5 = MD5.Create()) sig = md5.ComputeHash(Encoding.UTF8.GetBytes(sig_url_s)); // Append url with digest url += "&sig="; for (int i = 0; i < sig.Length; i++) url += sig[i].ToString("x2"); } // Do HTTP request string response_xml = await AsyncHttpGetString(url); // Parse XML data var xml = new XmlDocument(); xml.LoadXml(response_xml); // Parse error info, if there is an error if (xml["error"] != null) { // Get error message XmlElement e = xml["error"]["error_msg"]; string msg = e != null ? e.InnerText : "Error description is unspecified."; // Parse error code e = xml["error"]["error_code"]; var code = e != null ? (VK.APIError)uint.Parse(e.InnerText) : VK.APIError.UnspecifiedUnsupported; // Captcha validation needed? if (code == VK.APIError.CaptchaNeeded) { // Remove captcha key from previous validation attempt methodCallArguments.Remove("captcha_sid"); methodCallArguments.Remove("captcha_key"); // Trigger captcha validation var eventData = VKSession.TriggerCaptchaNeeded(HostSession, new Uri(xml["error"]["captcha_img"].InnerText), xml["error"]["captcha_sid"].InnerText); // Send the request again with/out captcha validation if (eventData.Retry || eventData.IsSolved) { pendingCaptchaKey = eventData; goto _pendingCaptcha; } } // Throw error throw new VK.APIException(HostSession, xmlMethodName, msg, code, xml["error"]["request_params"], null); } else { // Done return xml["response"]; } }
/// <summary> /// Allows you to set captcha solution for next API request (used for delayed captcha validation). /// </summary> /// <param name="captchaKey">Captcha key</param> public void SetCaptchaKey(VK.CaptchaEventArgs captchaKey) { if (captchaKey != null && (!captchaKey.IsSolved || !captchaKey.IsValidCaptcha)) throw new ArgumentException("Specified captchaKey is not valid or not solved'."); pendingCaptchaKey = captchaKey; }
/// <summary> /// Triggers CaptchaNeeded event. /// </summary> public static VK.CaptchaEventArgs TriggerCaptchaNeeded(VKSession self, Uri img, string sid) { var eventData = new VK.CaptchaEventArgs(img, sid); if (self.CaptchaNeeded != null) self.CaptchaNeeded(self, eventData); return eventData; }