Commit 2b8291c3 authored by Ghislain MARY's avatar Ghislain MARY

Merge remote-tracking branch 'origin/dev_conference_info' into dev_refactor_cpp

parents 93c17c21 4d1e1c8d
......@@ -133,6 +133,7 @@ else()
find_package(Belr REQUIRED)
endif()
find_package(XML2 REQUIRED)
find_package(LibXsd REQUIRED)
find_package(Soci)
find_package(Zlib)
if(ENABLE_TUNNEL)
......
......@@ -33,7 +33,6 @@ if(ANDROID)
find_package(Support REQUIRED)
endif()
list(APPEND LINPHONE_PRIVATE_HEADER_FILES
bellesip_sal/sal_impl.h
carddav.h
......@@ -121,6 +120,7 @@ set(LINPHONE_SOURCE_FILES_C
set(LINPHONE_SOURCE_FILES_CXX
conference.cc
)
set(LINPHONE_INCLUDE_DIRS ${LINPHONE_INCLUDE_DIRS})
if(ANDROID)
list(APPEND LINPHONE_SOURCE_FILES_CXX linphonecore_jni.cc)
set_source_files_properties(linphonecore_jni.cc PROPERTIES COMPILE_DEFINITIONS "USE_JAVAH")
......@@ -157,6 +157,7 @@ set(LIBS
${ORTP_LIBRARIES}
${XML2_LIBRARIES}
${BELR_LIBRARIES}
${LIBXSD_LIBRARIES}
)
if(WIN32 AND NOT CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
list(APPEND LIBS "Ws2_32")
......
......@@ -45,6 +45,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "mediastreamer2/msjpegwriter.h"
#include "mediastreamer2/msogl.h"
#include "mediastreamer2/msvolume.h"
#include "conference/remote-conference-event-handler.h"
// For migration purpose.
#include "address/address-p.h"
......@@ -2123,12 +2124,17 @@ static void linphone_core_register_default_codecs(LinphoneCore *lc){
static void linphone_core_internal_notify_received(LinphoneCore *lc, LinphoneEvent *lev, const char *notified_event, const LinphoneContent *body) {
if (strcmp(notified_event, "Presence") == 0) {
const bctbx_list_t* friendLists = linphone_core_get_friends_lists(lc);
while( friendLists != NULL ){
LinphoneFriendList* list = reinterpret_cast<LinphoneFriendList *>(friendLists->data);
ms_message("notify presence for list %p", list);
for (const bctbx_list_t *it = linphone_core_get_friends_lists(lc); it; it = bctbx_list_next(it)) {
LinphoneFriendList *list = reinterpret_cast<LinphoneFriendList *>(bctbx_list_get_data(it));
ms_message("Notify presence for list %p", list);
linphone_friend_list_notify_presence_received(list, lev, body);
friendLists = friendLists->next;
}
} else if (strcmp(notified_event, "Conference") == 0) {
LinphonePrivate::RemoteConferenceEventHandler *handler =
reinterpret_cast<LinphonePrivate::RemoteConferenceEventHandler *>(linphone_event_get_user_data(lev));
if (handler) {
ms_message("Notify event for conference %s", handler->getConfId().c_str());
handler->notifyReceived((char *)linphone_content_get_buffer(body));
}
}
}
......@@ -2136,6 +2142,8 @@ static void linphone_core_internal_notify_received(LinphoneCore *lc, LinphoneEve
static void linphone_core_internal_subscription_state_changed(LinphoneCore *lc, LinphoneEvent *lev, LinphoneSubscriptionState state) {
if (strcasecmp(linphone_event_get_name(lev), "Presence") == 0) {
linphone_friend_list_subscription_state_changed(lc, lev, state);
} else if (strcmp(linphone_event_get_name(lev), "Conference") == 0) {
}
}
......
......@@ -53,6 +53,7 @@ set(LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES
conference/conference-listener.h
conference/conference.h
conference/local-conference.h
conference/local-conference-event-handler.h
conference/params/call-session-params-p.h
conference/params/call-session-params.h
conference/params/media-session-params-p.h
......@@ -60,6 +61,7 @@ set(LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES
conference/participant-p.h
conference/participant.h
conference/remote-conference.h
conference/remote-conference-event-handler.h
conference/session/call-session-listener.h
conference/session/call-session-p.h
conference/session/call-session.h
......@@ -93,6 +95,8 @@ set(LINPHONE_CXX_OBJECTS_PRIVATE_HEADER_FILES
object/singleton.h
utils/payload-type-handler.h
variant/variant.h
xml/conference-info.h
xml/xml.h
)
set(LINPHONE_CXX_OBJECTS_SOURCE_FILES
......@@ -122,10 +126,12 @@ set(LINPHONE_CXX_OBJECTS_SOURCE_FILES
chat/real-time-text-chat-room.cpp
conference/conference.cpp
conference/local-conference.cpp
conference/local-conference-event-handler.cpp
conference/params/call-session-params.cpp
conference/params/media-session-params.cpp
conference/participant.cpp
conference/remote-conference.cpp
conference/remote-conference-event-handler.cpp
conference/session/call-session.cpp
conference/session/media-session.cpp
content/content-type.cpp
......@@ -150,8 +156,15 @@ set(LINPHONE_CXX_OBJECTS_SOURCE_FILES
utils/payload-type-handler.cpp
utils/utils.cpp
variant/variant.cpp
xml/conference-info.cpp
xml/xml.cpp
)
ADD_XSD_WRAPPERS(xml/xml "XML XSD - xml.xsd")
ADD_XSD_WRAPPERS(xml/conference-info "Conference info XSD - conference-info.xsd")
ADD_XSD_WRAPPERS(xml/resource-lists "Resourece lists XSD - resource-lists.xsd")
set(LINPHONE_CXX_OBJECTS_INCLUDE_DIRS ${BELR_INCLUDE_DIRS} ${LIBXSD_INCLUDE_DIRS})
set(LINPHONE_CXX_OBJECTS_DEFINITIONS "-DLIBLINPHONE_EXPORTS")
set(LINPHONE_CXX_OBJECTS_INCLUDE_DIRS ${BELR_INCLUDE_DIRS})
......
......@@ -19,19 +19,22 @@
#ifndef _CONFERENCE_LISTENER_H_
#define _CONFERENCE_LISTENER_H_
#include "address/address.h"
// =============================================================================
LINPHONE_BEGIN_NAMESPACE
class ConferenceListener {
public:
virtual void onConferenceCreated (LinphoneAddress *addr) = 0;
virtual void onConferenceTerminated (LinphoneAddress *addr) = 0;
virtual void onParticipantAdded (LinphoneAddress *addr) = 0;
virtual void onParticipantRemoved (LinphoneAddress *addr) = 0;
virtual void onParticipantSetAdmin (LinphoneAddress *addr, bool isAdmin) = 0;
virtual void onConferenceCreated (const Address &addr) = 0;
virtual void onConferenceTerminated (const Address &addr) = 0;
virtual void onParticipantAdded (const Address &addr) = 0;
virtual void onParticipantRemoved (const Address &addr) = 0;
virtual void onParticipantSetAdmin (const Address &addr, bool isAdmin) = 0;
};
LINPHONE_END_NAMESPACE
#endif // ifndef _CONFERENCE_LISTENER_H_
......@@ -141,4 +141,14 @@ void Conference::onResetFirstVideoFrameDecoded (const CallSession &session) {
callListener->onResetFirstVideoFrameDecoded();
}
// -----------------------------------------------------------------------------
std::shared_ptr<Participant> Conference::findParticipant (const Address &addr) {
for (const auto &participant : participants) {
if (addr.equal(participant->getAddress()))
return participant;
}
return nullptr;
}
LINPHONE_END_NAMESPACE
......@@ -78,6 +78,8 @@ private:
protected:
explicit Conference (LinphoneCore *core, const Address &myAddress, CallListener *listener = nullptr);
std::shared_ptr<Participant> findParticipant (const Address &addr);
protected:
LinphoneCore *core = nullptr;
CallListener *callListener = nullptr;
......
/*
* local-conference-event-handler.cpp
* Copyright (C) 2017 Belledonne Communications SARL
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "conference/local-conference.h"
#include "conference/participant.h"
#include "local-conference-event-handler.h"
#include "object/object-p.h"
#include "xml/conference-info.h"
#include "private.h"
using namespace std;
using namespace conference_info;
using namespace LinphonePrivate;
LINPHONE_BEGIN_NAMESPACE
class LocalConferenceEventHandlerPrivate : public ObjectPrivate {
public:
void notifyFullState(string notify, LinphoneEvent *lev);
void notifyAllExcept(string notify, const Address &addr);
LinphoneCore *core = nullptr;
LocalConference *conf = nullptr;
};
LINPHONE_END_NAMESPACE
void LocalConferenceEventHandlerPrivate::notifyFullState(string notify, LinphoneEvent *lev) {
LinphoneContent *content = linphone_core_create_content(lev->lc);
linphone_content_set_buffer(content, notify.c_str(), strlen(notify.c_str()));
linphone_event_notify(lev, content);
linphone_content_unref(content);
// linphone_event_unref(lev); ??
}
void LocalConferenceEventHandlerPrivate::notifyAllExcept(string notify, const Address &addr) {
for (const auto &participant : conf->getParticipants()) {
if (!addr.equal(participant->getAddress())) {
LinphoneAddress *cAddr = linphone_address_new(participant->getAddress().asString().c_str());
LinphoneEvent *lev = linphone_core_create_notify(core, cAddr, "Conference");
linphone_address_unref(cAddr);
LinphoneContent *content = linphone_core_create_content(lev->lc);
linphone_content_set_buffer(content, notify.c_str(), strlen(notify.c_str()));
linphone_event_notify(lev, content);
linphone_content_unref(content);
linphone_event_unref(lev);
}
}
}
// -------- Conference::LocalConferenceEventHandler public methods ---------
LocalConferenceEventHandler::LocalConferenceEventHandler(LinphoneCore *core, LocalConference *localConf) : Object(*new LocalConferenceEventHandlerPrivate) {
L_D(LocalConferenceEventHandler);
xercesc::XMLPlatformUtils::Initialize();
d->conf = localConf;
d->core = core; // conf->getCore() ?
}
LocalConferenceEventHandler::~LocalConferenceEventHandler() {
xercesc::XMLPlatformUtils::Terminate();
}
string LocalConferenceEventHandler::subscribeReceived(LinphoneEvent *lev) {
L_D(LocalConferenceEventHandler);
string entity = d->conf->getMe()->getAddress().asStringUriOnly();
Conference_type confInfo = Conference_type(entity);
Users_type users;
confInfo.setUsers(users);
xml_schema::NamespaceInfomap map;
map[""].name = "urn:ietf:params:xml:ns:conference-info";
for (const auto &participant : d->conf->getParticipants()) {
User_type user = User_type();
User_roles_type roles;
user.setRoles(roles);
user.setEntity(participant->getAddress().asStringUriOnly());
user.getRoles()->getEntry().push_back(participant->isAdmin() ? "admin" : "participant");
user.setState("full");
confInfo.getUsers()->getUser().push_back(user);
}
stringstream notify;
serializeConference_info(notify, confInfo, map);
//d->notifyFullState(notify.str(), lev);
return notify.str();
}
string LocalConferenceEventHandler::notifyParticipantAdded(const Address &addr) {
L_D(LocalConferenceEventHandler);
string entity = d->conf->getMe()->getAddress().asStringUriOnly();
Conference_type confInfo = Conference_type(entity);
Users_type users;
confInfo.setUsers(users);
User_type user = User_type();
User_roles_type roles;
user.setRoles(roles);
user.setEntity(addr.asStringUriOnly());
user.getRoles()->getEntry().push_back("participant");
user.setState("full");
confInfo.getUsers()->getUser().push_back(user);
xml_schema::NamespaceInfomap map;
stringstream notify;
serializeConference_info(notify, confInfo, map);
//d->notifyAllExcept(notify.str(), addr);
return notify.str();
}
string LocalConferenceEventHandler::notifyParticipantRemoved(const Address &addr) {
L_D(LocalConferenceEventHandler);
string entity = d->conf->getMe()->getAddress().asStringUriOnly();
Conference_type confInfo = Conference_type(entity);
Users_type users;
confInfo.setUsers(users);
User_type user = User_type();
user.setEntity(addr.asStringUriOnly());
user.setState("deleted");
confInfo.getUsers()->getUser().push_back(user);
xml_schema::NamespaceInfomap map;
stringstream notify;
serializeConference_info(notify, confInfo, map);
//d->notifyAllExcept(notify.str(), addr);
return notify.str();
}
string LocalConferenceEventHandler::notifyParticipantSetAdmin(const Address &addr, bool isAdmin) {
L_D(LocalConferenceEventHandler);
string entity = d->conf->getMe()->getAddress().asStringUriOnly();
Conference_type confInfo = Conference_type(entity);
Users_type users;
confInfo.setUsers(users);
User_type user = User_type();
User_roles_type roles;
user.setRoles(roles);
user.setEntity(addr.asStringUriOnly());
user.getRoles()->getEntry().push_back(isAdmin ? "admin" : "participant");
user.setState("partial");
confInfo.getUsers()->getUser().push_back(user);
xml_schema::NamespaceInfomap map;
stringstream notify;
serializeConference_info(notify, confInfo, map);
//d->notifyAllExcept(notify.str(), addr);
return notify.str();
}
/*
* local-conference-event-handler.h
* Copyright (C) 2017 Belledonne Communications SARL
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _LOCAL_CONFERENCE_EVENT_HANDLER_H_
#define _LOCAL_CONFERENCE_EVENT_HANDLER_H_
#include <string>
#include "linphone/types.h"
#include "address/address.h"
#include "object/object.h"
LINPHONE_BEGIN_NAMESPACE
class LocalConference;
class LocalConferenceEventHandlerPrivate;
class LocalConferenceEventHandler : public Object {
public:
LocalConferenceEventHandler(LinphoneCore *core, LocalConference *localConf);
~LocalConferenceEventHandler();
std::string subscribeReceived(LinphoneEvent *lev);
std::string notifyParticipantAdded(const Address &addr);
std::string notifyParticipantRemoved(const Address &addr);
std::string notifyParticipantSetAdmin(const Address &addr, bool isAdmin);
private:
L_DECLARE_PRIVATE(LocalConferenceEventHandler);
L_DISABLE_COPY(LocalConferenceEventHandler);
};
LINPHONE_END_NAMESPACE
#endif // ifndef _LOCAL_CONFERENCE_EVENT_HANDLER_H_
......@@ -17,12 +17,67 @@
*/
#include "local-conference.h"
#include "participant-p.h"
LINPHONE_BEGIN_NAMESPACE
using namespace std;
using namespace LinphonePrivate;
// =============================================================================
LocalConference::LocalConference (LinphoneCore *core, const Address &myAddress, CallListener *listener)
: Conference(core, myAddress, listener) {}
: Conference(core, myAddress, listener) {
eventHandler = new LocalConferenceEventHandler(core, this);
}
LINPHONE_END_NAMESPACE
LocalConference::~LocalConference () {
delete eventHandler;
}
// -----------------------------------------------------------------------------
shared_ptr<Participant> LocalConference::addParticipant (const Address &addr, const CallSessionParams *params, bool hasMedia) {
shared_ptr<Participant> participant = findParticipant(addr);
if (participant)
return participant;
participant = make_shared<Participant>(addr);
participant->getPrivate()->createSession(*this, params, hasMedia, this);
participants.push_back(participant);
activeParticipant = participant;
return participant;
}
void LocalConference::addParticipants (const list<Address> &addresses, const CallSessionParams *params, bool hasMedia) {
for (const auto &addr : addresses)
addParticipant(addr, params, hasMedia);
}
bool LocalConference::canHandleParticipants () const {
return true;
}
const string& LocalConference::getId () const {
return id;
}
int LocalConference::getNbParticipants () const {
participants.size();
return 1;
}
list<shared_ptr<Participant>> LocalConference::getParticipants () const {
return participants;
}
void LocalConference::removeParticipant (const shared_ptr<Participant> participant) {
for (const auto &p : participants) {
if (participant->getAddress().equal(p->getAddress())) {
participants.remove(p);
return;
}
}
}
void LocalConference::removeParticipants (const list<shared_ptr<Participant>> participants) {
for (const auto &p : participants)
removeParticipant(p);
}
......@@ -20,6 +20,7 @@
#define _LOCAL_CONFERENCE_H_
#include "conference.h"
#include "local-conference-event-handler.h"
// =============================================================================
......@@ -28,10 +29,25 @@ LINPHONE_BEGIN_NAMESPACE
class LocalConference : public Conference {
public:
LocalConference (LinphoneCore *core, const Address &myAddress, CallListener *listener = nullptr);
virtual ~LocalConference() = default;
virtual ~LocalConference();
LocalConferenceEventHandler * getEventHandler() const { return eventHandler; }
public:
/* ConferenceInterface */
virtual std::shared_ptr<Participant> addParticipant (const Address &addr, const CallSessionParams *params, bool hasMedia);
virtual void addParticipants (const std::list<Address> &addresses, const CallSessionParams *params, bool hasMedia);
virtual bool canHandleParticipants () const;
virtual const std::string& getId () const;
virtual int getNbParticipants () const;
virtual std::list<std::shared_ptr<Participant>> getParticipants () const;
virtual void removeParticipant (const std::shared_ptr<Participant> participant);
virtual void removeParticipants (const std::list<std::shared_ptr<Participant>> participants);
private:
L_DISABLE_COPY(LocalConference);
LocalConferenceEventHandler *eventHandler = nullptr;
};
LINPHONE_END_NAMESPACE
......
......@@ -36,6 +36,7 @@ class Participant : public Object {
friend class CallPrivate;
friend class ClientGroupChatRoom;
friend class Conference;
friend class LocalConference;
friend class MediaSessionPrivate;
public:
......
/*
* remote-conference-event-handler.cpp
* Copyright (C) 2017 Belledonne Communications SARL
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "remote-conference-event-handler.h"
#include "xml/conference-info.h"
#include "private.h"
#include "object/object-p.h"
using namespace std;
using namespace conference_info;
using namespace LinphonePrivate;
LINPHONE_BEGIN_NAMESPACE
class RemoteConferenceEventHandlerPrivate : public ObjectPrivate {
public:
LinphoneCore *core = nullptr;
ConferenceListener *listener = nullptr;
Address confAddr;
string confId;
LinphoneEvent *lev = nullptr;
};
LINPHONE_END_NAMESPACE
// -------- RemoteConferenceEventHandler public methods ---------
RemoteConferenceEventHandler::RemoteConferenceEventHandler(LinphoneCore *core, ConferenceListener *listener, const Address &confAddr) : Object(*new RemoteConferenceEventHandlerPrivate) {
L_D(RemoteConferenceEventHandler);
xercesc::XMLPlatformUtils::Initialize();
d->core = core;
d->listener = listener;
d->confAddr = confAddr;
}
RemoteConferenceEventHandler::~RemoteConferenceEventHandler() {
L_D(RemoteConferenceEventHandler);
xercesc::XMLPlatformUtils::Terminate();
if (d->lev)
linphone_event_unref(d->lev);
}
void RemoteConferenceEventHandler::subscribe(string confId) {
L_D(RemoteConferenceEventHandler);
d->confId = confId;
LinphoneAddress *addr = linphone_address_new(d->confAddr.asString().c_str());
d->lev = linphone_core_create_subscribe(d->core, addr, "Conference", 600);
linphone_address_unref(addr);
linphone_event_ref(d->lev);
linphone_event_set_internal(d->lev, TRUE);
linphone_event_set_user_data(d->lev, this);
linphone_event_add_custom_header(d->lev, "Conf-id", d->confId.c_str()); // TODO : ???
linphone_event_send_subscribe(d->lev, nullptr);
}
void RemoteConferenceEventHandler::unsubscribe() {
L_D(RemoteConferenceEventHandler);
linphone_event_terminate(d->lev);
}
void RemoteConferenceEventHandler::notifyReceived(string xmlBody) {
L_D(RemoteConferenceEventHandler);
istringstream data(xmlBody);
unique_ptr<Conference_type> confInfo = parseConference_info(data, xml_schema::Flags::dont_validate);
if (confInfo->getEntity() == d->confAddr.asString()) {
for (const auto &user : confInfo->getUsers()->getUser()) {
LinphoneAddress *cAddr = linphone_core_interpret_url(d->core, user.getEntity()->c_str());
Address addr(linphone_address_as_string(cAddr));
if (user.getState() == "deleted")
d->listener->onParticipantRemoved(addr);
else {
bool isAdmin = false;
if (user.getRoles()) {
for (const auto &entry : user.getRoles()->getEntry()) {
if (entry == "admin") {
isAdmin = true;
break;
}
}
}
if (user.getState() == "full")
d->listener->onParticipantAdded(addr);
d->listener->onParticipantSetAdmin(addr, isAdmin);
}
linphone_address_unref(cAddr);
}
}
}
string RemoteConferenceEventHandler::getConfId() {
L_D(RemoteConferenceEventHandler);
return d->confId;
}
/*
* remote-conference-event-handler.h
* Copyright (C) 2017 Belledonne Communications SARL
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _REMOTE_CONFERENCE_EVENT_HANDLER_H_
#define _REMOTE_CONFERENCE_EVENT_HANDLER_H_
#include <string>
#include "linphone/types.h"
#include "object/object.h"
#include "conference-listener.h"
LINPHONE_BEGIN_NAMESPACE
class RemoteConferenceEventHandlerPrivate;
class RemoteConferenceEventHandler : public Object {
public:
RemoteConferenceEventHandler(LinphoneCore *core, ConferenceListener *listener, const Address &confAddr);
~RemoteConferenceEventHandler();
void subscribe(std::string confId);
void notifyReceived(std::string xmlBody);
void unsubscribe();
std::string getConfId();
private:
L_DECLARE_PRIVATE(RemoteConferenceEventHandler);
L_DISABLE_COPY(RemoteConferenceEventHandler);
};
LINPHONE_END_NAMESPACE
#endif // ifndef _REMOTE_CONFERENCE_EVENT_HANDLER_H_
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="urn:ietf:params:xml:ns:resource-lists"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="urn:ietf:params:xml:ns:resource-lists"
elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2001/xml.xsd"/>
<xs:complexType name="listType">
<xs:sequence>
<xs:element name="display-name" type="display-nameType"
minOccurs="0"/>
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:choice>
<xs:element name="list">
<xs:complexType>
<xs:complexContent>
<xs:extension base="listType"/>
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:element name="external" type="externalType"/>
<xs:element name="entry" type="entryType"/>
<xs:element name="entry-ref" type="entry-refType"/>
</xs:choice>
</xs:sequence>
<xs:any namespace="##other" processContents="lax" minOccurs="0"
maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="optional"/>
<xs:anyAttribute namespace="##other" processContents="lax"/>
</xs:complexType>
<xs:complexType name="entryType">
<xs:sequence>
<xs:element name="display-name" minOccurs="0">