ipprequest.cpp

00001 /*
00002  *  This file is part of the KDE libraries
00003  *  Copyright (c) 2001 Michael Goffioul <kdeprint@swing.be>
00004  *
00005  *  This library is free software; you can redistribute it and/or
00006  *  modify it under the terms of the GNU Library General Public
00007  *  License version 2 as published by the Free Software Foundation.
00008  *
00009  *  This library is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  *  Library General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU Library General Public License
00015  *  along with this library; see the file COPYING.LIB.  If not, write to
00016  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  *  Boston, MA 02110-1301, USA.
00018  **/
00019 
00020 #include "ipprequest.h"
00021 #include "cupsinfos.h"
00022 
00023 #include <stdlib.h>
00024 #include <cups/language.h>
00025 #include <kdebug.h>
00026 #include <kglobal.h>
00027 #include <klocale.h>
00028 #include <qdatetime.h>
00029 #include <qregexp.h>
00030 #include <cups/cups.h>
00031 
00032 #ifdef HAVE_CONFIG_H
00033 #include <config.h>
00034 #endif
00035 
00036 #ifdef HAVE_CUPS_NO_PWD_CACHE
00037 #include <qcstring.h>
00038 static QCString cups_authstring = "";
00039 #endif
00040 
00041 void dumpRequest(ipp_t *req, bool answer = false, const QString& s = QString::null)
00042 {
00043     kdDebug(500) << "==========" << endl;
00044     if (s.isEmpty())
00045         kdDebug(500) << (answer ? "Answer" : "Request") << endl;
00046     else
00047         kdDebug(500) << s << endl;
00048     kdDebug(500) << "==========" << endl;
00049     if (!req)
00050     {
00051         kdDebug(500) << "Null request" << endl;
00052         return;
00053     }
00054     kdDebug(500) << "State = 0x" << QString::number(req->state, 16) << endl;
00055     kdDebug(500) << "ID = 0x" << QString::number(req->request.status.request_id, 16) << endl;
00056     if (answer)
00057     {
00058         kdDebug(500) << "Status = 0x" << QString::number(req->request.status.status_code, 16) << endl;
00059         kdDebug(500) << "Status message = " << ippErrorString(req->request.status.status_code) << endl;
00060     }
00061     else
00062         kdDebug(500) << "Operation = 0x" << QString::number(req->request.op.operation_id, 16) << endl;
00063     kdDebug(500) << "Version = " << (int)(req->request.status.version[0]) << "." << (int)(req->request.status.version[1]) << endl;
00064     kdDebug(500) << endl;
00065 
00066     ipp_attribute_t *attr = req->attrs;
00067     while (attr)
00068     {
00069         QString s = QString::fromLatin1("%1 (0x%2) = ").arg(attr->name).arg(attr->value_tag, 0, 16);
00070         for (int i=0;i<attr->num_values;i++)
00071         {
00072             switch (attr->value_tag)
00073             {
00074                 case IPP_TAG_INTEGER:
00075                 case IPP_TAG_ENUM:
00076                     s += ("0x"+QString::number(attr->values[i].integer, 16));
00077                     break;
00078                 case IPP_TAG_BOOLEAN:
00079                     s += (attr->values[i].boolean ? "true" : "false");
00080                     break;
00081                 case IPP_TAG_STRING:
00082                 case IPP_TAG_TEXT:
00083                 case IPP_TAG_NAME:
00084                 case IPP_TAG_KEYWORD:
00085                 case IPP_TAG_URI:
00086                 case IPP_TAG_MIMETYPE:
00087                 case IPP_TAG_NAMELANG:
00088                 case IPP_TAG_TEXTLANG:
00089                 case IPP_TAG_CHARSET:
00090                 case IPP_TAG_LANGUAGE:
00091                     s += attr->values[i].string.text;
00092                     break;
00093                 default:
00094                     break;
00095             }
00096             if (i != (attr->num_values-1))
00097                 s += ", ";
00098         }
00099         kdDebug(500) << s << endl;
00100         attr = attr->next;
00101     }
00102 }
00103 
00104 QString errorString(int status)
00105 {
00106     QString str;
00107     switch (status)
00108     {
00109         case IPP_FORBIDDEN:
00110             str = i18n("You don't have access to the requested resource.");
00111             break;
00112         case IPP_NOT_AUTHORIZED:
00113             str = i18n("You are not authorized to access the requested resource.");
00114             break;
00115         case IPP_NOT_POSSIBLE:
00116             str = i18n("The requested operation cannot be completed.");
00117             break;
00118         case IPP_SERVICE_UNAVAILABLE:
00119             str = i18n("The requested service is currently unavailable.");
00120             break;
00121         case IPP_NOT_ACCEPTING:
00122             str = i18n("The target printer is not accepting print jobs.");
00123             break;
00124         default:
00125             str = QString::fromLocal8Bit(ippErrorString((ipp_status_t)status));
00126             break;
00127     }
00128     return str;
00129 }
00130 
00131 //*************************************************************************************
00132 
00133 IppRequest::IppRequest()
00134 {
00135     request_ = 0;
00136     port_ = -1;
00137     host_ = QString::null;
00138     dump_ = 0;
00139     init();
00140 }
00141 
00142 IppRequest::~IppRequest()
00143 {
00144     ippDelete(request_);
00145 }
00146 
00147 void IppRequest::init()
00148 {
00149     connect_ = true;
00150 
00151     if (request_)
00152     {
00153         ippDelete(request_);
00154         request_ = 0;
00155     }
00156     request_ = ippNew();
00157     //kdDebug(500) << "kdeprint: IPP request, lang=" << KGlobal::locale()->language() << endl;
00158         QCString langstr = KGlobal::locale()->language().latin1();
00159     cups_lang_t*    lang = cupsLangGet(langstr.data());
00160     // default charset to UTF-8 (ugly hack)
00161     lang->encoding = CUPS_UTF8;
00162     ippAddString(request_, IPP_TAG_OPERATION, IPP_TAG_CHARSET, "attributes-charset", NULL, cupsLangEncoding(lang));
00163     ippAddString(request_, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, "attributes-natural-language", NULL, lang->language);
00164     cupsLangFree(lang);
00165 }
00166 
00167 void IppRequest::addString_p(int group, int type, const QString& name, const QString& value)
00168 {
00169     if (!name.isEmpty())
00170         ippAddString(request_,(ipp_tag_t)group,(ipp_tag_t)type,name.latin1(),NULL,(value.isEmpty() ? "" : value.local8Bit().data()));
00171 }
00172 
00173 void IppRequest::addStringList_p(int group, int type, const QString& name, const QStringList& values)
00174 {
00175     if (!name.isEmpty())
00176     {
00177         ipp_attribute_t *attr = ippAddStrings(request_,(ipp_tag_t)group,(ipp_tag_t)type,name.latin1(),(int)(values.count()),NULL,NULL);
00178         int i(0);
00179         for (QStringList::ConstIterator it=values.begin(); it != values.end(); ++it, i++)
00180             attr->values[i].string.text = strdup((*it).local8Bit());
00181     }
00182 }
00183 
00184 void IppRequest::addInteger_p(int group, int type, const QString& name, int value)
00185 {
00186     if (!name.isEmpty()) ippAddInteger(request_,(ipp_tag_t)group,(ipp_tag_t)type,name.latin1(),value);
00187 }
00188 
00189 void IppRequest::addIntegerList_p(int group, int type, const QString& name, const QValueList<int>& values)
00190 {
00191     if (!name.isEmpty())
00192     {
00193         ipp_attribute_t *attr = ippAddIntegers(request_,(ipp_tag_t)group,(ipp_tag_t)type,name.latin1(),(int)(values.count()),NULL);
00194         int i(0);
00195         for (QValueList<int>::ConstIterator it=values.begin(); it != values.end(); ++it, i++)
00196             attr->values[i].integer = *it;
00197     }
00198 }
00199 
00200 void IppRequest::addBoolean(int group, const QString& name, bool value)
00201 {
00202     if (!name.isEmpty()) ippAddBoolean(request_,(ipp_tag_t)group,name.latin1(),(char)value);
00203 }
00204 
00205 void IppRequest::addBoolean(int group, const QString& name, const QValueList<bool>& values)
00206 {
00207     if (!name.isEmpty())
00208     {
00209         ipp_attribute_t *attr = ippAddBooleans(request_,(ipp_tag_t)group,name.latin1(),(int)(values.count()),NULL);
00210         int i(0);
00211         for (QValueList<bool>::ConstIterator it=values.begin(); it != values.end(); ++it, i++)
00212             attr->values[i].boolean = (char)(*it);
00213     }
00214 }
00215 
00216 void IppRequest::setOperation(int op)
00217 {
00218     request_->request.op.operation_id = (ipp_op_t)op;
00219     request_->request.op.request_id = 1;    // 0 is not RFC-compliant, should be at least 1
00220 }
00221 
00222 int IppRequest::status()
00223 {
00224     return (request_ ? request_->request.status.status_code : (connect_ ? cupsLastError() : -2));
00225 }
00226 
00227 QString IppRequest::statusMessage()
00228 {
00229     QString msg;
00230     switch (status())
00231     {
00232         case -2:
00233             msg = i18n("Connection to CUPS server failed. Check that the CUPS server is correctly installed and running.");
00234             break;
00235         case -1:
00236             msg = i18n("The IPP request failed for an unknown reason.");
00237             break;
00238         default:
00239             msg = errorString(status());
00240             break;
00241     }
00242     return msg;
00243 }
00244 
00245 bool IppRequest::integerValue_p(const QString& name, int& value, int type)
00246 {
00247     if (!request_ || name.isEmpty()) return false;
00248     ipp_attribute_t *attr = ippFindAttribute(request_, name.latin1(), (ipp_tag_t)type);
00249     if (attr)
00250     {
00251         value = attr->values[0].integer;
00252         return true;
00253     }
00254     else return false;
00255 }
00256 
00257 bool IppRequest::stringValue_p(const QString& name, QString& value, int type)
00258 {
00259     if (!request_ || name.isEmpty()) return false;
00260     ipp_attribute_t *attr = ippFindAttribute(request_, name.latin1(), (ipp_tag_t)type);
00261     if (attr)
00262     {
00263         value = QString::fromLocal8Bit(attr->values[0].string.text);
00264         return true;
00265     }
00266     else return false;
00267 }
00268 
00269 bool IppRequest::stringListValue_p(const QString& name, QStringList& values, int type)
00270 {
00271     if (!request_ || name.isEmpty()) return false;
00272     ipp_attribute_t *attr = ippFindAttribute(request_, name.latin1(), (ipp_tag_t)type);
00273     values.clear();
00274     if (attr)
00275     {
00276         for (int i=0;i<attr->num_values;i++)
00277             values.append(QString::fromLocal8Bit(attr->values[i].string.text));
00278         return true;
00279     }
00280     else return false;
00281 }
00282 
00283 bool IppRequest::boolean(const QString& name, bool& value)
00284 {
00285     if (!request_ || name.isEmpty()) return false;
00286     ipp_attribute_t *attr = ippFindAttribute(request_, name.latin1(), IPP_TAG_BOOLEAN);
00287     if (attr)
00288     {
00289         value = (bool)attr->values[0].boolean;
00290         return true;
00291     }
00292     else return false;
00293 }
00294 
00295 bool IppRequest::doFileRequest(const QString& res, const QString& filename)
00296 {
00297     QString myHost = host_;
00298     int     myPort = port_;
00299     if (myHost.isEmpty()) myHost = CupsInfos::self()->host();
00300     if (myPort <= 0) myPort = CupsInfos::self()->port();
00301     http_t  *HTTP = httpConnect(myHost.latin1(),myPort);
00302 
00303     connect_ = (HTTP != NULL);
00304 
00305     if (HTTP == NULL)
00306     {
00307         ippDelete(request_);
00308         request_ = 0;
00309         return false;
00310     }
00311 
00312 #if defined(HAVE_CUPS_NO_PWD_CACHE) && \
00313     CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR < 3
00314     strncpy( HTTP->authstring, cups_authstring.data(), HTTP_MAX_VALUE );
00315 #endif
00316 
00317     if (dump_ > 0)
00318     {
00319         dumpRequest(request_, false, "Request to "+myHost+":"+QString::number(myPort));
00320     }
00321 
00322     request_ = cupsDoFileRequest(HTTP, request_, (res.isEmpty() ? "/" : res.latin1()), (filename.isEmpty() ? NULL : filename.latin1()));
00323 #if defined(HAVE_CUPS_NO_PWD_CACHE) && \
00324     CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR < 3
00325     cups_authstring = HTTP->authstring;
00326 #endif
00327     httpClose(HTTP);
00328 
00329     if (dump_ > 1)
00330     {
00331         dumpRequest(request_, true);
00332     }
00333 
00334     /* No printers found */
00335     if ( request_ && request_->request.status.status_code == 0x406 )
00336         return true;
00337 
00338     if (!request_ || request_->state == IPP_ERROR || (request_->request.status.status_code & 0x0F00))
00339         return false;
00340 
00341 
00342     return true;
00343 }
00344 
00345 bool IppRequest::htmlReport(int group, QTextStream& output)
00346 {
00347     if (!request_) return false;
00348     // start table
00349     output << "<table border=\"1\" cellspacing=\"0\" cellpadding=\"0\">" << endl;
00350     output << "<tr><th bgcolor=\"dark blue\"><font color=\"white\">" << i18n("Attribute") << "</font></th>" << endl;
00351     output << "<th bgcolor=\"dark blue\"><font color=\"white\">" << i18n("Values") << "</font></th></tr>" << endl;
00352     // go to the first attribute of the specified group
00353     ipp_attribute_t *attr = request_->attrs;
00354     while (attr && attr->group_tag != group)
00355         attr = attr->next;
00356     // print each attribute
00357     ipp_uchar_t *d;
00358     QCString    dateStr;
00359     QDateTime   dt;
00360     bool    bg(false);
00361     while (attr && attr->group_tag == group)
00362     {
00363         output << "  <tr bgcolor=\"" << (bg ? "#ffffd9" : "#ffffff") << "\">\n    <td><b>" << attr->name << "</b></td>\n    <td>" << endl;
00364         bg = !bg;
00365         for (int i=0; i<attr->num_values; i++)
00366         {
00367             switch (attr->value_tag)
00368             {
00369                 case IPP_TAG_INTEGER:
00370                     if (attr->name && strstr(attr->name, "time"))
00371                     {
00372                         dt.setTime_t((unsigned int)(attr->values[i].integer));
00373                         output << dt.toString();
00374                     }
00375                     else
00376                         output << attr->values[i].integer;
00377                     break;
00378                 case IPP_TAG_ENUM:
00379                     output << "0x" << hex << attr->values[i].integer << dec;
00380                     break;
00381                 case IPP_TAG_BOOLEAN:
00382                     output << (attr->values[i].boolean ? i18n("True") : i18n("False"));
00383                     break;
00384                 case IPP_TAG_STRING:
00385                 case IPP_TAG_TEXTLANG:
00386                 case IPP_TAG_NAMELANG:
00387                 case IPP_TAG_TEXT:
00388                 case IPP_TAG_NAME:
00389                 case IPP_TAG_KEYWORD:
00390                 case IPP_TAG_URI:
00391                 case IPP_TAG_CHARSET:
00392                 case IPP_TAG_LANGUAGE:
00393                 case IPP_TAG_MIMETYPE:
00394                     output << attr->values[i].string.text;
00395                     break;
00396                 case IPP_TAG_RESOLUTION:
00397                     output << "( " << attr->values[i].resolution.xres
00398                            << ", " << attr->values[i].resolution.yres << " )";
00399                     break;
00400                 case IPP_TAG_RANGE:
00401                     output << "[ " << (attr->values[i].range.lower > 0 ? attr->values[i].range.lower : 1)
00402                            << ", " << (attr->values[i].range.upper > 0 ? attr->values[i].range.upper : 65535) << " ]";
00403                     break;
00404                 case IPP_TAG_DATE:
00405                     d = attr->values[i].date;
00406                     dateStr.sprintf("%.4d-%.2d-%.2d, %.2d:%.2d:%.2d %c%.2d%.2d",
00407                             d[0]*256+d[1], d[2], d[3],
00408                             d[4], d[5], d[6],
00409                             d[8], d[9], d[10]);
00410                     output << dateStr;
00411                     break;
00412                 default:
00413                     continue;
00414             }
00415             if (i < attr->num_values-1)
00416                 output << "<br>";
00417         }
00418         output << "</td>\n  </tr>" << endl;
00419         attr = attr->next;
00420     }
00421     // end table
00422     output << "</table>" << endl;
00423 
00424     return true;
00425 }
00426 
00427 QMap<QString,QString> IppRequest::toMap(int group)
00428 {
00429     QMap<QString,QString>   opts;
00430     if (request_)
00431     {
00432         ipp_attribute_t *attr = first();
00433         while (attr)
00434         {
00435             if (group != -1 && attr->group_tag != group)
00436             {
00437                 attr = attr->next;
00438                 continue;
00439             }
00440             QString value;
00441             for (int i=0; i<attr->num_values; i++)
00442             {
00443                 switch (attr->value_tag)
00444                 {
00445                     case IPP_TAG_INTEGER:
00446                     case IPP_TAG_ENUM:
00447                         value.append(QString::number(attr->values[i].integer)).append(",");
00448                         break;
00449                     case IPP_TAG_BOOLEAN:
00450                         value.append((attr->values[i].boolean ? "true" : "false")).append(",");
00451                         break;
00452                     case IPP_TAG_RANGE:
00453                         if (attr->values[i].range.lower > 0)
00454                             value.append(QString::number(attr->values[i].range.lower));
00455                         if (attr->values[i].range.lower != attr->values[i].range.upper)
00456                         {
00457                             value.append("-");
00458                             if (attr->values[i].range.upper > 0)
00459                                 value.append(QString::number(attr->values[i].range.upper));
00460                         }
00461                         value.append(",");
00462                         break;
00463                     case IPP_TAG_STRING:
00464                     case IPP_TAG_TEXT:
00465                     case IPP_TAG_NAME:
00466                     case IPP_TAG_KEYWORD:
00467                     case IPP_TAG_URI:
00468                     case IPP_TAG_MIMETYPE:
00469                     case IPP_TAG_NAMELANG:
00470                     case IPP_TAG_TEXTLANG:
00471                     case IPP_TAG_CHARSET:
00472                     case IPP_TAG_LANGUAGE:
00473                         value.append(QString::fromLocal8Bit(attr->values[i].string.text)).append(",");
00474                         break;
00475                     default:
00476                         break;
00477                 }
00478             }
00479             if (!value.isEmpty())
00480                 value.truncate(value.length()-1);
00481             opts[QString::fromLocal8Bit(attr->name)] = value;
00482             attr = attr->next;
00483         }
00484     }
00485     return opts;
00486 }
00487 
00488 void IppRequest::setMap(const QMap<QString,QString>& opts)
00489 {
00490     if (!request_)
00491         return;
00492 
00493     QRegExp re("^\"|\"$");
00494     cups_option_t   *options = NULL;
00495     int n = 0;
00496     for (QMap<QString,QString>::ConstIterator it=opts.begin(); it!=opts.end(); ++it)
00497     {
00498         if (it.key().startsWith("kde-") || it.key().startsWith("app-"))
00499             continue;
00500         QString value = it.data().stripWhiteSpace(), lovalue;
00501         value.replace(re, "");
00502         lovalue = value.lower();
00503 
00504         // handles specific cases: boolean, empty strings, or option that has that boolean
00505         // keyword as value (to prevent them from conversion to real boolean)
00506         if (value == "true" || value == "false")
00507             addBoolean(IPP_TAG_JOB, it.key(), (value == "true"));
00508         else if (value.isEmpty() || lovalue == "off" || lovalue == "on"
00509                  || lovalue == "yes" || lovalue == "no"
00510              || lovalue == "true" || lovalue == "false")
00511             addName(IPP_TAG_JOB, it.key(), value);
00512         else
00513             n = cupsAddOption(it.key().local8Bit(), value.local8Bit(), n, &options);
00514     }
00515     if (n > 0)
00516         cupsEncodeOptions(request_, n, options);
00517     cupsFreeOptions(n, options);
00518 
00519     // find an remove that annoying "document-format" attribute
00520 #if CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 2
00521     ipp_attribute_t *attr = ippFindAttribute(request_, "document-format", IPP_TAG_NAME);
00522     ippDeleteAttribute(request_, attr);
00523 #else
00524     // (can't use IppDeleteAttribute as older cups doesn't have that)
00525     ipp_attribute_t *attr = request_->attrs;
00526     while (attr)
00527     {
00528         if (attr->next && strcmp(attr->next->name, "document-format") == 0)
00529         {
00530             ipp_attribute_t *attr2 = attr->next;
00531             attr->next = attr2->next;
00532             _ipp_free_attr(attr2);
00533             break;
00534         }
00535         attr = attr->next;
00536     }
00537 #endif
00538 }
KDE Home | KDE Accessibility Home | Description of Access Keys