dataprotocol.cpp
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 // (C) 2002, 2003 by Leo Savernik 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 <kmdcodec.h> 00023 #include <kurl.h> 00024 #include <tdeio/global.h> 00025 00026 #include <tqcstring.h> 00027 #include <tqstring.h> 00028 #include <tqstringlist.h> 00029 #include <tqtextcodec.h> 00030 00031 #ifdef DATAKIOSLAVE 00032 # include <kinstance.h> 00033 # include <stdlib.h> 00034 #endif 00035 #ifdef TESTKIO 00036 # include <iostream> 00037 #endif 00038 00039 #if !defined(DATAKIOSLAVE) && !defined(TESTKIO) 00040 # define DISPATCH(f) dispatch_##f 00041 #else 00042 # define DISPATCH(f) f 00043 #endif 00044 00045 using namespace TDEIO; 00046 #ifdef DATAKIOSLAVE 00047 extern "C" { 00048 00049 int kdemain( int argc, char **argv ) { 00050 TDEInstance instance( "tdeio_data" ); 00051 00052 kdDebug(7101) << "*** Starting tdeio_data " << endl; 00053 00054 if (argc != 4) { 00055 kdDebug(7101) << "Usage: tdeio_data protocol domain-socket1 domain-socket2" << endl; 00056 exit(-1); 00057 } 00058 00059 DataProtocol slave(argv[2], argv[3]); 00060 slave.dispatchLoop(); 00061 00062 kdDebug(7101) << "*** tdeio_data Done" << endl; 00063 return 0; 00064 } 00065 } 00066 #endif 00067 00069 struct DataHeader { 00070 TQString mime_type; // mime type of content (lowercase) 00071 MetaData attributes; // attribute/value pairs (attribute lowercase, 00072 // value unchanged) 00073 bool is_base64; // true if data is base64 encoded 00074 TQString url; // reference to decoded url 00075 int data_offset; // zero-indexed position within url 00076 // where the real data begins. May point beyond 00077 // the end to indicate that there is no data 00078 TQString *charset; // shortcut to charset (it always exists) 00079 }; 00080 00081 // constant string data 00082 const TQChar text_plain_str[] = { 't','e','x','t','/','p','l','a','i','n' }; 00083 const TQChar charset_str[] = { 'c','h','a','r','s','e','t' }; 00084 const TQChar us_ascii_str[] = { 'u','s','-','a','s','c','i','i' }; 00085 const TQChar base64_str[] = { 'b','a','s','e','6','4' }; 00086 00095 static int find(const TQString &buf, int begin, TQChar c1, TQChar c2 = '\0', 00096 TQChar c3 = '\0') { 00097 int pos = begin; 00098 int size = (int)buf.length(); 00099 while (pos < size) { 00100 TQChar ch = buf[pos]; 00101 if (ch == c1 00102 || (c2 != '\0' && ch == c2) 00103 || (c3 != '\0' && ch == c3)) 00104 break; 00105 pos++; 00106 }/*wend*/ 00107 return pos; 00108 } 00109 00120 inline TQString extract(const TQString &buf, int &pos, TQChar c1, 00121 TQChar c2 = '\0', TQChar c3 = '\0') { 00122 int oldpos = pos; 00123 pos = find(buf,oldpos,c1,c2,c3); 00124 return TQString(buf.unicode() + oldpos, pos - oldpos); 00125 } 00126 00133 inline void ignoreWS(const TQString &buf, int &pos) { 00134 int size = (int)buf.length(); 00135 TQChar ch = buf[pos]; 00136 while (pos < size && (ch == ' ' || ch == '\t' || ch == '\n' 00137 || ch == '\r')) 00138 ch = buf[++pos]; 00139 } 00140 00149 static TQString parseQuotedString(const TQString &buf, int &pos) { 00150 int size = (int)buf.length(); 00151 TQString res; 00152 pos++; // jump over leading quote 00153 bool escaped = false; // if true means next character is literal 00154 bool parsing = true; // true as long as end quote not found 00155 while (parsing && pos < size) { 00156 TQChar ch = buf[pos++]; 00157 if (escaped) { 00158 res += ch; 00159 escaped = false; 00160 } else { 00161 switch (ch) { 00162 case '"': parsing = false; break; 00163 case '\\': escaped = true; break; 00164 default: res += ch; break; 00165 }/*end switch*/ 00166 }/*end if*/ 00167 }/*wend*/ 00168 return res; 00169 } 00170 00176 static void parseDataHeader(const KURL &url, DataHeader &header_info) { 00177 TQConstString text_plain(text_plain_str,sizeof text_plain_str/sizeof text_plain_str[0]); 00178 TQConstString charset(charset_str,sizeof charset_str/sizeof charset_str[0]); 00179 TQConstString us_ascii(us_ascii_str,sizeof us_ascii_str/sizeof us_ascii_str[0]); 00180 TQConstString base64(base64_str,sizeof base64_str/sizeof base64_str[0]); 00181 // initialize header info members 00182 header_info.mime_type = text_plain.string(); 00183 header_info.charset = &header_info.attributes.insert( 00184 charset.string(),us_ascii.string()) 00185 .data(); 00186 header_info.is_base64 = false; 00187 00188 // decode url and save it 00189 TQString &raw_url = header_info.url = TQString::fromLatin1("data:") + url.path(); 00190 int raw_url_len = (int)raw_url.length(); 00191 00192 // jump over scheme part (must be "data:", we don't even check that) 00193 header_info.data_offset = raw_url.find(':'); 00194 header_info.data_offset++; // jump over colon or to begin if scheme was missing 00195 00196 // read mime type 00197 if (header_info.data_offset >= raw_url_len) return; 00198 TQString mime_type = extract(raw_url,header_info.data_offset,';',',') 00199 .stripWhiteSpace(); 00200 if (!mime_type.isEmpty()) header_info.mime_type = mime_type; 00201 00202 if (header_info.data_offset >= raw_url_len) return; 00203 // jump over delimiter token and return if data reached 00204 if (raw_url[header_info.data_offset++] == ',') return; 00205 00206 // read all attributes and store them 00207 bool data_begin_reached = false; 00208 while (!data_begin_reached && header_info.data_offset < raw_url_len) { 00209 // read attribute 00210 TQString attribute = extract(raw_url,header_info.data_offset,'=',';',',') 00211 .stripWhiteSpace(); 00212 if (header_info.data_offset >= raw_url_len 00213 || raw_url[header_info.data_offset] != '=') { 00214 // no assigment, must be base64 option 00215 if (attribute == base64.string()) 00216 header_info.is_base64 = true; 00217 } else { 00218 header_info.data_offset++; // jump over '=' token 00219 00220 // read value 00221 ignoreWS(raw_url,header_info.data_offset); 00222 if (header_info.data_offset >= raw_url_len) return; 00223 00224 TQString value; 00225 if (raw_url[header_info.data_offset] == '"') { 00226 value = parseQuotedString(raw_url,header_info.data_offset); 00227 ignoreWS(raw_url,header_info.data_offset); 00228 } else 00229 value = extract(raw_url,header_info.data_offset,';',',') 00230 .stripWhiteSpace(); 00231 00232 // add attribute to map 00233 header_info.attributes[attribute.lower()] = value; 00234 00235 }/*end if*/ 00236 if (header_info.data_offset < raw_url_len 00237 && raw_url[header_info.data_offset] == ',') 00238 data_begin_reached = true; 00239 header_info.data_offset++; // jump over separator token 00240 }/*wend*/ 00241 } 00242 00243 #ifdef DATAKIOSLAVE 00244 DataProtocol::DataProtocol(const TQCString &pool_socket, const TQCString &app_socket) 00245 : SlaveBase("tdeio_data", pool_socket, app_socket) { 00246 #else 00247 DataProtocol::DataProtocol() { 00248 #endif 00249 kdDebug() << "DataProtocol::DataProtocol()" << endl; 00250 } 00251 00252 /* --------------------------------------------------------------------- */ 00253 00254 DataProtocol::~DataProtocol() { 00255 kdDebug() << "DataProtocol::~DataProtocol()" << endl; 00256 } 00257 00258 /* --------------------------------------------------------------------- */ 00259 00260 void DataProtocol::get(const KURL& url) { 00261 ref(); 00262 //kdDebug() << "===============================================================================================================================================================================" << endl; 00263 kdDebug() << "tdeio_data@"<<this<<"::get(const KURL& url)" << endl ; 00264 00265 DataHeader hdr; 00266 parseDataHeader(url,hdr); 00267 00268 int size = (int)hdr.url.length(); 00269 int data_ofs = TQMIN(hdr.data_offset,size); 00270 // FIXME: string is copied, would be nice if we could have a reference only 00271 TQString url_data = hdr.url.mid(data_ofs); 00272 TQCString outData; 00273 00274 #ifdef TESTKIO 00275 // cout << "current charset: \"" << *hdr.charset << "\"" << endl; 00276 #endif 00277 if (hdr.is_base64) { 00278 // base64 stuff is expected to contain the correct charset, so we just 00279 // decode it and pass it to the receiver 00280 KCodecs::base64Decode(url_data.local8Bit(),outData); 00281 } else { 00282 // FIXME: This is all flawed, must be reworked thoroughly 00283 // non encoded data must be converted to the given charset 00284 TQTextCodec *codec = TQTextCodec::codecForName(hdr.charset->latin1()); 00285 if (codec != 0) { 00286 outData = codec->fromUnicode(url_data); 00287 } else { 00288 // if there is no approprate codec, just use local encoding. This 00289 // should work for >90% of all cases. 00290 outData = url_data.local8Bit(); 00291 }/*end if*/ 00292 }/*end if*/ 00293 00294 //kdDebug() << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl; 00295 //kdDebug() << "emit mimeType@"<<this << endl ; 00296 mimeType(hdr.mime_type); 00297 //kdDebug() << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl; 00298 //kdDebug() << "emit totalSize@"<<this << endl ; 00299 totalSize(outData.size()); 00300 00301 //kdDebug() << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl; 00302 //kdDebug() << "emit setMetaData@"<<this << endl ; 00303 #if defined(TESTKIO) || defined(DATAKIOSLAVE) 00304 MetaData::ConstIterator it; 00305 for (it = hdr.attributes.begin(); it != hdr.attributes.end(); ++it) { 00306 setMetaData(it.key(),it.data()); 00307 }/*next it*/ 00308 #else 00309 setAllMetaData(hdr.attributes); 00310 #endif 00311 00312 //kdDebug() << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl; 00313 //kdDebug() << "emit sendMetaData@"<<this << endl ; 00314 sendMetaData(); 00315 //kdDebug() << "^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C" << endl; 00316 // kdDebug() << "(1) queue size " << dispatchQueue.size() << endl; 00317 // empiric studies have shown that this shouldn't be queued & dispatched 00318 /*DISPATCH*/(data(outData)); 00319 // kdDebug() << "(2) queue size " << dispatchQueue.size() << endl; 00320 DISPATCH(data(TQByteArray())); 00321 // kdDebug() << "(3) queue size " << dispatchQueue.size() << endl; 00322 DISPATCH(finished()); 00323 // kdDebug() << "(4) queue size " << dispatchQueue.size() << endl; 00324 deref(); 00325 } 00326 00327 /* --------------------------------------------------------------------- */ 00328 00329 void DataProtocol::mimetype(const KURL &url) { 00330 ref(); 00331 DataHeader hdr; 00332 parseDataHeader(url,hdr); 00333 mimeType(hdr.mime_type); 00334 finished(); 00335 deref(); 00336 } 00337 00338 /* --------------------------------------------------------------------- */ 00339