1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/types.h>
21 #include <osl/thread.h>
22 #include <tools/datetime.hxx>
23 #include <tools/inetmime.hxx>
24 #include <tools/inetmsg.hxx>
25 #include <tools/inetstrm.hxx>
26 #include <tools/contnr.hxx>
27 #include <rtl/instance.hxx>
28 #include <rtl/strbuf.hxx>
29 #include <comphelper/string.hxx>
34 inline bool ascii_isDigit( sal_Unicode ch
)
36 return ((ch
>= 0x0030) && (ch
<= 0x0039));
39 inline bool ascii_isLetter( sal_Unicode ch
)
41 return (( (ch
>= 0x0041) && (ch
<= 0x005A)) || ((ch
>= 0x0061) && (ch
<= 0x007A)));
44 inline sal_Unicode
ascii_toLowerCase( sal_Unicode ch
)
46 if ( (ch
>= 0x0041) && (ch
<= 0x005A) )
52 void INetMIMEMessage::ListCleanup_Impl()
55 sal_uIntPtr i
, n
= m_aHeaderList
.size();
56 for (i
= 0; i
< n
; i
++)
57 delete m_aHeaderList
[ i
];
58 m_aHeaderList
.clear();
61 void INetMIMEMessage::ListCopy (const INetMIMEMessage
&rMsg
)
69 sal_uIntPtr i
, n
= rMsg
.GetHeaderCount();
70 for (i
= 0; i
< n
; i
++)
72 INetMessageHeader
*p
= rMsg
.m_aHeaderList
[ i
];
73 m_aHeaderList
.push_back( new INetMessageHeader(*p
) );
78 void INetMIMEMessage::SetHeaderField_Impl (
79 INetMIME::HeaderFieldType eType
,
81 const OUString
&rValue
,
84 INetMIMEStringOutputSink
aSink (0, 32767); /* weird the mime standard says that aline MUST not be longeur that 998 */
85 INetMIME::writeHeaderFieldBody (
86 aSink
, eType
, rValue
, osl_getThreadTextEncoding(), false);
88 INetMessageHeader (rName
, aSink
.takeBuffer()), rnIndex
);
91 static const std::map
<InetMessageField
, const char *> ImplINetRFC822MessageHeaderData
=
93 { InetMessageField::BCC
, "BCC" } ,
94 { InetMessageField::CC
, "CC" } ,
95 { InetMessageField::COMMENTS
, "Comments" } ,
96 { InetMessageField::DATE
, "Date" } ,
97 { InetMessageField::FROM
, "From" } ,
98 { InetMessageField::IN_REPLY_TO
, "In-Reply-To" } ,
99 { InetMessageField::KEYWORDS
, "Keywords" } ,
100 { InetMessageField::MESSAGE_ID
, "Message-ID" } ,
101 { InetMessageField::REFERENCES
, "References" } ,
102 { InetMessageField::REPLY_TO
, "Reply-To" } ,
103 { InetMessageField::RETURN_PATH
, "Return-Path" } ,
104 { InetMessageField::SUBJECT
, "Subject" } ,
105 { InetMessageField::SENDER
, "Sender" } ,
106 { InetMessageField::TO
, "To" } ,
107 { InetMessageField::X_MAILER
, "X-Mailer" } ,
108 { InetMessageField::RETURN_RECEIPT_TO
, "Return-Receipt-To" } ,
112 State of RFC822 header parsing
114 enum class HeaderState
128 /* ParseDateField and local helper functions.
130 * Parses a String in (implied) GMT format into class Date and tools::Time objects.
131 * Four formats are accepted:
133 * [Wkd,] 1*2DIGIT Mon 2*4DIGIT 00:00:00 [GMT] (rfc1123)
134 * [Wkd,] 00 Mon 0000 00:00:00 [GMT]) (rfc822, rfc1123)
135 * Weekday, 00-Mon-00 00:00:00 [GMT] (rfc850, rfc1036)
136 * Wkd Mon 00 00:00:00 0000 [GMT] (ctime)
137 * 1*DIGIT (delta seconds)
140 static const sal_Char
*months
[12] =
142 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
143 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
146 static sal_uInt16
ParseNumber(const OString
& rStr
, sal_uInt16
& nIndex
)
148 sal_uInt16 n
= nIndex
;
149 while ((n
< rStr
.getLength()) && ascii_isDigit(rStr
[n
])) n
++;
151 OString
aNum(rStr
.copy(nIndex
, (n
- nIndex
)));
154 return (sal_uInt16
)(aNum
.toInt32());
157 static sal_uInt16
ParseMonth(const OString
& rStr
, sal_uInt16
& nIndex
)
159 sal_uInt16 n
= nIndex
;
160 while ((n
< rStr
.getLength()) && ascii_isLetter(rStr
[n
])) n
++;
162 OString
aMonth(rStr
.copy(nIndex
, 3));
166 for (i
= 0; i
< 12; i
++)
167 if (aMonth
.equalsIgnoreAsciiCase(months
[i
])) break;
171 bool INetMIMEMessage::ParseDateField (
172 const OUString
& rDateFieldW
, DateTime
& rDateTime
)
174 OString
aDateField(OUStringToOString(rDateFieldW
,
175 RTL_TEXTENCODING_ASCII_US
));
177 if (aDateField
.isEmpty()) return false;
179 if (aDateField
.indexOf(':') != -1)
181 // Some DateTime format.
182 sal_uInt16 nIndex
= 0;
184 // Skip over <Wkd> or <Weekday>, leading and trailing space.
185 while ((nIndex
< aDateField
.getLength()) &&
186 (aDateField
[nIndex
] == ' '))
190 (nIndex
< aDateField
.getLength()) &&
191 (ascii_isLetter (aDateField
[nIndex
]) ||
192 (aDateField
[nIndex
] == ',') ))
195 while ((nIndex
< aDateField
.getLength()) &&
196 (aDateField
[nIndex
] == ' '))
199 if (ascii_isLetter (aDateField
[nIndex
]))
202 if ((aDateField
.getLength() - nIndex
) < 20) return false;
204 rDateTime
.SetMonth (ParseMonth (aDateField
, nIndex
)); nIndex
++;
205 rDateTime
.SetDay (ParseNumber (aDateField
, nIndex
)); nIndex
++;
207 rDateTime
.SetHour (ParseNumber (aDateField
, nIndex
)); nIndex
++;
208 rDateTime
.SetMin (ParseNumber (aDateField
, nIndex
)); nIndex
++;
209 rDateTime
.SetSec (ParseNumber (aDateField
, nIndex
)); nIndex
++;
210 rDateTime
.SetNanoSec (0);
212 sal_uInt16 nYear
= ParseNumber (aDateField
, nIndex
);
213 if (nYear
< 100) nYear
+= 1900;
214 rDateTime
.SetYear (nYear
);
218 // Format: RFC1036 or RFC1123.
219 if ((aDateField
.getLength() - nIndex
) < 17) return false;
221 rDateTime
.SetDay (ParseNumber (aDateField
, nIndex
)); nIndex
++;
222 rDateTime
.SetMonth (ParseMonth (aDateField
, nIndex
)); nIndex
++;
224 sal_uInt16 nYear
= ParseNumber (aDateField
, nIndex
); nIndex
++;
225 if (nYear
< 100) nYear
+= 1900;
226 rDateTime
.SetYear (nYear
);
228 rDateTime
.SetHour (ParseNumber (aDateField
, nIndex
)); nIndex
++;
229 rDateTime
.SetMin (ParseNumber (aDateField
, nIndex
)); nIndex
++;
230 rDateTime
.SetSec (ParseNumber (aDateField
, nIndex
)); nIndex
++;
231 rDateTime
.SetNanoSec (0);
233 const char cPossiblePlusMinus
= nIndex
< aDateField
.getLength() ? aDateField
[nIndex
] : 0;
234 if (cPossiblePlusMinus
== '+' || cPossiblePlusMinus
== '-')
236 // Offset from GMT: "(+|-)HHMM".
237 bool bEast
= (aDateField
[nIndex
++] == '+');
238 sal_uInt16 nOffset
= ParseNumber (aDateField
, nIndex
);
241 tools::Time
aDiff( tools::Time::EMPTY
);
242 aDiff
.SetHour (nOffset
/ 100);
243 aDiff
.SetMin (nOffset
% 100);
245 aDiff
.SetNanoSec (0);
255 else if (comphelper::string::isdigitAsciiString(aDateField
))
257 // Format: delta seconds.
258 tools::Time
aDelta (0);
259 aDelta
.SetTime (aDateField
.toInt32() * 100);
261 DateTime
aNow( DateTime::SYSTEM
);
265 rDateTime
.SetDate (aNow
.GetDate());
266 rDateTime
.SetTime (aNow
.GetTime());
274 return (rDateTime
.IsValidAndGregorian() &&
275 !((rDateTime
.GetSec() > 59) ||
276 (rDateTime
.GetMin() > 59) ||
277 (rDateTime
.GetHour() > 23) ));
280 // Header Field Parser
281 sal_uIntPtr
INetMIMEMessage::SetRFC822HeaderField (
282 const INetMessageHeader
&rHeader
, sal_uIntPtr nNewIndex
)
284 OString
aName (rHeader
.GetName());
285 const sal_Char
*pData
= aName
.getStr();
286 const sal_Char
*pStop
= pData
+ aName
.getLength() + 1;
287 const sal_Char
*check
= "";
289 InetMessageField nIdx
= static_cast<InetMessageField
>(CONTAINER_APPEND
);
290 HeaderState eState
= HeaderState::BEGIN
;
291 HeaderState eOkState
= HeaderState::OK
;
293 while (pData
< pStop
)
297 case HeaderState::BEGIN
:
298 eState
= HeaderState::CHECK
;
299 eOkState
= HeaderState::OK
;
301 switch (ascii_toLowerCase (*pData
))
305 nIdx
= InetMessageField::BCC
;
309 eState
= HeaderState::LETTER_C
;
314 nIdx
= InetMessageField::DATE
;
319 nIdx
= InetMessageField::FROM
;
323 check
= "n-reply-to";
324 nIdx
= InetMessageField::IN_REPLY_TO
;
329 nIdx
= InetMessageField::KEYWORDS
;
334 nIdx
= InetMessageField::MESSAGE_ID
;
339 eOkState
= HeaderState::TOKEN_RE
;
343 eState
= HeaderState::LETTER_S
;
348 nIdx
= InetMessageField::TO
;
353 eOkState
= HeaderState::TOKEN_XMINUS
;
357 eState
= HeaderState::JUNK
;
363 case HeaderState::TOKEN_RE
:
364 eState
= HeaderState::CHECK
;
365 eOkState
= HeaderState::OK
;
367 switch (ascii_toLowerCase (*pData
))
371 nIdx
= InetMessageField::REFERENCES
;
376 nIdx
= InetMessageField::REPLY_TO
;
381 eOkState
= HeaderState::TOKEN_RETURNMINUS
;
385 eState
= HeaderState::JUNK
;
391 case HeaderState::TOKEN_RETURNMINUS
:
392 eState
= HeaderState::CHECK
;
393 eOkState
= HeaderState::OK
;
395 switch (ascii_toLowerCase (*pData
))
399 nIdx
= InetMessageField::RETURN_PATH
;
404 nIdx
= InetMessageField::RETURN_RECEIPT_TO
;
408 eState
= HeaderState::JUNK
;
414 case HeaderState::TOKEN_XMINUS
:
415 eState
= HeaderState::CHECK
;
416 eOkState
= HeaderState::OK
;
418 switch (ascii_toLowerCase (*pData
))
422 nIdx
= InetMessageField::X_MAILER
;
426 eState
= HeaderState::JUNK
;
432 case HeaderState::LETTER_C
:
433 eState
= HeaderState::CHECK
;
434 eOkState
= HeaderState::OK
;
436 switch (ascii_toLowerCase (*pData
))
440 nIdx
= InetMessageField::CC
;
445 nIdx
= InetMessageField::COMMENTS
;
449 eState
= HeaderState::JUNK
;
455 case HeaderState::LETTER_S
:
456 eState
= HeaderState::CHECK
;
457 eOkState
= HeaderState::OK
;
459 switch (ascii_toLowerCase (*pData
))
463 nIdx
= InetMessageField::SENDER
;
468 nIdx
= InetMessageField::SUBJECT
;
472 eState
= HeaderState::JUNK
;
478 case HeaderState::CHECK
:
481 while (*pData
&& *check
&&
482 (ascii_toLowerCase (*pData
) == *check
))
492 eState
= (*check
== '\0') ? eOkState
: HeaderState::JUNK
;
495 case HeaderState::OK
:
497 SetHeaderField_Impl (
498 INetMessageHeader( ImplINetRFC822MessageHeaderData
.at(nIdx
), rHeader
.GetValue() ),
499 m_nRFC822Index
[nIdx
]);
500 nNewIndex
= m_nRFC822Index
[nIdx
];
503 default: // INETMSG_RFC822_JUNK
505 SetHeaderField_Impl(rHeader
, nNewIndex
);
512 static const std::map
<InetMessageMime
, const char*> ImplINetMIMEMessageHeaderData
=
514 { InetMessageMime::VERSION
, "MIME-Version"},
515 { InetMessageMime::CONTENT_DESCRIPTION
, "Content-Description"},
516 { InetMessageMime::CONTENT_DISPOSITION
, "Content-Disposition"},
517 { InetMessageMime::CONTENT_ID
, "Content-ID"},
518 { InetMessageMime::CONTENT_TYPE
, "Content-Type"},
519 { InetMessageMime::CONTENT_TRANSFER_ENCODING
, "Content-Transfer-Encoding"}
522 enum _ImplINetMIMEMessageHeaderState
529 INETMSG_MIME_TOKEN_CONTENT
,
530 INETMSG_MIME_TOKEN_CONTENT_D
,
531 INETMSG_MIME_TOKEN_CONTENT_T
534 INetMIMEMessage::INetMIMEMessage()
539 for (sal_uInt16 i
= 0; i
< static_cast<int>(InetMessageField::NUMHDR
); i
++)
540 m_nRFC822Index
[static_cast<InetMessageField
>(i
)] = CONTAINER_ENTRY_NOTFOUND
;
541 for (sal_uInt16 i
= 0; i
< static_cast<int>(InetMessageMime::NUMHDR
); i
++)
542 m_nMIMEIndex
[static_cast<InetMessageMime
>(i
)] = CONTAINER_ENTRY_NOTFOUND
;
545 INetMIMEMessage::INetMIMEMessage (const INetMIMEMessage
& rMsg
)
546 : m_nDocSize(rMsg
.m_nDocSize
),
547 m_aDocName(rMsg
.m_aDocName
),
548 m_xDocLB(rMsg
.m_xDocLB
),
552 m_nRFC822Index
= rMsg
.m_nRFC822Index
;
556 INetMIMEMessage
& INetMIMEMessage::operator= (
557 const INetMIMEMessage
& rMsg
)
561 m_nDocSize
= rMsg
.m_nDocSize
;
562 m_aDocName
= rMsg
.m_aDocName
;
563 m_xDocLB
= rMsg
.m_xDocLB
;
565 m_nRFC822Index
= rMsg
.m_nRFC822Index
;
572 INetMIMEMessage::~INetMIMEMessage()
578 void INetMIMEMessage::CleanupImp()
580 for( size_t i
= 0, n
= aChildren
.size(); i
< n
; ++i
) {
581 delete aChildren
[ i
];
586 void INetMIMEMessage::CopyImp (const INetMIMEMessage
& rMsg
)
588 bHeaderParsed
= rMsg
.bHeaderParsed
;
591 m_nMIMEIndex
= rMsg
.m_nMIMEIndex
;
592 m_aBoundary
= rMsg
.m_aBoundary
;
594 for (i
= 0; i
< rMsg
.aChildren
.size(); i
++)
596 INetMIMEMessage
*pChild
= rMsg
.aChildren
[ i
];
598 if (pChild
->pParent
== &rMsg
)
600 pChild
= INetMIMEMessage::CreateMessage (*pChild
);
601 pChild
->pParent
= this;
603 aChildren
.push_back( pChild
);
607 INetMIMEMessage
*INetMIMEMessage::CreateMessage (
608 const INetMIMEMessage
& rMsg
)
610 return new INetMIMEMessage (rMsg
);
613 // Header Field Parser
614 sal_uIntPtr
INetMIMEMessage::SetHeaderField (
615 const INetMessageHeader
&rHeader
, sal_uIntPtr nNewIndex
)
617 OString
aName (rHeader
.GetName());
618 const sal_Char
*pData
= aName
.getStr();
619 const sal_Char
*pStop
= pData
+ aName
.getLength() + 1;
620 const sal_Char
*check
= "";
622 InetMessageMime nIdx
= static_cast<InetMessageMime
>(CONTAINER_APPEND
);
623 int eState
= INETMSG_MIME_BEGIN
;
624 int eOkState
= INETMSG_MIME_OK
;
626 while (pData
< pStop
)
630 case INETMSG_MIME_BEGIN
:
631 eState
= INETMSG_MIME_CHECK
;
632 eOkState
= INETMSG_MIME_OK
;
634 switch (ascii_toLowerCase (*pData
))
638 eOkState
= INETMSG_MIME_TOKEN_CONTENT
;
642 check
= "ime-version";
643 nIdx
= InetMessageMime::VERSION
;
647 eState
= INETMSG_MIME_JUNK
;
653 case INETMSG_MIME_TOKEN_CONTENT
:
654 eState
= INETMSG_MIME_CHECK
;
655 eOkState
= INETMSG_MIME_OK
;
657 switch (ascii_toLowerCase (*pData
))
660 eState
= INETMSG_MIME_TOKEN_CONTENT_D
;
665 nIdx
= InetMessageMime::CONTENT_ID
;
669 eState
= INETMSG_MIME_TOKEN_CONTENT_T
;
673 eState
= INETMSG_MIME_JUNK
;
679 case INETMSG_MIME_TOKEN_CONTENT_D
:
680 eState
= INETMSG_MIME_CHECK
;
681 eOkState
= INETMSG_MIME_OK
;
683 switch (ascii_toLowerCase (*pData
))
687 nIdx
= InetMessageMime::CONTENT_DESCRIPTION
;
692 nIdx
= InetMessageMime::CONTENT_DISPOSITION
;
696 eState
= INETMSG_MIME_JUNK
;
702 case INETMSG_MIME_TOKEN_CONTENT_T
:
703 eState
= INETMSG_MIME_CHECK
;
704 eOkState
= INETMSG_MIME_OK
;
706 switch (ascii_toLowerCase (*pData
))
709 check
= "ansfer-encoding";
710 nIdx
= InetMessageMime::CONTENT_TRANSFER_ENCODING
;
715 nIdx
= InetMessageMime::CONTENT_TYPE
;
719 eState
= INETMSG_MIME_JUNK
;
725 case INETMSG_MIME_CHECK
:
728 while (*pData
&& *check
&&
729 (ascii_toLowerCase (*pData
) == *check
))
739 eState
= (*check
== '\0') ? eOkState
: INETMSG_MIME_JUNK
;
742 case INETMSG_MIME_OK
:
744 SetHeaderField_Impl (
745 INetMessageHeader( ImplINetMIMEMessageHeaderData
.at(nIdx
), rHeader
.GetValue()),
747 nNewIndex
= m_nMIMEIndex
[nIdx
];
750 default: // INETMSG_MIME_JUNK
752 nNewIndex
= SetRFC822HeaderField(rHeader
, nNewIndex
);
759 void INetMIMEMessage::SetMIMEVersion (const OUString
& rVersion
)
761 SetHeaderField_Impl (
762 INetMIME::HEADER_FIELD_TEXT
,
763 ImplINetMIMEMessageHeaderData
.at(InetMessageMime::VERSION
), rVersion
,
764 m_nMIMEIndex
[InetMessageMime::VERSION
]);
767 void INetMIMEMessage::SetContentDisposition (const OUString
& rDisposition
)
769 SetHeaderField_Impl (
770 INetMIME::HEADER_FIELD_TEXT
,
771 ImplINetMIMEMessageHeaderData
.at(InetMessageMime::CONTENT_DISPOSITION
), rDisposition
,
772 m_nMIMEIndex
[InetMessageMime::CONTENT_DISPOSITION
]);
775 void INetMIMEMessage::SetContentType (const OUString
& rType
)
777 SetHeaderField_Impl (
778 INetMIME::HEADER_FIELD_TEXT
,
779 ImplINetMIMEMessageHeaderData
.at(InetMessageMime::CONTENT_TYPE
), rType
,
780 m_nMIMEIndex
[InetMessageMime::CONTENT_TYPE
]);
783 void INetMIMEMessage::SetContentTransferEncoding (
784 const OUString
& rEncoding
)
786 SetHeaderField_Impl (
787 INetMIME::HEADER_FIELD_TEXT
,
788 ImplINetMIMEMessageHeaderData
.at(InetMessageMime::CONTENT_TRANSFER_ENCODING
), rEncoding
,
789 m_nMIMEIndex
[InetMessageMime::CONTENT_TRANSFER_ENCODING
]);
792 OUString
INetMIMEMessage::GetDefaultContentType()
796 OUString
aParentCT (pParent
->GetContentType());
797 if (aParentCT
.isEmpty())
798 aParentCT
= pParent
->GetDefaultContentType();
800 if (aParentCT
.equalsIgnoreAsciiCase("multipart/digest"))
801 return OUString("message/rfc822");
803 return OUString("text/plain; charset=us-ascii");
806 bool INetMIMEMessage::EnableAttachChild (INetMessageContainerType eType
)
812 // Setup Content-Type header field.
813 OStringBuffer aContentType
;
816 case INETMSG_MESSAGE_RFC822
:
817 aContentType
.append("message/rfc822");
820 case INETMSG_MULTIPART_ALTERNATIVE
:
821 aContentType
.append("multipart/alternative");
824 case INETMSG_MULTIPART_DIGEST
:
825 aContentType
.append("multipart/digest");
828 case INETMSG_MULTIPART_PARALLEL
:
829 aContentType
.append("multipart/parallel");
832 case INETMSG_MULTIPART_RELATED
:
833 aContentType
.append("multipart/related");
836 case INETMSG_MULTIPART_FORM_DATA
:
837 aContentType
.append("multipart/form-data");
841 aContentType
.append("multipart/mixed");
845 // Setup boundary for multipart types.
846 if (aContentType
.toString().equalsIgnoreAsciiCase("multipart/"))
848 // Generate a unique boundary from current time.
849 sal_Char sTail
[16 + 1];
850 tools::Time
aCurTime( tools::Time::SYSTEM
);
851 sal_uInt64 nThis
= reinterpret_cast< sal_uIntPtr
>( this ); // we can be on a 64bit architecture
852 nThis
= ( ( nThis
>> 32 ) ^ nThis
) & SAL_MAX_UINT32
;
853 sprintf (sTail
, "%08X%08X",
854 static_cast< unsigned int >(aCurTime
.GetTime()),
855 static_cast< unsigned int >(nThis
));
856 m_aBoundary
= "------------_4D48";
857 m_aBoundary
+= sTail
;
859 // Append boundary as ContentType parameter.
860 aContentType
.append("; boundary=");
861 aContentType
.append(m_aBoundary
);
864 // Set header fields.
865 SetMIMEVersion(OUString("1.0"));
866 SetContentType(OStringToOUString(aContentType
.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US
));
867 SetContentTransferEncoding(OUString("7bit"));
873 bool INetMIMEMessage::AttachChild(INetMIMEMessage
& rChildMsg
, bool bOwner
)
877 if (bOwner
) rChildMsg
.pParent
= this;
878 aChildren
.push_back( &rChildMsg
);
885 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */