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