/** * yatemime.h * This file is part of the YATE Project http://YATE.null.ro * * MIME types, body codecs and related functions * * Yet Another Telephony Engine - a fully featured software PBX and IVR * Copyright (C) 2004-2023 Null Team * * This software is distributed under multiple licenses; * see the COPYING file in the main directory for licensing * information for this specific distribution. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef __YATEMIME_H #define __YATEMIME_H #ifndef __cplusplus #error C++ is required #endif #include /** * Holds all Telephony Engine related classes. */ namespace TelEngine { /** * A MIME header line. * The NamedString's value contain the first parameter after the header name * @short MIME header line */ class YATE_API MimeHeaderLine : public NamedString { public: /** * Constructor. * Builds a MIME header line from a string buffer. * Splits the value into header parameters * @param name The header name * @param value The header value * @param sep Optional parameter separator. If 0, the default ';' will be used */ MimeHeaderLine(const char* name, const String& value, char sep = 0); /** * Constructor. * Builds this MIME header line from another one * @param original Original header line to build from. * @param newName Optional new header name. If 0, the original name will be used */ MimeHeaderLine(const MimeHeaderLine& original, const char* newName = 0); /** * Destructor. */ virtual ~MimeHeaderLine(); /** * RTTI method, get a pointer to a derived class given the class name. * @param name Name of the class we are asking for * @return Pointer to the requested class or NULL if this object doesn't implement it */ virtual void* getObject(const String& name) const; /** * Duplicate this MIME header line. * @param newName Optional new header name. If 0, this header's name will be used * @return Copy of this MIME header line */ virtual MimeHeaderLine* clone(const char* newName = 0) const; /** * Build a string line from this MIME header without adding a line separator * @param line Destination string * @param header True to add the header in front of line text */ virtual void buildLine(String& line, bool header = true) const; /** * Assignement operator. Set the header's value * @param value The new headr value */ inline MimeHeaderLine& operator=(const char* value) { NamedString::operator=(value); return *this; } /** * Get the header's parameters * @return This header's list of parameters */ inline const ObjList& params() const { return m_params; } /** * Get the character used as separator in header line * @return This header's separator */ inline char separator() const { return m_separator; } /** * Replace the value of an existing parameter or add a new one * @param name Parameter's name * @param value Parameter's value */ void setParam(const char* name, const char* value = 0); /** * Remove a parameter from list * @param name Parameter's name */ void delParam(const char* name); /** * Get a header parameter * @param name Parameter's name * @return Pointer to the desired parameter or 0 if not found */ const NamedString* getParam(const char* name) const; /** * Utility function, puts quotes around a string. * @param str String to put quotes around. * @param force True to force quoting even if was already quoted */ static void addQuotes(String& str, bool force = false); /** * Utility function, removes quotes around a string. * @param str String to remove quotes. * @param force True to force unquoting even if wasn't properly quoted */ static void delQuotes(String& str, bool force = false); /** * Utility function, puts quotes around a string. * @param str String to put quotes around. * @param force True to force quoting even if was already quoted * @return The input string enclosed in quotes. */ static String quote(const String& str, bool force = false); /** * Utility function, removes quotes around a string. * @param str String to remove quotes around. * @param force True to force unquoting even if wasn't properly quoted * @return The input string with enclosing quotes removed. */ static String unquote(const String& str, bool force = false); /** * Utility function to find a separator not in "quotes" or inside \. * @param str Input string used to find the separator. * @param sep The separator to find. * @param offs Starting offset in input string. * @return The position of the separator in input string or -1 if not found. */ static int findSep(const char* str, char sep, int offs = 0); /** * Build a string from a list of MIME header lines. * Add a CR/LF terminator after each line * @param buf Destination string * @param headers The list with the header lines */ static void buildHeaders(String& buf, const ObjList& headers); protected: ObjList m_params; // Header list of parameters char m_separator; // Parameter separator private: void operator=(const MimeHeaderLine&); // no assignment }; /** * A MIME header line containing authentication data. * @short MIME authentication line */ class YATE_API MimeAuthLine : public MimeHeaderLine { public: /** * Constructor. * Builds a MIME authentication header line from a string buffer. * Splits the value into header parameters * @param name The header name * @param value The header value */ MimeAuthLine(const char* name, const String& value); /** * Constructor. * Builds this MIME authentication header line from another one * @param original Original header line to build from. * @param newName Optional new header name. If 0, the original name will be used */ MimeAuthLine(const MimeAuthLine& original, const char* newName = 0); /** * RTTI method, get a pointer to a derived class given the class name. * @param name Name of the class we are asking for * @return Pointer to the requested class or NULL if this object doesn't implement it */ virtual void* getObject(const String& name) const; /** * Duplicate this MIME header line. * @param newName Optional new header name. If 0, this header's name will be used * @return Copy of this MIME header line */ virtual MimeHeaderLine* clone(const char* newName = 0) const; /** * Build a string line from this MIME header without adding a line separator * @param line Destination string * @param header True to add the header in front of line text */ virtual void buildLine(String& line, bool header = true) const; private: void operator=(const MimeAuthLine&); // no assignment }; /** * Abstract base class for holding Multipurpose Internet Mail Extensions data. * Keeps a Content-Type header line with body type and parameters and * any additional header lines the body may have. * The body type contains lower case characters. * @short Abstract MIME data holder */ class YATE_API MimeBody : public GenObject { YNOCOPY(MimeBody); // no automatic copies please public: /** * Destructor */ virtual ~MimeBody(); /** * RTTI method, get a pointer to a derived class given the class name * @param name Name of the class we are asking for * @return Pointer to the requested class or NULL if this object doesn't implement it */ virtual void* getObject(const String& name) const; /** * Retrieve the MIME type of this body * @return Name of the MIME type/subtype */ inline const MimeHeaderLine& getType() const { return m_type; } /** * Get the first body that matches a requested type, descends into multiparts * @param type Name of the MIME type to search for * @return Pointer to requested body or NULL if not found */ MimeBody* getFirst(const String& type) const; /** * Retrieve the additional headers of this MIME body (other then Content-Type) * @return The list of header lines of this MIME body */ inline const ObjList& headers() const { return m_headers; } /** * Append an additional header line to this body * @param hdr The header line to append */ inline void appendHdr(MimeHeaderLine* hdr) { if (hdr) m_headers.append(hdr); } /** * Remove an additional header line from this body * @param hdr The header line to remove * @param delobj True to delete the header, false to remove from list without deleting it */ inline void removeHdr(MimeHeaderLine* hdr, bool delobj = true) { if (hdr) m_headers.remove(hdr,delobj); } /** * Find an additional header line by its name. The names are compared case insensitive * @param name The name of the header to find * @param start The starting point in the list. 0 to start from the beginning * @return Pointer to MimeHeaderLine or 0 if not found */ MimeHeaderLine* findHdr(const String& name, const MimeHeaderLine* start = 0) const; /** * Build a string with this body's header lines * @param buf Destination string */ inline void buildHeaders(String& buf) { m_type.buildLine(buf); buf << "\r\n"; MimeHeaderLine::buildHeaders(buf,m_headers); } /** * Replace the value of an existing parameter or add a new one * @param name Parameter's name * @param value Parameter's value * @param header Header whose parameter will be changed. * Set to 0 to use the body's content type header * @return False if the header doesn't exist */ bool setParam(const char* name, const char* value = 0, const char* header = 0); /** * Remove a header parameter * @param name Parameter's name * @param header Header whose parameter will be removed. * Set to 0 to use the body's content type header * @return False if the header doesn't exist */ bool delParam(const char* name, const char* header = 0); /** * Get a header parameter * @param name Parameter's name * @param header Header whose parameter will be retrieved. * Set to 0 to use the body's content type header * @return Pointer to the desired parameter or 0 if not found */ const NamedString* getParam(const char* name, const char* header = 0) const; /** * Retrieve the binary encoding of this MIME body. Build the body if empty. * The body doesn't contain the Content-Type header or the additional headers * @return Block of binary data */ const DataBlock& getBody() const; /** * Get the binary data of this MIME body without building it. * @return Block of binary data */ inline const DataBlock& body() const { return m_body; } /** * Check if this body is a Session Description Protocol * @return True if this body holds a SDP */ virtual bool isSDP() const { return false; } /** * Check if this body is multipart (can hold other MIME bodies) * @return True if this body is multipart */ virtual bool isMultipart() const { return false; } /** * Duplicate this MIME body * @return Copy of this MIME body */ virtual MimeBody* clone() const = 0; /** * Method to build a MIME body from a type and data buffer. * Unknown body types are built into a binary body. Exactly 1 leading CRLF * is removed from the beginning of the buffer if found before building it * @param buf Pointer to buffer of data just after the body headers * @param len Length of data in buffer * @param type The header line declaring the body's content. * Usually this is a Content-Type header line * @return Newly allocated MIME body or NULL if the buffer is empty */ static MimeBody* build(const char* buf, int len, const MimeHeaderLine& type); /** * Utility method, returns an unfolded line and advances the pointer * @param buf Reference to pointer to start of buffer data * @param len Reference to variable holding buffer length * @return Newly allocated String holding the line of text */ static String* getUnfoldedLine(const char*& buf, int& len); protected: /** * Constructor to be used only by derived classes. * Converts the MIME type string to lower case * @param type The value of the Content-Type header line */ MimeBody(const String& type); /** * Constructor to be used only by derived classes. * Builds this body from a header line. * Converts the MIME type string to lower case * @param type The content type header line */ MimeBody(const MimeHeaderLine& type); /** * Method that is called internally to build the binary encoded body */ virtual void buildBody() const = 0; /** * Block of binary data that @ref buildBody() must fill */ mutable DataBlock m_body; /** * Additional body headers (other then Content-Type) */ ObjList m_headers; private: MimeHeaderLine m_type; // Content type header line }; /** * An object holding the bodies of a multipart MIME * @short MIME multipart container */ class YATE_API MimeMultipartBody : public MimeBody { public: /** * Constructor to build an empty multipart body * @param subtype The multipart subtype * @param boundary The string used as separator for enclosed bodies. * A random one will be created if missing. The length will be truncated * to 70 if this value is exceeded */ explicit MimeMultipartBody(const char* subtype = "mixed", const char* boundary = 0); /** * Constructor from block of data * @param type The value of the Content-Type header line * @param buf Pointer to buffer of data * @param len Length of data in buffer */ MimeMultipartBody(const String& type, const char* buf, int len); /** * Constructor from block of data * @param type The content type header line * @param buf Pointer to buffer of data * @param len Length of data in buffer */ MimeMultipartBody(const MimeHeaderLine& type, const char* buf, int len); /** * Destructor */ virtual ~MimeMultipartBody(); /** * Get the list of bodies enclosed contained in this multipart * @return The list of bodies enclosed contained in this multipart */ inline const ObjList& bodies() const { return m_bodies; } /** * Append a body to this multipart * @param body The body to append */ inline void appendBody(MimeBody* body) { if (body) m_bodies.append(body); } /** * Remove a body from this multipart * @param body The body to remove * @param delobj True to delete the body, false to remove from list without deleting it */ inline void removeBody(MimeBody* body, bool delobj = true) { if (body) m_bodies.remove(body,delobj); } /** * Find a body. Enclosed multiparts are also searched for the requested body * @param content The value of the body to find. Must be lower case * @param start The starting point in the list. 0 to start from the beginning. * Be aware that this parameter is used internally to search within enclosed * multipart bodies and set to 0 when the starting point is found * @return Pointer to MimeBody or 0 if not found */ MimeBody* findBody(const String& content, MimeBody** start = 0) const; /** * RTTI method, get a pointer to a derived class given the class name * @param name Name of the class we are asking for * @return Pointer to the requested class or NULL if this object doesn't implement it */ virtual void* getObject(const String& name) const; /** * Check if this body is multipart (can hold other MIME bodies) * @return True if this body is multipart */ virtual bool isMultipart() const { return true; } /** * Duplicate this MIME body * @return Copy of this MIME body */ virtual MimeBody* clone() const; protected: /** * Copy constructor */ MimeMultipartBody(const MimeMultipartBody& original); /** * Method that is called internally to build the binary encoded body */ virtual void buildBody() const; /** * Parse a data buffer and append any valid body to this multipart * Ignore prolog, epilog and invalid bodies * @param buf Pointer to buffer of data * @param len Length of data in buffer */ void parse(const char* buf, int len); private: // Parse input buffer for first body boundary or data end // Advance buffer pass the boundary line and decrease the buffer length // Set endBody to true if the last boundary was found // Return the length of data before the found boundary int findBoundary(const char*& buf, int& len, const char* boundary, unsigned int bLen, bool& endBody); // Build a boundary string to be used when parsing or building body // Remove quotes if present. Trim blanks // Insert CRLF and boundary marks ('--') before parameter // @param boundary Destination string // @return False if the parameter is missing or the boundary is empty bool getBoundary(String& boundary) const; ObjList m_bodies; // The list of bodies contained in this multipart }; /** * An object holding the lines of an application/sdp MIME type * @short MIME for application/sdp */ class YATE_API MimeSdpBody : public MimeBody { public: /** * Default constructor, builds an empty application/sdp * @param hashing Enable hashing the content lines */ MimeSdpBody(bool hashing = false); /** * Constructor from block of data * @param type The value of the Content-Type header line * @param buf Pointer to buffer of data * @param len Length of data in buffer */ MimeSdpBody(const String& type, const char* buf, int len); /** * Constructor from block of data * @param type The content type header line * @param buf Pointer to buffer of data * @param len Length of data in buffer */ MimeSdpBody(const MimeHeaderLine& type, const char* buf, int len); /** * Destructor */ virtual ~MimeSdpBody(); /** * RTTI method, get a pointer to a derived class given the class name * @param name Name of the class we are asking for * @return Pointer to the requested class or NULL if this object doesn't implement it */ virtual void* getObject(const String& name) const; /** * Override that checks if this body is an Session Description Protocol * @return True, since this body holds a SDP */ virtual bool isSDP() const { return true; } /** * Duplicate this MIME body * @return Copy of this MIME body - a new MimeSdpBody */ virtual MimeBody* clone() const; /** * Retrieve the lines hold in data * @return List of NamedStrings */ inline const ObjList& lines() const { return m_lines; } /** * Retrieve the hash of body lines * @return Hash of body, zero if hashing not enabled */ inline unsigned int hash() const { return m_hash; } /** * Append a new name=value line of SDP data * @param name Name of the line, should be one character * @param value Text of the line * @return Pointer to new added line */ NamedString* addLine(const char* name, const char* value = 0); /** * Retrieve the first line matching a name * @param name Name of the line to search * @return First instance of the searched name or NULL if none present */ const NamedString* getLine(const char* name) const; /** * Retrieve the next line of the same type as the current * @param line Current line * @return Next instance of same name or NULL if no more */ const NamedString* getNextLine(const NamedString* line) const; protected: /** * Copy constructor */ MimeSdpBody(const MimeSdpBody& original); /** * Override that is called internally to build the binary encoded body */ virtual void buildBody() const; private: // Build the lines from a data buffer void buildLines(const char* buf, int len); ObjList m_lines; ObjList* m_lineAppend; unsigned int m_hash; bool m_hashing; }; /** * An object holding a binary block of MIME data * @short MIME for obscure binary data */ class YATE_API MimeBinaryBody : public MimeBody { public: /** * Constructor from block of data * @param type The value of the Content-Type header line * @param buf Pointer to buffer of data * @param len Length of data in buffer */ MimeBinaryBody(const String& type, const char* buf, int len); /** * Constructor from block of data * @param type The content type header line * @param buf Pointer to buffer of data * @param len Length of data in buffer */ MimeBinaryBody(const MimeHeaderLine& type, const char* buf, int len); /** * Destructor */ virtual ~MimeBinaryBody(); /** * RTTI method, get a pointer to a derived class given the class name * @param name Name of the class we are asking for * @return Pointer to the requested class or NULL if this object doesn't implement it */ virtual void* getObject(const String& name) const; /** * Duplicate this MIME body * @return Copy of this MIME body - a new MimeBinaryBody */ virtual MimeBody* clone() const; protected: /** * Copy constructor */ MimeBinaryBody(const MimeBinaryBody& original); /** * Override that is called internally to build the binary encoded body */ virtual void buildBody() const; }; /** * An object holding MIME data as just one text string * @short MIME for one text string */ class YATE_API MimeStringBody : public MimeBody { public: /** * Constructor from block of data * @param type The value of the Content-Type header line * @param buf Pointer to buffer of data * @param len Length of data in buffer */ MimeStringBody(const String& type, const char* buf, int len = -1); /** * Constructor from block of data * @param type The content type header line * @param buf Pointer to buffer of data * @param len Length of data in buffer */ MimeStringBody(const MimeHeaderLine& type, const char* buf, int len = -1); /** * Destructor */ virtual ~MimeStringBody(); /** * RTTI method, get a pointer to a derived class given the class name * @param name Name of the class we are asking for * @return Pointer to the requested class or NULL if this object doesn't implement it */ virtual void* getObject(const String& name) const; /** * Duplicate this MIME body * @return Copy of this MIME body - a new MimeStringBody */ virtual MimeBody* clone() const; /** * Retrieve the stored data * @return String holding the data text */ inline const String& text() const { return m_text; } protected: /** * Copy constructor */ MimeStringBody(const MimeStringBody& original); /** * Override that is called internally to build the binary encoded body */ virtual void buildBody() const; private: String m_text; }; /** * An object holding MIME data as separate text lines * @short MIME for multiple text lines */ class YATE_API MimeLinesBody : public MimeBody { public: /** * Constructor from block of data * @param type The value of the Content-Type header line * @param buf Pointer to buffer of data * @param len Length of data in buffer */ MimeLinesBody(const String& type, const char* buf, int len); /** * Constructor from block of data * @param type The content type header line * @param buf Pointer to buffer of data * @param len Length of data in buffer */ MimeLinesBody(const MimeHeaderLine& type, const char* buf, int len); /** * Destructor */ virtual ~MimeLinesBody(); /** * RTTI method, get a pointer to a derived class given the class name * @param name Name of the class we are asking for * @return Pointer to the requested class or NULL if this object doesn't implement it */ virtual void* getObject(const String& name) const; /** * Duplicate this MIME body * @return Copy of this MIME body - a new MimeLinesBody */ virtual MimeBody* clone() const; /** * Retrieve the stored lines of text * @return List of Strings */ inline const ObjList& lines() const { return m_lines; } /** * Append a line of text to the data * @param line Text to append */ inline void addLine(const char* line) { m_lines.append(new String(line)); } protected: /** * Copy constructor */ MimeLinesBody(const MimeLinesBody& original); /** * Override that is called internally to build the binary encoded body */ virtual void buildBody() const; private: ObjList m_lines; }; }; // namespace TelEngine #endif /* __YATEMIME_H */ /* vi: set ts=8 sw=4 sts=4 noet: */