Beispiel #1
0
        /// <summary>
        /// This is designed to be easily unit testable by not taking actual HttpContext, but doing everything through this IRequestInfo object
        /// </summary>
        /// <param name="info"></param>
        public void MakeReply(IRequestInfo info)
        {
            var r = info.LocalPathWithoutQuery.Replace("/bloom/", "");

            r = r.Replace("library/", "");
            if (r.Contains("libraryContents"))
            {
                GetLibraryBooks(info);
            }
            else if (r == "libraryName")
            {
                info.WriteCompleteOutput(_collectionSettings.CollectionName + " Library");
            }
            else if (r.Contains("SourceCollectionsList"))
            {
                GetStoreBooks(info);
            }
            else if (r.StartsWith("thumbnails/"))
            {
                r = r.Replace("thumbnails/", "");
                r = r.Replace("%5C", "/");
                r = r.Replace("%20", " ");
                if (File.Exists(r))
                {
                    info.ReplyWithImage(r);
                }
            }
            else if (r.EndsWith(".png"))
            {
                info.ContentType = "image/png";

                r = r.Replace("thumbnail", "");
                //if (r.Contains("thumb"))
                {
                    if (File.Exists(r))
                    {
                        info.ReplyWithImage(r);
                    }
                    else
                    {
                        var imgPath = FileLocator.GetFileDistributedWithApplication("BloomBrowserUI", "book.png");
                        info.ReplyWithImage(imgPath);
                        //book.GetThumbNailOfBookCoverAsync(book.Type != Book.Book.BookType.Publication,image => RefreshOneThumbnail(book, image));
                    }
                }
//				else
//				{
//					var imgPath = FileLocator.GetFileDistributedWithApplication("root", "ui", "book.png");
//					info.ReplyWithImage(imgPath);
//				}
            }
            else
            {
                string path = FileLocator.GetFileDistributedWithApplication("BloomBrowserUI", r);


                //request.QueryString.GetValues()
                info.WriteCompleteOutput(File.ReadAllText(path));
            }
        }
Beispiel #2
0
        private bool CheckForSampleTextChanges(IRequestInfo info)
        {
            lock (SyncObj)
            {
                if (_sampleTextsWatcher == null)
                {
                    if (string.IsNullOrEmpty(CurrentCollectionSettings?.SettingsFilePath))
                    {
                        // We've had cases (BL-4744) where this is apparently called before CurrentCollectionSettings is
                        // established. I'm not sure how this can happen but if we haven't even established a current collection
                        // yet I think it's pretty safe to say its sample texts haven't changed since we last read them.
                        info.ContentType = "text/plain";
                        info.WriteCompleteOutput("no");
                        return(true);
                    }
                    var path = Path.Combine(Path.GetDirectoryName(CurrentCollectionSettings.SettingsFilePath), "Sample Texts");
                    if (!Directory.Exists(path))
                    {
                        Directory.CreateDirectory(path);
                    }

                    _sampleTextsWatcher = new FileSystemWatcher {
                        Path = path
                    };
                    _sampleTextsWatcher.Created            += SampleTextsOnChange;
                    _sampleTextsWatcher.Changed            += SampleTextsOnChange;
                    _sampleTextsWatcher.Renamed            += SampleTextsOnChange;
                    _sampleTextsWatcher.Deleted            += SampleTextsOnChange;
                    _sampleTextsWatcher.EnableRaisingEvents = true;
                }
            }

            lock (_sampleTextsWatcher)
            {
                var hasChanged = _sampleTextsChanged;

                // Reset the changed flag.
                // NOTE: we are only resetting the flag if it was "true" when we checked in case the FileSystemWatcher detects a change
                // after we check the flag but we reset it to false before we check again.
                if (hasChanged)
                {
                    _sampleTextsChanged = false;
                }

                info.ContentType = "text/plain";
                info.WriteCompleteOutput(hasChanged ? "yes" : "no");

                return(true);
            }
        }
		private bool CheckForSampleTextChanges(IRequestInfo info)
		{
			if (_sampleTextsWatcher == null)
			{
				var path = Path.Combine(Path.GetDirectoryName(CurrentCollectionSettings.SettingsFilePath), "Sample Texts");
				if (!Directory.Exists(path))
					Directory.CreateDirectory(path);

				_sampleTextsWatcher = new FileSystemWatcher {Path = path};
				_sampleTextsWatcher.Created += SampleTextsOnChange;
				_sampleTextsWatcher.Changed += SampleTextsOnChange;
				_sampleTextsWatcher.Deleted += SampleTextsOnChange;
				_sampleTextsWatcher.EnableRaisingEvents = true;
			}

			var hasChanged = _sampleTextsChanged;

			// Reset the changed flag.
			// NOTE: we are only resetting the flag if it was "true" when we checked in case the FileSystemWatcher detects a change
			// after we check the flag but we reset it to false before we check again.
			if (hasChanged) _sampleTextsChanged = false;

			info.ContentType = "text/plain";
			info.WriteCompleteOutput(hasChanged ? "yes" : "no");

			return true;
		}
Beispiel #4
0
        /// <summary>
        /// This is designed to be easily unit testable by not taking actual HttpContext, but doing everything through this IRequestInfo object
        /// </summary>
        /// <param name="info"></param>
        public void MakeReply(IRequestInfo info)
        {
            if (info.LocalPathWithoutQuery.EndsWith("testconnection"))
            {
                info.WriteCompleteOutput("OK");
                return;
            }

            var r = info.LocalPathWithoutQuery.Replace("/bloom/", "");

            r = r.Replace("%3A", ":");
            r = r.Replace("%20", " ");
            r = r.Replace("%27", "'");
            if (r.EndsWith(".png") || r.EndsWith(".jpg"))
            {
                info.ContentType = r.EndsWith(".png") ? "image/png" : "image/jpeg";

                r = r.Replace("thumbnail", "");
                //if (r.Contains("thumb"))
                {
                    if (File.Exists(r))
                    {
                        info.ReplyWithImage(_cache.GetPathToResizedImage(r));
                    }
                    else
                    {
                        Logger.WriteEvent("**ImageServer: File Missing: " + r);
                        info.WriteError(404);
                    }
                }
            }
        }
Beispiel #5
0
 protected virtual bool ProcessRequest(IRequestInfo info)
 {
     if (info.LocalPathWithoutQuery.EndsWith("testconnection"))
     {
         info.WriteCompleteOutput("OK");
         return(true);
     }
     return(false);
 }
        private void GetStoreBooks(IRequestInfo info)
        {
            //enhance: it will eventually work better to do sorting client-side, according to user's current prefs
            var reply = new StringBuilder();
            var list  = new List <BookCollection>();

            list.AddRange(_sourceCollectionsesList.GetSourceCollections());

            list.Sort(CompareBookCollections);

            foreach (BookCollection collection in list)
            {
                reply.AppendFormat("<li class='collectionGroup'><h2>{0}</h2><ul class='collection'>", collection.Name);
                reply.Append(GetBookListItems(collection.GetBookInfos()));
                reply.AppendFormat(@"</ul></li>");
            }
            info.WriteCompleteOutput(reply.ToString());
        }
Beispiel #7
0
        public static bool HandleRequest(string localPath, IRequestInfo info, CollectionSettings currentCollectionSettings)
        {
            var lastSep     = localPath.IndexOf("/", System.StringComparison.Ordinal);
            var lastSegment = (lastSep > -1) ? localPath.Substring(lastSep + 1) : localPath;

            switch (lastSegment)
            {
            case "loadStrings":

                while (_localizing)
                {
                    Thread.Sleep(0);
                }

                try
                {
                    _localizing = true;

                    var d    = new Dictionary <string, string>();
                    var post = info.GetPostData();

                    foreach (string key in post.Keys)
                    {
                        var translation = LocalizationManager.GetDynamicString("Bloom", key, post[key]);
                        if (!d.ContainsKey(key))
                        {
                            d.Add(key, translation);
                        }
                    }

                    info.ContentType = "application/json";
                    info.WriteCompleteOutput(JsonConvert.SerializeObject(d));
                    return(true);
                }
                finally
                {
                    _localizing = false;
                }
            }

            return(false);
        }
Beispiel #8
0
        /// <summary>
        /// This method is overridden in classes inheriting from this class to handle specific request types
        /// </summary>
        /// <param name="info"></param>
        /// <returns></returns>
        protected virtual bool ProcessRequest(IRequestInfo info)
        {
#if MEMORYCHECK
            // Check memory for the benefit of developers.  (Also see all requests as a side benefit.)
            var debugMsg = String.Format("ServerBase.ProcessRequest(\"{0}\"", info.RawUrl);
            SIL.Windows.Forms.Reporting.MemoryManagement.CheckMemory(true, debugMsg, false);
#endif
            // process request for directory index
            var requestedPath = GetLocalPathWithoutQuery(info);
            if (info.RawUrl.EndsWith("/") && (Directory.Exists(requestedPath)))
            {
                info.WriteError(403, "Directory listing denied");
                return(true);
            }

            if (requestedPath.EndsWith("testconnection"))
            {
                info.WriteCompleteOutput("OK");
                return(true);
            }
            return(false);
        }
Beispiel #9
0
        private static bool GetTopicList(IRequestInfo info)
        {
            var keyToLocalizedTopicDictionary = new Dictionary <string, string>();

            foreach (var topic in BookInfo.TopicsKeys)
            {
                var localized = LocalizationManager.GetDynamicString("Bloom", "Topics." + topic, topic,
                                                                     @"shows in the topics chooser in the edit tab");
                keyToLocalizedTopicDictionary.Add(topic, localized);
            }
            string localizedNoTopic = LocalizationManager.GetDynamicString("Bloom", "Topics.NoTopic", "No Topic",
                                                                           @"shows in the topics chooser in the edit tab");
            var arrayOfKeyValuePairs = from key in keyToLocalizedTopicDictionary.Keys
                                       orderby keyToLocalizedTopicDictionary[key]
                                       select string.Format("\"{0}\": \"{1}\"", key, keyToLocalizedTopicDictionary[key]);

            var pairs = arrayOfKeyValuePairs.Concat(",");

            info.ContentType = "application/json";
            var data = string.Format("{{\"NoTopic\": \"{0}\", {1} }}", localizedNoTopic, pairs);

            info.WriteCompleteOutput(data);

            /*			var data = new {NoTopic = localizedNoTopic, pairs = arrayOfKeyValuePairs};
             * var serializeObject = JsonConvert.SerializeObject(data, new JsonSerializerSettings
             *                      {
             *                              TypeNameHandling = TypeNameHandling.None,
             *                              TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple,
             *                      });
             */
            //info.WriteCompleteOutput(serializeObject);



            return(true);
        }
Beispiel #10
0
        private bool ProcessContent(IRequestInfo info, string localPath)
        {
            if (localPath.EndsWith(".css"))
            {
                return(ProcessCssFile(info, localPath));
            }

            switch (localPath)
            {
            case "currentPageContent":
                info.ContentType = "text/html";
                info.WriteCompleteOutput(CurrentPageContent ?? "");
                return(true);

            case "toolboxContent":
                info.ContentType = "text/html";
                info.WriteCompleteOutput(ToolboxContent ?? "");
                return(true);

            case "availableFontNames":
                info.ContentType = "application/json";
                var list = new List <string>(Browser.NamesOfFontsThatBrowserCanRender());
                list.Sort();
                info.WriteCompleteOutput(JsonConvert.SerializeObject(new{ fonts = list }));
                return(true);

            case "uiLanguages":
                // Returns json with property languages, an array of objects (one for each UI language Bloom knows about)
                // each having label (what to show in a menu) and tag (the language code).
                // Used in language select control in hint bubbles tab of text box properties dialog
                // brought up from cog control in origami mode.
                var langs = new List <object>();
                foreach (var lang in L10NSharp.LocalizationManager.GetUILanguages(true))
                {
                    langs.Add(new { label = WorkspaceView.MenuItemName(lang), tag = lang.IetfLanguageTag });
                }
                info.ContentType = "application/json";
                info.WriteCompleteOutput(JsonConvert.SerializeObject(new { languages = langs }));
                return(true);

            case "bubbleLanguages":
                // Returns a list of lang codes such that if a block has hints in multiple languages,
                // we prefer the one that comes first in the list.
                // Used to select the best label to show in a hint bubble when a bloom-translationGroup has multiple
                // labels with different languages.
                var bubbleLangs = new List <string>();
                bubbleLangs.Add(LocalizationManager.UILanguageId);
                if (_bookSelection.CurrentSelection.MultilingualContentLanguage2 != null)
                {
                    bubbleLangs.Add(_bookSelection.CurrentSelection.MultilingualContentLanguage2);
                }
                if (_bookSelection.CurrentSelection.MultilingualContentLanguage3 != null)
                {
                    bubbleLangs.Add(_bookSelection.CurrentSelection.MultilingualContentLanguage3);
                }
                bubbleLangs.AddRange(new [] { "en", "fr", "sp", "ko", "zh-Hans" });
                // If we don't have a hint in the UI language or any major language, it's still
                // possible the page was made just for this langauge and has a hint in that language.
                // Not sure whether this should be before or after the list above.
                // Definitely wants to be after UILangage, otherwise we get the surprising result
                // that in a French collection these hints stay French even when all the rest of the
                // UI changes to English.
                bubbleLangs.Add(_bookSelection.CurrentSelection.CollectionSettings.Language1Iso639Code);
                // if it isn't available in any of those we'll arbitrarily take the first one.
                info.ContentType = "application/json";
                info.WriteCompleteOutput(JsonConvert.SerializeObject(new { langs = bubbleLangs }));
                return(true);

            case "authorMode":
                info.ContentType = "text/plain";
                info.WriteCompleteOutput(AuthorMode ? "true" : "false");
                return(true);

            case "topics":
                return(GetTopicList(info));
            }
            return(ProcessAnyFileContent(info, localPath));
        }
Beispiel #11
0
        // Every path should return false or send a response.
        // Otherwise we can get a timeout error as the browser waits for a response.
        //
        // NOTE: this method gets called on different threads!
        protected override bool ProcessRequest(IRequestInfo info)
        {
            var localPath = GetLocalPathWithoutQuery(info);

            //enhance: something feeds back these branding logos with a weird URL, that shouldn't be.
            if (localPath.IndexOf("api/branding") > 20)            // this 20 is just arbitrary... the point is, if it doesn't start with api/branding, it is bogus
            {
                return(false);
            }

            if (localPath.ToLower().StartsWith("api/"))
            {
                var endpoint = localPath.Substring(3).ToLowerInvariant().Trim(new char[] { '/' });
                foreach (var pair in _endpointRegistrations.Where(pair =>
                                                                  Regex.Match(endpoint,
                                                                              "^" + //must match the beginning
                                                                              pair.Key.ToLower()
                                                                              ).Success))
                {
                    // A single synchronization object won't do, because when processing a request to create a thumbnail,
                    // we have to load the HTML page the thumbnail is based on. If the page content somehow includes
                    // an api request (api/branding/image is one example), that request will deadlock if the
                    // api/pageTemplateThumbnail request already has the main lock.
                    // To the best of my knowledge, there's no shared data between the thumbnailing process and any
                    // other api requests, so it seems safe to have one lock that prevents working on multiple
                    // thumbnails at the same time, and one that prevents working on other api requests at the same time.
                    var syncOn = SyncObj;
                    if (localPath.ToLowerInvariant().StartsWith("api/pagetemplatethumbnail"))
                    {
                        syncOn = ThumbnailSyncObj;
                    }
                    lock (syncOn)
                    {
                        return(ApiRequest.Handle(pair.Value, info, CurrentCollectionSettings, _bookSelection.CurrentSelection));
                    }
                }
            }

            //OK, no more obvious simple API requests, dive into the rat's nest of other possibilities
            if (base.ProcessRequest(info))
            {
                return(true);
            }

            if (localPath.Contains("CURRENTPAGE"))            //useful when debugging. E.g. http://localhost:8091/bloom/CURRENTPAGE.htm will always show the page we're on.
            {
                localPath = _keyToCurrentPage;
            }

            string content;
            bool   gotSimulatedPage;

            lock (_urlToSimulatedPageContent)
            {
                gotSimulatedPage = _urlToSimulatedPageContent.TryGetValue(localPath, out content);
            }
            if (gotSimulatedPage)
            {
                info.ContentType = "text/html";
                info.WriteCompleteOutput(content ?? "");
                return(true);
            }

            if (localPath.StartsWith(OriginalImageMarker) && IsImageTypeThatCanBeDegraded(localPath))
            {
                // Path relative to simulated page file, and we want the file contents without modification.
                // (Note that the simulated page file's own URL starts with this, so it's important to check
                // for that BEFORE we do this check.)
                localPath = localPath.Substring(OriginalImageMarker.Length + 1);
                return(ProcessAnyFileContent(info, localPath));
            }

            if (localPath.StartsWith("error", StringComparison.InvariantCulture))
            {
                ProcessError(info);
                return(true);
            }
            else if (localPath.StartsWith("i18n/", StringComparison.InvariantCulture))
            {
                if (ProcessI18N(localPath, info))
                {
                    return(true);
                }
            }
            else if (localPath.StartsWith("directoryWatcher/", StringComparison.InvariantCulture))
            {
                return(ProcessDirectoryWatcher(info));
            }
            else if (localPath.StartsWith("localhost/", StringComparison.InvariantCulture))
            {
                var temp = LocalHostPathToFilePath(localPath);
                if (RobustFile.Exists(temp))
                {
                    localPath = temp;
                }
            }
            // this is used only by the readium viewer
            else if (localPath.StartsWith("node_modules/jquery/dist/jquery.js"))
            {
                localPath = BloomFileLocator.GetBrowserFile("jquery.min.js");
                // Avoid having "output/browser/" removed on Linux developer machines.
                // GetBrowserFile adds output to the path on developer machines, but not user installs.
                return(ProcessContent(info, localPath));
            }
            //Firefox debugger, looking for a source map, was prefixing in this unexpected
            //way.
            localPath = localPath.Replace("output/browser/", "");

            return(ProcessContent(info, localPath));
        }
        private bool ProcessContent(IRequestInfo info, string localPath)
        {
            if (localPath.EndsWith(".css"))
            {
                return ProcessCssFile(info, localPath);
            }

            switch (localPath)
            {
                case "currentPageContent":
                    info.ContentType = "text/html";
                    info.WriteCompleteOutput(CurrentPageContent ?? "");
                    return true;
                case "toolboxContent":
                    info.ContentType = "text/html";
                    info.WriteCompleteOutput(ToolboxContent ?? "");
                    return true;
                case "availableFontNames":
                    info.ContentType = "application/json";
                    var list = new List<string>(Browser.NamesOfFontsThatBrowserCanRender());
                    list.Sort();
                    info.WriteCompleteOutput(JsonConvert.SerializeObject(new{fonts = list}));
                    return true;
                case "authorMode":
                    info.ContentType = "text/plain";
                    info.WriteCompleteOutput(AuthorMode ? "true" : "false");
                    return true;
                case "topics":
                    return GetTopicList(info);
            }
            return ProcessAnyFileContent(info, localPath);
        }
Beispiel #13
0
 /// <summary>
 /// This is safe to use with axios.Post. See BL-4901. There, not returning any text at all
 /// caused some kind of problem in axios.post(), after the screen had been shut down.
 /// </summary>
 public void PostSucceeded()
 {
     _requestInfo.ResponseContentType = "text/plain";
     _requestInfo.WriteCompleteOutput("OK");
 }
Beispiel #14
0
        /// <summary>
        /// This is designed to be easily unit testable by not taking actual HttpContext, but doing everything through this IRequestInfo object
        /// </summary>
        /// <param name="info"></param>
        public void MakeReply(IRequestInfo info)
        {
            var r = info.LocalPathWithoutQuery.Replace("/bloom/", "");
            r = r.Replace("library/", "");
            if (r.Contains("libraryContents"))
            {
                GetLibraryBooks(info);
            }
            else if (r == "libraryName")
            {
                info.WriteCompleteOutput(_collectionSettings.CollectionName + " Library");
            }
            else if (r.Contains("SourceCollectionsList"))
            {
                GetStoreBooks(info);
            }
            else if(r.StartsWith("thumbnails/"))
            {
                r = r.Replace("thumbnails/", "");
                r = r.Replace("%5C", "/");
                r = r.Replace("%20", " ");
                if (File.Exists(r))
                {
                    info.ReplyWithImage(r);
                }
            }
            else if (r.EndsWith(".png"))
            {
                info.ContentType = "image/png";

                r = r.Replace("thumbnail", "");
                //if (r.Contains("thumb"))
                {
                    if (File.Exists(r))
                    {
                        info.ReplyWithImage(r);
                    }
                    else
                    {
                        var imgPath = FileLocator.GetFileDistributedWithApplication("BloomBrowserUI", "book.png");
                        info.ReplyWithImage(imgPath);
                        //book.GetThumbNailOfBookCoverAsync(book.Type != Book.Book.BookType.Publication,image => RefreshOneThumbnail(book, image));
                    }
                }
            //				else
            //				{
            //					var imgPath = FileLocator.GetFileDistributedWithApplication("root", "ui", "book.png");
            //					info.ReplyWithImage(imgPath);
            //				}
            }
            else
            {
                string path = FileLocator.GetFileDistributedWithApplication("BloomBrowserUI", r);

                //request.QueryString.GetValues()
                info.WriteCompleteOutput(File.ReadAllText(path));
            }
        }
		// Every path should return false or send a response.
		// Otherwise we can get a timeout error as the browser waits for a response.
		protected override bool ProcessRequest(IRequestInfo info)
		{
			if (base.ProcessRequest(info))
				return true;

			var localPath = GetLocalPathWithoutQuery(info);

			// routing
			if (localPath.StartsWith("readers/"))
			{
				if (ReadersHandler.HandleRequest(localPath, info, CurrentCollectionSettings))
					return true;
			}
			else if (localPath.StartsWith("i18n/"))
			{
				if (I18NHandler.HandleRequest(localPath, info, CurrentCollectionSettings))
					return true;
			}
			else if (localPath.StartsWith("directoryWatcher/"))
			{
				var dirName = info.GetPostData()["dir"];

				if (dirName == "Sample Texts")
				{
					if (CheckForSampleTextChanges(info))
						return true;
				}

				return false;
			}
			else if (localPath.StartsWith("leveledRTInfo/"))
			{
				var queryPart = string.Empty;
				if (info.RawUrl.Contains("?"))
					queryPart = "#" + info.RawUrl.Split('?')[1];
				var langCode = LocalizationManager.UILanguageId;
				var completeEnglishPath = FileLocator.GetFileDistributedWithApplication(localPath);
				var completeUiLangPath = GetUiLanguageFileVersion(completeEnglishPath, langCode);
				string url;
				if (langCode != "en" && File.Exists(completeUiLangPath))
					url = completeUiLangPath;
				else
					url = completeEnglishPath;
				var cleanUrl = url.Replace("\\", "/"); // allows jump to file to work

				string browser = string.Empty;
				if (Palaso.PlatformUtilities.Platform.IsLinux)
				{
					// REVIEW: This opens HTML files in the browser. Do we have any non-html
					// files that this code needs to open in the browser? Currently they get
					// opened in whatever application the user has selected for that file type
					// which might well be an editor.
					browser = "xdg-open";
				}
				else
				{
					// If we don't provide the path of the browser, i.e. Process.Start(url + queryPart), we get file not found exception.
					// If we prepend "file:///", the anchor part of the link (#xxx) is not sent unless we provide the browser path too.
					// This is the same behavior when simply typing a url into the Run command on Windows.
					// If we fail to get the browser path for some reason, we still load the page, just without navigating to the anchor.
					string defaultBrowserPath;
					if (TryGetDefaultBrowserPath(out defaultBrowserPath))
					{
						browser = defaultBrowserPath;
					}
				}

				if (!string.IsNullOrEmpty(browser))
				{
					try
					{
						Process.Start(browser, "\"file:///" + cleanUrl + queryPart + "\"");
						return false;
					}
					catch (Exception)
					{
						Debug.Fail("Jumping to browser with anchor failed.");
						// Don't crash Bloom because we can't open an external file.
					}
				}

				// If the above failed, either for lack of default browser or exception, try this:
				Process.Start("\"" + cleanUrl + "\"");
				return false;
			}

			switch (localPath)
			{
				case "currentPageContent":
					info.ContentType = "text/html";
					info.WriteCompleteOutput(CurrentPageContent ?? "");
					return true;

				case "accordionContent":
					info.ContentType = "text/html";
					info.WriteCompleteOutput(AccordionContent ?? "");
					return true;

				case "availableFontNames":
					InstalledFontCollection installedFontCollection = new InstalledFontCollection();
					info.WriteCompleteOutput(string.Join(",", installedFontCollection.Families.Select(f => f.Name)));
					return true;

				case "help":
					var post = info.GetPostData();
					HelpLauncher.Show(null, post["data"]);
					return true;

				case "getNextBookStyle":
					info.ContentType = "text/html";
					info.WriteCompleteOutput(CurrentBook.NextStyleNumber.ToString(CultureInfo.InvariantCulture));
					return true;
			}

			string path = null;
			try
			{
				path = FileLocator.GetFileDistributedWithApplication("BloomBrowserUI", localPath);
			}
			catch (ApplicationException)
			{
				// ignore
			}

			if (!File.Exists(path)) return false;

			info.ContentType = GetContentType(Path.GetExtension(localPath));
			info.ReplyWithFileContent(path);
			return true;
		}
Beispiel #16
0
        private void GetLibraryBooks(IRequestInfo info)
        {
            var books = _booksInProjectLibrary.GetBookInfos();

            info.WriteCompleteOutput(GetBookListItems(books));
        }
Beispiel #17
0
        public static bool HandleRequest(string localPath, IRequestInfo info, CollectionSettings currentCollectionSettings)
        {
            var lastSep     = localPath.IndexOf("/", System.StringComparison.Ordinal);
            var lastSegment = (lastSep > -1) ? localPath.Substring(lastSep + 1) : localPath;

            switch (lastSegment)
            {
            case "loadStrings":

                while (_localizing)
                {
                    Thread.Sleep(0);
                }

                try
                {
                    _localizing = true;

                    var d    = new Dictionary <string, string>();
                    var post = info.GetPostDataWhenFormEncoded();

                    if (post != null)
                    {
                        foreach (string key in post.Keys)
                        {
                            try
                            {
                                if (d.ContainsKey(key))
                                {
                                    continue;
                                }

                                // Now that end users can create templates, it's annoying to report that their names,
                                // page labels, and page descriptions don't have localizations.
                                if (IsTemplateBookKey(key))
                                {
                                    continue;
                                }

                                var translation = GetTranslationDefaultMayNotBeEnglish(key, post[key]);
                                d.Add(key, translation);
                            }
                            catch (Exception error)
                            {
                                Debug.Fail("Debug Only:" + error.Message + Environment.NewLine + "A bug reported at this location is BL-923");
                                //Until BL-923 is fixed (hard... it's a race condition, it's better to swallow this for users
                            }
                        }
                    }

                    info.ContentType = "application/json";
                    info.WriteCompleteOutput(JsonConvert.SerializeObject(d));
                    return(true);
                }
                finally
                {
                    _localizing = false;
                }
                break;

            case "translate":
                var    parameters  = info.GetQueryParameters();
                string id          = parameters["key"];
                string englishText = parameters["englishText"];
                string langId      = parameters["langId"];
                langId = langId.Replace("V", currentCollectionSettings.Language1Iso639Code);
                langId = langId.Replace("N1", currentCollectionSettings.Language2Iso639Code);
                langId = langId.Replace("N2", currentCollectionSettings.Language3Iso639Code);
                langId = langId.Replace("UI", LocalizationManager.UILanguageId);
                if (LocalizationManager.GetIsStringAvailableForLangId(id, langId))
                {
                    info.ContentType = "text/plain";
                    info.WriteCompleteOutput(LocalizationManager.GetDynamicStringOrEnglish("Bloom", id, englishText, null, langId));
                    return(true);
                }
                else
                {
                    // Don't report missing strings if they are numbers
                    // Enhance: We might get the Javascript to do locale specific numbers someday
                    // The C# side doesn't currently have the smarts to do DigitSubstitution
                    // See Remark at https://msdn.microsoft.com/en-us/library/system.globalization.numberformatinfo.digitsubstitution(v=vs.110).aspx
                    if (IsInteger(id))
                    {
                        englishText = id;
                    }
                    else
                    {
                        // Now that end users can create templates, it's annoying to report that their names,
                        // page labels, and page descriptions don't have localizations.
                        if (IsTemplateBookKey(id))
                        {
                            englishText = englishText.Trim();
                        }
                        else
                        {
                            // it's ok if we don't have a translation, but if the string isn't even in the list of things that need translating,
                            // then we want to remind the developer to add it to the english xlf file.
                            if (!LocalizationManager.GetIsStringAvailableForLangId(id, "en"))
                            {
                                ReportL10NMissingString(id, englishText, UrlPathString.CreateFromUrlEncodedString(parameters["comment"] ?? "").NotEncoded);
                            }
                            else
                            {
                                //ok, so we don't have it translated yet. Make sure it's at least listed in the things that can be translated.
                                // And return the English string, which is what we would do the next time anyway.  (BL-3374)
                                LocalizationManager.GetDynamicString("Bloom", id, englishText);
                            }
                        }
                    }
                    info.ContentType = "text/plain";
                    info.WriteCompleteOutput(englishText);
                    return(true);
                }
                break;
            }

            return(false);
        }
Beispiel #18
0
        public static bool HandleRequest(string localPath, IRequestInfo info, CollectionSettings currentCollectionSettings)
        {
            var lastSep = localPath.IndexOf("/", System.StringComparison.Ordinal);
            var lastSegment = (lastSep > -1) ? localPath.Substring(lastSep + 1) : localPath;

            switch (lastSegment)
            {
                case "loadStrings":

                    while (_localizing)
                    {
                        Thread.Sleep(0);
                    }

                    try
                    {
                        _localizing = true;

                        var d = new Dictionary<string, string>();
                        var post = info.GetPostDataWhenFormEncoded();

                        if (post != null)
                        {
                            foreach (string key in post.Keys)
                            {
                                try
                                {
                                    if (!d.ContainsKey(key))
                                    {
                                        var translation = GetTranslationDefaultMayNotBeEnglish(key, post[key]);
                                        d.Add(key, translation);
                                    }
                                }
                                catch (Exception error)
                                {
                                    Debug.Fail("Debug Only:" +error.Message+Environment.NewLine+"A bug reported at this location is BL-923");
                                    //Until BL-923 is fixed (hard... it's a race condition, it's better to swallow this for users
                                }
                            }
                        }

                        info.ContentType = "application/json";
                        info.WriteCompleteOutput(JsonConvert.SerializeObject(d));
                        return true;
                    }
                    finally
                    {
                        _localizing = false;
                    }
                    break;

                case "translate":
                    var parameters = info.GetQueryParameters();
                    string id = parameters["key"];
                    string englishText = parameters["englishText"];
                    string langId = parameters["langId"];
                    langId = langId.Replace("V", currentCollectionSettings.Language1Iso639Code);
                    langId = langId.Replace("N1", currentCollectionSettings.Language2Iso639Code);
                    langId = langId.Replace("N2", currentCollectionSettings.Language3Iso639Code);
                    langId = langId.Replace("UI", LocalizationManager.UILanguageId);
                    if (LocalizationManager.GetIsStringAvailableForLangId(id, langId))
                    {
                        info.ContentType = "text/plain";
                        info.WriteCompleteOutput(LocalizationManager.GetDynamicStringOrEnglish("Bloom", id, englishText, null, langId));
                        return true;
                    }
                    else
                    {
                        // Don't report missing strings if they are numbers
                        // Enhance: We might get the Javascript to do locale specific numbers someday
                        // The C# side doesn't currently have the smarts to do DigitSubstitution
                        // See Remark at https://msdn.microsoft.com/en-us/library/system.globalization.numberformatinfo.digitsubstitution(v=vs.110).aspx
                        if (IsInteger(id))
                        {
                            englishText = id;
                        }
                        else
                        {
                            // it's ok if we don't have a translation, but if the string isn't even in the list of things that need translating,
                            // then we want to remind the developer to add it to the english tmx file.
                            if (!LocalizationManager.GetIsStringAvailableForLangId(id, "en"))
                            {
                                ReportL10NMissingString(id, englishText);
                            }
                            else
                            {
                                //ok, so we don't have it translated yet. Make sure it's at least listed in the things that can be translated.
                                // And return the English string, which is what we would do the next time anyway.  (BL-3374)
                                LocalizationManager.GetDynamicString("Bloom", id, englishText);
                            }
                        }
                        info.ContentType = "text/plain";
                        info.WriteCompleteOutput(englishText);
                        return true;
                    }
                    break;
            }

            return false;
        }
Beispiel #19
0
        /// <summary>
        /// This is designed to be easily unit testable by not taking actual HttpContext, but doing everything through this IRequestInfo object
        /// </summary>
        /// <param name="info"></param>
        public void MakeReply(IRequestInfo info)
        {
            if(info.LocalPathWithoutQuery.EndsWith("testconnection"))
            {
                info.WriteCompleteOutput("OK");
                return;
            }

            var r = info.LocalPathWithoutQuery.Replace("/bloom/", "");
            r = r.Replace("%3A", ":");
            r = r.Replace("%20", " ");
            r = r.Replace("%27", "'");
            if (r.EndsWith(".png") || r.EndsWith(".jpg"))
            {
                info.ContentType = r.EndsWith(".png") ? "image/png" : "image/jpeg";

                r = r.Replace("thumbnail", "");
                //if (r.Contains("thumb"))
                {
                    if (File.Exists(r))
                    {
                        info.ReplyWithImage(_cache.GetPathToResizedImage(r));
                    }
                    else
                    {
                        Logger.WriteEvent("**ImageServer: File Missing: "+r);
                        info.WriteError(404);
                    }
                }

            }
        }
Beispiel #20
0
        protected override bool ProcessRequest(IRequestInfo info)
        {
            if (base.ProcessRequest(info))
                return true;

            var r = CorrectedLocalPath(info);
            const string slashBloomSlash = "/bloom/";
            if (r.StartsWith(slashBloomSlash))
                r = r.Substring(slashBloomSlash.Length);
            r = r.Replace("library/", "");
            if (r.Contains("libraryContents"))
            {
                GetLibraryBooks(info);
            }
            else if (r == "libraryName")
            {
                info.WriteCompleteOutput(_collectionSettings.CollectionName + " Library");
            }
            else if (r.Contains("SourceCollectionsList"))
            {
                GetStoreBooks(info);
            }
            else if(r.StartsWith("thumbnails/"))
            {
                r = r.Replace("thumbnails/", "");
                r = r.Replace("%5C", "/");
                r = r.Replace("%20", " ");
                if (File.Exists(r))
                {
                    info.ReplyWithImage(r);
                }
            }
            else if (r.EndsWith(".png") && r.Contains("thumbnail"))
            {
                r = r.Replace("thumbnail", "");
                //if (r.Contains("thumb"))
                {
                    if (File.Exists(r))
                    {
                        info.ReplyWithImage(r);
                    }
                    else
                    {
                        var imgPath = BloomFileLocator.GetBrowserFile("book.png");
                        info.ReplyWithImage(imgPath);
                        //book.GetThumbNailOfBookCoverAsync(book.Type != Book.Book.BookType.Publication,image => RefreshOneThumbnail(book, image));
                    }
                }
            //				else
            //				{
            //					var imgPath = FileLocator.GetFileDistributedWithApplication("root", "ui", "book.png");
            //					info.ReplyWithImage(imgPath);
            //				}
            }
            else
            {
                info.ContentType = GetContentType(Path.GetExtension(r));
                string path = BloomFileLocator.GetBrowserFile(r);

                //request.QueryString.GetValues()
                info.ReplyWithFileContent(path);
            }
            return true;
        }
Beispiel #21
0
 private void GetLibraryBooks(IRequestInfo info)
 {
     var books = _booksInProjectLibrary.GetBookInfos();
     info.WriteCompleteOutput(GetBookListItems(books));
 }
        private static bool GetTopicList(IRequestInfo info)
        {
            var keyToLocalizedTopicDictionary = new Dictionary<string, string>();
            foreach (var topic in BookInfo.TopicsKeys)
            {
                var localized = LocalizationManager.GetDynamicString("Bloom", "Topics." + topic, topic,
                    @"shows in the topics chooser in the edit tab");
                keyToLocalizedTopicDictionary.Add(topic, localized);
            }
            string localizedNoTopic = LocalizationManager.GetDynamicString("Bloom", "Topics.NoTopic", "No Topic",
                @"shows in the topics chooser in the edit tab");
            var arrayOfKeyValuePairs = from key in keyToLocalizedTopicDictionary.Keys
                orderby keyToLocalizedTopicDictionary[key]
                select string.Format("\"{0}\": \"{1}\"",key,keyToLocalizedTopicDictionary[key]);
            var pairs = arrayOfKeyValuePairs.Concat(",");
            info.ContentType = "application/json";
            var data = string.Format("{{\"NoTopic\": \"{0}\", {1} }}", localizedNoTopic, pairs);

            info.WriteCompleteOutput(data);
            /*			var data = new {NoTopic = localizedNoTopic, pairs = arrayOfKeyValuePairs};
             * var serializeObject = JsonConvert.SerializeObject(data, new JsonSerializerSettings
                        {
                            TypeNameHandling = TypeNameHandling.None,
                            TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple,
                        });
                        */
            //info.WriteCompleteOutput(serializeObject);

            return true;
        }
Beispiel #23
0
        private void GetStoreBooks(IRequestInfo info)
        {
            //enhance: it will eventually work better to do sorting client-side, according to user's current prefs
            var reply = new StringBuilder();
            var list = new List<BookCollection>();
            list.AddRange(_sourceCollectionsesList.GetSourceCollections());

            list.Sort(CompareBookCollections);

            foreach (BookCollection collection in list)
            {
                reply.AppendFormat("<li class='collectionGroup'><h2>{0}</h2><ul class='collection'>", collection.Name);
                reply.Append(GetBookListItems(collection.GetBookInfos()));
                reply.AppendFormat(@"</ul></li>");
            }
            info.WriteCompleteOutput(reply.ToString());
        }
        private bool CheckForSampleTextChanges(IRequestInfo info)
        {
            lock (SyncObj)
            {
                if (_sampleTextsWatcher == null)
                {
                    var path = Path.Combine(Path.GetDirectoryName(CurrentCollectionSettings.SettingsFilePath), "Sample Texts");
                    if (!Directory.Exists(path))
                        Directory.CreateDirectory(path);

                    _sampleTextsWatcher = new FileSystemWatcher { Path = path };
                    _sampleTextsWatcher.Created += SampleTextsOnChange;
                    _sampleTextsWatcher.Changed += SampleTextsOnChange;
                    _sampleTextsWatcher.Renamed += SampleTextsOnChange;
                    _sampleTextsWatcher.Deleted += SampleTextsOnChange;
                    _sampleTextsWatcher.EnableRaisingEvents = true;
                }
            }

            lock (_sampleTextsWatcher)
            {
                var hasChanged = _sampleTextsChanged;

                // Reset the changed flag.
                // NOTE: we are only resetting the flag if it was "true" when we checked in case the FileSystemWatcher detects a change
                // after we check the flag but we reset it to false before we check again.
                if (hasChanged)
                    _sampleTextsChanged = false;

                info.ContentType = "text/plain";
                info.WriteCompleteOutput(hasChanged ? "yes" : "no");

                return true;
            }
        }
        public static bool HandleRequest(string localPath, IRequestInfo info, CollectionSettings currentCollectionSettings)
        {
            var lastSep     = localPath.IndexOf("/", StringComparison.Ordinal);
            var lastSegment = (lastSep > -1) ? localPath.Substring(lastSep + 1) : localPath;

            switch (lastSegment)
            {
            case "loadReaderToolSettings":
                info.ContentType = "application/json";
                info.WriteCompleteOutput(GetDefaultReaderSettings(currentCollectionSettings));
                return(true);

            case "saveReaderToolSettings":
                var path    = currentCollectionSettings.DecodableLevelPathName;
                var content = info.GetPostData()["data"];
                File.WriteAllText(path, content, Encoding.UTF8);
                info.ContentType = "text/plain";
                info.WriteCompleteOutput("OK");
                return(true);

            case "getDefaultFont":
                var bookFontName = currentCollectionSettings.DefaultLanguage1FontName;
                if (string.IsNullOrEmpty(bookFontName))
                {
                    bookFontName = "sans-serif";
                }
                info.ContentType = "text/plain";
                info.WriteCompleteOutput(bookFontName);
                return(true);

            case "getSampleTextsList":
                info.ContentType = "text/plain";
                info.WriteCompleteOutput(GetSampleTextsList(currentCollectionSettings.SettingsFilePath));
                return(true);

            case "getSampleFileContents":
                var fileName = info.GetQueryString()["data"];
                info.ContentType = "text/plain";
                info.WriteCompleteOutput(GetSampleFileContents(fileName, currentCollectionSettings.SettingsFilePath));
                return(true);

            case "getTextOfPages":
                info.ContentType = "text/plain";
                info.WriteCompleteOutput(GetTextOfPages());
                return(true);

            case "saveReaderToolsWords":
                info.ContentType = "text/plain";
                info.WriteCompleteOutput(SaveReaderToolsWordsFile(info.GetPostData()["data"]));
                return(true);

            case "makeLetterAndWordList":
                MakeLetterAndWordList(info.GetPostData()["settings"], info.GetPostData()["allWords"]);
                info.ContentType = "text/plain";
                info.WriteCompleteOutput("OK");
                return(true);

            case "openTextsFolder":
                OpenTextsFolder();
                info.ContentType = "text/plain";
                info.WriteCompleteOutput("OK");
                return(true);
            }

            return(false);
        }
Beispiel #26
0
        /// <summary>
        /// This method is overridden in classes inheriting from this class to handle specific request types
        /// </summary>
        /// <param name="info"></param>
        /// <returns></returns>
        protected virtual bool ProcessRequest(IRequestInfo info)
        {
            #if MEMORYCHECK
            // Check memory for the benefit of developers.  (Also see all requests as a side benefit.)
            var debugMsg = String.Format("ServerBase.ProcessRequest(\"{0}\"", info.RawUrl);
            SIL.Windows.Forms.Reporting.MemoryManagement.CheckMemory(true, debugMsg, false);
            #endif
            // process request for directory index
            var requestedPath = GetLocalPathWithoutQuery(info);
            if (info.RawUrl.EndsWith("/") && (Directory.Exists(requestedPath)))
            {
                info.WriteError(403, "Directory listing denied");
                return true;
            }

            if (requestedPath.EndsWith("testconnection"))
            {
                info.WriteCompleteOutput("OK");
                return true;
            }
            return false;
        }
Beispiel #27
0
        protected override bool ProcessRequest(IRequestInfo info)
        {
            if (base.ProcessRequest(info))
            {
                return(true);
            }

            var          r = CorrectedLocalPath(info);
            const string slashBloomSlash = "/bloom/";

            if (r.StartsWith(slashBloomSlash))
            {
                r = r.Substring(slashBloomSlash.Length);
            }
            r = r.Replace("library/", "");
            if (r.Contains("libraryContents"))
            {
                GetLibraryBooks(info);
            }
            else if (r == "libraryName")
            {
                info.WriteCompleteOutput(_collectionSettings.CollectionName + " Library");
            }
            else if (r.Contains("SourceCollectionsList"))
            {
                GetStoreBooks(info);
            }
            else if (r.StartsWith("thumbnails/"))
            {
                r = r.Replace("thumbnails/", "");
                r = r.Replace("%5C", "/");
                r = r.Replace("%20", " ");
                if (File.Exists(r))
                {
                    info.ReplyWithImage(r);
                }
            }
            else if (r.EndsWith(".png") && r.Contains("thumbnail"))
            {
                r = r.Replace("thumbnail", "");
                //if (r.Contains("thumb"))
                {
                    if (File.Exists(r))
                    {
                        info.ReplyWithImage(r);
                    }
                    else
                    {
                        var imgPath = BloomFileLocator.GetBrowserFile("book.png");
                        info.ReplyWithImage(imgPath);
                        //book.GetThumbNailOfBookCoverAsync(book.Type != Book.Book.BookType.Publication,image => RefreshOneThumbnail(book, image));
                    }
                }
//				else
//				{
//					var imgPath = FileLocator.GetFileDistributedWithApplication("root", "ui", "book.png");
//					info.ReplyWithImage(imgPath);
//				}
            }
            else
            {
                info.ContentType = GetContentType(Path.GetExtension(r));
                string path = BloomFileLocator.GetBrowserFile(r);


                //request.QueryString.GetValues()
                info.ReplyWithFileContent(path);
            }
            return(true);
        }
        // Every path should return false or send a response.
        // Otherwise we can get a timeout error as the browser waits for a response.
        //
        // NOTE: this method gets called on different threads!
        protected override bool ProcessRequest(IRequestInfo info)
        {
            var localPath = GetLocalPathWithoutQuery(info);

            //enhance: something feeds back these branding logos with a weird URL, that shouldn't be.
            if(localPath.IndexOf("api/branding") > 20) // this 20 is just arbitrary... the point is, if it doesn't start with api/branding, it is bogus
            {
                return false;
            }

            if (localPath.ToLower().StartsWith("api/"))
            {
                var endpoint = localPath.Substring(3).ToLowerInvariant().Trim(new char[] {'/'});
                foreach (var pair in _endpointRegistrations.Where(pair =>
                        Regex.Match(endpoint,
                            "^" + //must match the beginning
                            pair.Key.ToLower()
                        ).Success))
                {
                    lock(SyncObj)
                    {
                        return ApiRequest.Handle(pair.Value, info, CurrentCollectionSettings, _bookSelection.CurrentSelection);
                    }
                }
            }

            //OK, no more obvious simple API requests, dive into the rat's nest of other possibilities
            if (base.ProcessRequest(info))
                return true;

            if(localPath.Contains("CURRENTPAGE")) //useful when debugging. E.g. http://localhost:8091/bloom/CURRENTPAGE.htm will always show the page we're on.
            {
                localPath = _keyToCurrentPage;
            }

            string content;
            bool gotSimulatedPage;
            lock (_urlToSimulatedPageContent)
            {
                gotSimulatedPage = _urlToSimulatedPageContent.TryGetValue(localPath, out content);
            }
            if (gotSimulatedPage)
            {
                info.ContentType = "text/html";
                info.WriteCompleteOutput(content ?? "");
                return true;
            }

            if (localPath.StartsWith(OriginalImageMarker) && IsImageTypeThatCanBeDegraded(localPath))
            {
                // Path relative to simulated page file, and we want the file contents without modification.
                // (Note that the simulated page file's own URL starts with this, so it's important to check
                // for that BEFORE we do this check.)
                localPath = localPath.Substring(OriginalImageMarker.Length + 1);
                return ProcessAnyFileContent(info, localPath);
            }

            if (localPath.StartsWith("error", StringComparison.InvariantCulture))
            {
                ProcessError(info);
                return true;
            }
            else if (localPath.StartsWith("i18n/", StringComparison.InvariantCulture))
            {
                if (ProcessI18N(localPath, info))
                    return true;
            }
            else if (localPath.StartsWith("directoryWatcher/", StringComparison.InvariantCulture))
                return ProcessDirectoryWatcher(info);
            else if (localPath.StartsWith("localhost/", StringComparison.InvariantCulture))
            {
                var temp = LocalHostPathToFilePath(localPath);
                if (RobustFile.Exists(temp))
                    localPath = temp;
            }
            // this is used only by the readium viewer
            else if (localPath.StartsWith("node_modules/jquery/dist/jquery.js"))
            {
                localPath = BloomFileLocator.GetBrowserFile("jquery.min.js");
                // Avoid having "output/browser/" removed on Linux developer machines.
                // GetBrowserFile adds output to the path on developer machines, but not user installs.
                return ProcessContent(info, localPath);
            }
            //Firefox debugger, looking for a source map, was prefixing in this unexpected
            //way.
            localPath = localPath.Replace("output/browser/", "");

            return ProcessContent(info, localPath);
        }