2019-12-01 22:27:00 +03:00
|
|
|
#include "ippdiscovery.h"
|
2021-02-13 19:13:11 +03:00
|
|
|
#include "ippprinter.h"
|
2021-06-19 19:16:42 +03:00
|
|
|
#include "settings.h"
|
2020-01-20 22:11:41 +03:00
|
|
|
#include <seaprint_version.h>
|
|
|
|
|
2019-12-01 22:27:00 +03:00
|
|
|
#define A 1
|
|
|
|
#define PTR 12
|
|
|
|
#define TXT 16
|
|
|
|
#define AAAA 28
|
|
|
|
#define SRV 33
|
|
|
|
|
2020-01-02 23:05:04 +03:00
|
|
|
#define ALL 255 //for querying
|
|
|
|
|
2019-12-01 22:27:00 +03:00
|
|
|
QStringList get_addr(Bytestream& bts)
|
|
|
|
{
|
|
|
|
QStringList addr;
|
|
|
|
while(true)
|
|
|
|
{
|
|
|
|
if(bts.nextU8(0))
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if ((bts.peekU8()&0xc0)==0xc0)
|
|
|
|
{
|
|
|
|
quint16 ref = bts.getU16() & 0x0fff;
|
|
|
|
Bytestream tmp = bts;
|
|
|
|
tmp.setPos(ref);
|
|
|
|
addr += get_addr(tmp);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::string elem;
|
|
|
|
bts/bts.getU8() >> elem;
|
|
|
|
addr.append(QString(elem.c_str()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
|
2021-02-13 16:58:08 +03:00
|
|
|
IppDiscovery::IppDiscovery() : QStringListModel(), QQuickImageProvider(QQuickImageProvider::Image, ForceAsynchronousImageLoading)
|
2019-12-01 22:27:00 +03:00
|
|
|
{
|
|
|
|
socket = new QUdpSocket(this);
|
2021-06-19 19:16:42 +03:00
|
|
|
connect(socket, &QUdpSocket::readyRead, this, &IppDiscovery::readPendingDatagrams);
|
|
|
|
connect(this, &IppDiscovery::favouritesChanged, this, &IppDiscovery::cleanUpdate);
|
|
|
|
_transactionid = 0;
|
2019-12-01 22:27:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
IppDiscovery::~IppDiscovery() {
|
|
|
|
delete socket;
|
|
|
|
}
|
|
|
|
|
2020-08-29 18:32:11 +03:00
|
|
|
IppDiscovery* IppDiscovery::m_Instance = nullptr;
|
2020-01-05 20:41:24 +03:00
|
|
|
|
|
|
|
IppDiscovery* IppDiscovery::instance()
|
|
|
|
{
|
|
|
|
static QMutex mutex;
|
|
|
|
if (!m_Instance)
|
|
|
|
{
|
|
|
|
mutex.lock();
|
|
|
|
|
|
|
|
if (!m_Instance)
|
|
|
|
m_Instance = new IppDiscovery;
|
|
|
|
|
|
|
|
mutex.unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
return m_Instance;
|
|
|
|
}
|
|
|
|
|
2019-12-01 22:27:00 +03:00
|
|
|
void IppDiscovery::discover() {
|
2021-06-19 19:16:42 +03:00
|
|
|
sendQuery(PTR, {"_ipp._tcp.local", "_ipps._tcp.local"});
|
2020-01-02 23:05:04 +03:00
|
|
|
}
|
|
|
|
|
2020-01-02 23:28:35 +03:00
|
|
|
void IppDiscovery::reset() {
|
2021-06-19 19:16:42 +03:00
|
|
|
_ipp.clear();
|
|
|
|
_rps.clear();
|
|
|
|
_ports.clear();
|
|
|
|
_targets.clear();
|
|
|
|
|
|
|
|
_AAs.clear();
|
|
|
|
_AAAAs.clear();
|
2020-01-02 23:28:35 +03:00
|
|
|
|
2021-06-19 19:16:42 +03:00
|
|
|
_outstandingQueries.clear();
|
2020-01-02 23:28:35 +03:00
|
|
|
|
2021-06-19 19:16:42 +03:00
|
|
|
setStringList({});
|
2020-01-02 23:28:35 +03:00
|
|
|
discover();
|
|
|
|
}
|
|
|
|
|
2021-06-19 19:16:42 +03:00
|
|
|
|
|
|
|
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;
|
2019-12-01 22:27:00 +03:00
|
|
|
|
|
|
|
Bytestream query;
|
2021-06-19 19:16:42 +03:00
|
|
|
QMap<QString, quint16> suffixPositions;
|
|
|
|
|
2019-12-01 22:27:00 +03:00
|
|
|
quint16 flags = 0;
|
2021-06-19 19:16:42 +03:00
|
|
|
quint16 questions = addrs.length();
|
2019-12-01 22:27:00 +03:00
|
|
|
|
2021-06-19 19:16:42 +03:00
|
|
|
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;
|
|
|
|
|
|
|
|
}
|
2019-12-01 22:27:00 +03:00
|
|
|
|
|
|
|
QByteArray bytes((char*)(query.raw()), query.size());
|
|
|
|
socket->writeDatagram(bytes, QHostAddress("224.0.0.251"), 5353);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-06-19 19:16:42 +03:00
|
|
|
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();
|
|
|
|
}
|
2020-01-02 23:05:04 +03:00
|
|
|
|
2019-12-01 22:27:00 +03:00
|
|
|
void IppDiscovery::update()
|
|
|
|
{
|
2020-01-02 23:28:35 +03:00
|
|
|
QStringList found;
|
2021-06-19 19:16:42 +03:00
|
|
|
QList<QPair<QString,QString>> ippsIpRps;
|
2020-01-02 23:28:35 +03:00
|
|
|
|
2021-06-19 19:16:42 +03:00
|
|
|
foreach(QString it, _ipps)
|
2020-01-02 23:05:04 +03:00
|
|
|
{
|
2021-06-19 19:16:42 +03:00
|
|
|
quint16 port = _ports[it];
|
|
|
|
QString target = _targets[it];
|
|
|
|
QString rp = _rps[it];
|
2020-01-02 23:05:04 +03:00
|
|
|
|
|
|
|
for(QMultiMap<QString,QString>::Iterator ait = _AAs.begin(); ait != _AAs.end(); ait++)
|
|
|
|
{
|
|
|
|
if(ait.key() == target)
|
|
|
|
{
|
|
|
|
QString ip = ait.value();
|
2021-06-19 19:16:42 +03:00
|
|
|
QString addr = make_ipps_addr(port, ip, rp);
|
2020-01-02 23:28:35 +03:00
|
|
|
if(!found.contains(addr))
|
2021-06-19 19:16:42 +03:00
|
|
|
{
|
|
|
|
ippsIpRps.append({ip, rp});
|
|
|
|
found.append(addr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach(QString it, _ipp)
|
|
|
|
{
|
|
|
|
quint16 port = _ports[it];
|
|
|
|
QString target = _targets[it];
|
|
|
|
QString rp = _rps[it];
|
|
|
|
|
|
|
|
for(QMultiMap<QString,QString>::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}))
|
2020-01-02 23:05:04 +03:00
|
|
|
{
|
2020-01-02 23:28:35 +03:00
|
|
|
found.append(addr);
|
2020-01-02 23:05:04 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-19 19:16:42 +03:00
|
|
|
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});
|
|
|
|
}
|
|
|
|
}
|
2019-12-01 22:27:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void IppDiscovery::readPendingDatagrams()
|
|
|
|
{
|
|
|
|
while (socket->hasPendingDatagrams()) {
|
|
|
|
|
|
|
|
size_t size = socket->pendingDatagramSize();
|
2020-08-29 18:26:26 +03:00
|
|
|
Bytestream resp(size);
|
2019-12-01 22:27:00 +03:00
|
|
|
QHostAddress sender;
|
|
|
|
quint16 senderPort;
|
|
|
|
|
2020-04-30 19:45:43 +03:00
|
|
|
QStringList new_ipp_ptrs;
|
2021-06-19 19:16:42 +03:00
|
|
|
QStringList new_ipps_ptrs;
|
2020-04-30 19:45:43 +03:00
|
|
|
QStringList new_targets;
|
2019-12-01 22:27:00 +03:00
|
|
|
|
|
|
|
socket->readDatagram((char*)(resp.raw()), size, &sender, &senderPort);
|
|
|
|
sender = QHostAddress(sender.toIPv4Address());
|
|
|
|
|
|
|
|
quint16 transactionid, flags, questions, answerRRs, authRRs, addRRs;
|
|
|
|
|
2020-01-04 22:22:13 +03:00
|
|
|
try {
|
2019-12-01 22:27:00 +03:00
|
|
|
|
2020-01-04 22:22:13 +03:00
|
|
|
resp >> transactionid >> flags >> questions >> answerRRs >> authRRs >> addRRs;
|
2019-12-01 22:27:00 +03:00
|
|
|
|
2020-01-04 22:22:13 +03:00
|
|
|
for(quint16 i = 0; i < questions; i++)
|
|
|
|
{
|
|
|
|
quint16 qtype, qflags;
|
|
|
|
QString qaddr = get_addr(resp).join('.');
|
|
|
|
resp >> qtype >> qflags;
|
|
|
|
}
|
2019-12-01 22:27:00 +03:00
|
|
|
|
2020-01-04 22:22:13 +03:00
|
|
|
for(quint16 i = 0; i < answerRRs; i++)
|
2019-12-01 22:27:00 +03:00
|
|
|
{
|
2020-01-04 22:22:13 +03:00
|
|
|
quint16 atype, aflags, len;
|
|
|
|
quint32 ttl;
|
|
|
|
|
|
|
|
QString aaddr = get_addr(resp).join('.');
|
|
|
|
resp >> atype >> aflags >> ttl >> len;
|
|
|
|
|
|
|
|
quint16 pos_before = resp.pos();
|
|
|
|
if (atype == PTR)
|
2020-01-02 23:05:04 +03:00
|
|
|
{
|
2020-01-04 22:22:13 +03:00
|
|
|
QString tmpname = get_addr(resp).join(".");
|
|
|
|
if(aaddr.endsWith("_ipp._tcp.local"))
|
|
|
|
{
|
2020-04-30 19:45:43 +03:00
|
|
|
new_ipp_ptrs.append(tmpname);
|
2020-01-04 22:22:13 +03:00
|
|
|
}
|
2021-06-19 19:16:42 +03:00
|
|
|
else if(aaddr.endsWith("_ipps._tcp.local"))
|
|
|
|
{
|
|
|
|
new_ipps_ptrs.append(tmpname);
|
|
|
|
}
|
2020-01-02 23:05:04 +03:00
|
|
|
}
|
2020-01-04 22:22:13 +03:00
|
|
|
else if(atype == TXT)
|
2019-12-01 22:27:00 +03:00
|
|
|
{
|
2020-01-04 22:22:13 +03:00
|
|
|
Bytestream tmp;
|
|
|
|
while(resp.pos() < pos_before+len)
|
2019-12-01 22:27:00 +03:00
|
|
|
{
|
2020-01-04 22:22:13 +03:00
|
|
|
resp/resp.getU8() >> tmp;
|
|
|
|
if(tmp >>= "rp=")
|
|
|
|
{
|
|
|
|
std::string tmprp;
|
|
|
|
tmp/tmp.remaining() >> tmprp;
|
|
|
|
_rps[aaddr] = tmprp.c_str();
|
|
|
|
}
|
2019-12-01 22:27:00 +03:00
|
|
|
}
|
|
|
|
}
|
2020-01-04 22:22:13 +03:00
|
|
|
else if (atype == SRV)
|
|
|
|
{
|
|
|
|
quint16 prio, w, port;
|
|
|
|
resp >> prio >> w >> port;
|
|
|
|
QString target = get_addr(resp).join(".");
|
|
|
|
_ports[aaddr] = port;
|
|
|
|
_targets[aaddr] = target;
|
2021-06-19 19:16:42 +03:00
|
|
|
if(!new_targets.contains(target))
|
|
|
|
{
|
|
|
|
new_targets.append(target);
|
|
|
|
}
|
2020-01-04 22:22:13 +03:00
|
|
|
}
|
|
|
|
else if(atype == A)
|
|
|
|
{
|
|
|
|
quint32 addr;
|
|
|
|
resp >> addr;
|
|
|
|
QHostAddress haddr(addr);
|
2021-06-19 19:16:42 +03:00
|
|
|
if(!_AAs.contains(aaddr, haddr.toString()))
|
|
|
|
{
|
|
|
|
_AAs.insert(aaddr, haddr.toString());
|
|
|
|
}
|
2020-01-04 22:22:13 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
resp += len;
|
|
|
|
}
|
|
|
|
Q_ASSERT(resp.pos() == pos_before+len);
|
2019-12-01 22:27:00 +03:00
|
|
|
|
2020-01-04 22:22:13 +03:00
|
|
|
}
|
|
|
|
}
|
2020-08-29 18:32:11 +03:00
|
|
|
catch(const std::exception& e)
|
2020-01-04 22:22:13 +03:00
|
|
|
{
|
|
|
|
qDebug() << e.what();
|
|
|
|
return;
|
2019-12-01 22:27:00 +03:00
|
|
|
}
|
2020-04-30 19:45:43 +03:00
|
|
|
qDebug() << "new ipp ptrs" << new_ipp_ptrs;
|
2021-06-19 19:16:42 +03:00
|
|
|
qDebug() << "new ipps ptrs" << new_ipps_ptrs;
|
2020-01-02 23:05:04 +03:00
|
|
|
qDebug() << "ipp ptrs" << _ipp;
|
2021-06-19 19:16:42 +03:00
|
|
|
qDebug() << "ipp ptrs" << _ipps;
|
2020-01-02 23:05:04 +03:00
|
|
|
qDebug() << "rps" << _rps;
|
|
|
|
qDebug() << "ports" << _ports;
|
2020-04-30 19:45:43 +03:00
|
|
|
qDebug() << "new targets" << new_targets;
|
2020-01-02 23:05:04 +03:00
|
|
|
qDebug() << "targets" << _targets;
|
|
|
|
qDebug() << "AAs" << _AAs;
|
|
|
|
qDebug() << "AAAAs" << _AAAAs;
|
2019-12-19 22:12:19 +03:00
|
|
|
|
2021-06-19 19:16:42 +03:00
|
|
|
// 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)
|
2019-12-01 22:27:00 +03:00
|
|
|
{
|
2021-06-19 19:16:42 +03:00
|
|
|
// If target does not resolve to an address, query about it
|
|
|
|
if(!_AAs.contains(target))
|
2019-12-01 22:27:00 +03:00
|
|
|
{
|
2021-06-19 19:16:42 +03:00
|
|
|
unresolvedAddrs.append(target);
|
2019-12-01 22:27:00 +03:00
|
|
|
}
|
|
|
|
}
|
2021-06-19 19:16:42 +03:00
|
|
|
|
|
|
|
if(!unresolvedAddrs.empty())
|
2020-04-30 19:45:43 +03:00
|
|
|
{
|
2021-06-19 19:16:42 +03:00
|
|
|
sendQuery(A, unresolvedAddrs);
|
2020-04-30 19:45:43 +03:00
|
|
|
}
|
2020-01-02 23:05:04 +03:00
|
|
|
|
2019-12-01 22:27:00 +03:00
|
|
|
}
|
|
|
|
this->update();
|
|
|
|
|
|
|
|
}
|
2020-01-05 20:41:24 +03:00
|
|
|
|
|
|
|
QImage IppDiscovery::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
|
|
|
|
{ //TODO: consider caching images (doesn't appear to be needed currently)
|
|
|
|
Q_UNUSED(requestedSize);
|
|
|
|
qDebug() << "requesting image" << id;
|
|
|
|
|
|
|
|
QImage img;
|
|
|
|
|
2021-02-13 19:13:11 +03:00
|
|
|
QNetworkAccessManager* nam = new QNetworkAccessManager();
|
2020-01-05 20:41:24 +03:00
|
|
|
QUrl url(id);
|
2021-05-18 22:16:23 +03:00
|
|
|
QString host = url.host();
|
|
|
|
|
|
|
|
if(host.endsWith("."))
|
|
|
|
{
|
|
|
|
host.chop(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
qDebug() << url.host() << host << _AAs;
|
2020-01-05 20:41:24 +03:00
|
|
|
// TODO IPv6
|
2021-05-18 22:16:23 +03:00
|
|
|
if(_AAs.contains(host))
|
2020-01-05 20:41:24 +03:00
|
|
|
{ // TODO: retry potential other IPs
|
2021-05-18 22:16:23 +03:00
|
|
|
url.setHost(_AAs.value(host));
|
2020-01-05 20:41:24 +03:00
|
|
|
}
|
|
|
|
|
2020-01-20 22:11:41 +03:00
|
|
|
QNetworkRequest request(url);
|
2020-08-29 18:32:11 +03:00
|
|
|
request.setHeader(QNetworkRequest::UserAgentHeader, "SeaPrint " SEAPRINT_VERSION);
|
2021-06-19 19:16:42 +03:00
|
|
|
connect(nam, &QNetworkAccessManager::sslErrors, &IppPrinter::onSslErrors);
|
2021-02-13 19:13:11 +03:00
|
|
|
|
2020-01-20 22:11:41 +03:00
|
|
|
QNetworkReply* reply = nam->get(request);
|
2020-01-05 20:41:24 +03:00
|
|
|
|
2021-02-13 19:13:11 +03:00
|
|
|
QEventLoop loop;
|
|
|
|
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
|
|
|
|
loop.exec();
|
2020-01-05 20:41:24 +03:00
|
|
|
|
|
|
|
if (reply->error() == QNetworkReply::NoError)
|
|
|
|
{
|
|
|
|
QImageReader imageReader(reply);
|
|
|
|
img = imageReader.read();
|
|
|
|
}
|
|
|
|
|
2021-05-18 22:14:51 +03:00
|
|
|
*size = img.size();
|
2020-01-05 20:41:24 +03:00
|
|
|
delete nam;
|
|
|
|
return img;
|
|
|
|
|
|
|
|
}
|