reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
Contents.cxx
Go to the documentation of this file.
1#include <vector>
2
3#if defined(HAVE_CONFIG_H)
4#include "config.h"
5#endif
6
9#include "rutil/Logger.hxx"
13
14using namespace resip;
15using namespace std;
16
17#define RESIPROCATE_SUBSYSTEM Subsystem::CONTENTS
18
21
22Contents::Contents(const HeaderFieldValue& headerFieldValue,
23 const Mime& contentType)
24 : LazyParser(headerFieldValue),
25 mType(contentType)
26{
27 init();
28}
29
30Contents::Contents(const Mime& contentType)
31 : mType(contentType)
32{
33 init();
34}
35
37 : LazyParser(rhs)
38{
39 init(rhs);
40}
41
47
48Contents::Contents(const HeaderFieldValue& headerFieldValue,
50 const Mime& contentsType)
51 : LazyParser(headerFieldValue,e),
52 mType(contentsType)
53{
54 init();
55}
56
57
62
63static const Data errorContextData("Contents");
64const Data&
66{
67 return errorContextData;
68}
69
72{
73 if (this != &rhs)
74 {
75 freeMem();
77 init(rhs);
78 }
79
80 return *this;
81}
82
83void
85{
86 mBufferList.clear();
87 mType = orig.mType;
88 if (orig.mDisposition)
89 {
90 mDisposition = new H_ContentDisposition::Type(*orig.mDisposition);
91 }
92 else
93 {
94 mDisposition = 0;
95 }
96
97 if (orig.mTransferEncoding)
98 {
99 mTransferEncoding = new H_ContentTransferEncoding::Type(*orig.mTransferEncoding);
100 }
101 else
102 {
104 }
105
106 if (orig.mLanguages)
107 {
108 mLanguages = new H_ContentLanguages::Type(*orig.mLanguages);
109 }
110 else
111 {
112 mLanguages = 0;
113 }
114
115 if (orig.mId)
116 {
117 mId = new Token(*orig.mId);
118 }
119 else
120 {
121 mId = 0;
122 }
123
124 if (orig.mDescription)
125 {
127 }
128 else
129 {
130 mDescription = 0;
131 }
132
133 if(orig.mLength)
134 {
135 mLength = new StringCategory(*orig.mLength);
136 }
137 else
138 {
139 mLength = 0;
140 }
141
142 mVersion = orig.mVersion;
144
145}
146
148Contents::createContents(const Mime& contentType,
149 const Data& contents)
150{
151 // !ass! why are we asserting that the Data doesn't own the buffer?
152 // .dlb. because this method is to be called only within a multipart
153 // !ass! HFV is an overlay -- then setting c->mIsMine to true ?? dlb Q
154 // .dlb. we are telling the content that it owns its HFV, not the data that it
155 // .dlb. owns its memory
156 // .bwc. So, we are _violating_ _encapsulation_, to make an assertion that the
157 // Data is an intermediate instead of owning the buffer itself? What do we
158 // care how this buffer is owned? All we require is that the buffer doesn't
159 // get dealloced/modified while we're still around. The assertion might mean
160 // that the Data will not do either of these things, but it affords no
161 // protection from the actual owner doing so. We are no more protected with
162 // the assertion, so I am removing it.
163// assert(!contents.mMine);
164
165 HeaderFieldValue hfv(contents.data(), (unsigned int)contents.size());
166
167// !bwc! This padding stuff is now the responsibility of the Contents class.
168// if(contentType.subType()=="sipfrag"||contentType.subType()=="external-body")
169// {
170// // .bwc. The parser for sipfrag requires padding at the end of the hfv.
171// HeaderFieldValue* temp = hfv;
172// hfv = new HeaderFieldValue(*temp,HeaderFieldValue::CopyPadding);
173// delete temp;
174// }
175
176 Contents* c;
178 {
179 c = ContentsFactoryBase::getFactoryMap()[contentType]->create(hfv, contentType);
180 }
181 else
182 {
183 c = new OctetContents(hfv, contentType);
184 }
185 return c;
186}
187
188bool
189Contents::exists(const HeaderBase& headerType) const
190{
191 checkParsed();
192 switch (headerType.getTypeNum())
193 {
194 case Headers::ContentType :
195 {
196 return true;
197 }
198 case Headers::ContentDisposition :
199 {
200 return mDisposition != 0;
201 }
202 case Headers::ContentTransferEncoding :
203 {
204 return mTransferEncoding != 0;
205 }
206 case Headers::ContentLanguage :
207 {
208 return mLanguages != 0;
209 }
210 default : return false;
211 }
212}
213
214bool
216{
217 if (&type == &h_ContentID)
218 {
219 return mId != 0;
220 }
221
222 if (&type == &h_ContentDescription)
223 {
224 return mDescription != 0;
225 }
226
227 resip_assert(false);
228 return false;
229}
230
231void
232Contents::remove(const HeaderBase& headerType)
233{
234 switch (headerType.getTypeNum())
235 {
236 case Headers::ContentDisposition :
237 {
238 delete mDisposition;
239 mDisposition = 0;
240 break;
241 }
242 case Headers::ContentLanguage :
243 {
244 delete mLanguages;
245 mLanguages = 0;
246 break;
247 }
248 case Headers::ContentTransferEncoding :
249 {
250 delete mTransferEncoding;
252 break;
253 }
254 default :
255 ;
256 }
257}
258
259void
261{
262 if (&type == &h_ContentID)
263 {
264 delete mId;
265 mId = 0;
266 return;
267 }
268
269 if (&type == &h_ContentDescription)
270 {
271 delete mDescription;
272 mDescription = 0;
273 return;
274 }
275
276 resip_assert(false);
277}
278
279const H_ContentType::Type&
280Contents::header(const H_ContentType& headerType) const
281{
282 return mType;
283}
284
285H_ContentType::Type&
286Contents::header(const H_ContentType& headerType)
287{
288 return mType;
289}
290
291const H_ContentDisposition::Type&
292Contents::header(const H_ContentDisposition& headerType) const
293{
294 checkParsed();
295 if (mDisposition == 0)
296 {
297 ErrLog(<< "You called "
298 "Contents::header(const H_ContentDisposition& headerType) _const_ "
299 "without first calling exists(), and the header does not exist. Our"
300 " behavior in this scenario is to implicitly create the header(using const_cast!); "
301 "this is probably not what you want, but it is either this or "
302 "assert/throw an exception. Since this has been the behavior for "
303 "so long, we are not throwing here, _yet_. You need to fix your "
304 "code, before we _do_ start throwing. This is why const-correctness"
305 " should never be made a TODO item </rant>");
306 Contents* ncthis = const_cast<Contents*>(this);
307 ncthis->mDisposition = new H_ContentDisposition::Type;
308 }
309 return *mDisposition;
310}
311
312H_ContentDisposition::Type&
313Contents::header(const H_ContentDisposition& headerType)
314{
315 checkParsed();
316 if (mDisposition == 0)
317 {
318 mDisposition = new H_ContentDisposition::Type;
319 }
320 return *mDisposition;
321}
322
323const H_ContentTransferEncoding::Type&
324Contents::header(const H_ContentTransferEncoding& headerType) const
325{
326 checkParsed();
327 if (mTransferEncoding == 0)
328 {
329 ErrLog(<< "You called "
330 "Contents::header(const H_ContentTransferEncoding& headerType) _const_ "
331 "without first calling exists(), and the header does not exist. Our"
332 " behavior in this scenario is to implicitly create the header(using const_cast!); "
333 "this is probably not what you want, but it is either this or "
334 "assert/throw an exception. Since this has been the behavior for "
335 "so long, we are not throwing here, _yet_. You need to fix your "
336 "code, before we _do_ start throwing. This is why const-correctness"
337 " should never be made a TODO item </rant>");
338 Contents* ncthis = const_cast<Contents*>(this);
339 ncthis->mTransferEncoding = new H_ContentTransferEncoding::Type;
340 }
341 return *mTransferEncoding;
342}
343
344H_ContentTransferEncoding::Type&
345Contents::header(const H_ContentTransferEncoding& headerType)
346{
347 checkParsed();
348 if (mTransferEncoding == 0)
349 {
350 mTransferEncoding = new H_ContentTransferEncoding::Type;
351 }
352 return *mTransferEncoding;
353}
354
355const H_ContentLanguages::Type&
356Contents::header(const H_ContentLanguages& headerType) const
357{
358 checkParsed();
359 if (mLanguages == 0)
360 {
361 ErrLog(<< "You called "
362 "Contents::header(const H_ContentLanguages& headerType) _const_ "
363 "without first calling exists(), and the header does not exist. Our"
364 " behavior in this scenario is to implicitly create the header(using const_cast!); "
365 "this is probably not what you want, but it is either this or "
366 "assert/throw an exception. Since this has been the behavior for "
367 "so long, we are not throwing here, _yet_. You need to fix your "
368 "code, before we _do_ start throwing. This is why const-correctness"
369 " should never be made a TODO item </rant>");
370 Contents* ncthis = const_cast<Contents*>(this);
371 ncthis->mLanguages = new H_ContentLanguages::Type;
372 }
373 return *mLanguages;
374}
375
376H_ContentLanguages::Type&
377Contents::header(const H_ContentLanguages& headerType)
378{
379 checkParsed();
380 if (mLanguages == 0)
381 {
382 mLanguages = new H_ContentLanguages::Type;
383 }
384 return *mLanguages;
385}
386
389{
390 checkParsed();
391 if (mDescription == 0)
392 {
393 ErrLog(<< "You called "
394 "Contents::header(const H_ContentDescription& headerType) _const_ "
395 "without first calling exists(), and the header does not exist. Our"
396 " behavior in this scenario is to implicitly create the header(using const_cast!); "
397 "this is probably not what you want, but it is either this or "
398 "assert/throw an exception. Since this has been the behavior for "
399 "so long, we are not throwing here, _yet_. You need to fix your "
400 "code, before we _do_ start throwing. This is why const-correctness"
401 " should never be made a TODO item </rant>");
402 Contents* ncthis = const_cast<Contents*>(this);
404 }
405 return *mDescription;
406}
407
410{
411 checkParsed();
412 if (mDescription == 0)
413 {
415 }
416 return *mDescription;
417}
418
420Contents::header(const H_ContentID& headerType) const
421{
422 checkParsed();
423 if (mId == 0)
424 {
425 ErrLog(<< "You called "
426 "Contents::header(const H_ContentID& headerType) _const_ "
427 "without first calling exists(), and the header does not exist. Our"
428 " behavior in this scenario is to implicitly create the header(using const_cast!); "
429 "this is probably not what you want, but it is either this or "
430 "assert/throw an exception. Since this has been the behavior for "
431 "so long, we are not throwing here, _yet_. You need to fix your "
432 "code, before we _do_ start throwing. This is why const-correctness"
433 " should never be made a TODO item </rant>");
434 Contents* ncthis = const_cast<Contents*>(this);
435 ncthis->mId = new H_ContentID::Type;
436 }
437 return *mId;
438}
439
442{
443 checkParsed();
444 if (mId == 0)
445 {
447 }
448 return *mId;
449}
450
451// !dlb! headers except Content-Disposition may contain (comments)
452void
454{
455 const char* start = pb.position();
456 Data all( start, (Data::size_type)(pb.end()-start));
457
458 Data headerName;
459
460 try
461 {
462
463 while (!pb.eof())
464 {
465 const char* anchor = pb.skipWhitespace();
467 pb.data(headerName, anchor);
468
469 pb.skipWhitespace();
471 anchor = pb.skipWhitespace();
472 pb.skipToTermCRLF();
473
474 Headers::Type type = Headers::getType(headerName.data(), headerName.size());
475 ParseBuffer subPb(anchor, pb.position() - anchor);
476
477 switch (type)
478 {
479 case Headers::ContentType :
480 {
481 // already set
482 break;
483 }
484 case Headers::ContentDisposition :
485 {
486 mDisposition = new H_ContentDisposition::Type;
487 mDisposition->parse(subPb);
488 break;
489 }
490 case Headers::ContentTransferEncoding :
491 {
492 mTransferEncoding = new H_ContentTransferEncoding::Type;
493 mTransferEncoding->parse(subPb);
494 break;
495 }
496 // !dlb! not sure this ever happens?
497 case Headers::ContentLanguage :
498 {
499 if (mLanguages == 0)
500 {
501 mLanguages = new H_ContentLanguages::Type;
502 }
503
504 subPb.skipWhitespace();
505 while (!subPb.eof() && *subPb.position() != Symbols::COMMA[0])
506 {
507 // Append to mLanguages directly. The header(h_ContentLanguages)
508 // accessor calls checkParsed(), which would parse this object's
509 // whole body while we are still pre-parsing its headers -- for a
510 // multipart part that means eagerly descending into every nested
511 // level instead of leaving it for lazy parsing. Every other
512 // header case here sets its member without parsing the body;
513 // this one should too. mLanguages is non-null by now.
514 H_ContentLanguages::Type::value_type tmp;
515 mLanguages->push_back(tmp);
516 mLanguages->back().parse(subPb);
517 subPb.skipLWS();
518 }
519 break; // .kw. added -- this is needed, right?
520 }
521 default :
522 {
523 if (isEqualNoCase(headerName, "Content-Transfer-Encoding"))
524 {
526 mTransferEncoding->parse(subPb);
527 }
528 else if (isEqualNoCase(headerName, "Content-Description"))
529 {
531 mDescription->parse(subPb);
532 }
533 else if (isEqualNoCase(headerName, "Content-Id"))
534 {
535 mId = new Token();
536 mId->parse(subPb);
537 }
538 // Some people put this in ...
539 else if (isEqualNoCase(headerName, "Content-Length"))
540 {
541 mLength = new StringCategory();
542 mLength->parse(subPb);
543 }
544 else if (isEqualNoCase(headerName, "MIME-Version"))
545 {
546 subPb.skipWhitespace();
547 if (!subPb.eof() && *subPb.position() == Symbols::LPAREN[0])
548 {
550 subPb.skipChar(Symbols::RPAREN[0]);
551 }
552 mVersion = subPb.integer();
553
554 if (!subPb.eof() && *subPb.position() == Symbols::LPAREN[0])
555 {
557 subPb.skipChar(Symbols::RPAREN[0]);
558 }
559 subPb.skipChar(Symbols::PERIOD[0]);
560
561 if (!subPb.eof() && *subPb.position() == Symbols::LPAREN[0])
562 {
564 subPb.skipChar(Symbols::RPAREN[0]);
565 }
566
567 mMinorVersion = subPb.integer();
568 }
569 else
570 {
571 // add to application headers someday
572 std::cerr << "Unknown MIME Content- header: " << headerName << std::endl;
573 ErrLog(<< "Unknown MIME Content- header: " << headerName);
574 resip_assert(false);
575 }
576 }
577 }
578 }
579 }
580 catch (ParseException & e )
581 {
582 ErrLog( << "Some problem parsing contents: " << e );
583 throw e;
584 }
585}
586
588Contents::encodeHeaders(EncodeStream& str) const
589{
590 if (mVersion != 1 || mMinorVersion != 0)
591 {
592 str << "MIME-Version" << Symbols::COLON[0] << Symbols::SPACE[0]
594 << Symbols::CRLF;
595 }
596
597 str << "Content-Type" << Symbols::COLON[0] << Symbols::SPACE[0]
598 << mType
599 << Symbols::CRLF;
600
601 if (exists(h_ContentDisposition))
602 {
603 str << "Content-Disposition" << Symbols::COLON[0] << Symbols::SPACE[0];
604
605 header(h_ContentDisposition).encode(str);
606 str << Symbols::CRLF;
607 }
608
609 if (exists(h_ContentLanguages))
610 {
611 str << "Content-Languages" << Symbols::COLON[0] << Symbols::SPACE[0];
612
613 size_t count = 0;
614 size_t size = header(h_ContentLanguages).size();
615
616 for (H_ContentLanguages::Type::const_iterator
617 i = header(h_ContentLanguages).begin();
618 i != header(h_ContentLanguages).end(); ++i)
619 {
620 i->encode(str);
621
622 if (++count < size)
624 }
625 str << Symbols::CRLF;
626 }
627
629 {
630 str << "Content-Transfer-Encoding" << Symbols::COLON[0] << Symbols::SPACE[0]
632 << Symbols::CRLF;
633 }
634
635 if (mId)
636 {
637 str << "Content-Id" << Symbols::COLON[0] << Symbols::SPACE[0]
638 << *mId
639 << Symbols::CRLF;
640 }
641
642 if (mDescription)
643 {
644 str << "Content-Description" << Symbols::COLON[0] << Symbols::SPACE[0]
645 << *mDescription
646 << Symbols::CRLF;
647 }
648
649 if (mLength)
650 {
651 str << "Content-Length" << Symbols::COLON[0] << Symbols::SPACE[0]
652 << *mLength
653 << Symbols::CRLF;
654 }
655
656 str << Symbols::CRLF;
657 return str;
658}
659
660Data
662{
663 checkParsed();
664 return Data::from(*this);
665}
666
667void
669{
670 mBufferList.push_back(buf);
671}
672
673bool
674resip::operator==(const Contents& lhs, const Contents& rhs)
675{
676 DigestStream lhsStream;
677 lhsStream << lhs;
678 DigestStream rhsStream;
679 rhsStream << rhs;
680 return lhsStream.getHex() == rhsStream.getHex();
681}
682
683bool
684resip::operator!=(const Contents& lhs, const Contents& rhs)
685{
686 return !operator==(lhs,rhs);
687}
688
689/* ====================================================================
690 * The Vovida Software License, Version 1.0
691 *
692 * Copyright (c) 2026 SIP Spectrum, Inc. https://www.sipspectrum.com
693 * Copyright (c) 2000-2005
694 *
695 * Redistribution and use in source and binary forms, with or without
696 * modification, are permitted provided that the following conditions
697 * are met:
698 *
699 * 1. Redistributions of source code must retain the above copyright
700 * notice, this list of conditions and the following disclaimer.
701 *
702 * 2. Redistributions in binary form must reproduce the above copyright
703 * notice, this list of conditions and the following disclaimer in
704 * the documentation and/or other materials provided with the
705 * distribution.
706 *
707 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
708 * and "Vovida Open Communication Application Library (VOCAL)" must
709 * not be used to endorse or promote products derived from this
710 * software without prior written permission. For written
711 * permission, please contact vocal@vovida.org.
712 *
713 * 4. Products derived from this software may not be called "VOCAL", nor
714 * may "VOCAL" appear in their name, without prior written
715 * permission of Vovida Networks, Inc.
716 *
717 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
718 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
719 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
720 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
721 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
722 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
723 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
724 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
725 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
726 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
727 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
728 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
729 * DAMAGE.
730 *
731 * ====================================================================
732 *
733 * This software consists of voluntary contributions made by Vovida
734 * Networks, Inc. and many individuals on behalf of Vovida Networks,
735 * Inc. For more information on Vovida Networks, Inc., please see
736 * <http://www.vovida.org/>.
737 *
738 */
static const Data errorContextData("Contents")
#define ErrLog(args_)
Definition Logger.hxx:137
#define resip_assert(x)
This file needs to work for both C and C++ code.
static HashMap< Mime, ContentsFactoryBase * > & getFactoryMap()
Base class for all SIP body types.
Definition Contents.hxx:51
void addBuffer(char *buf)
Definition Contents.cxx:668
bool exists(const HeaderBase &headerType) const
checks to see if a header is present
Definition Contents.cxx:189
virtual Data getBodyData() const
Definition Contents.cxx:661
Contents(const HeaderFieldValue &headerFieldValue, const Mime &contentsType)
pass Mime instance for parameters
Definition Contents.cxx:22
virtual ~Contents()
Definition Contents.cxx:58
H_ContentDescription::Type * mDescription
Definition Contents.hxx:352
std::vector< char * > mBufferList
Definition Contents.hxx:361
const H_ContentType::Type & header(const H_ContentType &headerType) const
returns the value of the Content-Type header Throws an Contents::Exception if the header doesn't exis...
Definition Contents.cxx:280
EncodeStream & encodeHeaders(EncodeStream &str) const
encodes headers to an output stream
Definition Contents.cxx:588
Contents & operator=(const Contents &rhs)
Assignment operator.
Definition Contents.cxx:71
H_ContentLanguages::Type * mLanguages
Definition Contents.hxx:348
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
H_ContentTransferEncoding::Type * mTransferEncoding
Definition Contents.hxx:346
void preParseHeaders(ParseBuffer &pb)
Preforms preparsing on the headers stored in the ParseBuffer pb.
Definition Contents.cxx:453
H_ContentDisposition::Type * mDisposition
Definition Contents.hxx:344
StringCategory * mLength
Definition Contents.hxx:354
void remove(const HeaderBase &headerType)
removes a header if it is present
Definition Contents.cxx:232
virtual const Data & errorContext() const
Definition Contents.cxx:65
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
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
static Data from(const T &x)
Converts from arbitrary other type to Data.
Definition Data.hxx:398
const char * data() const noexcept
Returns a pointer to the contents of this Data.
Definition Data.hxx:608
Used to accumlate data written to the stream in a DigestBuffer and convert the data to Digest.
Data getHex()
Calls flush() on itself and returns the Digest data in hex format.
Content-ID MIME header identifier class.
Definition Contents.hxx:38
Content-ID MIME header identifier class.
Definition Contents.hxx:30
virtual Headers::Type getTypeNum() const =0
static Type getType(const char *name, size_t len)
Definition Headers.cxx:351
The base-class for all lazily-parsed SIP grammar elements.
EncodeStream & encode(EncodeStream &str) const
Encodes this element to a stream, in the fashion it should be represented on the wire.
void checkParsed() const
LazyParser & operator=(const LazyParser &rhs)
friend class Contents
MIME header identifier base class.
Definition Contents.hxx:25
Represents the "media-type" element in the RFC 3261 grammar.
Definition Mime.hxx:20
SIP body type for holding Octet contents (MIME content-type application/octet-stream).
Provides string-parsing functionality with protection from buffer overflow.
CurrentPosition skipLWS()
Pointer end() const
CurrentPosition skipWhitespace()
CurrentPosition position() const
CurrentPosition skipToOneOf(const char *cs)
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)
const char * skipToEndQuote(char quote='"')
static const char * Whitespace
BaseException used to indicate parse failures.
Represents the "header-value" element in the RFC 3261 grammar (ie, an unknown header field value).
virtual void parse(ParseBuffer &pb)
static const char * RPAREN
Definition Symbols.hxx:38
static const char * LPAREN
Definition Symbols.hxx:37
static const char * SPACE
Definition Symbols.hxx:34
static const char * COLON
Definition Symbols.hxx:15
static const char * COMMA
Definition Symbols.hxx:35
static const char * PERIOD
Definition Symbols.hxx:41
static const char * CRLF
Definition Symbols.hxx:21
Represents the "token" element in the RFC 3261 grammar.
Definition Token.hxx:18
virtual void parse(ParseBuffer &pb)
Definition Token.cxx:91
dcm! – add UnusedChecking(_enum) below;
bool isEqualNoCase(const Data &left, const Data &right)
Definition Data.hxx:1116
H_ContentDescription h_ContentDescription
Definition Contents.cxx:20
H_ContentID h_ContentID
Definition Contents.cxx:19
bool operator==(const char *lhs, const Data &rhs)
Definition Data.hxx:1197
bool operator!=(const Data &lhs, const Data &rhs)
Definition Data.hxx:1187
#define EncodeStream