File indexing completed on 2024-09-08 13:30:54
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 ¶m) 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 ¶m) 0210 { 0211 say(QLatin1String("0 ") + m_values.value(param)); 0212 } 0213 0214 void DebconfFrontend::cmd_input(const QString ¶m) 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 ¶m) 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 ¶m) 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 ¶m) 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 ¶m) 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 ¶m) 0295 { 0296 Q_UNUSED(param); 0297 say(QLatin1String("0 pong")); 0298 } 0299 0300 void DebconfFrontend::cmd_version(const QString ¶m) 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 ¶m) 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 ¶m) 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 ¶m) 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 ¶m) 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 ¶m) 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 ¶m) 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 ¶m) 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"