File indexing completed on 2024-05-19 04:44:48

0001 /*
0002  * SPDX-FileCopyrightText: 2007-2010 Kare Sars <kare dot sars at iki dot fi>
0003  * SPDX-FileCopyrightText: 2009 Matthias Nagl <matthias at nagl dot info>
0004  * SPDX-FileCopyrightText: 2009 Grzegorz Kurtyka <grzegorz dot kurtyka at gmail dot com>
0005  * SPDX-FileCopyrightText: 2007-2008 Gilles Caulier <caulier dot gilles at gmail dot com>
0006  * SPDX-FileCopyrightText: 2014 Gregor Mitsch : port to KDE5 frameworks
0007  * SPDX-FileCopyrightText: 2021 Alexander Stippich <a.stippich@gmx.net>
0008  *
0009  * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0010  */
0011 
0012 // Qt includes
0013 
0014 #include <QMutex>
0015 
0016 // Sane includes
0017 extern "C" {
0018 #include <sane/sane.h>
0019 #include <sane/saneopts.h>
0020 }
0021 
0022 #include "interface.h"
0023 #include "interface_p.h"
0024 
0025 #include <ksanecore_debug.h>
0026 
0027 namespace KSaneCore
0028 {
0029 static int s_objectCount = 0;
0030 
0031 Q_GLOBAL_STATIC(QMutex, s_objectMutex)
0032 
0033 Interface::Interface(QObject *parent)
0034     : QObject(parent)
0035     , d(std::make_unique<InterfacePrivate>(this))
0036 {
0037     SANE_Int version;
0038     SANE_Status status;
0039 
0040     s_objectMutex->lock();
0041     s_objectCount++;
0042 
0043     if (s_objectCount == 1) {
0044         // only call sane init for the first instance
0045         status = sane_init(&version, &Authentication::authorization);
0046         if (status != SANE_STATUS_GOOD) {
0047             qCDebug(KSANECORE_LOG) << "libksane: sane_init() failed(" << sane_strstatus(status) << ")";
0048         }
0049     }
0050     s_objectMutex->unlock();
0051 
0052     d->m_readValuesTimer.setSingleShot(true);
0053     connect(&d->m_readValuesTimer, &QTimer::timeout, d.get(), &InterfacePrivate::reloadValues);
0054 }
0055 
0056 Interface::~Interface()
0057 {
0058     closeDevice();
0059 
0060     s_objectMutex->lock();
0061     s_objectCount--;
0062     if (s_objectCount <= 0) {
0063         // only delete the find-devices and authorization singletons and call sane_exit
0064         // if this is the last instance
0065         delete d->m_findDevThread;
0066         delete d->m_auth;
0067         sane_exit();
0068     }
0069     s_objectMutex->unlock();
0070 }
0071 
0072 QString Interface::deviceName() const
0073 {
0074     return d->m_devName;
0075 }
0076 
0077 QString Interface::deviceVendor() const
0078 {
0079     return d->m_vendor;
0080 }
0081 
0082 QString Interface::deviceModel() const
0083 {
0084     return d->m_model;
0085 }
0086 
0087 bool Interface::reloadDevicesList(const DeviceType type)
0088 {
0089     /* On some SANE backends, the handle becomes invalid when
0090      * querying for new devices. Hence, this is only allowed when
0091      * no device is currently opened. */
0092     if (d->m_saneHandle == nullptr) {
0093         d->m_findDevThread->setDeviceType(type);
0094         d->m_findDevThread->start();
0095         return true;
0096     }
0097     return false;
0098 }
0099 
0100 Interface::OpenStatus Interface::openDevice(const QString &deviceName)
0101 {
0102     SANE_Status status;
0103 
0104     if (d->m_saneHandle != nullptr) {
0105         // this CoreInterface already has an open device
0106         return OpenStatus::OpeningFailed;
0107     }
0108 
0109     // don't bother trying to open if the device string is empty
0110     if (deviceName.isEmpty()) {
0111         return OpenStatus::OpeningFailed;
0112     }
0113     // save the device name
0114     d->m_devName = deviceName;
0115 
0116     // Try to open the device
0117     status = sane_open(deviceName.toLatin1().constData(), &d->m_saneHandle);
0118 
0119     if (status == SANE_STATUS_ACCESS_DENIED) {
0120         return OpenStatus::OpeningDenied;
0121     }
0122 
0123     if (status != SANE_STATUS_GOOD) {
0124         qCDebug(KSANECORE_LOG) << "sane_open(\"" << deviceName << "\", &handle) failed! status = " << sane_strstatus(status);
0125         d->m_devName.clear();
0126         return OpenStatus::OpeningFailed;
0127     }
0128 
0129     return d->loadDeviceOptions();
0130 }
0131 
0132 Interface::OpenStatus Interface::openRestrictedDevice(const QString &deviceName, const QString &userName, const QString &password)
0133 {
0134     SANE_Status status;
0135 
0136     if (d->m_saneHandle != nullptr) {
0137         // this CoreInterface already has an open device
0138         return OpenStatus::OpeningFailed;
0139     }
0140 
0141     // don't bother trying to open if the device string is empty
0142     if (deviceName.isEmpty()) {
0143         return OpenStatus::OpeningFailed;
0144     }
0145     // save the device name
0146     d->m_devName = deviceName;
0147 
0148     // add/update the device user-name and password for authentication
0149     d->m_auth->setDeviceAuth(d->m_devName, userName, password);
0150 
0151     // Try to open the device
0152     status = sane_open(deviceName.toLatin1().constData(), &d->m_saneHandle);
0153 
0154     if (status == SANE_STATUS_ACCESS_DENIED) {
0155         return OpenStatus::OpeningDenied;
0156     }
0157 
0158     if (status != SANE_STATUS_GOOD) {
0159         qCDebug(KSANECORE_LOG) << "sane_open(\"" << deviceName << "\", &handle) failed! status = " << sane_strstatus(status);
0160         d->m_auth->clearDeviceAuth(d->m_devName);
0161         d->m_devName.clear();
0162         return OpenStatus::OpeningFailed;
0163     }
0164 
0165     return d->loadDeviceOptions();
0166 }
0167 
0168 bool Interface::closeDevice()
0169 {
0170     if (!d->m_saneHandle) {
0171         return false;
0172     }
0173     stopScan();
0174 
0175     disconnect(d->m_scanThread);
0176     if (d->m_scanThread->isRunning()) {
0177         connect(d->m_scanThread, &QThread::finished, d->m_scanThread, &QThread::deleteLater);
0178     }
0179     if (d->m_scanThread->isFinished()) {
0180         d->m_scanThread->deleteLater();
0181     }
0182     d->m_scanThread = nullptr;
0183 
0184     d->m_auth->clearDeviceAuth(d->m_devName);
0185     sane_close(d->m_saneHandle);
0186     d->m_saneHandle = nullptr;
0187     d->clearDeviceOptions();
0188 
0189     return true;
0190 }
0191 
0192 void Interface::startScan()
0193 {
0194     if (!d->m_saneHandle) {
0195         return;
0196     }
0197     d->m_cancelMultiPageScan = false;
0198     // execute a pending value reload
0199     while (d->m_readValuesTimer.isActive()) {
0200         d->m_readValuesTimer.stop();
0201         d->reloadValues();
0202     }
0203     d->m_optionPollTimer.stop();
0204     Q_EMIT scanProgress(-1);
0205     d->m_scanThread->start();
0206 }
0207 
0208 void Interface::stopScan()
0209 {
0210     if (!d->m_saneHandle) {
0211         return;
0212     }
0213 
0214     d->m_cancelMultiPageScan = true;
0215     if (d->m_scanThread->isRunning()) {
0216         d->m_scanThread->cancelScan();
0217     }
0218     if (d->m_batchModeTimer.isActive()) {
0219         d->m_batchModeTimer.stop();
0220         Q_EMIT batchModeCountDown(0);
0221         Q_EMIT scanFinished(ScanStatus::NoError, i18n("Scanning stopped by user."));
0222     }
0223 }
0224 
0225 QImage *Interface::scanImage() const
0226 {
0227     if (d->m_saneHandle != nullptr) {
0228         return d->m_scanThread->scanImage();
0229     }
0230     return nullptr;
0231 }
0232 
0233 void Interface::lockScanImage()
0234 {
0235     if (d->m_saneHandle != nullptr) {
0236         d->m_scanThread->lockScanImage();
0237     }
0238 }
0239 
0240 void Interface::unlockScanImage()
0241 {
0242     if (d->m_saneHandle != nullptr) {
0243         d->m_scanThread->unlockScanImage();
0244     }
0245 }
0246 
0247 QList<Option *> Interface::getOptionsList()
0248 {
0249     return d->m_externalOptionsList;
0250 }
0251 
0252 Option *Interface::getOption(Interface::OptionName optionEnum)
0253 {
0254     auto it = d->m_optionsLocation.find(optionEnum);
0255     if (it != d->m_optionsLocation.end()) {
0256         return d->m_externalOptionsList.at(it.value());
0257     }
0258     return nullptr;
0259 }
0260 
0261 Option *Interface::getOption(const QString &optionName)
0262 {
0263     for (const auto &option : qAsConst(d->m_externalOptionsList)) {
0264         if (option->name() == optionName) {
0265             return option;
0266         }
0267     }
0268     return nullptr;
0269 }
0270 
0271 QMap<QString, QString> Interface::getOptionsMap()
0272 {
0273     QMap<QString, QString> options;
0274     QString tmp;
0275 
0276     for (const auto option : qAsConst(d->m_optionsList)) {
0277         tmp = option->valueAsString();
0278         if (!tmp.isEmpty()) {
0279             options[option->name()] = tmp;
0280         }
0281     }
0282     return options;
0283 }
0284 
0285 int Interface::setOptionsMap(const QMap<QString, QString> &options)
0286 {
0287     if (!d->m_saneHandle || d->m_scanThread->isRunning()) {
0288         return -1;
0289     }
0290 
0291     QMap<QString, QString> optionMapCopy = options;
0292 
0293     int i;
0294     int ret = 0;
0295 
0296     Option *sourceOption = getOption(SourceOption);
0297     Option *modeOption = getOption(ScanModeOption);
0298 
0299     // Priorize source option
0300     if (sourceOption != nullptr && optionMapCopy.contains(sourceOption->name())) {
0301         if (sourceOption->setValue(optionMapCopy[sourceOption->name()])) {
0302             ret++;
0303         }
0304         optionMapCopy.remove(sourceOption->name());
0305     }
0306 
0307     // Priorize mode option
0308     if (modeOption != nullptr && optionMapCopy.contains(modeOption->name())) {
0309         if (modeOption->setValue(optionMapCopy[modeOption->name()])) {
0310             ret++;
0311         }
0312         optionMapCopy.remove(modeOption->name());
0313     }
0314 
0315     // Update remaining options
0316     for (i = 0; i < d->m_optionsList.size(); i++) {
0317         const auto it = optionMapCopy.find(d->m_optionsList.at(i)->name());
0318         if (it != optionMapCopy.end() && d->m_optionsList.at(i)->setValue(it.value())) {
0319             ret++;
0320         }
0321     }
0322     return ret;
0323 }
0324 
0325 } // NameSpace KSaneCore
0326 
0327 #include "moc_interface.cpp"