/// <summary> /// Takes an OAuth code and turns it into an API token. /// </summary> /// <param name="code">A code returned from the OAuth page (https://quizlet.com/authorize/)</param> /// <param name="success">A delegate to be called when the authentication succeeds.</param> /// <param name="failure">What to do when the authentication fails.</param> /// <param name="token">A CancellationToken, which is currently useless.</param> public void Authenticate(string code, Action success, Action<Exception> failure, CancellationToken token) { var fields = "grant_type=authorization_code&code=" + code + "&redirect_uri=https://q.asztal.net/"; var req = new HttpsRequest("POST", "/oauth/token"); req.BasicAuthorization(ClientID, SecretKey); req.ContentType = "application/x-www-form-urlencoded; charset=UTF-8"; req.PostData = Encoding.UTF8.GetBytes(fields); FetchJSON(req, json => { try { if (json is JsonDictionary) { var ctx = new JsonContext(); var accessToken = ctx.FromJson<string>((json as JsonDictionary).Items["access_token"]); var userName = ctx.FromJson<string>((json as JsonDictionary).Items["user_id"]); var tokenExpiry = DateTime.Now.AddSeconds(ctx.FromJson<double>((json as JsonDictionary).Items["expires_in"])); Credentials = new Credentials(accessToken, userName, tokenExpiry); success(); } else { failure(new FormatException("Quizlet server returned an invalid response.")); } } catch (KeyNotFoundException) { failure(new FormatException("Quizlet server returned an invalid response.")); } catch (JsonConvertException) { failure(new FormatException("Quizlet server returned an invalid response.")); } }, failure, token); }
void AuthorizeRequest(HttpsRequest request, bool needsToken) { if (Credentials != null) { request.Authorization = "Bearer " + Credentials.Token; } else { if (needsToken) throw new QuizletException("A quizlet API endpoint was called which needs authentication"); // TODO: check if GET or POST or PUT etc. char delim = '?'; if (request.Path.Contains("?")) delim = '&'; request.Path = request.Path + delim + "client_id=" + ClientID; } }
void FetchJSON(HttpsRequest request, Action<JsonValue> completion, Action<Exception> errorHandler, CancellationToken token) { var op = System.ComponentModel.AsyncOperationManager.CreateOperation(null); request.UserAgent = "FlashcardsWP7/0.1 (" + Environment.OSVersion + "; .NET " + Environment.Version + ")"; try { token.Register(delegate { throw new OperationCanceledException(); }); ThreadPool.QueueUserWorkItem( _ => { Exception error; try { var client = new HttpsClient(Host.Host, Host.Port, Configuration.VerifyCertificates); var response = client.MakeRequest(request); var text = response.DecodeTextBody(); if (response.StatusCode == 204) { op.PostOperationCompleted(delegate { completion(null); }, null); return; } var json = JsonValue.Parse(new StringReader(text)); if (response.StatusCode / 100 != 2) { var dict = (JsonDictionary) json; var errorCode = new JsonContext().FromJson<string>(dict.Items["error"]); var errorText = new JsonContext().FromJson<string>(dict.Items["error_description"]); if (errorCode != null && errorText != null) { // TODO: find error code for needing password if (errorCode == "invalid_access") throw new AccessDeniedException(errorText); if (errorCode == "item_not_found") throw new ItemNotFoundException(errorText); if (errorCode == "item_deleted") throw new ItemDeletedException(errorText); throw new QuizletException(errorText); } throw new QuizletException("The Quizlet request failed (HTTP error " + response.StatusCode.ToString(CultureInfo.InvariantCulture) + ")."); } op.PostOperationCompleted(delegate { completion(json); }, null); return; } catch (QuizletException e) { error = e; } catch (OperationCanceledException e) { error = e; } catch (HttpException e) { error = e; } catch (System.Net.Sockets.SocketException e) { error = e; } catch (IOException e) { error = e; } catch (ArgumentException e) { error = e; } catch (InvalidOperationException e) { error = e; } catch (NotSupportedException e) { error = e; } catch (FormatException e) { error = new QuizletException("The Quizlet server returned an invalid document.", e); } catch (JsonConvertException e) { error = new QuizletException("The Quizlet server returned an invalid document.", e); } catch (InvalidCastException e) { error = new QuizletException("The Quizlet server returned an invalid document.", e); } catch (KeyNotFoundException e) { error = new QuizletException("The Quizlet server returned an invalid document.", e); } op.PostOperationCompleted(delegate { errorHandler(error); }, null); }); } catch (OperationCanceledException e) { errorHandler(e); } }
public void SetStarred(long setID, bool starred, Action success, Action<Exception> errorHandler, CancellationToken token) { if (Credentials == null) throw new InvalidOperationException("Must be logged in to mark or unmark a group as favourite"); var req = new HttpsRequest(starred ? "PUT" : "DELETE", "/2.0/users/" + Credentials.UserName + "/favorites/" + setID.ToString(CultureInfo.InvariantCulture)); AuthorizeRequest(req, true); FetchJSON( req, _ => success(), // No content is returned (HTTP 204) errorHandler, token); }
public void SubmitPassword(long setID, string password, Action success, Action<Exception> failure, CancellationToken token) { var req = new HttpsRequest("POST", string.Format(CultureInfo.InvariantCulture, "/2.0/sets/{0}/password", setID)); AuthorizeRequest(req, true); var postData = new MemoryStream(); using (var writer = new StreamWriter(postData)) WriteQueryString(writer, "password", password); req.ContentType = "application/x-www-form-urlencoded"; req.PostData = postData.ToArray(); FetchJSON( req, json => success(), failure, token); }
public void FetchUserSets(string userName, Action<List<SetInfo>> completion, Action<Exception> errorHandler, CancellationToken token) { var req = new HttpsRequest("GET", "/2.0/users/" + Uri.EscapeUriString(userName) + "/sets"); AuthorizeRequest(req, true); FetchJSON( req, json => { try { var sets = new JsonContext().FromJson<List<SetInfo>>(json); completion(sets); } catch (JsonConvertException e) { errorHandler(e); } }, errorHandler, token); }
public void SearchSets(string query, bool searchTerms, Action<List<SetInfo>> completion, Action<Exception> errorHandler, CancellationToken token) { var req = new HttpsRequest("GET", "/2.0/search/sets?sort=most_studied&" + (searchTerms ? "term=" : "q=") + Uri.EscapeDataString(query)); AuthorizeRequest(req, false); FetchJSON( req, json => { try { var sets = new JsonContext().FromJson<List<SetInfo>>(((JsonDictionary)json).Items["sets"]); completion(sets); } catch (JsonConvertException e) { errorHandler(e); } }, errorHandler, token); }
public void FetchUserData(string userName, Action<UserInfo> completion, Action<Exception> errorHandler, CancellationToken token) { var req = new HttpsRequest("GET", "/2.0/users/" + Uri.EscapeUriString(userName)); AuthorizeRequest(req, false); FetchJSON( req, json => { try { completion(new JsonContext().FromJson<UserInfo>(json)); } catch (JsonConvertException e) { errorHandler(e); } }, errorHandler, token); }
public void FetchUserGroups(string userName, Action<List<GroupInfo>> completion, Action<Exception> errorHandler, CancellationToken token) { var req = new HttpsRequest("GET", string.Format(CultureInfo.InvariantCulture, "/2.0/users/{0}/groups", Uri.EscapeUriString(userName))); AuthorizeRequest(req, false); FetchJSON( req, json => { try { var groups = new JsonContext { RelaxedNumericConversion = true }.FromJson<List<GroupInfo>>(json); completion(groups); } catch (JsonConvertException e) { errorHandler(e); } }, errorHandler, token); }
public void FetchSet(long id, Action<SetInfo> completion, Action<Exception> errorHandler, CancellationToken token) { var req = new HttpsRequest("GET", "/2.0/sets/" + id.ToString(CultureInfo.InvariantCulture)); AuthorizeRequest(req, false); FetchJSON( req, json => { try { completion(new JsonContext().FromJson<SetInfo>(json)); } catch (JsonConvertException e) { errorHandler(e); } }, errorHandler, token); }
public void FetchSets(IEnumerable<long> ids, Action<List<SetInfo>> completion, Action<Exception> errorHandler, CancellationToken token) { var req = new HttpsRequest("GET", "/2.0/sets?set_ids=" + string.Join(",", from id in ids select id.ToString(CultureInfo.InvariantCulture))); AuthorizeRequest(req, false); FetchJSON( req, json => { try { completion(new JsonContext().FromJson<List<SetInfo>>(json)); } catch (JsonConvertException e) { errorHandler(e); } }, errorHandler, token); }
public void EditSet(SetInfo si, string newPassword, string newVisibility, string newPermissions, IEnumerable<long> groupIDs, Action success, Action<Exception> errorHandler, CancellationToken token) { var req = new HttpsRequest("PUT", string.Format(CultureInfo.InvariantCulture, "/2.0/sets/{0}", si.ID)); var postData = new MemoryStream(); using (var writer = new StreamWriter(postData)) WriteSetInfo(si, writer, newPassword, newVisibility, newPermissions, groupIDs); req.ContentType = "application/x-www-form-urlencoded"; req.PostData = postData.ToArray(); AuthorizeRequest(req, true); FetchJSON( req, json => success(), errorHandler, token); }
public void DeleteSet(long setID, Action success, Action<Exception> errorHandler, CancellationToken token) { var req = new HttpsRequest("DELETE", string.Format(CultureInfo.InvariantCulture, "/2.0/sets/{0}", setID)); AuthorizeRequest(req, true); FetchJSON( req, _ => success(), errorHandler, token); }
public void CreateSet(SetInfo si, string password, IEnumerable<long> groupIDs, Action<long, Uri> completion, Action<Exception> errorHandler, CancellationToken token) { var req = new HttpsRequest("POST", "/2.0/sets"); var postData = new MemoryStream(); using (var writer = new StreamWriter(postData)) WriteSetInfo(si, writer, password, si.Visibility, si.Editable, groupIDs); req.ContentType = "application/x-www-form-urlencoded"; req.PostData = postData.ToArray(); AuthorizeRequest(req, true); FetchJSON( req, json => { try { if (!(json is JsonDictionary)) throw new JsonConvertException("Expected a JSON dictionary"); var ctx = new JsonContext(); var dict = (json as JsonDictionary).Items; var uri = new Uri(ctx.FromJson<string>(dict["url"])); var id = ctx.FromJson<long>(dict["set_id"]); completion(id, uri); } catch (JsonConvertException err) { errorHandler(err); } catch (KeyNotFoundException err) { errorHandler(new QuizletException("Invalid response from server", err)); } }, errorHandler, token); }
public HttpsResponse MakeRequest(HttpsRequest request) { using (var writer = new NoCloseStreamWriter(tlsStream, Encoding.GetEncoding("ISO-8859-1"))) { writer.NewLine = "\r\n"; writer.WriteLine(request.Method + " " + request.Path + " HTTP/1.0"); writer.WriteLine("Host: " + host); if (request.PostData != null) { if (request.ContentType == null) throw new HttpException("HttpsRequest did not specifify a ContentType"); writer.WriteLine("Content-Type: " + request.ContentType); writer.WriteLine("Content-Length: " + request.PostData.Length); } if (request.Authorization != null) writer.WriteLine("Authorization: " + request.Authorization); writer.WriteLine(); if (request.PostData != null) { //writer.WriteLine(); writer.Flush(); tlsStream.Write(request.PostData, 0, request.PostData.Length); } } var response = new HttpsResponse(); var ms = new MemoryStream(); tlsStream.CopyToSafe(ms); ms.Position = 0; var statusLine = ms.ReadLineUTF8(); var parts = statusLine.Split(); if (parts.Length < 2 || !parts[0].StartsWith("HTTP")) throw new HttpException("Invalid HTTP response"); int statusCode; if(!int.TryParse(parts[1], out statusCode)) throw new HttpException("Invalid HTTP response"); response.StatusCode = statusCode; while(true) { var line = ms.ReadLineUTF8(); if (line.Length == 0) break; var match = Regex.Match(line, "^([^:]+):\\s*(.*)$"); if (match.Success) response.AddHeader(match.Groups[1].Value, match.Groups[2].Value); } response.SetBody(ms); tlsStream.Dispose(); return response; }
public HttpsResponse MakeRequest(HttpsRequest request) { using (var writer = new NoCloseStreamWriter(tlsStream, Encoding.GetEncoding("ISO-8859-1"))) { writer.NewLine = "\r\n"; writer.WriteLine(request.Method + " " + request.Path + " HTTP/1.0"); writer.WriteLine("Host: " + host); if (request.PostData != null) { if (request.ContentType == null) { throw new HttpException("HttpsRequest did not specifify a ContentType"); } writer.WriteLine("Content-Type: " + request.ContentType); writer.WriteLine("Content-Length: " + request.PostData.Length); } if (request.Authorization != null) { writer.WriteLine("Authorization: " + request.Authorization); } writer.WriteLine(); if (request.PostData != null) { //writer.WriteLine(); writer.Flush(); tlsStream.Write(request.PostData, 0, request.PostData.Length); } } var response = new HttpsResponse(); var ms = new MemoryStream(); tlsStream.CopyToSafe(ms); ms.Position = 0; var statusLine = ms.ReadLineUTF8(); var parts = statusLine.Split(); if (parts.Length < 2 || !parts[0].StartsWith("HTTP")) { throw new HttpException("Invalid HTTP response"); } int statusCode; if (!int.TryParse(parts[1], out statusCode)) { throw new HttpException("Invalid HTTP response"); } response.StatusCode = statusCode; while (true) { var line = ms.ReadLineUTF8(); if (line.Length == 0) { break; } var match = Regex.Match(line, "^([^:]+):\\s*(.*)$"); if (match.Success) { response.AddHeader(match.Groups[1].Value, match.Groups[2].Value); } } response.SetBody(ms); tlsStream.Dispose(); return(response); }