public static async Task OnRequest(Microsoft.AspNetCore.Http.HttpContext e, E_RPWS_User user, WebPebbleProject proj) { //Just return the PBW that was asked for. string id = e.Request.Path.ToString().Split('/')[4]; var build = proj.builds.Find(x => x.id == id); if (build == null) { //Not found. await Program.QuickWriteToDoc(e, "Not Found", "text/plain", 404); } else { //Check if this build was okay. if (build.passed) { //Safe to load file. byte[] data = File.ReadAllBytes(Program.config.user_project_build_dir + proj.projectId + "/" + build.id + "/build.pbw"); await Program.QuickWriteBytesToDoc(e, data, "application/octet-stream", 200); } else { //There won't be a pbw. complain. await Program.QuickWriteToDoc(e, "This build failed and no PBW file was created.", "text/plain", 404); } } }
public static async Task OnRequest(Microsoft.AspNetCore.Http.HttpContext e, E_RPWS_User user, WebPebbleProject proj) { //Check if we're listing all, or one build. string[] split = e.Request.Path.ToString().Split('/'); bool showList = split.Length >= 5; if (showList) { showList = split[4].Length == 0; } if (showList) { //Print the build history. Remove the log data because it is expensive to bandwidth, and convert the time to a readable format. if (proj.builds == null) { proj.builds = new List <WebPebbleProjectBuild>(); proj.SaveProject(); } OutputBuild[] builds = new OutputBuild[proj.builds.Count]; //Go through each. for (int i = 0; i < proj.builds.Count; i++) { var b = proj.builds[i]; DateTime time = new DateTime(b.time); OutputBuild ob = new OutputBuild { id = b.id, passed = b.passed }; ob.api_log = "build_history/" + ob.id + "/"; ob.api_pbw = "/project/" + proj.projectId + "/pbw_media/" + ob.id + "/" + proj.projectId + "_build_" + b.id + ".pbw"; ob.time = time.ToShortDateString() + " at " + time.ToLongTimeString(); builds[i] = ob; } Array.Reverse(builds); await Program.QuickWriteJsonToDoc(e, builds); } else { //Show just one item. Check if it exists. string id = split[4]; var build = proj.builds.Find(x => x.id == id); if (build == null) { //Not found. await Program.QuickWriteToDoc(e, "Not Found", "text/plain", 404); } else { //Write this one file. await Program.QuickWriteJsonToDoc(e, build); } } }
public static async Task DoCompile(Microsoft.AspNetCore.Http.HttpContext e, E_RPWS_User user, WebPebbleProject proj) { //Compile this Pebble project. PebbleProject pp = new PebbleProject(proj.projectId); //Compile bool ok = pp.DoBuild(out string log); //Generate an ID. if (proj.builds == null) { proj.builds = new List <WebPebbleProjectBuild>(); } string id = LibRpws.LibRpwsCore.GenerateRandomHexString(8); while (proj.builds.Where(x => x.id == id).ToArray().Length != 0) { id = LibRpws.LibRpwsCore.GenerateRandomHexString(8); } //Create the object. WebPebbleProjectBuild b = new WebPebbleProjectBuild(); b.id = id; b.log = log; b.passed = ok; b.time = DateTime.UtcNow.Ticks; //Add this and save proj.builds.Add(b); proj.SaveProject(); //If this build passed, move the pbw into a project folder. Directory.CreateDirectory(Program.config.user_project_build_dir + proj.projectId + "/" + id + "/"); if (ok) { File.Move(pp.pathnane + "build/" + proj.projectId + ".pbw", Program.config.user_project_build_dir + proj.projectId + "/" + id + "/" + "build.pbw"); } //Clean up. try { Directory.Delete(pp.pathnane + "build/", true); } catch { //Huh. Weird. } //Create a reply. await Program.QuickWriteJsonToDoc(e, b); }
public static async Task OnRequest(Microsoft.AspNetCore.Http.HttpContext e, E_RPWS_User user, WebPebbleProject nonProject) { string devName = user.appDevName; if (!user.isAppDev) { devName = user.name.Split(' ')[0]; } string name = e.Request.Query["title"]; if (name.Length > 24 || name.Length < 4) { await Program.QuickWriteJsonToDoc(e, new Reply { data = $"Make sure your project name is between 4 - 24 characters. (It is {name.Length})", ok = false }); return; } if (Program.database.GetCollection <WebPebbleProject>("projects").FindOne(x => x.authorId == user.uuid && x.name == name) != null) { await Program.QuickWriteJsonToDoc(e, new Reply { data = $"You already own a project with that name!", ok = false }); return; } WebPebbleProject project = WebPebbleProject.CreateProject(name, devName, user.uuid, e.Request.Query["watchface"] == "true", "3"); //Return the new location await Program.QuickWriteJsonToDoc(e, new Reply { data = $"/project/{project.projectId}/manage/", ok = true }); }
public static async Task ListFiles(Microsoft.AspNetCore.Http.HttpContext e, E_RPWS_User user, WebPebbleProject proj) { await Program.QuickWriteJsonToDoc(e, proj.media.Values); }
public static async Task OnHttpRequest(Microsoft.AspNetCore.Http.HttpContext e) { /* Main code happens here! */ e.Response.Headers.Add("Access-Control-Allow-Origin", "https://webpebble.get-rpws.com"); e.Response.Headers.Add("Server", "WebPebble API - Version 12/7/18b"); if (e.Request.Headers.ContainsKey("origin")) { if (e.Request.Headers["origin"] == "https://webpebble.get-rpws.com") { //Allow creds e.Response.Headers.Add("Access-Control-Allow-Credentials", "true"); } } //If this is a websocket, switch to that. if (e.WebSockets.IsWebSocketRequest) { //Determine the service this is accessing. string pathname = e.Request.Path.ToString().ToLower(); switch (pathname) { case "/device": WebSocket deviceWs = await e.WebSockets.AcceptWebSocketAsync(); CloudPebbleDevice device = new CloudPebbleDevice(); await device.StartSession(e, deviceWs); return; case "/webpebble": WebSocket webpebbleWs = await e.WebSockets.AcceptWebSocketAsync(); WebPebbleClient wc = new WebPebbleClient(); await wc.StartSession(e, webpebbleWs); return; default: //Unknown. Abort. break; } } //Manage CORS by responding with the preflight header request. if (e.Request.Method.ToLower() == "options") { e.Response.Headers.Add("Access-Control-Allow-Headers", "*"); e.Response.Headers.Add("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE"); await QuickWriteToDoc(e, "Preflight OK"); return; } //Try to find a service to handle the request. Ew. //This is ugly and was written wayyyyy too quickly to be such a core part of the program. HttpService service = new HttpService(); bool serviceFound = false; foreach (HttpService serv in services) { //Check if the pathname requested begins with this. bool projectRequest = e.Request.Path.ToString().Contains("/project"); string requestPath = e.Request.Path.ToString().TrimEnd('/').Replace("/project", ""); //If this is a project request, we can remove the ID of the project. if (projectRequest && requestPath.Split('/').Length >= 2) { requestPath = requestPath.Substring(1 + requestPath.TrimStart('/').IndexOf('/')); } string servicePath = serv.pathname.TrimEnd('/'); if (requestPath.StartsWith(servicePath)) { //The request path starts with the service. If wildcard is true, this is okay. //Check if this path is longer than the currently selected service. bool isOlderLonger = false; if (serviceFound) { isOlderLonger = service.pathname.TrimEnd('/').Length > servicePath.Length; } //If we're longer than the new one, use us. if (!isOlderLonger) { //Check if this requires a project. if (projectRequest == serv.inProject) { //This service is okay to use. service = serv; serviceFound = true; } } } } //If we did find a service, use it. if (serviceFound) { //Try to authenticate. E_RPWS_User user = null; if (e.Request.Cookies.ContainsKey("access-token")) { user = Oauth.RpwsAuth.AuthenticateUser(e.Request.Cookies["access-token"]); } if (e.Request.Headers.ContainsKey("Authorization") && user == null) { user = Oauth.RpwsAuth.AuthenticateUser(e.Request.Headers["Authorization"]); } if (e.Request.Query.ContainsKey("one_time_token") && user == null) { string token = e.Request.Query["one_time_token"]; //Find if (Services.Me.OneTimeRequestTokens.oneTimeRequestTokens.ContainsKey(token)) { string userToken = Services.Me.OneTimeRequestTokens.oneTimeRequestTokens[token]; //Remove Services.Me.OneTimeRequestTokens.oneTimeRequestTokens.Remove(token); //Authenticate user = Oauth.RpwsAuth.AuthenticateUser(userToken); } } //If the user was authorized, get the WebPebble data. if (user != null) { //Get the collection for user data var userDataCollection = database.GetCollection <WebPebbleUserData>("users"); //Try to find this user WebPebbleUserData data = userDataCollection.FindOne(x => x.rpwsId == user.uuid); if (data == null) { //We'll need to create data. data = new WebPebbleUserData() { rpwsId = user.uuid, theme = "dark_visualstudio.css" }; data._id = userDataCollection.Insert(data); } user.webpebble_data = data; } //Check if you must be logged in to do this. if (service.requiresAuth && user == null) { await WriteErrorText(e, "You are not logged in.", ErrorHttpCode.NotLoggedIn, 400); return; } //Get the project if there is one. WebPebbleProject proj = null; if (e.Request.Path.ToString().Contains("/project/")) { //Get the project ID. string id = e.Request.Path.ToString().Substring(e.Request.Path.ToString().IndexOf("/project/") + "/project/".Length); if (id.Contains("/")) { id = id.Substring(0, id.IndexOf('/')); //Now, use this ID to load it from the database. var collect = database.GetCollection <WebPebbleProject>("projects"); var projects = collect.Find(x => x.projectId == id && x.authorId == user.uuid); if (projects.Count() == 1) { proj = projects.ToArray()[0]; } } else { //Malformed URL path. await QuickWriteToDoc(e, "Malformed URL path."); } } //Run the code. try { if (service.inProject == (proj == null)) { //Requires a project, but none was found. await WriteErrorText(e, "You don't have access to this project, or it didn't exist.", ErrorHttpCode.BadOwnership, 400); return; } await service.code(e, user, proj); } catch (Exception ex) { //Error. Console.WriteLine($"Error! {ex.Message} @ {ex.StackTrace}"); await WriteErrorText(e, "error " + ex.Message + " @ " + ex.StackTrace, ErrorHttpCode.Unknown, 500); return; } } else { await WriteErrorText(e, "Service not found.", ErrorHttpCode.ServiceNotFound, 404); return; } return; }
public static async Task ZipProjectDownload(Microsoft.AspNetCore.Http.HttpContext e, E_RPWS_User user, WebPebbleProject proj) { //Zip all of the project content and send it to the client encoded in base 64. using (MemoryStream ms = new MemoryStream()) { using (ZipArchive zip = new ZipArchive(ms, ZipArchiveMode.Create, true)) { //Loop through every file in the data. string root = proj.GetAbsolutePathname(); ZipEntireDirectory(zip, root, root.Length); } //Copy this stream to create base64 data. byte[] buf = new byte[ms.Length]; ms.Position = 0; ms.Read(buf, 0, buf.Length); await Program.QuickWriteToDoc(e, Convert.ToBase64String(buf), "text/plain", 200); } }
public static async Task OnMediaRequest(Microsoft.AspNetCore.Http.HttpContext e, E_RPWS_User user, WebPebbleProject proj) { //Get the request method RequestHttpMethod method = Program.FindRequestMethod(e); //Get the params string[] urlParams = Program.GetUrlPathRequestFromInsideProject(e); string id = urlParams[0]; //If the ID is create, pass this request to the creation. if (id == "create") { await OnMediaCreateRequest(e, user, proj); return; } //Try to find the asset. It's ok if it doesn't exist. WebPebbleProjectAsset media = null; if (proj.media.ContainsKey(id)) { media = proj.media[id]; } //Decide what to do. //If it doesn't exist. if (media == null) { //Requesting a non-existing asset. await ThrowError(e, "This asset ID does not exist.", 1); return; } //Handle object editing (POST). if (method == RequestHttpMethod.post) { //Get the payload. MediaRequestPostPayload payload = Program.GetPostBodyJson <MediaRequestPostPayload>(e); //If a value isn't null, apply it. if (payload.name != null) { media.nickname = payload.name; } if (payload.type != null) { //Relocate RelocateAsset(ref media, (ref WebPebbleProjectAsset m) => { m.type = (AssetType)payload.type; }, proj); } if (payload.sub_type != null) { //Relocate RelocateAsset(ref media, (ref WebPebbleProjectAsset m) => { m.innerType = (InnerAssetType)payload.sub_type; }, proj); } if (payload.filename != null) { //Relocate RelocateAsset(ref media, (ref WebPebbleProjectAsset m) => { m.filename = WebPebbleProject.CreateSafeFilename(payload.filename); }, proj); } if (payload.appinfoData != null) { //Modify the appinfo data and set it on the PebbleProject. SaveOutsideAppinfoJsonOnAsset(media, payload.appinfoData, proj); } //Save proj.media[id] = media; proj.SaveProject(); await Program.QuickWriteJsonToDoc(e, media); return; } //Handle object uploading. if (method == RequestHttpMethod.put) { //Check the upload type in the query. FileUploadType uploadType = Enum.Parse <FileUploadType>(e.Request.Query["upload_method"]); Stream source; int length; if (uploadType == FileUploadType.Binary) { //Read body directly length = (int)e.Request.ContentLength; source = e.Request.Body; } else { //This is sent from the uploader in the interface. //Get the file uploaded. var f = e.Request.Form.Files["data"]; //Check if the file is valid. if (f == null) { //No file uploaded. await Program.QuickWriteJsonToDoc(e, new PutRequestReply { ok = false, size = -1, uploader_error = $"No file was uploaded." }); return; } //Set source = f.OpenReadStream(); length = (int)f.Length; } //Check if a file is too large. if (length > MAXIMUM_UPLOADED_SIZE) { //Yup. Stop. source.Close(); await Program.QuickWriteJsonToDoc(e, new PutRequestReply { ok = false, size = length, uploader_error = $"Your file is too powerful! The maximum filesize is {MAXIMUM_UPLOADED_SIZE_NAME} ({MAXIMUM_UPLOADED_SIZE.ToString()} bytes)." }); } //Remove an existing file if it exists. if (File.Exists(media.GetAbsolutePath(proj.projectId))) { File.Delete(media.GetAbsolutePath(proj.projectId)); } //Save using (FileStream fs = new FileStream(media.GetAbsolutePath(proj.projectId), FileMode.CreateNew)) await source.CopyToAsync(fs); //Tell the user it is ok await Program.QuickWriteJsonToDoc(e, new PutRequestReply { ok = true, size = length, uploader_error = null }); return; } //Handle object downloading if (method == RequestHttpMethod.get) { //Check if the file has been created yet string path = media.GetAbsolutePath(proj.projectId); if (!File.Exists(path)) { await ThrowError(e, "This asset exists, but has not been uploaded yet.", 4); return; } //Get the MIME type from the query. if (!e.Request.Query.ContainsKey("mime")) { //Assume we are querying the information. await Program.QuickWriteJsonToDoc(e, media); return; } //Set content type. e.Response.ContentType = e.Request.Query["mime"]; //Set no-cache headers e.Response.Headers.Add("Cache-Control", "no-cache, no-store, must-revalidate"); //Just load the data and copy it to the output stream. using (FileStream fs = new FileStream(path, FileMode.Open)) { e.Response.ContentLength = fs.Length; e.Response.StatusCode = 200; await fs.CopyToAsync(e.Response.Body); } return; } //Handle object deleting if (method == RequestHttpMethod.delete) { //Delete the file if it exists. string path = media.GetAbsolutePath(proj.projectId); if (File.Exists(path)) { File.Delete(path); } //Delete in appinfo.json. DeleteOutsideAppinfoJsonOnAsset(media.id, proj); //Delete proj.media.Remove(media.id); proj.SaveProject(); //Tell the user it is ok await WriteOkReply(e); return; } //Unknown. await ThrowError(e, $"Invalid method for requesting media '{media.id}'.", 5); }
/* Api */ private void DoAuth(WebPebbleRequest req) { //Get the token that was offered. We get this from the HTTP request. string token = http.Request.Cookies["access-token"]; //Check this against the RPWS server. E_RPWS_User user = Oauth.RpwsAuth.AuthenticateUser(token); if (user == null) { //Respond with failure. QuickReply(req.requestid, WebPebbleRequestType.Auth, new Dictionary <string, object>() { { "ok", "false" } }); } else { user_uuid = user.uuid; authenticated = true; //Respond with ok. QuickReply(req.requestid, WebPebbleRequestType.Auth, new Dictionary <string, object>() { { "ok", "true" } }); //Add myself to the list of clients. if (WebPebble.WebSockets.WebSocketServer.connectedClients.ContainsKey(user_uuid)) { pair = WebPebble.WebSockets.WebSocketServer.connectedClients[user_uuid]; //If the phone is connected, tell it we have connected. try { pair.phone.SetStatus(true); pair.connected = true; } catch { //Remain in "disconnected" state. pair.connected = false; } //If another WebPebble session is connected, boot them. try { pair.web.QuickReply(-1, WebPebbleRequestType.CloseOldClient, new Dictionary <string, object>() { }); } catch { } //Replace old WebPebble user, if any. WebPebble.WebSockets.WebSocketServer.connectedClients[user_uuid].web = this; //Set our connection status. SetStatus(pair.connected); } else { //Add ourself. WebSocketPair new_pair = new WebSocketPair { web = this }; WebPebble.WebSockets.WebSocketServer.connectedClients.Add(user_uuid, new_pair); this.pair = WebPebble.WebSockets.WebSocketServer.connectedClients[user_uuid]; } } }
public static async Task OnMediaCreateRequest(Microsoft.AspNetCore.Http.HttpContext e, E_RPWS_User user, WebPebbleProject proj) { //Validate that the request method is indeed POST. RequestHttpMethod method = Program.FindRequestMethod(e); if (method != RequestHttpMethod.post) { await ThrowError(e, "Unknown method.", 2); return; } //Read the JSON data from the stream. MediaCreateRequestBody request = Program.GetPostBodyJson <MediaCreateRequestBody>(e); //Validate if (request.name == null || request.sub_type == null || request.type == null) { await ThrowError(e, "Missing one or more required values in the JSON payload.", 3); return; } //Generate a unique ID string id = LibRpws.LibRpwsCore.GenerateRandomString(16); if (proj.media == null) { proj.media = new Dictionary <string, WebPebbleProjectAsset>(); } while (proj.media.ContainsKey(id)) { id = LibRpws.LibRpwsCore.GenerateRandomString(16); } //Create the object to save to disk. var media = new WebPebbleProjectAsset(); //Find types from URL. media.type = request.type; media.innerType = request.sub_type; media.nickname = request.name; media.id = id; if (request.filename == null) { request.filename = media.id; } //Ensure directory is created. media.filename = ""; Directory.CreateDirectory(media.GetAbsolutePath(proj.projectId)); //Append filename media.filename = WebPebbleProject.CreateSafeFilename(request.filename); //If this was a template, load it. if (request.template != null) { //Try to find the template ID. if (templateMap.ContainsKey(request.template)) { //Write this template to the location. File.Copy(Program.config.media_dir + "Templates/" + templateMap[request.template], media.GetAbsolutePath(proj.projectId)); } else { //Invalid template! await ThrowError(e, "Invalid template name.", 6); return; } } //If this requested that we use appinfo.json, apply it. if (request.appInfoJson != null) { SaveOutsideAppinfoJsonOnAsset(media, request.appInfoJson, proj); } //Save proj.media.Add(id, media); proj.SaveProject(); //Write to user await Program.QuickWriteJsonToDoc(e, media); }
public static async Task OnProjSettingsRequest(Microsoft.AspNetCore.Http.HttpContext e, E_RPWS_User user, WebPebbleProject proj) { //Hide bandwidth-intensive bits. proj.builds = null; //Serve the project's data. await Program.QuickWriteJsonToDoc(e, proj); }
public static async Task CheckIfIdentifierExists(Microsoft.AspNetCore.Http.HttpContext e, E_RPWS_User user, WebPebbleProject proj) { //Get the project file and check if the identifier exists. PebbleProject pp = new PebbleProject(proj.projectId); bool exists = pp.package.pebble.resources.media.Find(x => x.name == e.Request.Query["resrc_id"]) != null; CheckIfIdentifierExistsReply reply = new CheckIfIdentifierExistsReply { exists = exists, request_id = e.Request.Query["request_id"] }; await Program.QuickWriteJsonToDoc(e, reply); }
public static async Task AppInfoJson(Microsoft.AspNetCore.Http.HttpContext e, E_RPWS_User user, WebPebbleProject proj) { //Edit or serve appinfo.json. PebbleProject pp = new PebbleProject(proj.projectId); if (e.Request.Method.ToLower() == "get") { await Program.QuickWriteJsonToDoc(e, pp.package); } if (e.Request.Method.ToLower() == "put") { await Program.QuickWriteToDoc(e, "The put endpoint has been disabled due to a security hole.", "text/plain", 404); } }
public static async Task DeleteProject(Microsoft.AspNetCore.Http.HttpContext e, E_RPWS_User user, WebPebbleProject proj) { //Confirm the challenge if (e.Request.Query["c"] != proj.projectId) { return; } //Delete the folder. Directory.Delete(proj.GetAbsolutePathname(), true); //Remove records Program.database.GetCollection <WebPebbleProject>("projects").Delete(proj._id); //Respond await Program.QuickWriteJsonToDoc(e, new Dictionary <string, bool> { { "ok", true } }); }
public static void Serve(Microsoft.AspNetCore.Http.HttpContext e, E_RPWS_User user, WebPebbleProject proj) { e.Response.Headers.Add("Set-Cookie", "access-token=" + e.Request.Query["token"] + "; Path=/"); Program.QuickWriteToDoc(e, "ok"); }
public static void HandleRequest(Microsoft.AspNetCore.Http.HttpContext e, E_RPWS_User user, WebPebbleProject project) { string d = TemplateManager.GetTemplate("Services/Projects/Manage/main_template.html", new string[] { }, new string[] { }); Program.QuickWriteToDoc(e, d); }
/// <summary> /// Generates a one-time request token for assets. This will grant access to any service one time and is used mostly for requesting assets. It can only be sent via query. /// </summary> /// <param name="e"></param> /// <param name="user"></param> /// <param name="proj"></param> /// <returns></returns> public static async Task CreateOneTimeRequestToken(Microsoft.AspNetCore.Http.HttpContext e, E_RPWS_User user, WebPebbleProject proj) { //Generate a token string token = LibRpws.LibRpwsCore.GenerateRandomString(32); while (oneTimeRequestTokens.ContainsKey(token)) { token = LibRpws.LibRpwsCore.GenerateRandomString(32); } //Insert oneTimeRequestTokens.Add(token, user.token); //Respond await Program.QuickWriteJsonToDoc(e, new Dictionary <string, string> { { "token", token } }); }