KIO
dataprotocol.cpp
Go to the documentation of this file.
00001 // dataprotocol.cpp 00002 // ================== 00003 // 00004 // Implementation of the data protocol (rfc 2397) 00005 // 00006 // Author: Leo Savernik 00007 // Email: l.savernik@aon.at 00008 // Copyright (C) 2002, 2003 by Leo Savernik <l.savernik@aon.at> 00009 // Created: Sam Dez 28 14:11:18 CET 2002 00010 00011 /*************************************************************************** 00012 * * 00013 * This program is free software; you can redistribute it and/or modify * 00014 * it under the terms of the GNU Lesser General Public License as * 00015 * published by the Free Software Foundation; version 2. * 00016 * * 00017 ***************************************************************************/ 00018 00019 #include "dataprotocol.h" 00020 00021 #include <kdebug.h> 00022 #include <kurl.h> 00023 #include "global.h" 00024 #include <kglobal.h> 00025 00026 #include <QtCore/QByteArray> 00027 #include <QtCore/QCharRef> 00028 #include <QtCore/QMutableStringListIterator> 00029 #include <QtCore/QTextCodec> 00030 00031 #ifdef DATAKIOSLAVE 00032 # include <kinstance.h> 00033 # include <stdlib.h> 00034 #endif 00035 00036 #if !defined(DATAKIOSLAVE) && !defined(TESTKIO) 00037 # define DISPATCH(f) dispatch_##f 00038 #else 00039 # define DISPATCH(f) f 00040 #endif 00041 00042 using namespace KIO; 00043 #ifdef DATAKIOSLAVE 00044 extern "C" { 00045 00046 int kdemain( int argc, char **argv ) { 00047 KComponentData componentData( "kio_data" ); 00048 00049 kDebug(7101) << "*** Starting kio_data "; 00050 00051 if (argc != 4) { 00052 kDebug(7101) << "Usage: kio_data protocol domain-socket1 domain-socket2"; 00053 exit(-1); 00054 } 00055 00056 DataProtocol slave(argv[2], argv[3]); 00057 slave.dispatchLoop(); 00058 00059 kDebug(7101) << "*** kio_data Done"; 00060 return 0; 00061 } 00062 } 00063 #endif 00064 00066 struct DataHeader { 00067 QString mime_type; // mime type of content (lowercase) 00068 MetaData attributes; // attribute/value pairs (attribute lowercase, 00069 // value unchanged) 00070 bool is_base64; // true if data is base64 encoded 00071 QString url; // reference to decoded url 00072 int data_offset; // zero-indexed position within url 00073 // where the real data begins. May point beyond 00074 // the end to indicate that there is no data 00075 QString charset; // shortcut to charset (it always exists) 00076 }; 00077 00087 static int find(const QString &buf, int begin, QChar c1, 00088 QChar c2 = QLatin1Char('\0'), QChar c3 = QLatin1Char('\0')) { 00089 int pos = begin; 00090 int size = buf.length(); 00091 while (pos < size) { 00092 QChar ch = buf[pos]; 00093 if (ch == c1 00094 || (c2 != QLatin1Char('\0') && ch == c2) 00095 || (c3 != QLatin1Char('\0') && ch == c3)) 00096 break; 00097 pos++; 00098 }/*wend*/ 00099 return pos; 00100 } 00101 00112 inline QString extract(const QString &buf, int &pos, QChar c1, 00113 QChar c2 = QLatin1Char('\0'), QChar c3 = QLatin1Char('\0')) { 00114 int oldpos = pos; 00115 pos = find(buf,oldpos,c1,c2,c3); 00116 return buf.mid(oldpos, pos-oldpos); 00117 } 00118 00125 inline void ignoreWS(const QString &buf, int &pos) { 00126 int size = buf.length(); 00127 QChar ch = buf[pos]; 00128 while (pos < size && ch.isSpace()) 00129 ch = buf[++pos]; 00130 } 00131 00140 static QString parseQuotedString(const QString &buf, int &pos) { 00141 int size = buf.length(); 00142 QString res; 00143 res.reserve(size); // can't be larger than buf 00144 pos++; // jump over leading quote 00145 bool escaped = false; // if true means next character is literal 00146 bool parsing = true; // true as long as end quote not found 00147 while (parsing && pos < size) { 00148 QChar ch = buf[pos++]; 00149 if (escaped) { 00150 res += ch; 00151 escaped = false; 00152 } else { 00153 switch (ch.unicode()) { 00154 case '"': parsing = false; break; 00155 case '\\': escaped = true; break; 00156 default: res += ch; break; 00157 }/*end switch*/ 00158 }/*end if*/ 00159 }/*wend*/ 00160 res.squeeze(); 00161 return res; 00162 } 00163 00169 static void parseDataHeader(const KUrl &url, DataHeader &header_info) { 00170 static const QString& text_plain = KGlobal::staticQString("text/plain"); 00171 static const QString& charset = KGlobal::staticQString("charset"); 00172 static const QString& us_ascii = KGlobal::staticQString("us-ascii"); 00173 static const QString& base64 = KGlobal::staticQString("base64"); 00174 00175 // initialize header info members 00176 header_info.mime_type = text_plain; 00177 header_info.charset = header_info.attributes.insert(charset,us_ascii).value(); 00178 header_info.is_base64 = false; 00179 00180 // decode url and save it 00181 QString &raw_url = header_info.url = QUrl::fromPercentEncoding( url.url().toLatin1() ); 00182 int raw_url_len = raw_url.length(); 00183 00184 // jump over scheme part (must be "data:", we don't even check that) 00185 header_info.data_offset = raw_url.indexOf(QLatin1Char(':')); 00186 header_info.data_offset++; // jump over colon or to begin if scheme was missing 00187 00188 // read mime type 00189 if (header_info.data_offset >= raw_url_len) return; 00190 QString mime_type = extract(raw_url, header_info.data_offset, 00191 QLatin1Char(';'), QLatin1Char(',')).trimmed(); 00192 if (!mime_type.isEmpty()) header_info.mime_type = mime_type; 00193 00194 if (header_info.data_offset >= raw_url_len) return; 00195 // jump over delimiter token and return if data reached 00196 if (raw_url[header_info.data_offset++] == QLatin1Char(',')) return; 00197 00198 // read all attributes and store them 00199 bool data_begin_reached = false; 00200 while (!data_begin_reached && header_info.data_offset < raw_url_len) { 00201 // read attribute 00202 QString attribute = extract(raw_url, header_info.data_offset, 00203 QLatin1Char('='), QLatin1Char(';'), 00204 QLatin1Char(',')).trimmed(); 00205 if (header_info.data_offset >= raw_url_len 00206 || raw_url[header_info.data_offset] != QLatin1Char('=')) { 00207 // no assigment, must be base64 option 00208 if (attribute == base64) 00209 header_info.is_base64 = true; 00210 } else { 00211 header_info.data_offset++; // jump over '=' token 00212 00213 // read value 00214 ignoreWS(raw_url,header_info.data_offset); 00215 if (header_info.data_offset >= raw_url_len) return; 00216 00217 QString value; 00218 if (raw_url[header_info.data_offset] == QLatin1Char('"')) { 00219 value = parseQuotedString(raw_url,header_info.data_offset); 00220 ignoreWS(raw_url,header_info.data_offset); 00221 } else 00222 value = extract(raw_url, header_info.data_offset, QLatin1Char(';'), 00223 QLatin1Char(',')).trimmed(); 00224 00225 // add attribute to map 00226 header_info.attributes[attribute.toLower()] = value; 00227 00228 }/*end if*/ 00229 if (header_info.data_offset < raw_url_len 00230 && raw_url[header_info.data_offset] == QLatin1Char(',')) 00231 data_begin_reached = true; 00232 header_info.data_offset++; // jump over separator token 00233 }/*wend*/ 00234 } 00235 00236 #ifdef DATAKIOSLAVE 00237 DataProtocol::DataProtocol(const QByteArray &pool_socket, const QByteArray &app_socket) 00238 : SlaveBase("kio_data", pool_socket, app_socket) { 00239 #else 00240 DataProtocol::DataProtocol() { 00241 #endif 00242 kDebug(); 00243 } 00244 00245 /* --------------------------------------------------------------------- */ 00246 00247 DataProtocol::~DataProtocol() { 00248 kDebug(); 00249 } 00250 00251 /* --------------------------------------------------------------------- */ 00252 00253 void DataProtocol::get(const KUrl& url) { 00254 ref(); 00255 kDebug() << "kio_data@"<<this<<"::get(const KUrl& url)"; 00256 00257 DataHeader hdr; 00258 parseDataHeader(url,hdr); 00259 00260 int size = hdr.url.length(); 00261 int data_ofs = qMin(hdr.data_offset,size); 00262 // FIXME: string is copied, would be nice if we could have a reference only 00263 QString url_data = hdr.url.mid(data_ofs); 00264 QByteArray outData; 00265 00266 if (hdr.is_base64) { 00267 // base64 stuff is expected to contain the correct charset, so we just 00268 // decode it and pass it to the receiver 00269 outData = QByteArray::fromBase64(url_data.toUtf8()); 00270 } else { 00271 // FIXME: This is all flawed, must be reworked thoroughly 00272 // non encoded data must be converted to the given charset 00273 QTextCodec *codec = QTextCodec::codecForName(hdr.charset.toLatin1()); 00274 if (codec != 0) { 00275 outData = codec->fromUnicode(url_data); 00276 } else { 00277 // if there is no approprate codec, just use local encoding. This 00278 // should work for >90% of all cases. 00279 outData = url_data.toLocal8Bit(); 00280 }/*end if*/ 00281 }/*end if*/ 00282 00283 //kDebug() << "emit mimeType@"<<this; 00284 mimeType(hdr.mime_type); 00285 //kDebug() << "emit totalSize@"<<this; 00286 totalSize(outData.size()); 00287 00288 //kDebug() << "emit setMetaData@"<<this; 00289 #if defined(TESTKIO) || defined(DATAKIOSLAVE) 00290 MetaData::ConstIterator it; 00291 for (it = hdr.attributes.constBegin(); it != hdr.attributes.constEnd(); ++it) { 00292 setMetaData(it.key(),it.value()); 00293 }/*next it*/ 00294 #else 00295 setAllMetaData(hdr.attributes); 00296 #endif 00297 00298 //kDebug() << "emit sendMetaData@"<<this; 00299 sendMetaData(); 00300 // kDebug() << "(1) queue size " << dispatchQueue.size(); 00301 // empiric studies have shown that this shouldn't be queued & dispatched 00302 data(outData); 00303 // kDebug() << "(2) queue size " << dispatchQueue.size(); 00304 DISPATCH(data(QByteArray())); 00305 // kDebug() << "(3) queue size " << dispatchQueue.size(); 00306 DISPATCH(finished()); 00307 // kDebug() << "(4) queue size " << dispatchQueue.size(); 00308 deref(); 00309 } 00310 00311 /* --------------------------------------------------------------------- */ 00312 00313 void DataProtocol::mimetype(const KUrl &url) { 00314 ref(); 00315 DataHeader hdr; 00316 parseDataHeader(url,hdr); 00317 mimeType(hdr.mime_type); 00318 finished(); 00319 deref(); 00320 } 00321 00322 /* --------------------------------------------------------------------- */
KDE 4.7 API Reference