summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/import/src/MapiMessage.h
blob: c06a84c57aaf45410e65ae37dc4b05bf21dc7826 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MapiMessage_h___
#define MapiMessage_h___

#include "nsTArray.h"
#include "nsString.h"
#include "nsIFile.h"
#include "nsIMsgSend.h"
#include "MapiApi.h"

#include <vector>

#ifndef PR_LAST_VERB_EXECUTED
#  define PR_LAST_VERB_EXECUTED PROP_TAG(PT_LONG, 0x1081)
#endif

#define EXCHIVERB_REPLYTOSENDER (102)
#define EXCHIVERB_REPLYTOALL (103)
#define EXCHIVERB_FORWARD (104)

#ifndef PR_ATTACH_CONTENT_ID
#  define PR_ATTACH_CONTENT_ID PROP_TAG(PT_TSTRING, 0x3712)
#endif
#ifndef PR_ATTACH_CONTENT_ID_W
#  define PR_ATTACH_CONTENT_ID_W PROP_TAG(PT_UNICODE, 0x3712)
#endif
#ifndef PR_ATTACH_CONTENT_ID_A
#  define PR_ATTACH_CONTENT_ID_A PROP_TAG(PT_STRING8, 0x3712)
#endif

#ifndef PR_ATTACH_FLAGS
#  define PR_ATTACH_FLAGS PROP_TAG(PT_LONG, 0x3714)
#endif

#ifndef ATT_INVISIBLE_IN_HTML
#  define ATT_INVISIBLE_IN_HTML (0x1)
#endif
#ifndef ATT_INVISIBLE_IN_RTF
#  define ATT_INVISIBLE_IN_RTF (0x2)
#endif
#ifndef ATT_MHTML_REF
#  define ATT_MHTML_REF (0x4)
#endif

//////////////////////////////////////////////////////////////////////////////

class CMapiMessageHeaders {
 public:
  // Special headers that MUST appear at most once (see RFC822)
  enum SpecialHeader {
    hdrNone = -1,
    hdrFirst = 0,  // utility values
    hdrDate = hdrFirst,
    hdrFrom,
    hdrSender,
    hdrReplyTo,
    hdrTo,
    hdrCc,
    hdrBcc,
    hdrMessageID,
    hdrSubject,
    hdrMimeVersion,
    hdrContentType,
    hdrContentTransferEncoding,
    hdrMax  // utility value
  };

  explicit CMapiMessageHeaders(const char* headers = 0) { Assign(headers); }
  ~CMapiMessageHeaders();
  void Assign(const char* headers);

  inline bool IsEmpty() const { return m_headerFields.empty(); }
  // if no such header exists then 0 is returned, else the first value returned
  const char* Value(const char* name) const;
  // if no such header exists then 0 is returned
  const char* Value(SpecialHeader special) const;

  void UnfoldValue(const char* name, nsString& dest,
                   const char* fallbackCharset) const;
  void UnfoldValue(SpecialHeader special, nsString& dest,
                   const char* fallbackCharset) const;

  // value must be utf-8 or 7-bit; supposed that this function will be called
  // when the charset of the value is known
  // TODO: if replace is set, then all headers with this name will be removed
  //  and one with this value will be added, otherwise a new header is added
  // (Unnecessary for now)
  int SetValue(const char* name, const char* value, bool replace = true);
  int SetValue(SpecialHeader special, const char* value);

  static const char* SpecialName(SpecialHeader special);

  nsresult ToStream(nsIOutputStream* pDst) const;

 private:
  class CHeaderField {
   public:
    CHeaderField(const char* begin, int len);
    CHeaderField(const char* name, const char* body, bool utf8 = false);
    ~CHeaderField();
    inline bool Valid() const { return m_fname; }
    inline const char* fname() const { return m_fname; }
    inline const char* fbody() const { return m_fbody; }

    // txt must be utf-8 or 7-bit; supposed that this function will be called
    // when the charset of the txt is known
    void set_fbody(const char* txt);

    void GetUnfoldedString(nsString& dest, const char* fallbackCharset) const;

   private:
    char* m_fname;
    char* m_fbody;
    bool m_fbody_utf8;
  };  // class HeaderField

  class write_to_stream {
   public:
    explicit write_to_stream(nsIOutputStream* pDst)
        : m_pDst(pDst), m_rv(NS_OK) {}
    void operator()(const CHeaderField* f);
    inline operator nsresult() const { return m_rv; }

   private:
    nsIOutputStream* m_pDst;
    nsresult m_rv;
  };

  // Search helper
  class fname_equals {
   public:
    explicit fname_equals(const char* search) : m_search(search) {}
    inline bool operator()(const CHeaderField* f) const {
      return stricmp(f->fname(), m_search) == 0;
    }

   private:
    const char* m_search;
  };  // class fname_equals

  // The common array of special headers' names
  static const char* Specials[hdrMax];

  std::vector<CHeaderField*> m_headerFields;
  CHeaderField* m_SpecialHeaders[hdrMax];  // Pointers into the m_headerFields

  void ClearHeaderFields();
  void Add(CHeaderField* f);
  static void Delete(CHeaderField* p);
  static SpecialHeader CheckSpecialHeader(const char* fname);
  const CHeaderField* CFind(const char* name) const;
  inline CHeaderField* Find(const char* name) {
    return const_cast<CHeaderField*>(CFind(name));
  }

};  // class CMapiMessageHeaders

//////////////////////////////////////////////////////

class CMapiMessage {
 public:
  explicit CMapiMessage(LPMESSAGE lpMsg);
  ~CMapiMessage();

  // Attachments
  // Ordinary (not embedded) attachments.
  nsresult GetAttachments(nsTArray<RefPtr<nsIMsgAttachedFile>>& attachments);
  // Embedded attachments
  size_t EmbeddedAttachmentsCount() const { return m_embattachments.size(); }
  bool GetEmbeddedAttachmentInfo(unsigned int i, nsIURI** uri, const char** cid,
                                 const char** name) const;
  // We don't check MSGFLAG_HASATTACH, since it returns true even if there are
  // only embedded attachmentsin the message. TB only counts the ordinary
  // attachments when shows the message status, so here we check only for the
  // ordinary attachments.
  inline bool HasAttach() const { return !m_stdattachments.empty(); }

  // Retrieve info for message
  inline bool BodyIsHtml(void) const { return m_bodyIsHtml; }
  const char* GetFromLine(int& len) const {
    if (m_fromLine.IsEmpty())
      return NULL;
    else {
      len = m_fromLine.Length();
      return m_fromLine.get();
    }
  }
  inline CMapiMessageHeaders* GetHeaders() { return &m_headers; }
  inline const wchar_t* GetBody(void) const { return m_body.get(); }
  inline size_t GetBodyLen(void) const { return m_body.Length(); }
  void GetBody(nsCString& dest) const;
  inline const char* GetBodyCharset(void) const { return m_mimeCharset.get(); }
  inline bool IsRead() const { return m_msgFlags & MSGFLAG_READ; }
  inline bool IsReplied() const {
    return (m_msgLastVerb == EXCHIVERB_REPLYTOSENDER) ||
           (m_msgLastVerb == EXCHIVERB_REPLYTOALL);
  }
  inline bool IsForvarded() const { return m_msgLastVerb == EXCHIVERB_FORWARD; }

  bool HasContentHeader(void) const { return !m_mimeContentType.IsEmpty(); }
  bool HasMimeVersion(void) const {
    return m_headers.Value(CMapiMessageHeaders::hdrMimeVersion);
  }
  const char* GetMimeContent(void) const { return m_mimeContentType.get(); }
  int32_t GetMimeContentLen(void) const { return m_mimeContentType.Length(); }
  const char* GetMimeBoundary(void) const { return m_mimeBoundary.get(); }

  // The only required part of a message is its header
  inline bool ValidState() const { return !m_headers.IsEmpty(); }
  inline bool FullMessageDownloaded() const { return !m_dldStateHeadersOnly; }

 private:
  struct attach_data {
    nsCOMPtr<nsIURI> orig_url;
    nsCOMPtr<nsIFile> tmp_file;
    char* type;
    char* encoding;
    char* real_name;
    char* cid;
    bool delete_file;
    attach_data()
        : type(0), encoding(0), real_name(0), cid(0), delete_file(false) {}
  };

  static const nsCString m_whitespace;

  LPMESSAGE m_lpMsg;

  bool m_dldStateHeadersOnly;  // if the message has not been downloaded yet
  CMapiMessageHeaders m_headers;
  nsCString m_fromLine;         // utf-8
  nsCString m_mimeContentType;  // utf-8
  nsCString m_mimeBoundary;     // utf-8
  nsCString m_mimeCharset;      // utf-8

  std::vector<attach_data*> m_stdattachments;
  std::vector<attach_data*> m_embattachments;  // Embedded

  nsString m_body;  // to be converted from UTF-16 using m_mimeCharset
  bool m_bodyIsHtml;

  uint32_t m_msgFlags;
  uint32_t m_msgLastVerb;

  nsCOMPtr<nsIIOService> m_pIOService;

  void GetDownloadState();

  // Headers - fetch will get PR_TRANSPORT_MESSAGE_HEADERS
  // or if they do not exist will build a header from
  //  PR_DISPLAY_TO, _CC, _BCC
  //  PR_SUBJECT
  //  PR_MESSAGE_RECIPIENTS
  // and PR_CREATION_TIME if needed?
  bool FetchHeaders(void);
  bool FetchBody(void);
  void FetchFlags(void);

  static bool GetTmpFile(/*out*/ nsIFile** aResult);
  static bool CopyMsgAttachToFile(LPATTACH lpAttach,
                                  /*out*/ nsIFile** tmp_file);
  static bool CopyBinAttachToFile(LPATTACH lpAttach, nsIFile** tmp_file);

  static void ClearAttachment(attach_data* data);
  void ClearAttachments();
  bool AddAttachment(DWORD aNum);
  bool IterateAttachTable(LPMAPITABLE tbl);
  bool GetURL(nsIFile* aFile, nsIURI** url);
  void ProcessAttachments();

  bool EnsureHeader(CMapiMessageHeaders::SpecialHeader special, ULONG mapiTag);
  bool EnsureDate();

  void ProcessContentType();
  bool CheckBodyInCharsetRange(const char* charset);
  void FormatDateTime(SYSTEMTIME& tm, nsCString& s, bool includeTZ = true);
  void BuildFromLine(void);

  inline static bool IsSpace(char c) {
    return c == ' ' || c == '\r' || c == '\n' || c == '\b' || c == '\t';
  }
  inline static bool IsSpace(wchar_t c) {
    return ((c & 0xFF) == c) && IsSpace(static_cast<char>(c));
  }  // Avoid false detections
};

#endif /* MapiMessage_h__ */