File indexing completed on 2024-04-21 05:46:32

0001 // This license reflects the original Adept code:
0002 // -*- C++ -*- (c) 2008 Petr Rockai <me@mornfall.net>
0003 //             (c) 2011 Modestas Vainius <modax@debian.org>
0004 // Redistribution and use in source and binary forms, with or without
0005 // modification, are permitted provided that the following conditions are
0006 // met:
0007 //
0008 //     * Redistributions of source code must retain the above copyright
0009 //       notice, this list of conditions and the following disclaimer.
0010 //
0011 //     * Redistributions in binary form must reproduce the above
0012 //       copyright notice, this list of conditions and the following
0013 //       disclaimer in the documentation and/or other materials provided
0014 //       with the distribution.
0015 //
0016 //     * Neither the name of [original copyright holder] nor the names of
0017 //       its contributors may be used to endorse or promote products
0018 //       derived from this software without specific prior written
0019 //       permission.
0020 //
0021 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
0022 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
0023 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
0024 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
0025 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0026 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0027 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0028 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0029 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0030 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
0031 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0032 /*
0033  * All the modifications below are licensed under this license
0034  * Copyright (C) 2010-2018 Daniel Nicoletti <dantti12@gmail.com>
0035  *
0036  * This library is free software; you can redistribute it and/or
0037  * modify it under the terms of the GNU Library General Public
0038  * License as published by the Free Software Foundation; either
0039  * version 2 of the License, or (at your option) any later version.
0040  *
0041  * This library is distributed in the hope that it will be useful,
0042  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0043  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0044  * Library General Public License for more details.
0045  *
0046  * You should have received a copy of the GNU Library General Public License
0047  * along with this library; see the file COPYING.LIB. If not, write to
0048  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0049  * Boston, MA 02110-1301, USA.
0050  */
0051 
0052 #include "debconf.h"
0053 
0054 #include <QtCore/QSocketNotifier>
0055 #include <QtCore/QRegExp>
0056 #include <QtCore/QFile>
0057 #include <cstdio>
0058 #include <unistd.h>
0059 
0060 #include "Debug_p.h"
0061 
0062 namespace DebconfKde {
0063 
0064 const DebconfFrontend::Cmd DebconfFrontend::commands[] = {
0065     { "SET", &DebconfFrontend::cmd_set },
0066     { "GO", &DebconfFrontend::cmd_go },
0067     { "TITLE", &DebconfFrontend::cmd_title },
0068     { "SETTITLE", &DebconfFrontend::cmd_title },
0069     { "DATA", &DebconfFrontend::cmd_data },
0070     { "SUBST", &DebconfFrontend::cmd_subst },
0071     { "INPUT", &DebconfFrontend::cmd_input },
0072     { "GET", &DebconfFrontend::cmd_get },
0073     { "CAPB", &DebconfFrontend::cmd_capb },
0074     { "PROGRESS", &DebconfFrontend::cmd_progress },
0075     { "X_PING", &DebconfFrontend::cmd_x_ping },
0076     { "VERSION", &DebconfFrontend::cmd_version },
0077     { "X_LOADTEMPLATEFILE", &DebconfFrontend::cmd_x_loadtemplatefile },
0078     { "INFO", &DebconfFrontend::cmd_info },
0079     { "FGET", &DebconfFrontend::cmd_fget },
0080     { "FSET", &DebconfFrontend::cmd_fset },
0081     { "BEGINBLOCK", &DebconfFrontend::cmd_beginblock },
0082     { "ENDBLOCK", &DebconfFrontend::cmd_endblock },
0083     { "STOP", &DebconfFrontend::cmd_stop },
0084     { 0, 0 } };
0085 
0086 DebconfFrontend::DebconfFrontend(QObject *parent)
0087   : QObject(parent)
0088 {
0089 }
0090 
0091 DebconfFrontend::~DebconfFrontend()
0092 {
0093 }
0094 
0095 void DebconfFrontend::disconnected()
0096 {
0097     reset();
0098     Q_EMIT finished();
0099 }
0100 
0101 QString DebconfFrontend::value(const QString &key) const
0102 {
0103     return m_values[key];
0104 }
0105 
0106 void DebconfFrontend::setValue(const QString &key, const QString &value)
0107 {
0108     m_values[key] = value;
0109 }
0110 
0111 template<class T> int DebconfFrontend::enumFromString(const QString &str, const char *enumName)
0112 {
0113     QString realName(str);
0114     realName.replace(0, 1, str.at(0).toUpper());
0115     int pos;
0116     while ((pos = realName.indexOf(QLatin1Char( '_' ))) != -1) {
0117         if (pos + 1 >= realName.size()) { // pos is from 0, size from 1, mustn't go off-by-one
0118             realName.chop(1);
0119         } else{
0120             realName.replace(pos, 2, realName.at(pos + 1).toUpper());
0121         }
0122     }
0123 
0124     int id = T::staticMetaObject.indexOfEnumerator(enumName);
0125     QMetaEnum e = T::staticMetaObject.enumerator(id);
0126     int enumValue = e.keyToValue(realName.toLatin1().data());
0127 
0128     if(enumValue == -1) {
0129         enumValue = e.keyToValue(QString(QLatin1String("Unknown") + QLatin1String(enumName)).toLatin1().data());
0130         qCDebug(DEBCONF) << "enumFromString (" <<QLatin1String(enumName) << ") : converted" << realName << "to" << QString(QLatin1String("Unknown") + QLatin1String(enumName)) << ", enum value" << enumValue;
0131     }
0132     return enumValue;
0133 }
0134 
0135 DebconfFrontend::PropertyKey DebconfFrontend::propertyKeyFromString(const QString &string)
0136 {
0137     return static_cast<PropertyKey>(enumFromString<DebconfFrontend>(string, "PropertyKey" ));
0138 }
0139 
0140 DebconfFrontend::TypeKey DebconfFrontend::type(const QString &string) const
0141 {
0142     return static_cast<TypeKey>(enumFromString<DebconfFrontend>(property(string, Type), "TypeKey" ));
0143 }
0144 
0145 void DebconfFrontend::reset()
0146 {
0147     Q_EMIT backup(false);
0148     m_data.clear();
0149     m_subst.clear();
0150     m_values.clear();
0151     m_flags.clear();
0152 }
0153 
0154 void DebconfFrontend::say(const QString &string)
0155 {
0156     qCDebug(DEBCONF) << "DEBCONF ---> " << string;
0157     QTextStream out(getWriteDevice());
0158     out << string << "\n";
0159     out.flush();
0160 }
0161 
0162 QString DebconfFrontend::substitute(const QString &key, const QString &rest) const
0163 {
0164     Substitutions sub = m_subst[key];
0165     QString result, var, escape;
0166     QRegExp rx(QLatin1String("^(.*)(\\\\)?\\$\\{([^\\{\\}]+)\\}(.*)$"));
0167     QString last(rest);
0168     int pos = 0;
0169     while ((pos = rx.indexIn(rest, pos)) != -1) {
0170         qCDebug(DEBCONF) << "var found! at" << pos;
0171         result += rx.cap(1);
0172         escape = rx.cap(2);
0173         var = rx.cap(3);
0174         last = rx.cap(4);
0175         if (!escape.isEmpty()) {
0176             result += QLatin1Literal("${") + var + QLatin1Char('}');
0177         } else {
0178             result += sub.value(var);
0179         }
0180         pos += rx.matchedLength();
0181     }
0182     return result + last;
0183 }
0184 
0185 QString DebconfFrontend::property(const QString &key, PropertyKey p) const
0186 {
0187     const QString r = m_data.value(key).value(p);
0188     if (p == Description || p == Choices) {
0189         return substitute(key, r);
0190     }
0191     return r;
0192 }
0193 
0194 void DebconfFrontend::cmd_capb(const QString &caps)
0195 {
0196     Q_EMIT backup(caps.split(QLatin1String(", ")).contains(QLatin1String("backup")));
0197     say(QLatin1String("0 backup"));
0198 }
0199 
0200 void DebconfFrontend::cmd_set(const QString &param)
0201 {
0202     const QString item = param.section(QLatin1Char(' '), 0, 0);
0203     const QString value = param.section(QLatin1Char(' '), 1);
0204     m_values[item] = value;
0205     qCDebug(DEBCONF) << "# SET: [" << item << "] " << value;
0206     say(QLatin1String("0 ok"));
0207 }
0208 
0209 void DebconfFrontend::cmd_get(const QString &param)
0210 {
0211     say(QLatin1String("0 ") + m_values.value(param));
0212 }
0213 
0214 void DebconfFrontend::cmd_input(const QString &param)
0215 {
0216     m_input.append(param.section(QLatin1Char(' '), 1));
0217     say(QLatin1String("0 will ask" ));
0218 }
0219 
0220 void DebconfFrontend::cmd_go(const QString &)
0221 {
0222     qCDebug(DEBCONF) << "# GO";
0223     m_input.removeDuplicates();
0224     Q_EMIT go(m_title, m_input);
0225     m_input.clear();
0226 }
0227 
0228 void DebconfFrontend::cmd_progress(const QString &param)
0229 {
0230     qCDebug(DEBCONF) << "DEBCONF: PROGRESS " << param;
0231     Q_EMIT progress(param);
0232 }
0233 
0234 void DebconfFrontend::next()
0235 {
0236     m_input.clear();
0237     say(QLatin1String("0 ok, got the answers"));
0238 }
0239 
0240 void DebconfFrontend::back()
0241 {
0242     m_input.clear();
0243     say(QLatin1String("30 go back"));
0244 }
0245 
0246 void DebconfFrontend::cancel()
0247 {
0248     reset();
0249 }
0250 
0251 void DebconfFrontend::cmd_title(const QString &param)
0252 {
0253     if (!property(param, Description).isEmpty()) {
0254         m_title = property(param, Description);
0255     } else {
0256         m_title = param;
0257     }
0258     qCDebug(DEBCONF) << "DEBCONF: TITLE " << m_title;
0259     say(QLatin1String("0 ok"));
0260 }
0261 
0262 void DebconfFrontend::cmd_data(const QString &param)
0263 {
0264     // We get strings like
0265     // aiccu/brokername description Tunnel broker:
0266     // item = "aiccu/brokername"
0267     // type = "description"
0268     // rest = "Tunnel broker:"
0269     const QString item = param.section(QLatin1Char(' '), 0, 0);
0270     const QString type = param.section(QLatin1Char(' '), 1, 1);
0271     const QString value = param.section(QLatin1Char(' '), 2);
0272 
0273     m_data[item][propertyKeyFromString(type)] = value;
0274     qCDebug(DEBCONF) << "# NOTED: [" << item << "] [" << type << "] " << value;
0275     say(QStringLiteral( "0 ok" ));
0276 }
0277 
0278 void DebconfFrontend::cmd_subst(const QString &param)
0279 {
0280     // We get strings like
0281     // aiccu/brokername brokers AARNet, Hexago / Freenet6, SixXS, Wanadoo France
0282     // item = "aiccu/brokername"
0283     // type = "brokers"
0284     // value = "AARNet, Hexago / Freenet6, SixXS, Wanadoo France"
0285     const QString item = param.section(QLatin1Char(' '), 0, 0);
0286     const QString type = param.section(QLatin1Char(' '), 1, 1);
0287     const QString value = param.section(QLatin1Char(' '), 2);
0288 
0289     m_subst[item][type] = value;
0290     qCDebug(DEBCONF) << "# SUBST: [" << item << "] [" << type << "] " << value;
0291     say(QLatin1String("0 ok"));
0292 }
0293 
0294 void DebconfFrontend::cmd_x_ping(const QString &param)
0295 {
0296     Q_UNUSED(param);
0297     say(QLatin1String("0 pong"));
0298 }
0299 
0300 void DebconfFrontend::cmd_version(const QString &param)
0301 {
0302     if ( !param.isEmpty() ) {
0303         const QString major_version_str = param.section(QLatin1Char('.'), 0, 0);
0304         bool ok = false;
0305         int major_version = major_version_str.toInt( &ok );
0306         if ( !ok || (major_version != 2) ) {
0307             say(QLatin1String("30 wrong or too old protocol version"));
0308             return;
0309         }
0310     }
0311     //This debconf frontend is suposed to use the version 2.1 of the protocol.
0312     say(QLatin1String("0 2.1"));
0313 }
0314 
0315 void DebconfFrontend::cmd_x_loadtemplatefile(const QString &param)
0316 {
0317     QFile template_file(param);
0318     if (template_file.open(QFile::ReadOnly)) {
0319         QTextStream template_stream(&template_file);
0320         QString line = QLatin1String("");
0321         int linecount = 0;
0322         QHash<QString,QString> field_short_value;
0323         QHash<QString,QString> field_long_value;
0324         QString last_field_name;
0325         while ( !line.isNull() ) {
0326             ++linecount;
0327             line = template_stream.readLine();
0328             qCDebug(DEBCONF) << linecount << line;
0329             if ( line.isEmpty() ) {
0330                 if (!last_field_name.isEmpty()) {
0331                     //Submit last block values.
0332                     qCDebug(DEBCONF) << "submit" << last_field_name;
0333                     const QString item = field_short_value[QLatin1String("template")];
0334                     const QString type = field_short_value[QLatin1String("type")];
0335                     const QString short_description = field_short_value[QLatin1String("description")];
0336                     const QString long_description = field_long_value[QLatin1String("description")];
0337 
0338                     m_data[item][DebconfFrontend::Type] = type;
0339                     m_data[item][DebconfFrontend::Description] = short_description;
0340                     m_data[item][DebconfFrontend::ExtendedDescription] = long_description;
0341                     
0342                     //Clear data.
0343                     field_short_value.clear();
0344                     field_long_value.clear();
0345                     last_field_name.clear();
0346                 }
0347             } else {
0348                 if (!line.startsWith(QLatin1Char(' '))) {
0349                     last_field_name = line.section(QLatin1String(": "), 0, 0).toLower();
0350                     field_short_value[last_field_name] = line.section(QLatin1String(": "), 1);
0351                 } else {
0352                     if ( field_long_value[last_field_name].isEmpty() ){
0353                         field_long_value[last_field_name] = line.remove(0, 1);
0354                     } else {
0355                         field_long_value[last_field_name].append(QLatin1Char('\n'));
0356                         if ( !(line.trimmed() == QLatin1String(".")) ) {
0357                             field_long_value[last_field_name].append(line.remove(0, 1));
0358                         }
0359                     }
0360                 }
0361             }
0362         }
0363     } else {
0364         say(QLatin1String("30 couldn't open file"));
0365         return;
0366     }
0367     say(QLatin1String("0 ok"));
0368 }
0369 
0370 void DebconfFrontend::cmd_info(const QString &param)
0371 {
0372     Q_UNUSED(param)
0373     //FIXME: this is a dummy command, we should actually do something
0374     //with param.
0375     say(QLatin1String("0 ok"));
0376 }
0377 
0378 void DebconfFrontend::cmd_fget(const QString &param)
0379 {
0380     // We get strings like
0381     // foo/bar seen false
0382     // question = "foo/bar"
0383     // flag = "seen"
0384     const QString question = param.section(QLatin1Char(' '), 0, 0);
0385     const QString flag = param.section(QLatin1Char(' '), 1, 1);
0386 
0387     if (m_flags[question][flag]) {
0388         say(QLatin1String("0 true"));
0389     } else {
0390         say(QLatin1String("0 false"));
0391     }
0392 }
0393 
0394 void DebconfFrontend::cmd_fset(const QString &param)
0395 {
0396     // We get strings like
0397     // foo/bar seen false
0398     // question = "foo/bar"
0399     // flag = "seen"
0400     // value = "false"
0401     const QString question = param.section(QLatin1Char(' '), 0, 0);
0402     const QString flag = param.section(QLatin1Char(' '), 1, 1);
0403     const QString value = param.section(QLatin1Char(' '), 2, 2);
0404 
0405     if ( value == QLatin1String("false") ) {
0406         m_flags[question][flag] = false;
0407     } else {
0408         m_flags[question][flag] = true;
0409     }
0410     say(QLatin1String("0 ok"));
0411 }
0412 
0413 void DebconfFrontend::cmd_beginblock(const QString &param)
0414 {
0415     Q_UNUSED(param)
0416     //FIXME: this is a dummy command, we should actually do something
0417     //with param.
0418     say(QLatin1String("0 ok"));
0419 }
0420 
0421 void DebconfFrontend::cmd_endblock(const QString &param)
0422 {
0423     Q_UNUSED(param)
0424     //FIXME: this is a dummy command, we should actually do something
0425     //with param.
0426     say(QLatin1String("0 ok"));
0427 }
0428 
0429 void DebconfFrontend::cmd_stop(const QString &param)
0430 {
0431      Q_UNUSED(param)
0432      //Do nothing.
0433 }
0434 
0435 bool DebconfFrontend::process()
0436 {
0437     QTextStream in(getReadDevice());
0438     QString line = in.readLine();
0439 
0440     if (line.isEmpty()) {
0441         return false;
0442     }
0443 
0444     const QString command = line.section(QLatin1Char(' '), 0, 0);
0445     const QString value = line.section(QLatin1Char(' '), 1);
0446 
0447     qCDebug(DEBCONF) << "DEBCONF <--- [" << command << "] " << value;
0448     const Cmd *c = commands;
0449     while (c->cmd != 0) {
0450         if (command == QLatin1String(c->cmd)) {
0451             (this->*(c->run))(value);
0452             return true;
0453         }
0454         ++ c;
0455     }
0456     return false;
0457 }
0458 
0459 DebconfFrontendSocket::DebconfFrontendSocket(const QString &socketName, QObject *parent)
0460   : DebconfFrontend(parent), m_socket(0)
0461 {
0462     m_server = new QLocalServer(this);
0463     QFile::remove(socketName);
0464     m_server->listen(socketName);
0465     connect(m_server, &QLocalServer::newConnection, this, &DebconfFrontendSocket::newConnection);
0466 }
0467 
0468 DebconfFrontendSocket::~DebconfFrontendSocket()
0469 {
0470     QFile::remove(m_server->fullServerName());
0471 }
0472 
0473 void DebconfFrontendSocket::newConnection()
0474 {
0475     if (m_socket) {
0476         QLocalSocket *socket = m_server->nextPendingConnection();
0477         socket->disconnectFromServer();
0478         socket->deleteLater();
0479         return;
0480     }
0481 
0482     m_socket = m_server->nextPendingConnection();
0483     if (m_socket) {
0484         connect(m_socket, &QLocalSocket::readyRead, this, &DebconfFrontendSocket::process);
0485         connect(m_socket, &QLocalSocket::disconnected, this, &DebconfFrontendSocket::disconnected);
0486     }
0487 }
0488 
0489 void DebconfFrontendSocket::reset()
0490 {
0491     if (m_socket) {
0492         m_socket->deleteLater();
0493         m_socket = 0;
0494     }
0495     DebconfFrontend::reset();
0496 }
0497 
0498 void DebconfFrontendSocket::cancel()
0499 {
0500     if (m_socket) {
0501         m_socket->disconnectFromServer();
0502     }
0503     DebconfFrontend::cancel();
0504 }
0505 
0506 DebconfFrontendFifo::DebconfFrontendFifo(int readfd, int writefd, QObject *parent)
0507   : DebconfFrontend(parent)
0508 {
0509     m_readf = new QFile(this);
0510     // Use QFile::open(fh,mode) method for opening read file descriptor as
0511     // sequential files opened with QFile::open(fd,mode) are not handled
0512     // properly.
0513     FILE *readfh = ::fdopen(readfd, "rb");
0514     m_readf->open(readfh, QIODevice::ReadOnly);
0515 
0516     m_writef = new QFile(this);
0517     m_writef->open(writefd, QIODevice::WriteOnly);
0518     // QIODevice::readyReady() does not work with QFile
0519     // http://bugreports.qt.nokia.com/browse/QTBUG-16089
0520     m_readnotifier = new QSocketNotifier(readfd, QSocketNotifier::Read, this);
0521     connect(m_readnotifier, &QSocketNotifier::activated, this, &DebconfFrontendFifo::process);
0522 }
0523 
0524 void DebconfFrontendFifo::reset()
0525 {
0526     if (m_readf) {
0527         // Close file descriptors
0528         int readfd = m_readf->handle();
0529         int writefd = m_writef->handle();
0530         m_readnotifier->setEnabled(false);
0531         m_readf->close();
0532         m_writef->close();
0533         m_readf = m_writef = 0;
0534 
0535         // Use C library calls because QFile::close() won't close them actually
0536         ::close(readfd);
0537         ::close(writefd);
0538     }
0539     DebconfFrontend::reset();
0540 }
0541 
0542 void DebconfFrontendFifo::cancel()
0543 {
0544     disconnected();
0545 }
0546 
0547 bool DebconfFrontendFifo::process()
0548 {
0549     // We will get notification when the other end is closed
0550     if (m_readf->atEnd()) {
0551         cancel();
0552         return false;
0553     }
0554     return DebconfFrontend::process();
0555 }
0556 
0557 }
0558 
0559 #include "moc_debconf.cpp"