// // 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 #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 = "" PP_LF PP_IN "" ; const char* ObserverEnd_ObsSeqStart = "" PP_LF PP_IN ""; const char* ObsSeqEnd_ObsTimeStart = "" PP_LF PP_IN "" ; const char* ObsTimeEnd = "" PP_LF ; const char* CallEventElementEnd = "" PP_LF ; const char* ObsMsgStart = PP_IN "" PP_LF PP_IN PP_IN "" ; const char* ObsMsgMiddle = "" PP_LF PP_IN PP_IN "" ; const char* ObsText_Schema_ObsMsg_End = "" PP_LF PP_IN PP_IN "http://www.sipfoundry.org/sipX/schema/xml/cse-01-00" PP_LF PP_IN "" PP_LF ; const char* CallRequestElementStart = PP_IN "" PP_LF ; const char* CallRequestElementEnd = PP_IN "" PP_LF ; const char* CallSetupElementStart = PP_IN "" PP_LF ; const char* CallSetupElementEnd = PP_IN "" PP_LF ; const char* CallFailureElementStart = PP_IN "" PP_LF ; const char* CallFailureElementEnd = PP_IN "" PP_LF ; const char* CallEndElementStart = PP_IN "" PP_LF ; const char* CallEndElementEnd = PP_IN "" PP_LF ; const char* Call_Dialog_CallId_Start = PP_IN PP_IN "" PP_LF PP_IN PP_IN PP_IN "" PP_LF PP_IN PP_IN PP_IN PP_IN "" ; const char* CallIdEnd = "" PP_LF ; const char* FromTagStart = PP_IN PP_IN PP_IN PP_IN "" ; const char* FromTagEnd = "" PP_LF ; const char* ToTagStart = PP_IN PP_IN PP_IN PP_IN "" ; const char* ToTagEnd = "" PP_LF ; const char* DialogEnd_FromFieldStart = PP_IN PP_IN PP_IN "" PP_LF PP_IN PP_IN PP_IN "" ; const char* FromFieldEnd_ToFieldStart = "" PP_LF PP_IN PP_IN PP_IN "" ; const char* ToField_CallEnd = "" PP_LF PP_IN PP_IN "" PP_LF ; const char* ContactElementStart = PP_IN PP_IN "" ; const char* ContactElementEnd = "" PP_LF ; const char* Response_Status_Start = PP_IN PP_IN "" PP_LF PP_IN PP_IN PP_IN"" ; const char* StatusEnd_ReasonStart = "" PP_LF PP_IN PP_IN PP_IN"" ; const char* Reason_Response_End = "" PP_LF PP_IN PP_IN "" PP_LF ; const char* ViaStart = PP_IN PP_IN "" ; const char* ViaEnd = "" 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; }