// dataprotocol.cpp // ================== // // Implementation of the data protocol (rfc 2397) // // Author: Leo Savernik // Email: l.savernik@aon.at // (C) 2002, 2003 by Leo Savernik // Created: Sam Dez 28 14:11:18 CET 2002 /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * * published by the Free Software Foundation; version 2. * * * ***************************************************************************/ #include "dataprotocol.h" #include #include #include #include #include #include #include #include #ifdef DATAKIOSLAVE # include # include #endif #ifdef TESTKIO # include #endif #if !defined(DATAKIOSLAVE) && !defined(TESTKIO) # define DISPATCH(f) dispatch_##f #else # define DISPATCH(f) f #endif using namespace KIO; #ifdef DATAKIOSLAVE extern "C" { int kdemain( int argc, char **argv ) { TDEInstance instance( "kio_data" ); kdDebug(7101) << "*** Starting kio_data " << endl; if (argc != 4) { kdDebug(7101) << "Usage: kio_data protocol domain-socket1 domain-socket2" << endl; exit(-1); } DataProtocol slave(argv[2], argv[3]); slave.dispatchLoop(); kdDebug(7101) << "*** kio_data Done" << endl; return 0; } } #endif /** structure containing header information */ struct DataHeader { TQString mime_type; // mime type of content (lowercase) MetaData attributes; // attribute/value pairs (attribute lowercase, // value unchanged) bool is_base64; // true if data is base64 encoded TQString url; // reference to decoded url int data_offset; // zero-indexed position within url // where the real data begins. May point beyond // the end to indicate that there is no data TQString *charset; // shortcut to charset (it always exists) }; // constant string data const TQChar text_plain_str[] = { 't','e','x','t','/','p','l','a','i','n' }; const TQChar charset_str[] = { 'c','h','a','r','s','e','t' }; const TQChar us_ascii_str[] = { 'u','s','-','a','s','c','i','i' }; const TQChar base64_str[] = { 'b','a','s','e','6','4' }; /** returns the position of the first occurrence of any of the given characters * @p c1 to @p c3 or buf.length() if none is contained. * @param buf buffer where to look for c * @param begin zero-indexed starting position * @param c1 character to find * @param c2 alternative character to find or '\0' to ignore * @param c3 alternative character to find or '\0' to ignore */ static int find(const TQString &buf, int begin, TQChar c1, TQChar c2 = '\0', TQChar c3 = '\0') { int pos = begin; int size = (int)buf.length(); while (pos < size) { TQChar ch = buf[pos]; if (ch == c1 || (c2 != '\0' && ch == c2) || (c3 != '\0' && ch == c3)) break; pos++; }/*wend*/ return pos; } /** extracts the string between the current position @p pos and the first * occurrence of either @p c1 to @p c3 exclusively and updates @p pos * to point at the found delimiter or at the end of the buffer if * neither character occurred. * @param buf buffer where to look for * @param pos zero-indexed position within buffer * @param c1 character to find * @param c2 alternative character to find or 0 to ignore * @param c3 alternative character to find or 0 to ignore */ inline TQString extract(const TQString &buf, int &pos, TQChar c1, TQChar c2 = '\0', TQChar c3 = '\0') { int oldpos = pos; pos = find(buf,oldpos,c1,c2,c3); return TQString(buf.unicode() + oldpos, pos - oldpos); } /** ignores all whitespaces * @param buf buffer to operate on * @param pos position to shift to first non-whitespace character * Upon return @p pos will either point to the first non-whitespace * character or to the end of the buffer. */ inline void ignoreWS(const TQString &buf, int &pos) { int size = (int)buf.length(); TQChar ch = buf[pos]; while (pos < size && (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) ch = buf[++pos]; } /** parses a quoted string as per rfc 822. * * If trailing quote is missing, the whole rest of the buffer is returned. * @param buf buffer to operate on * @param pos position pointing to the leading quote * @return the extracted string. @p pos will be updated to point to the * character following the trailing quote. */ static TQString parseQuotedString(const TQString &buf, int &pos) { int size = (int)buf.length(); TQString res; pos++; // jump over leading quote bool escaped = false; // if true means next character is literal bool parsing = true; // true as long as end quote not found while (parsing && pos < size) { TQChar ch = buf[pos++]; if (escaped) { res += ch; escaped = false; } else { switch (ch) { case '"': parsing = false; break; case '\\': escaped = true; break; default: res += ch; break; }/*end switch*/ }/*end if*/ }/*wend*/ return res; } /** parses the header of a data url * @param url the data url * @param header_info fills the given DataHeader structure with the header * information */ static void parseDataHeader(const KURL &url, DataHeader &header_info) { TQConstString text_plain(text_plain_str,sizeof text_plain_str/sizeof text_plain_str[0]); TQConstString charset(charset_str,sizeof charset_str/sizeof charset_str[0]); TQConstString us_ascii(us_ascii_str,sizeof us_ascii_str/sizeof us_ascii_str[0]); TQConstString base64(base64_str,sizeof base64_str/sizeof base64_str[0]); // initialize header info members header_info.mime_type = text_plain.string(); header_info.charset = &header_info.attributes.insert( charset.string(),us_ascii.string()) .data(); header_info.is_base64 = false; // decode url and save it TQString &raw_url = header_info.url = TQString::fromLatin1("data:") + url.path(); int raw_url_len = (int)raw_url.length(); // jump over scheme part (must be "data:", we don't even check that) header_info.data_offset = raw_url.find(':'); header_info.data_offset++; // jump over colon or to begin if scheme was missing // read mime type if (header_info.data_offset >= raw_url_len) return; TQString mime_type = extract(raw_url,header_info.data_offset,';',',') .stripWhiteSpace(); if (!mime_type.isEmpty()) header_info.mime_type = mime_type; if (header_info.data_offset >= raw_url_len) return; // jump over delimiter token and return if data reached if (raw_url[header_info.data_offset++] == ',') return; // read all attributes and store them bool data_begin_reached = false; while (!data_begin_reached && header_info.data_offset < raw_url_len) { // read attribute TQString attribute = extract(raw_url,header_info.data_offset,'=',';',',') .stripWhiteSpace(); if (header_info.data_offset >= raw_url_len || raw_url[header_info.data_offset] != '=') { // no assigment, must be base64 option if (attribute == base64.string()) header_info.is_base64 = true; } else { header_info.data_offset++; // jump over '=' token // read value ignoreWS(raw_url,header_info.data_offset); if (header_info.data_offset >= raw_url_len) return; TQString value; if (raw_url[header_info.data_offset] == '"') { value = parseQuotedString(raw_url,header_info.data_offset); ignoreWS(raw_url,header_info.data_offset); } else value = extract(raw_url,header_info.data_offset,';',',') .stripWhiteSpace(); // add attribute to map header_info.attributes[attribute.lower()] = value; }/*end if*/ if (header_info.data_offset < raw_url_len && raw_url[header_info.data_offset] == ',') data_begin_reached = true; header_info.data_offset++; // jump over separator token }/*wend*/ } #ifdef DATAKIOSLAVE DataProtocol::DataProtocol(const TQCString &pool_socket, const TQCString &app_socket) : SlaveBase("kio_data", pool_socket, app_socket) { #else DataProtocol::DataProtocol() { #endif kdDebug() << "DataProtocol::DataProtocol()" << endl; } /* --------------------------------------------------------------------- */ DataProtocol::~DataProtocol() { kdDebug() << "DataProtocol::~DataProtocol()" << endl; } /* --------------------------------------------------------------------- */ void DataProtocol::get(const KURL& url) { ref(); //kdDebug() << "===============================================================================================================================================================================" << endl; kdDebug() << "kio_data@"<latin1()); if (codec != 0) { outData = codec->fromUnicode(url_data); } else { // if there is no approprate codec, just use local encoding. This // should work for >90% of all cases. outData = url_data.local8Bit(); }/*end if*/ }/*end if*/ //kdDebug() << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl; //kdDebug() << "emit mimeType@"<