Coverage Report

Created: 2025-11-16 06:32

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/MD5Stream.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
64.6k
   : LazyParser(headerFieldValue),
25
64.6k
      mType(contentType)
26
64.6k
{
27
64.6k
   init();
28
64.6k
}
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
64.6k
{
60
64.6k
   freeMem();
61
64.6k
}
62
63
static const Data errorContextData("Contents");
64
const Data&
65
Contents::errorContext() const
66
64.6k
{
67
64.6k
   return errorContextData;
68
64.6k
}
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
0
               H_ContentLanguages::Type::value_type tmp;
508
0
               header(h_ContentLanguages).push_back(tmp);
509
0
               header(h_ContentLanguages).back().parse(subPb);
510
0
               subPb.skipLWS();
511
0
            }
512
0
            break;  // .kw. added -- this is needed, right?
513
0
         }
514
0
         default :
515
0
         {
516
0
            if (isEqualNoCase(headerName, "Content-Transfer-Encoding"))
517
0
            {
518
0
               mTransferEncoding = new StringCategory();
519
0
               mTransferEncoding->parse(subPb);
520
0
            }
521
0
            else if (isEqualNoCase(headerName, "Content-Description"))
522
0
            {
523
0
               mDescription = new StringCategory();
524
0
               mDescription->parse(subPb);
525
0
            }
526
0
            else if (isEqualNoCase(headerName, "Content-Id"))
527
0
            {
528
0
               mId = new Token();
529
0
               mId->parse(subPb);
530
0
            }
531
            // Some people put this in ...
532
0
            else if (isEqualNoCase(headerName, "Content-Length"))
533
0
            {
534
0
               mLength = new StringCategory();
535
0
               mLength->parse(subPb);
536
0
            }
537
0
            else if (isEqualNoCase(headerName, "MIME-Version"))
538
0
            {
539
0
               subPb.skipWhitespace();
540
0
               if (!subPb.eof() && *subPb.position() == Symbols::LPAREN[0])
541
0
               {
542
0
                  subPb.skipToEndQuote(Symbols::RPAREN[0]);
543
0
                  subPb.skipChar(Symbols::RPAREN[0]);
544
0
               }
545
0
               mVersion = subPb.integer();
546
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
               subPb.skipChar(Symbols::PERIOD[0]);
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
               
560
0
               mMinorVersion = subPb.integer();
561
0
            }
562
0
            else
563
0
            {
564
               // add to application headers someday
565
0
               std::cerr << "Unknown MIME Content- header: " << headerName << std::endl;
566
0
               ErrLog(<< "Unknown MIME Content- header: " << headerName);
567
0
               resip_assert(false);
568
0
            }
569
0
         }
570
0
      }
571
0
   }
572
0
   }
573
0
   catch (ParseException &  e )
574
0
   {
575
0
      ErrLog( << "Some problem parsing contents: " << e );
576
0
      throw e;
577
0
   }
578
0
}
579
580
EncodeStream&
581
Contents::encodeHeaders(EncodeStream& str) const
582
0
{
583
0
   if (mVersion != 1 || mMinorVersion != 0)
584
0
   {
585
0
      str << "MIME-Version" << Symbols::COLON[0] << Symbols::SPACE[0]
586
0
          << mVersion << Symbols::PERIOD[0] << mMinorVersion 
587
0
          << Symbols::CRLF;
588
0
   }
589
590
0
   str << "Content-Type" << Symbols::COLON[0] << Symbols::SPACE[0]
591
0
       << mType 
592
0
       << Symbols::CRLF;
593
594
0
   if (exists(h_ContentDisposition))
595
0
   {
596
0
      str <<  "Content-Disposition" << Symbols::COLON[0] << Symbols::SPACE[0];
597
598
0
      header(h_ContentDisposition).encode(str);
599
0
      str << Symbols::CRLF;
600
0
   }
601
602
0
   if (exists(h_ContentLanguages))
603
0
   {
604
0
      str <<  "Content-Languages" << Symbols::COLON[0] << Symbols::SPACE[0];
605
606
0
      size_t count = 0;
607
0
      size_t size = header(h_ContentLanguages).size();
608
609
0
      for (H_ContentLanguages::Type::const_iterator 
610
0
              i = header(h_ContentLanguages).begin();
611
0
           i != header(h_ContentLanguages).end(); ++i)
612
0
      {
613
0
         i->encode(str);
614
615
0
         if (++count < size)
616
0
             str << Symbols::COMMA << Symbols::SPACE;
617
0
      }
618
0
      str << Symbols::CRLF;
619
0
   }
620
621
0
   if (mTransferEncoding)
622
0
   {
623
0
      str << "Content-Transfer-Encoding" << Symbols::COLON[0] << Symbols::SPACE[0]
624
0
          << *mTransferEncoding
625
0
          << Symbols::CRLF;
626
0
   }
627
628
0
   if (mId)
629
0
   {
630
0
      str << "Content-Id" << Symbols::COLON[0] << Symbols::SPACE[0]
631
0
          << *mId
632
0
          << Symbols::CRLF;
633
0
   }
634
635
0
   if (mDescription)
636
0
   {
637
0
      str << "Content-Description" << Symbols::COLON[0] << Symbols::SPACE[0]
638
0
          << *mDescription
639
0
          << Symbols::CRLF;
640
0
   }
641
642
0
   if (mLength)
643
0
   {
644
0
      str << "Content-Length" << Symbols::COLON[0] << Symbols::SPACE[0]
645
0
          <<  *mLength 
646
0
          << Symbols::CRLF;
647
0
   }
648
   
649
0
   str << Symbols::CRLF;
650
0
   return str;
651
0
}
652
653
Data
654
Contents::getBodyData() const 
655
0
{
656
0
   checkParsed();
657
0
   return Data::from(*this);
658
0
}
659
660
void
661
Contents::addBuffer(char* buf)
662
0
{
663
0
   mBufferList.push_back(buf);
664
0
}
665
666
bool
667
resip::operator==(const Contents& lhs, const Contents& rhs)
668
0
{
669
0
   MD5Stream lhsStream;
670
0
   lhsStream << lhs;
671
0
   MD5Stream rhsStream;
672
0
   rhsStream << rhs;
673
0
   return lhsStream.getHex() == rhsStream.getHex();
674
0
}
675
676
bool
677
resip::operator!=(const Contents& lhs, const Contents& rhs)
678
0
{
679
0
   return !operator==(lhs,rhs);
680
0
}
681
682
/* ====================================================================
683
 * The Vovida Software License, Version 1.0 
684
 * 
685
 * Copyright (c) 2000-2005
686
 * 
687
 * Redistribution and use in source and binary forms, with or without
688
 * modification, are permitted provided that the following conditions
689
 * are met:
690
 * 
691
 * 1. Redistributions of source code must retain the above copyright
692
 *    notice, this list of conditions and the following disclaimer.
693
 * 
694
 * 2. Redistributions in binary form must reproduce the above copyright
695
 *    notice, this list of conditions and the following disclaimer in
696
 *    the documentation and/or other materials provided with the
697
 *    distribution.
698
 * 
699
 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
700
 *    and "Vovida Open Communication Application Library (VOCAL)" must
701
 *    not be used to endorse or promote products derived from this
702
 *    software without prior written permission. For written
703
 *    permission, please contact vocal@vovida.org.
704
 *
705
 * 4. Products derived from this software may not be called "VOCAL", nor
706
 *    may "VOCAL" appear in their name, without prior written
707
 *    permission of Vovida Networks, Inc.
708
 * 
709
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
710
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
711
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
712
 * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
713
 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
714
 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
715
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
716
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
717
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
718
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
719
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
720
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
721
 * DAMAGE.
722
 * 
723
 * ====================================================================
724
 * 
725
 * This software consists of voluntary contributions made by Vovida
726
 * Networks, Inc. and many individuals on behalf of Vovida Networks,
727
 * Inc.  For more information on Vovida Networks, Inc., please see
728
 * <http://www.vovida.org/>.
729
 *
730
 */