version 0.1.0
[fms.git] / include / nntp / mime / MimeCode.h
1 //////////////////////////////////////////////////////////////////////\r
2 //\r
3 // MIME Encoding/Decoding:\r
4 //      Quoted-printable and Base64 for content encoding;\r
5 //      Encoded-word for header field encoding.\r
6 //\r
7 // Jeff Lee\r
8 // Dec 11, 2000\r
9 //\r
10 //////////////////////////////////////////////////////////////////////\r
11 \r
12 #if !defined(_MIME_CODING_H)\r
13 #define _MIME_CODING_H\r
14 \r
15 #if _MSC_VER > 1000\r
16 #pragma once\r
17 #endif // _MSC_VER > 1000\r
18 \r
19 #include <utility>\r
20 #include <string>\r
21 #include <list>\r
22 using namespace std;\r
23 \r
24 #pragma warning(disable:4786)   // identifier was truncated to 'number' characters in the debug information\r
25 \r
26 #if !defined(ASSERT)\r
27 #if defined(_DEBUG)\r
28         #include <assert.h>\r
29         #define ASSERT(exp)     assert(exp)\r
30 #else\r
31         #define ASSERT(exp)     ((void)0)\r
32 #endif\r
33 #endif\r
34 \r
35 #if defined(_DEBUG) && !defined(DEBUG_NEW)\r
36 #define DEBUG_NEW new\r
37 #endif\r
38 \r
39 // maximum length of an encoded line (RFC 2045)\r
40 #define MAX_MIME_LINE_LEN       76\r
41 #define MAX_ENCODEDWORD_LEN     75\r
42 \r
43 //////////////////////////////////////////////////////////////////////\r
44 // CMimeEnvironment - global environment to manage encoding/decoding\r
45 //////////////////////////////////////////////////////////////////////\r
46 class CMimeCodeBase;\r
47 class CFieldCodeBase;\r
48 class CMimeBody;\r
49 class CMimeEnvironment\r
50 {\r
51 public:\r
52         CMimeEnvironment();\r
53 \r
54 public:\r
55         // global options\r
56         static void SetAutoFolding(bool bAutoFolding=true);\r
57         static bool AutoFolding() { return m_bAutoFolding; }\r
58         static void SetGlobalCharset(const char* pszCharset) { m_strCharset = pszCharset; }\r
59         static const char* GetGlobalCharset() { return m_strCharset.c_str(); }\r
60 \r
61         // Content-Transfer-Encoding coder management\r
62         typedef CMimeCodeBase* (*CODER_FACTORY)();\r
63         static void RegisterCoder(const char* pszCodingName, CODER_FACTORY pfnCreateObject=NULL);\r
64         static CMimeCodeBase* CreateCoder(const char* pszCodingName);\r
65 \r
66         // header fields encoding/folding management\r
67         typedef CFieldCodeBase* (*FIELD_CODER_FACTORY)();\r
68         static void RegisterFieldCoder(const char* pszFieldName, FIELD_CODER_FACTORY pfnCreateObject=NULL);\r
69         static CFieldCodeBase* CreateFieldCoder(const char* pszFieldName);\r
70 \r
71         // media type management\r
72         typedef CMimeBody* (*BODY_PART_FACTORY)();\r
73         static void RegisterMediaType(const char* pszMediaType, BODY_PART_FACTORY pfnCreateObject=NULL);\r
74         static CMimeBody* CreateBodyPart(const char* pszMediaType);\r
75 \r
76 private:\r
77         static bool m_bAutoFolding;\r
78         static string m_strCharset;\r
79 \r
80         typedef pair<const char*, CODER_FACTORY> CODER_PAIR;\r
81         static list<CODER_PAIR> m_listCoders;\r
82 \r
83         typedef pair<const char*, FIELD_CODER_FACTORY> FIELD_CODER_PAIR;\r
84         static list<FIELD_CODER_PAIR> m_listFieldCoders;\r
85 \r
86         typedef pair<const char*, BODY_PART_FACTORY> MEDIA_TYPE_PAIR;\r
87         static list<MEDIA_TYPE_PAIR> m_listMediaTypes;\r
88 \r
89         static CMimeEnvironment m_globalMgr;\r
90 };\r
91 \r
92 #define DECLARE_MIMECODER(class_name) \\r
93         public: static CMimeCodeBase* CreateObject() { return new class_name; }\r
94 \r
95 #define REGISTER_MIMECODER(coding_name, class_name) \\r
96         CMimeEnvironment::RegisterCoder(coding_name, class_name::CreateObject)\r
97 \r
98 #define DEREGISTER_MIMECODER(coding_name) \\r
99         CMimeEnvironment::RegisterCoder(coding_name, 0)\r
100 \r
101 #define DECLARE_FIELDCODER(class_name) \\r
102         public: static CFieldCodeBase* CreateObject() { return new class_name; }\r
103 \r
104 #define REGISTER_FIELDCODER(field_name, class_name) \\r
105         CMimeEnvironment::RegisterFieldCoder(field_name, class_name::CreateObject)\r
106 \r
107 #define DEREGISTER_FIELDCODER(field_name) \\r
108         CMimeEnvironment::RegisterFieldCoder(field_name, 0)\r
109 \r
110 #define DECLARE_MEDIATYPE(class_name) \\r
111         public: static CMimeBody* CreateObject() { return new class_name; }\r
112 \r
113 #define REGISTER_MEDIATYPE(media_type, class_name) \\r
114         CMimeEnvironment::RegisterMediaType(media_type, class_name::CreateObject)\r
115 \r
116 #define DEREGISTER_MEDIATYPE(media_type) \\r
117         CMimeEnvironment::RegisterMediaType(media_type, 0)\r
118 \r
119 //////////////////////////////////////////////////////////////////////\r
120 // CMimeCodeBase - base class for MIME encoding/decoding\r
121 // default implementation for binary/unknown encoding mechanism\r
122 //////////////////////////////////////////////////////////////////////\r
123 class CMimeCodeBase\r
124 {\r
125 public:\r
126         CMimeCodeBase() :\r
127                 m_pbInput(NULL),\r
128                 m_nInputSize(0),\r
129                 m_bIsEncoding(false) {}\r
130 \r
131 public:\r
132         void SetInput(const char* pbInput, int nInputSize, bool bEncoding)\r
133         {\r
134                 m_pbInput = (const unsigned char*) pbInput;\r
135                 m_nInputSize = nInputSize;\r
136                 m_bIsEncoding = bEncoding;\r
137         }\r
138         int GetOutputLength() const\r
139         {\r
140                 return m_bIsEncoding ? GetEncodeLength() : GetDecodeLength();\r
141         }\r
142         int GetOutput(unsigned char* pbOutput, int nMaxSize)\r
143         {\r
144                 return m_bIsEncoding ? Encode(pbOutput, nMaxSize) : Decode(pbOutput, nMaxSize);\r
145         }\r
146 \r
147 protected:\r
148         // overrides\r
149         virtual int GetEncodeLength() const { return m_nInputSize; }\r
150         virtual int GetDecodeLength() const { return m_nInputSize; }\r
151         virtual int Encode(unsigned char* pbOutput, int nMaxSize) const\r
152         {\r
153                 int nSize = min(nMaxSize, m_nInputSize);\r
154                 ::memcpy(pbOutput, m_pbInput, nSize);\r
155                 return nSize;\r
156         }\r
157         virtual int Decode(unsigned char* pbOutput, int nMaxSize)\r
158         {\r
159                 return CMimeCodeBase::Encode(pbOutput, nMaxSize);\r
160         }\r
161 \r
162 protected:\r
163         const unsigned char* m_pbInput;\r
164         int m_nInputSize;\r
165         bool m_bIsEncoding;\r
166 };\r
167 \r
168 //////////////////////////////////////////////////////////////////////\r
169 // CMimeCode7bit - for 7bit/8bit encoding mechanism (fold long line)\r
170 //////////////////////////////////////////////////////////////////////\r
171 class CMimeCode7bit : public CMimeCodeBase\r
172 {\r
173         DECLARE_MIMECODER(CMimeCode7bit)\r
174 \r
175 protected:\r
176         virtual int GetEncodeLength() const;\r
177         virtual int Encode(unsigned char* pbOutput, int nMaxSize) const;\r
178 };\r
179 \r
180 //////////////////////////////////////////////////////////////////////\r
181 // CMimeCodeQP - for quoted-printable encoding mechanism\r
182 //////////////////////////////////////////////////////////////////////\r
183 class CMimeCodeQP : public CMimeCodeBase\r
184 {\r
185 public:\r
186         CMimeCodeQP() :\r
187                 m_bQuoteLineBreak(false) {}\r
188 \r
189 public:\r
190         DECLARE_MIMECODER(CMimeCodeQP)\r
191         void QuoteLineBreak(bool bQuote=true) { m_bQuoteLineBreak = bQuote; }\r
192 \r
193 protected:\r
194         virtual int GetEncodeLength() const;\r
195         //virtual int GetDecodeLength() const;\r
196         virtual int Encode(unsigned char* pbOutput, int nMaxSize) const;\r
197         virtual int Decode(unsigned char* pbOutput, int nMaxSize);\r
198 \r
199 private:\r
200         bool m_bQuoteLineBreak;\r
201 };\r
202 \r
203 //////////////////////////////////////////////////////////////////////\r
204 // CMimeCodeBase64 - for base64 encoding mechanism\r
205 //////////////////////////////////////////////////////////////////////\r
206 class CMimeCodeBase64 : public CMimeCodeBase\r
207 {\r
208 public:\r
209         CMimeCodeBase64() :\r
210                 m_bAddLineBreak(true) {}\r
211 \r
212 public:\r
213         DECLARE_MIMECODER(CMimeCodeBase64)\r
214         void AddLineBreak(bool bAdd=true) { m_bAddLineBreak = bAdd; }\r
215 \r
216 protected:\r
217         virtual int GetEncodeLength() const;\r
218         virtual int GetDecodeLength() const;\r
219         virtual int Encode(unsigned char* pbOutput, int nMaxSize) const;\r
220         virtual int Decode(unsigned char* pbOutput, int nMaxSize);\r
221 \r
222 private:\r
223         bool m_bAddLineBreak;\r
224 \r
225 private:\r
226         static inline int DecodeBase64Char(unsigned int nCode)\r
227         {\r
228                 if (nCode >= 'A' && nCode <= 'Z')\r
229                         return nCode - 'A';\r
230                 if (nCode >= 'a' && nCode <= 'z')\r
231                         return nCode - 'a' + 26;\r
232                 if (nCode >= '0' && nCode <= '9')\r
233                         return nCode - '0' + 52;\r
234                 if (nCode == '+')\r
235                         return 62;\r
236                 if (nCode == '/')\r
237                         return 63;\r
238                 return 64;\r
239         }\r
240 };\r
241 \r
242 //////////////////////////////////////////////////////////////////////\r
243 // CMimeEncodedWord - encoded word for non-ascii text (RFC 2047)\r
244 //////////////////////////////////////////////////////////////////////\r
245 class CMimeEncodedWord : public CMimeCodeBase\r
246 {\r
247 public:\r
248         CMimeEncodedWord() :\r
249                 m_nEncoding(0) {}\r
250 \r
251         void SetEncoding(int nEncoding, const char* pszCharset)\r
252         {\r
253                 m_nEncoding = nEncoding;\r
254                 m_strCharset = pszCharset;\r
255         }\r
256         int GetEncoding() const { return m_nEncoding; }\r
257         const char* GetCharset() const { return m_strCharset.c_str(); }\r
258 \r
259 protected:\r
260         virtual int GetEncodeLength() const;\r
261         virtual int Encode(unsigned char* pbOutput, int nMaxSize) const;\r
262         virtual int Decode(unsigned char* pbOutput, int nMaxSize);\r
263 \r
264 private:\r
265         int m_nEncoding;\r
266         string m_strCharset;\r
267 \r
268         int BEncode(unsigned char* pbOutput, int nMaxSize) const;\r
269         int QEncode(unsigned char* pbOutput, int nMaxSize) const;\r
270 };\r
271 \r
272 //////////////////////////////////////////////////////////////////////\r
273 // CFieldCodeBase - base class to encode/decode header fields\r
274 // default coder for any unregistered fields\r
275 //////////////////////////////////////////////////////////////////////\r
276 class CFieldCodeBase : public CMimeCodeBase\r
277 {\r
278 public:\r
279         void SetCharset(const char* pszCharset) { m_strCharset = pszCharset; }\r
280         const char* GetCharset() const { return m_strCharset.c_str(); }\r
281 \r
282 protected:\r
283         string m_strCharset;\r
284 \r
285         virtual bool IsFoldingChar(char /*ch*/) const { return false; }\r
286         virtual int GetDelimeter() const { return 0; }\r
287         int FindSymbol(const char* pszData, int nSize, int& nDelimeter, int& nNonAscChars) const;\r
288         void UnfoldField(string& strField) const;\r
289         int SelectEncoding(int nLength, int nNonAsciiChars) const\r
290         {\r
291                 int nQEncodeSize = nLength + nNonAsciiChars * 2;\r
292                 int nBEncodeSize = (nLength + 2) / 3 * 4;\r
293                 return (nQEncodeSize <= nBEncodeSize || nNonAsciiChars*5 <= nLength) ? 'Q' : 'B';\r
294         }\r
295 \r
296 protected:\r
297         virtual int GetEncodeLength() const;\r
298         virtual int Encode(unsigned char* pbOutput, int nMaxSize) const;\r
299         virtual int Decode(unsigned char* pbOutput, int nMaxSize);\r
300 };\r
301 \r
302 //////////////////////////////////////////////////////////////////////\r
303 // CFieldCodeText - encode/decode header fields defined as *text\r
304 //////////////////////////////////////////////////////////////////////\r
305 class CFieldCodeText : public CFieldCodeBase\r
306 {\r
307         DECLARE_FIELDCODER(CFieldCodeText)\r
308 \r
309 protected:\r
310         virtual int GetDelimeter() const { return 0xff; }\r
311 };\r
312 \r
313 //////////////////////////////////////////////////////////////////////\r
314 // CFieldCodeAddress - encode/decode header fields defined as address\r
315 //////////////////////////////////////////////////////////////////////\r
316 class CFieldCodeAddress : public CFieldCodeBase\r
317 {\r
318         DECLARE_FIELDCODER(CFieldCodeAddress)\r
319 \r
320 protected:\r
321         virtual bool IsFoldingChar(char ch) const { return ch == ',' || ch == ':'; }\r
322 };\r
323 \r
324 //////////////////////////////////////////////////////////////////////\r
325 // CFieldCodeParameter - encode/decode header fields have parameters\r
326 //////////////////////////////////////////////////////////////////////\r
327 class CFieldCodeParameter : public CFieldCodeBase\r
328 {\r
329         DECLARE_FIELDCODER(CFieldCodeParameter)\r
330 \r
331 protected:\r
332         virtual bool IsFoldingChar(char ch) const { return ch == ';'; }\r
333 };\r
334 \r
335 #endif // !defined(_MIME_CODING_H)\r