mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-11-22 01:59:02 +00:00
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.
This commit is contained in:
parent
ff0c95aba6
commit
244de5d2c1
@ -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'],
|
||||
|
@ -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_) {
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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<std::string> auth_files_;
|
||||
};
|
||||
|
||||
/// The global instance.
|
||||
|
@ -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<string>(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<string>(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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include "GuiExternal.h"
|
||||
|
||||
#include "Buffer.h"
|
||||
#include "FuncRequest.h"
|
||||
#include "support/gettext.h"
|
||||
#include "Length.h"
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -304,6 +304,51 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" >
|
||||
<widget class="QGroupBox" name="securityGB" >
|
||||
<property name="title" >
|
||||
<string>Security</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" >
|
||||
<property name="margin" >
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item row="0" column="0" >
|
||||
<layout class="QHBoxLayout" >
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="needauthForbiddenCB" >
|
||||
<property name="text" >
|
||||
<string>&Forbid use of needauth converters</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>When enabled, use of converters with the 'needauth' option is forbidden.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="needauthCB" >
|
||||
<property name="text" >
|
||||
<string>Use need&auth option</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>When enabled, ask user before launching any external converter with the 'needauth' option.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
|
@ -101,7 +101,7 @@ vector<string> 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));
|
||||
}
|
||||
|
||||
|
||||
|
@ -46,7 +46,7 @@ public:
|
||||
std::vector<std::string> 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;
|
||||
|
@ -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<string> FormatList;
|
||||
FormatList const & formats = Cache::get().loadableFormats();
|
||||
typedef vector<string> 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<Converter>(filename, to_file_base.absFileName(),
|
||||
converter_ = make_unique<Converter>(doc_file_, filename, to_file_base.absFileName(),
|
||||
from, to_);
|
||||
converter_->connect(bind(&Impl::imageConverted, this, _1));
|
||||
converter_->startConversion();
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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<Image> 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);
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -74,36 +74,6 @@ FileName const unique_tex_filename(FileName const & bufferpath)
|
||||
}
|
||||
|
||||
|
||||
lyx::Converter const * setConverter(string const & from)
|
||||
{
|
||||
typedef vector<string> 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<double> & 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<string> 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_;
|
||||
|
@ -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));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user