Commit 9c4cd6cd authored by Ghislain MARY's avatar Ghislain MARY

Use xsd for imdn xml handling.

parent 41259c58
......@@ -164,6 +164,8 @@ set(LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES
utils/payload-type-handler.h
variant/variant.h
xml/conference-info.h
xml/imdn.h
xml/linphone-imdn.h
xml/resource-lists.h
xml/xml.h
)
......@@ -286,6 +288,8 @@ set(LINPHONE_CXX_OBJECTS_SOURCE_FILES
utils/utils.cpp
variant/variant.cpp
xml/conference-info.cpp
xml/imdn.cpp
xml/linphone-imdn.cpp
xml/resource-lists.cpp
xml/xml.cpp
)
......
......@@ -23,6 +23,8 @@
#include "chat/chat-room/chat-room-p.h"
#include "core/core.h"
#include "logger/logger.h"
#include "xml/imdn.h"
#include "xml/linphone-imdn.h"
#include "imdn.h"
......@@ -32,8 +34,6 @@ using namespace std;
LINPHONE_BEGIN_NAMESPACE
const string Imdn::imdnPrefix = "/imdn:imdn";
// -----------------------------------------------------------------------------
Imdn::Imdn (ChatRoom *chatRoom) : chatRoom(chatRoom) {}
......@@ -74,184 +74,75 @@ void Imdn::notifyDisplay (const shared_ptr<ChatMessage> &message) {
// -----------------------------------------------------------------------------
string Imdn::createXml (const string &id, time_t time, Imdn::Type imdnType, LinphoneReason reason) {
xmlBufferPtr buf;
xmlTextWriterPtr writer;
int err;
string content;
char *datetime = nullptr;
// Check that the chat message has a message id.
if (id.empty())
return content;
buf = xmlBufferCreate();
if (buf == nullptr) {
lError() << "Error creating the XML buffer";
return content;
}
writer = xmlNewTextWriterMemory(buf, 0);
if (writer == nullptr) {
lError() << "Error creating the XML writer";
return content;
}
datetime = linphone_timestamp_to_rfc3339_string(time);
err = xmlTextWriterStartDocument(writer, "1.0", "UTF-8", nullptr);
if (err >= 0) {
err = xmlTextWriterStartElementNS(writer, nullptr, (const xmlChar *)"imdn",
(const xmlChar *)"urn:ietf:params:xml:ns:imdn");
}
if ((err >= 0) && (reason != LinphoneReasonNone)) {
err = xmlTextWriterWriteAttributeNS(writer, (const xmlChar *)"xmlns", (const xmlChar *)"linphoneimdn", nullptr, (const xmlChar *)"http://www.linphone.org/xsds/imdn.xsd");
}
if (err >= 0) {
err = xmlTextWriterWriteElement(writer, (const xmlChar *)"message-id", (const xmlChar *)id.c_str());
}
if (err >= 0) {
err = xmlTextWriterWriteElement(writer, (const xmlChar *)"datetime", (const xmlChar *)datetime);
}
if (err >= 0) {
if (imdnType == Imdn::Type::Delivery) {
err = xmlTextWriterStartElement(writer, (const xmlChar *)"delivery-notification");
} else {
err = xmlTextWriterStartElement(writer, (const xmlChar *)"display-notification");
}
}
if (err >= 0) {
err = xmlTextWriterStartElement(writer, (const xmlChar *)"status");
}
if (err >= 0) {
string Imdn::createXml (const string &id, time_t timestamp, Imdn::Type imdnType, LinphoneReason reason) {
char *datetime = linphone_timestamp_to_rfc3339_string(timestamp);
Xsd::Imdn::Imdn imdn(id, datetime);
ms_free(datetime);
if (imdnType == Imdn::Type::Delivery) {
Xsd::Imdn::Status status;
if (reason == LinphoneReasonNone) {
if (imdnType == Imdn::Type::Delivery) {
err = xmlTextWriterStartElement(writer, (const xmlChar *)"delivered");
} else {
err = xmlTextWriterStartElement(writer, (const xmlChar *)"displayed");
}
auto delivered = Xsd::Imdn::Delivered();
status.setDelivered(delivered);
} else {
err = xmlTextWriterStartElement(writer, (const xmlChar *)"error");
}
}
if (err >= 0) {
// Close the "delivered", "displayed" or "error" element.
err = xmlTextWriterEndElement(writer);
}
if ((err >= 0) && (reason != LinphoneReasonNone)) {
err = xmlTextWriterStartElementNS(writer, (const xmlChar *)"linphoneimdn", (const xmlChar *)"reason", nullptr);
if (err >= 0) {
char codestr[16];
snprintf(codestr, 16, "%d", linphone_reason_to_error_code(reason));
err = xmlTextWriterWriteAttribute(writer, (const xmlChar *)"code", (const xmlChar *)codestr);
}
if (err >= 0) {
err = xmlTextWriterWriteString(writer, (const xmlChar *)linphone_reason_to_string(reason));
auto failed = Xsd::Imdn::Failed();
status.setFailed(failed);
Xsd::LinphoneImdn::ImdnReason imdnReason(linphone_reason_to_string(reason));
imdnReason.setCode(linphone_reason_to_error_code(reason));
status.setReason(imdnReason);
}
if (err >= 0) {
err = xmlTextWriterEndElement(writer);
}
}
if (err >= 0) {
// Close the "status" element.
err = xmlTextWriterEndElement(writer);
}
if (err >= 0) {
// Close the "delivery-notification" or "display-notification" element.
err = xmlTextWriterEndElement(writer);
}
if (err >= 0) {
// Close the "imdn" element.
err = xmlTextWriterEndElement(writer);
}
if (err >= 0) {
err = xmlTextWriterEndDocument(writer);
}
if (err > 0) {
// xmlTextWriterEndDocument returns the size of the content.
content = string((char *)buf->content);
}
xmlFreeTextWriter(writer);
xmlBufferFree(buf);
ms_free(datetime);
return content;
Xsd::Imdn::DeliveryNotification deliveryNotification(status);
imdn.setDeliveryNotification(deliveryNotification);
} else if (imdnType == Imdn::Type::Display) {
Xsd::Imdn::Status1 status;
auto displayed = Xsd::Imdn::Displayed();
status.setDisplayed(displayed);
Xsd::Imdn::DisplayNotification displayNotification(status);
imdn.setDisplayNotification(displayNotification);
}
stringstream ss;
Xsd::XmlSchema::NamespaceInfomap map;
map[""].name = "urn:ietf:params:xml:ns:imdn";
map["imdn"].name = "http://www.linphone.org/xsds/imdn.xsd";
Xsd::Imdn::serializeImdn(ss, imdn, map);
return ss.str();
}
void Imdn::parse (const shared_ptr<ChatMessage> &chatMessage) {
xmlparsing_context_t *xmlCtx = linphone_xmlparsing_context_new();
xmlSetGenericErrorFunc(xmlCtx, linphone_xmlparsing_genericxml_error);
xmlCtx->doc = xmlReadDoc((const unsigned char *)chatMessage->getPrivate()->getText().c_str(), 0, nullptr, 0);
if (xmlCtx->doc)
parse(chatMessage, xmlCtx);
else
lWarning() << "Wrongly formatted IMDN XML: " << xmlCtx->errorBuffer;
linphone_xmlparsing_context_destroy(xmlCtx);
}
// -----------------------------------------------------------------------------
void Imdn::parse (const shared_ptr<ChatMessage> &imdnMessage, xmlparsing_context_t *xmlCtx) {
char xpathStr[MAX_XPATH_LENGTH];
char *messageIdStr = nullptr;
char *datetimeStr = nullptr;
if (linphone_create_xml_xpath_context(xmlCtx) < 0)
return;
xmlXPathRegisterNs(xmlCtx->xpath_ctx, (const xmlChar *)"imdn", (const xmlChar *)"urn:ietf:params:xml:ns:imdn");
xmlXPathObjectPtr imdnObject = linphone_get_xml_xpath_object_for_node_list(xmlCtx, imdnPrefix.c_str());
if (imdnObject) {
if (imdnObject->nodesetval && (imdnObject->nodesetval->nodeNr >= 1)) {
snprintf(xpathStr, sizeof(xpathStr), "%s[1]/imdn:message-id", imdnPrefix.c_str());
messageIdStr = linphone_get_xml_text_content(xmlCtx, xpathStr);
snprintf(xpathStr, sizeof(xpathStr), "%s[1]/imdn:datetime", imdnPrefix.c_str());
datetimeStr = linphone_get_xml_text_content(xmlCtx, xpathStr);
}
xmlXPathFreeObject(imdnObject);
}
if (messageIdStr && datetimeStr) {
shared_ptr<AbstractChatRoom> cr = imdnMessage->getChatRoom();
shared_ptr<ChatMessage> cm = cr->findChatMessage(messageIdStr);
const IdentityAddress &participantAddress = imdnMessage->getFromAddress().getAddressWithoutGruu();
shared_ptr<AbstractChatRoom> cr = chatMessage->getChatRoom();
for (const auto &content : chatMessage->getPrivate()->getContents()) {
istringstream data(content->getBodyAsString());
unique_ptr<Xsd::Imdn::Imdn> imdn(Xsd::Imdn::parseImdn(data, Xsd::XmlSchema::Flags::dont_validate));
if (!imdn)
continue;
shared_ptr<ChatMessage> cm = cr->findChatMessage(imdn->getMessageId());
if (!cm) {
lWarning() << "Received IMDN for unknown message " << messageIdStr;
lWarning() << "Received IMDN for unknown message " << imdn->getMessageId();
} else {
time_t imdnTime = imdnMessage->getTime();
LinphoneImNotifPolicy *policy = linphone_core_get_im_notif_policy(cr->getCore()->getCCore());
snprintf(xpathStr, sizeof(xpathStr), "%s[1]/imdn:delivery-notification/imdn:status", imdnPrefix.c_str());
xmlXPathObjectPtr deliveryStatusObject = linphone_get_xml_xpath_object_for_node_list(xmlCtx, xpathStr);
snprintf(xpathStr, sizeof(xpathStr), "%s[1]/imdn:display-notification/imdn:status", imdnPrefix.c_str());
xmlXPathObjectPtr displayStatusObject = linphone_get_xml_xpath_object_for_node_list(xmlCtx, xpathStr);
if (deliveryStatusObject && linphone_im_notif_policy_get_recv_imdn_delivered(policy)) {
if (deliveryStatusObject->nodesetval && (deliveryStatusObject->nodesetval->nodeNr >= 1)) {
xmlNodePtr node = deliveryStatusObject->nodesetval->nodeTab[0];
if (node->children && node->children->name) {
if (strcmp((const char *)node->children->name, "delivered") == 0) {
cm->getPrivate()->setParticipantState(participantAddress, ChatMessage::State::DeliveredToUser, imdnTime);
} else if (strcmp((const char *)node->children->name, "error") == 0) {
cm->getPrivate()->setParticipantState(participantAddress, ChatMessage::State::NotDelivered, imdnTime);
}
}
}
xmlXPathFreeObject(deliveryStatusObject);
}
if (displayStatusObject && linphone_im_notif_policy_get_recv_imdn_displayed(policy)) {
if (displayStatusObject->nodesetval && (displayStatusObject->nodesetval->nodeNr >= 1)) {
xmlNodePtr node = displayStatusObject->nodesetval->nodeTab[0];
if (node->children && node->children->name) {
if (strcmp((const char *)node->children->name, "displayed") == 0) {
cm->getPrivate()->setParticipantState(participantAddress, ChatMessage::State::Displayed, imdnTime);
}
}
}
xmlXPathFreeObject(displayStatusObject);
auto policy = linphone_core_get_im_notif_policy(cr->getCore()->getCCore());
time_t imdnTime = chatMessage->getTime();
const IdentityAddress &participantAddress = chatMessage->getFromAddress().getAddressWithoutGruu();
auto &deliveryNotification = imdn->getDeliveryNotification();
auto &displayNotification = imdn->getDisplayNotification();
if (deliveryNotification.present()) {
auto &status = deliveryNotification.get().getStatus();
if (status.getDelivered().present() && linphone_im_notif_policy_get_recv_imdn_delivered(policy))
cm->getPrivate()->setParticipantState(participantAddress, ChatMessage::State::DeliveredToUser, imdnTime);
else if ((status.getFailed().present() || status.getError().present())
&& linphone_im_notif_policy_get_recv_imdn_delivered(policy)
)
cm->getPrivate()->setParticipantState(participantAddress, ChatMessage::State::NotDelivered, imdnTime);
} else if (displayNotification.present()) {
auto &status = displayNotification.get().getStatus();
if (status.getDisplayed().present() && linphone_im_notif_policy_get_recv_imdn_displayed(policy))
cm->getPrivate()->setParticipantState(participantAddress, ChatMessage::State::Displayed, imdnTime);
}
}
}
if (messageIdStr)
linphone_free_xml_text_content(messageIdStr);
if (datetimeStr)
linphone_free_xml_text_content(datetimeStr);
}
// -----------------------------------------------------------------------------
int Imdn::timerExpired (void *data, unsigned int revents) {
Imdn *d = reinterpret_cast<Imdn *>(data);
d->stopTimer();
......
......@@ -57,7 +57,6 @@ public:
static void parse (const std::shared_ptr<ChatMessage> &chatMessage);
private:
static void parse (const std::shared_ptr<ChatMessage> &chatMessage, xmlparsing_context_t *xmlCtx);
static int timerExpired (void *data, unsigned int revents);
void send ();
......@@ -65,8 +64,6 @@ private:
void stopTimer ();
private:
static const std::string imdnPrefix;
ChatRoom *chatRoom = nullptr;
std::list<const std::shared_ptr<ChatMessage>> deliveredMessages;
std::list<const std::shared_ptr<ChatMessage>> displayedMessages;
......
......@@ -36,10 +36,11 @@
#if __clang__ || __GNUC__ >= 4
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
#if __GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsuggest-override"
#endif
#endif
//
// End prologue.
......@@ -54,7 +55,7 @@ namespace LinphonePrivate
namespace ConferenceInfo
{
// ConferenceType
//
//
const ConferenceType::ConferenceDescriptionOptional& ConferenceType::
getConferenceDescription () const
......@@ -376,7 +377,7 @@ namespace LinphonePrivate
// StateType
//
//
StateType::
StateType (Value v)
......@@ -413,7 +414,7 @@ namespace LinphonePrivate
StateType& StateType::
operator= (Value v)
{
static_cast< ::LinphonePrivate::Xsd::XmlSchema::String& > (*this) =
static_cast< ::LinphonePrivate::Xsd::XmlSchema::String& > (*this) =
::LinphonePrivate::Xsd::XmlSchema::String (_xsd_StateType_literals_[v]);
return *this;
......@@ -421,7 +422,7 @@ namespace LinphonePrivate
// ConferenceDescriptionType
//
//
const ConferenceDescriptionType::DisplayTextOptional& ConferenceDescriptionType::
getDisplayText () const
......@@ -707,7 +708,7 @@ namespace LinphonePrivate
// HostType
//
//
const HostType::DisplayTextOptional& HostType::
getDisplayText () const
......@@ -849,7 +850,7 @@ namespace LinphonePrivate
// ConferenceStateType
//
//
const ConferenceStateType::UserCountOptional& ConferenceStateType::
getUserCount () const
......@@ -973,7 +974,7 @@ namespace LinphonePrivate
// ConferenceMediaType
//
//
const ConferenceMediaType::EntrySequence& ConferenceMediaType::
getEntry () const
......@@ -1025,7 +1026,7 @@ namespace LinphonePrivate
// ConferenceMediumType
//
//
const ConferenceMediumType::DisplayTextOptional& ConferenceMediumType::
getDisplayText () const
......@@ -1197,7 +1198,7 @@ namespace LinphonePrivate
// UrisType
//
//
const UrisType::EntrySequence& UrisType::
getEntry () const
......@@ -1285,7 +1286,7 @@ namespace LinphonePrivate
// UriType
//
//
const UriType::UriType1& UriType::
getUri () const
......@@ -1481,7 +1482,7 @@ namespace LinphonePrivate
}
// UsersType
//
//
const UsersType::UserSequence& UsersType::
getUser () const
......@@ -1587,7 +1588,7 @@ namespace LinphonePrivate
// UserType
//
//
const UserType::DisplayTextOptional& UserType::
getDisplayText () const
......@@ -1873,7 +1874,7 @@ namespace LinphonePrivate
// UserRolesType
//
//
const UserRolesType::EntrySequence& UserRolesType::
getEntry () const
......@@ -1949,7 +1950,7 @@ namespace LinphonePrivate
}
// EndpointType
//
//
const EndpointType::DisplayTextOptional& EndpointType::
getDisplayText () const
......@@ -2325,7 +2326,7 @@ namespace LinphonePrivate
// EndpointStatusType
//
//
EndpointStatusType::
EndpointStatusType (Value v)
......@@ -2362,7 +2363,7 @@ namespace LinphonePrivate
EndpointStatusType& EndpointStatusType::
operator= (Value v)
{
static_cast< ::LinphonePrivate::Xsd::XmlSchema::String& > (*this) =
static_cast< ::LinphonePrivate::Xsd::XmlSchema::String& > (*this) =
::LinphonePrivate::Xsd::XmlSchema::String (_xsd_EndpointStatusType_literals_[v]);
return *this;
......@@ -2370,7 +2371,7 @@ namespace LinphonePrivate
// JoiningType
//
//
JoiningType::
JoiningType (Value v)
......@@ -2407,7 +2408,7 @@ namespace LinphonePrivate
JoiningType& JoiningType::
operator= (Value v)
{
static_cast< ::LinphonePrivate::Xsd::XmlSchema::String& > (*this) =
static_cast< ::LinphonePrivate::Xsd::XmlSchema::String& > (*this) =
::LinphonePrivate::Xsd::XmlSchema::String (_xsd_JoiningType_literals_[v]);
return *this;
......@@ -2415,7 +2416,7 @@ namespace LinphonePrivate
// DisconnectionType
//
//
DisconnectionType::
DisconnectionType (Value v)
......@@ -2452,7 +2453,7 @@ namespace LinphonePrivate
DisconnectionType& DisconnectionType::
operator= (Value v)
{
static_cast< ::LinphonePrivate::Xsd::XmlSchema::String& > (*this) =
static_cast< ::LinphonePrivate::Xsd::XmlSchema::String& > (*this) =
::LinphonePrivate::Xsd::XmlSchema::String (_xsd_DisconnectionType_literals_[v]);
return *this;
......@@ -2460,7 +2461,7 @@ namespace LinphonePrivate
// ExecutionType
//
//
const ExecutionType::WhenOptional& ExecutionType::
getWhen () const
......@@ -2584,7 +2585,7 @@ namespace LinphonePrivate
// CallType
//
//
const CallType::SipOptional& CallType::
getSip () const
......@@ -2666,7 +2667,7 @@ namespace LinphonePrivate
// SipDialogIdType
//
//
const SipDialogIdType::DisplayTextOptional& SipDialogIdType::
getDisplayText () const
......@@ -2838,7 +2839,7 @@ namespace LinphonePrivate
// MediaType
//
//
const MediaType::DisplayTextOptional& MediaType::
getDisplayText () const
......@@ -3070,7 +3071,7 @@ namespace LinphonePrivate
// MediaStatusType
//
//
MediaStatusType::
MediaStatusType (Value v)
......@@ -3107,7 +3108,7 @@ namespace LinphonePrivate
MediaStatusType& MediaStatusType::
operator= (Value v)
{
static_cast< ::LinphonePrivate::Xsd::XmlSchema::String& > (*this) =
static_cast< ::LinphonePrivate::Xsd::XmlSchema::String& > (*this) =
::LinphonePrivate::Xsd::XmlSchema::String (_xsd_MediaStatusType_literals_[v]);
return *this;
......@@ -3115,7 +3116,7 @@ namespace LinphonePrivate
// SidebarsByValType
//
//
const SidebarsByValType::EntrySequence& SidebarsByValType::
getEntry () const
......@@ -3208,6 +3209,15 @@ namespace LinphonePrivate
#include <xsd/cxx/xml/dom/parsing-source.hxx>
#include <xsd/cxx/tree/type-factory-map.hxx>
namespace _xsd
{
static
const ::xsd::cxx::tree::type_factory_plate< 0, char >
type_factory_plate_init;
}
namespace LinphonePrivate
{
namespace Xsd
......@@ -6779,6 +6789,15 @@ namespace LinphonePrivate
#include <ostream>
#include <xsd/cxx/tree/std-ostream-map.hxx>
namespace _xsd
{
static
const ::xsd::cxx::tree::std_ostream_plate< 0, char >
std_ostream_plate_init;
}
namespace LinphonePrivate
{
namespace Xsd
......@@ -7563,6 +7582,15 @@ namespace LinphonePrivate
#include <xsd/cxx/tree/error-handler.hxx>
#include <xsd/cxx/xml/dom/serialization-source.hxx>
#include <xsd/cxx/tree/type-serializer-map.hxx>
namespace _xsd
{
static
const ::xsd::cxx::tree::type_serializer_plate< 0, char >
type_serializer_plate_init;
}
namespace LinphonePrivate
{
namespace Xsd
......@@ -9304,8 +9332,12 @@ namespace LinphonePrivate
// Begin epilogue.
//
#if __GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)
#pragma GCC diagnostic pop
#endif
#if __clang__ || __GNUC__ >= 4
#pragma GCC diagnostic pop
#endif
//
// End epilogue.
......@@ -51,10 +51,11 @@
#if __clang__ || __GNUC__ >= 4
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
#if __GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsuggest-override"
#endif
#endif
//
// End prologue.
......@@ -240,6 +241,8 @@ namespace LinphonePrivate
typedef ::xsd::cxx::tree::unexpected_enumerator< char > UnexpectedEnumerator;
typedef ::xsd::cxx::tree::expected_text_content< char > ExpectedTextContent;
typedef ::xsd::cxx::tree::no_prefix_mapping< char > NoPrefixMapping;
typedef ::xsd::cxx::tree::no_type_info< char > NoTypeInfo;
typedef ::xsd::cxx::tree::not_derived< char > NotDerived;
typedef ::xsd::cxx::tree::serialization< char > Serialization;
// Error handler callback interface.
......@@ -573,7 +576,7 @@ namespace LinphonePrivate
ConferenceType&
operator= (const ConferenceType& x);