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 #ifdef HAVE_CUPS_NO_PWD_CACHE
00313     strncpy( HTTP->authstring, cups_authstring.data(), HTTP_MAX_VALUE );
00314 #endif
00315 
00316     if (dump_ > 0)
00317     {
00318         dumpRequest(request_, false, "Request to "+myHost+":"+QString::number(myPort));
00319     }
00320 
00321     request_ = cupsDoFileRequest(HTTP, request_, (res.isEmpty() ? "/" : res.latin1()), (filename.isEmpty() ? NULL : filename.latin1()));
00322 #ifdef HAVE_CUPS_NO_PWD_CACHE
00323     cups_authstring = HTTP->authstring;
00324 #endif
00325     httpClose(HTTP);
00326 
00327     if (dump_ > 1)
00328     {
00329         dumpRequest(request_, true);
00330     }
00331 
00332     /* No printers found */
00333     if ( request_ && request_->request.status.status_code == 0x406 )
00334         return true;
00335 
00336     if (!request_ || request_->state == IPP_ERROR || (request_->request.status.status_code & 0x0F00))
00337         return false;
00338 
00339 
00340     return true;
00341 }
00342 
00343 bool IppRequest::htmlReport(int group, QTextStream& output)
00344 {
00345     if (!request_) return false;
00346     // start table
00347     output << "<table border=\"1\" cellspacing=\"0\" cellpadding=\"0\">" << endl;
00348     output << "<tr><th bgcolor=\"dark blue\"><font color=\"white\">" << i18n("Attribute") << "</font></th>" << endl;
00349     output << "<th bgcolor=\"dark blue\"><font color=\"white\">" << i18n("Values") << "</font></th></tr>" << endl;
00350     // go to the first attribute of the specified group
00351     ipp_attribute_t *attr = request_->attrs;
00352     while (attr && attr->group_tag != group)
00353         attr = attr->next;
00354     // print each attribute
00355     ipp_uchar_t *d;
00356     QCString    dateStr;
00357     QDateTime   dt;
00358     bool    bg(false);
00359     while (attr && attr->group_tag == group)
00360     {
00361         output << "  <tr bgcolor=\"" << (bg ? "#ffffd9" : "#ffffff") << "\">\n    <td><b>" << attr->name << "</b></td>\n    <td>" << endl;
00362         bg = !bg;
00363         for (int i=0; i<attr->num_values; i++)
00364         {
00365             switch (attr->value_tag)
00366             {
00367                 case IPP_TAG_INTEGER:
00368                     if (attr->name && strstr(attr->name, "time"))
00369                     {
00370                         dt.setTime_t((unsigned int)(attr->values[i].integer));
00371                         output << dt.toString();
00372                     }
00373                     else
00374                         output << attr->values[i].integer;
00375                     break;
00376                 case IPP_TAG_ENUM:
00377                     output << "0x" << hex << attr->values[i].integer << dec;
00378                     break;
00379                 case IPP_TAG_BOOLEAN:
00380                     output << (attr->values[i].boolean ? i18n("True") : i18n("False"));
00381                     break;
00382                 case IPP_TAG_STRING:
00383                 case IPP_TAG_TEXTLANG:
00384                 case IPP_TAG_NAMELANG:
00385                 case IPP_TAG_TEXT:
00386                 case IPP_TAG_NAME:
00387                 case IPP_TAG_KEYWORD:
00388                 case IPP_TAG_URI:
00389                 case IPP_TAG_CHARSET:
00390                 case IPP_TAG_LANGUAGE:
00391                 case IPP_TAG_MIMETYPE:
00392                     output << attr->values[i].string.text;
00393                     break;
00394                 case IPP_TAG_RESOLUTION:
00395                     output << "( " << attr->values[i].resolution.xres
00396                            << ", " << attr->values[i].resolution.yres << " )";
00397                     break;
00398                 case IPP_TAG_RANGE:
00399                     output << "[ " << (attr->values[i].range.lower > 0 ? attr->values[i].range.lower : 1)
00400                            << ", " << (attr->values[i].range.upper > 0 ? attr->values[i].range.upper : 65535) << " ]";
00401                     break;
00402                 case IPP_TAG_DATE:
00403                     d = attr->values[i].date;
00404                     dateStr.sprintf("%.4d-%.2d-%.2d, %.2d:%.2d:%.2d %c%.2d%.2d",
00405                             d[0]*256+d[1], d[2], d[3],
00406                             d[4], d[5], d[6],
00407                             d[8], d[9], d[10]);
00408                     output << dateStr;
00409                     break;
00410                 default:
00411                     continue;
00412             }
00413             if (i < attr->num_values-1)
00414                 output << "<br>";
00415         }
00416         output << "</td>\n  </tr>" << endl;
00417         attr = attr->next;
00418     }
00419     // end table
00420     output << "</table>" << endl;
00421 
00422     return true;
00423 }
00424 
00425 QMap<QString,QString> IppRequest::toMap(int group)
00426 {
00427     QMap<QString,QString>   opts;
00428     if (request_)
00429     {
00430         ipp_attribute_t *attr = first();
00431         while (attr)
00432         {
00433             if (group != -1 && attr->group_tag != group)
00434             {
00435                 attr = attr->next;
00436                 continue;
00437             }
00438             QString value;
00439             for (int i=0; i<attr->num_values; i++)
00440             {
00441                 switch (attr->value_tag)
00442                 {
00443                     case IPP_TAG_INTEGER:
00444                     case IPP_TAG_ENUM:
00445                         value.append(QString::number(attr->values[i].integer)).append(",");
00446                         break;
00447                     case IPP_TAG_BOOLEAN:
00448                         value.append((attr->values[i].boolean ? "true" : "false")).append(",");
00449                         break;
00450                     case IPP_TAG_RANGE:
00451                         if (attr->values[i].range.lower > 0)
00452                             value.append(QString::number(attr->values[i].range.lower));
00453                         if (attr->values[i].range.lower != attr->values[i].range.upper)
00454                         {
00455                             value.append("-");
00456                             if (attr->values[i].range.upper > 0)
00457                                 value.append(QString::number(attr->values[i].range.upper));
00458                         }
00459                         value.append(",");
00460                         break;
00461                     case IPP_TAG_STRING:
00462                     case IPP_TAG_TEXT:
00463                     case IPP_TAG_NAME:
00464                     case IPP_TAG_KEYWORD:
00465                     case IPP_TAG_URI:
00466                     case IPP_TAG_MIMETYPE:
00467                     case IPP_TAG_NAMELANG:
00468                     case IPP_TAG_TEXTLANG:
00469                     case IPP_TAG_CHARSET:
00470                     case IPP_TAG_LANGUAGE:
00471                         value.append(QString::fromLocal8Bit(attr->values[i].string.text)).append(",");
00472                         break;
00473                     default:
00474                         break;
00475                 }
00476             }
00477             if (!value.isEmpty())
00478                 value.truncate(value.length()-1);
00479             opts[QString::fromLocal8Bit(attr->name)] = value;
00480             attr = attr->next;
00481         }
00482     }
00483     return opts;
00484 }
00485 
00486 void IppRequest::setMap(const QMap<QString,QString>& opts)
00487 {
00488     if (!request_)
00489         return;
00490 
00491     QRegExp re("^\"|\"$");
00492     cups_option_t   *options = NULL;
00493     int n = 0;
00494     for (QMap<QString,QString>::ConstIterator it=opts.begin(); it!=opts.end(); ++it)
00495     {
00496         if (it.key().startsWith("kde-") || it.key().startsWith("app-"))
00497             continue;
00498         QString value = it.data().stripWhiteSpace(), lovalue;
00499         value.replace(re, "");
00500         lovalue = value.lower();
00501 
00502         // handles specific cases: boolean, empty strings, or option that has that boolean
00503         // keyword as value (to prevent them from conversion to real boolean)
00504         if (value == "true" || value == "false")
00505             addBoolean(IPP_TAG_JOB, it.key(), (value == "true"));
00506         else if (value.isEmpty() || lovalue == "off" || lovalue == "on"
00507                  || lovalue == "yes" || lovalue == "no"
00508              || lovalue == "true" || lovalue == "false")
00509             addName(IPP_TAG_JOB, it.key(), value);
00510         else
00511             n = cupsAddOption(it.key().local8Bit(), value.local8Bit(), n, &options);
00512     }
00513     if (n > 0)
00514         cupsEncodeOptions(request_, n, options);
00515     cupsFreeOptions(n, options);
00516 
00517     // find an remove that annoying "document-format" attribute
00518 #if CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 2
00519     ipp_attribute_t *attr = ippFindAttribute(request_, "document-format", IPP_TAG_NAME);
00520     ippDeleteAttribute(request_, attr);
00521 #else
00522     // (can't use IppDeleteAttribute as older cups doesn't have that)
00523     ipp_attribute_t *attr = request_->attrs;
00524     while (attr)
00525     {
00526         if (attr->next && strcmp(attr->next->name, "document-format") == 0)
00527         {
00528             ipp_attribute_t *attr2 = attr->next;
00529             attr->next = attr2->next;
00530             _ipp_free_attr(attr2);
00531             break;
00532         }
00533         attr = attr->next;
00534     }
00535 #endif
00536 }
KDE Home | KDE Accessibility Home | Description of Access Keys