/* * This file is part of the KDE libraries * Copyright (c) 2001 Michael Goffioul * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. **/ #include "ipprequest.h" #include "cupsinfos.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_CONFIG_H #include #endif #ifdef HAVE_CUPS_NO_PWD_CACHE #include static TQCString cups_authstring = ""; #endif void dumpRequest(ipp_t *req, bool answer = false, const TQString& s = TQString::null) { kdDebug(500) << "==========" << endl; if (s.isEmpty()) kdDebug(500) << (answer ? "Answer" : "Request") << endl; else kdDebug(500) << s << endl; kdDebug(500) << "==========" << endl; if (!req) { kdDebug(500) << "Null request" << endl; return; } #ifdef HAVE_CUPS_1_6 kdDebug(500) << "State = 0x" << TQString::number(ippGetState(req), 16) << endl; kdDebug(500) << "ID = 0x" << TQString::number(ippGetRequestId(req), 16) << endl; if (answer) { kdDebug(500) << "Status = 0x" << TQString::number(ippGetStatusCode(req), 16) << endl; kdDebug(500) << "Status message = " << ippErrorString(ippGetStatusCode(req)) << endl; } else kdDebug(500) << "Operation = 0x" << TQString::number(ippGetOperation(req), 16) << endl; int minorVersion; int majorVersion = ippGetVersion(req, &minorVersion); kdDebug(500) << "Version = " << (int)(majorVersion) << "." << (int)(minorVersion) << endl; kdDebug(500) << endl; ipp_attribute_t *attr = ippFirstAttribute(req); while (attr) { TQString s = TQString::fromLatin1("%1 (0x%2) = ").arg(ippGetName(attr)).arg(ippGetValueTag(attr), 0, 16); for (int i=0;istate, 16) << endl; kdDebug(500) << "ID = 0x" << TQString::number(req->request.status.request_id, 16) << endl; if (answer) { kdDebug(500) << "Status = 0x" << TQString::number(req->request.status.status_code, 16) << endl; kdDebug(500) << "Status message = " << ippErrorString(req->request.status.status_code) << endl; } else kdDebug(500) << "Operation = 0x" << TQString::number(req->request.op.operation_id, 16) << endl; kdDebug(500) << "Version = " << (int)(req->request.status.version[0]) << "." << (int)(req->request.status.version[1]) << endl; kdDebug(500) << endl; ipp_attribute_t *attr = req->attrs; while (attr) { TQString s = TQString::fromLatin1("%1 (0x%2) = ").arg(attr->name).arg(attr->value_tag, 0, 16); for (int i=0;inum_values;i++) { switch (attr->value_tag) { case IPP_TAG_INTEGER: case IPP_TAG_ENUM: s += ("0x"+TQString::number(attr->values[i].integer, 16)); break; case IPP_TAG_BOOLEAN: s += (attr->values[i].boolean ? "true" : "false"); break; case IPP_TAG_STRING: case IPP_TAG_TEXT: case IPP_TAG_NAME: case IPP_TAG_KEYWORD: case IPP_TAG_URI: case IPP_TAG_MIMETYPE: case IPP_TAG_NAMELANG: case IPP_TAG_TEXTLANG: case IPP_TAG_CHARSET: case IPP_TAG_LANGUAGE: s += attr->values[i].string.text; break; default: break; } if (i != (attr->num_values-1)) s += ", "; } kdDebug(500) << s << endl; attr = attr->next; } #endif // HAVE_CUPS_1_6 } TQString errorString(int status) { TQString str; switch (status) { case IPP_FORBIDDEN: str = i18n("You don't have access to the requested resource."); break; case IPP_NOT_AUTHORIZED: str = i18n("You are not authorized to access the requested resource."); break; case IPP_NOT_POSSIBLE: str = i18n("The requested operation cannot be completed."); break; case IPP_SERVICE_UNAVAILABLE: str = i18n("The requested service is currently unavailable."); break; case IPP_NOT_ACCEPTING: str = i18n("The target printer is not accepting print jobs."); break; default: str = TQString::fromLocal8Bit(ippErrorString((ipp_status_t)status)); break; } return str; } //************************************************************************************* IppRequest::IppRequest() { request_ = 0; port_ = -1; host_ = TQString(); dump_ = 0; init(); } IppRequest::~IppRequest() { ippDelete(request_); } void IppRequest::init() { connect_ = true; if (request_) { ippDelete(request_); request_ = 0; } request_ = ippNew(); //kdDebug(500) << "kdeprint: IPP request, lang=" << KGlobal::locale()->language() << endl; TQCString langstr = KGlobal::locale()->language().latin1(); cups_lang_t* lang = cupsLangGet(langstr.data()); // default charset to UTF-8 (ugly hack) lang->encoding = CUPS_UTF8; ippAddString(request_, IPP_TAG_OPERATION, IPP_TAG_CHARSET, "attributes-charset", NULL, cupsLangEncoding(lang)); ippAddString(request_, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, "attributes-natural-language", NULL, lang->language); cupsLangFree(lang); } void IppRequest::addString_p(int group, int type, const TQString& name, const TQString& value) { if (!name.isEmpty()) ippAddString(request_,(ipp_tag_t)group,(ipp_tag_t)type,name.latin1(),NULL,(value.isEmpty() ? "" : value.local8Bit().data())); } void IppRequest::addStringList_p(int group, int type, const TQString& name, const TQStringList& values) { if (!name.isEmpty()) { //> Values buffer and references offset prepare const char *vlsRefs[values.count()]; std::string vlsBuf; for(unsigned i_vl = 0; i_vl < values.count(); i_vl++) { vlsRefs[i_vl] = (const char*)vlsBuf.size(); vlsBuf += values[i_vl].local8Bit(); vlsBuf += (char)0; } //> References update to pointers for(unsigned i_vl = 0; i_vl < values.count(); i_vl++) vlsRefs[i_vl] = vlsBuf.data()+(intptr_t)vlsRefs[i_vl]; ippAddStrings(request_,(ipp_tag_t)group,(ipp_tag_t)type,name.latin1(),(int)(values.count()),NULL,(const char**)&vlsRefs); } } void IppRequest::addInteger_p(int group, int type, const TQString& name, int value) { if (!name.isEmpty()) ippAddInteger(request_,(ipp_tag_t)group,(ipp_tag_t)type,name.latin1(),value); } void IppRequest::addIntegerList_p(int group, int type, const TQString& name, const TQValueList& values) { if (!name.isEmpty()) { ipp_attribute_t *attr = ippAddIntegers(request_,(ipp_tag_t)group,(ipp_tag_t)type,name.latin1(),(int)(values.count()),NULL); int i(0); for (TQValueList::ConstIterator it=values.begin(); it != values.end(); ++it, i++) #ifdef HAVE_CUPS_1_6 ippSetInteger(request_, &attr, i, *it); #else // HAVE_CUPS_1_6 attr->values[i].integer = *it; #endif // HAVE_CUPS_1_6 } } void IppRequest::addBoolean(int group, const TQString& name, bool value) { if (!name.isEmpty()) ippAddBoolean(request_,(ipp_tag_t)group,name.latin1(),(char)value); } void IppRequest::addBoolean(int group, const TQString& name, const TQValueList& values) { if (!name.isEmpty()) { ipp_attribute_t *attr = ippAddBooleans(request_,(ipp_tag_t)group,name.latin1(),(int)(values.count()),NULL); int i(0); for (TQValueList::ConstIterator it=values.begin(); it != values.end(); ++it, i++) #ifdef HAVE_CUPS_1_6 ippSetBoolean(request_, &attr, i, (char)(*it)); #else // HAVE_CUPS_1_6 attr->values[i].boolean = (char)(*it); #endif // HAVE_CUPS_1_6 } } void IppRequest::setOperation(int op) { #ifdef HAVE_CUPS_1_6 ippSetOperation(request_, (ipp_op_t)op); ippSetRequestId(request_, 1); // 0 is not RFC-compliant, should be at least 1 #else // HAVE_CUPS_1_6 request_->request.op.operation_id = (ipp_op_t)op; request_->request.op.request_id = 1; // 0 is not RFC-compliant, should be at least 1 #endif // HAVE_CUPS_1_6 } int IppRequest::status() { #ifdef HAVE_CUPS_1_6 return (request_ ? ippGetStatusCode(request_) : (connect_ ? cupsLastError() : -2)); #else // HAVE_CUPS_1_6 return (request_ ? request_->request.status.status_code : (connect_ ? cupsLastError() : -2)); #endif // HAVE_CUPS_1_6 } TQString IppRequest::statusMessage() { TQString msg; switch (status()) { case -2: msg = i18n("Connection to CUPS server failed. Check that the CUPS server is correctly installed and running."); break; case -1: msg = i18n("The IPP request failed for an unknown reason."); break; default: msg = errorString(status()); break; } return msg; } bool IppRequest::integerValue_p(const TQString& name, int& value, int type) { if (!request_ || name.isEmpty()) return false; ipp_attribute_t *attr = ippFindAttribute(request_, name.latin1(), (ipp_tag_t)type); if (attr) { #ifdef HAVE_CUPS_1_6 value = ippGetInteger(attr, 0); #else // HAVE_CUPS_1_6 value = attr->values[0].integer; #endif // HAVE_CUPS_1_6 return true; } else return false; } bool IppRequest::stringValue_p(const TQString& name, TQString& value, int type) { if (!request_ || name.isEmpty()) return false; ipp_attribute_t *attr = ippFindAttribute(request_, name.latin1(), (ipp_tag_t)type); if (attr) { #ifdef HAVE_CUPS_1_6 value = TQString::fromLocal8Bit(ippGetString(attr, 0, NULL)); #else // HAVE_CUPS_1_6 value = TQString::fromLocal8Bit(attr->values[0].string.text); #endif // HAVE_CUPS_1_6 return true; } else return false; } bool IppRequest::stringListValue_p(const TQString& name, TQStringList& values, int type) { if (!request_ || name.isEmpty()) return false; ipp_attribute_t *attr = ippFindAttribute(request_, name.latin1(), (ipp_tag_t)type); values.clear(); if (attr) { #ifdef HAVE_CUPS_1_6 for (int i=0;inum_values;i++) values.append(TQString::fromLocal8Bit(attr->values[i].string.text)); #endif // HAVE_CUPS_1_6 return true; } else return false; } bool IppRequest::boolean(const TQString& name, bool& value) { if (!request_ || name.isEmpty()) return false; ipp_attribute_t *attr = ippFindAttribute(request_, name.latin1(), IPP_TAG_BOOLEAN); if (attr) { #ifdef HAVE_CUPS_1_6 value = (bool)ippGetBoolean(attr, 0); #else // HAVE_CUPS_1_6 value = (bool)attr->values[0].boolean; #endif // HAVE_CUPS_1_6 return true; } else return false; } bool IppRequest::doFileRequest(const TQString& res, const TQString& filename) { TQString myHost = host_; int myPort = port_; if (myHost.isEmpty()) myHost = CupsInfos::self()->host(); if (myPort <= 0) myPort = CupsInfos::self()->port(); http_t *HTTP = httpConnect(myHost.latin1(),myPort); connect_ = (HTTP != NULL); if (HTTP == NULL) { ippDelete(request_); request_ = 0; return false; } #ifdef HAVE_CUPS_NO_PWD_CACHE #if CUPS_VERSION_MAJOR < 1 || (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR < 2) strncpy( HTTP->authstring, cups_authstring.data(), HTTP_MAX_VALUE ); #else httpSetAuthString( HTTP, NULL, cups_authstring.data() ); #endif #endif if (dump_ > 0) { dumpRequest(request_, false, "Request to "+myHost+":"+TQString::number(myPort)); } request_ = cupsDoFileRequest(HTTP, request_, (res.isEmpty() ? "/" : res.latin1()), (filename.isEmpty() ? NULL : filename.latin1())); #ifdef HAVE_CUPS_NO_PWD_CACHE #if CUPS_VERSION_MAJOR < 1 || (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR < 2) cups_authstring = HTTP->authstring; #else cups_authstring = httpGetAuthString( HTTP ); #endif #endif httpClose(HTTP); if (dump_ > 1) { dumpRequest(request_, true); } /* No printers found */ #ifdef HAVE_CUPS_1_6 if ( request_ && ippGetStatusCode(request_) == 0x406 ) #else // HAVE_CUPS_1_6 if ( request_ && request_->request.status.status_code == 0x406 ) #endif // HAVE_CUPS_1_6 return true; #ifdef HAVE_CUPS_1_6 if (!request_ || ippGetState(request_) == IPP_ERROR || (ippGetStatusCode(request_) & 0x0F00)) #else // HAVE_CUPS_1_6 if (!request_ || request_->state == IPP_ERROR || (request_->request.status.status_code & 0x0F00)) #endif // HAVE_CUPS_1_6 return false; return true; } bool IppRequest::htmlReport(int group, TQTextStream& output) { if (!request_) return false; // start table output << "" << endl; output << "" << endl; output << "" << endl; // go to the first attribute of the specified group #ifdef HAVE_CUPS_1_6 ipp_attribute_t *attr = ippFirstAttribute(request_); while (attr && ippGetGroupTag(attr) != group) attr = ippNextAttribute(request_); #else // HAVE_CUPS_1_6 ipp_attribute_t *attr = request_->attrs; while (attr && attr->group_tag != group) attr = attr->next; #endif // HAVE_CUPS_1_6 // print each attribute const ipp_uchar_t *d; TQCString dateStr; TQDateTime dt; bool bg(false); #ifdef HAVE_CUPS_1_6 while (attr && ippGetGroupTag(attr) == group) { output << " \n \n \n " << endl; attr = ippNextAttribute(request_); #else // HAVE_CUPS_1_6 while (attr && attr->group_tag == group) { output << " \n \n \n " << endl; attr = attr->next; #endif // HAVE_CUPS_1_6 } // end table output << "
" << i18n("Attribute") << "" << i18n("Values") << "
" << ippGetName(attr) << "" << endl; bg = !bg; for (int i=0; i 0 ? lowervalue : 1) << ", " << (uppervalue > 0 ? uppervalue : 65535) << " ]"; break; case IPP_TAG_DATE: d = ippGetDate(attr, i); dateStr.sprintf("%.4d-%.2d-%.2d, %.2d:%.2d:%.2d %c%.2d%.2d", d[0]*256+d[1], d[2], d[3], d[4], d[5], d[6], d[8], d[9], d[10]); output << dateStr; break; default: continue; } if (i < ippGetCount(attr)-1) output << "
"; } output << "
" << attr->name << "" << endl; bg = !bg; for (int i=0; inum_values; i++) { switch (attr->value_tag) { case IPP_TAG_INTEGER: if (attr->name && strstr(attr->name, "time")) { dt.setTime_t((unsigned int)(attr->values[i].integer)); output << dt.toString(); } else output << attr->values[i].integer; break; case IPP_TAG_ENUM: output << "0x" << hex << attr->values[i].integer << dec; break; case IPP_TAG_BOOLEAN: output << (attr->values[i].boolean ? i18n("True") : i18n("False")); break; case IPP_TAG_STRING: case IPP_TAG_TEXTLANG: case IPP_TAG_NAMELANG: case IPP_TAG_TEXT: case IPP_TAG_NAME: case IPP_TAG_KEYWORD: case IPP_TAG_URI: case IPP_TAG_CHARSET: case IPP_TAG_LANGUAGE: case IPP_TAG_MIMETYPE: output << attr->values[i].string.text; break; case IPP_TAG_RESOLUTION: output << "( " << attr->values[i].resolution.xres << ", " << attr->values[i].resolution.yres << " )"; break; case IPP_TAG_RANGE: output << "[ " << (attr->values[i].range.lower > 0 ? attr->values[i].range.lower : 1) << ", " << (attr->values[i].range.upper > 0 ? attr->values[i].range.upper : 65535) << " ]"; break; case IPP_TAG_DATE: d = attr->values[i].date; dateStr.sprintf("%.4d-%.2d-%.2d, %.2d:%.2d:%.2d %c%.2d%.2d", d[0]*256+d[1], d[2], d[3], d[4], d[5], d[6], d[8], d[9], d[10]); output << dateStr; break; default: continue; } if (i < attr->num_values-1) output << "
"; } output << "
" << endl; return true; } TQMap IppRequest::toMap(int group) { TQMap opts; if (request_) { ipp_attribute_t *attr = first(); while (attr) { #ifdef HAVE_CUPS_1_6 if (group != -1 && ippGetGroupTag(attr) != group) { attr = ippNextAttribute(request_); continue; } TQString value; for (int i=0; i 0) value.append(TQString::number(lowervalue)); if (lowervalue != uppervalue) { value.append("-"); if (uppervalue > 0) value.append(TQString::number(uppervalue)); } value.append(","); break; case IPP_TAG_STRING: case IPP_TAG_TEXT: case IPP_TAG_NAME: case IPP_TAG_KEYWORD: case IPP_TAG_URI: case IPP_TAG_MIMETYPE: case IPP_TAG_NAMELANG: case IPP_TAG_TEXTLANG: case IPP_TAG_CHARSET: case IPP_TAG_LANGUAGE: value.append(TQString::fromLocal8Bit(ippGetString(attr, i, NULL))).append(","); break; default: break; } } if (!value.isEmpty()) value.truncate(value.length()-1); opts[TQString::fromLocal8Bit(ippGetName(attr))] = value; attr = ippNextAttribute(request_); #else // HAVE_CUPS_1_6 if (group != -1 && attr->group_tag != group) { attr = attr->next; continue; } TQString value; for (int i=0; inum_values; i++) { switch (attr->value_tag) { case IPP_TAG_INTEGER: case IPP_TAG_ENUM: value.append(TQString::number(attr->values[i].integer)).append(","); break; case IPP_TAG_BOOLEAN: value.append((attr->values[i].boolean ? "true" : "false")).append(","); break; case IPP_TAG_RANGE: if (attr->values[i].range.lower > 0) value.append(TQString::number(attr->values[i].range.lower)); if (attr->values[i].range.lower != attr->values[i].range.upper) { value.append("-"); if (attr->values[i].range.upper > 0) value.append(TQString::number(attr->values[i].range.upper)); } value.append(","); break; case IPP_TAG_STRING: case IPP_TAG_TEXT: case IPP_TAG_NAME: case IPP_TAG_KEYWORD: case IPP_TAG_URI: case IPP_TAG_MIMETYPE: case IPP_TAG_NAMELANG: case IPP_TAG_TEXTLANG: case IPP_TAG_CHARSET: case IPP_TAG_LANGUAGE: value.append(TQString::fromLocal8Bit(attr->values[i].string.text)).append(","); break; default: break; } } if (!value.isEmpty()) value.truncate(value.length()-1); opts[TQString::fromLocal8Bit(attr->name)] = value; attr = attr->next; #endif // HAVE_CUPS_1_6 } } return opts; } void IppRequest::setMap(const TQMap& opts) { if (!request_) return; TQRegExp re("^\"|\"$"); cups_option_t *options = NULL; int n = 0; for (TQMap::ConstIterator it=opts.begin(); it!=opts.end(); ++it) { if (it.key().startsWith("kde-") || it.key().startsWith("app-")) continue; TQString value = it.data().stripWhiteSpace(), lovalue; value.replace(re, ""); lovalue = value.lower(); // handles specific cases: boolean, empty strings, or option that has that boolean // keyword as value (to prevent them from conversion to real boolean) if (value == "true" || value == "false") addBoolean(IPP_TAG_JOB, it.key(), (value == "true")); else if (value.isEmpty() || lovalue == "off" || lovalue == "on" || lovalue == "yes" || lovalue == "no" || lovalue == "true" || lovalue == "false") addName(IPP_TAG_JOB, it.key(), value); else n = cupsAddOption(it.key().local8Bit(), value.local8Bit(), n, &options); } if (n > 0) cupsEncodeOptions(request_, n, options); cupsFreeOptions(n, options); // find an remove that annoying "document-format" attribute #if CUPS_VERSION_MAJOR > 1 || (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 2) ipp_attribute_t *attr = ippFindAttribute(request_, "document-format", IPP_TAG_NAME); ippDeleteAttribute(request_, attr); #else // (can't use IppDeleteAttribute as older cups doesn't have that) ipp_attribute_t *attr = request_->attrs; while (attr) { if (attr->next && strcmp(attr->next->name, "document-format") == 0) { ipp_attribute_t *attr2 = attr->next; attr->next = attr2->next; _ipp_free_attr(attr2); break; } attr = attr->next; } #endif } #ifdef HAVE_CUPS_1_6 ipp_attribute_t* IppRequest::first() { return (request_ ? ippFirstAttribute(request_) : NULL); } #else // HAVE_CUPS_1_6 ipp_attribute_t* IppRequest::first() { return (request_ ? request_->attrs : NULL); } #endif // HAVE_CUPS_1_6