Coverage Report

Created: 2025-11-16 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/resiprocate/resip/stack/MessageWaitingContents.cxx
Line
Count
Source
1
#if defined(HAVE_CONFIG_H)
2
#include "config.h"
3
#endif
4
5
#include <ctype.h>
6
7
#include "resip/stack/MessageWaitingContents.hxx"
8
#include "rutil/Logger.hxx"
9
#include "rutil/ParseBuffer.hxx"
10
#include "rutil/WinLeakCheck.hxx"
11
12
13
using namespace resip;
14
using namespace std;
15
16
#define RESIPROCATE_SUBSYSTEM Subsystem::CONTENTS
17
18
bool
19
MessageWaitingContents::init()
20
4
{
21
4
   static ContentsFactory<MessageWaitingContents> factory;
22
4
   (void)factory;
23
4
   return true;
24
4
}
25
26
resip::MessageWaitingContents::AccountHeader resip::mw_account;
27
const char* MessageHeaders[MW_MAX] = {"voice-message", 
28
                                      "fax-message", 
29
                                      "pager-message", 
30
                                      "multimedia-message", 
31
                                      "text-message",
32
                                      "none"};
33
34
MessageWaitingContents::MessageWaitingContents()
35
0
   : Contents(getStaticType()),
36
0
     mHasMessages(false),
37
0
     mAccountUri(0)
38
0
{
39
0
   for(int i = 0; i < (int)MW_MAX; i++)
40
0
   {
41
0
      mHeaders[i] = 0;
42
0
   }
43
0
}
44
45
MessageWaitingContents::MessageWaitingContents(const HeaderFieldValue& hfv, const Mime& contentType)
46
5.87k
   : Contents(hfv, contentType),
47
5.87k
     mHasMessages(false),
48
5.87k
     mAccountUri(0)
49
5.87k
{
50
41.1k
   for(int i = 0; i < (int)MW_MAX; i++)
51
35.2k
   {
52
35.2k
      mHeaders[i] = 0;
53
35.2k
   }
54
5.87k
}
55
56
MessageWaitingContents::MessageWaitingContents(const Data& data, const Mime& contentType)
57
0
   : Contents(contentType),
58
0
     mHasMessages(false),
59
0
     mAccountUri(0)
60
0
{
61
0
   for(int i = 0; i < (int)MW_MAX; i++)
62
0
   {
63
0
      mHeaders[i] = 0;
64
0
   }
65
0
   resip_assert(0);
66
0
}
67
68
MessageWaitingContents::MessageWaitingContents(const MessageWaitingContents& rhs)
69
0
   : Contents(rhs),
70
0
     mHasMessages(rhs.mHasMessages),
71
0
     mAccountUri(rhs.mAccountUri ? new Uri(*rhs.mAccountUri) : 0),
72
0
     mExtensions(rhs.mExtensions)
73
0
{
74
0
   for(int i = 0; i < (int)MW_MAX; i++)
75
0
   {
76
0
      if (rhs.mHeaders[i] != 0)
77
0
      {
78
0
         mHeaders[i] = new Header(*rhs.mHeaders[i]);
79
0
      }
80
0
      else
81
0
      {
82
0
         mHeaders[i] = 0;
83
0
      }
84
0
   }
85
0
}   
86
87
MessageWaitingContents::~MessageWaitingContents()
88
5.87k
{
89
5.87k
   clear();
90
5.87k
}
91
92
void
93
MessageWaitingContents::clear()
94
5.87k
{
95
5.87k
   mHasMessages = false;
96
97
5.87k
   delete mAccountUri;
98
5.87k
   mAccountUri = 0;
99
   
100
41.1k
   for (int i = 0; i < (int)MW_MAX; i++)
101
35.2k
   {
102
35.2k
      delete mHeaders[i];
103
35.2k
   }
104
5.87k
}
105
106
MessageWaitingContents&
107
MessageWaitingContents::operator=(const MessageWaitingContents& rhs)
108
0
{
109
0
   if (this != &rhs)
110
0
   {
111
0
      Contents::operator=(rhs);
112
0
      clear();
113
114
0
      mHasMessages = rhs.mHasMessages;
115
0
      mAccountUri = rhs.mAccountUri ? new Uri(*rhs.mAccountUri) : 0;
116
0
      mExtensions = rhs.mExtensions;
117
118
0
      for(int i = 0; i < (int)MW_MAX; i++)
119
0
      {
120
0
         if (rhs.mHeaders[i] != 0)
121
0
         {
122
0
            mHeaders[i] = new Header(*rhs.mHeaders[i]);
123
0
         }
124
0
         else
125
0
         {
126
0
            mHeaders[i] = 0;
127
0
         }
128
0
      }
129
0
   }
130
0
   return *this;
131
0
}
132
133
const Mime& 
134
MessageWaitingContents::getStaticType() 
135
2
{
136
2
   static Mime type("application", "simple-message-summary");
137
   //static Mime type("text", "data");
138
2
   return type;
139
2
}
140
141
Contents*
142
MessageWaitingContents::clone() const
143
0
{
144
0
   return new MessageWaitingContents(*this);
145
0
}
146
147
EncodeStream& 
148
MessageWaitingContents::encodeParsed(EncodeStream& s) const
149
0
{
150
0
   s << "Messages-Waiting" << Symbols::COLON[0] << Symbols::SPACE[0]
151
0
     << (mHasMessages ? "yes" : "no") << Symbols::CRLF;
152
153
0
   if (exists(mw_account))
154
0
   {
155
0
      s << "Message-Account" << Symbols::COLON[0] << Symbols::SPACE[0];
156
0
      header(mw_account).encode(s);
157
0
      s << Symbols::CRLF;
158
0
   }
159
160
0
   for(int i = 0; i < (int)MW_MAX; i++)
161
0
   {
162
0
      if (mHeaders[i] != 0)
163
0
      {
164
0
         s << MessageHeaders[i] << Symbols::COLON[0] << Symbols::SPACE[0]
165
0
           << mHeaders[i]->mNew << Symbols::SLASH[0] 
166
0
           << mHeaders[i]->mOld;
167
168
0
         if (mHeaders[i]->mHasUrgent)
169
0
         {
170
0
            s << Symbols::SPACE[0] << Symbols::LPAREN[0]    
171
0
              << mHeaders[i]->mUrgentNew << Symbols::SLASH[0] 
172
0
              << mHeaders[i]->mUrgentOld << Symbols::RPAREN[0]; 
173
0
         }
174
175
0
         s << Symbols::CRLF;
176
0
      }
177
0
   }
178
179
0
   if (!mExtensions.empty())
180
0
   {
181
0
      s << Symbols::CRLF;
182
0
      for (map<Data, Data>::const_iterator i = mExtensions.begin();
183
0
           i != mExtensions.end(); i++)
184
0
      {
185
0
         s << i->first << Symbols::COLON[0] << Symbols::SPACE[0]
186
0
           << i->second << Symbols::CRLF;
187
0
      }
188
0
   }
189
190
0
   return s;
191
0
}
192
193
inline
194
bool
195
isWhite(char c)
196
0
{
197
0
   switch (c)
198
0
   {
199
0
      case ' ' :
200
0
      case '\t' : 
201
0
      case '\r' : 
202
0
      case '\n' : 
203
0
         return true;
204
0
      default:
205
0
         return false;
206
0
   }
207
0
}
208
209
const char*
210
resip::skipSipLWS(ParseBuffer& pb)
211
0
{
212
0
   enum {WS, CR, LF, CR1};
213
214
0
   int state = WS;
215
216
0
   while (!pb.eof())
217
0
   {
218
0
      if (!isWhite(*pb.position()))
219
0
      {
220
0
         if (state == LF)
221
0
         {
222
0
            pb.reset(pb.position() - 2);
223
0
         }
224
0
         return pb.position();
225
0
      }
226
0
      if (!pb.eof())
227
0
      {
228
0
   switch (state)
229
0
   {
230
0
      case WS:
231
0
         if (*pb.position() == Symbols::CR[0])
232
0
         {
233
0
      state = CR;
234
0
         }
235
0
         break;
236
0
      case CR:
237
0
         if (*pb.position() == Symbols::CR[0])
238
0
         {
239
0
      state = CR;
240
0
         }
241
0
         else if (*pb.position() == Symbols::LF[0])
242
0
         {
243
0
      state = LF;
244
0
         }
245
0
         else
246
0
         {
247
0
      state = WS;
248
0
         }
249
0
         break;
250
0
      case LF:
251
0
         if (*pb.position() == Symbols::CR[0])
252
0
         {
253
0
      state = CR1;
254
0
         }
255
0
         else if (!pb.eof() && *pb.position() == Symbols::LF[0])
256
0
         {
257
0
      state = WS;
258
0
         }
259
0
         break;
260
0
      case CR1:
261
0
         if (*pb.position() == Symbols::CR[0])
262
0
         {
263
0
      state = CR;
264
0
         }
265
0
         else if (*pb.position() == Symbols::LF[0])
266
0
         {
267
0
      pb.reset(pb.position() - 3);
268
0
      return pb.position();
269
0
         }
270
0
         else
271
0
         {
272
0
      state = WS;
273
0
         }
274
0
         break;
275
0
      default:
276
0
         resip_assert(false);
277
0
   }
278
0
      }
279
0
      pb.skipChar();
280
0
   }
281
282
0
   if (state == LF)
283
0
   {
284
0
      pb.reset(pb.position() - 2);
285
0
   }
286
0
   return pb.position();
287
0
}
288
289
void
290
MessageWaitingContents::parse(ParseBuffer& pb)
291
5.87k
{
292
5.87k
   pb.skipChars("Messages-Waiting");
293
5.87k
   pb.skipWhitespace();
294
5.87k
   pb.skipChar(Symbols::COLON[0]);
295
5.87k
   const char* anchor = pb.skipWhitespace();
296
5.87k
   pb.skipNonWhitespace();
297
   
298
5.87k
   Data has;
299
5.87k
   pb.data(has, anchor);
300
5.87k
   if (isEqualNoCase(has, "yes"))
301
0
   {
302
0
      mHasMessages = true;
303
0
   }
304
5.87k
   else if (isEqualNoCase(has, "no"))
305
0
   {
306
0
      mHasMessages = false;
307
0
   }
308
5.87k
   else
309
5.87k
   {
310
5.87k
      pb.fail(__FILE__, __LINE__);
311
5.87k
   }
312
313
5.87k
   anchor = pb.skipWhitespace();
314
5.87k
   if (pb.eof())
315
0
   {
316
0
      return;
317
0
   }
318
319
5.87k
   Data accountHeader;
320
5.87k
   pb.skipToOneOf(ParseBuffer::Whitespace, Symbols::COLON);
321
5.87k
   pb.data(accountHeader, anchor);
322
5.87k
   static const Data AccountMessage("message-account");
323
5.87k
   if (isEqualNoCase(accountHeader, AccountMessage))
324
0
   {
325
0
      pb.skipWhitespace();
326
0
      pb.skipChar(Symbols::COLON[0]);
327
0
      pb.skipWhitespace();
328
      
329
0
      mAccountUri = new Uri();
330
0
      mAccountUri->parse(pb);
331
0
      pb.skipChars(Symbols::CRLF);
332
0
   }
333
5.87k
   else
334
5.87k
   {
335
5.87k
      pb.reset(anchor);
336
5.87k
   }
337
338
5.87k
   while (!pb.eof() && *pb.position() != Symbols::CR[0])
339
0
   {
340
0
      int ht = -1;
341
0
      switch (tolower(static_cast< unsigned char >(*pb.position())))
342
0
      {
343
0
         case 'v' :
344
0
            ht = mw_voice;
345
0
            break;
346
0
         case 'f' :
347
0
            ht = mw_fax;
348
0
            break;
349
0
         case 'p' :
350
0
            ht = mw_pager;
351
0
            break;
352
0
         case 'm' :
353
0
            ht = mw_multimedia;
354
0
            break;
355
0
         case 't' :
356
0
            ht = mw_text;
357
0
            break;
358
0
         case 'n' :
359
0
            ht = mw_none;
360
0
            break;
361
0
         default :
362
0
            pb.fail(__FILE__, __LINE__);
363
0
      }
364
0
      resip_assert(ht != -1);
365
366
0
      pb.skipToOneOf(ParseBuffer::Whitespace, Symbols::COLON);
367
0
      pb.skipWhitespace();
368
0
      pb.skipChar(Symbols::COLON[0]);
369
0
      pb.skipWhitespace();
370
371
0
      unsigned int numNew = pb.integer();
372
0
      pb.skipWhitespace();
373
0
      pb.skipChar(Symbols::SLASH[0]);
374
0
      pb.skipWhitespace();
375
376
0
      unsigned int numOld = pb.integer();
377
0
      skipSipLWS(pb);
378
379
0
      if (!pb.eof() && *pb.position() != Symbols::LPAREN[0])
380
0
      {
381
0
         if (mHeaders[ht] != 0)
382
0
         {
383
0
            pb.fail(__FILE__, __LINE__);
384
0
         }
385
0
         mHeaders[ht] = new Header(numNew, numOld);
386
0
      }
387
0
      else
388
0
      {
389
0
         pb.skipChar();
390
0
         pb.skipWhitespace();
391
392
0
         unsigned int numUrgentNew = pb.integer();
393
0
         pb.skipWhitespace();
394
0
         pb.skipChar(Symbols::SLASH[0]);
395
0
         pb.skipWhitespace();
396
397
0
         unsigned int numUrgentOld = pb.integer();
398
0
         pb.skipWhitespace();
399
0
         pb.skipChar(Symbols::RPAREN[0]);
400
         // skip LWS as specified in rfc3261
401
0
         skipSipLWS(pb);
402
403
0
         if (mHeaders[ht] != 0)
404
0
         {
405
0
            pb.fail(__FILE__, __LINE__);
406
0
         }
407
0
         mHeaders[ht] = new Header(numNew, numOld, numUrgentNew, numUrgentOld);
408
0
      }
409
      
410
0
      pb.skipChars(Symbols::CRLF);
411
0
   }
412
413
5.87k
   if (!pb.eof() && *pb.position() == Symbols::CR[0])
414
0
   {
415
0
      pb.skipChars(Symbols::CRLF);
416
      
417
0
      while (!pb.eof())
418
0
      {
419
0
         anchor = pb.position();
420
0
         Data header;
421
0
         pb.skipToOneOf(ParseBuffer::Whitespace, Symbols::COLON);
422
0
         pb.data(header, anchor);
423
424
0
         pb.skipWhitespace();
425
0
         pb.skipChar(Symbols::COLON[0]);
426
0
         anchor = pb.skipWhitespace();
427
428
0
         while (true)
429
0
         {
430
            // CodeWarrior isn't helpful enough to pick the "obvious" operator definition
431
            // so we add volatile here so CW is completely unconfused what to do.
432
0
            const volatile char* pos = pb.skipToChar(Symbols::CR[0]);
433
0
            skipSipLWS(pb);
434
0
            if (pb.position() == pos)
435
0
            {
436
0
               Data content;
437
0
               pb.data(content, anchor);
438
0
               mExtensions[header] = content;
439
440
0
               pb.skipChars(Symbols::CRLF);
441
0
               break;
442
0
            }
443
0
         }
444
0
      }
445
0
   }
446
5.87k
}
447
448
MessageWaitingContents::Header::Header(unsigned int numNew,
449
                                       unsigned int numOld)
450
0
   : mNew(numNew),
451
0
     mOld(numOld),
452
0
     mHasUrgent(false),
453
0
     mUrgentNew(0),
454
0
     mUrgentOld(0)
455
0
{}
456
457
MessageWaitingContents::Header::Header(unsigned int numNew,
458
                                       unsigned int numOld,
459
                                       unsigned int numUrgentNew,
460
                                       unsigned int numUrgentOld)
461
0
   : mNew(numNew),
462
0
     mOld(numOld),
463
0
     mHasUrgent(true),
464
0
     mUrgentNew(numUrgentNew),
465
0
     mUrgentOld(numUrgentOld)
466
0
{}
467
468
MessageWaitingContents::Header& 
469
MessageWaitingContents::header(HeaderType ht)
470
0
{
471
0
   checkParsed();
472
473
   /* this is a trick to allow a const method to update "this" with an empty
474
      Header in case there wasn't a corresponding header line in the MessageWaiting doc
475
    */
476
0
   if (mHeaders[ht] == 0)
477
0
   {
478
0
      mHeaders[ht] = new Header(0, 0);
479
0
   }
480
0
   return *mHeaders[ht];
481
0
}
482
483
const MessageWaitingContents::Header& 
484
MessageWaitingContents::header(HeaderType ht) const
485
0
{
486
0
   checkParsed();
487
488
   /* this is a trick to allow a const method to update "this" with an empty
489
      Header in case there wasn't a corresponding header line in the MessageWaiting doc
490
    */
491
0
   if (mHeaders[ht] == 0)
492
0
   {
493
0
      ErrLog(<< "You called "
494
0
            "MessageWaitingContents::header(HeaderType ht) _const_ "
495
0
            "without first calling exists(), and the header does not exist. Our"
496
0
            " behavior in this scenario is to implicitly create the header(using const_cast!); "
497
0
            "this is probably not what you want, but it is either this or "
498
0
            "assert/throw an exception. Since this has been the behavior for "
499
0
            "so long, we are not throwing here, _yet_. You need to fix your "
500
0
            "code, before we _do_ start throwing. This is why const-correctness"
501
0
            " should never be made a TODO item </rant>");
502
0
      MessageWaitingContents* ncthis = const_cast<MessageWaitingContents*>(this);
503
0
      ncthis->mHeaders[ht] = new Header(0, 0);
504
0
   }
505
0
   return *mHeaders[ht];
506
0
}
507
508
bool 
509
MessageWaitingContents::exists(HeaderType ht) const
510
0
{
511
0
   checkParsed();
512
0
   return mHeaders[ht] != 0;
513
0
}
514
515
void
516
MessageWaitingContents::remove(HeaderType ht)
517
0
{
518
0
   checkParsed();
519
0
   delete mHeaders[ht];
520
0
   mHeaders[ht] = 0;
521
0
}
522
523
Uri& 
524
MessageWaitingContents::header(const AccountHeader& ht)
525
0
{
526
0
   checkParsed();
527
528
   /* this is a trick to allow a const method to update "this" with an empty
529
      Uri in case there wasn't a Message-Account line in the MessageWaiting doc
530
    */
531
0
   if (mAccountUri == 0)
532
0
   {
533
0
      mAccountUri = new Uri();
534
0
   }
535
0
   return *mAccountUri;
536
0
}
537
538
const Uri& 
539
MessageWaitingContents::header(const AccountHeader& ht) const
540
0
{
541
0
   checkParsed();
542
543
   /* this is a trick to allow a const method to update "this" with an empty
544
      Uri in case there wasn't a Message-Account line in the MessageWaiting doc
545
    */
546
0
   if (mAccountUri == 0)
547
0
   {
548
0
      ErrLog(<< "You called "
549
0
            "MessageWaitingContents::header(const AccountHeader& ht) _const_ "
550
0
            "without first calling exists(), and the header does not exist. Our"
551
0
            " behavior in this scenario is to implicitly create the header(using const_cast!); "
552
0
            "this is probably not what you want, but it is either this or "
553
0
            "assert/throw an exception. Since this has been the behavior for "
554
0
            "so long, we are not throwing here, _yet_. You need to fix your "
555
0
            "code, before we _do_ start throwing. This is why const-correctness"
556
0
            " should never be made a TODO item </rant>");
557
0
      MessageWaitingContents* ncthis = const_cast<MessageWaitingContents*>(this);
558
0
      ncthis->mAccountUri = new Uri();
559
0
   }
560
0
   return *mAccountUri;
561
0
}
562
563
bool 
564
MessageWaitingContents::exists(const AccountHeader& ht) const
565
0
{
566
0
   checkParsed();
567
0
   return mAccountUri != 0;
568
0
}
569
570
void
571
MessageWaitingContents::remove(const AccountHeader& ht)
572
0
{
573
0
   checkParsed();
574
0
   delete mAccountUri;
575
0
   mAccountUri = 0;
576
0
}
577
578
Data&
579
MessageWaitingContents::header(const Data& hn)
580
0
{
581
0
   checkParsed();
582
0
   return mExtensions[hn];
583
0
}
584
585
const Data&
586
MessageWaitingContents::header(const Data& hn) const
587
0
{
588
0
   checkParsed();
589
0
   std::map<Data, Data>::const_iterator h=mExtensions.find(hn);
590
0
   if(h==mExtensions.end())
591
0
   {
592
0
      ErrLog(<< "You called "
593
0
            "MessageWaitingContents::header(const Data& hn) _const_ "
594
0
            "without first calling exists(), and the header does not exist. Our"
595
0
            " behavior in this scenario is to implicitly create the header(using const_cast!); "
596
0
            "this is probably not what you want, but it is either this or "
597
0
            "assert/throw an exception. Since this has been the behavior for "
598
0
            "so long, we are not throwing here, _yet_. You need to fix your "
599
0
            "code, before we _do_ start throwing. This is why const-correctness"
600
0
            " should never be made a TODO item </rant>");
601
0
      MessageWaitingContents* ncthis = const_cast<MessageWaitingContents*>(this);
602
0
      h=ncthis->mExtensions.insert(std::make_pair(hn,Data::Empty)).first;
603
0
   }
604
0
   return h->second;
605
0
}
606
607
bool
608
MessageWaitingContents::exists(const Data& hn) const
609
0
{
610
0
   checkParsed();
611
0
   return mExtensions.find(hn) != mExtensions.end();
612
0
}
613
614
void
615
MessageWaitingContents::remove(const Data& hn)
616
0
{
617
0
   checkParsed();
618
0
   mExtensions.erase(hn);
619
0
}
620
621
/* ====================================================================
622
 * The Vovida Software License, Version 1.0 
623
 * 
624
 * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
625
 * 
626
 * Redistribution and use in source and binary forms, with or without
627
 * modification, are permitted provided that the following conditions
628
 * are met:
629
 * 
630
 * 1. Redistributions of source code must retain the above copyright
631
 *    notice, this list of conditions and the following disclaimer.
632
 * 
633
 * 2. Redistributions in binary form must reproduce the above copyright
634
 *    notice, this list of conditions and the following disclaimer in
635
 *    the documentation and/or other materials provided with the
636
 *    distribution.
637
 * 
638
 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
639
 *    and "Vovida Open Communication Application Library (VOCAL)" must
640
 *    not be used to endorse or promote products derived from this
641
 *    software without prior written permission. For written
642
 *    permission, please contact vocal@vovida.org.
643
 *
644
 * 4. Products derived from this software may not be called "VOCAL", nor
645
 *    may "VOCAL" appear in their name, without prior written
646
 *    permission of Vovida Networks, Inc.
647
 * 
648
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
649
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
650
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
651
 * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
652
 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
653
 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
654
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
655
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
656
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
657
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
658
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
659
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
660
 * DAMAGE.
661
 * 
662
 * ====================================================================
663
 * 
664
 * This software consists of voluntary contributions made by Vovida
665
 * Networks, Inc. and many individuals on behalf of Vovida Networks,
666
 * Inc.  For more information on Vovida Networks, Inc., please see
667
 * <http://www.vovida.org/>.
668
 *
669
 */