diff --git a/CMakeLists.txt b/CMakeLists.txt index 82dab5d..7f6132d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,66 +1,67 @@ project(print-manager) cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) set(QT_MIN_VERSION "5.3.0") # Print-Manager version set(PM_VERSION "0.3.0" CACHE STRING "Print Manager version") ################# set KDE specific information ################# find_package(ECM 1.3.0 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(ECMPackageConfigHelpers) include(ECMOptionalAddSubdirectory) include(FeatureSummary) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Core DBus Network Widgets Qml Quick ) find_package(CUPS "1.5" REQUIRED) find_package(KF5 REQUIRED Config ConfigWidgets CoreAddons DBusAddons IconThemes I18n KCMUtils KIO Notifications Plasma WidgetsAddons WindowSystem) add_definitions( -DQT_DISABLE_DEPRECATED_BEFORE=0x050900 -DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS -DQT_NO_URL_CAST_FROM_STRING + -DQT_NO_CAST_FROM_BYTEARRAY ) -remove_definitions(-DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_FROM_BYTEARRAY -DQT_NO_KEYWORDS) +remove_definitions(-DQT_NO_CAST_FROM_ASCII -DQT_NO_KEYWORDS) # Generate config.h configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/libkcups ${CUPS_INCLUDE_DIR} ) add_definitions(-DTRANSLATION_DOMAIN="print-manager") add_subdirectory(libkcups) add_subdirectory(configure-printer) add_subdirectory(add-printer) add_subdirectory(printer-manager-kcm) add_subdirectory(printqueue) add_subdirectory(print-manager-kded) add_subdirectory(plasmoid) add_subdirectory(declarative-plugins) diff --git a/configure-printer/PrinterOptions.cpp b/configure-printer/PrinterOptions.cpp index 65e668a..3db1e5e 100644 --- a/configure-printer/PrinterOptions.cpp +++ b/configure-printer/PrinterOptions.cpp @@ -1,827 +1,827 @@ /*************************************************************************** * Copyright (C) 2010-2018 by Daniel Nicoletti * * dantti12@gmail.com * * * * This program 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 2 of the License, or * * (at your option) any later version. * * * * This program 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 this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * * * * The save PPD snipet is from CUPS * * Copyright 2007-2009 by Apple Inc. * * Copyright 1997-2007 by Easy Software Products. * * * * This program 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 2 of the License, or * * (at your option) any later version. * * This is converted from LGPL 2 in accordance with section 3 * * See http://www.cups.org/documentation.php/license.html * ***************************************************************************/ #include "PrinterOptions.h" #include "ui_PrinterOptions.h" #include "Debug.h" #include #include #include #include #include #include #include #include #include #include #include #define DEFAULT_CHOICE "defaultChoice" PrinterOptions::PrinterOptions(const QString &destName, bool isClass, bool isRemote, QWidget *parent) : PrinterPage(parent), ui(new Ui::PrinterOptions), m_destName(destName), m_isClass(isClass), m_isRemote(isRemote), m_ppd(NULL), m_changes(0) { ui->setupUi(this); reloadPPD(); } void PrinterOptions::on_autoConfigurePB_clicked() { QPointer request = new KCupsRequest; request->printCommand(m_destName, "AutoConfigure", i18n("Set Default Options")); request->waitTillFinished(); if (request) { request->deleteLater(); } } void PrinterOptions::reloadPPD() { // The caller "owns" the file that is created and must unlink the returned filename. if (!m_filename.isEmpty()) { - unlink(m_filename.toUtf8()); + unlink(qUtf8Printable(m_filename)); } // remove all the options while (ui->verticalLayout->count()) { qCDebug(PM_CONFIGURE_PRINTER) << "removing" << ui->verticalLayout->count(); QLayoutItem *item = ui->verticalLayout->itemAt(0); ui->verticalLayout->removeItem(item); if (item->widget()) { item->widget()->deleteLater(); delete item; } else if (item->layout()) { qCDebug(PM_CONFIGURE_PRINTER) << "removing layout" << ui->verticalLayout->count(); // item->layout()->deleteLater(); } else if (item->spacerItem()) { delete item->spacerItem(); } } m_changes = 0; m_customValues.clear(); emit changed(false); QPointer request = new KCupsRequest; request->getPrinterPPD(m_destName); request->waitTillFinished(); if (!request) { return; } m_filename = request->printerPPD(); - m_ppd = ppdOpenFile(m_filename.toUtf8()); + m_ppd = ppdOpenFile(qUtf8Printable(m_filename)); request->deleteLater(); if (m_ppd == NULL) { qCWarning(PM_CONFIGURE_PRINTER) << "Could not open ppd file:" << m_filename << request->errorMsg(); m_filename.clear(); return; } ppdLocalize(m_ppd); // select the default options on the ppd file ppdMarkDefaults(m_ppd); // TODO try to use QTextCodec aliases const char *lang_encoding; lang_encoding = m_ppd->lang_encoding; if (lang_encoding && !strcasecmp (lang_encoding, "ISOLatin1")) { m_codec = QTextCodec::codecForName("ISO-8859-1"); } else if (lang_encoding && !strcasecmp (lang_encoding, "ISOLatin2")) { m_codec = QTextCodec::codecForName("ISO-8859-2"); } else if (lang_encoding && !strcasecmp (lang_encoding, "ISOLatin5")) { m_codec = QTextCodec::codecForName("ISO-8859-5"); } else if (lang_encoding && !strcasecmp (lang_encoding, "JIS83-RKSJ")) { m_codec = QTextCodec::codecForName("SHIFT-JIS"); } else if (lang_encoding && !strcasecmp (lang_encoding, "MacStandard")) { m_codec = QTextCodec::codecForName("MACINTOSH"); } else if (lang_encoding && !strcasecmp (lang_encoding, "WindowsANSI")) { m_codec = QTextCodec::codecForName("WINDOWS-1252"); } else { // Guess m_codec = QTextCodec::codecForName(lang_encoding); } if (m_codec == 0) { m_codec = QTextCodec::codecForName("UTF-8"); } if (m_ppd->manufacturer) { m_make = m_codec->toUnicode(m_ppd->manufacturer); } if (m_ppd->nickname) { m_makeAndModel = m_codec->toUnicode(m_ppd->nickname); } ui->autoConfigurePB->hide(); ppd_attr_t *ppdattr; if (m_ppd->num_filters == 0 || ((ppdattr = ppdFindAttr(m_ppd, "cupsCommands", NULL)) != NULL && ppdattr->value && strstr(ppdattr->value, "AutoConfigure"))) { ui->autoConfigurePB->show(); } else { for (int i = 0; i < m_ppd->num_filters; i ++) { if (!strncmp(m_ppd->filters[i], "application/vnd.cups-postscript", 31)) { ui->autoConfigurePB->show(); break; } } } createGroups(); } void PrinterOptions::createGroups() { int i; ppd_group_t *group; // Iterate over the groups for (i = 0, group = m_ppd->groups; i < m_ppd->num_groups; i++, group++) { // The name of the group QString name = m_codec->toUnicode(group->name); // The humman name of the group QString text = m_codec->toUnicode(group->text); // The group box were the options will be laid out auto groupBox = new QGroupBox(text, ui->scrollArea); // Create the form layout to put options in auto gFormLayout = new QFormLayout(groupBox); gFormLayout->setFormAlignment(Qt::AlignCenter); groupBox->setLayout(gFormLayout); ui->verticalLayout->addWidget(groupBox); int j; ppd_option_t *option; // Iterate over the options in the group for (j = 0, option = group->options; j < group->num_options; j++, option++) { QString oKeyword = m_codec->toUnicode(option->keyword); QString oText = m_codec->toUnicode(option->text); QString oDefChoice = m_codec->toUnicode(option->defchoice); // The python system-config-printer skips this one // which has the same data as "PageSize", let's hope // they did the right thing if (oKeyword == "PageRegion") { continue; } QWidget *optionW = 0; switch (option->ui) { case PPD_UI_BOOLEAN: optionW = pickBoolean(option, oKeyword, ui->scrollAreaWidgetContents); break; case PPD_UI_PICKMANY: optionW = pickMany(option, oKeyword, ui->scrollAreaWidgetContents); break; case PPD_UI_PICKONE: optionW = pickOne(option, oKeyword, ui->scrollAreaWidgetContents); break; default: qCWarning(PM_CONFIGURE_PRINTER) << "Option type not recognized: " << option->ui; // let's use the most common optionW = pickOne(option, oKeyword, ui->scrollAreaWidgetContents); break; } if (optionW) { // insert the option widget gFormLayout->addRow(oText, optionW); } } } ui->verticalLayout->addStretch(); } QWidget* PrinterOptions::pickBoolean(ppd_option_t *option, const QString &keyword, QWidget *parent) const { Q_UNUSED(keyword) auto widget = new QWidget(parent); auto layout = new QHBoxLayout(widget); auto radioGroup = new QButtonGroup(widget); widget->setLayout(layout); int i; ppd_choice_t *choice; QString defChoice = m_codec->toUnicode(option->defchoice); // Iterate over the choices in the option for (i = 0, choice = option->choices; i < option->num_choices; ++i, ++choice) { QString choiceName = m_codec->toUnicode(choice->choice); QString cText = m_codec->toUnicode(choice->text); auto button = new QRadioButton(cText, widget); button->setChecked(defChoice == choiceName); button->setProperty("choice", choiceName); // if we are in looking at a remote printer we can't save it button->setEnabled(!m_isRemote); layout->addWidget(button); radioGroup->addButton(button); } // store the default choice radioGroup->setProperty(DEFAULT_CHOICE, defChoice); radioGroup->setProperty("Keyword", keyword); connect(radioGroup, static_cast(&QButtonGroup::buttonClicked), this, &PrinterOptions::radioBtClicked); return widget; } void PrinterOptions::radioBtClicked(QAbstractButton *button) { QObject *radioGroup = sender(); bool isDifferent = radioGroup->property(DEFAULT_CHOICE).toString() != button->property("choice"); if (isDifferent != radioGroup->property("different").toBool()) { // it's different from the last time so add or remove changes isDifferent ? m_changes++ : m_changes--; radioGroup->setProperty("different", isDifferent); emit changed(m_changes); } QString keyword = radioGroup->property("Keyword").toString(); QString choice = button->property("choice").toString(); radioGroup->setProperty("currentChoice", choice); // TODO warning about conflicts // ppdMarkOption(m_ppd, // m_codec->fromUnicode(keyword), // m_codec->fromUnicode(choice)); // store the new value if (isDifferent) { m_customValues[keyword] = radioGroup; } else { m_customValues.remove(keyword); } } QWidget* PrinterOptions::pickMany(ppd_option_t *option, const QString &keyword, QWidget *parent) const { Q_UNUSED(keyword) auto listView = new QListView(parent); auto model = new QStandardItemModel(listView); listView->setModel(model); listView->setItemDelegate(new NoSelectionRectDelegate(listView)); int i; ppd_choice_t *choice; QString oDefChoice = m_codec->toUnicode(option->defchoice); // Iterate over the choices in the option for (i = 0, choice = option->choices; i < option->num_choices; ++i, ++choice) { QString cName = m_codec->toUnicode(choice->choice); QString cText = m_codec->toUnicode(choice->text); auto item = new QStandardItem(cText); item->setData(cName); item->setCheckable(true); item->setEditable(false); // TODO there is only ONE default choice, what about the other // Items selected?! item->setCheckState(oDefChoice == cName ? Qt::Checked : Qt::Unchecked); model->appendRow(item); } // if we are in looking at a remote printer we can't save it listView->setEnabled(!m_isRemote); return qobject_cast(listView); } QWidget* PrinterOptions::pickOne(ppd_option_t *option, const QString &keyword, QWidget *parent) const { int i; ppd_choice_t *choice; QString defChoice = m_codec->toUnicode(option->defchoice); auto comboBox = new QComboBox(parent); // Iterate over the choices in the option for (i = 0, choice = option->choices; i < option->num_choices; ++i, ++choice) { QString cName = m_codec->toUnicode(choice->choice); QString cText = m_codec->toUnicode(choice->text); comboBox->addItem(cText, cName); } // store the default choice comboBox->setProperty(DEFAULT_CHOICE, defChoice); comboBox->setProperty("Keyword", keyword); comboBox->setCurrentIndex(comboBox->findData(defChoice)); // connect the signal AFTER setCurrentIndex is called connect(comboBox, static_cast(&QComboBox::currentIndexChanged), this, &PrinterOptions::currentIndexChangedCB); // if we are in looking at a remote printer we can't save it comboBox->setEnabled(!m_isRemote); return qobject_cast(comboBox); } void PrinterOptions::currentIndexChangedCB(int index) { auto comboBox = qobject_cast(sender()); bool isDifferent = comboBox->property(DEFAULT_CHOICE).toString() != comboBox->itemData(index); if (isDifferent != comboBox->property("different").toBool()) { // it's different from the last time so add or remove changes isDifferent ? m_changes++ : m_changes--; comboBox->setProperty("different", isDifferent); emit changed(m_changes); } QString keyword = comboBox->property("Keyword").toString(); QString value = comboBox->itemData(index).toString(); comboBox->setProperty("currentChoice", value); // TODO warning about conflicts // ppdMarkOption(m_ppd, // m_codec->fromUnicode(keyword), // m_codec->fromUnicode(value)); // store the new value if (isDifferent) { m_customValues[keyword] = qobject_cast(comboBox); } else { m_customValues.remove(keyword); } } PrinterOptions::~PrinterOptions() { if (m_ppd != NULL) { ppdClose(m_ppd); } if (!m_filename.isEmpty()) { - unlink(m_filename.toUtf8()); + unlink(qUtf8Printable(m_filename)); } delete ui; } const char * /* O - Value of variable */ PrinterOptions::getVariable(const char *name) const /* I - Name of variable */ { QString keyword = m_codec->toUnicode(name); if (m_customValues.contains(keyword)) { QString value = m_customValues[keyword]->property("currentChoice").toString(); - return m_codec->fromUnicode(value); + return m_codec->fromUnicode(value).constData(); } else { return NULL; } } /* * 'get_points()' - Get a value in points. */ double /* O - Number in points */ PrinterOptions::get_points(double number, /* I - Original number */ const char *uval) /* I - Units */ { if (!strcmp(uval, "mm")) /* Millimeters */ return (number * 72.0 / 25.4); else if (!strcmp(uval, "cm")) /* Centimeters */ return (number * 72.0 / 2.54); else if (!strcmp(uval, "in")) /* Inches */ return (number * 72.0); else if (!strcmp(uval, "ft")) /* Feet */ return (number * 72.0 * 12.0); else if (!strcmp(uval, "m")) /* Meters */ return (number * 72.0 / 0.0254); else /* Points */ return (number); } /* * 'get_option_value()' - Return the value of an option. * * This function also handles generation of custom option values. */ char * /* O - Value string or NULL on error */ PrinterOptions::get_option_value( ppd_file_t *ppd, /* I - PPD file */ const char *name, /* I - Option name */ char *buffer, /* I - String buffer */ size_t bufsize) const /* I - Size of buffer */ { char *bufptr, /* Pointer into buffer */ *bufend; /* End of buffer */ ppd_coption_t *coption; /* Custom option */ ppd_cparam_t *cparam; /* Current custom parameter */ char keyword[256]; /* Parameter name */ const char *val, /* Parameter value */ *uval; /* Units value */ long integer; /* Integer value */ double number, /* Number value */ number_points; /* Number in points */ /* * See if we have a custom option choice... */ if ((val = getVariable(name)) == NULL) { /* * Option not found! */ return (NULL); } else if (strcasecmp(val, "Custom") || (coption = ppdFindCustomOption(ppd, name)) == NULL) { /* * Not a custom choice... */ qstrncpy(buffer, val, bufsize); return (buffer); } /* * OK, we have a custom option choice, format it... */ *buffer = '\0'; if (!strcmp(coption->keyword, "PageSize")) { const char *lval; /* Length string value */ double width, /* Width value */ width_points, /* Width in points */ length, /* Length value */ length_points; /* Length in points */ val = getVariable("PageSize.Width"); lval = getVariable("PageSize.Height"); uval = getVariable("PageSize.Units"); if (!val || !lval || !uval || (width = strtod(val, NULL)) == 0.0 || (length = strtod(lval, NULL)) == 0.0 || (strcmp(uval, "pt") && strcmp(uval, "in") && strcmp(uval, "ft") && strcmp(uval, "cm") && strcmp(uval, "mm") && strcmp(uval, "m"))) { return (NULL); } width_points = get_points(width, uval); length_points = get_points(length, uval); if (width_points < ppd->custom_min[0] || width_points > ppd->custom_max[0] || length_points < ppd->custom_min[1] || length_points > ppd->custom_max[1]) { return (NULL); } snprintf(buffer, bufsize, "Custom.%gx%g%s", width, length, uval); } else if (cupsArrayCount(coption->params) == 1) { cparam = ppdFirstCustomParam(coption); snprintf(keyword, sizeof(keyword), "%s.%s", coption->keyword, cparam->name); if ((val = getVariable(keyword)) == NULL) return (NULL); switch (cparam->type) { case PPD_CUSTOM_CURVE : case PPD_CUSTOM_INVCURVE : case PPD_CUSTOM_REAL : if ((number = strtod(val, NULL)) == 0.0 || number < cparam->minimum.custom_real || number > cparam->maximum.custom_real) return (NULL); snprintf(buffer, bufsize, "Custom.%g", number); break; case PPD_CUSTOM_INT : if (!*val || (integer = strtol(val, NULL, 10)) == LONG_MIN || integer == LONG_MAX || integer < cparam->minimum.custom_int || integer > cparam->maximum.custom_int) return (NULL); snprintf(buffer, bufsize, "Custom.%ld", integer); break; case PPD_CUSTOM_POINTS : snprintf(keyword, sizeof(keyword), "%s.Units", coption->keyword); if ((number = strtod(val, NULL)) == 0.0 || (uval = getVariable(keyword)) == NULL || (strcmp(uval, "pt") && strcmp(uval, "in") && strcmp(uval, "ft") && strcmp(uval, "cm") && strcmp(uval, "mm") && strcmp(uval, "m"))) return (NULL); number_points = get_points(number, uval); if (number_points < cparam->minimum.custom_points || number_points > cparam->maximum.custom_points) return (NULL); snprintf(buffer, bufsize, "Custom.%g%s", number, uval); break; case PPD_CUSTOM_PASSCODE : for (uval = val; *uval; ++uval) { if (!isdigit(*uval & 255)) { return (NULL); } } case PPD_CUSTOM_PASSWORD : case PPD_CUSTOM_STRING : integer = (long)strlen(val); if (integer < cparam->minimum.custom_string || integer > cparam->maximum.custom_string) { return (NULL); } snprintf(buffer, bufsize, "Custom.%s", val); break; } } else { const char *prefix = "{"; /* Prefix string */ bufptr = buffer; bufend = buffer + bufsize; for (cparam = ppdFirstCustomParam(coption); cparam; cparam = ppdNextCustomParam(coption)) { snprintf(keyword, sizeof(keyword), "%s.%s", coption->keyword, cparam->name); if ((val = getVariable(keyword)) == NULL) { return (NULL); } snprintf(bufptr, bufend - bufptr, "%s%s=", prefix, cparam->name); bufptr += strlen(bufptr); prefix = " "; switch (cparam->type) { case PPD_CUSTOM_CURVE : case PPD_CUSTOM_INVCURVE : case PPD_CUSTOM_REAL : if ((number = strtod(val, NULL)) == 0.0 || number < cparam->minimum.custom_real || number > cparam->maximum.custom_real) return (NULL); snprintf(bufptr, bufend - bufptr, "%g", number); break; case PPD_CUSTOM_INT : if (!*val || (integer = strtol(val, NULL, 10)) == LONG_MIN || integer == LONG_MAX || integer < cparam->minimum.custom_int || integer > cparam->maximum.custom_int) { return (NULL); } snprintf(bufptr, bufend - bufptr, "%ld", integer); break; case PPD_CUSTOM_POINTS : snprintf(keyword, sizeof(keyword), "%s.Units", coption->keyword); if ((number = strtod(val, NULL)) == 0.0 || (uval = getVariable(keyword)) == NULL || (strcmp(uval, "pt") && strcmp(uval, "in") && strcmp(uval, "ft") && strcmp(uval, "cm") && strcmp(uval, "mm") && strcmp(uval, "m"))) { return (NULL); } number_points = get_points(number, uval); if (number_points < cparam->minimum.custom_points || number_points > cparam->maximum.custom_points) { return (NULL); } snprintf(bufptr, bufend - bufptr, "%g%s", number, uval); break; case PPD_CUSTOM_PASSCODE : for (uval = val; *uval; uval ++) { if (!isdigit(*uval & 255)) { return (NULL); } } case PPD_CUSTOM_PASSWORD : case PPD_CUSTOM_STRING : integer = (long)strlen(val); if (integer < cparam->minimum.custom_string || integer > cparam->maximum.custom_string) { return (NULL); } if ((bufptr + 2) > bufend) { return (NULL); } bufend --; *bufptr++ = '\"'; while (*val && bufptr < bufend) { if (*val == '\\' || *val == '\"') { if ((bufptr + 1) >= bufend) { return (NULL); } *bufptr++ = '\\'; } *bufptr++ = *val++; } if (bufptr >= bufend) { return (NULL); } *bufptr++ = '\"'; *bufptr = '\0'; bufend ++; break; } bufptr += strlen(bufptr); } if (bufptr == buffer || (bufend - bufptr) < 2) { return (NULL); } strcpy(bufptr, "}"); } return (buffer); } void PrinterOptions::save() { char tempfile[1024]; const char *var; cups_file_t *in, /* Input file */ *out; /* Output file */ char line[1024], /* Line from PPD file */ value[1024], /* Option value */ keyword[1024], /* Keyword from Default line */ *keyptr; /* Pointer into keyword... */ // copy cups-1.4.2/cgi-bin line 3779 if (!m_filename.isEmpty()) { out = cupsTempFile2(tempfile, sizeof(tempfile)); - in = cupsFileOpen(m_filename.toUtf8(), "r"); + in = cupsFileOpen(qUtf8Printable(m_filename), "r"); if (!in || !out) { if (in) { cupsFileClose(in); } if (out) { cupsFileClose(out); unlink(tempfile); } // TODO add a KMessageBox::error return; } while (cupsFileGets(in, line, sizeof(line))) { if (!strncmp(line, "*cupsProtocol:", 14)) { continue; } else if (strncmp(line, "*Default", 8)) { cupsFilePrintf(out, "%s\n", line); } else { /* * Get default option name... */ qstrncpy(keyword, line + 8, sizeof(keyword)); for (keyptr = keyword; *keyptr; keyptr ++) { if (*keyptr == ':' || isspace(*keyptr & 255)) { break; } } *keyptr = '\0'; if (!strcmp(keyword, "PageRegion") || !strcmp(keyword, "PaperDimension") || !strcmp(keyword, "ImageableArea")) { var = get_option_value(m_ppd, "PageSize", value, sizeof(value)); } else { var = get_option_value(m_ppd, keyword, value, sizeof(value)); } if (!var) { cupsFilePrintf(out, "%s\n", line); } else { cupsFilePrintf(out, "*Default%s: %s\n", keyword, var); } } } cupsFileClose(in); cupsFileClose(out); } else { // TODO add a KMessageBox::error qCWarning(PM_CONFIGURE_PRINTER) << "No printer PPD file set, can't save options."; return; } QVariantHash values; // we need null values QPointer request = new KCupsRequest; if (m_isClass) { request->addOrModifyClass(m_destName, values); } else { request->addOrModifyPrinter(m_destName, values, tempfile); } // Disable the widget till the request is processed // Otherwise the user might change something in the ui // which won't be saved but the apply but when the request // finishes we will set the current options as default setEnabled(false); request->waitTillFinished(); // unlink the file unlink(tempfile); if (request) { setEnabled(true); if (!request->hasError()) { // if we succefully save the new ppd we need now to // clear our changes QHash::const_iterator i = m_customValues.constBegin(); while (i != m_customValues.constEnd()) { QString currentChoice; currentChoice = i.value()->property("currentChoice").toString(); // Store the current choice as the default one i.value()->setProperty(DEFAULT_CHOICE, currentChoice); i.value()->setProperty("currentChoice", QVariant()); i.value()->setProperty("different", false); ++i; } m_changes = 0; m_customValues.clear(); emit changed(false); } request->deleteLater(); } } bool PrinterOptions::hasChanges() { return m_changes; } QString PrinterOptions::currentMake() const { return m_make; } QString PrinterOptions::currentMakeAndModel() const { return m_makeAndModel; } diff --git a/libkcups/KCupsConnection.cpp b/libkcups/KCupsConnection.cpp index ce86113..bab9e5a 100644 --- a/libkcups/KCupsConnection.cpp +++ b/libkcups/KCupsConnection.cpp @@ -1,898 +1,898 @@ /*************************************************************************** * Copyright (C) 2010-2018 by Daniel Nicoletti * * dantti12@gmail.com * * Copyright (C) 2012 Harald Sitter * * * * This program 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 2 of the License, or * * (at your option) any later version. * * * * This program 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 this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "KCupsConnection.h" #include "Debug.h" #include "KCupsPasswordDialog.h" #include "KIppRequest.h" #include #include #include #include #include #include #include #include #define RENEW_INTERVAL 3500 #define SUBSCRIPTION_DURATION 3600 #define DBUS_SERVER_RESTARTED "server-restarted" // ServerRestarted #define DBUS_SERVER_STARTED "server-started" // ServerStarted #define DBUS_SERVER_STOPPED "server-stopped" // ServerStopped #define DBUS_SERVER_AUDIT "server-audit" // ServerAudit #define DBUS_PRINTER_RESTARTED "printer-restarted" // PrinterRestarted #define DBUS_PRINTER_SHUTDOWN "printer-shutdown" // PrinterShutdown #define DBUS_PRINTER_STOPPED "printer-stopped" // PrinterStopped #define DBUS_PRINTER_STATE_CHANGED "printer-state-changed" // PrinterStateChanged #define DBUS_PRINTER_FINISHINGS_CHANGED "printer-finishings-changed" // PrinterFinishingsChanged #define DBUS_PRINTER_MEDIA_CHANGED "printer-media-changed" // PrinterMediaChanged #define DBUS_PRINTER_ADDED "printer-added" // PrinterAdded #define DBUS_PRINTER_DELETED "printer-deleted" // PrinterDeleted #define DBUS_PRINTER_MODIFIED "printer-modified" // PrinterModified #define DBUS_JOB_STATE_CHANGED "job-state-changed" // JobState #define DBUS_JOB_CREATED "job-created" // JobCreated #define DBUS_JOB_COMPLETED "job-completed" // JobCompleted #define DBUS_JOB_STOPPED "job-stopped" // JobStopped #define DBUS_JOB_CONFIG_CHANGED "job-config-changed" // JobConfigChanged #define DBUS_JOB_PROGRESS "job-progress" // JobProgress Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(QList) KCupsConnection* KCupsConnection::m_instance = 0; static int password_retries = 0; static int total_retries = 0; static int internalErrorCount = 0; const char * password_cb(const char *prompt, http_t *http, const char *method, const char *resource, void *user_data); KCupsConnection* KCupsConnection::global() { if (!m_instance) { m_instance = new KCupsConnection(qApp); } return m_instance; } KCupsConnection::KCupsConnection(QObject *parent) : QThread(parent) { init(); } KCupsConnection::KCupsConnection(const QUrl &server, QObject *parent) : QThread(parent), m_serverUrl(server) { qRegisterMetaType("KIppRequest"); init(); } KCupsConnection::~KCupsConnection() { if (m_instance == this) { m_instance = 0; } m_passwordDialog->deleteLater(); quit(); wait(); delete m_renewTimer; delete m_subscriptionTimer; } void KCupsConnection::setPasswordMainWindow(WId mainwindow) { m_passwordDialog->setMainWindow(mainwindow); } void KCupsConnection::init() { // Creating the dialog before start() will make it run on the gui thread m_passwordDialog = new KCupsPasswordDialog; m_subscriptionId = -1; m_inited = false; // setup the DBus subscriptions // Server related signals // ServerStarted notifierConnect(QLatin1String("ServerStarted"), this, SIGNAL(serverStarted(QString))); // ServerStopped notifierConnect(QLatin1String("ServerStopped"), this, SIGNAL(serverStopped(QString))); // ServerRestarted notifierConnect(QLatin1String("ServerRestarted"), this, SIGNAL(serverRestarted(QString))); // ServerAudit notifierConnect(QLatin1String("ServerAudit"), this, SIGNAL(serverAudit(QString))); // Printer related signals // PrinterAdded notifierConnect(QLatin1String("PrinterAdded"), this, SIGNAL(printerAdded(QString,QString,QString,uint,QString,bool))); // PrinterModified notifierConnect(QLatin1String("PrinterModified"), this, SIGNAL(printerModified(QString,QString,QString,uint,QString,bool))); // PrinterDeleted notifierConnect(QLatin1String("PrinterDeleted"), this, SIGNAL(printerDeleted(QString,QString,QString,uint,QString,bool))); // PrinterStateChanged notifierConnect(QLatin1String("PrinterStateChanged"), this, SIGNAL(printerStateChanged(QString,QString,QString,uint,QString,bool))); // PrinterStopped notifierConnect(QLatin1String("PrinterStopped"), this, SIGNAL(printerStopped(QString,QString,QString,uint,QString,bool))); // PrinterShutdown notifierConnect(QLatin1String("PrinterShutdown"), this, SIGNAL(printerShutdown(QString,QString,QString,uint,QString,bool))); // PrinterRestarted notifierConnect(QLatin1String("PrinterRestarted"), this, SIGNAL(printerRestarted(QString,QString,QString,uint,QString,bool))); // PrinterMediaChanged notifierConnect(QLatin1String("PrinterMediaChanged"), this, SIGNAL(printerMediaChanged(QString,QString,QString,uint,QString,bool))); // PrinterFinishingsChanged notifierConnect(QLatin1String("PrinterFinishingsChanged"), this, SIGNAL(PrinterFinishingsChanged(QString,QString,QString,uint,QString,bool))); // Job related signals // JobState notifierConnect(QLatin1String("JobState"), this, SIGNAL(jobState(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint))); // JobCreated notifierConnect(QLatin1String("JobCreated"), this, SIGNAL(jobCreated(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint))); // JobStopped notifierConnect(QLatin1String("JobStopped"), this, SIGNAL(jobStopped(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint))); // JobConfigChanged notifierConnect(QLatin1String("JobConfigChanged"), this, SIGNAL(jobConfigChanged(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint))); // JobProgress notifierConnect(QLatin1String("JobProgress"), this, SIGNAL(jobProgress(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint))); // JobCompleted notifierConnect(QLatin1String("JobCompleted"), this, SIGNAL(jobCompleted(QString,QString,QString,uint,QString,bool,uint,uint,QString,QString,uint))); // This signal is needed since the cups registration thing // doesn't emit printerAdded when we add a printer class // This is emitted when a printer/queue is changed QDBusConnection::systemBus().connect(QLatin1String(""), QLatin1String("/com/redhat/PrinterSpooler"), QLatin1String("com.redhat.PrinterSpooler"), QLatin1String("PrinterAdded"), this, SIGNAL(rhPrinterAdded(QString))); // This signal is needed since the cups registration thing // sometimes simple stops working... don't ask me why // This is emitted when a printer/queue is changed QDBusConnection::systemBus().connect(QLatin1String(""), QLatin1String("/com/redhat/PrinterSpooler"), QLatin1String("com.redhat.PrinterSpooler"), QLatin1String("QueueChanged"), this, SIGNAL(rhQueueChanged(QString))); // This signal is needed since the cups registration thing // doesn't emit printerRemoved when we add a printer class // This is emitted when a printer/queue is changed QDBusConnection::systemBus().connect(QLatin1String(""), QLatin1String("/com/redhat/PrinterSpooler"), QLatin1String("com.redhat.PrinterSpooler"), QLatin1String("PrinterRemoved"), this, SIGNAL(rhPrinterRemoved(QString))); QDBusConnection::systemBus().connect(QLatin1String(""), QLatin1String("/com/redhat/PrinterSpooler"), QLatin1String("com.redhat.PrinterSpooler"), QLatin1String("JobQueuedLocal"), this, SIGNAL(rhJobQueuedLocal(QString,uint,QString))); QDBusConnection::systemBus().connect(QLatin1String(""), QLatin1String("/com/redhat/PrinterSpooler"), QLatin1String("com.redhat.PrinterSpooler"), QLatin1String("JobStartedLocal"), this, SIGNAL(rhJobStartedLocal(QString,uint,QString))); // Creates the timer that will renew the DBus subscription m_renewTimer = new QTimer; m_renewTimer->setInterval(RENEW_INTERVAL*1000); m_renewTimer->moveToThread(this); connect(m_renewTimer, &QTimer::timeout, this, static_cast(&KCupsConnection::renewDBusSubscription), Qt::DirectConnection); // Creates the timer to merge updates on the DBus subscription m_subscriptionTimer = new QTimer; m_subscriptionTimer->setInterval(0); m_subscriptionTimer->setSingleShot(true); m_subscriptionTimer->moveToThread(this); connect(m_subscriptionTimer, &QTimer::timeout, this, &KCupsConnection::updateSubscription, Qt::DirectConnection); // Starts this thread start(); } void KCupsConnection::run() { // Check if we need an special connection if (!m_serverUrl.isEmpty()) { if (m_serverUrl.port() < 0) { // TODO find out if there's a better way of hardcoding // the CUPS port m_serverUrl.setPort(631); } - cupsSetServer(m_serverUrl.authority().toUtf8()); + cupsSetServer(qUtf8Printable(m_serverUrl.authority())); } // This is dead cool, cups will call the thread_password_cb() // function when a password set is needed, as we passed the // password dialog pointer the functions just need to call // it on a blocking mode. cupsSetPasswordCB2(password_cb, m_passwordDialog); m_inited = true; exec(); // Event loop quit so cancelDBusSubscription() if (m_subscriptionId != -1) { cancelDBusSubscription(); } } bool KCupsConnection::readyToStart() { if (QThread::currentThread() == this) { password_retries = 0; total_retries = 0; internalErrorCount = 0; return true; } return false; } ReturnArguments KCupsConnection::request(const KIppRequest &request, ipp_tag_t groupTag) const { ReturnArguments ret; ipp_t *response = NULL; do { ippDelete(response); response = NULL; response = request.sendIppRequest(); - } while (retry(request.resource().toUtf8(), request.operation())); + } while (retry(qUtf8Printable(request.resource()), request.operation())); if (response && groupTag != IPP_TAG_ZERO) { ret = parseIPPVars(response, groupTag); } ippDelete(response); return ret; } int KCupsConnection::renewDBusSubscription(int subscriptionId, int leaseDuration, const QStringList &events) { int ret = -1; ipp_op_t operation; // check if we have a valid subscription ID if (subscriptionId >= 0) { // Add the "notify-events" values to the request operation = IPP_RENEW_SUBSCRIPTION; } else { operation = IPP_CREATE_PRINTER_SUBSCRIPTION; } KIppRequest request(operation, "/"); request.addString(IPP_TAG_OPERATION, IPP_TAG_URI, KCUPS_PRINTER_URI, QLatin1String("/")); request.addInteger(IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER, KCUPS_NOTIFY_LEASE_DURATION, leaseDuration); if (operation == IPP_CREATE_PRINTER_SUBSCRIPTION) { // Add the "notify-events" values to the request request.addStringList(IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD, KCUPS_NOTIFY_EVENTS, events); request.addString(IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD, KCUPS_NOTIFY_PULL_METHOD, "ippget"); request.addString(IPP_TAG_SUBSCRIPTION, IPP_TAG_URI, KCUPS_NOTIFY_RECIPIENT_URI, "dbus://"); } else { request.addInteger(IPP_TAG_OPERATION, IPP_TAG_INTEGER, KCUPS_NOTIFY_SUBSCRIPTION_ID, subscriptionId); } ipp_t *response = NULL; do { // Do the request response = request.sendIppRequest(); } while (retry("/", operation)); #if !(CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR < 6) if (response && ippGetStatusCode(response) == IPP_OK) { #else if (response && response->request.status.status_code == IPP_OK) { #endif // !(CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR < 6) ipp_attribute_t *attr; if (subscriptionId >= 0) { // Request was ok, just return the current subscription ret = subscriptionId; } else if ((attr = ippFindAttribute(response, "notify-subscription-id", IPP_TAG_INTEGER)) == NULL) { qCWarning(LIBKCUPS) << "No notify-subscription-id in response!"; ret = -1; } else { #if !(CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR < 6) ret = ippGetInteger(attr, 0); } } else if (subscriptionId >= 0 && response && ippGetStatusCode(response) == IPP_NOT_FOUND) { qCDebug(LIBKCUPS) << "Subscription not found"; // When the subscription is not found try to get a new one return renewDBusSubscription(-1, leaseDuration, events); #else ret = attr->values[0].integer; } } else if (subscriptionId >= 0 && response && response->request.status.status_code == IPP_NOT_FOUND) { qCDebug(LIBKCUPS) << "Subscription not found"; // When the subscription is not found try to get a new one return renewDBusSubscription(-1, leaseDuration, events); #endif // !(CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR < 6) } else { qCDebug(LIBKCUPS) << "Request failed" << cupsLastError() << httpGetStatus(CUPS_HTTP_DEFAULT); // When the server stops/restarts we will have some error so ignore it ret = subscriptionId; } ippDelete(response); return ret; } void KCupsConnection::notifierConnect(const QString &signal, QObject *receiver, const char *slot) { QDBusConnection systemBus = QDBusConnection::systemBus(); systemBus.connect(QString(), QLatin1String("/org/cups/cupsd/Notifier"), QLatin1String("org.cups.cupsd.Notifier"), signal, receiver, slot); } void KCupsConnection::connectNotify(const QMetaMethod & signal) { QMutexLocker locker(&m_mutex); QString event = eventForSignal(signal); if (!event.isNull()) { m_connectedEvents << event; QMetaObject::invokeMethod(m_subscriptionTimer, "start", Qt::QueuedConnection); } } void KCupsConnection::disconnectNotify(const QMetaMethod & signal) { QMutexLocker locker(&m_mutex); QString event = eventForSignal(signal); if (!event.isNull()) { m_connectedEvents.removeOne(event); QMetaObject::invokeMethod(m_subscriptionTimer, "start", Qt::QueuedConnection); } } QString KCupsConnection::eventForSignal(const QMetaMethod & signal) const { // Server signals if (signal == QMetaMethod::fromSignal(&KCupsConnection::serverAudit)) { return DBUS_SERVER_AUDIT; } if (signal == QMetaMethod::fromSignal(&KCupsConnection::serverStarted)) { return DBUS_SERVER_STARTED; } if (signal == QMetaMethod::fromSignal(&KCupsConnection::serverStopped)) { return DBUS_SERVER_STOPPED; } if (signal == QMetaMethod::fromSignal(&KCupsConnection::serverRestarted)) { return DBUS_SERVER_RESTARTED; } // Printer signals if (signal == QMetaMethod::fromSignal(&KCupsConnection::printerAdded)) { return DBUS_PRINTER_ADDED; } if (signal == QMetaMethod::fromSignal(&KCupsConnection::printerDeleted)) { return DBUS_PRINTER_DELETED; } if (signal == QMetaMethod::fromSignal(&KCupsConnection::printerFinishingsChanged)) { return DBUS_PRINTER_FINISHINGS_CHANGED; } if (signal == QMetaMethod::fromSignal(&KCupsConnection::printerMediaChanged)) { return DBUS_PRINTER_MEDIA_CHANGED; } if (signal == QMetaMethod::fromSignal(&KCupsConnection::printerModified)) { return DBUS_PRINTER_MODIFIED; } if (signal == QMetaMethod::fromSignal(&KCupsConnection::printerRestarted)) { return DBUS_PRINTER_RESTARTED; } if (signal == QMetaMethod::fromSignal(&KCupsConnection::printerShutdown)) { return DBUS_PRINTER_SHUTDOWN; } if (signal == QMetaMethod::fromSignal(&KCupsConnection::printerStateChanged)) { return DBUS_PRINTER_STATE_CHANGED; } if (signal == QMetaMethod::fromSignal(&KCupsConnection::printerStopped)) { return DBUS_PRINTER_STOPPED; } // job signals if (signal == QMetaMethod::fromSignal(&KCupsConnection::jobCompleted)) { return DBUS_JOB_COMPLETED; } if (signal == QMetaMethod::fromSignal(&KCupsConnection::jobConfigChanged)) { return DBUS_JOB_CONFIG_CHANGED; } if (signal == QMetaMethod::fromSignal(&KCupsConnection::jobCreated)) { return DBUS_JOB_CREATED; } if (signal == QMetaMethod::fromSignal(&KCupsConnection::jobProgress)) { return DBUS_JOB_PROGRESS; } if (signal == QMetaMethod::fromSignal(&KCupsConnection::jobState)) { return DBUS_JOB_STATE_CHANGED; } if (signal == QMetaMethod::fromSignal(&KCupsConnection::jobStopped)) { return DBUS_JOB_STOPPED; } // No registered event signal matched return QString(); } void KCupsConnection::updateSubscription() { QMutexLocker locker(&m_mutex); // Build the current list QStringList currentEvents = m_connectedEvents; currentEvents.sort(); currentEvents.removeDuplicates(); // Check if the requested events are already being asked if (m_requestedDBusEvents != currentEvents) { m_requestedDBusEvents = currentEvents; // If we alread have a subscription lets cancel // and create a new one if (m_subscriptionId >= 0) { cancelDBusSubscription(); } // Canculates the new events renewDBusSubscription(); } } void KCupsConnection::renewDBusSubscription() { // check if we have a valid subscription ID if (m_subscriptionId >= 0) { m_subscriptionId = renewDBusSubscription(m_subscriptionId, SUBSCRIPTION_DURATION); } // The above request might fail if the subscription was cancelled if (m_subscriptionId < 0) { if (m_requestedDBusEvents.isEmpty()) { m_renewTimer->stop(); } else { m_subscriptionId = renewDBusSubscription(m_subscriptionId, SUBSCRIPTION_DURATION, m_requestedDBusEvents); m_renewTimer->start(); } } } void KCupsConnection::cancelDBusSubscription() { KIppRequest request(IPP_CANCEL_SUBSCRIPTION, "/"); request.addString(IPP_TAG_OPERATION, IPP_TAG_URI, KCUPS_PRINTER_URI, "/"); request.addInteger(IPP_TAG_OPERATION, IPP_TAG_INTEGER, KCUPS_NOTIFY_SUBSCRIPTION_ID, m_subscriptionId); do { // Do the request ippDelete(request.sendIppRequest()); - } while (retry(request.resource().toUtf8(), request.operation())); + } while (retry(qUtf8Printable(request.resource()), request.operation())); // Reset the subscription id m_subscriptionId = -1; } ReturnArguments KCupsConnection::parseIPPVars(ipp_t *response, ipp_tag_t group_tag) { ipp_attribute_t *attr; ReturnArguments ret; #if !(CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR < 6) QVariantHash destAttributes; for (attr = ippFirstAttribute(response); attr != NULL; attr = ippNextAttribute(response)) { // We hit an attribute sepparator if (ippGetName(attr) == NULL) { ret << destAttributes; destAttributes.clear(); continue; } // Skip leading attributes until we hit a a group which can be a printer, job... if (ippGetGroupTag(attr) != group_tag || (ippGetValueTag(attr) != IPP_TAG_INTEGER && ippGetValueTag(attr) != IPP_TAG_ENUM && ippGetValueTag(attr) != IPP_TAG_BOOLEAN && ippGetValueTag(attr) != IPP_TAG_TEXT && ippGetValueTag(attr) != IPP_TAG_TEXTLANG && ippGetValueTag(attr) != IPP_TAG_LANGUAGE && ippGetValueTag(attr) != IPP_TAG_NAME && ippGetValueTag(attr) != IPP_TAG_NAMELANG && ippGetValueTag(attr) != IPP_TAG_KEYWORD && ippGetValueTag(attr) != IPP_TAG_RANGE && ippGetValueTag(attr) != IPP_TAG_URI)) { continue; } // Add a printer description attribute... destAttributes[QString::fromUtf8(ippGetName(attr))] = ippAttrToVariant(attr); } if (!destAttributes.isEmpty()) { ret << destAttributes; } #else for (attr = response->attrs; attr != NULL; attr = attr->next) { /* * Skip leading attributes until we hit a a group which can be a printer, job... */ while (attr && attr->group_tag != group_tag) { attr = attr->next; } if (attr == NULL) { break; } /* * Pull the needed attributes from this printer... */ QVariantHash destAttributes; for (; attr && attr->group_tag == group_tag; attr = attr->next) { if (attr->value_tag != IPP_TAG_INTEGER && attr->value_tag != IPP_TAG_ENUM && attr->value_tag != IPP_TAG_BOOLEAN && attr->value_tag != IPP_TAG_TEXT && attr->value_tag != IPP_TAG_TEXTLANG && attr->value_tag != IPP_TAG_LANGUAGE && attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_NAMELANG && attr->value_tag != IPP_TAG_KEYWORD && attr->value_tag != IPP_TAG_RANGE && attr->value_tag != IPP_TAG_URI) { continue; } /* * Add a printer description attribute... */ destAttributes[QString::fromUtf8(attr->name)] = ippAttrToVariant(attr); } ret << destAttributes; if (attr == NULL) { break; } } #endif // !(CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR < 6) return ret; } QVariant KCupsConnection::ippAttrToVariant(ipp_attribute_t *attr) { QVariant ret; #if !(CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR < 6) switch (ippGetValueTag(attr)) { case IPP_TAG_INTEGER: case IPP_TAG_ENUM: if (ippGetCount(attr) == 1) { ret = ippGetInteger(attr, 0); } else { QList values; for (int i = 0; i < ippGetCount(attr); ++i) { values << ippGetInteger(attr, i); } ret = qVariantFromValue(values); } break; case IPP_TAG_BOOLEAN: if (ippGetCount(attr)== 1) { ret = ippGetBoolean(attr, 0); } else { QList values; for (int i = 0; i < ippGetCount(attr); ++i) { values << ippGetBoolean(attr, i); } ret = qVariantFromValue(values); } break; case IPP_TAG_RANGE: { QVariantList values; for (int i = 0; i < ippGetCount(attr); ++i) { int rangeUpper; values << ippGetRange(attr, i, &rangeUpper); values << rangeUpper; } ret = values; } break; default: if (ippGetCount(attr)== 1) { ret = QString::fromUtf8(ippGetString(attr, 0, NULL)); } else { QStringList values; for (int i = 0; i < ippGetCount(attr); ++i) { values << QString::fromUtf8(ippGetString(attr, i, NULL)); } ret = values; } } #else switch (attr->value_tag) { case IPP_TAG_INTEGER: case IPP_TAG_ENUM: if (attr->num_values == 1) { ret = attr->values[0].integer; } else { QList values; for (int i = 0; i < attr->num_values; ++i) { values << attr->values[i].integer; } ret = qVariantFromValue(values); } break; case IPP_TAG_BOOLEAN: if (attr->num_values == 1) { ret = static_cast(attr->values[0].integer); } else { QList values; for (int i = 0; i < attr->num_values; ++i) { values << static_cast(attr->values[i].integer); } ret = qVariantFromValue(values); } break; case IPP_TAG_RANGE: { QVariantList values; for (int i = 0; i < attr->num_values; ++i) { values << attr->values[i].range.lower; values << attr->values[i].range.upper; } ret = values; } break; default: if (attr->num_values == 1) { ret = QString::fromUtf8(attr->values[0].string.text); } else { QStringList values; for (int i = 0; i < attr->num_values; ++i) { values << QString::fromUtf8(attr->values[i].string.text); } ret = values; } } #endif // !(CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR < 6) return ret; } bool KCupsConnection::retry(const char *resource, int operation) const { ipp_status_t status = cupsLastError(); if (operation != -1) { qCDebug(LIBKCUPS) << ippOpString(static_cast(operation)) << "last error:" << status << cupsLastErrorString(); } else { qCDebug(LIBKCUPS) << operation << "last error:" << status << cupsLastErrorString(); } // When CUPS process stops our connection // with it fails and has to be re-established if (status == IPP_INTERNAL_ERROR) { // Deleting this connection thread forces it // to create a new CUPS connection qCWarning(LIBKCUPS) << "IPP_INTERNAL_ERROR: clearing cookies and reconnecting"; // TODO maybe reconnect is enough // httpClearCookie(CUPS_HTTP_DEFAULT); // Reconnect to CUPS if (httpReconnect(CUPS_HTTP_DEFAULT)) { qCWarning(LIBKCUPS) << "Failed to reconnect" << cupsLastErrorString(); // Server might be restarting sleep for a few ms msleep(500); } // Try the request again return ++internalErrorCount < 3; } total_retries++; if (total_retries > (password_retries + 3)) { // Something is wrong. // This will happen if the password_cb function is not called, // which will for example be the case if the server has // an IP blacklist and thus always return 403. // In this case, there is nothing we can do. return false; } bool forceAuth = false; // If our user is forbidden to perform the // task we try again using the root user // ONLY if it was the first time if (status == IPP_FORBIDDEN && password_retries == 0) { // Pretend to be the root user // Sometimes setting this just works cupsSetUser("root"); // force authentication forceAuth = true; } if (status == IPP_NOT_AUTHORIZED || status == IPP_NOT_AUTHENTICATED) { if (password_retries > 3 || password_retries == -1) { // the authentication failed 3 times // OR the dialog was canceld (-1) // reset to 0 and quit the do-while loop password_retries = 0; total_retries = 0; return false; } // force authentication forceAuth = true; } if (forceAuth) { // force authentication qCDebug(LIBKCUPS) << "Calling cupsDoAuthentication() password_retries:" << password_retries; int ret = cupsDoAuthentication(CUPS_HTTP_DEFAULT, "POST", resource); qCDebug(LIBKCUPS) << "Called cupsDoAuthentication(), success:" << (ret == -1 ? true : false); // If the authentication was succefull // sometimes just trying to be root works return ret == -1 ? true : false; } // the action was not forbidden return false; } const char * password_cb(const char *prompt, http_t *http, const char *method, const char *resource, void *user_data) { Q_UNUSED(prompt) Q_UNUSED(http) Q_UNUSED(method) Q_UNUSED(resource) if (++password_retries > 3) { // cancel the authentication cupsSetUser(NULL); return NULL; } auto passwordDialog = static_cast(user_data); bool wrongPassword = password_retries > 1; // This will block this thread until exec is not finished qCDebug(LIBKCUPS) << password_retries; QMetaObject::invokeMethod(passwordDialog, "exec", Qt::BlockingQueuedConnection, Q_ARG(QString, QString::fromUtf8(cupsUser())), Q_ARG(bool, wrongPassword)); qCDebug(LIBKCUPS) << passwordDialog->accepted(); // The password dialog has just returned check the result // method that returns QDialog enums if (passwordDialog->accepted()) { - cupsSetUser(passwordDialog->username().toUtf8()); - return passwordDialog->password().toUtf8(); + cupsSetUser(qUtf8Printable(passwordDialog->username())); + return qUtf8Printable(passwordDialog->password()); } else { // the dialog was canceled password_retries = -1; cupsSetUser(NULL); return NULL; } } diff --git a/libkcups/KCupsRequest.cpp b/libkcups/KCupsRequest.cpp index 199a2a6..e02c881 100644 --- a/libkcups/KCupsRequest.cpp +++ b/libkcups/KCupsRequest.cpp @@ -1,670 +1,670 @@ /*************************************************************************** * Copyright (C) 2010-2012 by Daniel Nicoletti * * dantti12@gmail.com * * * * This program 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 2 of the License, or * * (at your option) any later version. * * * * This program 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 this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "KCupsRequest.h" #include "Debug.h" #include "KIppRequest.h" #include "KCupsJob.h" #include "KCupsPrinter.h" #include #include #include #include #define CUPS_DATADIR "/usr/share/cups" KCupsRequest::KCupsRequest(KCupsConnection *connection) : m_connection(connection), m_finished(true), m_error(IPP_OK) { // If no connection was specified use default one if (m_connection == 0) { m_connection = KCupsConnection::global(); } connect(this, &KCupsRequest::finished, &m_loop, &QEventLoop::quit); } QString KCupsRequest::serverError() const { switch (error()) { case IPP_SERVICE_UNAVAILABLE: return i18n("Print service is unavailable"); case IPP_NOT_FOUND : return i18n("Not found"); default : // In this case we don't want to map all enums qCWarning(LIBKCUPS) << "status unrecognised: " << error(); return QString::fromUtf8(ippErrorString(error())); } } void KCupsRequest::getPPDS(const QString &make) { if (m_connection->readyToStart()) { KIppRequest request(CUPS_GET_PPDS, "/"); if (!make.isEmpty()) { request.addString(IPP_TAG_PRINTER, IPP_TAG_TEXT, KCUPS_PPD_MAKE_AND_MODEL, make); } m_ppds = m_connection->request(request, IPP_TAG_PRINTER); setError(httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError(), QString::fromUtf8(cupsLastErrorString())); setFinished(); } else { invokeMethod("getPPDS", make); } } static void choose_device_cb(const char *device_class, /* I - Class */ const char *device_id, /* I - 1284 device ID */ const char *device_info, /* I - Description */ const char *device_make_and_model, /* I - Make and model */ const char *device_uri, /* I - Device URI */ const char *device_location, /* I - Location */ void *user_data) /* I - Result object */ { /* * Add the device to the array... */ auto request = static_cast(user_data); QMetaObject::invokeMethod(request, "device", Qt::QueuedConnection, Q_ARG(QString, QString::fromUtf8(device_class)), Q_ARG(QString, QString::fromUtf8(device_id)), Q_ARG(QString, QString::fromUtf8(device_info)), Q_ARG(QString, QString::fromUtf8(device_make_and_model)), Q_ARG(QString, QString::fromUtf8(device_uri)), Q_ARG(QString, QString::fromUtf8(device_location))); } void KCupsRequest::getDevices(int timeout) { getDevices(timeout, QStringList(), QStringList()); } void KCupsRequest::getDevices(int timeout, QStringList includeSchemes, QStringList excludeSchemes) { if (m_connection->readyToStart()) { do { const char *include; if (includeSchemes.isEmpty()) { include = CUPS_INCLUDE_ALL; } else { - include = includeSchemes.join(QLatin1String(",")).toUtf8(); + include = qUtf8Printable(includeSchemes.join(QLatin1String(","))); } const char *exclude; if (excludeSchemes.isEmpty()) { exclude = CUPS_EXCLUDE_NONE; } else { - exclude = excludeSchemes.join(QLatin1String(",")).toUtf8(); + exclude = qUtf8Printable(excludeSchemes.join(QLatin1String(","))); } // Scan for devices for "timeout" seconds cupsGetDevices(CUPS_HTTP_DEFAULT, timeout, include, exclude, (cups_device_cb_t) choose_device_cb, this); } while (m_connection->retry("/admin/", CUPS_GET_DEVICES)); setError(httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError(), QString::fromUtf8(cupsLastErrorString())); setFinished(true); } else { invokeMethod("getDevices", timeout, includeSchemes, excludeSchemes); } } // THIS function can get the default server dest through the // "printer-is-default" attribute BUT it does not get user // defined default printer, see cupsGetDefault() on www.cups.org for details void KCupsRequest::getPrinters(QStringList attributes, int mask) { if (m_connection->readyToStart()) { KIppRequest request(CUPS_GET_PRINTERS, "/"); request.addInteger(IPP_TAG_OPERATION, IPP_TAG_ENUM, KCUPS_PRINTER_TYPE, CUPS_PRINTER_LOCAL); if (!attributes.isEmpty()) { request.addStringList(IPP_TAG_OPERATION, IPP_TAG_KEYWORD, KCUPS_REQUESTED_ATTRIBUTES, attributes); } if (mask != -1) { request.addInteger(IPP_TAG_OPERATION, IPP_TAG_ENUM, KCUPS_PRINTER_TYPE_MASK, mask); } const ReturnArguments ret = m_connection->request(request, IPP_TAG_PRINTER); for (const QVariantHash &arguments : ret) { m_printers << KCupsPrinter(arguments); } setError(httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError(), QString::fromUtf8(cupsLastErrorString())); setFinished(); } else { invokeMethod("getPrinters", qVariantFromValue(attributes), mask); } } void KCupsRequest::getPrinterAttributes(const QString &printerName, bool isClass, QStringList attributes) { if (m_connection->readyToStart()) { KIppRequest request(IPP_GET_PRINTER_ATTRIBUTES, "/"); request.addPrinterUri(printerName, isClass); request.addInteger(IPP_TAG_OPERATION, IPP_TAG_ENUM, KCUPS_PRINTER_TYPE, CUPS_PRINTER_LOCAL); request.addStringList(IPP_TAG_OPERATION, IPP_TAG_KEYWORD, KCUPS_REQUESTED_ATTRIBUTES, attributes); const ReturnArguments ret = m_connection->request(request, IPP_TAG_PRINTER); for (const QVariantHash &arguments : ret) { // Inject the printer name back to the arguments hash QVariantHash args = arguments; args[KCUPS_PRINTER_NAME] = printerName; m_printers << KCupsPrinter(args); } setError(httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError(), QString::fromUtf8(cupsLastErrorString())); setFinished(); } else { invokeMethod("getPrinterAttributes", printerName, isClass, qVariantFromValue(attributes)); } } void KCupsRequest::getJobs(const QString &printerName, bool myJobs, int whichJobs, QStringList attributes) { if (m_connection->readyToStart()) { KIppRequest request(IPP_GET_JOBS, "/"); // printer-uri makes the Name of the Job and owner came blank lol request.addPrinterUri(printerName, false); request.addInteger(IPP_TAG_OPERATION, IPP_TAG_ENUM, KCUPS_PRINTER_TYPE, CUPS_PRINTER_LOCAL); request.addStringList(IPP_TAG_OPERATION, IPP_TAG_KEYWORD, KCUPS_REQUESTED_ATTRIBUTES, attributes); request.addInteger(IPP_TAG_OPERATION, IPP_TAG_ENUM, KCUPS_MY_JOBS, myJobs); if (whichJobs == CUPS_WHICHJOBS_COMPLETED) { request.addString(IPP_TAG_OPERATION, IPP_TAG_KEYWORD, KCUPS_WHICH_JOBS, "completed"); } else if (whichJobs == CUPS_WHICHJOBS_ALL) { request.addString(IPP_TAG_OPERATION, IPP_TAG_KEYWORD, KCUPS_WHICH_JOBS, "all"); } const ReturnArguments ret = m_connection->request(request, IPP_TAG_JOB); for (const QVariantHash &arguments : ret) { m_jobs << KCupsJob(arguments); } setError(httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError(), QString::fromUtf8(cupsLastErrorString())); setFinished(); } else { invokeMethod("getJobs", printerName, myJobs, whichJobs, qVariantFromValue(attributes)); } } void KCupsRequest::getJobAttributes(int jobId, const QString &printerUri, QStringList attributes) { if (m_connection->readyToStart()) { KIppRequest request(IPP_GET_JOB_ATTRIBUTES, "/"); request.addString(IPP_TAG_OPERATION, IPP_TAG_URI, KCUPS_PRINTER_URI, printerUri); request.addInteger(IPP_TAG_OPERATION, IPP_TAG_ENUM, KCUPS_PRINTER_TYPE, CUPS_PRINTER_LOCAL); request.addStringList(IPP_TAG_OPERATION, IPP_TAG_KEYWORD, KCUPS_REQUESTED_ATTRIBUTES, attributes); request.addInteger(IPP_TAG_OPERATION, IPP_TAG_INTEGER, KCUPS_JOB_ID, jobId); const ReturnArguments ret = m_connection->request(request, IPP_TAG_PRINTER); for (const QVariantHash &arguments : ret) { m_jobs << KCupsJob(arguments); } setError(httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError(), QString::fromUtf8(cupsLastErrorString())); setFinished(); } else { invokeMethod("getJobAttributes", jobId, printerUri, qVariantFromValue(attributes)); } } void KCupsRequest::getServerSettings() { if (m_connection->readyToStart()) { do { int num_settings; cups_option_t *settings; QVariantHash arguments; int ret = cupsAdminGetServerSettings(CUPS_HTTP_DEFAULT, &num_settings, &settings); for (int i = 0; i < num_settings; ++i) { QString name = QString::fromUtf8(settings[i].name); QString value = QString::fromUtf8(settings[i].value); arguments[name] = value; } cupsFreeOptions(num_settings, settings); if (ret) { setError(HTTP_OK, IPP_OK, QString()); } else { setError(httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError(), QString::fromUtf8(cupsLastErrorString())); } m_server = KCupsServer(arguments); } while (m_connection->retry("/admin/", -1)); setFinished(); } else { invokeMethod("getServerSettings"); } } void KCupsRequest::getPrinterPPD(const QString &printerName) { if (m_connection->readyToStart()) { do { const char *filename; - filename = cupsGetPPD2(CUPS_HTTP_DEFAULT, printerName.toUtf8()); + filename = cupsGetPPD2(CUPS_HTTP_DEFAULT, qUtf8Printable(printerName)); qCDebug(LIBKCUPS) << filename; m_ppdFile = filename; qCDebug(LIBKCUPS) << m_ppdFile; } while (m_connection->retry("/", CUPS_GET_PPD)); setError(httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError(), QString::fromUtf8(cupsLastErrorString())); setFinished(); } else { invokeMethod("getPrinterPPD", printerName); } } void KCupsRequest::setServerSettings(const KCupsServer &server) { if (m_connection->readyToStart()) { do { QVariantHash args = server.arguments(); int num_settings = 0; cups_option_t *settings; QVariantHash::const_iterator i = args.constBegin(); while (i != args.constEnd()) { - num_settings = cupsAddOption(i.key().toUtf8(), - i.value().toString().toUtf8(), + num_settings = cupsAddOption(qUtf8Printable(i.key()), + qUtf8Printable(i.value().toString()), num_settings, &settings); ++i; } cupsAdminSetServerSettings(CUPS_HTTP_DEFAULT, num_settings, settings); cupsFreeOptions(num_settings, settings); } while (m_connection->retry("/admin/", -1)); setError(httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError(), QString::fromUtf8(cupsLastErrorString())); setFinished(); } else { invokeMethod("setServerSettings", qVariantFromValue(server)); } } void KCupsRequest::addOrModifyPrinter(const QString &printerName, const QVariantHash &attributes, const QString &filename) { KIppRequest request(CUPS_ADD_MODIFY_PRINTER, "/admin/", filename); request.addPrinterUri(printerName); request.addVariantValues(attributes); process(request); } void KCupsRequest::addOrModifyClass(const QString &printerName, const QVariantHash &attributes) { KIppRequest request(CUPS_ADD_MODIFY_CLASS, "/admin/"); request.addPrinterUri(printerName, true); request.addVariantValues(attributes); process(request); } void KCupsRequest::setShared(const QString &printerName, bool isClass, bool shared) { KIppRequest request(isClass ? CUPS_ADD_MODIFY_CLASS : CUPS_ADD_MODIFY_PRINTER, "/admin/"); request.addPrinterUri(printerName, isClass); request.addBoolean(IPP_TAG_OPERATION, KCUPS_PRINTER_IS_SHARED, shared); process(request); } void KCupsRequest::pausePrinter(const QString &printerName) { KIppRequest request(IPP_PAUSE_PRINTER, "/admin/"); request.addPrinterUri(printerName); process(request); } void KCupsRequest::resumePrinter(const QString &printerName) { KIppRequest request(IPP_RESUME_PRINTER, "/admin/"); request.addPrinterUri(printerName); process(request); } void KCupsRequest::rejectJobs(const QString &printerName) { KIppRequest request(CUPS_REJECT_JOBS, "/admin/"); request.addPrinterUri(printerName); process(request); } void KCupsRequest::acceptJobs(const QString &printerName) { KIppRequest request(CUPS_ACCEPT_JOBS, "/admin/"); request.addPrinterUri(printerName); process(request); } void KCupsRequest::setDefaultPrinter(const QString &printerName) { KIppRequest request(CUPS_SET_DEFAULT, "/admin/"); request.addPrinterUri(printerName); process(request); } void KCupsRequest::deletePrinter(const QString &printerName) { KIppRequest request(CUPS_DELETE_PRINTER, "/admin/"); request.addPrinterUri(printerName); process(request); } void KCupsRequest::printTestPage(const QString &printerName, bool isClass) { QString resource; /* POST resource path */ QString filename; /* Test page filename */ QString datadir; /* CUPS_DATADIR env var */ /* * Locate the test page file... */ datadir = qgetenv("CUPS_DATADIR"); if (datadir.isEmpty()) { datadir = CUPS_DATADIR; } filename = datadir % QLatin1String("/data/testprint"); /* * Point to the printer/class... */ if (isClass) { resource = QLatin1String("/classes/") % printerName; } else { resource = QLatin1String("/printers/") % printerName; } - KIppRequest request(IPP_PRINT_JOB, resource.toUtf8(), filename); + KIppRequest request(IPP_PRINT_JOB, qUtf8Printable(resource), filename); request.addPrinterUri(printerName); request.addString(IPP_TAG_OPERATION, IPP_TAG_NAME, KCUPS_JOB_NAME, i18n("Test Page")); process(request); } void KCupsRequest::printCommand(const QString &printerName, const QString &command, const QString &title) { if (m_connection->readyToStart()) { do { int job_id; /* Command file job */ char command_file[1024]; /* Command "file" */ http_status_t status; /* Document status */ cups_option_t hold_option; /* job-hold-until option */ /* * Create the CUPS command file... */ snprintf(command_file, sizeof(command_file), "#CUPS-COMMAND\n%s\n", command.toUtf8().data()); /* * Send the command file job... */ hold_option.name = const_cast("job-hold-until"); hold_option.value = const_cast("no-hold"); if ((job_id = cupsCreateJob(CUPS_HTTP_DEFAULT, - printerName.toUtf8(), - title.toUtf8(), + qUtf8Printable(printerName), + qUtf8Printable(title), 1, &hold_option)) < 1) { qWarning() << "Unable to send command to printer driver!"; setError(HTTP_OK, IPP_NOT_POSSIBLE, i18n("Unable to send command to printer driver!")); setFinished(); return; } status = cupsStartDocument(CUPS_HTTP_DEFAULT, - printerName.toUtf8(), + qUtf8Printable(printerName), job_id, NULL, CUPS_FORMAT_COMMAND, 1); if (status == HTTP_CONTINUE) { status = cupsWriteRequestData(CUPS_HTTP_DEFAULT, command_file, strlen(command_file)); } if (status == HTTP_CONTINUE) { - cupsFinishDocument(CUPS_HTTP_DEFAULT, printerName.toUtf8()); + cupsFinishDocument(CUPS_HTTP_DEFAULT, qUtf8Printable(printerName)); } setError(httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError(), QString::fromUtf8(cupsLastErrorString())); if (httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError() >= IPP_REDIRECTION_OTHER_SITE) { qWarning() << "Unable to send command to printer driver!"; - cupsCancelJob(printerName.toUtf8(), job_id); + cupsCancelJob(qUtf8Printable(printerName), job_id); setFinished(); return; // Return to avoid a new try } } while (m_connection->retry("/", IPP_CREATE_JOB)); setError(httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError(), QString::fromUtf8(cupsLastErrorString())); setFinished(); } else { invokeMethod("printCommand", printerName, command, title); } } void KCupsRequest::cancelJob(const QString &printerName, int jobId) { KIppRequest request(IPP_CANCEL_JOB, "/jobs/"); request.addPrinterUri(printerName); request.addInteger(IPP_TAG_OPERATION, IPP_TAG_INTEGER, KCUPS_JOB_ID, jobId); process(request); } void KCupsRequest::holdJob(const QString &printerName, int jobId) { KIppRequest request(IPP_HOLD_JOB, "/jobs/"); request.addPrinterUri(printerName); request.addInteger(IPP_TAG_OPERATION, IPP_TAG_INTEGER, KCUPS_JOB_ID, jobId); process(request); } void KCupsRequest::releaseJob(const QString &printerName, int jobId) { KIppRequest request(IPP_RELEASE_JOB, "/jobs/"); request.addPrinterUri(printerName); request.addInteger(IPP_TAG_OPERATION, IPP_TAG_INTEGER, KCUPS_JOB_ID, jobId); process(request); } void KCupsRequest::restartJob(const QString &printerName, int jobId) { KIppRequest request(IPP_RESTART_JOB, "/jobs/"); request.addPrinterUri(printerName); request.addInteger(IPP_TAG_OPERATION, IPP_TAG_INTEGER, KCUPS_JOB_ID, jobId); process(request); } void KCupsRequest::moveJob(const QString &fromPrinterName, int jobId, const QString &toPrinterName) { if (jobId < -1 || fromPrinterName.isEmpty() || toPrinterName.isEmpty() || jobId == 0) { qWarning() << "Internal error, invalid input data" << jobId << fromPrinterName << toPrinterName; setFinished(); return; } KIppRequest request(CUPS_MOVE_JOB, "/jobs/"); request.addPrinterUri(fromPrinterName); request.addInteger(IPP_TAG_OPERATION, IPP_TAG_INTEGER, KCUPS_JOB_ID, jobId); QString toPrinterUri = KIppRequest::assembleUrif(toPrinterName, false); request.addString(IPP_TAG_OPERATION, IPP_TAG_URI, KCUPS_JOB_PRINTER_URI, toPrinterUri); process(request); } void KCupsRequest::invokeMethod(const char *method, const QVariant &arg1, const QVariant &arg2, const QVariant &arg3, const QVariant &arg4, const QVariant &arg5, const QVariant &arg6, const QVariant &arg7, const QVariant &arg8) { m_error = IPP_OK; m_errorMsg.clear(); m_printers.clear(); m_jobs.clear(); m_ppds.clear(); m_ppdFile.clear(); // If this fails we get into a infinite loop // Do not use global()->thread() which point // to the KCupsConnection parent thread moveToThread(m_connection); m_finished = !QMetaObject::invokeMethod(this, method, Qt::QueuedConnection, QGenericArgument(arg1.typeName(), arg1.data()), QGenericArgument(arg2.typeName(), arg2.data()), QGenericArgument(arg3.typeName(), arg3.data()), QGenericArgument(arg4.typeName(), arg4.data()), QGenericArgument(arg5.typeName(), arg5.data()), QGenericArgument(arg6.typeName(), arg6.data()), QGenericArgument(arg7.typeName(), arg7.data()), QGenericArgument(arg8.typeName(), arg8.data())); if (m_finished) { setError(HTTP_ERROR, IPP_BAD_REQUEST, i18n("Failed to invoke method: %1", method)); setFinished(); } } void KCupsRequest::process(const KIppRequest &request) { if (m_connection->readyToStart()) { m_connection->request(request); setError(httpGetStatus(CUPS_HTTP_DEFAULT), cupsLastError(), QString::fromUtf8(cupsLastErrorString())); setFinished(); } else { invokeMethod("process", qVariantFromValue(request)); } } ReturnArguments KCupsRequest::ppds() const { return m_ppds; } KCupsServer KCupsRequest::serverSettings() const { return m_server; } QString KCupsRequest::printerPPD() const { return m_ppdFile; } KCupsPrinters KCupsRequest::printers() const { return m_printers; } KCupsJobs KCupsRequest::jobs() const { return m_jobs; } void KCupsRequest::waitTillFinished() { if (m_finished) { return; } m_loop.exec(); } bool KCupsRequest::hasError() const { return m_error; } ipp_status_t KCupsRequest::error() const { return m_error; } http_status_t KCupsRequest::httpStatus() const { return m_httpStatus; } QString KCupsRequest::errorMsg() const { return m_errorMsg; } KCupsConnection *KCupsRequest::connection() const { return m_connection; } void KCupsRequest::setError(http_status_t httpStatus, ipp_status_t error, const QString &errorMsg) { m_httpStatus = httpStatus; m_error = error; m_errorMsg = errorMsg; } void KCupsRequest::setFinished(bool delayed) { m_finished = true; if (delayed) { QTimer::singleShot(0, this, [this] () { emit finished(this); }); } else { emit finished(this); } } diff --git a/libkcups/KIppRequest.cpp b/libkcups/KIppRequest.cpp index 0ece5ef..bf2567b 100644 --- a/libkcups/KIppRequest.cpp +++ b/libkcups/KIppRequest.cpp @@ -1,276 +1,276 @@ /*************************************************************************** * Copyright (C) 2010-2013 by Daniel Nicoletti * * dantti12@gmail.com * * * * This program 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 2 of the License, or * * (at your option) any later version. * * * * This program 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 this program; see the file COPYING. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "KIppRequest.h" #include "KIppRequest_p.h" #include "Debug.h" #include KIppRequest::KIppRequest() : d_ptr(new KIppRequestPrivate) { } KIppRequest::KIppRequest(const KIppRequest &other) : d_ptr(new KIppRequestPrivate) { *this = other; } KIppRequest::KIppRequest(ipp_op_t operation, const char *resource, const QString &filename) : d_ptr(new KIppRequestPrivate) { Q_D(KIppRequest); d->operation = operation; d->resource = resource; d->filename = filename; // send our user name on the request too addString(IPP_TAG_OPERATION, IPP_TAG_NAME, KCUPS_REQUESTING_USER_NAME, cupsUser()); } KIppRequest::~KIppRequest() { Q_D(KIppRequest); delete d; } ipp_op_t KIppRequest::operation() const { Q_D(const KIppRequest); return d->operation; } QString KIppRequest::resource() const { Q_D(const KIppRequest); return d->resource; } QString KIppRequest::filename() const { Q_D(const KIppRequest); return d->filename; } ipp_t *KIppRequest::sendIppRequest() const { Q_D(const KIppRequest); ipp_t *request = ippNewRequest(d->operation); d->addRawRequestsToIpp(request); if (d->filename.isNull()) { - return cupsDoRequest(CUPS_HTTP_DEFAULT, request, d->resource.toUtf8()); + return cupsDoRequest(CUPS_HTTP_DEFAULT, request, qUtf8Printable(d->resource)); } else { - return cupsDoFileRequest(CUPS_HTTP_DEFAULT, request, d->resource.toUtf8(), d->filename.toUtf8()); + return cupsDoFileRequest(CUPS_HTTP_DEFAULT, request, qUtf8Printable(d->resource), qUtf8Printable(d->filename)); } } void KIppRequest::addString(ipp_tag_t group, ipp_tag_t valueTag, const QString &name, const QString &value) { Q_D(KIppRequest); d->addRequest(group, valueTag, name.toUtf8(), value); } void KIppRequest::addStringList(ipp_tag_t group, ipp_tag_t valueTag, const QString &name, const QStringList &value) { Q_D(KIppRequest); d->addRequest(group, valueTag, name.toUtf8(), value); } void KIppRequest::addInteger(ipp_tag_t group, ipp_tag_t valueTag, const QString &name, int value) { Q_D(KIppRequest); d->addRequest(group, valueTag, name.toUtf8(), value); } void KIppRequest::addBoolean(ipp_tag_t group, const QString &name, bool value) { Q_D(KIppRequest); d->addRequest(group, IPP_TAG_ZERO, name.toUtf8(), value); } void KIppRequest::addVariantValues(const QVariantHash &values) { QVariantHash::ConstIterator i = values.constBegin(); while (i != values.constEnd()) { QString key = i.key(); QVariant value = i.value(); switch (value.type()) { case QVariant::Bool: // Still in use at add-printer/PageAddPrinter.cpp if (key == QLatin1String(KCUPS_PRINTER_IS_ACCEPTING_JOBS)) { addBoolean(IPP_TAG_PRINTER, key, value.toBool()); } else { addBoolean(IPP_TAG_OPERATION, key, value.toBool()); } break; case QVariant::Int: // Still in use at add-printer/PageAddPrinter.cpp if (key == QLatin1String(KCUPS_PRINTER_STATE)) { addInteger(IPP_TAG_PRINTER, IPP_TAG_ENUM, key, value.toInt()); } else { addInteger(IPP_TAG_OPERATION, IPP_TAG_ENUM, key, value.toInt()); } break; case QVariant::String: // Still in use at add-printer/* if (key == QLatin1String(KCUPS_DEVICE_URI)) { // device uri has a different TAG addString(IPP_TAG_PRINTER, IPP_TAG_URI, key, value.toString()); } else if (key == QLatin1String(KCUPS_PRINTER_OP_POLICY) || key == QLatin1String(KCUPS_PRINTER_ERROR_POLICY) || key == QLatin1String("ppd-name")) { // printer-op-policy has a different TAG addString(IPP_TAG_PRINTER, IPP_TAG_NAME, key, value.toString()); } else if (key == QLatin1String(KCUPS_JOB_NAME)) { addString(IPP_TAG_OPERATION, IPP_TAG_NAME, key, value.toString()); } else if (key == QLatin1String(KCUPS_WHICH_JOBS)) { addString(IPP_TAG_OPERATION, IPP_TAG_KEYWORD, key, value.toString()); } else { addString(IPP_TAG_PRINTER, IPP_TAG_TEXT, key, value.toString()); } break; case QVariant::StringList: if (key == QLatin1String(KCUPS_MEMBER_URIS)) { addStringList(IPP_TAG_PRINTER, IPP_TAG_URI, key, value.toStringList()); } else { addStringList(IPP_TAG_PRINTER, IPP_TAG_NAME, key, value.toStringList()); } break; case QVariant::UInt: addInteger(IPP_TAG_OPERATION, IPP_TAG_ENUM, key, value.toInt()); break; default: qCWarning(LIBKCUPS) << "type NOT recognized! This will be ignored:" << key << "values" << i.value(); } ++i; } } void KIppRequest::addPrinterUri(const QString &printerName, bool isClass) { QString uri = assembleUrif(printerName, isClass); addString(IPP_TAG_OPERATION, IPP_TAG_URI, KCUPS_PRINTER_URI, uri); } QString KIppRequest::assembleUrif(const QString &name, bool isClass) { char uri[HTTP_MAX_URI]; // printer URI QString destination; if (isClass) { destination = QLatin1String("/classes/") % name; } else { destination = QLatin1String("/printers/") % name; } httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", cupsUser(), "localhost", - ippPort(), destination.toUtf8()); + ippPort(), destination.toUtf8().constData()); return uri; } KIppRequest &KIppRequest::operator =(const KIppRequest &other) { Q_D(KIppRequest); if (this == &other) return *this; *d = *other.d_ptr; return *this; } void KIppRequestPrivate::addRequest(ipp_tag_t group, ipp_tag_t valueTag, const QString &name, const QVariant &value) { KCupsRawRequest request; request.group = group; request.valueTag = valueTag; request.name = name; request.value = value; rawRequests << request; } void KIppRequestPrivate::addRawRequestsToIpp(ipp_t *ipp) const { // sort the values as CUPS requires it std::sort(rawRequests.begin(), rawRequests.end(), [] (const KCupsRawRequest &a, const KCupsRawRequest &b) { return a.group < b.group; }); const QList &requests = rawRequests; for (const KCupsRawRequest &request :requests) { switch (request.value.type()) { case QVariant::Bool: ippAddBoolean(ipp, request.group, - request.name.toUtf8(), + request.name.toUtf8().constData(), request.value.toBool()); break; case QVariant::Int: case QVariant::UInt: ippAddInteger(ipp, request.group, request.valueTag, - request.name.toUtf8(), + request.name.toUtf8().constData(), request.value.toInt()); break; case QVariant::String: ippAddString(ipp, request.group, request.valueTag, - request.name.toUtf8(), + request.name.toUtf8().constData(), "utf-8", - request.value.toString().toUtf8()); + request.value.toString().toUtf8().constData()); break; case QVariant::StringList: { QStringList list = request.value.toStringList(); QList valuesQByteArrayList; const char **values = qStringListToCharPtrPtr(list, valuesQByteArrayList); ippAddStrings(ipp, request.group, request.valueTag, - request.name.toUtf8(), + request.name.toUtf8().constData(), list.size(), "utf-8", values); // ippAddStrings deep copies everything so we can throw away the values. // the QBAList and content is auto discarded when going out of scope. delete [] values; break; } default: qCWarning(LIBKCUPS) << "type NOT recognized! This will be ignored:" << request.name << "values" << request.value; } } }