version 0.1.0
[fms.git] / include / nntp / mime / Mime.h
1 //////////////////////////////////////////////////////////////////////\r
2 //\r
3 // MIME message encoding/decoding\r
4 //\r
5 // Jeff Lee\r
6 // Dec 11, 2000\r
7 //\r
8 //////////////////////////////////////////////////////////////////////\r
9 \r
10 #if !defined(_MIME_H)\r
11 #define _MIME_H\r
12 \r
13 #if _MSC_VER > 1000\r
14 #pragma once\r
15 #endif // _MSC_VER > 1000\r
16 \r
17 #include <string>\r
18 #include <list>\r
19 using namespace std;\r
20 \r
21 // RFC 1521 - Mechanisms for Specifying and Describing the Format of Internet Message Bodies\r
22 // RFC 2045 - Format of Internet Message Bodies\r
23 // RFC 2046 - Media Types\r
24 // RFC 2047 - Message Header Extensions for Non-ASCII Text\r
25 // RFC 2049 - Conformance Criteria and Examples\r
26 // RFC 822 - Standard For The Format of ARPA Internet Text Message\r
27 \r
28 class CMimeConst\r
29 {\r
30 public:\r
31         // field names\r
32         static inline const char* MimeVersion() { return "MIME-Version"; }\r
33         static inline const char* ContentType() { return "Content-Type"; }\r
34         static inline const char* TransferEncoding() { return "Content-Transfer-Encoding"; }\r
35         static inline const char* ContentID() { return "Content-ID"; }\r
36         static inline const char* ContentDescription() { return "Content-Description"; }\r
37         static inline const char* ContentDisposition() { return "Content-Disposition"; }\r
38 \r
39         // parameter names\r
40         static inline const char* Charset() { return "charset"; }\r
41         static inline const char* Name() { return "name"; }\r
42         static inline const char* Filename() { return "filename"; }\r
43         static inline const char* Boundary() { return "boundary"; }\r
44 \r
45         // parameter values\r
46         static inline const char* Encoding7Bit() { return "7bit"; }\r
47         static inline const char* Encoding8Bit() { return "8bit"; }\r
48         static inline const char* EncodingBinary() { return "binary"; }\r
49         static inline const char* EncodingQP() { return "quoted-printable"; }\r
50         static inline const char* EncodingBase64() { return "base64"; }\r
51 \r
52         static inline const char* MediaText() { return "text"; }\r
53         static inline const char* MediaImage() { return "image"; }\r
54         static inline const char* MediaAudio() { return "audio"; }\r
55         static inline const char* MediaVedio() { return "vedio"; }\r
56         static inline const char* MediaApplication() { return "application"; }\r
57         static inline const char* MediaMultiPart() { return "multipart"; }\r
58         static inline const char* MediaMessage() { return "message"; }\r
59 };\r
60 \r
61 //////////////////////////////////////////////////////////////////////\r
62 // CMimeField class - Represents a field of a MIME body part header\r
63 //////////////////////////////////////////////////////////////////////\r
64 class CMimeField\r
65 {\r
66 public:\r
67         CMimeField() {}\r
68         ~CMimeField() {}\r
69 \r
70 public:\r
71         void SetName(const char* pszName);\r
72         const char* GetName() const;\r
73         void SetValue(const char* pszValue);\r
74         const char* GetValue() const;\r
75         void GetValue(string& strValue) const;\r
76         void SetParameter(const char* pszAttr, const char* pszValue);\r
77         bool GetParameter(const char* pszAttr, string& strValue) const;\r
78         void SetCharset(const char* pszCharset);\r
79         const char* GetCharset() const;\r
80 \r
81         void Clear();\r
82         int GetLength() const;\r
83         int Store(char* pszData, int nMaxSize) const;\r
84         int Load(const char* pszData, int nDataSize);\r
85 \r
86 private:\r
87         string m_strName;                               // field name\r
88         string m_strValue;                              // field value\r
89         string m_strCharset;                    // charset for non-ascii text\r
90 \r
91 private:\r
92         bool FindParameter(const char* pszAttr, int& nPos, int& nSize) const;\r
93 };\r
94 \r
95 inline void CMimeField::SetName(const char* pszName)\r
96 { m_strName = pszName; }\r
97 \r
98 inline const char* CMimeField::GetName() const\r
99 { return m_strName.data(); }\r
100 \r
101 inline void CMimeField::SetValue(const char* pszValue)\r
102 { m_strValue = pszValue; }\r
103 \r
104 inline const char* CMimeField::GetValue() const\r
105 { return m_strValue.data(); }\r
106 \r
107 inline void CMimeField::SetCharset(const char* pszCharset)\r
108 { m_strCharset = pszCharset; }\r
109 \r
110 inline const char* CMimeField::GetCharset() const\r
111 { return m_strCharset.c_str(); }\r
112 \r
113 inline void CMimeField::Clear()\r
114 { m_strName.clear(); m_strValue.clear(); m_strCharset.clear(); }\r
115 \r
116 //////////////////////////////////////////////////////////////////////\r
117 // CMimeHeader class - Represents the header of a MIME body part\r
118 //////////////////////////////////////////////////////////////////////\r
119 class CMimeHeader\r
120 {\r
121 public:\r
122         CMimeHeader() {}\r
123         virtual ~CMimeHeader() { Clear(); }\r
124 \r
125 public:\r
126         enum MediaType\r
127         {\r
128                 MEDIA_TEXT, MEDIA_IMAGE, MEDIA_AUDIO, MEDIA_VEDIO, MEDIA_APPLICATION,\r
129                 MEDIA_MULTIPART, MEDIA_MESSAGE,\r
130                 MEDIA_UNKNOWN\r
131         };\r
132         MediaType GetMediaType() const;\r
133 \r
134         // set/get the values of header fields\r
135         void SetField(const CMimeField& field);\r
136         const CMimeField* GetField(const char* pszFieldName) const;\r
137         CMimeField* GetField(const char* pszFieldName);\r
138         void SetFieldValue(const char* pszFieldName, const char* pszFieldValue, const char* pszCharset=NULL);\r
139         const char* GetFieldValue(const char* pszFieldName) const;\r
140         bool SetParameter(const char* pszFieldName, const char* pszAttr, const char* pszValue);\r
141         string GetParameter(const char* pszFieldName, const char* pszAttr) const;\r
142         void SetFieldCharset(const char* pszFieldName, const char* pszCharset);\r
143         const char* GetFieldCharset(const char* pszFieldName) const;\r
144 \r
145         // helper functions for standard body part fields\r
146         void SetContentType(const char* pszValue, const char* pszCharset=NULL);\r
147         const char* GetContentType() const;                     // Content-Type: mediatype/subtype\r
148         string GetMainType() const;\r
149         string GetSubType() const;\r
150         void SetCharset(const char* pszCharset);        // Content-Type: text/...; charset=...\r
151         string GetCharset() const;\r
152         void SetName(const char* pszName);                      // Content-Type: image/...; name=...\r
153         string GetName() const;\r
154         void SetBoundary(const char* pszBoundary=NULL); // Content-Type: multipart/...; boundary=...\r
155         string GetBoundary() const;\r
156 \r
157         void SetTransferEncoding(const char* pszValue);\r
158         const char* GetTransferEncoding() const;        // Content-Transfer-Encoding: ...\r
159         void SetDisposition(const char* pszValue, const char* pszCharset=NULL);\r
160         const char* GetDisposition() const;                     // Content-Disposition: ...\r
161         string GetFilename() const;                                     // Content-Disposition: ...; filename=...\r
162         void SetDescription(const char* pszValue, const char* pszCharset=NULL);\r
163         const char* GetDescription() const;                     // Content-Description: ...\r
164 \r
165         typedef list<CMimeField> CFieldList;\r
166         CFieldList& Fields() { return m_listFields; }\r
167 \r
168 public:\r
169         // overrides\r
170         virtual void Clear();\r
171         virtual int GetLength() const;\r
172         // serialization\r
173         virtual int Store(char* pszData, int nMaxSize) const;\r
174         virtual int Load(const char* pszData, int nDataSize);\r
175 \r
176 protected:\r
177         list<CMimeField> m_listFields;  // list of all header fields\r
178         list<CMimeField>::const_iterator FindField(const char* pszFieldName) const;\r
179         list<CMimeField>::iterator FindField(const char* pszFieldName);\r
180 \r
181         struct MediaTypeCvt\r
182         {\r
183                 int nMediaType;                         // media type\r
184                 const char* pszSubType;         // subtype\r
185                 const char* pszFileExt;         // file extension name\r
186         };\r
187         static const MediaTypeCvt m_TypeCvtTable[];\r
188         static const char* m_TypeTable[];\r
189 \r
190 private:\r
191         CMimeHeader& operator=(const CMimeHeader&);             // forbid operator =\r
192 };\r
193 \r
194 // add a new field or update an existing field\r
195 inline void CMimeHeader::SetField(const CMimeField& field)\r
196 {\r
197         list<CMimeField>::iterator it = FindField(field.GetName());\r
198         if (it != m_listFields.end())\r
199                 *it = field;\r
200         else\r
201                 m_listFields.push_back(field);\r
202 }\r
203 \r
204 // find a field by name\r
205 inline const CMimeField* CMimeHeader::GetField(const char* pszFieldName) const\r
206 {\r
207         list<CMimeField>::const_iterator it = FindField(pszFieldName);\r
208         if (it != m_listFields.end())\r
209                 return &(*it);\r
210         return NULL;\r
211 }\r
212 \r
213 inline CMimeField* CMimeHeader::GetField(const char* pszFieldName)\r
214 {\r
215         list<CMimeField>::iterator it = FindField(pszFieldName);\r
216         if (it != m_listFields.end())\r
217                 return &(*it);\r
218         return NULL;\r
219 }\r
220 \r
221 // add a new field or update an existing field\r
222 inline void CMimeHeader::SetFieldValue(const char* pszFieldName, const char* pszFieldValue, const char* pszCharset)\r
223 {\r
224         CMimeField fd;\r
225         fd.SetName(pszFieldName);\r
226         fd.SetValue(pszFieldValue);\r
227         if (pszCharset != NULL)\r
228                 fd.SetCharset(pszCharset);\r
229         SetField(fd);\r
230 }\r
231 \r
232 inline const char* CMimeHeader::GetFieldValue(const char* pszFieldName) const\r
233 {\r
234         const CMimeField* pfd = GetField(pszFieldName);\r
235         return pfd != NULL ? pfd->GetValue() : NULL;\r
236 }\r
237 \r
238 inline void CMimeHeader::SetFieldCharset(const char* pszFieldName, const char* pszCharset)\r
239 {\r
240         CMimeField *pfd = GetField(pszFieldName);\r
241         if (pfd)\r
242                 pfd->SetCharset(pszCharset);\r
243         else\r
244         {\r
245                 CMimeField fd;\r
246                 fd.SetName(pszFieldName);\r
247                 fd.SetCharset(pszCharset);\r
248                 SetField(fd);\r
249         }\r
250 }\r
251 \r
252 inline const char* CMimeHeader::GetFieldCharset(const char* pszFieldName) const\r
253 {\r
254         const CMimeField* pfd = GetField(pszFieldName);\r
255         return pfd != NULL ? pfd->GetCharset() : NULL;\r
256 }\r
257 \r
258 inline bool CMimeHeader::SetParameter(const char* pszFieldName, const char* pszAttr, const char* pszValue)\r
259 {\r
260         CMimeField *pfd = GetField(pszFieldName);\r
261         if (pfd)\r
262         {\r
263                 pfd->SetParameter(pszAttr, pszValue);\r
264                 return true;\r
265         }\r
266         return false;\r
267 }\r
268 \r
269 inline string CMimeHeader::GetParameter(const char* pszFieldName, const char* pszAttr) const\r
270 {\r
271         string strVal;\r
272         const CMimeField *pfd = GetField(pszFieldName);\r
273         if (pfd)\r
274                 pfd->GetParameter(pszAttr, strVal);\r
275         return strVal;\r
276 }\r
277 \r
278 inline void CMimeHeader::SetContentType(const char* pszValue, const char* pszCharset)\r
279 { SetFieldValue(CMimeConst::ContentType(), pszValue, pszCharset); }\r
280 \r
281 inline const char* CMimeHeader::GetContentType() const\r
282 { return GetFieldValue(CMimeConst::ContentType()); }\r
283 \r
284 inline string CMimeHeader::GetCharset() const\r
285 { return GetParameter(CMimeConst::ContentType(), CMimeConst::Charset()); }\r
286 \r
287 inline string CMimeHeader::GetName() const\r
288 { return GetParameter(CMimeConst::ContentType(), CMimeConst::Name()); }\r
289 \r
290 inline string CMimeHeader::GetBoundary() const\r
291 { return GetParameter(CMimeConst::ContentType(), CMimeConst::Boundary()); }\r
292 \r
293 inline void CMimeHeader::SetTransferEncoding(const char* pszValue)\r
294 { SetFieldValue(CMimeConst::TransferEncoding(), pszValue); }\r
295 \r
296 inline const char* CMimeHeader::GetTransferEncoding() const\r
297 { return GetFieldValue(CMimeConst::TransferEncoding()); }\r
298 \r
299 // Content-Disposition header field specifies how to present this body part. it could be\r
300 // inline or attachment. 'inline' indicates this body part should be displayed in the main body;\r
301 // 'attachment' indicates it is separate from the main body. (RFC 2183)\r
302 \r
303 inline void CMimeHeader::SetDisposition(const char* pszValue, const char* pszCharset)\r
304 { SetFieldValue(CMimeConst::ContentDisposition(), pszValue, pszCharset); }\r
305 \r
306 inline const char* CMimeHeader::GetDisposition() const\r
307 { return GetFieldValue(CMimeConst::ContentDisposition()); }\r
308 \r
309 inline string CMimeHeader::GetFilename() const\r
310 { return GetParameter(CMimeConst::ContentDisposition(), CMimeConst::Filename()); }\r
311 \r
312 inline void CMimeHeader::SetDescription(const char* pszValue, const char* pszCharset)\r
313 { SetFieldValue(CMimeConst::ContentDescription(), pszValue, pszCharset); }\r
314 \r
315 inline const char* CMimeHeader::GetDescription() const\r
316 { return GetFieldValue(CMimeConst::ContentDescription()); }\r
317 \r
318 //////////////////////////////////////////////////////////////////////\r
319 // CMimeBody class - Represents a body part in a MIME message\r
320 //////////////////////////////////////////////////////////////////////\r
321 class CMimeMessage;\r
322 class CMimeBody : public CMimeHeader\r
323 {\r
324 protected:\r
325         CMimeBody() :                           // instantiate a CMimeBody object explicitly is not allowed. call CreatePart()\r
326                 m_pbText(NULL),\r
327                 m_nTextSize(0) {}\r
328         virtual ~CMimeBody() { Clear(); }\r
329 \r
330 public:\r
331         int GetContentLength() const;\r
332         const unsigned char* GetContent() const;\r
333 \r
334         // operations for 'text' or 'message' media\r
335         bool IsText() const;\r
336         int SetText(const char* pbText, int nLength=0);\r
337         int GetText(char* pbText, int nMaxSize);\r
338         int GetText(string& strText);\r
339 \r
340         // operations for 'message' media\r
341         bool IsMessage() const;\r
342         bool SetMessage(const CMimeMessage* pMM);\r
343         void GetMessage(CMimeMessage* pMM) const;\r
344 \r
345         // operations for 'image/audio/vedio/application' (attachment) media\r
346         bool IsAttachment() const;\r
347         bool ReadFromFile(const char* pszFilename);\r
348         bool WriteToFile(const char* pszFilename);\r
349 \r
350         // operations for 'multipart' media\r
351         bool IsMultiPart() const;\r
352         void DeleteAll();\r
353         CMimeBody* CreatePart(const char* pszMediaType=NULL, CMimeBody* pWhere=NULL);\r
354         void ErasePart(CMimeBody* pBP);\r
355         CMimeBody* FindFirstPart();\r
356         CMimeBody* FindNextPart();\r
357 \r
358         typedef list<CMimeBody*> CBodyList;\r
359         int GetBodyPartList(CBodyList& rList) const;\r
360         int GetAttachmentList(CBodyList& rList) const;\r
361 \r
362 public:\r
363         // overrides\r
364         virtual void Clear();\r
365         virtual int GetLength() const;\r
366         // serialization\r
367         virtual int Store(char* pszData, int nMaxSize) const;\r
368         virtual void Store(std::string &str) const;\r
369         virtual int Load(const char* pszData, int nDataSize);\r
370 \r
371 protected:\r
372         unsigned char* m_pbText;                // content (text) of the body part\r
373         int m_nTextSize;                                // length of content\r
374         CBodyList m_listBodies;                 // list of all child body parts\r
375         CBodyList::iterator m_itFind;\r
376 \r
377 protected:\r
378         bool AllocateBuffer(int nBufSize);\r
379         void FreeBuffer();\r
380 \r
381         friend class CMimeEnvironment;\r
382 };\r
383 \r
384 inline int CMimeBody::GetContentLength() const\r
385 { return m_nTextSize; }\r
386 \r
387 inline const unsigned char* CMimeBody::GetContent() const\r
388 { return m_pbText; }\r
389 \r
390 inline bool CMimeBody::IsText() const\r
391 { return GetMediaType() == MEDIA_TEXT; }\r
392 \r
393 inline bool CMimeBody::IsMessage() const\r
394 { return GetMediaType() == MEDIA_MESSAGE; }\r
395 \r
396 inline bool CMimeBody::IsAttachment() const\r
397 { return GetName().size() > 0; }\r
398 \r
399 inline bool CMimeBody::IsMultiPart() const\r
400 { return GetMediaType() == MEDIA_MULTIPART; }\r
401 \r
402 inline CMimeBody* CMimeBody::FindFirstPart()\r
403 {\r
404         m_itFind = m_listBodies.begin();\r
405         return FindNextPart();\r
406 }\r
407 \r
408 inline CMimeBody* CMimeBody::FindNextPart()\r
409 {\r
410         if (m_itFind != m_listBodies.end())\r
411                 return *m_itFind++;\r
412         return NULL;\r
413 }\r
414 \r
415 inline bool CMimeBody::AllocateBuffer(int nBufSize)\r
416 {\r
417         FreeBuffer();\r
418         m_pbText = new unsigned char[nBufSize];\r
419         if (!m_pbText)  return false;\r
420         m_nTextSize = nBufSize;\r
421         return true;\r
422 }\r
423 \r
424 inline void CMimeBody::FreeBuffer()\r
425 {\r
426         delete []m_pbText;\r
427         m_pbText = NULL;\r
428         m_nTextSize = 0;\r
429 }\r
430 \r
431 //////////////////////////////////////////////////////////////////////\r
432 // CMimeMessage - Represents a MIME message\r
433 //////////////////////////////////////////////////////////////////////\r
434 class CMimeMessage : public CMimeBody\r
435 {\r
436 public:\r
437         CMimeMessage() { /*SetVersion();*/ }\r
438         virtual ~CMimeMessage() { Clear(); }\r
439 \r
440 public:\r
441         // set/get RFC 822 message header fields\r
442         void SetFrom(const char* pszFrom, const char* pszCharset=NULL);\r
443         const char* GetFrom() const;\r
444         void SetTo(const char* pszTo, const char* pszCharset=NULL);\r
445         const char* GetTo() const;\r
446         void SetCc(const char* pszCc, const char* pszCharset=NULL);\r
447         const char* GetCc() const;\r
448         void SetBcc(const char* pszBcc, const char* pszCharset=NULL);\r
449         const char* GetBcc() const;\r
450 \r
451         void SetSubject(const char* pszSubject, const char* pszCharset=NULL);\r
452         const char* GetSubject() const;\r
453 \r
454         void SetDate();\r
455         void SetDate(int nYear, int nMonth, int nDay, int nHour, int nMinute, int nSecond);\r
456         const char* GetDate() const;\r
457         void SetVersion();\r
458 };\r
459 \r
460 inline void CMimeMessage::SetFrom(const char* pszAddr, const char* pszCharset)\r
461 { SetFieldValue("From", pszAddr, pszCharset); }\r
462 \r
463 inline const char* CMimeMessage::GetFrom() const\r
464 { return GetFieldValue("From"); }\r
465 \r
466 inline void CMimeMessage::SetTo(const char* pszAddr, const char* pszCharset)\r
467 { SetFieldValue("To", pszAddr, pszCharset); }\r
468 \r
469 inline const char* CMimeMessage::GetTo() const\r
470 { return GetFieldValue("To"); }\r
471 \r
472 inline void CMimeMessage::SetCc(const char* pszAddr, const char* pszCharset)\r
473 { SetFieldValue("CC", pszAddr, pszCharset); }\r
474 \r
475 inline const char* CMimeMessage::GetCc() const\r
476 { return GetFieldValue("CC"); }\r
477 \r
478 inline void CMimeMessage::SetBcc(const char* pszAddr, const char* pszCharset)\r
479 { SetFieldValue("BCC", pszAddr, pszCharset); }\r
480 \r
481 inline const char* CMimeMessage::GetBcc() const\r
482 { return GetFieldValue("BCC"); }\r
483 \r
484 inline void CMimeMessage::SetSubject(const char* pszSubject, const char* pszCharset)\r
485 { SetFieldValue("Subject", pszSubject, pszCharset); }\r
486 \r
487 inline const char* CMimeMessage::GetSubject() const\r
488 { return GetFieldValue("Subject"); }\r
489 \r
490 inline const char* CMimeMessage::GetDate() const\r
491 { return GetFieldValue("Date"); }\r
492 \r
493 inline void CMimeMessage::SetVersion()\r
494 { SetFieldValue(CMimeConst::MimeVersion(), "1.0"); }\r
495 \r
496 #endif // !defined(_MIME_H)\r