annotate plugin/PluginXml.cpp @ 1839:915d316a5609

Fix out of range access to magnitudes
author Chris Cannam
date Thu, 09 Apr 2020 14:59:05 +0100
parents 5f8fbbde08ff
children
rev   line source
Chris@66 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@66 2
Chris@66 3 /*
Chris@66 4 Sonic Visualiser
Chris@66 5 An audio file viewer and annotation editor.
Chris@66 6 Centre for Digital Music, Queen Mary, University of London.
Chris@66 7 This file copyright 2006 Chris Cannam.
Chris@66 8
Chris@66 9 This program is free software; you can redistribute it and/or
Chris@66 10 modify it under the terms of the GNU General Public License as
Chris@66 11 published by the Free Software Foundation; either version 2 of the
Chris@66 12 License, or (at your option) any later version. See the file
Chris@66 13 COPYING included with this distribution for more information.
Chris@66 14 */
Chris@66 15
Chris@66 16 #include "PluginXml.h"
Chris@66 17
Chris@66 18 #include <QRegExp>
Chris@66 19 #include <QXmlAttributes>
Chris@66 20
Chris@66 21 #include <QDomDocument>
Chris@66 22 #include <QDomElement>
Chris@66 23 #include <QDomNamedNodeMap>
Chris@66 24 #include <QDomAttr>
Chris@66 25
Chris@314 26 #include <QTextStream>
Chris@314 27
Chris@475 28 #include <vamp-hostsdk/PluginBase.h>
Chris@75 29 #include "RealTimePluginInstance.h"
Chris@66 30
Chris@66 31 #include <iostream>
Chris@66 32
Chris@1830 33 using std::shared_ptr;
Chris@1830 34 using std::dynamic_pointer_cast;
Chris@1830 35
Chris@1830 36 PluginXml::PluginXml(shared_ptr<Vamp::PluginBase> plugin) :
Chris@66 37 m_plugin(plugin)
Chris@66 38 {
Chris@66 39 }
Chris@66 40
Chris@66 41 PluginXml::~PluginXml() { }
Chris@66 42
Chris@66 43 QString
Chris@81 44 PluginXml::encodeConfigurationChars(QString text)
Chris@81 45 {
Chris@81 46 QString rv(text);
Chris@81 47 rv.replace(";", "[[SEMICOLON]]");
Chris@81 48 rv.replace("=", "[[EQUALS]]");
Chris@81 49 return rv;
Chris@81 50 }
Chris@81 51
Chris@81 52 QString
Chris@81 53 PluginXml::decodeConfigurationChars(QString text)
Chris@81 54 {
Chris@81 55 QString rv(text);
Chris@81 56 rv.replace("[[SEMICOLON]]", ";");
Chris@81 57 rv.replace("[[EQUALS]]", "=");
Chris@81 58 return rv;
Chris@81 59 }
Chris@81 60
Chris@314 61 void
Chris@314 62 PluginXml::toXml(QTextStream &stream,
Chris@314 63 QString indent, QString extraAttributes) const
Chris@66 64 {
Chris@314 65 stream << indent;
Chris@66 66
Chris@314 67 stream << QString("<plugin identifier=\"%1\" name=\"%2\" description=\"%3\" maker=\"%4\" version=\"%5\" copyright=\"%6\" %7 ")
Chris@239 68 .arg(encodeEntities(QString(m_plugin->getIdentifier().c_str())))
Chris@66 69 .arg(encodeEntities(QString(m_plugin->getName().c_str())))
Chris@66 70 .arg(encodeEntities(QString(m_plugin->getDescription().c_str())))
Chris@66 71 .arg(encodeEntities(QString(m_plugin->getMaker().c_str())))
Chris@66 72 .arg(m_plugin->getPluginVersion())
Chris@66 73 .arg(encodeEntities(QString(m_plugin->getCopyright().c_str())))
Chris@66 74 .arg(extraAttributes);
Chris@66 75
Chris@66 76 if (!m_plugin->getPrograms().empty()) {
Chris@314 77 stream << QString("program=\"%1\" ")
Chris@66 78 .arg(encodeEntities(m_plugin->getCurrentProgram().c_str()));
Chris@66 79 }
Chris@66 80
Chris@66 81 Vamp::PluginBase::ParameterList parameters =
Chris@66 82 m_plugin->getParameterDescriptors();
Chris@66 83
Chris@66 84 for (Vamp::PluginBase::ParameterList::const_iterator i = parameters.begin();
Chris@66 85 i != parameters.end(); ++i) {
Chris@185 86
Chris@690 87 // SVDEBUG << "PluginXml::toXml: parameter name \""
Chris@185 88 // << i->name.c_str() << "\" has value "
Chris@687 89 // << m_plugin->getParameter(i->name) << endl;
Chris@185 90
Chris@314 91 stream << QString("param-%1=\"%2\" ")
Chris@239 92 .arg(stripInvalidParameterNameCharacters(QString(i->identifier.c_str())))
Chris@239 93 .arg(m_plugin->getParameter(i->identifier));
Chris@66 94 }
Chris@66 95
Chris@1830 96 auto rtpi = dynamic_pointer_cast<RealTimePluginInstance>(m_plugin);
Chris@75 97 if (rtpi) {
Chris@75 98 std::map<std::string, std::string> configurePairs =
Chris@75 99 rtpi->getConfigurePairs();
Chris@75 100 QString config;
Chris@75 101 for (std::map<std::string, std::string>::iterator i = configurePairs.begin();
Chris@75 102 i != configurePairs.end(); ++i) {
Chris@75 103 QString key = i->first.c_str();
Chris@75 104 QString value = i->second.c_str();
Chris@81 105 key = encodeConfigurationChars(key);
Chris@81 106 value = encodeConfigurationChars(value);
Chris@75 107 if (config != "") config += ";";
Chris@75 108 config += QString("%1=%2").arg(key).arg(value);
Chris@75 109 }
Chris@75 110 if (config != "") {
Chris@314 111 stream << QString("configuration=\"%1\" ")
Chris@75 112 .arg(encodeEntities(config));
Chris@75 113 }
Chris@75 114 }
Chris@75 115
Chris@314 116 stream << "/>\n";
Chris@66 117 }
Chris@66 118
Chris@66 119 #define CHECK_ATTRIBUTE(ATTRIBUTE, ACCESSOR) \
Chris@66 120 QString ATTRIBUTE = attrs.value(#ATTRIBUTE); \
Chris@66 121 if (ATTRIBUTE != "" && ATTRIBUTE != ACCESSOR().c_str()) { \
Chris@843 122 cerr << "WARNING: PluginXml::setParameters: Plugin " \
Chris@66 123 << #ATTRIBUTE << " does not match (attributes have \"" \
Chris@686 124 << ATTRIBUTE << "\", my " \
Chris@843 125 << #ATTRIBUTE << " is \"" << ACCESSOR() << "\")" << endl; \
Chris@66 126 }
Chris@66 127
Chris@66 128 void
Chris@66 129 PluginXml::setParameters(const QXmlAttributes &attrs)
Chris@66 130 {
Chris@239 131 CHECK_ATTRIBUTE(identifier, m_plugin->getIdentifier);
Chris@66 132 CHECK_ATTRIBUTE(name, m_plugin->getName);
Chris@66 133 CHECK_ATTRIBUTE(description, m_plugin->getDescription);
Chris@66 134 CHECK_ATTRIBUTE(maker, m_plugin->getMaker);
Chris@66 135 CHECK_ATTRIBUTE(copyright, m_plugin->getCopyright);
Chris@66 136
Chris@66 137 bool ok;
Chris@66 138 int version = attrs.value("version").trimmed().toInt(&ok);
Chris@66 139 if (ok && version != m_plugin->getPluginVersion()) {
Chris@843 140 cerr << "WARNING: PluginXml::setParameters: Plugin version does not match (attributes have " << version << ", my version is " << m_plugin->getPluginVersion() << ")" << endl;
Chris@66 141 }
Chris@66 142
Chris@1830 143 auto rtpi = dynamic_pointer_cast<RealTimePluginInstance>(m_plugin);
Chris@75 144 if (rtpi) {
Chris@75 145 QString config = attrs.value("configuration");
Chris@75 146 if (config != "") {
Chris@75 147 QStringList configList = config.split(";");
Chris@75 148 for (QStringList::iterator i = configList.begin();
Chris@75 149 i != configList.end(); ++i) {
Chris@75 150 QStringList kv = i->split("=");
Chris@75 151 if (kv.count() < 2) {
Chris@844 152 cerr << "WARNING: PluginXml::setParameters: Malformed configure pair string: \"" << *i << "\"" << endl;
Chris@75 153 continue;
Chris@75 154 }
Chris@75 155 QString key(kv[0]), value(kv[1]);
Chris@81 156 key = decodeConfigurationChars(key);
Chris@81 157 value = decodeConfigurationChars(value);
Chris@75 158 rtpi->configure(key.toStdString(), value.toStdString());
Chris@75 159 }
Chris@75 160 }
Chris@75 161 }
Chris@75 162
Chris@66 163 if (!m_plugin->getPrograms().empty()) {
Chris@66 164 m_plugin->selectProgram(attrs.value("program").toStdString());
Chris@66 165 }
Chris@66 166
Chris@66 167 Vamp::PluginBase::ParameterList parameters =
Chris@66 168 m_plugin->getParameterDescriptors();
Chris@66 169
Chris@66 170 for (Vamp::PluginBase::ParameterList::const_iterator i =
Chris@66 171 parameters.begin(); i != parameters.end(); ++i) {
Chris@66 172
Chris@239 173 QString pname = QString("param-%1")
Chris@66 174 .arg(stripInvalidParameterNameCharacters
Chris@239 175 (QString(i->identifier.c_str())));
Chris@66 176
Chris@239 177 if (attrs.value(pname) == "") {
Chris@690 178 // SVDEBUG << "PluginXml::setParameters: no parameter \"" << i->name << "\" (attribute \"" << name << "\")" << endl;
Chris@66 179 continue;
Chris@66 180 }
Chris@66 181
Chris@66 182 bool ok;
Chris@239 183 float value = attrs.value(pname).trimmed().toFloat(&ok);
Chris@66 184 if (ok) {
Chris@690 185 // SVDEBUG << "PluginXml::setParameters: setting parameter \""
Chris@687 186 // << i->identifier << "\" to value " << value << endl;
Chris@239 187 m_plugin->setParameter(i->identifier, value);
Chris@66 188 } else {
Chris@843 189 cerr << "WARNING: PluginXml::setParameters: Invalid value \"" << attrs.value(pname) << "\" for parameter \"" << i->identifier << "\" (attribute \"" << pname << "\")" << endl;
Chris@66 190 }
Chris@66 191 }
Chris@66 192 }
Chris@66 193
Chris@66 194 void
Chris@66 195 PluginXml::setParametersFromXml(QString xml)
Chris@66 196 {
Chris@66 197 QDomDocument doc;
Chris@66 198
Chris@66 199 QString error;
Chris@66 200 int errorLine;
Chris@66 201 int errorColumn;
Chris@66 202
Chris@690 203 // SVDEBUG << "PluginXml::setParametersFromXml: XML is \""
Chris@845 204 // << xml << "\"" << endl;
Chris@185 205
Chris@66 206 if (!doc.setContent(xml, false, &error, &errorLine, &errorColumn)) {
Chris@843 207 cerr << "PluginXml::setParametersFromXml: Error in parsing XML: " << error << " at line " << errorLine << ", column " << errorColumn << endl;
Chris@843 208 cerr << "Input follows:" << endl;
Chris@843 209 cerr << xml << endl;
Chris@843 210 cerr << "Input ends." << endl;
Chris@66 211 return;
Chris@66 212 }
Chris@66 213
Chris@66 214 QDomElement pluginElt = doc.firstChildElement("plugin");
Chris@66 215 QDomNamedNodeMap attrNodes = pluginElt.attributes();
Chris@66 216 QXmlAttributes attrs;
Chris@66 217
Chris@929 218 for (int i = 0; i < attrNodes.length(); ++i) {
Chris@66 219 QDomAttr attr = attrNodes.item(i).toAttr();
Chris@66 220 if (attr.isNull()) continue;
Chris@690 221 // SVDEBUG << "PluginXml::setParametersFromXml: Adding attribute \"" << attr.name()// << "\" with value \"" << attr.value() << "\"" << endl;
Chris@66 222 attrs.append(attr.name(), "", "", attr.value());
Chris@66 223 }
Chris@66 224
Chris@66 225 setParameters(attrs);
Chris@66 226 }
Chris@163 227
Chris@66 228 QString
Chris@66 229 PluginXml::stripInvalidParameterNameCharacters(QString s) const
Chris@66 230 {
Chris@66 231 s.replace(QRegExp("[^a-zA-Z0-9_]*"), "");
Chris@66 232 return s;
Chris@66 233 }
Chris@66 234