From 244de5d2c10a990828eafdd72283fc87742dc133 Mon Sep 17 00:00:00 2001 From: Tommaso Cucinotta Date: Sat, 5 Nov 2016 01:00:44 +0100 Subject: [PATCH] Add 'needauth' option to converters that need explicit user authorization. Addressing #10481. This patch adds the new 'needauth' option for converters launching external programs that are capable of running arbitrary code on behalf of the user. These converters won't be run unless the user gives explicit authorization, which is asked on-demand when the converter is about to be run (question is not asked if the file is cached and calling the converter is not needed). The user prompt has a 3rd button so that he/she's not prompted again for (any converter over) the same document (identified through buffer->absFileName()). Two preference options are added: lyxrc.use_converter_needauth_forbidden disables any converter with the 'needauth' option, which is meant to force user to an explicit action via the preferences pane, before being able to use advanced converters that can potentially bring security threats; lyxrc.use_converter_needauth enables prompting the user for 'needauth' converters, or bypasses the check if not enabled, falling back to the previous behavior. So, the first option is for maximum security, the second is for maximum usability. --- lib/configure.py | 28 +++++------ src/Buffer.cpp | 5 +- src/Buffer.h | 3 ++ src/Converter.cpp | 51 +++++++++++++++++++- src/Converter.h | 17 +++++++ src/LyXRC.cpp | 38 +++++++++++++++ src/LyXRC.h | 8 ++++ src/factory.cpp | 2 +- src/frontends/qt4/GuiExternal.cpp | 1 + src/frontends/qt4/GuiPrefs.cpp | 14 ++++++ src/frontends/qt4/GuiPrefs.h | 1 + src/frontends/qt4/ui/PrefConvertersUi.ui | 45 +++++++++++++++++ src/graphics/GraphicsCache.cpp | 4 +- src/graphics/GraphicsCache.h | 2 +- src/graphics/GraphicsCacheItem.cpp | 24 +++++----- src/graphics/GraphicsCacheItem.h | 2 +- src/graphics/GraphicsConverter.cpp | 31 ++++++++---- src/graphics/GraphicsConverter.h | 3 +- src/graphics/GraphicsLoader.cpp | 38 ++++++++++----- src/graphics/GraphicsLoader.h | 12 +++-- src/graphics/PreviewImage.cpp | 8 +++- src/graphics/PreviewImage.h | 3 ++ src/graphics/PreviewLoader.cpp | 61 ++++++++++++------------ src/insets/RenderGraphic.cpp | 2 + 24 files changed, 313 insertions(+), 90 deletions(-) diff --git a/lib/configure.py b/lib/configure.py index a11050fb91..b282a284ed 100644 --- a/lib/configure.py +++ b/lib/configure.py @@ -749,8 +749,8 @@ def checkConverterEntries(): rc_entry = [r'''\converter latex lyx "%% -f $$i $$o" "" \converter latexclipboard lyx "%% -fixedenc utf8 -f $$i $$o" "" \converter literate lyx "%% -n -m noweb -f $$i $$o" "" -\converter sweave lyx "%% -n -m sweave -f $$i $$o" "" -\converter knitr lyx "%% -n -m knitr -f $$i $$o" ""'''], not_found = 'tex2lyx') +\converter sweave lyx "%% -n -m sweave -f $$i $$o" "needauth" +\converter knitr lyx "%% -n -m knitr -f $$i $$o" "needauth"'''], not_found = 'tex2lyx') if path == '': logger.warning("Failed to find tex2lyx on your system.") @@ -763,24 +763,24 @@ def checkConverterEntries(): \converter literate dviluatex "%%" ""''']) # checkProg('a Sweave -> LaTeX converter', ['Rscript --verbose --no-save --no-restore $$s/scripts/lyxsweave.R $$p$$i $$p$$o $$e $$r'], - rc_entry = [r'''\converter sweave latex "%%" "" -\converter sweave pdflatex "%%" "" -\converter sweave xetex "%%" "" -\converter sweave luatex "%%" "" -\converter sweave dviluatex "%%" ""''']) + rc_entry = [r'''\converter sweave latex "%%" "needauth" +\converter sweave pdflatex "%%" "needauth" +\converter sweave xetex "%%" "needauth" +\converter sweave luatex "%%" "needauth" +\converter sweave dviluatex "%%" "needauth"''']) # checkProg('a knitr -> LaTeX converter', ['Rscript --verbose --no-save --no-restore $$s/scripts/lyxknitr.R $$p$$i $$p$$o $$e $$r'], - rc_entry = [r'''\converter knitr latex "%%" "" -\converter knitr pdflatex "%%" "" -\converter knitr xetex "%%" "" -\converter knitr luatex "%%" "" -\converter knitr dviluatex "%%" ""''']) + rc_entry = [r'''\converter knitr latex "%%" "needauth" +\converter knitr pdflatex "%%" "needauth" +\converter knitr xetex "%%" "needauth" +\converter knitr luatex "%%" "needauth" +\converter knitr dviluatex "%%" "needauth"''']) # checkProg('a Sweave -> R/S code converter', ['Rscript --verbose --no-save --no-restore $$s/scripts/lyxstangle.R $$i $$e $$r'], - rc_entry = [ r'\converter sweave r "%%" ""' ]) + rc_entry = [ r'\converter sweave r "%%" "needauth"' ]) # checkProg('a knitr -> R/S code converter', ['Rscript --verbose --no-save --no-restore $$s/scripts/lyxknitr.R $$p$$i $$p$$o $$e $$r tangle'], - rc_entry = [ r'\converter knitr r "%%" ""' ]) + rc_entry = [ r'\converter knitr r "%%" "needauth"' ]) # checkProg('an HTML -> LaTeX converter', ['html2latex $$i', 'gnuhtml2latex', 'htmltolatex -input $$i -output $$o', 'htmltolatex.jar -input $$i -output $$o'], diff --git a/src/Buffer.cpp b/src/Buffer.cpp index 8efa53bb4c..4facf09479 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -79,6 +79,7 @@ #include "mathed/MathMacroTemplate.h" #include "mathed/MathSupport.h" +#include "graphics/GraphicsCache.h" #include "graphics/PreviewLoader.h" #include "frontends/alert.h" @@ -421,8 +422,8 @@ Buffer::Impl::Impl(Buffer * owner, FileName const & file, bool readonly_, ignore_parent(false), toc_backend(owner), macro_lock(false), timestamp_(0), checksum_(0), wa_(0), gui_(0), undo_(*owner), bibinfo_cache_valid_(false), bibfile_cache_valid_(false), cite_labels_valid_(false), preview_error_(false), - inset(0), preview_loader_(0), cloned_buffer_(cloned_buffer), clone_list_(0), - doing_export(false), parent_buffer(0), + inset(0), preview_loader_(0), cloned_buffer_(cloned_buffer), + clone_list_(0), doing_export(false), parent_buffer(0), word_count_(0), char_count_(0), blank_count_(0) { if (!cloned_buffer_) { diff --git a/src/Buffer.h b/src/Buffer.h index 477a8ac8ad..0cb7026637 100644 --- a/src/Buffer.h +++ b/src/Buffer.h @@ -74,6 +74,7 @@ class FileNameList; namespace graphics { class PreviewLoader; +class Cache; } @@ -587,6 +588,8 @@ public: void updatePreviews() const; /// Remove any previewed LaTeX snippets associated with this buffer void removePreviews() const; + /// + graphics::Cache & graphicsCache() const; /// Our main text (inside the top InsetText) Text & text() const; diff --git a/src/Converter.cpp b/src/Converter.cpp index 58e486e67d..a2e5e9be49 100644 --- a/src/Converter.cpp +++ b/src/Converter.cpp @@ -21,6 +21,7 @@ #include "Format.h" #include "Language.h" #include "LaTeX.h" +#include "LyXRC.h" #include "Mover.h" #include "frontends/alert.h" @@ -100,7 +101,7 @@ Converter::Converter(string const & f, string const & t, string const & c, string const & l) : from_(f), to_(t), command_(c), flags_(l), From_(0), To_(0), latex_(false), xml_(false), - need_aux_(false), nice_(false) + need_aux_(false), nice_(false), need_auth_(false) {} @@ -128,6 +129,8 @@ void Converter::readFlags() parselog_ = flag_value; else if (flag_name == "nice") nice_ = true; + else if (flag_name == "needauth") + need_auth_ = true; } if (!result_dir_.empty() && result_file_.empty()) result_file_ = "index." + formats.extension(to_); @@ -275,6 +278,49 @@ OutputParams::FLAVOR Converters::getFlavor(Graph::EdgePath const & path, } +bool Converters::checkAuth(Converter const & conv, string const & doc_fname) +{ + if (!conv.need_auth()) + return true; + if (lyxrc.use_converter_needauth_forbidden) { + frontend::Alert::warning( + _("Potentially harmful external converters disabled"), + _("Requested operation needs use of a potentially harmful external converter program," + "which is forbidden by default.\nThese converters are tagged by the 'needauth' option. " + "In order to unlock execution of these converters,\nplease, go to " + "Preferences->File Handling->Converters and uncheck " + "Security->Forbid needauth converters."), true); + return false; + } + if (!lyxrc.use_converter_needauth) + return true; + static const docstring security_title = _("Launch of external converter needs user authorization"); + static const char security_warning[] = "LyX is about to run converter '%1$s' which is launching an external program " + "that normally acts as a picture/format converter. However, this external program is known to be able to " + "execute arbitrary actions on the system on behalf of the user, including dangerous ones such as deleting " + "files, if instructed to do so by a maliciously crafted .lyx document.\n\nWould you like to run the converter?\n\n" + "ANSWER RUN ONLY IF YOU TRUST THE ORIGIN/SENDER OF THE LYX DOCUMENT!"; + int choice; + if (!doc_fname.empty()) { + LYXERR(Debug::FILES, "looking up: " << doc_fname); + if (auth_files_.find(doc_fname) == auth_files_.end()) { + choice = frontend::Alert::prompt(security_title, + bformat(_(security_warning), from_utf8(conv.command())), + 0, 0, _("Do &NOT run"), _("&Run"), _("&Always run for this document")); + if (choice == 2) + auth_files_.insert(doc_fname); + } else { + choice = 1; + } + } else { + choice = frontend::Alert::prompt(security_title, + bformat(_(security_warning), from_utf8(conv.command())), + 0, 0, _("Do &NOT run"), _("&Run")); + } + return choice != 0; +} + + bool Converters::convert(Buffer const * buffer, FileName const & from_file, FileName const & to_file, FileName const & orig_from, @@ -402,6 +448,9 @@ bool Converters::convert(Buffer const * buffer, "tmpfile.out")); } + if (!checkAuth(conv, buffer->absFileName())) + return false; + if (conv.latex()) { run_latex = true; string command = conv.command(); diff --git a/src/Converter.h b/src/Converter.h index c9c44238ef..10f88fec88 100644 --- a/src/Converter.h +++ b/src/Converter.h @@ -69,6 +69,8 @@ public: bool xml() const { return xml_; } /// bool need_aux() const { return need_aux_; } + /// Return whether or not the needauth option is set for this converter + bool need_auth() const { return need_auth_; } /// bool nice() const { return nice_; } /// @@ -77,6 +79,7 @@ public: std::string const result_file() const { return result_file_; } /// std::string const parselog() const { return parselog_; } + private: /// trivstring from_; @@ -101,6 +104,8 @@ private: bool need_aux_; /// we need a "nice" file from the backend, c.f. OutputParams.nice. bool nice_; + /// Use of this converter needs explicit user authorization + bool need_auth_; /// If the converter put the result in a directory, then result_dir /// is the name of the directory trivstring result_dir_; @@ -181,6 +186,16 @@ public: const_iterator end() const { return converterlist_.end(); } /// void buildGraph(); + + /// Check whether converter conv is authorized to be run for elements + /// within document doc_fname. + /// The check succeeds for safe converters, whilst for those potentially + /// able to execute arbitrary code, tagged with the 'needauth' option, + /// authorization is: always denied if lyxrc.use_converter_needauth_forbidden + /// is enabled; always allowed if the lyxrc.use_converter_needauth + /// is disabled; user is prompted otherwise + bool checkAuth(Converter const & conv, std::string const & doc_fname); + private: /// FormatList const @@ -210,6 +225,8 @@ private: bool copy); /// Graph G_; + /// set of document files authorized for external conversion + std::set auth_files_; }; /// The global instance. diff --git a/src/LyXRC.cpp b/src/LyXRC.cpp index fe3b042348..355e065119 100644 --- a/src/LyXRC.cpp +++ b/src/LyXRC.cpp @@ -189,6 +189,8 @@ LexerKeyword lyxrcTags[] = { { "\\thesaurusdir_path", LyXRC::RC_THESAURUSDIRPATH }, { "\\ui_file", LyXRC::RC_UIFILE }, { "\\use_converter_cache", LyXRC::RC_USE_CONVERTER_CACHE }, + { "\\use_converter_needauth", LyXRC::RC_USE_CONVERTER_NEEDAUTH }, + { "\\use_converter_needauth_forbidden", LyXRC::RC_USE_CONVERTER_NEEDAUTH_FORBIDDEN }, { "\\use_lastfilepos", LyXRC::RC_USELASTFILEPOS }, { "\\use_pixmap_cache", LyXRC::RC_USE_PIXMAP_CACHE }, { "\\use_qimage", LyXRC::RC_USE_QIMAGE }, @@ -316,6 +318,8 @@ void LyXRC::setDefaults() preview_hashed_labels = false; preview_scale_factor = 1.0; use_converter_cache = true; + use_converter_needauth_forbidden = true; + use_converter_needauth = true; use_system_colors = false; use_tooltip = true; use_pixmap_cache = false; @@ -1112,6 +1116,12 @@ LyXRC::ReturnValues LyXRC::read(Lexer & lexrc, bool check_format) case RC_USE_CONVERTER_CACHE: lexrc >> use_converter_cache; break; + case RC_USE_CONVERTER_NEEDAUTH_FORBIDDEN: + lexrc >> use_converter_needauth_forbidden; + break; + case RC_USE_CONVERTER_NEEDAUTH: + lexrc >> use_converter_needauth; + break; case RC_CONVERTER_CACHE_MAXAGE: lexrc >> converter_cache_maxage; break; @@ -1593,6 +1603,24 @@ void LyXRC::write(ostream & os, bool ignore_system_lyxrc, string const & name) c if (tag != RC_LAST) break; + case RC_USE_CONVERTER_NEEDAUTH_FORBIDDEN: + if (ignore_system_lyxrc || + use_converter_needauth_forbidden != system_lyxrc.use_converter_needauth_forbidden) { + os << "\\use_converter_needauth_forbidden " + << convert(use_converter_needauth_forbidden) << '\n'; + } + if (tag != RC_LAST) + break; + + case RC_USE_CONVERTER_NEEDAUTH: + if (ignore_system_lyxrc || + use_converter_needauth != system_lyxrc.use_converter_needauth) { + os << "\\use_converter_needauth " + << convert(use_converter_needauth) << '\n'; + } + if (tag != RC_LAST) + break; + case RC_CONVERTER_CACHE_MAXAGE: if (ignore_system_lyxrc || converter_cache_maxage != system_lyxrc.converter_cache_maxage) { @@ -2833,6 +2861,8 @@ void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new) case LyXRC::RC_USER_EMAIL: case LyXRC::RC_USER_NAME: case LyXRC::RC_USE_CONVERTER_CACHE: + case LyXRC::RC_USE_CONVERTER_NEEDAUTH_FORBIDDEN: + case LyXRC::RC_USE_CONVERTER_NEEDAUTH: case LyXRC::RC_USE_SYSTEM_COLORS: case LyXRC::RC_USE_TOOLTIP: case LyXRC::RC_USE_PIXMAP_CACHE: @@ -2925,6 +2955,14 @@ string const LyXRC::getDescription(LyXRCTags tag) case RC_CONVERTER: break; + case RC_CONVERTER_NEEDAUTH_FORBIDDEN: + str = _("Forbid use of external converters with 'needauth' option to prevent undesired effects."); + break; + + case RC_CONVERTER_NEEDAUTH: + str = _("Ask user before calling external converters with 'needauth' option to prevent undesired effects."); + break; + case RC_COPIER: break; diff --git a/src/LyXRC.h b/src/LyXRC.h index 7b50ce0a56..010e957153 100644 --- a/src/LyXRC.h +++ b/src/LyXRC.h @@ -167,6 +167,8 @@ public: RC_USER_EMAIL, RC_USER_NAME, RC_USE_CONVERTER_CACHE, + RC_USE_CONVERTER_NEEDAUTH_FORBIDDEN, + RC_USE_CONVERTER_NEEDAUTH, RC_USE_SYSTEM_COLORS, RC_USE_TOOLTIP, RC_USE_PIXMAP_CACHE, @@ -443,6 +445,12 @@ public: std::string texinputs_prefix; /// Use the cache for file converters? bool use_converter_cache; + /// Forbid use of external converters with 'needauth' option + bool use_converter_needauth_forbidden; + /// Ask user before calling external converters with 'needauth' option + bool use_converter_needauth; + /// Apply hardening when calling external converters + bool use_converter_wrappers; /// The maximum age of cache files in seconds unsigned int converter_cache_maxage; /// Sort layouts alphabetically diff --git a/src/factory.cpp b/src/factory.cpp index a03f88fa28..9fdc37da62 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -591,7 +591,7 @@ Inset * readInset(Lexer & lex, Buffer * buf) return 0; } inset->setBuffer(*buf); - } else { + } else { // FIXME This branch should be made to use inset codes // as the preceding branch does. Unfortunately, that // will take some doing. It requires converting the diff --git a/src/frontends/qt4/GuiExternal.cpp b/src/frontends/qt4/GuiExternal.cpp index 2e7b6f633c..e031f53eb1 100644 --- a/src/frontends/qt4/GuiExternal.cpp +++ b/src/frontends/qt4/GuiExternal.cpp @@ -14,6 +14,7 @@ #include "GuiExternal.h" +#include "Buffer.h" #include "FuncRequest.h" #include "support/gettext.h" #include "Length.h" diff --git a/src/frontends/qt4/GuiPrefs.cpp b/src/frontends/qt4/GuiPrefs.cpp index dd59dbfe64..ee43b4fdab 100644 --- a/src/frontends/qt4/GuiPrefs.cpp +++ b/src/frontends/qt4/GuiPrefs.cpp @@ -1610,6 +1610,10 @@ PrefConverters::PrefConverters(GuiPreferences * form) this, SIGNAL(changed())); connect(maxAgeLE, SIGNAL(textEdited(QString)), this, SIGNAL(changed())); + connect(needauthForbiddenCB, SIGNAL(toggled(bool)), + this, SIGNAL(changed())); + connect(needauthCB, SIGNAL(toggled(bool)), + this, SIGNAL(changed())); converterED->setValidator(new NoNewLineValidator(converterED)); converterFlagED->setValidator(new NoNewLineValidator(converterFlagED)); @@ -1621,6 +1625,8 @@ PrefConverters::PrefConverters(GuiPreferences * form) void PrefConverters::applyRC(LyXRC & rc) const { rc.use_converter_cache = cacheCB->isChecked(); + rc.use_converter_needauth_forbidden = needauthForbiddenCB->isChecked(); + rc.use_converter_needauth = needauthCB->isChecked(); rc.converter_cache_maxage = int(widgetToDouble(maxAgeLE) * 86400.0); } @@ -1628,6 +1634,8 @@ void PrefConverters::applyRC(LyXRC & rc) const void PrefConverters::updateRC(LyXRC const & rc) { cacheCB->setChecked(rc.use_converter_cache); + needauthForbiddenCB->setChecked(rc.use_converter_needauth_forbidden); + needauthCB->setChecked(rc.use_converter_needauth); QString max_age; doubleToWidget(maxAgeLE, (double(rc.converter_cache_maxage) / 86400.0), 'g', 6); updateGui(); @@ -1788,6 +1796,12 @@ void PrefConverters::on_cacheCB_stateChanged(int state) } +void PrefConverters::on_needauthForbiddenCB_toggled(bool checked) +{ + needauthCB->setEnabled(!checked); +} + + ///////////////////////////////////////////////////////////////////// // // FormatValidator diff --git a/src/frontends/qt4/GuiPrefs.h b/src/frontends/qt4/GuiPrefs.h index 1b37b323e3..612ffe6bd4 100644 --- a/src/frontends/qt4/GuiPrefs.h +++ b/src/frontends/qt4/GuiPrefs.h @@ -334,6 +334,7 @@ private Q_SLOTS: void removeConverter(); void changeConverter(); void on_cacheCB_stateChanged(int state); + void on_needauthForbiddenCB_toggled(bool); private: void updateButtons(); diff --git a/src/frontends/qt4/ui/PrefConvertersUi.ui b/src/frontends/qt4/ui/PrefConvertersUi.ui index 3fe6b9b8ce..7e4a1b3244 100644 --- a/src/frontends/qt4/ui/PrefConvertersUi.ui +++ b/src/frontends/qt4/ui/PrefConvertersUi.ui @@ -304,6 +304,51 @@ + + + + Security + + + + 9 + + + 6 + + + + + 0 + + + 6 + + + + + &Forbid use of needauth converters + + + When enabled, use of converters with the 'needauth' option is forbidden. + + + + + + + Use need&auth option + + + When enabled, ask user before launching any external converter with the 'needauth' option. + + + + + + + + diff --git a/src/graphics/GraphicsCache.cpp b/src/graphics/GraphicsCache.cpp index a51f2322fd..a3b8ebd3a9 100644 --- a/src/graphics/GraphicsCache.cpp +++ b/src/graphics/GraphicsCache.cpp @@ -101,7 +101,7 @@ vector const & Cache::loadableFormats() const } -void Cache::add(FileName const & file) const +void Cache::add(FileName const & file, FileName const & doc_file) const { // Is the file in the cache already? if (inCache(file)) { @@ -110,7 +110,7 @@ void Cache::add(FileName const & file) const return; } - pimpl_->cache[file] = ItemPtr(new CacheItem(file)); + pimpl_->cache[file] = ItemPtr(new CacheItem(file, doc_file)); } diff --git a/src/graphics/GraphicsCache.h b/src/graphics/GraphicsCache.h index 66cfc85bf6..0fd1dd0102 100644 --- a/src/graphics/GraphicsCache.h +++ b/src/graphics/GraphicsCache.h @@ -46,7 +46,7 @@ public: std::vector const & loadableFormats() const; /// Add a graphics file to the cache. - void add(support::FileName const & file) const; + void add(support::FileName const & file, support::FileName const & doc_file) const; /// Remove a file from the cache. void remove(support::FileName const & file) const; diff --git a/src/graphics/GraphicsCacheItem.cpp b/src/graphics/GraphicsCacheItem.cpp index ea59173eff..c698168dee 100644 --- a/src/graphics/GraphicsCacheItem.cpp +++ b/src/graphics/GraphicsCacheItem.cpp @@ -14,6 +14,7 @@ #include "GraphicsCacheItem.h" +#include "Buffer.h" #include "GraphicsCache.h" #include "GraphicsConverter.h" #include "GraphicsImage.h" @@ -42,7 +43,7 @@ class CacheItem::Impl : public boost::signals2::trackable { public: /// - Impl(FileName const & file); + Impl(FileName const & file, FileName const & doc_file); /** * If no file conversion is needed, then tryDisplayFormat() calls @@ -93,6 +94,8 @@ public: /// The filename we refer too. FileName const filename_; + /// The document filename this graphic item belongs to + FileName const & doc_file_; /// FileMonitor const monitor_; @@ -125,8 +128,8 @@ public: }; -CacheItem::CacheItem(FileName const & file) - : pimpl_(new Impl(file)) +CacheItem::CacheItem(FileName const & file, FileName const & doc_file) + : pimpl_(new Impl(file,doc_file)) {} @@ -204,8 +207,8 @@ boost::signals2::connection CacheItem::connect(slot_type const & slot) const //------------------------------ -CacheItem::Impl::Impl(FileName const & file) - : filename_(file), +CacheItem::Impl::Impl(FileName const & file, FileName const & doc_file) + : filename_(file), doc_file_(doc_file), monitor_(file, 2000), zipped_(false), remove_loaded_file_(false), @@ -312,11 +315,10 @@ bool CacheItem::Impl::loadImage() } -static string const findTargetFormat(string const & from) -{ - typedef vector FormatList; - FormatList const & formats = Cache::get().loadableFormats(); +typedef vector FormatList; +static string const findTargetFormat(FormatList const & formats, string const & from) +{ // There must be a format to load from. LASSERT(!formats.empty(), return string()); @@ -392,7 +394,7 @@ bool CacheItem::Impl::tryDisplayFormat(FileName & filename, string & from) LYXERR(Debug::GRAPHICS, "\tCould not determine file format."); } LYXERR(Debug::GRAPHICS, "\n\tThe file contains " << from << " format data."); - to_ = findTargetFormat(from); + to_ = findTargetFormat(Cache::get().loadableFormats(), from); if (from == to_) { // No conversion needed! @@ -438,7 +440,7 @@ void CacheItem::Impl::convertToDisplayFormat() // Connect a signal to this->imageConverted and pass this signal to // the graphics converter so that we can load the modified file // on completion of the conversion process. - converter_ = make_unique(filename, to_file_base.absFileName(), + converter_ = make_unique(doc_file_, filename, to_file_base.absFileName(), from, to_); converter_->connect(bind(&Impl::imageConverted, this, _1)); converter_->startConversion(); diff --git a/src/graphics/GraphicsCacheItem.h b/src/graphics/GraphicsCacheItem.h index 289b827a4f..ee2348d8fc 100644 --- a/src/graphics/GraphicsCacheItem.h +++ b/src/graphics/GraphicsCacheItem.h @@ -46,7 +46,7 @@ class Converter; class CacheItem { public: /// - CacheItem(support::FileName const & file); + CacheItem(support::FileName const & file, support::FileName const & doc_file); /// Needed for the pimpl ~CacheItem(); diff --git a/src/graphics/GraphicsConverter.cpp b/src/graphics/GraphicsConverter.cpp index 67b1580fd9..55ae24624b 100644 --- a/src/graphics/GraphicsConverter.cpp +++ b/src/graphics/GraphicsConverter.cpp @@ -12,9 +12,12 @@ #include "GraphicsConverter.h" +#include "Buffer.h" #include "Converter.h" #include "Format.h" +#include "LyXRC.h" +#include "frontends/alert.h" #include "support/lassert.h" #include "support/convert.h" #include "support/debug.h" @@ -40,7 +43,7 @@ namespace graphics { class Converter::Impl : public boost::signals2::trackable { public: /// - Impl(FileName const &, string const &, string const &, string const &); + Impl(FileName const &, FileName const &, string const &, string const &, string const &); /// void startConversion(); @@ -59,6 +62,8 @@ public: /// SignalType finishedConversion; + /// + FileName const & doc_fname_; /// string script_command_; /// @@ -79,9 +84,10 @@ bool Converter::isReachable(string const & from_format_name, } -Converter::Converter(FileName const & from_file, string const & to_file_base, +Converter::Converter(FileName const & doc_fname, + FileName const & from_file, string const & to_file_base, string const & from_format, string const & to_format) - : pimpl_(new Impl(from_file, to_file_base, from_format, to_format)) + : pimpl_(new Impl(doc_fname, from_file, to_file_base, from_format, to_format)) {} @@ -112,17 +118,20 @@ FileName const & Converter::convertedFile() const /** Build the conversion script. * The script is output to the stream \p script. */ -static void build_script(string const & from_file, string const & to_file_base, +static void build_script(string const & doc_fname, + string const & from_file, string const & to_file_base, string const & from_format, string const & to_format, ostream & script); -Converter::Impl::Impl(FileName const & from_file, string const & to_file_base, +Converter::Impl::Impl(FileName const & doc_fname, + FileName const & from_file, string const & to_file_base, string const & from_format, string const & to_format) - : valid_process_(false), finished_(false) + : doc_fname_(doc_fname), valid_process_(false), finished_(false) { LYXERR(Debug::GRAPHICS, "Converter c-tor:\n" - << "\tfrom_file: " << from_file + << "doc_fname: " << doc_fname + << "\n\tfrom_file: " << from_file << "\n\tto_file_base: " << to_file_base << "\n\tfrom_format: " << from_format << "\n\tto_format: " << to_format); @@ -134,7 +143,7 @@ Converter::Impl::Impl(FileName const & from_file, string const & to_file_base, // The conversion commands are stored in a stringstream ostringstream script; - build_script(from_file.toFilesystemEncoding(), + build_script(doc_fname_.absFileName(), from_file.toFilesystemEncoding(), to_file_.toFilesystemEncoding(), from_format, to_format, script); LYXERR(Debug::GRAPHICS, "\tConversion script:" @@ -263,7 +272,8 @@ static string const strip_digit(string const & format) } -static void build_script(string const & from_file, +static void build_script(string const & doc_fname, + string const & from_file, string const & to_file, string const & from_format, string const & to_format, @@ -372,6 +382,9 @@ static void build_script(string const & from_file, outfile = tempfile.name().toFilesystemEncoding(); } + if (!theConverters().checkAuth(conv, doc_fname)) + return; + // Store these names in the python script script << "infile = " << quoteName(infile, quote_python) << "\n" diff --git a/src/graphics/GraphicsConverter.h b/src/graphics/GraphicsConverter.h index 2bd0003790..d3d0b8155d 100644 --- a/src/graphics/GraphicsConverter.h +++ b/src/graphics/GraphicsConverter.h @@ -34,7 +34,8 @@ public: /** One Converter per conversion ensures that the (hidden) signal * is always connected to the expected slot. */ - Converter(support::FileName const & from_file, std::string const & to_file_base, + Converter(support::FileName const & doc_fname, + support::FileName const & from_file, std::string const & to_file_base, std::string const & from_format, std::string const & to_format); /// Needed for the pimpl diff --git a/src/graphics/GraphicsLoader.cpp b/src/graphics/GraphicsLoader.cpp index 36a6c06d2c..987a973139 100644 --- a/src/graphics/GraphicsLoader.cpp +++ b/src/graphics/GraphicsLoader.cpp @@ -18,6 +18,7 @@ #include "GraphicsCache.h" #include "support/debug.h" +#include "support/lassert.h" #include "support/Timeout.h" #include "support/bind.h" @@ -30,6 +31,7 @@ using namespace std; using namespace lyx::support; namespace lyx { + namespace graphics { @@ -162,9 +164,10 @@ void LoaderQueue::touch(Cache::ItemPtr const & item) typedef std::shared_ptr ImagePtr; class Loader::Impl : public boost::signals2::trackable { + friend class Loader; public: /// - Impl(); + Impl(FileName const & doc_file); /// ~Impl(); /// @@ -178,6 +181,8 @@ public: /// Params const & params() const { return params_; } + /// + FileName doc_file_; /// The loading status of the image. ImageStatus status_; /** Must store a copy of the cached item to ensure that it is not @@ -211,27 +216,35 @@ private: }; -Loader::Loader() - : pimpl_(new Impl) +Loader::Loader(FileName const & doc_file) + : pimpl_(new Impl(doc_file)) {} -Loader::Loader(FileName const & file, bool display) - : pimpl_(new Impl) +Loader::Loader(FileName const & doc_file, FileName const & file, bool display) + : pimpl_(new Impl(doc_file)) { reset(file, display); } -Loader::Loader(FileName const & file, Params const & params) - : pimpl_(new Impl) +Loader::Loader(FileName const & doc_file, FileName const & file, Params const & params) + : pimpl_(new Impl(doc_file)) { reset(file, params); } +Loader::Loader(FileName const & doc_file, Loader const & other) + : pimpl_(new Impl(doc_file)) +{ + Params const & params = other.pimpl_->params(); + reset(params.filename, params); +} + + Loader::Loader(Loader const & other) - : pimpl_(new Impl) + : pimpl_(new Impl(other.pimpl_->doc_file_)) { Params const & params = other.pimpl_->params(); reset(params.filename, params); @@ -246,7 +259,10 @@ Loader::~Loader() Loader & Loader::operator=(Loader const & other) { + LASSERT(false, /**/); if (this != &other) { + delete pimpl_; + pimpl_ = new Impl(other.pimpl_->doc_file_); Params const & params = other.pimpl_->params(); reset(params.filename, params); } @@ -359,8 +375,8 @@ Image const * Loader::image() const } -Loader::Impl::Impl() - : status_(WaitingToLoad) +Loader::Impl::Impl(FileName const & doc_file) + : doc_file_(doc_file), status_(WaitingToLoad) { } @@ -404,7 +420,7 @@ void Loader::Impl::resetFile(FileName const & file) Cache & gc = Cache::get(); if (!gc.inCache(file)) - gc.add(file); + gc.add(file, doc_file_); // We /must/ make a local copy of this. cached_item_ = gc.item(file); diff --git a/src/graphics/GraphicsLoader.h b/src/graphics/GraphicsLoader.h index a90bd96963..8b97114f60 100644 --- a/src/graphics/GraphicsLoader.h +++ b/src/graphics/GraphicsLoader.h @@ -40,13 +40,15 @@ class Params; class Loader { public: /// Must use the reset methods to make this instance usable. - Loader(); + Loader(support::FileName const & doc_file); /// The image is not transformed, just displayed as-is. - Loader(support::FileName const & file_with_path, bool display = true); + Loader(support::FileName const & doc_file, support::FileName const & file_with_path, bool display = true); /// The image is transformed before display. - Loader(support::FileName const & file_with_path, Params const &); + Loader(support::FileName const & doc_file, support::FileName const & file_with_path, Params const &); /// - Loader(Loader const &); + Loader(support::FileName const & doc_file, Loader const &); + /// + Loader(Loader const & other); /// Needed for the pimpl ~Loader(); @@ -108,7 +110,7 @@ private: /// Use the Pimpl idiom to hide the internals. class Impl; /// The pointer never changes although *pimpl_'s contents may. - Impl * const pimpl_; + Impl * pimpl_; }; } // namespace graphics diff --git a/src/graphics/PreviewImage.cpp b/src/graphics/PreviewImage.cpp index 80e8e20135..da0f1e21ce 100644 --- a/src/graphics/PreviewImage.cpp +++ b/src/graphics/PreviewImage.cpp @@ -99,11 +99,17 @@ Image const * PreviewImage::image() const } +PreviewLoader & PreviewImage::previewLoader() const +{ + return pimpl_->ploader_; +} + + PreviewImage::Impl::Impl(PreviewImage & p, PreviewLoader & l, string const & s, FileName const & bf, double af) - : parent_(p), ploader_(l), iloader_(bf), + : parent_(p), ploader_(l), iloader_(l.buffer().fileName(), bf), snippet_(s), ascent_frac_(af) { iloader_.setDisplayPixelRatio(l.displayPixelRatio()); diff --git a/src/graphics/PreviewImage.h b/src/graphics/PreviewImage.h index 04f1338e10..6b5ac7e66c 100644 --- a/src/graphics/PreviewImage.h +++ b/src/graphics/PreviewImage.h @@ -22,6 +22,7 @@ class Dimension; namespace graphics { +class Cache; class PreviewLoader; class Image; @@ -49,6 +50,8 @@ public: /// support::FileName const & filename() const; + PreviewLoader & previewLoader() const; + private: /// Use the Pimpl idiom to hide the internals. class Impl; diff --git a/src/graphics/PreviewLoader.cpp b/src/graphics/PreviewLoader.cpp index e1a47e8439..df025e4153 100644 --- a/src/graphics/PreviewLoader.cpp +++ b/src/graphics/PreviewLoader.cpp @@ -74,36 +74,6 @@ FileName const unique_tex_filename(FileName const & bufferpath) } -lyx::Converter const * setConverter(string const & from) -{ - typedef vector FmtList; - typedef lyx::graphics::Cache GCache; - FmtList const & loadableFormats = GCache::get().loadableFormats(); - FmtList::const_iterator it = loadableFormats.begin(); - FmtList::const_iterator const end = loadableFormats.end(); - - for (; it != end; ++it) { - string const to = *it; - if (from == to) - continue; - - lyx::Converter const * ptr = lyx::theConverters().getConverter(from, to); - if (ptr) - return ptr; - } - - // Show the error only once. This is thread-safe. - static nullptr_t no_conv = [&]{ - LYXERR0("PreviewLoader::startLoading()\n" - << "No converter from \"" << from - << "\" format has been defined."); - return nullptr; - } (); - - return no_conv; -} - - void setAscentFractions(vector & ascent_fractions, FileName const & metrics_file) { @@ -223,6 +193,8 @@ public: Buffer const & buffer() const { return buffer_; } + lyx::Converter const * setConverter(string const & from); + private: /// Called by the ForkedCall process that generated the bitmap files. void finishedGenerating(pid_t, int); @@ -433,6 +405,35 @@ PreviewLoader::Impl::Impl(PreviewLoader & p, Buffer const & b) } +lyx::Converter const * PreviewLoader::Impl::setConverter(string const & from) +{ + typedef vector FmtList; + FmtList const & loadableFormats = graphics::Cache::get().loadableFormats(); + FmtList::const_iterator it = loadableFormats.begin(); + FmtList::const_iterator const end = loadableFormats.end(); + + for (; it != end; ++it) { + string const to = *it; + if (from == to) + continue; + + lyx::Converter const * ptr = lyx::theConverters().getConverter(from, to); + if (ptr) + return ptr; + } + + // Show the error only once. This is thread-safe. + static nullptr_t no_conv = [&]{ + LYXERR0("PreviewLoader::startLoading()\n" + << "No converter from \"" << from + << "\" format has been defined."); + return nullptr; + } (); + + return no_conv; +} + + PreviewLoader::Impl::~Impl() { delete delay_refresh_; diff --git a/src/insets/RenderGraphic.cpp b/src/insets/RenderGraphic.cpp index 9d9cf31789..7ab14cb63f 100644 --- a/src/insets/RenderGraphic.cpp +++ b/src/insets/RenderGraphic.cpp @@ -14,6 +14,7 @@ #include "insets/Inset.h" +#include "Buffer.h" #include "LyX.h" #include "LyXRC.h" #include "MetricsInfo.h" @@ -35,6 +36,7 @@ namespace lyx { RenderGraphic::RenderGraphic(Inset const * inset) + : loader_(inset->buffer().fileName()) { loader_.connect(bind(&Inset::updateFrontend, inset)); }