Coverage Report

Created: 2026-10-28 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/resiprocate/resip/stack/Contents.cxx
Line
Count
Source
1
#include <vector>
2
3
#if defined(HAVE_CONFIG_H)
4
#include "config.h"
5
#endif
6
7
#include "resip/stack/Contents.hxx"
8
#include "rutil/ParseBuffer.hxx"
9
#include "rutil/Logger.hxx"
10
#include "resip/stack/OctetContents.hxx"
11
#include "rutil/DigestStream.hxx"
12
#include "rutil/WinLeakCheck.hxx"
13
14
using namespace resip;
15
using namespace std;
16
17
#define RESIPROCATE_SUBSYSTEM Subsystem::CONTENTS
18
19
H_ContentID resip::h_ContentID;
20
H_ContentDescription resip::h_ContentDescription;
21
22
Contents::Contents(const HeaderFieldValue& headerFieldValue,
23
                   const Mime& contentType) 
24
68.8k
   : LazyParser(headerFieldValue),
25
68.8k
      mType(contentType)
26
68.8k
{
27
68.8k
   init();
28
68.8k
}
29
30
Contents::Contents(const Mime& contentType) 
31
20
   : mType(contentType)
32
20
{
33
20
   init();
34
20
}
35
36
Contents::Contents(const Contents& rhs) 
37
0
    : LazyParser(rhs)
38
0
{
39
0
   init(rhs);
40
0
}
41
42
Contents::Contents(const Contents& rhs,HeaderFieldValue::CopyPaddingEnum e) 
43
0
    : LazyParser(rhs,e)
44
0
{
45
0
   init(rhs);
46
0
}
47
48
Contents::Contents(const HeaderFieldValue& headerFieldValue,
49
                     HeaderFieldValue::CopyPaddingEnum e,
50
                     const Mime& contentsType)
51
0
    : LazyParser(headerFieldValue,e),
52
0
    mType(contentsType)
53
0
{
54
0
   init();
55
0
}
56
57
58
Contents::~Contents()
59
68.8k
{
60
68.8k
   freeMem();
61
68.8k
}
62
63
static const Data errorContextData("Contents");
64
const Data&
65
Contents::errorContext() const
66
68.8k
{
67
68.8k
   return errorContextData;
68
68.8k
}
69
70
Contents& 
71
Contents::operator=(const Contents& rhs) 
72
0
{
73
0
   if (this != &rhs)
74
0
   {
75
0
      freeMem();
76
0
      LazyParser::operator=(rhs);
77
0
      init(rhs);
78
0
   }
79
80
0
   return *this;
81
0
}
82
83
void
84
Contents::init(const Contents& orig)
85
0
{
86
0
   mBufferList.clear();
87
0
   mType = orig.mType;
88
0
   if (orig.mDisposition)
89
0
   {
90
0
       mDisposition = new H_ContentDisposition::Type(*orig.mDisposition);
91
0
   }
92
0
   else
93
0
   {
94
0
      mDisposition = 0;
95
0
   }
96
   
97
0
   if (orig.mTransferEncoding)
98
0
   {
99
0
       mTransferEncoding = new H_ContentTransferEncoding::Type(*orig.mTransferEncoding);
100
0
   }
101
0
   else
102
0
   {
103
0
      mTransferEncoding = 0;
104
0
   }
105
   
106
0
   if (orig.mLanguages)
107
0
   {
108
0
       mLanguages = new H_ContentLanguages::Type(*orig.mLanguages);
109
0
   }
110
0
   else
111
0
   {
112
0
      mLanguages = 0;
113
0
   }
114
   
115
0
   if (orig.mId)
116
0
   {
117
0
       mId = new Token(*orig.mId);
118
0
   }
119
0
   else
120
0
   {
121
0
      mId = 0;
122
0
   }
123
   
124
0
   if (orig.mDescription)
125
0
   {
126
0
       mDescription = new StringCategory(*orig.mDescription);
127
0
   }
128
0
   else
129
0
   {
130
0
      mDescription = 0;
131
0
   }
132
   
133
0
   if(orig.mLength)
134
0
   {
135
0
      mLength = new StringCategory(*orig.mLength);
136
0
   }
137
0
   else
138
0
   {
139
0
      mLength = 0;
140
0
   }
141
142
0
   mVersion = orig.mVersion;
143
0
   mMinorVersion = orig.mMinorVersion;
144
145
0
}
146
147
Contents*
148
Contents::createContents(const Mime& contentType, 
149
                         const Data& contents)
150
0
{
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
0
   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
0
   Contents* c;
177
0
   if (ContentsFactoryBase::getFactoryMap().find(contentType) != ContentsFactoryBase::getFactoryMap().end())
178
0
   {
179
0
      c = ContentsFactoryBase::getFactoryMap()[contentType]->create(hfv, contentType);
180
0
   }
181
0
   else
182
0
   {
183
0
      c = new OctetContents(hfv, contentType);
184
0
   }
185
0
   return c;
186
0
}
187
188
bool
189
Contents::exists(const HeaderBase& headerType) const
190
0
{
191
0
   checkParsed();
192
0
   switch (headerType.getTypeNum())
193
0
   {
194
0
      case Headers::ContentType :
195
0
      {
196
0
         return true;
197
0
      }
198
0
      case Headers::ContentDisposition :
199
0
      {
200
0
         return mDisposition != 0;
201
0
      }
202
0
      case Headers::ContentTransferEncoding :
203
0
      {
204
0
         return mTransferEncoding != 0;
205
0
      }
206
0
      case Headers::ContentLanguage :
207
0
      {
208
0
         return mLanguages != 0;
209
0
      }
210
0
      default : return false;
211
0
   }
212
0
}
213
214
bool
215
Contents::exists(const MIME_Header& type) const
216
0
{
217
0
   if (&type == &h_ContentID)
218
0
   {
219
0
      return mId != 0;
220
0
   }
221
   
222
0
   if (&type == &h_ContentDescription)
223
0
   {
224
0
      return mDescription != 0;
225
0
   }
226
227
0
   resip_assert(false);
228
0
   return false;
229
0
}
230
231
void
232
Contents::remove(const HeaderBase& headerType)
233
0
{
234
0
   switch (headerType.getTypeNum())
235
0
   {
236
0
      case Headers::ContentDisposition :
237
0
      {
238
0
         delete mDisposition;
239
0
         mDisposition = 0;
240
0
         break;
241
0
      }
242
0
      case Headers::ContentLanguage :
243
0
      {
244
0
         delete mLanguages;
245
0
         mLanguages = 0;
246
0
         break;
247
0
      }
248
0
      case Headers::ContentTransferEncoding :
249
0
      {
250
0
         delete mTransferEncoding;
251
0
         mTransferEncoding = 0;
252
0
         break;
253
0
      }
254
0
      default :
255
0
         ;
256
0
   }
257
0
}
258
259
void
260
Contents::remove(const MIME_Header& type)
261
0
{
262
0
   if (&type == &h_ContentID)
263
0
   {
264
0
      delete mId;
265
0
      mId = 0;
266
0
      return;
267
0
   }
268
    
269
0
   if (&type == &h_ContentDescription)
270
0
   {
271
0
      delete mDescription;
272
0
      mDescription = 0;
273
0
      return;
274
0
   }
275
276
0
   resip_assert(false);
277
0
}
278
279
const H_ContentType::Type&
280
Contents::header(const H_ContentType& headerType) const
281
0
{
282
0
   return mType;
283
0
}
284
285
H_ContentType::Type&
286
Contents::header(const H_ContentType& headerType)
287
0
{
288
0
   return mType;
289
0
}
290
291
const H_ContentDisposition::Type&
292
Contents::header(const H_ContentDisposition& headerType) const
293
0
{
294
0
   checkParsed();
295
0
   if (mDisposition == 0)
296
0
   {
297
0
      ErrLog(<< "You called "
298
0
            "Contents::header(const H_ContentDisposition& headerType) _const_ "
299
0
            "without first calling exists(), and the header does not exist. Our"
300
0
            " behavior in this scenario is to implicitly create the header(using const_cast!); "
301
0
            "this is probably not what you want, but it is either this or "
302
0
            "assert/throw an exception. Since this has been the behavior for "
303
0
            "so long, we are not throwing here, _yet_. You need to fix your "
304
0
            "code, before we _do_ start throwing. This is why const-correctness"
305
0
            " should never be made a TODO item </rant>");
306
0
      Contents* ncthis = const_cast<Contents*>(this);
307
0
      ncthis->mDisposition = new H_ContentDisposition::Type;
308
0
   }
309
0
   return *mDisposition;
310
0
}
311
312
H_ContentDisposition::Type&
313
Contents::header(const H_ContentDisposition& headerType)
314
0
{
315
0
   checkParsed();
316
0
   if (mDisposition == 0)
317
0
   {
318
0
      mDisposition = new H_ContentDisposition::Type;
319
0
   }
320
0
   return *mDisposition;
321
0
}
322
323
const H_ContentTransferEncoding::Type&
324
Contents::header(const H_ContentTransferEncoding& headerType) const
325
0
{
326
0
   checkParsed();
327
0
   if (mTransferEncoding == 0)
328
0
   {
329
0
      ErrLog(<< "You called "
330
0
            "Contents::header(const H_ContentTransferEncoding& headerType) _const_ "
331
0
            "without first calling exists(), and the header does not exist. Our"
332
0
            " behavior in this scenario is to implicitly create the header(using const_cast!); "
333
0
            "this is probably not what you want, but it is either this or "
334
0
            "assert/throw an exception. Since this has been the behavior for "
335
0
            "so long, we are not throwing here, _yet_. You need to fix your "
336
0
            "code, before we _do_ start throwing. This is why const-correctness"
337
0
            " should never be made a TODO item </rant>");
338
0
      Contents* ncthis = const_cast<Contents*>(this);
339
0
      ncthis->mTransferEncoding = new H_ContentTransferEncoding::Type;
340
0
   }
341
0
   return *mTransferEncoding;
342
0
}
343
344
H_ContentTransferEncoding::Type&
345
Contents::header(const H_ContentTransferEncoding& headerType)
346
0
{
347
0
   checkParsed();
348
0
   if (mTransferEncoding == 0)
349
0
   {
350
0
      mTransferEncoding = new H_ContentTransferEncoding::Type;
351
0
   }
352
0
   return *mTransferEncoding;
353
0
}
354
355
const H_ContentLanguages::Type&
356
Contents::header(const H_ContentLanguages& headerType) const 
357
0
{
358
0
   checkParsed();
359
0
   if (mLanguages == 0)
360
0
   {
361
0
      ErrLog(<< "You called "
362
0
            "Contents::header(const H_ContentLanguages& headerType) _const_ "
363
0
            "without first calling exists(), and the header does not exist. Our"
364
0
            " behavior in this scenario is to implicitly create the header(using const_cast!); "
365
0
            "this is probably not what you want, but it is either this or "
366
0
            "assert/throw an exception. Since this has been the behavior for "
367
0
            "so long, we are not throwing here, _yet_. You need to fix your "
368
0
            "code, before we _do_ start throwing. This is why const-correctness"
369
0
            " should never be made a TODO item </rant>");
370
0
      Contents* ncthis = const_cast<Contents*>(this);
371
0
      ncthis->mLanguages = new H_ContentLanguages::Type;
372
0
   }
373
0
   return *mLanguages;
374
0
}
375
376
H_ContentLanguages::Type&
377
Contents::header(const H_ContentLanguages& headerType)
378
0
{
379
0
   checkParsed();
380
0
   if (mLanguages == 0)
381
0
   {
382
0
      mLanguages = new H_ContentLanguages::Type;
383
0
   }
384
0
   return *mLanguages;
385
0
}
386
387
const H_ContentDescription::Type&
388
Contents::header(const H_ContentDescription& headerType) const
389
0
{
390
0
   checkParsed();
391
0
   if (mDescription == 0)
392
0
   {
393
0
      ErrLog(<< "You called "
394
0
            "Contents::header(const H_ContentDescription& headerType) _const_ "
395
0
            "without first calling exists(), and the header does not exist. Our"
396
0
            " behavior in this scenario is to implicitly create the header(using const_cast!); "
397
0
            "this is probably not what you want, but it is either this or "
398
0
            "assert/throw an exception. Since this has been the behavior for "
399
0
            "so long, we are not throwing here, _yet_. You need to fix your "
400
0
            "code, before we _do_ start throwing. This is why const-correctness"
401
0
            " should never be made a TODO item </rant>");
402
0
      Contents* ncthis = const_cast<Contents*>(this);
403
0
      ncthis->mDescription = new H_ContentDescription::Type;
404
0
   }
405
0
   return *mDescription;
406
0
}
407
408
H_ContentDescription::Type&
409
Contents::header(const H_ContentDescription& headerType)
410
0
{
411
0
   checkParsed();
412
0
   if (mDescription == 0)
413
0
   {
414
0
      mDescription = new H_ContentDescription::Type;
415
0
   }
416
0
   return *mDescription;
417
0
}
418
419
const H_ContentID::Type&
420
Contents::header(const H_ContentID& headerType) const
421
0
{
422
0
   checkParsed();
423
0
   if (mId == 0)
424
0
   {
425
0
      ErrLog(<< "You called "
426
0
            "Contents::header(const H_ContentID& headerType) _const_ "
427
0
            "without first calling exists(), and the header does not exist. Our"
428
0
            " behavior in this scenario is to implicitly create the header(using const_cast!); "
429
0
            "this is probably not what you want, but it is either this or "
430
0
            "assert/throw an exception. Since this has been the behavior for "
431
0
            "so long, we are not throwing here, _yet_. You need to fix your "
432
0
            "code, before we _do_ start throwing. This is why const-correctness"
433
0
            " should never be made a TODO item </rant>");
434
0
      Contents* ncthis = const_cast<Contents*>(this);
435
0
      ncthis->mId = new H_ContentID::Type;
436
0
   }
437
0
   return *mId;
438
0
}
439
440
H_ContentID::Type&
441
Contents::header(const H_ContentID& headerType)
442
0
{
443
0
   checkParsed();
444
0
   if (mId == 0)
445
0
   {
446
0
      mId = new H_ContentID::Type;
447
0
   }
448
0
   return *mId;
449
0
}
450
451
// !dlb! headers except Content-Disposition may contain (comments)
452
void
453
Contents::preParseHeaders(ParseBuffer& pb)
454
0
{
455
0
   const char* start = pb.position();
456
0
   Data all( start, (Data::size_type)(pb.end()-start));
457
458
0
   Data headerName;
459
460
0
   try
461
0
   {
462
      
463
0
   while (!pb.eof())
464
0
   {
465
0
      const char* anchor = pb.skipWhitespace();
466
0
      pb.skipToOneOf(Symbols::COLON, ParseBuffer::Whitespace);
467
0
      pb.data(headerName, anchor);
468
469
0
      pb.skipWhitespace();
470
0
      pb.skipChar(Symbols::COLON[0]);
471
0
      anchor = pb.skipWhitespace();
472
0
      pb.skipToTermCRLF();
473
474
0
      Headers::Type type = Headers::getType(headerName.data(), headerName.size());
475
0
      ParseBuffer subPb(anchor, pb.position() - anchor);
476
477
0
      switch (type)
478
0
      {
479
0
         case Headers::ContentType :
480
0
         {
481
            // already set
482
0
            break;
483
0
         }
484
0
         case Headers::ContentDisposition :
485
0
         {
486
0
            mDisposition = new H_ContentDisposition::Type;
487
0
            mDisposition->parse(subPb);
488
0
            break;
489
0
         }
490
0
         case Headers::ContentTransferEncoding :
491
0
         {
492
0
            mTransferEncoding = new H_ContentTransferEncoding::Type;
493
0
            mTransferEncoding->parse(subPb);
494
0
            break;
495
0
         }
496
         // !dlb! not sure this ever happens?
497
0
         case Headers::ContentLanguage :
498
0
         {
499
0
            if (mLanguages == 0)
500
0
            {
501
0
               mLanguages = new H_ContentLanguages::Type;
502
0
            }
503
504
0
            subPb.skipWhitespace();
505
0
            while (!subPb.eof() && *subPb.position() != Symbols::COMMA[0])
506
0
            {
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
0
               H_ContentLanguages::Type::value_type tmp;
515
0
               mLanguages->push_back(tmp);
516
0
               mLanguages->back().parse(subPb);
517
0
               subPb.skipLWS();
518
0
            }
519
0
            break;  // .kw. added -- this is needed, right?
520
0
         }
521
0
         default :
522
0
         {
523
0
            if (isEqualNoCase(headerName, "Content-Transfer-Encoding"))
524
0
            {
525
0
               mTransferEncoding = new StringCategory();
526
0
               mTransferEncoding->parse(subPb);
527
0
            }
528
0
            else if (isEqualNoCase(headerName, "Content-Description"))
529
0
            {
530
0
               mDescription = new StringCategory();
531
0
               mDescription->parse(subPb);
532
0
            }
533
0
            else if (isEqualNoCase(headerName, "Content-Id"))
534
0
            {
535
0
               mId = new Token();
536
0
               mId->parse(subPb);
537
0
            }
538
            // Some people put this in ...
539
0
            else if (isEqualNoCase(headerName, "Content-Length"))
540
0
            {
541
0
               mLength = new StringCategory();
542
0
               mLength->parse(subPb);
543
0
            }
544
0
            else if (isEqualNoCase(headerName, "MIME-Version"))
545
0
            {
546
0
               subPb.skipWhitespace();
547
0
               if (!subPb.eof() && *subPb.position() == Symbols::LPAREN[0])
548
0
               {
549
0
                  subPb.skipToEndQuote(Symbols::RPAREN[0]);
550
0
                  subPb.skipChar(Symbols::RPAREN[0]);
551
0
               }
552
0
               mVersion = subPb.integer();
553
554
0
               if (!subPb.eof() && *subPb.position() == Symbols::LPAREN[0])
555
0
               {
556
0
                  subPb.skipToEndQuote(Symbols::RPAREN[0]);
557
0
                  subPb.skipChar(Symbols::RPAREN[0]);
558
0
               }
559
0
               subPb.skipChar(Symbols::PERIOD[0]);
560
               
561
0
               if (!subPb.eof() && *subPb.position() == Symbols::LPAREN[0])
562
0
               {
563
0
                  subPb.skipToEndQuote(Symbols::RPAREN[0]);
564
0
                  subPb.skipChar(Symbols::RPAREN[0]);
565
0
               }
566
               
567
0
               mMinorVersion = subPb.integer();
568
0
            }
569
0
            else
570
0
            {
571
               // add to application headers someday
572
0
               std::cerr << "Unknown MIME Content- header: " << headerName << std::endl;
573
0
               ErrLog(<< "Unknown MIME Content- header: " << headerName);
574
0
               resip_assert(false);
575
0
            }
576
0
         }
577
0
      }
578
0
   }
579
0
   }
580
0
   catch (ParseException &  e )
581
0
   {
582
0
      ErrLog( << "Some problem parsing contents: " << e );
583
0
      throw e;
584
0
   }
585
0
}
586
587
EncodeStream&
588
Contents::encodeHeaders(EncodeStream& str) const
589
0
{
590
0
   if (mVersion != 1 || mMinorVersion != 0)
591
0
   {
592
0
      str << "MIME-Version" << Symbols::COLON[0] << Symbols::SPACE[0]
593
0
          << mVersion << Symbols::PERIOD[0] << mMinorVersion 
594
0
          << Symbols::CRLF;
595
0
   }
596
597
0
   str << "Content-Type" << Symbols::COLON[0] << Symbols::SPACE[0]
598
0
       << mType 
599
0
       << Symbols::CRLF;
600
601
0
   if (exists(h_ContentDisposition))
602
0
   {
603
0
      str <<  "Content-Disposition" << Symbols::COLON[0] << Symbols::SPACE[0];
604
605
0
      header(h_ContentDisposition).encode(str);
606
0
      str << Symbols::CRLF;
607
0
   }
608
609
0
   if (exists(h_ContentLanguages))
610
0
   {
611
0
      str <<  "Content-Languages" << Symbols::COLON[0] << Symbols::SPACE[0];
612
613
0
      size_t count = 0;
614
0
      size_t size = header(h_ContentLanguages).size();
615
616
0
      for (H_ContentLanguages::Type::const_iterator 
617
0
              i = header(h_ContentLanguages).begin();
618
0
           i != header(h_ContentLanguages).end(); ++i)
619
0
      {
620
0
         i->encode(str);
621
622
0
         if (++count < size)
623
0
             str << Symbols::COMMA << Symbols::SPACE;
624
0
      }
625
0
      str << Symbols::CRLF;
626
0
   }
627
628
0
   if (mTransferEncoding)
629
0
   {
630
0
      str << "Content-Transfer-Encoding" << Symbols::COLON[0] << Symbols::SPACE[0]
631
0
          << *mTransferEncoding
632
0
          << Symbols::CRLF;
633
0
   }
634
635
0
   if (mId)
636
0
   {
637
0
      str << "Content-Id" << Symbols::COLON[0] << Symbols::SPACE[0]
638
0
          << *mId
639
0
          << Symbols::CRLF;
640
0
   }
641
642
0
   if (mDescription)
643
0
   {
644
0
      str << "Content-Description" << Symbols::COLON[0] << Symbols::SPACE[0]
645
0
          << *mDescription
646
0
          << Symbols::CRLF;
647
0
   }
648
649
0
   if (mLength)
650
0
   {
651
0
      str << "Content-Length" << Symbols::COLON[0] << Symbols::SPACE[0]
652
0
          <<  *mLength 
653
0
          << Symbols::CRLF;
654
0
   }
655
   
656
0
   str << Symbols::CRLF;
657
0
   return str;
658
0
}
659
660
Data
661
Contents::getBodyData() const 
662
0
{
663
0
   checkParsed();
664
0
   return Data::from(*this);
665
0
}
666
667
void
668
Contents::addBuffer(char* buf)
669
0
{
670
0
   mBufferList.push_back(buf);
671
0
}
672
673
bool
674
resip::operator==(const Contents& lhs, const Contents& rhs)
675
0
{
676
0
   DigestStream lhsStream;
677
0
   lhsStream << lhs;
678
0
   DigestStream rhsStream;
679
0
   rhsStream << rhs;
680
0
   return lhsStream.getHex() == rhsStream.getHex();
681
0
}
682
683
bool
684
resip::operator!=(const Contents& lhs, const Contents& rhs)
685
0
{
686
0
   return !operator==(lhs,rhs);
687
0
}
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
 */