/src/libreoffice/tools/source/inet/inetmsg.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
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/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
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 . |
18 | | */ |
19 | | |
20 | | #include <sal/types.h> |
21 | | #include <tools/datetime.hxx> |
22 | | #include <tools/inetmsg.hxx> |
23 | | #include <comphelper/string.hxx> |
24 | | #include <rtl/character.hxx> |
25 | | #include <o3tl/safeint.hxx> |
26 | | #include <o3tl/sprintf.hxx> |
27 | | #include <o3tl/string_view.hxx> |
28 | | |
29 | | #include <map> |
30 | | |
31 | | void INetMIMEMessage::SetHeaderField_Impl ( |
32 | | const OString &rName, |
33 | | const OUString &rValue, |
34 | | sal_uInt32 &rnIndex) |
35 | 0 | { |
36 | 0 | SetHeaderField_Impl ( |
37 | 0 | INetMessageHeader (rName, rValue.toUtf8()), rnIndex); |
38 | 0 | } |
39 | | |
40 | | /* ParseDateField and local helper functions. |
41 | | * |
42 | | * Parses a String in (implied) GMT format into class Date and tools::Time objects. |
43 | | * Four formats are accepted: |
44 | | * |
45 | | * [Wkd,] 1*2DIGIT Mon 2*4DIGIT 00:00:00 [GMT] (rfc1123) |
46 | | * [Wkd,] 00 Mon 0000 00:00:00 [GMT]) (rfc822, rfc1123) |
47 | | * Weekday, 00-Mon-00 00:00:00 [GMT] (rfc850, rfc1036) |
48 | | * Wkd Mon 00 00:00:00 0000 [GMT] (ctime) |
49 | | * 1*DIGIT (delta seconds) |
50 | | */ |
51 | | |
52 | | const char * const months[12] = |
53 | | { |
54 | | "Jan", "Feb", "Mar", "Apr", "May", "Jun", |
55 | | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" |
56 | | }; |
57 | | |
58 | | static sal_uInt16 ParseNumber(std::string_view rStr, size_t& nIndex) |
59 | 0 | { |
60 | 0 | size_t n = nIndex; |
61 | 0 | while ((n < rStr.size()) |
62 | 0 | && rtl::isAsciiDigit(static_cast<unsigned char>(rStr[n]))) |
63 | 0 | n++; |
64 | |
|
65 | 0 | std::string_view aNum(rStr.substr(nIndex, (n - nIndex))); |
66 | 0 | nIndex = n; |
67 | |
|
68 | 0 | return static_cast<sal_uInt16>(o3tl::toInt32(aNum)); |
69 | 0 | } |
70 | | |
71 | | static sal_uInt16 ParseMonth(std::string_view rStr, size_t& nIndex) |
72 | 0 | { |
73 | 0 | size_t n = nIndex; |
74 | 0 | while ((n < rStr.size()) |
75 | 0 | && rtl::isAsciiAlpha(static_cast<unsigned char>(rStr[n]))) |
76 | 0 | n++; |
77 | |
|
78 | 0 | std::string_view aMonth(rStr.substr(nIndex, 3)); |
79 | 0 | nIndex = n; |
80 | |
|
81 | 0 | sal_uInt16 i; |
82 | 0 | for (i = 0; i < 12; i++) |
83 | 0 | if (o3tl::equalsIgnoreAsciiCase(aMonth, months[i])) break; |
84 | 0 | return (i + 1); |
85 | 0 | } |
86 | | |
87 | | bool INetMIMEMessage::ParseDateField ( |
88 | | std::u16string_view rDateFieldW, DateTime& rDateTime) |
89 | 0 | { |
90 | 0 | OString aDateField(OUStringToOString(rDateFieldW, |
91 | 0 | RTL_TEXTENCODING_ASCII_US)); |
92 | |
|
93 | 0 | if (aDateField.isEmpty()) return false; |
94 | | |
95 | 0 | if (aDateField.indexOf(':') != -1) |
96 | 0 | { |
97 | | // Some DateTime format. |
98 | 0 | size_t nIndex = 0; |
99 | | |
100 | | // Skip over <Wkd> or <Weekday>, leading and trailing space. |
101 | 0 | while ((nIndex < o3tl::make_unsigned(aDateField.getLength())) && |
102 | 0 | (aDateField[nIndex] == ' ')) |
103 | 0 | nIndex++; |
104 | |
|
105 | 0 | while ( |
106 | 0 | (nIndex < o3tl::make_unsigned(aDateField.getLength())) && |
107 | 0 | (rtl::isAsciiAlpha (static_cast<unsigned char>(aDateField[nIndex])) || |
108 | 0 | (aDateField[nIndex] == ',') )) |
109 | 0 | nIndex++; |
110 | |
|
111 | 0 | while ((nIndex < o3tl::make_unsigned(aDateField.getLength())) && |
112 | 0 | (aDateField[nIndex] == ' ')) |
113 | 0 | nIndex++; |
114 | |
|
115 | 0 | if (rtl::isAsciiAlpha (static_cast<unsigned char>(aDateField[nIndex]))) |
116 | 0 | { |
117 | | // Format: ctime(). |
118 | 0 | if ((aDateField.getLength() - nIndex) < 20) return false; |
119 | | |
120 | 0 | rDateTime.SetMonth (ParseMonth (aDateField, nIndex)); nIndex++; |
121 | 0 | rDateTime.SetDay (ParseNumber (aDateField, nIndex)); nIndex++; |
122 | |
|
123 | 0 | rDateTime.SetHour (ParseNumber (aDateField, nIndex)); nIndex++; |
124 | 0 | rDateTime.SetMin (ParseNumber (aDateField, nIndex)); nIndex++; |
125 | 0 | rDateTime.SetSec (ParseNumber (aDateField, nIndex)); nIndex++; |
126 | 0 | rDateTime.SetNanoSec (0); |
127 | |
|
128 | 0 | sal_uInt16 nYear = ParseNumber (aDateField, nIndex); |
129 | 0 | if (nYear < 100) nYear += 1900; |
130 | 0 | rDateTime.SetYear (nYear); |
131 | 0 | } |
132 | 0 | else |
133 | 0 | { |
134 | | // Format: RFC1036 or RFC1123. |
135 | 0 | if ((aDateField.getLength() - nIndex) < 17) return false; |
136 | | |
137 | 0 | rDateTime.SetDay (ParseNumber (aDateField, nIndex)); nIndex++; |
138 | 0 | rDateTime.SetMonth (ParseMonth (aDateField, nIndex)); nIndex++; |
139 | |
|
140 | 0 | sal_uInt16 nYear = ParseNumber (aDateField, nIndex); nIndex++; |
141 | 0 | if (nYear < 100) nYear += 1900; |
142 | 0 | rDateTime.SetYear (nYear); |
143 | |
|
144 | 0 | rDateTime.SetHour (ParseNumber (aDateField, nIndex)); nIndex++; |
145 | 0 | rDateTime.SetMin (ParseNumber (aDateField, nIndex)); nIndex++; |
146 | 0 | rDateTime.SetSec (ParseNumber (aDateField, nIndex)); nIndex++; |
147 | 0 | rDateTime.SetNanoSec (0); |
148 | |
|
149 | 0 | const char cPossiblePlusMinus = nIndex < o3tl::make_unsigned(aDateField.getLength()) ? aDateField[nIndex] : 0; |
150 | 0 | if (cPossiblePlusMinus == '+' || cPossiblePlusMinus == '-') |
151 | 0 | { |
152 | | // Offset from GMT: "(+|-)HHMM". |
153 | 0 | bool bEast = (aDateField[nIndex++] == '+'); |
154 | 0 | sal_uInt16 nOffset = ParseNumber (aDateField, nIndex); |
155 | 0 | if (nOffset > 0) |
156 | 0 | { |
157 | 0 | tools::Time aDiff( tools::Time::EMPTY ); |
158 | 0 | aDiff.SetHour (nOffset / 100); |
159 | 0 | aDiff.SetMin (nOffset % 100); |
160 | 0 | aDiff.SetSec (0); |
161 | 0 | aDiff.SetNanoSec (0); |
162 | |
|
163 | 0 | if (bEast) |
164 | 0 | rDateTime -= aDiff; |
165 | 0 | else |
166 | 0 | rDateTime += aDiff; |
167 | 0 | } |
168 | 0 | } |
169 | 0 | } |
170 | 0 | } |
171 | 0 | else if (comphelper::string::isdigitAsciiString(aDateField)) |
172 | 0 | { |
173 | | // Format: delta seconds. |
174 | 0 | tools::Time aDelta(tools::Time::EMPTY); |
175 | 0 | aDelta.SetTime (aDateField.toInt32() * 100); |
176 | |
|
177 | 0 | DateTime aNow( DateTime::SYSTEM ); |
178 | 0 | aNow += aDelta; |
179 | 0 | aNow.ConvertToUTC(); |
180 | |
|
181 | 0 | rDateTime.SetDate (aNow.GetDate()); |
182 | 0 | rDateTime.SetTime (aNow.GetTime()); |
183 | 0 | } |
184 | 0 | else |
185 | 0 | { |
186 | | // Junk. |
187 | 0 | return false; |
188 | 0 | } |
189 | | |
190 | 0 | return (rDateTime.IsValidAndGregorian() && |
191 | 0 | !((rDateTime.GetSec() > 59) || |
192 | 0 | (rDateTime.GetMin() > 59) || |
193 | 0 | (rDateTime.GetHour() > 23) )); |
194 | 0 | } |
195 | | |
196 | | const std::map<InetMessageMime, const char*> ImplINetMIMEMessageHeaderData = |
197 | | { |
198 | | { InetMessageMime::VERSION, "MIME-Version"}, |
199 | | { InetMessageMime::CONTENT_DISPOSITION, "Content-Disposition"}, |
200 | | { InetMessageMime::CONTENT_TYPE, "Content-Type"}, |
201 | | { InetMessageMime::CONTENT_TRANSFER_ENCODING, "Content-Transfer-Encoding"} |
202 | | }; |
203 | | |
204 | | INetMIMEMessage::INetMIMEMessage() |
205 | 0 | : pParent(nullptr) |
206 | 0 | { |
207 | 0 | for (sal_uInt16 i = 0; i < static_cast<int>(InetMessageMime::NUMHDR); i++) |
208 | 0 | m_nMIMEIndex[static_cast<InetMessageMime>(i)] = SAL_MAX_UINT32; |
209 | 0 | } |
210 | | |
211 | | INetMIMEMessage::~INetMIMEMessage() |
212 | 0 | { |
213 | 0 | } |
214 | | |
215 | | void INetMIMEMessage::SetMIMEVersion (const OUString& rVersion) |
216 | 0 | { |
217 | 0 | SetHeaderField_Impl ( |
218 | 0 | ImplINetMIMEMessageHeaderData.at(InetMessageMime::VERSION), rVersion, |
219 | 0 | m_nMIMEIndex[InetMessageMime::VERSION]); |
220 | 0 | } |
221 | | |
222 | | void INetMIMEMessage::SetContentDisposition (const OUString& rDisposition) |
223 | 0 | { |
224 | 0 | SetHeaderField_Impl ( |
225 | 0 | ImplINetMIMEMessageHeaderData.at(InetMessageMime::CONTENT_DISPOSITION), rDisposition, |
226 | 0 | m_nMIMEIndex[InetMessageMime::CONTENT_DISPOSITION]); |
227 | 0 | } |
228 | | |
229 | | void INetMIMEMessage::SetContentType (const OUString& rType) |
230 | 0 | { |
231 | 0 | SetHeaderField_Impl ( |
232 | 0 | ImplINetMIMEMessageHeaderData.at(InetMessageMime::CONTENT_TYPE), rType, |
233 | 0 | m_nMIMEIndex[InetMessageMime::CONTENT_TYPE]); |
234 | 0 | } |
235 | | |
236 | | void INetMIMEMessage::SetContentTransferEncoding ( |
237 | | const OUString& rEncoding) |
238 | 0 | { |
239 | 0 | SetHeaderField_Impl ( |
240 | 0 | ImplINetMIMEMessageHeaderData.at(InetMessageMime::CONTENT_TRANSFER_ENCODING), rEncoding, |
241 | 0 | m_nMIMEIndex[InetMessageMime::CONTENT_TRANSFER_ENCODING]); |
242 | 0 | } |
243 | | |
244 | | OUString INetMIMEMessage::GetDefaultContentType() |
245 | 0 | { |
246 | 0 | if (pParent != nullptr) |
247 | 0 | { |
248 | 0 | OUString aParentCT (pParent->GetContentType()); |
249 | 0 | if (aParentCT.isEmpty()) |
250 | 0 | aParentCT = pParent->GetDefaultContentType(); |
251 | |
|
252 | 0 | if (aParentCT.equalsIgnoreAsciiCase("multipart/digest")) |
253 | 0 | return u"message/rfc822"_ustr; |
254 | 0 | } |
255 | 0 | return u"text/plain; charset=us-ascii"_ustr; |
256 | 0 | } |
257 | | |
258 | | void INetMIMEMessage::EnableAttachMultipartFormDataChild() |
259 | 0 | { |
260 | | // Check context. |
261 | 0 | if (IsContainer()) |
262 | 0 | return; |
263 | | |
264 | | // Generate a unique boundary from current time. |
265 | 0 | char sTail[16 + 1]; |
266 | 0 | tools::Time aCurTime( tools::Time::SYSTEM ); |
267 | 0 | sal_uInt64 nThis = reinterpret_cast< sal_uIntPtr >( this ); // we can be on a 64bit architecture |
268 | 0 | nThis = ( ( nThis >> 32 ) ^ nThis ) & SAL_MAX_UINT32; |
269 | 0 | o3tl::sprintf (sTail, "%08X%08X", |
270 | 0 | static_cast< unsigned int >(aCurTime.GetTime()), |
271 | 0 | static_cast< unsigned int >(nThis)); |
272 | 0 | m_aBoundary = "------------_4D48"_ostr; |
273 | 0 | m_aBoundary += sTail; |
274 | | |
275 | | // Set header fields. |
276 | 0 | SetMIMEVersion(u"1.0"_ustr); |
277 | 0 | SetContentType( |
278 | 0 | "multipart/form-data; boundary=" + OUString::fromUtf8(m_aBoundary)); |
279 | 0 | SetContentTransferEncoding(u"7bit"_ustr); |
280 | 0 | } |
281 | | |
282 | | void INetMIMEMessage::AttachChild(std::unique_ptr<INetMIMEMessage> pChildMsg) |
283 | 0 | { |
284 | 0 | assert(IsContainer()); |
285 | 0 | if (IsContainer()) |
286 | 0 | { |
287 | 0 | pChildMsg->pParent = this; |
288 | 0 | aChildren.push_back( std::move(pChildMsg) ); |
289 | 0 | } |
290 | 0 | } |
291 | | |
292 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |