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