diff --git a/src/modules/locale/CMakeLists.txt b/src/modules/locale/CMakeLists.txt index e32f6e613..3c0cc3712 100644 --- a/src/modules/locale/CMakeLists.txt +++ b/src/modules/locale/CMakeLists.txt @@ -1,9 +1,26 @@ +find_package(ECM ${ECM_VERSION} NO_MODULE) +if( ECM_FOUND AND BUILD_TESTING ) + include( ECMAddTests ) + find_package( Qt5 COMPONENTS Core Test REQUIRED ) +endif() + include_directories( ${PROJECT_BINARY_DIR}/src/libcalamaresui ) +set( geoip_src GeoIP.cpp GeoIPFreeGeoIP.cpp ) +set( geoip_libs ) + +find_package(Qt5 COMPONENTS Xml) +if( Qt5Xml_FOUND ) + list( APPEND geoip_src GeoIPXML.cpp ) + list( APPEND geoip_libs Qt5::Xml ) + add_definitions( -DHAVE_XML ) +endif() + calamares_add_plugin( locale TYPE viewmodule EXPORT_MACRO PLUGINDLLEXPORT_PRO SOURCES + ${geoip_src} LCLocaleDialog.cpp LocaleConfiguration.cpp LocalePage.cpp @@ -17,6 +34,28 @@ calamares_add_plugin( locale LINK_PRIVATE_LIBRARIES calamaresui Qt5::Network + ${geoip_libs} ${YAMLCPP_LIBRARY} SHARED_LIB ) + +if( ECM_FOUND AND BUILD_TESTING ) + ecm_add_test( + GeoIPTests.cpp + ${geoip_src} + TEST_NAME + geoiptest + LINK_LIBRARIES + calamaresui + Qt5::Network + Qt5::Test + ${geoip_libs} + ${YAMLCPP_LIBRARY} + ) + set_target_properties( geoiptest PROPERTIES AUTOMOC TRUE ) +endif() + +if( BUILD_TESTING ) + add_executable( test_geoip test_geoip.cpp ${geoip_src} ) + target_link_libraries( test_geoip calamaresui Qt5::Network ${geoip_libs} ${YAMLCPP_LIBRARY} ) +endif() diff --git a/src/modules/locale/GeoIP.cpp b/src/modules/locale/GeoIP.cpp new file mode 100644 index 000000000..1bed7d3f4 --- /dev/null +++ b/src/modules/locale/GeoIP.cpp @@ -0,0 +1,40 @@ +/* === This file is part of Calamares - === + * + * Copyright 2018, Adriaan de Groot + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + */ + +#include "GeoIP.h" + +#include "utils/Logger.h" + +GeoIP::~GeoIP() +{ +} + +GeoIP::RegionZonePair +GeoIP::splitTZString( const QString& timezoneString ) +{ + QStringList tzParts = timezoneString.split( '/', QString::SkipEmptyParts ); + if ( tzParts.size() >= 2 ) + { + cDebug() << "GeoIP reporting" << timezoneString; + QString region = tzParts.takeFirst(); + QString zone = tzParts.join( '/' ); + return qMakePair( region, zone ); + } + + return qMakePair( QString(), QString() ); +} diff --git a/src/modules/locale/GeoIP.h b/src/modules/locale/GeoIP.h new file mode 100644 index 000000000..c84a9b5e4 --- /dev/null +++ b/src/modules/locale/GeoIP.h @@ -0,0 +1,56 @@ +/* === This file is part of Calamares - === + * + * Copyright 2018, Adriaan de Groot + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + */ + +#ifndef GEOIP_H +#define GEOIP_H + +#include +#include +#include + +class QByteArray; + +/** + * @brief Interface for GeoIP retrievers. + * + * A GeoIP retriever takes a configured URL (from the config file) + * and can handle the data returned from its interpretation of that + * configured URL, returning a region and zone. + */ +struct GeoIP +{ + using RegionZonePair = QPair; + + virtual ~GeoIP(); + + /** @brief Handle a (successful) request by interpreting the data. + * + * Should return a ( , ) pair, e.g. + * ( "Europe", "Amsterdam" ). This is called **only** if the + * request to the fullUrl was successful; the handler + * is free to read as much, or as little, data as it + * likes. On error, returns a RegionZonePair with empty + * strings (e.g. ( "", "" ) ). + */ + virtual RegionZonePair processReply( const QByteArray& ) = 0; + + /** @brief Splits a region/zone string into a pair. */ + static RegionZonePair splitTZString( const QString& s ); +} ; + +#endif diff --git a/src/modules/locale/GeoIPFreeGeoIP.cpp b/src/modules/locale/GeoIPFreeGeoIP.cpp new file mode 100644 index 000000000..9ef534a5d --- /dev/null +++ b/src/modules/locale/GeoIPFreeGeoIP.cpp @@ -0,0 +1,55 @@ +/* === This file is part of Calamares - === + * + * Copyright 2014-2016, Teo Mrnjavac + * Copyright 2018, Adriaan de Groot + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + */ + +#include "GeoIPFreeGeoIP.h" + +#include "utils/Logger.h" +#include "utils/YamlUtils.h" + +#include + +#include + +GeoIP::RegionZonePair +FreeGeoIP::processReply( const QByteArray& data ) +{ + try + { + YAML::Node doc = YAML::Load( data ); + + QVariant var = CalamaresUtils::yamlToVariant( doc ); + if ( !var.isNull() && + var.isValid() && + var.type() == QVariant::Map ) + { + QVariantMap map = var.toMap(); + if ( map.contains( "time_zone" ) && + !map.value( "time_zone" ).toString().isEmpty() ) + { + return splitTZString( map.value( "time_zone" ).toString() ); + } + } + } + catch ( YAML::Exception& e ) + { + CalamaresUtils::explainYamlException( e, data, "GeoIP data"); + } + + return qMakePair( QString(), QString() ); +} diff --git a/src/modules/locale/GeoIPFreeGeoIP.h b/src/modules/locale/GeoIPFreeGeoIP.h new file mode 100644 index 000000000..e8986db3f --- /dev/null +++ b/src/modules/locale/GeoIPFreeGeoIP.h @@ -0,0 +1,37 @@ +/* === This file is part of Calamares - === + * + * Copyright 2018, Adriaan de Groot + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + */ + +#ifndef GEOIPFREEGEOIP_H +#define GEOIPFREEGEOIP_H + +#include "GeoIP.h" + +/** @brief GeoIP lookup via freegeoip.com + * + * This is the original implementation of GeoIP lookup, + * using the FreeGeoIP service, or similar which returns + * data in the same format. + * + * The data is assumed to be in JSON format with a time_zone attribute. + */ +struct FreeGeoIP : public GeoIP +{ + virtual RegionZonePair processReply( const QByteArray& ); +} ; + +#endif diff --git a/src/modules/locale/GeoIPTests.cpp b/src/modules/locale/GeoIPTests.cpp new file mode 100644 index 000000000..2c59aa729 --- /dev/null +++ b/src/modules/locale/GeoIPTests.cpp @@ -0,0 +1,140 @@ +/* === This file is part of Calamares - === + * + * Copyright 2018, Adriaan de Groot + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + */ + +#include "GeoIPTests.h" + +#include "GeoIPFreeGeoIP.h" +#ifdef HAVE_XML +#include "GeoIPXML.h" +#endif + +#include + +QTEST_GUILESS_MAIN( GeoIPTests ) + +GeoIPTests::GeoIPTests() +{ +} + +GeoIPTests::~GeoIPTests() +{ +} + +void +GeoIPTests::initTestCase() +{ +} + +void +GeoIPTests::testJSON() +{ + static const char data[] = + "{\"time_zone\":\"Europe/Amsterdam\"}"; + + FreeGeoIP handler; + auto tz = handler.processReply( data ); + + QCOMPARE( tz.first, QLatin1String( "Europe" ) ); + QCOMPARE( tz.second, QLatin1String( "Amsterdam" ) ); + + // JSON is quite tolerant + tz = handler.processReply( "time_zone: \"Europe/Brussels\"" ); + QCOMPARE( tz.second, QLatin1String( "Brussels" ) ); + + tz = handler.processReply( "time_zone: America/New_York\n" ); + QCOMPARE( tz.first, "America" ); +} + +void +GeoIPTests::testJSONbad() +{ + static const char data[] = "time_zone: 1"; + + FreeGeoIP handler; + auto tz = handler.processReply( data ); + + tz = handler.processReply( data ); + QCOMPARE( tz.first, QString() ); + + tz = handler.processReply( "" ); + QCOMPARE( tz.first, QString() ); + + tz = handler.processReply( "404 Forbidden" ); + QCOMPARE( tz.first, QString() ); +} + + +void +GeoIPTests::testXML() +{ + static const char data[] = + R"( + 85.150.1.1 + OK + NL + NLD + Netherlands + None + None + None + + 50.0 + 4.0 + 0 + Europe/Amsterdam +)"; + +#ifdef HAVE_XML + XMLGeoIP handler; + auto tz = handler.processReply( data ); + + QCOMPARE( tz.first, QLatin1String( "Europe" ) ); + QCOMPARE( tz.second, QLatin1String( "Amsterdam" ) ); +#endif +} + +void +GeoIPTests::testXML2() +{ + static const char data[] = + "America/North Dakota/Beulah"; + +#ifdef HAVE_XML + XMLGeoIP handler; + auto tz = handler.processReply( data ); + + QCOMPARE( tz.first, QLatin1String( "America" ) ); + QCOMPARE( tz.second, QLatin1String( "North Dakota/Beulah" ) ); +#endif +} + +void +GeoIPTests::testXMLbad() +{ +#ifdef HAVE_XML + XMLGeoIP handler; + auto tz = handler.processReply( "{time_zone: \"Europe/Paris\"}" ); + QCOMPARE( tz.first, QString() ); + + tz = handler.processReply( "" ); + QCOMPARE( tz.first, QString() ); + + tz = handler.processReply( "fnord" ); + QCOMPARE( tz.first, QString() ); +#endif +} diff --git a/src/modules/locale/GeoIPTests.h b/src/modules/locale/GeoIPTests.h new file mode 100644 index 000000000..e673a0740 --- /dev/null +++ b/src/modules/locale/GeoIPTests.h @@ -0,0 +1,40 @@ +/* === This file is part of Calamares - === + * + * Copyright 2018, Adriaan de Groot + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + */ + +#ifndef GEOIPTESTS_H +#define GEOIPTESTS_H + +#include + +class GeoIPTests : public QObject +{ + Q_OBJECT +public: + GeoIPTests(); + ~GeoIPTests() override; + +private Q_SLOTS: + void initTestCase(); + void testJSON(); + void testJSONbad(); + void testXML(); + void testXML2(); + void testXMLbad(); +}; + +#endif diff --git a/src/modules/locale/GeoIPXML.cpp b/src/modules/locale/GeoIPXML.cpp new file mode 100644 index 000000000..a0f9b7a2d --- /dev/null +++ b/src/modules/locale/GeoIPXML.cpp @@ -0,0 +1,50 @@ +/* === This file is part of Calamares - === + * + * Copyright 2018, Adriaan de Groot + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + */ + +#include "GeoIPXML.h" + +#include "utils/Logger.h" + +#include +#include + +GeoIP::RegionZonePair +XMLGeoIP::processReply( const QByteArray& data ) +{ + QString domError; + int errorLine, errorColumn; + + QDomDocument doc; + if ( doc.setContent( data, false, &domError, &errorLine, &errorColumn ) ) + { + const auto tzElements = doc.elementsByTagName( "TimeZone" ); + cDebug() << "GeoIP found" << tzElements.length() << "elements"; + for ( int it = 0; it < tzElements.length(); ++it ) + { + auto e = tzElements.at(it).toElement(); + return splitTZString( e.text() ); + } + } + else + { + cWarning() << "GeoIP XML data error:" << domError << "(line" << errorLine << errorColumn << ')'; + } + + return qMakePair( QString(), QString() ); + +} diff --git a/src/modules/locale/GeoIPXML.h b/src/modules/locale/GeoIPXML.h new file mode 100644 index 000000000..8eec22a75 --- /dev/null +++ b/src/modules/locale/GeoIPXML.h @@ -0,0 +1,36 @@ +/* === This file is part of Calamares - === + * + * Copyright 2018, Adriaan de Groot + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + */ + +#ifndef GEOIPXML_H +#define GEOIPXML_H + +#include "GeoIP.h" + +/** @brief GeoIP lookup with XML data + * + * The data is assumed to be in XML format with a + * + * element, which contains the text (string) for the region/zone. This + * format is expected by, e.g. the Ubiquity installer. + */ +struct XMLGeoIP : public GeoIP +{ + virtual RegionZonePair processReply( const QByteArray& ); +} ; + +#endif diff --git a/src/modules/locale/LocaleViewStep.cpp b/src/modules/locale/LocaleViewStep.cpp index 827a1a47b..7cafd8793 100644 --- a/src/modules/locale/LocaleViewStep.cpp +++ b/src/modules/locale/LocaleViewStep.cpp @@ -1,6 +1,7 @@ /* === This file is part of Calamares - === * * Copyright 2014-2016, Teo Mrnjavac + * Copyright 2018, Adriaan de Groot * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,12 +19,19 @@ #include "LocaleViewStep.h" +#include "GeoIP.h" +#include "GeoIPFreeGeoIP.h" +#ifdef HAVE_XML +#include "GeoIPXML.h" +#endif +#include "GlobalStorage.h" +#include "JobQueue.h" #include "LocalePage.h" + #include "timezonewidget/localeglobal.h" #include "widgets/WaitingWidget.h" -#include "JobQueue.h" -#include "GlobalStorage.h" +#include "utils/CalamaresUtils.h" #include "utils/CalamaresUtilsGui.h" #include "utils/Logger.h" #include "utils/YamlUtils.h" @@ -110,54 +118,52 @@ LocaleViewStep::setUpPage() void LocaleViewStep::fetchGeoIpTimezone() { + QString actualUrl( m_geoipUrl ); + GeoIP *handler = nullptr; + + if ( m_geoipStyle.isEmpty() || m_geoipStyle == "legacy" ) + { + actualUrl.append( "/json/" ); + handler = new FreeGeoIP; + } + else if ( m_geoipStyle == "json" ) + { + handler = new FreeGeoIP; + } +#if defined(HAVE_XML) + else if ( m_geoipStyle == "xml" ) + { + handler = new XMLGeoIP; + } +#endif + else + { + cWarning() << "GeoIP Style" << m_geoipStyle << "is not recognized."; + setUpPage(); + return; + } + cDebug() << "Fetching GeoIP data from" << actualUrl; + QNetworkAccessManager *manager = new QNetworkAccessManager( this ); connect( manager, &QNetworkAccessManager::finished, [=]( QNetworkReply* reply ) { if ( reply->error() == QNetworkReply::NoError ) { - QByteArray data = reply->readAll(); - - try - { - YAML::Node doc = YAML::Load( data ); - - QVariant var = CalamaresUtils::yamlToVariant( doc ); - if ( !var.isNull() && - var.isValid() && - var.type() == QVariant::Map ) - { - QVariantMap map = var.toMap(); - if ( map.contains( "time_zone" ) && - !map.value( "time_zone" ).toString().isEmpty() ) - { - QString timezoneString = map.value( "time_zone" ).toString(); - QStringList tzParts = timezoneString.split( '/', QString::SkipEmptyParts ); - if ( tzParts.size() >= 2 ) - { - cDebug() << "GeoIP reporting" << timezoneString; - QString region = tzParts.takeFirst(); - QString zone = tzParts.join( '/' ); - m_startingTimezone = qMakePair( region, zone ); - } - } - } - } - catch ( YAML::Exception& e ) - { - CalamaresUtils::explainYamlException( e, data, "GeoIP data"); - } + auto tz = handler->processReply( reply->readAll() ); + if ( !tz.first.isEmpty() ) + m_startingTimezone = tz; + else + cWarning() << "GeoIP lookup at" << reply->url() << "failed."; } - + delete handler; reply->deleteLater(); manager->deleteLater(); setUpPage(); } ); QNetworkRequest request; - QString requestUrl = QString( "%1/json" ) - .arg( QUrl::fromUserInput( m_geoipUrl ).toString() ); - request.setUrl( QUrl( requestUrl ) ); + request.setUrl( QUrl::fromUserInput( actualUrl ) ); request.setAttribute( QNetworkRequest::FollowRedirectsAttribute, true ); manager->get( request ); } @@ -287,10 +293,6 @@ LocaleViewStep::setConfigurationMap( const QVariantMap& configurationMap ) } // Optional - if ( configurationMap.contains( "geoipUrl" ) && - configurationMap.value( "geoipUrl" ).type() == QVariant::String && - !configurationMap.value( "geoipUrl" ).toString().isEmpty() ) - { - m_geoipUrl = configurationMap.value( "geoipUrl" ).toString(); - } + m_geoipUrl = CalamaresUtils::getString( configurationMap, "geoipUrl" ); + m_geoipStyle = CalamaresUtils::getString( configurationMap, "geoipStyle" ); } diff --git a/src/modules/locale/LocaleViewStep.h b/src/modules/locale/LocaleViewStep.h index 03d1147b6..003978d6a 100644 --- a/src/modules/locale/LocaleViewStep.h +++ b/src/modules/locale/LocaleViewStep.h @@ -76,6 +76,7 @@ private: QPair< QString, QString > m_startingTimezone; QString m_localeGenPath; QString m_geoipUrl; + QString m_geoipStyle; QList< Calamares::job_ptr > m_jobs; }; diff --git a/src/modules/locale/locale.conf b/src/modules/locale/locale.conf index 48ea6d336..de8e09a6c 100644 --- a/src/modules/locale/locale.conf +++ b/src/modules/locale/locale.conf @@ -33,15 +33,38 @@ zone: "New_York" # # Leave commented out to disable GeoIP. # -# An HTTP request is made to http://*geoipUrl*/json (which just happens -# to be the GET path needed by freegeoip.net, so calling this a URL -# is a stretch). The request must return valid JSON data; there should -# be an attribute *time_zone*, with a string value set to the -# timezone, in / form. +# An HTTP request is made to *geoipUrl* -- depending on the geoipStyle, +# the URL may be modified before use. The request should return +# valid data in a suitable format, depending on geoipStyle; +# generally this includes a string value with the timezone +# in / format. # -# Suitable data looks like +# Note that this example URL works, but the service is shutting +# down in June 2018. +# +# Suitable JSON data looks like # ``` # {"time_zone":"America/New_York"} # ``` +# Suitable XML data looks like +# ``` +# Europe/Brussels +# ``` # geoipUrl: "freegeoip.net" + +# GeoIP style. Leave commented out for the "legacy" interpretation. +# This setting only makes sense if geoipUrl is set, enabliing geoIP. +# +# Possible values are: +# unset same as "legacy" +# blank same as "legacy" +# "legacy" appends "/json" to geoipUrl, above, and uses JSON format +# (which is what freegeoip.net provides there). +# "json" URL is not modified, uses JSON format. +# "xml" URL is not modified, uses XML format. +# +# The JSON format is provided by freegeoip.net, but that service is +# shutting down in June 2018. There are other providers with the same +# format. XML format is provided for Ubiquity. +geoipStyle: "legacy" diff --git a/src/modules/locale/test_geoip.cpp b/src/modules/locale/test_geoip.cpp new file mode 100644 index 000000000..5797ad9ff --- /dev/null +++ b/src/modules/locale/test_geoip.cpp @@ -0,0 +1,73 @@ +/* === This file is part of Calamares - === + * + * Copyright 2018, Adriaan de Groot + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + */ + +/** + * This is a test-application that does one GeoIP parse. + */ + +#include + +#include "GeoIPFreeGeoIP.h" +#ifdef HAVE_XML +#include "GeoIPXML.h" +#endif + +using std::cerr; + +int main(int argc, char** argv) +{ + if (argc != 2) + { + cerr << "Usage: curl url | test_geoip \n"; + return 1; + } + + GeoIP* handler = nullptr; + if ( QLatin1String( "json" ) == argv[1] ) + handler = new FreeGeoIP; +#ifdef HAVE_XML + else if ( QLatin1String( "xml" ) == argv[1] ) + handler = new XMLGeoIP; +#endif + + if ( !handler ) + { + cerr << "Unknown format '" << argv[1] << "'\n"; + return 1; + } + + QByteArray ba; + while( !std::cin.eof() ) { + char arr[1024]; + std::cin.read(arr,sizeof(arr)); + int s = std::cin.gcount(); + ba.append(arr, s); + } + + auto tz = handler->processReply( ba ); + if ( tz.first.isEmpty() ) + { + std::cout << "No TimeZone determined from input.\n"; + } + else + { + std::cout << "TimeZone Region=" << tz.first.toLatin1().constData() << "\nTimeZone Zone=" << tz.second.toLatin1().constData() << '\n'; + } + + return 0; +} diff --git a/src/modules/plasmalnf/ThemeWidget.cpp b/src/modules/plasmalnf/ThemeWidget.cpp index 125085db4..92a88197f 100644 --- a/src/modules/plasmalnf/ThemeWidget.cpp +++ b/src/modules/plasmalnf/ThemeWidget.cpp @@ -22,10 +22,39 @@ #include "utils/CalamaresUtilsGui.h" #include "utils/Logger.h" +#include "Branding.h" +#include +#include #include #include #include +#include + +/** + * Massage the given @p path to the most-likely + * path that actually contains a screenshot. For + * empty image paths, returns the QRC path for an + * empty screenshot. Returns blank if the path + * doesn't exist anywhere in the search paths. + */ +static QString _munge_imagepath( const QString& path ) +{ + if ( path.isEmpty() ) + return ":/view-preview.png"; + + if ( path.startsWith( '/' ) ) + return path; + + if ( QFileInfo::exists( path ) ) + return path; + + QFileInfo fi( QDir( Calamares::Branding::instance()->componentDirectory() ), path ); + if ( fi.exists() ) + return fi.absoluteFilePath(); + + return QString(); +} ThemeWidget::ThemeWidget(const ThemeInfo& info, QWidget* parent) : QWidget( parent ) @@ -33,27 +62,22 @@ ThemeWidget::ThemeWidget(const ThemeInfo& info, QWidget* parent) , m_check( new QRadioButton( info.name.isEmpty() ? info.id : info.name, parent ) ) , m_description( new QLabel( info.description, parent ) ) { + const QSize image_size{ + qMax(12 * CalamaresUtils::defaultFontHeight(), 120), + qMax(8 * CalamaresUtils::defaultFontHeight(), 80) }; + QHBoxLayout* layout = new QHBoxLayout( this ); this->setLayout( layout ); layout->addWidget( m_check, 1 ); - const QSize image_size{ - qMax(12 * CalamaresUtils::defaultFontHeight(), 120), - qMax(8 * CalamaresUtils::defaultFontHeight(), 80) }; - - QPixmap image( info.imagePath ); - if ( info.imagePath.isEmpty() ) - { - // Image can't possibly be valid - image = QPixmap( ":/view-preview.png" ); - } - else if ( image.isNull() ) + QPixmap image( _munge_imagepath( info.imagePath ) ); + if ( image.isNull() ) { // Not found or not specified, so convert the name into some (horrible, likely) // color instead. image = QPixmap( image_size ); - uint hash_color = qHash( info.imagePath.isEmpty() ? info.id : info.imagePath ); + auto hash_color = qHash( info.imagePath.isEmpty() ? info.id : info.imagePath ); cDebug() << "Theme image" << info.imagePath << "not found, hash" << hash_color; image.fill( QColor( QRgb( hash_color ) ) ); } @@ -62,6 +86,9 @@ ThemeWidget::ThemeWidget(const ThemeInfo& info, QWidget* parent) QLabel* image_label = new QLabel( this ); image_label->setPixmap( image ); + image_label->setMinimumSize( image_size ); + image_label->setMaximumSize( image_size ); + image_label->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); layout->addWidget( image_label, 1 ); layout->addWidget( m_description, 3 ); diff --git a/src/modules/plasmalnf/plasmalnf.conf b/src/modules/plasmalnf/plasmalnf.conf index 85df64f0a..a954c685a 100644 --- a/src/modules/plasmalnf/plasmalnf.conf +++ b/src/modules/plasmalnf/plasmalnf.conf @@ -32,6 +32,10 @@ lnftool: "/usr/bin/lookandfeeltool" # Themes with no image set at all get a "missing screenshot" image; if the # image file is not found, they get a color swatch based on the image name. # +# The image may be an absolute path. If it is a relative path, though, +# it is searched in the current directory and in the branding directory +# (i.e. relative to the directory where your branding.desc lives). +# # Valid forms of entries in the *themes* key: # - A single string (unquoted), which is the theme id # - A pair of *theme* and *image* keys, e.g.