File indexing completed on 2024-05-12 04:58:11

0001 /*
0002  * Copyright 2009 Jakub Wieczorek <faw217@gmail.com>
0003  *
0004  * This program is free software; you can redistribute it and/or modify
0005  * it under the terms of the GNU General Public License as published by
0006  * the Free Software Foundation; either version 2 of the License, or
0007  * (at your option) any later version.
0008  *
0009  * This program is distributed in the hope that it will be useful,
0010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0012  * GNU General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU General Public License
0015  * along with this program; if not, write to the Free Software
0016  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA  02110-1301  USA
0018  */
0019 /* ============================================================
0020 * Falkon - Qt web browser
0021 * Copyright (C) 2010-2014  David Rosca <nowrep@gmail.com>
0022 *
0023 * This program is free software: you can redistribute it and/or modify
0024 * it under the terms of the GNU General Public License as published by
0025 * the Free Software Foundation, either version 3 of the License, or
0026 * (at your option) any later version.
0027 *
0028 * This program is distributed in the hope that it will be useful,
0029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
0030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0031 * GNU General Public License for more details.
0032 *
0033 * You should have received a copy of the GNU General Public License
0034 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0035 * ============================================================ */
0036 #include "opensearchreader.h"
0037 
0038 #include "opensearchengine.h"
0039 
0040 #include <qiodevice.h>
0041 
0042 /*!
0043     \class OpenSearchReader
0044     \brief A class reading a search engine description from an external source
0045 
0046     OpenSearchReader is a class that can be used to read search engine descriptions
0047     formed using the OpenSearch format.
0048 
0049     It inherits QXmlStreamReader and thus provides additional functions, such as
0050     QXmlStreamReader::error(), QXmlStreamReader::hasError() that can be used to make sure
0051     the reading procedure succeeded.
0052 
0053     For more information see:
0054     http://www.opensearch.org/Specifications/OpenSearch/1.1/Draft_4#OpenSearch_description_document
0055 
0056     \sa OpenSearchEngine, OpenSearchWriter
0057 */
0058 
0059 /*!
0060     Constructs a new reader.
0061 
0062     \note One instance can be used to read multiple files, one by one.
0063 */
0064 OpenSearchReader::OpenSearchReader()
0065     : QXmlStreamReader()
0066 {
0067 }
0068 
0069 /*!
0070     Reads an OpenSearch engine from the \a device and returns an OpenSearchEngine object,
0071     filled in with all the data that has been retrieved from the document.
0072 
0073     If the \a device is closed, it will be opened.
0074 
0075     To make sure if the procedure succeeded, check QXmlStreamReader::error().
0076 
0077     \return a new constructed OpenSearchEngine object
0078 
0079     \note The function returns an object of the OpenSearchEngine class even if the document
0080           is bad formed or doesn't conform to the specification. It needs to be manually
0081           deleted afterwards, if intended.
0082     \note The lifetime of the returned OpenSearchEngine object is up to the user.
0083           The object should be deleted once it is not used anymore to avoid memory leaks.
0084 */
0085 OpenSearchEngine* OpenSearchReader::read(QIODevice* device)
0086 {
0087     clear();
0088 
0089     if (!device->isOpen()) {
0090         device->open(QIODevice::ReadOnly);
0091     }
0092 
0093     setDevice(device);
0094     return read();
0095 }
0096 
0097 OpenSearchEngine* OpenSearchReader::read()
0098 {
0099     auto* engine = new OpenSearchEngine();
0100     m_searchXml = QString::fromLatin1(device()->peek(1024 * 5));
0101 
0102     if (!m_searchXml.contains(QLatin1String("http://a9.com/-/spec/opensearch/1.1/")) &&
0103         !m_searchXml.contains(QLatin1String("http://www.mozilla.org/2006/browser/search/"))
0104        ) {
0105         raiseError(QObject::tr("The file is not an OpenSearch 1.1 file."));
0106         return engine;
0107     }
0108 
0109     // It just skips the XML declaration
0110     // The parsing code bellow for some reason doesn't like it -,-
0111 
0112     int index = m_searchXml.indexOf(QLatin1String("<?xml"));
0113     if (index > 0) {
0114         int end = m_searchXml.indexOf(QLatin1String("?>"), index);
0115 
0116         if (end > 0) {
0117             device()->read(end + 2);
0118         }
0119     }
0120 
0121     while (!isStartElement() && !atEnd()) {
0122         readNext();
0123     }
0124 
0125 
0126     while (!atEnd()) {
0127         readNext();
0128 
0129         if (!isStartElement()) {
0130             continue;
0131         }
0132 
0133         if (name() == QLatin1String("ShortName") || name() == QLatin1String("os:ShortName")) {
0134             engine->setName(readElementText());
0135         }
0136         else if (name() == QLatin1String("Description") || name() == QLatin1String("os:Description")) {
0137             engine->setDescription(readElementText());
0138         }
0139         else if (name() == QLatin1String("Url") || name() == QLatin1String("os:Url")) {
0140             QString type = attributes().value(QLatin1String("type")).toString();
0141             QString url = attributes().value(QLatin1String("template")).toString();
0142             QString method = attributes().value(QLatin1String("method")).toString();
0143 
0144             if (type == QLatin1String("application/x-suggestions+json") &&
0145                 !engine->suggestionsUrlTemplate().isEmpty()
0146                ) {
0147                 continue;
0148             }
0149 
0150             if ((type.isEmpty() ||
0151                  type == QLatin1String("text/html") ||
0152                  type == QLatin1String("application/xhtml+xml")) &&
0153                 !engine->searchUrlTemplate().isEmpty()
0154                ) {
0155                 continue;
0156             }
0157 
0158             if (url.isEmpty()) {
0159                 continue;
0160             }
0161 
0162             QList<OpenSearchEngine::Parameter> parameters;
0163 
0164             readNext();
0165 
0166             while (!isEndElement() || (name() != QLatin1String("Url") && name() != QLatin1String("os:Url"))) {
0167                 if (!isStartElement() || (name() != QLatin1String("Param") && name() != QLatin1String("Parameter") &&
0168                                           name() != QLatin1String("os:Param") && name() != QLatin1String("os:Parameter"))
0169                    ) {
0170                     readNext();
0171                     continue;
0172                 }
0173 
0174                 QString key = attributes().value(QLatin1String("name")).toString();
0175                 QString value = attributes().value(QLatin1String("value")).toString();
0176 
0177                 if (!key.isEmpty() && !value.isEmpty()) {
0178                     parameters.append(OpenSearchEngine::Parameter(key, value));
0179                 }
0180 
0181                 while (!isEndElement()) {
0182                     readNext();
0183                 }
0184             }
0185 
0186             if (type == QLatin1String("application/x-suggestions+json")) {
0187                 engine->setSuggestionsUrlTemplate(url);
0188                 engine->setSuggestionsParameters(parameters);
0189                 engine->setSuggestionsMethod(method);
0190             }
0191             else if (type.isEmpty() || type == QLatin1String("text/html") || type == QLatin1String("application/xhtml+xml")) {
0192                 engine->setSearchUrlTemplate(url);
0193                 engine->setSearchParameters(parameters);
0194                 engine->setSearchMethod(method);
0195             }
0196 
0197         }
0198         else if (name() == QLatin1String("Image") || name() == QLatin1String("os:Image")) {
0199             engine->setImageUrl(readElementText());
0200         }
0201 
0202         if (!engine->name().isEmpty() &&
0203             !engine->description().isEmpty() &&
0204             !engine->suggestionsUrlTemplate().isEmpty() &&
0205             !engine->searchUrlTemplate().isEmpty() &&
0206             !engine->imageUrl().isEmpty()
0207            ) {
0208             break;
0209         }
0210     }
0211 
0212     return engine;
0213 }
0214