//
// Copyright (C) 2004 SIPfoundry Inc.
// License by SIPfoundry under the LGPL license.
//
// Copyright (C) 2004 Pingtel Corp.
// Licensed to SIPfoundry under a Contributor Agreement.
//
//////////////////////////////////////////////////////////////////////////////
// SYSTEM INCLUDES
#include <assert.h>
#include "os/OsFS.h"
#include "os/OsSysLog.h"
#include "os/OsDateTime.h"
#include "utl/XmlContent.h"
// APPLICATION INCLUDES
#include "CallStateEventBuilder_XML.h"
// DEFINES
// MACROS
// EXTERNAL FUNCTIONS
// EXTERNAL VARIABLES
// CONSTANTS
// Note: the unit test will fail if this is defined, since it compares against expected results without it.
#undef PRETTYPRINT_EVENTS
#ifdef PRETTYPRINT_EVENTS
# define PP_LF "\n"
# define PP_IN " "
#else
# define PP_LF ""
# define PP_IN ""
#endif
const char* CallEvent_Observer_Start =
"<call_event>" PP_LF
PP_IN "<observer>"
;
const char* ObserverEnd_ObsSeqStart =
"</observer>" PP_LF
PP_IN "<obs_seq>";
const char* ObsSeqEnd_ObsTimeStart =
"</obs_seq>" PP_LF
PP_IN "<obs_time>"
;
const char* ObsTimeEnd =
"</obs_time>" PP_LF
;
const char* CallEventElementEnd =
"</call_event>" PP_LF
;
const char* ObsMsgStart =
PP_IN "<obs_msg>" PP_LF
PP_IN PP_IN "<obs_status>"
;
const char* ObsMsgMiddle =
"</obs_status>" PP_LF
PP_IN PP_IN "<obs_text>"
;
const char* ObsText_Schema_ObsMsg_End =
"</obs_text>" PP_LF
PP_IN PP_IN "<uri>http://www.sipfoundry.org/sipX/schema/xml/cse-01-00</uri>" PP_LF
PP_IN "</obs_msg>" PP_LF
;
const char* CallRequestElementStart =
PP_IN "<call_request>" PP_LF
;
const char* CallRequestElementEnd =
PP_IN "</call_request>" PP_LF
;
const char* CallSetupElementStart =
PP_IN "<call_setup>" PP_LF
;
const char* CallSetupElementEnd =
PP_IN "</call_setup>" PP_LF
;
const char* CallFailureElementStart =
PP_IN "<call_failure>" PP_LF
;
const char* CallFailureElementEnd =
PP_IN "</call_failure>" PP_LF
;
const char* CallEndElementStart =
PP_IN "<call_end>" PP_LF
;
const char* CallEndElementEnd =
PP_IN "</call_end>" PP_LF
;
const char* Call_Dialog_CallId_Start =
PP_IN PP_IN "<call>" PP_LF
PP_IN PP_IN PP_IN "<dialog>" PP_LF
PP_IN PP_IN PP_IN PP_IN "<call_id>"
;
const char* CallIdEnd =
"</call_id>" PP_LF
;
const char* FromTagStart =
PP_IN PP_IN PP_IN PP_IN "<from_tag>"
;
const char* FromTagEnd =
"</from_tag>" PP_LF
;
const char* ToTagStart =
PP_IN PP_IN PP_IN PP_IN "<to_tag>"
;
const char* ToTagEnd =
"</to_tag>" PP_LF
;
const char* DialogEnd_FromFieldStart =
PP_IN PP_IN PP_IN "</dialog>" PP_LF
PP_IN PP_IN PP_IN "<from>"
;
const char* FromFieldEnd_ToFieldStart =
"</from>" PP_LF
PP_IN PP_IN PP_IN "<to>"
;
const char* ToField_CallEnd =
"</to>" PP_LF
PP_IN PP_IN "</call>" PP_LF
;
const char* ContactElementStart =
PP_IN PP_IN "<contact>"
;
const char* ContactElementEnd =
"</contact>" PP_LF
;
const char* Response_Status_Start =
PP_IN PP_IN "<response>" PP_LF
PP_IN PP_IN PP_IN"<status>"
;
const char* StatusEnd_ReasonStart =
"</status>" PP_LF
PP_IN PP_IN PP_IN"<reason>"
;
const char* Reason_Response_End =
"</reason>" PP_LF
PP_IN PP_IN "</response>" PP_LF
;
const char* ViaStart =
PP_IN PP_IN "<via>"
;
const char* ViaEnd =
"</via>" PP_LF
;
#define CONTENT_BUF_MAX 2048
const char* EventText[] =
{
"BuilderReset",
"CallRequestEvent",
"CallSetupEvent",
"CallFailureEvent",
"CallEndEvent",
"AddCallData",
"AddVia",
"CompleteCallEvent"
};
// STRUCTS
// TYPEDEFS
// FORWARD DECLARATIONS
/* //////////////////////////// PUBLIC //////////////////////////////////// */
/* ============================ CREATORS ================================== */
/// Instantiate an event builder and set the observer name for its events
CallStateEventBuilder_XML::CallStateEventBuilder_XML(const char* observerDnsName ///< the DNS name to be recorded in all events
) :
CallStateEventBuilder(observerDnsName)
{
}
/// Destructor
CallStateEventBuilder_XML::~CallStateEventBuilder_XML()
{
}
/**
* Generate a metadata event.
* This method generates a complete event - it does not require that the callEventComplete method be called.
*/
void CallStateEventBuilder_XML::observerEvent(int sequenceNumber, ///< for ObserverReset, this should be zero
const OsTime& timestamp, ///< obtained using getCurTime(OsTime)
ObserverEvent eventCode,
const char* eventMsg ///< for human consumption
)
{
BuilderMethod eventMethod;
switch (eventCode)
{
case ObserverReset:
reset(); // because this event is ok any time, clear out any partial event.
eventMethod = BuilderReset;
break;
default:
assert(false);
OsSysLog::add(FAC_SIP, PRI_ERR, "observerEvent: invalid eventCode %d", eventCode);
eventMethod = InvalidEvent;
break;
}
if (builderStateIsOk(eventMethod))
{
newEvent(sequenceNumber, timestamp, ObsMsgStart);
char ec[11];
sprintf(ec, "%d", eventCode);
mCurrentEvent.append(ec);
mCurrentEvent.append(ObsMsgMiddle);
XmlEscape(mCurrentEvent, eventMsg);
mCurrentEvent.append(ObsText_Schema_ObsMsg_End);
mEventComplete = true;
}
else
{
assert(false);
OsSysLog::add(FAC_SIP, PRI_ERR, "observerEvent: %d not allowed.", eventCode);
}
}
/// Begin a Call Request Event - an INVITE without a to tag has been observed
/**
* Requires:
* - callRequestEvent
* - addCallData (the toTag in the addCallRequest will be a null string)
* - addEventVia (at least for via index zero)
* - completeCallEvent
*/
void CallStateEventBuilder_XML::callRequestEvent(int sequenceNumber,
const OsTime& timestamp, ///< obtain using getCurTime(OsTime)
const UtlString& contact
)
{
if (builderStateIsOk(CallRequestEvent))
{
newEvent(sequenceNumber, timestamp, CallRequestElementStart);
mLaterElement.append(ContactElementStart);
XmlEscape(mLaterElement, contact);
mLaterElement.append(ContactElementEnd);
mEndElement = CallRequestElementEnd;
}
else
{
assert(false);
OsSysLog::add(FAC_SIP, PRI_ERR, "CallStateEventBuilder_XML::callRequestEvent not allowed.");
}
}
/// Begin a Call Setup Event - a 2xx response to an INVITE has been observed
/**
* Requires:
* - callSetupEvent
* - addCallData
* - addEventVia (at least for via index zero)
* - completeCallEvent
*/
void CallStateEventBuilder_XML::callSetupEvent(int sequenceNumber,
const OsTime& timestamp, ///< obtain using getCurTime(OsTime)
const UtlString& contact
)
{
if (builderStateIsOk(CallSetupEvent))
{
newEvent(sequenceNumber, timestamp, CallSetupElementStart);
mLaterElement.append(ContactElementStart);
XmlEscape(mLaterElement, contact);
mLaterElement.append(ContactElementEnd);
mEndElement = CallSetupElementEnd;
}
else
{
assert(false);
OsSysLog::add(FAC_SIP, PRI_ERR, "CallStateEventBuilder_XML::callSetupEvent not allowed.");
}
}
/// Begin a Call Failure Event - an error response to an INVITE has been observed
/**
* Requires:
* - callFailureEvent
* - addCallData
* - addEventVia (at least for via index zero)
* - completeCallEvent
*/
void CallStateEventBuilder_XML::callFailureEvent(int sequenceNumber,
const OsTime& timestamp, ///< obtain using getCurTime(OsTime)
int statusCode,
const UtlString& statusMsg
)
{
if (builderStateIsOk(CallFailureEvent))
{
newEvent(sequenceNumber, timestamp, CallFailureElementStart);
mLaterElement.append(Response_Status_Start);
char sc[11];
sprintf(sc, "%d", statusCode);
mLaterElement.append(sc);
mLaterElement.append(StatusEnd_ReasonStart);
XmlEscape(mLaterElement, statusMsg);
mLaterElement.append(Reason_Response_End);
mEndElement = CallFailureElementEnd;
}
else
{
assert(false);
OsSysLog::add(FAC_SIP, PRI_ERR, "CallStateEventBuilder_XML::callFailureEvent not allowed.");
}
}
/// Begin a Call End Event - a BYE request has been observed
/**
* Requires:
* - callEndEvent
* - addCallData
* - addEventVia (at least for via index zero)
* - completeCallEvent
*/
void CallStateEventBuilder_XML::callEndEvent(const int sequenceNumber,
const OsTime& timestamp ///< obtain using getCurTime(OsTime)
)
{
if (builderStateIsOk(CallEndEvent))
{
newEvent(sequenceNumber, timestamp, CallEndElementStart);
mEndElement = CallEndElementEnd;
}
else
{
assert(false);
OsSysLog::add(FAC_SIP, PRI_ERR, "CallStateEventBuilder_XML::callEndEvent not allowed.");
}
}
void CallStateEventBuilder_XML::callTransferEvent(int mSequenceNumber,
const OsTime& timeStamp,
const UtlString& contact,
const UtlString& refer_to,
const UtlString& referred_by,
const UtlString& request_uri)
{
// Not logging transfer events in XML
}
/// Add the dialog and call information for the event being built.
void CallStateEventBuilder_XML::addCallData(const int cseqNumber,
const UtlString& callId,
const UtlString& fromTag, /// may be a null string
const UtlString& toTag, /// may be a null string
const UtlString& fromField,
const UtlString& toField
)
{
if (builderStateIsOk(AddCallData))
{
mCallInfo.append(Call_Dialog_CallId_Start);
XmlEscape(mCallInfo, callId);
mCallInfo.append(CallIdEnd);
if (!fromTag.isNull())
{
mCallInfo.append(FromTagStart);
XmlEscape(mCallInfo, fromTag);
mCallInfo.append(FromTagEnd);
}
if (!toTag.isNull())
{
mCallInfo.append(ToTagStart);
XmlEscape(mCallInfo, toTag);
mCallInfo.append(ToTagEnd);
}
mCallInfo.append(DialogEnd_FromFieldStart);
XmlEscape(mCallInfo, fromField);
mCallInfo.append(FromFieldEnd_ToFieldStart);
XmlEscape(mCallInfo, toField);
mCallInfo.append(ToField_CallEnd);
}
else
{
assert(false);
OsSysLog::add(FAC_SIP, PRI_ERR, "CallStateEventBuilder_XML::callEndEvent not allowed.");
}
}
/// Add a via element for the event
/**
* Record a Via from the message for this event
* Calls to this routine are in reverse cronological order - the last
* call for an event should be the via added by the message originator
*/
void CallStateEventBuilder_XML::addEventVia(const UtlString& via
)
{
if (builderStateIsOk(AddVia))
{
// construct the element locally
UtlString viaElement;
viaElement.append(ViaStart);
XmlEscape(viaElement, via);
viaElement.append(ViaEnd);
// prepend it to the recorded vias so that the first one is first
mViaHeader.prepend(viaElement);
}
else
{
assert(false);
OsSysLog::add(FAC_SIP, PRI_ERR, "CallStateEventBuilder_XML::callEndEvent not allowed.");
}
}
/// Indicates that all information for the current call event has been added.
void CallStateEventBuilder_XML::completeCallEvent()
{
if (builderStateIsOk(CompleteCallEvent))
{
mEventComplete = true;
}
else
{
assert(false);
OsSysLog::add(FAC_SIP, PRI_ERR, "CallStateEventBuilder_XML::completeCallEvent not allowed.");
}
}
/// Clears all the object state
void CallStateEventBuilder_XML::reset()
{
mCurrentEvent.remove(0);
mCallInfo.remove(0);
mViaHeader.remove(0);
mLaterElement.remove(0);
mEndElement.remove(0);
mEventComplete = false;
}
void CallStateEventBuilder_XML::newEvent(int sequenceNumber,
const OsTime& timestamp, ///< obtain using getCurTime(OsTime)
const char* elementStart
)
{
// assemble the parts common to all events
mCurrentEvent = CallEvent_Observer_Start;
XmlEscape(mCurrentEvent, observerName);
mCurrentEvent.append(ObserverEnd_ObsSeqStart);
char sn[11];
sprintf(sn, "%d", sequenceNumber);
mCurrentEvent.append(sn);
mCurrentEvent.append(ObsSeqEnd_ObsTimeStart);
OsDateTime timeValue(timestamp);
UtlString timeString;
timeValue.getIsoTimeStringZ(timeString);
mCurrentEvent.append(timeString); // this format has nothing to escape
mCurrentEvent.append(ObsTimeEnd);
// add the start tag for the new element
mCurrentEvent.append(elementStart);
}
/// Copies the element into the provided UtlString
bool CallStateEventBuilder_XML::finishElement(UtlString& event)
/**<
* @returns
* - true if the returned element is validly constructed
* - false if not (a caller error)
*/
{
bool isComplete = mEventComplete;
event.remove(0);
if (isComplete)
{
event.append(mCurrentEvent);
event.append(mCallInfo);
event.append(mLaterElement);
event.append(mViaHeader);
event.append(mEndElement);
event.append(CallEventElementEnd);
event.append('\n');
reset();
}
return isComplete;
}
syntax highlighted by Code2HTML, v. 0.9.1