Coverage Report

Created: 2026-06-30 11:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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: */