public void Run() { #if !DEBUG try { #endif // To store headers and styles List<string> AdditionalStyles = new List<string>(); // Set HTTP response version to 1.1 (experimental) // Context.Response.ProtocolVersion = new Version("1.1"); BrowserSender browserSender = new BrowserSender(Context); currentClientIP = Request.RemoteEndPoint.Address.ToString(); qsParams = Request.QueryString; if (Settings.Default.DebugServer) spoolMessage("Client Connected (" + Request.RemoteEndPoint.Address.ToString() + ")"); if ((Settings.Default.DebugAdvanced) && (Settings.Default.DebugServer)) { spoolMessage("Headers from client:"); for (int i = 0; i < Request.Headers.Count; ++i) spoolMessage(string.Format("{0}: {1}", Request.Headers.Keys[i], Request.Headers[i])); } // Split request into lines string txtPostObjects = ""; if (Request.HttpMethod.Equals("POST")) { StreamReader sr = new StreamReader(Request.InputStream); txtPostObjects = sr.ReadToEnd(); } // User agent - detect mobile processUserAgentStringFromRequestHeaders(); // Get action string from Url string txtAction = GetActionFromBrowserRequest(); if (Settings.Default.DebugServer) { Functions.WriteLineToLogFile("From Client: " + txtAction); } // Build response txtResponse = ""; txtPageTitle = Settings.Default.MainMenuTitle; bool foo; // R.I.P. Open server (keep this for legacy compatibility) if (txtAction.StartsWith("open")) txtAction = txtAction.Substring(5); // Special cases / conversions if (txtAction.ToLowerInvariant().Equals("apple-touch-icon.png")) txtAction = "static/images/apple-touch-icon.png"; // Querystring authentication is one possible method that overrides all others if true: check for token (and renew) if (txtAction.StartsWith("xml/checktoken")) // Special open method - check a token { bool ignore = CheckForTokenAuthentication(); string checkForTokenResult = AuthenticatedByToken ? "GOOD" : "BAD"; string xCheckResponse = "<?xml version=\"1.0\"?><checktokenresponse result=\"" + checkForTokenResult + "\" />"; browserSender.SendXMLToBrowser(xCheckResponse); return; } else if (!CheckForTokenAuthentication()) { // invalid token browserSender.SendGenericStatusCodePage("403", "Incorrect authentication"); spoolMessage("API: failed authentication via token."); return; } // XML METHODS - no HTTP authentication required (uses token-based auth) if (txtAction.StartsWith("xml")) { XMLresponse = ""; WebServiceProcess(txtAction, ref browserSender, ref txtPostObjects); return; } // Any other non-authenticated methods switch (txtAction) { // SPECIAL FILE NAME SHORTCUTS - NO AUTH REQUIRED ************** case "robots.txt": if (!browserSender.SendFileToBrowser("static\\robots.txt")) Functions.WriteLineToLogFile("Could not send robots.txt to browser"); return; case "clientaccesspolicy.xml": if (!browserSender.SendFileToBrowser("static\\clientaccesspolicy.xml")) Functions.WriteLineToLogFile("Could not send clientaccesspolicy.xml to browser (presumably to Silverlight)"); return; case "silverlightsource": if (!browserSender.SendFileToBrowser("static\\silverlight\\SilverPotato.xap")) Functions.WriteLineToLogFile("Could not send SilverPotato XAP to browser"); return; //Ping is allowed case "ping": Version v = Functions.ServerVersion; string xResponse = "<?xml version=\"1.0\"?><pingresponse result=\"PING_RESULT\" serverversion=\"SERVER_VERSION\" serverrevision=\"SERVER_REVISION\" serverosversionstring=\"SERVER_OS_VERSION_STRING\" serverosversion=\"SERVER_OS_VERSION\" servercapabilities=\"CAP_FLAGS\" />"; xResponse = xResponse.Replace("PING_RESULT", Settings.Default.RequirePassword ? "NEED_PASSWORD" : "OK"); xResponse = xResponse.Replace("SERVER_VERSION", v.Major.ToString() + "." + v.Minor.ToString() ); // This is culture invariant xResponse = xResponse.Replace("SERVER_OS_VERSION_STRING", Environment.OSVersion.VersionString); xResponse = xResponse.Replace("SERVER_OS_VERSION", Environment.OSVersion.Version.ToString(2) ); xResponse = xResponse.Replace("SERVER_REVISION", v.Build.ToString()); xResponse = xResponse.Replace("CAP_FLAGS", Functions.ServerCapabilities); browserSender.SendXMLToBrowser(xResponse); return; // Fav Icon is allowed case "favicon.ico": browserSender.SendFileToBrowser(HttpUtility.UrlDecode("static\\images\\remotepotatoicon.ico")); return; default: break; } // Channel logos are allowed if ((txtAction.StartsWith("logo"))) { int hashlocation = txtAction.LastIndexOf("/"); if (hashlocation < 1) { bool fooa = browserSender.Send404Page(); } else { txtAction = txtAction.Replace("logo/", ""); string logoSvcID = HttpUtility.UrlDecode(txtAction); // Send logo to browser browserSender.SendLogoToBrowser(logoSvcID); } return; } // Special case 'static' files that aren't => legacy support for streaming if (txtAction.StartsWith("httplivestream")) { ProcessHTTPLSURL(txtAction, ref browserSender); return; } // Static Files if ( (txtAction.StartsWith("static")) ) { int hashlocation = txtAction.LastIndexOf("/"); if (hashlocation < 1) { bool fooa = browserSender.Send404Page(); } else { // Send file browserSender.SendFileToBrowser(HttpUtility.UrlDecode(txtAction)); } return; } // Skin files if ( (txtAction.StartsWith("skin"))) { int hashlocation = txtAction.LastIndexOf("/"); if (hashlocation < 1) { bool fooa = browserSender.Send404Page(); } else { // Send file browserSender.SendFileToBrowser(HttpUtility.UrlDecode(txtAction), true, false); } return; } // Thumbnails are allowed if (txtAction == "rectvthumbnail64") { GetRecTVThumbnail(ref browserSender, true); return; } else if (txtAction == "rectvthumbnail") { GetRecTVThumbnail(ref browserSender, false); return; } if (txtAction.StartsWith("getfilethumbnail64")) { GetFileThumbnailUsingQueryString(ref browserSender, true); return; } else if (txtAction.StartsWith("getfilethumbnail")) { GetFileThumbnailUsingQueryString(ref browserSender, false); return; } if (txtAction.StartsWith("filethumbnail")) { string txtSize = txtAction.Replace("filethumbnail/",""); FatAttitude.ThumbnailSizes size = (FatAttitude.ThumbnailSizes) Enum.Parse( (new FatAttitude.ThumbnailSizes().GetType() ), txtSize, true); SendFileThumbnail(txtPostObjects, size, ref browserSender); return; } if (txtAction.StartsWith("musicalbumthumbnail")) { GetAlbumThumbnail(ref browserSender, txtAction.Contains("musicalbumthumbnail64") ); return; } if (txtAction.StartsWith("musicsongthumbnail")) { GetSongThumbnail(ref browserSender, txtAction.Contains("musicsongthumbnail64")); return; } // Silverlight is allowed (no longer contains password info) bool showSilverlight = (txtAction.StartsWith("silverlight")); if (Settings.Default.SilverlightIsDefault) showSilverlight = showSilverlight | (txtAction.Trim().Equals("")); if (showSilverlight) { string silverTemplate = FileCache.ReadTextFile("static\\silverlight\\default_template.htm"); browserSender.SendNormalHTMLPageToBrowser(silverTemplate); return; } // MORE OPEN METHODS... if (txtAction.StartsWith("streamsong")) { bool isBase64Encoded = (txtAction.StartsWith("streamsong64")); if (!SendSongToBrowser(ref browserSender, isBase64Encoded, true, false)) browserSender.Send404Page(); return; } // MORE OPEN METHODS... if (txtAction.StartsWith("downloadsong")) { bool isBase64Encoded = (txtAction.StartsWith("downloadsong64")); if (!SendSongToBrowser(ref browserSender, isBase64Encoded, true, true)) browserSender.Send404Page(); return; } // ******************************************************************************************** // Cookie Authentication Required for all Methods below here ********************************** // ******************************************************************************************** bool processMoreActions = false; if (canProceedAuthenticatedByHTTPCookie()) { processMoreActions = true; } else { spoolMessage("Webserver: requesting login."); bool LoginSuccess = false; string destURL = ""; string destQueryString = ""; ViewLoginPage(txtPostObjects, ref LoginSuccess, ref destURL, ref destQueryString); // Successful login if (LoginSuccess) { processMoreActions = true; txtPageTitle = ""; // Assign new (old) action and querystring for further processing txtAction = destURL; qsParams = HttpUtility.ParseQueryString(destQueryString); // We've missed the silverlight check (it's up above), so check again if (Settings.Default.SilverlightIsDefault) { string silverTemplate = FileCache.ReadTextFile("static\\silverlight\\default_template.htm"); browserSender.SendNormalHTMLPageToBrowser(silverTemplate); return; } } } bool sentWholePage = false; if (processMoreActions) { switch (txtAction) { // Legacy Streamsong (secured) case "streamsong.mp3": if (!SendSongToBrowser(ref browserSender, false, true, false)) browserSender.Send404Page(); return; case "streamsong": if (!SendSongToBrowser(ref browserSender, false, true, false)) browserSender.Send404Page(); return; // MANUAL RECORDING ====================================================== case "recordmanual": foo = TryManualRecording(); break; // Remote Control case "remotecontrol": foo = ViewRemoteControl(); break; // Remote Control case "rc": bool haveSentHTMLPage = SendRemoteControlCommand(ref browserSender); if (haveSentHTMLPage) return; // Don't continue; this method sends a blank page break; // RECORD A SERIES case "recordshow_series": foo = RecordSeries(); break; // RECORD (FROM RecordingRequest): MULTIPURPOSE case "recordfromqueue": foo = RecordFromQueue(); break; // PICS case "browsepics": ViewPicturesLibrary(); break; case "viewpic": foo = ViewPicture(ref browserSender, ref sentWholePage); if (sentWholePage) return; // no more processing required break; case "picfolderaszip": foo = GetPicturesFolderAsZip(ref browserSender); return; // Don't continue, no Reponse left to output // VIDEOS case "browsevideos": ViewVideoLibrary(); break; case "streamvideo": foo = StreamVideo(); break; // MUSIC case "musicroot": ViewMusic(); break; case "musicartists": ViewMusicArtists(false); break; case "musicartist": ViewMusicArtist(); break; case "musicalbumartists": ViewMusicArtists(true); break; case "musicalbums": ViewMusicAlbums(); break; case "musicalbum": ViewMusicAlbum(); break; case "musicgenres": ViewMusicGenres(); break; case "musicgenre": ViewMusicGenre(); break; case "musicsong": ViewMusicSong(); break; // LIST RECORDINGS case "scheduledrecordings": foo = ViewScheduledRecordings(); break; case "log-out": DoLogOut(); break; // LIST RECORDINGS case "recordedtv": foo = ViewRecordedTVList(); AdditionalStyles.Add("rectv"); break; // VIEW A SPECIFIC SERIES case "viewseriesrequest": foo = ViewSeriesRequest(); AdditionalStyles.Add("showdetails"); break; // MANAGE ALL SERIES case "viewseriesrequests": foo = ViewSeriesRequests(); break; // VIEW AN EPG PAGE case "viewepglist": foo = ViewEPGList(); AdditionalStyles.Add("epg-list"); break; // VIEW AN EPG PAGE - GRID case "viewepggrid": Functions.WriteLineToLogFile("RP: (VEPG)"); foo = ViewEPGGrid(); AdditionalStyles.Add("epg-grid"); break; // Shift EPG Grid Up case "epgnavup": foo = EPGGridChannelRetreat(); AdditionalStyles.Add("epg-grid"); break; // Shift EPG Grid Down case "epgnavdown": foo = EPGGridChannelAdvance(); AdditionalStyles.Add("epg-grid"); break; // Shift EPG Grid Right case "epgnavright": foo = EPGGridTimeWindowShiftByMinutes(EPGManager.TimespanMinutes); AdditionalStyles.Add("epg-grid"); break; // Shift EPG Grid Left case "epgnavleft": foo = EPGGridTimeWindowShiftByMinutes(0 - EPGManager.TimespanMinutes); AdditionalStyles.Add("epg-grid"); break; // Shift EPG Grid Left case "epgnavtop": foo = EPGGridChannelSetAbsolute(true, false); AdditionalStyles.Add("epg-grid"); break; // Shift EPG Grid Left case "epgnavbottom": foo = EPGGridChannelSetAbsolute(false, true); AdditionalStyles.Add("epg-grid"); break; // Shift EPG To Page case "epgjumptopage": foo = EPGGridChannelJump(); AdditionalStyles.Add("epg-grid"); break; // VIEW AN EPG SHOW case "viewepgprogramme": foo = ViewEPGProgramme(); AdditionalStyles.Add("showdetails"); break; // STREAM A SHOW case "streamprogramme": foo = StreamRecordedProgramme(); AdditionalStyles.Add("showdetails"); break; // SEARCH BY TITLE case "searchbytitle": foo = SearchShowsByText(); break; // DELETE A RECORDING case "deletefile": foo = DeleteFileFromFilePath(false); break; case "deletefile64": foo = DeleteFileFromFilePath(true); break; // CANCEL A RECORDING case "cancelseriesrequest": foo = CancelRequest(); break; // CANCEL A RECORDING case "cancelrecording": foo = CancelRecording(); break; // VIEW MOVIES case "movies": foo = ViewMovies(); AdditionalStyles.Add("movies"); break; // VIEW MOVIES case "viewmovie": foo = ViewMovie(); AdditionalStyles.Add("showdetails"); AdditionalStyles.Add("movies"); break; case "info": txtResponse += "This is the Remote Potato Server v" + Functions.VersionText + " running on " + Environment.OSVersion.VersionString + "."; txtResponse += "<br/><br/>For help and support please visit the <a href='http://forums.fatattitude.com'>FatAttitude Forums</a>."; break; case "mainmenu": ShowMainMenu(); break; default: ShowMainMenu(); break; } } // Finalise response: convert to master page string txtOutputPage = FileCache.ReadSkinTextFile("masterpage.htm"); // Commit response txtOutputPage = txtOutputPage.Replace("**PAGECONTENT**", txtResponse); txtResponse = ""; // Style inclusion? (this line must be before the Skin section, as the returned string includes **SKINFOLDER** to be replaced txtOutputPage = txtOutputPage.Replace("**PAGEADDITIONALSTYLES**", AdditionalStyleLinks(AdditionalStyles)); // Orientation txtOutputPage = txtOutputPage.Replace("**PAGEORIENTATION**", txtOutputPage.Contains("PAGEORIENTATION=LANDSCAPE") ? "landscape" : "portrait"); // Skin txtOutputPage = txtOutputPage.Replace("**SKINFOLDER**", "/static/skins/" + Themes.ActiveThemeName); txtOutputPage = txtOutputPage.Replace("**HEADER**", "Remote Potato"); // Default Page Title txtOutputPage = txtOutputPage.Replace("**PAGETITLE**", txtPageTitle); // Copyright / Timestamp txtOutputPage = txtOutputPage.Replace("**TIMEANDVERSIONSTRING**", DateTime.Now.ToLongTimeString() + ", v" + Functions.VersionText); if (!browserSender.SendNormalHTMLPageToBrowser(txtOutputPage)) { spoolMessage("Webserver failed to send data."); } #if !DEBUG } catch (Exception e) { Functions.WriteExceptionToLogFile(e); spoolMessage("EXCEPTION OCCURRED: " + e.Message); BrowserSender exceptionBrowserSender = new BrowserSender(Context); exceptionBrowserSender.SendNormalHTMLPageToBrowser("<h1>Remote Potato Error</h1>An error occurred and remote potato was unable to serve this web page.<br /><br />Check the debug logs for more information."); } #endif }