/src/resiprocate/resip/stack/GenericPidfContents.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/GenericPidfContents.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 BasePidfNamespaceUri("urn:ietf:params:xml:ns:pidf"); |
22 | | |
23 | | bool |
24 | | GenericPidfContents::init() |
25 | 4 | { |
26 | 4 | static ContentsFactory<GenericPidfContents> factory; |
27 | 4 | (void)factory; |
28 | 4 | return true; |
29 | 4 | } |
30 | | |
31 | | const GenericPidfContents GenericPidfContents::Empty; |
32 | | |
33 | | GenericPidfContents::GenericPidfContents() |
34 | | : Contents(getStaticType()), mSimplePresenceExtracted(false) |
35 | 2 | { |
36 | 2 | } |
37 | | |
38 | | GenericPidfContents::GenericPidfContents(const Mime& contentType) |
39 | | : Contents(getStaticType()), mSimplePresenceExtracted(false) |
40 | 0 | { |
41 | 0 | } |
42 | | |
43 | | GenericPidfContents::GenericPidfContents(const HeaderFieldValue& hfv, const Mime& contentsType) |
44 | | : Contents(hfv, contentsType), mSimplePresenceExtracted(false) |
45 | 0 | { |
46 | 0 | } |
47 | | |
48 | | GenericPidfContents& |
49 | | GenericPidfContents::operator=(const GenericPidfContents& rhs) |
50 | 0 | { |
51 | 0 | if (this != &rhs) |
52 | 0 | { |
53 | 0 | Contents::operator=(rhs); |
54 | | |
55 | | // clear any data then merge in new stuff |
56 | 0 | reset(); |
57 | 0 | mergeNoCheckParse(rhs); |
58 | 0 | } |
59 | 0 | return *this; |
60 | 0 | } |
61 | | |
62 | | GenericPidfContents::GenericPidfContents(const GenericPidfContents& rhs) |
63 | | : Contents(rhs), mSimplePresenceExtracted(false) |
64 | 0 | { |
65 | | // merge in new stuff |
66 | 0 | mergeNoCheckParse(rhs); |
67 | 0 | } |
68 | | |
69 | | GenericPidfContents::~GenericPidfContents() |
70 | 0 | { |
71 | 0 | reset(); |
72 | 0 | } |
73 | | |
74 | | void |
75 | | GenericPidfContents::reset() |
76 | 0 | { |
77 | | // Cleanup Node Memory recursively |
78 | 0 | cleanupNodeMemory(mRootNodes); |
79 | | |
80 | | // Clear namespace map |
81 | 0 | mNamespaces.clear(); |
82 | |
|
83 | 0 | mRootPidfNamespacePrefix.clear(); |
84 | 0 | mEntity.host().clear(); |
85 | 0 | mEntity.user().clear(); |
86 | |
|
87 | 0 | clearSimplePresenceInfo(); |
88 | 0 | } |
89 | | |
90 | | void GenericPidfContents::clearSimplePresenceInfo() |
91 | 0 | { |
92 | 0 | SimplePresenceInfoList::iterator itSPList = mSimplePresenceInfoList.begin(); |
93 | 0 | for (; itSPList != mSimplePresenceInfoList.end(); itSPList++) |
94 | 0 | { |
95 | 0 | delete *itSPList; |
96 | 0 | } |
97 | 0 | mSimplePresenceInfoList.clear(); |
98 | 0 | mSimplePresenceExtracted = false; |
99 | 0 | } |
100 | | |
101 | | void |
102 | | GenericPidfContents::cleanupNodeMemory(NodeList& nodeList) |
103 | 0 | { |
104 | | // Cleanup Node Memory recursively |
105 | 0 | NodeList::iterator itNode = nodeList.begin(); |
106 | 0 | for (; itNode != nodeList.end(); itNode++) |
107 | 0 | { |
108 | 0 | cleanupNodeMemory((*itNode)->mChildren); |
109 | 0 | delete *itNode; |
110 | 0 | } |
111 | 0 | nodeList.clear(); |
112 | 0 | } |
113 | | |
114 | | Contents* |
115 | | GenericPidfContents::clone() const |
116 | 0 | { |
117 | 0 | return new GenericPidfContents(*this); |
118 | 0 | } |
119 | | |
120 | | const Mime& |
121 | | GenericPidfContents::getStaticType() |
122 | 4 | { |
123 | 4 | static Mime type("application","pidf+xml"); |
124 | 4 | return type; |
125 | 4 | } |
126 | | |
127 | | EncodeStream& |
128 | | GenericPidfContents::encodeParsed(EncodeStream& str) const |
129 | 0 | { |
130 | 0 | str << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << Symbols::CRLF; |
131 | 0 | str << "<" << mRootPidfNamespacePrefix << "presence "; |
132 | 0 | NamespaceMap::const_iterator itNs = mNamespaces.begin(); |
133 | 0 | bool first = true; |
134 | 0 | for (; itNs != mNamespaces.end(); itNs++) |
135 | 0 | { |
136 | 0 | if (first) |
137 | 0 | { |
138 | 0 | first = false; |
139 | 0 | str << "xmlns"; |
140 | 0 | } |
141 | 0 | else |
142 | 0 | { |
143 | 0 | str << " xmlns"; |
144 | 0 | } |
145 | 0 | if (!itNs->second.empty()) // Check if prefix is not-empty |
146 | 0 | { |
147 | 0 | str << ":" << itNs->second.substr(0, itNs->second.size() - 1); // remove trailing ":" |
148 | 0 | } |
149 | 0 | str << "=\"" << itNs->first << "\"" << Symbols::CRLF; |
150 | 0 | } |
151 | 0 | str << " entity=\"" << mEntity << "\">" << Symbols::CRLF; |
152 | 0 | NodeList::const_iterator itNode = mRootNodes.begin(); |
153 | 0 | Data indent(" "); |
154 | 0 | for (; itNode != mRootNodes.end(); itNode++) |
155 | 0 | { |
156 | 0 | (*itNode)->encode(str, indent); |
157 | 0 | } |
158 | 0 | str << "</" << mRootPidfNamespacePrefix << "presence>" << Symbols::CRLF; |
159 | |
|
160 | 0 | return str; |
161 | 0 | } |
162 | | |
163 | | void |
164 | | GenericPidfContents::parse(ParseBuffer& pb) |
165 | 0 | { |
166 | 0 | mSimplePresenceExtracted = false; |
167 | |
|
168 | 0 | XMLCursor xml(pb); |
169 | 0 | const XMLCursor::AttributeMap& attr = xml.getAttributes(); |
170 | 0 | XMLCursor::AttributeMap::const_iterator itAttr = attr.begin(); |
171 | 0 | for (; itAttr != attr.end(); itAttr++) |
172 | 0 | { |
173 | 0 | if (itAttr->first.prefix("xmlns")) |
174 | 0 | { |
175 | 0 | Data prefix; |
176 | 0 | ParseBuffer pb(itAttr->first); |
177 | 0 | pb.skipToChar(Symbols::COLON[0]); |
178 | 0 | if (!pb.eof()) |
179 | 0 | { |
180 | 0 | pb.skipChar(); |
181 | 0 | const char* anchor = pb.position(); |
182 | 0 | pb.skipToEnd(); |
183 | 0 | pb.data(prefix, anchor); |
184 | 0 | prefix += Symbols::COLON; |
185 | 0 | } |
186 | 0 | if (isEqualNoCase(itAttr->second, BasePidfNamespaceUri)) |
187 | 0 | { |
188 | 0 | mRootPidfNamespacePrefix = prefix; |
189 | 0 | } |
190 | |
|
191 | 0 | mNamespaces[itAttr->second] = prefix; |
192 | 0 | } |
193 | 0 | else if (itAttr->first == "entity") |
194 | 0 | { |
195 | 0 | mEntity = Uri(itAttr->second); // can throw! |
196 | 0 | } |
197 | 0 | else |
198 | 0 | { |
199 | 0 | DebugLog(<< "Unknown root attribute: " << itAttr->first << "=" << itAttr->second); |
200 | 0 | } |
201 | 0 | } |
202 | | |
203 | | // Ensure root presence node is present |
204 | 0 | if (xml.getTag() == mRootPidfNamespacePrefix + Symbols::Presence) |
205 | 0 | { |
206 | 0 | if (xml.firstChild()) |
207 | 0 | { |
208 | 0 | do |
209 | 0 | { |
210 | 0 | parseChildren(xml, mRootNodes); |
211 | 0 | } while (xml.nextSibling()); |
212 | 0 | xml.parent(); |
213 | 0 | } |
214 | 0 | } |
215 | 0 | else |
216 | 0 | { |
217 | | // TODO Throw? |
218 | 0 | DebugLog(<< "Aborting parse, root presence node missing: " << mRootPidfNamespacePrefix + Symbols::Presence); |
219 | 0 | } |
220 | 0 | } |
221 | | |
222 | | void |
223 | | GenericPidfContents::parseChildren(XMLCursor& xml, NodeList& nodeList) |
224 | 0 | { |
225 | 0 | Node* node = new Node(); |
226 | 0 | node->mAttributes = xml.getAttributes(); // !slg! yuck - yes we are copying memory for attributes |
227 | 0 | node->mValue.duplicate(xml.getValue()); // use Data::duplicate to avoid copying memory |
228 | 0 | ParseBuffer pb(xml.getTag()); |
229 | 0 | const char* anchor = pb.position(); |
230 | 0 | pb.skipToChar(Symbols::COLON[0]); |
231 | 0 | if (!pb.eof()) |
232 | 0 | { |
233 | 0 | pb.skipChar(); |
234 | 0 | pb.data(node->mNamespacePrefix, anchor); |
235 | 0 | anchor = pb.position(); |
236 | 0 | pb.skipToEnd(); |
237 | 0 | pb.data(node->mTag, anchor); |
238 | 0 | } |
239 | 0 | else |
240 | 0 | { |
241 | | // No namespace prefix |
242 | 0 | node->mTag.duplicate(xml.getTag()); // use Data::duplicate to avoid copying memory |
243 | 0 | } |
244 | |
|
245 | 0 | if (node->mValue.empty() && xml.firstChild()) |
246 | 0 | { |
247 | 0 | do |
248 | 0 | { |
249 | 0 | if (!xml.getValue().empty()) |
250 | 0 | { |
251 | 0 | node->mValue.duplicate(xml.getValue()); // use Data::duplicate to avoid copying memory |
252 | 0 | } |
253 | 0 | else |
254 | 0 | { |
255 | 0 | parseChildren(xml, node->mChildren); |
256 | 0 | } |
257 | 0 | } while (xml.nextSibling()); |
258 | 0 | xml.parent(); |
259 | 0 | } |
260 | 0 | nodeList.push_back(node); |
261 | 0 | } |
262 | | |
263 | | EncodeStream& |
264 | | GenericPidfContents::Node::encodeAttributes(EncodeStream& str) const |
265 | 0 | { |
266 | 0 | AttributeMap::const_iterator itAttrib = mAttributes.begin(); |
267 | 0 | for (; itAttrib != mAttributes.end(); itAttrib++) |
268 | 0 | { |
269 | 0 | str << " " << itAttrib->first << "=\"" << itAttrib->second << "\""; |
270 | 0 | } |
271 | 0 | return str; |
272 | 0 | } |
273 | | |
274 | | EncodeStream& |
275 | | GenericPidfContents::Node::encode(EncodeStream& str, Data indent) const |
276 | 0 | { |
277 | 0 | if (!mTag.empty()) |
278 | 0 | { |
279 | 0 | if (mChildren.size() == 0) |
280 | 0 | { |
281 | 0 | if (mValue.empty()) |
282 | 0 | { |
283 | 0 | str << indent << "<" << mNamespacePrefix << mTag; |
284 | 0 | encodeAttributes(str); |
285 | 0 | str << "/>" << Symbols::CRLF; |
286 | 0 | } |
287 | 0 | else |
288 | 0 | { |
289 | 0 | str << indent << "<" << mNamespacePrefix << mTag; |
290 | 0 | encodeAttributes(str); |
291 | 0 | str << ">" << mValue << "</" << mNamespacePrefix << mTag << ">" << Symbols::CRLF; |
292 | 0 | } |
293 | 0 | } |
294 | | // The following else collapses simple single nodes (no attributes or values) to one line: ie: |
295 | | // <outernode></innernode></outernode> |
296 | 0 | else if (mChildren.size() == 1 && mAttributes.empty() && |
297 | 0 | mChildren.front()->mValue.empty() && mChildren.front()->mAttributes.empty() && mChildren.front()->mChildren.size() == 0) |
298 | 0 | { |
299 | 0 | str << indent << "<" << mNamespacePrefix << mTag << "><" << mChildren.front()->mNamespacePrefix; |
300 | 0 | str << mChildren.front()->mTag << "/></" << mNamespacePrefix << mTag << ">" << Symbols::CRLF; |
301 | 0 | } |
302 | 0 | else |
303 | 0 | { |
304 | 0 | str << indent << "<" << mNamespacePrefix << mTag; |
305 | 0 | encodeAttributes(str); |
306 | 0 | str << ">" << Symbols::CRLF; |
307 | 0 | NodeList::const_iterator itNode = mChildren.begin(); |
308 | 0 | for (; itNode != mChildren.end(); itNode++) |
309 | 0 | { |
310 | 0 | (*itNode)->encode(str, indent + " "); |
311 | 0 | } |
312 | 0 | str << indent << "</" << mNamespacePrefix << mTag << ">" << Symbols::CRLF; |
313 | 0 | } |
314 | 0 | } |
315 | 0 | return str; |
316 | 0 | } |
317 | | |
318 | | void |
319 | | GenericPidfContents::Node::copy(const Node& rhs, HashMap<Data, Data>* namespacePrefixCorrections) |
320 | 0 | { |
321 | 0 | if (namespacePrefixCorrections) |
322 | 0 | { |
323 | | // Check if namespace should be corrected |
324 | 0 | HashMap<Data, Data>::iterator itNsCorr = namespacePrefixCorrections->find(rhs.mNamespacePrefix); |
325 | 0 | if (itNsCorr != namespacePrefixCorrections->end()) |
326 | 0 | { |
327 | 0 | mNamespacePrefix = itNsCorr->second; |
328 | 0 | } |
329 | 0 | else |
330 | 0 | { |
331 | 0 | mNamespacePrefix = rhs.mNamespacePrefix; |
332 | 0 | } |
333 | 0 | } |
334 | 0 | else |
335 | 0 | { |
336 | 0 | mNamespacePrefix = rhs.mNamespacePrefix; |
337 | 0 | } |
338 | 0 | mTag = rhs.mTag; |
339 | 0 | mAttributes = rhs.mAttributes; |
340 | 0 | mValue = rhs.mValue; |
341 | 0 | NodeList::const_iterator itNode = rhs.mChildren.begin(); |
342 | 0 | for (; itNode != rhs.mChildren.end(); itNode++) |
343 | 0 | { |
344 | 0 | Node* node = new Node(); |
345 | 0 | node->copy(*(*itNode), namespacePrefixCorrections); |
346 | 0 | mChildren.push_back(node); |
347 | 0 | } |
348 | 0 | } |
349 | | |
350 | | bool |
351 | | GenericPidfContents::merge(const GenericPidfContents& other) |
352 | 0 | { |
353 | | // Ensure both sides are parsed if not already |
354 | 0 | checkParsed(); |
355 | 0 | other.checkParsed(); |
356 | 0 | return mergeNoCheckParse(other); |
357 | 0 | } |
358 | | |
359 | | void |
360 | | GenericPidfContents::setRootNodes(const NodeList& nodeList) |
361 | 0 | { |
362 | 0 | mRootNodes.clear(); |
363 | 0 | mRootNodes = nodeList; |
364 | 0 | } |
365 | | |
366 | | const Data& |
367 | | GenericPidfContents::getSubNodeValue(Node* node, const Data& tag) |
368 | 0 | { |
369 | 0 | NodeList::iterator it = node->mChildren.begin(); |
370 | 0 | for (; it != node->mChildren.end(); it++) |
371 | 0 | { |
372 | 0 | if ((*it)->mTag == tag) |
373 | 0 | { |
374 | 0 | return (*it)->mValue; |
375 | 0 | } |
376 | 0 | } |
377 | 0 | return Data::Empty; |
378 | 0 | } |
379 | | |
380 | | bool |
381 | | GenericPidfContents::mergeNoCheckParse(const GenericPidfContents& other) |
382 | 0 | { |
383 | 0 | mSimplePresenceExtracted = false; |
384 | | |
385 | | // Validate entity user and host - we allow mismatched schemes |
386 | 0 | if (mEntity.host().empty()) |
387 | 0 | { |
388 | 0 | mEntity = other.mEntity; |
389 | 0 | } |
390 | 0 | else if(mEntity.user() != other.mEntity.user() || |
391 | 0 | mEntity.host() != other.mEntity.host()) |
392 | 0 | { |
393 | 0 | DebugLog(<< "Merge failed, entities do not match: " << mEntity << ", other=" << other.mEntity); |
394 | 0 | return false; |
395 | 0 | } |
396 | | |
397 | 0 | HashMap<Data, Data> namespacePrefixCorrections; // other/old prefix name, new/dest prefix name |
398 | | |
399 | | // Copy over namespaces - looking for mismatched prefixes |
400 | 0 | bool checkNamespaceMismatches = mNamespaces.size() > 0; |
401 | 0 | NamespaceMap::const_iterator itOtherNs = other.mNamespaces.begin(); |
402 | 0 | for(; itOtherNs != other.mNamespaces.end(); itOtherNs++) |
403 | 0 | { |
404 | | // Check if namespace is already in list and if so - verify prefix will match |
405 | 0 | bool found = false; |
406 | 0 | if (checkNamespaceMismatches) |
407 | 0 | { |
408 | 0 | NamespaceMap::iterator itNs = mNamespaces.find(itOtherNs->first); |
409 | 0 | if (itNs != mNamespaces.end()) |
410 | 0 | { |
411 | 0 | if (itNs->second != itOtherNs->second) |
412 | 0 | { |
413 | | // Prefix used for same namespace does not match |
414 | 0 | namespacePrefixCorrections[itOtherNs->second] = itNs->second; |
415 | 0 | } |
416 | 0 | found = true; |
417 | 0 | } |
418 | 0 | } |
419 | 0 | if(!found) |
420 | 0 | { |
421 | 0 | mNamespaces[itOtherNs->first] = itOtherNs->second; // Copy over |
422 | 0 | } |
423 | 0 | } |
424 | | // If we didn't check for namespace mismatches then we didn't have any namespaces |
425 | | // to start with, which means we didn't have a root namespace - set it now |
426 | 0 | if (!checkNamespaceMismatches) |
427 | 0 | { |
428 | 0 | mRootPidfNamespacePrefix = other.mRootPidfNamespacePrefix; |
429 | 0 | } |
430 | | |
431 | | // Merge root presence nodes |
432 | 0 | bool checkMatches = mRootNodes.size() > 0; |
433 | 0 | NodeList::const_iterator itOtherNode = other.mRootNodes.begin(); |
434 | 0 | for(; itOtherNode != other.mRootNodes.end(); itOtherNode++) |
435 | 0 | { |
436 | | // If there is an ID attribute then see if tag/id combo lives already in local list |
437 | 0 | bool matchFound = false; |
438 | 0 | if (checkMatches) |
439 | 0 | { |
440 | 0 | Node::AttributeMap::iterator itOtherAttrib = (*itOtherNode)->mAttributes.find("id"); |
441 | 0 | if (itOtherAttrib != (*itOtherNode)->mAttributes.end()) |
442 | 0 | { |
443 | 0 | NodeList::iterator itNode = mRootNodes.begin(); |
444 | 0 | for (; itNode != mRootNodes.end(); itNode++) |
445 | 0 | { |
446 | 0 | if ((*itNode)->mTag == (*itOtherNode)->mTag) |
447 | 0 | { |
448 | | // Node found - check for id match |
449 | 0 | Node::AttributeMap::iterator itAttrib = (*itNode)->mAttributes.find("id"); |
450 | 0 | if (itAttrib != (*itNode)->mAttributes.end()) |
451 | 0 | { |
452 | 0 | if (itOtherAttrib->second == itAttrib->second) // Check if Id's match |
453 | 0 | { |
454 | | // compare timestamps |
455 | 0 | const Data& ts1 = getSubNodeValue((*itNode), "timestamp"); |
456 | 0 | const Data& ts2 = getSubNodeValue((*itOtherNode), "timestamp"); |
457 | | // Note: to compare timestamps we use a string compare and rely on properties from RFC3339 (section 5.1) with |
458 | | // the assumption that same id items will generate timestamps in the same timezone and timezone format |
459 | 0 | if (ts1.empty() || ts2.empty() || ts2 >= ts1) |
460 | 0 | { |
461 | 0 | cleanupNodeMemory((*itNode)->mChildren); |
462 | 0 | (*itNode)->copy(*(*itOtherNode), &namespacePrefixCorrections); |
463 | 0 | } |
464 | 0 | matchFound = true; |
465 | 0 | break; |
466 | 0 | } |
467 | 0 | } |
468 | 0 | } |
469 | 0 | } |
470 | 0 | } |
471 | 0 | } |
472 | 0 | if (!matchFound) |
473 | 0 | { |
474 | 0 | Node* node = new Node(); |
475 | 0 | node->copy(*(*itOtherNode), namespacePrefixCorrections.size() > 0 ? &namespacePrefixCorrections : 0); |
476 | 0 | mRootNodes.push_back(node); |
477 | 0 | } |
478 | 0 | } |
479 | 0 | return true; |
480 | 0 | } |
481 | | |
482 | | void |
483 | | GenericPidfContents::setEntity(const Uri& entity) |
484 | 0 | { |
485 | 0 | checkParsed(); |
486 | 0 | mEntity = entity; |
487 | 0 | } |
488 | | |
489 | | const Uri& |
490 | | GenericPidfContents::getEntity() const |
491 | 0 | { |
492 | 0 | checkParsed(); |
493 | 0 | return mEntity; |
494 | 0 | } |
495 | | |
496 | | void |
497 | | GenericPidfContents::addNamespace(const Data& uri, const Data& prefix) |
498 | 0 | { |
499 | 0 | checkParsed(); |
500 | 0 | Data adjustedPrefix(prefix); |
501 | | // Add colon to end if missing if prefix was provided |
502 | 0 | if (!prefix.empty() && !prefix.postfix(Symbols::COLON)) |
503 | 0 | { |
504 | 0 | adjustedPrefix += Symbols::COLON; |
505 | 0 | } |
506 | 0 | if (isEqualNoCase(uri, BasePidfNamespaceUri)) |
507 | 0 | { |
508 | 0 | mRootPidfNamespacePrefix = adjustedPrefix; |
509 | 0 | } |
510 | 0 | mNamespaces[uri] = adjustedPrefix; |
511 | 0 | } |
512 | | |
513 | | void |
514 | | GenericPidfContents::setSimplePresenceTupleNode(const Data& id, |
515 | | bool online, |
516 | | const Data& timestamp, |
517 | | const Data& note, |
518 | | const Data& contact, |
519 | | const Data& contactPriority) |
520 | 0 | { |
521 | | // Make sure we have extract any existing simple presence info |
522 | 0 | extractSimplePresenceInfo(); |
523 | |
|
524 | 0 | if (mNamespaces.empty()) |
525 | 0 | { |
526 | | // Add default namespace |
527 | 0 | addNamespace(BasePidfNamespaceUri, Data::Empty); // no custom prefix |
528 | 0 | } |
529 | | |
530 | | // See if node exists and if so delete it - otherwise add new |
531 | 0 | bool foundExisting = false; |
532 | 0 | NodeList::iterator it = mRootNodes.begin(); |
533 | 0 | for(; it != mRootNodes.end(); it++) |
534 | 0 | { |
535 | 0 | if((*it)->mTag == "tuple") |
536 | 0 | { |
537 | 0 | Node::AttributeMap::iterator itAttrib = (*it)->mAttributes.find("id"); |
538 | 0 | if(itAttrib != (*it)->mAttributes.end()) |
539 | 0 | { |
540 | 0 | if(itAttrib->second == id) |
541 | 0 | { |
542 | 0 | foundExisting = true; |
543 | 0 | break; |
544 | 0 | } |
545 | 0 | } |
546 | 0 | } |
547 | 0 | } |
548 | |
|
549 | 0 | Node* tupleNode; |
550 | 0 | if(foundExisting) |
551 | 0 | { |
552 | | // Remove all children and add back, below |
553 | 0 | cleanupNodeMemory((*it)->mChildren); |
554 | 0 | tupleNode = (*it); |
555 | 0 | } |
556 | 0 | else |
557 | 0 | { |
558 | 0 | tupleNode = new Node(); |
559 | 0 | tupleNode->mNamespacePrefix = mRootPidfNamespacePrefix; |
560 | 0 | tupleNode->mTag = "tuple"; |
561 | 0 | tupleNode->mAttributes["id"] = id; |
562 | 0 | } |
563 | | |
564 | | // Add Status Node with Basic subnode |
565 | 0 | Node* statusNode = new Node(); |
566 | 0 | statusNode->mNamespacePrefix = mRootPidfNamespacePrefix; |
567 | 0 | statusNode->mTag = "status"; |
568 | 0 | Node* basicNode = new Node(); |
569 | 0 | basicNode->mNamespacePrefix = mRootPidfNamespacePrefix; |
570 | 0 | basicNode->mTag = "basic"; |
571 | 0 | basicNode->mValue = online ? "open" : "closed"; |
572 | 0 | statusNode->mChildren.push_back(basicNode); |
573 | 0 | tupleNode->mChildren.push_back(statusNode); |
574 | | |
575 | | // Add Contact node if required |
576 | 0 | if (!contact.empty()) |
577 | 0 | { |
578 | 0 | Node* contactNode = new Node(); |
579 | 0 | contactNode->mNamespacePrefix = mRootPidfNamespacePrefix; |
580 | 0 | contactNode->mTag = "contact"; |
581 | 0 | contactNode->mValue = contact; |
582 | 0 | if (!contactPriority.empty()) |
583 | 0 | { |
584 | 0 | contactNode->mAttributes["priority"] = contactPriority; |
585 | 0 | } |
586 | 0 | tupleNode->mChildren.push_back(contactNode); |
587 | 0 | } |
588 | | |
589 | | // Add Note node if required |
590 | 0 | if (!note.empty()) |
591 | 0 | { |
592 | 0 | Node* noteNode = new Node(); |
593 | 0 | noteNode->mNamespacePrefix = mRootPidfNamespacePrefix; |
594 | 0 | noteNode->mTag = "note"; |
595 | 0 | noteNode->mValue = note; |
596 | 0 | tupleNode->mChildren.push_back(noteNode); |
597 | 0 | } |
598 | | |
599 | | // Add Timestamp node if required |
600 | 0 | if (!timestamp.empty()) |
601 | 0 | { |
602 | 0 | Node* timestampNode = new Node(); |
603 | 0 | timestampNode->mNamespacePrefix = mRootPidfNamespacePrefix; |
604 | 0 | timestampNode->mTag = "timestamp"; |
605 | 0 | timestampNode->mValue = timestamp; |
606 | 0 | tupleNode->mChildren.push_back(timestampNode); |
607 | 0 | } |
608 | |
|
609 | 0 | if (!foundExisting) |
610 | 0 | { |
611 | 0 | mRootNodes.push_back(tupleNode); |
612 | 0 | } |
613 | | |
614 | | // store info in list - if TupleId exists already then update it, otherwise add new |
615 | 0 | foundExisting = false; // reuse flag from above |
616 | 0 | SimplePresenceInfoList::iterator itSPList = mSimplePresenceInfoList.begin(); |
617 | 0 | for (; itSPList != mSimplePresenceInfoList.end(); itSPList++) |
618 | 0 | { |
619 | 0 | if ((*itSPList)->mTupleId == id) |
620 | 0 | { |
621 | 0 | (*itSPList)->mOnline = online; |
622 | 0 | (*itSPList)->mTimestamp = timestamp; |
623 | 0 | (*itSPList)->mNote = note; |
624 | 0 | (*itSPList)->mContact = contact; |
625 | 0 | (*itSPList)->mContactPriority = contactPriority; |
626 | 0 | foundExisting = true; |
627 | 0 | } |
628 | 0 | } |
629 | 0 | if (!foundExisting) |
630 | 0 | { |
631 | 0 | SimplePresenceInfo* info = new SimplePresenceInfo; |
632 | 0 | info->mTupleId = id; |
633 | 0 | info->mOnline = online; |
634 | 0 | info->mTimestamp = timestamp; |
635 | 0 | info->mNote = note; |
636 | 0 | info->mContact = contact; |
637 | 0 | info->mContactPriority = contactPriority; |
638 | 0 | mSimplePresenceInfoList.push_back(info); |
639 | 0 | } |
640 | 0 | mSimplePresenceExtracted = true; |
641 | 0 | } |
642 | | |
643 | | const Data& |
644 | | GenericPidfContents::getSimplePresenceTupleId() |
645 | 0 | { |
646 | 0 | checkParsed(); |
647 | 0 | extractSimplePresenceInfo(); |
648 | 0 | if (mSimplePresenceInfoList.empty()) |
649 | 0 | { |
650 | 0 | return Data::Empty; |
651 | 0 | } |
652 | 0 | else |
653 | 0 | { |
654 | 0 | return mSimplePresenceInfoList.front()->mTupleId; |
655 | 0 | } |
656 | 0 | } |
657 | | |
658 | | const bool |
659 | | GenericPidfContents::getSimplePresenceOnline() |
660 | 0 | { |
661 | 0 | checkParsed(); |
662 | 0 | extractSimplePresenceInfo(); |
663 | 0 | if (mSimplePresenceInfoList.empty()) |
664 | 0 | { |
665 | 0 | return false; |
666 | 0 | } |
667 | 0 | else |
668 | 0 | { |
669 | 0 | return mSimplePresenceInfoList.front()->mOnline; |
670 | 0 | } |
671 | 0 | } |
672 | | |
673 | | const Data& |
674 | | GenericPidfContents::getSimplePresenceTimestamp() |
675 | 0 | { |
676 | 0 | checkParsed(); |
677 | 0 | extractSimplePresenceInfo(); |
678 | 0 | if (mSimplePresenceInfoList.empty()) |
679 | 0 | { |
680 | 0 | return Data::Empty; |
681 | 0 | } |
682 | 0 | else |
683 | 0 | { |
684 | 0 | return mSimplePresenceInfoList.front()->mTimestamp; |
685 | 0 | } |
686 | 0 | } |
687 | | |
688 | | const Data& |
689 | | GenericPidfContents::getSimplePresenceNote() |
690 | 0 | { |
691 | 0 | checkParsed(); |
692 | 0 | extractSimplePresenceInfo(); |
693 | 0 | if (mSimplePresenceInfoList.empty()) |
694 | 0 | { |
695 | 0 | return Data::Empty; |
696 | 0 | } |
697 | 0 | else |
698 | 0 | { |
699 | 0 | return mSimplePresenceInfoList.front()->mNote; |
700 | 0 | } |
701 | 0 | } |
702 | | |
703 | | const Data& |
704 | | GenericPidfContents::getSimplePresenceContact() |
705 | 0 | { |
706 | 0 | checkParsed(); |
707 | 0 | extractSimplePresenceInfo(); |
708 | 0 | if (mSimplePresenceInfoList.empty()) |
709 | 0 | { |
710 | 0 | return Data::Empty; |
711 | 0 | } |
712 | 0 | else |
713 | 0 | { |
714 | 0 | return mSimplePresenceInfoList.front()->mContact; |
715 | 0 | } |
716 | 0 | } |
717 | | |
718 | | const Data& |
719 | | GenericPidfContents::getSimplePresenceContactPriority() |
720 | 0 | { |
721 | 0 | checkParsed(); |
722 | 0 | extractSimplePresenceInfo(); |
723 | 0 | if (mSimplePresenceInfoList.empty()) |
724 | 0 | { |
725 | 0 | return Data::Empty; |
726 | 0 | } |
727 | 0 | else |
728 | 0 | { |
729 | 0 | return mSimplePresenceInfoList.front()->mContactPriority; |
730 | 0 | } |
731 | 0 | } |
732 | | |
733 | | void |
734 | | GenericPidfContents::extractSimplePresenceInfo() |
735 | 0 | { |
736 | 0 | if (!mSimplePresenceExtracted) |
737 | 0 | { |
738 | 0 | clearSimplePresenceInfo(); |
739 | | |
740 | | // Iterate through root nodes and find first tuple |
741 | 0 | NodeList::const_iterator itNode = mRootNodes.begin(); |
742 | 0 | for (; itNode != mRootNodes.end(); itNode++) |
743 | 0 | { |
744 | 0 | if ((*itNode)->mTag == "tuple") |
745 | 0 | { |
746 | 0 | Node::AttributeMap::iterator itAttrib = (*itNode)->mAttributes.find("id"); |
747 | 0 | if (itAttrib != (*itNode)->mAttributes.end()) |
748 | 0 | { |
749 | 0 | SimplePresenceInfo* info = new SimplePresenceInfo; |
750 | 0 | info->mTupleId = itAttrib->second; |
751 | | // iterate through children looking for nodes we want |
752 | 0 | NodeList::const_iterator itTupleChild = (*itNode)->mChildren.begin(); |
753 | 0 | for (; itTupleChild != (*itNode)->mChildren.end(); itTupleChild++) |
754 | 0 | { |
755 | 0 | if ((*itTupleChild)->mTag == "status") |
756 | 0 | { |
757 | | // iterate through children looking for basic node |
758 | 0 | NodeList::const_iterator itStatusChild = (*itTupleChild)->mChildren.begin(); |
759 | 0 | for (; itStatusChild != (*itTupleChild)->mChildren.end(); itStatusChild++) |
760 | 0 | { |
761 | 0 | if ((*itStatusChild)->mTag == "basic") |
762 | 0 | { |
763 | 0 | info->mOnline = (*itStatusChild)->mValue == "open"; |
764 | 0 | break; |
765 | 0 | } |
766 | 0 | } |
767 | 0 | } |
768 | 0 | else if (info->mContact.empty() && (*itTupleChild)->mTag == "contact") |
769 | 0 | { |
770 | 0 | info->mContact = (*itTupleChild)->mValue; |
771 | 0 | Node::AttributeMap::iterator itAttrib = (*itTupleChild)->mAttributes.find("priority"); |
772 | 0 | if (itAttrib != (*itTupleChild)->mAttributes.end()) |
773 | 0 | { |
774 | 0 | info->mContactPriority = itAttrib->second; |
775 | 0 | } |
776 | 0 | } |
777 | 0 | else if (info->mNote.empty() && (*itTupleChild)->mTag == "note") |
778 | 0 | { |
779 | 0 | info->mNote = (*itTupleChild)->mValue; |
780 | 0 | } |
781 | 0 | else if (info->mTimestamp.empty() && (*itTupleChild)->mTag == "timestamp") |
782 | 0 | { |
783 | 0 | info->mTimestamp = (*itTupleChild)->mValue; |
784 | 0 | } |
785 | 0 | } |
786 | | // Just push on the back, we could consider checking if the TupleId already exists before |
787 | | // adding, but we are assuming unique tupleid entries in the document for now. |
788 | 0 | mSimplePresenceInfoList.push_back(info); |
789 | 0 | } |
790 | 0 | } |
791 | 0 | } |
792 | 0 | mSimplePresenceExtracted = true; |
793 | 0 | } |
794 | 0 | } |
795 | | |
796 | | Data |
797 | | GenericPidfContents::generateNowTimestampData() |
798 | 0 | { |
799 | 0 | time_t now; |
800 | 0 | time(&now); |
801 | 0 | return generateTimestampData(now); |
802 | 0 | } |
803 | | |
804 | | static void pad2(const int x, DataStream& str) |
805 | 0 | { |
806 | 0 | if (x < 10) |
807 | 0 | { |
808 | 0 | str << Symbols::ZERO[0]; |
809 | 0 | } |
810 | 0 | str << x; |
811 | 0 | } |
812 | | |
813 | | Data |
814 | | GenericPidfContents::generateTimestampData(time_t datetime) |
815 | 0 | { |
816 | 0 | struct tm gmt; |
817 | | #if defined(WIN32) || defined(__sun) |
818 | | struct tm *gmtp = gmtime(&datetime); |
819 | | if (gmtp == 0) |
820 | | { |
821 | | int e = getErrno(); |
822 | | DebugLog(<< "Failed to convert to gmt: " << strerror(e)); |
823 | | return Data::Empty; |
824 | | } |
825 | | memcpy(&gmt, gmtp, sizeof(gmt)); |
826 | | #else |
827 | 0 | if (gmtime_r(&datetime, &gmt) == 0) |
828 | 0 | { |
829 | 0 | int e = getErrno(); |
830 | 0 | DebugLog(<< "Failed to convert to gmt: " << strerror(e)); |
831 | 0 | return Data::Empty; |
832 | 0 | } |
833 | 0 | #endif |
834 | | |
835 | 0 | Data timestamp; |
836 | 0 | { |
837 | 0 | DataStream ds(timestamp); |
838 | 0 | ds << gmt.tm_year + 1900 << "-"; |
839 | 0 | pad2(gmt.tm_mon + 1, ds); |
840 | 0 | ds << "-"; |
841 | 0 | pad2(gmt.tm_mday, ds); |
842 | 0 | ds << "T"; |
843 | 0 | pad2(gmt.tm_hour, ds); |
844 | 0 | ds << ":"; |
845 | 0 | pad2(gmt.tm_min, ds); |
846 | 0 | ds << ":"; |
847 | 0 | pad2(gmt.tm_sec, ds); |
848 | 0 | ds << "Z"; |
849 | 0 | } |
850 | 0 | return timestamp; |
851 | 0 | } |
852 | | |
853 | | /* ==================================================================== |
854 | | * |
855 | | * Copyright (c) 2015 SIP Spectrum, Inc. All rights reserved. |
856 | | * |
857 | | * Redistribution and use in source and binary forms, with or without |
858 | | * modification, are permitted provided that the following conditions |
859 | | * are met: |
860 | | * |
861 | | * 1. Redistributions of source code must retain the above copyright |
862 | | * notice, this list of conditions and the following disclaimer. |
863 | | * |
864 | | * 2. Redistributions in binary form must reproduce the above copyright |
865 | | * notice, this list of conditions and the following disclaimer in |
866 | | * the documentation and/or other materials provided with the |
867 | | * distribution. |
868 | | * |
869 | | * 3. Neither the name of the author(s) nor the names of any contributors |
870 | | * may be used to endorse or promote products derived from this software |
871 | | * without specific prior written permission. |
872 | | * |
873 | | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS "AS IS" AND |
874 | | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
875 | | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
876 | | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE |
877 | | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
878 | | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
879 | | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
880 | | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
881 | | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
882 | | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
883 | | * SUCH DAMAGE. |
884 | | * |
885 | | * ==================================================================== |
886 | | * |
887 | | */ |
888 | | /* |
889 | | * vi: set shiftwidth=3 expandtab: |
890 | | */ |