reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
MultipartMixedContents.cxx
Go to the documentation of this file.
1#if defined(HAVE_CONFIG_H)
2#include "config.h"
3#endif
4
7#include "rutil/Logger.hxx"
8#include "rutil/Random.hxx"
10#include "rutil/ParseBuffer.hxx"
12
13using namespace resip;
14using namespace std;
15
16#define RESIPROCATE_SUBSYSTEM Subsystem::CONTENTS
17
18bool
20{
22 (void)factory;
23 return true;
24}
25
27 : Contents(getStaticType()),
28 mContents(),
29 mRawFirstPart(),
30 mDepth(0)
31{
33}
34
36 : Contents(contentsType),
37 mContents(),
38 mRawFirstPart(),
39 mDepth(0)
40{
41 if (!mType.exists(p_boundary))
42 {
44 }
45}
46
48 : Contents(hfv, contentsType),
49 mContents(),
50 mRawFirstPart(),
51 mDepth(0)
52{
53 if (!mType.exists(p_boundary))
54 {
56 }
57}
58
59// mRawFirstPart is deliberately not carried over: it is a view into the buffer
60// rhs was parsed from, and the copy has a buffer of its own. Whether the copy
61// ends up with a record depends on when it was taken. Copying an unparsed body
62// leaves the copy unparsed as well, holding a deep copy of the buffer, and its
63// first access parses that buffer and records a view into it. Copying a body
64// that was already parsed leaves the parts and no record, because nothing
65// parses a second time, and checkSignature() then falls back to the
66// re-encoding. See getRawFirstPart() in the header.
68 : Contents(rhs),
69 mContents(),
70 mRawFirstPart(),
71 mDepth(rhs.mDepth)
72{
73 vector<Contents*>::const_iterator j;
74
75 // .bwc. Don't trigger a parse of the original by calling parts()
76 const vector<Contents*>& list = rhs.mContents;
77
78 for ( j = list.begin();
79 j != list.end(); ++j)
80 {
81 resip_assert( *j );
82 mContents.push_back( (*j)->clone() );
83 }
84}
85
86void
87MultipartMixedContents::setBoundary()
88{
89 Data boundaryToken = Random::getRandomHex(8);
90 mType.param(p_boundary) = boundaryToken;
91}
92
93void
94MultipartMixedContents::setBoundary(const Data& boundary)
95{
96 mType.param(p_boundary) = boundary;
97}
98
99Data
101{
102 // The const overload of checkParsed() parses without marking the object
103 // DIRTY. Calling the non-const one here would drop the very bytes this
104 // function exists to return.
105 checkParsed();
106 return mRawFirstPart;
107}
108
109void
111{
112 for (vector<Contents*>::iterator i = mContents.begin();
113 i != mContents.end(); ++i)
114 {
115 delete *i;
116 }
117 // Assign a fresh Data rather than clear(): clear() keeps the shared pointer
118 // and only sets the size to zero.
119 mRawFirstPart = Data();
120}
121
126
129{
130 if (this != &rhs)
131 {
132 // Drop the view first: Contents::operator=() replaces the buffer it
133 // points into. clear() below would do it too, but only after that, and
134 // a reader should not have to reason about the order. Whether a record
135 // reappears afterwards depends on when the assignment happened, exactly
136 // as for the copy constructor: from a source nothing has read, this
137 // object stays unparsed with a buffer of its own and its first access
138 // records a view into that buffer; from a source that was already
139 // parsed, no record.
140 mRawFirstPart = Data();
142 clear();
143 mDepth = rhs.mDepth;
144
145 for (vector<Contents*>::const_iterator i = rhs.mContents.begin();
146 i != rhs.mContents.end(); ++i)
147 {
148 mContents.push_back( (*i)->clone() );
149 }
150 }
151 return *this;
152}
153
154Contents*
156{
157 return new MultipartMixedContents(*this);
158}
159
160const Mime&
162{
163 static Mime type("multipart","mixed");
164 return type;
165}
166
169{
170 const Data& boundaryToken = mType.param(p_boundary);
171 Data boundary(boundaryToken.size() + 2, Data::Preallocate);
172 boundary = Symbols::DASHDASH;
173 boundary += boundaryToken;
174 boundary.replace("\"", ""); // remove quotes
175
176 resip_assert( mContents.size() > 0 );
177
178 bool first = true;
179 for (vector<Contents*>::const_iterator i = mContents.begin();
180 i != mContents.end(); ++i)
181 {
182 if (!first)
183 {
184 str << Symbols::CRLF;
185 }
186 else
187 {
188 first = false;
189 }
190 str << boundary << Symbols::CRLF;
191 (*i)->encodeHeaders(str);
192 (*i)->encode(str);
193 }
194
195 // Note: last CRLF isn't strictly required by RFC2046 ABNF, but some implementations seem to require it so we include it
196 str << Symbols::CRLF << boundary << Symbols::DASHDASH << Symbols::CRLF;
197 return str;
198}
199
200// The boundary delimiter MUST occur at the beginning of a line, i.e., following
201// a CRLF, and the initial CRLF is considered to be attached to the boundary
202// delimiter line rather than part of the preceding part.
203void
205{
206 // The depth is stamped on this object when the enclosing part created it
207 // (below), so this holds whether the parse runs right away or much later
208 // from a tree walk such as Helper::getSdp().
209 if (mDepth > MaxNestingDepth)
210 {
211 throw Exception("Multipart nesting too deep", __FILE__, __LINE__);
212 }
213
214 const Data& boundaryToken = mType.param(p_boundary);
215
216 Data boundary(boundaryToken.size() + 4, Data::Preallocate);
217 boundary += Symbols::CRLF;
218 boundary += Symbols::DASHDASH;
219 boundary += boundaryToken;
220
221 Data boundaryNoCRLF(boundaryToken.size() + 2, Data::Preallocate);
222 boundaryNoCRLF += Symbols::DASHDASH;
223 boundaryNoCRLF += boundaryToken;
224
225 pb.skipToChars(boundaryNoCRLF);
226 pb.skipN(boundaryNoCRLF.size());
227 pb.assertNotEof();
228
229 // Start from a known state instead of inferring "first part" from mContents
230 // being empty: clear() deletes the parts but leaves the vector filled.
231 bool firstPartSeen = false;
232 mRawFirstPart = Data();
233
234 do
235 {
236 // skip over boundary
237
238 if( pb.eof() || *pb.position() != Symbols::CR[0] )
239 {
240 throw Exception("Invalid line ending, missing CR",__FILE__,__LINE__);
241 }
242 pb.skipChar();
243 if( pb.eof() || *pb.position() != Symbols::LF[0] )
244 {
245 throw Exception("Invalid line ending, missing LF",__FILE__,__LINE__);
246 }
247 pb.skipChar();
248
249 pb.assertNotEof();
250
251 const char* headerStart = pb.position();
252
253 // pull out contents type only
254 pb.skipToChars("Content-Type");
255 if (pb.eof())
256 {
257 pb.reset(headerStart);
258 pb.skipToChars("CONTENT-TYPE");
259 }
260 pb.assertNotEof();
261
263 pb.skipChar();
264 pb.assertNotEof();
265
266 pb.skipWhitespace();
267 const char* typeStart = pb.position();
268 pb.assertNotEof();
269
270 // determine contents-type header buffer
271 pb.skipToTermCRLF();
272 pb.assertNotEof();
273
274 ParseBuffer subPb(typeStart, pb.position() - typeStart);
275 Mime contentType;
276 contentType.parse(subPb);
277
278 pb.assertNotEof();
279
280 // determine body start
281 pb.reset(typeStart);
282 const char* bodyStart = pb.skipToChars(Symbols::CRLFCRLF);
283 pb.assertNotEof();
284 bodyStart += 4;
285
286 // determine contents body buffer
287 pb.skipToChars(boundary);
288 pb.assertNotEof();
289 Data tmp;
290 pb.data(tmp, bodyStart);
291 if (!firstPartSeen)
292 {
293 firstPartSeen = true;
294 // Keep a view of the first part exactly as it arrived, header block
295 // included. The parse position sits on the CRLF that belongs to the
296 // next boundary (RFC 2046 5.1.1), so that CRLF is correctly left out.
297 // Data::Share means this points into the buffer being parsed instead
298 // of copying it, so an ordinary multipart message costs nothing here.
299 // Only the first part is kept: it is the one RFC 1847 signs.
300 mRawFirstPart = Data(Data::Share, headerStart,
301 static_cast<Data::size_type>(pb.position() - headerStart));
302 }
303 // create contents against body
304 mContents.push_back(createContents(contentType, tmp));
305 // A nested multipart part (of any subtype) sits one level deeper.
306 MultipartMixedContents* nested = dynamic_cast<MultipartMixedContents*>(mContents.back());
307 if (nested)
308 {
309 nested->mDepth = mDepth + 1;
310 }
311 // pre-parse headers
312 ParseBuffer headersPb(headerStart, bodyStart-4-headerStart);
313 mContents.back()->preParseHeaders(headersPb);
314
315 pb.skipN(boundary.size());
316
317 const char* loc = pb.position();
318 pb.skipChar();
319 pb.skipChar();
320 Data next;
321 pb.data(next, loc);
322
323 if ( next == Symbols::DASHDASH )
324 {
325 break;
326 }
327 pb.reset( loc );
328 }
329 while ( !pb.eof() );
330
331}
332
333/* ====================================================================
334 * The Vovida Software License, Version 1.0
335 *
336 * Copyright (c) 2026 SIP Spectrum, Inc. https://www.sipspectrum.com
337 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
338 *
339 * Redistribution and use in source and binary forms, with or without
340 * modification, are permitted provided that the following conditions
341 * are met:
342 *
343 * 1. Redistributions of source code must retain the above copyright
344 * notice, this list of conditions and the following disclaimer.
345 *
346 * 2. Redistributions in binary form must reproduce the above copyright
347 * notice, this list of conditions and the following disclaimer in
348 * the documentation and/or other materials provided with the
349 * distribution.
350 *
351 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
352 * and "Vovida Open Communication Application Library (VOCAL)" must
353 * not be used to endorse or promote products derived from this
354 * software without prior written permission. For written
355 * permission, please contact vocal@vovida.org.
356 *
357 * 4. Products derived from this software may not be called "VOCAL", nor
358 * may "VOCAL" appear in their name, without prior written
359 * permission of Vovida Networks, Inc.
360 *
361 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
362 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
363 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
364 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
365 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
366 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
367 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
368 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
369 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
370 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
371 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
372 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
373 * DAMAGE.
374 *
375 * ====================================================================
376 *
377 * This software consists of voluntary contributions made by Vovida
378 * Networks, Inc. and many individuals on behalf of Vovida Networks,
379 * Inc. For more information on Vovida Networks, Inc., please see
380 * <http://www.vovida.org/>.
381 *
382 */
#define resip_assert(x)
This file needs to work for both C and C++ code.
Base class for all SIP body types.
Definition Contents.hxx:51
Contents & operator=(const Contents &rhs)
Assignment operator.
Definition Contents.cxx:71
static Contents * createContents(const Mime &contentType, const Data &contents)
factory method to create a Contents object from a mime type and a payload
Definition Contents.cxx:148
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
@ Share
The Data instance will use the buffer in a read-only mode.
Definition Data.hxx:266
int replace(const Data &match, const Data &target, int max=INT_MAX)
Replaces up to max occurrences of the bytes match with target.
Definition Data.cxx:1964
static const PreallocateType Preallocate
used only to disambiguate constructors
Definition Data.hxx:116
size_t size_type
Definition Data.hxx:94
size_type size() const noexcept
Returns the number of bytes in this Data.
Definition Data.hxx:599
void checkParsed() const
Represents the "media-type" element in the RFC 3261 grammar.
Definition Mime.hxx:20
virtual void parse(ParseBuffer &pb)
Definition Mime.cxx:120
bool exists(const Param< Mime > &paramType) const
Definition Mime.cxx:178
const Data & param(const ExtensionParameter &param) const
Const accessor for non-natively-supported parameter types.
Thrown when there is an error parsing a multi-part mixed contents envelope.
SIP body type for holding Multipart-Mixed body contents (MIME content-type multipart/mixed).
static constexpr unsigned int MaxNestingDepth
parse() fails (Exception) a body nested more than this many multipart levels below the outermost one.
Data getRawFirstPart() const
The first body part exactly as it arrived: header block, the empty line, and the body.
virtual Contents * clone() const
duplicate an MultipartMixedContents object
virtual void parse(ParseBuffer &pb)
virtual EncodeStream & encodeParsed(EncodeStream &str) const
void setBoundary(const Data &boundary)
MultipartMixedContents & operator=(const MultipartMixedContents &rhs)
Provides string-parsing functionality with protection from buffer overflow.
CurrentPosition skipWhitespace()
CurrentPosition skipToChars(const char *cs)
CurrentPosition skipToChar(char c)
CurrentPosition position() const
void reset(const char *pos)
void assertNotEof() const
CurrentPosition skipN(size_t count)
CurrentPosition skipChar()
CurrentPosition skipToTermCRLF()
void data(Data &data, const char *start) const
make the passed in data share memory with the buffer (uses Data::Share)
static Data getRandomHex(unsigned int numBytes)
Definition Random.cxx:382
static const char * LF
Definition Symbols.hxx:24
static const char * COLON
Definition Symbols.hxx:15
static const char * DASHDASH
Definition Symbols.hxx:18
static const char * CR
Definition Symbols.hxx:23
static const char * CRLFCRLF
Definition Symbols.hxx:22
static const char * CRLF
Definition Symbols.hxx:21
dcm! – add UnusedChecking(_enum) below;
#define EncodeStream
void list()
Definition userAdmin.cxx:66