reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
AccountingCollector.cxx
Go to the documentation of this file.
1#if defined(HAVE_CONFIG_H)
2#include "config.h"
3#endif
4
5#include <iostream>
6
7// JSON library includes
8#include "cajun/json/reader.h"
9#include "cajun/json/writer.h"
10#include "cajun/json/elements.h"
11
14#include "repro/ProxyConfig.hxx"
16#include "resip/stack/Tuple.hxx"
19#include "rutil/Logger.hxx"
20
22
23#define RESIPROCATE_SUBSYSTEM Subsystem::REPRO
24
25using namespace resip;
26using namespace repro;
27using namespace json;
28using namespace std;
29
30const static Data sessionEventQueueName = "sessioneventqueue";
31const static Data registrationEventQueueName = "regeventqueue";
32
34 mDbBaseDir(config.getConfigData("DatabasePath", "./", true)),
35 mSessionEventQueue(0),
36 mRegistrationEventQueue(0),
37 mSessionAccountingAddRoutingHeaders(config.getConfigBool("SessionAccountingAddRoutingHeaders", false)),
38 mSessionAccountingAddViaHeaders(config.getConfigBool("SessionAccountingAddViaHeaders", false)),
39 mRegistrationAccountingAddRoutingHeaders(config.getConfigBool("RegistrationAccountingAddRoutingHeaders", false)),
40 mRegistrationAccountingAddViaHeaders(config.getConfigBool("RegistrationAccountingAddViaHeaders", false)),
41 mRegistrationAccountingLogRefreshes(config.getConfigBool("RegistrationAccountingLogRefreshes", false)),
42 mFifo(0, 0) // not limited by time or size
43{
44 if(config.getConfigBool("SessionAccountingEnabled", false))
45 {
46 if(!initializeEventQueue(SessionEventType))
47 {
48 ErrLog(<< "AccountingCollector: cannot initialize session event queue!");
49 }
50 }
51 if(config.getConfigBool("RegistrationAccountingEnabled", false))
52 {
53 if(!initializeEventQueue(RegistrationEventType))
54 {
55 ErrLog(<< "AccountingCollector: cannot initialize registration event queue!");
56 }
57 }
58
59 run(); // Start thread
60}
61
63{
64 // Shutdown thread and wait for it to complete
65 shutdown();
66 join();
67
68 delete mSessionEventQueue;
69 delete mRegistrationEventQueue;
70}
71
72void
74{
76 resip_assert(msg.method() == REGISTER);
77
78 if(regevent == RegistrationRefreshed && !mRegistrationAccountingLogRefreshes)
79 {
80 // if mRegistrationAccountingLogRefreshes is false then don't log refreshes
81 return;
82 }
83
84 DateCategory datetime;
85 if(!msg.exists(h_CallId) || !msg.header(h_CallId).isWellFormed())
86 {
87 ErrLog(<< "AccountingCollector::doRegistrationAccounting: missing proper callId header: " << msg);
88 return;
89 }
90 Object regEvent;
91 regEvent["EventId"] = Number(regevent);
92 switch(regevent)
93 {
95 regEvent["EventName"] = String("Registration Added");
96 break;
98 regEvent["EventName"] = String("Registration Refreshed");
99 break;
101 regEvent["EventName"] = String("Registration Removed");
102 break;
104 regEvent["EventName"] = String("Registration Removed All");
105 break;
106 }
107 regEvent["Datetime"] = String(Data::from(datetime).c_str());
108 regEvent["CallId"] = String(msg.header(h_CallId).value().c_str());
109 if(msg.exists(h_To) && msg.header(h_To).isWellFormed())
110 {
111 if(!msg.header(h_To).displayName().empty())
112 {
113 regEvent["User"]["DisplayName"] = String(msg.header(h_To).displayName().c_str());
114 }
115 regEvent["User"]["Aor"] = String(Data::from(msg.header(h_To).uri().getAorAsUri(msg.getSource().getType())).c_str());
116 }
117 if(msg.exists(h_From) && msg.header(h_From).isWellFormed())
118 {
119 if(msg.header(h_From).uri() != msg.header(h_To).uri()) // Only log from is different from To
120 {
121 if(!msg.header(h_From).displayName().empty())
122 {
123 regEvent["From"]["DisplayName"] = String(msg.header(h_From).displayName().c_str());
124 }
125 regEvent["From"]["Uri"] = String(Data::from(msg.header(h_From).uri()).c_str());
126 }
127 }
128 if(msg.exists(h_Contacts) && !msg.header(h_Contacts).empty())
129 {
130 Array arrayContacts;
131 NameAddrs::const_iterator contactIt = msg.header(h_Contacts).begin();
132 for(; contactIt != msg.header(h_Contacts).end(); contactIt++)
133 {
134 if(contactIt->isWellFormed())
135 {
136 arrayContacts.Insert(String(Data::from(*contactIt).c_str()));
137 }
138 }
139 if(!arrayContacts.Empty())
140 {
141 regEvent["Contacts"] = arrayContacts;
142 }
143 }
144 if(msg.exists(h_Expires) && msg.header(h_Expires).isWellFormed())
145 {
146 regEvent["Expires"] = Number(msg.header(h_Expires).value());
147 }
148 if(mRegistrationAccountingAddViaHeaders &&
149 msg.exists(h_Vias) && !msg.header(h_Vias).empty())
150 {
151 Array arrayVias;
152 Vias::const_iterator viaIt = msg.header(h_Vias).begin();
153 for(; viaIt != msg.header(h_Vias).end(); viaIt++)
154 {
155 if(viaIt->isWellFormed())
156 {
157 arrayVias.Insert(String(Data::from(*viaIt).c_str()));
158 }
159 }
160 if(!arrayVias.Empty())
161 {
162 regEvent["Vias"] = arrayVias;
163 }
164 }
165 Tuple publicAddress = Helper::getClientPublicAddress(msg);
166 if(publicAddress.getType() != UNKNOWN_TRANSPORT)
167 {
168 regEvent["ClientPublicAddress"]["Transport"] = String(Tuple::toData(publicAddress.getType()).c_str());
169 regEvent["ClientPublicAddress"]["IP"] = String(Tuple::inet_ntop(publicAddress).c_str());
170 regEvent["ClientPublicAddress"]["Port"] = Number(publicAddress.getPort());
171 }
172 if(mRegistrationAccountingAddRoutingHeaders &&
173 msg.exists(h_Routes) && !msg.header(h_Routes).empty())
174 {
175 Array arrayRoutes;
176 NameAddrs::const_iterator routeIt = msg.header(h_Routes).begin();
177 for(; routeIt != msg.header(h_Routes).end(); routeIt++)
178 {
179 if(routeIt->isWellFormed())
180 {
181 arrayRoutes.Insert(String(Data::from(*routeIt).c_str()));
182 }
183 }
184 if(!arrayRoutes.Empty())
185 {
186 regEvent["Routes"] = arrayRoutes;
187 }
188 }
189 if(mRegistrationAccountingAddRoutingHeaders &&
190 msg.exists(h_Paths) && !msg.header(h_Paths).empty())
191 {
192 Array arrayPaths;
193 NameAddrs::const_iterator pathIt = msg.header(h_Paths).begin();
194 for(; pathIt != msg.header(h_Paths).end(); pathIt++)
195 {
196 if(pathIt->isWellFormed())
197 {
198 arrayPaths.Insert(String(Data::from(*pathIt).c_str()));
199 }
200 }
201 if(!arrayPaths.Empty())
202 {
203 regEvent["Paths"] = arrayPaths;
204 }
205 }
206 if(msg.exists(h_UserAgent) && msg.header(h_UserAgent).isWellFormed())
207 {
208 regEvent["UserAgent"] = String(msg.header(h_UserAgent).value().c_str());
209 }
210 pushEventObjectToQueue(regEvent, RegistrationEventType);
211}
212
213void
215{
216 //DebugLog(<< "AccountingCollector::doSessionAccounting: " << msg.brief());
217 if(msg.isRequest())
218 {
219 if(msg.method() == INVITE && !msg.header(h_To).exists(p_tag))
220 {
221 // Dialog creating INVITE
222 if(received)
223 {
224 // This came from the wire - so it is new session
225 DateCategory datetime;
226 if(!msg.exists(h_CallId) || !msg.header(h_CallId).isWellFormed())
227 {
228 ErrLog(<< "AccountingCollector::doSessionAccounting: missing proper callId header: " << msg);
229 return;
230 }
231 Object sessionEvent;
232 sessionEvent["EventId"] = Number(SessionCreated);
233 sessionEvent["EventName"] = String("Session Created");
234 sessionEvent["Datetime"] = String(Data::from(datetime).c_str());
235 sessionEvent["CallId"] = String(msg.header(h_CallId).value().c_str());
236 sessionEvent["RequestUri"] = String(Data::from(msg.header(h_RequestLine).uri()).c_str());
237 if(msg.exists(h_To) && msg.header(h_To).isWellFormed())
238 {
239 if(!msg.header(h_To).displayName().empty())
240 {
241 sessionEvent["To"]["DisplayName"] = String(msg.header(h_To).displayName().c_str());
242 }
243 sessionEvent["To"]["Uri"] = String(Data::from(msg.header(h_To).uri()).c_str());
244 }
245 if(msg.exists(h_From) && msg.header(h_From).isWellFormed())
246 {
247 if(!msg.header(h_From).displayName().empty())
248 {
249 sessionEvent["From"]["DisplayName"] = String(msg.header(h_From).displayName().c_str());
250 }
251 sessionEvent["From"]["Uri"] = String(Data::from(msg.header(h_From).uri()).c_str());
252 }
253 if(msg.exists(h_Contacts) && !msg.header(h_Contacts).empty() && msg.header(h_Contacts).front().isWellFormed())
254 {
255 sessionEvent["Contact"] = String(Data::from(msg.header(h_Contacts).front()).c_str());
256 }
257 if(mSessionAccountingAddViaHeaders &&
258 msg.exists(h_Vias) && !msg.header(h_Vias).empty())
259 {
260 Array arrayVias;
261 Vias::const_iterator viaIt = msg.header(h_Vias).begin();
262 for(; viaIt != msg.header(h_Vias).end(); viaIt++)
263 {
264 if(viaIt->isWellFormed())
265 {
266 arrayVias.Insert(String(Data::from(*viaIt).c_str()));
267 }
268 }
269 if(!arrayVias.Empty())
270 {
271 sessionEvent["Vias"] = arrayVias;
272 }
273 }
274 Tuple publicAddress = Helper::getClientPublicAddress(msg);
275 if(publicAddress.getType() != UNKNOWN_TRANSPORT)
276 {
277 sessionEvent["ClientPublicAddress"]["Transport"] = String(Tuple::toData(publicAddress.getType()).c_str());
278 sessionEvent["ClientPublicAddress"]["IP"] = String(Tuple::inet_ntop(publicAddress).c_str());
279 sessionEvent["ClientPublicAddress"]["Port"] = Number(publicAddress.getPort());
280 }
281 if(mSessionAccountingAddRoutingHeaders &&
282 msg.exists(h_Routes) && !msg.header(h_Routes).empty())
283 {
284 Array arrayRoutes;
285 NameAddrs::const_iterator routeIt = msg.header(h_Routes).begin();
286 for(; routeIt != msg.header(h_Routes).end(); routeIt++)
287 {
288 if(routeIt->isWellFormed())
289 {
290 arrayRoutes.Insert(String(Data::from(*routeIt).c_str()));
291 }
292 }
293 if(!arrayRoutes.Empty())
294 {
295 sessionEvent["Routes"] = arrayRoutes;
296 }
297 }
298 if(mSessionAccountingAddRoutingHeaders &&
299 msg.exists(h_RecordRoutes) && !msg.header(h_RecordRoutes).empty())
300 {
301 Array arrayRecordRoutes;
302 NameAddrs::const_iterator recordRouteIt = msg.header(h_RecordRoutes).begin();
303 for(; recordRouteIt != msg.header(h_RecordRoutes).end(); recordRouteIt++)
304 {
305 if(recordRouteIt->isWellFormed())
306 {
307 arrayRecordRoutes.Insert(String(Data::from(*recordRouteIt).c_str()));
308 }
309 }
310 if(!arrayRecordRoutes.Empty())
311 {
312 sessionEvent["RecordRoutes"] = arrayRecordRoutes;
313 }
314 }
315 if(msg.exists(h_UserAgent) && msg.header(h_UserAgent).isWellFormed())
316 {
317 sessionEvent["UserAgent"] = String(msg.header(h_UserAgent).value().c_str());
318 }
320 pushEventObjectToQueue(sessionEvent, SessionEventType);
321 }
322 else
323 {
324 // This is being forwarded to a target
325 DateCategory datetime;
326 if(!msg.exists(h_CallId) || !msg.header(h_CallId).isWellFormed())
327 {
328 ErrLog(<< "AccountingCollector::doSessionAccounting: missing proper callId header: " << msg);
329 return;
330 }
331 Object sessionEvent;
332 sessionEvent["EventId"] = Number(SessionRouted);
333 sessionEvent["EventName"] = String("Session Routed");
334 sessionEvent["Datetime"] = String(Data::from(datetime).c_str());
335 sessionEvent["CallId"] = String(msg.header(h_CallId).value().c_str());
336 sessionEvent["TargetUri"] = String(Data::from(msg.header(h_RequestLine).uri()).c_str());
337 if(mSessionAccountingAddRoutingHeaders &&
338 msg.exists(h_Routes) && !msg.header(h_Routes).empty())
339 {
340 Array arrayRoutes;
341 NameAddrs::const_iterator routeIt = msg.header(h_Routes).begin();
342 for(; routeIt != msg.header(h_Routes).end(); routeIt++)
343 {
344 if(routeIt->isWellFormed())
345 {
346 arrayRoutes.Insert(String(Data::from(*routeIt).c_str()));
347 }
348 }
349 if(!arrayRoutes.Empty())
350 {
351 sessionEvent["Routes"] = arrayRoutes;
352 }
353 }
354 pushEventObjectToQueue(sessionEvent, SessionEventType);
355 }
356 }
357 else if(msg.method() == BYE && received)
358 {
359 // Session Ended
360 DateCategory datetime;
361 if(!msg.exists(h_CallId) || !msg.header(h_CallId).isWellFormed())
362 {
363 ErrLog(<< "AccountingCollector::doSessionAccounting: missing proper callId header: " << msg);
364 return;
365 }
366 Object sessionEvent;
367 sessionEvent["EventId"] = Number(SessionEnded);
368 sessionEvent["EventName"] = String("Session Ended");
369 sessionEvent["Datetime"] = String(Data::from(datetime).c_str());
370 sessionEvent["CallId"] = String(msg.header(h_CallId).value().c_str());
371 if(msg.exists(h_From) && msg.header(h_From).isWellFormed())
372 {
373 if(!msg.header(h_From).displayName().empty())
374 {
375 sessionEvent["From"]["DisplayName"] = String(msg.header(h_From).displayName().c_str());
376 }
377 sessionEvent["From"]["Uri"] = String(Data::from(msg.header(h_From).uri()).c_str());
378 }
379 if(msg.exists(h_Reasons) && !msg.header(h_Reasons).empty() && msg.header(h_Reasons).front().isWellFormed())
380 {
381 // Just look at first occurance
382 sessionEvent["Reason"]["Value"] = String(msg.header(h_Reasons).front().value().c_str());
383 if(msg.header(h_Reasons).front().exists(p_cause))
384 {
385 sessionEvent["Reason"]["Cause"] = Number(msg.header(h_Reasons).front().param(p_cause));
386 }
387 if(msg.header(h_Reasons).front().exists(p_text) && !msg.header(h_Reasons).front().param(p_text).empty())
388 {
389 sessionEvent["Reason"]["Text"] = String(msg.header(h_Reasons).front().param(p_text).c_str());
390 }
391 }
392 pushEventObjectToQueue(sessionEvent, SessionEventType);
393 }
394 else if(msg.method() == CANCEL && received)
395 {
396 // Session Cancelled
397 DateCategory datetime;
398 if(!msg.exists(h_CallId) || !msg.header(h_CallId).isWellFormed())
399 {
400 ErrLog(<< "AccountingCollector::doSessionAccounting: missing proper callId header: " << msg);
401 return;
402 }
403 Object sessionEvent;
404 sessionEvent["EventId"] = Number(SessionCancelled);
405 sessionEvent["EventName"] = String("Session Cancelled");
406 sessionEvent["Datetime"] = String(Data::from(datetime).c_str());
407 sessionEvent["CallId"] = String(msg.header(h_CallId).value().c_str());
408 if(msg.exists(h_Reasons) && !msg.header(h_Reasons).empty() && msg.header(h_Reasons).front().isWellFormed())
409 {
410 // Just look at first occurance
411 sessionEvent["Reason"]["Value"] = String(msg.header(h_Reasons).front().value().c_str());
412 if(msg.header(h_Reasons).front().exists(p_cause))
413 {
414 sessionEvent["Reason"]["Cause"] = Number(msg.header(h_Reasons).front().param(p_cause));
415 }
416 if(msg.header(h_Reasons).front().exists(p_text) && !msg.header(h_Reasons).front().param(p_text).empty())
417 {
418 sessionEvent["Reason"]["Text"] = String(msg.header(h_Reasons).front().param(p_text).c_str());
419 }
420 }
421 pushEventObjectToQueue(sessionEvent, SessionEventType);
422 }
423 else if(msg.method() == REFER && received && msg.header(h_To).exists(p_tag))
424 {
425 // Only handle mid-dialog REFERs for now
426 // Session Redirected
427 DateCategory datetime;
428 if(!msg.exists(h_CallId) || !msg.header(h_CallId).isWellFormed())
429 {
430 ErrLog(<< "AccountingCollector::doSessionAccounting: missing proper callId header: " << msg);
431 return;
432 }
433 Object sessionEvent;
434 sessionEvent["EventId"] = Number(SessionRedirected);
435 sessionEvent["EventName"] = String("Session Redirected");
436 sessionEvent["Datetime"] = String(Data::from(datetime).c_str());
437 sessionEvent["CallId"] = String(msg.header(h_CallId).value().c_str());
438 if(msg.exists(h_From) && msg.header(h_From).isWellFormed())
439 {
440 if(!msg.header(h_From).displayName().empty())
441 {
442 sessionEvent["ReferredBy"]["DisplayName"] = String(msg.header(h_From).displayName().c_str());
443 }
444 sessionEvent["ReferredBy"]["Uri"] = String(Data::from(msg.header(h_From).uri()).c_str());
445 }
446 if(msg.exists(h_ReferTo) && msg.header(h_ReferTo).isWellFormed())
447 {
448 sessionEvent["TargetUri"] = String(Data::from(msg.header(h_ReferTo).uri()).c_str());
449 }
450 pushEventObjectToQueue(sessionEvent, SessionEventType);
451 }
452 }
453 // Response
454 else
455 {
456 if(!received && msg.method() == INVITE)
457 {
458 if(msg.header(h_StatusLine).statusCode() >= 200 &&
459 msg.header(h_StatusLine).statusCode() < 300)
460 {
461 // Session Answered
462 DateCategory datetime;
463 if(!msg.exists(h_CallId) || !msg.header(h_CallId).isWellFormed())
464 {
465 ErrLog(<< "AccountingCollector::doSessionAccounting: missing proper callId header: " << msg);
466 return;
467 }
468 Object sessionEvent;
469 sessionEvent["EventId"] = Number(SessionEstablished);
470 sessionEvent["EventName"] = String("Session Established");
471 sessionEvent["Datetime"] = String(Data::from(datetime).c_str());
472 sessionEvent["CallId"] = String(msg.header(h_CallId).value().c_str());
473 if(msg.exists(h_Contacts) && !msg.header(h_Contacts).empty() && msg.header(h_Contacts).front().isWellFormed())
474 {
475 sessionEvent["Contact"] = String(Data::from(msg.header(h_Contacts).front()).c_str());
476 }
477 if(mSessionAccountingAddRoutingHeaders &&
478 msg.exists(h_RecordRoutes) && !msg.header(h_RecordRoutes).empty())
479 {
480 Array arrayRecordRoutes;
481 NameAddrs::const_iterator recordRouteIt = msg.header(h_RecordRoutes).begin();
482 for(; recordRouteIt != msg.header(h_RecordRoutes).end(); recordRouteIt++)
483 {
484 if(recordRouteIt->isWellFormed())
485 {
486 arrayRecordRoutes.Insert(String(Data::from(*recordRouteIt).c_str()));
487 }
488 }
489 if(!arrayRecordRoutes.Empty())
490 {
491 sessionEvent["RecordRoutes"] = arrayRecordRoutes;
492 }
493 }
494 if(msg.exists(h_UserAgent) && msg.header(h_UserAgent).isWellFormed())
495 {
496 sessionEvent["UserAgent"] = String(msg.header(h_UserAgent).value().c_str());
497 }
499 pushEventObjectToQueue(sessionEvent, SessionEventType);
500 }
501 else if(msg.header(h_StatusLine).statusCode() >= 300 &&
502 msg.header(h_StatusLine).statusCode() < 400)
503 {
504 // Session Redirected
505 // Session Answered
506 DateCategory datetime;
507 if(!msg.exists(h_CallId) || !msg.header(h_CallId).isWellFormed())
508 {
509 ErrLog(<< "AccountingCollector::doSessionAccounting: missing proper callId header: " << msg);
510 return;
511 }
512 Object sessionEvent;
513 sessionEvent["EventId"] = Number(SessionRedirected);
514 sessionEvent["EventName"] = String("Session Redirected");
515 sessionEvent["Datetime"] = String(Data::from(datetime).c_str());
516 sessionEvent["CallId"] = String(msg.header(h_CallId).value().c_str());
517 if(msg.exists(h_Contacts) && !msg.header(h_Contacts).empty())
518 {
519 Array arrayContacts;
520 NameAddrs::const_iterator contactIt = msg.header(h_Contacts).begin();
521 for(; contactIt != msg.header(h_Contacts).end(); contactIt++)
522 {
523 if(contactIt->isWellFormed())
524 {
525 arrayContacts.Insert(String(Data::from(*contactIt).c_str()));
526 }
527 }
528 if(!arrayContacts.Empty())
529 {
530 sessionEvent["TargetUris"] = arrayContacts;
531 }
532 }
533 if(msg.exists(h_UserAgent) && msg.header(h_UserAgent).isWellFormed())
534 {
535 sessionEvent["UserAgent"] = String(msg.header(h_UserAgent).value().c_str());
536 }
537 pushEventObjectToQueue(sessionEvent, SessionEventType);
538 }
539 else if(msg.header(h_StatusLine).statusCode() >= 400 &&
540 msg.header(h_StatusLine).statusCode() < 700)
541 {
542 // Session Error
543 Object sessionEvent;
544 // Session Answered
545 DateCategory datetime;
546 if(!msg.exists(h_CallId) || !msg.header(h_CallId).isWellFormed())
547 {
548 ErrLog(<< "AccountingCollector::doSessionAccounting: missing proper callId header: " << msg);
549 return;
550 }
551 sessionEvent["EventId"] = Number(SessionError);
552 sessionEvent["EventName"] = String("Session Error");
553 sessionEvent["Datetime"] = String(Data::from(datetime).c_str());
554 sessionEvent["CallId"] = String(msg.header(h_CallId).value().c_str());
555 sessionEvent["Status"]["Code"] = Number(msg.header(h_StatusLine).statusCode());
556 if(!msg.header(h_StatusLine).reason().empty())
557 {
558 sessionEvent["Status"]["Text"] = String(msg.header(h_StatusLine).reason().c_str());
559 }
560 if(msg.exists(h_Warnings) && !msg.header(h_Warnings).empty() && msg.header(h_Warnings).front().isWellFormed())
561 {
562 // Just look at first occurance
563 sessionEvent["Warning"]["Code"] = Number(msg.header(h_Warnings).front().code());
564 if(!msg.header(h_Warnings).front().text().empty())
565 {
566 sessionEvent["Warning"]["Text"] = String(msg.header(h_Warnings).front().text().c_str());
567 }
568 }
569 // Note: a reason header is not usually present on a response - but we will use one if it is
570 if(msg.exists(h_Reasons) && !msg.header(h_Reasons).empty() && msg.header(h_Reasons).front().isWellFormed())
571 {
572 // Just look at first occurance
573 sessionEvent["Reason"]["Value"] = String(msg.header(h_Reasons).front().value().c_str());
574 if(msg.header(h_Reasons).front().exists(p_cause))
575 {
576 sessionEvent["Reason"]["Cause"] = Number(msg.header(h_Reasons).front().param(p_cause));
577 }
578 if(msg.header(h_Reasons).front().exists(p_text) && !msg.header(h_Reasons).front().param(p_text).empty())
579 {
580 sessionEvent["Reason"]["Text"] = String(msg.header(h_Reasons).front().param(p_text).c_str());
581 }
582 }
583 if(msg.exists(h_UserAgent) && msg.header(h_UserAgent).isWellFormed())
584 {
585 sessionEvent["UserAgent"] = String(msg.header(h_UserAgent).value().c_str());
586 }
587 pushEventObjectToQueue(sessionEvent, SessionEventType);
588 }
589 }
590 }
591}
592
594AccountingCollector::initializeEventQueue(FifoEventType type, bool destroyFirst)
595{
596 switch(type)
597 {
598 case SessionEventType:
599 if(destroyFirst)
600 {
601 delete mSessionEventQueue;
602 mSessionEventQueue = 0;
603 }
604 if(!mSessionEventQueue)
605 {
606 mSessionEventQueue = new PersistentMessageEnqueue(mDbBaseDir);
607 if(!mSessionEventQueue->init(true, sessionEventQueueName))
608 {
609 delete mSessionEventQueue;
610 mSessionEventQueue = 0;
611 }
612 }
613 return mSessionEventQueue;
614 case RegistrationEventType:
615 if(destroyFirst)
616 {
617 delete mRegistrationEventQueue;
618 mRegistrationEventQueue = 0;
619 }
620 if(!mRegistrationEventQueue)
621 {
622 mRegistrationEventQueue = new PersistentMessageEnqueue(mDbBaseDir);
623 if(!mRegistrationEventQueue->init(true, registrationEventQueueName))
624 {
625 delete mRegistrationEventQueue;
626 mRegistrationEventQueue = 0;
627 }
628 }
629 return mRegistrationEventQueue;
630 default:
631 resip_assert(false);
632 break;
633 }
634 return 0;
635}
636
637void
638AccountingCollector::pushEventObjectToQueue(Object& eventObject, AccountingCollector::FifoEventType type)
639{
640 FifoEvent* eventData = new FifoEvent;
641 eventData->mType = type;
642 {
643 DataStream ds(eventData->mData);
644 Writer::Write(eventObject, ds);
645 }
646
647 // Note: BerkeleyDb calls can block (ie. deaklock after consumer crash), so we use a
648 // Fifo and thread to ensure we don't block the core proxy processing
650}
651
652void
653AccountingCollector::internalProcess(std::unique_ptr<FifoEvent> eventData)
654{
655 InfoLog(<< "AccountingCollector::internalProcess: JSON=" << endl << eventData->mData);
656
657 PersistentMessageEnqueue* queue = initializeEventQueue(eventData->mType);
658
659 if(!queue)
660 {
661 ErrLog(<< "AccountingCollector: cannot initialize PersistentMessageQueue - dropping event!");
662 return;
663 }
664
665 if(!queue->push(eventData->mData))
666 {
667 // Error pushing - see if db recovery is needed
668 if(queue->isRecoveryNeeded())
669 {
670 if((queue = initializeEventQueue(eventData->mType, true /* destoryFirst */)) == 0)
671 {
672 ErrLog(<< "AccountingCollector: cannot initialize PersistentMessageQueue - dropping event!");
673 return;
674 }
675 else
676 {
677 if(!queue->push(eventData->mData))
678 {
679 ErrLog(<< "AccountingCollector: error pushing event to queue - dropping event!");
680 }
681 }
682 }
683 else
684 {
685 ErrLog(<< "AccountingCollector: error pushing event to queue - dropping event!");
686 }
687 }
688}
689
690void
691AccountingCollector::thread()
692{
693 while (!isShutdown() || !mFifo.empty()) // Ensure we drain the queue before shutting down
694 {
695 try
696 {
697 std::unique_ptr<FifoEvent> eventData(mFifo.getNext(1000)); // Only need to wake up to see if we are shutdown
698 if (eventData)
699 {
700 internalProcess(std::move(eventData));
701 }
702 }
703 catch (BaseException& e)
704 {
705 WarningLog (<< "Unhandled exception: " << e);
706 }
707 }
708}
709
710
711/* ====================================================================
712 * The Vovida Software License, Version 1.0
713 *
714 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
715 *
716 * Redistribution and use in source and binary forms, with or without
717 * modification, are permitted provided that the following conditions
718 * are met:
719 *
720 * 1. Redistributions of source code must retain the above copyright
721 * notice, this list of conditions and the following disclaimer.
722 *
723 * 2. Redistributions in binary form must reproduce the above copyright
724 * notice, this list of conditions and the following disclaimer in
725 * the documentation and/or other materials provided with the
726 * distribution.
727 *
728 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
729 * and "Vovida Open Communication Application Library (VOCAL)" must
730 * not be used to endorse or promote products derived from this
731 * software without prior written permission. For written
732 * permission, please contact vocal@vovida.org.
733 *
734 * 4. Products derived from this software may not be called "VOCAL", nor
735 * may "VOCAL" appear in their name, without prior written
736 * permission of Vovida Networks, Inc.
737 *
738 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
739 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
740 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
741 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
742 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
743 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
744 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
745 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
746 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
747 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
748 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
749 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
750 * DAMAGE.
751 *
752 * ====================================================================
753 *
754 * This software consists of voluntary contributions made by Vovida
755 * Networks, Inc. and many individuals on behalf of Vovida Networks,
756 * Inc. For more information on Vovida Networks, Inc., please see
757 * <http://www.vovida.org/>.
758 *
759 */
static const Data sessionEventQueueName
static const Data registrationEventQueueName
#define InfoLog(args_)
Definition Logger.hxx:131
#define WarningLog(args_)
Definition Logger.hxx:134
#define ErrLog(args_)
Definition Logger.hxx:137
#define resip_assert(x)
This file needs to work for both C and C++ code.
virtual void doSessionAccounting(const resip::SipMessage &sip, bool received, RequestContext &context)
virtual void doRegistrationAccounting(RegistrationEvent regevent, const resip::SipMessage &sip)
AccountingCollector(ProxyConfig &config)
bool push(const resip::Data &data)
bool init(bool sync, const resip::Data &queueName)
bool empty() const
is the queue empty?
The abstract base-class for all exceptions thrown by resip code.
bool getConfigBool(const resip::Data &name, bool defaultValue) const
An iostream that operates on an existing Data.
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
const char * c_str() const
Returns a null-terminated string representing.
Definition Data.cxx:1097
static Data from(const T &x)
Converts from arbitrary other type to Data.
Definition Data.hxx:398
Represents the "SIP-date" element in the RFC 3261 grammar.
static Tuple getClientPublicAddress(const SipMessage &request)
Look at Via headers, and find the first public IP address closest to the sending client.
Definition Helper.cxx:2400
bool isWellFormed() const
Returns true iff this element was parsed successfully, according to the implementation of parse().
const Uri & uri() const
Represents a SIP message.
bool isRequest() const
Returns true if message is a request, false otherwise.
const RequestLine & header(const RequestLineType &l) const
Returns the RequestLine. This is only valid for request messages.
bool exists(const HeaderBase &headerType) const
Returns true if the given header field is present, false otherwise.
const Tuple & getSource() const
Returns the source tuple that the message was received from only makes sense for messages received fr...
resip::MethodTypes method() const
returns the method type of the message
virtual void shutdown()
Definition ThreadIf.cxx:190
virtual void run()
Definition ThreadIf.cxx:75
bool isShutdown() const
Definition ThreadIf.cxx:212
Msg * getNext()
Returns the first message available.
bool add(Msg *msg, DepthUsage usage)
Add a message to the fifo.
Represents a network address.
Definition Tuple.hxx:59
static const Data & toData(TransportType)
Converts the TransportType to a string representation of the transport type, e.g.
Definition Tuple.cxx:927
int getPort() const
Definition Tuple.cxx:466
TransportType getType() const
Definition Tuple.hxx:122
static Data inet_ntop(const Tuple &tuple)
Converts the binary socket address to presentation format, via the DnsUtil::inet_ntop() method.
Definition Tuple.cxx:939
Uri getAorAsUri(TransportType transportTypeToRemoveDefaultPort=UNKNOWN_TRANSPORT) const
Definition Uri.cxx:1103
This Class is used to implement a primitive form of RPC using loose XML formatting.
dcm! – add UnusedChecking(_enum) below;
StatusLineType h_StatusLine
Definition Headers.cxx:348
@ UNKNOWN_TRANSPORT
RequestLineType h_RequestLine
Definition Headers.cxx:347
H_CallId h_CallId
Definition Headers.cxx:266