tag.h

Go to the documentation of this file.
00001 /**
00002  *  Copyright (C) 2004-2005 Alo Sarv <madcat_@users.sourceforge.net>
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017  */
00018 
00019 #ifndef __TAG_H__
00020 #define __TAG_H__
00021 
00022 #include <hn/osdep.h>
00023 #include <boost/any.hpp>
00024 #include <boost/format.hpp>
00025 #include <iosfwd>
00026 #include <stdexcept>
00027 #include <string>
00028 
00029 /**
00030  * Exception class, thrown when Tag parsing fails.
00031  */
00032 class TagError : public std::runtime_error {
00033 public:
00034         TagError(const std::string &msg) : std::runtime_error(msg) {}
00035         TagError(boost::format fmt) : std::runtime_error(fmt.str()) {}
00036 };
00037 
00038 /**
00039  * Tag class represents one Tag in the protocol. A tag has its identifier
00040  * (either 8-bit opcode or string value), and a data field. Refer to
00041  * eDonkey2000 protocol tag system overview for detailed information on the
00042  * Tag parsing system.
00043  */
00044 class Tag {
00045 public:
00046         /**
00047          * Construct and load from input stream
00048          *
00049          * @param i        Input stream to read the data from
00050          * \throws TagError if parsing fails
00051          */
00052         Tag(std::istream &i);
00053 
00054         /**
00055          * @name Construct using opcode (or name) and value. Only string and
00056          *       integer data types are allowed for construction.
00057          */
00058         //@{
00059         Tag(uint8_t opcode, const std::string &value)
00060         : m_opcode(opcode), m_value(value), m_valueType(TT_STRING) {}
00061         Tag(uint8_t opcode, uint32_t value)
00062         : m_opcode(opcode), m_value(value), m_valueType(TT_UINT32) {}
00063         Tag(const std::string &name, const std::string &value)
00064         : m_opcode(0), m_name(name), m_value(value), m_valueType(TT_STRING) {}
00065         Tag(const std::string &name, uint32_t value)
00066         : m_opcode(0), m_name(name), m_value(value), m_valueType(TT_UINT32) {}
00067         //@}
00068 
00069         /**
00070          * @name Accessors for preconstructed or preloaded Tag data.
00071          */
00072         //@{
00073         uint32_t getInt()   const try {
00074                 return boost::any_cast<uint32_t>(m_value);
00075         } catch (boost::bad_any_cast&) {
00076                 throw TagError(
00077                         boost::format(
00078                                 "Invalid Tag value retrieval (int). \n"
00079                                 "Tag is %s"
00080                         ) % dump()
00081                 );
00082         }
00083         float    getFloat() const try {
00084                 return boost::any_cast<float>(m_value);
00085         } catch (boost::bad_any_cast&) {
00086                 throw TagError(
00087                         boost::format(
00088                                 "Invalid Tag value retrieval (float). \n"
00089                                 "Tag is %s"
00090                         ) % dump()
00091                 );
00092         }
00093         std::string getStr() const try {
00094                 return boost::any_cast<std::string>(m_value);
00095         } catch (boost::bad_any_cast&) {
00096                 throw TagError(
00097                         boost::format(
00098                                 "Invalid Tag value retrieval (string). \n"
00099                                 "Tag is %s"
00100                         ) % dump()
00101                 );
00102         }
00103         uint8_t     getOpcode()    const { return m_opcode;    }
00104         std::string getName()      const { return m_name;      }
00105         uint8_t     getValueType() const { return m_valueType; }
00106         //@}
00107 
00108         /**
00109          * Generate a debugging string from this tag's contents.
00110          *
00111          * @param data    If true, also include tag data/value field
00112          * @return        Human-readable (mostly) hexdump of tag's contents
00113          */
00114         std::string dump(bool data = true) const;
00115 private:
00116         Tag();                       //!< Default constructor protected
00117 
00118         uint8_t     m_opcode;        //!< Opcode
00119         std::string m_name;          //!< Name (in case of string-tag)
00120         boost::any  m_value;         //!< Value
00121         uint8_t     m_valueType;     //!< Type of value, one of ValueTypes
00122 
00123         //! Different tag value types used in the protocol
00124         enum ValueTypes {
00125                 TT_HASH    = 0x01,    //!< unsupported, 16 bytes
00126                 TT_STRING  = 0x02,    //!< [u16]len[len]data
00127                 TT_UINT32  = 0x03,    //!< 4 bytes
00128                 TT_FLOAT   = 0x04,    //!< 4 bytes
00129                 TT_BOOL    = 0x05,    //!< unsupported, 1 byte
00130                 TT_BOOLARR = 0x06,    //!< unsupported, [u16]len[len]data
00131                 TT_BLOB    = 0x07,    //!< unsupported, [u16]len[len]data
00132                 TT_UINT16  = 0x08,    //!< 2 bytes
00133                 TT_UINT8   = 0x09,    //!< 1 byte
00134                 TT_BSOB    = 0x0a     //!< unsupported, [u16]len[len]data
00135         };
00136 
00137         //! Writes ed2k-compatible tag structure to specified output stream.
00138         friend std::ostream& operator<<(std::ostream &o, const Tag &t);
00139 
00140         /**
00141          * Small helper function to localize the warning messages on unhandled
00142          * tags found while parsing.
00143          *
00144          * @param loc  Location where the tag was found, e.g. "server.met"
00145          * @param t    The unhandled tag itself.
00146          */
00147         friend void warnUnHandled(const std::string &loc, const Tag &t);
00148 };
00149 
00150 /**
00151  * \page ed2k-tag   eDonkey2000 protocol tag system overview
00152  * \section Abstract
00153  * This document describes the Tag system used in eDonkey2000 protocol.
00154  * \sa \ref ed2kproto
00155  *
00156  * \section toc Table of Contents
00157  * \par
00158  * \ref overview <br>
00159  * \ref header   <br>
00160  * \ref desc     <br>
00161  *
00162  * \section overview 1. Overview
00163  *
00164  * Tags are used in ed2k network to add any number of additional data fields
00165  * to existing data structures while keeping the protocol backwards compatible.
00166  * The basic concept is that a tag can be identified by its data type (and/or
00167  * length), and can be simply ignored if the specific tag is not supported
00168  * for whatever reason.
00169  *
00170  * Client's can detect and handle various tags by their opcode, which is
00171  * 8-byte value. Additionally, there have been some protocol extensions which
00172  * also allow 'named' tags, which - logically - have a name instead of simple
00173  * opcode.
00174  *
00175  * \section header 2. Header specification
00176  *
00177  * Standard ed2k tag:
00178  * \code
00179  * +--------+--------+--------+--------+--------
00180  * |  type  |  length = 0x01  | opcode |   type-specific amount of data
00181  * +--------+--------+--------+--------+--------
00182  * \endcode
00183  * Extended protocol, string tag:
00184  * \code
00185  * +-------+-------+-------+~~~~~~~~~~~~~~~+------
00186  * |  type |     length    | <length>name  | type-specific amount of data
00187  * +-------+-------+-------+~~~~~~~~~~~~~~~+------
00188  * \endcode
00189  * Lugdunum extended protocol, special types
00190  * \code
00191  * +-------+--------+-------
00192  * |  type | opcode | (type & 0x7f) - 0x10 bytes of string data
00193  * +-------+--------+-------
00194  * \endcode
00195  *
00196  * \section desc 3. Description
00197  *
00198  * As can be seen, lugdunum's extended protocol allows incorporating the string
00199  * field length into the type bits, lowering the header overhead. This format
00200  * can be detected by looking at the highest-order bit of type field. If it is
00201  * set, the type is encoded using this extension. To retrieve the actual data
00202  * type, apply &0x7f to the type. If this resolves into an integer of size
00203  * 0x10 or more, the string data length can be retrieved by substracting 0x10
00204  * from the type. Otherwise, the tag should be treated as normal ed2k tag type.
00205  *
00206  * Type field in the tag may be one of the following:
00207  * \dontinclude tag.h
00208  * \skip TT_HASH
00209  * \until TT_BSOB
00210  */
00211 
00212 #endif