From 01e7e02bcd86c3b86cb62396898f95c8da1ba099 Mon Sep 17 00:00:00 2001 From: Anton Thomasson Date: Sat, 19 Jun 2021 18:16:42 +0200 Subject: [PATCH] Add ipps support --- qml/harbour-seaprint.qml | 7 +- qml/pages/FirstPage.qml | 1 - qml/pages/SettingsPage.qml | 9 + src/ippdiscovery.cpp | 251 +++++++++++++++++++------ src/ippdiscovery.h | 13 ++ src/ippprinter.cpp | 64 ++++--- src/ippprinter.h | 5 +- src/overrider.cpp | 1 - translations/harbour-seaprint-de.ts | 8 + translations/harbour-seaprint-es.ts | 8 + translations/harbour-seaprint-fr.ts | 8 + translations/harbour-seaprint-nl.ts | 8 + translations/harbour-seaprint-pl.ts | 8 + translations/harbour-seaprint-zh_CN.ts | 8 + translations/harbour-seaprint.ts | 8 + 15 files changed, 315 insertions(+), 92 deletions(-) diff --git a/qml/harbour-seaprint.qml b/qml/harbour-seaprint.qml index 8eb8575..6542d21 100644 --- a/qml/harbour-seaprint.qml +++ b/qml/harbour-seaprint.qml @@ -5,6 +5,7 @@ import Nemo.Notifications 1.0 import Nemo.Configuration 1.0 import seaprint.mimer 1.0 import seaprint.settings 1.0 + import "pages" import "components" @@ -151,9 +152,11 @@ ApplicationWindow defaultValue: SeaPrintSettings.alwaysUseMediaColDefault } - Component.onCompleted: + ConfigurationValue { - console.log(SeaPrintSettings.ignoreSslErrorsPath, SeaPrintSettings.ignoreSslErrorsDefault) + id: ignoreSslErrorsSetting + key: SeaPrintSettings.ignoreSslErrorsPath + defaultValue: SeaPrintSettings.ignoreSslErrorsDefault } } diff --git a/qml/pages/FirstPage.qml b/qml/pages/FirstPage.qml index 2c7f46b..5b9c7bf 100644 --- a/qml/pages/FirstPage.qml +++ b/qml/pages/FirstPage.qml @@ -26,7 +26,6 @@ Page { else { IppDiscovery.favourites = [] } - } property bool initialSSIDchange: true diff --git a/qml/pages/SettingsPage.qml b/qml/pages/SettingsPage.qml index b635c5b..abcea22 100644 --- a/qml/pages/SettingsPage.qml +++ b/qml/pages/SettingsPage.qml @@ -53,6 +53,15 @@ Page { } } + TextSwitch { + text: qsTr("Ignore SSL errors") + description: qsTr("In order to work with self-signed certificates of printers and CUPS instances, SSL errors needs to be ignored.") + checked: ignoreSslErrorsSetting.value + onCheckedChanged: { + ignoreSslErrorsSetting.value = checked + } + } + } } } diff --git a/src/ippdiscovery.cpp b/src/ippdiscovery.cpp index f5936fc..cf0af79 100644 --- a/src/ippdiscovery.cpp +++ b/src/ippdiscovery.cpp @@ -1,5 +1,6 @@ #include "ippdiscovery.h" #include "ippprinter.h" +#include "settings.h" #include #define A 1 @@ -10,16 +11,6 @@ #define ALL 255 //for querying -void put_addr(Bytestream& bts, QStringList addr) -{ - for(int i = 0; i < addr.length(); i++) - { - QString elem = addr[i]; - bts << (quint8)elem.size() << elem.toStdString(); - } - bts << (quint8)0; -} - QStringList get_addr(Bytestream& bts) { QStringList addr; @@ -50,10 +41,9 @@ QStringList get_addr(Bytestream& bts) IppDiscovery::IppDiscovery() : QStringListModel(), QQuickImageProvider(QQuickImageProvider::Image, ForceAsynchronousImageLoading) { socket = new QUdpSocket(this); - connect(socket, SIGNAL(readyRead()), - this, SLOT(readPendingDatagrams())); - connect(this, SIGNAL(favouritesChanged()), - this, SLOT(update())); + connect(socket, &QUdpSocket::readyRead, this, &IppDiscovery::readPendingDatagrams); + connect(this, &IppDiscovery::favouritesChanged, this, &IppDiscovery::cleanUpdate); + _transactionid = 0; } IppDiscovery::~IppDiscovery() { @@ -79,67 +69,201 @@ IppDiscovery* IppDiscovery::instance() } void IppDiscovery::discover() { - sendQuery(PTR, {"_ipp","_tcp","local"}); + sendQuery(PTR, {"_ipp._tcp.local", "_ipps._tcp.local"}); } void IppDiscovery::reset() { - _ipp = QStringList(); - _rps = QMap(); - _ports = QMap(); - _targets = QMap(); + _ipp.clear(); + _rps.clear(); + _ports.clear(); + _targets.clear(); - _AAs = QMultiMap(); - _AAAAs = QMultiMap(); + _AAs.clear(); + _AAAAs.clear(); + _outstandingQueries.clear(); + + setStringList({}); discover(); } -void IppDiscovery::sendQuery(quint16 qtype, QStringList addr) { - qDebug() << "discovering" << qtype << addr; + +void IppDiscovery::sendQuery(quint16 qtype, QStringList addrs) { + addrs.removeDuplicates(); + + QTime now = QTime::currentTime(); + QTime aWhileAgo = now.addSecs(-1); + + foreach(QString oq, _outstandingQueries.keys()) + { + if(_outstandingQueries[oq] < aWhileAgo) + { // Housekeeping for _outstandingQueries + _outstandingQueries.remove(oq); + } + else if(addrs.contains(oq)) + { // we recently asked about this, remove it + addrs.removeOne(oq); + } + } + + if(addrs.empty()) + { + qDebug() << "nothing to do"; + return; + } + + qDebug() << "discovering" << qtype << addrs; Bytestream query; - quint16 transactionid = 0; - quint16 flags = 0; - quint16 questions = 1; + QMap suffixPositions; - query << transactionid << flags << questions << (quint16)0 << (quint16)0 << (quint16)0; - put_addr(query, addr); - query << qtype << (quint16)0x0001; + quint16 flags = 0; + quint16 questions = addrs.length(); + + query << _transactionid++ << flags << questions << (quint16)0 << (quint16)0 << (quint16)0; + + foreach(QString addr, addrs) + { + _outstandingQueries.insert(addr, now); + + QStringList addrParts = addr.split("."); + QString addrPart, restAddr; + while(!addrParts.isEmpty()) + { + restAddr = addrParts.join("."); + if(suffixPositions.contains(restAddr)) + { + query << (quint16)(0xc000 | (0x0fff & suffixPositions[restAddr])); + break; + } + else + { + // We are putting in at least one part of the address, remember where that was + suffixPositions.insert(restAddr, query.size()); + addrPart = addrParts.takeFirst(); + query << (quint8)addrPart.size() << addrPart.toStdString(); + } + } + if(addrParts.isEmpty()) + { + // Whole addr was put in without c-pointers, 0-terminate it + query << (quint8)0; + } + + query << qtype << (quint16)0x0001; + + } QByteArray bytes((char*)(query.raw()), query.size()); socket->writeDatagram(bytes, QHostAddress("224.0.0.251"), 5353); } +QString make_addr(QString proto, int defaultPort, quint16 port, QString ip, QString rp) +{ + QString maybePort = port != defaultPort ? ":"+QString::number(port) : ""; + QString addr = proto+"://"+ip+maybePort+"/"+rp; + return addr; +} + +QString make_ipp_addr(quint16 port, QString ip, QString rp) +{ + return make_addr("ipp", 631, port, ip, rp); +} + +QString make_ipps_addr(quint16 port, QString ip, QString rp) +{ + return make_addr("ipps", 443, port, ip, rp); +} + +void IppDiscovery::cleanUpdate() +{ + setStringList(_favourites); + update(); +} void IppDiscovery::update() { QStringList found; + QList> ippsIpRps; - for(QStringList::Iterator it = _ipp.begin(); it != _ipp.end(); it++) + foreach(QString it, _ipps) { - quint16 port = _ports[*it]; - QString target = _targets[*it]; - QString rp = _rps[*it]; + quint16 port = _ports[it]; + QString target = _targets[it]; + QString rp = _rps[it]; for(QMultiMap::Iterator ait = _AAs.begin(); ait != _AAs.end(); ait++) { if(ait.key() == target) { QString ip = ait.value(); - QString maybePort = port != 631 ? ":"+QString::number(port) : ""; - QString addr = "ipp://"+ip+maybePort+"/"+rp; + QString addr = make_ipps_addr(port, ip, rp); if(!found.contains(addr)) { + ippsIpRps.append({ip, rp}); found.append(addr); - found.sort(Qt::CaseInsensitive); } } } } - qDebug() << _favourites << found; - this->setStringList(_favourites+found); + foreach(QString it, _ipp) + { + quint16 port = _ports[it]; + QString target = _targets[it]; + QString rp = _rps[it]; + + for(QMultiMap::Iterator ait = _AAs.begin(); ait != _AAs.end(); ait++) + { + if(ait.key() == target) + { + QString ip = ait.value(); + QString addr = make_ipp_addr(port, ip, rp); + + // IP+RP assumed unique, don't add ipp version of the printer if ipps already added + if(!found.contains(addr) && !ippsIpRps.contains({ip, rp})) + { + found.append(addr); + } + } + } + } + + found.sort(Qt::CaseInsensitive); + + // Counting on that _ipp duplicates doesn't resolve fully any erlier than their _ipps counterpart + + // TODO?: replace this with some logica that can bpoth add and remove + // and it can consider _favourites, so we can drop cleanUpdate + foreach(QString f, found) + { + if(!this->stringList().contains(f)) + { + if(this->insertRow(this->rowCount())) + { + QModelIndex index = this->index(this->rowCount() - 1, 0); + this->setData(index, f); + } + } + } +} + +void IppDiscovery::updateAndQueryPtrs(QStringList& ptrs, QStringList new_ptrs) +{ + new_ptrs.removeDuplicates(); + foreach(QString ptr, new_ptrs) + { + if(!ptrs.contains(ptr)) + { + ptrs.append(ptr); + } + // If pointer does not resolve to a target or is missing information, query about it + if(!_targets.contains(ptr) || !_ports.contains(ptr) || !_rps.contains(ptr)) + { // if the PTR doesn't already resolve, ask for everything about it + sendQuery(ALL, {ptr}); + } + } } void IppDiscovery::readPendingDatagrams() @@ -152,6 +276,7 @@ void IppDiscovery::readPendingDatagrams() quint16 senderPort; QStringList new_ipp_ptrs; + QStringList new_ipps_ptrs; QStringList new_targets; socket->readDatagram((char*)(resp.raw()), size, &sender, &senderPort); @@ -186,6 +311,10 @@ void IppDiscovery::readPendingDatagrams() { new_ipp_ptrs.append(tmpname); } + else if(aaddr.endsWith("_ipps._tcp.local")) + { + new_ipps_ptrs.append(tmpname); + } } else if(atype == TXT) { @@ -208,14 +337,20 @@ void IppDiscovery::readPendingDatagrams() QString target = get_addr(resp).join("."); _ports[aaddr] = port; _targets[aaddr] = target; - new_targets.append(target); + if(!new_targets.contains(target)) + { + new_targets.append(target); + } } else if(atype == A) { quint32 addr; resp >> addr; QHostAddress haddr(addr); - _AAs.insert(aaddr, haddr.toString()); + if(!_AAs.contains(aaddr, haddr.toString())) + { + _AAs.insert(aaddr, haddr.toString()); + } } else { @@ -231,7 +366,9 @@ void IppDiscovery::readPendingDatagrams() return; } qDebug() << "new ipp ptrs" << new_ipp_ptrs; + qDebug() << "new ipps ptrs" << new_ipps_ptrs; qDebug() << "ipp ptrs" << _ipp; + qDebug() << "ipp ptrs" << _ipps; qDebug() << "rps" << _rps; qDebug() << "ports" << _ports; qDebug() << "new targets" << new_targets; @@ -239,27 +376,27 @@ void IppDiscovery::readPendingDatagrams() qDebug() << "AAs" << _AAs; qDebug() << "AAAAs" << _AAAAs; - for(QStringList::Iterator it = new_ipp_ptrs.begin(); it != new_ipp_ptrs.end(); it++) - { - if(!_ipp.contains(*it)) - { - _ipp.append(*it); - } - // If pointer does not resolve to a target or is missing information, query about it - if( !_targets.contains(*it) || !_ports.contains(*it) || !_rps.contains(*it)) - { // if the PTR doesn't already resolve, ask for everything about it - sendQuery(ALL, it->split('.')); - } - } - for(QStringList::Iterator it = new_targets.begin(); it != new_targets.end(); it++) + // These will send one query per unique new ptr. + // some responders doesn't give TXT records for more than one thing at at time :( + updateAndQueryPtrs(_ipp, new_ipp_ptrs); + updateAndQueryPtrs(_ipps, new_ipps_ptrs); + + QStringList unresolvedAddrs; + + foreach(QString target, new_targets) { // If target does not resolve to an address, query about it - if(!_AAs.contains(*it)) + if(!_AAs.contains(target)) { - sendQuery(ALL, it->split('.')); + unresolvedAddrs.append(target); } } + if(!unresolvedAddrs.empty()) + { + sendQuery(A, unresolvedAddrs); + } + } this->update(); @@ -290,9 +427,7 @@ QImage IppDiscovery::requestImage(const QString &id, QSize *size, const QSize &r QNetworkRequest request(url); request.setHeader(QNetworkRequest::UserAgentHeader, "SeaPrint " SEAPRINT_VERSION); - - connect(nam, &QNetworkAccessManager::sslErrors, - &IppPrinter::ignoreKnownSslErrors); + connect(nam, &QNetworkAccessManager::sslErrors, &IppPrinter::onSslErrors); QNetworkReply* reply = nam->get(request); diff --git a/src/ippdiscovery.h b/src/ippdiscovery.h index f7df8cd..091b8f1 100644 --- a/src/ippdiscovery.h +++ b/src/ippdiscovery.h @@ -17,20 +17,25 @@ public: static IppDiscovery* instance(); Q_PROPERTY(QStringList favourites MEMBER _favourites NOTIFY favouritesChanged) + Q_INVOKABLE void discover(); Q_INVOKABLE void reset(); signals: void favouritesChanged(); + void ignoreSslErrorsChanged(); public slots: void readPendingDatagrams(); + void cleanUpdate(); void update(); protected: private: static IppDiscovery* m_Instance; + quint16 _transactionid; + IppDiscovery(); ~IppDiscovery(); IppDiscovery(const IppDiscovery &); @@ -38,9 +43,15 @@ private: void sendQuery(quint16 qtype, QStringList addr); + void sendQuery(quint16 qtype, QStringList prefixes, QStringList suffixes); + + void updateAndQueryPtrs(QStringList& ptrs, QStringList new_ptrs); + + QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override; QStringList _ipp; + QStringList _ipps; QMap _rps; QMap _ports; QMap _targets; @@ -48,6 +59,8 @@ private: QMultiMap _AAs; QMultiMap _AAAAs; + QMap _outstandingQueries; + QStringList _favourites; QUdpSocket* socket; }; diff --git a/src/ippprinter.cpp b/src/ippprinter.cpp index e079912..d979238 100644 --- a/src/ippprinter.cpp +++ b/src/ippprinter.cpp @@ -13,16 +13,16 @@ IppPrinter::IppPrinter() _job_cancel_nam = new QNetworkAccessManager(this); connect(_nam, &QNetworkAccessManager::finished, this, &IppPrinter::getPrinterAttributesFinished); - connect(_nam, &QNetworkAccessManager::sslErrors, this, &IppPrinter::ignoreKnownSslErrors); + connect(_nam, &QNetworkAccessManager::sslErrors, &IppPrinter::onSslErrors); connect(_print_nam, &QNetworkAccessManager::finished, this, &IppPrinter::printRequestFinished); - connect(_print_nam, &QNetworkAccessManager::sslErrors, this, &IppPrinter::ignoreKnownSslErrors); + connect(_print_nam, &QNetworkAccessManager::sslErrors, &IppPrinter::onSslErrors); connect(_jobs_nam, &QNetworkAccessManager::finished,this, &IppPrinter::getJobsRequestFinished); - connect(_jobs_nam, &QNetworkAccessManager::sslErrors, this, &IppPrinter::ignoreKnownSslErrors); + connect(_jobs_nam, &QNetworkAccessManager::sslErrors, &IppPrinter::onSslErrors); connect(_job_cancel_nam, &QNetworkAccessManager::finished,this, &IppPrinter::cancelJobFinished); - connect(_job_cancel_nam, &QNetworkAccessManager::sslErrors, this, &IppPrinter::ignoreKnownSslErrors); + connect(_job_cancel_nam, &QNetworkAccessManager::sslErrors, &IppPrinter::onSslErrors); QObject::connect(this, &IppPrinter::urlChanged, this, &IppPrinter::onUrlChanged); qRegisterMetaType("QTemporaryFile*"); @@ -68,9 +68,9 @@ void IppPrinter::setUrl(QString url_s) qDebug() << url.scheme(); - if(url.scheme() != "ipp" /* or ipps */ && url.scheme() != "file") + // If not already a good scheme, try to fixup, or give an empty url + if(url.scheme() != "ipp" && url.scheme() != "ipps" && url.scheme() != "file") { - //if https -> ipps, else: if(url.scheme() == "") { url = QUrl("ipp://"+url_s); // Why isn't setScheme working? @@ -78,6 +78,9 @@ void IppPrinter::setUrl(QString url_s) else if (url.scheme() == "http") { url.setScheme("ipp"); } + else if (url.scheme() == "https") { + url.setScheme("ipps"); + } else { url = QUrl(); } @@ -104,6 +107,12 @@ void IppPrinter::refresh() { // _additionalDocumentFormats = QStringList(); // emit additionalDocumentFormatsChanged(); + // FFFFUUUU + _nam->clearAccessCache(); + _jobs_nam->clearAccessCache(); + _job_cancel_nam->clearAccessCache(); + _print_nam->clearAccessCache(); + if(_url.scheme() == "file") { _attrs = QJsonObject(); @@ -163,7 +172,7 @@ void IppPrinter::UpdateAdditionalDocumentFormats() void IppPrinter::getPrinterAttributesFinished(QNetworkReply *reply) { - qDebug() << reply->error() << reply->errorString() << reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toString(); + qDebug() << reply->request().url() << reply->error() << reply->errorString() << reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toString(); _attrs = QJsonObject(); if(reply->error() == QNetworkReply::NoError) @@ -254,25 +263,14 @@ void IppPrinter::cancelJobFinished(QNetworkReply *reply) } - -void IppPrinter::ignoreKnownSslErrors(QNetworkReply *reply, const QList &errors) +void IppPrinter::onSslErrors(QNetworkReply *reply, const QList &errors) { - QList IgnoredSslErrors = {QSslError::NoError, - QSslError::SelfSignedCertificate, - QSslError::HostNameMismatch, - QSslError::UnableToGetLocalIssuerCertificate, - QSslError::UnableToVerifyFirstCertificate - }; - - qDebug() << errors; - for (QList::const_iterator it = errors.constBegin(); it != errors.constEnd(); it++) { - if(!IgnoredSslErrors.contains(it->error())) { - qDebug() << "Bad error: " << int(it->error()) << it->error(); - return; - } + bool ignore = Settings::instance()->ignoreSslErrors(); + qDebug() << reply->request().url() << "SSL handshake failed" << errors << ignore; + if(ignore) + { + reply->ignoreSslErrors(errors); } - // For whatever reason, it doesn't work to pass IgnoredSslErrors here - reply->ignoreSslErrors(errors); } void IppPrinter::convertDone(QNetworkRequest request, QTemporaryFile* data) @@ -600,10 +598,21 @@ bool IppPrinter::cancelJob(qint32 jobId) { } QUrl IppPrinter::httpUrl() { + qDebug() << _url; QUrl url = _url; - url.setScheme("http"); - if(url.port() == -1) { - url.setPort(631); + if(url.scheme() == "ipps") + { + url.setScheme("https"); + if(url.port() == -1) { + url.setPort(443); + } + } + else + { + url.setScheme("http"); + if(url.port() == -1) { + url.setPort(631); + } } return url; } @@ -614,6 +623,7 @@ QNetworkRequest IppPrinter::mkReq() { request.setHeader(QNetworkRequest::ContentTypeHeader, "application/ipp"); request.setHeader(QNetworkRequest::UserAgentHeader, "SeaPrint " SEAPRINT_VERSION); request.setRawHeader("Accept-Encoding", "identity"); + request.setSslConfiguration(QSslConfiguration()); return request; } diff --git a/src/ippprinter.h b/src/ippprinter.h index 1f60afc..7828d5e 100644 --- a/src/ippprinter.h +++ b/src/ippprinter.h @@ -5,6 +5,7 @@ #include #include "ippmsg.h" #include "convertworker.h" +#include class IppPrinter : public QObject { @@ -17,7 +18,6 @@ class IppPrinter : public QObject Q_PROPERTY(QString busyMessage MEMBER _busyMessage NOTIFY busyMessageChanged) Q_PROPERTY(QString progress MEMBER _progress NOTIFY progressChanged) - public: IppPrinter(); ~IppPrinter(); @@ -73,7 +73,7 @@ public slots: void getJobsRequestFinished(QNetworkReply* reply); void cancelJobFinished(QNetworkReply* reply); - static void ignoreKnownSslErrors(QNetworkReply *reply, const QList &errors); + static void onSslErrors(QNetworkReply *reply, const QList &errors); void convertDone(QNetworkRequest request, QTemporaryFile* data); void convertFailed(QString message); @@ -108,7 +108,6 @@ private: QThread _workerThread; ConvertWorker* _worker; - }; #endif // IPPPRINTER_H diff --git a/src/overrider.cpp b/src/overrider.cpp index 84969e8..450b392 100644 --- a/src/overrider.cpp +++ b/src/overrider.cpp @@ -7,7 +7,6 @@ Overrider::Overrider() { // QFile file(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation)+"/overrides"); QFile file(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)+"/.seaprint_overrides"); - qDebug() << "AAAAAAAAAAAAAAA" << file.fileName(); if(file.open(QIODevice::ReadOnly)) { QJsonDocument JsonDocument = QJsonDocument::fromJson(file.readAll()); diff --git a/translations/harbour-seaprint-de.ts b/translations/harbour-seaprint-de.ts index 421bc70..66197ff 100644 --- a/translations/harbour-seaprint-de.ts +++ b/translations/harbour-seaprint-de.ts @@ -511,6 +511,14 @@ Settings Einstellungen + + Ignore SSL errors + + + + In order to work with self-signed certificates of printers and CUPS instances, SSL errors needs to be ignored. + + SupplyItem diff --git a/translations/harbour-seaprint-es.ts b/translations/harbour-seaprint-es.ts index b2753a2..bd1051b 100644 --- a/translations/harbour-seaprint-es.ts +++ b/translations/harbour-seaprint-es.ts @@ -511,6 +511,14 @@ Settings Ajustes + + Ignore SSL errors + + + + In order to work with self-signed certificates of printers and CUPS instances, SSL errors needs to be ignored. + + SupplyItem diff --git a/translations/harbour-seaprint-fr.ts b/translations/harbour-seaprint-fr.ts index d08bad3..a736be6 100644 --- a/translations/harbour-seaprint-fr.ts +++ b/translations/harbour-seaprint-fr.ts @@ -511,6 +511,14 @@ Settings Paramètres + + Ignore SSL errors + + + + In order to work with self-signed certificates of printers and CUPS instances, SSL errors needs to be ignored. + + SupplyItem diff --git a/translations/harbour-seaprint-nl.ts b/translations/harbour-seaprint-nl.ts index a68a50e..280b23a 100644 --- a/translations/harbour-seaprint-nl.ts +++ b/translations/harbour-seaprint-nl.ts @@ -511,6 +511,14 @@ Settings Instellingen + + Ignore SSL errors + + + + In order to work with self-signed certificates of printers and CUPS instances, SSL errors needs to be ignored. + + SupplyItem diff --git a/translations/harbour-seaprint-pl.ts b/translations/harbour-seaprint-pl.ts index 9f7d3b6..f7d8e67 100644 --- a/translations/harbour-seaprint-pl.ts +++ b/translations/harbour-seaprint-pl.ts @@ -511,6 +511,14 @@ Use the attribute media-col instead of media for paper sizes. I.e. do parametric selection of print media rather than by name. If you use zero print margins, parametric selection will be used regardless of this setting. + + Ignore SSL errors + + + + In order to work with self-signed certificates of printers and CUPS instances, SSL errors needs to be ignored. + + SupplyItem diff --git a/translations/harbour-seaprint-zh_CN.ts b/translations/harbour-seaprint-zh_CN.ts index 77f3cd0..a32bc22 100644 --- a/translations/harbour-seaprint-zh_CN.ts +++ b/translations/harbour-seaprint-zh_CN.ts @@ -511,6 +511,14 @@ Settings 设置 + + Ignore SSL errors + + + + In order to work with self-signed certificates of printers and CUPS instances, SSL errors needs to be ignored. + + SupplyItem diff --git a/translations/harbour-seaprint.ts b/translations/harbour-seaprint.ts index f878f07..30e85e7 100644 --- a/translations/harbour-seaprint.ts +++ b/translations/harbour-seaprint.ts @@ -511,6 +511,14 @@ Settings + + Ignore SSL errors + + + + In order to work with self-signed certificates of printers and CUPS instances, SSL errors needs to be ignored. + + SupplyItem