2 * Copyright 2011-2013, Haiku, Inc. All rights reserved.
3 * Copyright 2011, Clemens Zeidler <haiku@clemens-zeidler.de>
4 * Copyright 2001-2003 Dr. Zoidberg Enterprises. All rights reserved.
6 * Distributed under the terms of the MIT License.
10 #include "HaikuMailFormatFilter.h"
14 #include <Directory.h>
18 #include <mail_util.h>
21 struct mail_header_field
{
23 const char* attr_name
;
25 // currently either B_STRING_TYPE and B_TIME_TYPE
29 static const mail_header_field gDefaultFields
[] = {
30 { "To", B_MAIL_ATTR_TO
, B_STRING_TYPE
},
31 { "From", B_MAIL_ATTR_FROM
, B_STRING_TYPE
},
32 { "Cc", B_MAIL_ATTR_CC
, B_STRING_TYPE
},
33 { "Date", B_MAIL_ATTR_WHEN
, B_TIME_TYPE
},
34 { "Delivery-Date", B_MAIL_ATTR_WHEN
, B_TIME_TYPE
},
35 { "Reply-To", B_MAIL_ATTR_REPLY
, B_STRING_TYPE
},
36 { "Subject", B_MAIL_ATTR_SUBJECT
, B_STRING_TYPE
},
37 { "X-Priority", B_MAIL_ATTR_PRIORITY
, B_STRING_TYPE
},
38 // Priorities with preferred
39 { "Priority", B_MAIL_ATTR_PRIORITY
, B_STRING_TYPE
},
40 // one first - the numeric
41 { "X-Msmail-Priority", B_MAIL_ATTR_PRIORITY
, B_STRING_TYPE
},
42 // one (has more levels).
43 { "Mime-Version", B_MAIL_ATTR_MIME
, B_STRING_TYPE
},
44 { "STATUS", B_MAIL_ATTR_STATUS
, B_STRING_TYPE
},
45 { "THREAD", B_MAIL_ATTR_THREAD
, B_STRING_TYPE
},
46 { "NAME", B_MAIL_ATTR_NAME
, B_STRING_TYPE
},
51 //! Replaces tabs and other white space with spaces, compresses spaces.
53 sanitize_white_space(BString
& string
)
55 char* buffer
= string
.LockBuffer(string
.Length() + 1);
59 int32 count
= string
.Length();
61 for (int32 i
= 0; buffer
[i
] != '\0'; i
++, count
--) {
62 if (isspace(buffer
[i
])) {
67 memmove(buffer
+ i
+ 1 - spaces
, buffer
+ i
, count
+ 1);
72 string
.UnlockBuffer();
79 HaikuMailFormatFilter::HaikuMailFormatFilter(BMailProtocol
& protocol
,
80 const BMailAccountSettings
& settings
)
82 BMailFilter(protocol
, NULL
),
83 fAccountID(settings
.AccountID()),
84 fAccountName(settings
.Name())
86 const BMessage
& outboundSettings
= settings
.OutboundSettings();
87 outboundSettings
.FindString("destination", &fOutboundDirectory
);
92 HaikuMailFormatFilter::DescriptiveName() const
94 // This will not be called by the UI; no need to translate it
100 HaikuMailFormatFilter::HeaderFetched(entry_ref
& ref
, BFile
& file
,
101 BMessage
& attributes
)
103 file
.Seek(0, SEEK_SET
);
105 // TODO: attributes.AddInt32(B_MAIL_ATTR_CONTENT, length);
106 attributes
.AddInt32(B_MAIL_ATTR_ACCOUNT_ID
, fAccountID
);
107 attributes
.AddString(B_MAIL_ATTR_ACCOUNT
, fAccountName
);
111 if (file
.GetSize(&size
) == B_OK
) {
112 char* buffer
= header
.LockBuffer(size
);
116 ssize_t bytesRead
= file
.Read(buffer
, size
);
119 if (bytesRead
!= size
)
122 header
.UnlockBuffer(size
);
125 for (int i
= 0; gDefaultFields
[i
].rfc_name
; ++i
) {
127 status_t status
= extract_from_header(header
,
128 gDefaultFields
[i
].rfc_name
, target
);
132 switch (gDefaultFields
[i
].attr_type
){
134 sanitize_white_space(target
);
135 attributes
.AddString(gDefaultFields
[i
].attr_name
, target
);
141 when
= ParseDateWithTimeZone(target
);
143 when
= time(NULL
); // Use current time if it's undecodable.
144 attributes
.AddData(B_MAIL_ATTR_WHEN
, B_TIME_TYPE
, &when
,
151 BString senderName
= _ExtractName(attributes
.FindString(B_MAIL_ATTR_FROM
));
152 attributes
.AddString(B_MAIL_ATTR_NAME
, senderName
);
154 // Generate a file name for the incoming message. See also
155 // Message::RenderTo which does a similar thing for outgoing messages.
156 BString name
= attributes
.FindString(B_MAIL_ATTR_SUBJECT
);
157 SubjectToThread(name
); // Extract the core subject words.
158 if (name
.Length() <= 0)
160 attributes
.AddString(B_MAIL_ATTR_THREAD
, name
);
162 // Convert the date into a year-month-day fixed digit width format, so that
163 // sorting by file name will give all the messages with the same subject in
165 time_t dateAsTime
= 0;
166 const time_t* datePntr
;
168 char numericDateString
[40];
169 struct tm timeFields
;
171 if (attributes
.FindData(B_MAIL_ATTR_WHEN
, B_TIME_TYPE
,
172 (const void**)&datePntr
, &dateSize
) == B_OK
)
173 dateAsTime
= *datePntr
;
174 localtime_r(&dateAsTime
, &timeFields
);
175 snprintf(numericDateString
, sizeof(numericDateString
),
176 "%04d%02d%02d%02d%02d%02d",
177 timeFields
.tm_year
+ 1900, timeFields
.tm_mon
+ 1, timeFields
.tm_mday
,
178 timeFields
.tm_hour
, timeFields
.tm_min
, timeFields
.tm_sec
);
179 name
<< " " << numericDateString
;
181 BString workerName
= attributes
.FindString(B_MAIL_ATTR_FROM
);
182 extract_address_name(workerName
);
183 name
<< " " << workerName
;
185 name
.Truncate(222); // reserve space for the unique number
187 // Get rid of annoying characters which are hard to use in the shell.
188 name
.ReplaceAll('/', '_');
189 name
.ReplaceAll('\'', '_');
190 name
.ReplaceAll('"', '_');
191 name
.ReplaceAll('!', '_');
192 name
.ReplaceAll('<', '_');
193 name
.ReplaceAll('>', '_');
194 _RemoveExtraWhitespace(name
);
195 _RemoveLeadingDots(name
);
196 // Avoid files starting with a dot.
198 if (!attributes
.HasString(B_MAIL_ATTR_STATUS
))
199 attributes
.AddString(B_MAIL_ATTR_STATUS
, "New");
201 _SetType(attributes
, B_PARTIAL_MAIL_TYPE
);
203 ref
.set_name(name
.String());
205 return B_MOVE_MAIL_ACTION
;
210 HaikuMailFormatFilter::BodyFetched(const entry_ref
& ref
, BFile
& file
,
211 BMessage
& attributes
)
213 _SetType(attributes
, B_MAIL_TYPE
);
218 HaikuMailFormatFilter::MessageSent(const entry_ref
& ref
, BFile
& file
)
220 mail_flags flags
= B_MAIL_SENT
;
221 file
.WriteAttr(B_MAIL_ATTR_FLAGS
, B_INT32_TYPE
, 0, &flags
, sizeof(int32
));
222 file
.WriteAttr(B_MAIL_ATTR_STATUS
, B_STRING_TYPE
, 0, "Sent", 5);
224 if (!fOutboundDirectory
.IsEmpty()) {
225 create_directory(fOutboundDirectory
, 755);
226 BDirectory
dir(fOutboundDirectory
);
228 // fMailProtocol.Looper()->TriggerFileMove(ref, dir);
231 // TODO: report error (via BMailProtocol::MailNotifier())
237 HaikuMailFormatFilter::_RemoveExtraWhitespace(BString
& name
)
240 for (int i
= 0; i
<= name
.Length();) {
241 if (i
< name
.Length() && isspace(name
.ByteAt(i
))) {
244 } else if (spaces
> 0) {
245 int remove
= spaces
- 1;
246 // Also remove leading and trailing spaces
247 if (i
== remove
+ 1 || i
== name
.Length())
250 name
[i
- spaces
] = ' ';
251 name
.Remove(i
- remove
, remove
);
261 HaikuMailFormatFilter::_RemoveLeadingDots(BString
& name
)
264 while (dots
< name
.Length() && name
.ByteAt(dots
) == '.')
268 name
.Remove(0, dots
);
273 HaikuMailFormatFilter::_ExtractName(const BString
& from
)
275 // extract name from something like "name" <email@domain.com>
276 // if name is empty return the mail address without "<>"
279 int32 emailStart
= from
.FindFirst("<");
280 if (emailStart
< 0) {
285 from
.CopyInto(name
, 0, emailStart
);
287 if (name
.Length() >= 2) {
288 if (name
[name
.Length() - 1] == '\"')
289 name
.Truncate(name
.Length() - 1, true);
297 // empty name extract email address
299 name
.Remove(0, emailStart
+ 1);
301 if (name
.Length() < 1)
303 if (name
[name
.Length() - 1] == '>')
304 name
.Truncate(name
.Length() - 1, true);
311 HaikuMailFormatFilter::_SetType(BMessage
& attributes
, const char* mimeType
)
313 return attributes
.SetData("BEOS:TYPE", B_MIME_STRING_TYPE
, mimeType
,
314 strlen(mimeType
) + 1, false);