version 0.1.0
[fms.git] / include / nntp / mime / Mime.h
diff --git a/include/nntp/mime/Mime.h b/include/nntp/mime/Mime.h
new file mode 100644 (file)
index 0000000..a4ca304
--- /dev/null
@@ -0,0 +1,496 @@
+//////////////////////////////////////////////////////////////////////\r
+//\r
+// MIME message encoding/decoding\r
+//\r
+// Jeff Lee\r
+// Dec 11, 2000\r
+//\r
+//////////////////////////////////////////////////////////////////////\r
+\r
+#if !defined(_MIME_H)\r
+#define _MIME_H\r
+\r
+#if _MSC_VER > 1000\r
+#pragma once\r
+#endif // _MSC_VER > 1000\r
+\r
+#include <string>\r
+#include <list>\r
+using namespace std;\r
+\r
+// RFC 1521 - Mechanisms for Specifying and Describing the Format of Internet Message Bodies\r
+// RFC 2045 - Format of Internet Message Bodies\r
+// RFC 2046 - Media Types\r
+// RFC 2047 - Message Header Extensions for Non-ASCII Text\r
+// RFC 2049 - Conformance Criteria and Examples\r
+// RFC 822 - Standard For The Format of ARPA Internet Text Message\r
+\r
+class CMimeConst\r
+{\r
+public:\r
+       // field names\r
+       static inline const char* MimeVersion() { return "MIME-Version"; }\r
+       static inline const char* ContentType() { return "Content-Type"; }\r
+       static inline const char* TransferEncoding() { return "Content-Transfer-Encoding"; }\r
+       static inline const char* ContentID() { return "Content-ID"; }\r
+       static inline const char* ContentDescription() { return "Content-Description"; }\r
+       static inline const char* ContentDisposition() { return "Content-Disposition"; }\r
+\r
+       // parameter names\r
+       static inline const char* Charset() { return "charset"; }\r
+       static inline const char* Name() { return "name"; }\r
+       static inline const char* Filename() { return "filename"; }\r
+       static inline const char* Boundary() { return "boundary"; }\r
+\r
+       // parameter values\r
+       static inline const char* Encoding7Bit() { return "7bit"; }\r
+       static inline const char* Encoding8Bit() { return "8bit"; }\r
+       static inline const char* EncodingBinary() { return "binary"; }\r
+       static inline const char* EncodingQP() { return "quoted-printable"; }\r
+       static inline const char* EncodingBase64() { return "base64"; }\r
+\r
+       static inline const char* MediaText() { return "text"; }\r
+       static inline const char* MediaImage() { return "image"; }\r
+       static inline const char* MediaAudio() { return "audio"; }\r
+       static inline const char* MediaVedio() { return "vedio"; }\r
+       static inline const char* MediaApplication() { return "application"; }\r
+       static inline const char* MediaMultiPart() { return "multipart"; }\r
+       static inline const char* MediaMessage() { return "message"; }\r
+};\r
+\r
+//////////////////////////////////////////////////////////////////////\r
+// CMimeField class - Represents a field of a MIME body part header\r
+//////////////////////////////////////////////////////////////////////\r
+class CMimeField\r
+{\r
+public:\r
+       CMimeField() {}\r
+       ~CMimeField() {}\r
+\r
+public:\r
+       void SetName(const char* pszName);\r
+       const char* GetName() const;\r
+       void SetValue(const char* pszValue);\r
+       const char* GetValue() const;\r
+       void GetValue(string& strValue) const;\r
+       void SetParameter(const char* pszAttr, const char* pszValue);\r
+       bool GetParameter(const char* pszAttr, string& strValue) const;\r
+       void SetCharset(const char* pszCharset);\r
+       const char* GetCharset() const;\r
+\r
+       void Clear();\r
+       int GetLength() const;\r
+       int Store(char* pszData, int nMaxSize) const;\r
+       int Load(const char* pszData, int nDataSize);\r
+\r
+private:\r
+       string m_strName;                               // field name\r
+       string m_strValue;                              // field value\r
+       string m_strCharset;                    // charset for non-ascii text\r
+\r
+private:\r
+       bool FindParameter(const char* pszAttr, int& nPos, int& nSize) const;\r
+};\r
+\r
+inline void CMimeField::SetName(const char* pszName)\r
+{ m_strName = pszName; }\r
+\r
+inline const char* CMimeField::GetName() const\r
+{ return m_strName.data(); }\r
+\r
+inline void CMimeField::SetValue(const char* pszValue)\r
+{ m_strValue = pszValue; }\r
+\r
+inline const char* CMimeField::GetValue() const\r
+{ return m_strValue.data(); }\r
+\r
+inline void CMimeField::SetCharset(const char* pszCharset)\r
+{ m_strCharset = pszCharset; }\r
+\r
+inline const char* CMimeField::GetCharset() const\r
+{ return m_strCharset.c_str(); }\r
+\r
+inline void CMimeField::Clear()\r
+{ m_strName.clear(); m_strValue.clear(); m_strCharset.clear(); }\r
+\r
+//////////////////////////////////////////////////////////////////////\r
+// CMimeHeader class - Represents the header of a MIME body part\r
+//////////////////////////////////////////////////////////////////////\r
+class CMimeHeader\r
+{\r
+public:\r
+       CMimeHeader() {}\r
+       virtual ~CMimeHeader() { Clear(); }\r
+\r
+public:\r
+       enum MediaType\r
+       {\r
+               MEDIA_TEXT, MEDIA_IMAGE, MEDIA_AUDIO, MEDIA_VEDIO, MEDIA_APPLICATION,\r
+               MEDIA_MULTIPART, MEDIA_MESSAGE,\r
+               MEDIA_UNKNOWN\r
+       };\r
+       MediaType GetMediaType() const;\r
+\r
+       // set/get the values of header fields\r
+       void SetField(const CMimeField& field);\r
+       const CMimeField* GetField(const char* pszFieldName) const;\r
+       CMimeField* GetField(const char* pszFieldName);\r
+       void SetFieldValue(const char* pszFieldName, const char* pszFieldValue, const char* pszCharset=NULL);\r
+       const char* GetFieldValue(const char* pszFieldName) const;\r
+       bool SetParameter(const char* pszFieldName, const char* pszAttr, const char* pszValue);\r
+       string GetParameter(const char* pszFieldName, const char* pszAttr) const;\r
+       void SetFieldCharset(const char* pszFieldName, const char* pszCharset);\r
+       const char* GetFieldCharset(const char* pszFieldName) const;\r
+\r
+       // helper functions for standard body part fields\r
+       void SetContentType(const char* pszValue, const char* pszCharset=NULL);\r
+       const char* GetContentType() const;                     // Content-Type: mediatype/subtype\r
+       string GetMainType() const;\r
+       string GetSubType() const;\r
+       void SetCharset(const char* pszCharset);        // Content-Type: text/...; charset=...\r
+       string GetCharset() const;\r
+       void SetName(const char* pszName);                      // Content-Type: image/...; name=...\r
+       string GetName() const;\r
+       void SetBoundary(const char* pszBoundary=NULL); // Content-Type: multipart/...; boundary=...\r
+       string GetBoundary() const;\r
+\r
+       void SetTransferEncoding(const char* pszValue);\r
+       const char* GetTransferEncoding() const;        // Content-Transfer-Encoding: ...\r
+       void SetDisposition(const char* pszValue, const char* pszCharset=NULL);\r
+       const char* GetDisposition() const;                     // Content-Disposition: ...\r
+       string GetFilename() const;                                     // Content-Disposition: ...; filename=...\r
+       void SetDescription(const char* pszValue, const char* pszCharset=NULL);\r
+       const char* GetDescription() const;                     // Content-Description: ...\r
+\r
+       typedef list<CMimeField> CFieldList;\r
+       CFieldList& Fields() { return m_listFields; }\r
+\r
+public:\r
+       // overrides\r
+       virtual void Clear();\r
+       virtual int GetLength() const;\r
+       // serialization\r
+       virtual int Store(char* pszData, int nMaxSize) const;\r
+       virtual int Load(const char* pszData, int nDataSize);\r
+\r
+protected:\r
+       list<CMimeField> m_listFields;  // list of all header fields\r
+       list<CMimeField>::const_iterator FindField(const char* pszFieldName) const;\r
+       list<CMimeField>::iterator FindField(const char* pszFieldName);\r
+\r
+       struct MediaTypeCvt\r
+       {\r
+               int nMediaType;                         // media type\r
+               const char* pszSubType;         // subtype\r
+               const char* pszFileExt;         // file extension name\r
+       };\r
+       static const MediaTypeCvt m_TypeCvtTable[];\r
+       static const char* m_TypeTable[];\r
+\r
+private:\r
+       CMimeHeader& operator=(const CMimeHeader&);             // forbid operator =\r
+};\r
+\r
+// add a new field or update an existing field\r
+inline void CMimeHeader::SetField(const CMimeField& field)\r
+{\r
+       list<CMimeField>::iterator it = FindField(field.GetName());\r
+       if (it != m_listFields.end())\r
+               *it = field;\r
+       else\r
+               m_listFields.push_back(field);\r
+}\r
+\r
+// find a field by name\r
+inline const CMimeField* CMimeHeader::GetField(const char* pszFieldName) const\r
+{\r
+       list<CMimeField>::const_iterator it = FindField(pszFieldName);\r
+       if (it != m_listFields.end())\r
+               return &(*it);\r
+       return NULL;\r
+}\r
+\r
+inline CMimeField* CMimeHeader::GetField(const char* pszFieldName)\r
+{\r
+       list<CMimeField>::iterator it = FindField(pszFieldName);\r
+       if (it != m_listFields.end())\r
+               return &(*it);\r
+       return NULL;\r
+}\r
+\r
+// add a new field or update an existing field\r
+inline void CMimeHeader::SetFieldValue(const char* pszFieldName, const char* pszFieldValue, const char* pszCharset)\r
+{\r
+       CMimeField fd;\r
+       fd.SetName(pszFieldName);\r
+       fd.SetValue(pszFieldValue);\r
+       if (pszCharset != NULL)\r
+               fd.SetCharset(pszCharset);\r
+       SetField(fd);\r
+}\r
+\r
+inline const char* CMimeHeader::GetFieldValue(const char* pszFieldName) const\r
+{\r
+       const CMimeField* pfd = GetField(pszFieldName);\r
+       return pfd != NULL ? pfd->GetValue() : NULL;\r
+}\r
+\r
+inline void CMimeHeader::SetFieldCharset(const char* pszFieldName, const char* pszCharset)\r
+{\r
+       CMimeField *pfd = GetField(pszFieldName);\r
+       if (pfd)\r
+               pfd->SetCharset(pszCharset);\r
+       else\r
+       {\r
+               CMimeField fd;\r
+               fd.SetName(pszFieldName);\r
+               fd.SetCharset(pszCharset);\r
+               SetField(fd);\r
+       }\r
+}\r
+\r
+inline const char* CMimeHeader::GetFieldCharset(const char* pszFieldName) const\r
+{\r
+       const CMimeField* pfd = GetField(pszFieldName);\r
+       return pfd != NULL ? pfd->GetCharset() : NULL;\r
+}\r
+\r
+inline bool CMimeHeader::SetParameter(const char* pszFieldName, const char* pszAttr, const char* pszValue)\r
+{\r
+       CMimeField *pfd = GetField(pszFieldName);\r
+       if (pfd)\r
+       {\r
+               pfd->SetParameter(pszAttr, pszValue);\r
+               return true;\r
+       }\r
+       return false;\r
+}\r
+\r
+inline string CMimeHeader::GetParameter(const char* pszFieldName, const char* pszAttr) const\r
+{\r
+       string strVal;\r
+       const CMimeField *pfd = GetField(pszFieldName);\r
+       if (pfd)\r
+               pfd->GetParameter(pszAttr, strVal);\r
+       return strVal;\r
+}\r
+\r
+inline void CMimeHeader::SetContentType(const char* pszValue, const char* pszCharset)\r
+{ SetFieldValue(CMimeConst::ContentType(), pszValue, pszCharset); }\r
+\r
+inline const char* CMimeHeader::GetContentType() const\r
+{ return GetFieldValue(CMimeConst::ContentType()); }\r
+\r
+inline string CMimeHeader::GetCharset() const\r
+{ return GetParameter(CMimeConst::ContentType(), CMimeConst::Charset()); }\r
+\r
+inline string CMimeHeader::GetName() const\r
+{ return GetParameter(CMimeConst::ContentType(), CMimeConst::Name()); }\r
+\r
+inline string CMimeHeader::GetBoundary() const\r
+{ return GetParameter(CMimeConst::ContentType(), CMimeConst::Boundary()); }\r
+\r
+inline void CMimeHeader::SetTransferEncoding(const char* pszValue)\r
+{ SetFieldValue(CMimeConst::TransferEncoding(), pszValue); }\r
+\r
+inline const char* CMimeHeader::GetTransferEncoding() const\r
+{ return GetFieldValue(CMimeConst::TransferEncoding()); }\r
+\r
+// Content-Disposition header field specifies how to present this body part. it could be\r
+// inline or attachment. 'inline' indicates this body part should be displayed in the main body;\r
+// 'attachment' indicates it is separate from the main body. (RFC 2183)\r
+\r
+inline void CMimeHeader::SetDisposition(const char* pszValue, const char* pszCharset)\r
+{ SetFieldValue(CMimeConst::ContentDisposition(), pszValue, pszCharset); }\r
+\r
+inline const char* CMimeHeader::GetDisposition() const\r
+{ return GetFieldValue(CMimeConst::ContentDisposition()); }\r
+\r
+inline string CMimeHeader::GetFilename() const\r
+{ return GetParameter(CMimeConst::ContentDisposition(), CMimeConst::Filename()); }\r
+\r
+inline void CMimeHeader::SetDescription(const char* pszValue, const char* pszCharset)\r
+{ SetFieldValue(CMimeConst::ContentDescription(), pszValue, pszCharset); }\r
+\r
+inline const char* CMimeHeader::GetDescription() const\r
+{ return GetFieldValue(CMimeConst::ContentDescription()); }\r
+\r
+//////////////////////////////////////////////////////////////////////\r
+// CMimeBody class - Represents a body part in a MIME message\r
+//////////////////////////////////////////////////////////////////////\r
+class CMimeMessage;\r
+class CMimeBody : public CMimeHeader\r
+{\r
+protected:\r
+       CMimeBody() :                           // instantiate a CMimeBody object explicitly is not allowed. call CreatePart()\r
+               m_pbText(NULL),\r
+               m_nTextSize(0) {}\r
+       virtual ~CMimeBody() { Clear(); }\r
+\r
+public:\r
+       int GetContentLength() const;\r
+       const unsigned char* GetContent() const;\r
+\r
+       // operations for 'text' or 'message' media\r
+       bool IsText() const;\r
+       int SetText(const char* pbText, int nLength=0);\r
+       int GetText(char* pbText, int nMaxSize);\r
+       int GetText(string& strText);\r
+\r
+       // operations for 'message' media\r
+       bool IsMessage() const;\r
+       bool SetMessage(const CMimeMessage* pMM);\r
+       void GetMessage(CMimeMessage* pMM) const;\r
+\r
+       // operations for 'image/audio/vedio/application' (attachment) media\r
+       bool IsAttachment() const;\r
+       bool ReadFromFile(const char* pszFilename);\r
+       bool WriteToFile(const char* pszFilename);\r
+\r
+       // operations for 'multipart' media\r
+       bool IsMultiPart() const;\r
+       void DeleteAll();\r
+       CMimeBody* CreatePart(const char* pszMediaType=NULL, CMimeBody* pWhere=NULL);\r
+       void ErasePart(CMimeBody* pBP);\r
+       CMimeBody* FindFirstPart();\r
+       CMimeBody* FindNextPart();\r
+\r
+       typedef list<CMimeBody*> CBodyList;\r
+       int GetBodyPartList(CBodyList& rList) const;\r
+       int GetAttachmentList(CBodyList& rList) const;\r
+\r
+public:\r
+       // overrides\r
+       virtual void Clear();\r
+       virtual int GetLength() const;\r
+       // serialization\r
+       virtual int Store(char* pszData, int nMaxSize) const;\r
+       virtual void Store(std::string &str) const;\r
+       virtual int Load(const char* pszData, int nDataSize);\r
+\r
+protected:\r
+       unsigned char* m_pbText;                // content (text) of the body part\r
+       int m_nTextSize;                                // length of content\r
+       CBodyList m_listBodies;                 // list of all child body parts\r
+       CBodyList::iterator m_itFind;\r
+\r
+protected:\r
+       bool AllocateBuffer(int nBufSize);\r
+       void FreeBuffer();\r
+\r
+       friend class CMimeEnvironment;\r
+};\r
+\r
+inline int CMimeBody::GetContentLength() const\r
+{ return m_nTextSize; }\r
+\r
+inline const unsigned char* CMimeBody::GetContent() const\r
+{ return m_pbText; }\r
+\r
+inline bool CMimeBody::IsText() const\r
+{ return GetMediaType() == MEDIA_TEXT; }\r
+\r
+inline bool CMimeBody::IsMessage() const\r
+{ return GetMediaType() == MEDIA_MESSAGE; }\r
+\r
+inline bool CMimeBody::IsAttachment() const\r
+{ return GetName().size() > 0; }\r
+\r
+inline bool CMimeBody::IsMultiPart() const\r
+{ return GetMediaType() == MEDIA_MULTIPART; }\r
+\r
+inline CMimeBody* CMimeBody::FindFirstPart()\r
+{\r
+       m_itFind = m_listBodies.begin();\r
+       return FindNextPart();\r
+}\r
+\r
+inline CMimeBody* CMimeBody::FindNextPart()\r
+{\r
+       if (m_itFind != m_listBodies.end())\r
+               return *m_itFind++;\r
+       return NULL;\r
+}\r
+\r
+inline bool CMimeBody::AllocateBuffer(int nBufSize)\r
+{\r
+       FreeBuffer();\r
+       m_pbText = new unsigned char[nBufSize];\r
+       if (!m_pbText)  return false;\r
+       m_nTextSize = nBufSize;\r
+       return true;\r
+}\r
+\r
+inline void CMimeBody::FreeBuffer()\r
+{\r
+       delete []m_pbText;\r
+       m_pbText = NULL;\r
+       m_nTextSize = 0;\r
+}\r
+\r
+//////////////////////////////////////////////////////////////////////\r
+// CMimeMessage - Represents a MIME message\r
+//////////////////////////////////////////////////////////////////////\r
+class CMimeMessage : public CMimeBody\r
+{\r
+public:\r
+       CMimeMessage() { /*SetVersion();*/ }\r
+       virtual ~CMimeMessage() { Clear(); }\r
+\r
+public:\r
+       // set/get RFC 822 message header fields\r
+       void SetFrom(const char* pszFrom, const char* pszCharset=NULL);\r
+       const char* GetFrom() const;\r
+       void SetTo(const char* pszTo, const char* pszCharset=NULL);\r
+       const char* GetTo() const;\r
+       void SetCc(const char* pszCc, const char* pszCharset=NULL);\r
+       const char* GetCc() const;\r
+       void SetBcc(const char* pszBcc, const char* pszCharset=NULL);\r
+       const char* GetBcc() const;\r
+\r
+       void SetSubject(const char* pszSubject, const char* pszCharset=NULL);\r
+       const char* GetSubject() const;\r
+\r
+       void SetDate();\r
+       void SetDate(int nYear, int nMonth, int nDay, int nHour, int nMinute, int nSecond);\r
+       const char* GetDate() const;\r
+       void SetVersion();\r
+};\r
+\r
+inline void CMimeMessage::SetFrom(const char* pszAddr, const char* pszCharset)\r
+{ SetFieldValue("From", pszAddr, pszCharset); }\r
+\r
+inline const char* CMimeMessage::GetFrom() const\r
+{ return GetFieldValue("From"); }\r
+\r
+inline void CMimeMessage::SetTo(const char* pszAddr, const char* pszCharset)\r
+{ SetFieldValue("To", pszAddr, pszCharset); }\r
+\r
+inline const char* CMimeMessage::GetTo() const\r
+{ return GetFieldValue("To"); }\r
+\r
+inline void CMimeMessage::SetCc(const char* pszAddr, const char* pszCharset)\r
+{ SetFieldValue("CC", pszAddr, pszCharset); }\r
+\r
+inline const char* CMimeMessage::GetCc() const\r
+{ return GetFieldValue("CC"); }\r
+\r
+inline void CMimeMessage::SetBcc(const char* pszAddr, const char* pszCharset)\r
+{ SetFieldValue("BCC", pszAddr, pszCharset); }\r
+\r
+inline const char* CMimeMessage::GetBcc() const\r
+{ return GetFieldValue("BCC"); }\r
+\r
+inline void CMimeMessage::SetSubject(const char* pszSubject, const char* pszCharset)\r
+{ SetFieldValue("Subject", pszSubject, pszCharset); }\r
+\r
+inline const char* CMimeMessage::GetSubject() const\r
+{ return GetFieldValue("Subject"); }\r
+\r
+inline const char* CMimeMessage::GetDate() const\r
+{ return GetFieldValue("Date"); }\r
+\r
+inline void CMimeMessage::SetVersion()\r
+{ SetFieldValue(CMimeConst::MimeVersion(), "1.0"); }\r
+\r
+#endif // !defined(_MIME_H)\r