Coverage Report

Created: 2023-06-07 06:03

/src/resiprocate/resip/stack/DialogInfoContents.cxx
Line
Count
Source (jump to first uncovered line)
1
#if defined(HAVE_CONFIG_H)
2
#include "config.h"
3
#endif
4
5
#include <time.h>
6
#include <iomanip>
7
8
#include "resip/stack/DialogInfoContents.hxx"
9
#include "resip/stack/SipMessage.hxx"
10
#include "resip/stack/Symbols.hxx"
11
#include "rutil/XMLCursor.hxx"
12
#include "rutil/Logger.hxx"
13
#include "rutil/Inserter.hxx"
14
#include "rutil/WinLeakCheck.hxx"
15
16
using namespace resip;
17
using namespace std;
18
19
#define RESIPROCATE_SUBSYSTEM Subsystem::SIP
20
21
const static Data BaseDialogInfoNamespaceUri("urn:ietf:params:xml:ns:dialog-info");
22
const static Data SharedAppearanceDialogInfoNamespaceUri("urn:ietf:params:xml:ns:sa-dialog-info");
23
const static Data SharedAppearanceDialogInfoNamespacePrefix("sa:");
24
25
const static Data DefaultEncodeIndent("  ");
26
27
bool
28
DialogInfoContents::init()
29
4
{
30
4
   static ContentsFactory<DialogInfoContents> factory;
31
4
   (void)factory;
32
4
   return true;
33
4
}
34
35
static const char* DialogInfoStateStrings[] =
36
{
37
   "full",
38
   "partial"
39
};
40
41
const char* 
42
DialogInfoContents::dialogInfoStateToString(const DialogInfoState& dialogInfoState)
43
0
{
44
0
   return DialogInfoStateStrings[dialogInfoState];
45
0
}
46
47
DialogInfoContents::DialogInfoState 
48
DialogInfoContents::dialogInfoStateStringToEnum(const Data& dialogInfoStateString)
49
0
{
50
0
   for (int i = 0; i < MaxDialogInfoState; i++)
51
0
   {
52
0
      if (isEqualNoCase(DialogInfoStateStrings[i], dialogInfoStateString))
53
0
      {
54
0
         return (DialogInfoState)i;
55
0
      }
56
0
   }
57
0
   return MaxDialogInfoState;
58
0
}
59
60
static const char* DialogStateStrings[] =
61
{
62
   "trying",
63
   "proceeding",
64
   "early",
65
   "confirmed",
66
   "terminated"
67
};
68
69
const char* 
70
DialogInfoContents::dialogStateToString(const DialogState& dialogState)
71
0
{
72
0
   return DialogStateStrings[dialogState];
73
0
}
74
75
DialogInfoContents::DialogState 
76
DialogInfoContents::dialogStateStringToEnum(const Data& dialogStateString)
77
0
{
78
0
   for (int i = 0; i < MaxDialogState; i++)
79
0
   {
80
0
      if (isEqualNoCase(DialogStateStrings[i], dialogStateString))
81
0
      {
82
0
         return (DialogState)i;
83
0
      }
84
0
   }
85
0
   return MaxDialogState;
86
0
}
87
88
static const char* DialogStateEventStrings[] =
89
{
90
   "cancelled",
91
   "rejected",
92
   "replaced",
93
   "local-bye",
94
   "remote-bye",
95
   "error",
96
   "timeout"
97
};
98
99
const char* 
100
DialogInfoContents::dialogStateEventToString(const DialogStateEvent& dialogStateEvent)
101
0
{
102
0
   return DialogStateEventStrings[dialogStateEvent];
103
0
}
104
105
DialogInfoContents::DialogStateEvent
106
DialogInfoContents::dialogStateEventStringToEnum(const Data& dialogStateEventString)
107
0
{
108
0
   for (int i = 0; i < MaxOrUnsetDialogStateEvent; i++)
109
0
   {
110
0
      if (isEqualNoCase(DialogStateEventStrings[i], dialogStateEventString))
111
0
      {
112
0
         return (DialogStateEvent)i;
113
0
      }
114
0
   }
115
0
   return MaxOrUnsetDialogStateEvent;
116
0
}
117
118
static const char* DirectionStrings[] =
119
{
120
   "initiator",
121
   "recipient"
122
};
123
124
const char*
125
DialogInfoContents::directionToString(const Direction& direction)
126
0
{
127
0
   return DirectionStrings[direction];
128
0
}
129
130
DialogInfoContents::Direction
131
DialogInfoContents::directionStringToEnum(const Data& directionString)
132
0
{
133
0
   for (int i = 0; i < MaxOrUnsetDirection; i++)
134
0
   {
135
0
      if (isEqualNoCase(DirectionStrings[i], directionString))
136
0
      {
137
0
         return (Direction)i;
138
0
      }
139
0
   }
140
0
   return MaxOrUnsetDirection;
141
0
}
142
143
bool 
144
DialogInfoContents::compareTag(const Data& tagName, const Data& compareToTagNoPrefix, const Data& namespacePrefix)
145
0
{
146
0
   if (namespacePrefix.empty())
147
0
   {
148
0
      return tagName == compareToTagNoPrefix;
149
0
   }
150
0
   Data compareToTagWithPrefix(namespacePrefix);
151
0
   compareToTagWithPrefix += compareToTagNoPrefix;
152
0
   return tagName == compareToTagWithPrefix;
153
0
}
154
155
const DialogInfoContents DialogInfoContents::Empty;
156
157
DialogInfoContents::DialogInfoContents()
158
   : Contents(getStaticType()), mIndent(DefaultEncodeIndent), mVersion(0), mDialogInfoState(Partial)
159
2
{
160
2
}
161
162
DialogInfoContents::DialogInfoContents(const Mime& contentType)
163
   : Contents(getStaticType()), mIndent(DefaultEncodeIndent), mVersion(0), mDialogInfoState(Partial)
164
0
{
165
0
}
166
167
DialogInfoContents::DialogInfoContents(const HeaderFieldValue& hfv, const Mime& contentsType)
168
   : Contents(hfv, contentsType), mIndent(DefaultEncodeIndent), mVersion(0), mDialogInfoState(Partial)
169
0
{
170
0
}
171
172
DialogInfoContents::~DialogInfoContents()
173
0
{
174
0
}
175
176
Contents* 
177
DialogInfoContents::clone() const
178
0
{
179
0
   return new DialogInfoContents(*this);
180
0
}
181
182
const Mime& 
183
DialogInfoContents::getStaticType() 
184
4
{
185
4
   static Mime type("application","dialog-info+xml");
186
4
   return type;
187
4
}
188
189
EncodeStream& 
190
DialogInfoContents::encodeParsed(EncodeStream& str) const
191
0
{
192
0
   bool hasSharedAppearanceElements = false;
193
194
   // Check if any dialogs have shared appearance elements, if so, we need to add the namespace
195
0
   for (DialogList::const_iterator it = mDialogs.begin(); it != mDialogs.end(); it++)
196
0
   {
197
0
      if (it->hasExclusive() || it->hasAppearance() || !it->getReplacedDialogs().empty() || !it->getJoinedDialogs().empty())
198
0
      {
199
0
         hasSharedAppearanceElements = true;
200
0
         break;  // no need to continue, we found at least one
201
0
      }
202
0
   }
203
204
0
   str << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << Symbols::CRLF;
205
0
   str << "<dialog-info xmlns=\"" << BaseDialogInfoNamespaceUri << "\"" << Symbols::CRLF;
206
0
   if (hasSharedAppearanceElements)
207
0
   {
208
0
      str << "             xmlns:" << SharedAppearanceDialogInfoNamespacePrefix << "=\"" << SharedAppearanceDialogInfoNamespaceUri << "\"" << Symbols::CRLF;
209
0
   }
210
0
   str << "             version=\"" << mVersion << "\" state=\"" << dialogInfoStateToString(mDialogInfoState) << "\"" << Symbols::CRLF;
211
0
   str << "             entity=\"" << Data::from(mEntity).xmlCharDataEncode() << "\">" << Symbols::CRLF;
212
213
0
   for (DialogList::const_iterator it = mDialogs.begin(); it != mDialogs.end(); it++)
214
0
   {
215
0
      it->encodeParsed(str, mIndent);
216
0
   }
217
218
0
   str << "</dialog-info>" << Symbols::CRLF;
219
220
0
   return str;
221
0
}
222
223
bool 
224
DialogInfoContents::Dialog::getDialogElement(const Data& childElementName, Data& elementValue, int instance) const
225
0
{
226
0
   bool found = false;
227
0
   elementValue = "";
228
0
   std::pair <std::multimap<Data, Data>::const_iterator, std::multimap<Data, Data>::const_iterator> filteredList;
229
0
   filteredList = mExtraDialogElements.equal_range(childElementName);
230
0
   int matchCount = 0;
231
0
   for (std::multimap<Data, Data>::const_iterator matchListIterator = filteredList.first; matchListIterator != filteredList.second; ++matchListIterator)
232
0
   {
233
0
      if(instance == matchCount)
234
0
      {
235
0
         elementValue = matchListIterator->second;
236
0
         found = true;
237
0
         break;
238
0
      }
239
0
      matchCount++;
240
0
   }
241
242
0
   return(found);
243
0
}
244
245
EncodeStream& 
246
DialogInfoContents::Dialog::encodeParsed(EncodeStream& str, const Data& indent) const
247
0
{
248
   // Encode dialog attributes
249
0
   str << indent << "<dialog id=\"" << mId.xmlCharDataEncode() << "\"";
250
0
   if (!mCallId.empty())
251
0
   {
252
0
      str << " call-id=\"" << mCallId.xmlCharDataEncode() << "\"";
253
0
   }
254
0
   if (!mLocalTag.empty())
255
0
   {
256
0
      str << Symbols::CRLF << indent << "        local-tag=\"" << mLocalTag.xmlCharDataEncode() << "\"";
257
0
   }
258
0
   if (!mRemoteTag.empty())
259
0
   {
260
0
      str << Symbols::CRLF << indent << "        remote-tag=\"" << mRemoteTag.xmlCharDataEncode() << "\"";
261
0
   }
262
0
   if (mDirection != MaxOrUnsetDirection)
263
0
   {
264
0
      str << " direction=\"" << directionToString(mDirection) << "\"";
265
0
   }
266
0
   str << ">" << Symbols::CRLF;
267
268
   // Encode state element
269
0
   str << indent << indent << "<state";
270
0
   if (mStateEvent != MaxOrUnsetDialogStateEvent)
271
0
   {
272
0
      str << " event=\"" << dialogStateEventToString(mStateEvent) << "\"";
273
0
   }
274
0
   if (mStateCode != 0)
275
0
   {
276
0
      str << " code=\"" << mStateCode << "\"";
277
0
   }
278
0
   str << ">" << dialogStateToString(mState) << "</state>" << Symbols::CRLF;
279
280
   // Encode duration element
281
0
   if (mHasDuration)
282
0
   {
283
0
      str << indent << indent << "<duration>" << mDuration << "</duration>" << Symbols::CRLF;
284
0
   }
285
286
   // Encode replaces element
287
0
   if (!mReplaces.mCallId.empty())
288
0
   {
289
0
      str << indent << indent << "<replaces call-id=\"" << mReplaces.mCallId.xmlCharDataEncode() << "\"" << Symbols::CRLF;
290
0
      str << indent << indent << "          local-tag=\"" << mReplaces.mLocalTag.xmlCharDataEncode() << "\" remote-tag=\"" << mReplaces.mRemoteTag.xmlCharDataEncode() << "\"/>" << Symbols::CRLF;
291
0
   }
292
293
   // Encode referred by element
294
0
   if (!mReferredBy.uri().host().empty())
295
0
   {
296
0
      str << indent << indent;
297
0
      encodeNameAddrElement(str, "referred-by", mReferredBy);
298
0
      str << Symbols::CRLF;
299
0
   }
300
301
   // Encode route set
302
0
   if (!mRouteSet.empty())
303
0
   {
304
0
      str << indent << indent << "<route-set>" << Symbols::CRLF;
305
0
      for (NameAddrs::const_iterator itNA = mRouteSet.begin(); itNA != mRouteSet.end(); itNA++)
306
0
      {
307
0
         str << indent << indent << indent << "<hop>" << Data::from(itNA->uri()).xmlCharDataEncode() << "</hop>" << Symbols::CRLF;
308
0
      }
309
0
      str << indent << indent << "</route-set>" << Symbols::CRLF;
310
0
   }
311
312
   // Encode local participant (if set)
313
0
   mLocalParticipant.encode(str, "local", indent);
314
315
   // Encode remote participant (if set)
316
0
   mRemoteParticipant.encode(str, "remote", indent);
317
318
319
   //////////////////////////////////////
320
   // Encode RFC7463 Specific Elements
321
   //////////////////////////////////////
322
323
   // Encode shared appearance number
324
0
   if (mHasAppearance)
325
0
   {
326
0
      str << indent << indent << "<" << SharedAppearanceDialogInfoNamespacePrefix << "appearance>" << mAppearance << "</" << SharedAppearanceDialogInfoNamespacePrefix << "appearance>" << Symbols::CRLF;
327
0
   }
328
329
   // Encode exclusive flag
330
0
   if (mHasExclusive)
331
0
   {
332
0
      str << indent << indent << "<" << SharedAppearanceDialogInfoNamespacePrefix << "exclusive>" << (mExclusive ? "true" : "false") << "</" << SharedAppearanceDialogInfoNamespacePrefix << "exclusive>" << Symbols::CRLF;
333
0
   }
334
335
   // Encode replaced-dialog's
336
0
   for (auto replacedDialogsIterator = mReplacedDialogs.begin(); replacedDialogsIterator != mReplacedDialogs.end(); replacedDialogsIterator++)
337
0
   {
338
0
      str << indent << indent << "<" << SharedAppearanceDialogInfoNamespacePrefix << "replaced-dialog" << Symbols::CRLF
339
0
          << indent << indent << indent << "call-id=\"" << replacedDialogsIterator->mCallId.xmlCharDataEncode() << "\"" << Symbols::CRLF
340
0
          << indent << indent << indent << "local-tag=\"" << replacedDialogsIterator->mLocalTag.xmlCharDataEncode() << "\"" << Symbols::CRLF
341
0
          << indent << indent << indent << "remote-tag=\"" << replacedDialogsIterator->mRemoteTag.xmlCharDataEncode() << "\"/>" << Symbols::CRLF;
342
0
   }
343
344
   // Encode joined-dialog's
345
0
   for (auto joinedDialogsIterator = mJoinedDialogs.begin(); joinedDialogsIterator != mJoinedDialogs.end(); joinedDialogsIterator++)
346
0
   {
347
0
      str << indent << indent << "<" << SharedAppearanceDialogInfoNamespacePrefix << "joined-dialog" << Symbols::CRLF
348
0
         << indent << indent << indent << "call-id=\"" << joinedDialogsIterator->mCallId.xmlCharDataEncode() << "\"" << Symbols::CRLF
349
0
         << indent << indent << indent << "local-tag=\"" << joinedDialogsIterator->mLocalTag.xmlCharDataEncode() << "\"" << Symbols::CRLF
350
0
         << indent << indent << indent << "remote-tag=\"" << joinedDialogsIterator->mRemoteTag.xmlCharDataEncode() << "\"/>" << Symbols::CRLF;
351
0
   }
352
353
354
   // User specific/non-standard Dialog elements
355
0
   for(std::multimap<Data, Data>::const_iterator elementIterator = mExtraDialogElements.begin(); 
356
0
       elementIterator != mExtraDialogElements.end(); 
357
0
       elementIterator++)
358
0
   {
359
0
      DebugLog(<< "Dialog child element Name: \"" << elementIterator->first << "\" Value: \"" << elementIterator->second << "\"");
360
0
      str << indent << indent << '<' << elementIterator->first << '>'
361
0
          << elementIterator->second.xmlCharDataEncode()
362
0
          << "</" << elementIterator->first << '>' << Symbols::CRLF;
363
0
   }
364
365
0
   str << indent << "</dialog>" << Symbols::CRLF;
366
367
0
   return str;
368
0
}
369
370
EncodeStream& 
371
DialogInfoContents::encodeNameAddrElement(EncodeStream& str, const char* elementName, const NameAddr& nameAddr)
372
0
{
373
0
   str << "<" << elementName;
374
0
   if (!nameAddr.displayName().empty())
375
0
   {
376
0
      str << " display=\"" << nameAddr.displayName().xmlCharDataEncode() << "\"";
377
0
   }
378
0
   str << ">" << Data::from(nameAddr.uri()).xmlCharDataEncode() << "</" << elementName << ">";
379
0
   return str;
380
0
}
381
382
void 
383
DialogInfoContents::Dialog::Participant::setTarget(const NameAddr& targetWithContactParams) 
384
0
{ 
385
0
   mTarget = targetWithContactParams.uri(); 
386
0
   Data paramsData;
387
0
   {  // scope so that stream flushs
388
0
      DataStream dstr(paramsData);
389
0
      targetWithContactParams.encodeParameters(dstr);
390
0
   }
391
   // If there are parameters present - parse them
392
0
   if (!paramsData.empty())
393
0
   {
394
0
      ParseBuffer pb(paramsData);
395
0
      pb.skipChar();  // Skip first ; that is always present
396
0
      pb.skipWhitespace();
397
398
0
      do
399
0
      {
400
0
         const char* anchor = pb.position();
401
0
         pb.skipToOneOf("=;");
402
0
         if (*pb.position() == '=')
403
0
         {
404
0
            Data name;
405
0
            Data value;
406
0
            pb.data(name, anchor);
407
0
            pb.skipChar();
408
0
            pb.skipWhitespace();
409
0
            if (*pb.position() == '"')
410
0
            {
411
               // Quoted Value
412
0
               pb.skipChar();
413
0
               anchor = pb.position();
414
0
               pb.skipToChar('"');
415
0
               value = pb.data(anchor);
416
0
               pb.skipChar();
417
0
               pb.skipToChar(';');
418
0
            }
419
0
            else
420
0
            {
421
               // Unquoted value
422
0
               anchor = pb.position();
423
0
               pb.skipToChar(';');
424
0
               value = pb.data(anchor);
425
0
            }
426
0
            mTargetParams[name] = value;
427
0
         }
428
0
         else // ';' or eof
429
0
         {
430
            // No equals operator - this is a boolean param
431
0
            mTargetParams[pb.data(anchor)] = "true";
432
0
            pb.skipToChar(';');
433
0
         }
434
0
         if (!pb.eof())
435
0
         {
436
            // Skip ;
437
0
            pb.skipChar();
438
0
         }
439
0
      } while (!pb.eof());
440
0
   }
441
0
}
442
443
bool 
444
DialogInfoContents::Dialog::Participant::getTargetParam(const Data& name, Data& value) const
445
0
{
446
0
   TargetParams::const_iterator it = mTargetParams.find(name);
447
0
   if (it != mTargetParams.end())
448
0
   {
449
0
      value = it->second;
450
0
      return true;
451
0
   }
452
0
   return false;
453
0
}
454
455
EncodeStream& 
456
DialogInfoContents::Dialog::Participant::encode(EncodeStream& str, const char* baseElementName, const Data& indent) const
457
0
{
458
   // Only encode if any of the optional sub elements are actually set
459
0
   if (!mIdentity.uri().host().empty() || !mTarget.host().empty() || !mSessionDescription.empty() || mHasCSeq)
460
0
   {
461
0
      str << indent << indent << "<" << baseElementName << ">" << Symbols::CRLF;
462
0
      if (!mIdentity.uri().host().empty())
463
0
      {
464
0
         str << indent << indent << indent;
465
0
         encodeNameAddrElement(str, "identity", mIdentity);
466
0
         str << Symbols::CRLF;
467
0
      }
468
0
      if (!mTarget.host().empty())
469
0
      {
470
0
         str << indent << indent << indent << "<target uri=\"" << Data::from(mTarget).xmlCharDataEncode() << "\"";
471
0
         if (mTargetParams.empty())
472
0
         {
473
            // no params - just close tag
474
0
            str << "/>" << Symbols::CRLF;
475
0
         }
476
0
         else
477
0
         {
478
0
            str << ">" << Symbols::CRLF;
479
0
            for (TargetParams::const_iterator itParm = mTargetParams.begin(); itParm != mTargetParams.end(); itParm++)
480
0
            {
481
0
               str << indent << indent << indent << indent << "<param pname=\"" << itParm->first.xmlCharDataEncode() << "\" pval=\"" << itParm->second.xmlCharDataEncode() << "\"/>" << Symbols::CRLF;
482
0
            }
483
0
            str << indent << indent << indent << "</target>" << Symbols::CRLF;
484
0
         }
485
0
      }
486
0
      if (!mSessionDescription.empty())
487
0
      {
488
         // SDP is multiline, so whitespace is important - don't format for "pretty" output
489
0
         str << indent << indent << indent << "<session-description type=\"" << mSessionDescriptionType << "\">" << mSessionDescription.xmlCharDataEncode() << "</session-description>" << Symbols::CRLF;
490
0
      }
491
0
      if (mHasCSeq)
492
0
      {
493
0
         str << indent << indent << indent << "<cseq>" << mCSeq << "</cseq>" << Symbols::CRLF;
494
0
      }
495
0
      str << indent << indent << "</" << baseElementName << ">" << Symbols::CRLF;
496
0
   }
497
0
   return str;
498
0
}
499
500
void
501
DialogInfoContents::parse(ParseBuffer& pb)
502
0
{
503
0
   XMLCursor xml(pb);
504
0
   const XMLCursor::AttributeMap& attr = xml.getAttributes();
505
0
   XMLCursor::AttributeMap::const_iterator itAttr = attr.begin();
506
0
   bool baseDialogInfoNamespaceUriFound = false;
507
0
   for (; itAttr != attr.end(); itAttr++)
508
0
   {
509
0
      if (itAttr->first.prefix("xmlns"))
510
0
      {
511
0
         Data prefix;
512
0
         ParseBuffer pb(itAttr->first);
513
0
         pb.skipToChar(Symbols::COLON[0]);
514
0
         if (!pb.eof())
515
0
         {
516
0
            pb.skipChar();
517
0
            const char* anchor = pb.position();
518
0
            pb.skipToEnd();
519
0
            pb.data(prefix, anchor);
520
0
         }
521
0
         if (isEqualNoCase(itAttr->second, BaseDialogInfoNamespaceUri))
522
0
         {
523
0
            baseDialogInfoNamespaceUriFound = true;
524
0
            mBaseDialogInfoNamespacePrefix = prefix;
525
0
         }
526
0
         else if (isEqualNoCase(itAttr->second, SharedAppearanceDialogInfoNamespaceUri))
527
0
         {
528
0
            mSharedAppearanceDialogInfoNamespacePrefix = prefix;
529
0
         }
530
0
      }
531
0
      else if (itAttr->first == "version")
532
0
      {
533
0
         mVersion = itAttr->second.convertUnsignedLong();
534
0
      }
535
0
      else if (itAttr->first == "state")
536
0
      {
537
0
         mDialogInfoState = dialogInfoStateStringToEnum(itAttr->second);
538
0
      }
539
0
      else if (itAttr->first == "entity")
540
0
      {
541
0
         mEntity = Uri(itAttr->second.xmlCharDataDecode());  // can throw!
542
0
      }
543
0
      else
544
0
      {
545
0
         DebugLog(<< "Unknown root attribute: " << itAttr->first << "=" << itAttr->second);
546
0
      }
547
0
   }
548
549
0
   if (!baseDialogInfoNamespaceUriFound)
550
0
   {
551
0
      WarningLog(<< "Base xmlns from RFC4235 was not found, expected: " << BaseDialogInfoNamespaceUri);
552
      // ?slg? - throw or be tolerant?
553
0
   }
554
555
0
   if (xml.firstChild())
556
0
   {
557
0
      do
558
0
      {
559
0
         if (compareTag(xml.getTag(), "dialog", mBaseDialogInfoNamespacePrefix))
560
0
         {
561
0
            parseDialog(xml);
562
0
         }
563
0
         else
564
0
         {
565
0
            DebugLog(<< "Unknown root element: " << xml.getTag());
566
0
         }
567
0
      } while (xml.nextSibling());
568
0
      xml.parent();
569
0
   }
570
0
}
571
572
void 
573
DialogInfoContents::parseDialog(XMLCursor& xml)
574
0
{
575
0
   const XMLCursor::AttributeMap& attr = xml.getAttributes();
576
0
   XMLCursor::AttributeMap::const_iterator itAttr = attr.begin();
577
0
   Dialog dialog;
578
0
   for (; itAttr != attr.end(); itAttr++)
579
0
   {
580
0
      if (itAttr->first == "id")
581
0
      {
582
0
         dialog.mId = itAttr->second.xmlCharDataDecode();
583
0
      }
584
0
      else if (itAttr->first == "call-id")
585
0
      {
586
0
         dialog.mCallId = itAttr->second.xmlCharDataDecode();
587
0
      }
588
0
      else if (itAttr->first == "local-tag")
589
0
      {
590
0
         dialog.mLocalTag = itAttr->second.xmlCharDataDecode();
591
0
      }
592
0
      else if (itAttr->first == "remote-tag")
593
0
      {
594
0
         dialog.mRemoteTag = itAttr->second.xmlCharDataDecode();
595
0
      }
596
0
      else if (itAttr->first == "direction")
597
0
      {
598
0
         dialog.mDirection = directionStringToEnum(itAttr->second);
599
0
      }
600
0
      else
601
0
      {
602
0
         DebugLog(<< "Unknown dialog attribute: " << itAttr->first << "=" << itAttr->second);
603
0
      }
604
0
   }
605
606
0
   if (dialog.mId.empty())
607
0
   {
608
0
      WarningLog(<< "Dialog Id was not found for dialog element");
609
      // ?slg? - throw or be tolerant?
610
0
   }
611
612
0
   if (xml.firstChild())
613
0
   {
614
0
      do
615
0
      {
616
0
         if (compareTag(xml.getTag(), "state", mBaseDialogInfoNamespacePrefix))
617
0
         {
618
0
            const XMLCursor::AttributeMap& attr = xml.getAttributes();
619
0
            for (itAttr = attr.begin(); itAttr != attr.end(); itAttr++)
620
0
            {
621
0
               if (itAttr->first == "event")
622
0
               {
623
0
                  dialog.mStateEvent = dialogStateEventStringToEnum(itAttr->second);
624
0
               }
625
0
               else if (itAttr->first == "code")
626
0
               {
627
0
                  dialog.mStateCode = itAttr->second.convertInt();
628
0
               }
629
0
               else
630
0
               {
631
0
                  DebugLog(<< "Unknown state attribute: " << itAttr->first << "=" << itAttr->second);
632
0
               }
633
0
            }
634
0
            if (xml.firstChild())
635
0
            {
636
0
               dialog.mState = dialogStateStringToEnum(xml.getValue());
637
0
               xml.parent();
638
0
            }
639
0
         }
640
0
         else if (compareTag(xml.getTag(), "duration", mBaseDialogInfoNamespacePrefix))
641
0
         {
642
0
            if (xml.firstChild())
643
0
            {
644
0
               dialog.mDuration = xml.getValue().convertUnsignedLong();
645
0
               dialog.mHasDuration = true;
646
0
               xml.parent();
647
0
            }
648
0
         }
649
0
         else if (compareTag(xml.getTag(), "replaces", mBaseDialogInfoNamespacePrefix))
650
0
         {
651
0
            parseDialogIdInfo(xml, dialog.mReplaces);
652
0
         }
653
0
         else if (compareTag(xml.getTag(), "referred-by", mBaseDialogInfoNamespacePrefix))
654
0
         {
655
0
            parseNameAddrElement(xml, dialog.mReferredBy);
656
0
         }
657
0
         else if (compareTag(xml.getTag(), "route-set", mBaseDialogInfoNamespacePrefix))
658
0
         {
659
0
            if (xml.firstChild())
660
0
            {
661
0
               do
662
0
               {
663
0
                  if (compareTag(xml.getTag(), "hop", mBaseDialogInfoNamespacePrefix))
664
0
                  {
665
0
                     NameAddr nameAddr;
666
0
                     if (parseUriValue(xml, nameAddr.uri()))
667
0
                     {
668
0
                        dialog.mRouteSet.push_back(nameAddr);
669
0
                     }
670
0
                  }
671
0
                  else
672
0
                  {
673
0
                     DebugLog(<< "Unknown dialog/route-set element: " << xml.getTag());
674
0
                  }
675
0
               } while (xml.nextSibling());
676
0
               xml.parent();
677
0
            }
678
0
         }
679
0
         else if (compareTag(xml.getTag(), "local", mBaseDialogInfoNamespacePrefix))
680
0
         {
681
0
            dialog.mLocalParticipant.parse(xml, mBaseDialogInfoNamespacePrefix);
682
0
         }
683
0
         else if (compareTag(xml.getTag(), "remote", mBaseDialogInfoNamespacePrefix))
684
0
         {
685
0
            dialog.mRemoteParticipant.parse(xml, mBaseDialogInfoNamespacePrefix);
686
0
         }
687
         // Now Look for RFC7463 tags
688
0
         else if (compareTag(xml.getTag(), "appearance", mSharedAppearanceDialogInfoNamespacePrefix))
689
0
         {
690
0
            if (xml.firstChild())
691
0
            {
692
0
               dialog.mAppearance = xml.getValue().convertUnsignedLong();
693
0
               dialog.mHasAppearance = true;
694
0
               xml.parent();
695
0
            }
696
0
         }
697
0
         else if (compareTag(xml.getTag(), "exclusive", mSharedAppearanceDialogInfoNamespacePrefix))
698
0
         {
699
0
            if (xml.firstChild())
700
0
            {
701
0
               dialog.mExclusive = isEqualNoCase(xml.getValue(), "true");
702
0
               dialog.mHasExclusive = true;
703
0
               xml.parent();
704
0
            }
705
0
         }
706
0
         else if (compareTag(xml.getTag(), "replaced-dialog", mSharedAppearanceDialogInfoNamespacePrefix))
707
0
         {
708
0
            Dialog::DialogIdInfo dialogIdInfo;
709
0
            if (parseDialogIdInfo(xml, dialogIdInfo))
710
0
            {
711
0
               dialog.mReplacedDialogs.push_back(dialogIdInfo);
712
0
            }
713
0
         }
714
0
         else if (compareTag(xml.getTag(), "joined-dialog", mSharedAppearanceDialogInfoNamespacePrefix))
715
0
         {
716
0
            Dialog::DialogIdInfo dialogIdInfo;
717
0
            if (parseDialogIdInfo(xml, dialogIdInfo))
718
0
            {
719
0
               dialog.mJoinedDialogs.push_back(dialogIdInfo);
720
0
            }
721
0
         }
722
0
         else
723
0
         {
724
0
            Data elementName = xml.getTag();
725
0
            if (xml.firstChild())
726
0
            {
727
0
               DebugLog(<< "Unknown dialog element: " << elementName << " value: " << xml.getValue().xmlCharDataDecode());
728
0
               dialog.addDialogElement(elementName, xml.getValue().xmlCharDataDecode());
729
0
               xml.parent();
730
0
            }
731
0
            else
732
0
            {
733
0
               DebugLog(<< "Unknown dialog element: " << elementName);
734
0
            }
735
0
         }
736
0
      } while (xml.nextSibling());
737
0
      xml.parent();
738
0
   }
739
740
   //DebugLog(<< "Pushing " << mDialogs.size() << "'th dialog " << dialog.mId << " parsed into DialogInfoCOntents::mDialogs");
741
0
   mDialogs.push_back(dialog);
742
0
}
743
744
bool
745
DialogInfoContents::parseDialogIdInfo(XMLCursor& xml, Dialog::DialogIdInfo& dialogIdInfo)
746
0
{
747
0
   const XMLCursor::AttributeMap& attr = xml.getAttributes();
748
0
   for (XMLCursor::AttributeMap::const_iterator itAttr = attr.begin(); itAttr != attr.end(); itAttr++)
749
0
   {
750
0
      if (itAttr->first == "call-id")
751
0
      {
752
0
         dialogIdInfo.mCallId = itAttr->second.xmlCharDataDecode();
753
0
      }
754
0
      else if (itAttr->first == "local-tag")
755
0
      {
756
0
         dialogIdInfo.mLocalTag = itAttr->second.xmlCharDataDecode();
757
0
      }
758
0
      else if (itAttr->first == "remote-tag")
759
0
      {
760
0
         dialogIdInfo.mRemoteTag = itAttr->second.xmlCharDataDecode();
761
0
      }
762
0
      else
763
0
      {
764
0
         DebugLog(<< "Unknown dialog/replaces attribute: " << itAttr->first << "=" << itAttr->second);
765
0
      }
766
0
   }
767
0
   return !dialogIdInfo.mCallId.empty() && !dialogIdInfo.mLocalTag.empty() && !dialogIdInfo.mRemoteTag.empty();
768
0
}
769
770
bool
771
DialogInfoContents::parseUriValue(XMLCursor& xml, Uri& uri)
772
0
{
773
0
   bool parseGood = false;
774
0
   if (xml.firstChild())
775
0
   {
776
0
      try
777
0
      {
778
0
         uri = Uri(xml.getValue().xmlCharDataDecode());
779
0
         parseGood = true;
780
0
      }
781
0
      catch (BaseException& ex)  // ?slg? - catch here or not - let exception bubble up? or rethrow?
782
0
      {
783
0
         DebugLog(<< "Could not parse NameAddr value: " << xml.getValue().xmlCharDataDecode() << ": " << ex);
784
0
      }
785
0
      xml.parent();
786
0
   }
787
0
   return parseGood;
788
0
}
789
790
bool 
791
DialogInfoContents::parseNameAddrElement(XMLCursor& xml, NameAddr& nameAddr)
792
0
{
793
0
   const XMLCursor::AttributeMap& attr = xml.getAttributes();
794
0
   XMLCursor::AttributeMap::const_iterator itAttr = attr.begin();
795
0
   for (; itAttr != attr.end(); itAttr++)
796
0
   {
797
0
      if (itAttr->first == "display")
798
0
      {
799
0
         nameAddr.displayName() = itAttr->second.xmlCharDataDecode();
800
0
      }
801
0
      else
802
0
      {
803
0
         DebugLog(<< "Unknown NameAddr attribute: " << itAttr->first << "=" << itAttr->second);
804
0
      }
805
0
   }
806
0
   return parseUriValue(xml, nameAddr.uri());
807
0
}
808
809
void
810
DialogInfoContents::Dialog::Participant::parse(XMLCursor& xml, const Data& namespacePrefix)
811
0
{
812
0
   if (xml.firstChild())
813
0
   {
814
0
      do
815
0
      {
816
0
         if (compareTag(xml.getTag(), "identity", namespacePrefix))
817
0
         {
818
0
            parseNameAddrElement(xml, mIdentity);
819
0
         }
820
0
         else if (compareTag(xml.getTag(), "target", namespacePrefix))
821
0
         {
822
0
            try
823
0
            {
824
0
               const XMLCursor::AttributeMap& attr = xml.getAttributes();
825
0
               XMLCursor::AttributeMap::const_iterator itAttr = attr.begin();
826
0
               for (; itAttr != attr.end(); itAttr++)
827
0
               {
828
0
                  if (itAttr->first == "uri")
829
0
                  {
830
0
                     mTarget = Uri(itAttr->second.xmlCharDataDecode());
831
0
                  }
832
0
                  else
833
0
                  {
834
0
                     DebugLog(<< "Unknown dialog/participant/target attribute: " << itAttr->first << "=" << itAttr->second);
835
0
                  }
836
0
               }
837
838
0
               if (xml.firstChild())
839
0
               {
840
0
                  do
841
0
                  {
842
0
                     if (compareTag(xml.getTag(), "param", namespacePrefix))
843
0
                     {
844
0
                        parseParam(xml);
845
0
                     }
846
0
                     else
847
0
                     {
848
0
                        DebugLog(<< "Unknown dialog/particpant/target element: " << xml.getTag());
849
0
                     }
850
0
                  } while (xml.nextSibling());
851
0
                  xml.parent();
852
0
               }
853
0
            }
854
0
            catch (BaseException& ex)  // ?slg? - catch here or not - let exception bubble up? or rethrow?
855
0
            {
856
0
               DebugLog(<< "Could not parse dialog/participatn/target uri value: " << xml.getValue().xmlCharDataDecode() << ": " << ex);
857
0
            }
858
0
         }
859
0
         else if (compareTag(xml.getTag(), "session-description", namespacePrefix))
860
0
         {
861
0
            const XMLCursor::AttributeMap& attr = xml.getAttributes();
862
0
            XMLCursor::AttributeMap::const_iterator itAttr = attr.begin();
863
0
            for (; itAttr != attr.end(); itAttr++)
864
0
            {
865
0
               if (itAttr->first == "type")
866
0
               {
867
0
                  mSessionDescriptionType = itAttr->second.xmlCharDataDecode();
868
0
               }
869
0
               else
870
0
               {
871
0
                  DebugLog(<< "Unknown dialog/participant/session-description attribute: " << itAttr->first << "=" << itAttr->second);
872
0
               }
873
0
            }
874
0
            if (xml.firstChild())
875
0
            {
876
0
               mSessionDescription = xml.getValue().xmlCharDataDecode();
877
0
               xml.parent();
878
0
            }
879
0
         }
880
0
         else if (compareTag(xml.getTag(), "cseq", namespacePrefix))
881
0
         {
882
0
            if (xml.firstChild())
883
0
            {
884
0
               mCSeq = xml.getValue().convertUnsignedLong();
885
0
               mHasCSeq = true;
886
0
               xml.parent();
887
0
            }
888
0
         }
889
0
         else
890
0
         {
891
0
            DebugLog(<< "Unknown dialog participant element: " << xml.getTag());
892
0
         }
893
0
      } while (xml.nextSibling());
894
0
      xml.parent();
895
0
   }
896
0
}
897
898
void 
899
DialogInfoContents::Dialog::Participant::parseParam(XMLCursor& xml)
900
0
{
901
0
   const XMLCursor::AttributeMap& attr = xml.getAttributes();
902
0
   XMLCursor::AttributeMap::const_iterator itAttr = attr.begin();
903
0
   Data name;
904
0
   Data value;
905
0
   for (; itAttr != attr.end(); itAttr++)
906
0
   {
907
0
      if (itAttr->first == "pname")
908
0
      {
909
0
         name = itAttr->second.xmlCharDataDecode();
910
0
      }
911
0
      else if (itAttr->first == "pval")
912
0
      {
913
0
         value = itAttr->second.xmlCharDataDecode();
914
0
      }
915
0
      else
916
0
      {
917
0
         DebugLog(<< "Unknown dialog/participant/target/param attribute: " << itAttr->first << "=" << itAttr->second);
918
0
      }
919
0
   }
920
0
   if (!name.empty())
921
0
   {
922
0
      mTargetParams[name] = value;
923
0
   }
924
0
}
925
926
void 
927
DialogInfoContents::addDialog(const Dialog& dialog)
928
0
{
929
0
   checkParsed();
930
   // Ensure id doesn't already exist
931
0
   removeDialog(dialog.mId);
932
0
   mDialogs.push_back(dialog);
933
0
}
934
935
bool 
936
DialogInfoContents::removeDialog(const Data& id) 
937
0
{ 
938
0
   checkParsed();
939
0
   for (DialogList::iterator it = mDialogs.begin(); it != mDialogs.end(); it++)
940
0
   {
941
0
      if (it->mId == id)
942
0
      {
943
0
         mDialogs.erase(it);
944
0
         return true;
945
0
      }
946
0
   }
947
0
   return false;
948
0
}
949
950
951
/* ====================================================================
952
*
953
* Copyright (c) 2016 SIP Spectrum, Inc.  All rights reserved.
954
*
955
* Redistribution and use in source and binary forms, with or without
956
* modification, are permitted provided that the following conditions
957
* are met:
958
*
959
* 1. Redistributions of source code must retain the above copyright
960
*    notice, this list of conditions and the following disclaimer.
961
*
962
* 2. Redistributions in binary form must reproduce the above copyright
963
*    notice, this list of conditions and the following disclaimer in
964
*    the documentation and/or other materials provided with the
965
*    distribution.
966
*
967
* 3. Neither the name of the author(s) nor the names of any contributors
968
*    may be used to endorse or promote products derived from this software
969
*    without specific prior written permission.
970
*
971
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS "AS IS" AND
972
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
973
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
974
* ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE
975
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
976
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
977
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
978
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
979
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
980
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
981
* SUCH DAMAGE.
982
*
983
* ====================================================================
984
*
985
*/
986
/*
987
* vi: set shiftwidth=3 expandtab:
988
*/