From 07066991842560c8c3923f49ee2aa54e7f10376b Mon Sep 17 00:00:00 2001 From: plfiorini Date: Fri, 28 May 2010 01:04:31 +0000 Subject: [PATCH 01/36] Import Jabber GPL'ed protocol (beware, it crashes but it's too late to find out why). --- Jamfile | 7 + Jamrules | 71 + build/jam/BuildSettings | 113 ++ build/jam/CheckRules | 106 ++ build/jam/ConfigRules | 146 ++ build/jam/DistroRules | 35 + build/jam/FileRules | 31 + build/jam/HelperRules | 52 + build/jam/InstallRules | 27 + build/jam/MainBuildRules | 380 ++++++ build/jam/OverriddenJamRules | 218 +++ build/jam/PackageRules | 247 ++++ build/jam/UserBuildConfig.sample | 63 + configure | 145 ++ libs/Jamfile | 4 + libs/libgloox/Jamfile | 106 ++ libs/libgloox/adhoc.cpp | 472 +++++++ libs/libgloox/adhoc.h | 487 +++++++ libs/libgloox/adhoccommandprovider.h | 79 ++ libs/libgloox/adhochandler.h | 75 + libs/libgloox/amp.cpp | 189 +++ libs/libgloox/amp.h | 243 ++++ libs/libgloox/annotations.cpp | 90 ++ libs/libgloox/annotations.h | 147 ++ libs/libgloox/annotationshandler.h | 66 + libs/libgloox/attention.cpp | 43 + libs/libgloox/attention.h | 70 + libs/libgloox/base64.cpp | 126 ++ libs/libgloox/base64.h | 51 + libs/libgloox/bookmarkhandler.h | 82 ++ libs/libgloox/bookmarkstorage.cpp | 118 ++ libs/libgloox/bookmarkstorage.h | 150 ++ libs/libgloox/bytestream.h | 180 +++ libs/libgloox/bytestreamdatahandler.h | 81 ++ libs/libgloox/bytestreamhandler.h | 90 ++ libs/libgloox/capabilities.cpp | 183 +++ libs/libgloox/capabilities.h | 132 ++ libs/libgloox/chatstate.cpp | 60 + libs/libgloox/chatstate.h | 87 ++ libs/libgloox/chatstatefilter.cpp | 65 + libs/libgloox/chatstatefilter.h | 102 ++ libs/libgloox/chatstatehandler.h | 51 + libs/libgloox/client.cpp | 603 ++++++++ libs/libgloox/client.h | 441 ++++++ libs/libgloox/clientbase.cpp | 1516 +++++++++++++++++++++ libs/libgloox/clientbase.h | 1030 ++++++++++++++ libs/libgloox/component.cpp | 62 + libs/libgloox/component.h | 77 ++ libs/libgloox/compressionbase.h | 85 ++ libs/libgloox/compressiondatahandler.h | 58 + libs/libgloox/compressiondefault.cpp | 92 ++ libs/libgloox/compressiondefault.h | 80 ++ libs/libgloox/compressionzlib.cpp | 135 ++ libs/libgloox/compressionzlib.h | 74 + libs/libgloox/config.h | 27 + libs/libgloox/config.h.unix | 123 ++ libs/libgloox/connectionbase.h | 167 +++ libs/libgloox/connectionbosh.cpp | 641 +++++++++ libs/libgloox/connectionbosh.h | 225 +++ libs/libgloox/connectiondatahandler.h | 66 + libs/libgloox/connectionhandler.h | 52 + libs/libgloox/connectionhttpproxy.cpp | 215 +++ libs/libgloox/connectionhttpproxy.h | 171 +++ libs/libgloox/connectionlistener.h | 106 ++ libs/libgloox/connectionsocks5proxy.cpp | 377 +++++ libs/libgloox/connectionsocks5proxy.h | 178 +++ libs/libgloox/connectiontcpbase.cpp | 200 +++ libs/libgloox/connectiontcpbase.h | 134 ++ libs/libgloox/connectiontcpclient.cpp | 159 +++ libs/libgloox/connectiontcpclient.h | 83 ++ libs/libgloox/connectiontcpserver.cpp | 163 +++ libs/libgloox/connectiontcpserver.h | 77 ++ libs/libgloox/connectiontls.cpp | 204 +++ libs/libgloox/connectiontls.h | 199 +++ libs/libgloox/connectiontlsserver.cpp | 46 + libs/libgloox/connectiontlsserver.h | 81 ++ libs/libgloox/dataform.cpp | 137 ++ libs/libgloox/dataform.h | 197 +++ libs/libgloox/dataformfield.cpp | 134 ++ libs/libgloox/dataformfield.h | 242 ++++ libs/libgloox/dataformfieldcontainer.cpp | 47 + libs/libgloox/dataformfieldcontainer.h | 125 ++ libs/libgloox/dataformitem.cpp | 54 + libs/libgloox/dataformitem.h | 62 + libs/libgloox/dataformreported.cpp | 54 + libs/libgloox/dataformreported.h | 64 + libs/libgloox/delayeddelivery.cpp | 74 + libs/libgloox/delayeddelivery.h | 130 ++ libs/libgloox/disco.cpp | 535 ++++++++ libs/libgloox/disco.h | 636 +++++++++ libs/libgloox/discohandler.h | 83 ++ libs/libgloox/disconodehandler.h | 81 ++ libs/libgloox/dns.cpp | 488 +++++++ libs/libgloox/dns.h | 179 +++ libs/libgloox/error.cpp | 138 ++ libs/libgloox/error.h | 139 ++ libs/libgloox/event.h | 81 ++ libs/libgloox/eventdispatcher.cpp | 73 + libs/libgloox/eventdispatcher.h | 88 ++ libs/libgloox/eventhandler.h | 47 + libs/libgloox/featureneg.cpp | 60 + libs/libgloox/featureneg.h | 86 ++ libs/libgloox/flexoff.cpp | 207 +++ libs/libgloox/flexoff.h | 178 +++ libs/libgloox/flexoffhandler.h | 82 ++ libs/libgloox/gloox.cpp | 124 ++ libs/libgloox/gloox.h | 1231 +++++++++++++++++ libs/libgloox/glooxversion.h | 13 + libs/libgloox/gpgencrypted.cpp | 60 + libs/libgloox/gpgencrypted.h | 91 ++ libs/libgloox/gpgsigned.cpp | 62 + libs/libgloox/gpgsigned.h | 91 ++ libs/libgloox/inbandbytestream.cpp | 297 ++++ libs/libgloox/inbandbytestream.h | 214 +++ libs/libgloox/instantmucroom.cpp | 31 + libs/libgloox/instantmucroom.h | 60 + libs/libgloox/iq.cpp | 69 + libs/libgloox/iq.h | 96 ++ libs/libgloox/iqhandler.h | 66 + libs/libgloox/jid.cpp | 123 ++ libs/libgloox/jid.h | 190 +++ libs/libgloox/lastactivity.cpp | 132 ++ libs/libgloox/lastactivity.h | 166 +++ libs/libgloox/lastactivityhandler.h | 58 + libs/libgloox/loghandler.h | 53 + libs/libgloox/logsink.cpp | 49 + libs/libgloox/logsink.h | 118 ++ libs/libgloox/macros.h | 49 + libs/libgloox/md5.cpp | 466 +++++++ libs/libgloox/md5.h | 137 ++ libs/libgloox/message.cpp | 96 ++ libs/libgloox/message.h | 156 +++ libs/libgloox/messageevent.cpp | 65 + libs/libgloox/messageevent.h | 88 ++ libs/libgloox/messageeventfilter.cpp | 112 ++ libs/libgloox/messageeventfilter.h | 95 ++ libs/libgloox/messageeventhandler.h | 51 + libs/libgloox/messagefilter.cpp | 42 + libs/libgloox/messagefilter.h | 84 ++ libs/libgloox/messagehandler.h | 58 + libs/libgloox/messagesession.cpp | 111 ++ libs/libgloox/messagesession.h | 309 +++++ libs/libgloox/messagesessionhandler.h | 67 + libs/libgloox/mucinvitationhandler.h | 72 + libs/libgloox/mucmessagesession.cpp | 54 + libs/libgloox/mucmessagesession.h | 66 + libs/libgloox/mucroom.cpp | 1317 ++++++++++++++++++ libs/libgloox/mucroom.h | 994 ++++++++++++++ libs/libgloox/mucroomconfighandler.h | 226 +++ libs/libgloox/mucroomhandler.h | 216 +++ libs/libgloox/mutex.cpp | 130 ++ libs/libgloox/mutex.h | 77 ++ libs/libgloox/mutexguard.h | 61 + libs/libgloox/nickname.cpp | 44 + libs/libgloox/nickname.h | 87 ++ libs/libgloox/nonsaslauth.cpp | 174 +++ libs/libgloox/nonsaslauth.h | 147 ++ libs/libgloox/oob.cpp | 81 ++ libs/libgloox/oob.h | 101 ++ libs/libgloox/parser.cpp | 800 +++++++++++ libs/libgloox/parser.h | 139 ++ libs/libgloox/prep.cpp | 121 ++ libs/libgloox/prep.h | 85 ++ libs/libgloox/presence.cpp | 143 ++ libs/libgloox/presence.h | 161 +++ libs/libgloox/presencehandler.h | 50 + libs/libgloox/privacyitem.cpp | 42 + libs/libgloox/privacyitem.h | 126 ++ libs/libgloox/privacylisthandler.h | 99 ++ libs/libgloox/privacymanager.cpp | 316 +++++ libs/libgloox/privacymanager.h | 231 ++++ libs/libgloox/privatexml.cpp | 129 ++ libs/libgloox/privatexml.h | 160 +++ libs/libgloox/privatexmlhandler.h | 73 + libs/libgloox/pubsub.h | 249 ++++ libs/libgloox/pubsubevent.cpp | 278 ++++ libs/libgloox/pubsubevent.h | 173 +++ libs/libgloox/pubsubitem.cpp | 63 + libs/libgloox/pubsubitem.h | 90 ++ libs/libgloox/pubsubmanager.cpp | 1199 ++++++++++++++++ libs/libgloox/pubsubmanager.h | 828 +++++++++++ libs/libgloox/pubsubresulthandler.h | 391 ++++++ libs/libgloox/receipt.cpp | 53 + libs/libgloox/receipt.h | 95 ++ libs/libgloox/registration.cpp | 391 ++++++ libs/libgloox/registration.h | 339 +++++ libs/libgloox/registrationhandler.h | 128 ++ libs/libgloox/resource.h | 106 ++ libs/libgloox/rosteritem.cpp | 177 +++ libs/libgloox/rosteritem.h | 196 +++ libs/libgloox/rosteritemdata.h | 190 +++ libs/libgloox/rosterlistener.h | 173 +++ libs/libgloox/rostermanager.cpp | 421 ++++++ libs/libgloox/rostermanager.h | 283 ++++ libs/libgloox/search.cpp | 223 +++ libs/libgloox/search.h | 215 +++ libs/libgloox/searchhandler.h | 195 +++ libs/libgloox/sha.cpp | 250 ++++ libs/libgloox/sha.h | 98 ++ libs/libgloox/shim.cpp | 71 + libs/libgloox/shim.h | 91 ++ libs/libgloox/sihandler.h | 70 + libs/libgloox/simanager.cpp | 259 ++++ libs/libgloox/simanager.h | 245 ++++ libs/libgloox/siprofileft.cpp | 312 +++++ libs/libgloox/siprofileft.h | 341 +++++ libs/libgloox/siprofilefthandler.h | 104 ++ libs/libgloox/siprofilehandler.h | 62 + libs/libgloox/socks5bytestream.cpp | 155 +++ libs/libgloox/socks5bytestream.h | 136 ++ libs/libgloox/socks5bytestreammanager.cpp | 486 +++++++ libs/libgloox/socks5bytestreammanager.h | 307 +++++ libs/libgloox/socks5bytestreamserver.cpp | 220 +++ libs/libgloox/socks5bytestreamserver.h | 144 ++ libs/libgloox/softwareversion.cpp | 74 + libs/libgloox/softwareversion.h | 101 ++ libs/libgloox/stanza.cpp | 127 ++ libs/libgloox/stanza.h | 174 +++ libs/libgloox/stanzaextension.h | 241 ++++ libs/libgloox/stanzaextensionfactory.cpp | 85 ++ libs/libgloox/stanzaextensionfactory.h | 90 ++ libs/libgloox/statisticshandler.h | 80 ++ libs/libgloox/subscription.cpp | 77 ++ libs/libgloox/subscription.h | 106 ++ libs/libgloox/subscriptionhandler.h | 49 + libs/libgloox/tag.cpp | 1372 +++++++++++++++++++ libs/libgloox/tag.h | 710 ++++++++++ libs/libgloox/taghandler.h | 51 + libs/libgloox/tlsbase.h | 149 ++ libs/libgloox/tlsdefault.cpp | 146 ++ libs/libgloox/tlsdefault.h | 105 ++ libs/libgloox/tlsgnutlsbase.cpp | 178 +++ libs/libgloox/tlsgnutlsbase.h | 92 ++ libs/libgloox/tlsgnutlsclient.cpp | 223 +++ libs/libgloox/tlsgnutlsclient.h | 81 ++ libs/libgloox/tlsgnutlsclientanon.cpp | 101 ++ libs/libgloox/tlsgnutlsclientanon.h | 70 + libs/libgloox/tlsgnutlsserveranon.cpp | 113 ++ libs/libgloox/tlsgnutlsserveranon.h | 75 + libs/libgloox/tlshandler.h | 68 + libs/libgloox/tlsopensslbase.cpp | 333 +++++ libs/libgloox/tlsopensslbase.h | 108 ++ libs/libgloox/tlsopensslclient.cpp | 47 + libs/libgloox/tlsopensslclient.h | 63 + libs/libgloox/tlsopensslserver.cpp | 266 ++++ libs/libgloox/tlsopensslserver.h | 64 + libs/libgloox/tlsschannel.cpp | 803 +++++++++++ libs/libgloox/tlsschannel.h | 108 ++ libs/libgloox/uniquemucroom.cpp | 109 ++ libs/libgloox/uniquemucroom.h | 117 ++ libs/libgloox/util.cpp | 121 ++ libs/libgloox/util.h | 269 ++++ libs/libgloox/vcard.cpp | 555 ++++++++ libs/libgloox/vcard.h | 627 +++++++++ libs/libgloox/vcardhandler.h | 75 + libs/libgloox/vcardmanager.cpp | 127 ++ libs/libgloox/vcardmanager.h | 156 +++ libs/libgloox/vcardupdate.cpp | 79 ++ libs/libgloox/vcardupdate.h | 95 ++ libs/libgloox/xhtmlim.cpp | 56 + libs/libgloox/xhtmlim.h | 78 ++ protocols/Jamfile | 4 + protocols/jabber/Jabber.cpp | 493 +++++++ protocols/jabber/Jabber.h | 97 ++ protocols/jabber/Jamfile | 20 + protocols/jabber/jabber.rdef | 56 + protocols/jabber/main.cpp | 25 + protocols/jabber/settings_template.rdef | 24 + 268 files changed, 49140 insertions(+) create mode 100644 Jamfile create mode 100644 Jamrules create mode 100644 build/jam/BuildSettings create mode 100644 build/jam/CheckRules create mode 100644 build/jam/ConfigRules create mode 100644 build/jam/DistroRules create mode 100644 build/jam/FileRules create mode 100644 build/jam/HelperRules create mode 100644 build/jam/InstallRules create mode 100644 build/jam/MainBuildRules create mode 100644 build/jam/OverriddenJamRules create mode 100644 build/jam/PackageRules create mode 100644 build/jam/UserBuildConfig.sample create mode 100755 configure create mode 100644 libs/Jamfile create mode 100644 libs/libgloox/Jamfile create mode 100644 libs/libgloox/adhoc.cpp create mode 100644 libs/libgloox/adhoc.h create mode 100644 libs/libgloox/adhoccommandprovider.h create mode 100644 libs/libgloox/adhochandler.h create mode 100644 libs/libgloox/amp.cpp create mode 100644 libs/libgloox/amp.h create mode 100644 libs/libgloox/annotations.cpp create mode 100644 libs/libgloox/annotations.h create mode 100644 libs/libgloox/annotationshandler.h create mode 100644 libs/libgloox/attention.cpp create mode 100644 libs/libgloox/attention.h create mode 100644 libs/libgloox/base64.cpp create mode 100644 libs/libgloox/base64.h create mode 100644 libs/libgloox/bookmarkhandler.h create mode 100644 libs/libgloox/bookmarkstorage.cpp create mode 100644 libs/libgloox/bookmarkstorage.h create mode 100644 libs/libgloox/bytestream.h create mode 100644 libs/libgloox/bytestreamdatahandler.h create mode 100644 libs/libgloox/bytestreamhandler.h create mode 100644 libs/libgloox/capabilities.cpp create mode 100644 libs/libgloox/capabilities.h create mode 100644 libs/libgloox/chatstate.cpp create mode 100644 libs/libgloox/chatstate.h create mode 100644 libs/libgloox/chatstatefilter.cpp create mode 100644 libs/libgloox/chatstatefilter.h create mode 100644 libs/libgloox/chatstatehandler.h create mode 100644 libs/libgloox/client.cpp create mode 100644 libs/libgloox/client.h create mode 100644 libs/libgloox/clientbase.cpp create mode 100644 libs/libgloox/clientbase.h create mode 100644 libs/libgloox/component.cpp create mode 100644 libs/libgloox/component.h create mode 100644 libs/libgloox/compressionbase.h create mode 100644 libs/libgloox/compressiondatahandler.h create mode 100644 libs/libgloox/compressiondefault.cpp create mode 100644 libs/libgloox/compressiondefault.h create mode 100644 libs/libgloox/compressionzlib.cpp create mode 100644 libs/libgloox/compressionzlib.h create mode 100644 libs/libgloox/config.h create mode 100644 libs/libgloox/config.h.unix create mode 100644 libs/libgloox/connectionbase.h create mode 100644 libs/libgloox/connectionbosh.cpp create mode 100644 libs/libgloox/connectionbosh.h create mode 100644 libs/libgloox/connectiondatahandler.h create mode 100644 libs/libgloox/connectionhandler.h create mode 100644 libs/libgloox/connectionhttpproxy.cpp create mode 100644 libs/libgloox/connectionhttpproxy.h create mode 100644 libs/libgloox/connectionlistener.h create mode 100644 libs/libgloox/connectionsocks5proxy.cpp create mode 100644 libs/libgloox/connectionsocks5proxy.h create mode 100644 libs/libgloox/connectiontcpbase.cpp create mode 100644 libs/libgloox/connectiontcpbase.h create mode 100644 libs/libgloox/connectiontcpclient.cpp create mode 100644 libs/libgloox/connectiontcpclient.h create mode 100644 libs/libgloox/connectiontcpserver.cpp create mode 100644 libs/libgloox/connectiontcpserver.h create mode 100644 libs/libgloox/connectiontls.cpp create mode 100644 libs/libgloox/connectiontls.h create mode 100644 libs/libgloox/connectiontlsserver.cpp create mode 100644 libs/libgloox/connectiontlsserver.h create mode 100644 libs/libgloox/dataform.cpp create mode 100644 libs/libgloox/dataform.h create mode 100644 libs/libgloox/dataformfield.cpp create mode 100644 libs/libgloox/dataformfield.h create mode 100644 libs/libgloox/dataformfieldcontainer.cpp create mode 100644 libs/libgloox/dataformfieldcontainer.h create mode 100644 libs/libgloox/dataformitem.cpp create mode 100644 libs/libgloox/dataformitem.h create mode 100644 libs/libgloox/dataformreported.cpp create mode 100644 libs/libgloox/dataformreported.h create mode 100644 libs/libgloox/delayeddelivery.cpp create mode 100644 libs/libgloox/delayeddelivery.h create mode 100644 libs/libgloox/disco.cpp create mode 100644 libs/libgloox/disco.h create mode 100644 libs/libgloox/discohandler.h create mode 100644 libs/libgloox/disconodehandler.h create mode 100644 libs/libgloox/dns.cpp create mode 100644 libs/libgloox/dns.h create mode 100644 libs/libgloox/error.cpp create mode 100644 libs/libgloox/error.h create mode 100644 libs/libgloox/event.h create mode 100644 libs/libgloox/eventdispatcher.cpp create mode 100644 libs/libgloox/eventdispatcher.h create mode 100644 libs/libgloox/eventhandler.h create mode 100644 libs/libgloox/featureneg.cpp create mode 100644 libs/libgloox/featureneg.h create mode 100644 libs/libgloox/flexoff.cpp create mode 100644 libs/libgloox/flexoff.h create mode 100644 libs/libgloox/flexoffhandler.h create mode 100644 libs/libgloox/gloox.cpp create mode 100644 libs/libgloox/gloox.h create mode 100644 libs/libgloox/glooxversion.h create mode 100644 libs/libgloox/gpgencrypted.cpp create mode 100644 libs/libgloox/gpgencrypted.h create mode 100644 libs/libgloox/gpgsigned.cpp create mode 100644 libs/libgloox/gpgsigned.h create mode 100644 libs/libgloox/inbandbytestream.cpp create mode 100644 libs/libgloox/inbandbytestream.h create mode 100644 libs/libgloox/instantmucroom.cpp create mode 100644 libs/libgloox/instantmucroom.h create mode 100644 libs/libgloox/iq.cpp create mode 100644 libs/libgloox/iq.h create mode 100644 libs/libgloox/iqhandler.h create mode 100644 libs/libgloox/jid.cpp create mode 100644 libs/libgloox/jid.h create mode 100644 libs/libgloox/lastactivity.cpp create mode 100644 libs/libgloox/lastactivity.h create mode 100644 libs/libgloox/lastactivityhandler.h create mode 100644 libs/libgloox/loghandler.h create mode 100644 libs/libgloox/logsink.cpp create mode 100644 libs/libgloox/logsink.h create mode 100644 libs/libgloox/macros.h create mode 100644 libs/libgloox/md5.cpp create mode 100644 libs/libgloox/md5.h create mode 100644 libs/libgloox/message.cpp create mode 100644 libs/libgloox/message.h create mode 100644 libs/libgloox/messageevent.cpp create mode 100644 libs/libgloox/messageevent.h create mode 100644 libs/libgloox/messageeventfilter.cpp create mode 100644 libs/libgloox/messageeventfilter.h create mode 100644 libs/libgloox/messageeventhandler.h create mode 100644 libs/libgloox/messagefilter.cpp create mode 100644 libs/libgloox/messagefilter.h create mode 100644 libs/libgloox/messagehandler.h create mode 100644 libs/libgloox/messagesession.cpp create mode 100644 libs/libgloox/messagesession.h create mode 100644 libs/libgloox/messagesessionhandler.h create mode 100644 libs/libgloox/mucinvitationhandler.h create mode 100644 libs/libgloox/mucmessagesession.cpp create mode 100644 libs/libgloox/mucmessagesession.h create mode 100644 libs/libgloox/mucroom.cpp create mode 100644 libs/libgloox/mucroom.h create mode 100644 libs/libgloox/mucroomconfighandler.h create mode 100644 libs/libgloox/mucroomhandler.h create mode 100644 libs/libgloox/mutex.cpp create mode 100644 libs/libgloox/mutex.h create mode 100644 libs/libgloox/mutexguard.h create mode 100644 libs/libgloox/nickname.cpp create mode 100644 libs/libgloox/nickname.h create mode 100644 libs/libgloox/nonsaslauth.cpp create mode 100644 libs/libgloox/nonsaslauth.h create mode 100644 libs/libgloox/oob.cpp create mode 100644 libs/libgloox/oob.h create mode 100644 libs/libgloox/parser.cpp create mode 100644 libs/libgloox/parser.h create mode 100644 libs/libgloox/prep.cpp create mode 100644 libs/libgloox/prep.h create mode 100644 libs/libgloox/presence.cpp create mode 100644 libs/libgloox/presence.h create mode 100644 libs/libgloox/presencehandler.h create mode 100644 libs/libgloox/privacyitem.cpp create mode 100644 libs/libgloox/privacyitem.h create mode 100644 libs/libgloox/privacylisthandler.h create mode 100644 libs/libgloox/privacymanager.cpp create mode 100644 libs/libgloox/privacymanager.h create mode 100644 libs/libgloox/privatexml.cpp create mode 100644 libs/libgloox/privatexml.h create mode 100644 libs/libgloox/privatexmlhandler.h create mode 100644 libs/libgloox/pubsub.h create mode 100644 libs/libgloox/pubsubevent.cpp create mode 100644 libs/libgloox/pubsubevent.h create mode 100644 libs/libgloox/pubsubitem.cpp create mode 100644 libs/libgloox/pubsubitem.h create mode 100644 libs/libgloox/pubsubmanager.cpp create mode 100644 libs/libgloox/pubsubmanager.h create mode 100644 libs/libgloox/pubsubresulthandler.h create mode 100644 libs/libgloox/receipt.cpp create mode 100644 libs/libgloox/receipt.h create mode 100644 libs/libgloox/registration.cpp create mode 100644 libs/libgloox/registration.h create mode 100644 libs/libgloox/registrationhandler.h create mode 100644 libs/libgloox/resource.h create mode 100644 libs/libgloox/rosteritem.cpp create mode 100644 libs/libgloox/rosteritem.h create mode 100644 libs/libgloox/rosteritemdata.h create mode 100644 libs/libgloox/rosterlistener.h create mode 100644 libs/libgloox/rostermanager.cpp create mode 100644 libs/libgloox/rostermanager.h create mode 100644 libs/libgloox/search.cpp create mode 100644 libs/libgloox/search.h create mode 100644 libs/libgloox/searchhandler.h create mode 100644 libs/libgloox/sha.cpp create mode 100644 libs/libgloox/sha.h create mode 100644 libs/libgloox/shim.cpp create mode 100644 libs/libgloox/shim.h create mode 100644 libs/libgloox/sihandler.h create mode 100644 libs/libgloox/simanager.cpp create mode 100644 libs/libgloox/simanager.h create mode 100644 libs/libgloox/siprofileft.cpp create mode 100644 libs/libgloox/siprofileft.h create mode 100644 libs/libgloox/siprofilefthandler.h create mode 100644 libs/libgloox/siprofilehandler.h create mode 100644 libs/libgloox/socks5bytestream.cpp create mode 100644 libs/libgloox/socks5bytestream.h create mode 100644 libs/libgloox/socks5bytestreammanager.cpp create mode 100644 libs/libgloox/socks5bytestreammanager.h create mode 100644 libs/libgloox/socks5bytestreamserver.cpp create mode 100644 libs/libgloox/socks5bytestreamserver.h create mode 100644 libs/libgloox/softwareversion.cpp create mode 100644 libs/libgloox/softwareversion.h create mode 100644 libs/libgloox/stanza.cpp create mode 100644 libs/libgloox/stanza.h create mode 100644 libs/libgloox/stanzaextension.h create mode 100644 libs/libgloox/stanzaextensionfactory.cpp create mode 100644 libs/libgloox/stanzaextensionfactory.h create mode 100644 libs/libgloox/statisticshandler.h create mode 100644 libs/libgloox/subscription.cpp create mode 100644 libs/libgloox/subscription.h create mode 100644 libs/libgloox/subscriptionhandler.h create mode 100644 libs/libgloox/tag.cpp create mode 100644 libs/libgloox/tag.h create mode 100644 libs/libgloox/taghandler.h create mode 100644 libs/libgloox/tlsbase.h create mode 100644 libs/libgloox/tlsdefault.cpp create mode 100644 libs/libgloox/tlsdefault.h create mode 100644 libs/libgloox/tlsgnutlsbase.cpp create mode 100644 libs/libgloox/tlsgnutlsbase.h create mode 100644 libs/libgloox/tlsgnutlsclient.cpp create mode 100644 libs/libgloox/tlsgnutlsclient.h create mode 100644 libs/libgloox/tlsgnutlsclientanon.cpp create mode 100644 libs/libgloox/tlsgnutlsclientanon.h create mode 100644 libs/libgloox/tlsgnutlsserveranon.cpp create mode 100644 libs/libgloox/tlsgnutlsserveranon.h create mode 100644 libs/libgloox/tlshandler.h create mode 100644 libs/libgloox/tlsopensslbase.cpp create mode 100644 libs/libgloox/tlsopensslbase.h create mode 100644 libs/libgloox/tlsopensslclient.cpp create mode 100644 libs/libgloox/tlsopensslclient.h create mode 100644 libs/libgloox/tlsopensslserver.cpp create mode 100644 libs/libgloox/tlsopensslserver.h create mode 100644 libs/libgloox/tlsschannel.cpp create mode 100644 libs/libgloox/tlsschannel.h create mode 100644 libs/libgloox/uniquemucroom.cpp create mode 100644 libs/libgloox/uniquemucroom.h create mode 100644 libs/libgloox/util.cpp create mode 100644 libs/libgloox/util.h create mode 100644 libs/libgloox/vcard.cpp create mode 100644 libs/libgloox/vcard.h create mode 100644 libs/libgloox/vcardhandler.h create mode 100644 libs/libgloox/vcardmanager.cpp create mode 100644 libs/libgloox/vcardmanager.h create mode 100644 libs/libgloox/vcardupdate.cpp create mode 100644 libs/libgloox/vcardupdate.h create mode 100644 libs/libgloox/xhtmlim.cpp create mode 100644 libs/libgloox/xhtmlim.h create mode 100644 protocols/Jamfile create mode 100644 protocols/jabber/Jabber.cpp create mode 100644 protocols/jabber/Jabber.h create mode 100644 protocols/jabber/Jamfile create mode 100644 protocols/jabber/jabber.rdef create mode 100644 protocols/jabber/main.cpp create mode 100644 protocols/jabber/settings_template.rdef diff --git a/Jamfile b/Jamfile new file mode 100644 index 0000000..2ce9b46 --- /dev/null +++ b/Jamfile @@ -0,0 +1,7 @@ +SubDir TOP ; + +# Include all the components +SubInclude TOP libs ; +SubInclude TOP protocols ; + +UninstallTarget $(CAYA_DIRECTORY) ; diff --git a/Jamrules b/Jamrules new file mode 100644 index 0000000..985e000 --- /dev/null +++ b/Jamrules @@ -0,0 +1,71 @@ +# The directories used by the build. +BUILD_DIR = [ FDirName $(TOP) build ] ; +JAM_DIR = [ FDirName $(BUILD_DIR) jam ] ; +SCRIPTS_DIR = [ FDirName $(JAM_DIR) scripts ] ; +GENERATED_DIR = [ FDirName $(TOP) generated ] ; +DISTRO_DIR = [ FDirName $(TOP) generated distro ] ; +FULL_DISTRO_DIR = [ FDirName $(TOP) generated fulldistro ] ; +PACKAGE_DIR = [ FDirName $(GENERATED_DIR) packages ] ; +PACKAGE_OBJECT_DIR = [ FDirName $(PACKAGE_DIR) objects ] ; + +##------------------------------------------------------------------- +## Defines +##------------------------------------------------------------------- +VERSION = 0.0.1 ; +DEFINES += VERSION=\"\\\"$(VERSION)\\\"\" ; +DEFINES += BUILD_DATE=\"\\\"$(JAMDATE)\\\"\" ; +CHGRP = ; +CHOWN = ; + +# Include BuildConfig generated by configure +{ + local buildConfig = [ Glob $(GENERATED_DIR) : BuildConfig ] ; + if $(buildConfig) { + LOCATE on BuildConfig = $(GENERATED_DIR) ; + include BuildConfig ; + } else { + Exit "Run ./configure first!" ; + } +} + +# Include UserBuildConfig +{ + local userBuildConfig = [ Glob $(JAM_DIR) : UserBuildConfig ] ; + if $(userBuildConfig) { + LOCATE on UserBuildConfig = $(JAM_DIR) ; + include UserBuildConfig ; + } +} + +# Cache files for header scanning and jamfile caching. +HCACHEFILE = header_cache ; +JCACHEFILE = jamfile_cache ; +LOCATE on $(HCACHEFILE) $(JCACHEFILE) = $(GENERATED_DIR) ; + +# Perform configuration checks +include [ FDirName $(JAM_DIR) CheckRules ] ; +CheckGccPlatform ; +CheckCaya ; +if ! $(HAVE_CAYA) { + Echo "** Caya library is needed!" ; + Exit 1 ; +} +CheckOpenSSL ; +if ! $(HAVE_OPENSSL) { + Echo "** MSN, Jabber, GoogleTalk and Facebook protocols are disabled for lack of OpenSSL" ; +} +CheckLibYahoo2 ; +if ! $(HAVE_LIBYAHOO2) { + Echo "** Yahoo protocol is disabled for lack of libyahoo2" ; +} + +# Include jam scripts +include [ FDirName $(JAM_DIR) HelperRules ] ; +include [ FDirName $(JAM_DIR) ConfigRules ] ; +include [ FDirName $(JAM_DIR) OverriddenJamRules ] ; +include [ FDirName $(JAM_DIR) MainBuildRules ] ; +include [ FDirName $(JAM_DIR) FileRules ] ; +include [ FDirName $(JAM_DIR) InstallRules ] ; +include [ FDirName $(JAM_DIR) PackageRules ] ; +include [ FDirName $(JAM_DIR) DistroRules ] ; +include [ FDirName $(JAM_DIR) BuildSettings ] ; diff --git a/build/jam/BuildSettings b/build/jam/BuildSettings new file mode 100644 index 0000000..06d6b0b --- /dev/null +++ b/build/jam/BuildSettings @@ -0,0 +1,113 @@ +# BuildSettings +# +# Setup global variables. + +# C and C++ flags +if $(OSPLAT) = PPC { + # filter out -nosyspath + CFLAGS = [ FFilter $(CFLAGS) : -nosyspath ] ; + C++FLAGS = [ FFilter $(C++FLAGS) : -nosyspath ] ; + LINKFLAGS += -warn -export pragma ; +} + +# Use copyattr for copying. +CP = copyattr --data ; + +# Default paths for bison and flex: +BISON = bison ; +LEX = flex ; + +# mkdir shall not fail, if the directory already exists. +MKDIR = mkdir -p ; + +# by default we do not strip and do not build tests: +STRIP_APPS ?= 0 ; +BUILD_TESTS ?= 0 ; + +# Enable debugging by default +DEBUG ?= 1 ; + +rule SetUpSubDirBuildSettings +{ + # SetUpSubDirBuildSettings ; + # + # Sets up the compiler flags and defines based on the WARNINGS, DEBUG, and + # OPTIMIZE variables. Also sets the locations for the targets (objects, + # libraries and executables). + # + # : Parameters as passed to the SubDir rule, i.e. the name of the + # TOP variable and the subdir tokens. + # + local dir = $(1) ; + + # warnings settings + if $(WARNINGS) != 0 { + if $(OSPLAT) = X86 { + CCFLAGS += -Wall -Wno-multichar -Wpointer-arith + -Wmissing-prototypes -Wcast-align -Wsign-compare ; + C++FLAGS += -Wall -Wno-multichar -Wno-ctor-dtor-privacy -Woverloaded-virtual + -Wconversion -Wpointer-arith -Wcast-align + -Wsign-compare -Wno-reorder -Wno-unknown-pragmas ; + } else { + CCFLAGS += -w on -requireprotos ; + } + } + + local gccString = ; + if $(IS_GCC4_PLATFORM) { + gccString += gcc4 ; + } else { + gccString += gcc2 ; + } + + local binModeString = ; + if $(DEBUG) && $(DEBUG) != 0 { + binModeString += debug ; + } else { + binModeString += release ; + } + + # debugging settings + if $(DEBUG) && $(DEBUG) != 0 { + OPTIMIZE = 0 ; + STRIP_APPS = 0 ; + DEFINES += DEBUG=$(DEBUG) BM_REF_DEBUGGING ; + CCFLAGS += -g ; + C++FLAGS += -g -fno-inline ; + LINKFLAGS += -g ; + } + + DISTRO_DIR = [ FDirName $(TOP) generated distro-$(OS:L)-$(OSPLAT:L)-$(gccString)-$(binModeString) ] ; + OBJECTS_DIR = [ FDirName $(TOP) generated objects-$(OS:L)-$(OSPLAT:L)-$(gccString)-$(binModeString) ] ; + + # optimization settings + if $(OPTIMIZE) = 0 { + if $(OSPLAT) = X86 { + OPTIM = -O0 ; + } else { + OPTIM = -O0 ; + } + } else { + if $(OSPLAT) = X86 { + OPTIM ?= -O3 -fstrict-aliasing ; + } else { + OPTIM ?= -O7 ; + } + } + + # setup objects location + local objdir = [ FDirName $(OBJECTS_DIR) $(dir[2-]) ] ; + SEARCH_SOURCE += $(objdir) ; + LOCATE_SOURCE = $(objdir) ; + LOCATE_TARGET = $(objdir) ; + + # setup main targets location + LOCATE_MAIN_TARGET ?= [ FDirName $(DISTRO_DIR) ] ; +} + +# The LOCATE_MAIN_TARGET variable shall be reset for each subdirectory. +AUTO_SET_UP_CONFIG_VARIABLES += LOCATE_MAIN_TARGET ; + +# Add the rules setting up the build settings for a subdirectory to the +# rules invoked by SubDir. +SUBDIRRULES += SetUpSubDirBuildSettings ; diff --git a/build/jam/CheckRules b/build/jam/CheckRules new file mode 100644 index 0000000..ac2e326 --- /dev/null +++ b/build/jam/CheckRules @@ -0,0 +1,106 @@ +# CheckRules +# +# Common checks. + +rule CheckGccPlatform +{ + # CheckGccPlatform ; + # Detects if we are using gcc4 and set IS_GCC4_PLATFORM according. + + # First find out which gcc version the platform uses. + IS_GCC4_PLATFORM = ; + if $(OS) = HAIKU { + # Only Haiku might use gcc 4. We use the existence of a libstdc++.r4.so in + # /boot/develop/lib/x86 to judge whether this is a BeOS compatible and thus + # gcc 2 platform. This is not entirely correct, but should be good enough + # for the time being. + local haveLibStdC++.R4 = [ Glob /boot/develop/lib/x86 : libstdc++.r4.so ] ; + if ! $(haveLibStdC++.R4) { + IS_GCC4_PLATFORM = 1 ; + Echo Using GCC4 platform ; + } + } +} + +rule CheckOpenSSL +{ + # CheckOpenSSL ; + # Check for OpenSSL and defined HAVE_OPENSSL according, it also defines + # OPENSSL_INCLUDE_DIR and OPENSSL_LIBRARY_DIR with location of respectively + # include and library files. + + HAVE_OPENSSL = ; + OPENSSL_INCLUDE_DIR = ; + OPENSSL_LIBRARY_DIR = ; + + local haveHeaders = [ Glob $(COMMON_INCLUDE_DIRECTORY)/openssl : ssl.h ] ; + if $(haveHeaders) { + OPENSSL_INCLUDE_DIR = $(COMMON_INCLUDE_DIRECTORY)/openssl ; + + local haveLibs = [ Glob $(COMMON_LIB_DIRECTORY) : libssl.so ] ; + if $(haveLibs) { + OPENSSL_LIBRARY_DIR = $(COMMON_LIB_DIRECTORY) ; + + Echo OpenSSL Headers: $(OPENSSL_INCLUDE_DIR) ; + Echo OpenSSL Libs: $(OPENSSL_LIBRARY_DIR) ; + } + + HAVE_OPENSSL = $(haveLibs) ; + } +} + +rule CheckLibYahoo2 +{ + # CheckLibYahoo2 + # Check for LibYahoo2 and defined HAVE_LIBYAHOO2 according, it also defines + # LIBYAHOO2_INCLUDE_DIR and LIBYAHOO2_LIBRARY_DIR with location of respectively + # include and library files. + + HAVE_LIBYAHOO2 = ; + LIBYAHOO2_INCLUDE_DIR = ; + LIBYAHOO2_LIBRARY_DIR = ; + + local haveHeaders = [ Glob $(COMMON_INCLUDE_DIRECTORY)/libyahoo2 : yahoo2.h ] ; + if $(haveHeaders) { + LIBYAHOO2_INCLUDE_DIR = $(COMMON_INCLUDE_DIRECTORY)/libyahoo2 ; + + local haveLibs = [ Glob $(COMMON_LIB_DIRECTORY) : libyahoo2.so ] ; + if $(haveLibs) { + LIBYAHOO2_LIBRARY_DIR = $(COMMON_LIB_DIRECTORY) ; + + Echo Yahoo Headers: $(LIBYAHOO2_INCLUDE_DIR) ; + Echo Yahoo Libs: $(LIBYAHOO2_LIBRARY_DIR) ; + } + + HAVE_LIBYAHOO2 = $(haveLibs) ; + } +} + +rule CheckCaya +{ + # CheckCaya + # Check for Caya and defined HAVE_CAYA according, it also defines + # CAYA_INCLUDE_DIR and CAYA_LIBRARY_DIR with location of respectively + # include and library files. + + HAVE_CAYA = ; + CAYA_INCLUDE_DIR = ; + CAYA_LIBRARY_DIR = ; + + local haveHeaders = [ Glob $(COMMON_INCLUDE_DIRECTORY)/caya : CayaProtocol.h ] ; + if $(haveHeaders) { + CAYA_INCLUDE_DIR = $(COMMON_INCLUDE_DIRECTORY)/caya ; + +# local haveLibs = [ Glob $(COMMON_LIB_DIRECTORY) : libinfopopper.so ] ; +# if $(haveLibs) { +# CAYA_LIBRARY_DIR = $(COMMON_LIB_DIRECTORY) ; + + Echo Caya Headers: $(CAYA_INCLUDE_DIR) ; +# Echo Caya Libs: $(CAYA_LIBRARY_DIR) ; +# } + +# HAVE_CAYA = $(haveLibs) ; + } + + HAVE_CAYA = $(haveHeaders) ; +} diff --git a/build/jam/ConfigRules b/build/jam/ConfigRules new file mode 100644 index 0000000..082ec46 --- /dev/null +++ b/build/jam/ConfigRules @@ -0,0 +1,146 @@ +# ConfigRules +# +# Contains rules providing the config variable feature. It allows to set the +# values for certain variables for subdirectories in a central place. That is +# one can, for instance, specify in a file like UserBuildConfig for which +# directories to enable debugging, warnings, set special defines, compiler +# flags and the like without needing to edit the Jamfiles for the respective +# dirs. + +rule ConfigObject +{ + # ConfigObject ; + # + # Private rule. Returns the dummy object on which the config variables are + # set for a given subdir. + # + # : Parameters as passed to the SubDir rule, i.e. the name of the + # TOP variable and the subdir tokens. + # + local config = __config__ ; + local grist = [ FGrist root $(1) ] ; + return $(config:G=$(grist)) ; +} + +rule SetConfigVar +{ + # SetConfigVar : : [ : ] ; + # + # Sets a config variable for a specified directory to the given value. + # + # : The name of the variable to be set. + # : Parameters as passed to the SubDir rule, i.e. the name of the + # TOP variable and the subdir tokens. + # : The value to which the variable shall be set. + # : Either "global" or "local". The former implies that the variable + # value shall also be used for subdirectories (recursively), if + # for them the variable has not been set. The latter has the same + # effect regarding subdirs as if the variable for the directory + # is not set. Defaults to "global". + # + local var = $(1[1]) ; + local config = [ ConfigObject $(2) ] ; + local scope = $(4) ; + if ! $(scope) { + scope = global ; + } + $(var) on $(config) = $(3) ; + __set_$(var) on $(config) = $(scope) ; +} + +rule AppendToConfigVar +{ + # AppendToConfigVar : : [ : ] ; + # + # Appends a value to a config variable for a specified directory. Shortcut + # for + # SetConfigVar : : [ ConfigVar : ] ; + # + # : The name of the variable to be set. + # : Parameters as passed to the SubDir rule, i.e. the name of the + # TOP variable and the subdir tokens. + # : The value which to append to the variables current value. + # : Either "global" or "local". The former implies that the variable + # value shall also be used for subdirectories (recursively), if + # for them the variable has not been set. The latter has the same + # effect regarding subdirs as if the variable for the directory + # is not set. Defaults to "global". + # + SetConfigVar $(1) : $(2) : [ ConfigVar $(1) : $(2) ] $(3) : $(4) ; +} + +rule ConfigVar +{ + # ConfigVar : [ : ] ; + # + # Returns the value of a configuration variable for a given subdir. + # If the variable is not set for the subdir, the rule is invoked + # recursively for the parent directory with the scope "global". When + # the root is reached without yielding a value, the value of the global + # variable is returned. + # + # : The name of the variable whose value shall be returned. + # : Parameters as passed to the SubDir rule, i.e. the name of the + # TOP variable and the subdir tokens. + # : If not given any scope passed to SetConfigVar for the given + # directory will be accepted, otherwise it must match the scope + # passed to SetConfigVar. + # + local var = $(1[1]) ; + local dir = $(2) ; + local config = [ ConfigObject $(dir) ] ; + local scope = $(3) ; + local varScope = [ on $(config) return $(__set_$(var)) ] ; + if ( ! $(scope) && $(varScope) ) + || ( $(scope) && $(scope) = $(varScope) ) + || ! $(dir) { + on $(config) return $($(var)) ; + } else { + dir = [ FReverse $(dir) ] ; + return [ ConfigVar $(var) : [ FReverse $(dir[2-]) ] : global ] ; + } +} + +rule SetUpConfigVars { + # SetUpConfigVars ; + # + # Sets the global variables defined in AUTO_SET_UP_CONFIG_VARIABLES to the + # values specified for the subdirectory . + # + # : Parameters as passed to the SubDir rule, i.e. the name of the + # TOP variable and the subdir tokens. + # + local dir = $(1) ; + + # Backup the global variable value on first invocation, otherwise restore + # them, so that ConfigVar returns the right values for not explicity set + # local variables. + local var ; + if ! $(__config_var_backup__) { + __config_var_backup__ = true ; + for var in $(AUTO_SET_UP_CONFIG_VARIABLES) { + __config_var_backup_$(var)__ = $($(var)) ; + } + } else { + for var in $(AUTO_SET_UP_CONFIG_VARIABLES) { + $(var) = $(__config_var_backup_$(var)__) ; + } + } + + # Set the variables to their configured values. + for var in $(AUTO_SET_UP_CONFIG_VARIABLES) { + $(var) = [ ConfigVar $(var) : $(dir) ] ; + } +} + +# Add the SetUpConfigVars rule to the rules that are invoked at the end of the +# SubDir rule. Prepend it, so that the variables are set up before any other +# rule is invoked. +SUBDIRRULES = SetUpConfigVars $(SUBDIRRULES) ; + +# Some config variables that should be set up automatically for subdirs. +AUTO_SET_UP_CONFIG_VARIABLES += + CCFLAGS C++FLAGS DEBUG DEFINES HDRS LINKFLAGS OPTIM OPTIMIZE + SYSHDRS WARNINGS +; diff --git a/build/jam/DistroRules b/build/jam/DistroRules new file mode 100644 index 0000000..e8b94a0 --- /dev/null +++ b/build/jam/DistroRules @@ -0,0 +1,35 @@ +# DistroRules +# +# Rules to archive binary distributions. + +rule Distro +{ + local target = $(1) ; + + NotFile $(target) ; + Always $(target) ; +} + +actions Distro +{ + echo "== Making distro $(DISTRO_DIR) ==" ; + mimeset $(DISTRO_DIR) ; +} + +Depends fulldistro : distro ; + +rule FullDistro +{ + local target = $(1) ; + + NotFile $(target) ; + Always $(target) ; +} + +actions FullDistro +{ + echo "== Making full distro $(FULL_DISTRO_DIR) ==" ; + rm -rf $(FULL_DISTRO_DIR) ; + cp -a $(DISTRO_DIR) $(FULL_DISTRO_DIR) ; + mimeset $(FULL_DISTRO_DIR) ; +} diff --git a/build/jam/FileRules b/build/jam/FileRules new file mode 100644 index 0000000..f13ff3d --- /dev/null +++ b/build/jam/FileRules @@ -0,0 +1,31 @@ +# FileRules +# +# Rules for files and symbolic links. + +rule SymLink +{ + # SymLink : : ; + # Links to . + # is the exact link contents. No binding is done. + # If true, will be made a dependency + # of the `all' pseudo target, i.e. it will be made by default, and removed + # on `jam clean'. + + local target = $(1) ; + local source = $(2) ; + local makeDefaultDependencies = $(3) ; + if ! $(makeDefaultDependencies) { + makeDefaultDependencies = true ; + } + LINKCONTENTS on $(target) = $(source) ; + SymLink1 $(target) ; + if $(makeDefaultDependencies) = true { + LocalDepends files : $(target) ; + LocalClean clean : $(target) ; + } +} + +actions SymLink1 +{ + $(RM) "$(1)" && $(LN) -s "$(LINKCONTENTS)" "$(1)" +} diff --git a/build/jam/HelperRules b/build/jam/HelperRules new file mode 100644 index 0000000..0876ddf --- /dev/null +++ b/build/jam/HelperRules @@ -0,0 +1,52 @@ +# HelperRules +# +# Helper rules without side effects. + +rule FFilter +{ + # FFilter : ; + # + # Removes all occurrences of in . + + local list = $(1) ; + local excludes = $(2) ; + local newList ; + local item ; + for item in $(list) { + local skip ; + local exclude ; + for exclude in $(excludes) { + if $(item) = $(exclude) { + skip = true ; + } + } + if ! $(skip) { + newList += $(item) ; + } + } + return $(newList) ; +} + +rule FSplitPath +{ + # FSplitPath ; + # + # Decomposes a path into its components. + # + # : The path to be decomposed. + # + local path = $(1:G=) ; + + local components ; + # $(path:D) for "/" is "/". Therefore the second condition. + while $(path:D) && $(path:D) != $(path) + { + # Note: $(path:B) returns "." for "..", but $(path:D=) is fine. + components = $(path:D=) $(components) ; + path = $(path:D) ; + } + components = $(components) ; + # Use this to return initial / + #components = $(path) $(components) ; + return $(components) ; +} diff --git a/build/jam/InstallRules b/build/jam/InstallRules new file mode 100644 index 0000000..80be960 --- /dev/null +++ b/build/jam/InstallRules @@ -0,0 +1,27 @@ +# InstallRules +# +# Missing rules for installation. + +rule InstallSymLink +{ + # InstallSymlink linkname : source ; + + LocalDepends install : $(>) ; + LocalDepends install : $(<) ; + LocalClean uninstall : $(<) ; + + NoCare $(>) ; + InstallSymLink1 $(<) : $(>) ; +} + +actions InstallSymLink1 +{ + $(RM) $(<) && $(LN) -s $(>) $(<) +} + +rule UninstallTarget +{ + # UninstallTarget target ; + + LocalClean uninstall : $(<) ; +} diff --git a/build/jam/MainBuildRules b/build/jam/MainBuildRules new file mode 100644 index 0000000..292a2a5 --- /dev/null +++ b/build/jam/MainBuildRules @@ -0,0 +1,380 @@ +# MainBuildRules +# +# Rules that specify what to build and how to do it. + +rule AddResources +{ + # AddResources : ; + # + # Adds resources to the application. + # + # : Name of the application. + # : List of resource files. Grist will be set. + # + local resfiles ; + local file ; + for file in $(2) { + if ! $(file:G) { + file = [ FGristFiles $(file) ] ; + } + resfiles += $(file) ; + } + + SEARCH on $(resfile) += $(SEARCH_SOURCE) ; + + for file in $(resfiles) { + if $(file:S) = .rdef { + local rdef = $(file) ; + file = $(rdef:S=.rsrc) ; + ResComp $(file) : $(rdef) ; + } + RESFILES on $(1) += $(file) ; + } +} + +rule Application +{ + # Application : : : ; + # + # Creates an application from sources. + # + # : Name of the application. Grist is allowed. + # : List of source files. Grist will be set. + # : List of libraries to link against. + # : List of resource files. Grist will be set. + # + local app = $(1) ; + local sources = $(2) ; + local libs = $(3) ; + local res = $(4) ; + + AddResources $(app) : $(res) ; + Main $(app) : $(sources) ; + MakeLocate $(app) : $(LOCATE_MAIN_TARGET) ; + LinkAgainst $(app) : $(libs) ; +} + +actions Strip +{ + strip "$(1)" ; +} + +rule AddOn +{ + # AddOn : : : ; + # + # Creates an add-on from sources. + # + # : Name of the add-on. Grist is allowed. + # : List of source files. Grist will be set. + # : List of libraries to link against. + # : List of resource files. Grist will be set. + # + SharedLibrary $(1) : $(2) : $(3) : $(4) ; +} + +rule SharedLibrary +{ + # SharedLibrary : : : ; + # + # Creates a shared library from sources. + # + # : Name of the shared library. Grist is allowed. + # : List of source files. Grist will be set. + # : List of libraries to link against. + # : List of resource files. Grist will be set. + # + local lib = $(1) ; + local sources = $(2) ; + local libs = $(3) ; + local res = $(4) ; + + AddResources $(lib) : $(res) ; + Main $(lib) : $(sources) ; + MakeLocate $(lib) : $(LOCATE_MAIN_TARGET) ; + local linkFlags ; + if $(OSPLAT) = X86 { + linkFlags = -nostart -Xlinker -soname=\"$(lib)\" -Xlinker --no-undefined ; + } else { + linkFlags = -xms ; + } + LINKFLAGS on $(lib) = [ on $(lib) return $(LINKFLAGS) ] $(linkFlags) ; + LinkAgainst $(lib) : $(libs) ; +} + +rule StaticLibrary +{ + # StaticLibrary : ; + # + # Creates a static library from sources. + # + # : Name of the static library. Grist is allowed. + # : List of source files. Grist will be set. + # + local lib = $(1) ; + Library $(lib) : $(2) ; + MakeLocate $(lib) : $(LOCATE_MAIN_TARGET) ; + + # If KEEPOBJS is set, Library doesn't make the library depend on `lib'. + if $(KEEPOBJS) { + Depends lib : $(lib) ; + } +} + +rule LinkAgainst +{ + # LinkAgainst : ; + # + # Adds libraries to the list of libraries a (Main) target shall be linked + # against. + # + # : The name of the target for which to add libraries. + # : The libraries (actually arbitrary shared objects and static + # libraries) to be added. Valid elements are e.g. "be" or + # "libopenbeos.so" or "/boot/.../libfoo.so". If the basename starts + # with "lib" or the thingy has a dirname or grist, it is added to + # the NEEDLIBS variable (i.e. the file will be bound!), otherwise + # it is prefixed "-l" and added to LINKLIBS. If you want to specify + # a target that isn't a library and also has neither grist nor a + # dirname, you can prepend "" as grist; it will be + # stripped by this rule. + # + for i in $(>) + { + local isfile = ; + if $(i:D) || $(i:G) { + isfile = true ; + if $(i:G) = { + i = $(i:G=) ; + } + } else { + switch $(i:B) + { + # XXX: _APP_ and _KERNEL_ should not be needed for ELF. + case _APP_ : isfile = true ; + case _KERNEL_ : isfile = true ; + case lib* : isfile = true ; + case * : isfile = ; + } + if ! $(isfile) && ( $(i:S) = .so || $(i:S) = .o || $(i:S) = .a ) { + isfile = true ; + } + } + if $(isfile) { + NEEDLIBS on $(1) = [ on $(1) return $(NEEDLIBS) ] $(i) ; + Depends $(1) : $(i) ; + } else { + LINKLIBS on $(1) = [ on $(1) return $(LINKLIBS) ] -l$(i) ; + } + } +} + +rule XRes +{ + # XRes : ; + # + # Adds resources to a file. + # + # : The files to which resources shall be added. + # : The resource files. + # + if $(2) + { + Depends $(1) : $(2) ; + XRes1 $(1) : $(2) ; + } +} + +rule ResComp +{ + # ResComp : ; + # + # Creates a binary resource file from a rdef script. + # + # : The resource file. Grist is required. + # : The rdef script. Grist is required. + # + local defines ; + + on $(1) { + defines = $(DEFINES) ; + } + + DEFINES on $(1) = $(defines) ; + CCDEFS on $(1) = [ FDefines $(defines) ] ; + HDRS on $(1) = [ on $(1) FIncludes $(SEARCH_SOURCE) $(SUBDIRHDRS) $(HDRS) ] + $(HDRS_INCLUDES_SEPARATOR) ; + RCHDRS on $(1) = [ FRcIncludes $(SEARCH_SOURCE) $(SUBDIRHDRS) $(HDRS) ] ; + + SEARCH on $(2) += $(SEARCH_SOURCE) ; + MakeLocate $(1) : $(LOCATE_TARGET) ; + Depends $(1) : $(2) $(RC) ; + LocalClean clean : $(1) ; + ResComp1 $(1) : $(RC) $(2) ; +} + +# Note: We pipe the input files into the preprocessor, since *.rdef files are +# considered linker scripts, and thus we can use preprocessor features. +actions ResComp1 +{ + cat "$(2[2-])" | $(CC) -E $(CCDEFS) $(HDRS) - | egrep -v '^#' | $(2[1]) $(RCHDRS) --auto-names -o "$(1)" - +} + +actions XRes1 +{ + xres -o "$(1)" "$(2)" ; +} + +actions MimeSet +{ + mimeset -f "$(1)" ; +} + +rule LexC++ +{ + Depends $(1) : $(2) ; + MakeLocate $(1) : $(LOCATE_SOURCE) ; + Clean clean : $(1) ; +} + +actions LexC++ +{ + $(LEX) -i -P$(<:B) -o$(1) $(2) +} + +rule Bison +{ + local _h ; + + _h = $(1:S=.h) ; + + MakeLocate $(<) $(_h) : $(LOCATE_SOURCE) ; + + Depends $(<) : $(>) ; + BisonC++ $(<) : $(>) ; + Clean clean : $(<) $(_h) ; + + # make sure someone includes $(_h) else it will be + # a deadly independent target + + Includes $(<) : $(_h) ; +} + +actions BisonC++ +{ + $(BISON) -v -d -p $(2:B) -o $(1) $(2) +} + +rule Rez +{ + Depends $(<) : $(>) ; +} + +rule PreCompile +{ + # PreCompile : + # + # precompiles the given src (a headerfile) into the specified header. + # + local _hdr = $(1) ; + local _src = $(2) ; + MakeLocate $(_hdr) : $(LOCATE_TARGET) ; + PreComp $(_hdr) : $(_src) ; + Clean clean : $(_hdr) ; +} + +rule PreComp +{ + Depends $(<) : $(>) ; +} + +actions PreComp +{ + mwcc -precompile $(<) -lang cplus "$(>)" ; +} + +rule SubDirSysHdrs +{ + # SubDirSysHdrs ; + # + # Adds directories to the system include search paths for the current + # subdirectory. Counterpart of SubDirHdrs which adds non-system include + # search paths. + # + # : The directories to be added to the current subdir's system + # include search paths. + # + SUBDIRSYSHDRS += [ FDirName $(1) ] ; +} + +rule ObjectSysHdrs +{ + # SubDirSysHdrs : ; + # + # Adds directories to the system include search paths for the given + # sources or objects. Counterpart of ObjectHdrs which adds non-system + # include search paths. + # + # NOTE: This rule must be invoked *after* the rule that generates the + # objects. + # + # : The targets for which to add system include + # search paths. + # : The directories to be added to the given objects' system + # include search paths. + # + + local s ; + for s in [ FGristFiles $(<:S=$(SUFOBJ)) ] { + SYSHDRS on $(s) += $(>) ; + CCHDRS on $(s) = [ on $(s) FIncludes $(HDRS) ] + $(HDRS_INCLUDES_SEPARATOR) [ on $(s) FSysIncludes $(SYSHDRS) ] ; + } +} + + +# FSysIncludes ; +# +# Counterpart of FIncludes for system include search paths. +# +if $(OSPLAT) = X86 { + rule FSysIncludes { return -I$(<) ; } +} else { + rule FSysIncludes { return "-i "$(<) ; } +} + +# FRcIncludes ; +# +# Counterpart of FIncludes for *.rdef scripts. +# +rule FRcIncludes +{ + return "-I "$(<) ; +} + +# Variable referring to the STL. +if $(OSPLAT) = X86 { + if $(IS_GCC4_PLATFORM) = 1 { + TARGET_LIBSTDC++ = stdc++ ; + } else { + TARGET_LIBSTDC++ = stdc++.r4 ; + } +} else { + TARGET_LIBSTDC++ = mslcpp_4_0 ; +} + + +rule CreateSVNRevisionFile file +{ + # CreateSVNRevisionFile + + local svnEntries = entries ; + SEARCH on $(svnEntries) = [ FDirName $(TOP) .svn ] ; + Depends $(file) : $(svnEntries) ; +} + +actions CreateSVNRevisionFile +{ + (LANG=C svn info $(TOP) 2> /dev/null || echo Revision: 0) | + grep Revision | awk '{printf $2}' > $(1) +} diff --git a/build/jam/OverriddenJamRules b/build/jam/OverriddenJamRules new file mode 100644 index 0000000..7a5acbf --- /dev/null +++ b/build/jam/OverriddenJamRules @@ -0,0 +1,218 @@ +# OverriddenJamRules +# +# Jam rules that need to be overridden for good reasons. + +# Overridden to allow for spaces in file names and to support resources. +# Also set the on target LINKFLAGS variable to prevent later changes to +# the global variable from having an effect on the setting for the target. +rule Link +{ + local dbg = [ on $(1) return $(DEBUG) ] ; + if $(STRIP_APPS) && $(STRIP_APPS) != 0 && (!$(dbg) || $(dbg) = 0) { + # strip app if requested so and if not in debug mode! + Strip $(1) ; + } + # Note: RESFILES must be set before invocation. + MODE on $(1) = $(EXEMODE) ; + on $(1) XRes $(1) : $(RESFILES) ; + Chmod $(1) ; + MimeSet $(1) ; + LINKFLAGS on $(1) = [ on $(1) return $(LINKFLAGS) ] ; +} + +actions Link bind NEEDLIBS +{ + $(LINK) $(LINKFLAGS) -o "$(1)" $(UNDEFS) "$(2)" "$(NEEDLIBS)" $(LINKLIBS) +} + + +# Overridden to allow for spaces in file names. +actions Chmod1 +{ + $(CHMOD) "$(MODE)" "$(1)" +} + +# Overridden to allow for spaces in file names. +actions piecemeal together existing Clean +{ + $(RM) -rf "$(>)" +} + +# Changes to rules for sake of discrimination between system and non-system +# headers. + +if $(OSPLAT) = X86 { + if $(IS_GCC4_PLATFORM) = 1 { + HDRS_INCLUDES_SEPARATOR = -iquote- ; + } else { + HDRS_INCLUDES_SEPARATOR = -I- ; + } +} else { + HDRS_INCLUDES_SEPARATOR = -i- ; +} + +rule Cc +{ + Depends $(<) : $(>) ; + + # If the compiler's -o flag doesn't work, relocate the .o + + if $(RELOCATE) + { + CcMv $(<) : $(>) ; + } + + # Just to clarify here: this sets the per-target CCFLAGS to + # be the current value of (global) CCFLAGS and SUBDIRCCFLAGS. + # CCHDRS and CCDEFS must be reformatted each time for some + # compiles (VMS, NT) that malign multiple -D or -I flags. + + CCFLAGS on $(<) += $(CCFLAGS) $(SUBDIRCCFLAGS) $(OPTIM) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] + $(HDRS_INCLUDES_SEPARATOR) [ on $(<) FSysIncludes $(SYSHDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +rule C++ +{ + Depends $(<) : $(>) ; + + if $(RELOCATE) + { + CcMv $(<) : $(>) ; + } + + C++FLAGS on $(<) += $(C++FLAGS) $(SUBDIRC++FLAGS) $(OPTIM) ; + + CCHDRS on $(<) = [ on $(<) FIncludes $(HDRS) ] + $(HDRS_INCLUDES_SEPARATOR) [ on $(<) FSysIncludes $(SYSHDRS) ] ; + CCDEFS on $(<) = [ on $(<) FDefines $(DEFINES) ] ; +} + +rule Object +{ + # locate object and search for source, if wanted + + Clean clean : $(<) ; + + MakeLocate $(<) : $(LOCATE_TARGET) ; + SEARCH on $(>) = $(SEARCH_SOURCE) ; + + # Save HDRS for -I$(HDRS) on compile. + # We shouldn't need -I$(SEARCH_SOURCE) as cc can find headers + # in the .c file's directory, but generated .c files (from + # yacc, lex, etc) are located in $(LOCATE_TARGET), possibly + # different from $(SEARCH_SOURCE). + + HDRS on $(<) = $(SEARCH_SOURCE) $(SUBDIRHDRS) $(HDRS) ; + SYSHDRS on $(<) = $(SUBDIRSYSHDRS) $(SYSHDRS) ; + + # handle #includes for source: Jam scans for headers with + # the regexp pattern $(HDRSCAN) and then invokes $(HDRRULE) + # with the scanned file as the target and the found headers + # as the sources. HDRSEARCH is the value of SEARCH used for + # the found header files. Finally, if jam must deal with + # header files of the same name in different directories, + # they can be distinguished with HDRGRIST. + + # $(SEARCH_SOURCE:E) is where cc first looks for #include + # "foo.h" files. If the source file is in a distant directory, + # look there. Else, look in "" (the current directory). + + HDRRULE on $(>) = HdrRule ; + HDRSCAN on $(>) = $(HDRPATTERN) ; + HDRSEARCH on $(>) = + $(SEARCH_SOURCE:E) $(SUBDIRHDRS) $(HDRS) $(SYSHDRS) $(STDHDRS) ; + + HDRGRIST on $(>) = $(HDRGRIST) ; + + # propagate target specific-defines + + DEFINES on $(<) += $(DEFINES) ; + + # if source is not .c, generate .c with specific rule + + switch $(>:S) + { + case .asm : As $(<) : $(>) ; + case .c : Cc $(<) : $(>) ; + case .C : C++ $(<) : $(>) ; + case .cc : C++ $(<) : $(>) ; + case .cpp : C++ $(<) : $(>) ; + case .f : Fortran $(<) : $(>) ; + case .l : Cc $(<) : $(<:S=.c) ; + LexC++ $(<:S=.c) : $(>) ; + case .s : As $(<) : $(>) ; + case .y : Cc $(<) : $(<:S=.c) ; + Bison $(<:S=.c) : $(>) ; + case * : UserObject $(<) : $(>) ; + } +} + +rule ObjectHdrs +{ + local s ; + for s in [ FGristFiles $(<:S=$(SUFOBJ)) ] { + HDRS on $(s) += $(>) ; + CCHDRS on $(s) = [ on $(s) FIncludes $(HDRS) ] + $(HDRS_INCLUDES_SEPARATOR) [ on $(s) FSysIncludes $(SYSHDRS) ] ; + } +} + +# Override Jam 2.5rc3 MakeLocate and MkDir to deal more intelligently +# with grist set on the supplied directory name. +rule MakeLocate +{ + if $(2[1]) + { + local dir = $(2[1]) ; + if ! $(dir:G) { + dir = $(dir:G=dir) ; + } + LOCATE on $(1) = $(dir:G=) ; + Depends $(1) : $(dir) ; + MkDir $(dir) ; + } +} + +rule MkDir +{ + # If dir exists, don't update it + # Do this even for $(DOT). + + local dir = $(<) ; + if ! $(dir:G) { + dir = $(dir:G=dir) ; + } + + NoUpdate $(dir) ; + + if $(dir:G=) != $(DOT) && ! $($(dir:G=)-mkdir) { + local s ; + + # Cheesy gate to prevent multiple invocations on same dir + # MkDir1 has the actions + # Arrange for jam dirs + + $(dir:G=)-mkdir = true ; + MkDir1 $(dir) ; + Depends dirs : $(dir) ; + + # Recursively make parent directories. + # $(dir:P) = $(dir)'s parent, & we recurse until root + + s = $(dir:P) ; # parent keeps grist + + if $(s:G=) && $(s) != $(dir) { + Depends $(dir) : $(s) ; + MkDir $(s) ; + } else if $(s) { + NotFile $(s) ; + } + } +} + +# Add SUBDIRSYSHDRS to the variables that shall be reset automatically by the +# SubDir rule. +SUBDIRRESET += SYSHDRS ; diff --git a/build/jam/PackageRules b/build/jam/PackageRules new file mode 100644 index 0000000..7bf932c --- /dev/null +++ b/build/jam/PackageRules @@ -0,0 +1,247 @@ +# PackageRules +# +# Rules to create archives for binary distribution + +rule Copy +{ + if $(2) { + SEARCH on $(2) += $(SEARCH_SOURCE) ; + Depends $(1) : $(COPYATTR) $(2) ; + Copy1 $(1) : $(COPYATTR) $(2) ; + } +} + +actions Copy1 +{ + "$(2[1])" -d "$(2[2-])" "$(1)" +} + +rule Packages +{ + local packagenames = $(1) ; + local packagefiles = $(2) ; + local path = $(3) ; + for name in $(packagenames) { + Package $(name) : $(packagefiles) : $(path) ; + } +} + +rule FPackageConfigSubPath +{ + # FPackageConfigSubPath + # + local packagename = $(1) ; + + local configSubPath ; + on $(packagename) { + configSubPath = $(OS:L) $(OSPLAT:L) ; + + if $(DEBUG) = 0 { + configSubPath += release ; + } else { + configSubPath += debug_$(DEBUG) ; + } + } + + return $(configSubPath) ; +} + +rule Package +{ + local packagename = $(1) ; + local packagefiles = $(2) ; + local path = $(3) ; + + local configSubPath = [ FPackageConfigSubPath $(packagename) ] ; + #local packagezip = $(packagename:S=.zip:G=_packages) ; + local packagezip = $(packagename:S=-$(VERSION).zip:G=_packages) ; + local targetDir = [ FDirName $(PACKAGE_DIR) $(configSubPath) ] ; + local packagedir = [ FDirName $(targetDir) $(packagename) ] ; + + local installscript = install.sh ; + local packageinstallscript = $(installscript:G=_packages!$(packagename)) ; + local installzip = install.zip ; + local packageinstallzip = $(installzip:G=_packages!$(packagename)) ; + + local packageobjectdir = [ FDirName $(PACKAGE_OBJECT_DIR) + $(configSubPath) $(packagename) ] ; + local packagefiledir = [ FDirName $(packageobjectdir) $(path) ] ; + local packagefileinstallzip + = $(installzip:G=_package_objects!$(packagename)) ; + + # add the files to the install.zip + local packagefilegrist = [ FGrist _package_files $(packagename) $(path) ] ; + for file in $(packagefiles) { + if $(path[0]) = "boot" { + local packagefile = $(file:G=$(packagefilegrist)) ; + MakeLocate $(packagefile) : $(packagefiledir) ; + Copy $(packagefile) : $(file) ; + Clean cleanPackages : $(packagefile) ; + PackageInstallZip $(packagefileinstallzip) : $(packagefile) ; + } else { + local packagefile = $(file:G=_packages!$(packagename)) ; + MakeLocate $(packagefile) : $(packagedir) ; + Copy $(packagefile) : [ FGristFiles $(file) ] ; + Clean cleanPackages : $(packagefile) ; + Depends $(packagezip) : $(packagefile) ; + } + } + + # general setup for this package -- only on first invocation + if ! $(_setup_$(packagename)) { + _setup_$(packagename) = true ; + + NotFile $(packagename) ; + LocalDepends packages : $(packagename) ; + + MakeLocate $(packagezip) : $(targetDir) ; + MakeLocate $(packageinstallscript) : $(packagedir) ; + MakeLocate $(packageinstallzip) : $(packagedir) ; + MakeLocate $(packagefileinstallzip) : $(packageobjectdir) ; + + PackageInstallScript $(packageinstallscript) : $(packagedir) ; + LinkInstallZip $(packageinstallzip) : $(packagefileinstallzip) ; + Depends $(packagename) : $(packagezip) ; + PackageZip $(packagezip) : $(packagedir) + : $(packageinstallscript) $(packageinstallzip) ; + } + +} + +rule PackageSymLink +{ + # PackageSymLink : + # : + # + local packagename = $(1) ; + local symlinkPath = $(2) ; + local symlinkTarget = $(3) ; + + local configSubPath = [ FPackageConfigSubPath $(packagename) ] ; + + local symlinkDir = [ FReverse $(symlinkPath) ] ; + local symlink = $(symlinkDir[1]) ; + symlinkDir = [ FReverse $(symlinkDir[2-]) ] ; + local symlinkGrist = [ FGrist _package $(packagename) $(symlinkDir) ] ; + symlink = $(symlink:G=$(symlinkGrist)) ; + + if $(symlinkDir[1]) = boot { + local installzip = install.zip ; + local packagefileinstallzip + = $(installzip:G=_package_objects!$(packagename)) ; + + local packageobjectdir = [ FDirName $(PACKAGE_OBJECT_DIR) + $(configSubPath) $(packagename) ] ; + symlinkDir = [ FDirName $(packageobjectdir) $(symlinkDir) ] ; + + PackageInstallZip $(packagefileinstallzip) : $(symlink) ; + + } else { + #local packagezip = $(packagename:S=.zip:G=_packages) ; + local packagezip = $(packagename:S=-$(VERSION).zip:G=_packages) ; + + local packagedir = [ FDirName $(PACKAGE_DIR) $(configSubPath) + $(packagename) ] ; + symlinkDir = [ FDirName $(packagedir) $(symlinkDir) ] ; + + Depends $(packagezip) : $(symlink) ; + } + + MakeLocate $(symlink) : $(symlinkDir) ; + SymLink $(symlink) : $(symlinkTarget) : false ; + Clean cleanPackages : $(symlink) ; +} + +rule PackageDriverSymLink +{ + # PackageDriverSymLink : ; + # : Package name. + # : Path components relative to the + # /boot/home/config/add-ons/kernel/drivers/dev directory, e.g. + # "graphics mga.driver" (no quotation, of course). + # + local packageName = $(1) ; + local symlinkComponents = $(2) ; + + # construct the symlink contents + local symlinkPath = [ FReverse $(symlinkComponents) ] ; + symlinkPath = bin $(symlinkPath[1]) ; + + for i in $(symlinkComponents) { + symlinkPath = $(DOTDOT) $(symlinkPath) ; + } + + PackageSymLink $(packageName) + : boot home config add-ons kernel drivers dev $(symlinkComponents) + : [ FDirName $(symlinkPath) ] ; +} + +rule PackageZip +{ + local dir = $(2:G=dir) ; + Depends $(1) : $(dir) $(3) ; + Clean cleanPackages : $(1) ; + PackageZip1 $(1) : $(dir) ; +} + +actions together PackageZip1 { + cd "$(2:P)" ; + zip -rq "$(1:BS)" "$(2:BS)" ; +} + +rule PackageInstallScript +{ + MakeLocate $(1) : $(2) ; + Clean cleanPackages : $(1) ; + PackageInstallScript1 $(1) : $(2:G=dir) ; +} + +actions together PackageInstallScript1 +{ +echo '#!/bin/sh +base=`dirname "$0"` +cd "$base" +if [ -n "$TTY" ] +then + unzip -d / install.zip +else + response=`alert "Would you like to automatically overwrite existing files, or receive a prompt?" "Overwrite" "Prompt"` + if [ $response == "Overwrite" ] + then + unzip -od / install.zip + alert "Finished installing" "Thanks" + else + if [ -e /boot/beos/apps/Terminal ] + then + terminal=/boot/beos/apps/Terminal + else + terminal=`query Terminal | head -1` + fi + $terminal -t "installer" /bin/sh "$0" + fi +fi' > "$(1)" ; + chmod 755 "$(1)" ; +} + +rule PackageInstallZip +{ + Depends $(1) : $(2) ; + Clean cleanPackages : $(1) ; +} + +actions together PackageInstallZip +{ + cd "$(1:P)" ; + zip -rqy "$(1:BS)" boot ; +} + +rule LinkInstallZip +{ + Depends $(1) : $(2) ; + Clean cleanPackages : $(1) ; +} + +actions together LinkInstallZip +{ + ln -sf "`pwd`/$(2)" "$(1)" ; +} diff --git a/build/jam/UserBuildConfig.sample b/build/jam/UserBuildConfig.sample new file mode 100644 index 0000000..4d9280b --- /dev/null +++ b/build/jam/UserBuildConfig.sample @@ -0,0 +1,63 @@ +# UserBuildConfig +# +# Sample of a UserBuildConfig file. It is a central place where the user can +# set variables affecting certain aspects of the build, such as debug, +# warnings and optimization settings. +# +# The following variables can be set: +# +# BUILD_TESTS - If not empty, all tests (TestBeam) will be build, too. +# Default is 0, i.e. no tests. +# CCFLAGS, C++FLAGS - Flags passed to the C/C++ compiler. +# DEBUG - If not empty, will turn on debugging, i.e. will +# add respective C/C++ compiler and linker flags and +# the CPP DEBUG macro. +# DEFINES - CPP macros to be defined, e.g. something like +# `SPECIAL_FEATURE' or `CACHE_SIZE=1024'. +# HDRS - List of directories to be added to the local include +# search paths. +# LINKFLAGS - Flags passed to the linker. +# LOCATE_MAIN_TARGET - Directory where the main targets (i.e. applications, +# libraries shall be placed). Should usually not be +# tampered with by the user. +# OPTIM - Optimization specific flags passed to the C/C++ +# compiler. Usually you want to use OPTIMIZE instead. +# OPTIMIZE - If not set to `0', will turn on optimization, i.e. +# will set the respective C/C++ compiler flags +# (i.e. the OPTIM variable). +# STRIP_APPS - if not set to '0', will cause all generated apps to +# be stripped. Default is '0', i.e. no stripping +# SYSHDRS - List of directories to be added to the system include +# search paths. +# WARNINGS - If not set to `0', will turn on warnings, i.e. will +# set the respective C/C++ compiler flags. + +# Examples: + +# Globally turn off debugging: +# +# DEBUG = 0 ; + +# Globally activate debugging: +# +# DEBUG = 1 ; + +# ... e.g. like this, for the `add-ons/catalogs' directory and all its +# subdirectories. +# +# SetConfigVar WARNINGS : TOP add-ons catalogs : 1 ; + +# Turn on debugging for the the directory `Languages' and all its subdirectories. +# +# SetConfigVar DEBUG : TOP Languages : 1 ; + +# Turn off optimization for the `rez' directory and all its subdirectories. +# +# SetConfigVar OPTIMIZE : TOP rez : 0 ; + +# Define the CPP macro INSANE_DEBUGGING_LEVEL to the value `100' in the +# `lpe' directory, but NOT in its subdirectories (note the use of the +# optional fourth parameter `local', which works for both SetConfigVar and +# AppendToConfigVar). +# +# AppendToConfigVar DEFINES : TOP lpe : INSANE_DEBUGGING_LEVEL=100 : local ; diff --git a/configure b/configure new file mode 100755 index 0000000..6879cee --- /dev/null +++ b/configure @@ -0,0 +1,145 @@ +#!/bin/sh +# +# Copyright 2009, Pier Luigi Fiorini. +# Distributed under the terms of the MIT License. +# +# Authors: +# Pier Luigi Fiorini, pierluigi.fiorini@gmail.com +# + +current_dir=`pwd` +defines="" + +# Binaries +jambin=`which jam` +rcbin=`which rc` +xresbin=`which xres` +settypebin=`which settype` +mimesetbin=`which mimeset` +setversionbin=`which setversion` +copyattrbin=`which copyattr` + +# Check operating system +platform=`uname -s` +release=`uname -r` +echo -n "Checking operating system... " +case "$platform" in + BeOS) + case "$release" in + 4.*) + echo "*** BeOS R4 is not supported!" + exit 1 + ;; + 5.*) + echo "*** BeOS R5 is not supported!" + exit 1 + ;; + 6.*) + echo "*** Zeta is not supported!" + exit 1 + ;; + *) + echo "*** Unsupported BeOS platform!" + exit 1 + ;; + esac + ;; + Haiku) + defines="HAIKU_TARGET_PLATFORM_HAIKU=1" + ;; + *) + echo "*** Unsupported $platform operating system!" + exit 1 + ;; +esac +echo "$platform $release" + +# Check whether jam exists +echo -n "Checking whether jam exists... " +if [ -z "$jambin" ]; then + echo "not found" + echo "*** Caya requires jam, please read our Build.txt file." + exit 1 +else + echo "found" +fi + +# Check for rc +echo -n "Checking for rc... " +if [ -z "$rcbin" ]; then + echo "not found" + exit 1 +fi +echo $rcbin + +# Check for xres +echo -n "Checking for xres..." +if [ -z "$xresbin" ]; then + echo "not found" + exit 1 +fi +echo $xresbin + +# Check for settype +echo -n "Checking for settype..." +if [ -z "$settypebin" ]; then + echo "not found" + exit 1 +fi +echo $settypebin + +# Check for mimeset +echo -n "Checking for mimeset..." +if [ -z "$mimesetbin" ]; then + echo "not found" + exit 1 +fi +echo $mimesetbin + +# Check for setverion +echo -n "Checking for setversion..." +if [ -z "$setversionbin" ]; then + echo "not found" + exit 1 +fi +echo $setversionbin + +# Check for copyattr +echo -n "Checking for copyattr..." +if [ -z "$copyattrbin" ]; then + echo "not found" + exit 1 +fi +echo $copyattrbin + +# Create the build configuration +mkdir -p $current_dir/generated +cat > $current_dir/generated/BuildConfig << EOF +RC = ${rcbin} ; +XRES = ${xresbin} ; +SETTYPE = ${settypebin} ; +MIMESET = ${mimesetbin} ; +SETVERSION = ${setversionbin} ; +COPYATTR = ${copyattrbin} ; + +COMMON_DIRECTORY = $(finddir B_COMMON_DIRECTORY) ; +COMMON_BIN_DIRECTORY = $(finddir B_COMMON_BIN_DIRECTORY) ; +COMMON_INCLUDE_DIRECTORY = $(finddir B_COMMON_DIRECTORY)/include ; +COMMON_LIB_DIRECTORY = $(finddir B_COMMON_LIB_DIRECTORY) ; +COMMON_SERVERS_DIRECTORY = $(finddir B_COMMON_SERVERS_DIRECTORY) ; +COMMON_ADDONS_DIRECTORY = $(finddir B_COMMON_ADDONS_DIRECTORY) ; +COMMON_DEVELOP_DIRECTORY = $(finddir B_COMMON_DEVELOP_DIRECTORY) ; +USER_CONFIG_DIRECTORY = $(finddir B_USER_CONFIG_DIRECTORY) ; +USER_INCLUDE_DIRECTORY = $(finddir B_USER_CONFIG_DIRECTORY)/include ; +SYSTEM_DIRECTORY = $(finddir B_SYSTEM_DIRECTORY) ; +SYSTEM_LIB_DIRECTORY = $(finddir B_SYSTEM_LIB_DIRECTORY) ; +BEOS_PREFERENCES_DIRECTORY = $(finddir B_BEOS_PREFERENCES_DIRECTORY) ; +PREFERENCES_DIRECTORY = $(finddir B_PREFERENCES_DIRECTORY) ; +USER_PREFERENCES_DIRECTORY = $(finddir B_USER_CONFIG_DIRECTORY)/be/Preferences ; +APPS_DIRECTORY = $(finddir B_APPS_DIRECTORY) ; +CAYA_DIRECTORY = $(finddir B_APPS_DIRECTORY)/Caya ; + +DEFINES += ${defines} ; +EOF + +echo "Configuration done." diff --git a/libs/Jamfile b/libs/Jamfile new file mode 100644 index 0000000..18a17d0 --- /dev/null +++ b/libs/Jamfile @@ -0,0 +1,4 @@ +SubDir TOP libs ; + +# Include all the components. +SubInclude TOP libs libgloox ; diff --git a/libs/libgloox/Jamfile b/libs/libgloox/Jamfile new file mode 100644 index 0000000..2e99205 --- /dev/null +++ b/libs/libgloox/Jamfile @@ -0,0 +1,106 @@ +SubDir TOP libs libgloox ; + +SubDirSysHdrs [ FDirName $(TOP) ] ; +SubDirSysHdrs [ FDirName $(TOP) libs ] ; +SubDirSysHdrs [ FDirName $(OPENSSL_INCLUDE_DIR) ] ; + +SEARCH_SOURCE += [ FDirName $(TOP) libs ] ; + +StaticLibrary libgloox.a : + adhoc.cpp + amp.cpp + annotations.cpp + attention.cpp + base64.cpp + bookmarkstorage.cpp + capabilities.cpp + chatstate.cpp + chatstatefilter.cpp + client.cpp + clientbase.cpp + component.cpp + compressiondefault.cpp + compressionzlib.cpp + connectionbosh.cpp + connectionhttpproxy.cpp + connectionsocks5proxy.cpp + connectiontcpbase.cpp + connectiontcpclient.cpp + connectiontcpserver.cpp + connectiontls.cpp + connectiontlsserver.cpp + dataform.cpp + dataformfield.cpp + dataformfieldcontainer.cpp + dataformitem.cpp + dataformreported.cpp + delayeddelivery.cpp + disco.cpp + dns.cpp + error.cpp + eventdispatcher.cpp + featureneg.cpp + flexoff.cpp + gloox.cpp + gpgencrypted.cpp + gpgsigned.cpp + inbandbytestream.cpp + instantmucroom.cpp + iq.cpp + jid.cpp + lastactivity.cpp + logsink.cpp + md5.cpp + message.cpp + messageevent.cpp + messageeventfilter.cpp + messagefilter.cpp + messagesession.cpp + mucmessagesession.cpp + mucroom.cpp + mutex.cpp + nickname.cpp + nonsaslauth.cpp + oob.cpp + parser.cpp + prep.cpp + presence.cpp + privacyitem.cpp + privacymanager.cpp + privatexml.cpp + pubsubevent.cpp + pubsubitem.cpp + pubsubmanager.cpp + receipt.cpp + registration.cpp + rosteritem.cpp + rostermanager.cpp + search.cpp + sha.cpp + shim.cpp + simanager.cpp + siprofileft.cpp + socks5bytestream.cpp + socks5bytestreammanager.cpp + socks5bytestreamserver.cpp + softwareversion.cpp + stanza.cpp + stanzaextensionfactory.cpp + subscription.cpp + tag.cpp + tlsdefault.cpp + tlsgnutlsbase.cpp + tlsgnutlsclient.cpp + tlsgnutlsclientanon.cpp + tlsgnutlsserveranon.cpp + tlsopensslbase.cpp + tlsopensslclient.cpp + tlsopensslserver.cpp + tlsschannel.cpp + uniquemucroom.cpp + util.cpp + vcard.cpp + vcardmanager.cpp + vcardupdate.cpp + xhtmlim.cpp +; diff --git a/libs/libgloox/adhoc.cpp b/libs/libgloox/adhoc.cpp new file mode 100644 index 0000000..629ed37 --- /dev/null +++ b/libs/libgloox/adhoc.cpp @@ -0,0 +1,472 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "adhoc.h" +#include "adhochandler.h" +#include "adhoccommandprovider.h" +#include "disco.h" +#include "error.h" +#include "discohandler.h" +#include "clientbase.h" +#include "dataform.h" +#include "util.h" + +namespace gloox +{ + + static const char* cmdActionStringValues[] = + { + "execute", "cancel", "prev", "next", "complete" + }; + + static inline const std::string actionString( Adhoc::Command::Action action ) + { + return util::lookup2( action, cmdActionStringValues ); + } + + static const char* cmdStatusStringValues[] = + { + "executing", "completed", "canceled" + }; + + static inline const std::string statusString( Adhoc::Command::Status status ) + { + return util::lookup( status, cmdStatusStringValues ); + } + + static const char* cmdNoteStringValues[] = + { + "info", "warn", "error" + }; + + static inline const std::string noteString( Adhoc::Command::Note::Severity sev ) + { + return util::lookup( sev, cmdNoteStringValues ); + } + + // ---- Adhoc::Command::Note ---- + Adhoc::Command::Note::Note( const Tag* tag ) + : m_severity( InvalidSeverity ) + { + if( !tag || tag->name() != "note" ) + return; + + m_severity = (Severity)util::deflookup( tag->findAttribute( "type" ), cmdNoteStringValues, Info ); + m_note = tag->cdata(); + } + + Tag* Adhoc::Command::Note::tag() const + { + if( m_note.empty() || m_severity == InvalidSeverity ) + return 0; + + Tag* n = new Tag( "note", m_note ); + n->addAttribute( TYPE, noteString( m_severity ) ); + return n; + } + // ---- ~Adhoc::Command::Note ---- + + // ---- Adhoc::Command ---- + Adhoc::Command::Command( const std::string& node, Adhoc::Command::Action action, + DataForm* form ) + : StanzaExtension( ExtAdhocCommand ), m_node( node ), m_form( form ), m_action( action ), + m_status( InvalidStatus ), m_actions( 0 ) + { + } + + Adhoc::Command::Command( const std::string& node, const std::string& sessionid, Status status, + DataForm* form ) + : StanzaExtension( ExtAdhocCommand ), m_node( node ), m_sessionid( sessionid ), + m_form( form ), m_action( InvalidAction ), m_status( status ), m_actions( 0 ) + { + } + + Adhoc::Command::Command( const std::string& node, const std::string& sessionid, + Adhoc::Command::Action action, + DataForm* form ) + : StanzaExtension( ExtAdhocCommand ), m_node( node ), m_sessionid( sessionid ), + m_form( form ), m_action( action ), m_actions( 0 ) + { + } + + Adhoc::Command::Command( const std::string& node, const std::string& sessionid, Status status, + Action executeAction, int allowedActions, + DataForm* form ) + : StanzaExtension( ExtAdhocCommand ), m_node( node ), m_sessionid( sessionid ), + m_form( form ), m_action( executeAction ), m_status( status ), m_actions( allowedActions ) + { + } + + Adhoc::Command::Command( const Tag* tag ) + : StanzaExtension( ExtAdhocCommand ), m_form( 0 ), m_actions( 0 ) + { + if( !tag || tag->name() != "command" || tag->xmlns() != XMLNS_ADHOC_COMMANDS ) + return; + + m_node = tag->findAttribute( "node" ); + m_sessionid = tag->findAttribute( "sessionid" ); + m_status = (Status)util::lookup( tag->findAttribute( "status" ), cmdStatusStringValues ); + + Tag* a = tag->findChild( "actions" ); + if( a ) + { + // Multi-stage response + m_action = (Action)util::deflookup2( a->findAttribute( "action" ), cmdActionStringValues, Complete ); + if( a->hasChild( "prev" ) ) + m_actions |= Previous; + if( a->hasChild( "next" ) ) + m_actions |= Next; + if( a->hasChild( "complete" ) ) + m_actions |= Complete; + } + else + { + m_action = (Action)util::deflookup2( tag->findAttribute( "action" ), cmdActionStringValues, Execute ); + } + + const ConstTagList& l = tag->findTagList( "/command/note" ); + ConstTagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + m_notes.push_back( new Note( (*it) ) ); + + Tag* x = tag->findChild( "x", "xmlns", XMLNS_X_DATA ); + if( x ) + m_form = new DataForm( x ); + } + + Adhoc::Command::~Command() + { + util::clearList( m_notes ); + delete m_form; + } + + const std::string& Adhoc::Command::filterString() const + { + static const std::string filter = "/iq/command[@xmlns='" + XMLNS_ADHOC_COMMANDS + "']"; + return filter; + } + + Tag* Adhoc::Command::tag() const + { + if( m_node.empty() ) + return 0; + + Tag* c = new Tag( "command" ); + c->setXmlns( XMLNS_ADHOC_COMMANDS ); + c->addAttribute( "node", m_node ); + if( m_actions != 0 ) + { + // Multi-stage command response + + if( m_status != InvalidStatus ) + c->addAttribute( "status", statusString( m_status ) ); + else + c->addAttribute( "status", statusString( Executing ) ); + + Tag* actions = new Tag( c, "actions" ); + + if( m_action != InvalidAction ) + c->addAttribute( "execute", actionString( m_action ) ); + else + c->addAttribute( "execute", actionString( Complete ) ); + + if( ( m_actions & Previous ) == Previous ) + new Tag( actions, "prev" ); + if( ( m_actions & Next ) == Next ) + new Tag( actions, "next" ); + if( ( m_actions & Complete ) == Complete ) + new Tag( actions, "complete" ); + } + else + { + // Single-stage command request/response or Multi-stage command request + + if( m_action != InvalidAction ) + c->addAttribute( "action", actionString( m_action ) ); + if( m_status != InvalidStatus ) + c->addAttribute( "status", statusString( m_status ) ); + } + + if ( !m_sessionid.empty() ) + c->addAttribute( "sessionid", m_sessionid ); + + if( m_form && *m_form ) + c->addChild( m_form->tag() ); + + NoteList::const_iterator it = m_notes.begin(); + for( ; it != m_notes.end(); ++it ) + c->addChild( (*it)->tag() ); + + return c; + } + // ---- ~Adhoc::Command ---- + + // ---- Adhoc ---- + Adhoc::Adhoc( ClientBase* parent ) + : m_parent( parent ) + { + if( !m_parent || !m_parent->disco() ) + return; + + m_parent->disco()->addFeature( XMLNS_ADHOC_COMMANDS ); + m_parent->disco()->registerNodeHandler( this, XMLNS_ADHOC_COMMANDS ); + m_parent->disco()->registerNodeHandler( this, EmptyString ); + m_parent->registerIqHandler( this, ExtAdhocCommand ); + m_parent->registerStanzaExtension( new Adhoc::Command() ); + } + + Adhoc::~Adhoc() + { + if( !m_parent || !m_parent->disco() ) + return; + + m_parent->disco()->removeFeature( XMLNS_ADHOC_COMMANDS ); + m_parent->disco()->removeNodeHandler( this, XMLNS_ADHOC_COMMANDS ); + m_parent->disco()->removeNodeHandler( this, EmptyString ); + m_parent->removeIqHandler( this, ExtAdhocCommand ); + m_parent->removeIDHandler( this ); + m_parent->removeStanzaExtension( ExtAdhocCommand ); + } + + StringList Adhoc::handleDiscoNodeFeatures( const JID& /*from*/, const std::string& /*node*/ ) + { + StringList features; + features.push_back( XMLNS_ADHOC_COMMANDS ); + return features; +// return StringList( 1, XMLNS_ADHOC_COMMANDS ); + } + + Disco::ItemList Adhoc::handleDiscoNodeItems( const JID& from, const JID& /*to*/, const std::string& node ) + { + Disco::ItemList l; + if( node.empty() ) + { + l.push_back( new Disco::Item( m_parent->jid(), XMLNS_ADHOC_COMMANDS, "Ad-Hoc Commands" ) ); + } + else if( node == XMLNS_ADHOC_COMMANDS ) + { + StringMap::const_iterator it = m_items.begin(); + for( ; it != m_items.end(); ++it ) + { + AdhocCommandProviderMap::const_iterator itp = m_adhocCommandProviders.find( (*it).first ); + if( itp != m_adhocCommandProviders.end() + && (*itp).second + && (*itp).second->handleAdhocAccessRequest( from, (*it).first ) ) + { + l.push_back( new Disco::Item( m_parent->jid(), (*it).first, (*it).second ) ); + } + } + } + return l; + } + + Disco::IdentityList Adhoc::handleDiscoNodeIdentities( const JID& /*from*/, const std::string& node ) + { + Disco::IdentityList l; + StringMap::const_iterator it = m_items.find( node ); + l.push_back( new Disco::Identity( "automation", + node == XMLNS_ADHOC_COMMANDS ? "command-list" : "command-node", + it == m_items.end() ? "Ad-Hoc Commands" : (*it).second ) ); + return l; + } + + bool Adhoc::handleIq( const IQ& iq ) + { + if( iq.subtype() != IQ::Set ) + return false; + + const Adhoc::Command* ac = iq.findExtension( ExtAdhocCommand ); + if( !ac || ac->node().empty()) + return false; + + AdhocCommandProviderMap::const_iterator it = m_adhocCommandProviders.find( ac->node() ); + if( it != m_adhocCommandProviders.end() ) + { + const std::string& sess = ac->sessionID().empty() ? m_parent->getID() : ac->sessionID(); + m_activeSessions[sess] = iq.id(); + (*it).second->handleAdhocCommand( iq.from(), *ac, sess ); + return true; + } + + return false; + } + + void Adhoc::handleIqID( const IQ& iq, int context ) + { + if( context != ExecuteAdhocCommand ) + return; + + AdhocTrackMap::iterator it = m_adhocTrackMap.find( iq.id() ); + if( it == m_adhocTrackMap.end() || (*it).second.context != context + || (*it).second.remote != iq.from() ) + return; + + switch( iq.subtype() ) + { + case IQ::Error: + (*it).second.ah->handleAdhocError( iq.from(), iq.error() ); + break; + case IQ::Result: + { + const Adhoc::Command* ac = iq.findExtension( ExtAdhocCommand ); + if( ac ) + (*it).second.ah->handleAdhocExecutionResult( iq.from(), *ac ); + break; + } + default: + break; + } + m_adhocTrackMap.erase( it ); + } + + void Adhoc::registerAdhocCommandProvider( AdhocCommandProvider* acp, const std::string& command, + const std::string& name ) + { + if( !m_parent || !m_parent->disco() ) + return; + + m_parent->disco()->registerNodeHandler( this, command ); + m_adhocCommandProviders[command] = acp; + m_items[command] = name; + } + + void Adhoc::handleDiscoInfo( const JID& from, const Disco::Info& info, int context ) + { + if( context != CheckAdhocSupport ) + return; + + AdhocTrackMap::iterator it = m_adhocTrackMap.begin(); + for( ; it != m_adhocTrackMap.end() && (*it).second.context != context + && (*it).second.remote != from; ++it ) + ; + if( it == m_adhocTrackMap.end() ) + return; + + (*it).second.ah->handleAdhocSupport( from, info.hasFeature( XMLNS_ADHOC_COMMANDS ) ); + m_adhocTrackMap.erase( it ); + } + + void Adhoc::handleDiscoItems( const JID& from, const Disco::Items& items, int context ) + { + if( context != FetchAdhocCommands ) + return; + + AdhocTrackMap::iterator it = m_adhocTrackMap.begin(); + for( ; it != m_adhocTrackMap.end(); ++it ) + { + if( (*it).second.context == context && (*it).second.remote == from ) + { + StringMap commands; + const Disco::ItemList& l = items.items(); + Disco::ItemList::const_iterator it2 = l.begin(); + for( ; it2 != l.end(); ++it2 ) + { + commands[(*it2)->node()] = (*it2)->name(); + } + (*it).second.ah->handleAdhocCommands( from, commands ); + + m_adhocTrackMap.erase( it ); + break; + } + } + } + + void Adhoc::handleDiscoError( const JID& from, const Error* error, int context ) + { + AdhocTrackMap::iterator it = m_adhocTrackMap.begin(); + for( ; it != m_adhocTrackMap.end(); ++it ) + { + if( (*it).second.context == context && (*it).second.remote == from ) + { + (*it).second.ah->handleAdhocError( from, error ); + + m_adhocTrackMap.erase( it ); + } + } + } + + void Adhoc::checkSupport( const JID& remote, AdhocHandler* ah ) + { + if( !remote || !ah || !m_parent || !m_parent->disco() ) + return; + + TrackStruct track; + track.remote = remote; + track.context = CheckAdhocSupport; + track.ah = ah; + const std::string& id = m_parent->getID(); + m_adhocTrackMap[id] = track; + m_parent->disco()->getDiscoInfo( remote, EmptyString, this, CheckAdhocSupport, id ); + } + + void Adhoc::getCommands( const JID& remote, AdhocHandler* ah ) + { + if( !remote || !ah || !m_parent || !m_parent->disco() ) + return; + + TrackStruct track; + track.remote = remote; + track.context = FetchAdhocCommands; + track.ah = ah; + const std::string& id = m_parent->getID(); + m_adhocTrackMap[id] = track; + m_parent->disco()->getDiscoItems( remote, XMLNS_ADHOC_COMMANDS, this, FetchAdhocCommands, id ); + } + + void Adhoc::execute( const JID& remote, const Adhoc::Command* command, AdhocHandler* ah ) + { + if( !remote || !command || !m_parent || !ah ) + return; + + const std::string& id = m_parent->getID(); + IQ iq( IQ::Set, remote, id ); + iq.addExtension( command ); + + TrackStruct track; + track.remote = remote; + track.context = ExecuteAdhocCommand; + track.session = command->sessionID(); + track.ah = ah; + m_adhocTrackMap[id] = track; + + m_parent->send( iq, this, ExecuteAdhocCommand ); + } + + void Adhoc::respond( const JID& remote, const Adhoc::Command* command, const Error* error ) + { + if( !remote || !command || !m_parent ) + return; + + StringMap::iterator it = m_activeSessions.find( command->sessionID() ); + if( it == m_activeSessions.end() ) + return; + + IQ re( error ? IQ::Error : IQ::Result, remote, (*it).second ); + re.addExtension( command ); + if( error ) + re.addExtension( error ); + m_parent->send( re ); + m_activeSessions.erase( it ); + } + + void Adhoc::removeAdhocCommandProvider( const std::string& command ) + { + if( !m_parent || !m_parent->disco() ) + return; + + m_parent->disco()->removeNodeHandler( this, command ); + m_adhocCommandProviders.erase( command ); + m_items.erase( command ); + } + +} diff --git a/libs/libgloox/adhoc.h b/libs/libgloox/adhoc.h new file mode 100644 index 0000000..849fbf8 --- /dev/null +++ b/libs/libgloox/adhoc.h @@ -0,0 +1,487 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef ADHOC_H__ +#define ADHOC_H__ + +#include "dataform.h" +#include "disco.h" +#include "disconodehandler.h" +#include "discohandler.h" +#include "iqhandler.h" +#include "stanzaextension.h" + +#include +#include +#include + +namespace gloox +{ + + class ClientBase; + class Stanza; + class AdhocHandler; + class AdhocCommandProvider; + + /** + * @brief This class implements a provider for XEP-0050 (Ad-hoc Commands). + * + * The current, not complete, implementation is probably best suited for fire-and-forget + * type of commands. Any additional feature, like multiple stages, etc., would have to be + * added separately. + * + * To offer commands to remote entities, use this class as follows:
+ * Create a class that will handle command execution requests and derive it from + * AdhocCommandProvider. Instantiate an Adhoc object and register your + * AdhocCommandProvider-derived object with the Adhoc object using + * registerAdhocCommandProvider(). The additional parameters to that method are the internal + * name of the command as used in the code, and the public name of the command as it + * will be shown to an end user: + * @code + * MyClass::someFunc() + * { + * Adhoc* m_adhoc = new Adhoc( m_client ); + * + * // this might be a bot monitoring a weather station, for example + * m_adhoc->registerAdhocCommandProvider( this, "getTemp", "Retrieve current temperature" ); + * m_adhoc->registerAdhocCommandProvider( this, "getPressure", "Retrieve current air pressure" ); + * [...] + * } + * @endcode + * In this example, MyClass is AdhocCommandProvider-derived so it is obviously the command handler, too. + * + * And that's about it you can do with the Adhoc class. Of course you can have a AdhocCommandProvider + * handle more than one command, just register it with the Adhoc object for every desired command, + * like shown above. + * + * What the Adhoc object does when you install a new command is tell the supplied Disco object + * to advertise these commands to clients using the 'Service Discovery' protocol to learn about + * this implementation's features. These clients can then call and execute the command. Of course you + * are free to implement access restrictions to not let anyone mess with your bot, for example. + * However, the commands offered using Service Discovery are publically visible in any case. + * + * To execute commands offered by a remote entity:
+ * ...TBC... + * + * XEP version: 1.2 + * @author Jakob Schroeter + */ + class GLOOX_API Adhoc : public DiscoNodeHandler, public DiscoHandler, public IqHandler + { + public: + /** + * @brief An abstraction of an Adhoc Command element (from Adhoc Commands, XEP-0050) + * as a StanzaExtension. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API Command : public StanzaExtension + { + friend class Adhoc; + + public: + + /** + * Specifies the action to undertake with the given command. + */ + enum Action + { + Execute = 1, /**< The command should be executed or continue to be executed. + * This is the default value. */ + Cancel = 2, /**< The command should be canceled. */ + Previous = 4, /**< The command should be digress to the previous stage of + * execution. */ + Next = 8, /**< The command should progress to the next stage of + * execution. */ + Complete = 16, /**< The command should be completed (if possible). */ + InvalidAction = 32 /**< The action is unknown or invalid. */ + }; + + /** + * Describes the current status of a command. + */ + enum Status + { + Executing, /**< The command is being executed. */ + Completed, /**< The command has completed. The command session has ended. */ + Canceled, /**< The command has been canceled. The command session has ended. */ + InvalidStatus /**< The status is unknown or invalid. */ + }; + + /** + * An abstraction of a command note. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API Note + { + + friend class Command; + + public: + /** + * Specifies the severity of a note. + */ + enum Severity + { + Info, /**< The note is informational only. This is not really an + * exceptional condition. */ + Warning, /**< The note indicates a warning. Possibly due to illogical + * (yet valid) data. */ + Error, /**< The note indicates an error. The text should indicate the + * reason for the error. */ + InvalidSeverity /**< The note type is unknown or invalid. */ + }; + + /** + * A convenience constructor. + * @param sev The note's severity. + * @param note The note's content. + */ + Note( Severity sev, const std::string& note ) + : m_severity( sev ), m_note( note ) {} + + /** + * Destructor. + */ + ~Note() {} + + /** + * Returns the note's severity. + * @return The note's severity. + */ + Severity severity() const { return m_severity; } + + /** + * Returns the note's content. + * @return The note's content. + */ + const std::string& content() const { return m_note; } + + /** + * Returns a Tag representation of the Note. + * @return A Tag representation. + */ + Tag* tag() const; + + private: +#ifdef ADHOC_COMMANDS_TEST + public: +#endif + /** + * Constructs a new Note from the given Tag. + * @param tag The Tag to parse. + */ + Note( const Tag* tag ); + + Severity m_severity; /**< The note's severity. */ + std::string m_note; /**< The note's content. */ + }; + + /** + * A list of command notes. + */ + typedef std::list NoteList; + + /** + * Creates a Command object that can be used to perform the provided Action. + * This constructor is used best to continue execution of a multi stage command + * (for which the session ID must be known). + * @param node The node (command) to perform the action on. + * @param sessionid The session ID of an already running adhoc command session. + * @param action The action to perform. + * @param form An optional DataForm to include in the request. Will be deleted in Command's + * destructor. + */ + Command( const std::string& node, const std::string& sessionid, Action action, + DataForm* form = 0 ); + + /** + * Creates a Command object that can be used to perform the provided Action. + * This constructor is used best to reply to an execute request. + * @param node The node (command) to perform the action on. + * @param sessionid The (possibly newly created) session ID of the adhoc command session. + * @param status The execution status. + * @param form An optional DataForm to include in the reply. Will be deleted in Command's + * destructor. + */ + Command( const std::string& node, const std::string& sessionid, Status status, + DataForm* form = 0 ); + + /** + * Creates a Command object that can be used to perform the provided Action. + * This constructor is used best to reply to a multi stage command that is not yet completed + * (for which the session ID must be known). + * @param node The node (command) to perform the action on. + * @param sessionid The (possibly newly created) session ID of the adhoc command session. + * @param status The execution status. + * @param executeAction The action to execute. + * @param allowedActions Allowed reply actions. + * @param form An optional DataForm to include in the reply. Will be deleted in Command's + * destructor. + */ + Command( const std::string& node, const std::string& sessionid, Status status, + Action executeAction, int allowedActions = Complete, + DataForm* form = 0 ); + + /** + * Creates a Command object that can be used to perform the provided Action. + * This constructor is used best to execute the initial step of a command + * (single or multi stage). + * @param node The node (command) to perform the action on. + * @param action The action to perform. + * @param form An optional DataForm to include in the request. Will be deleted in Command's + * destructor. + */ + Command( const std::string& node, Action action, + DataForm* form = 0 ); + + /** + * Creates a Command object from the given Tag. + * @param tag A <command> tag in the adhoc commands' namespace. + */ + Command( const Tag* tag = 0 ); + + /** + * Virtual destructor. + */ + virtual ~Command(); + + /** + * Returns the node identifier (the command). + * @return The node identifier. + */ + const std::string& node() const { return m_node; } + + /** + * Returns the command's session ID, if any. + * @return The command's session ID. + */ + const std::string& sessionID() const { return m_sessionid; } + + /** + * Returns the execution status for a command. Only valid for execution + * results. + * @return The execution status for a command. + */ + Status status() const { return m_status; } + + /** + * Returns the command's action. + * @return The command's action. + */ + Action action() const { return m_action; } + + /** + * Returns the ORed actions that are allowed to be executed on the + * current stage. + * @return An int containing the ORed actions. + */ + int actions() const { return m_actions; } + + /** + * Returns the list of notes associated with the command. + * @return The list of notes. + */ + const NoteList& notes() const { return m_notes; } + + /** + * Use this function to add a note to the command. + * @param note A pointer to a Note object. The Command will own + * the Note. + */ + void addNote( const Note* note ) { m_notes.push_back( note ); } + + /** + * Returns the command's embedded DataForm. + * @return The command's embedded DataForm. May be 0. + */ + const DataForm* form() const { return m_form; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new Command( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + Command* c = new Command(); + + NoteList::const_iterator it = m_notes.begin(); + for( ; it != m_notes.end(); ++it ) + c->m_notes.push_back( new Note( *(*it) ) ); + + c->m_node = m_node; + c->m_sessionid = m_sessionid; + c->m_form = m_form ? static_cast( m_form->clone() ) : 0; + c->m_action = m_action; + c->m_status = m_status; + c->m_actions = m_actions; + + return c; + } + + private: +#ifdef ADHOC_COMMANDS_TEST + public: +#endif + NoteList m_notes; + + std::string m_node; + std::string m_sessionid; + DataForm* m_form; + Action m_action; + Status m_status; + int m_actions; + }; + + /** + * Constructor. + * Creates a new Adhoc client that registers as IqHandler with a ClientBase. + * @param parent The ClientBase used for XMPP communication. + */ + Adhoc( ClientBase* parent ); + + /** + * Virtual destructor. + */ + virtual ~Adhoc(); + + /** + * This function queries the given remote entity for Adhoc Commands support. + * @param remote The remote entity's JID. + * @param ah The object handling the result of this request. + */ + void checkSupport( const JID& remote, AdhocHandler* ah ); + + /** + * Retrieves a list of commands from the remote entity. You should check whether the remote + * entity actually supports Adhoc Commands by means of checkSupport(). + * @param remote The remote entity's JID. + * @param ah The object handling the result of this request. + */ + void getCommands( const JID& remote, AdhocHandler* ah ); + + /** + * Executes or continues the given command on the given remote entity. + * To construct the @c command object, it is recommended to use either + * Command( const std::string&, Action ) to begin execution of a command, or + * Command( const std::string&, const std::string&, Action ) to continue execution + * of a command. + * @param remote The remote entity's JID. + * @param command The command to execute. + * @param ah The object handling the result of this request. + */ + void execute( const JID& remote, const Adhoc::Command* command, AdhocHandler* ah ); + + /** + * Use this function to respond to an execution request submitted by means + * of AdhocCommandProvider::handleAdhocCommand(). + * It is recommended to use + * Command( const std::string&, const std::string&, Status, DataForm* ) + * to construct the @c command object. + * Optionally, an Error object can be included. In that case the IQ sent is of type @c error. + * @param remote The requester's JID. + * @param command The response. The Adhoc object will own and delete the + * command object pointed to. + * @param error An optional Error obejct to include. + */ + void respond( const JID& remote, const Adhoc::Command* command, const Error* error = 0 ); + + /** + * Using this function, you can register a AdhocCommandProvider -derived object as + * handler for a specific Ad-hoc Command as defined in XEP-0050. + * @param acp The obejct to register as handler for the specified command. + * @param command The node name of the command. Will be announced in disco#items. + * @param name The natural-language name of the command. Will be announced in disco#items. + */ + void registerAdhocCommandProvider( AdhocCommandProvider* acp, const std::string& command, + const std::string& name ); + + /** + * Use this function to unregister an adhoc command previously registered using + * registerAdhocCommandProvider(). + * @param command The command to unregister. + */ + void removeAdhocCommandProvider( const std::string& command ); + + // reimplemented from DiscoNodeHandler + virtual StringList handleDiscoNodeFeatures( const JID& from, const std::string& node ); + + // reimplemented from DiscoNodeHandler + virtual Disco::IdentityList handleDiscoNodeIdentities( const JID& from, + const std::string& node ); + + // reimplemented from DiscoNodeHandler + virtual Disco::ItemList handleDiscoNodeItems( const JID& from, const JID& to, const std::string& node ); + + // reimplemented from IqHandler + virtual bool handleIq( const IQ& iq ); + + // reimplemented from IqHandler + virtual void handleIqID( const IQ& iq, int context ); + + // reimplemented from DiscoHandler + virtual void handleDiscoInfo( const JID& from, const Disco::Info& info, int context ); + + // reimplemented from DiscoHandler + virtual void handleDiscoItems( const JID& from, const Disco::Items& items, int context ); + + // reimplemented from DiscoHandler + virtual void handleDiscoError( const JID& from, const Error* error, int context ); + + private: +#ifdef ADHOC_TEST + public: +#endif + typedef std::map AdhocCommandProviderMap; + AdhocCommandProviderMap m_adhocCommandProviders; + + enum AdhocContext + { + CheckAdhocSupport, + FetchAdhocCommands, + ExecuteAdhocCommand + }; + + struct TrackStruct + { + JID remote; + AdhocContext context; + std::string session; + AdhocHandler* ah; + }; + typedef std::map AdhocTrackMap; + AdhocTrackMap m_adhocTrackMap; + + ClientBase* m_parent; + + StringMap m_items; + StringMap m_activeSessions; + + }; + +} + +#endif // ADHOC_H__ diff --git a/libs/libgloox/adhoccommandprovider.h b/libs/libgloox/adhoccommandprovider.h new file mode 100644 index 0000000..97bd937 --- /dev/null +++ b/libs/libgloox/adhoccommandprovider.h @@ -0,0 +1,79 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef ADHOCCOMMANDPROVIDER_H__ +#define ADHOCCOMMANDPROVIDER_H__ + +#include "tag.h" +#include "jid.h" +#include "adhoc.h" + +#include +#include +#include + +namespace gloox +{ + + /** + * @brief A virtual interface for an Ad-hoc Command Provider according to XEP-0050. + * + * Derived classes can be registered as Command Providers with the Adhoc object. + * + * @author Jakob Schroeter + */ + class GLOOX_API AdhocCommandProvider + { + public: + /** + * Virtual destructor. + */ + virtual ~AdhocCommandProvider() {} + + /** + * This function is called when an Ad-hoc Command needs to be handled. + * The callee is responsible for the whole command execution, i.e. session + * handling etc. + * @param from The sender of the command request. + * @param command The name of the command to be executed. + * @param sessionID The session ID. Either newly generated or taken from the command. + * When responding, its value must be passed to Adhoc::Command's constructor. + */ + virtual void handleAdhocCommand( const JID& from, const Adhoc::Command& command, + const std::string& sessionID ) = 0; + + /** + * This function gets called for each registered command when a remote + * entity requests the list of available commands. + * @param from The requesting entity. + * @param command The command's name. + * @return @b True if the remote entity is allowed to see the command, @b false if not. + * @note The return value of this function does not influence + * the execution of a command. That is, you have to + * implement additional access control at the execution + * stage. + * @note This function should not block. + */ + virtual bool handleAdhocAccessRequest( const JID& from, const std::string& command ) + { + (void)from; + (void)command; + return true; + } + + }; + +} + +#endif // ADHOCCOMMANDPROVIDER_H__ diff --git a/libs/libgloox/adhochandler.h b/libs/libgloox/adhochandler.h new file mode 100644 index 0000000..8b83065 --- /dev/null +++ b/libs/libgloox/adhochandler.h @@ -0,0 +1,75 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef ADHOCHANDLER_H__ +#define ADHOCHANDLER_H__ + +#include "adhoc.h" + +namespace gloox +{ + + /** + * @brief A virtual interface for an Ad-hoc Command users according to XEP-0050. + * + * Derived classes can be registered with the Adhoc object to receive notifications + * about Adhoc Commands remote entities support. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API AdhocHandler + { + public: + /** + * Virtual destructor. + */ + virtual ~AdhocHandler() {} + + /** + * This function is called in response to a call to Adhoc::checkSupport(). + * @param remote The queried remote entity's JID. + * @param support Whether the remote entity supports Adhoc Commands. + */ + virtual void handleAdhocSupport( const JID& remote, bool support ) = 0; + + /** + * This function is called in response to a call to Adhoc::getCommands() + * and delivers a list of supported commands. + * @param remote The queried remote entity's JID. + * @param commands A map of supported commands and their human-readable name. + * The map may be empty. + */ + virtual void handleAdhocCommands( const JID& remote, const StringMap& commands ) = 0; + + /** + * This function is called in response to a call to Adhoc::getCommands() or + * Adhoc::checkSupport() or Adhoc::execute() in case the respective request returned + * an error. + * @param remote The queried remote entity's JID. + * @param error The error condition. May be 0. + */ + virtual void handleAdhocError( const JID& remote, const Error* error ) = 0; + + /** + * This function is called in response to a remote command execution. + * @param remote The remote entity's JID. + * @param command The command being executed. + */ + virtual void handleAdhocExecutionResult( const JID& remote, const Adhoc::Command& command ) = 0; + }; + +} + +#endif // ADHOCHANDLER_H__ diff --git a/libs/libgloox/amp.cpp b/libs/libgloox/amp.cpp new file mode 100644 index 0000000..3ff2c2c --- /dev/null +++ b/libs/libgloox/amp.cpp @@ -0,0 +1,189 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "amp.h" +#include "tag.h" +#include "util.h" + +namespace gloox +{ + + static const char* conditionValues[] = + { + "deliver", "expire-at", "match-resource" + }; + + static const char* actionValues[] = + { + "alert", "error", "drop", "notify" + }; + + static const char* deliverValues[] = + { + "direct", "forward", "gateway", "none", "stored" + }; + + static const char* matchResourceValues[] = + { + "any", "exact", "other" + }; + + static const char* statusValues[] = + { + "alert", "notify" + }; + + // ---- AMP::Rule ---- + AMP::Rule::Rule( DeliverType deliver, ActionType action ) + : m_condition( ConditionDeliver ), m_deliver( deliver ), m_action( action ) + { + } + + AMP::Rule::Rule( const std::string& date, ActionType action ) + : m_condition( ConditionExpireAt ), m_expireat( new std::string( date ) ), m_action( action ) + { + } + + AMP::Rule::Rule( MatchResourceType match, ActionType action ) + : m_condition( ConditionMatchResource ), m_matchresource( match ), m_action( action ) + { + } + + AMP::Rule::Rule( const std::string& condition, const std::string& action, + const std::string& value ) + { + m_condition = (ConditionType)util::lookup( condition, conditionValues ); + m_action = (ActionType)util::lookup( action, actionValues ); + switch( m_condition ) + { + case ConditionDeliver: + m_deliver = (DeliverType)util::lookup( value, deliverValues ); + break; + case ConditionExpireAt: + m_expireat = new std::string( value ); + break; + case ConditionMatchResource: + m_matchresource = (MatchResourceType)util::lookup( value, matchResourceValues ); + break; + default: + case ConditionInvalid: // shouldn't happen + break; + } + } + + AMP::Rule::~Rule() + { + if( m_condition == ConditionExpireAt && m_expireat ) + delete m_expireat; + } + + Tag* AMP::Rule::tag() const + { + if( m_condition == ConditionInvalid || m_action == ActionInvalid + || ( m_condition == ConditionDeliver && m_deliver == DeliverInvalid ) + || ( m_condition == ConditionMatchResource && m_matchresource == MatchResourceInvalid ) + || ( m_condition == ConditionExpireAt && !m_expireat ) ) + return 0; + + Tag* rule = new Tag( "rule" ); + rule->addAttribute( "condition", util::lookup( m_condition, conditionValues ) ); + rule->addAttribute( "action", util::lookup( m_action, actionValues ) ); + + switch( m_condition ) + { + case ConditionDeliver: + rule->addAttribute( "value", util::lookup( m_deliver, deliverValues ) ); + break; + case ConditionExpireAt: + rule->addAttribute( "value", *m_expireat ); + break; + case ConditionMatchResource: + rule->addAttribute( "value", util::lookup( m_matchresource, matchResourceValues ) ); + break; + default: + break; + } + return rule; + } + // ---- AMP::Rule ---- + + // ---- AMP ---- + AMP::AMP( bool perhop ) + : StanzaExtension( ExtAMP ), m_perhop( perhop ), m_status( StatusInvalid ) + { + m_valid = true; + } + + AMP::AMP( const Tag* tag ) + : StanzaExtension( ExtAMP ), m_perhop( false ) + { + if( !tag || tag->name() != "amp" || tag->xmlns() != XMLNS_AMP ) + return; + + const ConstTagList& rules = tag->findTagList( "/amp/rule" ); + ConstTagList::const_iterator it = rules.begin(); + for( ; it != rules.end(); ++it ) + { + m_rules.push_back( new Rule( (*it)->findAttribute( "condition" ), + (*it)->findAttribute( "action" ), + (*it)->findAttribute( "value" ) ) ); + } + + m_from = tag->findAttribute( "from" ); + m_to = tag->findAttribute( "to" ); + m_status = (Status)util::lookup( tag->findAttribute( "status" ), statusValues ); + if( tag->hasAttribute( "per-hop", "true" ) || tag->hasAttribute( "per-hop", "1" ) ) + m_perhop = true; + m_valid = true; + } + + AMP::~AMP() + { + util::clearList( m_rules ); + } + + void AMP::addRule( const Rule* rule ) + { + if( rule ) + m_rules.push_back( rule ); + } + + const std::string& AMP::filterString() const + { + static const std::string filter = "/message/amp[@xmlns='" + XMLNS_AMP + "']"; + return filter; + } + + Tag* AMP::tag() const + { + if( !m_valid || !m_rules.size() ) + return 0; + + Tag* amp = new Tag( "amp" ); + amp->setXmlns( XMLNS_AMP ); + if( m_from ) + amp->addAttribute( "from", m_from.full() ); + if( m_to ) + amp->addAttribute( "to", m_to.full() ); + if( m_status != StatusInvalid ) + amp->addAttribute( "status", util::lookup( m_status, statusValues ) ); + if( m_perhop ) + amp->addAttribute( "per-hop", "true" ); + RuleList::const_iterator it = m_rules.begin(); + for( ; it != m_rules.end(); ++it ) + amp->addChild( (*it)->tag() ); + + return amp; + } + +} diff --git a/libs/libgloox/amp.h b/libs/libgloox/amp.h new file mode 100644 index 0000000..dc44d09 --- /dev/null +++ b/libs/libgloox/amp.h @@ -0,0 +1,243 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef AMP_H__ +#define AMP_H__ + +#include "stanzaextension.h" +#include "jid.h" + +#include +#include + +#include + +namespace gloox +{ + + class Tag; + + /** + * @brief This is an implementation of XEP-0079 (Advanced Message Processing) + * as a StanzaExtension. + * + * XEP Version: 1.2 + * @author Jakob Schroeter + * @author Vincent Thomasset + * @since 1.0 + */ + class GLOOX_API AMP : public StanzaExtension + { + + public: + /** + * Possible types for a rule's condition. + */ + enum ConditionType + { + ConditionDeliver, /**< Ensures (non-)delivery of the message */ + ConditionExpireAt, /**< Ensures delivery only before a certain time (UTC) */ + ConditionMatchResource, /**< Ensures delivery only to a specific resource type */ + ConditionInvalid /**< Invalid condition */ + }; + + /** + * Possible actions to take when the corresponding condition is met. + */ + enum ActionType + { + + ActionAlert, /**< Sends back a message stanza with an 'alert' status */ + ActionError, /**< Sends back a message stanza with an error type */ + ActionDrop, /**< Silently ignore the message */ + ActionNotify, /**< Sends back a message stanza with a 'notify' status */ + ActionInvalid /**< Invalid action */ + }; + + /** + * Possible delivery rules. + */ + enum DeliverType + { + DeliverDirect, /**< The message would be immediately delivered to the intended + * recipient or routed to the next hop. */ + DeliverForward, /**< The message would be forwarded to another XMPP address or + * account. */ + DeliverGateway, /**< The message would be sent through a gateway to an address + * or account on a non-XMPP system. */ + DeliverNone, /**< The message would not be delivered at all (e.g., because + * the intended recipient is offline and message storage is + * not enabled). */ + DeliverStored, /**< The message would be stored offline for later delivery + * to the intended recipient. */ + DeliverInvalid /**< Invalid deliver value */ + }; + + /** + * Possible resource matching rules. + */ + enum MatchResourceType + { + MatchResourceAny, /**< Destination resource matches any value, effectively + * ignoring the intended resource. */ + MatchResourceExact, /**< Destination resource exactly matches the intended + * resource. */ + MatchResourceOther, /**< Destination resource matches any value except for + * the intended resource. */ + MatchResourceInvalid /**< Invalid match-resource value */ + }; + + /** + * Available Stati. + */ + enum Status + { + StatusAlert, /**< The message is a reply to a @c Alert rule. */ + StatusNotify, /**< The message is a reply to a @c Notify rule. */ + StatusInvalid /**< Invalid status. */ + }; + + /** + * Describes an AMP rule. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API Rule + { + public: + /** + * Creates a new AMP rule object with a condition of 'deliver'. + * @param deliver The delivery type. + * @param action The rule's action. + */ + Rule( DeliverType deliver, ActionType action ); + + /** + * Creates a new AMP rule object with a condition of 'expire-at'. + * @param date The expiry date/time in the format defined in XEP-0082. + * @param action The rule's action. + */ + Rule( const std::string& date, ActionType action ); + + /** + * Creates a new AMP rule object with a condition of 'match-resource'. + * @param match The match type. + * @param action The rule's action. + */ + Rule( MatchResourceType match, ActionType action ); + + /** + * Creates a new AMP rule object from the given strings. + * @param condition The rule's condition. + * @param action The rule's action. + * @param value The rule's value. + */ + Rule( const std::string& condition, const std::string& action, + const std::string& value ); + + /** + * Destructor. + */ + ~Rule(); + + /** + * Creates a Tag representation from the current rule. + * @return A Tag representation of the rule. + */ + Tag* tag() const; + + private: + ConditionType m_condition; + union + { + DeliverType m_deliver; + MatchResourceType m_matchresource; + std::string* m_expireat; + }; + ActionType m_action; + + }; + + /** + * A list of AMP rules. + */ + typedef std::list RuleList; + + /** + * Constructs a new object. + * @param perhop Indicates whether the ruleset should be applied to all hops, + * or at the edge servers only. Default: @c false (edge servers only) + */ + AMP( bool perhop = false ); + + /** + * Constructs a new object from the given Tag. + * @param tag The AMP Tag to parse. + */ + AMP( const Tag* tag ); + + /** + * Adds the given rule to the list of rules. + * @param rule The rule to add. + */ + void addRule( const Rule* rule ); + + /** + * Returns the current list of rules for inspection. + * @return The current list of rules. + */ + const RuleList& rules() const { return m_rules; } + + /** + * @brief Virtual Destructor. + */ + virtual ~AMP(); + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new AMP( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + AMP* a = new AMP(); + a->m_perhop = m_perhop; + RuleList::const_iterator it = m_rules.begin(); + for( ; it != m_rules.end(); ++it ) + a->m_rules.push_back( new Rule( *(*it) ) ); + a->m_status = m_status; + a->m_from = m_from; + a->m_to = m_to; + return a; + } + + private: + bool m_perhop; + RuleList m_rules; + Status m_status; + JID m_from; + JID m_to; + }; + +} + +#endif // AMP_H__ diff --git a/libs/libgloox/annotations.cpp b/libs/libgloox/annotations.cpp new file mode 100644 index 0000000..bc3ac04 --- /dev/null +++ b/libs/libgloox/annotations.cpp @@ -0,0 +1,90 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "annotations.h" +#include "clientbase.h" + + +namespace gloox +{ + + Annotations::Annotations( ClientBase* parent ) + : PrivateXML( parent ), + m_annotationsHandler( 0 ) + { + } + + Annotations::~Annotations() + { + } + + void Annotations::storeAnnotations( const AnnotationsList& aList ) + { + Tag* s = new Tag( "storage", XMLNS, XMLNS_ANNOTATIONS ); + + AnnotationsList::const_iterator it = aList.begin(); + for( ; it != aList.end(); ++it ) + { + Tag* n = new Tag( s, "note", (*it).note ); + n->addAttribute( "jid", (*it).jid ); + n->addAttribute( "cdate", (*it).cdate ); + n->addAttribute( "mdate", (*it).mdate ); + } + + storeXML( s, this ); + } + + void Annotations::requestAnnotations() + { + requestXML( "storage", XMLNS_ANNOTATIONS, this ); + } + + void Annotations::handlePrivateXML( const Tag* xml ) + { + if( !xml ) + return; + + AnnotationsList aList; + const TagList& l = xml->children(); + TagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + { + if( (*it)->name() == "note" ) + { + const std::string& jid = (*it)->findAttribute( "jid" ); + const std::string& note = (*it)->cdata(); + + if( !jid.empty() && !note.empty() ) + { + const std::string& cdate = (*it)->findAttribute( "cdate" ); + const std::string& mdate = (*it)->findAttribute( "mdate" ); + AnnotationsListItem item; + item.jid = jid; + item.cdate = cdate; + item.mdate = mdate; + item.note = note; + aList.push_back( item ); + } + } + } + + if( m_annotationsHandler ) + m_annotationsHandler->handleAnnotations( aList ); + } + + void Annotations::handlePrivateXMLResult( const std::string& /*uid*/, PrivateXMLResult /*result*/ ) + { + } + +} diff --git a/libs/libgloox/annotations.h b/libs/libgloox/annotations.h new file mode 100644 index 0000000..ff65ba0 --- /dev/null +++ b/libs/libgloox/annotations.h @@ -0,0 +1,147 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef ANNOTATIONS_H__ +#define ANNOTATIONS_H__ + +#include "macros.h" + +#include "annotationshandler.h" +#include "privatexml.h" +#include "privatexmlhandler.h" + +#include +#include + +namespace gloox +{ + + class Tag; + + /** + * @brief This is an implementation of XEP-0145 (Annotations). + * + * You can use this class to store arbitrary notes about a roster item on the server + * (and to retrieve them later on). + * To retrieve all stored annotations for the current user's roster you have to create + * a class which inherits from AnnotationsHandler. This handler receives retrieved notes. + * + * @code + * class MyClass : public AnnotationsHandler + * { + * public: + * // ... + * void myFuncRetrieve(); + * void myFuncStore(); + * void handleAnnotations( const AnnotationsList &aList ); + * + * private: + * Annotations* m_notes; + * AnnotationsList m_list; + * }; + * + * void MyClass::myFuncRetrieve() + * { + * [...] + * m_notes = new Annotations( m_client ); + * m_notes->requestAnnotations(); + * } + * + * void MyClass::handleAnnotations( const AnnotationsList &aList ) + * { + * m_list = aList; + * } + * @endcode + * + * To store an additional note you have to fetch the currently stored notes first, + * add your new note to the list of notes, and transfer them all together back to the + * server. This protocol does not support storage of 'deltas', that is, when saving + * notes all previously saved notes are overwritten. + * + * @code + * void MyClass::myFuncStore() + * { + * annotationsListItem item; + * item.jid = "me@example.com"; + * item.cdate = "2006-02-04T15:23:21Z"; + * item.note = "some guy at example.com"; + * m_list.push_back( item ); + * + * item.jid = "abc@def.com"; + * item.cdate = "2006-01-24T15:23:21Z"; + * item.mdate = "2006-02-04T05:11:46Z"; + * item.note = "some other guy"; + * m_list.push_back( item ); + * + * m_notes->storeAnnotations( m_list ); + * } + * @endcode + * + * @author Jakob Schroeter + * @since 0.3 + */ + class GLOOX_API Annotations : public PrivateXML, public PrivateXMLHandler + { + public: + /** + * Constructs a new Annotations object. + * @param parent The ClientBase to use for communication. + */ + Annotations( ClientBase* parent ); + + /** + * Virtual destructor. + */ + virtual ~Annotations(); + + /** + * Use this function to store notes (annotations to contacts in a roster) on the server. + * Make sure you store the whole set of annotations, not a 'delta'. + * @param aList A list of notes to store. + */ + void storeAnnotations( const AnnotationsList& aList ); + + /** + * Use this function to initiate retrieval of annotations. Use registerAnnotationsHandler() + * to register an object which will receive the lists of notes. + */ + void requestAnnotations(); + + /** + * Use this function to register a AnnotationsHandler. + * @param ah The AnnotationsHandler which shall receive retrieved notes. + */ + void registerAnnotationsHandler( AnnotationsHandler* ah ) + { m_annotationsHandler = ah; } + + /** + * Use this function to un-register the AnnotationsHandler. + */ + void removeAnnotationsHandler() + { m_annotationsHandler = 0; } + + // reimplemented from PrivateXMLHandler + virtual void handlePrivateXML( const Tag* xml ); + + // reimplemented from PrivateXMLHandler + virtual void handlePrivateXMLResult( const std::string& uid, PrivateXMLResult pxResult ); + + private: + AnnotationsHandler* m_annotationsHandler; + + }; + +} + +#endif // ANNOTATIONS_H__ diff --git a/libs/libgloox/annotationshandler.h b/libs/libgloox/annotationshandler.h new file mode 100644 index 0000000..99178ea --- /dev/null +++ b/libs/libgloox/annotationshandler.h @@ -0,0 +1,66 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef ANNOTATIONSHANDLER_H__ +#define ANNOTATIONSHANDLER_H__ + +#include "macros.h" + +#include +#include + +namespace gloox +{ + + /** + * This describes a single note item. + */ + struct AnnotationsListItem + { + std::string jid; /**< The JID of the roster item this note is about */ + std::string cdate; /**< Creation date of this note. */ + std::string mdate; /**< Date of last modification of this note. */ + std::string note; /**< The note. */ + }; + + /** + * A list of note items. + */ + typedef std::list AnnotationsList; + + /** + * @brief A virtual interface which can be reimplemented to receive notes with help of + * the Annotations object. + * + * @author Jakob Schroeter + * @since 0.3 + */ + class GLOOX_API AnnotationsHandler + { + public: + /** + * Virtual destructor. + */ + virtual ~AnnotationsHandler() {} + + /** + * This function is called when notes arrive from the server. + * @param aList A list of notes. + */ + virtual void handleAnnotations( const AnnotationsList &aList ) = 0; + }; + +} + +#endif // ANNOTATIONSHANDLER_H__ diff --git a/libs/libgloox/attention.cpp b/libs/libgloox/attention.cpp new file mode 100644 index 0000000..437e804 --- /dev/null +++ b/libs/libgloox/attention.cpp @@ -0,0 +1,43 @@ +/* + Copyright (c) 2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "attention.h" +#include "tag.h" + +namespace gloox +{ + + Attention::Attention() + : StanzaExtension( ExtAttention ) + { + } + + Attention::~Attention() + { + } + + const std::string& Attention::filterString() const + { + static const std::string filter = "/message/attention[@xmlns='" + XMLNS_ATTENTION + "']"; + return filter; + } + + Tag* Attention::tag() const + { + Tag* t = new Tag( "attention" ); + t->setXmlns( XMLNS_ATTENTION ); + return t; + } + +} diff --git a/libs/libgloox/attention.h b/libs/libgloox/attention.h new file mode 100644 index 0000000..1a81af5 --- /dev/null +++ b/libs/libgloox/attention.h @@ -0,0 +1,70 @@ +/* + Copyright (c) 2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef ATTENTION_H__ +#define ATTENTION_H__ + + +#include "stanzaextension.h" + +#include + +namespace gloox +{ + + class Tag; + + /** + * @brief This is an implementation of XEP-0224 as a StanzaExtension. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API Attention : public StanzaExtension + { + + public: + /** + * Constructs a new object from the given Tag. + */ + Attention(); + + /** + * Virtual Destructor. + */ + virtual ~Attention(); + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* /*tag*/ ) const + { + return new Attention(); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new Attention(); + } + + }; + +} + +#endif// ATTENTION_H__ diff --git a/libs/libgloox/base64.cpp b/libs/libgloox/base64.cpp new file mode 100644 index 0000000..3c0253f --- /dev/null +++ b/libs/libgloox/base64.cpp @@ -0,0 +1,126 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "base64.h" + +namespace gloox +{ + + namespace Base64 + { + + static const std::string alphabet64( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" ); + static const char pad = '='; + static const char np = (char)std::string::npos; + static char table64vals[] = + { + 62, np, np, np, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, np, np, np, np, np, + np, np, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, np, np, np, np, np, np, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 + }; + + inline char table64( unsigned char c ) + { + return ( c < 43 || c > 122 ) ? np : table64vals[c-43]; + } + + const std::string encode64( const std::string& input ) + { + std::string encoded; + char c; + const std::string::size_type length = input.length(); + + encoded.reserve( length * 2 ); + + for( std::string::size_type i = 0; i < length; ++i ) + { + c = static_cast( ( input[i] >> 2 ) & 0x3f ); + encoded += alphabet64[c]; + + c = static_cast( ( input[i] << 4 ) & 0x3f ); + if( ++i < length ) + c = static_cast( c | static_cast( ( input[i] >> 4 ) & 0x0f ) ); + encoded += alphabet64[c]; + + if( i < length ) + { + c = static_cast( ( input[i] << 2 ) & 0x3c ); + if( ++i < length ) + c = static_cast( c | static_cast( ( input[i] >> 6 ) & 0x03 ) ); + encoded += alphabet64[c]; + } + else + { + ++i; + encoded += pad; + } + + if( i < length ) + { + c = static_cast( input[i] & 0x3f ); + encoded += alphabet64[c]; + } + else + { + encoded += pad; + } + } + + return encoded; + } + + const std::string decode64( const std::string& input ) + { + char c, d; + const std::string::size_type length = input.length(); + std::string decoded; + + decoded.reserve( length ); + + for( std::string::size_type i = 0; i < length; ++i ) + { + c = table64(input[i]); + ++i; + d = table64(input[i]); + c = static_cast( ( c << 2 ) | ( ( d >> 4 ) & 0x3 ) ); + decoded += c; + if( ++i < length ) + { + c = input[i]; + if( pad == c ) + break; + + c = table64(input[i]); + d = static_cast( ( ( d << 4 ) & 0xf0 ) | ( ( c >> 2 ) & 0xf ) ); + decoded += d; + } + + if( ++i < length ) + { + d = input[i]; + if( pad == d ) + break; + + d = table64(input[i]); + c = static_cast( ( ( c << 6 ) & 0xc0 ) | d ); + decoded += c; + } + } + + return decoded; + } + + } + +} diff --git a/libs/libgloox/base64.h b/libs/libgloox/base64.h new file mode 100644 index 0000000..158528f --- /dev/null +++ b/libs/libgloox/base64.h @@ -0,0 +1,51 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef BASE64_H__ +#define BASE64_H__ + +#include "macros.h" + +#include + +namespace gloox +{ + + /** + * @brief An implementation of the Base64 data encoding (RFC 3548) + * + * @author Jakob Schroeter + * @since 0.8 + */ + namespace Base64 + { + + /** + * Base64-encodes the input according to RFC 3548. + * @param input The data to encode. + * @return The encoded string. + */ + GLOOX_API const std::string encode64( const std::string& input ); + + /** + * Base64-decodes the input according to RFC 3548. + * @param input The encoded data. + * @return The decoded data. + */ + GLOOX_API const std::string decode64( const std::string& input ); + + } + +} + +#endif // BASE64_H__ diff --git a/libs/libgloox/bookmarkhandler.h b/libs/libgloox/bookmarkhandler.h new file mode 100644 index 0000000..141b3f9 --- /dev/null +++ b/libs/libgloox/bookmarkhandler.h @@ -0,0 +1,82 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef BOOKMARKHANDLER_H__ +#define BOOKMARKHANDLER_H__ + +#include "macros.h" + +#include +#include + +namespace gloox +{ + + /** + * This describes a single bookmarked URL item. + */ + struct BookmarkListItem + { + std::string name; /**< A human readable name of the bookmark. */ + std::string url; /**< The URL of the bookmark. */ + }; + + /** + * This describes a single bookmarked conference item. + */ + struct ConferenceListItem + { + std::string name; /**< A human readable name of the conference room. */ + std::string jid; /**< The address of the room. */ + std::string nick; /**< The nick name to use in this room. */ + std::string password; /**< The password to use for a protected room. */ + bool autojoin; /**< The conference shall be joined automatically on login. */ + }; + + /** + * A list of URL items. + */ + typedef std::list BookmarkList; + + /** + * A list of conference items. + */ + typedef std::list ConferenceList; + + /** + * @brief A virtual interface which can be reimplemented to receive bookmarks with help of a + * BookmarkStorage object. + * + * @author Jakob Schroeter + * @since 0.3 + */ + class GLOOX_API BookmarkHandler + { + public: + /** + * Virtual Destructor. + */ + virtual ~BookmarkHandler() {} + + /** + * This function is called when bookmarks arrive from the server. + * @param bList A list of URL bookmarks. + * @param cList A list of conference bookmarks. + */ + virtual void handleBookmarks( const BookmarkList &bList, const ConferenceList &cList ) = 0; + }; + +} + +#endif // BOOKMARKHANDLER_H__ diff --git a/libs/libgloox/bookmarkstorage.cpp b/libs/libgloox/bookmarkstorage.cpp new file mode 100644 index 0000000..a5c1904 --- /dev/null +++ b/libs/libgloox/bookmarkstorage.cpp @@ -0,0 +1,118 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "bookmarkstorage.h" +#include "clientbase.h" + + +namespace gloox +{ + + BookmarkStorage::BookmarkStorage( ClientBase* parent ) + : PrivateXML( parent ), + m_bookmarkHandler( 0 ) + { + } + + BookmarkStorage::~BookmarkStorage() + { + } + + void BookmarkStorage::storeBookmarks( const BookmarkList& bList, const ConferenceList& cList ) + { + Tag* s = new Tag( "storage" ); + s->addAttribute( XMLNS, XMLNS_BOOKMARKS ); + + BookmarkList::const_iterator itb = bList.begin(); + for( ; itb != bList.end(); ++itb ) + { + Tag* i = new Tag( s, "url", "name", (*itb).name ); + i->addAttribute( "url", (*itb).url ); + } + + ConferenceList::const_iterator itc = cList.begin(); + for( ; itc != cList.end(); ++itc ) + { + Tag* i = new Tag( s, "conference", "name", (*itc).name ); + i->addAttribute( "jid", (*itc).jid ); + i->addAttribute( "autojoin", (*itc).autojoin ? "true" : "false" ); + + new Tag( i, "nick", (*itc).nick ); + new Tag( i, "password", (*itc).password ); + } + + storeXML( s, this ); + } + + void BookmarkStorage::requestBookmarks() + { + requestXML( "storage", XMLNS_BOOKMARKS, this ); + } + + void BookmarkStorage::handlePrivateXML( const Tag* xml ) + { + if( !xml ) + return; + + BookmarkList bList; + ConferenceList cList; + const TagList& l = xml->children(); + TagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + { + if( (*it)->name() == "url" ) + { + const std::string& url = (*it)->findAttribute( "url" ); + const std::string& name = (*it)->findAttribute( "name" ); + + if( !url.empty() && !name.empty() ) + { + BookmarkListItem item; + item.url = url; + item.name = name; + bList.push_back( item ); + } + } + else if( (*it)->name() == "conference" ) + { + const std::string& jid = (*it)->findAttribute( "jid" ); + const std::string& name = (*it)->findAttribute( "name" ); + + if( !jid.empty() && !name.empty() ) + { + const std::string& join = (*it)->findAttribute( "autojoin" ); + ConferenceListItem item; + item.jid = jid; + item.name = name; + const Tag* nick = (*it)->findChild( "nick" ); + if( nick ) + item.nick = nick->cdata(); + const Tag* pwd = (*it)->findChild( "password" ); + if( pwd ) + item.password = pwd->cdata(); + item.autojoin = ( join == "true" || join == "1" ); + cList.push_back( item ); + } + } + } + + if( m_bookmarkHandler ) + m_bookmarkHandler->handleBookmarks( bList, cList ); + } + + void BookmarkStorage::handlePrivateXMLResult( const std::string& /*uid*/, PrivateXMLResult /*result*/ ) + { + } + +} diff --git a/libs/libgloox/bookmarkstorage.h b/libs/libgloox/bookmarkstorage.h new file mode 100644 index 0000000..c3c7fb0 --- /dev/null +++ b/libs/libgloox/bookmarkstorage.h @@ -0,0 +1,150 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef BOOKMARKSTORAGE_H__ +#define BOOKMARKSTORAGE_H__ + +#include "macros.h" + +#include "bookmarkhandler.h" +#include "privatexml.h" +#include "privatexmlhandler.h" + +#include +#include + +namespace gloox +{ + + class Tag; + + /** + * @brief This is an implementation of XEP-0048 (Bookmark Storage). + * + * You can use this class to store bookmarks to multi-user chat rooms or ordinary URLs + * on the server (and to retrieve them later on). + * To retrieve all stored bookmarks for the current user you have to create a class which + * inherits from BookmarkHandler. This handler receives retrieved bookmarks. + * + * @code + * class MyClass : public BookmarkHandler + * { + * public: + * // ... + * void myFuncRetrieve(); + * void myFuncStore(); + * void handleBookmarks( const BookmarkList &bList, const ConferenceList &cList ); + * + * private: + * BookmarkStorage* m_bs; + * BookmarkList m_bList; + * ConferenceList m_cList; + * }; + * + * void MyClass::myFuncRetrieve() + * { + * m_bs = new BookmarkStorage( m_client ); + * m_bs->requestBookmarks(); + * } + * + * void MyClass::handleBookmarks( const BookmarkList &bList, const ConferenceList &cList ) + * { + * m_bList = bList; + * m_cList = cList; + * } + * @endcode + * + * + * To store additional bookmarks you have to fetch the currently stored ones first, + * add your new bookmark to the list, and transfer them all together back to the + * server. This protocol does not support storage of 'deltas', that is, when saving + * bookmarks all previously saved bookmarks are overwritten. + * + * @code + * void MyClass::myFuncStore() + * { + * BookmarkListItem bi; + * bi.url = "http://www.jabber.org"; + * bi.name = "my favourite IM protocol"; + * m_bList.push_back( bi ); + * + * conferenceListItem ci + * ci.name = "jabber/xmpp development room"; + * ci.jid = "jdev@conference.jabber.org"; + * ci.nick = "myNick"; + * ci.password = EmptyString; + * ci.autojoin = true; + * m_cList.push_back( ci ); + * + * m_bs->storeBookmarks( m_bList, m_cList ); + * } + * @endcode + * + * @author Jakob Schroeter + * @since 0.3 + */ + class GLOOX_API BookmarkStorage : public PrivateXML, public PrivateXMLHandler + { + public: + /** + * Constructs a new BookmarkStorage object. + * @param parent The ClientBase to use for communication. + */ + BookmarkStorage( ClientBase* parent ); + + /** + * Virtual destructor. + */ + virtual ~BookmarkStorage(); + + /** + * Use this function to store a number of URL and conference bookmarks on the server. + * Make sure you store the whole set of bookmarks, not a 'delta'. + * @param bList A list of URLs to store. + * @param cList A list of conferences to store. + */ + void storeBookmarks( const BookmarkList& bList, const ConferenceList& cList ); + + /** + * Use this function to initiate retrieval of bookmarks. Use registerBookmarkHandler() + * to register an object which will receive the lists of bookmarks. + */ + void requestBookmarks(); + + /** + * Use this function to register a BookmarkHandler. + * @param bmh The BookmarkHandler which shall receive retrieved bookmarks. + */ + void registerBookmarkHandler( BookmarkHandler* bmh ) + { m_bookmarkHandler = bmh; } + + /** + * Use this function to un-register the BookmarkHandler. + */ + void removeBookmarkHandler() + { m_bookmarkHandler = 0; } + + // reimplemented from PrivateXMLHandler + virtual void handlePrivateXML( const Tag* xml ); + + // reimplemented from PrivateXMLHandler + virtual void handlePrivateXMLResult( const std::string& uid, PrivateXMLResult pxResult ); + + private: + BookmarkHandler* m_bookmarkHandler; + }; + +} + +#endif // BOOKMARKSTORAGE_H__ diff --git a/libs/libgloox/bytestream.h b/libs/libgloox/bytestream.h new file mode 100644 index 0000000..b790f51 --- /dev/null +++ b/libs/libgloox/bytestream.h @@ -0,0 +1,180 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef BYTESTREAM_H__ +#define BYTESTREAM_H__ + +#include "jid.h" +#include "logsink.h" + +#include + +namespace gloox +{ + + class BytestreamDataHandler; + + /** + * @brief An abstraction of a single bytestream. + * + * Used as a base class for InBand Bytestreams as well as SOCKS5 Bytestreams. + * You should not need to use this class directly. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API Bytestream + { + public: + /** + * Available stream types. + */ + enum StreamType + { + S5B, /**< SOCKS5 Bytestream */ + IBB /**< In-Band Bytestream */ + }; + + /** + * Creates a new Bytestream. + * @param type The stream type. + * @param logInstance A Logsink to use for logging. Obtain it from ClientBase::logInstance(). + * @param initiator The initiator of the stream (usually the sender). + * @param target The target of the stream (usually the receiver). + * @param sid The stream's ID. + */ + Bytestream( StreamType type, LogSink& logInstance, const JID& initiator, const JID& target, + const std::string& sid ) + : m_handler( 0 ), m_logInstance( logInstance ), m_initiator( initiator ), m_target( target ), + m_type( type ), m_sid( sid ), m_open( false ) + {} + + /** + * Virtual destructor. + */ + virtual ~Bytestream() {} + + /** + * Returns whether the bytestream is open, that is, accepted by both parties and ready + * to send/receive data. + * @return Whether or not the bytestream is open. + */ + bool isOpen() const { return m_open; } + + /** + * This function starts the connection process. + * @return @b True if a connection to a remote entity could be established, @b false + * otherwise. + * @note If @b false is returned you should pass this Bytestream object + * to SIProfileFT::dispose() for deletion. + * @note Make sure you have a BytestreamDataHandler registered (using + * registerBytestreamDataHandler()) before calling this function. + */ + virtual bool connect() = 0; + + /** + * Closes the bytestream. + */ + virtual void close() = 0; + + /** + * Use this function to send a chunk of data over an open bytestream. + * If the stream is not open or has been closed again + * (by the remote entity or locally), nothing is sent and @b false is returned. + * This function does any base64 encoding for you, if necessary. + * @param data The block of data to send. + * @return @b True if the data has been sent (no guarantee of receipt), @b false + * in case of an error. + */ + virtual bool send( const std::string& data ) = 0; + + /** + * Call this function repeatedly to receive data. You should even do this + * if you use the bytestream to merely @b send data. May be a NOOP, depending on the actual + * stream type. + * @param timeout The timeout to use for select in microseconds. Default of -1 means blocking. + * @return The state of the connection. + */ + virtual ConnectionError recv( int timeout = -1 ) = 0; + + /** + * Lets you retrieve the stream's ID. + * @return The stream's ID. + */ + const std::string& sid() const { return m_sid; } + + /** + * Returns the stream's type. + * @return The stream's type. + */ + StreamType type() const { return m_type; } + + /** + * Returns the target entity's JID. If this bytestream is remote-initiated, this is + * the local JID. If it is local-initiated, this is the remote entity's JID. + * @return The target's JID. + */ + const JID& target() const { return m_target; } + + /** + * Returns the initiating entity's JID. If this bytestream is remote-initiated, this is + * the remote entity's JID. If it is local-initiated, this is the local JID. + * @return The initiator's JID. + */ + const JID& initiator() const { return m_initiator; } + + /** + * Use this function to register an object that will receive any notifications from + * the Bytestream instance. Only one BytestreamDataHandler can be registered + * at any one time. + * @param bdh The BytestreamDataHandler-derived object to receive notifications. + */ + void registerBytestreamDataHandler( BytestreamDataHandler* bdh ) + { m_handler = bdh; } + + /** + * Removes the registered BytestreamDataHandler. + */ + void removeBytestreamDataHandler() + { m_handler = 0; } + + protected: + /** A handler for incoming data and open/close events. */ + BytestreamDataHandler* m_handler; + + /** A LogSink instance to use for logging. */ + const LogSink& m_logInstance; + + /** The initiator's JID. */ + const JID m_initiator; + + /** The target's JID. */ + const JID m_target; + + /** The stream type. */ + StreamType m_type; + + /** The stream ID. */ + std::string m_sid; + + /** Indicates whether or not the stream is open. */ + bool m_open; + + private: + Bytestream& operator=( const Bytestream& ); + + }; + +} + +#endif // BYTESTREAM_H__ diff --git a/libs/libgloox/bytestreamdatahandler.h b/libs/libgloox/bytestreamdatahandler.h new file mode 100644 index 0000000..8f0ff0a --- /dev/null +++ b/libs/libgloox/bytestreamdatahandler.h @@ -0,0 +1,81 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef BYTESTREAMDATAHANDLER_H__ +#define BYTESTREAMDATAHANDLER_H__ + +#include "macros.h" + +#include + +namespace gloox +{ + + class Bytestream; + class IQ; + + /** + * @brief A virtual interface that allows implementors to receive data + * sent over a SOCKS5 Bytestream as defined in XEP-0066, or an In-Band Bytestream + * as defined in XEP-0047. You'll also need it for sending of data. + * + * An BytestreamDataHandler is registered with a Bytestream. + * + * See SIProfileFT for more information regarding file transfer. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API BytestreamDataHandler + { + public: + /** + * Virtual destructor. + */ + virtual ~BytestreamDataHandler() {} + + /** + * Reimplement this function to receive data which is sent over the bytestream. + * The data received here is (probably) only a single chunk of the complete data (depending + * on the amount of data you want to send). + * @param bs The bytestream. + * @param data The actual stream payload. + */ + virtual void handleBytestreamData( Bytestream* bs, const std::string& data ) = 0; + + /** + * Notifies about an error occuring while using a bytestream. + * When this handler is called the stream has already been closed. + * @param bs The bytestream. + * @param iq The error stanza. + */ + virtual void handleBytestreamError( Bytestream* bs, const IQ& iq ) = 0; + + /** + * Notifies the handler that the given bytestream has been acknowledged + * and is ready to send/receive data. + * @param bs The opened bytestream. + */ + virtual void handleBytestreamOpen( Bytestream* bs ) = 0; + + /** + * Notifies the handler that the given bytestream has been closed. + * @param bs The closed bytestream. + */ + virtual void handleBytestreamClose( Bytestream* bs ) = 0; + + }; + +} + +#endif // BYTESTREAMDATAHANDLER_H__ diff --git a/libs/libgloox/bytestreamhandler.h b/libs/libgloox/bytestreamhandler.h new file mode 100644 index 0000000..a083f5b --- /dev/null +++ b/libs/libgloox/bytestreamhandler.h @@ -0,0 +1,90 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef BYTESTREAMHANDLER_H__ +#define BYTESTREAMHANDLER_H__ + +#include "macros.h" +#include "jid.h" +#include "bytestream.h" +#include "iq.h" + +namespace gloox +{ + + /** + * @brief A virtual interface that allows to receive new incoming Bytestream requests + * from remote entities. + * + * You should not need to use this interface directly. + * + * See SIProfileFT on how to implement file transfer in general. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API BytestreamHandler + { + public: + /** + * Virtual destructor. + */ + virtual ~BytestreamHandler() {} + + /** + * Notifies the implementor of a new incoming bytestream request. + * You have to call either + * BytestreamManager::acceptBytestream() or + * BytestreamManager::rejectBytestream(), to accept or reject the bytestream + * request, respectively. + * @param sid The bytestream's id, to be passed to BytestreamManager::acceptBytestream() + * and BytestreamManager::rejectBytestream(), respectively. + * @param from The remote initiator of the bytestream request. + */ + virtual void handleIncomingBytestreamRequest( const std::string& sid, const JID& from ) = 0; + + /** + * Notifies the implementor of a new incoming bytestream. The bytestream is not yet ready to + * send data. + * To initialize the bytestream and to prepare it for data transfer, register a + * BytestreamDataHandler with it and call its connect() method. + * To not block your application while the data transfer lasts, you most + * likely want to put the bytestream into its own thread or process (before calling connect() on it). + * It is safe to do so without additional synchronization. + * When you are finished using the bytestream, use SIProfileFT::dispose() to get rid of it. + * @param bs The bytestream. + */ + virtual void handleIncomingBytestream( Bytestream* bs ) = 0; + + /** + * Notifies the implementor of successful establishing of an outgoing bytestream request. + * The stream has been accepted by the remote entity and is ready to send data. + * The BytestreamHandler does @b not become the owner of the Bytestream object. + * Use SIProfileFT::dispose() to get rid of the bytestream object after it has been closed. + * @param bs The new bytestream. + */ + virtual void handleOutgoingBytestream( Bytestream* bs ) = 0; + + /** + * Notifies the handler of errors occuring when a bytestream was requested. + * For example, if the remote entity does not implement SOCKS5 bytestreams. + * @param iq The error stanza. + * @param sid The request's SID. + */ + virtual void handleBytestreamError( const IQ& iq, const std::string& sid ) = 0; + + }; + +} + +#endif // BYTESTREAMHANDLER_H__ diff --git a/libs/libgloox/capabilities.cpp b/libs/libgloox/capabilities.cpp new file mode 100644 index 0000000..0bdbc00 --- /dev/null +++ b/libs/libgloox/capabilities.cpp @@ -0,0 +1,183 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "capabilities.h" + +#include "base64.h" +#include "disco.h" +#include "dataform.h" +#include "sha.h" +#include "tag.h" + +namespace gloox +{ + + Capabilities::Capabilities( Disco* disco ) + : StanzaExtension( ExtCaps ), m_disco( disco ), m_node( GLOOX_CAPS_NODE ), + m_hash( "sha-1" ), m_valid( false ) + { + if( m_disco ) + m_valid = true; + } + + Capabilities::Capabilities( const Tag* tag ) + : StanzaExtension( ExtCaps ), m_disco( 0 ), m_valid( false ) + { + if( !tag || tag->name() != "c" || !tag->hasAttribute( XMLNS, XMLNS_CAPS ) + || !tag->hasAttribute( "node" ) || !tag->hasAttribute( "ver" ) ) + return; + + m_node = tag->findAttribute( "node" ); + m_ver = tag->findAttribute( "ver" ); + m_hash = tag->findAttribute( "hash" ); + m_valid = true; + } + + Capabilities::~Capabilities() + { + if( m_disco ) + m_disco->removeNodeHandlers( const_cast( this ) ); + } + + const std::string Capabilities::ver() const + { + if( !m_disco ) + return m_ver; + + SHA sha; + sha.feed( generate( m_disco->identities(), m_disco->features( true ), m_disco->form() ) ); + const std::string& hash = Base64::encode64( sha.binary() ); + m_disco->removeNodeHandlers( const_cast( this ) ); + m_disco->registerNodeHandler( const_cast( this ), m_node + '#' + hash ); + return hash; + } + + std::string Capabilities::generate( const Disco::IdentityList& il, const StringList& features, const DataForm* form ) + { + StringList sl; + Disco::IdentityList::const_iterator it = il.begin(); + for( ; it != il.end(); ++it ) + { + std::string id = (*it)->category(); + id += '/'; + id += (*it)->type(); + id += '/'; + // FIXME add xml:lang caps here. see XEP-0115 Section 5 + id += '/'; + id += (*it)->name(); + sl.push_back( id ); + } + sl.sort(); + + std::string s; + StringList::const_iterator it2 = sl.begin(); + for( ; it2 != sl.end(); ++it2 ) + { + s += (*it2); + s += '<'; + } + + StringList f = features; + f.sort(); + it2 = f.begin(); + for( ; it2 != f.end(); ++it2 ) + { + s += (*it2); + s += '<'; + } + + if( form ) + { + DataForm::FieldList::const_iterator it3 = form->fields().begin(); + typedef std::map MapSSL; + + MapSSL m; + for( ; it3 != form->fields().end(); ++it3 ) + { + if( (*it3)->name() == "FORM_TYPE" ) + { + s += (*it3)->value(); + s += '<'; + } + else + m.insert( std::make_pair( (*it3)->name(), (*it3)->values() ) ); + } + + MapSSL::iterator it4 = m.begin(); + for( ; it4 != m.end(); ++it4 ) + { + s += it4->first; + s += '<'; + it2 = it4->second.begin(); + for( ; it2 != it4->second.end(); ++it2 ) + { + s += (*it2); + s += '<'; + } + } + } + return s; + } + + std::string Capabilities::generate( const Disco::Info* info ) + { + return info ? generate( info->identities(), info->features(), info->form() ) : EmptyString; + } + + std::string Capabilities::generate( const Disco* disco ) + { + return disco ? generate( disco->identities(), disco->features(), disco->form() ) : EmptyString; + } + + const std::string& Capabilities::filterString() const + { + static const std::string filter = "/presence/c[@xmlns='" + XMLNS_CAPS + "']"; + return filter; + } + + Tag* Capabilities::tag() const + { + if( !m_valid || m_node.empty() ) + return 0; + + Tag* t = new Tag( "c" ); + t->setXmlns( XMLNS_CAPS ); + t->addAttribute( "hash", m_hash ); + t->addAttribute( "node", m_node ); + t->addAttribute( "ver", ver() ); + return t; + } + + StringList Capabilities::handleDiscoNodeFeatures( const JID&, const std::string& ) + { + return m_disco->features(); + } + + Disco::IdentityList Capabilities::handleDiscoNodeIdentities( const JID&, const std::string& ) + { + const Disco::IdentityList& il = m_disco->identities(); + Disco::IdentityList ret; + Disco::IdentityList::const_iterator it = il.begin(); + for( ; it != il.end(); ++it ) + { + ret.push_back( new Disco::Identity( *(*it) ) ); + } + return ret; + } + + Disco::ItemList Capabilities::handleDiscoNodeItems( const JID&, const JID&, const std::string& ) + { + return Disco::ItemList(); + } + +} diff --git a/libs/libgloox/capabilities.h b/libs/libgloox/capabilities.h new file mode 100644 index 0000000..a5d63e3 --- /dev/null +++ b/libs/libgloox/capabilities.h @@ -0,0 +1,132 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef CAPABILITIES_H__ +#define CAPABILITIES_H__ + +#include "disconodehandler.h" +#include "stanzaextension.h" +#include "tag.h" + +#include + +namespace gloox +{ + + class Disco; + class Tag; + + /** + * @brief This is an implementation of XEP-0115 (Entity Capabilities). + * + * XEP Version: 1.5-15 + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API Capabilities : public StanzaExtension, public DiscoNodeHandler + { + + public: + /** + * Constructs a new object and fills it according to the parameters. + * @param disco The current Client's Disco object. + */ + Capabilities( Disco* disco ); + + /** + * Constructs a new object from the given Tag. + * @param tag The Tag to parse. + */ + Capabilities( const Tag* tag = 0 ); + + /** + * Virtual Destructor. + */ + virtual ~Capabilities(); + + /** + * Returns the client's identifying node. + * @return The node. + */ + const std::string& node() const { return m_node; } + + /** + * Sets the client's identifying node. + * @param node The node. + */ + void setNode( const std::string& node ) { m_node = node; } + + /** + * Returns the client's identifying ver string. + * @return The ver string. + */ + const std::string ver() const; + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new Capabilities( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new Capabilities( *this ); + } + + // reimplemented from DiscoNodeHandler + virtual StringList handleDiscoNodeFeatures( const JID& from, const std::string& node ); + + // reimplemented from DiscoNodeHandler + virtual Disco::IdentityList handleDiscoNodeIdentities( const JID& from, + const std::string& node ); + + // reimplemented from DiscoNodeHandler + virtual Disco::ItemList handleDiscoNodeItems( const JID& from, const JID& to, + const std::string& node = EmptyString ); + + private: + /** + * Returns the hash function used for creating the caps info. + * @return The current hash function's name. + */ + const std::string& hash() const { return m_hash; } + + /** + * Use this function to set the hash function to use. + * @param hash The hash function. + * @todo Convert to using an enum and make public. + */ + void setHash( const std::string& hash ) { m_hash = hash; } + + static std::string generate( const Disco::IdentityList& identities, + const StringList& features, const DataForm* form = 0 ); + static std::string generate( const Disco::Info* info ); + static std::string generate( const Disco* disco ); + + Disco* m_disco; + std::string m_node; + std::string m_hash; + std::string m_ver; + bool m_valid; + }; + +} + +#endif // CAPABILITIES_H__ diff --git a/libs/libgloox/chatstate.cpp b/libs/libgloox/chatstate.cpp new file mode 100644 index 0000000..8a2a400 --- /dev/null +++ b/libs/libgloox/chatstate.cpp @@ -0,0 +1,60 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#include "chatstate.h" +#include "tag.h" +#include "util.h" + +namespace gloox +{ + + /* chat state type values */ + static const char* stateValues [] = { + "active", + "composing", + "paused", + "inactive", + "gone" + }; + + static inline ChatStateType chatStateType( const std::string& type ) + { + return (ChatStateType)util::lookup2( type, stateValues ); + } + + ChatState::ChatState( const Tag* tag ) + : StanzaExtension( ExtChatState ) + { + if( tag ) + m_state = chatStateType( tag->name() ); + } + + const std::string& ChatState::filterString() const + { + static const std::string filter = + "/message/active[@xmlns='" + XMLNS_CHAT_STATES + "']" + "|/message/composing[@xmlns='" + XMLNS_CHAT_STATES + "']" + "|/message/paused[@xmlns='" + XMLNS_CHAT_STATES + "']" + "|/message/inactive[@xmlns='" + XMLNS_CHAT_STATES + "']" + "|/message/gone[@xmlns='" + XMLNS_CHAT_STATES + "']"; + return filter; + } + + Tag* ChatState::tag() const + { + if( m_state == ChatStateInvalid ) + return 0; + + return new Tag( util::lookup2( m_state, stateValues ), XMLNS, XMLNS_CHAT_STATES ); + } + +} diff --git a/libs/libgloox/chatstate.h b/libs/libgloox/chatstate.h new file mode 100644 index 0000000..6f1c426 --- /dev/null +++ b/libs/libgloox/chatstate.h @@ -0,0 +1,87 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#ifndef CHATSTATE_H__ +#define CHATSTATE_H__ + +#include "gloox.h" +#include "stanzaextension.h" + +#include + +namespace gloox +{ + + class Tag; + + /** + * @brief An implementation of Chat State Notifications (XEP-0085) as a StanzaExtension. + * + * @author Vincent Thomasset + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API ChatState : public StanzaExtension + { + public: + + /** + * Constructs a new object from the given Tag. + * @param tag A Tag to parse. + */ + ChatState( const Tag* tag ); + + /** + * Constructs a new object of the given type. + * @param state The chat state. + */ + ChatState( ChatStateType state ) + : StanzaExtension( ExtChatState ), m_state( state ) + {} + + /** + * Virtual destructor. + */ + virtual ~ChatState() {} + + /** + * Returns the object's state. + * @return The object's state. + */ + ChatStateType state() const { return m_state; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new ChatState( tag ); + } + + // reimplemented from StanzaExtension + Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new ChatState( *this ); + } + + private: + ChatStateType m_state; + + }; + +} + +#endif // CHATSTATE_H__ diff --git a/libs/libgloox/chatstatefilter.cpp b/libs/libgloox/chatstatefilter.cpp new file mode 100644 index 0000000..8644689 --- /dev/null +++ b/libs/libgloox/chatstatefilter.cpp @@ -0,0 +1,65 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "chatstatefilter.h" +#include "chatstatehandler.h" +#include "messageeventhandler.h" +#include "messagesession.h" +#include "message.h" +#include "chatstate.h" + +namespace gloox +{ + + ChatStateFilter::ChatStateFilter( MessageSession* parent ) + : MessageFilter( parent ), m_chatStateHandler( 0 ), m_lastSent( ChatStateGone ), + m_enableChatStates( true ) + { + } + + ChatStateFilter::~ChatStateFilter() + { + } + + void ChatStateFilter::filter( Message& msg ) + { + if( m_enableChatStates && m_chatStateHandler ) + { + const ChatState* state = msg.findExtension( ExtChatState ); + + m_enableChatStates = state && state->state() != ChatStateInvalid; + if( m_enableChatStates && msg.body().empty() ) + m_chatStateHandler->handleChatState( msg.from(), state->state() ); + } + } + + void ChatStateFilter::setChatState( ChatStateType state ) + { + if( !m_enableChatStates || state == m_lastSent || state == ChatStateInvalid ) + return; + + Message m( Message::Chat, m_parent->target() ); + m.addExtension( new ChatState( state ) ); + + m_lastSent = state; + + send( m ); + } + + void ChatStateFilter::decorate( Message& msg ) + { + if( m_enableChatStates ) + msg.addExtension( new ChatState( ChatStateActive ) ); + } + +} diff --git a/libs/libgloox/chatstatefilter.h b/libs/libgloox/chatstatefilter.h new file mode 100644 index 0000000..b0f5a6b --- /dev/null +++ b/libs/libgloox/chatstatefilter.h @@ -0,0 +1,102 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef CHATSTATEFILTER_H__ +#define CHATSTATEFILTER_H__ + +#include "messagefilter.h" +#include "gloox.h" + +namespace gloox +{ + + class Tag; + class ChatStateHandler; + class MessageSession; + class Message; + + /** + * @brief This class adds Chat State Notifications (XEP-0085) support to a MessageSession. + * + * This implementation of Chat States is fully transparent to the user of the class. + * If the remote entity does not request chat states, ChatStateFilter will not send + * any, even if the user requests it. (This is required by the protocol specification.) + * You MUST annouce this capability by use of Disco (associated namespace is XMLNS_CHAT_STATES). + * (This is also required by the protocol specification.) + * + * @author Jakob Schroeter + * @since 0.8 + */ + class GLOOX_API ChatStateFilter : public MessageFilter + { + public: + /** + * Contstructs a new Chat State filter for a MessageSession. + * @param parent The MessageSession to decorate. + */ + ChatStateFilter( MessageSession* parent ); + + /** + * Virtual destructor. + */ + virtual ~ChatStateFilter(); + + /** + * Use this function to set a chat state as defined in XEP-0085. + * @note The Spec states that Chat States shall not be sent to an entity + * which did not request them. Reasonable effort is taken in this function to + * avoid spurious state sending. You should be safe to call this even if Message + * Events were not requested by the remote entity. However, + * calling setChatState( CHAT_STATE_COMPOSING ) for every keystroke still is + * discouraged. ;) + * @param state The state to set. + */ + void setChatState( ChatStateType state ); + + /** + * The ChatStateHandler registered here will receive Chat States according + * to XEP-0085. + * @param csh The ChatStateHandler to register. + */ + void registerChatStateHandler( ChatStateHandler* csh ) + { m_chatStateHandler = csh; } + + /** + * This function clears the internal pointer to the ChatStateHandler. + * Chat States will not be delivered anymore after calling this function until another + * ChatStateHandler is registered. + */ + void removeChatStateHandler() + { m_chatStateHandler = 0; } + + // reimplemented from MessageFilter + virtual void decorate( Message& msg ); + + // reimplemented from MessageFilter + virtual void filter( Message& msg ); + + protected: + /** A handler for incoming chat state changes. */ + ChatStateHandler* m_chatStateHandler; + + /** Holds the state sent last. */ + ChatStateType m_lastSent; + + /** Indicates whether or not chat states are currently enabled. */ + bool m_enableChatStates; + + }; + +} + +#endif // CHATSTATEFILTER_H__ diff --git a/libs/libgloox/chatstatehandler.h b/libs/libgloox/chatstatehandler.h new file mode 100644 index 0000000..8c302c8 --- /dev/null +++ b/libs/libgloox/chatstatehandler.h @@ -0,0 +1,51 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef CHATSTATEHANDLER_H__ +#define CHATSTATEHANDLER_H__ + +#include "gloox.h" + +namespace gloox +{ + + class JID; + + /** + * @brief A virtual interface that enables an object to be notified about + * a remote entity's Chat States (XEP-0085). + * + * @author Jakob Schroeter + * @since 0.8 + */ + class GLOOX_API ChatStateHandler + { + public: + /** + * Virtual Destructor. + */ + virtual ~ChatStateHandler() {} + + /** + * Notifies the ChatStateHandler that a different chat state has been set by the remote + * contact. + * @param from The originator of the Event. + * @param state The chat state set by the remote entity. + */ + virtual void handleChatState( const JID& from, ChatStateType state ) = 0; + + }; + +} + +#endif // CHATSTATEHANDLER_H__ diff --git a/libs/libgloox/client.cpp b/libs/libgloox/client.cpp new file mode 100644 index 0000000..6df569d --- /dev/null +++ b/libs/libgloox/client.cpp @@ -0,0 +1,603 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#include "config.h" + +#include "client.h" +#include "capabilities.h" +#include "rostermanager.h" +#include "disco.h" +#include "error.h" +#include "logsink.h" +#include "nonsaslauth.h" +#include "prep.h" +#include "stanzaextensionfactory.h" +#include "stanzaextension.h" +#include "tag.h" +#include "tlsbase.h" +#include "util.h" + +#if !defined( _WIN32 ) && !defined( _WIN32_WCE ) +# include +#endif + +#include + +namespace gloox +{ + + // ---- Client::ResourceBind ---- + Client::ResourceBind::ResourceBind( const std::string& resource, bool bind ) + : StanzaExtension( ExtResourceBind ), m_jid( JID() ), m_bind( bind ) + { + prep::resourceprep( resource, m_resource ); + m_valid = true; + } + + Client::ResourceBind::ResourceBind( const Tag* tag ) + : StanzaExtension( ExtResourceBind ), m_resource( EmptyString ), m_bind( true ) + { + if( !tag ) + return; + + if( tag->name() == "unbind" ) + m_bind = false; + else if( tag->name() == "bind" ) + m_bind = true; + else + return; + + if( tag->hasChild( "jid" ) ) + m_jid.setJID( tag->findChild( "jid" )->cdata() ); + else if( tag->hasChild( "resource" ) ) + m_resource = tag->findChild( "resource" )->cdata(); + + m_valid = true; + } + + Client::ResourceBind::~ResourceBind() + { + } + + const std::string& Client::ResourceBind::filterString() const + { + static const std::string filter = "/iq/bind[@xmlns='" + XMLNS_STREAM_BIND + "']" + "|/iq/unbind[@xmlns='" + XMLNS_STREAM_BIND + "']"; + return filter; + } + + Tag* Client::ResourceBind::tag() const + { + if( !m_valid ) + return 0; + + Tag* t = new Tag( m_bind ? "bind" : "unbind" ); + t->setXmlns( XMLNS_STREAM_BIND ); + + if( m_bind && m_resource.empty() && m_jid ) + new Tag( t, "jid", m_jid.full() ); + else + new Tag( t, "resource", m_resource ); + + return t; + } + // ---- ~Client::ResourceBind ---- + + // ---- Client::SessionCreation ---- + Tag* Client::SessionCreation::tag() const + { + Tag* t = new Tag( "session" ); + t->setXmlns( XMLNS_STREAM_SESSION ); + return t; + } + // ---- Client::SessionCreation ---- + + // ---- Client ---- + Client::Client( const std::string& server ) + : ClientBase( XMLNS_CLIENT, server ), + m_rosterManager( 0 ), m_auth( 0 ), + m_presence( Presence::Available, JID() ), m_resourceBound( false ), + m_forceNonSasl( false ), m_manageRoster( true ), + m_streamFeatures( 0 ) + { + m_jid.setServer( server ); + init(); + } + + Client::Client( const JID& jid, const std::string& password, int port ) + : ClientBase( XMLNS_CLIENT, password, EmptyString, port ), + m_rosterManager( 0 ), m_auth( 0 ), + m_presence( Presence::Available, JID() ), m_resourceBound( false ), + m_forceNonSasl( false ), m_manageRoster( true ), + m_streamFeatures( 0 ) + { + m_jid = jid; + m_server = m_jid.serverRaw(); + init(); + } + + Client::~Client() + { + delete m_rosterManager; + delete m_auth; + } + + void Client::init() + { + m_rosterManager = new RosterManager( this ); + m_disco->setIdentity( "client", "bot" ); + registerStanzaExtension( new ResourceBind( 0 ) ); + registerStanzaExtension( new Capabilities() ); + m_presenceExtensions.push_back( new Capabilities( m_disco ) ); + } + + void Client::setUsername( const std::string &username ) + { + m_jid.setUsername( username ); + } + + bool Client::handleNormalNode( Tag* tag ) + { + if( tag->name() == "features" && tag->xmlns() == XMLNS_STREAM ) + { + m_streamFeatures = getStreamFeatures( tag ); + + if( m_tls == TLSRequired && !m_encryptionActive + && ( !m_encryption || !( m_streamFeatures & StreamFeatureStartTls ) ) ) + { + logInstance().err( LogAreaClassClient, "Client is configured to require" + " TLS but either the server didn't offer TLS or" + " TLS support is not compiled in." ); + disconnect( ConnTlsNotAvailable ); + } + else if( m_tls > TLSDisabled && m_encryption && !m_encryptionActive + && ( m_streamFeatures & StreamFeatureStartTls ) ) + { + notifyStreamEvent( StreamEventEncryption ); + startTls(); + } + else if( m_compress && m_compression && !m_compressionActive + && ( m_streamFeatures & StreamFeatureCompressZlib ) ) + { + notifyStreamEvent( StreamEventCompression ); + logInstance().warn( LogAreaClassClient, "The server offers compression, but negotiating Compression at this stage is not recommended. See XEP-0170 for details. We'll continue anyway." ); + negotiateCompression( StreamFeatureCompressZlib ); + } + else if( m_sasl ) + { + if( m_authed ) + { + if( m_streamFeatures & StreamFeatureBind ) + { + notifyStreamEvent( StreamEventResourceBinding ); + bindResource( resource() ); + } + } + else if( !username().empty() && !password().empty() ) + { + if( !login() ) + { + logInstance().err( LogAreaClassClient, "The server doesn't support" + " any auth mechanisms we know about" ); + disconnect( ConnNoSupportedAuth ); + } + } + else if( !m_clientCerts.empty() && !m_clientKey.empty() + && m_streamFeatures & SaslMechExternal && m_availableSaslMechs & SaslMechExternal ) + { + notifyStreamEvent( StreamEventAuthentication ); + startSASL( SaslMechExternal ); + } +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + else if( m_streamFeatures & SaslMechGssapi && m_availableSaslMechs & SaslMechGssapi ) + { + notifyStreamEvent( StreamEventAuthentication ); + startSASL( SaslMechGssapi ); + } + else if( m_streamFeatures & SaslMechNTLM && m_availableSaslMechs & SaslMechNTLM ) + { + notifyStreamEvent( StreamEventAuthentication ); + startSASL( SaslMechNTLM ); + } +#endif + else if( m_streamFeatures & SaslMechAnonymous + && m_availableSaslMechs & SaslMechAnonymous ) + { + notifyStreamEvent( StreamEventAuthentication ); + startSASL( SaslMechAnonymous ); + } + else + { + notifyStreamEvent( StreamEventFinished ); + connected(); + } + } + else if( m_compress && m_compression && !m_compressionActive + && ( m_streamFeatures & StreamFeatureCompressZlib ) ) + { + notifyStreamEvent( StreamEventCompression ); + negotiateCompression( StreamFeatureCompressZlib ); + } +// else if( ( m_streamFeatures & StreamFeatureCompressDclz ) +// && m_connection->initCompression( StreamFeatureCompressDclz ) ) +// { +// negotiateCompression( StreamFeatureCompressDclz ); +// } + else if( m_streamFeatures & StreamFeatureIqAuth ) + { + notifyStreamEvent( StreamEventAuthentication ); + nonSaslLogin(); + } + else + { + logInstance().err( LogAreaClassClient, "fallback: the server doesn't " + "support any auth mechanisms we know about" ); + disconnect( ConnNoSupportedAuth ); + } + } + else + { + const std::string& name = tag->name(), + xmlns = tag->findAttribute( XMLNS ); + if( name == "proceed" && xmlns == XMLNS_STREAM_TLS ) + { + logInstance().dbg( LogAreaClassClient, "starting TLS handshake..." ); + + if( m_encryption ) + { + m_encryptionActive = true; + m_encryption->handshake(); + } + } + else if( name == "failure" ) + { + if( xmlns == XMLNS_STREAM_TLS ) + { + logInstance().err( LogAreaClassClient, "TLS handshake failed (server-side)!" ); + disconnect( ConnTlsFailed ); + } + else if( xmlns == XMLNS_COMPRESSION ) + { + logInstance().err( LogAreaClassClient, "Stream compression init failed!" ); + disconnect( ConnCompressionFailed ); + } + else if( xmlns == XMLNS_STREAM_SASL ) + { + logInstance().err( LogAreaClassClient, "SASL authentication failed!" ); + processSASLError( tag ); + disconnect( ConnAuthenticationFailed ); + } + } + else if( name == "compressed" && xmlns == XMLNS_COMPRESSION ) + { + logInstance().dbg( LogAreaClassClient, "Stream compression initialized" ); + m_compressionActive = true; + header(); + } + else if( name == "challenge" && xmlns == XMLNS_STREAM_SASL ) + { + logInstance().dbg( LogAreaClassClient, "Processing SASL challenge" ); + processSASLChallenge( tag->cdata() ); + } + else if( name == "success" && xmlns == XMLNS_STREAM_SASL ) + { + logInstance().dbg( LogAreaClassClient, "SASL authentication successful" ); + processSASLSuccess(); + setAuthed( true ); + header(); + } + else + return false; + } + + return true; + } + + int Client::getStreamFeatures( Tag* tag ) + { + if( tag->name() != "features" || tag->xmlns() != XMLNS_STREAM ) + return 0; + + int features = 0; + + if( tag->hasChild( "starttls", XMLNS, XMLNS_STREAM_TLS ) ) + features |= StreamFeatureStartTls; + + if( tag->hasChild( "mechanisms", XMLNS, XMLNS_STREAM_SASL ) ) + features |= getSaslMechs( tag->findChild( "mechanisms" ) ); + + if( tag->hasChild( "bind", XMLNS, XMLNS_STREAM_BIND ) ) + features |= StreamFeatureBind; + + if( tag->hasChild( "unbind", XMLNS, XMLNS_STREAM_BIND ) ) + features |= StreamFeatureUnbind; + + if( tag->hasChild( "session", XMLNS, XMLNS_STREAM_SESSION ) ) + features |= StreamFeatureSession; + + if( tag->hasChild( "auth", XMLNS, XMLNS_STREAM_IQAUTH ) ) + features |= StreamFeatureIqAuth; + + if( tag->hasChild( "register", XMLNS, XMLNS_STREAM_IQREGISTER ) ) + features |= StreamFeatureIqRegister; + + if( tag->hasChild( "compression", XMLNS, XMLNS_STREAM_COMPRESS ) ) + features |= getCompressionMethods( tag->findChild( "compression" ) ); + + if( features == 0 ) + features = StreamFeatureIqAuth; + + return features; + } + + int Client::getSaslMechs( Tag* tag ) + { + int mechs = SaslMechNone; + + const std::string mech = "mechanism"; + + if( tag->hasChildWithCData( mech, "DIGEST-MD5" ) ) + mechs |= SaslMechDigestMd5; + + if( tag->hasChildWithCData( mech, "PLAIN" ) ) + mechs |= SaslMechPlain; + + if( tag->hasChildWithCData( mech, "ANONYMOUS" ) ) + mechs |= SaslMechAnonymous; + + if( tag->hasChildWithCData( mech, "EXTERNAL" ) ) + mechs |= SaslMechExternal; + + if( tag->hasChildWithCData( mech, "GSSAPI" ) ) + mechs |= SaslMechGssapi; + + if( tag->hasChildWithCData( mech, "NTLM" ) ) + mechs |= SaslMechNTLM; + + return mechs; + } + + int Client::getCompressionMethods( Tag* tag ) + { + int meths = 0; + + if( tag->hasChildWithCData( "method", "zlib" ) ) + meths |= StreamFeatureCompressZlib; + + if( tag->hasChildWithCData( "method", "lzw" ) ) + meths |= StreamFeatureCompressDclz; + + return meths; + } + + bool Client::login() + { + bool retval = true; + + if( m_streamFeatures & SaslMechDigestMd5 && m_availableSaslMechs & SaslMechDigestMd5 + && !m_forceNonSasl ) + { + notifyStreamEvent( StreamEventAuthentication ); + startSASL( SaslMechDigestMd5 ); + } + else if( m_streamFeatures & SaslMechPlain && m_availableSaslMechs & SaslMechPlain + && !m_forceNonSasl ) + { + notifyStreamEvent( StreamEventAuthentication ); + startSASL( SaslMechPlain ); + } + else if( m_streamFeatures & StreamFeatureIqAuth || m_forceNonSasl ) + { + notifyStreamEvent( StreamEventAuthentication ); + nonSaslLogin(); + } + else + retval = false; + + return retval; + } + + void Client::handleIqIDForward( const IQ& iq, int context ) + { + switch( context ) + { + case CtxResourceUnbind: + // we don't store known resources anyway + break; + case CtxResourceBind: + processResourceBind( iq ); + break; + case CtxSessionEstablishment: + processCreateSession( iq ); + break; + default: + break; + } + } + + bool Client::bindOperation( const std::string& resource, bool bind ) + { + if( !( m_streamFeatures & StreamFeatureUnbind ) && m_resourceBound ) + return false; + + IQ iq( IQ::Set, JID(), getID() ); + iq.addExtension( new ResourceBind( resource, bind ) ); + + send( iq, this, bind ? CtxResourceBind : CtxResourceUnbind ); + return true; + } + + bool Client::selectResource( const std::string& resource ) + { + if( !( m_streamFeatures & StreamFeatureUnbind ) ) + return false; + + m_selectedResource = resource; + + return true; + } + + void Client::processResourceBind( const IQ& iq ) + { + switch( iq.subtype() ) + { + case IQ::Result: + { + const ResourceBind* rb = iq.findExtension( ExtResourceBind ); + if( !rb || !rb->jid() ) + { + notifyOnResourceBindError( 0 ); + break; + } + + m_jid = rb->jid(); + m_resourceBound = true; + m_selectedResource = m_jid.resource(); + notifyOnResourceBind( m_jid.resource() ); + + if( m_streamFeatures & StreamFeatureSession ) + createSession(); + else + connected(); + break; + } + case IQ::Error: + { + notifyOnResourceBindError( iq.error() ); + break; + } + default: + break; + } + } + + void Client::createSession() + { + notifyStreamEvent( StreamEventSessionCreation ); + IQ iq( IQ::Set, JID(), getID() ); + iq.addExtension( new SessionCreation() ); + send( iq, this, CtxSessionEstablishment ); + } + + void Client::processCreateSession( const IQ& iq ) + { + switch( iq.subtype() ) + { + case IQ::Result: + connected(); + break; + case IQ::Error: + notifyOnSessionCreateError( iq.error() ); + break; + default: + break; + } + } + + void Client::negotiateCompression( StreamFeature method ) + { + Tag* t = new Tag( "compress", XMLNS, XMLNS_COMPRESSION ); + + if( method == StreamFeatureCompressZlib ) + new Tag( t, "method", "zlib" ); + + if( method == StreamFeatureCompressDclz ) + new Tag( t, "method", "lzw" ); + + send( t ); + } + + void Client::setPresence( Presence::PresenceType pres, int priority, + const std::string& status ) + { + m_presence.setPresence( pres ); + m_presence.setPriority( priority ); + m_presence.addStatus( status ); + sendPresence( m_presence ); + } + + void Client::setPresence( const JID& to, Presence::PresenceType pres, int priority, + const std::string& status ) + { + Presence p( pres, to, status, priority ); + sendPresence( p ); + } + + void Client::sendPresence( Presence& pres ) + { + if( state() < StateConnected ) + return; + + send( pres ); + } + + void Client::disableRoster() + { + m_manageRoster = false; + delete m_rosterManager; + m_rosterManager = 0; + } + + void Client::nonSaslLogin() + { + if( !m_auth ) + m_auth = new NonSaslAuth( this ); + m_auth->doAuth( m_sid ); + } + + void Client::connected() + { + if( m_authed ) + { + if( m_manageRoster ) + { + notifyStreamEvent( StreamEventRoster ); + m_rosterManager->fill(); + } + else + rosterFilled(); + } + else + { + notifyStreamEvent( StreamEventFinished ); + notifyOnConnect(); + } + } + + void Client::rosterFilled() + { + sendPresence( m_presence ); + notifyStreamEvent( StreamEventFinished ); + notifyOnConnect(); + } + + void Client::disconnect() + { + disconnect( ConnUserDisconnected ); + } + + void Client::disconnect( ConnectionError reason ) + { + m_resourceBound = false; + m_authed = false; + m_streamFeatures = 0; + ClientBase::disconnect( reason ); + } + + void Client::cleanup() + { + m_authed = false; + m_resourceBound = false; + m_streamFeatures = 0; + } + +} diff --git a/libs/libgloox/client.h b/libs/libgloox/client.h new file mode 100644 index 0000000..5eb53f3 --- /dev/null +++ b/libs/libgloox/client.h @@ -0,0 +1,441 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef CLIENT_H__ +#define CLIENT_H__ + +#include "clientbase.h" +#include "presence.h" + +#include + +namespace gloox +{ + + class Capabilities; + class RosterManager; + class NonSaslAuth; + class IQ; + + /** + * @brief This class implements a basic Jabber Client. + * + * It supports @ref sasl_auth as well as TLS (Encryption), which can be + * switched on/off separately. They are used automatically if the server supports them. + * + * To use, create a new Client instance and feed it connection credentials, either in the Constructor or + * afterwards using the setters. You should then register packet handlers implementing the corresponding + * Interfaces (ConnectionListener, PresenceHandler, MessageHandler, IqHandler, SubscriptionHandler), + * and call @ref connect() to establish the connection to the server.
+ * + * @note While the MessageHandler interface is still available (and will be in future versions) + * it is now recommended to use the new @link gloox::MessageSession MessageSession @endlink for any + * serious messaging. + * + * Simple usage example: + * @code + * using namespace gloox; + * + * void TestProg::doIt() + * { + * Client* j = new Client( "user@server/resource", "password" ); + * j->registerPresenceHandler( this ); + * j->disco()->setVersion( "TestProg", "1.0" ); + * j->disco()->setIdentity( "client", "bot" ); + * j->connect(); + * } + * + * virtual void TestProg::presenceHandler( Presence* presence ) + * { + * // handle incoming presence packets here + * } + * @endcode + * + * However, you can skip the presence handling stuff if you make use of the RosterManager. + * + * By default, the library handles a few (incoming) IQ namespaces on the application's behalf. These + * include: + * @li jabber:iq:roster: by default the server-side roster is fetched and handled. Use + * @ref rosterManager() and @ref RosterManager to interact with the Roster. + * @li XEP-0092 (Software Version): If no version is specified, a name of "based on gloox" with + * gloox's current version is announced. + * @li XEP-0030 (Service Discovery): All supported/available services are announced. No items are + * returned. + * @note As of gloox 0.9, by default a priority of 0 is sent along with the initial presence. + * @note As of gloox 0.9, initial presence is automatically sent. Presence: available, Priority: 0. + * To disable sending of initial Presence use setPresence() with a value of Unavailable + * prior to connecting. + * + * @section sasl_auth SASL Authentication + * + * Besides the simple, IQ-based authentication (XEP-0078), gloox supports several SASL (Simple + * Authentication and Security Layer, RFC 2222) authentication mechanisms. + * @li DIGEST-MD5: This mechanism is preferred over all other mechanisms if username and password are + * provided to the Client instance. It is secure even without TLS encryption. + * @li PLAIN: This mechanism is used if DIGEST-MD5 is not available. It is @b not secure without + * encryption. + * @li ANONYMOUS This mechanism is used if neither username nor password are set. The server generates + * random, temporary username and resource and may restrict available services. + * @li EXTERNAL This mechanism is currently only available if client certificate and private key + * are provided. The server tries to figure out who the client is by external means -- for instance, + * using the provided certificate or even the IP address. (The restriction to certificate/key + * availability is likely to be lifted in the future.) + * + * Of course, all these mechanisms are not tried unless the server offers them. + * + * @author Jakob Schroeter + */ + class GLOOX_API Client : public ClientBase + { + public: + + friend class NonSaslAuth; + friend class Parser; + + /** + * Constructs a new Client which can be used for account registration only. + * SASL and TLS are on by default. The port will be determined by looking up SRV records. + * Alternatively, you can set the port explicitly by calling @ref setPort(). + * @param server The server to connect to. + */ + Client( const std::string& server ); + + /** + * Constructs a new Client. + * SASL and TLS are on by default. This should be the default constructor for most use cases. + * The server address will be taken from the JID. The actual host will be resolved using SRV + * records. The domain part of the JID is used as a fallback in case no SRV record is found, or + * you can set the server address separately by calling @ref setServer(). + * @param jid A full Jabber ID used for connecting to the server. + * @param password The password used for authentication. + * @param port The port to connect to. The default of -1 means to look up the port via DNS SRV. + */ + Client( const JID& jid, const std::string& password, int port = -1 ); + + /** + * Virtual destructor. + */ + virtual ~Client(); + + /** + * Use this function to bind an additional resource or to @b re-try to bind a + * resource in case previous binding failed and you were notified by means of + * ConnectionListener::onResourceBindError(). Use hasResourceBind() to find out if the + * server supports binding of multiple resources. bindResource() is a NOOP if it doesn't. + * @note ConnectionListener::onResourceBound() and ConnectionListener::onResourceBindError() + * will be called in case of success and failure, respectively. + * @param resource The resource identifier to bind. May be empty. In that case + * the server will assign a unique resource identifier. + * @return Returns @b true if binding of multiple resources is supported, @b false + * otherwise. A return value of @b true does not indicate that the resource was + * successfully bound. + * @note It is not necessary to call this function to bind the initial, main, resource. + * @since 1.0 + */ + bool bindResource( const std::string& resource ) + { return bindOperation( resource, true ); } + + /** + * Use this function to select a resource identifier that has been bound + * previously by means of bindResource(). It is not necessary to call this function + * if only one resource is bound. Use hasResourceBind() to find out if the + * server supports binding of multiple resources. selectResource() is a NOOP if it doesn't. + * @param resource A resource string that has been bound previously. + * @note If the resource string has not been bound previously, future sending of + * stanzas will fail. + */ + bool selectResource( const std::string& resource ); + + /** + * This function can be used to find out whether the server supports binding of multiple + * resources. + * @return @b True if binding of multiple resources is supported by the server, + * @b false otherwise. + */ + bool hasResourceBind() const { return ((m_streamFeatures & StreamFeatureUnbind) == StreamFeatureUnbind); } + + /** + * Use this function to unbind a resource identifier that has been bound + * previously by means of bindResource(). Use hasResourceBind() to find out if the + * server supports binding of multiple resources. unbindResource() is a NOOP if it doesn't. + * @param resource A resource string that has been bound previously. + * @note Servers are encouraged to terminate the connection should the only bound + * resource be unbound. + */ + bool unbindResource( const std::string& resource ) + { return bindOperation( resource, false ); } + + /** + * Returns the current prepped main resource. + * @return The resource used to connect. + */ + const std::string& resource() const { return m_jid.resource(); } + + /** + * Returns the current priority. + * @return The priority of the current resource. + */ + int priority() const { return m_presence.priority(); } + + /** + * Sets the username to use to connect to the XMPP server. + * @param username The username to authenticate with. + */ + void setUsername( const std::string &username ); + + /** + * Sets the main resource to use to connect to the XMPP server. + * @param resource The resource to use to log into the server. + */ + void setResource( const std::string &resource ) { m_jid.setResource( resource ); } + + /** + * Sends directed presence to the given JID. This is a NOOP if there's no active connection. + * To broadcast presence use setPresence( Presence::PresenceType, int, const std::string& ). + * @param to The JID to send directed Presence to. + * @param pres The presence to send. + * @param priority The priority to include. Legal values: -128 <= priority <= 127 + * @param status The optional status message to include. + * @note This function does not include any presence extensions (as added by + * means of addPresenceExtension()) to the stanza. + */ + void setPresence( const JID& to, Presence::PresenceType pres, int priority, + const std::string& status = EmptyString ); + + /** + * Use this function to set the entity's presence, that is, to broadcast presence to all + * subscribed entities. To send directed presence, use + * setPresence( const JID&, Presence::PresenceType, int, const std::string& ). + * If used prior to establishing a connection, the set values will be sent with + * the initial presence stanza. + * If used while a connection already is established, a presence stanza will be + * sent out immediately. + * @param pres The Presence value to set. + * @param priority An optional priority value. Legal values: -128 <= priority <= 127 + * @param status An optional message describing the presence state. + * @since 0.9 + */ + void setPresence( Presence::PresenceType pres, int priority, + const std::string& status = EmptyString ); + + /** + * Use this function to broadcast the entity's presence to all + * subscribed entities. This is a NOOP if there's no active connection. + * To send directed presence, use + * setPresence( const JID&, Presence::PresenceType, int, const std::string& ). + * If used while a connection already is established a repective presence stanza will be + * sent out immediately. Use presence() to modify the Presence object. + * @note When login is finished, initial presence will be sent automatically. + * So you do not need to call this function after login. + * @since 1.0 + */ + void setPresence() { sendPresence( m_presence ); } + + /** + * Returns the current presence. + * @return The current presence. + */ + Presence& presence() { return m_presence; } + + /** + * This is a temporary hack to enforce Non-SASL login. You should not need to use it. + * @param force Whether to force non-SASL auth. Default @b true. + * @deprecated Please update the server to properly support SASL instead. + */ + GLOOX_DEPRECATED void setForceNonSasl( bool force = true ) { m_forceNonSasl = force; } + + /** + * Disables the automatic roster management. + * You have to keep track of incoming presence yourself if + * you want to have a roster. + */ + void disableRoster(); + + /** + * This function gives access to the @c RosterManager object. + * @return A pointer to the RosterManager. + */ + RosterManager* rosterManager() { return m_rosterManager; } + + /** + * Disconnects from the server. + */ + void disconnect(); + + /** + * Initiates a login attempt (currently SASL External not supported). + * This is useful after registering a new account. Simply use setUsername() and setPassword(), + * and call login(). + * @return @b True if a login attempt could be started, @b false otherwise. A return + * value of @b true does not indicate that login was successful. + */ + bool login(); + + protected: + /** + * Initiates non-SASL login. + */ + void nonSaslLogin(); + + private: + /** + * @brief This is an implementation of a resource binding StanzaExtension. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class ResourceBind : public StanzaExtension + { + + public: + /** + * Constructs a new object with the given resource string. + * @param resource The resource to set. + * @param bind Indicates whether this is an bind or unbind request. + * Defaults to @b true (bind). + */ + ResourceBind( const std::string& resource, bool bind = true ); + + /** + * Constructs a new object from the given Tag. + * @param tag The Tag to parse. + */ + ResourceBind( const Tag* tag ); + + /** + * Destructor. + */ + ~ResourceBind(); + + /** + * Returns the requested resource. + * @return The requested resource. + */ + const std::string& resource() const { return m_resource; } + + /** + * Returns the assigned JID. + * @return The assigned JID. + */ + const JID& jid() const { return m_jid; } + + /** + * Use this function to find out whether the extension contains a + * bind or unbind request. + * @return @b True if the extension contains an unbind request, @b false otherwise. + */ + bool unbind() const { return !m_bind; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new ResourceBind( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new ResourceBind( *this ); + } + + private: + std::string m_resource; + JID m_jid; + bool m_bind; + }; + + /** + * @brief This is an implementation of a session creating StanzaExtension. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class SessionCreation : public StanzaExtension + { + + public: + /** + * Constructs a new object. + */ + SessionCreation() : StanzaExtension( ExtSessionCreation ) {} + + /** + * Destructor. + */ + ~SessionCreation() {} + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const { return EmptyString; } + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { (void)tag; return 0; } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { return 0; } + + }; + + virtual void handleStartNode() {} + virtual bool handleNormalNode( Tag* tag ); + virtual void disconnect( ConnectionError reason ); + virtual void handleIqIDForward( const IQ& iq, int context ); + + int getStreamFeatures( Tag* tag ); + int getSaslMechs( Tag* tag ); + int getCompressionMethods( Tag* tag ); + void processResourceBind( const IQ& iq ); + void processCreateSession( const IQ& iq ); + void sendPresence( Presence& pres ); + void createSession(); + void negotiateCompression( StreamFeature method ); + void connected(); + virtual void rosterFilled(); + virtual void cleanup(); + bool bindOperation( const std::string& resource, bool bind ); + + void init(); + + enum TrackContext + { + CtxResourceBind = 1000, // must be higher than the last element in ClientBase's TrackContext + CtxResourceUnbind, + CtxSessionEstablishment + }; + + RosterManager* m_rosterManager; + NonSaslAuth* m_auth; + + Presence m_presence; + + bool m_resourceBound; + bool m_forceNonSasl; + bool m_manageRoster; + + int m_streamFeatures; + + }; + +} + +#endif // CLIENT_H__ diff --git a/libs/libgloox/clientbase.cpp b/libs/libgloox/clientbase.cpp new file mode 100644 index 0000000..1619107 --- /dev/null +++ b/libs/libgloox/clientbase.cpp @@ -0,0 +1,1516 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "config.h" + +#include "clientbase.h" +#include "connectionbase.h" +#include "tlsbase.h" +#include "compressionbase.h" +#include "connectiontcpclient.h" +#include "disco.h" +#include "messagesessionhandler.h" +#include "tag.h" +#include "iq.h" +#include "message.h" +#include "subscription.h" +#include "presence.h" +#include "connectionlistener.h" +#include "iqhandler.h" +#include "messagehandler.h" +#include "presencehandler.h" +#include "rosterlistener.h" +#include "subscriptionhandler.h" +#include "loghandler.h" +#include "taghandler.h" +#include "mucinvitationhandler.h" +#include "mucroom.h" +#include "jid.h" +#include "base64.h" +#include "error.h" +#include "md5.h" +#include "util.h" +#include "tlsdefault.h" +#include "compressionzlib.h" +#include "stanzaextensionfactory.h" +#include "eventhandler.h" +#include "event.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include // for memset() + +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) +#include +#endif + +namespace gloox +{ + + // ---- ClientBase::Ping ---- + ClientBase::Ping::Ping() + : StanzaExtension( ExtPing ) + { + } + + ClientBase::Ping::~Ping() + { + } + + const std::string& ClientBase::Ping::filterString() const + { + static const std::string filter = "/iq/ping[@xmlns='" + XMLNS_XMPP_PING + "']"; + return filter; + } + // ---- ~ClientBase::Ping ---- + + // ---- ClientBase ---- + ClientBase::ClientBase( const std::string& ns, const std::string& server, int port ) + : m_connection( 0 ), m_encryption( 0 ), m_compression( 0 ), m_disco( 0 ), m_namespace( ns ), + m_xmllang( "en" ), m_server( server ), m_compressionActive( false ), m_encryptionActive( false ), + m_compress( true ), m_authed( false ), m_block( false ), m_sasl( true ), m_tls( TLSOptional ), m_port( port ), + m_availableSaslMechs( SaslMechAll ), + m_statisticsHandler( 0 ), m_mucInvitationHandler( 0 ), + m_messageSessionHandlerChat( 0 ), m_messageSessionHandlerGroupchat( 0 ), + m_messageSessionHandlerHeadline( 0 ), m_messageSessionHandlerNormal( 0 ), + m_parser( this ), m_seFactory( 0 ), m_authError( AuthErrorUndefined ), + m_streamError( StreamErrorUndefined ), m_streamErrorAppCondition( 0 ), + m_selectedSaslMech( SaslMechNone ), m_autoMessageSession( false ) + { + init(); + } + + ClientBase::ClientBase( const std::string& ns, const std::string& password, + const std::string& server, int port ) + : m_connection( 0 ), m_encryption( 0 ), m_compression( 0 ), m_disco( 0 ), m_namespace( ns ), + m_password( password ), + m_xmllang( "en" ), m_server( server ), m_compressionActive( false ), m_encryptionActive( false ), + m_compress( true ), m_authed( false ), m_block( false ), m_sasl( true ), m_tls( TLSOptional ), + m_port( port ), m_availableSaslMechs( SaslMechAll ), + m_statisticsHandler( 0 ), m_mucInvitationHandler( 0 ), + m_messageSessionHandlerChat( 0 ), m_messageSessionHandlerGroupchat( 0 ), + m_messageSessionHandlerHeadline( 0 ), m_messageSessionHandlerNormal( 0 ), + m_parser( this ), m_seFactory( 0 ), m_authError( AuthErrorUndefined ), + m_streamError( StreamErrorUndefined ), m_streamErrorAppCondition( 0 ), + m_selectedSaslMech( SaslMechNone ), m_autoMessageSession( false ) + { + init(); + } + + void ClientBase::init() + { + if( !m_disco ) + { + m_disco = new Disco( this ); + m_disco->setVersion( "based on gloox", GLOOX_VERSION ); + m_disco->addFeature( XMLNS_XMPP_PING ); + } + + registerStanzaExtension( new Error() ); + registerStanzaExtension( new Ping() ); + registerIqHandler( this, ExtPing ); + + m_streamError = StreamErrorUndefined; + m_block = false; + memset( &m_stats, 0, sizeof( m_stats ) ); + cleanup(); + } + + ClientBase::~ClientBase() + { + delete m_connection; + delete m_encryption; + delete m_compression; + delete m_seFactory; + m_seFactory = 0; // to avoid usage when Disco gets deleted below + delete m_disco; + m_disco = 0; + + util::clearList( m_messageSessions ); + + PresenceJidHandlerList::const_iterator it1 = m_presenceJidHandlers.begin(); + for( ; it1 != m_presenceJidHandlers.end(); ++it1 ) + delete (*it1).jid; + } + + ConnectionError ClientBase::recv( int timeout ) + { + if( !m_connection || m_connection->state() == StateDisconnected ) + return ConnNotConnected; + + return m_connection->recv( timeout ); + } + + bool ClientBase::connect( bool block ) + { + if( m_server.empty() ) + return false; + + if( !m_connection ) + m_connection = new ConnectionTCPClient( this, m_logInstance, m_server, m_port ); + + if( m_connection->state() >= StateConnecting ) + return true; + + if( !m_encryption ) + m_encryption = getDefaultEncryption(); + + if( !m_compression ) + m_compression = getDefaultCompression(); + + m_logInstance.dbg( LogAreaClassClientbase, "This is gloox " + GLOOX_VERSION + ", connecting to " + + m_server + ":" + util::int2string( m_port ) + "..." ); + m_block = block; + ConnectionError ret = m_connection->connect(); + if( ret != ConnNoError ) + return false; + + if( m_block ) + m_connection->receive(); + + return true; + } + + void ClientBase::handleTag( Tag* tag ) + { + if( !tag ) + { + logInstance().dbg( LogAreaClassClientbase, "stream closed" ); + disconnect( ConnStreamClosed ); + return; + } + + logInstance().dbg( LogAreaXmlIncoming, tag->xml() ); + ++m_stats.totalStanzasReceived; + + if( tag->name() == "stream" && tag->xmlns() == XMLNS_STREAM ) + { + const std::string& version = tag->findAttribute( "version" ); + if( !checkStreamVersion( version ) ) + { + logInstance().dbg( LogAreaClassClientbase, "This server is not XMPP-compliant" + " (it does not send a 'version' attribute). Please fix it or try another one.\n" ); + disconnect( ConnStreamVersionError ); + return; + } + + m_sid = tag->findAttribute( "id" ); + handleStartNode(); + } + else if( tag->name() == "error" && tag->xmlns() == XMLNS_STREAM ) + { + handleStreamError( tag ); + disconnect( ConnStreamError ); + } + else + { + if( !handleNormalNode( tag ) ) + { + if( tag->xmlns().empty() || tag->xmlns() == XMLNS_CLIENT ) + { + if( tag->name() == "iq" ) + { + IQ iq( tag ); + m_seFactory->addExtensions( iq, tag ); + notifyIqHandlers( iq ); + ++m_stats.iqStanzasReceived; + } + else if( tag->name() == "message" ) + { + Message msg( tag ); + m_seFactory->addExtensions( msg, tag ); + notifyMessageHandlers( msg ); + ++m_stats.messageStanzasReceived; + } + else if( tag->name() == "presence" ) + { + const std::string& type = tag->findAttribute( TYPE ); + if( type == "subscribe" || type == "unsubscribe" + || type == "subscribed" || type == "unsubscribed" ) + { + Subscription sub( tag ); + m_seFactory->addExtensions( sub, tag ); + notifySubscriptionHandlers( sub ); + ++m_stats.s10nStanzasReceived; + } + else + { + Presence pres( tag ); + m_seFactory->addExtensions( pres, tag ); + notifyPresenceHandlers( pres ); + ++m_stats.presenceStanzasReceived; + } + } + else + m_logInstance.err( LogAreaClassClientbase, "Received invalid stanza." ); + } + else + { + notifyTagHandlers( tag ); + } + } + } + + if( m_statisticsHandler ) + m_statisticsHandler->handleStatistics( getStatistics() ); + } + + void ClientBase::handleCompressedData( const std::string& data ) + { + if( m_encryption && m_encryptionActive ) + m_encryption->encrypt( data ); + else if( m_connection ) + m_connection->send( data ); + else + m_logInstance.err( LogAreaClassClientbase, "Compression finished, but chain broken" ); + } + + void ClientBase::handleDecompressedData( const std::string& data ) + { + parse( data ); + } + + void ClientBase::handleEncryptedData( const TLSBase* /*base*/, const std::string& data ) + { + if( m_connection ) + m_connection->send( data ); + else + m_logInstance.err( LogAreaClassClientbase, "Encryption finished, but chain broken" ); + } + + void ClientBase::handleDecryptedData( const TLSBase* /*base*/, const std::string& data ) + { + if( m_compression && m_compressionActive ) + m_compression->decompress( data ); + else + parse( data ); + } + + void ClientBase::handleHandshakeResult( const TLSBase* /*base*/, bool success, CertInfo &certinfo ) + { + if( success ) + { + if( !notifyOnTLSConnect( certinfo ) ) + { + logInstance().err( LogAreaClassClientbase, "Server's certificate rejected!" ); + disconnect( ConnTlsFailed ); + } + else + { + logInstance().dbg( LogAreaClassClientbase, "connection encryption active" ); + header(); + } + } + else + { + logInstance().err( LogAreaClassClientbase, "TLS handshake failed!" ); + disconnect( ConnTlsFailed ); + } + } + + void ClientBase::handleReceivedData( const ConnectionBase* /*connection*/, const std::string& data ) + { + if( m_encryption && m_encryptionActive ) + m_encryption->decrypt( data ); + else if( m_compression && m_compressionActive ) + m_compression->decompress( data ); + else + parse( data ); + } + + void ClientBase::handleConnect( const ConnectionBase* /*connection*/ ) + { + header(); + } + + void ClientBase::handleDisconnect( const ConnectionBase* /*connection*/, ConnectionError reason ) + { + if( m_connection ) + m_connection->cleanup(); + + if( m_encryption ) + m_encryption->cleanup(); + + if( m_compression ) + m_compression->cleanup(); + + m_encryptionActive = false; + m_compressionActive = false; + + notifyOnDisconnect( reason ); + } + + void ClientBase::disconnect( ConnectionError reason ) + { + if( !m_connection || m_connection->state() < StateConnecting ) + return; + + if( reason != ConnTlsFailed ) + send( "" ); + + m_connection->disconnect(); + m_connection->cleanup(); + + if( m_encryption ) + m_encryption->cleanup(); + + if( m_compression ) + m_compression->cleanup(); + + m_encryptionActive = false; + m_compressionActive = false; + + notifyOnDisconnect( reason ); + } + + void ClientBase::parse( const std::string& data ) + { + std::string copy = data; + int i = 0; + if( ( i = m_parser.feed( copy ) ) >= 0 ) + { + std::string error = "parse error (at pos "; + error += util::int2string( i ); + error += "): "; + m_logInstance.err( LogAreaClassClientbase, error + copy ); + Tag* e = new Tag( "stream:error" ); + new Tag( e, "restricted-xml", "xmlns", XMLNS_XMPP_STREAM ); + send( e ); + disconnect( ConnParseError ); + } + } + + void ClientBase::header() + { + std::string head = ""; + head += ""; + send( head ); + } + + bool ClientBase::hasTls() + { +#if defined( HAVE_GNUTLS ) || defined( HAVE_OPENSSL ) || defined( HAVE_WINTLS ) + return true; +#else + return false; +#endif + } + + void ClientBase::startTls() + { + send( new Tag( "starttls", XMLNS, XMLNS_STREAM_TLS ) ); + } + + void ClientBase::setServer( const std::string &server ) + { + m_server = server; + if( m_connection ) + m_connection->setServer( server ); + } + + void ClientBase::setClientCert( const std::string& clientKey, const std::string& clientCerts ) + { + m_clientKey = clientKey; + m_clientCerts = clientCerts; + } + + void ClientBase::startSASL( SaslMechanism type ) + { + m_selectedSaslMech = type; + + Tag* a = new Tag( "auth", XMLNS, XMLNS_STREAM_SASL ); + + switch( type ) + { + case SaslMechDigestMd5: + a->addAttribute( "mechanism", "DIGEST-MD5" ); + break; + case SaslMechPlain: + { + a->addAttribute( "mechanism", "PLAIN" ); + + std::string tmp; + if( m_authzid ) + tmp += m_authzid.bare(); + + tmp += '\0'; + if( !m_authcid.empty() ) + tmp += m_authcid; + else + tmp += m_jid.username(); + tmp += '\0'; + tmp += m_password; + a->setCData( Base64::encode64( tmp ) ); + break; + } + case SaslMechAnonymous: + a->addAttribute( "mechanism", "ANONYMOUS" ); + break; + case SaslMechExternal: + a->addAttribute( "mechanism", "EXTERNAL" ); + a->setCData( Base64::encode64( m_authzid ? m_authzid.bare() : m_jid.bare() ) ); + break; + case SaslMechGssapi: + { +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + a->addAttribute( "mechanism", "GSSAPI" ); +// The client calls GSS_Init_sec_context, passing in 0 for +// input_context_handle (initially) and a targ_name equal to output_name +// from GSS_Import_Name called with input_name_type of +// GSS_C_NT_HOSTBASED_SERVICE and input_name_string of +// "service@hostname" where "service" is the service name specified in +// the protocol's profile, and "hostname" is the fully qualified host +// name of the server. The client then responds with the resulting +// output_token. + std::string token; + a->setCData( Base64::encode64( token ) ); +// etc... see gssapi-sasl-draft.txt +#else + logInstance().err( LogAreaClassClientbase, + "SASL GSSAPI is not supported on this platform. You should never see this." ); +#endif + break; + } + case SaslMechNTLM: + { +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + a->addAttribute( "mechanism", "NTLM" ); + SEC_WINNT_AUTH_IDENTITY identity, *ident = 0; + memset( &identity, 0, sizeof( identity ) ); + if( m_jid.username().length() > 0 ) + { + identity.User = (unsigned char*)m_jid.username().c_str(); + identity.UserLength = (unsigned long)m_jid.username().length(); + identity.Domain = (unsigned char*)m_ntlmDomain.c_str(); + identity.DomainLength = (unsigned long)m_ntlmDomain.length(); + identity.Password = (unsigned char*)m_password.c_str(); + identity.PasswordLength = (unsigned long)m_password.length(); + identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; + ident = &identity; + } + AcquireCredentialsHandle( 0, _T( "NTLM" ), SECPKG_CRED_OUTBOUND, 0, ident, 0, 0, &m_credHandle, 0 ); +#else + logInstance().err( LogAreaClassClientbase, + "SASL NTLM is not supported on this platform. You should never see this." ); +#endif + break; + } + default: + break; + } + + send( a ); + } + + void ClientBase::processSASLChallenge( const std::string& challenge ) + { + Tag* t = new Tag( "response", XMLNS, XMLNS_STREAM_SASL ); + + const std::string& decoded = Base64::decode64( challenge ); + + switch( m_selectedSaslMech ) + { + case SaslMechDigestMd5: + { + if( !decoded.compare( 0, 7, "rspauth" ) ) + break; + + std::string realm; + std::string::size_type end = 0; + std::string::size_type pos = decoded.find( "realm=" ); + if( pos != std::string::npos ) + { + end = decoded.find( '"', pos + 7 ); + realm = decoded.substr( pos + 7, end - ( pos + 7 ) ); + } + else + realm = m_jid.server(); + + pos = decoded.find( "nonce=" ); + if( pos == std::string::npos ) + return; + + end = decoded.find( '"', pos + 7 ); + while( decoded[end-1] == '\\' ) + end = decoded.find( '"', end + 1 ); + std::string nonce = decoded.substr( pos + 7, end - ( pos + 7 ) ); + + std::string cnonce; + char cn[4*8+1]; + for( int i = 0; i < 4; ++i ) + sprintf( cn + i*8, "%08x", rand() ); + cnonce.assign( cn, 4*8 ); + + MD5 md5; + md5.feed( m_jid.username() ); + md5.feed( ":" ); + md5.feed( realm ); + md5.feed( ":" ); + md5.feed( m_password ); + md5.finalize(); + const std::string& a1_h = md5.binary(); + md5.reset(); + md5.feed( a1_h ); + md5.feed( ":" ); + md5.feed( nonce ); + md5.feed( ":" ); + md5.feed( cnonce ); + md5.finalize(); + const std::string& a1 = md5.hex(); + md5.reset(); + md5.feed( "AUTHENTICATE:xmpp/" ); + md5.feed( m_jid.server() ); + md5.finalize(); + const std::string& a2 = md5.hex(); + md5.reset(); + md5.feed( a1 ); + md5.feed( ":" ); + md5.feed( nonce ); + md5.feed( ":00000001:" ); + md5.feed( cnonce ); + md5.feed( ":auth:" ); + md5.feed( a2 ); + md5.finalize(); + + std::string response = "username=\""; + response += m_jid.username(); + response += "\",realm=\""; + response += realm; + response += "\",nonce=\""; + response += nonce; + response += "\",cnonce=\""; + response += cnonce; + response += "\",nc=00000001,qop=auth,digest-uri=\"xmpp/"; + response += m_jid.server(); + response += "\",response="; + response += md5.hex(); + response += ",charset=utf-8"; + + if( m_authzid ) + response += ",authzid=" + m_authzid.bare(); + + t->setCData( Base64::encode64( response ) ); + + break; + } + case SaslMechGssapi: +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + // see gssapi-sasl-draft.txt +#else + m_logInstance.err( LogAreaClassClientbase, + "Huh, received GSSAPI challenge?! This should have never happened!" ); +#endif + break; + case SaslMechNTLM: + { +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + bool type1 = ( decoded.length() < 7 ) ? true : false; + + SecBuffer bufferIn = { type1 ? 0 : (unsigned long)decoded.length(), + SECBUFFER_TOKEN, + (void*)decoded.c_str() }; + SecBufferDesc secIn = { 0, 1, &bufferIn }; + + char buffer[4096]; + + SecBuffer bufferOut = { sizeof( buffer ), SECBUFFER_TOKEN, buffer }; + SecBufferDesc secOut = { 0, 1, &bufferOut }; + + TimeStamp timestamp; + unsigned long contextAttr; + + SECURITY_STATUS status = InitializeSecurityContext( &m_credHandle, type1 ? 0 : &m_ctxtHandle, + 0, ISC_REQ_MUTUAL_AUTH, 0, 0, &secIn, 0, + &m_ctxtHandle, &secOut, &contextAttr, + ×tamp ); + std::string response; + if( SUCCEEDED( status ) ) + { + response = std::string( (const char *)bufferOut.pvBuffer, bufferOut.cbBuffer ); + } + else + { + logInstance().err( LogAreaClassClientbase, + "InitializeSecurityContext() failed, return value " + + util::int2string( status ) ); + } + + t->setCData( Base64::encode64( response ) ); +#else + m_logInstance.err( LogAreaClassClientbase, + "Huh, received NTLM challenge?! This should have never happened!" ); +#endif + break; + } + + default: + // should never happen. + break; + } + + send( t ); + } + + void ClientBase::processSASLError( Tag* tag ) + { + if( tag->hasChild( "aborted" ) ) + m_authError = SaslAborted; + else if( tag->hasChild( "incorrect-encoding" ) ) + m_authError = SaslIncorrectEncoding; + else if( tag->hasChild( "invalid-authzid" ) ) + m_authError = SaslInvalidAuthzid; + else if( tag->hasChild( "invalid-mechanism" ) ) + m_authError = SaslInvalidMechanism; + else if( tag->hasChild( "malformed-request" ) ) + m_authError = SaslMalformedRequest; + else if( tag->hasChild( "mechanism-too-weak" ) ) + m_authError = SaslMechanismTooWeak; + else if( tag->hasChild( "not-authorized" ) ) + m_authError = SaslNotAuthorized; + else if( tag->hasChild( "temporary-auth-failure" ) ) + m_authError = SaslTemporaryAuthFailure; + +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + if( m_selectedSaslMech == SaslMechNTLM ) + { + FreeCredentialsHandle( &m_credHandle ); + DeleteSecurityContext( &m_ctxtHandle ); + } +#endif + } + + void ClientBase::processSASLSuccess() + { +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + if( m_selectedSaslMech == SaslMechNTLM ) + { + FreeCredentialsHandle( &m_credHandle ); + DeleteSecurityContext( &m_ctxtHandle ); + } +#endif + } + + void ClientBase::send( IQ& iq, IqHandler* ih, int context, bool del ) + { + if( ih && ( iq.subtype() == IQ::Set || iq.subtype() == IQ::Get ) ) + { + if( iq.id().empty() ) + iq.setID( getID() ); + + TrackStruct track; + track.ih = ih; + track.context = context; + track.del = del; + m_iqHandlerMapMutex.lock(); + m_iqIDHandlers[iq.id()] = track; + m_iqHandlerMapMutex.unlock(); + } + + send( iq ); + } + + void ClientBase::send( const IQ& iq ) + { + ++m_stats.iqStanzasSent; + Tag* tag = iq.tag(); + addFrom( tag ); + addNamespace( tag ); + send( tag ); + } + + void ClientBase::send( const Message& msg ) + { + ++m_stats.messageStanzasSent; + Tag* tag = msg.tag(); + addFrom( tag ); + addNamespace( tag ); + send( tag ); + } + + void ClientBase::send( const Subscription& sub ) + { + ++m_stats.s10nStanzasSent; + Tag* tag = sub.tag(); + addFrom( tag ); + addNamespace( tag ); + send( tag ); + } + + void ClientBase::send( Presence& pres ) + { + ++m_stats.presenceStanzasSent; + Tag* tag = pres.tag(); + StanzaExtensionList::const_iterator it = m_presenceExtensions.begin(); + for( ; it != m_presenceExtensions.end(); ++it ) + tag->addChild( (*it)->tag() ); + addFrom( tag ); + addNamespace( tag ); + send( tag ); + } + + void ClientBase::send( Tag* tag ) + { + if( !tag ) + return; + + send( tag->xml() ); + + ++m_stats.totalStanzasSent; + + if( m_statisticsHandler ) + m_statisticsHandler->handleStatistics( getStatistics() ); + + delete tag; + } + + void ClientBase::send( const std::string& xml ) + { + if( m_connection && m_connection->state() == StateConnected ) + { + if( m_compression && m_compressionActive ) + m_compression->compress( xml ); + else if( m_encryption && m_encryptionActive ) + m_encryption->encrypt( xml ); + else + m_connection->send( xml ); + + logInstance().dbg( LogAreaXmlOutgoing, xml ); + } + } + + void ClientBase::addFrom( Tag* tag ) + { + if( !m_authed /*for IQ Auth */ || !tag || tag->hasAttribute( "from" ) ) + return; + + if ( m_selectedResource.empty() ) + tag->addAttribute( "from", m_jid.bare() ); + else + tag->addAttribute( "from", m_jid.bare() + '/' + m_selectedResource ); + } + + void ClientBase::addNamespace( Tag* tag ) + { + if( !tag || !tag->xmlns().empty() ) + return; + + tag->setXmlns( m_namespace ); + } + + void ClientBase::registerStanzaExtension( StanzaExtension* ext ) + { + if( !m_seFactory ) + m_seFactory = new StanzaExtensionFactory(); + + m_seFactory->registerExtension( ext ); + } + + bool ClientBase::removeStanzaExtension( int ext ) + { + if( !m_seFactory ) + return false; + + return m_seFactory->removeExtension( ext ); + } + + StatisticsStruct ClientBase::getStatistics() + { + if( m_connection ) + m_connection->getStatistics( m_stats.totalBytesReceived, m_stats.totalBytesSent ); + + return m_stats; + } + + ConnectionState ClientBase::state() const + { + return m_connection ? m_connection->state() : StateDisconnected; + } + + void ClientBase::whitespacePing() + { + send( " " ); + } + + void ClientBase::xmppPing( const JID& to, EventHandler* eh ) + { + const std::string& id = getID(); + IQ iq( IQ::Get, to, id ); + iq.addExtension( new Ping() ); + m_dispatcher.registerEventHandler( eh, id ); + send( iq, this, XMPPPing ); + } + + bool ClientBase::handleIq( const IQ& iq ) + { + const Ping* p = iq.findExtension( ExtPing ); + if( !p || iq.subtype() != IQ::Get ) + return false; + + m_dispatcher.dispatch( Event( Event::PingPing, iq ) ); + IQ re( IQ::Result, iq.from(), iq.id() ); + send( re ); + + return true; + } + + void ClientBase::handleIqID( const IQ& iq, int context ) + { + if( context == XMPPPing ) + m_dispatcher.dispatch( Event( ( iq.subtype() == IQ::Result ) ? Event::PingPong + : Event::PingError, iq ), + iq.id(), true ); + else + handleIqIDForward( iq, context ); + } + + const std::string ClientBase::getID() + { + static unsigned int uniqueBaseID = (unsigned int)time( 0 ); + char r[21+1]; + sprintf( r, "uid:%08x:%08x", uniqueBaseID, rand() ); + std::string ret( r, 21 ); + return ret; + } + + bool ClientBase::checkStreamVersion( const std::string& version ) + { + if( version.empty() ) + return false; + + int major = 0; + int minor = 0; + int myMajor = atoi( XMPP_STREAM_VERSION_MAJOR.c_str() ); + + size_t dot = version.find( '.' ); + if( !version.empty() && dot && dot != std::string::npos ) + { + major = atoi( version.substr( 0, dot ).c_str() ); + minor = atoi( version.substr( dot ).c_str() ); + } + + return myMajor >= major; + } + + void ClientBase::setConnectionImpl( ConnectionBase* cb ) + { + if( m_connection ) + { + delete m_connection; + } + m_connection = cb; + } + + void ClientBase::setEncryptionImpl( TLSBase* tb ) + { + if( m_encryption ) + { + delete m_encryption; + } + m_encryption = tb; + } + + void ClientBase::setCompressionImpl( CompressionBase* cb ) + { + if( m_compression ) + { + delete m_compression; + } + m_compression = cb; + } + + void ClientBase::handleStreamError( Tag* tag ) + { + StreamError err = StreamErrorUndefined; + const TagList& c = tag->children(); + TagList::const_iterator it = c.begin(); + for( ; it != c.end(); ++it ) + { + const std::string& name = (*it)->name(); + if( name == "bad-format" ) + err = StreamErrorBadFormat; + else if( name == "bad-namespace-prefix" ) + err = StreamErrorBadNamespacePrefix; + else if( name == "conflict" ) + err = StreamErrorConflict; + else if( name == "connection-timeout" ) + err = StreamErrorConnectionTimeout; + else if( name == "host-gone" ) + err = StreamErrorHostGone; + else if( name == "host-unknown" ) + err = StreamErrorHostUnknown; + else if( name == "improper-addressing" ) + err = StreamErrorImproperAddressing; + else if( name == "internal-server-error" ) + err = StreamErrorInternalServerError; + else if( name == "invalid-from" ) + err = StreamErrorInvalidFrom; + else if( name == "invalid-id" ) + err = StreamErrorInvalidId; + else if( name == "invalid-namespace" ) + err = StreamErrorInvalidNamespace; + else if( name == "invalid-xml" ) + err = StreamErrorInvalidXml; + else if( name == "not-authorized" ) + err = StreamErrorNotAuthorized; + else if( name == "policy-violation" ) + err = StreamErrorPolicyViolation; + else if( name == "remote-connection-failed" ) + err = StreamErrorRemoteConnectionFailed; + else if( name == "resource-constraint" ) + err = StreamErrorResourceConstraint; + else if( name == "restricted-xml" ) + err = StreamErrorRestrictedXml; + else if( name == "see-other-host" ) + { + err = StreamErrorSeeOtherHost; + m_streamErrorCData = tag->findChild( "see-other-host" )->cdata(); + } + else if( name == "system-shutdown" ) + err = StreamErrorSystemShutdown; + else if( name == "undefined-condition" ) + err = StreamErrorUndefinedCondition; + else if( name == "unsupported-encoding" ) + err = StreamErrorUnsupportedEncoding; + else if( name == "unsupported-stanza-type" ) + err = StreamErrorUnsupportedStanzaType; + else if( name == "unsupported-version" ) + err = StreamErrorUnsupportedVersion; + else if( name == "xml-not-well-formed" ) + err = StreamErrorXmlNotWellFormed; + else if( name == "text" ) + { + const std::string& lang = (*it)->findAttribute( "xml:lang" ); + if( !lang.empty() ) + m_streamErrorText[lang] = (*it)->cdata(); + else + m_streamErrorText["default"] = (*it)->cdata(); + } + else + m_streamErrorAppCondition = (*it); + + if( err != StreamErrorUndefined && (*it)->hasAttribute( XMLNS, XMLNS_XMPP_STREAM ) ) + m_streamError = err; + } + } + + const std::string& ClientBase::streamErrorText( const std::string& lang ) const + { + StringMap::const_iterator it = m_streamErrorText.find( lang ); + return ( it != m_streamErrorText.end() ) ? (*it).second : EmptyString; + } + + void ClientBase::registerMessageSessionHandler( MessageSessionHandler* msh, int types ) + { + if( types & Message::Chat || types == 0 ) + m_messageSessionHandlerChat = msh; + + if( types & Message::Normal || types == 0 ) + m_messageSessionHandlerNormal = msh; + + if( types & Message::Groupchat || types == 0 ) + m_messageSessionHandlerGroupchat = msh; + + if( types & Message::Headline || types == 0 ) + m_messageSessionHandlerHeadline = msh; + } + + void ClientBase::registerPresenceHandler( PresenceHandler* ph ) + { + if( ph ) + m_presenceHandlers.push_back( ph ); + } + + void ClientBase::removePresenceHandler( PresenceHandler* ph ) + { + if( ph ) + m_presenceHandlers.remove( ph ); + } + + void ClientBase::registerPresenceHandler( const JID& jid, PresenceHandler* ph ) + { + if( ph && jid ) + { + JidPresHandlerStruct jph; + jph.jid = new JID( jid.bare() ); + jph.ph = ph; + m_presenceJidHandlers.push_back( jph ); + } + } + + void ClientBase::removePresenceHandler( const JID& jid, PresenceHandler* ph ) + { + PresenceJidHandlerList::iterator t; + PresenceJidHandlerList::iterator it = m_presenceJidHandlers.begin(); + while( it != m_presenceJidHandlers.end() ) + { + t = it; + ++it; + if( ( !ph || (*t).ph == ph ) && (*t).jid->bare() == jid.bare() ) + { + delete (*t).jid; + m_presenceJidHandlers.erase( t ); + } + } + } + + void ClientBase::removeIDHandler( IqHandler* ih ) + { + IqTrackMap::iterator t; + m_iqHandlerMapMutex.lock(); + IqTrackMap::iterator it = m_iqIDHandlers.begin(); + while( it != m_iqIDHandlers.end() ) + { + t = it; + ++it; + if( ih == (*t).second.ih ) + m_iqIDHandlers.erase( t ); + } + m_iqHandlerMapMutex.unlock(); + } + + void ClientBase::registerIqHandler( IqHandler* ih, int exttype ) + { + if( !ih ) + return; + + typedef IqHandlerMap::const_iterator IQci; + std::pair g = m_iqExtHandlers.equal_range( exttype ); + for( IQci it = g.first; it != g.second; ++it ) + if( (*it).second == ih ) + return; + + m_iqExtHandlers.insert( std::make_pair( exttype, ih ) ); + } + + void ClientBase::removeIqHandler( IqHandler* ih, int exttype ) + { + if( !ih ) + return; + + typedef IqHandlerMap::iterator IQi; + std::pair g = m_iqExtHandlers.equal_range( exttype ); + IQi it2; + IQi it = g.first; + while( it != g.second ) + { + it2 = it++; + if( (*it2).second == ih ) + m_iqExtHandlers.erase( it2 ); + } + } + + void ClientBase::registerMessageSession( MessageSession* session ) + { + if( session ) + m_messageSessions.push_back( session ); + } + + void ClientBase::disposeMessageSession( MessageSession* session ) + { + if( !session ) + return; + + MessageSessionList::iterator it = std::find( m_messageSessions.begin(), + m_messageSessions.end(), + session ); + if( it != m_messageSessions.end() ) + { + delete (*it); + m_messageSessions.erase( it ); + } + } + + void ClientBase::registerMessageHandler( MessageHandler* mh ) + { + if( mh ) + m_messageHandlers.push_back( mh ); + } + + void ClientBase::removeMessageHandler( MessageHandler* mh ) + { + if( mh ) + m_messageHandlers.remove( mh ); + } + + void ClientBase::registerSubscriptionHandler( SubscriptionHandler* sh ) + { + if( sh ) + m_subscriptionHandlers.push_back( sh ); + } + + void ClientBase::removeSubscriptionHandler( SubscriptionHandler* sh ) + { + if( sh ) + m_subscriptionHandlers.remove( sh ); + } + + void ClientBase::registerTagHandler( TagHandler* th, const std::string& tag, const std::string& xmlns ) + { + if( th && !tag.empty() ) + { + TagHandlerStruct ths; + ths.tag = tag; + ths.xmlns = xmlns; + ths.th = th; + m_tagHandlers.push_back( ths ); + } + } + + void ClientBase::removeTagHandler( TagHandler* th, const std::string& tag, const std::string& xmlns ) + { + if( th ) + { + TagHandlerList::iterator it = m_tagHandlers.begin(); + for( ; it != m_tagHandlers.end(); ++it ) + { + if( (*it).th == th && (*it).tag == tag && (*it).xmlns == xmlns ) + m_tagHandlers.erase( it ); + } + } + } + + void ClientBase::registerStatisticsHandler( StatisticsHandler* sh ) + { + if( sh ) + m_statisticsHandler = sh; + } + + void ClientBase::removeStatisticsHandler() + { + m_statisticsHandler = 0; + } + + void ClientBase::registerMUCInvitationHandler( MUCInvitationHandler* mih ) + { + if( mih ) + { + m_mucInvitationHandler = mih; + m_disco->addFeature( XMLNS_MUC ); + } + } + + void ClientBase::removeMUCInvitationHandler() + { + m_mucInvitationHandler = 0; + m_disco->removeFeature( XMLNS_MUC ); + } + + void ClientBase::registerConnectionListener( ConnectionListener* cl ) + { + if( cl ) + m_connectionListeners.push_back( cl ); + } + + void ClientBase::removeConnectionListener( ConnectionListener* cl ) + { + if( cl ) + m_connectionListeners.remove( cl ); + } + + void ClientBase::notifyOnConnect() + { + util::ForEach( m_connectionListeners, &ConnectionListener::onConnect ); + } + + void ClientBase::notifyOnDisconnect( ConnectionError e ) + { + util::ForEach( m_connectionListeners, &ConnectionListener::onDisconnect, e ); + init(); + } + + bool ClientBase::notifyOnTLSConnect( const CertInfo& info ) + { + ConnectionListenerList::const_iterator it = m_connectionListeners.begin(); + for( ; it != m_connectionListeners.end() && (*it)->onTLSConnect( info ); ++it ) + ; + return m_stats.encryption = ( it == m_connectionListeners.end() ); + } + + void ClientBase::notifyOnResourceBindError( const Error* error ) + { + util::ForEach( m_connectionListeners, &ConnectionListener::onResourceBindError, error ); + } + + void ClientBase::notifyOnResourceBind( const std::string& resource ) + { + util::ForEach( m_connectionListeners, &ConnectionListener::onResourceBind, resource ); + } + + void ClientBase::notifyOnSessionCreateError( const Error* error ) + { + util::ForEach( m_connectionListeners, &ConnectionListener::onSessionCreateError, error ); + } + + void ClientBase::notifyStreamEvent( StreamEvent event ) + { + util::ForEach( m_connectionListeners, &ConnectionListener::onStreamEvent, event ); + } + + void ClientBase::notifyPresenceHandlers( Presence& pres ) + { + bool match = false; + PresenceJidHandlerList::const_iterator t; + PresenceJidHandlerList::const_iterator itj = m_presenceJidHandlers.begin(); + while( itj != m_presenceJidHandlers.end() ) + { + t = itj++; + if( (*t).jid->bare() == pres.from().bare() && (*t).ph ) + { + (*t).ph->handlePresence( pres ); + match = true; + } + } + if( match ) + return; + + // FIXME remove this for() for 1.1: + PresenceHandlerList::const_iterator it = m_presenceHandlers.begin(); + for( ; it != m_presenceHandlers.end(); ++it ) + { + (*it)->handlePresence( pres ); + } + // FIXME and reinstantiate this: +// util::ForEach( m_presenceHandlers, &PresenceHandler::handlePresence, pres ); + } + + void ClientBase::notifySubscriptionHandlers( Subscription& s10n ) + { + // FIXME remove this for() for 1.1: + SubscriptionHandlerList::const_iterator it = m_subscriptionHandlers.begin(); + for( ; it != m_subscriptionHandlers.end(); ++it ) + { + (*it)->handleSubscription( s10n ); + } + // FIXME and reinstantiate this: +// util::ForEach( m_subscriptionHandlers, &SubscriptionHandler::handleSubscription, s10n ); + } + + void ClientBase::notifyIqHandlers( IQ& iq ) + { + m_iqHandlerMapMutex.lock(); + IqTrackMap::iterator it_id = m_iqIDHandlers.find( iq.id() ); + m_iqHandlerMapMutex.unlock(); + if( it_id != m_iqIDHandlers.end() && iq.subtype() & ( IQ::Result | IQ::Error ) ) + { + (*it_id).second.ih->handleIqID( iq, (*it_id).second.context ); + if( (*it_id).second.del ) + delete (*it_id).second.ih; + m_iqHandlerMapMutex.lock(); + m_iqIDHandlers.erase( it_id ); + m_iqHandlerMapMutex.unlock(); + return; + } + + if( iq.extensions().empty() ) + return; + + bool res = false; + + // FIXME remove for 1.1 +// typedef IqHandlerMapXmlns::const_iterator IQciXmlns +// Tag *tag = iq.tag()->xmlns(); +// std::pair g = m_iqNSHandlers.equal_range( tag->xmlns() ); +// for( IQciXmlns it = g.first; it != g.second; ++it ) +// { +// if( (*it).second->handleIq( iq ) ) +// res = true; +// } +// delete tag; + + typedef IqHandlerMap::const_iterator IQci; + const StanzaExtensionList& sel = iq.extensions(); + StanzaExtensionList::const_iterator itse = sel.begin(); + for( ; itse != sel.end(); ++itse ) + { + std::pair g = m_iqExtHandlers.equal_range( (*itse)->extensionType() ); + for( IQci it = g.first; it != g.second; ++it ) + { + if( (*it).second->handleIq( iq ) ) + res = true; + } + } + + if( !res && iq.subtype() & ( IQ::Get | IQ::Set ) ) + { + IQ re( IQ::Error, iq.from(), iq.id() ); + re.addExtension( new Error( StanzaErrorTypeCancel, StanzaErrorServiceUnavailable ) ); + send( re ); + } + } + + void ClientBase::notifyMessageHandlers( Message& msg ) + { + if( m_mucInvitationHandler ) + { + const MUCRoom::MUCUser* mu = msg.findExtension( ExtMUCUser ); + if( mu && mu->operation() != MUCRoom::OpInviteTo ) + { + + m_mucInvitationHandler->handleMUCInvitation( msg.from(), + mu->jid() ? JID( *(mu->jid()) ) : JID(), + mu->reason() ? *(mu->reason()) : EmptyString, + msg.body(), + mu->password() ? *(mu->password()) : EmptyString, + mu->continued(), + mu->thread() ? *(mu->thread()) : EmptyString ); + return; + } + } + + MessageSessionList::const_iterator it1 = m_messageSessions.begin(); + for( ; it1 != m_messageSessions.end(); ++it1 ) + { + if( (*it1)->target().full() == msg.from().full() && + ( msg.thread().empty() + || (*it1)->threadID() == msg.thread() + || (*it1)->honorThreadID() ) && +// FIXME don't use '== 0' here + ( (*it1)->types() & msg.subtype() || (*it1)->types() == 0 ) ) + { + (*it1)->handleMessage( msg ); + return; + } + } + + it1 = m_messageSessions.begin(); + for( ; it1 != m_messageSessions.end(); ++it1 ) + { + if( (*it1)->target().bare() == msg.from().bare() && + ( msg.thread().empty() + || (*it1)->threadID() == msg.thread() + || (*it1)->honorThreadID() ) && +// FIXME don't use '== 0' here + ( (*it1)->types() & msg.subtype() || (*it1)->types() == 0 ) ) + { + (*it1)->handleMessage( msg ); + return; + } + } + + MessageSessionHandler* msHandler = 0; + + switch( msg.subtype() ) + { + case Message::Chat: + msHandler = m_messageSessionHandlerChat; + break; + case Message::Normal: + msHandler = m_messageSessionHandlerNormal; + break; + case Message::Groupchat: + msHandler = m_messageSessionHandlerGroupchat; + break; + case Message::Headline: + msHandler = m_messageSessionHandlerHeadline; + break; + default: + break; + } + + if( msHandler ) + { + if( msg.subtype() == Message::Chat && msg.body().empty() ) + return; // don't want a new MS for empty messages + MessageSession* session = new MessageSession( this, msg.from(), true, msg.subtype() ); + msHandler->handleMessageSession( session ); + session->handleMessage( msg ); + } + else + { + // FIXME remove this for() for 1.1: + MessageHandlerList::const_iterator it = m_messageHandlers.begin(); + for( ; it != m_messageHandlers.end(); ++it ) + { + (*it)->handleMessage( msg ); + } + // FIXME and reinstantiate this: +// util::ForEach( m_messageHandlers, &MessageHandler::handleMessage, msg ); // FIXME remove for 1.1 + } + } + + void ClientBase::notifyTagHandlers( Tag* tag ) + { + TagHandlerList::const_iterator it = m_tagHandlers.begin(); + for( ; it != m_tagHandlers.end(); ++it ) + { + if( (*it).tag == tag->name() && tag->hasAttribute( XMLNS, (*it).xmlns ) ) + (*it).th->handleTag( tag ); + } + } + + void ClientBase::addPresenceExtension( StanzaExtension* se ) + { + if( !se ) + return; + + removePresenceExtension( se->extensionType() ); + m_presenceExtensions.push_back( se ); + } + + bool ClientBase::removePresenceExtension( int type ) + { + StanzaExtensionList::iterator it = m_presenceExtensions.begin(); + for( ; it != m_presenceExtensions.end(); ++it ) + { + if( (*it)->extensionType() == type ) + { + delete (*it); + m_presenceExtensions.erase( it ); + return true; + } + } + + return false; + } + + CompressionBase* ClientBase::getDefaultCompression() + { + if( !m_compress ) + return 0; + +#ifdef HAVE_ZLIB + CompressionBase* cmp = new CompressionZlib( this ); + if( cmp->init() ) + return cmp; + + delete cmp; +#endif + return 0; + } + + TLSBase* ClientBase::getDefaultEncryption() + { + if( m_tls == TLSDisabled || !hasTls() ) + return 0; + + TLSDefault* tls = new TLSDefault( this, m_server ); + if( tls->init( m_clientKey, m_clientCerts, m_cacerts ) ) + return tls; + else + { + delete tls; + return 0; + } + } + +} diff --git a/libs/libgloox/clientbase.h b/libs/libgloox/clientbase.h new file mode 100644 index 0000000..4aadbda --- /dev/null +++ b/libs/libgloox/clientbase.h @@ -0,0 +1,1030 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef CLIENTBASE_H__ +#define CLIENTBASE_H__ + +#include "macros.h" +#include "gloox.h" +#include "eventdispatcher.h" +#include "iqhandler.h" +#include "jid.h" +#include "logsink.h" +#include "mutex.h" +#include "taghandler.h" +#include "statisticshandler.h" +#include "tlshandler.h" +#include "compressiondatahandler.h" +#include "connectiondatahandler.h" +#include "parser.h" + +#include +#include +#include + +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) +#include +#define SECURITY_WIN32 +#include +#endif + +namespace gloox +{ + + class Disco; + class EventHandler; + class Event; + class Tag; + class IQ; + class Message; + class Presence; + class Subscription; + class MessageSessionHandler; + class ConnectionListener; + class MessageHandler; + class MessageSession; + class PresenceHandler; + class SubscriptionHandler; + class MUCInvitationHandler; + class TagHandler; + class TLSBase; + class ConnectionBase; + class CompressionBase; + class StanzaExtensionFactory; + + /** + * @brief This is the common base class for a Jabber/XMPP Client and a Jabber Component. + * + * It manages connection establishing, authentication, filter registration and invocation. + * You should normally use Client for client connections and Component for component connections. + * + * @author Jakob Schroeter + * @since 0.3 + */ + class GLOOX_API ClientBase : public TagHandler, public ConnectionDataHandler, + public CompressionDataHandler, public TLSHandler, + public IqHandler + { + + friend class RosterManager; + + public: + /** + * Constructs a new ClientBase. + * You should not need to use this class directly. Use Client or Component instead. + * @param ns The namespace which qualifies the stream. Either jabber:client or jabber:component:* + * @param server The server to connect to. + * @param port The port to connect to. The default of -1 means to look up the port via DNS SRV + * or to use a default port of 5222 as defined in XMPP Core. + */ + ClientBase( const std::string& ns, const std::string& server, int port = -1 ); + + /** + * Constructs a new ClientBase. + * You should not need to use this class directly. Use Client or Component instead. + * @param ns The namespace which qualifies the stream. Either jabber:client or jabber:component:* + * @param password The password to use for further authentication. + * @param server The server to connect to. + * @param port The port to connect to. The default of -1 means to look up the port via DNS SRV + * or to use a default port of 5222 as defined in XMPP: Core. + */ + ClientBase( const std::string& ns, const std::string& password, + const std::string& server, int port = -1 ); + + /** + * Virtual destrcuctor. + */ + virtual ~ClientBase(); + + /** + * Initiates the connection to a server. This function blocks as long as a connection is + * established. + * You can have the connection block 'til the end of the connection, or you can have it return + * immediately. If you choose the latter, its your responsibility to call @ref recv() every now + * and then to actually receive data from the socket and to feed the parser. + * @param block @b True for blocking, @b false for non-blocking connect. Defaults to @b true. + * @return @b False if prerequisits are not met (server not set) or if the connection was refused, + * @b true otherwise. + * @note Since 0.9 @link ConnectionListener::onDisconnect() onDisconnect() @endlink is called + * in addition to a return value of @b false. + */ + bool connect( bool block = true ); + + /** + * Use this periodically to receive data from the socket and to feed the parser. You need to use + * this only if you chose to connect in non-blocking mode. + * @param timeout The timeout in microseconds to use for select. Default of -1 means blocking + * until data was available. + * @return The state of the connection. + */ + ConnectionError recv( int timeout = -1 ); + + /** + * Reimplement this function to provide a username for connection purposes. + * @return The username. + */ + virtual const std::string& username() const { return m_jid.username(); } + + /** + * Returns the current Jabber ID. If an authorization ID has been set (using setAuthzid()) + * this authzid is returned. + * @return A reference to the Jabber ID. + * @note If you change the server part of the JID, the server of the connection is not synced. + * You have to do that manually using @ref setServer(). + */ + const JID& jid() { return m_authzid ? m_authzid : m_jid; } + + /** + * Switches usage of SASL on/off. Default: on. SASL should only be disabled if there are + * problems with using it. + * @param sasl Whether to switch SASL usage on or off. + */ + void setSasl( bool sasl ) { m_sasl = sasl; } + + /** + * Sets the TLS policy. Default: TLS will be used if available. TLS should only be + * disabled if there are problems with using it. + * @param tls The TLS policy. + */ + void setTls( TLSPolicy tls ) { m_tls = tls; } + + /** + * Switches usage of Stream Compression on/off (if available). Default: on if available. Stream + * Compression should only be disabled if there are problems with using it. + * @param compression Whether to switch Stream Compression usage on or off. + */ + void setCompression( bool compression ) { m_compress = compression; } + + /** + * Sets the port to connect to. This is not necessary if either the default port (5222) is used + * or SRV records exist which will be resolved. + * @param port The port to connect to. + */ + void setPort( int port ) { m_port = port; } + + /** + * Sets the XMPP server to connect to. + * @param server The server to connect to. Either IP or fully qualified domain name. + * @note If you change the server, the server part of the JID is not synced. You have to do that + * manually using @ref jid() and @ref JID::setServer(). + * @note This function also sets the server of the Connection(Base) in use. + */ + void setServer( const std::string &server ); + + /** + * Sets the password to use to connect to the XMPP server. + * @param password The password to use for authentication. + */ + void setPassword( const std::string &password ) { m_password = password; } + + /** + * Returns the current prepped server. + * @return The server used to connect. + */ + const std::string& server() const { return m_server; } + + /** + * Returns whether SASL is currently enabled (not necessarily used). + * @return The current SASL status. + */ + bool sasl() const { return m_sasl; } + + /** + * Returns whether TLS is currently enabled (not necessarily used). + * @return The current TLS status. + */ + TLSPolicy tls() const { return m_tls; } + + /** + * Returns whether Stream Compression is currently enabled (not necessarily used). + * @return The current Stream Compression status. + */ + bool compression() const { return m_compress; } + + /** + * Returns the port. The default of -1 means that the actual port will be looked up using + * SRV records, or the XMPP default port of 5222 will be used. + * @return The port used to connect. + */ + int port() const { return m_port; } + + /** + * Returns the current password. + * @return The password used to connect. + */ + virtual const std::string& password() const { return m_password; } + + /** + * This function gives access to the @c Disco object. + * @return A pointer to the Disco object. + */ + virtual Disco* disco() const { return m_disco; } + + /** + * Creates a string which is unique in the current instance and + * can be used as an ID for queries. + * @return A unique string suitable for query IDs. + */ + const std::string getID(); + + /** + * Sends the given Tag over an established connection. + * The ClientBase object becomes the owner of this Tag and will delete it after sending it. + * You should not rely on the existance of the Tag after it's been sent. If you still need + * it after sending it, use Tag::clone() to create a deep copy. + * @param tag The Tag to send. + */ + void send( Tag* tag ); + + /** + * Sends the given IQ stanza. The given IqHandler is registered to be notified of replies. This, + * of course, only works for IQs of type get or set. An ID is added if necessary. + * @param iq The IQ stanza to send. + * @param ih The handler to register for replies. + * @param context A value that allows for restoring context. + * @param del Whether or not delete the IqHandler object after its being called. + * Default: @b false. + */ + void send( IQ& iq, IqHandler* ih, int context, bool del = false ); + + /** + * A convenience function that sends the given IQ stanza. + * @param iq The IQ stanza to send. + */ + void send( const IQ& iq ); + + /** + * A convenience function that sends the given Message stanza. + * @param msg The Message stanza to send. + */ + void send( const Message& msg ); + + /** + * A convenience function that sends the given Subscription stanza. + * @param sub The Subscription stanza to send. + */ + void send( const Subscription& sub ); + + /** + * A convenience function that sends the given Presence stanza. + * @param pres The Presence stanza to send. + */ + void send( Presence& pres ); + + /** + * Returns whether authentication has taken place and was successful. + * @return @b True if authentication has been carried out @b and was successful, @b false otherwise. + */ + bool authed() const { return m_authed; } + + /** + * Returns the current connection status. + * @return The status of the connection. + */ + ConnectionState state() const; + + /** + * Retrieves the value of the xml:lang attribute of the initial stream. + * Default is 'en', i.e. if not changed by a call to @ref setXmlLang(). + */ + const std::string& xmlLang() const { return m_xmllang; } + + /** + * Sets the value for the xml:lang attribute of the initial stream. + * @param xmllang The language identifier for the stream. It must conform to + * section 2.12 of the XML specification and RFC 3066. + * Default is 'en'. + */ + void setXmlLang( const std::string& xmllang ) { m_xmllang = xmllang; } + + /** + * This function returns the concrete connection implementation currently in use. + * @return The concrete connection implementation. + * @since 0.9 + */ + ConnectionBase* connectionImpl() const { return m_connection; } + + /** + * Use this function if you have a class implementing a UDP, SCTP (or whatever) + * connection. This should be called before calling connect(). If there already is a + * connection implementation set (either manually or automatically), it gets deleted. + * @param cb The connection to use. + * @since 0.9 + */ + void setConnectionImpl( ConnectionBase* cb ); + + /** + * This function returns the concrete encryption implementation currently in use. + * @return The concrete encryption implementation. + * @since 0.9 + */ + TLSBase* encryptionImpl() const { return m_encryption; } + + /** + * Use this function if you have a class supporting hardware encryption (or whatever). + * This should be called before calling connect(). If there already is a + * encryption implementation set (either manually or automatically), it gets deleted. + * @param tb The encryption implementation to use. + * @since 0.9 + */ + void setEncryptionImpl( TLSBase* tb ); + + /** + * This function returns the concrete compression implementation currently in use. + * @return The concrete compression implementation. + * @since 0.9 + */ + CompressionBase* compressionImpl() const { return m_compression; } + + /** + * Use this function if you have a class supporting some fancy compression algorithm. + * This should be called before calling connect(). If there already is a + * compression implementation set (either manually or automatically), it gets deleted. + * @param cb The compression implementation to use. + * @since 0.9 + */ + void setCompressionImpl( CompressionBase* cb ); + + /** + * Sends a whitespace ping to the server. + * @since 0.9 + */ + void whitespacePing(); + + /** + * Sends a XMPP Ping (XEP-0199) to the given JID. + * @param to Then entity to ping. + * @param eh An EventHandler to inform about the reply. + * @since 0.9 + */ + void xmppPing( const JID& to, EventHandler* eh ); + + /** + * Use this function to set an authorization ID (authzid). Provided the server supports it + * and the user has sufficient rights, they could then authenticate as bob@example.net but + * act as alice@example.net. + * @param authzid The JID to authorize as. Only the bare JID is used. + * @since 0.9 + */ + void setAuthzid( const JID& authzid ) { m_authzid = authzid; } + + /** + * Use this function to set an authentication ID (authcid) for SASL PLAIN. + * The default authcid is the username, i.e. the JID's node part. This should work in most cases. + * If this is not what you want to use for authentication, use this function. + * @param authcid The authentication ID. + * @since 1.0 + * @note Right now this is used for SASL PLAIN authentication only. + */ + void setAuthcid( const std::string& authcid ) { m_authcid = authcid; } + + /** + * Use this function to limit SASL mechanisms gloox can use. By default, all + * supported mechanisms are allowed. To exclude one (or more) mechanisms, remove + * it from SaslMechAll like so: + * @code + * int mymechs = SaslMechAll ^ SaslMechDigestMd5; + * @endcode + * @param mechanisms Bitwise ORed @ref SaslMechanism. + * @since 0.9 + */ + void setSASLMechanisms( int mechanisms ) { m_availableSaslMechs = mechanisms; } + + /** + * Registers a new StanzaExtension with the StanzaExtensionFactory. + * @param ext The extension to register. + */ + void registerStanzaExtension( StanzaExtension* ext ); + + /** + * Removes the given StanzaExtension type from the StanzaExtensionFactory. + * @param ext The extension type. + * @return @b True if the given type was found (and removed), @b false otherwise. + */ + bool removeStanzaExtension( int ext ); + + /** + * Registers @c cl as object that receives connection notifications. + * @param cl The object to receive connection notifications. + */ + void registerConnectionListener( ConnectionListener* cl ); + + /** + * Registers @c ih as object that receives notifications for IQ stanzas + * that contain StanzaExtensions of the given type. The number of handlers + * per extension type is not limited. + * @param ih The object to receive IQ stanza notifications. + * @param exttype The extension type. See StanzaExtension and + * @link gloox::StanzaExtensionType StanzaExtensionType @endlink. + * @since 1.0 + */ + void registerIqHandler( IqHandler* ih, int exttype ); + + /** + * Removes the given IqHandler from the list of handlers of pending operations, added + * using trackID(). Necessary, for example, when closing a GUI element that has an + * operation pending. + * @param ih The IqHandler to remove. + * @since 0.8.7 + */ + void removeIDHandler( IqHandler* ih ); + + /** + * Registers @c mh as object that receives Message stanza notifications. + * @param mh The object to receive Message stanza notifications. + */ + void registerMessageHandler( MessageHandler* mh ); + + /** + * Removes the given object from the list of message handlers. + * @param mh The object to remove from the list. + */ + void removeMessageHandler( MessageHandler* mh ); + + /** + * Registers the given MessageSession to receive Messages incoming from the session's + * target JID. + * @note The ClientBase instance becomes the owner of the MessageSession, it will be deleted + * in ClientBase's destructor. To get rid of the session before that, use disposeMessageSession(). + * @param session The MessageSession to register. + * @note Since a MessageSession automatically registers itself with the ClientBase, there is no + * need to call this function directly. + */ + void registerMessageSession( MessageSession* session ); + + /** + * Removes the given MessageSession from the list of MessageSessions and deletes it. + * @param session The MessageSession to be deleted. + */ + void disposeMessageSession( MessageSession* session ); + + /** + * Registers @c ph as object that receives Presence stanza notifications. + * @param ph The object to receive Presence stanza notifications. + */ + void registerPresenceHandler( PresenceHandler* ph ); + + /** + * Registers a new PresenceHandler for the given JID. Presences received for this + * particular JID will not be forwarded to the generic PresenceHandler (and therefore + * the Roster). + * This functionality is primarily intended for the MUC implementation. + * @param jid The JID to 'watch'. + * @param ph The PresenceHandler to inform about presence changes from @c jid. + * @since 0.9 + */ + void registerPresenceHandler( const JID& jid, PresenceHandler* ph ); + + /** + * Registers @c sh as object that receives Subscription stanza notifications. + * @param sh The object to receive Subscription stanza notifications. + */ + void registerSubscriptionHandler( SubscriptionHandler* sh ); + + /** + * Registers @c th as object that receives incoming packts with a given root tag + * qualified by the given namespace. + * @param th The object to receive Subscription packet notifications. + * @param tag The element's name. + * @param xmlns The element's namespace. + */ + void registerTagHandler( TagHandler* th, const std::string& tag, + const std::string& xmlns ); + + /** + * Registers @c sh as object that receives up-to-date connection statistics each time + * a Stanza is received or sent. Alternatively, you can use getStatistics() manually. + * Only one StatisticsHandler per ClientBase at a time is possible. + * @param sh The StatisticsHandler to register. + */ + void registerStatisticsHandler( StatisticsHandler* sh ); + + /** + * Removes the given object from the list of connection listeners. + * @param cl The object to remove from the list. + */ + void removeConnectionListener( ConnectionListener* cl ); + + /** + * Removes the given IQ handler for the given extension type. + * @param ih The IqHandler. + * @param exttype The extension type. See + * @link gloox::StanzaExtensionType StanzaExtensionType @endlink. + * @since 1.0 + */ + void removeIqHandler( IqHandler* ih, int exttype ); + + /** + * Removes the given object from the list of presence handlers. + * @param ph The object to remove from the list. + */ + void removePresenceHandler( PresenceHandler* ph ); + + /** + * Removes the given object from the list of presence handlers for the given JID. + * @param jid The JID to remove the PresenceHandler(s) for. + * @param ph The PresenceHandler to remove from the list. If @c ph is 0, + * all handlers for the given JID will be removed. + */ + void removePresenceHandler( const JID& jid, PresenceHandler* ph ); + + /** + * Removes the given object from the list of subscription handlers. + * @param sh The object to remove from the list. + */ + void removeSubscriptionHandler( SubscriptionHandler* sh ); + + /** + * Removes the given object from the list of tag handlers for the given element and namespace. + * @param th The object to remove from the list. + * @param tag The element to remove the handler for. + * @param xmlns The namespace qualifying the element. + */ + void removeTagHandler( TagHandler* th, const std::string& tag, + const std::string& xmlns ); + + /** + * Removes the current StatisticsHandler. + */ + void removeStatisticsHandler(); + + /** + * Use this function to set a number of trusted root CA certificates which shall be + * used to verify a servers certificate. + * @param cacerts A list of absolute paths to CA root certificate files in PEM format. + */ + void setCACerts( const StringList& cacerts ) { m_cacerts = cacerts; } + + /** + * Use this function to set the user's certificate and private key. The certificate will + * be presented to the server upon request and can be used for SASL EXTERNAL authentication. + * The user's certificate file should be a bundle of more than one certificate in PEM format. + * The first one in the file should be the user's certificate, each cert following that one + * should have signed the previous one. + * @note These certificates are not necessarily the same as those used to verify the server's + * certificate. + * @param clientKey The absolute path to the user's private key in PEM format. + * @param clientCerts A path to a certificate bundle in PEM format. + */ + void setClientCert( const std::string& clientKey, const std::string& clientCerts ); + + /** + * Use this function to register a MessageSessionHandler with the Client. + * Optionally the MessageSessionHandler can receive only MessageSessions with a given + * message type. There can be only one handler per message type.
+ * A MessageSession will be created for every incoming + * message stanza if there is no MessageHandler registered for the originating JID. + * @param msh The MessageSessionHandler that will receive the newly created MessageSession. + * @param types ORed StanzaSubType's that describe the desired message types the handler + * shall receive. Only StanzaMessage* types are valid. A value of 0 means any type (default). + */ + void registerMessageSessionHandler( MessageSessionHandler* msh, int types = 0 ); + + /** + * Returns the LogSink instance for this ClientBase and all related objects. + * @return The LogSink instance used in the current ClientBase. + */ + LogSink& logInstance() { return m_logInstance; } + + /** + * Use this function to retrieve the type of the stream error after it occurs and you received a + * ConnectionError of type @b ConnStreamError from the ConnectionListener. + * @return The StreamError. + * @note The return value is only meaningful when called from ConnectionListener::onDisconnect(). + */ + StreamError streamError() const { return m_streamError; } + + /** + * Returns the text of a stream error for the given language if available. + * If the requested language is not available, the default text (without a xml:lang + * attribute) will be returned. + * @param lang The language identifier for the desired language. It must conform to + * section 2.12 of the XML specification and RFC 3066. If empty, the default body + * will be returned, if any. + * @return The describing text of a stream error. Empty if no stream error occured. + */ + const std::string& streamErrorText( const std::string& lang = "default" ) const; + + /** + * In case the defined-condition element of an stream error contains XML character data you can + * use this function to retrieve it. RFC 3920 only defines one condition (see-other-host)where + * this is possible. + * @return The cdata of the stream error's text element (only for see-other-host). + */ + const std::string& streamErrorCData() const { return m_streamErrorCData; } + + /** + * This function can be used to retrieve the application-specific error condition of a stream error. + * @return The application-specific error element of a stream error. 0 if no respective element was + * found or no error occured. + */ + const Tag* streamErrorAppCondition() const { return m_streamErrorAppCondition; } + + /** + * Use this function to retrieve the type of the authentication error after it occurs and you + * received a ConnectionError of type @b ConnAuthenticationFailed from the ConnectionListener. + * @return The type of the authentication, if any, @b AuthErrorUndefined otherwise. + */ + AuthenticationError authError() const { return m_authError; } + + /** + * Returns a StatisticsStruct containing byte and stanza counts for the current + * active connection. + * @return A struct containing the current connection's statistics. + */ + StatisticsStruct getStatistics(); + + /** + * Registers a MUCInvitationHandler with the ClientBase. + * @param mih The MUCInvitationHandler to register. + */ + void registerMUCInvitationHandler( MUCInvitationHandler* mih ); + + /** + * Removes the currently registered MUCInvitationHandler. + */ + void removeMUCInvitationHandler(); + + /** + * Adds a StanzaExtension that will be sent with every Presence stanza + * sent. Capabilities are included by default if you are using a Client. + * @param se A StanzaExtension to add. If an extension of the same type + * has been added previously it will be replaced by the new one. + * Use removePresenceExtension() to remove an extension. + */ + void addPresenceExtension( StanzaExtension* se ); + + /** + * Removes the StanzaExtension of the given type from the list of Presence + * StanzaExtensions. + * Use addPresenceExtension() to replace an already added type. + */ + bool removePresenceExtension( int type ); + + /** + * Returns the current list of Presence StanzaExtensions. + * @return The current list of Presence StanzaExtensions. + */ + const StanzaExtensionList& presenceExtensions() const { return m_presenceExtensions; } + + // reimplemented from ParserHandler + virtual void handleTag( Tag* tag ); + + // reimplemented from CompressionDataHandler + virtual void handleCompressedData( const std::string& data ); + + // reimplemented from CompressionDataHandler + virtual void handleDecompressedData( const std::string& data ); + + // reimplemented from ConnectionDataHandler + virtual void handleReceivedData( const ConnectionBase* connection, const std::string& data ); + + // reimplemented from ConnectionDataHandler + virtual void handleConnect( const ConnectionBase* connection ); + + // reimplemented from ConnectionDataHandler + virtual void handleDisconnect( const ConnectionBase* connection, ConnectionError reason ); + + // reimplemented from TLSHandler + virtual void handleEncryptedData( const TLSBase* base, const std::string& data ); + + // reimplemented from TLSHandler + virtual void handleDecryptedData( const TLSBase* base, const std::string& data ); + + // reimplemented from TLSHandler + virtual void handleHandshakeResult( const TLSBase* base, bool success, CertInfo &certinfo ); + + protected: + /** + * This function is called when resource binding yieled an error. + * @param error A pointer to an Error object that contains more + * information. May be 0. + */ + void notifyOnResourceBindError( const Error* error ); + + /** + * This function is called when binding a resource succeeded. + * @param resource The bound resource. + */ + void notifyOnResourceBind( const std::string& resource ); + + /** + * This function is called when session creation yieled an error. + * @param error A pointer to an Error object that contains more + * information. May be 0. + */ + void notifyOnSessionCreateError( const Error* error ); + + /** + * This function is called when the TLS handshake completed correctly. The return + * value is used to determine whether or not the client accepted the server's + * certificate. If @b false is returned the connection is closed. + * @param info Information on the server's certificate. + * @return @b True if the certificate seems trustworthy, @b false otherwise. + */ + bool notifyOnTLSConnect( const CertInfo& info ); + + /** + * This function is called to notify about successful connection. + */ + void notifyOnConnect(); + + /** + * This function is used to notify subscribers of stream events. + * @param event The event to publish. + */ + void notifyStreamEvent( StreamEvent event ); + + /** + * Disconnects the underlying stream and broadcasts the given reason. + * @param reason The reason for the disconnect. + */ + virtual void disconnect( ConnectionError reason ); + + /** + * Sends the stream header. + */ + void header(); + + /** + * Tells ClientBase that authentication was successful (or not). + * @param authed Whether or not authentication was successful. + */ + void setAuthed( bool authed ) { m_authed = authed; } + + /** + * If authentication failed, this function tells ClientBase + * the reason. + * @param e The reason for the authentication failure. + */ + void setAuthFailure( AuthenticationError e ) { m_authError = e; } + + /** + * Implementors of this function can check if they support the advertized stream version. + * The return value indicates whether or not the stream can be handled. A default + * implementation is provided. + * @param version The advertized stream version. + * @return @b True if the stream can be handled, @b false otherwise. + */ + virtual bool checkStreamVersion( const std::string& version ); + + /** + * Starts authentication using the given SASL mechanism. + * @param type A SASL mechanism to use for authentication. + */ + void startSASL( SaslMechanism type ); + + /** + * Releases SASL related resources. + */ + void processSASLSuccess(); + + /** + * Processes the given SASL challenge and sends a response. + * @param challenge The SASL challenge to process. + */ + void processSASLChallenge( const std::string& challenge ); + + /** + * Examines the given Tag for SASL errors. + * @param tag The Tag to parse. + */ + void processSASLError( Tag* tag ); + + /** + * Sets the domain to use in SASL NTLM authentication. + * @param domain The domain. + */ + void setNTLMDomain( const std::string& domain ) { m_ntlmDomain = domain; } + + /** + * Starts the TLS handshake. + */ + void startTls(); + + /** + * Indicates whether or not TLS is supported. + * @return @b True if TLS is supported, @b false otherwise. + */ + bool hasTls(); + + JID m_jid; /**< The 'self' JID. */ + JID m_authzid; /**< An optional authorization ID. See setAuthzid(). */ + std::string m_authcid; /**< An alternative authentication ID. See setAuthcid(). */ + ConnectionBase* m_connection; /**< The transport connection. */ + TLSBase* m_encryption; /**< Used for connection encryption. */ + CompressionBase* m_compression; /**< Used for connection compression. */ + Disco* m_disco; /**< The local Service Discovery client. */ + + /** A list of permanent presence extensions. */ + StanzaExtensionList m_presenceExtensions; + + std::string m_selectedResource; /**< The currently selected resource. + * See Client::selectResource() and Client::binRessource(). */ + std::string m_clientCerts; /**< TLS client certificates. */ + std::string m_clientKey; /**< TLS client private key. */ + std::string m_namespace; /**< Default namespace. */ + std::string m_password; /**< Client's password. */ + std::string m_xmllang; /**< Default value of the xml:lang attribute. */ + std::string m_server; /**< The server to connect to, if different from the + * JID's server. */ + std::string m_sid; /**< The stream ID. */ + bool m_compressionActive; /**< Indicates whether or not stream compression + * is currently activated. */ + bool m_encryptionActive; /**< Indicates whether or not stream encryption + * is currently activated. */ + bool m_compress; /**< Whether stream compression + * is desired at all. */ + bool m_authed; /**< Whether authentication has been completed successfully. */ + bool m_block; /**< Whether blocking connection is wanted. */ + bool m_sasl; /**< Whether SASL authentication is wanted. */ + TLSPolicy m_tls; /**< The current TLS policy. */ + int m_port; /**< The port to connect to, if not to be determined + * by querying the server's SRV records. */ + + int m_availableSaslMechs; /**< The SASL mechanisms the server offered. */ + + private: +#ifdef CLIENTBASE_TEST + public: +#endif + /** + * @brief This is an implementation of an XMPP Ping (XEP-199). + * + * @author Jakob Schroeter + * @since 1.0 + */ + class Ping : public StanzaExtension + { + + public: + /** + * Constructs a new object. + */ + Ping(); + + /** + * Destructor. + */ + virtual ~Ping(); + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + (void)tag; + return new Ping(); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const + { + return new Tag( "ping", "xmlns", XMLNS_XMPP_PING ); + } + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new Ping(); + } + + }; + + ClientBase( const ClientBase& ); + ClientBase& operator=( const ClientBase& ); + + /** + * This function is called right after the opening <stream:stream> was received. + */ + virtual void handleStartNode() = 0; + + /** + * This function is called for each Tag. Only stream initiation/negotiation should + * be done here. + * @param tag A Tag to handle. + */ + virtual bool handleNormalNode( Tag* tag ) = 0; + virtual void rosterFilled() = 0; + virtual void cleanup() {} + virtual void handleIqIDForward( const IQ& iq, int context ) { (void) iq; (void) context; } + + void parse( const std::string& data ); + void init(); + void handleStreamError( Tag* tag ); + TLSBase* getDefaultEncryption(); + CompressionBase* getDefaultCompression(); + + void notifyIqHandlers( IQ& iq ); + void notifyMessageHandlers( Message& msg ); + void notifyPresenceHandlers( Presence& presence ); + void notifySubscriptionHandlers( Subscription& s10n ); + void notifyTagHandlers( Tag* tag ); + void notifyOnDisconnect( ConnectionError e ); + void send( const std::string& xml ); + void addFrom( Tag* tag ); + void addNamespace( Tag* tag ); + + // reimplemented from IqHandler + virtual bool handleIq( const IQ& iq ); + + // reimplemented from IqHandler + virtual void handleIqID( const IQ& iq, int context ); + + struct TrackStruct + { + IqHandler* ih; + int context; + bool del; + }; + + struct TagHandlerStruct + { + TagHandler* th; + std::string xmlns; + std::string tag; + }; + + struct JidPresHandlerStruct + { + JID* jid; + PresenceHandler* ph; + }; + + enum TrackContext + { + XMPPPing + }; + + typedef std::list ConnectionListenerList; + typedef std::multimap IqHandlerMapXmlns; + typedef std::multimap IqHandlerMap; + typedef std::map IqTrackMap; + typedef std::map MessageHandlerMap; + typedef std::list MessageSessionList; + typedef std::list MessageHandlerList; + typedef std::list PresenceHandlerList; + typedef std::list PresenceJidHandlerList; + typedef std::list SubscriptionHandlerList; + typedef std::list TagHandlerList; + + ConnectionListenerList m_connectionListeners; + IqHandlerMapXmlns m_iqNSHandlers; + IqHandlerMap m_iqExtHandlers; + IqTrackMap m_iqIDHandlers; + MessageSessionList m_messageSessions; + MessageHandlerList m_messageHandlers; + PresenceHandlerList m_presenceHandlers; + PresenceJidHandlerList m_presenceJidHandlers; + SubscriptionHandlerList m_subscriptionHandlers; + TagHandlerList m_tagHandlers; + StringList m_cacerts; + StatisticsHandler * m_statisticsHandler; + MUCInvitationHandler * m_mucInvitationHandler; + MessageSessionHandler * m_messageSessionHandlerChat; + MessageSessionHandler * m_messageSessionHandlerGroupchat; + MessageSessionHandler * m_messageSessionHandlerHeadline; + MessageSessionHandler * m_messageSessionHandlerNormal; + + util::Mutex m_iqHandlerMapMutex; + + Parser m_parser; + LogSink m_logInstance; + StanzaExtensionFactory* m_seFactory; + EventDispatcher m_dispatcher; + + AuthenticationError m_authError; + StreamError m_streamError; + StringMap m_streamErrorText; + std::string m_streamErrorCData; + Tag* m_streamErrorAppCondition; + + StatisticsStruct m_stats; + + SaslMechanism m_selectedSaslMech; + + std::string m_ntlmDomain; + bool m_autoMessageSession; + +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + CredHandle m_credHandle; + CtxtHandle m_ctxtHandle; +#endif + + }; + +} + +#endif // CLIENTBASE_H__ diff --git a/libs/libgloox/component.cpp b/libs/libgloox/component.cpp new file mode 100644 index 0000000..9f2e202 --- /dev/null +++ b/libs/libgloox/component.cpp @@ -0,0 +1,62 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "component.h" + +#include "disco.h" +#include "stanza.h" +#include "prep.h" +#include "sha.h" + +#include + +namespace gloox +{ + + Component::Component( const std::string& ns, const std::string& server, + const std::string& component, const std::string& password, int port ) + : ClientBase( ns, password, server, port ) + { + m_jid.setServer( component ); + m_disco->setIdentity( "component", "generic" ); + } + + void Component::handleStartNode() + { + if( m_sid.empty() ) + return; + + notifyStreamEvent( StreamEventAuthentication ); + + SHA sha; + sha.feed( m_sid + m_password ); + sha.finalize(); + + Tag* h = new Tag( "handshake", sha.hex() ); + send( h ); + } + + bool Component::handleNormalNode( Tag* tag ) + { + if( tag->name() != "handshake" ) + return false; + + m_authed = true; + notifyStreamEvent( StreamEventFinished ); + notifyOnConnect(); + + return true; + } + +} diff --git a/libs/libgloox/component.h b/libs/libgloox/component.h new file mode 100644 index 0000000..d6c02d7 --- /dev/null +++ b/libs/libgloox/component.h @@ -0,0 +1,77 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef COMPONENT_H__ +#define COMPONENT_H__ + +#include "clientbase.h" + +#include + +namespace gloox +{ + + /** + * @brief This is an implementation of a basic jabber Component. + * + * It's using XEP-0114 (Jabber Component Protocol) to authenticate with a server. + * + * @author Jakob Schroeter + * @since 0.3 + */ + class GLOOX_API Component : public ClientBase + { + public: + /** + * Constructs a new Component. + * @param ns The namespace that qualifies the stream. Either @b jabber:component:accept or + * @b jabber:component:connect. See XEP-0114 for details. + * @param server The server to connect to. + * @param component The component's hostname. FQDN. + * @param password The component's password. + * @param port The port to connect to. The default of 5347 is the default port of the router + * in jabberd2. + */ + Component( const std::string& ns, const std::string& server, + const std::string& component, const std::string& password, int port = 5347 ); + + /** + * Virtual Destructor. + */ + virtual ~Component() {} + + /** + * Disconnects from the server. + */ + void disconnect() { ClientBase::disconnect( ConnUserDisconnected ); } + + protected: + // reimplemented from ClientBase + virtual void handleStartNode(); + + // reimplemented from ClientBase + virtual bool handleNormalNode( Tag* tag ); + + // reimplemented from ClientBase + virtual bool checkStreamVersion( const std::string& /*version*/ ) { return true; } + + private: + // reimplemented from ClientBase + virtual void rosterFilled() {} + + }; + +} + +#endif // COMPONENT_H__ diff --git a/libs/libgloox/compressionbase.h b/libs/libgloox/compressionbase.h new file mode 100644 index 0000000..c3a178b --- /dev/null +++ b/libs/libgloox/compressionbase.h @@ -0,0 +1,85 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef COMPRESSIONBASE_H__ +#define COMPRESSIONBASE_H__ + +#include "gloox.h" +#include "compressiondatahandler.h" + +#include + +namespace gloox +{ + + /** + * @brief This is an abstract base class for stream compression implementations. + * + * You should not need to use this class directly. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API CompressionBase + { + public: + /** + * Contructor. + * @param cdh A CompressionDataHandler-derived object that will be notified + * about finished de/compression. + */ + CompressionBase( CompressionDataHandler* cdh ) : m_handler( cdh ), m_valid( false ) {} + + /** + * Virtual Destructor. + */ + virtual ~CompressionBase() {} + + /** + * This function initializes the compression module. + * it is mandatory to be called. + * @return @b True if the module was initialized successfully, false otherwise. + */ + virtual bool init() = 0; + + /** + * Compresses the given chunk of data. + * @param data The original (uncompressed) data. + */ + virtual void compress( const std::string& data ) = 0; + + /** + * Decompresses the given chunk of data. + * @param data The compressed data. + */ + virtual void decompress( const std::string& data ) = 0; + + /** + * Performs internal cleanup. + * @since 1.0 + */ + virtual void cleanup() = 0; + + protected: + /** A handler for compressed/uncompressed data. */ + CompressionDataHandler* m_handler; + + /** Whether the compression module can be used. */ + bool m_valid; + + }; + +} + +#endif // COMPRESSIONBASE_H__ diff --git a/libs/libgloox/compressiondatahandler.h b/libs/libgloox/compressiondatahandler.h new file mode 100644 index 0000000..0eb5178 --- /dev/null +++ b/libs/libgloox/compressiondatahandler.h @@ -0,0 +1,58 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef COMPRESSIONDATAHANDLER_H__ +#define COMPRESSIONDATAHANDLER_H__ + +#include "macros.h" + +#include + +namespace gloox +{ + + /** + * @brief An abstract base class used to receive de/compressed data from a + * CompressionBase-derived object. + * + * You should not need to use this class directly. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API CompressionDataHandler + { + public: + /** + * Virtual Destructor. + */ + virtual ~CompressionDataHandler() {} + + /** + * This function is called when compression is finished. + * @param data The compressed data. + */ + virtual void handleCompressedData( const std::string& data ) = 0; + + /** + * This function is called when decompression is finished. + * @param data The decompressed data. + */ + virtual void handleDecompressedData( const std::string& data ) = 0; + + }; + +} + +#endif // COMPRESSIONDATAHANDLER_H__ diff --git a/libs/libgloox/compressiondefault.cpp b/libs/libgloox/compressiondefault.cpp new file mode 100644 index 0000000..066cc93 --- /dev/null +++ b/libs/libgloox/compressiondefault.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2009 by Jakob Schroeter + * This file is part of the gloox library. http://camaya.net/gloox + * + * This software is distributed under a license. The full license + * agreement can be found in the file LICENSE in this distribution. + * This software may not be copied, modified, sold or distributed + * other than expressed in the named license agreement. + * + * This software is distributed without any warranty. + */ + +#include "compressiondefault.h" + +#include "compressiondatahandler.h" + +#include "config.h" + +#if defined( HAVE_ZLIB ) +# define HAVE_COMPRESSION +# include "compressionzlib.h" +#endif + +// #if defined( HAVE_LZW ) +// # define HAVE_COMPRESSION +// # include "compressionlzw.h" +// #endif + +namespace gloox +{ + + CompressionDefault::CompressionDefault( CompressionDataHandler* cdh, Method method ) + : CompressionBase( cdh ), m_impl( 0 ) + { + switch( method ) + { + case MethodZlib: +#ifdef HAVE_ZLIB + m_impl = new CompressionZlib( cdh ); +#endif + break; + case MethodLZW: +#ifdef HAVE_LZW + m_impl = new CompressionLZW( cdh ); +#endif + break; + default: + break; + } + } + + CompressionDefault::~CompressionDefault() + { + delete m_impl; + } + + bool CompressionDefault::init() + { + return m_impl ? m_impl->init() : false; + } + + int CompressionDefault::types() + { + int types = 0; +#ifdef HAVE_ZLIB + types |= MethodZlib; +#endif +#ifdef HAVE_LZW + types |= MethodLZW; +#endif + return types; + } + + void CompressionDefault::compress( const std::string& data ) + { + if( m_impl ) + m_impl->compress( data ); + } + + void CompressionDefault::decompress( const std::string& data ) + { + if( m_impl ) + m_impl->decompress( data ); + } + + void CompressionDefault::cleanup() + { + if( m_impl ) + m_impl->cleanup(); + } + +} diff --git a/libs/libgloox/compressiondefault.h b/libs/libgloox/compressiondefault.h new file mode 100644 index 0000000..38b83f0 --- /dev/null +++ b/libs/libgloox/compressiondefault.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2009 by Jakob Schroeter + * This file is part of the gloox library. http://camaya.net/gloox + * + * This software is distributed under a license. The full license + * agreement can be found in the file LICENSE in this distribution. + * This software may not be copied, modified, sold or distributed + * other than expressed in the named license agreement. + * + * This software is distributed without any warranty. + */ + + +#ifndef COMPRESSIONDEFAULT_H__ +#define COMPRESSIONDEFAULT_H__ + +#include "compressionbase.h" + +namespace gloox +{ + + class CompressionDataHandler; + + /** + * @brief This is an abstraction of the various Compression implementations. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API CompressionDefault : public CompressionBase + { + public: + + /** + * Supported ctypes. + */ + enum Method + { + MethodZlib = 1, /**< Zlib compression. */ + MethodLZW = 2 /**< LZW compression. */ + }; + + /** + * Constructs a new compression wrapper. + * @param cdh The CompressionDataHandler to handle de/compressed data. + * @param method The desired compression method. + */ + CompressionDefault( CompressionDataHandler* cdh, Method method = MethodZlib ); + + /** + * Virtual Destructor. + */ + virtual ~CompressionDefault(); + + /** + * Returns an int holding the available compression types, ORed. + * @return An int holding the available compression types, ORed. + */ + static int types(); + + // reimplemented from CompressionBase + virtual bool init(); + + // reimplemented from CompressionBase + virtual void compress( const std::string& data ); + + // reimplemented from CompressionBase + virtual void decompress( const std::string& data ); + + // reimplemented from CompressionBase + virtual void cleanup(); + + private: + CompressionBase* m_impl; + + }; + +} + +#endif // COMPRESSIONDEFAULT_H__ diff --git a/libs/libgloox/compressionzlib.cpp b/libs/libgloox/compressionzlib.cpp new file mode 100644 index 0000000..4d0b937 --- /dev/null +++ b/libs/libgloox/compressionzlib.cpp @@ -0,0 +1,135 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "compressionzlib.h" + +#ifdef HAVE_ZLIB + +namespace gloox +{ + + CompressionZlib::CompressionZlib( CompressionDataHandler* cdh ) + : CompressionBase( cdh ) + { + } + + bool CompressionZlib::init() + { + int ret = Z_OK; + m_zinflate.zalloc = Z_NULL; + m_zinflate.zfree = Z_NULL; + m_zinflate.opaque = Z_NULL; + m_zinflate.avail_in = 0; + m_zinflate.next_in = Z_NULL; + ret = inflateInit( &m_zinflate ); + if( ret != Z_OK ) + return false; + + m_zdeflate.zalloc = Z_NULL; + m_zdeflate.zfree = Z_NULL; + m_zdeflate.opaque = Z_NULL; + m_zinflate.avail_in = 0; + m_zinflate.next_in = Z_NULL; + ret = deflateInit( &m_zdeflate, Z_BEST_COMPRESSION/*Z_DEFAULT_COMPRESSION*/ ); + if( ret != Z_OK ) + return false; + + m_valid = true; + return true; + } + + CompressionZlib::~CompressionZlib() + { + cleanup(); + } + + void CompressionZlib::compress( const std::string& data ) + { + if( !m_valid ) + init(); + + if( !m_valid || !m_handler || data.empty() ) + return; + + long unsigned int CHUNK = data.length() + ( data.length() / 100 ) + 13; + Bytef* out = new Bytef[CHUNK]; + char* in = const_cast( data.c_str() ); + + m_compressMutex.lock(); + + m_zdeflate.avail_in = static_cast( data.length() ); + m_zdeflate.next_in = (Bytef*)in; + + int ret; + std::string result; + do { + m_zdeflate.avail_out = static_cast( CHUNK ); + m_zdeflate.next_out = (Bytef*)out; + + ret = deflate( &m_zdeflate, Z_SYNC_FLUSH ); + result.append( (char*)out, CHUNK - m_zdeflate.avail_out ); + } while( m_zdeflate.avail_out == 0 ); + + m_compressMutex.unlock(); + + delete[] out; + + m_handler->handleCompressedData( result ); + } + + void CompressionZlib::decompress( const std::string& data ) + { + if( !m_valid ) + init(); + + if( !m_valid || !m_handler || data.empty() ) + return; + + int CHUNK = 50; + char* out = new char[CHUNK]; + char* in = const_cast( data.c_str() ); + + m_zinflate.avail_in = static_cast( data.length() ); + m_zinflate.next_in = (Bytef*)in; + + int ret = Z_OK; + std::string result; + do + { + m_zinflate.avail_out = CHUNK; + m_zinflate.next_out = (Bytef*)out; + + ret = inflate( &m_zinflate, Z_SYNC_FLUSH ); + result.append( out, CHUNK - m_zinflate.avail_out ); + } while( m_zinflate.avail_out == 0 ); + + delete[] out; + + m_handler->handleDecompressedData( result ); + } + + void CompressionZlib::cleanup() + { + if( !m_valid ) + return; + + inflateEnd( &m_zinflate ); + deflateEnd( &m_zdeflate ); + + m_valid = false; + } + +} + +#endif // HAVE_ZLIB diff --git a/libs/libgloox/compressionzlib.h b/libs/libgloox/compressionzlib.h new file mode 100644 index 0000000..3f7ba6e --- /dev/null +++ b/libs/libgloox/compressionzlib.h @@ -0,0 +1,74 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef COMPRESSIONZLIB_H__ +#define COMPRESSIONZLIB_H__ + +#include "compressionbase.h" +#include "mutex.h" + +#include "config.h" + +#ifdef HAVE_ZLIB + +#include + +#include + +namespace gloox +{ + /** + * An implementation of CompressionBase using zlib. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API CompressionZlib : public CompressionBase + { + public: + /** + * Contructor. + * @param cdh The CompressionDataHandler to receive de/compressed data. + */ + CompressionZlib( CompressionDataHandler* cdh ); + + /** + * Virtual Destructor. + */ + virtual ~CompressionZlib(); + + // reimplemented from CompressionBase + virtual bool init(); + + // reimplemented from CompressionBase + virtual void compress( const std::string& data ); + + // reimplemented from CompressionBase + virtual void decompress( const std::string& data ); + + // reimplemented from CompressionBase + virtual void cleanup(); + + private: + z_stream m_zinflate; + z_stream m_zdeflate; + + util::Mutex m_compressMutex; + + }; + +} + +#endif // HAVE_ZLIB + +#endif // COMPRESSIONZLIB_H__ diff --git a/libs/libgloox/config.h b/libs/libgloox/config.h new file mode 100644 index 0000000..fb5d2af --- /dev/null +++ b/libs/libgloox/config.h @@ -0,0 +1,27 @@ +/* + Copyright (c) 2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef CONFIG_H__ +#define CONFIG_H__ + +#if ( defined _WIN32 ) && !defined( __SYMBIAN32__ ) +# include "../config.h.win" +#elif defined( _WIN32_WCE ) +# include "../config.h.win" +#elif defined( __SYMBIAN32__ ) +# include "../config.h.symbian" +#else +# include "config.h.unix" // run ./configure to create config.h.unix +#endif + +#endif // CONFIG_H__ diff --git a/libs/libgloox/config.h.unix b/libs/libgloox/config.h.unix new file mode 100644 index 0000000..9d24b5b --- /dev/null +++ b/libs/libgloox/config.h.unix @@ -0,0 +1,123 @@ +/* config.h.unix. Generated from config.h.unix.in by configure. */ +/* config.h.unix.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ARPA_NAMESER_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the `dn_skipname' function. */ +/* #undef HAVE_DN_SKIPNAME */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the `getaddrinfo' function. */ +/* #undef HAVE_GETADDRINFO */ + +/* Define to 1 if you want TLS support (GnuTLS). Undefine HAVE_OPENSSL. */ +/* #undef HAVE_GNUTLS */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `bind' library (-lbind). */ +/* #undef HAVE_LIBBIND */ + +/* Define to 1 if you want IDN support. */ +/* #undef HAVE_LIBIDN */ + +/* Define to 1 if you have the `network' library (-lnetwork). */ +#define HAVE_LIBNETWORK 1 + +/* Define to 1 if you have the `resolv' library (-lresolv). */ +/* #undef HAVE_LIBRESOLV */ + +/* Define to 1 if you have the `socket' library (-lsocket). */ +/* #undef HAVE_LIBSOCKET */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you want TLS support (OpenSSL). Undefine HAVE_GNUTLS. */ +#define HAVE_OPENSSL 1 + +/* Define if you have POSIX threads libraries and header files. */ +#define HAVE_PTHREAD 1 + +/* Define to 1 if you have the `res_query' function. */ +/* #undef HAVE_RES_QUERY */ + +/* Define to 1 if you have the `res_querydomain' function. */ +/* #undef HAVE_RES_QUERYDOMAIN */ + +/* Define to 1 if you have the `setsockopt' function. */ +#define HAVE_SETSOCKOPT 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you want Stream Compression support. */ +#define HAVE_ZLIB 1 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "gloox" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "js@camaya.net" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "gloox" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "gloox 1.0" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "gloox" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.0" + +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +/* #undef PTHREAD_CREATE_JOINABLE */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif diff --git a/libs/libgloox/connectionbase.h b/libs/libgloox/connectionbase.h new file mode 100644 index 0000000..ca2a96c --- /dev/null +++ b/libs/libgloox/connectionbase.h @@ -0,0 +1,167 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef CONNECTIONBASE_H__ +#define CONNECTIONBASE_H__ + +#include "gloox.h" +#include "connectiondatahandler.h" + +#include + +namespace gloox +{ + + /** + * @brief An abstract base class for a connection. + * + * You should not need to use this class directly. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API ConnectionBase + { + public: + /** + * Constructor. + * @param cdh An object derived from @ref ConnectionDataHandler that will receive + * received data. + */ + ConnectionBase( ConnectionDataHandler* cdh ) + : m_handler( cdh ), m_state( StateDisconnected ), m_port( -1 ) + {} + + /** + * Virtual destructor. + */ + virtual ~ConnectionBase() { cleanup(); } + + /** + * Used to initiate the connection. + * @return Returns the connection state. + */ + virtual ConnectionError connect() = 0; + + /** + * Use this periodically to receive data from the socket. + * @param timeout The timeout to use for select in microseconds. Default of -1 means blocking. + * @return The state of the connection. + */ + virtual ConnectionError recv( int timeout = -1 ) = 0; + + /** + * Use this function to send a string of data over the wire. The function returns only after + * all data has been sent. + * @param data The data to send. + * @return @b True if the data has been sent (no guarantee of receipt), @b false + * in case of an error. + */ + virtual bool send( const std::string& data ) = 0; + + /** + * Use this function to put the connection into 'receive mode', i.e. this function returns only + * when the connection is terminated. + * @return Returns a value indicating the disconnection reason. + */ + virtual ConnectionError receive() = 0; + + /** + * Disconnects an established connection. NOOP if no active connection exists. + */ + virtual void disconnect() = 0; + + /** + * This function is called after a disconnect to clean up internal state. It is also called by + * ConnectionBase's destructor. + */ + virtual void cleanup() {} + + /** + * Returns the current connection state. + * @return The state of the connection. + */ + ConnectionState state() const { return m_state; } + + /** + * Use this function to register a new ConnectionDataHandler. There can be only one + * ConnectionDataHandler at any one time. + * @param cdh The new ConnectionDataHandler. + */ + void registerConnectionDataHandler( ConnectionDataHandler* cdh ) { m_handler = cdh; } + + /** + * Sets the server to connect to. + * @param server The server to connect to. Either IP or fully qualified domain name. + * @param port The port to connect to. + */ + void setServer( const std::string &server, int port = -1 ) { m_server = server; m_port = port; } + + /** + * Returns the currently set server/IP. + * @return The server host/IP. + */ + const std::string& server() const { return m_server; } + + /** + * Returns the currently set port. + * @return The server port. + */ + int port() const { return m_port; } + + /** + * Returns the local port. + * @return The local port. + */ + virtual int localPort() const { return -1; } + + /** + * Returns the locally bound IP address. + * @return The locally bound IP address. + */ + virtual const std::string localInterface() const { return EmptyString; } + + /** + * Returns current connection statistics. + * @param totalIn The total number of bytes received. + * @param totalOut The total number of bytes sent. + */ + virtual void getStatistics( long int &totalIn, long int &totalOut ) = 0; + + /** + * This function returns a new instance of the current ConnectionBase-derived object. + * The idea is to be able to 'clone' ConnectionBase-derived objects without knowing of + * what type they are exactly. + * @return A new Connection* instance. + */ + virtual ConnectionBase* newInstance() const = 0; + + protected: + /** A handler for incoming data and connect/disconnect events. */ + ConnectionDataHandler* m_handler; + + /** Holds the current connection state. */ + ConnectionState m_state; + + /** Holds the server's name/address. */ + std::string m_server; + + /** Holds the port to connect to. */ + int m_port; + + }; + +} + +#endif // CONNECTIONBASE_H__ diff --git a/libs/libgloox/connectionbosh.cpp b/libs/libgloox/connectionbosh.cpp new file mode 100644 index 0000000..f0a1124 --- /dev/null +++ b/libs/libgloox/connectionbosh.cpp @@ -0,0 +1,641 @@ +/* + * Copyright (c) 2007-2009 by Jakob Schroeter + * This file is part of the gloox library. http://camaya.net/gloox + * + * This software is distributed under a license. The full license + * agreement can be found in the file LICENSE in this distribution. + * This software may not be copied, modified, sold or distributed + * other than expressed in the named license agreement. + * + * This software is distributed without any warranty. + */ + +#include "config.h" + +#include "gloox.h" + +#include "connectionbosh.h" +#include "logsink.h" +#include "prep.h" +#include "tag.h" +#include "util.h" + +#include +#include +#include +#include + +namespace gloox +{ + + ConnectionBOSH::ConnectionBOSH( ConnectionBase* connection, const LogSink& logInstance, + const std::string& boshHost, const std::string& xmppServer, + int xmppPort ) + : ConnectionBase( 0 ), + m_logInstance( logInstance ), m_parser( this ), m_boshHost( boshHost ), m_path( "/http-bind/" ), + m_rid( 0 ), m_initialStreamSent( false ), m_openRequests( 0 ), + m_maxOpenRequests( 2 ), m_wait( 30 ), m_hold( 2 ), m_streamRestart( false ), + m_lastRequestTime( std::time( 0 ) ), m_minTimePerRequest( 0 ), m_bufferContentLength( 0 ), + m_connMode( ModePipelining ) + { + initInstance( connection, xmppServer, xmppPort ); + } + + ConnectionBOSH::ConnectionBOSH( ConnectionDataHandler* cdh, ConnectionBase* connection, + const LogSink& logInstance, const std::string& boshHost, + const std::string& xmppServer, int xmppPort ) + : ConnectionBase( cdh ), + m_logInstance( logInstance ), m_parser( this ), m_boshHost( boshHost ), m_path( "/http-bind/" ), + m_rid( 0 ), m_initialStreamSent( false ), m_openRequests( 0 ), + m_maxOpenRequests( 2 ), m_wait( 30 ), m_hold( 2 ), m_streamRestart( false ), + m_lastRequestTime( std::time( 0 ) ), m_minTimePerRequest( 0 ), m_bufferContentLength( 0 ), + m_connMode( ModePipelining ) + { + initInstance( connection, xmppServer, xmppPort ); + } + + void ConnectionBOSH::initInstance( ConnectionBase* connection, const std::string& xmppServer, + const int xmppPort ) + { +// FIXME: check return value + prep::idna( xmppServer, m_server ); + m_port = xmppPort; + if( m_port != -1 ) + { + m_boshedHost = m_boshHost + ":" + util::int2string( m_port ); + } + + // drop this connection into our pool of available connections + if( connection ) + { + connection->registerConnectionDataHandler( this ); + m_connectionPool.push_back( connection ); + } + } + + ConnectionBOSH::~ConnectionBOSH() + { + util::clearList( m_activeConnections ); + util::clearList( m_connectionPool ); + } + + ConnectionBase* ConnectionBOSH::newInstance() const + { + ConnectionBase* pBaseConn = 0; + + if( !m_connectionPool.empty() ) + { + pBaseConn = m_connectionPool.front()->newInstance(); + } + else if( !m_activeConnections.empty() ) + { + pBaseConn = m_activeConnections.front()->newInstance(); + } + else + { + return 0; + } + + return new ConnectionBOSH( m_handler, pBaseConn, m_logInstance, + m_boshHost, m_server, m_port ); + } + + ConnectionError ConnectionBOSH::connect() + { + if( m_state >= StateConnecting ) + return ConnNoError; + + if( !m_handler ) + return ConnNotConnected; + + m_state = StateConnecting; + m_logInstance.dbg( LogAreaClassConnectionBOSH, + "bosh initiating connection to server: " + + ( ( m_connMode == ModePipelining ) ? std::string( "Pipelining" ) + : ( ( m_connMode == ModeLegacyHTTP ) ? std::string( "LegacyHTTP" ) + : std::string( "PersistentHTTP" ) ) ) ); + getConnection(); + return ConnNoError; // FIXME? + } + + void ConnectionBOSH::disconnect() + { + if( ( m_connMode == ModePipelining && m_activeConnections.empty() ) + || ( m_connectionPool.empty() && m_activeConnections.empty() ) ) + return; + + if( m_state != StateDisconnected ) + { + ++m_rid; + + std::string requestBody = ""; + m_sendBuffer = EmptyString; + } + sendRequest( requestBody ); + + m_logInstance.dbg( LogAreaClassConnectionBOSH, "bosh disconnection request sent" ); + } + else + { + m_logInstance.err( LogAreaClassConnectionBOSH, + "disconnecting from server in a non-graceful fashion" ); + } + + util::ForEach( m_activeConnections, &ConnectionBase::disconnect ); + util::ForEach( m_connectionPool, &ConnectionBase::disconnect ); + + m_state = StateDisconnected; + if( m_handler ) + m_handler->handleDisconnect( this, ConnUserDisconnected ); + } + + ConnectionError ConnectionBOSH::recv( int timeout ) + { + if( m_state == StateDisconnected ) + return ConnNotConnected; + + if( !m_connectionPool.empty() ) + m_connectionPool.front()->recv( 0 ); + if( !m_activeConnections.empty() ) + m_activeConnections.front()->recv( timeout ); + + // If there are no open requests then the spec allows us to send an empty request... + // (Some CMs do not obey this, it seems) + if( ( m_openRequests == 0 || m_sendBuffer.size() > 0 ) && m_state == StateConnected ) + { + m_logInstance.dbg( LogAreaClassConnectionBOSH, + "Sending empty request (or there is data in the send buffer)" ); + sendXML(); + } + + return ConnNoError; // FIXME? + } + + bool ConnectionBOSH::send( const std::string& data ) + { + + if( m_state == StateDisconnected ) + return false; + + if( data.substr( 0, 2 ) == " dropped" ); +// return true; +// } + } + else if( data == "
" ) + return true; + + m_sendBuffer += data; + sendXML(); + + return true; + } + + /* Sends XML. Wraps data in a tag, and then passes to sendRequest(). */ + bool ConnectionBOSH::sendXML() + { + if( m_state != StateConnected ) + { + m_logInstance.warn( LogAreaClassConnectionBOSH, + "Data sent before connection established (will be buffered)" ); + return false; + } + + if( m_sendBuffer.empty() ) + { + time_t now = time( 0 ); + unsigned int delta = (int)(now - m_lastRequestTime); + if( delta < m_minTimePerRequest && m_openRequests > 0 ) + { + m_logInstance.dbg( LogAreaClassConnectionBOSH, "Too little time between requests: " + util::int2string( delta ) + " seconds" ); + return false; + } + m_logInstance.dbg( LogAreaClassConnectionBOSH, "Send buffer is empty, sending empty request" ); + } + + ++m_rid; + + std::string requestBody = ""; + m_logInstance.dbg( LogAreaClassConnectionBOSH, "Restarting stream" ); + } + else + { + requestBody += ">" + m_sendBuffer + ""; + } + // Send a request. Force if we are not sending an empty request, or if there are no connections open + if( sendRequest( requestBody ) ) + { + m_logInstance.dbg( LogAreaClassConnectionBOSH, "Successfully sent m_sendBuffer" ); + m_sendBuffer = EmptyString; + m_streamRestart = false; + } + else + { + --m_rid; // I think... (may need to rethink when acks are implemented) + m_logInstance.warn( LogAreaClassConnectionBOSH, + "Unable to send. Connection not complete, or too many open requests," + " so added to buffer.\n" ); + } + + return true; + } + + /* Chooses the appropriate connection, or opens a new one if necessary. Wraps xml in HTTP and sends. */ + bool ConnectionBOSH::sendRequest( const std::string& xml ) + { + ConnectionBase* conn = getConnection(); + if( !conn ) + return false; + + std::string request = "POST " + m_path; + if( m_connMode == ModeLegacyHTTP ) + { + request += " HTTP/1.0\r\n"; + request += "Connection: close\r\n"; + } + else + request += " HTTP/1.1\r\n"; + + request += "Host: " + m_boshedHost + "\r\n"; + request += "Content-Type: text/xml; charset=utf-8\r\n"; + request += "Content-Length: " + util::int2string( xml.length() ) + "\r\n"; + request += "User-Agent: gloox/" + GLOOX_VERSION + "\r\n\r\n"; + request += xml; + + + if( conn->send( request ) ) + { + m_lastRequestTime = time( 0 ); + ++m_openRequests; + return true; + } +// else // FIXME What to do in this case? +// printf( "Error while trying to send on socket (state: %d)\n", conn->state() ); + + return false; + } + + bool ci_equal( char ch1, char ch2 ) + { + return std::toupper( (unsigned char)ch1 ) == std::toupper( (unsigned char)ch2 ); + } + + std::string::size_type ci_find( const std::string& str1, const std::string& str2 ) + { + std::string::const_iterator pos = std::search( str1.begin(), str1.end(), + str2.begin(), str2.end(), ci_equal ); + if( pos == str1.end() ) + return std::string::npos; + else + return std::distance( str1.begin(), pos ); + } + + const std::string ConnectionBOSH::getHTTPField( const std::string& field ) + { + std::string::size_type fp = ci_find( m_bufferHeader, "\r\n" + field + ": " ); + + if( fp == std::string::npos ) + return EmptyString; + + fp += field.length() + 4; + + const std::string::size_type fp2 = m_bufferHeader.find( "\r\n", fp ); + if( fp2 == std::string::npos ) + return EmptyString; + + return m_bufferHeader.substr( fp, fp2 - fp ); + } + + ConnectionError ConnectionBOSH::receive() + { + ConnectionError err = ConnNoError; + while( m_state != StateDisconnected && ( err = recv( 10 ) ) == ConnNoError ) + ; + return err == ConnNoError ? ConnNotConnected : err; + } + + void ConnectionBOSH::cleanup() + { + m_state = StateDisconnected; + + util::ForEach( m_activeConnections, &ConnectionBase::cleanup ); + util::ForEach( m_connectionPool, &ConnectionBase::cleanup ); + } + + void ConnectionBOSH::getStatistics( long int& totalIn, long int& totalOut ) + { + util::ForEach( m_activeConnections, &ConnectionBase::getStatistics, totalIn, totalOut ); + util::ForEach( m_connectionPool, &ConnectionBase::getStatistics, totalIn, totalOut ); + } + + void ConnectionBOSH::handleReceivedData( const ConnectionBase* /*connection*/, + const std::string& data ) + { + m_buffer += data; + std::string::size_type headerLength = 0; + while( ( headerLength = m_buffer.find( "\r\n\r\n" ) ) != std::string::npos ) + { + m_bufferHeader = m_buffer.substr( 0, headerLength+2 ); + + const std::string& statusCode = m_bufferHeader.substr( 9, 3 ); + if( statusCode != "200" ) + { + m_logInstance.warn( LogAreaClassConnectionBOSH, + "Received error via legacy HTTP status code: " + statusCode + + ". Disconnecting." ); + m_state = StateDisconnected; // As per XEP, consider connection broken + disconnect(); + } + + m_bufferContentLength = atol( getHTTPField( "Content-Length" ).c_str() ); + if( !m_bufferContentLength ) + return; + + if( m_connMode != ModeLegacyHTTP && ( getHTTPField( "Connection" ) == "close" + || m_bufferHeader.substr( 0, 8 ) == "HTTP/1.0" ) ) + { + m_logInstance.dbg( LogAreaClassConnectionBOSH, + "Server indicated lack of support for HTTP/1.1 - falling back to HTTP/1.0" ); + m_connMode = ModeLegacyHTTP; + } + + if( m_buffer.length() >= ( headerLength + 4 + m_bufferContentLength ) ) + { + putConnection(); + --m_openRequests; + std::string xml = m_buffer.substr( headerLength + 4, m_bufferContentLength ); + m_parser.feed( xml ); + m_buffer.erase( 0, headerLength + 4 + m_bufferContentLength ); + m_bufferContentLength = 0; + m_bufferHeader = EmptyString; + } + else + { + m_logInstance.warn( LogAreaClassConnectionBOSH, "buffer length mismatch" ); + break; + } + } + } + + void ConnectionBOSH::handleConnect( const ConnectionBase* /*connection*/ ) + { + if( m_state == StateConnecting ) + { + m_rid = rand() % 100000 + 1728679472; + + Tag requestBody( "body" ); + requestBody.setXmlns( XMLNS_HTTPBIND ); + requestBody.setXmlns( XMLNS_XMPP_BOSH, "xmpp" ); + + requestBody.addAttribute( "content", "text/xml; charset=utf-8" ); + requestBody.addAttribute( "hold", (long)m_hold ); + requestBody.addAttribute( "rid", (long)m_rid ); + requestBody.addAttribute( "ver", "1.6" ); + requestBody.addAttribute( "wait", (long)m_wait ); + requestBody.addAttribute( "ack", 0 ); + requestBody.addAttribute( "secure", "false" ); + requestBody.addAttribute( "route", "xmpp:" + m_server + ":5222" ); + requestBody.addAttribute( "xml:lang", "en" ); + requestBody.addAttribute( "xmpp:version", "1.0" ); + requestBody.addAttribute( "to", m_server ); + + m_logInstance.dbg( LogAreaClassConnectionBOSH, "sending bosh connection request" ); + sendRequest( requestBody.xml() ); + } + } + + void ConnectionBOSH::handleDisconnect( const ConnectionBase* /*connection*/, + ConnectionError reason ) + { + if( m_handler && m_state == StateConnecting ) + { + m_state = StateDisconnected; + m_handler->handleDisconnect( this, reason ); + return; + } + + switch( m_connMode ) // FIXME avoid that if we're disconnecting on purpose + { + case ModePipelining: + m_connMode = ModeLegacyHTTP; // Server seems not to support pipelining + m_logInstance.dbg( LogAreaClassConnectionBOSH, + "connection closed - falling back to HTTP/1.0 connection method" ); + break; + case ModeLegacyHTTP: + case ModePersistentHTTP: + // FIXME do we need to do anything here? +// printf( "A TCP connection %p was disconnected (reason: %d).\n", connection, reason ); + break; + } + } + + void ConnectionBOSH::handleTag( Tag* tag ) + { + if( !m_handler || tag->name() != "body" ) + return; + + if( m_streamRestart ) + { + m_streamRestart = false; + m_logInstance.dbg( LogAreaClassConnectionBOSH, "sending spoofed " ); + m_handler->handleReceivedData( this, "" + "" ); + } + + if( tag->hasAttribute( "sid" ) ) + { + m_state = StateConnected; + m_sid = tag->findAttribute( "sid" ); + + if( tag->hasAttribute( "requests" ) ) + { + const int serverRequests = atoi( tag->findAttribute( "requests" ).c_str() ); + if( serverRequests < m_maxOpenRequests ) + { + m_maxOpenRequests = serverRequests; + m_logInstance.dbg( LogAreaClassConnectionBOSH, + "bosh parameter 'requests' now set to " + tag->findAttribute( "requests" ) ); + } + } + if( tag->hasAttribute( "hold" ) ) + { + const int maxHold = atoi( tag->findAttribute( "hold" ).c_str() ); + if( maxHold < m_hold ) + { + m_hold = maxHold; + m_logInstance.dbg( LogAreaClassConnectionBOSH, + "bosh parameter 'hold' now set to " + tag->findAttribute( "hold" ) ); + } + } + if( tag->hasAttribute( "wait" ) ) + { + const int maxWait = atoi( tag->findAttribute( "wait" ).c_str() ); + if( maxWait < m_wait ) + { + m_wait = maxWait; + m_logInstance.dbg( LogAreaClassConnectionBOSH, + "bosh parameter 'wait' now set to " + tag->findAttribute( "wait" ) + + " seconds" ); + } + } + if( tag->hasAttribute( "polling" ) ) + { + const int minTime = atoi( tag->findAttribute( "polling" ).c_str() ); + m_minTimePerRequest = minTime; + m_logInstance.dbg( LogAreaClassConnectionBOSH, + "bosh parameter 'polling' now set to " + tag->findAttribute( "polling" ) + + " seconds" ); + } + + if( m_state < StateConnected ) + m_handler->handleConnect( this ); + + m_handler->handleReceivedData( this, "" // FIXME move to send() so that + // it is more clearly a response + // to the initial stream opener? + "" ); + } + + if( tag->findAttribute( "type" ) == "terminate" ) + { + m_logInstance.dbg( LogAreaClassConnectionBOSH, + "bosh connection closed by server: " + tag->findAttribute( "condition" ) ); + m_state = StateDisconnected; + m_handler->handleDisconnect( this, ConnStreamClosed ); + return; + } + + const TagList& stanzas = tag->children(); + TagList::const_iterator it = stanzas.begin(); + for( ; it != stanzas.end(); ++it ) + m_handler->handleReceivedData( this, (*it)->xml() ); + } + + ConnectionBase* ConnectionBOSH::getConnection() + { + if( m_openRequests > 0 && m_openRequests >= m_maxOpenRequests ) + { + m_logInstance.warn( LogAreaClassConnectionBOSH, + "Too many requests already open. Cannot send." ); + return 0; + } + + ConnectionBase* conn = 0; + switch( m_connMode ) + { + case ModePipelining: + if( !m_activeConnections.empty() ) + { + m_logInstance.dbg( LogAreaClassConnectionBOSH, "Using default connection for Pipelining." ); + return m_activeConnections.front(); + } + else if( !m_connectionPool.empty() ) + { + m_logInstance.warn( LogAreaClassConnectionBOSH, + "Pipelining selected, but no connection open. Opening one." ); + return activateConnection(); + } + else + m_logInstance.warn( LogAreaClassConnectionBOSH, + "No available connections to pipeline on." ); + break; + case ModeLegacyHTTP: + case ModePersistentHTTP: + { + if( !m_connectionPool.empty() ) + { + m_logInstance.dbg( LogAreaClassConnectionBOSH, "LegacyHTTP/PersistentHTTP selected, " + "using connection from pool." ); + return activateConnection(); + } + else if( !m_activeConnections.empty() ) + { + m_logInstance.dbg( LogAreaClassConnectionBOSH, "No connections in pool, creating a new one." ); + conn = m_activeConnections.front()->newInstance(); + conn->registerConnectionDataHandler( this ); + m_connectionPool.push_back( conn ); + conn->connect(); + } + else + m_logInstance.warn( LogAreaClassConnectionBOSH, + "No available connections to send on." ); + break; + } + } + return 0; + } + + ConnectionBase* ConnectionBOSH::activateConnection() + { + ConnectionBase* conn = m_connectionPool.front(); + m_connectionPool.pop_front(); + if( conn->state() == StateConnected ) + { + m_activeConnections.push_back( conn ); + return conn; + } + + m_logInstance.dbg( LogAreaClassConnectionBOSH, "Connecting pooled connection." ); + m_connectionPool.push_back( conn ); + conn->connect(); + return 0; + } + + void ConnectionBOSH::putConnection() + { + ConnectionBase* conn = m_activeConnections.front(); + + switch( m_connMode ) + { + case ModeLegacyHTTP: + m_logInstance.dbg( LogAreaClassConnectionBOSH, "Disconnecting LegacyHTTP connection" ); + conn->disconnect(); + conn->cleanup(); // This is necessary + m_activeConnections.pop_front(); + m_connectionPool.push_back( conn ); + break; + case ModePersistentHTTP: + m_logInstance.dbg( LogAreaClassConnectionBOSH, "Deactivating PersistentHTTP connection" ); + m_activeConnections.pop_front(); + m_connectionPool.push_back( conn ); + break; + case ModePipelining: + m_logInstance.dbg( LogAreaClassConnectionBOSH, "Keeping Pipelining connection" ); + default: + break; + } + } + +} diff --git a/libs/libgloox/connectionbosh.h b/libs/libgloox/connectionbosh.h new file mode 100644 index 0000000..8781c4c --- /dev/null +++ b/libs/libgloox/connectionbosh.h @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2007-2009 by Jakob Schroeter + * This file is part of the gloox library. http://camaya.net/gloox + * + * This software is distributed under a license. The full license + * agreement can be found in the file LICENSE in this distribution. + * This software may not be copied, modified, sold or distributed + * other than expressed in the named license agreement. + * + * This software is distributed without any warranty. + */ + +#ifndef CONNECTIONBOSH_H__ +#define CONNECTIONBOSH_H__ + +#include "gloox.h" +#include "connectionbase.h" +#include "logsink.h" +#include "taghandler.h" +#include "parser.h" + +#include +#include +#include + +namespace gloox +{ + + /** + * @brief This is an implementation of a BOSH (HTTP binding) connection. + * + * Usage: + * + * @code + * Client *c = new Client( ... ); + * c->setConnectionImpl( new ConnectionBOSH( c, + * new ConnectionTCPClient( c->logInstance(), httpServer, httpPort ), + * c->logInstance(), boshHost, xmpphost, xmppPort ) ); + * @endcode + * + * Make sure to pass the BOSH connection manager's host/port to the transport connection + * (ConnectionTCPClient in this case), and the XMPP server's host and port to the BOSH connection. + * You must also pass to BOSH the address of the BOSH server you are dealing with, this is used + * in the HTTP Host header. + * + * In the case of using ConnectionBOSH through a HTTP proxy, supply httpServer and httpPort as + * those of the proxy. In all cases, boshHost should be set to the hostname (not IP address) of + * the server running the BOSH connection manager. + * + * The reason why ConnectionBOSH doesn't manage its own ConnectionTCPClient is that it allows it + * to be used with other transports (like chained SOCKS5/HTTP proxies, or ConnectionTLS + * for HTTPS). + * + * @note To avoid problems, you should disable TLS in gloox by calling + * ClientBase::setTls( TLSDisabled ). + * + * Sample configurations for different servers can be found in the bosh_example.cpp file included + * with gloox in the @b src/examples/ directory. + * + * @author Matthew Wild + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API ConnectionBOSH : public ConnectionBase, ConnectionDataHandler, TagHandler + { + public: + /** + * Constructs a new ConnectionBOSH object. + * @param connection A transport connection. It should be configured to connect to + * the BOSH connection manager's (or a HTTP proxy's) host and port, @b not to the XMPP host. + * ConnectionBOSH will own the transport connection and delete it in its destructor. + * @param logInstance The log target. Obtain it from ClientBase::logInstance(). + * @param boshHost The hostname of the BOSH connection manager + * @param xmppServer A server to connect to. This is the XMPP server's address, @b not the + * connection manager's. + * @param xmppPort The port to connect to. This is the XMPP server's port, @b not the connection + * manager's. + * @note To properly use this object, you have to set a ConnectionDataHandler using + * registerConnectionDataHandler(). This is not necessary if this object is + * part of a 'connection chain', e.g. with ConnectionSOCKS5Proxy. + */ + ConnectionBOSH( ConnectionBase* connection, const LogSink& logInstance, const std::string& boshHost, + const std::string& xmppServer, int xmppPort = 5222 ); + + /** + * Constructs a new ConnectionBOSH object. + * @param cdh An ConnectionDataHandler-derived object that will handle incoming data. + * @param connection A transport connection. It should be configured to connect to + * the connection manager's (or proxy's) host and port, @b not to the XMPP host. ConnectionBOSH + * will own the transport connection and delete it in its destructor. + * @param logInstance The log target. Obtain it from ClientBase::logInstance(). + * @param boshHost The hostname of the BOSH connection manager (not any intermediate proxy) + * @param xmppServer A server to connect to. This is the XMPP server's address, @b not the connection + * manager's. + * @param xmppPort The port to connect to. This is the XMPP server's port, @b not the connection + * manager's. + */ + ConnectionBOSH( ConnectionDataHandler* cdh, ConnectionBase* connection, + const LogSink& logInstance, const std::string& boshHost, + const std::string& xmppServer, int xmppPort = 5222 ); + + /** + * Virtual destructor + */ + virtual ~ConnectionBOSH(); + + /** + * The supported connection modes. Usually auto-detected. + */ + enum ConnMode + { + ModeLegacyHTTP, /**< HTTP 1.0 connections, closed after receiving a response */ + ModePersistentHTTP, /**< HTTP 1.1 connections, re-used after receiving a response */ + ModePipelining /**< HTTP Pipelining (implies HTTP 1.1) a single connection is used */ + }; + + /** + * Sets the XMPP server to proxy to. + * @param xmppHost The XMPP server hostname (IP address). + * @param xmppPort The XMPP server port. + */ + void setServer( const std::string& xmppHost, unsigned short xmppPort = 5222 ) + { m_server = xmppHost; m_port = xmppPort; } + + /** + * Sets the path on the connection manager to request + * @param path The path, the default is "/http-bind/", which is the default for + * many connection managers. + */ + void setPath( const std::string& path ) { m_path = path; } + + /** + * Sets the connection mode + * @param mode The connection mode, @sa ConnMode + * @note In the case that a mode is selected that the connection manager + * or proxy does not support, gloox will fall back to using HTTP/1.0 connections, + * which should work with any server. + */ + void setMode( ConnMode mode ) { m_connMode = mode; } + + // reimplemented from ConnectionBase + virtual ConnectionError connect(); + + // reimplemented from ConnectionBase + virtual ConnectionError recv( int timeout = -1 ); + + // reimplemented from ConnectionBase + virtual bool send( const std::string& data ); + + // reimplemented from ConnectionBase + virtual ConnectionError receive(); + + // reimplemented from ConnectionBase + virtual void disconnect(); + + // reimplemented from ConnectionBase + virtual void cleanup(); + + // reimplemented from ConnectionBase + virtual void getStatistics( long int& totalIn, long int& totalOut ); + + // reimplemented from ConnectionDataHandler + virtual void handleReceivedData( const ConnectionBase* connection, const std::string& data ); + + // reimplemented from ConnectionDataHandler + virtual void handleConnect( const ConnectionBase* connection ); + + // reimplemented from ConnectionDataHandler + virtual void handleDisconnect( const ConnectionBase* connection, ConnectionError reason ); + + // reimplemented from ConnectionDataHandler + virtual ConnectionBase* newInstance() const; + + // reimplemented from TagHandler + virtual void handleTag( Tag* tag ); + + private: + ConnectionBOSH& operator=( const ConnectionBOSH& ); + void initInstance( ConnectionBase* connection, const std::string& xmppServer, const int xmppPort ); + bool sendRequest( const std::string& xml ); + bool sendXML(); + const std::string getHTTPField( const std::string& field ); + ConnectionBase* getConnection(); + ConnectionBase* activateConnection(); + void putConnection(); + + //ConnectionBase *m_connection; + const LogSink& m_logInstance; + + Parser m_parser; // Used for parsing XML section of responses + std::string m_boshHost; // The hostname of the BOSH connection manager + std::string m_boshedHost; // The hostname of the BOSH connection manager + : + port + std::string m_path; // The path part of the URL that we need to request + + // BOSH parameters + unsigned long m_rid; + std::string m_sid; + + bool m_initialStreamSent; + int m_openRequests; + int m_maxOpenRequests; + int m_wait; + int m_hold; + + bool m_streamRestart; // Set to true if we are waiting for an acknowledgement of a stream restart + + time_t m_lastRequestTime; + unsigned long m_minTimePerRequest; + + std::string m_buffer; // Buffer of received data + std::string m_bufferHeader; // HTTP header of data currently in buffer // FIXME doens't need to be member + std::string::size_type m_bufferContentLength; // Length of the data in the current response + + std::string m_sendBuffer; // Data waiting to be sent + + typedef std::list ConnectionList; + ConnectionList m_activeConnections; + ConnectionList m_connectionPool; + ConnMode m_connMode; + + }; + +} + +#endif // CONNECTIONBOSH_H__ diff --git a/libs/libgloox/connectiondatahandler.h b/libs/libgloox/connectiondatahandler.h new file mode 100644 index 0000000..424ca55 --- /dev/null +++ b/libs/libgloox/connectiondatahandler.h @@ -0,0 +1,66 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef CONNECTIONDATAHANDLER_H__ +#define CONNECTIONDATAHANDLER_H__ + +#include "gloox.h" + +#include + +namespace gloox +{ + + class ConnectionBase; + + /** + * @brief This is an abstract base class to receive events from a ConnectionBase-derived object. + * + * You should not need to use this class directly. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API ConnectionDataHandler + { + public: + /** + * Virtual Destructor. + */ + virtual ~ConnectionDataHandler() {} + + /** + * This function is called for received from the underlying transport. + * @param connection The connection that received the data. + * @param data The data received. + */ + virtual void handleReceivedData( const ConnectionBase* connection, const std::string& data ) = 0; + + /** + * This function is called when e.g. the raw TCP connection was established. + * @param connection The connection. + */ + virtual void handleConnect( const ConnectionBase* connection ) = 0; + + /** + * This connection is called when e.g. the raw TCP connection was closed. + * @param connection The connection. + * @param reason The reason for the disconnect. + */ + virtual void handleDisconnect( const ConnectionBase* connection, ConnectionError reason ) = 0; + }; + +} + +#endif // CONNECTIONDATAHANDLER_H__ diff --git a/libs/libgloox/connectionhandler.h b/libs/libgloox/connectionhandler.h new file mode 100644 index 0000000..fc0d1eb --- /dev/null +++ b/libs/libgloox/connectionhandler.h @@ -0,0 +1,52 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef CONNECTIONHANDLER_H__ +#define CONNECTIONHANDLER_H__ + +#include "connectionbase.h" + +namespace gloox +{ + + /** + * @brief This is an abstract base class to receive incoming connection attempts. Do not + * confuse this with ConnectionListener, which is used with XMPP streams and has a + * completely different meaning. + * + * You should not need to use this class directly. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API ConnectionHandler + { + public: + /** + * Virtual Destructor. + */ + virtual ~ConnectionHandler() {} + + /** + * This function is called to receive an incoming connection. + * @param server The server that the connection was made to. + * @param connection The incoming connection. + */ + virtual void handleIncomingConnection( ConnectionBase* server, ConnectionBase* connection ) = 0; + + }; + +} + +#endif // CONNECTIONHANDLER_H__ diff --git a/libs/libgloox/connectionhttpproxy.cpp b/libs/libgloox/connectionhttpproxy.cpp new file mode 100644 index 0000000..fa3223a --- /dev/null +++ b/libs/libgloox/connectionhttpproxy.cpp @@ -0,0 +1,215 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "gloox.h" + +#include "connectionhttpproxy.h" +#include "dns.h" +#include "logsink.h" +#include "prep.h" +#include "base64.h" +#include "util.h" + +#include + +namespace gloox +{ + + ConnectionHTTPProxy::ConnectionHTTPProxy( ConnectionBase* connection, + const LogSink& logInstance, + const std::string& server, int port ) + : ConnectionBase( 0 ), m_connection( connection ), + m_logInstance( logInstance ), m_http11( false ) + { +// FIXME check return value? + prep::idna( server, m_server ); + m_port = port; + + if( m_connection ) + m_connection->registerConnectionDataHandler( this ); + } + + ConnectionHTTPProxy::ConnectionHTTPProxy( ConnectionDataHandler* cdh, + ConnectionBase* connection, + const LogSink& logInstance, + const std::string& server, int port ) + : ConnectionBase( cdh ), m_connection( connection ), + m_logInstance( logInstance ) + { +// FIXME check return value? + prep::idna( server, m_server ); + m_port = port; + + if( m_connection ) + m_connection->registerConnectionDataHandler( this ); + } + + ConnectionHTTPProxy::~ConnectionHTTPProxy() + { + delete m_connection; + } + + ConnectionBase* ConnectionHTTPProxy::newInstance() const + { + ConnectionBase* conn = m_connection ? m_connection->newInstance() : 0; + return new ConnectionHTTPProxy( m_handler, conn, m_logInstance, m_server, m_port ); + } + + void ConnectionHTTPProxy::setConnectionImpl( ConnectionBase* connection ) + { + if( m_connection ) + delete m_connection; + + m_connection = connection; + } + + ConnectionError ConnectionHTTPProxy::connect() + { + if( m_connection && m_handler ) + { + m_state = StateConnecting; + return m_connection->connect(); + } + + return ConnNotConnected; + } + + void ConnectionHTTPProxy::disconnect() + { + m_state = StateDisconnected; + if( m_connection ) + m_connection->disconnect(); + } + + ConnectionError ConnectionHTTPProxy::recv( int timeout ) + { + return m_connection ? m_connection->recv( timeout ) : ConnNotConnected; + } + + ConnectionError ConnectionHTTPProxy::receive() + { + return m_connection ? m_connection->receive() : ConnNotConnected; + } + + bool ConnectionHTTPProxy::send( const std::string& data ) + { + return m_connection && m_connection->send( data ); + } + + void ConnectionHTTPProxy::cleanup() + { + m_state = StateDisconnected; + + if( m_connection ) + m_connection->cleanup(); + } + + void ConnectionHTTPProxy::getStatistics( long int& totalIn, long int& totalOut ) + { + if( m_connection ) + m_connection->getStatistics( totalIn, totalOut ); + else + totalIn = totalOut = 0; + } + + void ConnectionHTTPProxy::handleReceivedData( const ConnectionBase* /*connection*/, + const std::string& data ) + { + if( !m_handler ) + return; + + if( m_state == StateConnecting ) + { + m_proxyHandshakeBuffer += data; + if( ( !m_proxyHandshakeBuffer.compare( 0, 12, "HTTP/1.0 200" ) + || !m_proxyHandshakeBuffer.compare( 0, 12, "HTTP/1.1 200" ) ) + && !m_proxyHandshakeBuffer.compare( m_proxyHandshakeBuffer.length() - 4, 4, "\r\n\r\n" ) ) + { + m_proxyHandshakeBuffer = EmptyString; + m_state = StateConnected; + m_logInstance.dbg( LogAreaClassConnectionHTTPProxy, + "http proxy connection established" ); + m_handler->handleConnect( this ); + } + else if( !m_proxyHandshakeBuffer.compare( 9, 3, "407" ) ) + { + m_handler->handleDisconnect( this, ConnProxyAuthRequired ); + m_connection->disconnect(); + } + else if( !m_proxyHandshakeBuffer.compare( 9, 3, "403" ) + || !m_proxyHandshakeBuffer.compare( 9, 3, "404" ) ) + { + m_handler->handleDisconnect( this, ConnProxyAuthFailed ); + m_connection->disconnect(); + } + } + else if( m_state == StateConnected ) + m_handler->handleReceivedData( this, data ); + } + + void ConnectionHTTPProxy::handleConnect( const ConnectionBase* /*connection*/ ) + { + if( m_connection ) + { + std::string server = m_server; + int port = m_port; + if( port == -1 ) + { + const DNS::HostMap& servers = DNS::resolve( m_server, m_logInstance ); + if( !servers.empty() ) + { + const std::pair< std::string, int >& host = *servers.begin(); + server = host.first; + port = host.second; + } + } + std::string message = "Requesting http proxy connection to " + server + ":" + + util::int2string( port ); + m_logInstance.dbg( LogAreaClassConnectionHTTPProxy, message ); + + std::string os = "CONNECT " + server + ":" + util::int2string( port ) + " HTTP/1." + + util::int2string( m_http11 ? 1 : 0 ) + "\r\n" + "Host: " + server + "\r\n" + "Content-Length: 0\r\n" + "Proxy-Connection: Keep-Alive\r\n" + "Pragma: no-cache\r\n" + "User-Agent: gloox/" + GLOOX_VERSION + "\r\n"; + + if( !m_proxyUser.empty() && !m_proxyPwd.empty() ) + { + os += "Proxy-Authorization: Basic " + Base64::encode64( m_proxyUser + ":" + m_proxyPwd ) + + "\r\n"; + } + os += "\r\n"; + + if( !m_connection->send( os ) ) + { + m_state = StateDisconnected; + if( m_handler ) + m_handler->handleDisconnect( this, ConnIoError ); + } + } + } + + void ConnectionHTTPProxy::handleDisconnect( const ConnectionBase* /*connection*/, + ConnectionError reason ) + { + m_state = StateDisconnected; + m_logInstance.dbg( LogAreaClassConnectionHTTPProxy, "HTTP Proxy connection closed" ); + + if( m_handler ) + m_handler->handleDisconnect( this, reason ); + } + +} diff --git a/libs/libgloox/connectionhttpproxy.h b/libs/libgloox/connectionhttpproxy.h new file mode 100644 index 0000000..69ba399 --- /dev/null +++ b/libs/libgloox/connectionhttpproxy.h @@ -0,0 +1,171 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef CONNECTIONHTTPPROXY_H__ +#define CONNECTIONHTTPPROXY_H__ + +#include "gloox.h" +#include "connectionbase.h" +#include "logsink.h" + +#include + +namespace gloox +{ + + /** + * @brief This is an implementation of a simple HTTP Proxying connection. + * + * Usage: + * + * @code + * Client* c = new Client( ... ); + * ConnectionTCPClient* conn0 = new ConnectionTCPClient( c->logInstance(), + * proxyHost, proxyPort ); + * ConnectionHTTPProxy* conn1 = new ConnectionHTTPProxy( c, conn0, c->logInstance(), + * xmppHost, xmppPort ); + * c->setConnectionImpl( conn1 ); + * @endcode + * + * Make sure to pass the proxy host/port to the transport connection (ConnectionTCPClient in this case), + * and the XMPP host/port to the proxy connection. + * + * ConnectionHTTPProxy uses the CONNECT method to pass through the proxy. If your proxy does not + * allow this kind of connections, or if it kills connections after some time, you may want to use + * ConnectionBOSH instead or in addition. + * + * The reason why ConnectionHTTPProxy doesn't manage its own ConnectionTCPClient is that it allows it + * to be used with other transports (like IPv6 or chained SOCKS5/HTTP proxies). + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API ConnectionHTTPProxy : public ConnectionBase, public ConnectionDataHandler + { + public: + /** + * Constructs a new ConnectionHTTPProxy object. + * @param connection A transport connection. It should be configured to connect to + * the proxy host and port, @b not to the XMPP host. ConnectionHTTPProxy will own the + * transport connection and delete it in its destructor. + * @param logInstance The log target. Obtain it from ClientBase::logInstance(). + * @param server A server to connect to. This is the XMPP server's address, @b not the proxy. + * @param port The port to connect to. This is the XMPP server's port, @b not the proxy's. + * The default of -1 means that SRV records will be used to find out about the actual host:port. + * @note To properly use this object, you have to set a ConnectionDataHandler using + * registerConnectionDataHandler(). This is not necessary if this object is + * part of a 'connection chain', e.g. with ConnectionSOCKS5Proxy. + */ + ConnectionHTTPProxy( ConnectionBase* connection, const LogSink& logInstance, + const std::string& server, int port = -1 ); + + /** + * Constructs a new ConnectionHTTPProxy object. + * @param cdh An ConnectionDataHandler-derived object that will handle incoming data. + * @param connection A transport connection. It should be configured to connect to + * the proxy host and port, @b not to the XMPP host. ConnectionHTTPProxy will own the + * transport connection and delete it in its destructor. + * @param logInstance The log target. Obtain it from ClientBase::logInstance(). + * @param server A server to connect to. This is the XMPP server's address, @b not the proxy. + * @param port The port to connect to. This is the XMPP server's port, @b not the proxy's. + * The default of -1 means that SRV records will be used to find out about the actual host:port. + */ + ConnectionHTTPProxy( ConnectionDataHandler* cdh, ConnectionBase* connection, + const LogSink& logInstance, + const std::string& server, int port = -1 ); + + /** + * Virtual destructor + */ + virtual ~ConnectionHTTPProxy(); + + // reimplemented from ConnectionBase + virtual ConnectionError connect(); + + // reimplemented from ConnectionBase + virtual ConnectionError recv( int timeout = -1 ); + + // reimplemented from ConnectionBase + virtual bool send( const std::string& data ); + + // reimplemented from ConnectionBase + virtual ConnectionError receive(); + + // reimplemented from ConnectionBase + virtual void disconnect(); + + // reimplemented from ConnectionBase + virtual void cleanup(); + + // reimplemented from ConnectionBase + virtual void getStatistics( long int &totalIn, long int &totalOut ); + + // reimplemented from ConnectionDataHandler + virtual void handleReceivedData( const ConnectionBase* connection, const std::string& data ); + + // reimplemented from ConnectionDataHandler + virtual void handleConnect( const ConnectionBase* connection ); + + // reimplemented from ConnectionDataHandler + virtual void handleDisconnect( const ConnectionBase* connection, ConnectionError reason ); + + // reimplemented from ConnectionDataHandler + virtual ConnectionBase* newInstance() const; + + /** + * Sets the XMPP server to proxy to. + * @param host The XMPP server hostname (IP address). + * @param port The XMPP server port. The default of -1 means that SRV records will be used + * to find out about the actual host:port. + */ + void setServer( const std::string& host, int port = -1 ) + { m_server = host; m_port = port; } + + /** + * Sets proxy authorization credentials. + * @param user The user name to use for proxy authorization. + * @param password The password to use for proxy authorization. + */ + void setProxyAuth( const std::string& user, const std::string& password ) + { m_proxyUser = user; m_proxyPwd = password; } + + /** + * Sets the underlying transport connection. A possibly existing connection will be deleted. + * @param connection The ConnectionBase to replace the current connection, if any. + */ + void setConnectionImpl( ConnectionBase* connection ); + + /** + * Switches usage of HTTP/1.1 on or off. + * @param http11 Set this to @b true to connect through a HTTP/1.1-only proxy, or @b false + * to use HTTP/1.0. Defaults to HTTP/1.0 which should work with 99.9% of proxies. + */ + void setHTTP11( bool http11 ) { m_http11 = http11; } + + private: + ConnectionHTTPProxy &operator=( const ConnectionHTTPProxy& ); + + ConnectionBase* m_connection; + const LogSink& m_logInstance; + + std::string m_proxyUser; + std::string m_proxyPwd; + std::string m_proxyHandshakeBuffer; + + bool m_http11; + + }; + +} + +#endif // CONNECTIONHTTPPROXY_H__ diff --git a/libs/libgloox/connectionlistener.h b/libs/libgloox/connectionlistener.h new file mode 100644 index 0000000..27940cf --- /dev/null +++ b/libs/libgloox/connectionlistener.h @@ -0,0 +1,106 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef CONNECTIONLISTENER_H__ +#define CONNECTIONLISTENER_H__ + +#include "gloox.h" + +namespace gloox +{ + + class Error; + + /** + * @brief Derived classes can be registered as ConnectionListeners with the Client. + * + * This interface is mandatory to implement if a connection is to be made TLS-encrypted. + * In onTLSConnect(), the server's certificate information needs to be checked, and @b true + * returned if the certificate is to be accepted. + * + * @author Jakob Schroeter + */ + class GLOOX_API ConnectionListener + { + public: + /** + * Virtual Destructor. + */ + virtual ~ConnectionListener() {} + + /** + * This function notifies about successful connections. It will be called either after all + * authentication is finished if username/password were supplied, or after a connection has + * been established if no credentials were supplied. Depending on the setting of AutoPresence, + * a presence stanza is sent or not. + */ + virtual void onConnect() = 0; + + /** + * This function notifies about disconnection and its reason. + * If @b e indicates a stream error, you can use @ref ClientBase::streamError() to find out + * what exactly went wrong, and @ref ClientBase::streamErrorText() to retrieve any explaining text + * sent along with the error. + * If @b e indicates an authentication error, you can use @ref ClientBase::authError() + * to get a finer grained reason. + * @param e The reason for the disconnection. + */ + virtual void onDisconnect( ConnectionError e ) = 0; + + /** + * This function will be called when a resource has been bound to the stream. It + * will be called for any bound resource, including the main one. + * @note The bound resource may be different from the one requested. The server + * has the authority to change/overwrite the requested resource. + * @param resource The resource string. + * @since 1.0 + */ + virtual void onResourceBind( const std::string& resource ) { (void)resource; } + + /** + * This function is called (by a Client object) if an error occurs while trying to bind a resource. + * @param error A pointer to an Error object that contains more + * information. May be 0. + */ + virtual void onResourceBindError( const Error* error ) { (void) (error); } + + /** + * This function is called (by a Client object) if an error occurs while trying to establish + * a session. + * @param error A pointer to an Error object that contains more + * information. May be 0. + */ + virtual void onSessionCreateError( const Error* error ) { (void) (error); } + + /** + * This function is called when the connection was TLS/SSL secured. + * @param info Comprehensive info on the certificate. + * @return @b True if cert credentials are accepted, @b false otherwise. If @b false is returned + * the connection is terminated. + */ + virtual bool onTLSConnect( const CertInfo& info ) = 0; + + /** + * This function is called for certain stream events. Notifications are purely informational + * and implementation is optional. Not all StreamEvents will necessarily be emitted for + * a given connection. + * @param event A stream event. + * @since 0.9 + */ + virtual void onStreamEvent( StreamEvent event ) { (void) (event); } + + }; + +} + +#endif // CONNECTIONLISTENER_H__ diff --git a/libs/libgloox/connectionsocks5proxy.cpp b/libs/libgloox/connectionsocks5proxy.cpp new file mode 100644 index 0000000..0ee42f5 --- /dev/null +++ b/libs/libgloox/connectionsocks5proxy.cpp @@ -0,0 +1,377 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "config.h" + +#include "gloox.h" + +#include "connectionsocks5proxy.h" +#include "dns.h" +#include "logsink.h" +#include "prep.h" +#include "base64.h" +#include "util.h" + +#include +#include + +#include + +#if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ ) +# include +#endif + +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) +# include +#elif defined( _WIN32_WCE ) +# include +#endif + +namespace gloox +{ + + ConnectionSOCKS5Proxy::ConnectionSOCKS5Proxy( ConnectionBase* connection, + const LogSink& logInstance, + const std::string& server, + int port, bool ip ) + : ConnectionBase( 0 ), m_connection( connection ), + m_logInstance( logInstance ), m_s5state( S5StateDisconnected ), m_ip( ip ) + { +// FIXME check return value? + prep::idna( server, m_server ); + m_port = port; + + if( m_connection ) + m_connection->registerConnectionDataHandler( this ); + } + + ConnectionSOCKS5Proxy::ConnectionSOCKS5Proxy( ConnectionDataHandler* cdh, + ConnectionBase* connection, + const LogSink& logInstance, + const std::string& server, + int port, bool ip ) + : ConnectionBase( cdh ), m_connection( connection ), + m_logInstance( logInstance ), m_s5state( S5StateDisconnected ), m_ip( ip ) + { +// FIXME check return value? + prep::idna( server, m_server ); + m_port = port; + + if( m_connection ) + m_connection->registerConnectionDataHandler( this ); + } + + ConnectionSOCKS5Proxy::~ConnectionSOCKS5Proxy() + { + if( m_connection ) + delete m_connection; + } + + ConnectionBase* ConnectionSOCKS5Proxy::newInstance() const + { + ConnectionBase* conn = m_connection ? m_connection->newInstance() : 0; + return new ConnectionSOCKS5Proxy( m_handler, conn, m_logInstance, m_server, m_port, m_ip ); + } + + void ConnectionSOCKS5Proxy::setConnectionImpl( ConnectionBase* connection ) + { + if( m_connection ) + delete m_connection; + + m_connection = connection; + } + + ConnectionError ConnectionSOCKS5Proxy::connect() + { +// FIXME CHECKME + if( m_connection && m_connection->state() == StateConnected && m_handler ) + { + m_state = StateConnected; + m_s5state = S5StateConnected; + return ConnNoError; + } + + if( m_connection && m_handler ) + { + m_state = StateConnecting; + m_s5state = S5StateConnecting; + return m_connection->connect(); + } + + return ConnNotConnected; + } + + void ConnectionSOCKS5Proxy::disconnect() + { + if( m_connection ) + m_connection->disconnect(); + cleanup(); + } + + ConnectionError ConnectionSOCKS5Proxy::recv( int timeout ) + { + if( m_connection ) + return m_connection->recv( timeout ); + else + return ConnNotConnected; + } + + ConnectionError ConnectionSOCKS5Proxy::receive() + { + if( m_connection ) + return m_connection->receive(); + else + return ConnNotConnected; + } + + bool ConnectionSOCKS5Proxy::send( const std::string& data ) + { +// if( m_s5state != S5StateConnected ) +// { +// printf( "p data sent: " ); +// const char* x = data.c_str(); +// for( unsigned int i = 0; i < data.length(); ++i ) +// printf( "%02X ", (const char)x[i] ); +// printf( "\n" ); +// } + + if( m_connection ) + return m_connection->send( data ); + + return false; + } + + void ConnectionSOCKS5Proxy::cleanup() + { + m_state = StateDisconnected; + m_s5state = S5StateDisconnected; + + if( m_connection ) + m_connection->cleanup(); + } + + void ConnectionSOCKS5Proxy::getStatistics( long int &totalIn, long int &totalOut ) + { + if( m_connection ) + m_connection->getStatistics( totalIn, totalOut ); + else + { + totalIn = 0; + totalOut = 0; + } + } + + void ConnectionSOCKS5Proxy::handleReceivedData( const ConnectionBase* /*connection*/, + const std::string& data ) + { +// if( m_s5state != S5StateConnected ) +// { +// printf( "data recv: " ); +// const char* x = data.c_str(); +// for( unsigned int i = 0; i < data.length(); ++i ) +// printf( "%02X ", (const char)x[i] ); +// printf( "\n" ); +// } + + if( !m_connection || !m_handler ) + return; + + ConnectionError connError = ConnNoError; + + switch( m_s5state ) + { + case S5StateConnecting: + if( data.length() != 2 || data[0] != 0x05 ) + connError = ConnIoError; + + if( data[1] == 0x00 ) // no auth + { + negotiate(); + } + else if( data[1] == 0x02 && !m_proxyUser.empty() && !m_proxyPwd.empty() ) // user/password auth + { + m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy, + "authenticating to socks5 proxy as user " + m_proxyUser ); + m_s5state = S5StateAuthenticating; + char* d = new char[3 + m_proxyUser.length() + m_proxyPwd.length()]; + size_t pos = 0; + d[pos++] = 0x01; + d[pos++] = (char)m_proxyUser.length(); + strncpy( d + pos, m_proxyUser.c_str(), m_proxyUser.length() ); + pos += m_proxyUser.length(); + d[pos++] = (char)m_proxyPwd.length(); + strncpy( d + pos, m_proxyPwd.c_str(), m_proxyPwd.length() ); + pos += m_proxyPwd.length(); + + if( !send( std::string( d, pos ) ) ) + { + cleanup(); + m_handler->handleDisconnect( this, ConnIoError ); + } + delete[] d; + } + else + { + if( data[1] == (char)(unsigned char)0xFF && !m_proxyUser.empty() && !m_proxyPwd.empty() ) + connError = ConnProxyNoSupportedAuth; + else + connError = ConnProxyAuthRequired; + } + break; + case S5StateNegotiating: + if( data.length() >= 6 && data[0] == 0x05 ) + { + if( data[1] == 0x00 ) + { + m_state = StateConnected; + m_s5state = S5StateConnected; + m_handler->handleConnect( this ); + } + else // connection refused + connError = ConnConnectionRefused; + } + else + connError = ConnIoError; + break; + case S5StateAuthenticating: + if( data.length() == 2 && data[0] == 0x01 && data[1] == 0x00 ) + negotiate(); + else + connError = ConnProxyAuthFailed; + break; + case S5StateConnected: + m_handler->handleReceivedData( this, data ); + break; + default: + break; + } + + if( connError != ConnNoError ) + { + m_connection->disconnect(); + m_handler->handleDisconnect( this, connError ); + } + + } + + void ConnectionSOCKS5Proxy::negotiate() + { + m_s5state = S5StateNegotiating; + char* d = new char[m_ip ? 10 : 6 + m_server.length() + 1]; + size_t pos = 0; + d[pos++] = 0x05; // SOCKS version 5 + d[pos++] = 0x01; // command CONNECT + d[pos++] = 0x00; // reserved + int port = m_port; + std::string server = m_server; + if( m_ip ) // IP address + { + d[pos++] = 0x01; // IPv4 address + std::string s; + const size_t j = server.length(); + size_t l = 0; + for( size_t k = 0; k < j && l < 4; ++k ) + { + if( server[k] != '.' ) + s += server[k]; + + if( server[k] == '.' || k == j-1 ) + { + d[pos++] = static_cast( atoi( s.c_str() ) & 0xFF ); + s = EmptyString; + ++l; + } + } + } + else // hostname + { + if( port == -1 ) + { + const DNS::HostMap& servers = DNS::resolve( m_server, m_logInstance ); + if( servers.size() ) + { + const std::pair< std::string, int >& host = *servers.begin(); + server = host.first; + port = host.second; + } + } + d[pos++] = 0x03; // hostname + d[pos++] = (char)m_server.length(); + strncpy( d + pos, m_server.c_str(), m_server.length() ); + pos += m_server.length(); + } + int nport = htons( port ); + d[pos++] = static_cast( nport ); + d[pos++] = static_cast( nport >> 8 ); + + std::string message = "Requesting socks5 proxy connection to " + server + ":" + + util::int2string( port ); + m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy, message ); + + if( !send( std::string( d, pos ) ) ) + { + cleanup(); + m_handler->handleDisconnect( this, ConnIoError ); + } + delete[] d; + } + + void ConnectionSOCKS5Proxy::handleConnect( const ConnectionBase* /*connection*/ ) + { + if( m_connection ) + { + std::string server = m_server; + int port = m_port; + if( port == -1 ) + { + const DNS::HostMap& servers = DNS::resolve( m_server, m_logInstance ); + if( !servers.empty() ) + { + const std::pair< std::string, int >& host = *servers.begin(); + server = host.first; + port = host.second; + } + } + m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy, + "Attempting to negotiate socks5 proxy connection" ); + + const bool auth = !m_proxyUser.empty() && !m_proxyPwd.empty(); + const char d[4] = { + 0x05, // SOCKS version 5 + static_cast( auth ? 0x02 // two methods + : 0x01 ), // one method + 0x00, // method: no auth + 0x02 // method: username/password auth + }; + + if( !send( std::string( d, auth ? 4 : 3 ) ) ) + { + cleanup(); + if( m_handler ) + m_handler->handleDisconnect( this, ConnIoError ); + } + } + } + + void ConnectionSOCKS5Proxy::handleDisconnect( const ConnectionBase* /*connection*/, + ConnectionError reason ) + { + cleanup(); + m_logInstance.dbg( LogAreaClassConnectionSOCKS5Proxy, "socks5 proxy connection closed" ); + + if( m_handler ) + m_handler->handleDisconnect( this, reason ); + } + +} diff --git a/libs/libgloox/connectionsocks5proxy.h b/libs/libgloox/connectionsocks5proxy.h new file mode 100644 index 0000000..dfcbe1c --- /dev/null +++ b/libs/libgloox/connectionsocks5proxy.h @@ -0,0 +1,178 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef CONNECTIONSOCKS5PROXY_H__ +#define CONNECTIONSOCKS5PROXY_H__ + +#include "gloox.h" +#include "connectionbase.h" +#include "logsink.h" + +#include + +namespace gloox +{ + + /** + * @brief This is an implementation of a simple SOCKS5 Proxying connection (RFC 1928 + RFC 1929). + * + * To use with a SOCKS5 proxy: + * + * @code + * Client* c = new Client( ... ); + * c->setConnectionImpl( new ConnectionSOCKS5Proxy( c, + * new ConnectionTCPClient( c->logInstance(), proxyHost, proxyPort ), + * c->logInstance(), xmppHost, xmppPort ) ); + * @endcode + * + * Make sure to pass the proxy host/port to the transport connection (ConnectionTCPClient in this case), + * and the XMPP host/port to the proxy connection. + * + * The reason why ConnectionSOCKS5Proxy doesn't manage its own ConnectionTCPClient is that it allows it + * to be used with other transports (like IPv6 or chained HTTP/SOCKS5 proxies). + * + * @note This class is also used by the SOCKS5 bytestreams implementation (with slightly different + * semantics). + * + * @note Simple @b plain-text username/password authentication is supported. GSSAPI authentication + * is not supported. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API ConnectionSOCKS5Proxy : public ConnectionBase, public ConnectionDataHandler + { + public: + /** + * Constructs a new ConnectionSOCKS5Proxy object. + * @param connection A transport connection. It should be configured to connect to + * the proxy host and port, @b not to the (XMPP) host. ConnectionSOCKS5Proxy will own the + * transport connection and delete it in its destructor. + * @param logInstance The log target. Obtain it from ClientBase::logInstance(). + * @param server A server to connect to. This is the XMPP server's address, @b not the proxy. + * @param port The proxy's port to connect to. This is the (XMPP) server's port, @b not the proxy's. + * The default of -1 means that SRV records will be used to find out about the actual host:port. + * @param ip Indicates whether @c server is an IP address (true) or a host name (false). + * @note To properly use this object, you have to set a ConnectionDataHandler using + * registerConnectionDataHandler(). This is not necessary if this object is + * part of a 'connection chain', e.g. with ConnectionHTTPProxy. + */ + ConnectionSOCKS5Proxy( ConnectionBase* connection, const LogSink& logInstance, + const std::string& server, int port = -1, bool ip = false ); + + /** + * Constructs a new ConnectionSOCKS5Proxy object. + * @param cdh A ConnectionDataHandler-derived object that will handle incoming data. + * @param connection A transport connection. It should be configured to connect to + * the proxy host and port, @b not to the (XMPP) host. ConnectionSOCKS5Proxy will own the + * transport connection and delete it in its destructor. + * @param logInstance The log target. Obtain it from ClientBase::logInstance(). + * @param server A server to connect to. This is the XMPP server's address, @b not the proxy. + * @param port The proxy's port to connect to. This is the (XMPP) server's port, @b not the proxy's. + * The default of -1 means that SRV records will be used to find out about the actual host:port. + * @param ip Indicates whether @c server is an IP address (true) or a host name (false). + */ + ConnectionSOCKS5Proxy( ConnectionDataHandler* cdh, ConnectionBase* connection, + const LogSink& logInstance, + const std::string& server, int port = -1, bool ip = false ); + + /** + * Virtual destructor + */ + virtual ~ConnectionSOCKS5Proxy(); + + // reimplemented from ConnectionBase + virtual ConnectionError connect(); + + // reimplemented from ConnectionBase + virtual ConnectionError recv( int timeout = -1 ); + + // reimplemented from ConnectionBase + virtual bool send( const std::string& data ); + + // reimplemented from ConnectionBase + virtual ConnectionError receive(); + + // reimplemented from ConnectionBase + virtual void disconnect(); + + // reimplemented from ConnectionBase + virtual void cleanup(); + + // reimplemented from ConnectionBase + virtual void getStatistics( long int &totalIn, long int &totalOut ); + + // reimplemented from ConnectionDataHandler + virtual void handleReceivedData( const ConnectionBase* connection, const std::string& data ); + + // reimplemented from ConnectionDataHandler + virtual void handleConnect( const ConnectionBase* connection ); + + // reimplemented from ConnectionDataHandler + virtual void handleDisconnect( const ConnectionBase* connection, ConnectionError reason ); + + // reimplemented from ConnectionDataHandler + virtual ConnectionBase* newInstance() const; + + /** + * Sets the server to proxy to. + * @param host The server hostname (IP address). + * @param port The server port. The default of -1 means that SRV records will be used + * to find out about the actual host:port. + * @param ip Indicates whether @c host is an IP address (true) or a host name (false). + */ + void setServer( const std::string& host, int port = -1, bool ip = false ) + { m_server = host; m_port = port; m_ip = ip; } + + /** + * Sets proxy authorization credentials. + * @param user The user name to use for proxy authorization. + * @param password The password to use for proxy authorization. + */ + void setProxyAuth( const std::string& user, const std::string& password ) + { m_proxyUser = user; m_proxyPwd = password; } + + /** + * Sets the underlying transport connection. A possibly existing connection will be deleted. + * @param connection The ConnectionBase to replace the current connection, if any. + */ + void setConnectionImpl( ConnectionBase* connection ); + + private: + enum Socks5State + { + S5StateDisconnected, + S5StateConnecting, + S5StateNegotiating, + S5StateAuthenticating, + S5StateConnected + }; + + ConnectionSOCKS5Proxy &operator=( const ConnectionSOCKS5Proxy& ); + void negotiate(); + + ConnectionBase* m_connection; + const LogSink& m_logInstance; + + Socks5State m_s5state; + + std::string m_proxyUser; + std::string m_proxyPwd; + std::string m_proxyHandshakeBuffer; + bool m_ip; + + }; + +} + +#endif // CONNECTIONSOCKS5PROXY_H__ diff --git a/libs/libgloox/connectiontcpbase.cpp b/libs/libgloox/connectiontcpbase.cpp new file mode 100644 index 0000000..6a103a6 --- /dev/null +++ b/libs/libgloox/connectiontcpbase.cpp @@ -0,0 +1,200 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "gloox.h" + +#include "connectiontcpbase.h" +#include "dns.h" +#include "logsink.h" +#include "prep.h" +#include "mutexguard.h" + +#ifdef __MINGW32__ +# include +#endif + +#if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ ) +# include +# include +# include +# include +# include +# include +# include +#elif ( defined( _WIN32 ) || defined( _WIN32_WCE ) ) && !defined( __SYMBIAN32__ ) +# include +typedef int socklen_t; +#endif + +#include + +#include +#include + +namespace gloox +{ + + ConnectionTCPBase::ConnectionTCPBase( const LogSink& logInstance, + const std::string& server, int port ) + : ConnectionBase( 0 ), + m_logInstance( logInstance ), m_buf( 0 ), m_socket( -1 ), m_totalBytesIn( 0 ), + m_totalBytesOut( 0 ), m_bufsize( 1024 ), m_cancel( true ) + { + init( server, port ); + } + + ConnectionTCPBase::ConnectionTCPBase( ConnectionDataHandler* cdh, const LogSink& logInstance, + const std::string& server, int port ) + : ConnectionBase( cdh ), + m_logInstance( logInstance ), m_buf( 0 ), m_socket( -1 ), m_totalBytesIn( 0 ), + m_totalBytesOut( 0 ), m_bufsize( 1024 ), m_cancel( true ) + { + init( server, port ); + } + + void ConnectionTCPBase::init( const std::string& server, int port ) + { +// FIXME check return value? + prep::idna( server, m_server ); + m_port = port; + m_buf = (char*)calloc( m_bufsize + 1, sizeof( char ) ); + } + + ConnectionTCPBase::~ConnectionTCPBase() + { + cleanup(); + free( m_buf ); + m_buf = 0; + } + + void ConnectionTCPBase::disconnect() + { + util::MutexGuard rm( m_recvMutex ); + m_cancel = true; + } + + bool ConnectionTCPBase::dataAvailable( int timeout ) + { + if( m_socket < 0 ) + return true; // let recv() catch the closed fd + + fd_set fds; + struct timeval tv; + + FD_ZERO( &fds ); + // the following causes a C4127 warning in VC++ Express 2008 and possibly other versions. + // however, the reason for the warning can't be fixed in gloox. + FD_SET( m_socket, &fds ); + + tv.tv_sec = timeout / 1000000; + tv.tv_usec = timeout % 1000000; + + return ( ( select( m_socket + 1, &fds, 0, 0, timeout == -1 ? 0 : &tv ) > 0 ) + && FD_ISSET( m_socket, &fds ) != 0 ); + } + + ConnectionError ConnectionTCPBase::receive() + { + if( m_socket < 0 ) + return ConnNotConnected; + + ConnectionError err = ConnNoError; + while( !m_cancel && ( err = recv( 10 ) ) == ConnNoError ) + ; + return err == ConnNoError ? ConnNotConnected : err; + } + + bool ConnectionTCPBase::send( const std::string& data ) + { + m_sendMutex.lock(); + + if( data.empty() || ( m_socket < 0 ) ) + { + m_sendMutex.unlock(); + return false; + } + + int sent = 0; + for( size_t num = 0, len = data.length(); sent != -1 && num < len; num += sent ) + { + sent = static_cast( ::send( m_socket, (data.c_str()+num), (int)(len - num), 0 ) ); + } + + m_totalBytesOut += (int)data.length(); + + m_sendMutex.unlock(); + + if( sent == -1 && m_handler ) + m_handler->handleDisconnect( this, ConnIoError ); + + return sent != -1; + } + + void ConnectionTCPBase::getStatistics( long int &totalIn, long int &totalOut ) + { + totalIn = m_totalBytesIn; + totalOut = m_totalBytesOut; + } + + void ConnectionTCPBase::cleanup() + { + if( !m_sendMutex.trylock() ) + return; + + if( !m_recvMutex.trylock() ) + { + m_sendMutex.unlock(); + return; + } + + if( m_socket >= 0 ) + { + DNS::closeSocket( m_socket, m_logInstance ); + m_socket = -1; + } + + m_state = StateDisconnected; + m_cancel = true; + m_totalBytesIn = 0; + m_totalBytesOut = 0; + + m_recvMutex.unlock(), + m_sendMutex.unlock(); + } + + int ConnectionTCPBase::localPort() const + { + struct sockaddr local; + socklen_t len = (socklen_t)sizeof( local ); + if( getsockname ( m_socket, &local, &len ) < 0 ) + return -1; + else + return ntohs( ((struct sockaddr_in *)&local)->sin_port ); + } + + const std::string ConnectionTCPBase::localInterface() const + { + struct sockaddr_in local; + socklen_t len = (socklen_t)sizeof( local ); + if( getsockname ( m_socket, (reinterpret_cast( &local )), &len ) < 0 ) + return EmptyString; + else + { +// char addr[INET_ADDRSTRLEN]; +// return inet_ntop( AF_INET, &(local.sin_addr), addr, sizeof( addr ) ); //FIXME is this portable? + return inet_ntoa( local.sin_addr ); + } + } + +} diff --git a/libs/libgloox/connectiontcpbase.h b/libs/libgloox/connectiontcpbase.h new file mode 100644 index 0000000..5ece2b1 --- /dev/null +++ b/libs/libgloox/connectiontcpbase.h @@ -0,0 +1,134 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef CONNECTIONTCPBASE_H__ +#define CONNECTIONTCPBASE_H__ + +#include "gloox.h" +#include "connectionbase.h" +#include "logsink.h" +#include "mutex.h" + +#include + +namespace gloox +{ + + namespace util + { + class Mutex; + } + + /** + * @brief This is a base class for a simple TCP connection. + * + * You should not need to use this class directly. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API ConnectionTCPBase : public ConnectionBase + { + public: + /** + * Constructs a new ConnectionTCPBase object. + * @param logInstance The log target. Obtain it from ClientBase::logInstance(). + * @param server A server to connect to. + * @param port The port to connect to. The default of -1 means that XMPP SRV records + * will be used to find out about the actual host:port. + * @note To properly use this object, you have to set a ConnectionDataHandler using + * registerConnectionDataHandler(). This is not necessary if this object is + * part of a 'connection chain', e.g. with ConnectionHTTPProxy. + */ + ConnectionTCPBase( const LogSink& logInstance, const std::string& server, int port = -1 ); + + /** + * Constructs a new ConnectionTCPBase object. + * @param cdh An ConnectionDataHandler-derived object that will handle incoming data. + * @param logInstance The log target. Obtain it from ClientBase::logInstance(). + * @param server A server to connect to. + * @param port The port to connect to. The default of -1 means that SRV records will be used + * to find out about the actual host:port. + */ + ConnectionTCPBase( ConnectionDataHandler* cdh, const LogSink& logInstance, + const std::string& server, int port = -1 ); + + /** + * Virtual destructor + */ + virtual ~ConnectionTCPBase(); + + // reimplemented from ConnectionBase + virtual bool send( const std::string& data ); + + // reimplemented from ConnectionBase + virtual ConnectionError receive(); + + // reimplemented from ConnectionBase + virtual void disconnect(); + + // reimplemented from ConnectionBase + virtual void cleanup(); + + // reimplemented from ConnectionBase + virtual void getStatistics( long int &totalIn, long int &totalOut ); + + /** + * Gives access to the raw socket of this connection. Use it wisely. You can + * select()/poll() it and use ConnectionTCPBase::recv( -1 ) to fetch the data. + * @return The socket of the active connection, or -1 if no connection is established. + */ + int socket() const { return m_socket; } + + /** + * This function allows to set an existing socket with an established + * connection to use in this connection. You will still need to call connect() in order to + * negotiate the XMPP stream. You should not set a new socket after having called connect(). + * @param socket The existing socket. + */ + void setSocket( int socket ) { m_cancel = false; m_state = StateConnected; m_socket = socket; } + + /** + * Returns the local port. + * @return The local port. + */ + virtual int localPort() const; + + /** + * Returns the locally bound IP address. + * @return The locally bound IP address. + */ + virtual const std::string localInterface() const; + + protected: + ConnectionTCPBase& operator=( const ConnectionTCPBase& ); + void init( const std::string& server, int port ); + bool dataAvailable( int timeout = -1 ); + void cancel(); + + const LogSink& m_logInstance; + util::Mutex m_sendMutex; + util::Mutex m_recvMutex; + + char* m_buf; + int m_socket; + long int m_totalBytesIn; + long int m_totalBytesOut; + const int m_bufsize; + bool m_cancel; + + }; + +} + +#endif // CONNECTIONTCPBASE_H__ diff --git a/libs/libgloox/connectiontcpclient.cpp b/libs/libgloox/connectiontcpclient.cpp new file mode 100644 index 0000000..4b35bf6 --- /dev/null +++ b/libs/libgloox/connectiontcpclient.cpp @@ -0,0 +1,159 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "gloox.h" + +#include "connectiontcpclient.h" +#include "dns.h" +#include "logsink.h" +#include "mutexguard.h" + +#ifdef __MINGW32__ +# include +#endif + +#if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ ) +# include +# include +# include +# include +#elif ( defined( _WIN32 ) || defined( _WIN32_WCE ) ) && !defined( __SYMBIAN32__ ) +# include +#endif + +#include +#include + +namespace gloox +{ + + ConnectionTCPClient::ConnectionTCPClient( const LogSink& logInstance, + const std::string& server, int port ) + : ConnectionTCPBase( logInstance, server, port ) + { + } + + ConnectionTCPClient::ConnectionTCPClient( ConnectionDataHandler* cdh, const LogSink& logInstance, + const std::string& server, int port ) + : ConnectionTCPBase( cdh, logInstance, server, port ) + { + } + + + ConnectionTCPClient::~ConnectionTCPClient() + { + } + + ConnectionBase* ConnectionTCPClient::newInstance() const + { + return new ConnectionTCPClient( m_handler, m_logInstance, m_server, m_port ); + } + + ConnectionError ConnectionTCPClient::connect() + { + m_sendMutex.lock(); +// FIXME CHECKME + if( !m_handler ) + { + m_sendMutex.unlock(); + return ConnNotConnected; + } + + if( m_socket >= 0 && m_state > StateDisconnected ) + { + m_sendMutex.unlock(); + return ConnNoError; + } + + m_state = StateConnecting; + + if( m_socket < 0 ) + { + if( m_port == -1 ) + m_socket = DNS::connect( m_server, m_logInstance ); + else + m_socket = DNS::connect( m_server, m_port, m_logInstance ); + } + + m_sendMutex.unlock(); + + if( m_socket < 0 ) + { + switch( m_socket ) + { + case -ConnConnectionRefused: + m_logInstance.err( LogAreaClassConnectionTCPClient, + m_server + ": connection refused" ); + break; + case -ConnDnsError: + m_logInstance.err( LogAreaClassConnectionTCPClient, + m_server + ": host not found" ); + break; + default: + m_logInstance.err( LogAreaClassConnectionTCPClient, + "Unknown error condition" ); + break; + } + m_handler->handleDisconnect( this, (ConnectionError)-m_socket ); + return (ConnectionError)-m_socket; + } + else + { + m_state = StateConnected; + } + + m_cancel = false; + m_handler->handleConnect( this ); + return ConnNoError; + } + + ConnectionError ConnectionTCPClient::recv( int timeout ) + { + m_recvMutex.lock(); + + if( m_cancel || m_socket < 0 ) + { + m_recvMutex.unlock(); + return ConnNotConnected; + } + + if( !dataAvailable( timeout ) ) + { + m_recvMutex.unlock(); + return ConnNoError; + } + + int size = static_cast( ::recv( m_socket, m_buf, m_bufsize, 0 ) ); + if( size > 0 ) + m_totalBytesIn += size; + + m_recvMutex.unlock(); + + if( size <= 0 ) + { + ConnectionError error = ( size ? ConnIoError : ConnStreamClosed ); + if( m_handler ) + m_handler->handleDisconnect( this, error ); + return error; + } + + m_buf[size] = '\0'; + + if( m_handler ) + m_handler->handleReceivedData( this, std::string( m_buf, size ) ); + + return ConnNoError; + } + +} diff --git a/libs/libgloox/connectiontcpclient.h b/libs/libgloox/connectiontcpclient.h new file mode 100644 index 0000000..0f3a5b5 --- /dev/null +++ b/libs/libgloox/connectiontcpclient.h @@ -0,0 +1,83 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef CONNECTIONTCPCLIENT_H__ +#define CONNECTIONTCPCLIENT_H__ + +#include "gloox.h" +#include "connectiontcpbase.h" +#include "logsink.h" + +#include + +namespace gloox +{ + + /** + * @brief This is an implementation of a simple TCP connection. + * + * You should only need to use this class directly if you need access to some special feature, like + * the raw socket(), or if you need HTTP proxy support (see @ref gloox::ConnectionHTTPProxy for more + * information). + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API ConnectionTCPClient : public ConnectionTCPBase + { + public: + /** + * Constructs a new ConnectionTCPClient object. + * @param logInstance The log target. Obtain it from ClientBase::logInstance(). + * @param server A server to connect to. + * @param port The port to connect to. The default of -1 means that XMPP SRV records + * will be used to find out about the actual host:port. + * @note To properly use this object, you have to set a ConnectionDataHandler using + * registerConnectionDataHandler(). This is not necessary if this object is + * part of a 'connection chain', e.g. with ConnectionHTTPProxy. + */ + ConnectionTCPClient( const LogSink& logInstance, const std::string& server, int port = -1 ); + + /** + * Constructs a new ConnectionTCPClient object. + * @param cdh An ConnectionDataHandler-derived object that will handle incoming data. + * @param logInstance The log target. Obtain it from ClientBase::logInstance(). + * @param server A server to connect to. + * @param port The port to connect to. The default of -1 means that SRV records will be used + * to find out about the actual host:port. + */ + ConnectionTCPClient( ConnectionDataHandler* cdh, const LogSink& logInstance, + const std::string& server, int port = -1 ); + + /** + * Virtual destructor + */ + virtual ~ConnectionTCPClient(); + + // reimplemented from ConnectionBase + virtual ConnectionError recv( int timeout = -1 ); + + // reimplemented from ConnectionBase + virtual ConnectionError connect(); + + // reimplemented from ConnectionBase + virtual ConnectionBase* newInstance() const; + + private: + ConnectionTCPClient &operator=( const ConnectionTCPClient & ); + + }; + +} + +#endif // CONNECTIONTCPCLIENT_H__ diff --git a/libs/libgloox/connectiontcpserver.cpp b/libs/libgloox/connectiontcpserver.cpp new file mode 100644 index 0000000..fa108e2 --- /dev/null +++ b/libs/libgloox/connectiontcpserver.cpp @@ -0,0 +1,163 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "gloox.h" + +#include "connectiontcpserver.h" +#include "connectiontcpclient.h" +#include "connectionhandler.h" +#include "dns.h" +#include "logsink.h" +#include "mutex.h" +#include "mutexguard.h" +#include "util.h" + +#ifdef __MINGW32__ +# include +#endif + +#if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ ) +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) +# include +#elif defined( _WIN32_WCE ) +# include +#endif + +#include +#include + +#ifndef _WIN32_WCE +# include +#endif + +namespace gloox +{ + + ConnectionTCPServer::ConnectionTCPServer( ConnectionHandler* ch, const LogSink& logInstance, + const std::string& ip, int port ) + : ConnectionTCPBase( 0, logInstance, ip, port ), + m_connectionHandler( ch ) + { + } + + ConnectionTCPServer::~ConnectionTCPServer() + { + } + + ConnectionBase* ConnectionTCPServer::newInstance() const + { + return new ConnectionTCPServer( m_connectionHandler, m_logInstance, m_server, m_port ); + } + + ConnectionError ConnectionTCPServer::connect() + { + util::MutexGuard mg( &m_sendMutex ); + + if( m_socket >= 0 || m_state > StateDisconnected ) + return ConnNoError; + + m_state = StateConnecting; + + if( m_socket < 0 ) + m_socket = DNS::getSocket( m_logInstance ); + + if( m_socket < 0 ) + return ConnIoError; + + struct sockaddr_in local; + local.sin_family = AF_INET; + local.sin_port = static_cast( htons( m_port ) ); + local.sin_addr.s_addr = m_server.empty() ? INADDR_ANY : inet_addr( m_server.c_str() ); + memset( local.sin_zero, '\0', 8 ); + + if( bind( m_socket, (struct sockaddr*)&local, sizeof( struct sockaddr ) ) < 0 ) + { + std::string message = "bind() to " + ( m_server.empty() ? std::string( "*" ) : m_server ) + + " (" + inet_ntoa( local.sin_addr ) + ":" + util::int2string( m_port ) + ") failed. " +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + "WSAGetLastError: " + util::int2string( ::WSAGetLastError() ); +#else + "errno: " + util::int2string( errno ); +#endif + m_logInstance.dbg( LogAreaClassConnectionTCPServer, message ); + + return ConnIoError; + } + + if( listen( m_socket, 10 ) < 0 ) + { + std::string message = "listen on " + ( m_server.empty() ? std::string( "*" ) : m_server ) + + " (" + inet_ntoa( local.sin_addr ) + ":" + util::int2string( m_port ) + ") failed. " +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + "WSAGetLastError: " + util::int2string( ::WSAGetLastError() ); +#else + "errno: " + util::int2string( errno ); +#endif + m_logInstance.dbg( LogAreaClassConnectionTCPServer, message ); + + return ConnIoError; + } + + m_cancel = false; + return ConnNoError; + } + + ConnectionError ConnectionTCPServer::recv( int timeout ) + { + m_recvMutex.lock(); + + if( m_cancel || m_socket < 0 || !m_connectionHandler ) + { + m_recvMutex.unlock(); + return ConnNotConnected; + } + + if( !dataAvailable( timeout ) ) + { + m_recvMutex.unlock(); + return ConnNoError; + } + + struct sockaddr_in they; + int sin_size = sizeof( struct sockaddr_in ); +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + int newfd = static_cast( accept( static_cast( m_socket ), (struct sockaddr*)&they, &sin_size ) ); +#else + int newfd = accept( m_socket, (struct sockaddr*)&they, (socklen_t*)&sin_size ); +#endif + + m_recvMutex.unlock(); + + ConnectionTCPClient* conn = new ConnectionTCPClient( m_logInstance, inet_ntoa( they.sin_addr ), + ntohs( they.sin_port ) ); + conn->setSocket( newfd ); + m_connectionHandler->handleIncomingConnection( this, conn ); + + return ConnNoError; + } + +} diff --git a/libs/libgloox/connectiontcpserver.h b/libs/libgloox/connectiontcpserver.h new file mode 100644 index 0000000..ef0bd08 --- /dev/null +++ b/libs/libgloox/connectiontcpserver.h @@ -0,0 +1,77 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef CONNECTIONTCPSERVER_H__ +#define CONNECTIONTCPSERVER_H__ + +#include "gloox.h" +#include "connectiontcpbase.h" +#include "logsink.h" + +#include + +namespace gloox +{ + + class ConnectionHandler; + + /** + * @brief This is an implementation of a simple listening TCP connection. + * + * You should not need to use this class directly. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API ConnectionTCPServer : public ConnectionTCPBase + { + public: + /** + * Constructs a new ConnectionTCPServer object. + * @param ch An ConnectionHandler-derived object that will handle incoming connections. + * @param logInstance The log target. Obtain it from ClientBase::logInstance(). + * @param ip The local IP address to listen on. This must @b not be a hostname. + * Leave this empty to listen on all local interfaces. + * @param port The port to listen on. + */ + ConnectionTCPServer( ConnectionHandler* ch, const LogSink& logInstance, + const std::string& ip, int port ); + + /** + * Virtual destructor + */ + virtual ~ConnectionTCPServer(); + + // reimplemented from ConnectionBase + virtual ConnectionError recv( int timeout = -1 ); + + /** + * This function actually starts @c listening on the port given in the + * constructor. + */ + // reimplemented from ConnectionBase + virtual ConnectionError connect(); + + // reimplemented from ConnectionBase + virtual ConnectionBase* newInstance() const; + + private: + ConnectionTCPServer &operator=( const ConnectionTCPServer & ); + + ConnectionHandler* m_connectionHandler; + + }; + +} + +#endif // CONNECTIONTCPSERVER_H__ diff --git a/libs/libgloox/connectiontls.cpp b/libs/libgloox/connectiontls.cpp new file mode 100644 index 0000000..7429bed --- /dev/null +++ b/libs/libgloox/connectiontls.cpp @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2007-2009 by Jakob Schroeter + * This file is part of the gloox library. http://camaya.net/gloox + * + * This software is distributed under a license. The full license + * agreement can be found in the file LICENSE in this distribution. + * This software may not be copied, modified, sold or distributed + * other than expressed in the named license agreement. + * + * This software is distributed without any warranty. + */ + + +#include "connectiontls.h" +#include "tlsdefault.h" + +namespace gloox +{ + + ConnectionTLS::ConnectionTLS( ConnectionDataHandler* cdh, ConnectionBase* conn, const LogSink& log ) + : ConnectionBase( cdh ), + m_connection( conn ), m_tls( 0 ), m_tlsHandler( 0 ), + m_log( log ) + { + if( m_connection ) + m_connection->registerConnectionDataHandler( this ); + } + + ConnectionTLS::ConnectionTLS( ConnectionBase* conn, const LogSink& log ) + : ConnectionBase( 0 ), + m_connection( conn ), m_tls( 0 ), m_tlsHandler( 0 ), m_log( log ) + { + if( m_connection ) + m_connection->registerConnectionDataHandler( this ); + } + + ConnectionTLS::~ConnectionTLS() + { + delete m_connection; + delete m_tls; + } + + void ConnectionTLS::setConnectionImpl( ConnectionBase* connection ) + { + if( m_connection ) + m_connection->registerConnectionDataHandler( 0 ); + + m_connection = connection; + + if( m_connection ) + m_connection->registerConnectionDataHandler( this ); + } + + ConnectionError ConnectionTLS::connect() + { + if( !m_connection ) + return ConnNotConnected; + + if( m_state == StateConnected ) + return ConnNoError; + + if( !m_tls ) + m_tls = getTLSBase( this, m_connection->server() ); + + if( !m_tls ) + return ConnTlsNotAvailable; + + if( !m_tls->init( m_clientKey, m_clientCerts, m_cacerts ) ) + return ConnTlsFailed; + +// m_tls->setCACerts( m_cacerts ); +// m_tls->setClientCert( m_clientKey, m_clientCerts ); + + m_state = StateConnecting; + + if( m_connection->state() != StateConnected ) + return m_connection->connect(); + + if( m_tls->handshake() ) + return ConnNoError; + else + return ConnTlsFailed; + } + + ConnectionError ConnectionTLS::recv( int timeout ) + { + if( m_connection->state() == StateConnected ) + { + return m_connection->recv( timeout ); + } + else + { + m_log.log( LogLevelWarning, LogAreaClassConnectionTLS, + "Attempt to receive data on a connection that is not connected (or is connecting)" ); + return ConnNotConnected; + } + } + + bool ConnectionTLS::send( const std::string& data ) + { + if( m_state != StateConnected ) + return false; + + m_tls->encrypt( data ); + return true; + } + + ConnectionError ConnectionTLS::receive() + { + if( m_connection ) + return m_connection->receive(); + else + return ConnNotConnected; + } + + void ConnectionTLS::disconnect() + { + if( m_connection ) + m_connection->disconnect(); + + cleanup(); + } + + void ConnectionTLS::cleanup() + { + if( m_connection ) + m_connection->cleanup(); + if( m_tls ) + m_tls->cleanup(); + + m_state = StateDisconnected; + } + + void ConnectionTLS::getStatistics( long int& totalIn, long int& totalOut ) + { + if( m_connection ) + m_connection->getStatistics( totalIn, totalOut ); + } + + ConnectionBase* ConnectionTLS::newInstance() const + { + ConnectionBase* newConn = 0; + if( m_connection ) + newConn = m_connection->newInstance(); + return new ConnectionTLS( m_handler, newConn, m_log ); + } + + void ConnectionTLS::handleReceivedData( const ConnectionBase* /*connection*/, const std::string& data ) + { + if( m_tls ) + m_tls->decrypt( data ); + } + + void ConnectionTLS::handleConnect( const ConnectionBase* /*connection*/ ) + { + if( m_tls ) + m_tls->handshake(); + } + + void ConnectionTLS::handleDisconnect( const ConnectionBase* /*connection*/, ConnectionError reason ) + { + if( m_handler ) + m_handler->handleDisconnect( this, reason ); + + cleanup(); + } + + void ConnectionTLS::handleEncryptedData( const TLSBase* /*tls*/, const std::string& data ) + { + if( m_connection ) + m_connection->send( data ); + } + + void ConnectionTLS::handleDecryptedData( const TLSBase* /*tls*/, const std::string& data ) + { + if( m_handler ) + m_handler->handleReceivedData( this, data ); + else + { + m_log.log( LogLevelDebug, LogAreaClassConnectionTLS, "Data received and decrypted but no handler" ); + } + } + + void ConnectionTLS::handleHandshakeResult( const TLSBase* tls, bool success, CertInfo& certinfo ) + { + if( success ) + { + m_state = StateConnected; + m_log.log( LogLevelDebug, LogAreaClassConnectionTLS, "TLS handshake succeeded" ); + if( m_tlsHandler ) + m_tlsHandler->handleHandshakeResult( tls, success, certinfo ); + if( m_handler ) + m_handler->handleConnect( this ); + } + else + { + m_state = StateDisconnected; + m_log.log( LogLevelWarning, LogAreaClassConnectionTLS, "TLS handshake failed" ); + if( m_tlsHandler ) + m_tlsHandler->handleHandshakeResult( tls, success, certinfo ); + } + } + +} diff --git a/libs/libgloox/connectiontls.h b/libs/libgloox/connectiontls.h new file mode 100644 index 0000000..fc197c0 --- /dev/null +++ b/libs/libgloox/connectiontls.h @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2007-2009 by Jakob Schroeter + * This file is part of the gloox library. http://camaya.net/gloox + * + * This software is distributed under a license. The full license + * agreement can be found in the file LICENSE in this distribution. + * This software may not be copied, modified, sold or distributed + * other than expressed in the named license agreement. + * + * This software is distributed without any warranty. + */ + +#ifndef CONNECTIONTLS_H__ +#define CONNECTIONTLS_H__ + +#include "gloox.h" +#include "logsink.h" +#include "connectionbase.h" +#include "tlsdefault.h" +#include "connectiondatahandler.h" + +#include + +namespace gloox +{ + + /** + * @brief This is an implementation of a TLS/SSL connection. + * + * You should not need to use this function directly. However, + * you can use it to connect to the legacy Jabber SSL port, + * 5223. + * + * Usage: + * @code + * Client *c = new Client( ... ); + * c->setConnectionImpl( new ConnectionTLS( c, + * new ConnectionTCP( c->logInstance(), server, 5223 ), + * c->logInstance()) ); + * @endcode + * + * Due to the need for handshaking data to be sent/received before the connection is fully + * established, be sure not to use the connection until ConnectionDataHandler::handleConnect() + * of the specified ConnectionDataHandler is called. + * + * @author Jakob Schroeter + * @author Matthew Wild + * @since 1.0 + */ + + class GLOOX_API ConnectionTLS : public TLSHandler, public ConnectionBase, public ConnectionDataHandler + { + public: + /** + * Constructs a new ConnectionTLS object. + * @param cdh The ConnectionDataHandler that will be notified of events from this connection + * @param conn A transport connection. It should be configured to connect to + * the server and port you wish to make the encrypted connection to. + * ConnectionTLS will own the transport connection and delete it in its destructor. + * @param log The log target. Obtain it from ClientBase::logInstance(). + */ + ConnectionTLS( ConnectionDataHandler* cdh, ConnectionBase* conn, const LogSink& log ); + + /** + * Constructs a new ConnectionTLS object. + * @param conn A transport connection. It should be configured to connect to + * the server and port you wish to make the encrypted connection to. + * ConnectionTLS will own the transport connection and delete it in its destructor. + * @param log The log target. Obtain it from ClientBase::logInstance(). + */ + ConnectionTLS( ConnectionBase* conn, const LogSink& log ); + + /** + * Virtual Destructor. + */ + virtual ~ConnectionTLS(); + + /** + * Use this function to set a number of trusted root CA certificates which shall be + * used to verify a servers certificate. + * @param cacerts A list of absolute paths to CA root certificate files in PEM format. + * @note This function is a wrapper for TLSBase::setCACerts(). + */ + void setCACerts( const StringList& cacerts ) + { + m_cacerts = cacerts; + } + + /** + * This function is used to retrieve certificate and connection info of a encrypted connection. + * @return Certificate information. + * @note This funcztion is a wrapper around TLSBase::fetchTLSInfo(). + */ + const CertInfo& fetchTLSInfo() const { return m_certInfo; } + + /** + * Use this function to set the user's certificate and private key. The certificate will + * be presented to the server upon request and can be used for SASL EXTERNAL authentication. + * The user's certificate file should be a bundle of more than one certificate in PEM format. + * The first one in the file should be the user's certificate, each cert following that one + * should have signed the previous one. + * @note These certificates are not necessarily the same as those used to verify the server's + * certificate. + * @param clientKey The absolute path to the user's private key in PEM format. + * @param clientCerts A path to a certificate bundle in PEM format. + * @note This function is a wrapper around TLSBase::setClientCert(). + */ + void setClientCert( const std::string& clientKey, const std::string& clientCerts ) + { + m_clientKey = clientKey; + m_clientCerts = clientCerts; + } + + /** + * Sets the transport connection. + * @param connection The transport connection to use. + */ + void setConnectionImpl( ConnectionBase* connection ); + + /** + * Registers an TLSHandler derived object. Only the handleHandshakeResult() + * function will be used after a handshake took place. + * You can review certificate info there. + * @param th The TLSHandler to register. + * @note If no handler is set, ConnectionTLS will accept + * any certificate and continue with the connection. + */ + void registerTLSHandler( TLSHandler* th ) { m_tlsHandler = th; } + + // reimplemented from ConnectionBase + virtual ConnectionError connect(); + + // reimplemented from ConnectionBase + virtual ConnectionError recv( int timeout = -1 ); + + // reimplemented from ConnectionBase + virtual bool send( const std::string& data ); + + // reimplemented from ConnectionBase + virtual ConnectionError receive(); + + // reimplemented from ConnectionBase + virtual void disconnect(); + + // reimplemented from ConnectionBase + virtual void cleanup(); + + // reimplemented from ConnectionBase + virtual void getStatistics( long int& totalIn, long int& totalOut ); + + // reimplemented from ConnectionDataHandler + virtual void handleReceivedData( const ConnectionBase* connection, const std::string& data ); + + // reimplemented from ConnectionDataHandler + virtual void handleConnect( const ConnectionBase* connection ); + + // reimplemented from ConnectionDataHandler + virtual void handleDisconnect( const ConnectionBase* connection, ConnectionError reason ); + + // reimplemented from ConnectionDataHandler + virtual ConnectionBase* newInstance() const; + + // reimplemented from TLSHandler + virtual void handleEncryptedData( const TLSBase*, const std::string& data ); + + // reimplemented from TLSHandler + virtual void handleDecryptedData( const TLSBase*, const std::string& data ); + + // reimplemented from TLSHandler + virtual void handleHandshakeResult( const TLSBase* base, bool success, CertInfo& certinfo ); + + protected: + /** + * Returns a TLS object (client). Reimplement to change the + * type of the object. + * @return A TLS object. + */ + virtual TLSBase* getTLSBase( TLSHandler* th, const std::string server ) + { + return new TLSDefault( th, server, TLSDefault::VerifyingClient ); + } + + ConnectionBase* m_connection; + TLSBase* m_tls; + TLSHandler* m_tlsHandler; + CertInfo m_certInfo; + const LogSink& m_log; + StringList m_cacerts; + std::string m_clientCerts; + std::string m_clientKey; + + private: + ConnectionTLS& operator=( const ConnectionTLS& ); + + }; + +} + +#endif // CONNECTIONTLS_H__ diff --git a/libs/libgloox/connectiontlsserver.cpp b/libs/libgloox/connectiontlsserver.cpp new file mode 100644 index 0000000..01aff74 --- /dev/null +++ b/libs/libgloox/connectiontlsserver.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2009 by Jakob Schroeter + * This file is part of the gloox library. http://camaya.net/gloox + * + * This software is distributed under a license. The full license + * agreement can be found in the file LICENSE in this distribution. + * This software may not be copied, modified, sold or distributed + * other than expressed in the named license agreement. + * + * This software is distributed without any warranty. + */ + +#include "connectiontlsserver.h" + +namespace gloox +{ + + ConnectionTLSServer::ConnectionTLSServer( ConnectionDataHandler* cdh, ConnectionBase* conn, + const LogSink& log ) + : ConnectionTLS( cdh, conn, log ) + { + } + + ConnectionTLSServer::ConnectionTLSServer( ConnectionBase* conn, const LogSink& log ) + : ConnectionTLS( conn, log ) + { + } + + ConnectionTLSServer::~ConnectionTLSServer() + { + } + + TLSBase* ConnectionTLSServer::getTLSBase( TLSHandler* th, const std::string server ) + { + return new TLSDefault( th, server, TLSDefault::VerifyingServer ); + } + + ConnectionBase* ConnectionTLSServer::newInstance() const + { + ConnectionBase* newConn = 0; + if( m_connection ) + newConn = m_connection->newInstance(); + return new ConnectionTLSServer( m_handler, newConn, m_log ); + } + +} diff --git a/libs/libgloox/connectiontlsserver.h b/libs/libgloox/connectiontlsserver.h new file mode 100644 index 0000000..7fb5637 --- /dev/null +++ b/libs/libgloox/connectiontlsserver.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2009 by Jakob Schroeter + * This file is part of the gloox library. http://camaya.net/gloox + * + * This software is distributed under a license. The full license + * agreement can be found in the file LICENSE in this distribution. + * This software may not be copied, modified, sold or distributed + * other than expressed in the named license agreement. + * + * This software is distributed without any warranty. + */ + +#ifndef CONNECTIONTLSSERVER_H__ +#define CONNECTIONTLSSERVER_H__ + +#include "macros.h" +#include "logsink.h" +#include "connectionbase.h" +#include "connectiontls.h" +#include "tlsdefault.h" +#include "tlshandler.h" + +#include + +namespace gloox +{ + + class ConnectionDataHandler; + + /** + * @brief This is an implementation of the server-side of a TLS/SSL connection. + * + * You should not need to use this class directly. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API ConnectionTLSServer : public ConnectionTLS + { + public: + /** + * Constructs a new ConnectionTLSServer object. + * @param cdh The ConnectionDataHandler that will be notified of events from this connection + * @param conn A transport connection. It should be an established connection from + * a client that is about to perform a TLS handshake. + * ConnectionTLSServer will own the transport connection and delete it in its destructor. + * @param log The log target. Obtain it from ClientBase::logInstance(). + */ + ConnectionTLSServer( ConnectionDataHandler* cdh, ConnectionBase* conn, const LogSink& log ); + + /** + * Constructs a new ConnectionTLSServer object. + * @param conn A transport connection. It should be an established connection from + * a client that is about to perform a TLS handshake. + * ConnectionTLSServer will own the transport connection and delete it in its destructor. + * @param log The log target. Obtain it from ClientBase::logInstance(). + */ + ConnectionTLSServer( ConnectionBase* conn, const LogSink& log ); + + /** + * Virtual Destructor. + */ + virtual ~ConnectionTLSServer(); + + /** + * Returns a TLS server. + * @return A TLS server. + */ + virtual TLSBase* getTLSBase( TLSHandler* th, const std::string server ); + + // reimplemented from ConnectionTLS + virtual ConnectionBase* newInstance() const; + + private: + ConnectionTLSServer& operator=( const ConnectionTLSServer& ); + + }; + +} + +#endif // CONNECTIONTLSSERVER_H__ diff --git a/libs/libgloox/dataform.cpp b/libs/libgloox/dataform.cpp new file mode 100644 index 0000000..7cb6b12 --- /dev/null +++ b/libs/libgloox/dataform.cpp @@ -0,0 +1,137 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "dataform.h" +#include "dataformfield.h" +#include "dataformitem.h" +#include "dataformreported.h" +#include "util.h" +#include "tag.h" + +namespace gloox +{ + + DataForm::DataForm( FormType type, const StringList& instructions, const std::string& title ) + : StanzaExtension( ExtDataForm ), + m_type( type ), m_instructions( instructions ), m_title( title ), m_reported( 0 ) + { + } + + DataForm::DataForm( FormType type, const std::string& title ) + : StanzaExtension( ExtDataForm ), + m_type( type ), m_title( title ), m_reported( 0 ) + { + } + + DataForm::DataForm( const Tag* tag ) + : StanzaExtension( ExtDataForm ), + m_type( TypeInvalid ), m_reported( 0 ) + { + parse( tag ); + } + + DataForm::DataForm( const DataForm& form ) + : StanzaExtension( ExtDataForm ), DataFormFieldContainer( form ), + m_type( form.m_type ), m_instructions( form.m_instructions ), + m_title( form.m_title ), m_reported( form.m_reported ? new DataFormReported( form.m_reported->tag() ) : 0 ) + { + } + + DataForm::~DataForm() + { + util::clearList( m_items ); + delete m_reported; + m_reported = NULL; + } + + static const char* dfTypeValues[] = + { + "form", "submit", "cancel", "result" + }; + + bool DataForm::parse( const Tag* tag ) + { + if( !tag || tag->xmlns() != XMLNS_X_DATA || tag->name() != "x" ) + return false; + + const std::string& type = tag->findAttribute( TYPE ); + if( type.empty() ) + m_type = TypeForm; + else + { + m_type = (FormType)util::lookup( type, dfTypeValues ); + if( m_type == TypeInvalid ) + return false; + } + + const TagList& l = tag->children(); + TagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + { + if( (*it)->name() == "title" ) + m_title = (*it)->cdata(); + else if( (*it)->name() == "instructions" ) + m_instructions.push_back( (*it)->cdata() ); + else if( (*it)->name() == "field" ) + m_fields.push_back( new DataFormField( (*it) ) ); + else if( (*it)->name() == "reported" ) + { + if( m_reported == NULL ) + m_reported = new DataFormReported( (*it) ); + // else - Invalid data form - only one "reported" is allowed + } + else if( (*it)->name() == "item" ) + m_items.push_back( new DataFormItem( (*it) ) ); + } + + return true; + } + + const std::string& DataForm::filterString() const + { + static const std::string filter = "/message/x[@xmlns='" + XMLNS_X_DATA + "']"; + return filter; + } + + Tag* DataForm::tag() const + { + if( m_type == TypeInvalid ) + return 0; + + Tag* x = new Tag( "x" ); + x->setXmlns( XMLNS_X_DATA ); + x->addAttribute( TYPE, util::lookup( m_type, dfTypeValues ) ); + if( !m_title.empty() ) + new Tag( x, "title", m_title ); + + StringList::const_iterator it_i = m_instructions.begin(); + for( ; it_i != m_instructions.end(); ++it_i ) + new Tag( x, "instructions", (*it_i) ); + + FieldList::const_iterator it = m_fields.begin(); + for( ; it != m_fields.end(); ++it ) + x->addChild( (*it)->tag() ); + + if( m_reported != NULL ) + { + x->addChild( m_reported->tag() ); + } + + ItemList::const_iterator iti = m_items.begin(); + for( ; iti != m_items.end(); ++iti ) + x->addChild( (*iti)->tag() ); + + return x; + } + +} diff --git a/libs/libgloox/dataform.h b/libs/libgloox/dataform.h new file mode 100644 index 0000000..6fb75d5 --- /dev/null +++ b/libs/libgloox/dataform.h @@ -0,0 +1,197 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef DATAFORM_H__ +#define DATAFORM_H__ + +#include "dataformfieldcontainer.h" +#include "stanzaextension.h" + +#include +#include + +namespace gloox +{ + + class Tag; + class DataFormItem; + class DataFormReported; + + /** + * Describes the possible Form Types. + */ + enum FormType + { + TypeForm, /**< The forms-processing entity is asking the forms-submitting + * entity to complete a form. */ + TypeSubmit, /**< The forms-submitting entity is submitting data to the + * forms-processing entity. */ + TypeCancel, /**< The forms-submitting entity has cancelled submission of data + * to the forms-processing entity. */ + TypeResult, /**< The forms-processing entity is returning data (e.g., search + * results) to the forms-submitting entity, or the data is a + * generic data set. */ + TypeInvalid /**< The form is invalid. Only possible if the form was created + * from an Tag which doesn't correctly describe a Data Form. */ + }; + + /** + * @brief An abstraction of a XEP-0004 Data Form. + * + * + * + * @author Jakob Schroeter + * @since 0.7 + */ + class GLOOX_API DataForm : public StanzaExtension, public DataFormFieldContainer + { + public: + /** + * A list of DataFormItems. + */ + typedef std::list ItemList; + + /** + * Constructs a new, empty form. + * @param type The form type. + * @param instructions Natural-language instructions for filling out the form. Should not contain + * newlines (\\n, \\r). + * @param title The natural-language title of the form. Should not contain newlines (\\n, \\r). + */ + DataForm( FormType type, const StringList& instructions, const std::string& title = EmptyString ); + + /** + * Constructs a new, empty form without any instructions or title set. Probably best suited for + * result forms. + * @param type The form type. + * @param title The natural-language title of the form. Should not contain newlines (\\n, \\r). + * @since 0.9 + */ + DataForm( FormType type, const std::string& title = EmptyString ); + + /** + * Constructs a new DataForm from an existing Tag/XML representation. + * @param tag The existing form to parse. + */ + DataForm( const Tag* tag ); + + /** + * Creates a new DataForm, copying the given one. + * @param form The form to copy. + */ + DataForm( const DataForm& form ); + + /** + * Virtual destructor. + */ + virtual ~DataForm(); + + /** + * Use this function to retrieve the title of the form. + * @return The title of the form. + */ + const std::string& title() const { return m_title; } + + /** + * Use this function to set the title of the form. + * @param title The new title of the form. + * @note The title should not contain newlines (\\n, \\r). + */ + void setTitle( const std::string& title ) { m_title = title; } + + /** + * Retrieves the natural-language instructions for the form. + * @return The fill-in instructions for the form. + */ + const StringList& instructions() const { return m_instructions; } + + /** + * Use this function to set natural-language instructions for the form. + * @param instructions The instructions for the form. + * @note The instructions should not contain newlines (\\n, \\r). Instead, every line should be an + * element of the StringMap. This allows for platform dependent newline handling on the target + * platform. + */ + void setInstructions( const StringList& instructions ) { m_instructions = instructions; } + + /** + * Returns the reported field list in a DataForm. + * @return The reported section, containing 0..n fields. + */ + const DataFormReported* reported() const { return m_reported; } + + /** + * Returns a list of items in a DataForm. + * @return A list of items. + */ + const ItemList& items() const { return m_items; } + + /** + * Returns the form's type. + * @return The form's type. + * @since 0.9 + */ + FormType type() const { return m_type; } + + /** + * Sets the form's type. + * @param type The form's new type. + */ + void setType( FormType type ) { m_type = type; } + + /** + * Parses the given Tag and creates an appropriate DataForm representation. + * @param tag The Tag to parse. + * @return @b True on success, @b false otherwise. + * @since 0.9 + */ + bool parse( const Tag* tag ); + + /** + * Converts to @b true if the DataForm is valid, @b false otherwise. + */ + operator bool() const { return m_type != TypeInvalid; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new DataForm( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new DataForm( *this ); + } + + protected: + FormType m_type; + + private: + StringList m_instructions; + + std::string m_title; + DataFormReported* m_reported; + ItemList m_items; + + }; + +} + +#endif // DATAFORM_H__ diff --git a/libs/libgloox/dataformfield.cpp b/libs/libgloox/dataformfield.cpp new file mode 100644 index 0000000..462181c --- /dev/null +++ b/libs/libgloox/dataformfield.cpp @@ -0,0 +1,134 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#include "dataformfield.h" +#include "util.h" +#include "tag.h" + +namespace gloox +{ + + static const char* fieldTypeValues[] = + { + "boolean", "fixed", "hidden", "jid-multi", "jid-single", + "list-multi", "list-single", "text-multi", "text-private", "text-single", "" + }; + + DataFormField::DataFormField( FieldType type ) + : m_type( type ), m_required( false ) + { + } + + DataFormField::DataFormField( const std::string& name, const std::string& value, + const std::string& label, FieldType type ) + : m_type( type ), m_name( name ), m_label( label ), m_required( false ) + { + m_values.push_back( value ); + } + + DataFormField::DataFormField( const Tag* tag ) + : m_type( TypeInvalid ), m_required( false ) + { + if( !tag ) + return; + + const std::string& type = tag->findAttribute( TYPE ); + if( type.empty() ) + { + if( !tag->name().empty() ) + m_type = TypeNone; + } + else + m_type = (FieldType)util::lookup( type, fieldTypeValues ); + + if( tag->hasAttribute( "var" ) ) + m_name = tag->findAttribute( "var" ); + + if( tag->hasAttribute( "label" ) ) + m_label = tag->findAttribute( "label" ); + + const TagList& l = tag->children(); + TagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + { + if( (*it)->name() == "desc" ) + m_desc = (*it)->cdata(); + else if( (*it)->name() == "required" ) + m_required = true; + else if( (*it)->name() == "value" ) + { + if( m_type == TypeTextMulti || m_type == TypeListMulti || m_type == TypeJidMulti ) + addValue( (*it)->cdata() ); + else + setValue( (*it)->cdata() ); + } + else if( (*it)->name() == "option" ) + { + Tag* v = (*it)->findChild( "value" ); + if( v ) + m_options.insert( std::make_pair( (*it)->findAttribute( "label" ), v->cdata() ) ); + } + } + + } + + DataFormField::~DataFormField() + { + } + + Tag* DataFormField::tag() const + { + if( m_type == TypeInvalid ) + return 0; + + Tag* field = new Tag( "field" ); + field->addAttribute( TYPE, util::lookup( m_type, fieldTypeValues ) ); + field->addAttribute( "var", m_name ); + field->addAttribute( "label", m_label ); + if( m_required ) + new Tag( field, "required" ); + + if( !m_desc.empty() ) + new Tag( field, "desc", m_desc ); + + if( m_type == TypeListSingle || m_type == TypeListMulti ) + { + StringMultiMap::const_iterator it = m_options.begin(); + for( ; it != m_options.end(); ++it ) + { + Tag* option = new Tag( field, "option", "label", (*it).first ); + new Tag( option, "value", (*it).second ); + } + } + else if( m_type == TypeBoolean ) + { + if( m_values.size() == 0 || m_values.front() == "false" || m_values.front() == "0" ) + new Tag( field, "value", "0" ); + else + new Tag( field, "value", "1" ); + } + + if( m_type == TypeTextMulti || m_type == TypeListMulti || m_type == TypeJidMulti ) + { + StringList::const_iterator it = m_values.begin(); + for( ; it != m_values.end() ; ++it ) + new Tag( field, "value", (*it) ); + } + + if( m_values.size() && !( m_type == TypeTextMulti || m_type == TypeListMulti + || m_type == TypeBoolean || m_type == TypeJidMulti ) ) + new Tag( field, "value", m_values.front() ); + + return field; + } + +} diff --git a/libs/libgloox/dataformfield.h b/libs/libgloox/dataformfield.h new file mode 100644 index 0000000..c3e21c8 --- /dev/null +++ b/libs/libgloox/dataformfield.h @@ -0,0 +1,242 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef DATAFORMFIELD_H__ +#define DATAFORMFIELD_H__ + +#include "gloox.h" + +#include +#include + +namespace gloox +{ + + class Tag; + + /** + * @brief An abstraction of a single field in a XEP-0004 Data Form. + * + * @author Jakob Schroeter + * @since 0.7 + */ + class GLOOX_API DataFormField + { + + public: + /** + * Describes the possible types of a Data Form Field. + */ + enum FieldType + { + TypeBoolean, /**< The field enables an entity to gather or provide an either-or + * choice between two options. The default value is "false". */ + TypeFixed, /**< The field is intended for data description (e.g., + * human-readable text such as "section" headers) rather than data + * gathering or provision. The <value/> child SHOULD NOT contain + * newlines (the \\n and \\r characters); instead an application SHOULD + * generate multiple fixed fields, each with one <value/> child. */ + TypeHidden, /**< The field is not shown to the entity providing information, but + * instead is returned with the form. */ + TypeJidMulti, /**< The field enables an entity to gather or provide multiple Jabber + * IDs.*/ + TypeJidSingle, /**< The field enables an entity to gather or provide a single Jabber + * ID.*/ + TypeListMulti, /**< The field enables an entity to gather or provide one or more options + * from among many. */ + TypeListSingle, /**< The field enables an entity to gather or provide one option from + * among many. */ + TypeTextMulti, /**< The field enables an entity to gather or provide multiple lines of + * text. */ + TypeTextPrivate, /**< The field enables an entity to gather or provide a single line or + * word of text, which shall be obscured in an interface + * (e.g., *****). */ + TypeTextSingle, /**< The field enables an entity to gather or provide a single line or + * word of text, which may be shown in an interface. This field type is + * the default and MUST be assumed if an entity receives a field type it + * does not understand.*/ + TypeNone, /**< The field is child of either a <reported> or <item> + * element or has no type attribute. */ + TypeInvalid /**< The field is invalid. Only possible if the field was created from + * a Tag not correctly describing a Data Form Field. */ + }; + + public: + + /** + * Constructs a new DataForm field. + * @param type The type of the field. Default: text-single. + */ + DataFormField( FieldType type = TypeTextSingle ); + + /** + * Constructs a new DataForm field and fills it with the given values. + * @param name The field's name (the value of the 'var' attribute). + * @param value The field's value. + * @param label The field's label. + * @param type The field's type. + * @since 0.9 + */ + DataFormField( const std::string& name, const std::string& value = EmptyString, + const std::string& label = EmptyString, FieldType type = TypeTextSingle ); + + /** + * Constructs a new Data Form Field from an existing tag that describes a field. + * @param tag The tag to parse. + */ + DataFormField( const Tag* tag ); + + /** + * Virtual destructor. + */ + virtual ~DataFormField(); + + /** + * Use this function to retrieve the optional values of a field. + * @return The options of a field. + */ + const StringMultiMap& options() const { return m_options; } + + /** + * Use this function to create a Tag representation of the form field. This is usually called by + * DataForm. + * @return A Tag hierarchically describing the form field, or NULL if the field is invalid (i.e. + * created from a Tag not correctly describing a Data Form Field). + */ + virtual Tag* tag() const; + + /** + * Use this function to retrieve the name of the field (the content of the 'var' attribute). + * @return The name of the field. + */ + const std::string& name() const { return m_name; } + + /** + * Sets the name (the content of the 'var' attribute) of the field. The name identifies the + * field uniquely in the form. + * @param name The new name of the field. + * @note Fields of type other than 'fixed' MUST have a name, if it is 'fixed', it MAY. + */ + void setName( const std::string& name ) { m_name = name; } + + /** + * Use this function to set the optional values of the field. The key of the map + * will be used as the label of the option, while the value will be used as ... the + * value. ;) + * @param options The optional values of a list* or *multi type of field. + */ + void setOptions( const StringMultiMap& options ) { m_options = options; } + + /** + * Adds a single option to the list of options. + * @param label The label of the option. + * @param value The value of the option. + * @since 0.9.4 + */ + void addOption( const std::string& label, const std::string& value ) + { m_options.insert( std::make_pair( label, value ) ); } + + /** + * Use this function to determine whether or not this field is required. + * @return Whether or not this field is required. + */ + bool required() const { return m_required; } + + /** + * Use this field to set this field to be required. + * @param required Whether or not this field is required. + */ + void setRequired( bool required ) { m_required = required; } + + /** + * Use this function to retrieve the describing label of this field. + * @return The describing label of this field. + */ + const std::string& label() const { return m_label; } + + /** + * Use this function to set the describing label of this field. + * @param label The describing label of this field. + */ + void setLabel( const std::string& label ) { m_label = label; } + + /** + * Use this function to retrieve the description of this field. + * @return The description of this field + */ + const std::string& description() const { return m_desc; } + + /** + * Use this function to set the description of this field. + * @param desc The description of this field. + */ + void setDescription( const std::string& desc ) { m_desc = desc; } + + /** + * Use this function to retrieve the value of this field. + * @return The value of this field. + */ + const std::string& value() const { return ( m_values.size() > 0 ) ? m_values.front() : EmptyString; } + + /** + * Use this function to set the value of this field. + * @param value The new value of this field. + */ + void setValue( const std::string& value ) { m_values.clear(); addValue( value ); } + + /** + * Use this function to retrieve the values of this field, if its of type 'text-multi'. + * @return The value of this field. + */ + const StringList& values() const { return m_values; } + + /** + * Use this function to set multiple values of this field, if it is of type 'text-multi'. If its not, + * use @ref setValue() instead. + * @param values The new values of this field. + */ + void setValues( const StringList& values ) { m_values = values; } + + /** + * Adds a single value to the list of values. + * @param value The value to add. + */ + void addValue( const std::string& value ) { m_values.push_back( value ); } + + /** + * Use this function to retrieve the type of this field. + * @return The type of this field. + */ + FieldType type() const { return m_type; } + + /** + * Converts to @b true if the FormBase is valid, @b false otherwise. + */ + operator bool() const { return m_type != TypeInvalid; } + + private: + FieldType m_type; + + StringMultiMap m_options; + StringList m_values; + + std::string m_name; + std::string m_desc; + std::string m_label; + + bool m_required; + }; + +} + +#endif // DATAFORMFIELD_H__ diff --git a/libs/libgloox/dataformfieldcontainer.cpp b/libs/libgloox/dataformfieldcontainer.cpp new file mode 100644 index 0000000..dc639bc --- /dev/null +++ b/libs/libgloox/dataformfieldcontainer.cpp @@ -0,0 +1,47 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "dataformfieldcontainer.h" +#include "util.h" + + +namespace gloox +{ + + DataFormFieldContainer::DataFormFieldContainer() + { + } + + DataFormFieldContainer::DataFormFieldContainer( const DataFormFieldContainer& dffc ) + { + FieldList::const_iterator it = dffc.m_fields.begin(); + for( ; it != dffc.m_fields.end(); ++it ) + { + m_fields.push_back( new DataFormField( *(*it) ) ); + } + } + + DataFormFieldContainer::~DataFormFieldContainer() + { + util::clearList( m_fields ); + } + + DataFormField* DataFormFieldContainer::field( const std::string& field ) const + { + FieldList::const_iterator it = m_fields.begin(); + for( ; it != m_fields.end() && (*it)->name() != field; ++it ) + ; + return it != m_fields.end() ? (*it) : 0; + } + +} diff --git a/libs/libgloox/dataformfieldcontainer.h b/libs/libgloox/dataformfieldcontainer.h new file mode 100644 index 0000000..fbd2378 --- /dev/null +++ b/libs/libgloox/dataformfieldcontainer.h @@ -0,0 +1,125 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef DATAFORMFIELDCONTAINER_H__ +#define DATAFORMFIELDCONTAINER_H__ + +#include "dataformfield.h" + +#include +#include + +namespace gloox +{ + + class DataFormField; + + /** + * @brief An abstract base class for a XEP-0004 Data Form. + * + * You shouldn't need to use this class directly. Use DataForm instead. + * + * @author Jakob Schroeter + * @since 0.7 + */ + class GLOOX_API DataFormFieldContainer + { + public: + /** + * Creates a new FieldContainer. + */ + DataFormFieldContainer(); + + /** + * Creates a new FieldContainer, copying all fields from the given FieldContainer. + * @param dffc The FieldContainer to copy. + */ + DataFormFieldContainer( const DataFormFieldContainer& dffc ); + + /** + * Virtual destructor. + */ + virtual ~DataFormFieldContainer(); + + /** + * A list of XEP-0004 Data Form Fields. + */ + typedef std::list FieldList; + + /** + * Use this function to check whether this form contains a field with the given name. + * @param field The name of the field (the content of the 'var' attribute). + * @return Whether or not the form contains the named field. + */ + bool hasField( const std::string& field ) const + { return DataFormFieldContainer::field( field ) != 0; } + + /** + * Use this function to fetch a pointer to a field of the form. If no such field exists, + * 0 is returned. + * @param field The name of the field (the content of the 'var' attribute). + * @return A copy of the field with the given name if it exists, 0 otherwise. + */ + DataFormField* field( const std::string& field ) const; + + /** + * Use this function to retrieve the list of fields of a form. + * @return The list of fields the form contains. + */ + FieldList& fields() { return m_fields; } + + /** + * Use this function to retrieve the const list of fields of a form. + * @return The const list of fields the form contains. + */ + const FieldList& fields() const { return m_fields; } + + /** + * Use this function to set the fields the form contains. + * @param fields The list of fields. + * @note Any previously set fields will be deleted. Always set all fields, not a delta. + */ + virtual void setFields( FieldList& fields ) { m_fields = fields; } + + /** + * Use this function to add a single field to the list of existing fields. + * @param field The field to add. + * @since 0.9 + */ + virtual void addField( DataFormField* field ) { m_fields.push_back( field ); } + + /** + * Adds a single new Field and returns a pointer to that field. + * @param type The field's type. + * @param name The field's name (the value of the 'var' attribute). + * @param value The field's value. + * @param label The field's label. + * @since 0.9.4 + */ + DataFormField* addField( DataFormField::FieldType type, const std::string& name, + const std::string& value = EmptyString, + const std::string& label = EmptyString ) + { + DataFormField* field = new DataFormField( name, value, label, type ); + m_fields.push_back( field ); + return field; + } + + protected: + FieldList m_fields; + + }; + +} + +#endif // DATAFORMFIELDCONTAINER_H__ diff --git a/libs/libgloox/dataformitem.cpp b/libs/libgloox/dataformitem.cpp new file mode 100644 index 0000000..af8f6ca --- /dev/null +++ b/libs/libgloox/dataformitem.cpp @@ -0,0 +1,54 @@ + /* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "dataformitem.h" + +#include "tag.h" + +namespace gloox +{ + + DataFormItem::DataFormItem() + { + } + + DataFormItem::DataFormItem( const Tag* tag ) + { + if( tag->name() != "item" ) + return; + + const TagList &l = tag->children(); + TagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + { + DataFormField* f = new DataFormField( (*it) ); + m_fields.push_back( f ); + } + } + + DataFormItem::~DataFormItem() + { + } + + Tag* DataFormItem::tag() const + { + Tag* i = new Tag ( "item" ); + DataFormFieldContainer::FieldList::const_iterator it = m_fields.begin(); + for( ; it != m_fields.end(); ++it ) + { + i->addChild( (*it)->tag() ); + } + return i; + } + +} diff --git a/libs/libgloox/dataformitem.h b/libs/libgloox/dataformitem.h new file mode 100644 index 0000000..2f95dde --- /dev/null +++ b/libs/libgloox/dataformitem.h @@ -0,0 +1,62 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef DATAFORMITEM_H__ +#define DATAFORMITEM_H__ + +#include "dataformfieldcontainer.h" + +namespace gloox +{ + + /** + * @brief An abstraction of an <item> element in a XEP-0004 Data Form of type result. + * + * There are some constraints regarding usage of this element you should be aware of. Check XEP-0004 + * section 3.4. This class does not enforce correct usage at this point. + * + * @author Jakob Schroeter + * @since 0.7 + */ + class GLOOX_API DataFormItem : public DataFormFieldContainer + { + public: + /** + * Creates an empty 'item' element you can add fields to. + */ + DataFormItem(); + + /** + * Creates a 'item' element and fills it with the 'field' elements contained in the given Tag. + * The Tag's root element must be a 'item' element. Its child element should be 'field' elements. + * @param tag The tag to read the 'field' elements from. + * @since 0.8.5 + */ + DataFormItem( const Tag* tag ); + + /** + * Virtual destructor. + */ + virtual ~DataFormItem(); + + /** + * Creates and returns a Tag representation of the current object. + * @return A Tag representation of the current object. + */ + virtual Tag* tag() const; + + }; + +} + +#endif // DATAFORMITEM_H__ diff --git a/libs/libgloox/dataformreported.cpp b/libs/libgloox/dataformreported.cpp new file mode 100644 index 0000000..4a7264f --- /dev/null +++ b/libs/libgloox/dataformreported.cpp @@ -0,0 +1,54 @@ + /* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "dataformreported.h" + +#include "tag.h" + +namespace gloox +{ + + DataFormReported::DataFormReported() + { + } + + DataFormReported::DataFormReported( Tag* tag ) + { + if( tag->name() != "reported" ) + return; + + const TagList &l = tag->children(); + TagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + { + DataFormField* f = new DataFormField( (*it) ); + m_fields.push_back( f ); + } + } + + DataFormReported::~DataFormReported() + { + } + + Tag* DataFormReported::tag() const + { + Tag* r = new Tag ( "reported" ); + DataFormFieldContainer::FieldList::const_iterator it = m_fields.begin(); + for( ; it != m_fields.end(); ++it ) + { + r->addChild( (*it)->tag() ); + } + return r; + } + +} diff --git a/libs/libgloox/dataformreported.h b/libs/libgloox/dataformreported.h new file mode 100644 index 0000000..91294c6 --- /dev/null +++ b/libs/libgloox/dataformreported.h @@ -0,0 +1,64 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef DATAFORMREPORTED_H__ +#define DATAFORMREPORTED_H__ + +#include "dataformfieldcontainer.h" + +namespace gloox +{ + + class Tag; + + /** + * @brief An abstraction of a <reported> element in a XEP-0004 Data Form of type result. + * + * There are some constraints regarding usage of this element you should be aware of. Check XEP-0004 + * section 3.4. This class does not enforce correct usage at this point. + * + * @author Jakob Schroeter + * @since 0.7 + */ + class GLOOX_API DataFormReported : public DataFormFieldContainer + { + public: + /** + * Creates an empty 'reported' element you can add fields to. + */ + DataFormReported(); + + /** + * Creates a 'reported' element and fills it with the 'field' elements contained in the given Tag. + * The Tag's root element must be a 'reported' element. Its child element should be 'field' elements. + * @param tag The tag to read the 'field' elements from. + * @since 0.8.5 + */ + DataFormReported( Tag* tag ); + + /** + * Virtual destructor. + */ + virtual ~DataFormReported(); + + /** + * Creates and returns a Tag representation of the current object. + * @return A Tag representation of the current object. + */ + virtual Tag* tag() const; + + }; + +} + +#endif // DATAFORMREPORTED_H__ diff --git a/libs/libgloox/delayeddelivery.cpp b/libs/libgloox/delayeddelivery.cpp new file mode 100644 index 0000000..71038fb --- /dev/null +++ b/libs/libgloox/delayeddelivery.cpp @@ -0,0 +1,74 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "delayeddelivery.h" + +#include "tag.h" + +namespace gloox +{ + + DelayedDelivery::DelayedDelivery( const JID& from, const std::string stamp, const std::string& reason ) + : StanzaExtension( ExtDelay ), m_from( from ), m_stamp( stamp ), m_reason( reason ), m_valid( false ) + { + if( !m_stamp.empty() ) + m_valid = true; + } + + + DelayedDelivery::DelayedDelivery( const Tag* tag ) + : StanzaExtension( ExtDelay ), m_valid( false ) + { + if( !tag || !tag->hasAttribute( "stamp" ) ) + return; + if( !( tag->name() == "x" && tag->hasAttribute( XMLNS, XMLNS_X_DELAY ) ) ) + if( !( tag->name() == "delay" && tag->hasAttribute( XMLNS, XMLNS_DELAY ) ) ) + return; + + m_reason = tag->cdata(); + m_stamp = tag->findAttribute( "stamp" ); + m_from = tag->findAttribute( "from" ); + m_valid = true; + } + + DelayedDelivery::~DelayedDelivery() + { + } + + const std::string& DelayedDelivery::filterString() const + { + static const std::string filter = + "/presence/delay[@xmlns='" + XMLNS_DELAY + "']" + "|/message/delay[@xmlns='" + XMLNS_DELAY + "']" + "|/presence/x[@xmlns='" + XMLNS_X_DELAY + "']" + "|/message/x[@xmlns='" + XMLNS_X_DELAY + "']"; + return filter; + } + + Tag* DelayedDelivery::tag() const + { + if( !m_valid ) + return 0; + + Tag* t = new Tag( "delay" ); + t->addAttribute( XMLNS, XMLNS_DELAY ); + if( m_from ) + t->addAttribute( "from", m_from.full() ); + if( !m_stamp.empty() ) + t->addAttribute( "stamp", m_stamp ); + if( !m_reason.empty() ) + t->setCData( m_reason ); + return t; + } + +} diff --git a/libs/libgloox/delayeddelivery.h b/libs/libgloox/delayeddelivery.h new file mode 100644 index 0000000..f9953fa --- /dev/null +++ b/libs/libgloox/delayeddelivery.h @@ -0,0 +1,130 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef DELAYEDDELIVERY_H__ +#define DELAYEDDELIVERY_H__ + +#include "gloox.h" +#include "jid.h" +#include "stanzaextension.h" + +#include + +namespace gloox +{ + + class Tag; + + /** + * @brief This is an implementation of XEP-0203 (Delayed Delivery). + * + * The class also implements the deprecated XEP-0091 (Delayed Delivery) in a read-only fashion. + * It understands both XEP formats for input, but any output will conform to XEP-0203. + * + * XEP Version: 0.1 + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API DelayedDelivery : public StanzaExtension + { + + public: + /** + * Constructs a new object and fills it according to the parameters. + * @param from The JID of the original sender or the entity that delayed the sending. + * @param stamp The datetime stamp of the original send. + * @param reason An optional natural language reason for the delay. + */ + DelayedDelivery( const JID& from, const std::string stamp, + const std::string& reason = "" ); + + /** + * Constructs a new object from the given Tag. + * @param tag The Tag to parse. + */ + DelayedDelivery( const Tag* tag = 0 ); + + /** + * Virtual Destructor. + */ + virtual ~DelayedDelivery(); + + /** + * Returns the datetime when the stanza was originally sent. + * The format MUST adhere to the dateTime format specified in XEP-0082 and MUST + * be expressed in UTC. + * @return The original datetime. + */ + const std::string& stamp() const { return m_stamp; } + + /** + * Sets the original datetime. + * @param stamp The original datetime. + */ + void setStamp( const std::string& stamp ) { m_stamp = stamp; } + + /** + * Returns the JID of the original sender of the stanza or the entity that + * delayed the sending. + * The format MUST adhere to the dateTime format specified in XEP-0082 and MUST + * be expressed in UTC. + * @return The JID. + */ + const JID& from() const { return m_from; } + + /** + * Sets the JID of the origianl sender or the entity that delayed the sending. + * @param from The JID. + */ + void setFrom( const JID& from ) { m_from = from; } + + /** + * Returns a natural language reason for the delay. + * @return A natural language reason for the delay. + */ + const std::string& reason() const { return m_reason; } + + /** + * Sets the reason for the delay. + * @param reason The reason for the delay. + */ + void setReason( const std::string& reason ) { m_reason = reason; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new DelayedDelivery( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new DelayedDelivery( *this ); + } + + private: + JID m_from; + std::string m_stamp; + std::string m_reason; + bool m_valid; + }; + +} + +#endif // DELAYEDDELIVERY_H__ diff --git a/libs/libgloox/disco.cpp b/libs/libgloox/disco.cpp new file mode 100644 index 0000000..bfd0cf5 --- /dev/null +++ b/libs/libgloox/disco.cpp @@ -0,0 +1,535 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "disco.h" +#include "discohandler.h" +#include "dataform.h" +#include "error.h" +#include "clientbase.h" +#include "disconodehandler.h" +#include "softwareversion.h" +#include "util.h" + + +namespace gloox +{ + + // ---- Disco::Identity ---- + Disco::Identity::Identity( const std::string& category, + const std::string& type, + const std::string& name ) + : m_category( category ), m_type( type ), m_name( name ) + { + } + + Disco::Identity::Identity( const Tag* tag ) + { + if( !tag || tag->name() != "identity" ) + return; + + m_category = tag->findAttribute( "category" ); + m_type = tag->findAttribute( "type" ); + m_name = tag->findAttribute( "name" ); + } + + Disco::Identity::Identity( const Identity& id ) + : m_category( id.m_category ), m_type( id.m_type ), m_name( id.m_name ) + { + } + + Disco::Identity::~Identity() + { + } + + Tag* Disco::Identity::tag() const + { + if( m_category.empty() || m_type.empty() ) + return 0; + + Tag* i = new Tag( "identity" ); + i->addAttribute( "category", m_category ); + i->addAttribute( "type", m_type ); + + if( !m_name.empty() ) + i->addAttribute( "name", m_name ); + + return i; + } + // ---- ~Disco::Identity ---- + + // ---- Disco::Info ---- + Disco::Info::Info( const std::string& node, bool defaultFeatures ) + : StanzaExtension( ExtDiscoInfo ), m_node( node ), m_form( 0 ) + { + if( defaultFeatures ) + { + m_features.push_back( XMLNS_DISCO_INFO ); + m_features.push_back( XMLNS_DISCO_ITEMS ); + } + } + + Disco::Info::Info( const Tag* tag ) + : StanzaExtension( ExtDiscoInfo ), m_form( 0 ) + { + if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_DISCO_INFO ) + return; + + m_node = tag->findAttribute( "node" ); + + const TagList& l = tag->children(); + TagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + { + const std::string& name = (*it)->name(); + if( name == "identity" ) + m_identities.push_back( new Identity( (*it) ) ); + else if( name == "feature" && (*it)->hasAttribute( "var" ) ) + m_features.push_back( (*it)->findAttribute( "var" ) ); + else if( !m_form && name == "x" && (*it)->xmlns() == XMLNS_X_DATA ) + m_form = new DataForm( (*it) ); + } + } + + Disco::Info::Info( const Info& info ) + : StanzaExtension( ExtDiscoInfo ), m_node( info.m_node ), m_features( info.m_features ), + m_identities( info.m_identities ), m_form( info.m_form ? new DataForm( *(info.m_form) ) : 0 ) + { + } + + Disco::Info::~Info() + { + delete m_form; + util::clearList( m_identities ); + } + + void Disco::Info::setForm( DataForm* form ) + { + delete m_form; + m_form = form; + } + + bool Disco::Info::hasFeature( const std::string& feature ) const + { + StringList::const_iterator it = m_features.begin(); + for( ; it != m_features.end() && (*it) != feature; ++it ) + ; + return it != m_features.end(); + } + + const std::string& Disco::Info::filterString() const + { + static const std::string filter = "/iq/query[@xmlns='" + XMLNS_DISCO_INFO + "']"; + return filter; + } + + Tag* Disco::Info::tag() const + { + Tag* t = new Tag( "query", XMLNS, XMLNS_DISCO_INFO ); + + if( !m_node.empty() ) + t->addAttribute( "node", m_node ); + + IdentityList::const_iterator it_i = m_identities.begin(); + for( ; it_i != m_identities.end(); ++it_i ) + t->addChild( (*it_i)->tag() ); + + StringList::const_iterator it_f = m_features.begin(); + for( ; it_f != m_features.end(); ++it_f ) + new Tag( t, "feature", "var", (*it_f) ); + + if( m_form ) + t->addChild( m_form->tag() ); + + return t; + } + // ---- ~Disco::Info ---- + + // ---- Disco::Item ---- + Disco::Item::Item( const Tag* tag ) + { + if( !tag || tag->name() != "item" ) + return; + + m_jid = tag->findAttribute( "jid" ); + m_node = tag->findAttribute( "node" ); + m_name = tag->findAttribute( "name" ); + } + + Tag* Disco::Item::tag() const + { + if( !m_jid ) + return 0; + + Tag* i = new Tag( "item" ); + i->addAttribute( "jid", m_jid.full() ); + + if( !m_node.empty() ) + i->addAttribute( "node", m_node ); + if( !m_name.empty() ) + i->addAttribute( "name", m_name ); + + return i; + } + // ---- ~Disco::Item ---- + + // ---- Disco::Items ---- + Disco::Items::Items( const std::string& node ) + : StanzaExtension( ExtDiscoItems ), m_node( node ) + { + } + + Disco::Items::Items( const Tag* tag ) + : StanzaExtension( ExtDiscoItems ) + { + if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_DISCO_ITEMS ) + return; + + m_node = tag->findAttribute( "node" ); + + const TagList& l = tag->children(); + TagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + { + const std::string& name = (*it)->name(); + if( name == "item" ) + m_items.push_back( new Item( (*it) ) ); + } + } + + Disco::Items::~Items() + { + util::clearList( m_items ); + } + + void Disco::Items::setItems( const ItemList& items ) + { + util::clearList( m_items ); + m_items = items; + } + + + const std::string& Disco::Items::filterString() const + { + static const std::string filter = "/iq/query[@xmlns='" + XMLNS_DISCO_ITEMS + "']"; + return filter; + } + + Tag* Disco::Items::tag() const + { + Tag* t = new Tag( "query", XMLNS, XMLNS_DISCO_ITEMS ); + + if( !m_node.empty() ) + t->addAttribute( "node", m_node ); + + ItemList::const_iterator it_i = m_items.begin(); + for( ; it_i != m_items.end(); ++it_i ) + t->addChild( (*it_i)->tag() ); + + return t; + } + // ---- ~Disco::Items ---- + + // ---- Disco ---- + Disco::Disco( ClientBase* parent ) + : m_parent( parent ), m_form( 0 ) + { + addFeature( XMLNS_VERSION ); +// addFeature( XMLNS_DISCO_INFO ); //handled by Disco::Info now +// addFeature( XMLNS_DISCO_ITEMS ); //handled by Disco::Info now + if( m_parent ) + { + m_parent->registerIqHandler( this, ExtDiscoInfo ); + m_parent->registerIqHandler( this, ExtDiscoItems ); + m_parent->registerIqHandler( this, ExtVersion ); + m_parent->registerStanzaExtension( new Disco::Info() ); + m_parent->registerStanzaExtension( new Disco::Items() ); + m_parent->registerStanzaExtension( new SoftwareVersion() ); + } + } + + Disco::~Disco() + { + util::clearList( m_identities ); + delete m_form; + + if( m_parent ) + { + m_parent->removeIqHandler( this, ExtDiscoInfo ); + m_parent->removeIqHandler( this, ExtDiscoItems ); + m_parent->removeIqHandler( this, ExtVersion ); + m_parent->removeStanzaExtension( ExtDiscoInfo ); + m_parent->removeStanzaExtension( ExtDiscoItems ); + m_parent->removeStanzaExtension( ExtVersion ); + m_parent->removeIDHandler( this ); + } + } + + void Disco::setForm( DataForm* form ) + { + delete m_form; + m_form = form; + } + + bool Disco::handleIq( const IQ& iq ) + { + switch( iq.subtype() ) + { + case IQ::Get: + { + IQ re( IQ::Result, iq.from(), iq.id() ); + re.setFrom( iq.to() ); + + const SoftwareVersion* sv = iq.findExtension( ExtVersion ); + if( sv ) + { + re.addExtension( new SoftwareVersion( m_versionName, m_versionVersion, m_versionOs ) ); + m_parent->send( re ); + return true; + } + + const Info *info = iq.findExtension( ExtDiscoInfo ); + if( info ) + { + Info *i = new Info( EmptyString, true ); + if( !info->node().empty() ) + { + i->setNode( info->node() ); + IdentityList identities; + StringList features; + DiscoNodeHandlerMap::const_iterator it = m_nodeHandlers.find( info->node() ); + if( it == m_nodeHandlers.end() ) + { + delete i; + IQ re( IQ::Error, iq.from(), iq.id() ); + re.addExtension( new Error( StanzaErrorTypeCancel, StanzaErrorItemNotFound ) ); + m_parent->send( re ); + return true; + } + else + { + DiscoNodeHandlerList::const_iterator in = (*it).second.begin(); + for( ; in != (*it).second.end(); ++in ) + { + IdentityList il = (*in)->handleDiscoNodeIdentities( iq.from(), info->node() ); + il.sort(); // needed on win32 + identities.merge( il ); + StringList fl = (*in)->handleDiscoNodeFeatures( iq.from(), info->node() ); + fl.sort(); // needed on win32 + features.merge( fl ); + } + } + i->setIdentities( identities ); + i->setFeatures( features ); + } + else + { + IdentityList il; + IdentityList::const_iterator it = m_identities.begin(); + for( ; it != m_identities.end(); ++it ) + { + il.push_back( new Identity( *(*it) ) ); + } + i->setIdentities( il ); + i->setFeatures( m_features ); + if( m_form ) + i->setForm( new DataForm( *m_form ) ); + } + + re.addExtension( i ); + m_parent->send( re ); + return true; + } + + const Items *items = iq.findExtension( ExtDiscoItems ); + if( items ) + { + Items *i = new Items( items->node() ); + if( !items->node().empty() ) + { + DiscoNodeHandlerMap::const_iterator it = m_nodeHandlers.find( items->node() ); + if( it == m_nodeHandlers.end() ) + { + delete i; + IQ re( IQ::Error, iq.from(), iq.id() ); + re.addExtension( new Error( StanzaErrorTypeCancel, StanzaErrorItemNotFound ) ); + m_parent->send( re ); + return true; + } + else + { + ItemList itemlist; + DiscoNodeHandlerList::const_iterator in = (*it).second.begin(); + for( ; in != (*it).second.end(); ++in ) + { + ItemList il = (*in)->handleDiscoNodeItems( iq.from(), iq.to(), items->node() ); + il.sort(); // needed on win32 + itemlist.merge( il ); + } + i->setItems( itemlist ); + } + } + + re.addExtension( i ); + m_parent->send( re ); + return true; + } + break; + } + + case IQ::Set: + { + bool res = false; + DiscoHandlerList::const_iterator it = m_discoHandlers.begin(); + for( ; it != m_discoHandlers.end(); ++it ) + { + if( (*it)->handleDiscoSet( iq ) ) + res = true; + } + return res; + break; + } + + default: + break; + } + return false; + } + + void Disco::handleIqID( const IQ& iq, int context ) + { + DiscoHandlerMap::iterator it = m_track.find( iq.id() ); + if( it != m_track.end() && (*it).second.dh ) + { + switch( iq.subtype() ) + { + case IQ::Result: + switch( context ) + { + case GetDiscoInfo: + { + const Info* di = iq.findExtension( ExtDiscoInfo ); + if( di ) + (*it).second.dh->handleDiscoInfo( iq.from(), *di, (*it).second.context ); + break; + } + case GetDiscoItems: + { + const Items* di = iq.findExtension( ExtDiscoItems ); + if( di ) + (*it).second.dh->handleDiscoItems( iq.from(), *di, (*it).second.context ); + break; + } + } + break; + + case IQ::Error: + { + (*it).second.dh->handleDiscoError( iq.from(), iq.error(), (*it).second.context ); + break; + } + + default: + break; + } + + m_track.erase( it ); + } + } + + void Disco::getDisco( const JID& to, const std::string& node, DiscoHandler* dh, int context, + IdType idType, const std::string& tid ) + { + const std::string& id = tid.empty() ? m_parent->getID() : tid; + + IQ iq( IQ::Get, to, id ); + if( idType == GetDiscoInfo ) + iq.addExtension( new Info( node ) ); + else + iq.addExtension( new Items( node ) ); + + DiscoHandlerContext ct; + ct.dh = dh; + ct.context = context; + m_track[id] = ct; + m_parent->send( iq, this, idType ); + } + + void Disco::setVersion( const std::string& name, const std::string& version, const std::string& os ) + { + m_versionName = name; + m_versionVersion = version; + m_versionOs = os; + } + + void Disco::setIdentity( const std::string& category, const std::string& type, + const std::string& name ) + { + util::clearList( m_identities ); + addIdentity( category, type, name ); + } + + void Disco::removeDiscoHandler( DiscoHandler* dh ) + { + m_discoHandlers.remove( dh ); + DiscoHandlerMap::iterator t; + DiscoHandlerMap::iterator it = m_track.begin(); + while( it != m_track.end() ) + { + t = it; + ++it; + if( dh == (*t).second.dh ) + { + m_track.erase( t ); + } + } + } + + void Disco::registerNodeHandler( DiscoNodeHandler* nh, const std::string& node ) + { + m_nodeHandlers[node].push_back( nh ); + } + + void Disco::removeNodeHandler( DiscoNodeHandler* nh, const std::string& node ) + { + DiscoNodeHandlerMap::iterator it = m_nodeHandlers.find( node ); + if( it != m_nodeHandlers.end() ) + { + (*it).second.remove( nh ); + if( (*it).second.empty() ) + m_nodeHandlers.erase( it ); + } + } + + void Disco::removeNodeHandlers( DiscoNodeHandler* nh ) + { + DiscoNodeHandlerMap::iterator it = m_nodeHandlers.begin(); + DiscoNodeHandlerMap::iterator it2; + while( it != m_nodeHandlers.end() ) + { + it2 = it++; + removeNodeHandler( nh, (*it2).first ); + } + } + + const StringList Disco::features( bool defaultFeatures ) const + { + StringList f = m_features; + if( defaultFeatures ) + { + f.push_back( XMLNS_DISCO_INFO ); + f.push_back( XMLNS_DISCO_ITEMS ); + } + return f; + } + +} diff --git a/libs/libgloox/disco.h b/libs/libgloox/disco.h new file mode 100644 index 0000000..fa180a6 --- /dev/null +++ b/libs/libgloox/disco.h @@ -0,0 +1,636 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef DISCO_H__ +#define DISCO_H__ + +#include "gloox.h" + +#include "iqhandler.h" +#include "jid.h" + +#include +#include +#include + +namespace gloox +{ + + class ClientBase; + class DataForm; + class DiscoHandler; + class DiscoNodeHandler; + class IQ; + + /** + * @brief This class implements XEP-0030 (Service Discovery) and XEP-0092 (Software Version). + * + * ClientBase will automatically instantiate a Disco object. It can be used to + * announce special features of your client, or its version, or... + * + * XEP version: 2.2 + * @author Jakob Schroeter + */ + class GLOOX_API Disco : public IqHandler + { + friend class ClientBase; + + public: + + class Identity; // declared below class Info + + /** + * A list of pointers to Identity objects. Used with Disco::Info. + */ + typedef std::list IdentityList; + + /** + * @brief An abstraction of a Disco Info element (from Service Discovery, XEP-0030) + * as a StanzaExtension. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API Info : public StanzaExtension + { + friend class Disco; + + public: + /** + * Returns the queried node identifier, if any. + * @return The node identifier. May be empty. + */ + const std::string& node() const { return m_node; } + + /** + * Returns the entity's supported features. + * @return A list of supported features/namespaces. + */ + const StringList& features() const { return m_features; } + + /** + * Use this function to check if the entity the Info came from supports agiven feature. + * @param feature The feature to check for. + * @return @b True if the entity announces support for the feature, @b false otherwise. + */ + bool hasFeature( const std::string& feature ) const; + + /** + * Returns the entity's identities. + * @return A list of pointers to Identity objects. + */ + const IdentityList& identities() const { return m_identities; } + + /** + * Returns an optionally included data form. This is used by e.g. MUC (XEP-0045). + * @return An optional data form included in the disco#info. May be 0. + */ + const DataForm* form() const { return m_form; } + + /** + * Adds an optional DataForm, e.g. for XEP-0232. Only one form can be added + * at this point. + * @param form An optional DataForm to include in the Info reply. + * The form will be owned by and deleted on destruction of the Info object. + * @note If called more than once the previously set form will be deleted. + */ + void setForm( DataForm* form ); + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new Info( tag ); + } + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new Info( *this ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + private: +#ifdef DISCO_INFO_TEST + public: +#endif + /** + * Creates a empty Info object, suitable for making disco#info requests. + * @param node The node identifier to query (optional). + * @param defaultFeatures Indicates whether or not the default features should be + * included in the Info. Should be @b false for requests, @b true for replies. + * Defaults to @b false. + */ + Info( const std::string& node = EmptyString, bool defaultFeatures = false ); + + /** + * Creates an Info object from the given Tag. + * @param tag A <query> tag in the disco#info namespace, (possibly) containing + * a disco#info reply. + */ + Info( const Tag* tag ); + + /** + * Copy constructor. + * @param info An Info object to copy. + */ + Info( const Info& info ); + + /** + * Virtual destructor. + */ + virtual ~Info(); + + /** + * Sets the current info node. + * @param node The info node. + */ + void setNode( const std::string& node ) { m_node = node; } + + /** + * This function can be used to set the entity's features. + * @param features A list of supported features/namespaces. + */ + void setFeatures( const StringList& features ) + { + StringList fl( features ); + fl.sort(); // needed on win32 + m_features.merge( fl ); + } + + /** + * This function can be used to set the entity's identities. + * @param identities A list of pointers to the entity's identities. + * @note The Identity objects pointed to will be owned by the Info object. The + * list should neither be used again nor should the Identity objects be deleted. + */ + void setIdentities( const IdentityList& identities ) { m_identities = identities; } + + std::string m_node; + StringList m_features; + IdentityList m_identities; + DataForm* m_form; + }; + + /** + * @brief An abstraction of a Disco identity (Service Discovery, XEP-0030). + * + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API Identity + { + friend class Info; + friend class Disco; + + public: + /** + * Constructs a Disco Identity from a category, type and name. + * See http://www.xmpp.org/registrar/disco-categories.html for more info. + * @param category The identity's category. + * @param type The identity's type. + * @param name The identity's name. + */ + Identity( const std::string& category, + const std::string& type, + const std::string& name ); + + /** + * Copy Contructor. + * @param id An Identity to create a new Identity object from. + */ + Identity( const Identity& id ); + + /** + * Destructor. + */ + ~Identity(); + + /** + * Returns the identity's category. + * @return The identity's category. + */ + const std::string& category() const { return m_category; } + + /** + * Returns the identity's type. + * @return The identity's type. + */ + const std::string& type() const { return m_type; } + + /** + * Returns the identity's name. + * @return The identity's name. + */ + const std::string& name() const { return m_name; } + + /** + * Creates and returns a Tag representation of this identity. + * @return A Tag, or 0. + */ + Tag* tag() const; + + private: + /** + * Creates a Disco Identity from the given Tag. + * @param tag A Tag representation of a disco identity. + */ + Identity( const Tag* tag ); + + std::string m_category; /**< The identity's category. */ + std::string m_type; /**< The identity's type. */ + std::string m_name; /**< The identity's name. */ + + }; + + class Item; // declared below class Items + + /** + * A list of pointers to Item objects. Used with Disco::Items. + */ + typedef std::list ItemList; + + /** + * @brief An abstraction of a Disco query element (from Service Discovery, XEP-0030) + * in the disco#items namespace, implemented as a StanzaExtension. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API Items : public StanzaExtension + { + friend class Disco; + + public: + // This needs to be public so one can proactively send a list of adhoc commands + // see XEP-0050 + /** + * Creates an empty Items object, suitable for making disco#items requests. + * @param node The node identifier to query (optional). + */ + Items( const std::string& node = EmptyString ); + + /** + * Virtual destructor. + */ + virtual ~Items(); + + /** + * This function can be used to set the entity's/node's items. + * @param items A list of pointers to the entity's/node's items. + * @note The Item objects pointed to will be owned by the Items object. The + * list should neither be used again nor should the Item objects be deleted. + */ + void setItems( const ItemList& items ); + + /** + * Returns the queried node identifier, if any. + * @return The node identifier. May be empty. + */ + const std::string& node() const { return m_node; } + + /** + * Returns the entity's/node's items. + * @return A list of pointers to Item objects. + */ + const ItemList& items() const { return m_items; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new Items( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new Items( *this ); + } + + private: +#ifdef DISCO_ITEMS_TEST + public: +#endif + /** + * Creates an Items object from the given Tag. + * @param tag A <query> tag in the disco#items namespace, (possibly) containing + * a disco#items reply. + */ + Items( const Tag* tag ); + + std::string m_node; + ItemList m_items; + }; + + /** + * @brief An abstraction of a Disco item (Service Discovery, XEP-0030). + * + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API Item + { + friend class Items; + + public: + /** + * Constructs a Disco Item from a JID, node and name. + * @param jid The item's JID. + * @param node The item's type. + * @param name The item's name. + */ + Item( const JID& jid, + const std::string& node, + const std::string& name ) + : m_jid( jid ), m_node( node ), m_name( name ) {} + + /** + * Destructor. + */ + ~Item() {} + + /** + * Returns the item's JID. + * @return The item's JID. + */ + const JID& jid() const { return m_jid; } + + /** + * Returns the item's node. + * @return The item's node. + */ + const std::string& node() const { return m_node; } + + /** + * Returns the item's name. + * @return The item's name. + */ + const std::string& name() const { return m_name; } + + /** + * Creates and returns a Tag representation of this item. + * @return A Tag, or 0. + */ + Tag* tag() const; + + private: + /** + * Creates a Disco Item from the given Tag. + * @param tag A Tag representation of a Disco item. + */ + Item( const Tag* tag ); + + JID m_jid; /**< The item's jid. */ + std::string m_node; /**< The item's type. */ + std::string m_name; /**< The item's name. */ + + }; + + /** + * Adds a feature to the list of supported Jabber features. + * The list will be posted as an answer to IQ queries in the + * "http://jabber.org/protocol/disco#info" namespace. + * These IQ packets will also be forwarded to the + * application's IqHandler, if it listens to the @c disco\#info namespace. + * By default, disco(very) queries are handled by the library. + * By default, all supported, not disabled features are announced. + * @param feature A feature (namespace) the host app supports. + * @note Use this function for non-queryable features. For nodes that shall + * answer to @c disco\#info queries, use registerNodeHandler(). + */ + void addFeature( const std::string& feature ) + { m_features.push_back( feature ); } + + /** + * Removes the given feature from the list of advertised client features. + * @param feature The feature to remove. + * @since 0.9 + */ + void removeFeature( const std::string& feature ) + { m_features.remove( feature ); } + + /** + * Lets you retrieve the features this Disco instance supports. + * @param defaultFeatures Include default features. Defaults to @b false. + * @return A list of supported features/namespaces. + */ + const StringList features( bool defaultFeatures = false ) const; + + /** + * Queries the given JID for general infomation according to + * XEP-0030 (Service Discovery). + * To receive the results inherit from DiscoHandler and register with the Disco object. + * @param to The destination-JID of the query. + * @param node An optional node to query. Not inserted if empty. + * @param dh The DiscoHandler to notify about results. + * @param context A context identifier. + * @param tid An optional id that is going to be used as the IQ request's id. Only + * necessary if you need to know the request's id. + */ + void getDiscoInfo( const JID& to, const std::string& node, DiscoHandler* dh, int context, + const std::string& tid = EmptyString ) + { getDisco( to, node, dh, context, GetDiscoInfo, tid ); } + + /** + * Queries the given JID for its items according to + * XEP-0030 (Service Discovery). + * To receive the results inherit from DiscoHandler and register with the Disco object. + * @param to The destination-JID of the query. + * @param node An optional node to query. Not inserted if empty. + * @param dh The DiscoHandler to notify about results. + * @param context A context identifier. + * @param tid An optional id that is going to be used as the IQ request's id. Only + * necessary if you need to know the request's id. + */ + void getDiscoItems( const JID& to, const std::string& node, DiscoHandler* dh, int context, + const std::string& tid = EmptyString ) + { getDisco( to, node, dh, context, GetDiscoItems, tid ); } + + /** + * Sets the version of the host application using this library. + * The library takes care of jabber:iq:version requests. These + * IQ packets will not be forwarded to the IqHandlers. + * @param name The name to be returned to inquireing clients. + * @param version The version to be returned to inquireing clients. + * @param os The operating system to announce. Default: don't include. + */ + void setVersion( const std::string& name, const std::string& version, + const std::string& os = EmptyString ); + + /** + * Returns the application's advertised name. + * @return The application's advertised name. + */ + const std::string& name() const { return m_versionName; } + + /** + * Returns the application's advertised version. + * @return The application's advertised version. + */ + const std::string& version() const { return m_versionVersion; } + + /** + * Returns the application's advertised operating system. + * @return The application's advertised operating system. + */ + const std::string& os() const { return m_versionOs; } + + /** + * Sets the identity of this entity. + * The library uses this information to answer disco#info requests + * with a correct identity. + * XEP-0030 requires an entity to have at least one identity. See XEP-0030 + * for more information on categories and types. + * @param category The entity category of this client. Default: client. + * @param type The type of this entity. Default: bot. + * @param name The name of the entity. Default: empty. + * @note An entity can have more than one identity. You cann add more identities + * using addIdentity(). A call to setIdentity() will clear the list of identities + * and, after that, add the new identity given by the arguments to setIdentity(). + */ + void setIdentity( const std::string& category, const std::string& type, + const std::string& name = EmptyString ); + + /** + * Adds another identity to the list of identities. + * @param category The entity category of this client. Default: client. + * @param type The type of this entity. Default: bot. + * @param name The name of the entity. Default: empty. + */ + void addIdentity( const std::string& category, const std::string& type, + const std::string& name = EmptyString ) + { m_identities.push_back( new Identity( category, type, name ) ); } + + /** + * Returns the entity's identities. + * @return The entity's identities. + */ + const IdentityList& identities() const { return m_identities; } + + /** + * Adds an optional DataForm to Disco:Info replies, e.g. for XEP-0232. + * Only one form can be added at this point. + * @param form An optional DataForm to include in the Info reply. + * The form will be owned by and deleted on destruction of the Disco object. + * @note If called more than once the previously set form will be deleted. + */ + void setForm( DataForm* form ); + + /** + * Returns the DataForm set by setForm(). Used by Capabilities. + * @return The DataForm, or 0. + */ + const DataForm* form() const { return m_form; } + + /** + * Use this function to register an @ref DiscoHandler with the Disco + * object. This is only necessary if you want to receive Disco-set requests. Else + * a one-time registration happens when calling getDiscoInfo() and getDiscoItems(), respectively. + * @param dh The DiscoHandler-derived object to register. + */ + void registerDiscoHandler( DiscoHandler* dh ) + { m_discoHandlers.push_back( dh ); } + + /** + * Unregisters the given DiscoHandler. + * @param dh The DiscoHandler to unregister. + */ + void removeDiscoHandler( DiscoHandler* dh ); + + /** + * Use this function to register a @ref DiscoNodeHandler with the Disco + * object. The DiscoNodeHandler will receive disco#items queries which are + * directed to the corresponding node registered for the handler. + * @param nh The NodeHandler-derived object to register. + * @param node The node name to associate with this handler. Use an empty string to + * register for the root node. + */ + void registerNodeHandler( DiscoNodeHandler* nh, const std::string& node ); + + /** + * Removes the node handler for the given node. + * @param nh The NodeHandler to unregister. + * @param node The node for which the handler shall be removed. Use an empty string to + * remove the root node's handler. + */ + void removeNodeHandler( DiscoNodeHandler* nh, const std::string& node ); + + /** + * Removes all registered nodes of the given node handler. + * @param nh The NodeHandler to unregister. + */ + void removeNodeHandlers( DiscoNodeHandler* nh ); + + // reimplemented from IqHandler. + virtual bool handleIq( const IQ& iq ); + + // reimplemented from IqHandler. + virtual void handleIqID( const IQ& iq, int context ); + + private: +#ifdef DISCO_TEST + public: +#endif + Disco( ClientBase* parent ); + virtual ~Disco(); + + enum IdType + { + GetDiscoInfo, + GetDiscoItems + }; + + void getDisco( const JID& to, const std::string& node, DiscoHandler* dh, + int context, IdType idType, const std::string& tid ); + + struct DiscoHandlerContext + { + DiscoHandler* dh; + int context; + }; + + ClientBase* m_parent; + + typedef std::list DiscoHandlerList; + typedef std::list DiscoNodeHandlerList; + typedef std::map DiscoNodeHandlerMap; + typedef std::map DiscoHandlerMap; + + DiscoHandlerList m_discoHandlers; + DiscoNodeHandlerMap m_nodeHandlers; + DiscoHandlerMap m_track; + IdentityList m_identities; + StringList m_features; + StringMap m_queryIDs; + DataForm* m_form; + + std::string m_versionName; + std::string m_versionVersion; + std::string m_versionOs; + + }; + +} + +#endif // DISCO_H__ diff --git a/libs/libgloox/discohandler.h b/libs/libgloox/discohandler.h new file mode 100644 index 0000000..1a0941d --- /dev/null +++ b/libs/libgloox/discohandler.h @@ -0,0 +1,83 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef DISCOHANDLER_H__ +#define DISCOHANDLER_H__ + +#include "macros.h" +#include "disco.h" + +#include + +namespace gloox +{ + + class IQ; + + /** + * @brief A virtual interface that enables objects to receive Service Discovery (XEP-0030) events. + * + * A class implementing this interface can receive the results of sent disco queries. + * + * @author Jakob Schroeter + */ + class GLOOX_API DiscoHandler + { + public: + /** + * Virtual Destructor. + */ + virtual ~DiscoHandler() {} + + /** + * Reimplement this function if you want to be notified about the result + * of an disco#info query. + * @param from The sender of the disco#info result. + * @param info The Info. + * @param context A context identifier. + * @since 1.0 + */ + virtual void handleDiscoInfo( const JID& from, const Disco::Info& info, int context ) = 0; + + /** + * Reimplement this function if you want to be notified about the result + * of a disco#items query. + * @param from The sender of the disco#items result. + * @param items The Items. + * @param context A context identifier. + * @since 1.0 + */ + virtual void handleDiscoItems( const JID& from, const Disco::Items& items, int context ) = 0; + + /** + * Reimplement this function to receive disco error notifications. + * @param from The sender of the error result. + * @param error The Error. May be 0. + * @param context A context identifier. + * @since 1.0 + */ + virtual void handleDiscoError( const JID& from, const Error* error, int context ) = 0; + + /** + * Reimplement this function to receive notifications about incoming IQ + * stanzas of type 'set' in the disco namespace. + * @param iq The full IQ. + * @return Returns @b true if the stanza was handled and answered, @b false otherwise. + */ + virtual bool handleDiscoSet( const IQ& iq ) { (void)iq; return false; } + + }; + +} + +#endif // DISCOHANDLER_H__ diff --git a/libs/libgloox/disconodehandler.h b/libs/libgloox/disconodehandler.h new file mode 100644 index 0000000..8cdde36 --- /dev/null +++ b/libs/libgloox/disconodehandler.h @@ -0,0 +1,81 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef DISCONODEHANDLER_H__ +#define DISCONODEHANDLER_H__ + +#include "gloox.h" +#include "disco.h" + +#include +#include +#include + +namespace gloox +{ + + /** + * @brief Derived classes can be registered as NodeHandlers for certain nodes with the Disco object. + * + * Incoming disco#info and disco#items queries are delegated to their respective handlers. + * + * @author Jakob Schroeter + */ + class GLOOX_API DiscoNodeHandler + { + public: + /** + * Virtual Destructor. + */ + virtual ~DiscoNodeHandler() {} + + /** + * In addition to @c handleDiscoNodeIdentities, this function is used to gather + * more information on a specific node. It is called when a disco#info query + * arrives with a node attribute that matches the one registered for this handler. + * @param from The sender of the request. + * @param node The node this handler is supposed to handle. + * @return A list of features supported by this node. + */ + virtual StringList handleDiscoNodeFeatures( const JID& from, const std::string& node ) = 0; + + /** + * In addition to @c handleDiscoNodeFeatures, this function is used to gather + * more information on a specific node. It is called when a disco#info query + * arrives with a node attribute that matches the one registered for this handler. + * @param from The sender of the request. + * @param node The node this handler is supposed to handle. + * @return A list of identities for the given node. The caller will own the identities. + */ + virtual Disco::IdentityList handleDiscoNodeIdentities( const JID& from, + const std::string& node ) = 0; + + /** + * This function is used to gather more information on a specific node. + * It is called when a disco#items query arrives with a node attribute that + * matches the one registered for this handler. If node is empty, items for the + * root node (no node) shall be returned. + * @param from The sender of the request. + * @param to The receiving JID (useful for transports). + * @param node The node this handler is supposed to handle. + * @return A list of items supported by this node. + */ + virtual Disco::ItemList handleDiscoNodeItems( const JID& from, const JID& to, + const std::string& node = EmptyString ) = 0; + + }; + +} + +#endif // DISCONODEHANDLER_H__ diff --git a/libs/libgloox/dns.cpp b/libs/libgloox/dns.cpp new file mode 100644 index 0000000..0ddc081 --- /dev/null +++ b/libs/libgloox/dns.cpp @@ -0,0 +1,488 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "config.h" + +#include "gloox.h" +#include "dns.h" +#include "util.h" + +#ifndef _WIN32_WCE +# include +#endif + +#include +#include + +#if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ ) +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) +# include +#elif defined( _WIN32_WCE ) +# include +#endif + +#ifdef HAVE_WINDNS_H +# include +#endif + +#define SRV_COST (RRFIXEDSZ+0) +#define SRV_WEIGHT (RRFIXEDSZ+2) +#define SRV_PORT (RRFIXEDSZ+4) +#define SRV_SERVER (RRFIXEDSZ+6) +#define SRV_FIXEDSZ (RRFIXEDSZ+6) + +#ifndef T_SRV +# define T_SRV 33 +#endif + +// mingw +#ifndef DNS_TYPE_SRV +# define DNS_TYPE_SRV 33 +#endif + +#ifndef NS_CMPRSFLGS +# define NS_CMPRSFLGS 0xc0 +#endif + +#ifndef C_IN +# define C_IN 1 +#endif + +#ifndef INVALID_SOCKET +# define INVALID_SOCKET -1 +#endif + +#define XMPP_PORT 5222 + +namespace gloox +{ + +#if defined( HAVE_RES_QUERYDOMAIN ) && defined( HAVE_DN_SKIPNAME ) && defined( HAVE_RES_QUERY ) + DNS::HostMap DNS::resolve( const std::string& service, const std::string& proto, + const std::string& domain, const LogSink& logInstance ) + { + buffer srvbuf; + bool error = false; + + const std::string dname = "_" + service + "._" + proto; + + if( !domain.empty() ) + srvbuf.len = res_querydomain( dname.c_str(), const_cast( domain.c_str() ), + C_IN, T_SRV, srvbuf.buf, NS_PACKETSZ ); + else + srvbuf.len = res_query( dname.c_str(), C_IN, T_SRV, srvbuf.buf, NS_PACKETSZ ); + + if( srvbuf.len < 0 ) + return defaultHostMap( domain, logInstance ); + + HEADER* hdr = (HEADER*)srvbuf.buf; + unsigned char* here = srvbuf.buf + NS_HFIXEDSZ; + + if( ( hdr->tc ) || ( srvbuf.len < NS_HFIXEDSZ ) ) + error = true; + + if( hdr->rcode >= 1 && hdr->rcode <= 5 ) + error = true; + + if( ntohs( hdr->ancount ) == 0 ) + error = true; + + if( ntohs( hdr->ancount ) > NS_PACKETSZ ) + error = true; + + int cnt; + for( cnt = ntohs( hdr->qdcount ); cnt > 0; --cnt ) + { + int strlen = dn_skipname( here, srvbuf.buf + srvbuf.len ); + here += strlen + NS_QFIXEDSZ; + } + + unsigned char* srv[NS_PACKETSZ]; + int srvnum = 0; + for( cnt = ntohs( hdr->ancount ); cnt > 0; --cnt ) + { + int strlen = dn_skipname( here, srvbuf.buf + srvbuf.len ); + here += strlen; + srv[srvnum++] = here; + here += SRV_FIXEDSZ; + here += dn_skipname( here, srvbuf.buf + srvbuf.len ); + } + + if( error ) + { + return defaultHostMap( domain, logInstance ); + } + + // (q)sort here + + HostMap servers; + for( cnt = 0; cnt < srvnum; ++cnt ) + { + char srvname[NS_MAXDNAME]; + srvname[0] = '\0'; + + if( dn_expand( srvbuf.buf, srvbuf.buf + NS_PACKETSZ, + srv[cnt] + SRV_SERVER, srvname, NS_MAXDNAME ) < 0 + || !(*srvname) ) + continue; + + unsigned char* c = srv[cnt] + SRV_PORT; + servers.insert( std::make_pair( (char*)srvname, ntohs( c[1] << 8 | c[0] ) ) ); + } + + if( !servers.size() ) + return defaultHostMap( domain, logInstance ); + + return servers; + } + +#elif defined( _WIN32 ) && defined( HAVE_WINDNS_H ) + DNS::HostMap DNS::resolve( const std::string& service, const std::string& proto, + const std::string& domain, const LogSink& logInstance ) + { + const std::string dname = "_" + service + "._" + proto + "." + domain; + bool error = false; + + DNS::HostMap servers; + DNS_RECORD* pRecord = NULL; + DNS_STATUS status = DnsQuery_UTF8( dname.c_str(), DNS_TYPE_SRV, DNS_QUERY_STANDARD, NULL, &pRecord, NULL ); + if( status == ERROR_SUCCESS ) + { + DNS_RECORD* pRec = pRecord; + do + { + if( pRec->wType == DNS_TYPE_SRV ) + { + servers[pRec->Data.SRV.pNameTarget] = pRec->Data.SRV.wPort; + } + pRec = pRec->pNext; + } + while( pRec != NULL ); + DnsRecordListFree( pRecord, DnsFreeRecordList ); + } + else + { + logInstance.warn( LogAreaClassDns, "DnsQuery_UTF8() failed: " + util::int2string( status ) ); + error = true; + } + + if( error || !servers.size() ) + { + servers = defaultHostMap( domain, logInstance ); + } + + return servers; + } + +#else + DNS::HostMap DNS::resolve( const std::string& /*service*/, const std::string& /*proto*/, + const std::string& domain, const LogSink& logInstance ) + { + logInstance.warn( LogAreaClassDns, "Notice: gloox does not support SRV " + "records on this platform. Using A records instead." ); + return defaultHostMap( domain, logInstance ); + } +#endif + + DNS::HostMap DNS::defaultHostMap( const std::string& domain, const LogSink& logInstance ) + { + HostMap server; + + logInstance.warn( LogAreaClassDns, "Notice: no SRV record found for " + + domain + ", using default port." ); + + if( !domain.empty() ) + server[domain] = XMPP_PORT; + + return server; + } + +#ifdef HAVE_GETADDRINFO + void DNS::resolve( struct addrinfo** res, const std::string& service, const std::string& proto, + const std::string& domain, const LogSink& logInstance ) + { + logInstance.dbg( LogAreaClassDns, "Resolving: _" + service + "._" + proto + "." + domain ); + struct addrinfo hints; + if( proto == "tcp" ) + hints.ai_socktype = SOCK_STREAM; + else if( proto == "udp" ) + hints.ai_socktype = SOCK_DGRAM; + else + { + logInstance.err( LogAreaClassDns, "Unknown/Invalid protocol: " + proto ); + } + memset( &hints, '\0', sizeof( hints ) ); + hints.ai_flags = AI_ADDRCONFIG | AI_CANONNAME; + hints.ai_socktype = SOCK_STREAM; + int e = getaddrinfo( domain.c_str(), service.c_str(), &hints, res ); + if( e ) + logInstance.err( LogAreaClassDns, "getaddrinfo() failed" ); + } + + int DNS::connect( const std::string& host, const LogSink& logInstance ) + { + struct addrinfo* results = 0; + + resolve( &results, host, logInstance ); + if( !results ) + { + logInstance.err( LogAreaClassDns, "host not found: " + host ); + return -ConnDnsError; + } + + struct addrinfo* runp = results; + while( runp ) + { + int fd = DNS::connect( runp, logInstance ); + if( fd >= 0 ) + return fd; + + runp = runp->ai_next; + } + + freeaddrinfo( results ); + + return -ConnConnectionRefused; + } + + int DNS::connect( struct addrinfo* res, const LogSink& logInstance ) + { + if( !res ) + return -1; + + int fd = getSocket( res->ai_family, res->ai_socktype, res->ai_protocol, logInstance ); + if( fd < 0 ) + return fd; + + if( ::connect( fd, res->ai_addr, res->ai_addrlen ) == 0 ) + { + char ip[NI_MAXHOST]; + char port[NI_MAXSERV]; + + if( getnameinfo( res->ai_addr, sizeof( sockaddr ), + ip, sizeof( ip ), + port, sizeof( port ), + NI_NUMERICHOST | NI_NUMERICSERV ) ) + { + //FIXME do we need to handle this? How? Can it actually happen at all? +// printf( "could not get numeric hostname"); + } + + if( res->ai_canonname ) + logInstance.dbg( LogAreaClassDns, "Connecting to " + std::string( res->ai_canonname ) + + " (" + ip + "), port " + port ); + else + logInstance.dbg( LogAreaClassDns, "Connecting to " + ip + ":" + port ); + + return fd; + } + + std::string message = "connect() failed. " +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + "WSAGetLastError: " + util::int2string( ::WSAGetLastError() ); +#else + "errno: " + util::int2string( errno ); +#endif + logInstance.dbg( LogAreaClassDns, message ); + + closeSocket( fd, logInstance ); + return -ConnConnectionRefused; + } + +#else + + int DNS::connect( const std::string& host, const LogSink& logInstance ) + { + HostMap hosts = resolve( host, logInstance ); + if( hosts.size() == 0 ) + return -ConnDnsError; + + HostMap::const_iterator it = hosts.begin(); + for( ; it != hosts.end(); ++it ) + { + int fd = DNS::connect( (*it).first, (*it).second, logInstance ); + if( fd >= 0 ) + return fd; + } + + return -ConnConnectionRefused; + } +#endif + + int DNS::getSocket( const LogSink& logInstance ) + { +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + WSADATA wsaData; + if( WSAStartup( MAKEWORD( 1, 1 ), &wsaData ) != 0 ) + { + logInstance.dbg( LogAreaClassDns, "WSAStartup() failed. WSAGetLastError: " + + util::int2string( ::WSAGetLastError() ) ); + return -ConnDnsError; + } +#endif + + int protocol = IPPROTO_TCP; + struct protoent* prot; + if( ( prot = getprotobyname( "tcp" ) ) != 0 ) + { + protocol = prot->p_proto; + } + else + { + std::string message = "getprotobyname( \"tcp\" ) failed. " +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + "WSAGetLastError: " + util::int2string( ::WSAGetLastError() ) +#else + "errno: " + util::int2string( errno ); +#endif + + ". Falling back to IPPROTO_TCP: " + util::int2string( IPPROTO_TCP ); + logInstance.dbg( LogAreaClassDns, message ); + + // Do not return an error. We'll fall back to IPPROTO_TCP. + } + + return getSocket( PF_INET, SOCK_STREAM, protocol, logInstance ); + } + + int DNS::getSocket( int af, int socktype, int proto, const LogSink& logInstance ) + { +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + SOCKET fd; +#else + int fd; +#endif + if( ( fd = socket( af, socktype, proto ) ) == INVALID_SOCKET ) + { + std::string message = "getSocket( " + + util::int2string( af ) + ", " + + util::int2string( socktype ) + ", " + + util::int2string( proto ) + + " ) failed. " +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + "WSAGetLastError: " + util::int2string( ::WSAGetLastError() ); +#else + "errno: " + util::int2string( errno ); +#endif + logInstance.dbg( LogAreaClassDns, message ); + + cleanup( logInstance ); + return -ConnConnectionRefused; + } + +#ifdef HAVE_SETSOCKOPT + int timeout = 5000; + setsockopt( fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof( timeout ) ); + setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (char*)&timeout, sizeof( timeout ) ); +#endif + + return (int)fd; + } + + int DNS::connect( const std::string& host, int port, const LogSink& logInstance ) + { + int fd = getSocket( logInstance ); + if( fd < 0 ) + return fd; + + struct hostent* h; + if( ( h = gethostbyname( host.c_str() ) ) == 0 ) + { + logInstance.dbg( LogAreaClassDns, "gethostbyname() failed for " + host + "." ); + cleanup( logInstance ); + return -ConnDnsError; + } + + struct sockaddr_in target; + target.sin_family = AF_INET; + target.sin_port = htons( static_cast( port ) ); + + if( h->h_length != sizeof( struct in_addr ) ) + { + logInstance.dbg( LogAreaClassDns, "gethostbyname() returned unexpected structure." ); + cleanup( logInstance ); + return -ConnDnsError; + } + else + { + memcpy( &target.sin_addr, h->h_addr, sizeof( struct in_addr ) ); + } + + logInstance.dbg( LogAreaClassDns, "Connecting to " + host + + " (" + inet_ntoa( target.sin_addr ) + ":" + util::int2string( port ) + ")" ); + + memset( target.sin_zero, '\0', 8 ); + if( ::connect( fd, (struct sockaddr *)&target, sizeof( struct sockaddr ) ) == 0 ) + { + logInstance.dbg( LogAreaClassDns, "Connected to " + host + " (" + + inet_ntoa( target.sin_addr ) + ":" + util::int2string( port ) + ")" ); + return fd; + } + + std::string message = "Connection to " + host + " (" + + inet_ntoa( target.sin_addr ) + ":" + util::int2string( port ) + ") failed. " +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + "WSAGetLastError: " + util::int2string( ::WSAGetLastError() ); +#else + "errno: " + util::int2string( errno ); +#endif + logInstance.dbg( LogAreaClassDns, message ); + + closeSocket( fd, logInstance ); + return -ConnConnectionRefused; + } + + void DNS::closeSocket( int fd, const LogSink& logInstance ) + { +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + int result = closesocket( fd ); +#else + int result = close( fd ); +#endif + + if( result != 0 ) + { + std::string message = "closeSocket() failed. " +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + "WSAGetLastError: " + util::int2string( ::WSAGetLastError() ); +#else + "errno: " + util::int2string( errno ); +#endif + logInstance.dbg( LogAreaClassDns, message ); + } + } + + void DNS::cleanup( const LogSink& logInstance ) + { +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + if( WSACleanup() != 0 ) + { + logInstance.dbg( LogAreaClassDns, "WSACleanup() failed. WSAGetLastError: " + + util::int2string( ::WSAGetLastError() ) ); + } +#else + (void)logInstance; +#endif + } + +} diff --git a/libs/libgloox/dns.h b/libs/libgloox/dns.h new file mode 100644 index 0000000..5be2a6f --- /dev/null +++ b/libs/libgloox/dns.h @@ -0,0 +1,179 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef DNS_H__ +#define DNS_H__ + +#include "macros.h" +#include "logsink.h" + +#ifdef __MINGW32__ +# include +# include +#endif + +#ifdef HAVE_ARPA_NAMESER_H +# include +#endif + +#ifdef __APPLE__ +# include +#endif + +#ifndef NS_MAXDNAME +# define NS_MAXDNAME 1025 +#endif + +#ifndef NS_PACKETSZ +# define NS_PACKETSZ 512 +#endif + +#ifdef HAVE_GETADDRINFO +# include +# include +# include +#endif + +#include +#include + +namespace gloox +{ + + /** + * @brief This class holds a number of static functions used for DNS related stuff. + * + * You should not need to use these functions directly. + * + * @author Jakob Schroeter + * @since 0.3 + */ + class GLOOX_API DNS + { + public: + + /** + * A list of strings (used for server addresses) and ints (used for port numbers). + */ + typedef std::map HostMap; + + /** + * This function resolves a service/protocol/domain tuple. + * @param service The SRV service type. + * @param proto The SRV protocol. + * @param domain The domain to search for SRV records. + * @param logInstance A LogSink to use for logging. + * @return A list of weighted hostname/port pairs from SRV records, or A records if no SRV + * records where found. + */ + static HostMap resolve( const std::string& service, const std::string& proto, + const std::string& domain, const LogSink& logInstance ); + + /** + * This is a convenience funtion which uses @ref resolve() to resolve SRV records + * for a given domain, using a service of @b xmpp-client and a proto of @b tcp. + * @param domain The domain to resolve SRV records for. + * @param logInstance A LogSink to use for logging. + * @return A list of weighted hostname/port pairs from SRV records, or A records if no SRV + * records where found. + */ + static HostMap resolve( const std::string& domain, const LogSink& logInstance ) + { return resolve( "xmpp-client", "tcp", domain, logInstance ); } + + /** + * This is a convenience function which uses @ref resolve() to get a list of hosts + * and connects to one of them. + * @param host The host to resolve SRV records for. + * @param logInstance A LogSink to use for logging. + * @return A file descriptor for the established connection. + */ + static int connect( const std::string& host, const LogSink& logInstance ); + + /** + * This is a convenience function which connects to the given host and port. No SRV + * records are resolved. Use this function for special setups. + * @param host The host/IP address to connect to. + * @param port A custom port to connect to. + * @param logInstance A LogSink to use for logging. + * @return A file descriptor for the established connection. + */ + static int connect( const std::string& host, int port, const LogSink& logInstance ); + + /** + * A convenience function that prepares and returnes a simple, unconnected TCP socket. + * @param logInstance A LogSink to use for logging. + * @return A TCP socket. + */ + static int getSocket( const LogSink& logInstance ); + + /** + * Closes the given socket. + * @param fd The socket to close. + * @param logInstance A LogSink to use for logging. + */ + static void closeSocket( int fd, const LogSink& logInstance ); + + private: +#ifdef HAVE_GETADDRINFO + /** + * Resolves the given service for the given domain and protocol, using the IPv6-ready + * getaddrinfo(). The result is put into the first parameter. + * @param res A pointer to a pointer holding the query results. + * @param service A service string to query for, e.g. xmpp-client. + * @param proto A protocol name. + * @param domain The domain to query for. + * @param logInstance A LogSink to use for logging. + */ + static void resolve( struct addrinfo** res, const std::string& service, const std::string& proto, + const std::string& domain, const LogSink& logInstance ); + + /** + * This is a convenience funtion which uses @ref resolve() to resolve SRV records + * for a given domain, using a service of @b xmpp-client and a proto of @b tcp. + * @param res A pointer to a pointer holding the query results. + * @param domain The domain to resolve SRV records for. + * @param logInstance A LogSink to use for logging. + */ + static void resolve( struct addrinfo** res, const std::string& domain, const LogSink& logInstance ) + { resolve( res, "xmpp-client", "tcp", domain, logInstance ); } + + /** + * Tries to connect to the host/address contained in the addrinfo structure. + * @param res The connection parameters. + * @param logInstance A LogSink to use for logging. + * @return A file descriptor for the established connection. + */ + static int connect( struct addrinfo* res, const LogSink& logInstance ); +#endif + + /** + * This function prepares and returns a socket with the given parameters. + * @param af The address family. E.g. PF_INET. + * @param socktype The socket type. E.g. SOCK_STREAM. + * @param proto The protocol number. E.g. 6 (TCP). + */ + static int getSocket( int af, int socktype, int proto, const LogSink& logInstance ); + + static HostMap defaultHostMap( const std::string& domain, const LogSink& logInstance ); + static void cleanup( const LogSink& logInstance ); + + struct buffer + { + unsigned char buf[NS_PACKETSZ]; + int len; + }; + }; + +} + +#endif // DNS_H__ diff --git a/libs/libgloox/error.cpp b/libs/libgloox/error.cpp new file mode 100644 index 0000000..d0c23cf --- /dev/null +++ b/libs/libgloox/error.cpp @@ -0,0 +1,138 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#include "error.h" +#include "tag.h" +#include "util.h" + +namespace gloox +{ + + /* Error type values */ + static const char* errValues [] = { + "auth", + "cancel", + "continue", + "modify", + "wait" + }; + + /* Stanza error values */ + static const char* stanzaErrValues [] = { + "bad-request", + "conflict", + "feature-not-implemented", + "forbidden", + "gone", + "internal-server-error", + "item-not-found", + "jid-malformed", + "not-acceptable", + "not-allowed", + "not-authorized", + "not-modified", + "payment-required", + "recipient-unavailable", + "redirect", + "registration-required", + "remote-server-not-found", + "remote-server-timeout", + "resource-constraint", + "service-unavailable", + "subscription-required", + "undefined-condition", + "unexpected-request", + "unknown-sender" + }; + + static inline StanzaErrorType stanzaErrorType( const std::string& type ) + { + return (StanzaErrorType)util::lookup( type, errValues ); + } + + static inline StanzaError stanzaError( const std::string& type ) + { + return (StanzaError)util::lookup( type, stanzaErrValues ); + } + + Error::Error( const Tag* tag ) + : StanzaExtension( ExtError ), + m_error( StanzaErrorUndefined ), m_appError( 0 ) + { + if( !tag || tag->name() != "error" ) + return; + + m_type = stanzaErrorType( tag->findAttribute( TYPE ) ); + + TagList::const_iterator it = tag->children().begin(); + for( ; it != tag->children().end(); ++it ) + { + StanzaError srt = gloox::stanzaError( (*it)->name() ); + if( srt != StanzaErrorUndefined ) + m_error = srt; + else if( (*it)->name() == "text" ) + m_text[(*it)->findAttribute("xml:lang")] = (*it)->cdata(); + else + m_appError = (*it)->clone(); + } + } + + Error::Error( const Error& error ) + : StanzaExtension( ExtError ), m_type( error.m_type ), + m_error( error.m_error ), m_appError( error.m_appError ? m_appError->clone() : 0 ) + {} + + Error::~Error() + { + delete m_appError; + } + + const std::string& Error::filterString() const + { + static const std::string filter = "/iq/error" + "|/message/error" + "|/presence/error" + "|/subscription/error"; + return filter; + } + + + Tag* Error::tag() const + { + if( m_type == StanzaErrorTypeUndefined || m_error == StanzaErrorUndefined ) + return 0; + + Tag* error = new Tag( "error", TYPE, util::lookup( m_type, errValues ) ); + new Tag( error, util::lookup( m_error, stanzaErrValues ), XMLNS, XMLNS_XMPP_STANZAS ); + + StringMap::const_iterator it = m_text.begin(); + for( ; it != m_text.end(); ++it ) + { + Tag* txt = new Tag( error, "text" ); + txt->setXmlns( XMLNS_XMPP_STANZAS ); + txt->addAttribute( "xml:lang", (*it).first ); + txt->setCData( (*it).second ); + } + + if( m_appError ) + error->addChild( m_appError->clone() ); + + return error; + } + + const std::string& Error::text( const std::string& lang ) const + { + StringMap::const_iterator it = m_text.find( lang ); + return it != m_text.end() ? (*it).second : EmptyString; + } + +} diff --git a/libs/libgloox/error.h b/libs/libgloox/error.h new file mode 100644 index 0000000..24eccce --- /dev/null +++ b/libs/libgloox/error.h @@ -0,0 +1,139 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#ifndef ERROR_H__ +#define ERROR_H__ + +#include "gloox.h" +#include "stanzaextension.h" + +#include +#include + +namespace gloox +{ + + class Tag; + + /** + * @brief A stanza error abstraction implemented as a StanzaExtension. + * + * @author Vincent Thomasset + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API Error : public StanzaExtension + { + public: + +// Error() +// : StanzaExtension( ExtError ), m_type( StanzaErrorTypeUndefined ), +// m_error( StanzaErrorUndefined ), m_appError( 0 ) +// {} + + /** + * Creates a new Error object from the given Tag. + * @param tag The Tag to parse. + */ + Error( const Tag* tag = 0 ); + + /** + * Creates a new Error object. + * @param type The error type. + * @param error The actual stanza error. + * @param appError An optional application-specific error. + */ + Error( StanzaErrorType type, StanzaError error, Tag* appError = 0 ) + : StanzaExtension( ExtError ), m_type( type ), + m_error( error ), m_appError( appError ) + {} + + /** + * Virtual destructor. + */ + virtual ~Error(); + + /** + * Returns the error type. + * @return The error type. + */ + StanzaErrorType type() const { return m_type; } + + /** + * Return the stanza error. + * @return The actual error. + */ + StanzaError error() const { return m_error; } + + /** + * This function can be used to retrieve the application-specific error + * condition of a stanza error. + * @return The application-specific error element of a stanza error. + * 0 if no respective element was found or no error occured. + */ + const Tag* appError() const { return m_appError; } + + /** + * Returns the text of a error stanza for the given language if available. + * If the requested language is not available, the default text (without + * a xml:lang attribute) will be returned. + * @param lang The language identifier for the desired language. It must + * conform to section 2.12 of the XML specification and RFC 3066. If + * empty, the default text will be returned, if any. + * @return The text of an error stanza. + */ + const std::string& text( const std::string& lang = EmptyString ) const; + + /** + * Sets the text of a error stanza for the given language. + * @param text The error text to set. + * @param lang The language identifier for the desired language. It must + * conform to section 2.12 of the XML specification and RFC 3066. If + * empty, the default text will be set. + */ + void setText( const std::string& text, const std::string& lang = EmptyString ) + { + m_text[lang] = text; + } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new Error( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new Error( *this ); + } + + private: + Error( const Error& error ); + + void setValues( const Tag* tag ); + + StanzaErrorType m_type; + StanzaError m_error; + Tag* m_appError; + StringMap m_text; + }; + +} + +#endif /* ERROR_H__ */ diff --git a/libs/libgloox/event.h b/libs/libgloox/event.h new file mode 100644 index 0000000..3a2e3f2 --- /dev/null +++ b/libs/libgloox/event.h @@ -0,0 +1,81 @@ +/* + Copyright (c) 2008-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef EVENT_H__ +#define EVENT_H__ + +namespace gloox +{ + + class Stanza; + + /** + * @brief A base class for events. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class Event + { + + public: + /** + * Event types. + */ + enum EventType + { + PingPing, /**< Incoming Ping (XEP-0199). */ + PingPong, /**< Incoming Pong (XEP-0199). */ + PingError /**< Incoming Error Pong (XEP-0199). */ + }; + + /** + * Creates a new Event of the given type. + * @param type The Event type. + */ + Event( EventType type ) : m_eventType( type ), m_stanza( 0 ) {} + + /** + * Creates a new Event of the given type, referencing the given Stanza. + * @param type The Event type. + * @param stanza A Stanza to point at. No copy of the Stanza is taken, just its address. + */ + Event( EventType type, const Stanza& stanza ) : m_eventType( type ), m_stanza( &stanza ) {} + + /** + * Virtual Destructor. + */ + virtual ~Event() {} + + /** + * Returns the Event's type. + * @return The Event's type. + */ + EventType eventType() const { return m_eventType; } + + /** + * Returns a pointer to a Stanza-derived object. + * @return A pointer to a Stanza that caused the event. May be 0. + * @note You should @b not delete the Stanza object. + */ + const Stanza* stanza() const { return m_stanza; } + + protected: + EventType m_eventType; + const Stanza* m_stanza; + + }; + +} + +#endif // EVENT_H__ diff --git a/libs/libgloox/eventdispatcher.cpp b/libs/libgloox/eventdispatcher.cpp new file mode 100644 index 0000000..7db22a9 --- /dev/null +++ b/libs/libgloox/eventdispatcher.cpp @@ -0,0 +1,73 @@ +/* + Copyright (c) 2008-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "eventdispatcher.h" +#include "eventhandler.h" + +namespace gloox +{ + + EventDispatcher::EventDispatcher() + { + } + + EventDispatcher::~EventDispatcher() + { + } + + void EventDispatcher::dispatch( const Event& event, const std::string& context, bool remove ) + { + typedef ContextHandlerMap::iterator Ei; + std::pair g = m_contextHandlers.equal_range( context ); + Ei it = g.first; + Ei it2; + while( it != g.second ) + { + it2 = it++; + (*it2).second->handleEvent( event ); + if( remove ) + m_contextHandlers.erase( it2 ); + } + } + + void EventDispatcher::dispatch( const Event& event ) + { + TypeHandlerMap::iterator it = m_typeHandlers.begin(); + for( ; it != m_typeHandlers.end(); ++it ) + { + if( (*it).first == event.eventType() ) + (*it).second->handleEvent( event ); + } + } + + void EventDispatcher::registerEventHandler( EventHandler* eh, const std::string& context ) + { + if( !eh || context.empty() ) + return; + + m_contextHandlers.insert( std::make_pair( context, eh ) ); + } + + void EventDispatcher::removeEventHandler( EventHandler* eh ) + { + ContextHandlerMap::iterator it = m_contextHandlers.begin(); + ContextHandlerMap::iterator it2; + while( it != m_contextHandlers.end() ) + { + it2 = it++; + if( (*it2).second == eh ) + m_contextHandlers.erase( it2 ); + } + } + +} diff --git a/libs/libgloox/eventdispatcher.h b/libs/libgloox/eventdispatcher.h new file mode 100644 index 0000000..5c530fc --- /dev/null +++ b/libs/libgloox/eventdispatcher.h @@ -0,0 +1,88 @@ +/* + Copyright (c) 2008-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef EVENTDISPATCHER_H__ +#define EVENTDISPATCHER_H__ + +#include "event.h" + +#include +#include + +namespace gloox +{ + + class EventHandler; + + /** + * @brief An Event dispatcher. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class EventDispatcher + { + + public: + /** + * Creates a new EventDispatcher object. You should not need to use this class directly. + */ + EventDispatcher(); + + /** + * Virtual Destructor. + */ + virtual ~EventDispatcher(); + + /** + * Looks for handlers for the given Event, and removes the handlers if requested. + * @param event The Event to dispatch. + * @param context An identifier that limits the EventHandlers that will get notified to + * those that are specifically interested in this context. + * @param remove Whether or not to remove the context from the list of known contexts. Useful for + * IQ IDs. + */ + void dispatch( const Event& event, const std::string& context, bool remove ); + + /** + * Looks for handlers for the given Event, identified by its type. + * @param event The event to dispatch. + */ + void dispatch( const Event& event ); + + /** + * Registers the given EventHandler to be notified about Events with the given context. + * The context will usually be an IQ ID. + * @param eh The EventHandler to register. + * @param context The context to register the EventHandler for. + */ + void registerEventHandler( EventHandler* eh, const std::string& context ); + + /** + * Removes the given EventHandler. + * @param eh The EventHandler to remove. + */ + void removeEventHandler( EventHandler* eh ); + + private: + typedef std::multimap ContextHandlerMap; + typedef std::multimap TypeHandlerMap; + + ContextHandlerMap m_contextHandlers; + TypeHandlerMap m_typeHandlers; + + }; + +} + +#endif // EVENTDISPATCHER_H__ diff --git a/libs/libgloox/eventhandler.h b/libs/libgloox/eventhandler.h new file mode 100644 index 0000000..d841cd0 --- /dev/null +++ b/libs/libgloox/eventhandler.h @@ -0,0 +1,47 @@ +/* + Copyright (c) 2008-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef EVENTHANDLER_H__ +#define EVENTHANDLER_H__ + +namespace gloox +{ + + class Event; + + /** + * @brief An base class for event handlers. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class EventHandler + { + + public: + /** + * Virtual Destructor. + */ + virtual ~EventHandler() {} + + /** + * This function gets called for Events this handler was registered for. + * @param event The Event. + */ + virtual void handleEvent( const Event& event ) = 0; + + }; + +} + +#endif // EVENTHANDLER_H__ diff --git a/libs/libgloox/featureneg.cpp b/libs/libgloox/featureneg.cpp new file mode 100644 index 0000000..628dd97 --- /dev/null +++ b/libs/libgloox/featureneg.cpp @@ -0,0 +1,60 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "featureneg.h" +#include "dataform.h" +#include "tag.h" + +namespace gloox +{ + + FeatureNeg::FeatureNeg( DataForm* form ) + : StanzaExtension( ExtFeatureNeg ), m_form( form ) + { + } + + FeatureNeg::FeatureNeg( const Tag* tag ) + : StanzaExtension( ExtFeatureNeg ), m_form( 0 ) + { + if( !tag || tag->name() != "feature" || tag->xmlns() != XMLNS_FEATURE_NEG ) + return; + + const Tag* f = tag->findTag( "feature/x[@xmlns='" + XMLNS_X_DATA + "']" ); + if( f ) + m_form = new DataForm( f ); + } + + FeatureNeg::~FeatureNeg() + { + delete m_form; + } + + const std::string& FeatureNeg::filterString() const + { + static const std::string filter = "/message/feature[@xmlns='" + XMLNS_FEATURE_NEG + "']" + "|/iq/feature[@xmlns='" + XMLNS_FEATURE_NEG + "']" ; + return filter; + } + + Tag* FeatureNeg::tag() const + { + if( !m_form ) + return 0; + + Tag* t = new Tag( "feature" ); + t->setXmlns( XMLNS_FEATURE_NEG ); + t->addChild( m_form->tag() ); + return t; + } + +} diff --git a/libs/libgloox/featureneg.h b/libs/libgloox/featureneg.h new file mode 100644 index 0000000..5dca1a5 --- /dev/null +++ b/libs/libgloox/featureneg.h @@ -0,0 +1,86 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef FEATURENEG_H__ +#define FEATURENEG_H__ + +#include "stanzaextension.h" + +#include + +namespace gloox +{ + + class DataForm; + class Tag; + + /** + * @brief An abstraction of Feature Negotiation (XEP-0020), implemented + * as a StanzaExtension. + * + * XEP Version: 1.5 + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API FeatureNeg : public StanzaExtension + { + public: + /** + * Creates a new wrapper object using the given DataForm. + * @param form The DataForm to embed. The FeatureNeg object will own the DataForm. + */ + FeatureNeg( DataForm* form ); + + /** + * Creates a new wrapper object from the given Tag. + * @param tag The Tag to parse. + */ + FeatureNeg( const Tag* tag = 0 ); + + /** + * Virtual destructor. + */ + virtual ~FeatureNeg(); + + /** + * Returns the wrapped DataForm. + * @return The wrapped DataForm. May be 0. + */ + const DataForm* form() const { return m_form; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new FeatureNeg( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new FeatureNeg( m_form ); + } + + private: + DataForm* m_form; + + }; + +} + +#endif // FEATURENEG_H__ diff --git a/libs/libgloox/flexoff.cpp b/libs/libgloox/flexoff.cpp new file mode 100644 index 0000000..9716718 --- /dev/null +++ b/libs/libgloox/flexoff.cpp @@ -0,0 +1,207 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "flexoff.h" +#include "dataform.h" +#include "disco.h" +#include "error.h" + +#include + +namespace gloox +{ + + // ---- FlexibleOffline::Offline ---- + FlexibleOffline::Offline::Offline( const Tag* /*tag*/ ) + : StanzaExtension( ExtFlexOffline ) + { + // FIXME what to do here? + } + + FlexibleOffline::Offline::Offline( int context, const StringList& msgs ) + : StanzaExtension( ExtFlexOffline ), m_context( context ), m_msgs( msgs ) + { + } + + FlexibleOffline::Offline::~Offline() + { + } + + const std::string& FlexibleOffline::Offline::filterString() const + { + static const std::string filter = "/iq/offline[@xmlns='" + XMLNS_OFFLINE + "']"; + return filter; + } + + Tag* FlexibleOffline::Offline::tag() const + { + Tag* t = new Tag( "offline" ); + t->setXmlns( XMLNS_OFFLINE ); + + if( m_msgs.empty() ) + new Tag( t, m_context == FORequestMsgs ? "fetch" : "purge" ); + else + { + const std::string action = m_context == FORequestMsgs ? "view" : "remove"; + StringList::const_iterator it = m_msgs.begin(); + for( ; it != m_msgs.end(); ++it ) + { + Tag* i = new Tag( t, "item", "action", action ); + i->addAttribute( "node", (*it) ); + } + } + return t; + } + // ---- ~FlexibleOffline::Offline ---- + + // ---- FlexibleOffline ---- + FlexibleOffline::FlexibleOffline( ClientBase* parent ) + : m_parent( parent ), m_flexibleOfflineHandler( 0 ) + { + if( m_parent ) + m_parent->registerStanzaExtension( new Offline() ); + } + + FlexibleOffline::~FlexibleOffline() + { + if( m_parent ) + m_parent->removeIDHandler( this ); + } + + void FlexibleOffline::checkSupport() + { + m_parent->disco()->getDiscoInfo( m_parent->jid().server(), EmptyString, this, FOCheckSupport ); + } + + void FlexibleOffline::getMsgCount() + { + m_parent->disco()->getDiscoInfo( m_parent->jid().server(), XMLNS_OFFLINE, this, FORequestNum ); + } + + void FlexibleOffline::fetchHeaders() + { + m_parent->disco()->getDiscoItems( m_parent->jid().server(), XMLNS_OFFLINE, this, FORequestHeaders ); + } + + void FlexibleOffline::messageOperation( int context, const StringList& msgs ) + { + const std::string& id = m_parent->getID(); + IQ::IqType iqType = context == FORequestMsgs ? IQ::Get : IQ::Set; + IQ iq( iqType, JID(), id ); + iq.addExtension( new Offline( context, msgs ) ); + m_parent->send( iq, this, context ); + } + + void FlexibleOffline::registerFlexibleOfflineHandler( FlexibleOfflineHandler* foh ) + { + m_flexibleOfflineHandler = foh; + } + + void FlexibleOffline::removeFlexibleOfflineHandler() + { + m_flexibleOfflineHandler = 0; + } + + void FlexibleOffline::handleDiscoInfo( const JID& /*from*/, const Disco::Info& info, int context ) + { + if( !m_flexibleOfflineHandler ) + return; + + switch( context ) + { + case FOCheckSupport: + m_flexibleOfflineHandler->handleFlexibleOfflineSupport( info.hasFeature( XMLNS_OFFLINE ) ); + break; + + case FORequestNum: + int num = -1; + if( info.form() && info.form()->hasField( "number_of_messages" ) ) + num = atoi( info.form()->field( "number_of_messages" )->value().c_str() ); + + m_flexibleOfflineHandler->handleFlexibleOfflineMsgNum( num ); + break; + } + } + + void FlexibleOffline::handleDiscoItems( const JID& /*from*/, const Disco::Items& items, int context ) + { + if( context == FORequestHeaders && m_flexibleOfflineHandler ) + { + if( items.node() == XMLNS_OFFLINE ) + m_flexibleOfflineHandler->handleFlexibleOfflineMessageHeaders( items.items() ); + } + } + + void FlexibleOffline::handleDiscoError( const JID& /*from*/, const Error* /*error*/, int /*context*/ ) + { + } + + void FlexibleOffline::handleIqID( const IQ& iq, int context ) + { + if( !m_flexibleOfflineHandler ) + return; + + switch( context ) + { + case FORequestMsgs: + switch( iq.subtype() ) + { + case IQ::Result: + m_flexibleOfflineHandler->handleFlexibleOfflineResult( FomrRequestSuccess ); + break; + case IQ::Error: + switch( iq.error()->error() ) + { + case StanzaErrorForbidden: + m_flexibleOfflineHandler->handleFlexibleOfflineResult( FomrForbidden ); + break; + case StanzaErrorItemNotFound: + m_flexibleOfflineHandler->handleFlexibleOfflineResult( FomrItemNotFound ); + break; + default: + m_flexibleOfflineHandler->handleFlexibleOfflineResult( FomrUnknownError ); + break; + } + break; + default: + break; + } + break; + case FORemoveMsgs: + switch( iq.subtype() ) + { + case IQ::Result: + m_flexibleOfflineHandler->handleFlexibleOfflineResult( FomrRemoveSuccess ); + break; + case IQ::Error: + switch( iq.error()->error() ) + { + case StanzaErrorForbidden: + m_flexibleOfflineHandler->handleFlexibleOfflineResult( FomrForbidden ); + break; + case StanzaErrorItemNotFound: + m_flexibleOfflineHandler->handleFlexibleOfflineResult( FomrItemNotFound ); + break; + default: + m_flexibleOfflineHandler->handleFlexibleOfflineResult( FomrUnknownError ); + break; + } + break; + default: + break; + } + break; + } + } + +} diff --git a/libs/libgloox/flexoff.h b/libs/libgloox/flexoff.h new file mode 100644 index 0000000..92db971 --- /dev/null +++ b/libs/libgloox/flexoff.h @@ -0,0 +1,178 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef FLEXOFF_H__ +#define FLEXOFF_H__ + +#include "clientbase.h" +#include "discohandler.h" +#include "flexoffhandler.h" +#include "iqhandler.h" +#include "stanzaextension.h" + +namespace gloox +{ + + /** + * @brief An implementation of XEP-0013 (Flexible Offline Message Retrieval). + * + * Use the FlexibleOfflineHandler to receive results. + * + * @author Jakob Schroeter + * @since 0.7 + */ + class GLOOX_API FlexibleOffline : public DiscoHandler, public IqHandler + { + public: + /** + * Creates a new FlexibleOffline object that manages retrieval of offline messages. + * @param parent The ClientBase to use for communication. + */ + FlexibleOffline( ClientBase* parent ); + + /** + * Virtual Destructor. + */ + virtual ~FlexibleOffline(); + + /** + * Initiates querying the server for Flexible Offline Message Retrieval-support. + * The result is announced through the FlexibleOfflineHandler. + * An application could cache the result on a per-server basis to eliminate the associated delay. + */ + void checkSupport(); + + /** + * Asks the server for the number of stored offline messages. + * The result is announced through the FlexibleOfflineHandler. + */ + void getMsgCount(); + + /** + * Initiates fetching the offline message headers. + * The result is announced through the FlexibleOfflineHandler. + */ + void fetchHeaders(); + + /** + * Initiates fetching of one or more specific messages, or all messages. + * The result is announced through the FlexibleOfflineHandler. + * If the list of message nodes contains one or more nodes, the corresponding messages are + * fetched. If the list is empty all messages are fetched (<fetch>). + * @param msgs A list of message nodes to fetch. + */ + void fetchMessages( const StringList& msgs ) + { messageOperation( FORequestMsgs, msgs ); } + + /** + * Initiates removing of one or more specific messages, or all messages. + * The result is announced through the FlexibleOfflineHandler. + * If the list of message nodes contains one or more nodes, the corresponding messages are + * removed. If the list is empty all messages are removed (<purge>). + */ + void removeMessages( const StringList& msgs ) + { messageOperation( FORemoveMsgs, msgs ); } + + /** + * Registers a FlexibleOfflineHandler as object that receives results of XEP-0013 queries. + * Only one Handler at a time is possible. + * @param foh The Handler object to register. + */ + void registerFlexibleOfflineHandler( FlexibleOfflineHandler* foh ); + + /** + * Removes the registered handler. + */ + void removeFlexibleOfflineHandler(); + + // reimplemented from DiscoHandler + virtual void handleDiscoInfo( const JID& from, const Disco::Info& info, int context ); + + // reimplemented from DiscoHandler + virtual void handleDiscoItems( const JID& from, const Disco::Items& items, int context ); + + // reimplemented from DiscoHandler + virtual void handleDiscoError( const JID& from, const Error* error, int context ); + + // reimplemented from IqHandler. + virtual bool handleIq( const IQ& iq ) { (void)iq; return false; } + + // reimplemented from IqHandler. + virtual void handleIqID( const IQ& iq, int context ); + + private: +#ifdef FLEXOFF_TEST + public: +#endif + class Offline : public StanzaExtension + { + public: + /** + * Constructs a new Offline object from the given Tag. + * @param tag The Tag to parse. + */ + Offline( const Tag* tag = 0 ); + + /** + * Constructs a new Offline object for the given context and messages. + * @param context The context. + * @param msgs The messages. + */ + Offline( int context, const StringList& msgs ); + + /** + * Virtual destructor. + */ + virtual ~Offline(); + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new Offline( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new Offline( *this ); + } + + private: + int m_context; + StringList m_msgs; + }; + + void messageOperation( int context, const StringList& msgs ); + + enum FOContext + { + FOCheckSupport, + FORequestNum, + FORequestHeaders, + FORequestMsgs, + FORemoveMsgs + }; + + ClientBase* m_parent; + FlexibleOfflineHandler* m_flexibleOfflineHandler; + }; + +} + +#endif // FLEXOFF_H__ diff --git a/libs/libgloox/flexoffhandler.h b/libs/libgloox/flexoffhandler.h new file mode 100644 index 0000000..803654d --- /dev/null +++ b/libs/libgloox/flexoffhandler.h @@ -0,0 +1,82 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef FLEXOFFHANDLER_H__ +#define FLEXOFFHANDLER_H__ + +#include "disco.h" +#include "gloox.h" + +namespace gloox +{ + + /** + * Describes the possible results of a message retrieval or deletion request. + */ + enum FlexibleOfflineResult + { + FomrRemoveSuccess, /**< Message(s) were removed successfully. */ + FomrRequestSuccess, /**< Message(s) were fetched successfully. */ + FomrForbidden, /**< The requester is a JID other than an authorized resource of the + * user. Something wnet serieously wrong */ + FomrItemNotFound, /**< The requested node (message ID) does not exist. */ + FomrUnknownError /**< An error occurred which is not specified in XEP-0013. */ + }; + + /** + * @brief Implementation of this virtual interface allows for retrieval of offline messages following + * XEP-0030. + * + * @author Jakob Schroeter + * @since 0.7 + */ + class GLOOX_API FlexibleOfflineHandler + { + public: + /** + * Virtual Destructor. + */ + virtual ~FlexibleOfflineHandler() {} + + /** + * This function is called to indicate whether the server supports XEP-0013 or not. + * Call @ref FlexibleOffline::checkSupport() to trigger the check. + * @param support Whether the server support XEP-0013 or not. + */ + virtual void handleFlexibleOfflineSupport( bool support ) = 0; + + /** + * This function is called to announce the number of available offline messages. + * Call @ref FlexibleOffline::getMsgCount() to trigger the check. + * @param num The number of stored offline messages. + */ + virtual void handleFlexibleOfflineMsgNum( int num ) = 0; + + /** + * This function is called when the offline message headers arrive. + * Call @ref FlexibleOffline::fetchHeaders() to trigger the check. + * @param headers A map of ID/sender pairs describing the offline messages. + */ + virtual void handleFlexibleOfflineMessageHeaders( const Disco::ItemList& headers ) = 0; + + /** + * This function is called to indicate the result of a fetch or delete instruction. + * @param foResult The result of the operation. + */ + virtual void handleFlexibleOfflineResult( FlexibleOfflineResult foResult ) = 0; + + }; + +} + +#endif // FLEXOFFHANDLER_H__ diff --git a/libs/libgloox/gloox.cpp b/libs/libgloox/gloox.cpp new file mode 100644 index 0000000..eb3eaa6 --- /dev/null +++ b/libs/libgloox/gloox.cpp @@ -0,0 +1,124 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "gloox.h" + +namespace gloox +{ + + const std::string XMLNS_CLIENT = "jabber:client"; + const std::string XMLNS_COMPONENT_ACCEPT = "jabber:component:accept"; + const std::string XMLNS_COMPONENT_CONNECT = "jabber:component:connect"; + + const std::string XMLNS_DISCO_INFO = "http://jabber.org/protocol/disco#info"; + const std::string XMLNS_DISCO_ITEMS = "http://jabber.org/protocol/disco#items"; + const std::string XMLNS_DISCO_PUBLISH = "http://jabber.org/protocol/disco#publish"; + const std::string XMLNS_ADHOC_COMMANDS = "http://jabber.org/protocol/commands"; + const std::string XMLNS_COMPRESSION = "http://jabber.org/protocol/compress"; + const std::string XMLNS_OFFLINE = "http://jabber.org/protocol/offline"; + + const std::string XMLNS_CHAT_STATES = "http://jabber.org/protocol/chatstates"; + const std::string XMLNS_AMP = "http://jabber.org/protocol/amp"; + const std::string XMLNS_IBB = "http://jabber.org/protocol/ibb"; + const std::string XMLNS_FEATURE_NEG = "http://jabber.org/protocol/feature-neg"; + const std::string XMLNS_CHATNEG = "http://jabber.org/protocol/chatneg"; + + const std::string XMLNS_XHTML_IM = "http://jabber.org/protocol/xhtml-im"; + const std::string XMLNS_DELAY = "urn:xmpp:delay"; + const std::string XMLNS_ROSTER = "jabber:iq:roster"; + const std::string XMLNS_VERSION = "jabber:iq:version"; + const std::string XMLNS_REGISTER = "jabber:iq:register"; + + const std::string XMLNS_PRIVACY = "jabber:iq:privacy"; + const std::string XMLNS_AUTH = "jabber:iq:auth"; + const std::string XMLNS_PRIVATE_XML = "jabber:iq:private"; + const std::string XMLNS_LAST = "jabber:iq:last"; + const std::string XMLNS_SEARCH = "jabber:iq:search"; + + const std::string XMLNS_IQ_OOB = "jabber:iq:oob"; + const std::string XMLNS_X_DATA = "jabber:x:data"; + const std::string XMLNS_X_EVENT = "jabber:x:event"; + const std::string XMLNS_X_OOB = "jabber:x:oob"; + const std::string XMLNS_X_DELAY = "jabber:x:delay"; + + const std::string XMLNS_X_GPGSIGNED = "jabber:x:signed"; + const std::string XMLNS_X_GPGENCRYPTED = "jabber:x:encrypted"; + const std::string XMLNS_VCARD_TEMP = "vcard-temp"; + const std::string XMLNS_X_VCARD_UPDATE = "vcard-temp:x:update"; + const std::string XMLNS_BOOKMARKS = "storage:bookmarks"; + + const std::string XMLNS_ANNOTATIONS = "storage:rosternotes"; + const std::string XMLNS_ROSTER_DELIMITER = "roster:delimiter"; + const std::string XMLNS_XMPP_PING = "urn:xmpp:ping"; + const std::string XMLNS_SI = "http://jabber.org/protocol/si"; + const std::string XMLNS_SI_FT = "http://jabber.org/protocol/si/profile/file-transfer"; + + const std::string XMLNS_BYTESTREAMS = "http://jabber.org/protocol/bytestreams"; + const std::string XMLNS_MUC = "http://jabber.org/protocol/muc"; + const std::string XMLNS_MUC_USER = "http://jabber.org/protocol/muc#user"; + const std::string XMLNS_MUC_ADMIN = "http://jabber.org/protocol/muc#admin"; + const std::string XMLNS_MUC_UNIQUE = "http://jabber.org/protocol/muc#unique"; + + const std::string XMLNS_MUC_OWNER = "http://jabber.org/protocol/muc#owner"; + const std::string XMLNS_MUC_ROOMINFO = "http://jabber.org/protocol/muc#roominfo"; + const std::string XMLNS_MUC_ROOMS = "http://jabber.org/protocol/muc#rooms"; + const std::string XMLNS_MUC_REQUEST = "http://jabber.org/protocol/muc#request"; + + const std::string XMLNS_PUBSUB = "http://jabber.org/protocol/pubsub"; + const std::string XMLNS_PUBSUB_ERRORS = "http://jabber.org/protocol/pubsub#errors"; + const std::string XMLNS_PUBSUB_EVENT = "http://jabber.org/protocol/pubsub#event"; + const std::string XMLNS_PUBSUB_OWNER = "http://jabber.org/protocol/pubsub#owner"; + + const std::string XMLNS_CAPS = "http://jabber.org/protocol/caps"; + const std::string XMLNS_FT_FASTMODE = "http://affinix.com/jabber/stream"; + + const std::string XMLNS_STREAM = "http://etherx.jabber.org/streams"; + const std::string XMLNS_XMPP_STREAM = "urn:ietf:params:xml:ns:xmpp-streams"; + const std::string XMLNS_XMPP_STANZAS = "urn:ietf:params:xml:ns:xmpp-stanzas"; + const std::string XMLNS_STREAM_TLS = "urn:ietf:params:xml:ns:xmpp-tls"; + const std::string XMLNS_STREAM_SASL = "urn:ietf:params:xml:ns:xmpp-sasl"; + + const std::string XMLNS_STREAM_BIND = "urn:ietf:params:xml:ns:xmpp-bind"; + const std::string XMLNS_STREAM_SESSION = "urn:ietf:params:xml:ns:xmpp-session"; + const std::string XMLNS_STREAM_IQAUTH = "http://jabber.org/features/iq-auth"; + const std::string XMLNS_STREAM_IQREGISTER = "http://jabber.org/features/iq-register"; + const std::string XMLNS_STREAM_COMPRESS = "http://jabber.org/features/compress"; + + const std::string XMLNS_HTTPBIND = "http://jabber.org/protocol/httpbind"; + const std::string XMLNS_XMPP_BOSH = "urn:xmpp:xbosh"; + const std::string XMLNS_RECEIPTS = "urn:xmpp:receipts"; + const std::string XMLNS_NICKNAME = "http://jabber.org/protocol/nick"; + + const std::string XMLNS_JINGLE = "urn:xmpp:tmp:jingle"; + const std::string XMLNS_JINGLE_AUDIO_RTP = "urn:xmpp:tmp:jingle:apps:audio-rtp"; + const std::string XMLNS_JINGLE_ICE_UDP = "urn:xmpp:tmp:jingle:transports:ice-udp"; + const std::string XMLNS_JINGLE_RAW_UDP = "urn:xmpp:tmp:jingle:transports:raw-udp"; + const std::string XMLNS_JINGLE_VIDEO_RTP = "urn:xmpp:tmp:jingle:apps:video-rtp"; + + const std::string XMLNS_SHIM = "http://jabber.org/protocol/shim"; + const std::string XMLNS_ATTENTION = "urn:xmpp:attention:0"; + + const std::string XMPP_STREAM_VERSION_MAJOR = "1"; + const std::string XMPP_STREAM_VERSION_MINOR = "0"; + const std::string GLOOX_VERSION = "1.0"; + const std::string GLOOX_CAPS_NODE = "http://camaya.net/gloox"; + + const std::string XMLNS = "xmlns"; + const std::string TYPE = "type"; + const std::string EmptyString = ""; +} + +const char* gloox_version() +{ + return gloox::GLOOX_VERSION.c_str(); +} diff --git a/libs/libgloox/gloox.h b/libs/libgloox/gloox.h new file mode 100644 index 0000000..b86462e --- /dev/null +++ b/libs/libgloox/gloox.h @@ -0,0 +1,1231 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +/*! @mainpage gloox API Documentation + * + * @section contents Contents + * @ref intro_sec
+ * @ref handlers_sec
+ * @ref comp_sec
+ * @ref client_sec
+ * @ref block_conn_sec
+ * @ref roster_sec
+ * @ref privacy_sec
+ * @ref auth_sec
+ * @ref msg_sec
+ * @ref xeps_sec
+ * @ref filetransfer_sec
+ * @ref proxy_sec
+ * @ref upgrading_sec
+ *
+ * + * @section intro_sec Introduction + * + * The design of gloox follows the so-called observer pattern, which basically means that everything is + * event-driven. There are two ways you can connect to the Jabber/XMPP network using gloox, either as + * client or as component. For a C++ XMPP server library see . + * + * @note Section 11.5 of the XMPP specification (RFC 3290) requires that only UTF-8 is used as encoding + * for any traffic sent over the wire. Since gloox cannot know which encoding is used in any given input, + * it is a requirement that any input to gloox is valid UTF-8. + * + * @section handlers_sec Event Handlers + * + * The most important tools of gloox are the event handlers. Currently, there exist 4 handlers for + * the basic protocol as defined in the RFCs, as well as numerous handlers for events generated by + * the included XEP-implementations and for additional functionality. Additionally, a log handler, + * a generic tag handler and a handler for connection events are available. + * + * Basically these handlers are virtual interfaces from which you derive a class and implement a few + * virtual functions. Then you register such an object with the respective protocol implementation. A + * short example: + * @code + * class MyClass : public PresenceHandler + * { + * public: + * // reimplemented from PresenceHandler + * virtual void handlePresence( const Presence& presence ); + * + * [...] + * }; + * + * void MyClass::handlePresence( const Presence& presence ) + * { + * // extract further information from the Presence object + * } + * @endcode + * + * Somewhere else you do something like this: + * @code + * OtherClass::doSomething() + * { + * Client* client = new Client( ... ); + * [...] + * MyClass* handler = new MyClass( ... ); + * client->registerPresenceHandler( handler ); + * } + * @endcode + * + * Now, every time a presence stanza (not subscription stanza) is received, handlePresence() is called + * with the current stanza as argument. You can then use the extensive getters of the Stanza class to + * extract stanza data. + * + * This works similar for all the other event handlers. + * Another example, this time using the connection event handler (class @link gloox::ConnectionListener + * ConnectionListener @endlink): + * @code + * class MyClass : public ConnectionListener + * { + * public: + * virtual void onConnect(); + * + * virtual bool onTLSConnect( ... ); + * }; + * + * void MyClass::onConnect() + * { + * // do something when the connection is established + * } + * + * bool MyClass::onTLSConnect( const CertInfo& info ) + * { + * // decide whether you trust the certificate, examine the CertInfo structure + * return true; // if you trust it, otherwise return false + * } + * @endcode + * + * @note The ConnectionListener interface is a peculiarity. You MUST re-implement + * @link gloox::ConnectionListener::onTLSConnect() ConnectionListener::onTLSConnect() @endlink if + * you want to be able to connect successfully to TLS/SSL enabled servers. Even though gloox tries + * to verify the server's certificate it does not automatically trust a server. The client's programmer + * and/or user have to decide whether to trust a server or not. This trust is expressed by the return + * value of onTLSConnect(). @b False means you don't trust the server/certificate and as a consequence + * the connection is dropped immediately. + * + * @section comp_sec Components + * + * A component in the Jabber/XMPP network is an add-on to a server which runs externally + * to the actual server software, but can have similar privileges. Components use a protocol described in + * XEP-0114 to connect and authenticate to a server. + * + * The @link gloox::Component Component @endlink class supports this protocol and can be used to create + * a new Jabber component. It's as simple as: + * @code + * Component* comp = new Component( ... ); + * comp->connect(); + * @endcode + * + * @section client_sec Clients + * + * A client can be an end-user's chat client, a bot, or a similar entity not tied to a particular + * server. The @link gloox::Client Client @endlink class implements the necessary functionality to + * connect to an XMPP server. Usage is, again, pretty simple: + * @code + * class MyClass : public ConnectionListener, PresenceHandler + * { + * public: + * void doSomething(); + * + * virtual void handlePresence( ... ); + * + * virtual void onConnect(); + * + * virtual bool onTLSConnect( const CertInfo& info ); + * }; + * + * void MyClass::doSomething() + * { + * JID jid( "jid@server/resource" ); + * Client* client = new Client( jid, "password" ); + * client->registerConnectionListener( this ); + * client->registerPresenceHandler( this ); + * client->connect(); + * } + * + * void MyClass::onConnect() + * { + * // connection established, auth done (see API docs for exceptions) + * } + * + * bool MyClass::onTLSConnect( const CertInfo& info ) + * { + * // examine certificate info + * } + * + * void MyClass::handlePresence( Presence* presence ) + * { + * // presence info + * } + * @endcode + * + * @note gloox does not officially support the style of connection which is usually used on port + * 5223, i.e. SSL encryption before any XML is sent, because it's a legacy method and not standard XMPP. + * However, gloox includes a ConnectionTLS class that, as a side-effect, allows you to establish such + * connections. + * + * @note @link gloox::Client::connect() Client::connect() @endlink by default blocks until the + * connection ends (either @link gloox::Client::disconnect() Client::disconnect() @endlink is called + * or the server closes the connection). + * + * @section block_conn_sec Blocking vs. Non-blocking Connections + * + * For some kind of bots a blocking connection (the default behaviour) is ideal. All the bot does is + * react to events coming from the server. However, for end user clients or anything with a GUI this + * is far from perfect. + * + * In these cases non-blocking connections can be used. If + * @link gloox::ClientBase::connect() ClientBase::connect( false ) @endlink is + * called, the function returnes immediately after the connection has been established. It is then + * the resposibility of the programmer to initiate receiving of data from the socket. + * + * The easiest way is to call @link gloox::ClientBase::recv() ClientBase::recv() @endlink + * periodically with the desired timeout (in microseconds) as parameter. The default value of -1 + * means the call blocks until any data was received, which is then parsed automatically. + * + * As an alternative to periodic polling you can get a hold of the raw file descriptor used for the + * connection. You can then use select() on it and use + * @link gloox::ClientBase::recv() ClientBase::recv() @endlink when select indicates that data is + * available. You should @b not recv() any data from the file descriptor directly as there is no + * way to feed that back into the parser. + * + * To get the file descriptor you'll need to set a connection class (e.g. an instance of + * @link gloox::ConnectionTCPClient ConnectionTCPClient @endlink) manually, like so: + * + * @code + * Client* client = new Client( ... ); + * ConnectionTCPClient* conn = new ConnectionTCPClient( client, client->logInstance(), server, port ); + * client->setConnectionImpl( conn ); + * + * client->connect( false ); + * int sock = conn->socket(); + * + * [...] + * @endcode + * + * It would also be possible to fetch the fd like this: + * + * @code + * Client* client = new Client( ... ); + * client->connect( false ); + * int sock = static_cast( client->connectionImpl() )->socket(); + * + * [...] + * @endcode + * Obviously this will only work as long as you haven't set a different type of connection using setConnectionImpl(). + * + * @note This has changed in 0.9. ClientBase::fileDescriptor() is no longer available. + * + * @section roster_sec Roster Management + * + * Among others, RFC 3921 defines the protocol to manage one's contact list (roster). In gloox, the + * @link gloox::RosterManager RosterManager @endlink class implements this functionality. A few + * easy-to-use functions are available to subscribe to or unsubscribe from the presence of remote + * entities. It is also possible to add a contact to a roster without actually subscribing to the + * contacts presence. Additionally, the interface @link gloox::RosterListener RosterListener @endlink + * offers many callbacks for various roster-related events. + * + * If you create a Client object as shown above, you also get a RosterManager for free. + * @link gloox::Client::rosterManager() Client::rosterManager() @endlink returns a pointer to the + * object. + * + * @section privacy_sec Privacy Lists + * + * Also defined in RFC 3921: Privacy Lists. A Privacy List can be used to explicitely block or allow + * sending of stanzas from and to contacts, respectively. You can define rules based on JID, stanza type, + * etc. The @link gloox::PrivacyManager PrivacyManager @endlink class and the + * @link gloox::PrivacyListHandler PrivacyListHandler @endlink virtual interface allow for full + * flexibility in Privacy List handling. + * + * @code + * PrivacyManager* p = new PrivacyManager( ... ); + * [...] + * PrivacyListHandler::PrivacyList list; + * PrivacyItem item( PrivacyItem::TypeJid, PrivacyItem::ActionDeny, + * PrivacyItem::PacketMessage, "me@there.com" ); + * list.push_back( item ); + * + * PrivacyItem item2( PrivacyItem::TypeJid, PrivacyItem::ActionAllow, + * PrivacyItem::PacketIq, "me@example.org" ); + * list.push_back( item2 ); + * + * p->store( "myList", list ); + * @endcode + * + * @section auth_sec Authentication + * + * gloox supports old-style IQ-based authentication defined in XEP-0078 as well as several SASL mechanisms. + * See the documentation of the @link gloox::Client Client @endlink class for more information. + * + * @section msg_sec Sending and Receiving of Chat Messages + * + * For Messaging it is recommended to use the MessageSession interface. It handles sending and receiving + * of messages as well as message events and chat states (such as typing notifications, etc.). See + * @link gloox::MessageSession MessageSession @endlink for more details. + * + * @section xeps_sec Protocol Extensions (XEPs) + * + * The XMPP Standards Foundation has published a number of extensions to the core protocols, called + * XMPP Extension Protocols (XEPs). A couple of these XEPs are implemented in gloox: + * + * @li XEP-0004 @link gloox::DataForm Data Forms @endlink + * @li XEP-0012 @link gloox::LastActivity Last Activity @endlink + * @li XEP-0013 @link gloox::FlexibleOffline Flexible Offline Message Retrieval @endlink + * @li XEP-0022 Message Events (see @link gloox::MessageSession MessageSession @endlink for examples) + * @li XEP-0027 Current Jabber OpenPGP Usage (see @link gloox::GPGSigned GPGSigned @endlink + * and @link gloox::GPGEncrypted GPGEncrypted @endlink) + * @li XEP-0030 @link gloox::Disco Service Discovery @endlink + * @li XEP-0045 @link gloox::MUCRoom Multi-User Chat @endlink + * @li XEP-0047 Used with @ref filetransfer_sec + * @li XEP-0048 @link gloox::BookmarkStorage Bookmark Storage @endlink + * @li XEP-0049 @link gloox::PrivateXML Private XML Storage @endlink + * @li XEP-0050 @link gloox::Adhoc Ad-hoc Commands @endlink + * @li XEP-0054 @link gloox::VCardManager vcard-temp @endlink + * @li XEP-0060 @link gloox::PubSub::Manager Publish-Subscribe @endlink + * @li XEP-0065 @link gloox::SOCKS5BytestreamManager SOCKS5 Bytestreams @endlink, used with + * @ref filetransfer_sec and @ref proxy_sec + * @li XEP-0066 @link gloox::OOB Out of Band Data @endlink, also used with @ref filetransfer_sec + * @li XEP-0077 @link gloox::Registration In-Band Registration @endlink + * @li XEP-0078 Non-SASL Authentication (automatically used if the server does not support SASL) + * @li XEP-0079 @link gloox::AMP Advanced Message Processing @endlink + * @li XEP-0083 Nested Roster Groups (automatically used if supported by the server. see + * @link gloox::RosterManager::delimiter() RosterManager @endlink) + * @li XEP-0085 Chat State Notifications (see @link gloox::MessageSession MessageSession @endlink for + * examples) + * @li XEP-0091 @link gloox::DelayedDelivery Delayed Delivery @endlink (old spec) + * @li XEP-0092 Software Version (integrated into @link gloox::Disco Service Discovery @endlink) + * @li XEP-0095 @link gloox::SIManager Stream Initiation @endlink, used with @ref filetransfer_sec + * @li XEP-0096 @ref filetransfer_sec + * @li XEP-0106 @link gloox::JID::escapeNode() JID Escaping @endlink + * @li XEP-0114 @link gloox::Component Jabber Component Protocol @endlink + * @li XEP-0115 @link gloox::Capabilities Entity Capabilities @endlink (used automatically internally) + * @li XEP-0124 @link gloox::ConnectionBOSH Bidirectional-streams Over Synchronous HTTP (BOSH) @endlink + * @li XEP-0131 @link gloox::SHIM Stanza Headers and Internet Metadata @endlink + * @li XEP-0138 Stream Compression (used automatically if gloox is compiled with zlib and if the server + * supports it) + * @li XEP-0145 @link gloox::Annotations Annotations @endlink + * @li XEP-0153 @link gloox::VCardUpdate vCard-based Avatars @endlink + * @li XEP-0172 @link gloox::Nickname User Nickname @endlink + * @li XEP-0184 @link gloox::Receipt Message Receipts @endlink + * @li XEP-0199 @link gloox::ClientBase::xmppPing() XMPP Ping @endlink + * @li XEP-0203 @link gloox::DelayedDelivery Delayed Delivery @endlink (new spec) + * @li XEP-0206 @link gloox::ConnectionBOSH see BOSH @endlink + * @li XEP-0224 @link gloox::Attention Attention @endlink + * @li XEP-0256 @link gloox::LastActivity::Query Last Activity in Presence @endlink + * + * Further extensions can easily be implemented using + * @link gloox::StanzaExtension StanzaExtensions @endlink. + * + * @section filetransfer_sec File Transfer + * + * For file transfer, gloox implements XEP-0095 (Stream Initiation) as well XEP-0096 (File Transfer) + * for the signalling, and XEP-0065 (SOCKS5 Bytestreams) as well as XEP-0047 (In-Band Bytestreams) + * for the transport. See @link gloox::SIProfileFT SIProfileFT @endlink. + * + * @section proxy_sec HTTP and SOCKS5 Proxy support + * + * gloox is capable of traversing HTTP as well as SOCKS5 proxies, even chained. See + * @link gloox::ConnectionHTTPProxy ConnectionHTTPProxy @endlink and + * @link gloox::ConnectionSOCKS5Proxy ConnectionSOCKS5Proxy @endlink. + * + * @section upgrading_sec Upgrading from earlier versions + * + * See Upgrading. + * + */ + +#ifndef GLOOX_H__ +#define GLOOX_H__ + +#include "macros.h" + +#include +#include +#include + +/** + * @brief The namespace for the gloox library. + * + * @author Jakob Schroeter + * @since 0.3 + */ +namespace gloox +{ + /** Client namespace (RFC 3920)*/ + GLOOX_API extern const std::string XMLNS_CLIENT; + + /** Component Accept namespace (XEP-0114) */ + GLOOX_API extern const std::string XMLNS_COMPONENT_ACCEPT; + + /** Component Connect namespace (XEP-0114) */ + GLOOX_API extern const std::string XMLNS_COMPONENT_CONNECT; + + /** Service Discovery Info namespace (XEP-0030) */ + GLOOX_API extern const std::string XMLNS_DISCO_INFO; + + /** Service Discovery Items namespace (XEP-0030) */ + GLOOX_API extern const std::string XMLNS_DISCO_ITEMS; + + /** Service Discovery Publish namespace (XEP-0030) */ + GLOOX_API extern const std::string XMLNS_DISCO_PUBLISH; + + /** Adhoc Commands namespace (XEP-0050) */ + GLOOX_API extern const std::string XMLNS_ADHOC_COMMANDS; + + /** Stream Compression namespace (XEP-0138) */ + GLOOX_API extern const std::string XMLNS_COMPRESSION; + + /** Flexible Offline Message Retrieval (XEP-0013) */ + GLOOX_API extern const std::string XMLNS_OFFLINE; + + /** Chat State Notifications namespace (XEP-0085) */ + GLOOX_API extern const std::string XMLNS_CHAT_STATES; + + /** Advanced Message Processing (XEP-0079) */ + GLOOX_API extern const std::string XMLNS_AMP; + + /** In-Band Bytestreams namespace (XEP-0047) */ + GLOOX_API extern const std::string XMLNS_IBB; + + /** Feature Negotiation namespace (XEP-0020) */ + GLOOX_API extern const std::string XMLNS_FEATURE_NEG; + + /** Chat Session Negotiation namespace (XEP-0155) */ + GLOOX_API extern const std::string XMLNS_CHATNEG; + + /** XHTML-IM namespace (XEP-0071) */ + GLOOX_API extern const std::string XMLNS_XHTML_IM; + + /** Delayed Delivery namespace (XEP-0203) */ + GLOOX_API extern const std::string XMLNS_DELAY; + + /** Roster namespace (RFC 3921) */ + GLOOX_API extern const std::string XMLNS_ROSTER; + + /** Software Version namespace (XEP-0092) */ + GLOOX_API extern const std::string XMLNS_VERSION; + + /** In-Band Registration namespace (XEP-0077) */ + GLOOX_API extern const std::string XMLNS_REGISTER; + + /** Privacy lists namespace (RFC 3921) */ + GLOOX_API extern const std::string XMLNS_PRIVACY; + + /** Non-SASL Authentication namespace (XEP-0078) */ + GLOOX_API extern const std::string XMLNS_AUTH; + + /** Private XML Storage namespace (XEP-0049) */ + GLOOX_API extern const std::string XMLNS_PRIVATE_XML; + + /** Last Activity namespace (XEP-0012) */ + GLOOX_API extern const std::string XMLNS_LAST; + + /** Jabber Search namespace (XEP-0055) */ + GLOOX_API extern const std::string XMLNS_SEARCH; + + /** Out of Band Data (IQ) namespace (XEP-0066) */ + GLOOX_API extern const std::string XMLNS_IQ_OOB; + + /** Data Forms namespace (XEP-0004) */ + GLOOX_API extern const std::string XMLNS_X_DATA; + + /** Message Events (XEP-0022) */ + GLOOX_API extern const std::string XMLNS_X_EVENT; + + /** Out of Band Data (X) namespace (XEP-0066) */ + GLOOX_API extern const std::string XMLNS_X_OOB; + + /** Delayed Delivery namespace (XEP-0091) */ + GLOOX_API extern const std::string XMLNS_X_DELAY; + + /** Current Jabber OpenPGP Usage (Sign.) (XEP-0027) */ + GLOOX_API extern const std::string XMLNS_X_GPGSIGNED; + + /** Current Jabber OpenPGP Usage (Enc.) (XEP-0027) */ + GLOOX_API extern const std::string XMLNS_X_GPGENCRYPTED; + + /** vcard-temp namespace (XEP-0054) */ + GLOOX_API extern const std::string XMLNS_VCARD_TEMP; + + /** vCard-Based Avatars namespace (XEP-0153) */ + GLOOX_API extern const std::string XMLNS_X_VCARD_UPDATE; + + /** Bookmark Storage namespace (XEP-0048) */ + GLOOX_API extern const std::string XMLNS_BOOKMARKS; + + /** Annotations namespace (XEP-0145) */ + GLOOX_API extern const std::string XMLNS_ANNOTATIONS; + + /** Nested Roster Groups namespace (XEP-0083) */ + GLOOX_API extern const std::string XMLNS_ROSTER_DELIMITER; + + /** XMPP Ping namespace (XEP-0199) */ + GLOOX_API extern const std::string XMLNS_XMPP_PING; + + /** Stream Initiation namespace (XEP-0095) */ + GLOOX_API extern const std::string XMLNS_SI; + + /** File transfer profile of Stream Initiation (XEP-0096) */ + GLOOX_API extern const std::string XMLNS_SI_FT; + + /** SOCKS5 Bytestreams namespace (XEP-0065) */ + GLOOX_API extern const std::string XMLNS_BYTESTREAMS; + + /** Multi-User Chat namespace (XEP-0045) */ + GLOOX_API extern const std::string XMLNS_MUC; + + /** Multi-User Chat namespace (user) (XEP-0045) */ + GLOOX_API extern const std::string XMLNS_MUC_USER; + + /** Multi-User Chat namespace (admin) (XEP-0045) */ + GLOOX_API extern const std::string XMLNS_MUC_ADMIN; + + /** Multi-User Chat namespace (unique) (XEP-0045) */ + GLOOX_API extern const std::string XMLNS_MUC_UNIQUE; + + /** Multi-User Chat namespace (owner) (XEP-0045) */ + GLOOX_API extern const std::string XMLNS_MUC_OWNER; + + /** Multi-User Chat namespace (roominfo) (XEP-0045) */ + GLOOX_API extern const std::string XMLNS_MUC_ROOMINFO; + + /** Multi-User Chat namespace (rooms) (XEP-0045) */ + GLOOX_API extern const std::string XMLNS_MUC_ROOMS; + + /** Multi-User Chat namespace (request) (XEP-0045) */ + GLOOX_API extern const std::string XMLNS_MUC_REQUEST; + + /** PubSub namespace (XEP-0060) */ + GLOOX_API extern const std::string XMLNS_PUBSUB; + + /** PubSub namespace (errors) (XEP-0060) */ + GLOOX_API extern const std::string XMLNS_PUBSUB_ERRORS; + + /** PubSub namespace (event) (XEP-0060) */ + GLOOX_API extern const std::string XMLNS_PUBSUB_EVENT; + + /** PubSub namespace (owner) (XEP-0060) */ + GLOOX_API extern const std::string XMLNS_PUBSUB_OWNER; + + /** Entity Capabilities namespace (XEP-0115) */ + GLOOX_API extern const std::string XMLNS_CAPS; + + /** SOCKS5 Fast Mode namespace */ + GLOOX_API extern const std::string XMLNS_FT_FASTMODE; + + /** XMPP stream namespace (RFC 3920) */ + GLOOX_API extern const std::string XMLNS_STREAM; + + /** XMPP stream namespace (RFC 3920) */ + GLOOX_API extern const std::string XMLNS_XMPP_STREAM; + + /** XMPP stanzas namespace (RFC 3920) */ + GLOOX_API extern const std::string XMLNS_XMPP_STANZAS; + + /** TLS Stream Feature namespace (RFC 3920) */ + GLOOX_API extern const std::string XMLNS_STREAM_TLS; + + /** SASL Stream Feature namespace (RFC 3920) */ + GLOOX_API extern const std::string XMLNS_STREAM_SASL; + + /** Resource Bind Stream Feature (RFC 3921) */ + GLOOX_API extern const std::string XMLNS_STREAM_BIND; + + /** Session Create Stream Feature (RFC 3921) */ + GLOOX_API extern const std::string XMLNS_STREAM_SESSION; + + /** Non-SASL Auth. Stream Feature (XEP-0078) */ + GLOOX_API extern const std::string XMLNS_STREAM_IQAUTH; + + /** In-Band Registration namespace (XEP-0077) */ + GLOOX_API extern const std::string XMLNS_STREAM_IQREGISTER; + + /** Stream Compression Feature namespace (XEP-0138) */ + GLOOX_API extern const std::string XMLNS_STREAM_COMPRESS; + + /** General HTTP binding (BOSH) namespace (XEP-0124) */ + GLOOX_API extern const std::string XMLNS_HTTPBIND; + + /** XMPP-over-BOSH extensions (XEP-0206) */ + GLOOX_API extern const std::string XMLNS_XMPP_BOSH; + + /** Message Receipt namespace (XEP-0184) */ + GLOOX_API extern const std::string XMLNS_RECEIPTS; + + /** Message Receipt namespace (XEP-0172) */ + GLOOX_API extern const std::string XMLNS_NICKNAME; + + /** Jingle namespace (XEP-0166) */ + GLOOX_API extern const std::string XMLNS_JINGLE; + + /** Jingle Audio via RTP namespace (XEP-0167) */ + GLOOX_API extern const std::string XMLNS_JINGLE_AUDIO_RTP; + + /** Jingle ICE-UDP Transport namespace (XEP-0176) */ + GLOOX_API extern const std::string XMLNS_JINGLE_ICE_UDP; + + /** Jingle Raw UDP Transport namespace (XEP-0177) */ + GLOOX_API extern const std::string XMLNS_JINGLE_RAW_UDP; + + /** Jingle Video via RTP namespace (XEP-0180) */ + GLOOX_API extern const std::string XMLNS_JINGLE_VIDEO_RTP; + + /** Stanza Headers and Internet Metadata (SHIM) namespace (XEP-0131) */ + GLOOX_API extern const std::string XMLNS_SHIM; + + /** Attention namespace (XEP-0224) */ + GLOOX_API extern const std::string XMLNS_ATTENTION; + + + /** Supported stream version (major). */ + GLOOX_API extern const std::string XMPP_STREAM_VERSION_MAJOR; + + /** Supported stream version (minor). */ + GLOOX_API extern const std::string XMPP_STREAM_VERSION_MINOR; + + /** gloox version */ + GLOOX_API extern const std::string GLOOX_VERSION; + + /** gloox caps node */ + GLOOX_API extern const std::string GLOOX_CAPS_NODE; + + /** A string containing "xmlns". */ + GLOOX_API extern const std::string XMLNS; + + /** A string containing "type". */ + GLOOX_API extern const std::string TYPE; + + /** An empty string. */ + GLOOX_API extern const std::string EmptyString; + + /** + * This describes the possible states of a stream. + */ + enum ConnectionState + { + StateDisconnected, /**< The client is in disconnected state. */ + StateConnecting, /**< The client is currently trying to establish a connection. */ + StateConnected /**< The client is connected to the server but authentication is not + * (yet) done. */ + }; + + /** + * Describes stream events that get emitted by means of ConnectionListener::onStreamEvent(). + * @since 0.9 + */ + enum StreamEvent + { + StreamEventConnecting, /**< The Client is about to initaite the connection. */ + StreamEventEncryption, /**< The Client is about to negotiate encryption. */ + StreamEventCompression, /**< The Client is about to negotiate compression. */ + StreamEventAuthentication, /**< The Client is about to authenticate. */ + StreamEventSessionInit, /**< The Client is about to create a session. */ + StreamEventResourceBinding, /**< The Client is about to bind a resource to the stream. */ + StreamEventSessionCreation, /**< The Client is about to create a session. + * @since 0.9.1 */ + StreamEventRoster, /**< The Client is about to request the roster. */ + StreamEventFinished /**< The log-in phase is completed. */ + }; + + /** + * This describes connection error conditions. + */ + enum ConnectionError + { + ConnNoError, /**< Not really an error. Everything went just fine. */ + ConnStreamError, /**< A stream error occured. The stream has been closed. + * Use ClientBase::streamError() to find the reason. */ + ConnStreamVersionError, /**< The incoming stream's version is not supported */ + ConnStreamClosed, /**< The stream has been closed (by the server). */ + ConnProxyAuthRequired, /**< The HTTP/SOCKS5 proxy requires authentication. + * @since 0.9 */ + ConnProxyAuthFailed, /**< HTTP/SOCKS5 proxy authentication failed. + * @since 0.9 */ + ConnProxyNoSupportedAuth, /**< The HTTP/SOCKS5 proxy requires an unsupported auth mechanism. + * @since 0.9 */ + ConnIoError, /**< An I/O error occured. */ + ConnParseError, /**< An XML parse error occurred. */ + ConnConnectionRefused, /**< The connection was refused by the server (on the socket level). + * @since 0.9 */ + ConnDnsError, /**< Resolving the server's hostname failed. + * @since 0.9 */ + ConnOutOfMemory, /**< Out of memory. Uhoh. */ + ConnNoSupportedAuth, /**< The auth mechanisms the server offers are not supported + * or the server offered no auth mechanisms at all. */ + ConnTlsFailed, /**< The server's certificate could not be verified or the TLS + * handshake did not complete successfully. */ + ConnTlsNotAvailable, /**< The server didn't offer TLS while it was set to be required, + * or TLS was not compiled in. + * @since 0.9.4 */ + ConnCompressionFailed, /**< Negotiating/initializing compression failed. + * @since 0.9 */ + ConnAuthenticationFailed, /**< Authentication failed. Username/password wrong or account does + * not exist. Use ClientBase::authError() to find the reason. */ + ConnUserDisconnected, /**< The user (or higher-level protocol) requested a disconnect. */ + ConnNotConnected /**< There is no active connection. */ + }; + + /** + * ClientBase's policy regarding TLS usage. Use with ClientBase::setTls(). + */ + enum TLSPolicy + { + TLSDisabled, /**< Don't use TLS. */ + TLSOptional, /**< Use TLS if compiled in and offered by the server. */ + TLSRequired /**< Don't attempt to log in if the server didn't offer TLS + * or if TLS was not compiled in. Disconnect error will be + * ConnTlsNotAvailable. */ + }; + + /** + * Supported Stream Features. + */ + enum StreamFeature + { + StreamFeatureBind = 1, /**< The server supports resource binding. */ + StreamFeatureUnbind = 2, /**< The server supports binding multiple resources. */ + StreamFeatureSession = 4, /**< The server supports sessions. */ + StreamFeatureStartTls = 8, /**< The server supports <starttls>. */ + StreamFeatureIqRegister = 16, /**< The server supports XEP-0077 (In-Band + * Registration). */ + StreamFeatureIqAuth = 32, /**< The server supports XEP-0078 (Non-SASL + * Authentication). */ + StreamFeatureCompressZlib = 64, /**< The server supports XEP-0138 (Stream + * Compression) (Zlib). */ + StreamFeatureCompressDclz = 128 /**< The server supports XEP-0138 (Stream + * Compression) (LZW/DCLZ). */ + // SASLMechanism below must be adjusted accordingly. + }; + + /** + * Supported SASL mechanisms. + */ + // must be adjusted with changes to StreamFeature enum above + enum SaslMechanism + { + SaslMechNone = 0, /**< Invalid SASL Mechanism. */ + SaslMechDigestMd5 = 256, /**< SASL Digest-MD5 according to RFC 2831. */ + SaslMechPlain = 512, /**< SASL PLAIN according to RFC 2595 Section 6. */ + SaslMechAnonymous = 1024, /**< SASL ANONYMOUS according to draft-ietf-sasl-anon-05.txt/ + * RFC 2245 Section 6. */ + SaslMechExternal = 2048, /**< SASL EXTERNAL according to RFC 2222 Section 7.4. */ + SaslMechGssapi = 4096, /**< SASL GSSAPI (Win32 only). */ + SaslMechNTLM = 8192, /**< SASL NTLM (Win32 only). */ + SaslMechAll = 65535 /**< Includes all supported SASL mechanisms. */ + }; + + /** + * This decribes stream error conditions as defined in RFC 3920 Sec. 4.7.3. + */ + enum StreamError + { + StreamErrorBadFormat, /**< The entity has sent XML that cannot be processed; + * this error MAY be used instead of the more specific XML-related + * errors, such as <bad-namespace-prefix/>, <invalid-xml/>, + * <restricted-xml/>, <unsupported-encoding/>, and + * <xml-not-well-formed/>, although the more specific errors are + * preferred. */ + StreamErrorBadNamespacePrefix, /**< The entity has sent a namespace prefix that is unsupported, or has + * sent no namespace prefix on an element that requires such a prefix + * (see XML Namespace Names and Prefixes (Section 11.2)). */ + StreamErrorConflict, /**< The server is closing the active stream for this entity because a + * new stream has been initiated that conflicts with the existing + * stream. */ + StreamErrorConnectionTimeout, /**< The entity has not generated any traffic over the stream for some + * period of time (configurable according to a local service policy).*/ + StreamErrorHostGone, /**< the value of the 'to' attribute provided by the initiating entity + * in the stream header corresponds to a hostname that is no longer + * hosted by the server.*/ + StreamErrorHostUnknown, /**< The value of the 'to' attribute provided by the initiating entity + * in the stream header does not correspond to a hostname that is hosted + * by the server. */ + StreamErrorImproperAddressing, /**< A stanza sent between two servers lacks a 'to' or 'from' attribute + * (or the attribute has no value). */ + StreamErrorInternalServerError, /**< the server has experienced a misconfiguration or an + * otherwise-undefined internal error that prevents it from servicing the + * stream. */ + StreamErrorInvalidFrom, /**< The JID or hostname provided in a 'from' address does not match an + * authorized JID or validated domain negotiated between servers via SASL + * or dialback, or between a client and a server via authentication and + * resource binding.*/ + StreamErrorInvalidId, /**< The stream ID or dialback ID is invalid or does not match an ID + * previously provided. */ + StreamErrorInvalidNamespace, /**< The streams namespace name is something other than + * "http://etherx.jabber.org/streams" or the dialback namespace name is + * something other than "jabber:server:dialback" (see XML Namespace Names + * and Prefixes (Section 11.2)). */ + StreamErrorInvalidXml, /**< The entity has sent invalid XML over the stream to a server that + * performs validation (see Validation (Section 11.3)). */ + StreamErrorNotAuthorized, /**< The entity has attempted to send data before the stream has been + * authenticated, or otherwise is not authorized to perform an action + * related to stream negotiation; the receiving entity MUST NOT process + * the offending stanza before sending the stream error. */ + StreamErrorPolicyViolation, /**< The entity has violated some local service policy; the server MAY + * choose to specify the policy in the <text/> element or an + * application-specific condition element. */ + StreamErrorRemoteConnectionFailed,/**< The server is unable to properly connect to a remote entity that + * is required for authentication or authorization. */ + StreamErrorResourceConstraint, /**< the server lacks the system resources necessary to service the + * stream. */ + StreamErrorRestrictedXml, /**< The entity has attempted to send restricted XML features such as a + * comment, processing instruction, DTD, entity reference, or unescaped + * character (see Restrictions (Section 11.1)). */ + StreamErrorSeeOtherHost, /**< The server will not provide service to the initiating entity but is + * redirecting traffic to another host; the server SHOULD specify the + * alternate hostname or IP address (which MUST be a valid domain + * identifier) as the XML character data of the <see-other-host/> + * element. */ + StreamErrorSystemShutdown, /**< The server is being shut down and all active streams are being + * closed. */ + StreamErrorUndefinedCondition, /**< The error condition is not one of those defined by the other + * conditions in this list; this error condition SHOULD be used only in + * conjunction with an application-specific condition. */ + StreamErrorUnsupportedEncoding, /**< The initiating entity has encoded the stream in an encoding that is + * not supported by the server (see Character Encoding (Section 11.5)). + */ + StreamErrorUnsupportedStanzaType,/**< The initiating entity has sent a first-level child of the stream + * that is not supported by the server. */ + StreamErrorUnsupportedVersion, /**< The value of the 'version' attribute provided by the initiating + * entity in the stream header specifies a version of XMPP that is not + * supported by the server; the server MAY specify the version(s) it + * supports in the <text/> element. */ + StreamErrorXmlNotWellFormed, /**< The initiating entity has sent XML that is not well-formed as + * defined by [XML]. */ + StreamErrorUndefined /**< An undefined/unknown error occured. Also used if a diconnect was + * user-initiated. Also set before and during a established connection + * (where obviously no error occured). */ + }; + + /** + * Describes types of stanza errors. + */ + enum StanzaErrorType + { + StanzaErrorTypeAuth, /**< Retry after providing credentials. */ + StanzaErrorTypeCancel, /**< Do not retry (the error is unrecoverable). */ + StanzaErrorTypeContinue, /**< Proceed (the condition was only a warning). */ + StanzaErrorTypeModify, /**< Retry after changing the data sent. */ + + StanzaErrorTypeWait, /**< Retry after waiting (the error is temporary). */ + StanzaErrorTypeUndefined /**< No error. */ + }; + + /** + * Describes the defined stanza error conditions of RFC 3920. + * Used by, eg., Stanza::error(). + */ + enum StanzaError + { + + StanzaErrorBadRequest, /**< The sender has sent XML that is malformed or that cannot be + * processed (e.g., an IQ stanza that includes an unrecognized value + * of the 'type' attribute); the associated error type SHOULD be + * "modify". */ + StanzaErrorConflict, /**< Access cannot be granted because an existing resource or session + * exists with the same name or address; the associated error type + * SHOULD be "cancel". */ + StanzaErrorFeatureNotImplemented,/**< The feature requested is not implemented by the recipient or + * server and therefore cannot be processed; the associated error + * type SHOULD be "cancel". */ + StanzaErrorForbidden, /**< The requesting entity does not possess the required permissions to + * perform the action; the associated error type SHOULD be "auth". */ + StanzaErrorGone, /**< The recipient or server can no longer be contacted at this address + * (the error stanza MAY contain a new address in the XML character data + * of the <gone/> element); the associated error type SHOULD be + * "modify". */ + StanzaErrorInternalServerError, /**< The server could not process the stanza because of a + * misconfiguration or an otherwise-undefined internal server error; the + * associated error type SHOULD be "wait". */ + StanzaErrorItemNotFound, /**< The addressed JID or item requested cannot be found; the associated + * error type SHOULD be "cancel". */ + StanzaErrorJidMalformed, /**< The sending entity has provided or communicated an XMPP address + * (e.g., a value of the 'to' attribute) or aspect thereof (e.g., a + * resource identifier) that does not adhere to the syntax defined in + * Addressing Scheme (Section 3); the associated error type SHOULD be + * "modify". */ + StanzaErrorNotAcceptable, /**< The recipient or server understands the request but is refusing to + * process it because it does not meet criteria defined by the recipient + * or server (e.g., a local policy regarding acceptable words in + * messages); the associated error type SHOULD be "modify". */ + StanzaErrorNotAllowed, /**< The recipient or server does not allow any entity to perform the + * action; the associated error type SHOULD be "cancel". */ + StanzaErrorNotAuthorized, /**< The sender must provide proper credentials before being allowed to + * perform the action, or has provided impreoper credentials; the + * associated error type should be "auth". */ + StanzaErrorNotModified, /**< The item requested has not changed since it was last requested; + * the associated error type SHOULD be "continue". */ + StanzaErrorPaymentRequired, /**< The requesting entity is not authorized to access the requested + * service because payment is required; the associated error type SHOULD + * be "auth". */ + StanzaErrorRecipientUnavailable,/**< The intended recipient is temporarily unavailable; the associated + * error type SHOULD be "wait" (note: an application MUST NOT return + * this error if doing so would provide information about the intended + * recipient's network availability to an entity that is not authorized + * to know such information). */ + StanzaErrorRedirect, /**< The recipient or server is redirecting requests for this + * information to another entity, usually temporarily (the error + * stanza SHOULD contain the alternate address, which MUST be a valid + * JID, in the XML character data of the <redirect/> element); + * the associated error type SHOULD be "modify". */ + StanzaErrorRegistrationRequired,/**< The requesting entity is not authorized to access the requested + * service because registration is required; the associated error type + * SHOULD be "auth". */ + StanzaErrorRemoteServerNotFound,/**< A remote server or service specified as part or all of the JID of + * the intended recipient does not exist; the associated error type + * SHOULD be "cancel". */ + StanzaErrorRemoteServerTimeout, /**< A remote server or service specified as part or all of the JID of + * the intended recipient (or required to fulfill a request) could not + * be contacted within a reasonable amount of time; the associated error + * type SHOULD be "wait". */ + StanzaErrorResourceConstraint, /**< The server or recipient lacks the system resources necessary to + * service the request; the associated error type SHOULD be "wait". */ + StanzaErrorServiceUnavailable, /**< The server or recipient does not currently provide the requested + * service; the associated error type SHOULD be "cancel". */ + StanzaErrorSubscribtionRequired,/**< The requesting entity is not authorized to access the requested + * service because a subscription is required; the associated error type + * SHOULD be "auth". */ + StanzaErrorUndefinedCondition, /**< The error condition is not one of those defined by the other + * conditions in this list; any error type may be associated with this + * condition, and it SHOULD be used only in conjunction with an + * application-specific condition. */ + StanzaErrorUnexpectedRequest, /**< The recipient or server understood the request but was not + * expecting it at this time (e.g., the request was out of order); + * the associated error type SHOULD be "wait". */ + StanzaErrorUnknownSender, /**< The stanza 'from' address specified by a connected client is not + * valid for the stream (e.g., the stanza does not include a 'from' + * address when multiple resources are bound to the stream); the + * associated error type SHOULD be "modify".*/ + StanzaErrorUndefined /**< No stanza error occured. */ + }; + + /** + * Describes the possible 'available presence' types. + */ +// enum Presence +// { +// PresenceUnknown, /**< Unknown status. */ +// PresenceAvailable, /**< The entity or resource is online and available. */ +// PresenceChat, /**< The entity or resource is actively interested in chatting. */ +// PresenceAway, /**< The entity or resource is temporarily away. */ +// PresenceDnd, /**< The entity or resource is busy (dnd = "Do Not Disturb"). */ +// PresenceXa, /**< The entity or resource is away for an extended period (xa = +// * "eXtended Away"). */ +// PresenceUnavailable /**< The entity or resource is offline. */ +// }; + + /** + * Describes the verification results of a certificate. + */ + enum CertStatus + { + CertOk = 0, /**< The certificate is valid and trusted. */ + CertInvalid = 1, /**< The certificate is not trusted. */ + CertSignerUnknown = 2, /**< The certificate hasn't got a known issuer. */ + CertRevoked = 4, /**< The certificate has been revoked. */ + CertExpired = 8, /**< The certificate has expired. */ + CertNotActive = 16, /**< The certifiacte is not yet active. */ + CertWrongPeer = 32, /**< The certificate has not been issued for the + * peer we're connected to. */ + CertSignerNotCa = 64 /**< The signer is not a CA. */ + }; + + /** + * Describes the certificate presented by the peer. + */ + struct CertInfo + { + int status; /**< Bitwise or'ed CertStatus or CertOK. */ + bool chain; /**< Determines whether the cert chain verified ok. */ + std::string issuer; /**< The name of the issuing entity.*/ + std::string server; /**< The server the certificate has been issued for. */ + int date_from; /**< The date from which onwards the certificate is valid + * (UNIX timestamp; UTC; not set when using OpenSSL). */ + int date_to; /**< The date up to which the certificate is valid + * (UNIX timestamp; UTC; not set when using OpenSSL). */ + std::string protocol; /**< The encryption protocol used for the connection. */ + std::string cipher; /**< The cipher used for the connection. */ + std::string mac; /**< The MAC used for the connection. */ + std::string compression; /**< The compression used for the connection. */ + }; + + /** + * Describes the defined SASL (and non-SASL) error conditions. + */ + enum AuthenticationError + { + AuthErrorUndefined, /**< No error occurred, or error condition is unknown. */ + SaslAborted, /**< The receiving entity acknowledges an <abort/> element sent + * by the initiating entity; sent in reply to the <abort/> + * element. */ + SaslIncorrectEncoding, /**< The data provided by the initiating entity could not be processed + * because the [BASE64] encoding is incorrect (e.g., because the encoding + * does not adhere to the definition in Section 3 of [BASE64]); sent in + * reply to a <response/> element or an <auth/> element with + * initial response data. */ + SaslInvalidAuthzid, /**< The authzid provided by the initiating entity is invalid, either + * because it is incorrectly formatted or because the initiating entity + * does not have permissions to authorize that ID; sent in reply to a + * <response/> element or an <auth/> element with initial + * response data.*/ + SaslInvalidMechanism, /**< The initiating entity did not provide a mechanism or requested a + * mechanism that is not supported by the receiving entity; sent in reply + * to an <auth/> element. */ + SaslMalformedRequest, /**< The request is malformed (e.g., the <auth/> element includes + * an initial response but the mechanism does not allow that); sent in + * reply to an <abort/>, <auth/>, <challenge/>, or + * <response/> element. */ + SaslMechanismTooWeak, /**< The mechanism requested by the initiating entity is weaker than + * server policy permits for that initiating entity; sent in reply to a + * <response/> element or an <auth/> element with initial + * response data. */ + SaslNotAuthorized, /**< The authentication failed because the initiating entity did not + * provide valid credentials (this includes but is not limited to the + * case of an unknown username); sent in reply to a <response/> + * element or an <auth/> element with initial response data. */ + SaslTemporaryAuthFailure, /**< The authentication failed because of a temporary error condition + * within the receiving entity; sent in reply to an <auth/> element + * or <response/> element. */ + NonSaslConflict, /**< XEP-0078: Resource Conflict */ + NonSaslNotAcceptable, /**< XEP-0078: Required Information Not Provided */ + NonSaslNotAuthorized /**< XEP-0078: Incorrect Credentials */ + }; + + /** + * Identifies log sources. + */ + enum LogArea + { + LogAreaClassParser = 0x000001, /**< Log messages from Parser. */ + LogAreaClassConnectionTCPBase = 0x000002, /**< Log messages from ConnectionTCPBase. */ + LogAreaClassClient = 0x000004, /**< Log messages from Client. */ + LogAreaClassClientbase = 0x000008, /**< Log messages from ClientBase. */ + LogAreaClassComponent = 0x000010, /**< Log messages from Component. */ + LogAreaClassDns = 0x000020, /**< Log messages from DNS. */ + LogAreaClassConnectionHTTPProxy = 0x000040, /**< Log messages from ConnectionHTTPProxy */ + LogAreaClassConnectionSOCKS5Proxy = 0x000080, /**< Log messages from ConnectionSOCKS5Proxy */ + LogAreaClassConnectionTCPClient = 0x000100, /**< Log messages from ConnectionTCPClient. */ + LogAreaClassConnectionTCPServer = 0x000200, /**< Log messages from ConnectionTCPServer. */ + LogAreaClassS5BManager = 0x000400, /**< Log messages from SOCKS5BytestreamManager. */ + LogAreaClassSOCKS5Bytestream = 0x000800, /**< Log messages from SOCKS5Bytestream. */ + LogAreaClassConnectionBOSH = 0x001000, /**< Log messages from ConnectionBOSH */ + LogAreaClassConnectionTLS = 0x002000, /**< Log messages from ConnectionTLS */ + LogAreaAllClasses = 0x01FFFF, /**< All log messages from all the classes. */ + LogAreaXmlIncoming = 0x020000, /**< Incoming XML. */ + LogAreaXmlOutgoing = 0x040000, /**< Outgoing XML. */ + LogAreaUser = 0x800000, /**< User-defined sources. */ + LogAreaAll = 0xFFFFFF /**< All log sources. */ + }; + + /** + * Describes a log message's severity. + */ + enum LogLevel + { + LogLevelDebug, /**< Debug messages. */ + LogLevelWarning, /**< Non-crititcal warning messages. */ + LogLevelError /**< Critical, unrecoverable errors. */ + }; + + /** + * The possible Message Events according to XEP-0022. + */ + enum MessageEventType + { + MessageEventOffline = 1, /**< Indicates that the message has been stored offline by the + * intended recipient's server. */ + MessageEventDelivered = 2, /**< Indicates that the message has been delivered to the + * recipient. */ + MessageEventDisplayed = 4, /**< Indicates that the message has been displayed */ + MessageEventComposing = 8, /**< Indicates that a reply is being composed. */ + MessageEventInvalid = 16, /**< Invalid type. */ + MessageEventCancel = 32 /**< Cancels the 'Composing' event. */ + }; + + /** + * The possible Chat States according to XEP-0085. + */ + enum ChatStateType + { + ChatStateActive = 1, /**< User is actively participating in the chat session. */ + ChatStateComposing = 2, /**< User is composing a message. */ + ChatStatePaused = 4, /**< User had been composing but now has stopped. */ + ChatStateInactive = 8, /**< User has not been actively participating in the chat session. */ + ChatStateGone = 16, /**< User has effectively ended their participation in the chat + * session. */ + ChatStateInvalid = 32 /**< Invalid type. */ + }; + + /** + * Describes the possible error conditions for resource binding. + */ + enum ResourceBindError + { + RbErrorUnknownError, /**< An unknown error occured. */ + RbErrorBadRequest, /**< Resource identifier cannot be processed. */ + RbErrorNotAllowed, /**< Client is not allowed to bind a resource. */ + RbErrorConflict /**< Resource identifier is in use. */ + }; + + /** + * Describes the possible error conditions for session establishemnt. + */ + enum SessionCreateError + { + ScErrorUnknownError, /**< An unknown error occured. */ + ScErrorInternalServerError, /**< Internal server error. */ + ScErrorForbidden, /**< Username or resource not allowed to create session. */ + ScErrorConflict /**< Server informs newly-requested session of resource + * conflict. */ + }; + + /** + * Currently implemented message session filters. + */ + enum MessageSessionFilter + { + FilterMessageEvents = 1, /**< Message Events (XEP-0022) */ + FilterChatStates = 2 /**< Chat State Notifications (XEP-0085) */ + }; + + /** + * Defined MUC room affiliations. See XEP-0045 for default privileges. + */ + enum MUCRoomAffiliation + { + AffiliationNone, /**< No affiliation with the room. */ + AffiliationOutcast, /**< The user has been banned from the room. */ + AffiliationMember, /**< The user is a member of the room. */ + AffiliationOwner, /**< The user is a room owner. */ + AffiliationAdmin, /**< The user is a room admin. */ + AffiliationInvalid /**< Invalid affiliation. */ + }; + + /** + * Defined MUC room roles. See XEP-0045 for default privileges. + */ + enum MUCRoomRole + { + RoleNone, /**< Not present in room. */ + RoleVisitor, /**< The user visits a room. */ + RoleParticipant, /**< The user has voice in a moderatd room. */ + RoleModerator, /**< The user is a room moderator. */ + RoleInvalid /**< Invalid role. */ + }; + + /** + * Configuration flags for a room. + */ + enum MUCRoomFlag + { + FlagPasswordProtected = 1<< 1, /**< Password-protected room. */ + FlagPublicLogging = 1<< 2, /**< Room conversation is logged. Code: 170 */ + FlagPublicLoggingOff = 1<< 3, /**< Room conversation is not logged. Code: 171 */ + FlagHidden = 1<< 4, /**< Hidden room. */ + FlagMembersOnly = 1<< 5, /**< Members-only room. */ + FlagModerated = 1<< 6, /**< Moderated room. */ + FlagNonAnonymous = 1<< 7, /**< Non-anonymous room. Code: 100, 172 */ + FlagOpen = 1<< 8, /**< Open room. */ + FlagPersistent = 1<< 9, /**< Persistent room .*/ + FlagPublic = 1<<10, /**< Public room. */ + FlagSemiAnonymous = 1<<11, /**< Semi-anonymous room. Code: 173 */ + FlagTemporary = 1<<12, /**< Temporary room. */ + FlagUnmoderated = 1<<13, /**< Unmoderated room. */ + FlagUnsecured = 1<<14, /**< Unsecured room. */ + FlagFullyAnonymous = 1<<15 /**< Fully anonymous room. Code: 174 */ + // keep in sync with MUCUserFlag below + }; + + /** + * Configuration flags for a user. + */ + // keep in sync with MUCRoomFlag above + enum MUCUserFlag + { + UserSelf = 1<<16, /**< Other flags relate to the current user him/herself. Code: 110 */ + UserNickChanged = 1<<17, /**< The user changed his/her nickname. Code: 303 */ + UserKicked = 1<<18, /**< The user has been kicked. Code: 307 */ + UserBanned = 1<<19, /**< The user has been banned. Code: 301 */ + UserAffiliationChanged = 1<<20, /**< The user's affiliation with the room changed and as a result + * he/she has been removed from the room. Code: 321 */ + UserRoomDestroyed = 1<<21, /**< The room has been destroyed. */ + UserNickAssigned = 1<<22, /**< Service has assigned or modified occupant's roomnick. + * Code: 210*/ + UserNewRoom = 1<<23, /**< The room has been newly created. Code: 201*/ + UserMembershipRequired = 1<<24, /**< User is being removed from the room because the room has + * been changed to members-only and the user is not a member. + * Code: 322 */ + UserRoomShutdown = 1<<25, /**< User is being removed from the room because of a system + * shutdown. Code: 332 */ + UserAffiliationChangedWNR = 1<<26 /**< The user's affiliation changed While Not in the Room. + * Code: 101 */ + }; + + /** + * Describes possible subscription types according to RFC 3921, Section 9. + */ + enum SubscriptionType + { + S10nNone, /**< Contact and user are not subscribed to each other, and + * neither has requested a subscription from the other. */ + S10nNoneOut, /**< Contact and user are not subscribed to each other, and + * user has sent contact a subscription request but contact + * has not replied yet. */ + S10nNoneIn, /**< Contact and user are not subscribed to each other, and + * contact has sent user a subscription request but user has + * not replied yet (note: contact's server SHOULD NOT push or + * deliver roster items in this state, but instead SHOULD wait + * until contact has approved subscription request from user). */ + S10nNoneOutIn, /**< Contact and user are not subscribed to each other, contact + * has sent user a subscription request but user has not replied + * yet, and user has sent contact a subscription request but + * contact has not replied yet. */ + S10nTo, /**< User is subscribed to contact (one-way). */ + S10nToIn, /**< User is subscribed to contact, and contact has sent user a + * subscription request but user has not replied yet. */ + S10nFrom, /**< Contact is subscribed to user (one-way). */ + S10nFromOut, /**< Contact is subscribed to user, and user has sent contact a + * subscription request but contact has not replied yet. */ + S10nBoth /**< User and contact are subscribed to each other (two-way). */ + }; + + /** + * A list of strings. + */ + typedef std::list StringList; + + /** + * A list of pointers to strings. + */ + typedef std::list StringPList; + + /** + * A map of strings. + */ + typedef std::map StringMap; + + /** + * A multimap of strings. + */ + typedef std::multimap StringMultiMap; + + class StanzaExtension; + /** + * A list of StanzaExtensions. + */ + typedef std::list StanzaExtensionList; +} + +extern "C" +{ + GLOOX_API const char* gloox_version(); +} + +#endif // GLOOX_H__ diff --git a/libs/libgloox/glooxversion.h b/libs/libgloox/glooxversion.h new file mode 100644 index 0000000..9b4b282 --- /dev/null +++ b/libs/libgloox/glooxversion.h @@ -0,0 +1,13 @@ +/* + Copyright (c) 2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#define GLOOXVERSION 0x010000 diff --git a/libs/libgloox/gpgencrypted.cpp b/libs/libgloox/gpgencrypted.cpp new file mode 100644 index 0000000..95e1ab0 --- /dev/null +++ b/libs/libgloox/gpgencrypted.cpp @@ -0,0 +1,60 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "gpgencrypted.h" +#include "tag.h" + +namespace gloox +{ + + GPGEncrypted::GPGEncrypted( const std::string& encrypted ) + : StanzaExtension( ExtGPGEncrypted ), + m_encrypted( encrypted ), m_valid( true ) + { + if( m_encrypted.empty() ) + m_valid = false; + } + + GPGEncrypted::GPGEncrypted( const Tag* tag ) + : StanzaExtension( ExtGPGEncrypted ), + m_valid( false ) + { + if( tag && tag->name() == "x" && tag->hasAttribute( XMLNS, XMLNS_X_GPGENCRYPTED ) ) + { + m_valid = true; + m_encrypted = tag->cdata(); + } + } + + GPGEncrypted::~GPGEncrypted() + { + } + + const std::string& GPGEncrypted::filterString() const + { + static const std::string filter = "/message/x[@xmlns='" + XMLNS_X_GPGENCRYPTED + "']"; + return filter; + } + + Tag* GPGEncrypted::tag() const + { + if( !m_valid ) + return 0; + + Tag* x = new Tag( "x", m_encrypted ); + x->addAttribute( XMLNS, XMLNS_X_GPGENCRYPTED ); + + return x; + } + +} diff --git a/libs/libgloox/gpgencrypted.h b/libs/libgloox/gpgencrypted.h new file mode 100644 index 0000000..f6e16df --- /dev/null +++ b/libs/libgloox/gpgencrypted.h @@ -0,0 +1,91 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef GPGENCRYPTED_H__ +#define GPGENCRYPTED_H__ + +#include "gloox.h" +#include "stanzaextension.h" + +#include + +namespace gloox +{ + + class Tag; + + /** + * @brief This is an abstraction of a jabber:x:encrypted namespace element, as used in XEP-0027 + * (Current Jabber OpenPGP Usage). + * + * This class does not encrypt or decrypt any stanza content. It's meant to be an abstraction + * of the XML representation only. + * + * XEP version: 1.3 + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API GPGEncrypted : public StanzaExtension + { + public: + /** + * Constructs a new object with the given encrypted message. + * @param encrypted The encrypted message. + */ + GPGEncrypted( const std::string& encrypted ); + + /** + * Constructs an GPGEncrypted object from the given Tag. To be recognized properly, the Tag should + * have a name of 'x' in the @c jabber:x:encrypted namespace. + * @param tag The Tag to parse. + */ + GPGEncrypted( const Tag* tag ); + + /** + * Virtual destructor. + */ + virtual ~GPGEncrypted(); + + /** + * Returns the encrypted message. + * @return The encrypted message. + */ + const std::string& encrypted() const { return m_encrypted; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new GPGEncrypted( tag ); + } + + // reimplemented from StanzaExtension + Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new GPGEncrypted( *this ); + } + + private: + std::string m_encrypted; + bool m_valid; + + }; + +} + +#endif // GPGENCRYPTED_H__ diff --git a/libs/libgloox/gpgsigned.cpp b/libs/libgloox/gpgsigned.cpp new file mode 100644 index 0000000..bcc40ce --- /dev/null +++ b/libs/libgloox/gpgsigned.cpp @@ -0,0 +1,62 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "gpgsigned.h" +#include "tag.h" + +namespace gloox +{ + + GPGSigned::GPGSigned( const std::string& signature ) + : StanzaExtension( ExtGPGSigned ), + m_signature( signature ), m_valid( true ) + { + if( m_signature.empty() ) + m_valid = false; + } + + GPGSigned::GPGSigned( const Tag* tag ) + : StanzaExtension( ExtGPGSigned ), + m_valid( false ) + { + if( tag && tag->name() == "x" && tag->hasAttribute( XMLNS, XMLNS_X_GPGSIGNED ) ) + { + m_valid = true; + m_signature = tag->cdata(); + } + } + + GPGSigned::~GPGSigned() + { + } + + const std::string& GPGSigned::filterString() const + { + static const std::string filter = + "/presence/x[@xmlns='" + XMLNS_X_GPGSIGNED + "']" + "|/message/x[@xmlns='" + XMLNS_X_GPGSIGNED + "']"; + return filter; + } + + Tag* GPGSigned::tag() const + { + if( !m_valid ) + return 0; + + Tag* x = new Tag( "x", m_signature ); + x->addAttribute( XMLNS, XMLNS_X_GPGSIGNED ); + + return x; + } + +} diff --git a/libs/libgloox/gpgsigned.h b/libs/libgloox/gpgsigned.h new file mode 100644 index 0000000..93eb7eb --- /dev/null +++ b/libs/libgloox/gpgsigned.h @@ -0,0 +1,91 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef GPGSIGNED_H__ +#define GPGSIGNED_H__ + +#include "gloox.h" +#include "stanzaextension.h" + +#include + +namespace gloox +{ + + class Tag; + + /** + * @brief This is an abstraction of a jabber:x:signed namespace element, as used in XEP-0027 + * (Current Jabber OpenPGP Usage). + * + * This class does not sign or verify any stanza content. It's meant to be an abstraction + * of the XML representation only. + * + * XEP version: 1.3 + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API GPGSigned : public StanzaExtension + { + public: + /** + * Constructs a new object with the given signature. + * @param signature The signature. + */ + GPGSigned( const std::string& signature ); + + /** + * Constructs an GPGSigned object from the given Tag. To be recognized properly, the Tag should + * have a name of 'x' in the @c jabber:x:signed namespace. + * @param tag The Tag to parse. + */ + GPGSigned( const Tag* tag ); + + /** + * Virtual destructor. + */ + virtual ~GPGSigned(); + + /** + * Returns the signature. + * @return The signature. + */ + const std::string& signature() const { return m_signature; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new GPGSigned( tag ); + } + + // reimplemented from StanzaExtension + Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new GPGSigned( *this ); + } + + private: + std::string m_signature; + bool m_valid; + + }; + +} + +#endif // GPGSIGNED_H__ diff --git a/libs/libgloox/inbandbytestream.cpp b/libs/libgloox/inbandbytestream.cpp new file mode 100644 index 0000000..cf6254d --- /dev/null +++ b/libs/libgloox/inbandbytestream.cpp @@ -0,0 +1,297 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "inbandbytestream.h" +#include "base64.h" +#include "bytestreamdatahandler.h" +#include "disco.h" +#include "clientbase.h" +#include "error.h" +#include "message.h" +#include "util.h" + +#include + +namespace gloox +{ + + // ---- InBandBytestream::IBB ---- + static const char* typeValues[] = + { + "open", "data", "close" + }; + + InBandBytestream::IBB::IBB( const std::string& sid, int blocksize ) + : StanzaExtension( ExtIBB ), m_sid ( sid ), m_seq( 0 ), m_blockSize( blocksize ), + m_type( IBBOpen ) + { + } + + InBandBytestream::IBB::IBB( const std::string& sid, int seq, const std::string& data ) + : StanzaExtension( ExtIBB ), m_sid ( sid ), m_seq( seq ), m_blockSize( 0 ), + m_data( data ), m_type( IBBData ) + { + } + + InBandBytestream::IBB::IBB( const std::string& sid ) + : StanzaExtension( ExtIBB ), m_sid ( sid ), m_seq( 0 ), m_blockSize( 0 ), + m_type( IBBClose ) + { + } + + InBandBytestream::IBB::IBB( const Tag* tag ) + : StanzaExtension( ExtIBB ), m_type( IBBInvalid ) + { + if( !tag || tag->xmlns() != XMLNS_IBB ) + return; + + m_type = (IBBType)util::lookup( tag->name(), typeValues ); + m_blockSize = atoi( tag->findAttribute( "block-size" ).c_str() ); + m_seq = atoi( tag->findAttribute( "seq" ).c_str() ); + m_sid = tag->findAttribute( "sid" ); + m_data = Base64::decode64( tag->cdata() ); + } + + InBandBytestream::IBB::~IBB() + { + } + + const std::string& InBandBytestream::IBB::filterString() const + { + static const std::string filter = "/iq/open[@xmlns='" + XMLNS_IBB + "']" + "|/iq/data[@xmlns='" + XMLNS_IBB + "']" + "|/message/data[@xmlns='" + XMLNS_IBB + "']" + "|/iq/close[@xmlns='" + XMLNS_IBB + "']"; + return filter; + } + + Tag* InBandBytestream::IBB::tag() const + { + if( m_type == IBBInvalid ) + return 0; + + Tag* t = new Tag( util::lookup( m_type, typeValues ) ); + t->setXmlns( XMLNS_IBB ); + t->addAttribute( "sid", m_sid ); + if( m_type == IBBData ) + { + t->setCData( Base64::encode64( m_data ) ); + t->addAttribute( "seq", m_seq ); + } + else if( m_type == IBBOpen ) + t->addAttribute( "block-size", m_blockSize ); + + return t; + } + // ---- ~InBandBytestream::IBB ---- + + // ---- InBandBytestream ---- + InBandBytestream::InBandBytestream( ClientBase* clientbase, LogSink& logInstance, const JID& initiator, + const JID& target, const std::string& sid ) + : Bytestream( Bytestream::IBB, logInstance, initiator, target, sid ), + m_clientbase( clientbase ), m_blockSize( 4096 ), m_sequence( -1 ), m_lastChunkReceived( -1 ) + { + if( m_clientbase ) + { + m_clientbase->registerStanzaExtension( new IBB() ); + m_clientbase->registerIqHandler( this, ExtIBB ); + m_clientbase->registerMessageHandler( this ); + } + + m_open = false; + } + + InBandBytestream::~InBandBytestream() + { + if( m_open ) + close(); + + if( m_clientbase ) + { + m_clientbase->removeMessageHandler( this ); + m_clientbase->removeIqHandler( this, ExtIBB ); + m_clientbase->removeIDHandler( this ); + } + } + + bool InBandBytestream::connect() + { + if( !m_clientbase ) + return false; + + if( m_target == m_clientbase->jid() ) + return true; + + const std::string& id = m_clientbase->getID(); + IQ iq( IQ::Set, m_target, id ); + iq.addExtension( new IBB( m_sid, m_blockSize ) ); + m_clientbase->send( iq, this, IBBOpen ); + return true; + } + + void InBandBytestream::handleIqID( const IQ& iq, int context ) + { + switch( iq.subtype() ) + { + case IQ::Result: + if( context == IBBOpen && m_handler ) + { + m_handler->handleBytestreamOpen( this ); + m_open = true; + } + break; + case IQ::Error: + closed(); + break; + default: + break; + } + } + + bool InBandBytestream::handleIq( const IQ& iq ) // data or open request, always 'set' + { + const IBB* i = iq.findExtension( ExtIBB ); + if( !i || !m_handler || iq.subtype() != IQ::Set ) + return false; + + if( !m_open ) + { + if( i->type() == IBBOpen ) + { + returnResult( iq.from(), iq.id() ); + m_open = true; + m_handler->handleBytestreamOpen( this ); + return true; + } + return false; + } + + if( i->type() == IBBClose ) + { + returnResult( iq.from(), iq.id() ); + closed(); + return true; + } + + if( ( m_lastChunkReceived + 1 ) != i->seq() ) + { + m_open = false; + returnError( iq.from(), iq.id(), StanzaErrorTypeModify, StanzaErrorItemNotFound ); + return false; + } + + if( i->data().empty() ) + { + m_open = false; + returnError( iq.from(), iq.id(), StanzaErrorTypeModify, StanzaErrorBadRequest ); + return false; + } + + returnResult( iq.from(), iq.id() ); + m_handler->handleBytestreamData( this, i->data() ); + m_lastChunkReceived++; + return true; + } + + void InBandBytestream::handleMessage( const Message& msg, MessageSession* /*session*/ ) + { + if( msg.from() != m_target || !m_handler ) + return; + + const IBB* i = msg.findExtension( ExtIBB ); + if( !i ) + return; + + if( !m_open ) + return; + + if( m_lastChunkReceived != i->seq() ) + { + m_open = false; + return; + } + + if( i->data().empty() ) + { + m_open = false; + return; + } + + m_handler->handleBytestreamData( this, i->data() ); + m_lastChunkReceived++; + } + + void InBandBytestream::returnResult( const JID& to, const std::string& id ) + { + IQ iq( IQ::Result, to, id ); + m_clientbase->send( iq ); + } + + void InBandBytestream::returnError( const JID& to, const std::string& id, StanzaErrorType type, StanzaError error ) + { + IQ iq( IQ::Error, to, id ); + iq.addExtension( new Error( type, error ) ); + m_clientbase->send( iq ); + } + + bool InBandBytestream::send( const std::string& data ) + { + if( !m_open || !m_clientbase ) + return false; + + size_t pos = 0; + size_t len = data.length(); + do + { + const std::string& id = m_clientbase->getID(); + IQ iq( IQ::Set, m_target, id ); + iq.addExtension( new IBB( m_sid, ++m_sequence, data.substr( pos, m_blockSize ) ) ); + m_clientbase->send( iq, this, IBBData ); + + pos += m_blockSize; + if( m_sequence == 65535 ) + m_sequence = -1; + } + while( pos < len ); + + return true; + } + + void InBandBytestream::closed() + { + if( !m_open ) + return; + + m_open = false; + + if( m_handler ) + m_handler->handleBytestreamClose( this ); + } + + void InBandBytestream::close() + { + m_open = false; + + if( !m_clientbase ) + return; + + const std::string& id = m_clientbase->getID(); + IQ iq( IQ::Set, m_target, id ); + iq.addExtension( new IBB( m_sid ) ); + m_clientbase->send( iq, this, IBBClose ); + + if( m_handler ) + m_handler->handleBytestreamClose( this ); + } + +} diff --git a/libs/libgloox/inbandbytestream.h b/libs/libgloox/inbandbytestream.h new file mode 100644 index 0000000..9a6267a --- /dev/null +++ b/libs/libgloox/inbandbytestream.h @@ -0,0 +1,214 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef INBANDBYTESTREAM_H__ +#define INBANDBYTESTREAM_H__ + +#include "bytestream.h" +#include "iqhandler.h" +#include "messagehandler.h" +#include "gloox.h" + +namespace gloox +{ + + class BytestreamDataHandler; + class ClientBase; + class Message; + + /** + * @brief An implementation of a single In-Band Bytestream (XEP-0047). + * + * One instance of this class handles a single byte stream. + * + * See SIProfileFT for a detailed description on how to implement file transfer. + * + * @note This class can @b receive data wrapped in Message stanzas. This will only work if you + * are not using MessageSessions. However, it will always send + * data using IQ stanzas (which will always work). + * + * @author Jakob Schroeter + * @since 0.8 + */ + class GLOOX_API InBandBytestream : public Bytestream, public IqHandler, public MessageHandler + { + + friend class SIProfileFT; + + public: + /** + * Virtual destructor. + */ + virtual ~InBandBytestream(); + + /** + * Lets you retrieve this bytestream's block-size. + * @return The bytestream's block-size. + */ + int blockSize() const { return m_blockSize; } + + /** + * Sets the stream's block-size. Default: 4096 + * @param blockSize The new block size. + * @note You should not change the block size once connect() has been called. + */ + void setBlockSize( int blockSize ) { m_blockSize = blockSize; } + + // reimplemented from Bytestream + virtual ConnectionError recv( int timeout = -1 ) { (void)timeout; return ConnNoError; } + + // reimplemented from Bytestream + bool send( const std::string& data ); + + // reimplemented from Bytestream + virtual bool connect(); + + // reimplemented from Bytestream + virtual void close(); + + // reimplemented from IqHandler + virtual bool handleIq( const IQ& iq ); + + // reimplemented from IqHandler + virtual void handleIqID( const IQ& iq, int context ); + + // reimplemented from MessageHandler + virtual void handleMessage( const Message& msg, MessageSession* session = 0 ); + + private: +#ifdef INBANDBYTESTREAM_TEST + public: +#endif + enum IBBType + { + IBBOpen, + IBBData, + IBBClose, + IBBInvalid + }; + + /** + * @brief An abstraction of IBB elements, implemented as as StanzaExtension. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class IBB : public StanzaExtension + { + public: + /** + * Constructs a new IBB object that opens an IBB, using the given SID and block size. + * @param sid The SID of the IBB to open. + * @param blocksize The streams block size. + */ + IBB( const std::string& sid, int blocksize ); + + /** + * Constructs a new IBB object that can be used to send a single block of data, + * using the given SID and sequence number. + * @param sid The SID of the IBB. + * @param seq The block's sequence number. + * @param data The block data, not base64 encoded. + */ + IBB( const std::string& sid, int seq, const std::string& data ); + + /** + * Constructs a new IBB object that closes an IBB, using the given SID. + * @param sid The SID of the IBB to close. + */ + IBB( const std::string& sid ); + + /** + * Constructs a new IBB object from the given Tag. + * @param tag The Tag to parse. + */ + IBB( const Tag* tag = 0 ); + + /** + * Virtual destructor. + */ + virtual ~IBB(); + + /** + * Returns the IBB's type. + * @return The IBB's type. + */ + IBBType type() const { return m_type; } + + /** + * Returns the IBB's block size. Only meaningful if the IBB is of type() IBBOpen. + * @return The IBB's block size. + */ + int blocksize() const { return m_blockSize; } + + /** + * Returns the current block's sequence number. + * @return The current block's sequence number. + */ + int seq() const { return m_seq; } + + /** + * Returns the current block's SID. + * @return The current block's SID. + */ + const std::string sid() const { return m_sid; } + + /** + * Returns the current block's data (not base64 encoded). + * @return The current block's data. + */ + const std::string& data() const { return m_data; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new IBB( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new IBB( *this ); + } + + private: + std::string m_sid; + int m_seq; + int m_blockSize; + std::string m_data; + IBBType m_type; + }; + + InBandBytestream( ClientBase* clientbase, LogSink& logInstance, const JID& initiator, + const JID& target, const std::string& sid ); + InBandBytestream& operator=( const InBandBytestream& ); + void closed(); // by remote entity + void returnResult( const JID& to, const std::string& id ); + void returnError( const JID& to, const std::string& id, StanzaErrorType type, StanzaError error ); + + ClientBase* m_clientbase; + int m_blockSize; + int m_sequence; + int m_lastChunkReceived; + + }; + +} + +#endif // INBANDBYTESTREAM_H__ diff --git a/libs/libgloox/instantmucroom.cpp b/libs/libgloox/instantmucroom.cpp new file mode 100644 index 0000000..3303a2b --- /dev/null +++ b/libs/libgloox/instantmucroom.cpp @@ -0,0 +1,31 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "instantmucroom.h" +#include "clientbase.h" +#include "jid.h" + +namespace gloox +{ + + InstantMUCRoom::InstantMUCRoom( ClientBase* parent, const JID& nick, MUCRoomHandler* mrh ) + : MUCRoom( parent, nick, mrh, 0 ) + { + } + + InstantMUCRoom::~InstantMUCRoom() + { + } + +} diff --git a/libs/libgloox/instantmucroom.h b/libs/libgloox/instantmucroom.h new file mode 100644 index 0000000..88462ba --- /dev/null +++ b/libs/libgloox/instantmucroom.h @@ -0,0 +1,60 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef INSTANTMUCROOM_H__ +#define INSTANTMUCROOM_H__ + +#include "mucroom.h" + +namespace gloox +{ + + /** + * @brief This class implements an instant MUC room. + * + * XEP version: 1.21 + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API InstantMUCRoom : public MUCRoom + { + public: + /** + * Creates a new abstraction of a @b unique Multi-User Chat room. The room is not joined + * automatically. Use join() to join the room, use leave() to leave it. See MUCRoom for + * detailed info. + * @param parent The ClientBase object to use for the communication. + * @param nick The room's name and service plus the desired nickname in the form + * room\@service/nick. + * @param mrh The MUCRoomHandler that will listen to room events. May be 0 and may be specified + * later using registerMUCRoomHandler(). However, without one, MUC is no joy. + * @note To subsequently configure the room, use MUCRoom::registerMUCRoomConfigHandler(). + */ + InstantMUCRoom( ClientBase* parent, const JID& nick, MUCRoomHandler* mrh ); + + /** + * Virtual Destructor. + */ + virtual ~InstantMUCRoom(); + + protected: + // reimplemented from MUCRoom (acknowledges instant room creation w/o a + // call to the MUCRoomConfigHandler) + virtual bool instantRoomHook() const { return true; } + + }; + +} + +#endif // INSTANTMUCROOM_H__ diff --git a/libs/libgloox/iq.cpp b/libs/libgloox/iq.cpp new file mode 100644 index 0000000..e33e971 --- /dev/null +++ b/libs/libgloox/iq.cpp @@ -0,0 +1,69 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#include "iq.h" +#include "util.h" + +namespace gloox +{ + + static const char * iqTypeStringValues[] = + { + "get", "set", "result", "error" + }; + + static inline const char* typeString( IQ::IqType type ) + { + return iqTypeStringValues[type]; + } + + IQ::IQ( Tag* tag ) + : Stanza( tag ), m_subtype( Invalid ) + { + if( !tag || tag->name() != "iq" ) + return; + + m_subtype = (IQ::IqType)util::lookup( tag->findAttribute( TYPE ), iqTypeStringValues ); + } + + IQ::IQ( IqType type, const JID& to, const std::string& id ) + : Stanza( to ), m_subtype( type ) + { + m_id = id; + } + + IQ::~IQ() + { + } + + Tag* IQ::tag() const + { + if( m_subtype == Invalid ) + return 0; + + Tag* t = new Tag( "iq" ); + if( m_to ) + t->addAttribute( "to", m_to.full() ); + if( m_from ) + t->addAttribute( "from", m_from.full() ); + if( !m_id.empty() ) + t->addAttribute( "id", m_id ); + t->addAttribute( TYPE, typeString( m_subtype ) ); + + StanzaExtensionList::const_iterator it = m_extensionList.begin(); + for( ; it != m_extensionList.end(); ++it ) + t->addChild( (*it)->tag() ); + + return t; + } + +} diff --git a/libs/libgloox/iq.h b/libs/libgloox/iq.h new file mode 100644 index 0000000..7270531 --- /dev/null +++ b/libs/libgloox/iq.h @@ -0,0 +1,96 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#ifndef IQ_H__ +#define IQ_H__ + +#include "stanza.h" +#include "gloox.h" + +#include + +namespace gloox +{ + + class JID; + + /** + * @brief An abstraction of an IQ stanza. + * + * @author Vincent Thomasset + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API IQ : public Stanza + { + + friend class ClientBase; + + public: + + /** + * Describes the different valid IQ types. + */ + enum IqType + { + Get = 0, /**< The stanza is a request for information or requirements. */ + Set, /**< The stanza provides required data, sets new values, or + * replaces existing values. */ + Result, /**< The stanza is a response to a successful get or set request. */ + Error, /**< An error has occurred regarding processing or delivery of a + * previously-sent get or set (see Stanza Errors (Section 9.3)). */ + Invalid /**< The stanza is invalid */ + }; + + /** + * Creates an IQ Query. + * @param type The desired IqType. + * @param to The intended receiver. + * @param id The request's ID. Usually obtained from ClientBase::getID(). Optional, + * will be added by ClientBase if the IQ is sent by means of + * @link gloox::ClientBase::send( IQ&, IqHandler*, int, bool ) send( IQ&, IqHandler*, int, bool ) @endlink. + * You should only need to pass this when creating a reply (i.e. an IQ of type Result or Error). + */ + IQ( IqType type, const JID& to, const std::string& id = EmptyString ); + + /** + * Virtual destructor. + */ + virtual ~IQ(); + + /** + * Returns the IQ's type. + * @return The IQ's type. + */ + IqType subtype() const { return m_subtype; } + + // reimplemented from Stanza + virtual Tag* tag() const; + + private: +#ifdef IQ_TEST + public: +#endif + /** + * Creates an IQ from a tag. The original Tag will be ripped off. + * @param tag The Tag to parse. + */ + IQ( Tag* tag ); + + void setID( const std::string& id ) { m_id = id; } + + IqType m_subtype; + }; + +} + +#endif // IQ_H__ diff --git a/libs/libgloox/iqhandler.h b/libs/libgloox/iqhandler.h new file mode 100644 index 0000000..809e36f --- /dev/null +++ b/libs/libgloox/iqhandler.h @@ -0,0 +1,66 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef IQHANDLER_H__ +#define IQHANDLER_H__ + +#include "iq.h" + +namespace gloox +{ + + /** + * @brief A virtual interface which can be reimplemented to receive IQ stanzas. + * + * Derived classes can be registered as IqHandlers with the Client. + * Upon an incoming IQ packet @ref handleIq() will be called. + * + * @author Jakob Schroeter + */ + class GLOOX_API IqHandler + { + public: + /** + * Virtual Destructor. + */ + virtual ~IqHandler() {} + + /** + * Reimplement this function if you want to be notified about incoming IQs. + * @param iq The complete IQ stanza. + * @return Indicates whether a request of type 'get' or 'set' has been handled. This includes + * the obligatory 'result' answer. If you return @b false, a 'error' will be sent. + * @since 1.0 + */ + virtual bool handleIq( const IQ& iq ) = 0; + + /** + * Reimplement this function if you want to be notified about + * incoming IQs with a specific value of the @c id attribute. You + * have to enable tracking of those IDs using Client::trackID(). + * This is usually useful for IDs that generate a positive reply, i.e. + * <iq type='result' id='reg'/> where a namespace filter wouldn't + * work. + * @param iq The complete IQ stanza. + * @param context A value to restore context, stored with ClientBase::trackID(). + * @note Only IQ stanzas of type 'result' or 'error' can arrive here. + * @since 1.0 + */ + virtual void handleIqID( const IQ& iq, int context ) = 0; + + }; + +} + +#endif // IQHANDLER_H__ diff --git a/libs/libgloox/jid.cpp b/libs/libgloox/jid.cpp new file mode 100644 index 0000000..d91396e --- /dev/null +++ b/libs/libgloox/jid.cpp @@ -0,0 +1,123 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#include "jid.h" +#include "prep.h" +#include "gloox.h" +#include "util.h" + +namespace gloox +{ + + bool JID::setJID( const std::string& jid ) + { + if ( jid.empty() ) + { + m_bare = m_full = m_server = m_username = m_serverRaw = m_resource = EmptyString; + m_valid = false; + return false; + } + + const std::string::size_type at = jid.find( '@' ); + const std::string::size_type slash = jid.find( '/', at == std::string::npos ? 0 : at ); + + if( at != std::string::npos && !( m_valid = prep::nodeprep( jid.substr( 0, at ), m_username ) ) ) + return false; + + m_serverRaw = jid.substr( at == std::string::npos ? 0 : at + 1, slash - at - 1 ); + if( !( m_valid = prep::nameprep( m_serverRaw, m_server ) ) ) + return false; + + if( slash != std::string::npos + && !( m_valid = prep::resourceprep( jid.substr( slash + 1 ), m_resource ) ) ) + return false; + + setStrings(); + + return m_valid; + } + + bool JID::setUsername( const std::string& uname ) + { + m_valid = prep::nodeprep( uname, m_username ); + setStrings(); + return m_valid; + } + + bool JID::setServer( const std::string& serv ) + { + m_serverRaw = serv; + m_valid = prep::nameprep( m_serverRaw, m_server ); + setStrings(); + return m_valid; + } + + bool JID::setResource( const std::string& res ) + { + m_valid = prep::resourceprep( res, m_resource ); + setFull(); + return m_valid; + } + + void JID::setFull() + { + m_full = bare(); + if( !m_resource.empty() ) + m_full += '/' + m_resource; + } + + void JID::setBare() + { + if( !m_username.empty() ) + m_bare = m_username + '@'; + else + m_bare = EmptyString; + m_bare += m_server; + } + + std::string JID::escapeNode( const std::string& node ) + { + std::string escaped = node; + + util::replaceAll( escaped, "\\", "\\5c" ); + util::replaceAll( escaped, " ", "\\20" ); + util::replaceAll( escaped, "\"", "\\22" ); + util::replaceAll( escaped, "&", "\\26" ); + util::replaceAll( escaped, "'", "\\27" ); + util::replaceAll( escaped, "/", "\\2f" ); + util::replaceAll( escaped, ":", "\\3a" ); + util::replaceAll( escaped, "<", "\\3c" ); + util::replaceAll( escaped, ">", "\\3e" ); + util::replaceAll( escaped, "@", "\\40" ); + + return escaped; + } + + std::string JID::unescapeNode( const std::string& node ) + { + std::string unescaped = node; + + util::replaceAll( unescaped, "\\20", " " ); + util::replaceAll( unescaped, "\\22", "\"" ); + util::replaceAll( unescaped, "\\26", "&" ); + util::replaceAll( unescaped, "\\27", "'" ); + util::replaceAll( unescaped, "\\2f", "/" ); + util::replaceAll( unescaped, "\\3a", ":" ); + util::replaceAll( unescaped, "\\3c", "<" ); + util::replaceAll( unescaped, "\\3e", ">" ); + util::replaceAll( unescaped, "\\40", "@" ); + util::replaceAll( unescaped, "\\5c", "\\" ); + + return unescaped; + } + +} diff --git a/libs/libgloox/jid.h b/libs/libgloox/jid.h new file mode 100644 index 0000000..841f6c6 --- /dev/null +++ b/libs/libgloox/jid.h @@ -0,0 +1,190 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef JID_H__ +#define JID_H__ + +#include "macros.h" + +#include + +namespace gloox +{ + /** + * @brief An abstraction of a JID. + * + * @author Jakob Schroeter + * @since 0.4 + */ + class GLOOX_API JID + { + public: + + /** + * Constructs an empty JID. + */ + JID() : m_valid( false ) {} + + /** + * Constructs a new JID from a string. + * @param jid The string containing the JID. + */ + JID( const std::string& jid ) : m_valid( true ) { setJID( jid ); } + + /** + * Destructor. + */ + ~JID() {} + + /** + * Sets the JID from a string. + * @param jid The string containing the JID. + * @return @b True if the given JID was valid, @b false otherwise. + */ + bool setJID( const std::string& jid ); + + /** + * Returns the full (prepped) JID (user\@host/resource). + * @return The full JID. + */ + const std::string& full() const { return m_full; } + + /** + * Returns the bare (prepped) JID (user\@host). + * @return The bare JID. + */ + const std::string& bare() const { return m_bare; } + + /** + * Creates and returns a JID from this JID's node and server parts. + * @return The bare JID. + * @since 0.9 + */ + JID bareJID() const { return JID( bare() ); } + + /** + * Sets the username. + * @param username The new username. + */ + bool setUsername( const std::string& username ); + + /** + * Sets the server. + * @param server The new server. + */ + bool setServer( const std::string& server ); + + /** + * Sets the resource. + * @param resource The new resource. + */ + bool setResource( const std::string& resource ); + + /** + * Returns the prepped username. + * @return The current username. + */ + const std::string& username() const { return m_username; } + + /** + * Returns the prepped server name. + * @return The current server. + */ + const std::string& server() const { return m_server; } + + /** + * Returns the raw (unprepped) server name. + * @return The raw server name. + */ + const std::string& serverRaw() const { return m_serverRaw; } + + /** + * Returns the prepped resource. + * @return The current resource. + */ + const std::string& resource() const { return m_resource; } + + /** + * Compares a JID with a string. + * @param right The second JID in string representation. + */ + bool operator==( const std::string& right ) const { return full() == right; } + + /** + * Compares a JID with a string. + * @param right The second JID in string representation. + */ + bool operator!=( const std::string& right ) const { return full() != right; } + + /** + * Compares two JIDs. + * @param right The second JID. + */ + bool operator==( const JID& right ) const { return full() == right.full(); } + + /** + * Compares two JIDs. + * @param right The second JID. + */ + bool operator!=( const JID& right ) const { return full() != right.full(); } + + /** + * Converts to @b true if the JID is valid, @b false otherwise. + */ + operator bool() const { return m_valid; } + + /** + * XEP-0106: JID Escaping + * @param node The node to escape. + * @return The escaped node. + */ + static std::string escapeNode( const std::string& node ); + + /** + * XEP-0106: JID Escaping + * @param node The node to unescape. + * @return The unescaped node. + */ + static std::string unescapeNode( const std::string& node ); + + private: + /** + * Utility function to rebuild both the bare and full jid. + */ + void setStrings() { setBare(); setFull(); } + + /** + * Utility function rebuilding the bare jid. + * @note Do not use this function directly, instead use setStrings. + */ + void setBare(); + + /** + * Utility function rebuilding the full jid. + */ + void setFull(); + + std::string m_resource; + std::string m_username; + std::string m_server; + std::string m_serverRaw; + std::string m_bare; + std::string m_full; + bool m_valid; + + }; + +} + +#endif // JID_H__ diff --git a/libs/libgloox/lastactivity.cpp b/libs/libgloox/lastactivity.cpp new file mode 100644 index 0000000..3760dfb --- /dev/null +++ b/libs/libgloox/lastactivity.cpp @@ -0,0 +1,132 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "lastactivity.h" +#include "disco.h" +#include "discohandler.h" +#include "clientbase.h" +#include "error.h" +#include "lastactivityhandler.h" + +#include + +namespace gloox +{ + + // ---- LastActivity::Query ---- + LastActivity::Query::Query( const Tag* tag ) + : StanzaExtension( ExtLastActivity ), m_seconds( -1 ) + { + if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_LAST ) + return; + + if( tag->hasAttribute( "seconds" ) ) + m_seconds = atoi( tag->findAttribute( "seconds" ).c_str() ); + + m_status = tag->cdata(); + } + + LastActivity::Query::Query( const std::string& _status, long _seconds ) + : StanzaExtension( ExtLastActivity ), m_seconds( _seconds ), + m_status( _status ) + { + } + + LastActivity::Query::~Query() + { + } + + const std::string& LastActivity::Query::filterString() const + { + static const std::string filter = "/iq/query[@xmlns='" + XMLNS_LAST + "']" + "|/presence/query[@xmlns='" + XMLNS_LAST + "']"; + return filter; + } + + Tag* LastActivity::Query::tag() const + { + Tag* t = new Tag( "query" ); + t->setXmlns( XMLNS_LAST ); + t->addAttribute( "seconds", m_seconds ); + t->setCData( m_status ); + return t; + } + // ---- ~LastActivity::Query ---- + + // ---- LastActivity ---- + LastActivity::LastActivity( ClientBase* parent ) + : m_lastActivityHandler( 0 ), m_parent( parent ), + m_active( time ( 0 ) ) + { + if( m_parent ) + { + m_parent->registerStanzaExtension( new Query() ); + m_parent->registerIqHandler( this, ExtLastActivity ); + m_parent->disco()->addFeature( XMLNS_LAST ); + } + } + + LastActivity::~LastActivity() + { + if( m_parent ) + { + m_parent->disco()->removeFeature( XMLNS_LAST ); + m_parent->removeIqHandler( this, ExtLastActivity ); + m_parent->removeIDHandler( this ); + } + } + + void LastActivity::query( const JID& jid ) + { + IQ iq( IQ::Get, jid, m_parent->getID() ); + iq.addExtension( new Query() ); + m_parent->send( iq, this, 0 ); + } + + bool LastActivity::handleIq( const IQ& iq ) + { + const Query* q = iq.findExtension( ExtLastActivity ); + if( !q || iq.subtype() != IQ::Get ) + return false; + + IQ re( IQ::Result, iq.from(), iq.id() ); + re.addExtension( new Query( EmptyString, (long)( time( 0 ) - m_active ) ) ); + m_parent->send( re ); + + return true; + } + + void LastActivity::handleIqID( const IQ& iq, int /*context*/ ) + { + if( !m_lastActivityHandler ) + return; + + if( iq.subtype() == IQ::Result ) + { + const Query* q = iq.findExtension( ExtLastActivity ); + if( !q || q->seconds() < 0 ) + return; + + m_lastActivityHandler->handleLastActivityResult( iq.from(), q->seconds(), q->status() ); + } + else if( iq.subtype() == IQ::Error && iq.error() ) + m_lastActivityHandler->handleLastActivityError( iq.from(), iq.error()->error() ); + } + + void LastActivity::resetIdleTimer() + { + m_active = time( 0 ); + } + +} diff --git a/libs/libgloox/lastactivity.h b/libs/libgloox/lastactivity.h new file mode 100644 index 0000000..f0c6eb1 --- /dev/null +++ b/libs/libgloox/lastactivity.h @@ -0,0 +1,166 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef LASTACTIVITY_H__ +#define LASTACTIVITY_H__ + +#include "iqhandler.h" + +#include + +namespace gloox +{ + + class JID; + class ClientBase; + class LastActivityHandler; + + /** + * @brief This is an implementation of XEP-0012 (Last Activity) for both clients and components. + * + * LastActivity can be used to query remote entities about their last activity time as well + * as answer incoming last-activity-queries. + * + * XEP Version: 2.0 + * + * @author Jakob Schroeter + * @since 0.6 + */ + class GLOOX_API LastActivity : public IqHandler + { + public: + /** + * @brief This is an abstraction of a LastActivity Query that + * can be used in XEP-0012 as well as XEP-0256. + * + * XEP-Version: 2.0 (XEP-0012) + * XEP-Version: 0.1 (XEP-0256) + * + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API Query : public StanzaExtension + { + public: + /** + * Constructs a new Query object from the given Tag. + * @param tag The Tag to parse. + */ + Query( const Tag* tag = 0 ); + + /** + * Constructs a new Query object from the given long. + * @param status A status message. + * @param seconds The number of seconds since last activity. + */ + Query( const std::string& status, long seconds ); + + /** + * Virtual destructor. + */ + virtual ~Query(); + + /** + * Returns the number of seconds since last activity. + * @return The number of seconds since last activity. + * -1 if last activity is unknown. + */ + long seconds() const { return m_seconds; } + + /** + * Returns the last status message if the user is offline + * and specified a status message when logging off. + * @return The last status message, if any. + */ + const std::string& status() const { return m_status; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new Query( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new Query( *this ); + } + + private: + long m_seconds; + std::string m_status; + + }; + + /** + * Constructs a new LastActivity object. + * @param parent The ClientBase object to use for communication. + */ + LastActivity( ClientBase* parent ); + + /** + * Virtual destructor. + */ + virtual ~LastActivity(); + + /** + * Queries the given JID for their last activity. The result can be received by reimplementing + * @ref LastActivityHandler::handleLastActivityResult() and + * @ref LastActivityHandler::handleLastActivityError(). + */ + void query( const JID& jid ); + + /** + * Use this function to register an object as handler for incoming results of Last-Activity queries. + * Only one handler is possible at a time. + * @param lah The object to register as handler. + */ + void registerLastActivityHandler( LastActivityHandler* lah ) { m_lastActivityHandler = lah; } + + /** + * Use this function to un-register the LastActivityHandler set earlier. + */ + void removeLastActivityHandler() { m_lastActivityHandler = 0; } + + /** + * Use this function to reset the idle timer. By default the number of seconds since the + * instantiation will be used. + */ + void resetIdleTimer(); + + // reimplemented from IqHandler + virtual bool handleIq( const IQ& iq ); + + // reimplemented from IqHandler + virtual void handleIqID( const IQ& iq, int context ); + + private: +#ifdef LASTACTIVITY_TEST + public: +#endif + LastActivityHandler* m_lastActivityHandler; + ClientBase* m_parent; + + time_t m_active; + + }; + +} + +#endif // LASTACTIVITY_H__ diff --git a/libs/libgloox/lastactivityhandler.h b/libs/libgloox/lastactivityhandler.h new file mode 100644 index 0000000..fb3217d --- /dev/null +++ b/libs/libgloox/lastactivityhandler.h @@ -0,0 +1,58 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef LASTACTIVITYHANDLER_H__ +#define LASTACTIVITYHANDLER_H__ + +#include "gloox.h" +#include "jid.h" + +namespace gloox +{ + + /** + * @brief This is an virtual interface that, once reimplemented, allows to receive the + * results of Last-Activity-queries to other entities. + * + * @author Jakob Schroeter + * @since 0.6 + */ + class GLOOX_API LastActivityHandler + { + public: + /** + * Virtual Destructor. + */ + virtual ~LastActivityHandler() {} + + /** + * This function is called when a positive result of a query arrives. + * @param jid The JID of the queried contact. + * @param seconds The idle time or time of last presence of the contact. (Depends + * on the JID, check the spec.) + * @param status If the contact is offline, this is the last presence status message. May be empty. + */ + virtual void handleLastActivityResult( const JID& jid, long seconds, const std::string& status ) = 0; + + /** + * This function is called when an error is returned by the queried antity. + * @param jid The queried entity's address. + * @param error The reported error. + */ + virtual void handleLastActivityError( const JID& jid, StanzaError error ) = 0; + + }; + +} + +#endif // LASTACTIVITYHANDLER_H__ diff --git a/libs/libgloox/loghandler.h b/libs/libgloox/loghandler.h new file mode 100644 index 0000000..fa45a1b --- /dev/null +++ b/libs/libgloox/loghandler.h @@ -0,0 +1,53 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef LOGHANDLER_H__ +#define LOGHANDLER_H__ + +#include "gloox.h" + +#include + +namespace gloox +{ + + /** + * @brief A virtual interface which can be reimplemented to receive debug and log messages. + * + * @ref handleLog() is called for log messages. + * + * @author Jakob Schroeter + * @since 0.5 + */ + class GLOOX_API LogHandler + { + public: + /** + * Virtual Destructor. + */ + virtual ~LogHandler() {} + + /** + * Reimplement this function if you want to receive the chunks of the conversation + * between gloox and server or other debug info from gloox. + * @param level The log message's severity. + * @param area The log message's origin. + * @param message The log message. + */ + virtual void handleLog( LogLevel level, LogArea area, const std::string& message ) = 0; + }; + +} + +#endif // LOGHANDLER_H__ diff --git a/libs/libgloox/logsink.cpp b/libs/libgloox/logsink.cpp new file mode 100644 index 0000000..30719de --- /dev/null +++ b/libs/libgloox/logsink.cpp @@ -0,0 +1,49 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "logsink.h" + +namespace gloox +{ + + LogSink::LogSink() + { + } + + LogSink::~LogSink() + { + } + + void LogSink::log( LogLevel level, LogArea area, const std::string& message ) const + { + LogHandlerMap::const_iterator it = m_logHandlers.begin(); + for( ; it != m_logHandlers.end(); ++it ) + { + if( (*it).first && ( (*it).second.level <= level ) && ( (*it).second.areas & area ) ) + (*it).first->handleLog( level, area, message ); + } + } + + void LogSink::registerLogHandler( LogLevel level, int areas, LogHandler* lh ) + { + LogInfo info = { level, areas }; + m_logHandlers[lh] = info; + } + + void LogSink::removeLogHandler( LogHandler* lh ) + { + m_logHandlers.erase( lh ); + } + +} diff --git a/libs/libgloox/logsink.h b/libs/libgloox/logsink.h new file mode 100644 index 0000000..6b92280 --- /dev/null +++ b/libs/libgloox/logsink.h @@ -0,0 +1,118 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef LOGSINK_H__ +#define LOGSINK_H__ + +#include "gloox.h" +#include "loghandler.h" + +#include +// #include + +namespace gloox +{ + + /** + * @brief An implementation of log sink and source. + * + * To log the output of your Client or Component, use ClientBase's + * @link ClientBase::logInstance() logInstance() @endlink to get hold of the LogSink + * object for that ClientBase. Register your LogHandler with that instance. + * + * You should not need to use this class directly. + * + * @author Jakob Schroeter + * @since 0.8 + */ + class GLOOX_API LogSink + { + public: + /** + * Constructor. + */ + LogSink(); + + /** + * Virtual destructor. + */ + virtual ~LogSink(); + + /** + * Use this function to log a message with given LogLevel and LogIdentifier. + * dbg(), warn(), and err() are alternative shortcuts. + * @param level The severity of the logged event. + * @param area The part of the program/library the message comes from. + * @param message The actual log message. + */ + void log( LogLevel level, LogArea area, const std::string& message ) const; + + /** + * Use this function to log a debug message with given LogIdentifier. + * This is a convenience wrapper around log(). + * @param area The part of the program/library the message comes from. + * @param message The actual log message. + */ + void dbg( LogArea area, const std::string& message ) const + { log( LogLevelDebug, area, message ); } + + /** + * Use this function to log a warning message with given LogIdentifier. + * This is a convenience wrapper around log(). + * @param area The part of the program/library the message comes from. + * @param message The actual log message. + */ + void warn( LogArea area, const std::string& message ) const + { log( LogLevelWarning, area, message ); } + + /** + * Use this function to log a error message with given LogIdentifier. + * This is a convenience wrapper around log(). + * @param area The part of the program/library the message comes from. + * @param message The actual log message. + */ + void err( LogArea area, const std::string& message ) const + { log( LogLevelError, area, message ); } + + /** + * Registers @c lh as object that receives all debug messages of the specified type. + * Suitable for logging to a file, etc. + * @param level The LogLevel for this handler. + * @param areas Bit-wise ORed LogAreas the LogHandler wants to be informed about. + * @param lh The object to receive exchanged data. + */ + void registerLogHandler( LogLevel level, int areas, LogHandler* lh ); + + /** + * Removes the given object from the list of log handlers. + * @param lh The object to remove from the list. + */ + void removeLogHandler( LogHandler* lh ); + + private: + struct LogInfo + { + LogLevel level; + int areas; + }; + + LogSink( const LogSink& /*copy*/ ); + + typedef std::map LogHandlerMap; + LogHandlerMap m_logHandlers; + + }; + +} + +#endif // LOGSINK_H__ diff --git a/libs/libgloox/macros.h b/libs/libgloox/macros.h new file mode 100644 index 0000000..c8f622f --- /dev/null +++ b/libs/libgloox/macros.h @@ -0,0 +1,49 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef GLOOX_MACROS_H__ +#define GLOOX_MACROS_H__ + +#if defined( _MSC_VER ) || defined( _WIN32_WCE ) +# pragma warning( disable:4251 ) +# pragma warning( disable:4786 ) +#endif + +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) +# if defined( GLOOX_EXPORTS ) || defined( DLL_EXPORT ) +# define GLOOX_API __declspec( dllexport ) +# else +# if defined( GLOOX_IMPORTS ) || defined( DLL_IMPORT ) +# define GLOOX_API __declspec( dllimport ) +# endif +# endif +#endif + +#ifndef GLOOX_API +# define GLOOX_API +#endif + + +#if defined( __GNUC__ ) && ( __GNUC__ - 0 > 3 || ( __GNUC__ - 0 == 3 && __GNUC_MINOR__ - 0 >= 2 ) ) +# define GLOOX_DEPRECATED __attribute__ ( (__deprecated__) ) +# define GLOOX_DEPRECATED_CTOR explicit GLOOX_DEPRECATED +#elif defined( _MSC_VER ) && ( _MSC_VER >= 1300 ) +# define GLOOX_DEPRECATED __declspec( deprecated ) +# define GLOOX_DEPRECATED_CTOR explicit GLOOX_DEPRECATED +#else +# define GLOOX_DEPRECATED +# define GLOOX_DEPRECATED_CTOR +#endif + + +#endif // GLOOX_MACROS_H__ diff --git a/libs/libgloox/md5.cpp b/libs/libgloox/md5.cpp new file mode 100644 index 0000000..3b41b62 --- /dev/null +++ b/libs/libgloox/md5.cpp @@ -0,0 +1,466 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +/* + This class is based on a C implementation of the MD5 algorithm written by + L. Peter Deutsch. + The full notice as shipped with the original verson is included below. +*/ + +/* + Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.c is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order + either statically or dynamically; added missing #include + in library. + 2002-03-11 lpd Corrected argument list for main(), and added int return + type, in test program and T value program. + 2002-02-21 lpd Added missing #include in test program. + 2000-07-03 lpd Patched to eliminate warnings about "constant is + unsigned in ANSI C, signed in traditional"; made test program + self-checking. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +#include "config.h" + +#include "md5.h" + +#include +#include + +#include // [s]print[f] + +namespace gloox +{ +// #undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ +// #ifdef ARCH_IS_BIG_ENDIAN +// # define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) +// #else +// # define BYTE_ORDER 0 +// #endif + +#undef BYTE_ORDER +#define BYTE_ORDER 0 + +#define T_MASK ((unsigned int)~0) +#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) +#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) +#define T3 0x242070db +#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) +#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) +#define T6 0x4787c62a +#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) +#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) +#define T9 0x698098d8 +#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) +#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) +#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) +#define T13 0x6b901122 +#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) +#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) +#define T16 0x49b40821 +#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) +#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) +#define T19 0x265e5a51 +#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) +#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) +#define T22 0x02441453 +#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) +#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) +#define T25 0x21e1cde6 +#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) +#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) +#define T28 0x455a14ed +#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) +#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) +#define T31 0x676f02d9 +#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) +#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) +#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) +#define T35 0x6d9d6122 +#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) +#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) +#define T38 0x4bdecfa9 +#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) +#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) +#define T41 0x289b7ec6 +#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) +#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) +#define T44 0x04881d05 +#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) +#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) +#define T47 0x1fa27cf8 +#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) +#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) +#define T50 0x432aff97 +#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) +#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) +#define T53 0x655b59c3 +#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) +#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) +#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) +#define T57 0x6fa87e4f +#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) +#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) +#define T60 0x4e0811a1 +#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) +#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) +#define T63 0x2ad7d2bb +#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) + + + const unsigned char MD5::pad[64] = + { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + MD5::MD5() + : m_finished( false ) + { + init(); + } + + MD5::~MD5() + { + } + + void MD5::process( const unsigned char* data /*[64]*/ ) + { + unsigned int a = m_state.abcd[0]; + unsigned int b = m_state.abcd[1]; + unsigned int c = m_state.abcd[2]; + unsigned int d = m_state.abcd[3]; + unsigned int t; +#if BYTE_ORDER > 0 + /* Define storage only for big-endian CPUs. */ + unsigned int X[16]; +#else + /* Define storage for little-endian or both types of CPUs. */ + unsigned int xbuf[16]; + const unsigned int *X; +#endif + + { +#if BYTE_ORDER == 0 + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static const int w = 1; + + if( *((const unsigned char *)&w) ) /* dynamic little-endian */ +#endif +#if BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if( !((data - (const unsigned char*)0) & 3) ) + { + /* data are properly aligned */ + X = (const unsigned int*)data; + } + else + { + /* not aligned */ + memcpy( xbuf, data, 64 ); + X = xbuf; + } + } +#endif +#if BYTE_ORDER == 0 + else // dynamic big-endian +#endif +#if BYTE_ORDER >= 0 // big-endian + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const unsigned char* xp = data; + int i; + +# if BYTE_ORDER == 0 + X = xbuf; // (dynamic only) +# else +# define xbuf X /* (static only) */ +# endif + for( i = 0; i < 16; ++i, xp += 4 ) + xbuf[i] = xp[0] + ( xp[1] << 8 ) + ( xp[2] << 16 ) + ( xp[3] << 24 ); + } +#endif + } + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + m_state.abcd[0] += a; + m_state.abcd[1] += b; + m_state.abcd[2] += c; + m_state.abcd[3] += d; + } + + void MD5::init() + { + m_finished = false; + m_state.count[0] = 0; + m_state.count[1] = 0; + m_state.abcd[0] = 0x67452301; + m_state.abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; + m_state.abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; + m_state.abcd[3] = 0x10325476; + } + + void MD5::feed( const std::string& data ) + { + feed( (const unsigned char*)data.c_str(), (int)data.length() ); + } + + void MD5::feed( const unsigned char* data, int bytes ) + { + const unsigned char* p = data; + int left = bytes; + int offset = ( m_state.count[0] >> 3 ) & 63; + unsigned int nbits = (unsigned int)( bytes << 3 ); + + if( bytes <= 0 ) + return; + + /* Update the message length. */ + m_state.count[1] += bytes >> 29; + m_state.count[0] += nbits; + if( m_state.count[0] < nbits ) + m_state.count[1]++; + + /* Process an initial partial block. */ + if( offset ) + { + int copy = ( offset + bytes > 64 ? 64 - offset : bytes ); + + memcpy( m_state.buf + offset, p, copy ); + if( offset + copy < 64 ) + return; + p += copy; + left -= copy; + process( m_state.buf ); + } + + /* Process full blocks. */ + for( ; left >= 64; p += 64, left -= 64 ) + process( p ); + + /* Process a final partial block. */ + if( left ) + memcpy( m_state.buf, p, left ); + } + + void MD5::finalize() + { + if( m_finished ) + return; + + unsigned char data[8]; + + /* Save the length before padding. */ + for( int i = 0; i < 8; ++i ) + data[i] = (unsigned char)( m_state.count[i >> 2] >> ( ( i & 3 ) << 3 ) ); + + /* Pad to 56 bytes mod 64. */ + feed( pad, ( ( 55 - ( m_state.count[0] >> 3 ) ) & 63 ) + 1 ); + + /* Append the length. */ + feed( data, 8 ); + + m_finished = true; + } + + const std::string MD5::hex() + { + if( !m_finished ) + finalize(); + + char buf[33]; + + for( int i = 0; i < 16; ++i ) + sprintf( buf + i * 2, "%02x", (unsigned char)( m_state.abcd[i >> 2] >> ( ( i & 3 ) << 3 ) ) ); + + return std::string( buf, 32 ); + } + + const std::string MD5::binary() + { + if( !m_finished ) + finalize(); + + unsigned char digest[16]; + for( int i = 0; i < 16; ++i ) + digest[i] = (unsigned char)( m_state.abcd[i >> 2] >> ( ( i & 3 ) << 3 ) ); + + return std::string( (char*)digest, 16 ); + } + + void MD5::reset() + { + init(); + } + +} diff --git a/libs/libgloox/md5.h b/libs/libgloox/md5.h new file mode 100644 index 0000000..47dbdfc --- /dev/null +++ b/libs/libgloox/md5.h @@ -0,0 +1,137 @@ +/* + Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.h is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Removed support for non-ANSI compilers; removed + references to Ghostscript; clarified derivation from RFC 1321; + now handles byte order either statically or dynamically. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke . + 1999-05-03 lpd Original version. + */ + +#ifndef MD5_H__ +#define MD5_H__ + +#include "macros.h" + +#include + +namespace gloox +{ + + /** + * @brief An MD% implementation. + * + * This is an implementation of the Message Digest Algorithm as decribed in RFC 1321. + * The original code has been taken from an implementation by L. Peter Deutsch. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API MD5 + { + public: + /** + * Constructs a new MD5 object. + */ + MD5(); + + /** + * Virtual Destructor. + */ + virtual ~MD5(); + + /** + * Use this function to feed the hash. + * @param data The data to hash. + * @param bytes The size of @c data in bytes. + */ + void feed( const unsigned char* data, int bytes ); + + /** + * Use this function to feed the hash. + * @param data The data to hash. + */ + void feed( const std::string& data ); + + /** + * This function is used to finalize the hash operation. Use it after the last feed() and + * before calling hex(). + */ + void finalize(); + + /** + * Use this function to retrieve the hash value in hex. + * @return The hash in hex notation. + */ + const std::string hex(); + + /** + * Use this function to retrieve the raw binary hash. + * @return The raw binary hash. + */ + const std::string binary(); + + /** + * Use this function to reset the hash. + */ + void reset(); + + private: + struct MD5State + { + unsigned int count[2]; /* message length in bits, lsw first */ + unsigned int abcd[4]; /* digest buffer */ + unsigned char buf[64]; /* accumulate block */ + } m_state; + + void init(); + void process( const unsigned char* data ); + + static const unsigned char pad[64]; + + bool m_finished; + + }; + +} + +#endif // MD5_H__ diff --git a/libs/libgloox/message.cpp b/libs/libgloox/message.cpp new file mode 100644 index 0000000..66290d7 --- /dev/null +++ b/libs/libgloox/message.cpp @@ -0,0 +1,96 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#include "util.h" +#include "message.h" + +namespace gloox +{ + + static const char* msgTypeStringValues[] = + { + "chat", "error", "groupchat", "headline", "normal" + }; + + static inline const std::string typeString( Message::MessageType type ) + { + return util::lookup2( type, msgTypeStringValues ); + } + + Message::Message( Tag* tag ) + : Stanza( tag ), m_subtype( Invalid ), m_bodies( 0 ), m_subjects( 0 ) + { + if( !tag || tag->name() != "message" ) + return; + + const std::string& typestring = tag->findAttribute( TYPE ); + if( typestring.empty() ) + m_subtype = Normal; + else + m_subtype = (MessageType)util::lookup2( typestring, msgTypeStringValues ); + + const TagList& c = tag->children(); + TagList::const_iterator it = c.begin(); + for( ; it != c.end(); ++it ) + { + if( (*it)->name() == "body" ) + setLang( &m_bodies, m_body, (*it) ); + else if( (*it)->name() == "subject" ) + setLang( &m_subjects, m_subject, (*it) ); + else if( (*it)->name() == "thread" ) + m_thread = (*it)->cdata(); + } + } + + Message::Message( MessageType type, const JID& to, + const std::string& body, const std::string& subject, + const std::string& thread, const std::string& xmllang ) + : Stanza( to ), m_subtype( type ), m_bodies( 0 ), m_subjects( 0 ), m_thread( thread ) + { + setLang( &m_bodies, m_body, body, xmllang ); + setLang( &m_subjects, m_subject, subject, xmllang ); + } + + Message::~Message() + { + delete m_bodies; + delete m_subjects; + } + + Tag* Message::tag() const + { + if( m_subtype == Invalid ) + return 0; + + Tag* t = new Tag( "message" ); + if( m_to ) + t->addAttribute( "to", m_to.full() ); + if( m_from ) + t->addAttribute( "from", m_from.full() ); + if( !m_id.empty() ) + t->addAttribute( "id", m_id ); + t->addAttribute( TYPE, typeString( m_subtype ) ); + + getLangs( m_bodies, m_body, "body", t ); + getLangs( m_subjects, m_subject, "subject", t ); + + if( !m_thread.empty() ) + new Tag( t, "thread", m_thread ); + + StanzaExtensionList::const_iterator it = m_extensionList.begin(); + for( ; it != m_extensionList.end(); ++it ) + t->addChild( (*it)->tag() ); + + return t; + } + +} diff --git a/libs/libgloox/message.h b/libs/libgloox/message.h new file mode 100644 index 0000000..4eb21c4 --- /dev/null +++ b/libs/libgloox/message.h @@ -0,0 +1,156 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#ifndef MESSAGE_H__ +#define MESSAGE_H__ + +#include "delayeddelivery.h" +#include "stanza.h" + +#include + +namespace gloox +{ + + class JID; + + /** + * @brief An abstraction of a message stanza. + * + * @author Vincent Thomasset + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API Message : public Stanza + { + + friend class ClientBase; + + public: + + /** + * Describes the different valid message types. + */ + enum MessageType + { + Chat = 1, /**< A chat message. */ + Error = 2, /**< An error message. */ + Groupchat = 4, /**< A groupchat message. */ + Headline = 8, /**< A headline message. */ + Normal = 16, /**< A normal message. */ + Invalid = 32 /**< The message stanza is invalid. */ + }; + + /** + * Creates a Message. + * @param type The message type. + * @param to The intended receiver. + * @param body The message's body text. + * @param subject The message's optional subject. + * @param thread The message's optional thread ID. + * @param xmllang An optional xml:lang for the message body. + */ + Message( MessageType type, const JID& to, + const std::string& body = EmptyString, const std::string& subject = EmptyString, + const std::string& thread = EmptyString, const std::string& xmllang = EmptyString ); + + /** + * Destructor. + */ + virtual ~Message(); + + /** + * Returns the message's type. + * @return The message's type. + */ + MessageType subtype() const { return m_subtype; } + + /** + * Returns the message body for the given language if available. + * If the requested language is not available, the default body (without a xml:lang + * attribute) will be returned. + * @param lang The language identifier for the desired language. It must conform to + * section 2.12 of the XML specification and RFC 3066. If empty, the default body + * will be returned, if any. + * @return The message body. + */ + const std::string body( const std::string& lang = "default" ) const + { + return findLang( m_bodies, m_body, lang ); + } + + /** + * Returns the message subject for the given language if available. + * If the requested language is not available, the default subject (without a xml:lang + * attribute) will be returned. + * @param lang The language identifier for the desired language. It must conform to + * section 2.12 of the XML specification and RFC 3066. If empty, the default subject + * will be returned, if any. + * @return The message subject. + */ + const std::string subject( const std::string& lang = "default" ) const + { + return findLang( m_subjects, m_subject, lang ); + } + + /** + * Returns the thread ID of a message stanza. + * @return The thread ID of a message stanza. Empty for non-message stanzas. + */ + const std::string& thread() const { return m_thread; } + + /** + * Sets the thread ID. + * @param thread The thread ID. + */ + void setThread( const std::string& thread ) { m_thread = thread; } + + /** + * Sets the message's ID. Optional. + * @param id The ID. + */ + void setID( const std::string& id ) { m_id = id; } + + /** + * Convenience function that returns a pointer to a DelayedDelivery StanzaExtension, if the + * message contains one. + * @return A pointer to a DelayedDelivery object, or 0. + */ + const DelayedDelivery* when() const + { + return static_cast( findExtension( ExtDelay ) ); + } + + // reimplemented from Stanza + virtual Tag* tag() const; + + private: +#ifdef MESSAGE_TEST + public: +#endif + /** + * Creates a message Stanza from the given Tag. The original Tag will be ripped off. + * @param tag The Tag to parse. + */ + Message( Tag* tag ); + + MessageType m_subtype; + std::string m_body; + std::string m_subject; + StringMap* m_bodies; + StringMap* m_subjects; + std::string m_thread; + }; + +} + +#endif // MESSAGE_H__ diff --git a/libs/libgloox/messageevent.cpp b/libs/libgloox/messageevent.cpp new file mode 100644 index 0000000..3e5c8b6 --- /dev/null +++ b/libs/libgloox/messageevent.cpp @@ -0,0 +1,65 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#include "messageevent.h" +#include "tag.h" +#include "util.h" + +namespace gloox +{ + + /* chat state type values */ + static const char* eventValues [] = { + "offline", + "delivered", + "displayed", + "composing" + }; + + MessageEvent::MessageEvent( const Tag* tag ) + : StanzaExtension( ExtMessageEvent ), m_event( MessageEventCancel ) + { + const TagList& l = tag->children(); + TagList::const_iterator it = l.begin(); + int event = 0; + for( ; it != l.end(); ++it ) + event |= util::lookup2( (*it)->name(), eventValues ); + if( event ) + m_event = event; + } + + const std::string& MessageEvent::filterString() const + { + static const std::string filter = "/message/x[@xmlns='" + XMLNS_X_EVENT + "']"; + return filter; + } + + Tag* MessageEvent::tag() const + { + Tag* x = new Tag( "x", XMLNS, XMLNS_X_EVENT ); + + if( m_event & MessageEventOffline ) + new Tag( x, "offline" ); + if( m_event & MessageEventDelivered ) + new Tag( x, "delivered" ); + if( m_event & MessageEventDisplayed ) + new Tag( x, "displayed" ); + if( m_event & MessageEventComposing ) + new Tag( x, "composing" ); + + if( !m_id.empty() ) + new Tag( x, "id", m_id ); + + return x; + } + +} diff --git a/libs/libgloox/messageevent.h b/libs/libgloox/messageevent.h new file mode 100644 index 0000000..dc60b5b --- /dev/null +++ b/libs/libgloox/messageevent.h @@ -0,0 +1,88 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#ifndef MESSAGEEVENT_H__ +#define MESSAGEEVENT_H__ + +#include "gloox.h" +#include "stanzaextension.h" + +#include + +namespace gloox +{ + + class Tag; + + /** + * @brief An implementation of Message Events (XEP-0022) as a StanzaExtension. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API MessageEvent : public StanzaExtension + { + public: + + /** + * Constructs a new object from the given Tag. + * @param tag A Tag to parse. + */ + MessageEvent( const Tag* tag ); + + /** + * Constructs a new object of the given type, with an optional message ID. + * @param type One or more @link gloox::MessageEventType MessageEventType @endlink. + * @param id An optional message ID. Links this Event to the message it is generated for. + */ + MessageEvent( int type, const std::string& id = EmptyString ) + : StanzaExtension( ExtMessageEvent ), m_id( id ), m_event( type ) + {} + + /** + * Virtual destructor. + */ + virtual ~MessageEvent() {} + + /** + * Returns the object's event or events. + * @return The object's event or events. + */ + int event() const { return m_event; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new MessageEvent( tag ); + } + + // reimplemented from StanzaExtension + Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new MessageEvent( *this ); + } + + private: + std::string m_id; + int m_event; + + }; + +} + +#endif // MESSAGEEVENT_H__ diff --git a/libs/libgloox/messageeventfilter.cpp b/libs/libgloox/messageeventfilter.cpp new file mode 100644 index 0000000..12f71e1 --- /dev/null +++ b/libs/libgloox/messageeventfilter.cpp @@ -0,0 +1,112 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "messageeventfilter.h" +#include "messageeventhandler.h" +#include "messagesession.h" +#include "message.h" +#include "messageevent.h" +#include "error.h" + +namespace gloox +{ + + MessageEventFilter::MessageEventFilter( MessageSession* parent ) + : MessageFilter( parent ), m_messageEventHandler( 0 ), m_requestedEvents( 0 ), + m_lastSent( MessageEventCancel ), m_disable( false ) + { + } + + MessageEventFilter::~MessageEventFilter() + { + } + + void MessageEventFilter::filter( Message& msg ) + { + if( m_disable || !m_messageEventHandler ) + return; + + if( msg.subtype() == Message::Error ) + { + if( msg.error() && msg.error()->error() == StanzaErrorFeatureNotImplemented ) + m_disable = true; + + return; + } + + const MessageEvent* me = msg.findExtension( ExtMessageEvent ); + if( !me ) + { + m_requestedEvents = 0; + m_lastID = EmptyString; + return; + } + + if( msg.body().empty() ) + m_messageEventHandler->handleMessageEvent( msg.from(), (MessageEventType)me->event() ); + else + { + m_lastID = msg.id(); + m_requestedEvents = 0; + m_requestedEvents = me->event(); + } + } + + void MessageEventFilter::raiseMessageEvent( MessageEventType event ) + { + if( m_disable || ( !( m_requestedEvents & event ) && ( event != MessageEventCancel ) ) ) + return; + + switch( event ) + { + case MessageEventOffline: + case MessageEventDelivered: + case MessageEventDisplayed: + m_requestedEvents &= ~event; + break; + case MessageEventComposing: + if( m_lastSent == MessageEventComposing ) + return; + break; + case MessageEventCancel: + default: + break; + } + + m_lastSent = event; + Message m( Message::Normal, m_parent->target() ); + m.addExtension( new MessageEvent( event, m_lastID ) ); + send( m ); + } + + void MessageEventFilter::decorate( Message& msg ) + { + if( m_disable ) + return; + + msg.addExtension( new MessageEvent( MessageEventOffline | MessageEventDelivered | + MessageEventDisplayed | MessageEventComposing ) ); + m_lastSent = MessageEventCancel; + } + + void MessageEventFilter::registerMessageEventHandler( MessageEventHandler* meh ) + { + m_messageEventHandler = meh; + } + + void MessageEventFilter::removeMessageEventHandler() + { + m_messageEventHandler = 0; + } + +} diff --git a/libs/libgloox/messageeventfilter.h b/libs/libgloox/messageeventfilter.h new file mode 100644 index 0000000..b1c91a7 --- /dev/null +++ b/libs/libgloox/messageeventfilter.h @@ -0,0 +1,95 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef MESSAGEVENTFILTER_H__ +#define MESSAGEVENTFILTER_H__ + +#include "messagefilter.h" +#include "gloox.h" + +namespace gloox +{ + + class Tag; + class Message; + class MessageEventHandler; + class MessageSession; + + /** + * @brief This class adds Message Event (XEP-0022) support to a MessageSession. + * + * This implementation of Message Events is fully transparent to the user of the class. + * If the remote entity does not request message events, MessageEventFilter will not send + * any, even if the user requests it. (This is required by the protocol specification.) + * + * @author Jakob Schroeter + * @since 0.8 + */ + class GLOOX_API MessageEventFilter : public MessageFilter + { + public: + /** + * Contstructs a new Message Event filter for a MessageSession. + * @param parent The MessageSession to decorate. + */ + MessageEventFilter( MessageSession* parent ); + + /** + * Virtual destructor. + */ + virtual ~MessageEventFilter(); + + /** + * Use this function to raise an event as defined in XEP-0022. + * @note The Spec states that Message Events shall not be sent to an entity + * which did not request them. Reasonable effort is taken in this function to + * avoid spurious event sending. You should be safe to call this even if Message + * Events were not requested by the remote entity. However, + * calling raiseMessageEvent( MESSAGE_EVENT_COMPOSING ) for every keystroke still is + * discouraged. ;) + * @param event The event to raise. + */ + void raiseMessageEvent( MessageEventType event ); + + /** + * The MessageEventHandler registered here will receive Message Events according + * to XEP-0022. + * @param meh The MessageEventHandler to register. + */ + void registerMessageEventHandler( MessageEventHandler* meh ); + + /** + * This function clears the internal pointer to the MessageEventHandler. + * Message Events will not be delivered anymore after calling this function until another + * MessageEventHandler is registered. + */ + void removeMessageEventHandler(); + + // reimplemented from MessageFilter + virtual void decorate( Message& msg ); + + // reimplemented from MessageFilter + virtual void filter( Message& msg ); + + private: + MessageEventHandler* m_messageEventHandler; + std::string m_lastID; + int m_requestedEvents; + MessageEventType m_lastSent; + bool m_disable; + + }; + +} + +#endif // MESSAGEVENTFILTER_H__ diff --git a/libs/libgloox/messageeventhandler.h b/libs/libgloox/messageeventhandler.h new file mode 100644 index 0000000..6dbacb3 --- /dev/null +++ b/libs/libgloox/messageeventhandler.h @@ -0,0 +1,51 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef MESSAGEEVENTHANDLER_H__ +#define MESSAGEEVENTHANDLER_H__ + +#include "gloox.h" + +namespace gloox +{ + + class JID; + + /** + * @brief A virtual interface that enables an object to be notified about + * Message Events (XEP-0022). + * + * @author Jakob Schroeter + * @since 0.8 + */ + class GLOOX_API MessageEventHandler + { + public: + /** + * Virtual Destructor. + */ + virtual ~MessageEventHandler() {} + + /** + * Notifies the MessageEventHandler that an event has been raised by the remote + * contact. + * @param from The originator of the Event. + * @param event The Event which has been raised. + */ + virtual void handleMessageEvent( const JID& from, MessageEventType event ) = 0; + + }; + +} + +#endif // MESSAGEEVENTHANDLER_H__ diff --git a/libs/libgloox/messagefilter.cpp b/libs/libgloox/messagefilter.cpp new file mode 100644 index 0000000..2f80e6c --- /dev/null +++ b/libs/libgloox/messagefilter.cpp @@ -0,0 +1,42 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "messagefilter.h" + + +namespace gloox +{ + + MessageFilter::MessageFilter( MessageSession* parent ) + : m_parent( 0 ) + { + if( parent ) + attachTo( parent ); + } + + MessageFilter::~MessageFilter() + { + } + + void MessageFilter::attachTo( MessageSession* session ) + { + if( m_parent ) + m_parent->removeMessageFilter( this ); + + if( session ) + session->registerMessageFilter( this ); + + m_parent = session; + } + +} diff --git a/libs/libgloox/messagefilter.h b/libs/libgloox/messagefilter.h new file mode 100644 index 0000000..43e2cfa --- /dev/null +++ b/libs/libgloox/messagefilter.h @@ -0,0 +1,84 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef MESSAGEFILTER_H__ +#define MESSAGEFILTER_H__ + +#include "messagesession.h" + +namespace gloox +{ + + class Message; + + /** + * @brief Virtual base class for message filters. + * + * A message filter is fed with all messages passing through a MessageSession. It can + * modify the XML/XMPP structure and/or the message content at will. Messages arriving + * from the server as well as messages sent to the server can be altered. + * + * Messages to be sent out are presented to the filter via the decorate() function, incoming + * messages can be filtered in the -- filter() method. + * + * @author Jakob Schroeter + * @since 0.8 + */ + class GLOOX_API MessageFilter + { + + public: + /** + * Constructor. + * @param parent The MessageSession to attach to. + */ + MessageFilter( MessageSession* parent ); + + /** + * Virtual Destructor. + */ + virtual ~MessageFilter(); + + /** + * Attaches this MessageFilter to the given MessageSession and hooks it into + * the session's filter chain. + * If this filter was attached to a different MessageSession before, it is + * unregistered there prior to registering it with the new session. + * @param session The MessageSession to hook into. + */ + virtual void attachTo( MessageSession* session ); + + /** + * This function receives a message right before it is sent out (there may be other filters + * which get to see the message after this filter, though). + * @param msg The tag to decorate. It contains the message to be sent. + */ + virtual void decorate( Message& msg ) = 0; + + /** + * This function receives a message stanza right after it was received (there may be other filters + * which got to see the stanza before this filter, though). + * @param msg The complete message stanza. + */ + virtual void filter( Message& msg ) = 0; + + protected: + void send( Message& msg ) { if( m_parent ) m_parent->send( msg ); } + + MessageSession* m_parent; + + }; + +} + +#endif // MESSAGEFILTER_H__ diff --git a/libs/libgloox/messagehandler.h b/libs/libgloox/messagehandler.h new file mode 100644 index 0000000..47f302c --- /dev/null +++ b/libs/libgloox/messagehandler.h @@ -0,0 +1,58 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef MESSAGEHANDLER_H__ +#define MESSAGEHANDLER_H__ + +#include "macros.h" + +namespace gloox +{ + + class MessageSession; + class Message; + + /** + * @brief A virtual interface which can be reimplemented to receive incoming message stanzas. + * + * Derived classes can be registered as MessageHandlers with a ClientBase or MessageSession instance. + * Upon an incoming Message packet @ref handleMessage() will be called. If registered with a + * ClientBase this happens for every incoming message, regardless of the sender. With a MessageSession + * the registered handler will receive all messages originating from the Session's contact. See + * MessageSession for more details. + * + * @author Jakob Schroeter + */ + class GLOOX_API MessageHandler + { + public: + /** + * Virtual Destructor. + */ + virtual ~MessageHandler() {} + + /** + * Reimplement this function if you want to be notified about + * incoming messages. + * @param msg The complete Message. + * @param session If this MessageHandler is used with a MessageSession, this parameter + * holds a pointer to that MessageSession. + * @since 1.0 + */ + virtual void handleMessage( const Message& msg, MessageSession* session = 0 ) = 0; + + }; + +} + +#endif // MESSAGEHANDLER_H__ diff --git a/libs/libgloox/messagesession.cpp b/libs/libgloox/messagesession.cpp new file mode 100644 index 0000000..e8fd442 --- /dev/null +++ b/libs/libgloox/messagesession.cpp @@ -0,0 +1,111 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#include "messagesession.h" +#include "messagefilter.h" +#include "messagehandler.h" +#include "clientbase.h" +#include "disco.h" +#include "message.h" +#include "util.h" + +namespace gloox +{ + + MessageSession::MessageSession( ClientBase* parent, const JID& jid, bool wantUpgrade, int types, bool honorTID ) + : m_parent( parent ), m_target( jid ), m_messageHandler( 0 ), + m_types( types ), m_wantUpgrade( wantUpgrade ), m_hadMessages( false ), m_honorThreadID( honorTID ) + { + if( m_parent ) + m_parent->registerMessageSession( this ); + } + + MessageSession::~MessageSession() + { + util::clearList( m_messageFilterList ); + } + + void MessageSession::handleMessage( Message& msg ) + { + if( m_wantUpgrade && msg.from().bare() == m_target.full() ) + setResource( msg.from().resource() ); + + if( !m_hadMessages ) + { + m_hadMessages = true; + if( msg.thread().empty() ) + { + m_thread = "gloox" + m_parent->getID(); + msg.setThread( m_thread ); + } + else + m_thread = msg.thread(); + } + + MessageFilterList::const_iterator it = m_messageFilterList.begin(); + for( ; it != m_messageFilterList.end(); ++it ) + (*it)->filter( msg ); + + if( m_messageHandler && !msg.body().empty() ) + m_messageHandler->handleMessage( msg, this ); + } + + void MessageSession::send( const std::string& message, const std::string& subject, const StanzaExtensionList& sel ) + { + if( !m_hadMessages ) + { + m_thread = "gloox" + m_parent->getID(); + m_hadMessages = true; + } + + Message m( Message::Chat, m_target.full(), message, subject, m_thread ); + m.setID( m_parent->getID() ); + decorate( m ); + + if( sel.size() ) + { + StanzaExtensionList::const_iterator it = sel.begin(); + for( ; it != sel.end(); ++it ) + m.addExtension( (*it)); + } + + m_parent->send( m ); + } + + void MessageSession::send( const Message& msg ) + { + m_parent->send( msg ); + } + + void MessageSession::decorate( Message& msg ) + { + util::ForEach( m_messageFilterList, &MessageFilter::decorate, msg ); + } + + void MessageSession::resetResource() + { + m_wantUpgrade = true; + m_target.setResource( EmptyString ); + } + + void MessageSession::setResource( const std::string& resource ) + { + m_target.setResource( resource ); + } + + void MessageSession::disposeMessageFilter( MessageFilter* mf ) + { + removeMessageFilter( mf ); + delete mf; + } + +} diff --git a/libs/libgloox/messagesession.h b/libs/libgloox/messagesession.h new file mode 100644 index 0000000..d2232cd --- /dev/null +++ b/libs/libgloox/messagesession.h @@ -0,0 +1,309 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef MESSAGESESSION_H__ +#define MESSAGESESSION_H__ + +#include "jid.h" +#include "gloox.h" + +#include +#include + +namespace gloox +{ + + class ClientBase; + class MessageFilter; + class MessageHandler; + class Message; + + /** + * @brief An abstraction of a message session between any two entities. + * + * This is an alternative interface to unmanaged messaging. The original interface, using the simple + * MessageHandler-derived interface, is based on an all-or-nothing approach. Once registered with + * ClientBase, a handler receives all message stanzas sent to this client and has to do any filtering + * on its own. + * + * MessageSession adds an abstraction to a chat conversation. A MessageSession is responsible for + * communicating with exactly one (full) JID. It is extensible with so-called MessageFilters, which can + * provide additional features such as Message Events, or Chat State Notifications. + * + * You can still use the old MessageHandler in parallel, but messages will not be relayed to both + * the generic MessageHandler and a MessageSession established for the sender's JID. The MessageSession + * takes precedence. + * + * Using MessageSessions has the following advantages over the plain old MessageHandler: + * @li automatic creation of MessageSessions + * @li filtering by JID + * @li automatic handling of threading (i.e., XMPP message threads) + * @li simpler sending of messages + * @li support for MessageFilters. + * + * @b Usage:
+ * Derive an object from MessageSessionHandler and reimplement handleMessageSession() to store your + * shiny new sessions somewhere, or to create a new chat window, or whatever. Register your + * object with a ClientBase instance using registerMessageSessionHandler(). In code: + * @code + * void MyClass::myFunc() + * { + * JID jid( "abc@example.org/gloox" ); + * j = new Client( jid, "password" ); + * [...] + * j->registerMessageSessionHandler( this, 0 ); + * } + * @endcode + * MyClass is a MessageSessionHandler here. + * + * In this example, MyClass needs to be MessageHandler, MessageEventHandler and + * ChatStateHandler, too. The handlers are registered with the session to receive the + * respective events. + * @code + * virtual void MyClass::handleMessageSession( MessageSession* session ) + * { + * // for this example only, we delete any earlier session + * if( m_session ) + * j->disposeMessageSession( m_session ); + * m_session = session; + * m_session->registerMessageHandler( this ); + * + * // the following is optional + * m_messageEventFilter = new MessageEventFilter( m_session ); + * m_messageEventFilter->registerMessageEventHandler( this ); + * m_chatStateFilter = new ChatStateFilter( m_session ); + * m_chatStateFilter->registerChatStateHandler( this ); + * } + * @endcode + * + * MessageEventHandler::handleMessageEvent() and ChatStateHandler::handleChatState() are called + * for incoming Message Events and Chat States, respectively. + * @code + * virtual void MyClass::handleMessageEvent( const JID& from, MessageEventType event ) + * { + * // display contact's Message Event + * } + * + * virtual void MyClass::handleChatState( const JID& from, ChatStateType state ) + * { + * // display contact's Chat State + * } + * @endcode + * + * To let the chat partner now that the user is typing a message or has closed the chat window, use + * raiseMessageEvent() and setChatState(), respectively. For example: + * @code + * // user is typing a message + * m_messageEventFilter->raiseMessageEvent( MessageEventComposing ); + * + * // acknowledge receiving of a message + * m_messageEventFilter->raiseMessageEvent( MessageEventDelivered ); + * + * // user is not actively paying attention to the chat + * m_chatStateFilter->setChatState( ChatStateInactive ); + * + * // user has closed the chat window + * m_chatStateFilter->setChatState( ChatStateGone ); + * @endcode + * + * To send a message to the chat partner of the session, use + * @ref send( const std::string& message, const std::string& subject, const StanzaExtensionList& ). + * You don't have to care about + * receipient, thread id, etc., they are added automatically. + * + * @code + * m_session->send( "Hello World!", "No Subject" ); + * @endcode + * + * To initiate a new chat session, all you have to do is create a new MessageSession and register + * a MessageHandler with it: + * @code + * MessageSession* MyClass::newSession( const JID& to ) + * { + * MessageSession* session = new MessageSession( m_client, to ); + * session->registerMessageHandler( this ); + * return session; + * } + * @endcode + * + * @note You should never delete a MessageSession manually. Use ClientBase::disposeMessageSession() + * instead. + * + * @author Jakob Schroeter + * @since 0.8 + */ + class GLOOX_API MessageSession + { + + friend class MessageFilter; + + public: + /** + * Constructs a new MessageSession for the given JID. + * It is recommended to supply a full JID, in other words, it should have a resource set. + * No resource can lead to unexpected behavior. A thread ID is generated and sent along + * with every message sent through this session. + * @param parent The ClientBase to use for communication. + * @param jid The remote contact's full JID. If you don't know the full JID (this is probably the + * most common case) but still want replies from the full JID to be handled by this MessageSession, + * set the @b wantUpgrade parameter to true (or leave it untouched). + * @param wantUpgrade This flag indicates whether gloox should try to match an incoming message + * from a full JID to this MessageSession. If unsure, use the default. You probably only want to use + * a non-default value if this MessageSession is supposed to talk directly to a server or component + * JID that has no resource. This 'upgrade' will only happen once. + * @param types ORed list of Message::MessageType values this MessageSession shall receive. + * Defaults to 0 which means any type is received. + * @param honorTID Indicates whether thread IDs should be honored when matching incoming messages to MessageSessions. The default is usually fine. + */ + MessageSession( ClientBase* parent, const JID& jid, bool wantUpgrade = true, int types = 0, bool honorTID = true ); + + /** + * Virtual destructor. + * + * @note You should never delete a MessageSession manually. Use ClientBase::disposeMessageSession() + * instead. + */ + virtual ~MessageSession(); + + /** + * Use this function to find out where this session points at. + * @return The receipient's JID. + */ + const JID& target() const { return m_target; } + + /** + * By default, a thread ID is sent with every message to identify + * messages belonging together. + * @returns The thread ID for this session. + */ + const std::string& threadID() const { return m_thread; } + + /** + * Use this function to set the session's thread ID if e.g. a specific thread is + * continued. It should not normally be needed to set the thread ID manually. + * @param thread The new thread ID. + */ + void setThreadID( const std::string& thread ) { m_thread = thread; } + + /** + * Indicates whether thread IDs are honored when matching incoming + * messages to MessageSessions. + * @return Whether thread IDs are honored. + */ + bool honorThreadID() const { return m_honorThreadID; } + + /** + * Use this function to associate a MessageHandler with this MessageSession. + * The MessageHandler will receive all messages sent from this MessageSession's + * remote contact. + * @param mh The MessageHandler to register. + */ + void registerMessageHandler( MessageHandler* mh ) + { m_messageHandler = mh; } + + /** + * This function clears the internal pointer to the MessageHandler and therefore + * disables message delivery. + */ + void removeMessageHandler() + { m_messageHandler = 0; } + + /** + * A convenience function to quickly send a message (optionally with subject). This is + * the preferred way to send a message from a MessageSession. + * @param message The message to send. + * @param subject The optional subject to send. + * @param sel An optional list of StanzaExtensions. The extensions will be owned by the message-to-be-sent; + * do not attempt to re-use or delete them. + */ + virtual void send( const std::string& message, const std::string& subject = EmptyString, + const StanzaExtensionList& sel = StanzaExtensionList() ); + + /** + * Use this function to hook a new MessageFilter into a MessageSession. + * The filter will be able to read and/or modify a message stanza's content. + * @note The MessageSession will become the owner of the filter, it will be + * deleted by MessageSession's destructor. To get rid of the filter before that, + * use disposeMessageFilter(). + * @param mf The MessageFilter to add. + */ + void registerMessageFilter( MessageFilter* mf ) + { m_messageFilterList.push_back( mf ); } + + /** + * Use this function to remove a MessageFilter from the MessageSession. + * @param mf The MessageFilter to remove. + * @note To remove and delete the MessageFilter in one step use disposeMessageFilter(). + */ + void removeMessageFilter( MessageFilter* mf ) + { m_messageFilterList.remove( mf ); } + + /** + * Use this function to remove and delete a MessageFilter from the MessageSession. + * @param mf The MessageFilter to remove and delete. + * @note To just remove (and not delete) the MessageFilter use removeMessageFilter(). + */ + void disposeMessageFilter( MessageFilter* mf ); + + /** + * Returns the message type this MessageSession wants to receive. + * @return ORed list of Message::MessageType values this MessageSession wants to receive. + */ + int types() const { return m_types; } + + /** + * This function resets the session's target JID to its bare form such that + * subsequently sent messages will be sent to that bare JID. The server will + * determine the best resource to deliver to. Useful if the target + * resource changed presence to e.g. away or offline. + */ + void resetResource(); + + /** + * This function can be used to feed a message into the session. Ususally, only + * ClientBase should call this function. + * @param msg A Message to feed into the session. + */ + virtual void handleMessage( Message& msg ); + + protected: + /** + * A wrapper around ClientBase::send(). You should @b not use this function to send a + * chat message because the Tag is not prepared accordingly (neither a thread ID is added nor is + * the message ran through the message filters). + * @param msg A Message to send. + */ + virtual void send( const Message& msg ); + void decorate( Message& msg ); + + ClientBase* m_parent; + JID m_target; + MessageHandler* m_messageHandler; + + private: + void setResource( const std::string& resource ); + + typedef std::list MessageFilterList; + MessageFilterList m_messageFilterList; + + std::string m_thread; + int m_types; + bool m_wantUpgrade; + bool m_hadMessages; + bool m_honorThreadID; + + }; + +} + +#endif // MESSAGESESSION_H__ diff --git a/libs/libgloox/messagesessionhandler.h b/libs/libgloox/messagesessionhandler.h new file mode 100644 index 0000000..f6c4003 --- /dev/null +++ b/libs/libgloox/messagesessionhandler.h @@ -0,0 +1,67 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef MESSAGESESSIONHANDLER_H__ +#define MESSAGESESSIONHANDLER_H__ + +#include "stanza.h" +#include "messagesession.h" + +namespace gloox +{ + + /** + * @brief A virtual interface which can be reimplemented to receive incoming message sessions. + * + * Derived classes can be registered as MessageSessionHandlers with the Client. + * If you have enabled automatic MessageSession creation by calling Client::setAutoMessageSession(), + * handleMessageSession() will be called if a message stanza arrives for which there is no + * MessageSession yet. + * + * @author Jakob Schroeter + * @since 0.8 + */ + class GLOOX_API MessageSessionHandler + { + public: + /** + * Virtual Destructor. + */ + virtual ~MessageSessionHandler() {} + + /** + * Reimplement this function if you want to be notified about + * incoming messages by means of automatically created MessageSessions. + * You receive ownership of the supplied session (@b not the stanza) and + * are responsible for deleting it at the end of its life. + * + * @note Make sure to read the note in ClientBase::registerMessageSessionHandler() + * regarding the feeding of decorators. + * + * @note After receiving a MessageSession your object is the owner and is responsible + * for the destruction of the session. + * + * @note If you don't need the MessageSession, you should not delete it here. You will + * get an endless loop if you do. + * + * @note You should register your MessageHandler here, or else the first message + * (that caused the MessageSession to be created) may get lost. + * + * @param session The new MessageSession. + */ + virtual void handleMessageSession( MessageSession* session ) = 0; + }; + +} + +#endif // MESSAGESESSIONHANDLER_H__ diff --git a/libs/libgloox/mucinvitationhandler.h b/libs/libgloox/mucinvitationhandler.h new file mode 100644 index 0000000..fed2a06 --- /dev/null +++ b/libs/libgloox/mucinvitationhandler.h @@ -0,0 +1,72 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef MUCINVITATIONHANDLER_H__ +#define MUCINVITATIONHANDLER_H__ + +#include "clientbase.h" +#include "macros.h" +#include "jid.h" +#include "mucroom.h" + +#include + +namespace gloox +{ + + /** + * @brief A handler that can be used to receive invitations to MUC rooms. + * + * Register a derived class with ClientBase::registerMUCInvitationHandler(). + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API MUCInvitationHandler + { + public: + /** + * Constructor. Prepares the given ClientBase for receiving MUC invitations.. + * @param parent A ClientBase instance to prepare. + */ + MUCInvitationHandler( ClientBase* parent ) + { + if( parent ) + parent->registerStanzaExtension( new MUCRoom::MUCUser() ); + } + + /** + * Virtual Destructor. + */ + virtual ~MUCInvitationHandler() {} + + /** + * This function is called for incoming invitations to MUC rooms. + * @param room The JID of the room you're being invited to. + * @param from The JID of the inviter. + * @param reason A reason for the invitation. + * @param body The body of the message. May contain a MUC-service generated invitation message. + * @param password Optionally, a password for the room. + * @param cont Indicates whether or not the multi-user chat is a continuation of a private chat. + * @param thread An optional thread identifier in case this is a + * continued chat. + */ + virtual void handleMUCInvitation( const JID& room, const JID& from, const std::string& reason, + const std::string& body, const std::string& password, + bool cont, const std::string& thread ) = 0; + }; + +} + +#endif // MUCINVITATIONHANDLER_H__ diff --git a/libs/libgloox/mucmessagesession.cpp b/libs/libgloox/mucmessagesession.cpp new file mode 100644 index 0000000..36f97b6 --- /dev/null +++ b/libs/libgloox/mucmessagesession.cpp @@ -0,0 +1,54 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "mucmessagesession.h" +#include "clientbase.h" +#include "message.h" +#include "messagehandler.h" + +namespace gloox +{ + + MUCMessageSession::MUCMessageSession( ClientBase* parent, const JID& jid ) + : MessageSession( parent, jid, false, Message::Groupchat | Message::Chat + | Message::Normal | Message::Error, + false ) + { + } + + MUCMessageSession::~MUCMessageSession() + { + } + + void MUCMessageSession::handleMessage( Message& msg ) + { + if( m_messageHandler ) + m_messageHandler->handleMessage( msg ); + } + + void MUCMessageSession::send( const std::string& message ) + { + Message m( Message::Groupchat, m_target, message ); + +// decorate( m ); + + m_parent->send( m ); + } + + void MUCMessageSession::setSubject( const std::string& subject ) + { + Message m( Message::Groupchat, m_target.bareJID(), EmptyString, subject ); + m_parent->send( m ); + } + +} diff --git a/libs/libgloox/mucmessagesession.h b/libs/libgloox/mucmessagesession.h new file mode 100644 index 0000000..ece85c9 --- /dev/null +++ b/libs/libgloox/mucmessagesession.h @@ -0,0 +1,66 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef MUCMESSAGESESSION_H__ +#define MUCMESSAGESESSION_H__ + +#include "messagesession.h" + +namespace gloox +{ + + class ClientBase; + + /** + * @brief This is a MessageSession, adapted to be used in a MUC context. + * + * This class is used internally by MUCRoom. You should not need to use it directly. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API MUCMessageSession : public MessageSession + { + public: + /** + * Creates a new MUCMessageSession. + * @param parent The ClientBase to use for communication. + * @param jid The @b bare JID of the MUC room. + */ + MUCMessageSession( ClientBase* parent, const JID& jid ); + + /** + * Virtual Destructor. + */ + virtual ~MUCMessageSession(); + + /** + * Use this function to send a message to all room occupants. + * @param message The message to send. + */ + virtual void send( const std::string& message ); + + /** + * Use this function to set a new room subject. + * @param subject The new room subject. + */ + virtual void setSubject( const std::string& subject ); + + // reimplemented from MessageSession + virtual void handleMessage( Message& msg ); + + }; + +} + +#endif // MUCMESSAGESESSION_H__ diff --git a/libs/libgloox/mucroom.cpp b/libs/libgloox/mucroom.cpp new file mode 100644 index 0000000..d7074fb --- /dev/null +++ b/libs/libgloox/mucroom.cpp @@ -0,0 +1,1317 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "mucroom.h" +#include "clientbase.h" +#include "dataform.h" +#include "presence.h" +#include "disco.h" +#include "mucmessagesession.h" +#include "message.h" +#include "error.h" +#include "util.h" +#include "tag.h" + +namespace gloox +{ + + // ---- MUCRoom::MUCAdmin ---- + /* Error type values */ + static const char* affiliationValues [] = { + "none", + "outcast", + "member", + "owner", + "admin" + }; + + /* Stanza error values */ + static const char* roleValues [] = { + "none", + "visitor", + "participant", + "moderator", + }; + + /** Strings indicating the type of history to request. */ + const char* historyTypeValues[] = + { + "maxchars", "maxstanzas", "seconds", "since" + }; + + static inline MUCRoomAffiliation affiliationType( const std::string& type ) + { + return (MUCRoomAffiliation)util::lookup( type, affiliationValues ); + } + + static inline MUCRoomRole roleType( const std::string& type ) + { + return (MUCRoomRole)util::lookup( type, roleValues ); + } + + MUCRoom::MUCAdmin::MUCAdmin( MUCRoomRole role, const std::string& nick, + const std::string& reason ) + : StanzaExtension( ExtMUCAdmin ), m_affiliation( AffiliationInvalid ), m_role( role ) + { + m_list.push_back( MUCListItem( nick, role, reason ) ); + } + + MUCRoom::MUCAdmin::MUCAdmin( MUCRoomAffiliation affiliation, const std::string& nick, + const std::string& reason ) + : StanzaExtension( ExtMUCAdmin ), m_affiliation( affiliation ), m_role( RoleInvalid ) + { + m_list.push_back( MUCListItem( nick, affiliation, reason ) ); + } + + MUCRoom::MUCAdmin::MUCAdmin( MUCOperation operation, const MUCListItemList& jids ) + : StanzaExtension( ExtMUCAdmin ), m_list( jids ), m_affiliation( AffiliationInvalid ), + m_role( RoleInvalid ) + { + switch( operation ) + { + case StoreVoiceList: + case RequestVoiceList: + m_role = RoleParticipant; + break; + case StoreModeratorList: + case RequestModeratorList: + m_role = RoleModerator; + break; + case StoreBanList: + case RequestBanList: + m_affiliation = AffiliationOutcast; + break; + case StoreMemberList: + case RequestMemberList: + m_affiliation = AffiliationMember; + break; + case StoreOwnerList: + case RequestOwnerList: + m_affiliation = AffiliationOwner; + break; + case StoreAdminList: + case RequestAdminList: + m_affiliation = AffiliationAdmin; + break; + default: + return; + break; + } + + if( m_list.empty() ) + m_list.push_back( MUCListItem( JID() ) ); + } + + MUCRoom::MUCAdmin::MUCAdmin( const Tag* tag ) + : StanzaExtension( ExtMUCAdmin ), m_affiliation( AffiliationInvalid ), m_role( RoleInvalid ) + { + if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_MUC_ADMIN ) + return; + + const TagList& items = tag->findChildren( "item" ); + TagList::const_iterator it = items.begin(); + for( ; it != items.end(); ++it ) + { + m_list.push_back( MUCListItem( JID( (*it)->findAttribute( "jid" ) ), + roleType( (*it)->findAttribute( "role" ) ), + affiliationType( (*it)->findAttribute( "affiliation" ) ), + (*it)->findAttribute( "nick" ) ) ); + if( m_role == RoleInvalid ) + m_role = roleType( (*it)->findAttribute( "role" ) ); + if( m_affiliation == AffiliationInvalid ) + m_affiliation = affiliationType( (*it)->findAttribute( "affiliation" ) ); + } + } + + MUCRoom::MUCAdmin::~MUCAdmin() + { + } + + const std::string& MUCRoom::MUCAdmin::filterString() const + { + static const std::string filter = "/iq/query[@xmlns='" + XMLNS_MUC_ADMIN + "']"; + return filter; + } + + Tag* MUCRoom::MUCAdmin::tag() const + { + Tag* t = new Tag( "query" ); + t->setXmlns( XMLNS_MUC_ADMIN ); + + if( m_list.empty() || ( m_affiliation == AffiliationInvalid && m_role == RoleInvalid ) ) + return t; + + MUCListItemList::const_iterator it = m_list.begin(); + for( ; it != m_list.end(); ++it ) + { + Tag* i = new Tag( t, "item" ); + if( (*it).jid() ) + i->addAttribute( "jid", (*it).jid().bare() ); + if( !(*it).nick().empty() ) + i->addAttribute( "nick", (*it).nick() ); + + MUCRoomRole rol = RoleInvalid; + if( (*it).role() != RoleInvalid ) + rol = (*it).role(); + else if( m_role != RoleInvalid ) + rol = m_role; + if( rol != RoleInvalid ) + i->addAttribute( "role", util::lookup( rol, roleValues ) ); + + MUCRoomAffiliation aff = AffiliationInvalid; + if( (*it).affiliation() != AffiliationInvalid ) + aff = (*it).affiliation(); + else if( m_affiliation != AffiliationInvalid ) + aff = m_affiliation; + if( aff != AffiliationInvalid ) + i->addAttribute( "affiliation", util::lookup( aff, affiliationValues ) ); + if( !(*it).reason().empty() ) + new Tag( i, "reason", (*it).reason() ); + } + + return t; + } + // ---- ~MUCRoom::MUCAdmin ---- + + // ---- MUCRoom::MUCOwner ---- + MUCRoom::MUCOwner::MUCOwner( QueryType type, DataForm* form ) + : StanzaExtension( ExtMUCOwner ), m_type( type ), m_form( form ) + { + m_valid = true; + + if( m_form ) + return; + + switch( type ) + { + case TypeCancelConfig: + m_form = new DataForm( TypeCancel ); + break; + case TypeInstantRoom: + m_form = new DataForm( TypeSubmit ); + break; + default: + break; + } + } + + MUCRoom::MUCOwner::MUCOwner( const JID& alternate, const std::string& reason, + const std::string& password ) + : StanzaExtension( ExtMUCOwner ), m_type( TypeDestroy ), m_jid( alternate ), + m_reason( reason ), m_pwd( password ), m_form( 0 ) + { + m_valid = true; + } + + MUCRoom::MUCOwner::MUCOwner( const Tag* tag ) + : StanzaExtension( ExtMUCOwner ), m_type( TypeIncomingTag ), m_form( 0 ) + { + if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_MUC_OWNER ) + return; + + const TagList& l = tag->children(); + TagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + { + const std::string& name = (*it)->name(); + if( name == "x" && (*it)->xmlns() == XMLNS_X_DATA ) + { + m_form = new DataForm( (*it) ); + break; + } + else if( name == "destroy" ) + { + m_type = TypeDestroy; + m_jid = (*it)->findAttribute( "jid" ); + m_pwd = (*it)->findCData( "/query/destroy/password" ); + m_reason = (*it)->findCData( "/query/destroy/reason" ); + break; + } + } + m_valid = true; + } + + MUCRoom::MUCOwner::~MUCOwner() + { + delete m_form; + } + + const std::string& MUCRoom::MUCOwner::filterString() const + { + static const std::string filter = "/iq/query[@xmlns='" + XMLNS_MUC_OWNER + "']"; + return filter; + } + + Tag* MUCRoom::MUCOwner::tag() const + { + if( !m_valid ) + return 0; + + Tag* t = new Tag( "query" ); + t->setXmlns( XMLNS_MUC_OWNER ); + + switch( m_type ) + { + case TypeInstantRoom: + case TypeSendConfig: + case TypeCancelConfig: + case TypeIncomingTag: + if( m_form ) + t->addChild( m_form->tag() ); + break; + case TypeDestroy: + { + Tag* d = new Tag( t, "destroy" ); + if( m_jid ) + d->addAttribute( "jid", m_jid.bare() ); + + if( !m_reason.empty() ) + new Tag( d, "reason", m_reason ); + + if( !m_pwd.empty() ) + new Tag( d, "password", m_pwd ); + + break; + } + case TypeRequestConfig: + case TypeCreate: + default: + break; + } + + return t; + } + // ---- ~MUCRoom::MUCOwner ---- + + // ---- MUCRoom::MUCUser ---- + MUCRoom::MUCUser::MUCUser( MUCUserOperation operation, const std::string& to, + const std::string& reason, const std::string& thread ) + : StanzaExtension( ExtMUCUser ), m_affiliation( AffiliationInvalid ), m_role( RoleInvalid ), + m_jid( new std::string( to ) ), m_actor( 0 ), + m_thread( thread.empty() ? 0 : new std::string( thread ) ), + m_reason( new std::string( reason ) ), m_newNick( 0 ), m_password( 0 ), m_alternate( 0 ), + m_operation( operation ), + m_flags( 0 ), m_del( false ), m_continue( !thread.empty() ) + { + } + + MUCRoom::MUCUser::MUCUser( const Tag* tag ) + : StanzaExtension( ExtMUCUser ), m_affiliation( AffiliationInvalid ), m_role( RoleInvalid ), + m_jid( 0 ), m_actor( 0 ), m_thread( 0 ), m_reason( 0 ), m_newNick( 0 ), + m_password( 0 ), m_alternate( 0 ), m_operation( OpNone ), + m_flags( 0 ), m_del( false ), m_continue( false ) + { + if( !tag || tag->name() != "x" || tag->xmlns() != XMLNS_MUC_USER ) + return; + + const Tag* t = 0; + const TagList& l = tag->children(); + TagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + { + if( (*it)->name() == "item" ) + { + m_affiliation = getEnumAffiliation( (*it)->findAttribute( "affiliation" ) ); + m_role = getEnumRole( (*it)->findAttribute( "role" ) ); + + if( (*it)->hasAttribute( "jid" ) ) + m_jid = new std::string( (*it)->findAttribute( "jid" ) ); + + if( ( t = (*it)->findChild( "actor" ) ) ) + m_actor = new std::string( t->findAttribute( "jid" ) ); + + if( ( t = (*it)->findChild( "reason" ) ) ) + m_reason = new std::string( t->cdata() ); + + if( (*it)->hasAttribute( "nick" ) ) + m_newNick = new std::string( (*it)->findAttribute( "nick" ) ); + } + else if( (*it)->name() == "status" ) + { + const std::string& code = (*it)->findAttribute( "code" ); + if( code == "100" ) + m_flags |= FlagNonAnonymous; + else if( code == "101" ) + m_flags |= UserAffiliationChangedWNR; + else if( code == "110" ) + m_flags |= UserSelf; + else if( code == "170" ) + m_flags |= FlagPublicLogging; + else if( code == "201" ) + m_flags |= UserNewRoom; + else if( code == "210" ) + m_flags |= UserNickAssigned; + else if( code == "301" ) + m_flags |= UserBanned; + else if( code == "303" ) + m_flags |= UserNickChanged; + else if( code == "307" ) + m_flags |= UserKicked; + else if( code == "321" ) + m_flags |= UserAffiliationChanged; + else if( code == "322" ) + m_flags |= UserMembershipRequired; + else if( code == "332" ) + m_flags |= UserRoomShutdown; + } + else if( (*it)->name() == "destroy" ) + { + m_del = true; + if( (*it)->hasAttribute( "jid" ) ) + m_alternate = new std::string( (*it)->findAttribute( "jid" ) ); + + if( ( t = (*it)->findChild( "reason" ) ) ) + m_reason = new std::string( t->cdata() ); + + m_flags |= UserRoomDestroyed; + } + else if( (*it)->name() == "invite" ) + { + m_operation = OpInviteFrom; + m_jid = new std::string( (*it)->findAttribute( "from" ) ); + if( m_jid->empty() ) + { + m_operation = OpInviteTo; + m_jid->assign( (*it)->findAttribute( "to" ) ); + } + if( (*it)->hasChild( "reason" ) ) + m_reason = new std::string( (*it)->findChild( "reason" )->cdata() ); + if( (*it)->hasChild( "continue" ) ) + { + m_continue = true; + m_thread = new std::string( (*it)->findChild( "continue" )->findAttribute( "thread" ) ); + } + } + else if( (*it)->name() == "decline" ) + { + m_operation = OpDeclineFrom; + m_jid = new std::string( (*it)->findAttribute( "from" ) ); + if( m_jid->empty() ) + { + m_operation = OpDeclineTo; + m_jid->assign( (*it)->findAttribute( "from" ) ); + } + if( (*it)->hasChild( "reason" ) ) + m_reason = new std::string( (*it)->findChild( "reason" )->cdata() ); + } + else if( (*it)->name() == "password" ) + { + m_password = new std::string( (*it)->cdata() ); + } + } + } + + MUCRoom::MUCUser::~MUCUser() + { + delete m_jid; + delete m_actor; + delete m_thread; + delete m_reason; + delete m_newNick; + delete m_password; + delete m_alternate; + } + + MUCRoomRole MUCRoom::MUCUser::getEnumRole( const std::string& role ) + { + if( role == "moderator" ) + return RoleModerator; + if( role == "participant" ) + return RoleParticipant; + if( role == "visitor" ) + return RoleVisitor; + return RoleNone; + } + + MUCRoomAffiliation MUCRoom::MUCUser::getEnumAffiliation( const std::string& affiliation ) + { + if( affiliation == "owner" ) + return AffiliationOwner; + if( affiliation == "admin" ) + return AffiliationAdmin; + if( affiliation == "member" ) + return AffiliationMember; + if( affiliation == "outcast" ) + return AffiliationOutcast; + return AffiliationNone; + } + + const std::string& MUCRoom::MUCUser::filterString() const + { + static const std::string filter = "/presence/x[@xmlns='" + XMLNS_MUC_USER + "']" + "|/message/x[@xmlns='" + XMLNS_MUC_USER + "']"; + return filter; + } + + Tag* MUCRoom::MUCUser::tag() const + { + Tag* t = new Tag( "x" ); + t->setXmlns( XMLNS_MUC_USER ); + + if( m_affiliation != AffiliationInvalid || m_role != RoleInvalid ) + { + Tag* i = new Tag( t, "item" ); + if( m_jid ) + i->addAttribute( "jid", *m_jid ); + if( m_role != RoleInvalid ) + i->addAttribute( "role", util::lookup( m_role, roleValues ) ); + if( m_affiliation != AffiliationInvalid ) + i->addAttribute( "affiliation", util::lookup( m_affiliation, affiliationValues ) ); + + if( m_actor ) + new Tag( i, "actor", "jid", *m_actor ); + + if( m_flags & FlagNonAnonymous ) + new Tag( t, "status", "code", "100" ); + if( m_flags & UserAffiliationChangedWNR ) + new Tag( t, "status", "code", "101" ); + if( m_flags & UserSelf ) + new Tag( t, "status", "code", "110" ); + if( m_flags & FlagPublicLogging ) + new Tag( t, "status", "code", "170" ); + if( m_flags & UserNewRoom ) + new Tag( t, "status", "code", "201" ); + if( m_flags & UserNickAssigned ) + new Tag( t, "status", "code", "210" ); + if( m_flags & UserBanned ) + new Tag( t, "status", "code", "301" ); + if( m_flags & UserNickChanged ) + new Tag( t, "status", "code", "303" ); + if( m_flags & UserKicked ) + new Tag( t, "status", "code", "307" ); + if( m_flags & UserAffiliationChanged ) + new Tag( t, "status", "code", "321" ); + if( m_flags & UserMembershipRequired ) + new Tag( t, "status", "code", "322" ); + if( m_flags & UserRoomShutdown ) + new Tag( t, "status", "code", "332" ); + } + else if( m_del ) + { + Tag* d = new Tag( t, "destroy" ); + if( m_alternate ) + d->addAttribute( "jid", *m_alternate ); + if( m_reason ) + new Tag( d, "reason", *m_reason ); + } + else if( m_operation != OpNone && m_jid ) + { + Tag* d = 0; + if( m_operation == OpInviteTo ) + d = new Tag( t, "invite", "to", *m_jid ); + else if( m_operation == OpInviteFrom ) + d = new Tag( t, "invite", "from", *m_jid ); + else if( m_operation == OpDeclineTo ) + d = new Tag( t, "decline", "to", *m_jid ); + else if( m_operation == OpDeclineFrom ) + d = new Tag( t, "decline", "from", *m_jid ); + + if( m_reason ) + new Tag( d, "reason", *m_reason ); + + if( m_continue ) + { + Tag* c = new Tag( d, "continue" ); + if( m_thread ) + c->addAttribute( "thread", *m_thread ); + } + + if( m_password ) + new Tag( t, "password", *m_password ); + + } + + return t; + } + // ---- ~MUCRoom::MUCUser ---- + + // ---- MUCRoom::MUC ---- + MUCRoom::MUC::MUC( const std::string& password, + MUCRoom::HistoryRequestType historyType, + const std::string& historySince, + int historyValue ) + : StanzaExtension( ExtMUC ), + m_password( password.empty() ? 0 : new std::string( password ) ), + m_historySince( new std::string( historySince ) ), + m_historyType( historyType ), m_historyValue( historyValue ) + { + } + + MUCRoom::MUC::MUC( const Tag* tag ) + : StanzaExtension( ExtMUC ), + m_password( 0 ), m_historySince( 0 ), + m_historyType( HistoryUnknown ), m_historyValue( 0 ) + { + if( !tag || tag->name() != "x" || tag->xmlns() != XMLNS_MUC_USER ) + return; + + const TagList& l = tag->children(); + TagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + { + if( (*it)->name() == "history" ) + { + if( (*it)->hasAttribute( "seconds" ) ) + m_historyValue = atoi( (*it)->findAttribute( "seconds" ).c_str() ); + else if( (*it)->hasAttribute( "maxstanzas" ) ) + m_historyValue = atoi( (*it)->findAttribute( "maxstanzas" ).c_str() ); + else if( (*it)->hasAttribute( "maxchars" ) ) + m_historyValue = atoi( (*it)->findAttribute( "maxchars" ).c_str() ); + else if( (*it)->hasAttribute( "since" ) ) + m_historySince = new std::string( (*it)->findAttribute( "since" ) ); + } + else if( (*it)->name() == "password" ) + { + m_password = new std::string( (*it)->cdata() ); + } + } + } + + MUCRoom::MUC::~MUC() + { + delete m_password; + delete m_historySince; + } + + const std::string& MUCRoom::MUC::filterString() const + { + static const std::string filter = "/presence/x[@xmlns='" + XMLNS_MUC + "']"; + return filter; + } + + Tag* MUCRoom::MUC::tag() const + { + Tag* t = new Tag( "x" ); + t->setXmlns( XMLNS_MUC ); + + if( m_historyType != HistoryUnknown ) + { + const std::string& histStr = util::lookup( m_historyType, historyTypeValues ); + Tag* h = new Tag( t, "history" ); + if( m_historyType == HistorySince && m_historySince ) + h->addAttribute( histStr, *m_historySince ); + else + h->addAttribute( histStr, m_historyValue ); + } + + if( m_password ) + new Tag( t, "password", *m_password ); + + return t; + } + // ---- ~MUCRoom::MUC ---- + + // --- MUCRoom ---- + MUCRoom::MUCRoom( ClientBase* parent, const JID& nick, MUCRoomHandler* mrh, + MUCRoomConfigHandler* mrch ) + : m_parent( parent ), m_nick( nick ), m_joined( false ), m_roomHandler( mrh ), + m_roomConfigHandler( mrch ), m_affiliation( AffiliationNone ), m_role( RoleNone ), + m_historyType( HistoryUnknown ), m_historyValue( 0 ), m_flags( 0 ), + m_creationInProgress( false ), m_configChanged( false ), + m_publishNick( false ), m_publish( false ), m_unique( false ) + { + if( m_parent ) + { + m_parent->registerStanzaExtension( new MUCAdmin() ); + m_parent->registerStanzaExtension( new MUCOwner() ); + m_parent->registerStanzaExtension( new MUCUser() ); + m_parent->registerStanzaExtension( new MUC() ); + m_parent->registerStanzaExtension( new DelayedDelivery() ); + } + } + + MUCRoom::~MUCRoom() + { + if( m_joined ) + leave(); + + if( m_parent ) + { + if( m_publish ) + m_parent->disco()->removeNodeHandler( this, XMLNS_MUC_ROOMS ); + + m_parent->removeIDHandler( this ); +// m_parent->removeStanzaExtension( ExtMUCAdmin ); // don't remove, other rooms might need it +// m_parent->removeStanzaExtension( ExtMUCOwner ); + m_parent->removePresenceHandler( m_nick.bareJID(), this ); + m_parent->disco()->removeDiscoHandler( this ); + } + } + + void MUCRoom::join( Presence::PresenceType type, const std::string& status, int priority ) + { + if( m_joined || !m_parent ) + return; + + m_parent->registerPresenceHandler( m_nick.bareJID(), this ); + + m_session = new MUCMessageSession( m_parent, m_nick.bareJID() ); + m_session->registerMessageHandler( this ); + + Presence pres( type, m_nick.full(), status, priority ); + pres.addExtension( new MUC( m_password, m_historyType, m_historySince, m_historyValue ) ); + m_joined = true; + m_parent->send( pres ); + } + + void MUCRoom::leave( const std::string& msg ) + { + if( !m_joined ) + return; + + if( m_parent ) + { + Presence pres( Presence::Unavailable, m_nick.full(), msg ); + m_parent->send( pres ); + m_parent->removePresenceHandler( m_nick.bareJID(), this ); + m_parent->disposeMessageSession( m_session ); + } + + m_session = 0; + m_joined = false; + } + + void MUCRoom::destroy( const std::string& reason, const JID& alternate, const std::string& password ) + { + if( !m_parent ) + return; + + const std::string& id = m_parent->getID(); + IQ iq( IQ::Set, m_nick.bareJID(), id ); + iq.addExtension( new MUCOwner( alternate, reason, password ) ); + m_parent->send( iq, this, DestroyRoom ); + } + + void MUCRoom::send( const std::string& message ) + { + if( m_session && m_joined ) + m_session->send( message ); + } + + void MUCRoom::setSubject( const std::string& subject ) + { + if( m_session && m_joined ) + m_session->setSubject( subject ); + } + + void MUCRoom::setNick( const std::string& nick ) + { + if( m_parent && m_joined ) + { + m_newNick = nick; + + Presence p( Presence::Available, m_nick.bare() + "/" + m_newNick ); + m_parent->send( p ); + } + else + m_nick.setResource( nick ); + } + + void MUCRoom::getRoomInfo() + { + if( m_parent ) + m_parent->disco()->getDiscoInfo( m_nick.bare(), EmptyString, this, GetRoomInfo ); + } + + void MUCRoom::getRoomItems() + { + if( m_parent ) + m_parent->disco()->getDiscoItems( m_nick.bare(), EmptyString, this, GetRoomItems ); + } + + void MUCRoom::setPresence( Presence::PresenceType presence, const std::string& msg ) + { + if( m_parent && presence != Presence::Unavailable && m_joined ) + { + Presence p( presence, m_nick.full(), msg ); + m_parent->send( p ); + } + } + + void MUCRoom::invite( const JID& invitee, const std::string& reason, const std::string& thread ) + { + if( !m_parent || !m_joined ) + return; + + Message msg( Message::Normal, m_nick.bareJID() ); + msg.addExtension( new MUCUser( OpInviteTo, invitee.bare(), reason, thread ) ); + m_parent->send( msg ); + } + + Message* MUCRoom::declineInvitation( const JID& room, const JID& invitor, const std::string& reason ) + { + Message* msg = new Message( Message::Normal, room.bare() ); + msg->addExtension( new MUCUser( OpDeclineTo, invitor.bare(), reason ) ); + return msg; + } + + void MUCRoom::setPublish( bool publish, bool publishNick ) + { + m_publish = publish; + m_publishNick = publishNick; + + if( !m_parent ) + return; + + if( m_publish ) + m_parent->disco()->registerNodeHandler( this, XMLNS_MUC_ROOMS ); + else + m_parent->disco()->removeNodeHandler( this, XMLNS_MUC_ROOMS ); + } + + void MUCRoom::addHistory( const std::string& message, const JID& from, const std::string& stamp ) + { + if( !m_joined || !m_parent ) + return; + + Message m( Message::Groupchat, m_nick.bareJID(), message ); + m.addExtension( new DelayedDelivery( from, stamp ) ); + m_parent->send( m ); + } + + void MUCRoom::setRequestHistory( int value, MUCRoom::HistoryRequestType type ) + { + m_historyType = type; + m_historySince = EmptyString; + m_historyValue = value; + } + + void MUCRoom::setRequestHistory( const std::string& since ) + { + m_historyType = HistorySince; + m_historySince = since; + m_historyValue = 0; + } + + Message* MUCRoom::createDataForm( const JID& room, const DataForm* df ) + { + Message* m = new Message( Message::Normal, room.bare() ); + m->addExtension( df ); + return m; + } + + void MUCRoom::requestVoice() + { + if( !m_parent || !m_joined ) + return; + + DataForm* df = new DataForm( TypeSubmit ); + df->addField( DataFormField::TypeNone, "FORM_TYPE", XMLNS_MUC_REQUEST ); + df->addField( DataFormField::TypeTextSingle, "muc#role", "participant", "Requested role" ); + + Message m( Message::Normal, m_nick.bare() ); + m.addExtension( df ); + + m_parent->send( m ); + } + + void MUCRoom::setRole( const std::string& nick, MUCRoomRole role, + const std::string& reason ) + { + if( !m_parent || !m_joined || nick.empty() || role == RoleInvalid ) + return; + + MUCOperation action = InvalidOperation; + switch( role ) + { + case RoleNone: + action = SetRNone; + break; + case RoleVisitor: + action = SetVisitor; + break; + case RoleParticipant: + action = SetParticipant; + break; + case RoleModerator: + action = SetModerator; + break; + default: + break; + } + + IQ iq( IQ::Set, m_nick.bareJID() ); + iq.addExtension( new MUCAdmin( role, nick, reason ) ); + + m_parent->send( iq, this, action ); + } + + void MUCRoom::setAffiliation( const std::string& nick, MUCRoomAffiliation affiliation, + const std::string& reason ) + { + if( !m_parent || !m_joined || nick.empty() || affiliation == AffiliationInvalid ) + return; + + MUCOperation action = InvalidOperation; + switch( affiliation ) + { + case AffiliationOutcast: + action = SetOutcast; + break; + case AffiliationNone: + action = SetANone; + break; + case AffiliationMember: + action = SetMember; + break; + case AffiliationAdmin: + action = SetAdmin; + break; + case AffiliationOwner: + action = SetOwner; + break; + default: + break; + } + + IQ iq( IQ::Set, m_nick.bareJID() ); + iq.addExtension( new MUCAdmin( affiliation, nick, reason ) ); + + m_parent->send( iq, this, action ); + } + + void MUCRoom::requestList( MUCOperation operation ) + { + if( !m_parent || !m_joined || !m_roomConfigHandler ) + return; + + IQ iq( IQ::Get, m_nick.bareJID() ); + iq.addExtension( new MUCAdmin( operation ) ); + m_parent->send( iq, this, operation ); + } + + void MUCRoom::storeList( const MUCListItemList items, MUCOperation operation ) + { + if( !m_parent || !m_joined ) + return; + + IQ iq( IQ::Set, m_nick.bareJID() ); + iq.addExtension( new MUCAdmin( operation , items ) ); + m_parent->send( iq, this, operation ); + } + + void MUCRoom::handlePresence( const Presence& presence ) + { + if( ( presence.from().bare() != m_nick.bare() ) || !m_roomHandler ) + return; + + if( presence.subtype() == Presence::Error ) + { + if( m_newNick.empty() ) + { + m_parent->removePresenceHandler( m_nick.bareJID(), this ); + m_parent->disposeMessageSession( m_session ); + m_joined = false; + m_session = 0; + } + else + m_newNick = ""; + + m_roomHandler->handleMUCError( this, presence.error() + ? presence.error()->error() + : StanzaErrorUndefined ); + } + else + { + const MUCUser* mu = presence.findExtension( ExtMUCUser ); + if( !mu ) + return; + + MUCRoomParticipant party; + party.nick = new JID( presence.from() ); + party.status = presence.status(); + party.affiliation = mu->affiliation(); + party.role = mu->role(); + party.jid = mu->jid() ? new JID( *(mu->jid()) ) : 0; + party.actor = mu->actor() ? new JID( *(mu->actor()) ) : 0; + party.reason = mu->reason() ? *(mu->reason()) : EmptyString; + party.newNick = mu->newNick() ? *(mu->newNick()) : EmptyString; + party.alternate = mu->alternate() ? new JID( *(mu->alternate()) ) : 0; + party.flags = mu->flags(); + + if( party.flags & FlagNonAnonymous ) + setNonAnonymous(); + + if( party.flags & UserSelf ) + { + m_role = party.role; + m_affiliation = party.affiliation; + } + if( party.flags & UserNewRoom ) + { + m_creationInProgress = true; + if( instantRoomHook() || m_roomHandler->handleMUCRoomCreation( this ) ) + acknowledgeInstantRoom(); + } + if( party.flags & UserNickAssigned ) + m_nick.setResource( presence.from().resource() ); + + if( party.flags & UserNickChanged && !party.newNick.empty() + && m_nick.resource() == presence.from().resource() + && party.newNick == m_newNick ) + party.flags |= UserSelf; + + if( party.flags & UserNickChanged && party.flags & UserSelf && !party.newNick.empty() ) + m_nick.setResource( party.newNick ); + + if( m_roomHandler ) + m_roomHandler->handleMUCParticipantPresence( this, party, presence ); + + delete party.nick; + } + } + + void MUCRoom::instantRoom( int context ) + { + if( !m_creationInProgress || !m_parent || !m_joined ) + return; + + IQ iq( IQ::Set, m_nick.bareJID() ); + iq.addExtension( new MUCOwner( context == CreateInstantRoom + ? MUCOwner::TypeInstantRoom : MUCOwner::TypeCancelConfig ) ); + + m_parent->send( iq, this, context ); + + m_creationInProgress = false; + } + + void MUCRoom::requestRoomConfig() + { + if( !m_parent || !m_joined ) + return; + + IQ iq( IQ::Get, m_nick.bareJID() ); + iq.addExtension( new MUCOwner( MUCOwner::TypeRequestConfig ) ); + + m_parent->send( iq, this, RequestRoomConfig ); + + if( m_creationInProgress ) + m_creationInProgress = false; + } + + void MUCRoom::setRoomConfig( DataForm* form ) + { + if( !m_parent || !m_joined ) + return; + + IQ iq( IQ::Set, m_nick.bareJID() ); + iq.addExtension( new MUCOwner( MUCOwner::TypeSendConfig, form ) ); + + m_parent->send( iq, this, SendRoomConfig ); + } + + void MUCRoom::setNonAnonymous() + { + m_flags |= FlagNonAnonymous; + m_flags &= ~( FlagSemiAnonymous | FlagFullyAnonymous ); + } + + void MUCRoom::setSemiAnonymous() + { + m_flags &= ~( FlagNonAnonymous | FlagFullyAnonymous ); + m_flags |= FlagSemiAnonymous; + } + + void MUCRoom::setFullyAnonymous() + { + m_flags &= ~( FlagNonAnonymous | FlagSemiAnonymous ); + m_flags |= FlagFullyAnonymous; + } + + void MUCRoom::handleMessage( const Message& msg, MessageSession* /*session*/ ) + { + if( !m_roomHandler ) + return; + + if( msg.subtype() == Message::Error ) + { + m_roomHandler->handleMUCError( this, msg.error() ? msg.error()->error() : StanzaErrorUndefined ); + } + else + { + const MUCUser* mu = msg.findExtension( ExtMUCUser ); + if( mu ) + { + const int flags = mu->flags(); + if( flags & FlagNonAnonymous ) + setNonAnonymous(); + if( flags & FlagPublicLogging ) + { + m_flags &= ~FlagPublicLoggingOff; + m_flags |= FlagPublicLogging; + } + if( flags & FlagPublicLoggingOff ) + { + m_flags &= ~FlagPublicLogging; + m_flags |= FlagPublicLoggingOff; + } + if( flags & FlagSemiAnonymous ) + setSemiAnonymous(); + if( flags & FlagFullyAnonymous ) + setFullyAnonymous(); + + if( mu->operation() == OpDeclineFrom && mu->jid() ) + m_roomHandler->handleMUCInviteDecline( this, JID( *(mu->jid()) ), + mu->reason() ? *(mu->reason()) : EmptyString ); + } + + const DataForm* df = msg.findExtension( ExtDataForm ); + if( m_roomConfigHandler && df ) + { + m_roomConfigHandler->handleMUCRequest( this, *df ); + return; + } + + if( !msg.subject().empty() ) + { + m_roomHandler->handleMUCSubject( this, msg.from().resource(), msg.subject() ); + } + else if( !msg.body().empty() ) + { + std::string when; + bool privMsg = false; + bool history = false; + if( msg.when() ) + { + when = msg.when()->stamp(); + history = true; + } + if( msg.subtype() & ( Message::Chat | Message::Normal ) ) + privMsg = true; + + m_roomHandler->handleMUCMessage( this, msg, privMsg ); + } + } + } + + void MUCRoom::handleIqID( const IQ& iq, int context ) + { + if( !m_roomConfigHandler ) + return; + + switch( iq.subtype() ) + { + case IQ::Result: + handleIqResult( iq, context ); + break; + case IQ::Error: + handleIqError( iq, context ); + break; + default: + break; + } + } + + void MUCRoom::handleIqResult( const IQ& iq, int context ) + { + switch( context ) + { + case SetRNone: + case SetVisitor: + case SetParticipant: + case SetModerator: + case SetANone: + case SetOutcast: + case SetMember: + case SetAdmin: + case SetOwner: + case CreateInstantRoom: + case CancelRoomCreation: + case DestroyRoom: + case StoreVoiceList: + case StoreBanList: + case StoreMemberList: + case StoreModeratorList: + case StoreAdminList: + m_roomConfigHandler->handleMUCConfigResult( this, true, (MUCOperation)context ); + break; + case RequestRoomConfig: + { + const MUCOwner* mo = iq.findExtension( ExtMUCOwner ); + if( !mo ) + break; + + if( mo->form() ) + m_roomConfigHandler->handleMUCConfigForm( this, *(mo->form()) ); + break; + } + case RequestVoiceList: + case RequestBanList: + case RequestMemberList: + case RequestModeratorList: + case RequestOwnerList: + case RequestAdminList: + { + const MUCAdmin* ma = iq.findExtension( ExtMUCAdmin ); + if( !ma ) + break; + + m_roomConfigHandler->handleMUCConfigList( this, ma->list(), (MUCOperation)context ); + break; + } + default: + break; + } + } + + void MUCRoom::handleIqError( const IQ& /*iq*/, int context ) + { + switch( context ) + { + case SetRNone: + case SetVisitor: + case SetParticipant: + case SetModerator: + case SetANone: + case SetOutcast: + case SetMember: + case SetAdmin: + case SetOwner: + case CreateInstantRoom: + case CancelRoomCreation: + case RequestRoomConfig: + case DestroyRoom: + case RequestVoiceList: + case StoreVoiceList: + case RequestBanList: + case StoreBanList: + case RequestMemberList: + case StoreMemberList: + case RequestModeratorList: + case StoreModeratorList: + case RequestOwnerList: + case StoreOwnerList: + case RequestAdminList: + case StoreAdminList: + m_roomConfigHandler->handleMUCConfigResult( this, false, (MUCOperation)context ); + break; + } + } + + void MUCRoom::handleDiscoInfo( const JID& /*from*/, const Disco::Info& info, int context ) + { + switch( context ) + { + case GetRoomInfo: + { + int oldflags = m_flags; + m_flags = 0; + if( oldflags & FlagPublicLogging ) + m_flags |= FlagPublicLogging; + + std::string name; + const StringList& l = info.features(); + StringList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + { + if( (*it) == "muc_hidden" ) + m_flags |= FlagHidden; + else if( (*it) == "muc_membersonly" ) + m_flags |= FlagMembersOnly; + else if( (*it) == "muc_moderated" ) + m_flags |= FlagModerated; + else if( (*it) == "muc_nonanonymous" ) + setNonAnonymous(); + else if( (*it) == "muc_open" ) + m_flags |= FlagOpen; + else if( (*it) == "muc_passwordprotected" ) + m_flags |= FlagPasswordProtected; + else if( (*it) == "muc_persistent" ) + m_flags |= FlagPersistent; + else if( (*it) == "muc_public" ) + m_flags |= FlagPublic; + else if( (*it) == "muc_semianonymous" ) + setSemiAnonymous(); + else if( (*it) == "muc_temporary" ) + m_flags |= FlagTemporary; + else if( (*it) == "muc_fullyanonymous" ) + setFullyAnonymous(); + else if( (*it) == "muc_unmoderated" ) + m_flags |= FlagUnmoderated; + else if( (*it) == "muc_unsecured" ) + m_flags |= FlagUnsecured; + } + + const Disco::IdentityList& il = info.identities(); + if( il.size() ) + name = il.front()->name(); + + if( m_roomHandler ) + m_roomHandler->handleMUCInfo( this, m_flags, name, info.form() ); + break; + } + default: + break; + } + } + + void MUCRoom::handleDiscoItems( const JID& /*from*/, const Disco::Items& items, int context ) + { + if( !m_roomHandler ) + return; + + switch( context ) + { + case GetRoomItems: + { + m_roomHandler->handleMUCItems( this, items.items() ); + break; + } + default: + break; + } + } + + void MUCRoom::handleDiscoError( const JID& /*from*/, const Error* /*error*/, int context ) + { + if( !m_roomHandler ) + return; + + switch( context ) + { + case GetRoomInfo: + m_roomHandler->handleMUCInfo( this, 0, EmptyString, 0 ); + break; + case GetRoomItems: + m_roomHandler->handleMUCItems( this, Disco::ItemList() ); + break; + default: + break; + } + } + + StringList MUCRoom::handleDiscoNodeFeatures( const JID& /*from*/, const std::string& /*node*/ ) + { + return StringList(); + } + + Disco::IdentityList MUCRoom::handleDiscoNodeIdentities( const JID& /*from*/, + const std::string& /*node*/ ) + { + return Disco::IdentityList(); + } + + Disco::ItemList MUCRoom::handleDiscoNodeItems( const JID& /*from*/, const JID& /*to*/, + const std::string& node ) + { + Disco::ItemList l; + if( node == XMLNS_MUC_ROOMS && m_publish ) + { + l.push_back( new Disco::Item( m_nick.bareJID(), EmptyString, + m_publishNick ? m_nick.resource() : EmptyString ) ); + } + return l; + } + +} diff --git a/libs/libgloox/mucroom.h b/libs/libgloox/mucroom.h new file mode 100644 index 0000000..444fff5 --- /dev/null +++ b/libs/libgloox/mucroom.h @@ -0,0 +1,994 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef MUCROOM_H__ +#define MUCROOM_H__ + +#include "discohandler.h" +#include "disconodehandler.h" +#include "dataform.h" +#include "presencehandler.h" +#include "iqhandler.h" +#include "messagehandler.h" +#include "mucroomhandler.h" +#include "mucroomconfighandler.h" +#include "jid.h" +#include "stanzaextension.h" + +#include + +namespace gloox +{ + + class ClientBase; + class MUCMessageSession; + class Message; + + /** + * @brief This is an implementation of XEP-0045 (Multi-User Chat). + * + * Usage is pretty simple: + * + * Derrive an object from MUCRoomHandler and implement its virtuals: + * @code + * class MyClass : public MUCRoomHandler + * { + * ... + * }; + * @endcode + * + * Then create a new MUCRoom object and pass it a valid ClientBase, the desired full room JID, + * your MUCRoomHandler-derived object, and an optional MUCRoomConfigHandler-derived object. + * @code + * void MyOtherClass::joinRoom( const std::string& room, const std::string& service, + * const std::string& nick ) + * { + * MyClass* myHandler = new MyClass(...); + * JID roomJID( room + "@" + service + "/" + nick ); + * m_room = new MUCRoom( m_clientbase, roomJID, myHandler, 0 ); + * m_room->join(); + * } + * @endcode + * + * When joining the room was successful, the various MUCRoomHandler functions will start to + * be called. If joining was not successful, MUCRoomHandler::handleMUCError() will be called, + * giving a hint at the reason for the failure. + * + * To set up your own room, or to configure an existing room, you should also derive a + * class from MUCRoomConfigHandler and register it with the MUCRoom (either by using it + * with MUCRoom's constructor, or by calling registerMUCRoomConfigHandler()). + * + * To quickly create an instant room, see InstantMUCRoom. + * + * To quickly create an instant room to turn a one-to-one chat into a multi-user chat, + * see UniqueMUCRoom. + * + * To send a private message to a room participant, use + * @link MessageSession gloox::MessageSession @endlink with the participant's full room JID + * (room\@service/nick). + * + * XEP version: 1.21 + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API MUCRoom : private DiscoHandler, private PresenceHandler, + public IqHandler, private MessageHandler, private DiscoNodeHandler + { + public: + /** + * Allowable history request types. To disable sending of history, use any value except + * HistoryUnknown and specify a zero-length time span (using setRequestHistory()). + */ + enum HistoryRequestType + { + HistoryMaxChars, /**< Limit the total number of characters in the history to "X" + * (where the character count is the characters of the complete + * XML stanzas, not only their XML character data). */ + HistoryMaxStanzas, /**< Limit the total number of messages in the history to "X". */ + HistorySeconds, /**< Send only the messages received in the last "X" seconds. */ + HistorySince, /**< Send only the messages received since the datetime specified + * (which MUST conform to the DateTime profile specified in Jabber + * Date and Time Profiles (XEP-0082)). */ + HistoryUnknown /**< It is up to the service to decide how much history to send. + * This is the default. */ + }; + + /** + * Available operations. + */ + enum MUCUserOperation + { + OpNone, /**< No operation. */ + OpInviteTo, /**< Invitation being sent to soemone. */ + OpInviteFrom, /**< Invitation received from someone. */ + OpDeclineTo, /**< Someone's invitation declined. */ + OpDeclineFrom /**< Someone declined an invitation. */ + }; + + /** + * @brief An abstraction of a MUC query. + * + * You should not need to use this class directly. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class MUC : public StanzaExtension + { + public: + /** + * Creates a new MUC object. + * @param password An optional room password. + * @param historyType The type of room history to request. + * @param historySince A string describing the amount of room history. + * @param historyValue The amount of requested room history. + */ + MUC( const std::string& password, HistoryRequestType historyType = HistoryUnknown, + const std::string& historySince = EmptyString, int historyValue = 0 ); + + /** + * Constructs a new MUCUser object from the given Tag. + * @param tag The Tag to parse. + */ + MUC( const Tag* tag = 0 ); + + /** + * Virtual destructor. + */ + virtual ~MUC(); + + /** + * Returns a pointer to the current password, or 0. + * @return A pointer to the current password, or 0. + */ + const std::string* password() const { return m_password; } + + /** + * Returns a pointer to the description of the amount of room history requested. + * @return A pointer to the description of the amount of room history requested. + */ + const std::string* historySince() const { return m_historySince; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new MUC( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + MUC* m = new MUC(); + m->m_password = m_password ? new std::string( *m_password ) : 0; + m->m_historySince = m_historySince ? new std::string( *m_historySince ) : 0; + m->m_historyType = m_historyType; + m->m_historyValue = m_historyValue; + return m; + } + + private: + std::string* m_password; + std::string* m_historySince; + HistoryRequestType m_historyType; + int m_historyValue; + }; + + /** + * @brief An abstraction of a MUC user query. + * + * You should not need to use this class directly. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class MUCUser : public StanzaExtension + { + public: + /** + * Constructor. + * @param operation An operation to perform. + * @param to The recipient. + * @param reason The reason for the operation. + * @param thread If this is an invitation, and if the invitation is part of + * a transformation of a one-to-one chat to a MUC, include the one-to-one chat's + * thread ID here. Defaults to the empty string (i.e. not a continuation). + */ + MUCUser( MUCUserOperation operation, const std::string& to, const std::string& reason, + const std::string& thread = EmptyString ); + + /** + * Constructs a new MUCUser object from the given Tag. + * @param tag The Tag to parse. + */ + MUCUser( const Tag* tag = 0 ); + + /** + * Virtual destructor. + */ + virtual ~MUCUser(); + + /** + * Returns the current room flags. + * @return The current room flags. + */ + int flags() const { return m_flags; } + + /** + * Returns the user's current room affiliation. + * @return The user's current room affiliation. + */ + MUCRoomAffiliation affiliation() const { return m_affiliation; } + + /** + * Returns the user's current room role. + * @return The user's current room role. + */ + MUCRoomRole role() const { return m_role; } + + /** + * + */ + const std::string* jid() const { return m_jid; } + + /** + * + */ + const std::string* actor() const { return m_actor; } + + /** + * + */ + const std::string* password() const { return m_password; } + + /** + * + */ + const std::string* thread() const { return m_thread; } + + /** + * + */ + const std::string* reason() const { return m_reason; } + + /** + * + */ + const std::string* newNick() const { return m_newNick; } + + /** + * Returns an alternate venue, if set. + * @return An alternate venue, if set. + */ + const std::string* alternate() const { return m_alternate; } + + /** + * Whether or not the 'continue' flag is set. + * @return Whether or not the 'continue' flag is set. + */ + bool continued() const { return m_continue; } + + /** + * Returns the current operation. + * @return The current operation. + */ + MUCUserOperation operation() const { return m_operation; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new MUCUser( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + MUCUser* m = new MUCUser(); + m->m_affiliation = m_affiliation; + m->m_role = m_role; + m->m_jid = m_jid ? new std::string( *m_jid ) : 0; + m->m_actor = m_actor ? new std::string( *m_actor ) : 0; + m->m_thread = m_thread ? new std::string( *m_thread ) : 0; + m->m_reason = m_reason ? new std::string( *m_reason ) : 0; + m->m_newNick = m_newNick ? new std::string( *m_newNick ) : 0; + m->m_password = m_password ? new std::string( *m_password ) : 0; + m->m_alternate = m_alternate ? new std::string( *m_alternate ) : 0; + m->m_operation = m_operation; + m->m_flags = m_flags; + m->m_del = m_del; + m->m_continue = m_continue; + return m; + } + + private: + static MUCRoomAffiliation getEnumAffiliation( const std::string& affiliation ); + static MUCRoomRole getEnumRole( const std::string& role ); + + + MUCRoomAffiliation m_affiliation; + MUCRoomRole m_role; + std::string* m_jid; + std::string* m_actor; + std::string* m_thread; + std::string* m_reason; + std::string* m_newNick; + std::string* m_password; + std::string* m_alternate; + MUCUserOperation m_operation; + int m_flags; + bool m_del; + bool m_continue; + }; + + /** + * Creates a new abstraction of a Multi-User Chat room. The room is not joined automatically. + * Use join() to join the room, use leave() to leave it. + * @param parent The ClientBase object to use for the communication. + * @param nick The room's name and service plus the desired nickname in the form + * room\@service/nick. + * @param mrh The MUCRoomHandler that will listen to room events. May be 0 and may be specified + * later using registerMUCRoomHandler(). However, without one, MUC is no joy. + * @param mrch The MUCRoomConfigHandler that will listen to room config result. Defaults to 0 + * initially. However, at the latest you need one when you create a new room which is not an + * instant room. You can set a MUCRoomConfigHandler using registerMUCRoomConfigHandler(). + */ + MUCRoom( ClientBase* parent, const JID& nick, MUCRoomHandler* mrh, MUCRoomConfigHandler* mrch = 0 ); + + /** + * Virtual Destructor. + */ + virtual ~MUCRoom(); + + /** + * Use this function to set a password to use when joining a (password protected) + * room. + * @param password The password to use for this room. + * @note This function does not password-protect a room. + */ + void setPassword( const std::string& password ) { m_password = password; } + + /** + * A convenience function that returns the room's name. + * @return The room's name. + */ + const std::string name() const { return m_nick.username(); } + + /** + * A convenience function that returns the name/address of the MUC service the room is running on + * (e.g., conference.jabber.org). + * @return The MUC service's name/address. + */ + const std::string service() const { return m_nick.server(); } + + /** + * A convenience function that returns the user's nickname in the room. + * @return The user's nickname. + */ + const std::string nick() const { return m_nick.resource(); } + + /** + * Join this room. + * @param type The presence to join with, defaults to Available. + * @param status The presence's optional status text. + * @param priority The presence's optional priority, defaults to 0. + * ClientBase will automatically include the default Presence extensions added using + * @link gloox::ClientBase::addPresenceExtension() ClientBase::addPresenceExtension() @endlink. + */ + virtual void join( Presence::PresenceType type = Presence::Available, + const std::string& status = EmptyString, + int priority = 0 ); + + /** + * Leave this room. + * @param msg An optional msg indicating the reason for leaving the room. Default: empty. + */ + void leave( const std::string& msg = EmptyString ); + + /** + * Sends a chat message to the room. + * @param message The message to send. + */ + void send( const std::string& message ); + + /** + * Sets the subject of the room to the given string. + * The MUC service may decline the request to set a new subject. You should + * not assume the subject was set successfully util it is acknowledged via the MUCRoomHandler. + * @param subject The new subject. + */ + void setSubject( const std::string& subject ); + + /** + * Returns the user's current affiliation with this room. + * @return The user's current affiliation. + */ + MUCRoomAffiliation affiliation() const { return m_affiliation; } + + /** + * Returns the user's current role in this room. + * @return The user's current role. + */ + MUCRoomRole role() const { return m_role; } + + /** + * Use this function to change the user's nickname in the room. + * The MUC service may decline the request to set a new nickname. You should not assume + * the nick change was successful until it is acknowledged via the MUCRoomHandler. + * @param nick The user's new nickname. + */ + void setNick( const std::string& nick ); + + /** + * Use this function to set the user's presence in this room. It is not possible to + * use Unavailable with this function. + * @param presence The user's new presence. + * @param msg An optional status message. Default: empty. + */ + void setPresence( Presence::PresenceType presence, const std::string& msg = EmptyString ); + + /** + * Use this function to invite another user to this room. + * @param invitee The (bare) JID of the user to invite. + * @param reason The user-supplied reason for the invitation. + * @param thread If this invitation is part of a transformation of a + * one-to-one chat to a MUC, include the one-to-one chat's thread ID here. Defaults + * to the empty string (i.e. not a continuation). + */ + void invite( const JID& invitee, const std::string& reason, const std::string& thread = EmptyString ); + + /** + * Use this function to request basic room info, possibly prior to joining it. + * Results are announced using the MUCRoomHandler. + */ + void getRoomInfo(); + + /** + * Use this function to request information about the current room occupants, + * possibly prior to joining it. The room ay be configured not to disclose such + * information. + * Results are announced using the MUCRoomHandler. + */ + void getRoomItems(); + + /** + * The MUC spec enables other entities to discover via Service Discovery which rooms + * an entity is in. By default, gloox does not publish such info for privacy reasons. + * This function can be used to enable publishing the info for @b this room. + * @param publish Whether to enable other entities to discover the user's presence in + * @b this room. + * @param publishNick Whether to publish the nickname used in the room. This parameter + * is ignored if @c publish is @b false. + */ + void setPublish( bool publish, bool publishNick ); + + /** + * Use this function to register a (new) MUCRoomHandler with this room. There can be only one + * MUCRoomHandler per room at any one time. + * @param mrl The MUCRoomHandler to register. + */ + void registerMUCRoomHandler( MUCRoomHandler* mrl ) { m_roomHandler = mrl; } + + /** + * Use this function to remove the registered MUCRoomHandler. + */ + void removeMUCRoomHandler() { m_roomHandler = 0; } + + /** + * Use this function to register a (new) MUCRoomConfigHandler with this room. There can + * be only one MUCRoomConfigHandler per room at any one time. + * @param mrch The MUCRoomConfigHandler to register. + */ + void registerMUCRoomConfigHandler( MUCRoomConfigHandler* mrch ) { m_roomConfigHandler = mrch; } + + /** + * Use this function to remove the registered MUCRoomConfigHandler. + */ + void removeMUCRoomConfigHandler() { m_roomConfigHandler = 0; } + + /** + * Use this function to add history to a (newly created) room. The use case from the MUC spec + * is to add history to a room that was created in the process of a transformation of a + * one-to-one chat to a multi-user chat. + * @param message A reason for declining the invitation. + * @param from The JID of the original author of this part of the history. + * @param stamp The datetime of the original message in the format: 20061224T12:15:23Z + * @note You should not attempt to use this function before + * MUCRoomHandler::handleMUCParticipantPresence() was called for the first time. + */ + void addHistory( const std::string& message, const JID& from, const std::string& stamp ); + + /** + * Use this function to request room history. Set @c value to zero to disable the room + * history request. You should not use HistorySince type with this function. + * History is sent only once after entering a room. You should use this function before joining. + * @param value Represents either the number of requested characters, the number of requested + * message stanzas, or the number seconds, depending on the value of @c type. + * @param type + * @note If this function is not used to request a specific amount of room history, it is up + * to the MUC service to decide how much history to send. + */ + void setRequestHistory( int value, HistoryRequestType type ); + + /** + * Use this function to request room history since specific datetime. + * History is sent only once after entering a room. You should use this function before joining. + * @param since A string representing a datetime conforming to the DateTime profile specified + * in Jabber Date and Time Profiles (XEP-0082). + * @note If this function is not used to request a specific amount of room history, it is up + * to the MUC service to decide how much history to send. + */ + void setRequestHistory( const std::string& since ); + + /** + * This static function allows to formally decline a MUC + * invitation received via the MUCInvitationListener. + * @param room The JID of the room the invitation came from. + * @param invitor The JID of the invitor. + * @param reason An optional reason for the decline. + * @return A pointer to a Message. You will have to send (and + * possibly delete) this Message manually. + */ + static Message* declineInvitation( const JID& room, const JID& invitor, + const std::string& reason = EmptyString); + + /** + * It is not possible for a visitor to speak in a moderated room. Use this function to request + * voice from the moderator. + */ + void requestVoice(); + + /** + * Use this function to kick a user from the room. + * Depending on service and/or room configuration and role/affiliation + * this may not always succeed. Usually, a role of 'moderator' is necessary. + * @note This is a convenience function. It directly uses setRole() with a MUCRoomRole of RoleNone. + * @param nick The nick of the user to be kicked. + * @param reason An optional reason for the kick. + */ + void kick( const std::string& nick, const std::string& reason = EmptyString ) + { setRole( nick, RoleNone, reason ); } + + /** + * Use this function to ban a user from the room. + * Depending on service and/or room configuration and role/affiliation + * this may not always succeed. Usually, an affiliation of admin is necessary. + * @note This is a convenience function. It directly uses setAffiliation() with a MUCRoomAffiliation + * of RoleOutcast. + * @param nick The nick of the user to be banned. + * @param reason An optional reason for the ban. + */ + void ban( const std::string& nick, const std::string& reason ) + { setAffiliation( nick, AffiliationOutcast, reason ); } + + /** + * Use this function to grant voice to a user in a moderated room. + * Depending on service and/or room configuration and role/affiliation + * this may not always succeed. Usually, a role of 'moderator' is necessary. + * @note This is a convenience function. It directly uses setRole() with a MUCRoomRole + * of RoleParticipant. + * @param nick The nick of the user to be granted voice. + * @param reason An optional reason for the grant. + */ + void grantVoice( const std::string& nick, const std::string& reason ) + { setRole( nick, RoleParticipant, reason ); } + + /** + * Use this function to create a Tag that approves a voice request or registration request + * delivered via MUCRoomConfigHandler::handleMUCVoiceRequest(). You will need to send this + * Tag off manually using Client/ClientBase. + * @param room The room's JID. This is needed because you can use this function outside of + * room context (e.g, if the admin is not in the room). + * @param df The filled-in DataForm from the voice/registration request. The form object + * will be owned by the returned Message. + */ + static Message* createDataForm( const JID& room, const DataForm* df ); + + /** + * Use this function to revoke voice from a user in a moderated room. + * Depending on service and/or room configuration and role/affiliation + * this may not always succeed. Usually, a role of 'moderator' is necessary. + * @note This is a convenience function. It directly uses setRole() with a MUCRoomRole + * of RoleVisitor. + * @param nick The nick of the user. + * @param reason An optional reason for the revoke. + */ + void revokeVoice( const std::string& nick, const std::string& reason ) + { setRole( nick, RoleVisitor, reason ); } + + /** + * Use this function to change the role of a user in the room. + * Usually, at least moderator privileges are required to succeed. + * @param nick The nick of the user who's role shall be modfified. + * @param role The user's new role in the room. + * @param reason An optional reason for the role change. + */ + void setRole( const std::string& nick, MUCRoomRole role, const std::string& reason = EmptyString ); + + /** + * Use this function to change the affiliation of a user in the room. + * Usually, at least admin privileges are required to succeed. + * @param nick The nick of the user who's affiliation shall be modfified. + * @param affiliation The user's new affiliation in the room. + * @param reason An optional reason for the affiliation change. + */ + void setAffiliation( const std::string& nick, MUCRoomAffiliation affiliation, + const std::string& reason ); + + /** + * Use this function to request the room's configuration form. + * It can be used either after MUCRoomHandler::handleMUCRoomCreation() was called, + * or at any later time. + * + * Usually owner privileges are required for this action to + * succeed. + * + * Use setRoomConfig() to send the modified room config back. + */ + void requestRoomConfig(); + + /** + * After requesting (using requestRoomConfig()) and + * editing/filling in the room's configuration, + * use this function to send it back to the server. + * @param form The form to send. The function will delete the + * object pointed to. + */ + void setRoomConfig( DataForm* form ); + + /** + * Use this function to accept the room's default configuration. This function is useful + * only after MUCRoomHandler::handleMUCRoomCreation() was called. This is a NOOP at + * any other time. + */ + void acknowledgeInstantRoom() + { instantRoom( CreateInstantRoom ); } + + /** + * Use this function to cancel the creation of a room. This function is useful only after + * MUCRoomHandler::handleMUCRoomCreation() was called. This is a NOOP at any other time. + */ + void cancelRoomCreation() + { instantRoom( CancelRoomCreation ); } + + /** + * Use this function to destroy the room. All the occupants will be removed from the room. + * @param reason An optional reason for the destruction. + * @param alternate A pointer to a JID of an alternate venue (e.g., another MUC room). + * May be 0. + * @param password An optional password for the alternate venue. + * + * Usually owner privileges are required for this action to succeed. + */ + void destroy( const std::string& reason = EmptyString, + const JID& alternate = JID(), const std::string& password = EmptyString ); + + /** + * Use this function to request a particluar list of room occupants. + * @note There must be a MUCRoomConfigHandler registered with this room for this + * function to be executed. + * @param operation The following types of lists are available: + * @li Voice List: List of people having voice in a moderated room. Use RequestVoiceList. + * @li Members List: List of members of a room. Use RequestMemberList. + * @li Ban List: List of people banned from the room. Use RequestBanList. + * @li Moderator List: List of room moderators. Use RequestModeratorList. + * @li Admin List: List of room admins. Use RequestAdminList. + * @li Owner List: List of room owners. Use RequestOwnerList. + * Any other value of @c operation will be ignored. + */ + void requestList( MUCOperation operation ); + + /** + * Use this function to store a (modified) list for the room. + * @param items The list of items. Example:
+ * You want to set the Voice List. The privilege of Voice refers to the role of Participant. + * Furthermore, you only store the delta of the original (Voice)List. (Optionally, you could + * probably store the whole list, however, remeber to include those items that were modified, + * too.) + * You want to, say, add one occupant to the Voice List, and remove another one. + * Therefore you store: + * @li GuyOne, role participant -- this guy gets voice granted, he/she is now a participant. + * @li GuyTwo, role visitor -- this guy gets voice revoked, he/she is now a mere visitor + * (Visitor is the Role "below" Participant in the privileges hierarchy). + * + * For operations modifying Roles, you should specifiy only the new Role in the MUCListItem + * structure, for those modifying Affiliations, you should only specify the new Affiliation, + * respectively. The nickname is mandatory in the MUCListItem structure. Items without nickname + * will be ignored. + * + * You may specify a reason for the role/affiliation change in the MUCListItem structure. + * You should not specify a JID in the MUCListItem structure, it will be ignored. + * + * @param operation See requestList() for a list of available list types. Any other value will + * be ignored. + */ + void storeList( const MUCListItemList items, MUCOperation operation ); + + /** + * Returns the currently known room flags. + * @return ORed MUCRoomFlag's describing the current room configuration. + */ + int flags() const { return m_flags; } + + // reimplemented from DiscoHandler + virtual void handleDiscoInfo( const JID& from, const Disco::Info& info, int context ); + + // reimplemented from DiscoHandler + // reimplemented from DiscoHandler + virtual void handleDiscoItems( const JID& from, const Disco::Items& items, int context ); + + // reimplemented from DiscoHandler + virtual void handleDiscoError( const JID& from, const Error* error, int context ); + + // reimplemented from PresenceHandler + virtual void handlePresence( const Presence& presence ); + + // reimplemented from MessageHandler + virtual void handleMessage( const Message& msg, MessageSession* session = 0 ); + + // reimplemented from IqHandler + virtual bool handleIq( const IQ& iq ) { (void)iq; return false; } + + // reimplemented from IqHandler + virtual void handleIqID( const IQ& iq, int context ); + + // reimplemented from DiscoNodeHandler + virtual StringList handleDiscoNodeFeatures( const JID& from, const std::string& node ); + + // reimplemented from DiscoNodeHandler + virtual Disco::IdentityList handleDiscoNodeIdentities( const JID& from, + const std::string& node ); + + // reimplemented from DiscoNodeHandler + virtual Disco::ItemList handleDiscoNodeItems( const JID& from, const JID& to, + const std::string& node = EmptyString ); + + protected: + /** + * Sets the room's name. + * @param name The room's name. + */ + void setName( const std::string& name ) { m_nick.setUsername( name ); } + + /** + * Acknowledges instant room creation w/o a call to the MUCRoomConfigHandler. + * @return Whether an instant room is being created. + */ + virtual bool instantRoomHook() const { return false; } + + ClientBase* m_parent; + JID m_nick; + + bool m_joined; + + private: +#ifdef MUCROOM_TEST + public: +#endif + /** + * @brief An abstraction of a MUC owner query. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class MUCOwner : public StanzaExtension + { + public: + + /** + * Describes available query types for the muc#owner namespace. + */ + enum QueryType + { + TypeCreate, /**< Create a room. */ + TypeRequestConfig, /**< Request room config. */ + TypeSendConfig, /**< Submit configuration form to MUC service. */ + TypeCancelConfig, /**< Cancel room configuration. */ + TypeInstantRoom, /**< Request an instant room */ + TypeDestroy, /**< Destroy the room. */ + TypeIncomingTag /**< The Query has been created from an incoming Tag. */ + }; + + /** + * Creates a new MUCOwner object for the given query, possibly including + * the given DataForm. + * @param type The intended query type. + * @param form An optional pointer to a DataForm. Necessity depends on the query type. + */ + MUCOwner( QueryType type, DataForm* form = 0 ); + + /** + * Creates a new query that destroys the current room. + * @param alternate An optional alternate discussion venue. + * @param reason An optional reason for the room destruction. + * @param password An optional password for the new room. + */ + MUCOwner( const JID& alternate = JID(), const std::string& reason = EmptyString, + const std::string& password = EmptyString); + + /** + * Creates a new MUCOwner object from the given Tag. + * @param tag A Tag to parse. + */ + MUCOwner( const Tag* tag ); + + /** + * Virtual destructor. + */ + virtual ~MUCOwner(); + + /** + * Returns a pointer to a DataForm, included in the MUCOwner object. May be 0. + * @return A pointer to a configuration form. + */ + const DataForm* form() const { return m_form; } + + // reimplemented from StanzaExtension + const std::string& filterString() const; + + // reimplemented from StanzaExtension + StanzaExtension* newInstance( const Tag* tag ) const + { + return new MUCOwner( tag ); + } + + // reimplemented from StanzaExtension + Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + MUCOwner* m = new MUCOwner(); + m->m_type = m_type; + m->m_jid = m_jid; + m->m_reason = m_reason; + m->m_pwd = m_pwd; + m->m_form = m_form ? new DataForm( *m_form ) : 0; + return m; + } + + private: + QueryType m_type; + JID m_jid; + std::string m_reason; + std::string m_pwd; + DataForm* m_form; + }; + + /** + * @brief An abstraction of a MUC admin query. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class MUCAdmin : public StanzaExtension + { + public: + /** + * Creates a new object that can be used to change the role of a room participant. + * @param role The participant's new role. + * @param nick The participant's nick. + * @param reason An optional reason for the role change. + */ + MUCAdmin( MUCRoomRole role, const std::string& nick, + const std::string& reason = EmptyString ); + + /** + * Creates a new object that can be used to change the affiliation of a room participant. + * @param affiliation The participant's new affiliation. + * @param nick The participant's nick. + * @param reason An optional reason for the role change. + */ + MUCAdmin( MUCRoomAffiliation affiliation, const std::string& nick, + const std::string& reason = EmptyString ); + + /** + * Creates a new object that can be used to request or store a role/affiliation + * list. + * @param operation The MUCOperation to carry out. Only the Request* and Store* + * operations are valid. Any other value will be ignored. + * @param jids A list of bare JIDs. Only the JID member of the MUCListItem + * structure should be set. The type of the list will be determined from the + * @c operation parameter. + */ + MUCAdmin( MUCOperation operation, const MUCListItemList& jids = MUCListItemList() ); + + /** + * Constructs a new MUCAdmin object from the given Tag. + * @param tag The Tag to parse. + */ + MUCAdmin( const Tag* tag = 0 ); + + /** + * Virtual destructor. + */ + virtual ~MUCAdmin(); + + /** + * Returns the contained list of MUC items. + * @return The contained list of MUC items. + */ + const MUCListItemList& list() const { return m_list; } + + // reimplemented from StanzaExtension + const std::string& filterString() const; + + // reimplemented from StanzaExtension + StanzaExtension* newInstance( const Tag* tag ) const + { + return new MUCAdmin( tag ); + } + + // reimplemented from StanzaExtension + Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new MUCAdmin( *this ); + } + + private: + MUCListItemList m_list; + MUCRoomAffiliation m_affiliation; + MUCRoomRole m_role; + }; + + void handleIqResult( const IQ& iq, int context ); + void handleIqError( const IQ& iq, int context ); + void setNonAnonymous(); + void setSemiAnonymous(); + void setFullyAnonymous(); + void acknowledgeRoomCreation(); + void instantRoom( int context ); + + MUCRoomHandler* m_roomHandler; + MUCRoomConfigHandler* m_roomConfigHandler; + MUCMessageSession* m_session; + + typedef std::list ParticipantList; + ParticipantList m_participants; + + std::string m_password; + std::string m_newNick; + + MUCRoomAffiliation m_affiliation; + MUCRoomRole m_role; + + HistoryRequestType m_historyType; + + std::string m_historySince; + int m_historyValue; + int m_flags; + bool m_creationInProgress; + bool m_configChanged; + bool m_publishNick; + bool m_publish; + bool m_unique; + + }; + +} + +#endif // MUCROOM_H__ diff --git a/libs/libgloox/mucroomconfighandler.h b/libs/libgloox/mucroomconfighandler.h new file mode 100644 index 0000000..67223d8 --- /dev/null +++ b/libs/libgloox/mucroomconfighandler.h @@ -0,0 +1,226 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef MUCROOMCONFIGHANDLER_H__ +#define MUCROOMCONFIGHANDLER_H__ + +#include "gloox.h" +#include "jid.h" + +#include +#include + +namespace gloox +{ + + class MUCRoom; + class DataForm; + + /** + * An item in a list of MUC room users. Lists of these items are + * used when manipulating the lists of members, admins, owners, etc. + * of a room. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class MUCListItem + { + public: + /** + * Constructs a new object using the given JID. + * @param jid The item's JID. + */ + MUCListItem( const JID& jid ) + : m_jid( jid ), m_affiliation( AffiliationInvalid ), m_role( RoleInvalid ) + {} + + /** + * Creates a new object, setting JID, affiliation, role, and nick. + * @param jid The item's JID. + * @param role The item's role. + * @param affiliation The item's affiliation. + * @param nick The item's nick. + */ + MUCListItem( const JID& jid, MUCRoomRole role, MUCRoomAffiliation affiliation, + const std::string& nick ) + : m_jid( jid ), m_nick( nick ), m_affiliation( affiliation ), m_role( role ) + {} + + /** + * Creates a new object, using nick, affiliation and a reason. + * @param nick The item's nick. + * @param affiliation The item's affiliation. + * @param reason A reason. + */ + MUCListItem( const std::string& nick, MUCRoomAffiliation affiliation, const std::string& reason ) + : m_nick( nick ), m_affiliation( affiliation ), m_role( RoleInvalid ), + m_reason( reason ) + {} + + /** + * Creates a new object, using nick, role and a reason. + * @param nick The item's nick. + * @param role The item's role. + * @param reason A reason. + */ + MUCListItem( const std::string& nick, MUCRoomRole role, const std::string& reason ) + : m_nick( nick ), m_affiliation( AffiliationInvalid ), m_role( role ), + m_reason( reason ) + {} + + /** + * Destructor. Deletes the @c jid member. + */ + ~MUCListItem() {} + + /** + * Returns the item's JID. + * @return The item's JID. + */ + const JID& jid() const { return m_jid; } + + /** + * Returns the item's nick. + * @return The item's nick. + */ + const std::string& nick() const { return m_nick; } + + /** + * Returns the item's affiliation. + * @return The item's affiliation. + */ + MUCRoomAffiliation affiliation() const { return m_affiliation; } + + /** + * Returns the item's role. + * @return The item's role. + */ + MUCRoomRole role() const { return m_role; } + + /** + * Returns the reason. + * @return The reason. + */ + const std::string& reason() const { return m_reason; } + + private: + JID m_jid; /**< Pointer to the occupant's JID if available, 0 otherwise. */ + std::string m_nick; /**< The occupant's nick in the room. */ + MUCRoomAffiliation m_affiliation; /**< The occupant's affiliation. */ + MUCRoomRole m_role; /**< The occupant's role. */ + std::string m_reason; /**< Use this only when **setting** the item's role/affiliation to + * specify a reason for the role/affiliation change. This field is + * empty in items fetched from the MUC service. */ + }; + + /** + * A list of MUCListItems. + */ + typedef std::list MUCListItemList; + + /** + * Available operations on a room. + */ + enum MUCOperation + { + RequestUniqueName, /**< Request a unique room name. */ + CreateInstantRoom, /**< Create an instant room. */ + CancelRoomCreation, /**< Cancel room creation process. */ + RequestRoomConfig, /**< Request room configuration form. */ + SendRoomConfig, /**< Send room configuration */ + DestroyRoom, /**< Destroy room. */ + GetRoomInfo, /**< Fetch room info. */ + GetRoomItems, /**< Fetch room items (e.g., current occupants). */ + SetRNone, /**< Set a user's role to None. */ + SetVisitor, /**< Set a user's role to Visitor. */ + SetParticipant, /**< Set a user's role to Participant. */ + SetModerator, /**< Set a user's role to Moderator. */ + SetANone, /**< Set a user's affiliation to None. */ + SetOutcast, /**< Set a user's affiliation to Outcast. */ + SetMember, /**< Set a user's affiliation to Member. */ + SetAdmin, /**< Set a user's affiliation to Admin. */ + SetOwner, /**< Set a user's affiliation to Owner. */ + RequestVoiceList, /**< Request the room's Voice List. */ + StoreVoiceList, /**< Store the room's Voice List. */ + RequestBanList, /**< Request the room's Ban List. */ + StoreBanList, /**< Store the room's Ban List. */ + RequestMemberList, /**< Request the room's Member List. */ + StoreMemberList, /**< Store the room's Member List. */ + RequestModeratorList, /**< Request the room's Moderator List. */ + StoreModeratorList, /**< Store the room's Moderator List. */ + RequestOwnerList, /**< Request the room's Owner List. */ + StoreOwnerList, /**< Store the room's Owner List. */ + RequestAdminList, /**< Request the room's Admin List. */ + StoreAdminList, /**< Store the room's Admin List. */ + InvalidOperation /**< Invalid operation. */ + }; + + /** + * @brief An abstract interface that can be implemented for MUC room configuration. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API MUCRoomConfigHandler + { + public: + /** + * Virtual Destructor. + */ + virtual ~MUCRoomConfigHandler() {} + + /** + * This function is called in response to MUCRoom::requestList() if the list was + * fetched successfully. + * @param room The room for which the list arrived. + * @param items The requestd list's items. + * @param operation The type of the list. + */ + virtual void handleMUCConfigList( MUCRoom* room, const MUCListItemList& items, + MUCOperation operation ) = 0; + + /** + * This function is called when the room's configuration form arrives. This usually happens + * after a call to MUCRoom::requestRoomConfig(). Use + * MUCRoom::setRoomConfig() to send the configuration back to the + * room. + * @param room The room for which the config form arrived. + * @param form The configuration form. + */ + virtual void handleMUCConfigForm( MUCRoom* room, const DataForm& form ) = 0; + + /** + * This function is called in response to MUCRoom::kick(), MUCRoom::storeList(), + * MUCRoom::ban(), and others, to indcate the end of the operation. + * @param room The room for which the operation ended. + * @param success Whether or not the operation was successful. + * @param operation The finished operation. + */ + virtual void handleMUCConfigResult( MUCRoom* room, bool success, MUCOperation operation ) = 0; + + /** + * This function is called when a Voice request or a Registration request arrive through + * the room that need to be approved/rejected by the room admin. Use MUCRoom::createDataForm() + * to have a Tag created that answers the request. + * @param room The room the request arrived from. + * @param form A DataForm containing the request. + */ + virtual void handleMUCRequest( MUCRoom* room, const DataForm& form ) = 0; + + }; + +} + +#endif // MUCROOMCONFIGHANDLER_H__ diff --git a/libs/libgloox/mucroomhandler.h b/libs/libgloox/mucroomhandler.h new file mode 100644 index 0000000..d1e47eb --- /dev/null +++ b/libs/libgloox/mucroomhandler.h @@ -0,0 +1,216 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef MUCROOMHANDLER_H__ +#define MUCROOMHANDLER_H__ + +#include "gloox.h" +#include "presence.h" +#include "disco.h" + +#include + +namespace gloox +{ + + class JID; + class MUCRoom; + class Message; + class DataForm; + + /** + * Describes a participant in a MUC room. + */ + struct MUCRoomParticipant + { + JID* nick; /**< Pointer to a JID holding the participant's full JID + * in the form @c room\@service/nick.
+ * @note The MUC server @b may change the chosen nickname. + * If the @b self member of this struct is true, one should + * check the resource of this member if the actual nickname + * is important. */ + MUCRoomAffiliation affiliation; /**< The participant's affiliation with the room. */ + MUCRoomRole role; /**< The participant's role with the room. */ + JID* jid; /**< Pointer to the occupant's full JID in a non-anonymous room or + * in a semi-anonymous room if the user (of gloox) has a role of + * moderator. + * 0 if the MUC service doesn't provide the JID. */ + int flags; /**< ORed MUCUserFlag values. Indicate conditions like: user has + * been kicked or banned from the room. Also may indicate that + * this struct refers to this instance's user. + * (MUC servers send presence to all room occupants, including + * the originator of the presence.) */ + std::string reason; /**< If the presence change is the result of an action where the + * actor can provide a reason for the action, this reason is stored + * here. Examples: Kicking, banning, leaving the room. */ + JID* actor; /**< If the presence change is the result of an action of a room + * member, a pointer to the actor's JID is stored here, if the + * actor chose to disclose his or her identity. Examples: Kicking + * and banning. + * 0 if the identity is not disclosed. */ + std::string newNick; /**< In case of a nickname change, this holds the new nick, while the + * nick member holds the old room nick (in JID form). @c newNick is only + * set if @c flags contains @b UserNickChanged. If @c flags contains + * @b UserSelf as well, a foregoing nick change request (using + * MUCRoom::setNick()) can be considered acknowledged. In any case + * the user's presence sent with the nick change acknowledgement + * is of type @c unavailable. Another presence of type @c available + * (or whatever the user's presence was at the time of the nick change + * request) will follow (not necessarily immediately) coming from the + * user's new nickname. Empty if there is no nick change in progress. */ + std::string status; /**< If the presence packet contained a status message, it is stored + * here. */ + JID* alternate; /**< If @c flags contains UserRoomDestroyed, and if the user who + * destroyed the room specified an alternate room, this member holds + * a pointer to the alternate room's JID, else it is 0. */ + }; + + /** + * @brief This interface enables inheriting classes to be notified about certain events in a MUC room. + * + * See MUCRoom for examples how to use this interface. + * + * @note This interface does not notify about room configuration related events. Use + * MUCRoomConfigHandler for that puprose. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API MUCRoomHandler + { + public: + /** + * Virtual Destructor. + */ + virtual ~MUCRoomHandler() {} + + /** + * This function is called whenever a room occupant enters the room, changes presence + * inside the room, or leaves the room. + * @note The MUCRoomParticipant struct, including pointers to JIDs, will be cleaned up after + * this function returned. + * @param room The room. + * @param participant A struct describing the occupant's status and/or action. + * @param presence The occupant's full presence. + */ + virtual void handleMUCParticipantPresence( MUCRoom* room, const MUCRoomParticipant participant, + const Presence& presence ) = 0; + + /** + * This function is called when a message arrives through the room. + * @note This may be a private message! If the message is private, and you want to answer + * it privately, you should create a new MessageSession to the user's full room nick and use + * that for any further private communication with the user. + * @param room The room the message came from. + * @param msg The entire Message. + * @param priv Indicates whether this is a private message. + * @note The sender's nick name can be obtained with this call: + * @code + * const std::string nick = msg.from().resource(); + * @endcode + * @note The message may contain an extension of type DelayedDelivery describing the + * date/time when the message was originally sent. The presence of such an extension + * usually indicates that the message is sent as part of the room history. This extension + * can be obtained with this call: + * @code + * const DelayedDelivery* dd = msg.when(); // may be 0 if no such extension exists + * @endcode + */ + virtual void handleMUCMessage( MUCRoom* room, const Message& msg, bool priv ) = 0; + + /** + * This function is called if the room that was just joined didn't exist prior to the attempted + * join. Therfore the room was created by MUC service. To accept the default configuration of + * the room assigned by the MUC service, return @b true from this function. The room will be opened + * by the MUC service and available for other users to join. If you don't want to accept the default + * room configuration, return @b false from this function. The room will stay locked until it is + * either fully configured, created as an instant room, or creation is canceled. + * + * If you returned false from this function you should use one of the following options: + * @li use MUCRoom::cancelRoomCreation() to abort creation and delete the room, + * @li use MUCRoom::acknowledgeInstantRoom() to accept the room's default configuration, or + * @li use MUCRoom::requestRoomConfig() to request the room's configuration form. + * + * @param room The room. + * @return @b True to accept the default room configuration, @b false to keep the room locked + * until configured manually by the room owner. + */ + virtual bool handleMUCRoomCreation( MUCRoom* room ) = 0; + + /** + * This function is called when the room subject has been changed. + * @param room The room. + * @param nick The nick of the occupant that changed the room subject. + * @note With some MUC services the nick may be empty when a room is first entered. + * @param subject The new room subject. + */ + virtual void handleMUCSubject( MUCRoom* room, const std::string& nick, + const std::string& subject ) = 0; + + /** + * This function is called when the user invited somebody (e.g., by using MUCRoom::invite()) + * to the room, but the invitation was declined by that person. + * @param room The room. + * @param invitee The JID if the person that declined the invitation. + * @param reason An optional reason for declining the invitation. + */ + virtual void handleMUCInviteDecline( MUCRoom* room, const JID& invitee, + const std::string& reason ) = 0; + + /** + * This function is called when an error occurs in the room or when entering the room. + * @note The following error conditions are specified for MUC: + * @li @b Not @b Authorized: Password required. + * @li @b Forbidden: Access denied, user is banned. + * @li @b Item @b Not @b Found: The room does not exist. + * @li @b Not @b Allowed: Room creation is restricted. + * @li @b Not @b Acceptable: Room nicks are locked down. + * @li @b Registration @b Required: User is not on the member list. + * @li @b Conflict: Desired room nickname is in use or registered by another user. + * @li @b Service @b Unavailable: Maximum number of users has been reached. + * + * Other errors might appear, depending on the service implementation. + * @param room The room. + * @param error The error. + */ + virtual void handleMUCError( MUCRoom* room, StanzaError error ) = 0; + + /** + * This function usually (see below) is called in response to a call to MUCRoom::getRoomInfo(). + * @param room The room. + * @param features ORed MUCRoomFlag's. + * @param name The room's name as returned by Service Discovery. + * @param infoForm A DataForm containing extended room information. May be 0 if the service + * doesn't support extended room information. See Section 15.5 of XEP-0045 for defined + * field types. You should not delete the form. + * + * @note This function may be called without a prior call to MUCRoom::getRoomInfo(). This + * happens if the room config is changed, e.g. by a room admin. + */ + virtual void handleMUCInfo( MUCRoom* room, int features, const std::string& name, + const DataForm* infoForm ) = 0; + + /** + * This function is called in response to a call to MUCRoom::getRoomItems(). + * @param room The room. + * @param items A map of room participants. The key is the name, the value is the occupant's + * room JID. The map may be empty if such info is private. + */ + virtual void handleMUCItems( MUCRoom* room, const Disco::ItemList& items ) = 0; + + }; + +} + +#endif// MUCROOMHANDLER_H__ diff --git a/libs/libgloox/mutex.cpp b/libs/libgloox/mutex.cpp new file mode 100644 index 0000000..0f9898c --- /dev/null +++ b/libs/libgloox/mutex.cpp @@ -0,0 +1,130 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "mutex.h" + +#include "config.h" + +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) +# include +#endif + +#ifdef _WIN32_WCE +# include +#endif + +#ifdef HAVE_PTHREAD +# include +#endif + +namespace gloox +{ + + namespace util + { + + class Mutex::MutexImpl + { + public: + MutexImpl(); + ~MutexImpl(); + void lock(); + bool trylock(); + void unlock(); + private: + MutexImpl( const MutexImpl& ); + MutexImpl& operator=( const MutexImpl& ); + +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + CRITICAL_SECTION m_cs; +#elif defined( HAVE_PTHREAD ) + pthread_mutex_t m_mutex; +#endif + + }; + + Mutex::MutexImpl::MutexImpl() + { +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + InitializeCriticalSection( &m_cs ); +#elif defined( HAVE_PTHREAD ) + pthread_mutex_init( &m_mutex, 0 ); +#endif + } + + Mutex::MutexImpl::~MutexImpl() + { +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + DeleteCriticalSection( &m_cs ); +#elif defined( HAVE_PTHREAD ) + pthread_mutex_destroy( &m_mutex ); +#endif + } + + void Mutex::MutexImpl::lock() + { +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + EnterCriticalSection( &m_cs ); +#elif defined( HAVE_PTHREAD ) + pthread_mutex_lock( &m_mutex ); +#endif + } + + bool Mutex::MutexImpl::trylock() + { +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + return TryEnterCriticalSection( &m_cs ) ? true : false; +#elif defined( HAVE_PTHREAD ) + return !( pthread_mutex_trylock( &m_mutex ) ); +#else + return true; +#endif + } + + void Mutex::MutexImpl::unlock() + { +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + LeaveCriticalSection( &m_cs ); +#elif defined( HAVE_PTHREAD ) + pthread_mutex_unlock( &m_mutex ); +#endif + } + + Mutex::Mutex() + : m_mutex( new MutexImpl() ) + { + } + + Mutex::~Mutex() + { + delete m_mutex; + } + + void Mutex::lock() + { + m_mutex->lock(); + } + + bool Mutex::trylock() + { + return m_mutex->trylock(); + } + + void Mutex::unlock() + { + m_mutex->unlock(); + } + + } + +} diff --git a/libs/libgloox/mutex.h b/libs/libgloox/mutex.h new file mode 100644 index 0000000..dab121c --- /dev/null +++ b/libs/libgloox/mutex.h @@ -0,0 +1,77 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef MUTEX_H__ +#define MUTEX_H__ + +#include "macros.h" + +namespace gloox +{ + + namespace util + { + /** + * @brief A simple implementation of mutex as a wrapper around a pthread mutex + * or a win32 critical section. + * + * If you locked a mutex you MUST unlock it within the same thread. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API Mutex + { + public: + /** + * Contructs a new simple mutex. + */ + Mutex(); + + /** + * Destructor + */ + ~Mutex(); + + /** + * Locks the mutex. + */ + void lock(); + + /** + * Tries to lock the mutex. + * @return @b True if the attempt was successful, @b false otherwise. + * @note This function also returns @b true if mutex support is not available, ie. if gloox + * is compiled without pthreads on non-Windows platforms. Make sure threads/mutexes are available + * if your code relies on trylock(). + */ + bool trylock(); + + /** + * Releases the mutex. + */ + void unlock(); + + private: + class MutexImpl; + + Mutex& operator=( const Mutex& ); + MutexImpl* m_mutex; + + }; + + } + +} + +#endif // MUTEX_H__ diff --git a/libs/libgloox/mutexguard.h b/libs/libgloox/mutexguard.h new file mode 100644 index 0000000..09ade92 --- /dev/null +++ b/libs/libgloox/mutexguard.h @@ -0,0 +1,61 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef MUTEXGUARD_H__ +#define MUTEXGUARD_H__ + +#include "mutex.h" + +namespace gloox +{ + + namespace util + { + + /** + * @brief A simple implementation of a mutex guard. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API MutexGuard + { + public: + /** + * Contructs a new simple mutex guard and locks the supplied Mutex. + * @param mutex The Mutex to guard. + */ + MutexGuard( Mutex* mutex ) : m_mutex( *mutex ) { if( mutex ) m_mutex.lock(); } + + /** + * Contructs a new simple mutex guard and locks the supplied Mutex. + * @param mutex The Mutex to guard. + */ + MutexGuard( Mutex& mutex ) : m_mutex( mutex ) { m_mutex.lock(); } + + /** + * Destructor. Releases the guarded Mutex. + */ + ~MutexGuard() { m_mutex.unlock(); } + + private: + MutexGuard& operator=( const MutexGuard& ); + Mutex& m_mutex; + + }; + + } + +} + +#endif // MUTEXGUARD_H__ diff --git a/libs/libgloox/nickname.cpp b/libs/libgloox/nickname.cpp new file mode 100644 index 0000000..0b1ab6b --- /dev/null +++ b/libs/libgloox/nickname.cpp @@ -0,0 +1,44 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#include "nickname.h" +#include "tag.h" + +namespace gloox +{ + + Nickname::Nickname( const Tag* tag ) + : StanzaExtension( ExtNickname ) + { + if( tag ) + m_nick = tag->cdata(); + } + + const std::string& Nickname::filterString() const + { + static const std::string filter = + "/presence/nick[@xmlns='" + XMLNS_NICKNAME + "']" + "|/message/nick[@xmlns='" + XMLNS_NICKNAME + "']"; + return filter; + } + + Tag* Nickname::tag() const + { + if( m_nick.empty() ) + return 0; + + Tag* n = new Tag( "nick", XMLNS, XMLNS_NICKNAME ); + n->setCData( m_nick ); + return n; + } + +} diff --git a/libs/libgloox/nickname.h b/libs/libgloox/nickname.h new file mode 100644 index 0000000..8b21003 --- /dev/null +++ b/libs/libgloox/nickname.h @@ -0,0 +1,87 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#ifndef NICKNAME_H__ +#define NICKNAME_H__ + +#include "gloox.h" +#include "stanzaextension.h" + +#include + +namespace gloox +{ + + class Tag; + + /** + * @brief An implementation of User Nickname (XEP-0172) as a StanzaExtension. + * + * XEP version: 1.0 + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API Nickname : public StanzaExtension + { + public: + + /** + * Constructs a new object from the given Tag. + * @param tag A Tag to parse. + */ + Nickname( const Tag* tag ); + + /** + * Constructs a new Nickname object. + * @param nick The nickname to include. + */ + Nickname( const std::string& nick ) + : StanzaExtension( ExtNickname ), m_nick( nick ) + {} + + /** + * Virtual destructor. + */ + virtual ~Nickname() {} + + /** + * Returns the extension's saved nickname. + * @return The nickname. + */ + const std::string nick() const { return m_nick; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new Nickname( tag ); + } + + // reimplemented from StanzaExtension + Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new Nickname( *this ); + } + + private: + std::string m_nick; + + }; + +} + +#endif // NICKNAME_H__ diff --git a/libs/libgloox/nonsaslauth.cpp b/libs/libgloox/nonsaslauth.cpp new file mode 100644 index 0000000..a0cf734 --- /dev/null +++ b/libs/libgloox/nonsaslauth.cpp @@ -0,0 +1,174 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "nonsaslauth.h" +#include "client.h" +#include "error.h" +#include "sha.h" + +#include + +namespace gloox +{ + + // ---- NonSaslAuth::Query ---- + NonSaslAuth::Query::Query( const std::string& user ) + : StanzaExtension( ExtNonSaslAuth ), m_user( user ), m_digest( true ) + { + } + + NonSaslAuth::Query::Query( const Tag* tag ) + : StanzaExtension( ExtNonSaslAuth ) + { + if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_AUTH ) + return; + + m_digest = tag->hasChild( "digest" ); + } + + NonSaslAuth::Query* NonSaslAuth::Query::newInstance( const std::string& user, + const std::string& sid, + const std::string& pwd, + const std::string& resource ) const + { + Query* q = new Query( user ); + if( m_digest && !sid.empty() ) + { + SHA sha; + sha.feed( sid ); + sha.feed( pwd ); + q->m_pwd = sha.hex(); + } + else + q->m_pwd = pwd; + + q->m_resource = resource; + q->m_digest = m_digest; + return q; + } + + const std::string& NonSaslAuth::Query::filterString() const + { + static const std::string filter = "/iq/query[@xmlns='" + XMLNS_AUTH + "']"; + return filter; + } + + Tag* NonSaslAuth::Query::tag() const + { + if( m_user.empty() ) + return 0; + + Tag* t = new Tag( "query" ); + t->setXmlns( XMLNS_AUTH ); + new Tag( t, "username", m_user ); + + if( !m_pwd.empty() && !m_resource.empty() ) + { + new Tag( t, m_digest ? "digest" : "password", m_pwd ); + new Tag( t, "resource", m_resource ); + } + + return t; + } + // ---- ~NonSaslAuth::Query ---- + + // ---- NonSaslAuth ---- + NonSaslAuth::NonSaslAuth( Client* parent ) + : m_parent( parent ) + { + if( m_parent ) + { + m_parent->registerStanzaExtension( new Query() ); + m_parent->registerIqHandler( this, ExtNonSaslAuth ); + } + } + + NonSaslAuth::~NonSaslAuth() + { + if( m_parent ) + { + m_parent->removeStanzaExtension( ExtNonSaslAuth ); + m_parent->removeIqHandler( this, ExtNonSaslAuth ); + m_parent->removeIDHandler( this ); + } + } + + void NonSaslAuth::doAuth( const std::string& sid ) + { + m_sid = sid; + const std::string& id = m_parent->getID(); + + IQ iq( IQ::Get, m_parent->jid().server(), id ); + iq.addExtension( new Query( m_parent->username() ) ); + m_parent->send( iq, this, TrackRequestAuthFields ); + } + + void NonSaslAuth::handleIqID( const IQ& iq, int context ) + { + switch( iq.subtype() ) + { + case IQ::Error: + { + const Error* e = iq.error(); + if( e ) + { + switch( e->error() ) + { + case StanzaErrorConflict: + m_parent->setAuthFailure( NonSaslConflict ); + break; + case StanzaErrorNotAcceptable: + m_parent->setAuthFailure( NonSaslNotAcceptable ); + break; + case StanzaErrorNotAuthorized: + m_parent->setAuthFailure( NonSaslNotAuthorized ); + break; + default: + break; + } + } + m_parent->setAuthed( false ); + m_parent->disconnect( ConnAuthenticationFailed ); + break; + } + case IQ::Result: + switch( context ) + { + case TrackRequestAuthFields: + { + const Query* q = iq.findExtension( ExtNonSaslAuth ); + if( !q ) + return; + + const std::string& id = m_parent->getID(); + + IQ re( IQ::Set, JID(), id ); + re.addExtension( q->newInstance( m_parent->username(), m_sid, + m_parent->password(), + m_parent->jid().resource() ) ); + m_parent->send( re, this, TrackSendAuth ); + break; + } + case TrackSendAuth: + m_parent->setAuthed( true ); + m_parent->connected(); + break; + } + break; + + default: + break; + } + } + +} diff --git a/libs/libgloox/nonsaslauth.h b/libs/libgloox/nonsaslauth.h new file mode 100644 index 0000000..cfc1eae --- /dev/null +++ b/libs/libgloox/nonsaslauth.h @@ -0,0 +1,147 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef NONSASLAUTH_H__ +#define NONSASLAUTH_H__ + +#include "iqhandler.h" + +#include + +namespace gloox +{ + + class Client; + class Stanza; + class Tag; + + /** + * @brief This class is an implementation of XEP-0078 (Non-SASL Authentication). + * + * It is invoked by @ref Client automatically if supported by the server and if SASL authentication + * is not supported. + * You should not need to use this class manually. + * + * XEP Version: 2.3 + * @author Jakob Schroeter + * @since 0.3 + */ + class GLOOX_API NonSaslAuth : public IqHandler + { + public: + /** + * Constructor. + * @param parent The @ref ClientBase which is used to authenticate. + */ + NonSaslAuth( Client* parent ); + + /** + * Virtual Destructor. + */ + virtual ~NonSaslAuth(); + + /** + * Starts authentication by querying the server for the required authentication fields. + * Digest authentication is preferred over plain text passwords. + * @param sid The session ID given by the server with the stream opening tag. + */ + void doAuth( const std::string& sid ); + + // reimplemented from IqHandler + virtual bool handleIq( const IQ& iq ) { (void)iq; return false; } + + // reimplemented from IqHandler + virtual void handleIqID( const IQ& iq, int context ); + + private: +#ifdef NONSASLAUTH_TEST + public: +#endif + /** + * @brief An abstraction of an IQ extension used for Non-SASL authentication (XEP-0078). + * + * @author Jakob Schroeter + * @since 1.0 + */ + class Query : public StanzaExtension + { + public: + /** + * Creates a new object that can be used to query the server for + * authentication filds for the given user. + * @param user The user name to fetch authentication fields for. + */ + Query( const std::string& user ); + + /** + * Creates a now object from the given Tag. + * @param tag The Tag to parse. + */ + Query( const Tag* tag = 0 ); + + /** + * Creates a new object on the heap that can be used to + * authenticate, based on the current reply. + * @param user The uset o authenticate as. + * @param sid The stream's ID. + * @param pwd The password to use. + * @param resource The desired resource identifier. + */ + Query* newInstance( const std::string& user, const std::string& sid, + const std::string& pwd, const std::string& resource ) const; + + /** + * Virtual destructor. + */ + virtual ~Query() {} + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new Query( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new Query( *this ); + } + + private: + std::string m_user; + std::string m_pwd; + std::string m_resource; + bool m_digest; + + }; + + enum NonSaslAuthTrack + { + TrackRequestAuthFields, + TrackSendAuth + }; + + Client* m_parent; + std::string m_sid; + + }; + +} + +#endif // NONSASLAUTH_H__ diff --git a/libs/libgloox/oob.cpp b/libs/libgloox/oob.cpp new file mode 100644 index 0000000..c59244f --- /dev/null +++ b/libs/libgloox/oob.cpp @@ -0,0 +1,81 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "oob.h" +#include "tag.h" + +namespace gloox +{ + + OOB::OOB( const std::string& url, const std::string& description, bool iqext ) + : StanzaExtension( ExtOOB ), m_url( url ), m_desc( description ), m_iqext( iqext ), + m_valid( true ) + { + if( m_url.empty() ) + m_valid = false; + } + + OOB::OOB( const Tag* tag ) + : StanzaExtension( ExtOOB ), m_iqext( false ), m_valid( false ) + { + if( tag && ( ( tag->name() == "x" && tag->hasAttribute( XMLNS, XMLNS_X_OOB ) ) || + ( tag && tag->name() == "query" && tag->hasAttribute( XMLNS, XMLNS_IQ_OOB ) ) ) ) + { + if( tag->name() == "query" ) + m_iqext = true; + } + else + return; + + if( tag->hasChild( "url" ) ) + { + m_valid = true; + m_url = tag->findChild( "url" )->cdata(); + } + if( tag->hasChild( "desc" ) ) + m_desc = tag->findChild( "desc" )->cdata(); + } + + OOB::~OOB() + { + } + + const std::string& OOB::filterString() const + { + static const std::string filter = + "/presence/x[@xmlns='" + XMLNS_X_OOB + "']" + "|/message/x[@xmlns='" + XMLNS_X_OOB + "']" + "|/iq/query[@xmlns='" + XMLNS_IQ_OOB + "']"; + return filter; + } + + Tag* OOB::tag() const + { + if( !m_valid ) + return 0; + + Tag* t = 0; + + if( m_iqext ) + t = new Tag( "query", XMLNS, XMLNS_IQ_OOB ); + else + t = new Tag( "x", XMLNS, XMLNS_X_OOB ); + + new Tag( t, "url", m_url ); + if( !m_desc.empty() ) + new Tag( t, "desc", m_desc ); + + return t; + } + +} diff --git a/libs/libgloox/oob.h b/libs/libgloox/oob.h new file mode 100644 index 0000000..c3095b2 --- /dev/null +++ b/libs/libgloox/oob.h @@ -0,0 +1,101 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef OOB_H__ +#define OOB_H__ + +#include "gloox.h" +#include "stanzaextension.h" + +#include + +namespace gloox +{ + + class Tag; + + /** + * @brief This is an abstraction of a jabber:x:oob namespace element or a jabber:iq:oob namespace element + * as specified in XEP-0066. + * + * XEP version: 1.5 + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API OOB : public StanzaExtension + { + public: + /** + * Constructs an OOB StanzaExtension from teh given URL and description. + * @param url The out-of-band URL. + * @param description The URL's optional description. + * @param iqext Whether this object extends an IQ or a Presence/Message stanza (results in + * either jabber:iq:oob or jabber:x:oob namespaced element). + */ + OOB( const std::string& url, const std::string& description, bool iqext ); + + /** + * Constructs an OOB object from the given Tag. To be recognized properly, the Tag must + * have either a name of 'x' in the jabber:x:oob namespace, or a name of 'query' in the + * jabber:iq:oob namespace. + * @param tag The Tag to parse. + */ + OOB( const Tag* tag ); + + /** + * Virtual destructor. + */ + virtual ~OOB(); + + /** + * Returns the out-of-band URL. + * @return The out-of-band URL. + */ + const std::string& url() const { return m_url; } + + /** + * Returns the URL's description. + * @return The URL's description. + */ + const std::string& desc() const { return m_desc; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new OOB( tag ); + } + + // reimplemented from StanzaExtension + Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new OOB( *this ); + } + + private: + std::string m_xmlns; + std::string m_url; + std::string m_desc; + bool m_iqext; + bool m_valid; + + }; + +} + +#endif // OOB_H__ diff --git a/libs/libgloox/parser.cpp b/libs/libgloox/parser.cpp new file mode 100644 index 0000000..a72fbdc --- /dev/null +++ b/libs/libgloox/parser.cpp @@ -0,0 +1,800 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#include "gloox.h" +#include "util.h" +#include "parser.h" + +#include + +namespace gloox +{ + + Parser::Parser( TagHandler* ph, bool deleteRoot ) + : m_tagHandler( ph ), m_current( 0 ), m_root( 0 ), m_xmlnss( 0 ), m_state( Initial ), + m_preamble( 0 ), m_quote( false ), m_haveTagPrefix( false ), m_haveAttribPrefix( false ), + m_attribIsXmlns( false ), m_deleteRoot( deleteRoot ) + { + } + + Parser::~Parser() + { + delete m_root; + delete m_xmlnss; + } + + Parser::DecodeState Parser::decode( std::string::size_type& pos, const std::string& data ) + { + std::string::size_type p = data.find( ';', pos ); + std::string::size_type diff = p - pos; + + if( p == std::string::npos ) + { + m_backBuffer = data.substr( pos ); + return DecodeInsufficient; + } + + if( diff < 3 || diff > 9 ) + return DecodeInvalid; + + std::string rep; + switch( data[pos + 1] ) + { + case '#': + { + int base = 10; + int idx = 2; + + if( data[pos + 2] == 'x' || data[pos + 2] == 'X' ) + { + base = 16; + idx = 3; + } + + char* end; + const long int val = std::strtol( data.data() + pos + idx, &end, base ); + if( *end != ';' || val < 0 ) + return DecodeInvalid; + + if( val == 0x9 || val == 0xA || val == 0xD || ( val >= 0x20 && val <= 0x7F ) ) + { + rep += char( val ); + } + else if( val >= 0x80 && val <= 0x7FF ) + { + rep += char( 192 + ( val >> 6 ) ); + rep += char( 128 + ( val % 64 ) ); + } + else if( ( val >= 0x800 && val <= 0xD7FF ) || ( val >= 0xE000 && val <= 0xFFFD ) ) + { + rep += char( 224 + ( val >> 12 ) ); + rep += char( 128 + ( ( val >> 6 ) % 64 ) ); + rep += char( 128 + ( val % 64 ) ); + } + else if( val >= 0x100000 && val < 0x10FFFF ) + { + rep += char( 240 + ( val >> 18 ) ); + rep += char( 128 + ( ( val >> 12 ) % 64 ) ); + rep += char( 128 + ( ( val >> 6 ) % 64 ) ); + rep += char( 128 + ( val % 64 ) ); + } + else + return DecodeInvalid; + } + break; + case 'l': + if( diff == 3 && data[pos + 2] == 't' ) + rep += '<'; + else + return DecodeInvalid; + break; + case 'g': + if( diff == 3 && data[pos + 2] == 't' ) + rep += '>'; + else + return DecodeInvalid; + break; + case 'a': + if( diff == 5 && !data.compare( pos + 1, 5, "apos;" ) ) + rep += '\''; + else if( diff == 4 && !data.compare( pos + 1, 4, "amp;" ) ) + rep += '&'; + else + return DecodeInvalid; + break; + case 'q': + if( diff == 5 && !data.compare( pos + 1, 5, "quot;" ) ) + rep += '"'; + else + return DecodeInvalid; + break; + default: + return DecodeInvalid; + } + + switch( m_state ) + { + case TagInside: + m_cdata += rep; + break; + case TagAttributeValue: + m_value += rep; + break; + default: + break; + } + pos += diff; + return DecodeValid; + } + + Parser::ForwardScanState Parser::forwardScan( std::string::size_type& pos, const std::string& data, + const std::string& needle ) + { + if( pos + needle.length() <= data.length() ) + { + if( !data.compare( pos, needle.length(), needle ) ) + { + pos += needle.length() - 1; + return ForwardFound; + } + else + { + return ForwardNotFound; + } + } + else + { + m_backBuffer = data.substr( pos ); + return ForwardInsufficientSize; + } + } + + int Parser::feed( std::string& data ) + { + if( !m_backBuffer.empty() ) + { + data.insert( 0, m_backBuffer ); + m_backBuffer = EmptyString; + } + + std::string::size_type count = data.length(); + for( std::string::size_type i = 0; i < count; ++i ) + { + const unsigned char c = data[i]; +// printf( "found char: %c, ", c ); + + if( !isValid( c ) ) + { + cleanup(); + return static_cast( i ); + } + + switch( m_state ) + { + case Initial: +// printf( "Initial: %c\n", c ); + if( isWhitespace( c ) ) + break; + + switch( c ) + { + case '<': + m_state = TagOpening; + break; + default: + cleanup(); + return static_cast( i ); + break; + } + break; + case InterTag: +// printf( "InterTag: %c\n", c ); + m_tag = EmptyString; + if( isWhitespace( c ) ) + break; + + switch( c ) + { + case '<': + m_state = TagOpening; + break; + case '>': + default: + if( m_current ) + { + m_cdata += c; + m_state = TagInside; + } + break; + } + break; + case TagOpening: // opening '<' has been found before +// printf( "TagOpening: %c\n", c ); + if( isWhitespace( c ) ) + break; + + switch( c ) + { + case '<': + case '>': + case '&': + cleanup(); + return static_cast( i ); + break; + case '/': + m_state = TagClosingSlash; + break; + case '?': + m_state = TagNameCollect; + m_preamble = 1; + break; + case '!': + switch( forwardScan( i, data, "![CDATA[" ) ) + { + case ForwardFound: + m_state = TagCDATASection; + break; + case ForwardNotFound: + cleanup(); + return static_cast( i ); + case ForwardInsufficientSize: + return -1; + } + break; + default: + m_tag += c; + m_state = TagNameCollect; + break; + } + break; + case TagCDATASection: + switch( c ) + { + case ']': + switch( forwardScan( i, data, "]]>" ) ) + { + case ForwardFound: + m_state = TagInside; + break; + case ForwardNotFound: + m_cdata += c; + break; + case ForwardInsufficientSize: + return -1; + } + break; + default: + m_cdata += c; + break; + } + break; + case TagNameCollect: // we're collecting the tag's name, we have at least one octet already +// printf( "TagNameCollect: %c\n", c ); + if( isWhitespace( c ) ) + { + m_state = TagNameComplete; + break; + } + + switch( c ) + { + case '<': + case '?': + case '!': + case '&': + cleanup(); + return static_cast( i ); + break; + case '/': + m_state = TagOpeningSlash; + break; + case '>': + addTag(); + m_state = TagInside; + break; + case ':': + if( !m_haveTagPrefix ) + { + m_haveTagPrefix = true; + m_tagPrefix = m_tag; + m_tag = EmptyString; + } + else + { + cleanup(); + return static_cast( i ); + } + break; + default: + m_tag += c; + break; + } + break; + case TagInside: // we're inside a tag, expecting a child tag or cdata +// printf( "TagInside: %c\n", c ); + m_tag = EmptyString; + switch( c ) + { + case '<': + addCData(); + m_state = TagOpening; + break; + case '&': +// printf( "TagInside, calling decode\n" ); + switch( decode( i, data ) ) + { + case DecodeValid: + break; + case DecodeInvalid: + cleanup(); + return static_cast( i ); + case DecodeInsufficient: + return -1; + } + break; + default: + m_cdata += c; + break; + } + break; + case TagOpeningSlash: // a slash in an opening tag has been found, initing close of the tag +// printf( "TagOpeningSlash: %c\n", c ); + if( isWhitespace( c ) ) + break; + + if( c == '>' ) + { + addTag(); + if( !closeTag() ) + { +// printf( "noipe, here\n" ); + cleanup(); + return static_cast( i ); + } + + m_state = InterTag; + } + else + { + cleanup(); + return static_cast( i ); + } + break; + case TagClosingSlash: // we have found the '/' of a closing tag +// printf( "TagClosingSlash: %c\n", c ); + if( isWhitespace( c ) ) + break; + + switch( c ) + { + case '>': + case '<': + case '/': + cleanup(); + return static_cast( i ); + break; + default: + m_tag += c; + m_state = TagClosing; + break; + } + break; + case TagClosing: // we're collecting the name of a closing tag +// printf( "TagClosing: %c\n", c ); + switch( c ) + { + case '<': + case '/': + case '!': + case '?': + case '&': + cleanup(); + return static_cast( i ); + break; + case ':': + if( !m_haveTagPrefix ) + { + m_haveTagPrefix = true; + m_tagPrefix = m_tag; + m_tag = EmptyString; + } + else + { + cleanup(); + return static_cast( i ); + } + break; + case '>': + if( !closeTag() ) + { +// printf( "here\n" ); + cleanup(); + return static_cast( i ); + } + m_state = InterTag; + break; + default: + m_tag += c; + break; + } + break; + case TagNameComplete: // a tag name is complete, expect tag close or attribs +// printf( "TagNameComplete: %c\n", c ); + if( isWhitespace( c ) ) + break; + + switch( c ) + { + case '<': + case '!': + case '&': + cleanup(); + return static_cast( i ); + break; + case '/': + m_state = TagOpeningSlash; + break; + case '>': + if( m_preamble == 1 ) + { + cleanup(); + return static_cast( i ); + } + m_state = TagInside; + addTag(); + break; + case '?': + if( m_preamble == 1 ) + m_preamble = 2; + else + { + cleanup(); + return static_cast( i ); + } + break; + default: + m_attrib += c; + m_state = TagAttribute; + break; + } + break; + case TagAttribute: // we're collecting the name of an attribute, we have at least 1 octet +// printf( "TagAttribute: %c\n", c ); + if( isWhitespace( c ) ) + { + m_state = TagAttributeComplete; + break; + } + + switch( c ) + { + case '<': + case '/': + case '>': + case '?': + case '!': + case '&': + cleanup(); + return static_cast( i ); + break; + case '=': + m_state = TagAttributeEqual; + break; + case ':': + if( !m_haveAttribPrefix && m_attrib != XMLNS ) + { + m_haveAttribPrefix = true; + m_attribPrefix = m_attrib; + m_attrib = EmptyString; + } + else if( m_attrib == XMLNS ) + { + m_attribIsXmlns = true; + m_attrib = EmptyString; + } + else + { + cleanup(); + return static_cast( i ); + } + break; + default: + m_attrib += c; + } + break; + case TagAttributeComplete: // we're expecting an equals sign or ws +// printf( "TagAttributeComplete: %c\n", c ); + if( isWhitespace( c ) ) + break; + + switch( c ) + { + case '=': + m_state = TagAttributeEqual; + break; + default: + cleanup(); + return static_cast( i ); + break; + } + break; + case TagAttributeEqual: // we have found an equals sign +// printf( "TagAttributeEqual: %c\n", c ); + if( isWhitespace( c ) ) + break; + + switch( c ) + { + case '"': + m_quote = true; + case '\'': + m_state = TagAttributeValue; + break; + default: + cleanup(); + return static_cast( i ); + break; + } + break; + case TagAttributeValue: // we're expecting value data +// printf( "TagValue: %c\n", c ); + switch( c ) + { + case '<': + cleanup(); + return static_cast( i ); + break; + case '\'': + if( m_quote ) + { + m_value += c; + break; + } + case '"': + addAttribute(); + m_state = TagNameAlmostComplete; + m_quote = false; + break; + case '&': +// printf( "TagAttributeValue, calling decode\n" ); + switch( decode( i, data ) ) + { + case DecodeValid: + break; + case DecodeInvalid: + cleanup(); + return static_cast( i ); + case DecodeInsufficient: + return -1; + } + break; + case '>': + default: + m_value += c; + } + break; + case TagNameAlmostComplete: +// printf( "TagAttributeEqual: %c\n", c ); + if( isWhitespace( c ) ) + { + m_state = TagNameComplete; + break; + } + + switch( c ) + { + case '/': + m_state = TagOpeningSlash; + break; + case '>': + if( m_preamble == 1 ) + { + cleanup(); + return static_cast( i ); + } + m_state = TagInside; + addTag(); + break; + case '?': + if( m_preamble == 1 ) + m_preamble = 2; + else + { + cleanup(); + return static_cast( i ); + } + break; + default: + cleanup(); + return static_cast( i ); + break; + } + break; + default: +// printf( "default action!?\n" ); + break; + } +// printf( "parser state: %d\n", m_state ); + } + + return -1; + } + + void Parser::addTag() + { + if( !m_root ) + { +// printf( "created Tag named %s, ", m_tag.c_str() ); + m_root = new Tag( m_tag ); + m_current = m_root; + } + else + { +// printf( "created Tag named %s, ", m_tag.c_str() ); + m_current = new Tag( m_current, m_tag ); + } + + if( m_haveTagPrefix ) + { +// printf( "setting tag prefix: %s\n", m_tagPrefix.c_str() ); + m_current->setPrefix( m_tagPrefix ); + m_haveTagPrefix = false; + } + + if( m_attribs.size() ) + { + m_current->setAttributes( m_attribs ); +// printf( "added %d attributes, ", m_attribs.size() ); + m_attribs.clear(); + } + + if( m_xmlnss ) + { +// printf( "have ns decls\n" ); +// StringMap::const_iterator it = m_xmlnss->begin(); +// for( ; it != m_xmlnss->end(); ++it ) +// printf( "%s='%s'\n", (*it).first.c_str(), (*it).second.c_str() ); + m_current->setXmlns( m_xmlnss ); + m_xmlnss = 0; + } + + m_current->setXmlns( m_xmlns ); + m_xmlns = EmptyString; + + if( m_tag == "stream" && m_root->xmlns() == XMLNS_STREAM ) + { + streamEvent( m_root ); + cleanup( m_deleteRoot ); + return; + } +// else +// printf( "%s, ", m_root->xml().c_str() ); + + if( m_root && m_root == m_current && m_tagPrefix == "stream" ) + m_root->setXmlns( XMLNS_STREAM, m_tagPrefix ); + + if( m_tag == "xml" && m_preamble == 2 ) + cleanup(); + } + + void Parser::addAttribute() + { + Tag::Attribute* attr = new Tag::Attribute( m_attrib, m_value );; + if( m_attribIsXmlns ) + { + if( !m_xmlnss ) + m_xmlnss = new StringMap(); + + (*m_xmlnss)[m_attrib] = m_value; + attr->setPrefix( XMLNS ); + } + else + { +// printf( "adding attribute: %s:%s='%s'\n", m_attribPrefix.c_str(), m_attrib.c_str(), m_value.c_str() ); + if( !m_attribPrefix.empty() ) + attr->setPrefix( m_attribPrefix ); + if( m_attrib == XMLNS ) + m_xmlns = m_value; + } + m_attribs.push_back( attr ); + m_attrib = EmptyString; + m_value = EmptyString; + m_attribPrefix = EmptyString; + m_haveAttribPrefix = false; + m_attribIsXmlns = false; + } + + void Parser::addCData() + { + if( m_current && !m_cdata.empty() ) + { + m_current->addCData( m_cdata ); +// printf( "added cdata %s to %s: %s\n", +// m_cdata.c_str(), m_current->name().c_str(), m_current->xml().c_str() ); + m_cdata = EmptyString; + } + } + + bool Parser::closeTag() + { +// printf( "about to close, " ); + + if( m_tag == "stream" && m_tagPrefix == "stream" ) + return true; + + if( !m_current || m_current->name() != m_tag + || ( !m_current->prefix().empty() && m_current->prefix() != m_tagPrefix ) ) + { +// printf( "current xml: %s\n", m_current->xml().c_str() ); +// printf( "current name: %s, m_tag: %s\n", m_current->name().c_str(), m_tag.c_str() ); +// printf( "current prefix: %s, m_tagPrefix: %s\n", m_current->prefix().c_str(), m_tagPrefix.c_str() ); + return false; + } + +// printf( "m_current: %s, ", m_current->name().c_str() ); +// printf( "m_tag: %s, ", m_tag.c_str() ); + + m_tagPrefix = EmptyString; + m_haveTagPrefix = false; + + if( m_current->parent() ) + m_current = m_current->parent(); + else + { +// printf( "pushing upstream\n" ); + streamEvent( m_root ); + cleanup( m_deleteRoot ); + } + + return true; + } + + void Parser::cleanup( bool deleteRoot ) + { + if( deleteRoot ) + delete m_root; + m_root = 0; + m_current = 0; + delete m_xmlnss; + m_xmlnss = 0; + m_cdata = EmptyString; + m_tag = EmptyString; + m_attrib = EmptyString; + m_attribPrefix = EmptyString; + m_tagPrefix = EmptyString; + m_haveAttribPrefix = false; + m_haveTagPrefix = false; + m_value = EmptyString; + m_xmlns = EmptyString; + util::clearList( m_attribs ); + m_attribs.clear(); + m_state = Initial; + m_preamble = 0; + } + + bool Parser::isValid( unsigned char c ) + { + return ( c != 0xc0 || c != 0xc1 || c < 0xf5 ); + } + + bool Parser::isWhitespace( unsigned char c ) + { + return ( c == 0x09 || c == 0x0a || c == 0x0d || c == 0x20 ); + } + + void Parser::streamEvent( Tag* tag ) + { + if( m_tagHandler ) + m_tagHandler->handleTag( tag ); + } + +} diff --git a/libs/libgloox/parser.h b/libs/libgloox/parser.h new file mode 100644 index 0000000..c8e333c --- /dev/null +++ b/libs/libgloox/parser.h @@ -0,0 +1,139 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef PARSER_H__ +#define PARSER_H__ + +#include "gloox.h" +#include "taghandler.h" +#include "tag.h" + +#include + +namespace gloox +{ + + + /** + * @brief This class implements an XML parser. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API Parser + { + public: + /** + * Constructs a new Parser object. + * @param ph The object to send incoming Tags to. + * @param deleteRoot Indicates whether a parsed Tag should be + * deleted after pushing it upstream. Defaults to @p true. + */ + Parser( TagHandler* ph, bool deleteRoot = true ); + + /** + * Virtual destructor. + */ + virtual ~Parser(); + + /** + * Use this function to feed the parser with more XML. + * @param data Raw xml to parse. It may be modified if backbuffering is necessary. + * @return Returns @b -1 if parsing was successful. If a parse error occured, the + * character position where the error was occured is returned. + */ + int feed( std::string& data ); + + /** + * Resets internal state. + * @param deleteRoot Whether to delete the m_root member. For + * internal use only. + */ + void cleanup( bool deleteRoot = true ); + + private: + enum ParserInternalState + { + Initial, + InterTag, + TagOpening, + TagOpeningSlash, + TagOpeningLt, + TagInside, + TagNameCollect, + TagNameComplete, + TagNameAlmostComplete, + TagAttribute, + TagAttributeComplete, + TagAttributeEqual, + TagClosing, + TagClosingSlash, + TagValueApos, + TagAttributeValue, + TagPreamble, + TagCDATASection + }; + + enum ForwardScanState + { + ForwardFound, + ForwardNotFound, + ForwardInsufficientSize + }; + + enum DecodeState + { + DecodeValid, + DecodeInvalid, + DecodeInsufficient + }; + + void addTag(); + void addAttribute(); + void addCData(); + bool closeTag(); + bool isWhitespace( unsigned char c ); + bool isValid( unsigned char c ); + void streamEvent( Tag* tag ); + ForwardScanState forwardScan( std::string::size_type& pos, const std::string& data, + const std::string& needle ); + DecodeState decode( std::string::size_type& pos, const std::string& data ); + + TagHandler* m_tagHandler; + Tag* m_current; + Tag* m_root; + StringMap* m_xmlnss; + + ParserInternalState m_state; + Tag::AttributeList m_attribs; + std::string m_tag; + std::string m_cdata; + std::string m_attrib; + std::string m_value; + std::string m_xmlns; + std::string m_tagPrefix; + std::string m_attribPrefix; + std::string m_backBuffer; + int m_preamble; + bool m_quote; + bool m_haveTagPrefix; + bool m_haveAttribPrefix; + bool m_attribIsXmlns; + bool m_deleteRoot; + + }; + +} + +#endif // PARSER_H__ diff --git a/libs/libgloox/prep.cpp b/libs/libgloox/prep.cpp new file mode 100644 index 0000000..64dcc8d --- /dev/null +++ b/libs/libgloox/prep.cpp @@ -0,0 +1,121 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#include "prep.h" + +#include "config.h" + +#ifdef HAVE_LIBIDN +# include +# include +#endif + +#include +#include + +#include + +#define JID_PORTION_SIZE 1023 + +namespace gloox +{ + + namespace prep + { + +#ifdef HAVE_LIBIDN + /** + * Applies a Stringprep profile to a string. This function does the actual + * work behind nodeprep, nameprep and resourceprep. + * @param s The string to apply the profile to. + * @param out Contains the prepped string if prepping was successful, else untouched. + * @param profile The Stringprep profile to apply. + * @return Returns @b true if prepping was successful, @b false otherwise. + */ + static bool prepare( const std::string& s, std::string& out, const Stringprep_profile* profile ) + { + if( s.empty() || s.length() > JID_PORTION_SIZE ) + return false; + + char* p = static_cast( calloc( JID_PORTION_SIZE, sizeof( char ) ) ); + strncpy( p, s.c_str(), s.length() ); + int rc = stringprep( p, JID_PORTION_SIZE, (Stringprep_profile_flags)0, profile ); + if( rc == STRINGPREP_OK ) + out = p; + free( p ); + return rc == STRINGPREP_OK; + } +#endif + + bool nodeprep( const std::string& node, std::string& out ) + { +#ifdef HAVE_LIBIDN + return prepare( node, out, stringprep_xmpp_nodeprep ); +#else + if( node.length() > JID_PORTION_SIZE ) + return false; + out = node; + return true; +#endif + } + + bool nameprep( const std::string& domain, std::string& out ) + { +#ifdef HAVE_LIBIDN + return prepare( domain, out, stringprep_nameprep ); +#else + if( domain.length() > JID_PORTION_SIZE ) + return false; + out = domain; + return true; +#endif + } + + bool resourceprep( const std::string& resource, std::string& out ) + { +#ifdef HAVE_LIBIDN + return prepare( resource, out, stringprep_xmpp_resourceprep ); +#else + if( resource.length() > JID_PORTION_SIZE ) + return false; + out = resource; + return true; +#endif + } + + bool idna( const std::string& domain, std::string& out ) + { +#ifdef HAVE_LIBIDN + if( domain.empty() || domain.length() > JID_PORTION_SIZE ) + return false; + + char* prepped; + int rc = idna_to_ascii_8z( domain.c_str(), &prepped, (Idna_flags)IDNA_USE_STD3_ASCII_RULES ); + if( rc == IDNA_SUCCESS ) + { + out = prepped; + return true; + } + if( rc != IDNA_MALLOC_ERROR ) + free( prepped ); + return false; +#else + if( domain.length() > JID_PORTION_SIZE ) + return false; + out = domain; + return true; +#endif + } + + } + +} diff --git a/libs/libgloox/prep.h b/libs/libgloox/prep.h new file mode 100644 index 0000000..d8c5d37 --- /dev/null +++ b/libs/libgloox/prep.h @@ -0,0 +1,85 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef PREP_H__ +#define PREP_H__ + +#include "macros.h" + +#include + +namespace gloox +{ + + /** + * @brief This namespace offers functions to stringprep the individual parts of a JID. + * + * You should not need to use these functions directly. All the + * necessary prepping is done for you if you stick to the interfaces provided. + * If you write your own enhancements, check with the spec. + * + * @note These functions depend on an installed LibIDN at compile time of gloox. If + * LibIDN is not installed these functions return the string they are given + * without any modification. + * + * @author Jakob Schroeter + * @since 0.2 + */ + namespace prep + { + /** + * This function applies the Nodeprep profile of Stringprep to a string. + * @param node The string to apply the profile to. + * @param out The prepped string. In case of an error this string is not touched. + * If LibIDN is not available the string is returned unchanged. + * @return @b True if prepping was successful, @b false otherwise or of LibIDN + * is not available. + */ + bool nodeprep( const std::string& node, std::string& out ); + + /** + * This function applies the Nameprep profile of Stringprep to a string. + * @param domain The string to apply the profile to. + * @param out The prepped string. In case of an error this string is not touched. + * If LibIDN is not available the string is returned unchanged. + * @return @b True if prepping was successful, @b false otherwise or of LibIDN + * is not available. + */ + bool nameprep( const std::string& domain, std::string& out ); + + /** + * This function applies the Resourceprep profile of Stringprep to a std::string. + * @param resource The string to apply the profile to. + * @param out The prepped string. In case of an error this string is not touched. + * If LibIDN is not available the string is returned unchanged. + * @return @b True if prepping was successful, @b false otherwise or of LibIDN + * is not available. + */ + bool resourceprep( const std::string& resource, std::string& out ); + + /** + * This function applies the idna() function to a string. I.e. it transforms + * internationalized domain names into plain ASCII. + * @param domain The string to convert. + * @param out The converted string. In case of an error this string is not touched. + * If LibIDN is not available the string is returned unchanged. + * @return @b True if prepping was successful, @b false otherwise or of LibIDN + * is not available. + */ + bool idna( const std::string& domain, std::string& out ); + + } + +} + +#endif // PREP_H__ diff --git a/libs/libgloox/presence.cpp b/libs/libgloox/presence.cpp new file mode 100644 index 0000000..67c54a8 --- /dev/null +++ b/libs/libgloox/presence.cpp @@ -0,0 +1,143 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#include "presence.h" +#include "capabilities.h" +#include "util.h" + +#include + +namespace gloox +{ + + static const char* msgTypeStringValues[] = + { + "available", "", "", "", "", "unavailable", "probe", "error" + }; + + static inline const std::string typeString( Presence::PresenceType type ) + { + return util::lookup( type, msgTypeStringValues ); + } + + static const char* msgShowStringValues[] = + { + "", "chat", "away", "dnd", "xa", "", "", "" + }; + + static inline const std::string showString( Presence::PresenceType type ) + { + return util::lookup( type, msgShowStringValues ); + } + + Presence::Presence( Tag* tag ) + : Stanza( tag ), m_subtype( Invalid ), m_stati( 0 ), m_priority( 0 ) + { + if( !tag || tag->name() != "presence" ) + return; + + const std::string& type = tag->findAttribute( TYPE ); + if( type.empty() ) + m_subtype = Available; + else + m_subtype = (PresenceType)util::lookup( type, msgTypeStringValues ); + + if( m_subtype == Available ) + { + Tag* t = tag->findChild( "show" ); + if( t ) + m_subtype = (PresenceType)util::lookup( t->cdata(), msgShowStringValues ); + } + + const TagList& c = tag->children(); + TagList::const_iterator it = c.begin(); + for( ; it != c.end(); ++it ) + { + if( (*it)->name() == "status" ) + setLang( &m_stati, m_status, (*it) ); + else if( (*it)->name() == "priority" ) + m_priority = atoi( (*it)->cdata().c_str() ); + } + } + + Presence::Presence( PresenceType type, const JID& to, const std::string& status, + int priority, const std::string& xmllang ) + : Stanza( to ), m_subtype( type ), m_stati( 0 ) + { + setLang( &m_stati, m_status, status, xmllang ); + + setPriority( priority ); + } + + Presence::~Presence() + { + delete m_stati; + } + + void Presence::resetStatus() + { + delete m_stati; + m_stati = 0; + m_status = ""; + } + + void Presence::setPriority( int priority ) + { + if( priority < -128 ) + m_priority = -128; + else if( priority > 127 ) + m_priority = 127; + else + m_priority = priority; + } + + const Capabilities* Presence::capabilities() const + { + return findExtension( ExtCaps ); + } + + Tag* Presence::tag() const + { + if( m_subtype == Invalid ) + return 0; + + Tag* t = new Tag( "presence" ); + if( m_to ) + t->addAttribute( "to", m_to.full() ); + if( m_from ) + t->addAttribute( "from", m_from.full() ); + + const std::string& type = typeString( m_subtype ); + if( !type.empty() ) + { + if( type != "available" ) + t->addAttribute( "type", type ); + } + else + { + const std::string& show = showString( m_subtype ); + if( !show.empty() ) + new Tag( t, "show", show ); + } + + new Tag( t, "priority", util::int2string( m_priority ) ); + + getLangs( m_stati, m_status, "status", t ); + + StanzaExtensionList::const_iterator it = m_extensionList.begin(); + for( ; it != m_extensionList.end(); ++it ) + t->addChild( (*it)->tag() ); + + return t; + } + +} diff --git a/libs/libgloox/presence.h b/libs/libgloox/presence.h new file mode 100644 index 0000000..1611181 --- /dev/null +++ b/libs/libgloox/presence.h @@ -0,0 +1,161 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#ifndef PRESENCE_H__ +#define PRESENCE_H__ + +#include "stanza.h" + +#include + +namespace gloox +{ + + class Capabilities; + class JID; + + /** + * @brief An abstraction of a presence stanza. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API Presence : public Stanza + { + + friend class ClientBase; + + public: + + /** + * Describes the different valid presence types. + */ + enum PresenceType + { + Available, /**< The entity is online. */ + Chat, /**< The entity is 'available for chat'. */ + Away, /**< The entity is away. */ + DND, /**< The entity is DND (Do Not Disturb). */ + XA, /**< The entity is XA (eXtended Away). */ + Unavailable, /**< The entity is offline. */ + Probe, /**< This is a presence probe. */ + Error, /**< This is a presence error. */ + Invalid /**< The stanza is invalid. */ + }; + + /** + * Creates a Presence request. + * @param type The presence type. + * @param to The intended receiver. Use an empty JID to create a broadcast packet. + * @param status An optional status message (e.g. "gone fishing"). + * @param priority An optional presence priority. Legal range is between -128 and +127. + * Defaults to 0. + * @param xmllang An optional xml:lang for the status message. + */ + Presence( PresenceType type, const JID& to, const std::string& status = EmptyString, + int priority = 0, const std::string& xmllang = EmptyString ); + + /** + * Destructor. + */ + virtual ~Presence(); + + /** + * Returns the presence's type. + * @return The presence's type. + */ + PresenceType subtype() const { return m_subtype; } + + /** + * A convenience function returning the stanza's Capabilities, if any. May be 0. + * @return A pointer to a Capabilities object, or 0. + */ + const Capabilities* capabilities() const; + + /** + * Returns the presence's type. + * @return The presence's type. + */ +//#warning FIXME return something useful (only 'show' values?) or kill this func + PresenceType presence() const { return m_subtype; } + + /** + * Sets the presence type. + * @param type The presence type. + */ + void setPresence( PresenceType type ) { m_subtype = type; } + + /** + * Returns the status text of a presence stanza for the given language if available. + * If the requested language is not available, the default status text (without a xml:lang + * attribute) will be returned. + * @param lang The language identifier for the desired language. It must conform to + * section 2.12 of the XML specification and RFC 3066. If empty, the default body + * will be returned, if any. + * @return The status text set by the sender. + */ + const std::string status( const std::string& lang = "default" ) const + { + return findLang( m_stati, m_status, lang ); + } + + /** + * Adds a (possibly translated) status message. + * @param status The status message. + * @param lang The language identifier for the desired language. It must conform to + * section 2.12 of the XML specification and RFC 3066. + */ + void addStatus( const std::string& status, const std::string& lang = EmptyString ) + { + setLang( &m_stati, m_status, status, lang ); + } + + /** + * Resets the default status message as well as all language-specific ones. + */ + void resetStatus(); + + /** + * Returns the presence priority in the legal range: -128 to +127. + * @return The priority information contained in the stanza, defaults to 0. + */ + int priority() const { return m_priority; } + + /** + * Sets the priority. Legal range: -128 to +127. + * @param priority The priority to set. + */ + void setPriority( int priority ); + + // reimplemented from Stanza + virtual Tag* tag() const; + + private: +#ifdef PRESENCE_TEST + public: +#endif + /** + * Creates a Presence request from the given Tag. The original Tag will be ripped off. + * @param tag The Tag to parse. + */ + Presence( Tag* tag ); + + PresenceType m_subtype; + StringMap* m_stati; + std::string m_status; + int m_priority; + + }; + +} + +#endif // PRESENCE_H__ diff --git a/libs/libgloox/presencehandler.h b/libs/libgloox/presencehandler.h new file mode 100644 index 0000000..8e727e3 --- /dev/null +++ b/libs/libgloox/presencehandler.h @@ -0,0 +1,50 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef PRESENCEHANDLER_H__ +#define PRESENCEHANDLER_H__ + +#include "presence.h" + +namespace gloox +{ + + /** + * @brief A virtual interface which can be reimplemented to receive presence stanzas. + * + * Derived classes can be registered as PresenceHandlers with the Client. + * Upon an incoming Presence packet @ref handlePresence() will be called. + * @author Jakob Schroeter + */ + class GLOOX_API PresenceHandler + { + public: + /** + * Virtual Destructor. + */ + virtual ~PresenceHandler() {} + + /** + * Reimplement this function if you want to be updated on + * incoming presence notifications. + * @param presence The complete stanza. + * @since 1.0 + */ + virtual void handlePresence( const Presence& presence ) = 0; + + }; + +} + +#endif // PRESENCEHANDLER_H__ diff --git a/libs/libgloox/privacyitem.cpp b/libs/libgloox/privacyitem.cpp new file mode 100644 index 0000000..e2f6f02 --- /dev/null +++ b/libs/libgloox/privacyitem.cpp @@ -0,0 +1,42 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "privacyitem.h" + +namespace gloox +{ + + PrivacyItem::PrivacyItem( const ItemType type, const ItemAction action, + const int packetType, const std::string& value ) + : m_type( type ), m_action( action ), m_packetType( packetType ), + m_value( value ) + { + } + + PrivacyItem::~PrivacyItem() + { + } + + bool PrivacyItem::operator==( const PrivacyItem& item ) const + { + if( m_type == item.type() + && m_action == item.action() + && m_packetType == item.packetType() + && m_value == item.value() ) + return true; + else + return false; + } + +} diff --git a/libs/libgloox/privacyitem.h b/libs/libgloox/privacyitem.h new file mode 100644 index 0000000..727b982 --- /dev/null +++ b/libs/libgloox/privacyitem.h @@ -0,0 +1,126 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef PRIVACYITEM_H__ +#define PRIVACYITEM_H__ + +#include "macros.h" +#include "gloox.h" + +#include + +namespace gloox +{ + + /** + * @brief This is an abstraction of a single item of a privacy list, describing an allowed or + * forbidden action. + * + * @author Jakob Schroeter + * @since 0.3 + */ + class GLOOX_API PrivacyItem + { + public: + + /** + * Three possible types of an item. Only one is allowed at a time. + */ + enum ItemType + { + TypeUndefined, /**< None of the types below is explicitely selected, + * "fall-through" case. */ + TypeJid, /**< The item affects the JID which is given in the value attribute. */ + TypeGroup, /**< The item affects the group which is given in the value attribute and + * which must exist at least once in the users roster. */ + TypeSubscription /**< The item affects the subscription type which is given in the value + * attribute. */ + }; + + /** + * Two possible actions. Only one is allowed at a time. + */ + enum ItemAction + { + ActionAllow, /**< The item explicitely allows the described packets. */ + ActionDeny /**< The item forbids the described packets. */ + }; + + /** + * The packet type a privacy item affects (blocks). Combinations are allowed. + */ + enum ItemPacketType + { + PacketMessage = 1, /**< The item blocks message stanzas. */ + PacketPresenceIn = 2, /**< The item blocks incoming presence stanzas. */ + PacketPresenceOut = 4, /**< The item blocks outgoing presence stanzas. */ + PacketIq = 8, /**< The item blocks IQ stanzas. */ + PacketAll = 15 /**< The item blocks all of these stanza types. */ + }; + + /** + * Constructs a new privacy item. + * @param type Action is based on matching JID, Group or Subscription. + * @param action The action to carry out. (Deny or allow.) + * @param packetType Affected packet types. Bit-wise OR'ed ItemPacketType. + * @param value The value to check for and match. + */ + PrivacyItem( const ItemType type = TypeUndefined, const ItemAction action = ActionAllow, + const int packetType = 0, const std::string& value = EmptyString ); + + /** + * Virtual destructor. + */ + virtual ~PrivacyItem(); + + /** + * Returns the item type. + * @return The type of the item. + */ + ItemType type() const { return m_type; } + + /** + * Returns the item's action. + * @return The action of the item. + */ + ItemAction action() const { return m_action; } + + /** + * Returns the packet type the item affects. + * @return An OR'ed list of affected packet types. + */ + int packetType() const { return m_packetType; } + + /** + * Returns the value of the item's 'value' attribute. + * @return value The 'value' attribute's value. + */ + const std::string value() const { return m_value; } + + /** + * Compares the current PrivacyItem with another one. + * @param item The item which shall be compared. + * @return @b True if both items are equal, @b false otherwise. + */ + bool operator==( const PrivacyItem& item ) const; + + private: + ItemType m_type; + ItemAction m_action; + int m_packetType; + std::string m_value; + }; + +} + +#endif // PRIVACYITEM_H__ diff --git a/libs/libgloox/privacylisthandler.h b/libs/libgloox/privacylisthandler.h new file mode 100644 index 0000000..7a5c167 --- /dev/null +++ b/libs/libgloox/privacylisthandler.h @@ -0,0 +1,99 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef PRIVACYLISTHANDLER_H__ +#define PRIVACYLISTHANDLER_H__ + +#include "privacyitem.h" +#include "gloox.h" + +#include +#include + +namespace gloox +{ + + /** + * The possible results of an operation on a privacy list. + */ + enum PrivacyListResult + { + ResultStoreSuccess, /**< Storing was successful. */ + ResultActivateSuccess, /**< Activation was successful. */ + ResultDefaultSuccess, /**< Setting the default list was successful. */ + ResultRemoveSuccess, /**< Removing a list was successful. */ + ResultRequestNamesSuccess, /**< Requesting the list names was successful. */ + ResultRequestListSuccess, /**< The list was requested successfully. */ + ResultConflict, /**< A conflict occurred when activating a list or setting the default + * list. */ + ResultItemNotFound, /**< The requested list does not exist. */ + ResultBadRequest, /**< Bad request. */ + ResultUnknownError /**< An unknown error occured. */ + }; + + /** + * @brief A virtual interface that allows to retrieve Privacy Lists. + * + * @author Jakob Schroeter + * @since 0.3 + */ + class GLOOX_API PrivacyListHandler + { + public: + + /** + * A list of PrivacyItems. + */ + typedef std::list PrivacyList; + + /** + * Virtual Destructor. + */ + virtual ~PrivacyListHandler() {} + + /** + * Reimplement this function to retrieve the list of privacy list names after requesting it using + * PrivacyManager::requestListNames(). + * @param active The name of the active list. + * @param def The name of the default list. + * @param lists All the lists. + */ + virtual void handlePrivacyListNames( const std::string& active, const std::string& def, + const StringList& lists ) = 0; + + /** + * Reimplement this function to retrieve the content of a privacy list after requesting it using + * PrivacyManager::requestList(). + * @param name The name of the list. + * @param items A list of PrivacyItem's. + */ + virtual void handlePrivacyList( const std::string& name, const PrivacyList& items ) = 0; + + /** + * Reimplement this function to be notified about new or changed lists. + * @param name The name of the new or changed list. + */ + virtual void handlePrivacyListChanged( const std::string& name ) = 0; + + /** + * Reimplement this function to receive results of stores etc. + * @param id The ID of the request, as returned by the initiating function. + * @param plResult The result of an operation. + */ + virtual void handlePrivacyListResult( const std::string& id, PrivacyListResult plResult ) = 0; + + }; + +} + +#endif // PRIVACYLISTHANDLER_H__ diff --git a/libs/libgloox/privacymanager.cpp b/libs/libgloox/privacymanager.cpp new file mode 100644 index 0000000..f7e8fc8 --- /dev/null +++ b/libs/libgloox/privacymanager.cpp @@ -0,0 +1,316 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "privacymanager.h" +#include "clientbase.h" +#include "error.h" + +namespace gloox +{ + + // ---- PrivacyManager::Query ---- + PrivacyManager::Query::Query( const Tag* tag ) + : StanzaExtension( ExtPrivacy ) + { + if( !tag ) + return; + + const TagList& l = tag->children(); + TagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + { + const std::string& name = (*it)->findAttribute( "name" ); + if( (*it)->name() == "default" ) + m_default = name; + else if( (*it)->name() == "active" ) + m_active = name; + else if( (*it)->name() == "list" ) + { + m_names.push_back( name ); + + const TagList& l = (*it)->children(); + TagList::const_iterator it_l = l.begin(); + for( ; it_l != l.end(); ++it_l ) + { + PrivacyItem::ItemType type; + PrivacyItem::ItemAction action; + int packetType = 0; + + const std::string& t = (*it_l)->findAttribute( TYPE ); + if( t == "jid" ) + type = PrivacyItem::TypeJid; + else if( t == "group" ) + type = PrivacyItem::TypeGroup; + else if( t == "subscription" ) + type = PrivacyItem::TypeSubscription; + else + type = PrivacyItem::TypeUndefined; + + const std::string& a = (*it_l)->findAttribute( "action" ); + if( a == "allow" ) + action = PrivacyItem::ActionAllow; + else if( a == "deny" ) + action = PrivacyItem::ActionDeny; + else + action = PrivacyItem::ActionAllow; + + const std::string& value = (*it_l)->findAttribute( "value" ); + + const TagList& c = (*it_l)->children(); + TagList::const_iterator it_c = c.begin(); + for( ; it_c != c.end(); ++it_c ) + { + if( (*it_c)->name() == "iq" ) + packetType |= PrivacyItem::PacketIq; + else if( (*it_c)->name() == "presence-out" ) + packetType |= PrivacyItem::PacketPresenceOut; + else if( (*it_c)->name() == "presence-in" ) + packetType |= PrivacyItem::PacketPresenceIn; + else if( (*it_c)->name() == "message" ) + packetType |= PrivacyItem::PacketMessage; + } + + PrivacyItem item( type, action, packetType, value ); + m_items.push_back( item ); + } + } + } + } + + PrivacyManager::Query::Query( IdType context, const std::string& name, + const PrivacyListHandler::PrivacyList& list ) + : StanzaExtension( ExtPrivacy ), m_context( context ), m_items( list ) + { + m_names.push_back( name ); + } + + PrivacyManager::Query::~Query() + { + } + + const std::string& PrivacyManager::Query::filterString() const + { + static const std::string filter = "/iq/query[@xmlns='" + XMLNS_PRIVACY + "']"; + return filter; + } + + Tag* PrivacyManager::Query::tag() const + { + Tag* t = new Tag( "query" ); + t->setXmlns( XMLNS_PRIVACY ); + + std::string child; + switch( m_context ) + { + case PLRequestList: + case PLRemove: + case PLStore: + child = "list"; + break; + case PLDefault: + case PLUnsetDefault: + child = "default"; + break; + case PLActivate: + case PLUnsetActivate: + child = "active"; + break; + default: + case PLRequestNames: + return t; + break; + } + Tag* c = new Tag( t, child ); + + if( !m_names.empty() ) + c->addAttribute( "name", (*m_names.begin()) ); + + int count = 0; + PrivacyListHandler::PrivacyList::const_iterator it = m_items.begin(); + for( ; it != m_items.end(); ++it ) + { + Tag* i = new Tag( c, "item" ); + + switch( (*it).type() ) + { + case PrivacyItem::TypeJid: + i->addAttribute( TYPE, "jid" ); + break; + case PrivacyItem::TypeGroup: + i->addAttribute( TYPE, "group" ); + break; + case PrivacyItem::TypeSubscription: + i->addAttribute( TYPE, "subscription" ); + break; + default: + break; + } + + switch( (*it).action() ) + { + case PrivacyItem::ActionAllow: + i->addAttribute( "action", "allow" ); + break; + case PrivacyItem::ActionDeny: + i->addAttribute( "action", "deny" ); + break; + } + + int pType = (*it).packetType(); + if( pType != 15 ) + { + if( pType & PrivacyItem::PacketMessage ) + new Tag( i, "message" ); + if( pType & PrivacyItem::PacketPresenceIn ) + new Tag( i, "presence-in" ); + if( pType & PrivacyItem::PacketPresenceOut ) + new Tag( i, "presence-out" ); + if( pType & PrivacyItem::PacketIq ) + new Tag( i, "iq" ); + } + + i->addAttribute( "value", (*it).value() ); + i->addAttribute( "order", ++count ); + } + + return t; + } + // ---- ~PrivacyManager::Query ---- + + // ---- PrivacyManager ---- + PrivacyManager::PrivacyManager( ClientBase* parent ) + : m_parent( parent ), m_privacyListHandler( 0 ) + { + if( m_parent ) + { + m_parent->registerStanzaExtension( new Query() ); + m_parent->registerIqHandler( this, ExtPrivacy ); + } + } + + PrivacyManager::~PrivacyManager() + { + if( m_parent ) + { + m_parent->removeIqHandler( this, ExtPrivacy ); + m_parent->removeIDHandler( this ); + } + } + + std::string PrivacyManager::operation( IdType context, const std::string& name ) + { + const std::string& id = m_parent->getID(); + IQ::IqType iqType = IQ::Set; + if( context == PLRequestNames || context == PLRequestList ) + iqType = IQ::Get; + IQ iq( iqType, JID(), id ); + iq.addExtension( new Query( context, name ) ); + m_parent->send( iq, this, context ); + return id; + } + + std::string PrivacyManager::store( const std::string& name, const PrivacyListHandler::PrivacyList& list ) + { + if( list.empty() ) + return EmptyString; + + const std::string& id = m_parent->getID(); + + IQ iq( IQ::Set, JID(), id ); + iq.addExtension( new Query( PLStore, name, list ) ); + m_parent->send( iq, this, PLStore ); + return id; + } + + bool PrivacyManager::handleIq( const IQ& iq ) + { + const Query* q = iq.findExtension( ExtPrivacy ); + if( iq.subtype() != IQ::Set || !m_privacyListHandler + || !q || q->name().empty() ) + return false; + + m_privacyListHandler->handlePrivacyListChanged( q->name() ); + IQ re( IQ::Result, JID(), iq.id() ); + m_parent->send( re ); + return true; + } + + void PrivacyManager::handleIqID( const IQ& iq, int context ) + { + if( !m_privacyListHandler ) + return; + + switch( iq.subtype() ) + { + case IQ::Result: + switch( context ) + { + case PLStore: + m_privacyListHandler->handlePrivacyListResult( iq.id(), ResultStoreSuccess ); + break; + case PLActivate: + m_privacyListHandler->handlePrivacyListResult( iq.id(), ResultActivateSuccess ); + break; + case PLDefault: + m_privacyListHandler->handlePrivacyListResult( iq.id(), ResultDefaultSuccess ); + break; + case PLRemove: + m_privacyListHandler->handlePrivacyListResult( iq.id(), ResultRemoveSuccess ); + break; + case PLRequestNames: + { + const Query* q = iq.findExtension( ExtPrivacy ); + if( !q ) + return; + m_privacyListHandler->handlePrivacyListNames( q->def(), q->active(), + q->names() ); + break; + } + case PLRequestList: + { + const Query* q = iq.findExtension( ExtPrivacy ); + if( !q ) + return; + m_privacyListHandler->handlePrivacyList( q->name(), q->items() ); + break; + } + } + break; + + case IQ::Error: + { + switch( iq.error()->error() ) + { + case StanzaErrorConflict: + m_privacyListHandler->handlePrivacyListResult( iq.id(), ResultConflict ); + break; + case StanzaErrorItemNotFound: + m_privacyListHandler->handlePrivacyListResult( iq.id(), ResultItemNotFound ); + break; + case StanzaErrorBadRequest: + m_privacyListHandler->handlePrivacyListResult( iq.id(), ResultBadRequest ); + break; + default: + m_privacyListHandler->handlePrivacyListResult( iq.id(), ResultUnknownError ); + break; + } + break; + } + + default: + break; + } + } + +} diff --git a/libs/libgloox/privacymanager.h b/libs/libgloox/privacymanager.h new file mode 100644 index 0000000..84d8f69 --- /dev/null +++ b/libs/libgloox/privacymanager.h @@ -0,0 +1,231 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef PRIVACYMANAGER_H__ +#define PRIVACYMANAGER_H__ + +#include "iqhandler.h" +#include "privacylisthandler.h" +#include "stanzaextension.h" + +#include + +namespace gloox +{ + + class ClientBase; + + /** + * @brief This class implements a manager for privacy lists as defined in section 10 of RFC 3921. + * + * @author Jakob Schroeter + * @since 0.3 + */ + class GLOOX_API PrivacyManager : public IqHandler + { + public: + /** + * Constructs a new PrivacyManager. + * @param parent The ClientBase to use for communication. + */ + PrivacyManager( ClientBase* parent ); + + /** + * Virtual destructor. + */ + virtual ~PrivacyManager(); + + /** + * Stores the given list on the server. If a list with the given name exists, the existing + * list is overwritten. + * @param name The list's name. + * @param list A non empty list of privacy items which describe the list. + */ + std::string store( const std::string& name, const PrivacyListHandler::PrivacyList& list ); + + /** + * Triggers the request of the privacy lists currently stored on the server. + */ + std::string requestListNames() + { return operation( PLRequestNames, EmptyString ); } + + /** + * Triggers the retrieval of the named privacy lists. + * @param name The name of the list to retrieve. + */ + std::string requestList( const std::string& name ) + { return operation( PLRequestList, name ); } + + /** + * Removes a list by its name. + * @param name The name of the list to remove. + */ + std::string removeList( const std::string& name ) + { return operation( PLRemove, name ); } + + /** + * Sets the named list as the default list, i.e. active by default after login. + * @param name The name of the list to set as default. + */ + std::string setDefault( const std::string& name ) + { return operation( PLDefault, name ); } + + /** + * This function declines the use of any default list. + */ + std::string unsetDefault() + { return operation( PLUnsetDefault, EmptyString ); } + + /** + * Sets the named list as active, i.e. active for this session + * @param name The name of the list to set active. + */ + std::string setActive( const std::string& name ) + { return operation( PLActivate, name ); } + + /** + * This function declines the use of any active list. + */ + std::string unsetActive() + { return operation( PLUnsetActivate, EmptyString ); } + + /** + * Use this function to register an object as PrivacyListHandler. + * Only one PrivacyListHandler at a time is possible. + * @param plh The object to register as handler for privacy list related events. + */ + void registerPrivacyListHandler( PrivacyListHandler* plh ) + { m_privacyListHandler = plh; } + + /** + * Use this function to clear the registered PrivacyListHandler. + */ + void removePrivacyListHandler() + { m_privacyListHandler = 0; } + + // reimplemented from IqHandler. + virtual bool handleIq( const IQ& iq ); + + // reimplemented from IqHandler. + virtual void handleIqID( const IQ& iq, int context ); + + private: + enum IdType + { + PLRequestNames, + PLRequestList, + PLActivate, + PLDefault, + PLUnsetActivate, + PLUnsetDefault, + PLRemove, + PLStore + }; + + class Query : public StanzaExtension + { + public: + /** + * Creates a new query for storing or requesting a + * privacy list. + * @param context The context of the list. + * @param name The list's name. + * @param list The list's (optional) content. + */ + Query( IdType context, const std::string& name, + const PrivacyListHandler::PrivacyList& list = PrivacyListHandler::PrivacyList() ); + + /** + * Creates a new query from the given Tag. + * @param tag The Tag to parse. + */ + Query( const Tag* tag = 0 ); + + /** + * Virtual destructor. + */ + virtual ~Query(); + + /** + * Returns the name of the active list, if given. + * @return The active list's name. + */ + const std::string& active() const { return m_active; } + + /** + * Returns the name of the default list, if given. + * @return The default list's name. + */ + const std::string& def() const { return m_default; } + + /** + * Returns a list of privacy items, if given. + * @return A list of PrivacyItems. + */ + const PrivacyListHandler::PrivacyList& items() const + { return m_items; } + + /** + * Returns a list of list names. + * @return A list of list names. + */ + const StringList& names() const { return m_names; } + + /** + * A convenience function that returns the first name of the list that + * names() would return, or an empty string. + * @return A list name. + */ + const std::string& name() const + { + if( m_names.empty()) + return EmptyString; + else + return (*m_names.begin()); + } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new Query( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new Query( *this ); + } + + private: + IdType m_context; + StringList m_names; + std::string m_default; + std::string m_active; + PrivacyListHandler::PrivacyList m_items; + }; + + std::string operation( IdType context, const std::string& name ); + + ClientBase* m_parent; + PrivacyListHandler* m_privacyListHandler; + }; + +} + +#endif // PRIVACYMANAGER_H__ diff --git a/libs/libgloox/privatexml.cpp b/libs/libgloox/privatexml.cpp new file mode 100644 index 0000000..ada2a2a --- /dev/null +++ b/libs/libgloox/privatexml.cpp @@ -0,0 +1,129 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "privatexml.h" +#include "clientbase.h" +#include "stanza.h" + +namespace gloox +{ + + // ---- PrivateXML::Query ---- + PrivateXML::Query::Query( const Tag* tag ) + : StanzaExtension( ExtPrivateXML ), m_privateXML( 0 ) + { + if( !tag ) + return; + + if( tag->name() == "query" && tag->xmlns() == XMLNS_PRIVATE_XML ) + { + if( tag->children().size() ) + m_privateXML = tag->children().front()->clone(); + } + else + m_privateXML = tag; + } + + const std::string& PrivateXML::Query::filterString() const + { + static const std::string filter = "/iq/query[@xmlns='" + XMLNS_PRIVATE_XML + "']"; + return filter; + } + + Tag* PrivateXML::Query::tag() const + { + Tag* t = new Tag( "query" ); + t->setXmlns( XMLNS_PRIVATE_XML ); + if( m_privateXML ) + t->addChild( m_privateXML->clone() ); + return t; + } + // ---- ~PrivateXML::Query ---- + + // ---- PrivateXML ---- + PrivateXML::PrivateXML( ClientBase* parent ) + : m_parent( parent ) + { + if( !m_parent ) + return; + + m_parent->registerIqHandler( this, ExtPrivateXML ); + m_parent->registerStanzaExtension( new Query() ); + } + + PrivateXML::~PrivateXML() + { + if( !m_parent ) + return; + + m_parent->removeIqHandler( this, ExtPrivateXML ); + m_parent->removeIDHandler( this ); + m_parent->removeStanzaExtension( ExtPrivateXML ); + } + + std::string PrivateXML::requestXML( const std::string& tag, const std::string& xmlns, + PrivateXMLHandler* pxh ) + { + const std::string& id = m_parent->getID(); + + IQ iq( IQ::Get, JID(), id ); + iq.addExtension( new Query( tag, xmlns ) ); + + m_track[id] = pxh; + m_parent->send( iq, this, RequestXml ); + + return id; + } + + std::string PrivateXML::storeXML( const Tag* tag, PrivateXMLHandler* pxh ) + { + const std::string& id = m_parent->getID(); + + IQ iq( IQ::Set, JID(), id ); + iq.addExtension( new Query( tag ) ); + + m_track[id] = pxh; + m_parent->send( iq, this, StoreXml ); + + return id; + } + + void PrivateXML::handleIqID( const IQ& iq, int context ) + { + TrackMap::iterator t = m_track.find( iq.id() ); + if( t == m_track.end() ) + return; + + if( iq.subtype() == IQ::Result ) + { + if( context == RequestXml ) + { + const Query* q = iq.findExtension( ExtPrivateXML ); + if( q ) + (*t).second->handlePrivateXML( q->privateXML() ); + } + else if( context == StoreXml ) + (*t).second->handlePrivateXMLResult( iq.id(), PrivateXMLHandler::PxmlStoreOk ); + } + else if( iq.subtype() == IQ::Error ) + { + if( context == RequestXml ) + (*t).second->handlePrivateXMLResult( iq.id(), PrivateXMLHandler::PxmlRequestError ); + else if( context == StoreXml ) + (*t).second->handlePrivateXMLResult( iq.id(), PrivateXMLHandler::PxmlStoreError ); + } + + m_track.erase( t ); + } + +} diff --git a/libs/libgloox/privatexml.h b/libs/libgloox/privatexml.h new file mode 100644 index 0000000..b0e00f3 --- /dev/null +++ b/libs/libgloox/privatexml.h @@ -0,0 +1,160 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef PRIVATEXML_H__ +#define PRIVATEXML_H__ + +#include "iqhandler.h" +#include "privatexmlhandler.h" + +#include +#include +#include + +namespace gloox +{ + + class ClientBase; + class Tag; + class Stanza; + + /** + * @brief This class implements XEP-0049 (Private XML Storage). + * + * @author Jakob Schroeter + */ + class GLOOX_API PrivateXML : public IqHandler + { + public: + /** + * Constructor. + * Creates a new PrivateXML client that registers as IqHandler + * with @c ClientBase. + * @param parent The ClientBase used for XMPP communication + */ + PrivateXML( ClientBase* parent ); + + /** + * Virtual destructor. + */ + virtual ~PrivateXML(); + + /** + * Use this function to request the private XML stored in the given namespace. + * @param tag Child element of the query element used to identify the requested XML fragment. + * @param xmlns The namespace which qualifies the tag. + * @param pxh The handler to receive the result. + * @return The ID of the sent query. + */ + std::string requestXML( const std::string& tag, const std::string& xmlns, PrivateXMLHandler* pxh ); + + /** + * Use this function to store private XML stored in the given namespace. + * @param tag The XML to store. This is the complete tag including the unique namespace. + * It is deleted automatically after sending it. + * @param pxh The handler to receive the result. + * @return The ID of the sent query. + */ + std::string storeXML( const Tag* tag, PrivateXMLHandler* pxh ); + + // reimplemented from IqHandler. + virtual bool handleIq( const IQ& iq ) { (void)iq; return false; } + + // reimplemented from IqHandler. + virtual void handleIqID( const IQ& iq, int context ); + + protected: + ClientBase* m_parent; + + private: +#ifdef PRIVATEXML_TEST + public: +#endif + /** + * @brief An implementation of the Private XML Storage protocol as StanzaExtension. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class Query : public StanzaExtension + { + public: + /** + * Constructs a new Query object suitable for use with Private XML Storage. + * @param tag The private XML's element name. + * @param xmlns The private XML's namespace. + */ + Query( const std::string& tag, const std::string& xmlns ) + : StanzaExtension( ExtPrivateXML ) + { + m_privateXML = new Tag( tag, XMLNS, xmlns ); + } + + /** + * Constructs a new Query object suitable for storing an XML fragment in + * Private XML Storage. + * @param tag The private XML element to store. The Query object will own the Tag. + */ + Query( const Tag* tag = 0 ); + + /** + * Destructor. + */ + ~Query() { delete m_privateXML; } + + /** + * Returns the private XML fragment. The Tag is owned by the Query object. + * @return The stored private XML fragment. + */ + const Tag* privateXML() const { return m_privateXML; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new Query( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + Query* q = new Query(); + q->m_privateXML = m_privateXML ? m_privateXML->clone() : 0; + return q; + } + + private: + const Tag* m_privateXML; + + }; + + enum IdType + { + RequestXml, + StoreXml + }; + + typedef std::map TrackMap; + + TrackMap m_track; + }; + +} + +#endif // PRIVATEXML_H__ diff --git a/libs/libgloox/privatexmlhandler.h b/libs/libgloox/privatexmlhandler.h new file mode 100644 index 0000000..1a315d3 --- /dev/null +++ b/libs/libgloox/privatexmlhandler.h @@ -0,0 +1,73 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef PRIVATEXMLHANDLER_H__ +#define PRIVATEXMLHANDLER_H__ + +#include "macros.h" + +#include + +namespace gloox +{ + + class Tag; + + /** + * @brief A virtual interface which can be reimplemented to store and receive private XML data. + * + * Derived classes can be registered as PrivateXMLHandlers with the PrivateXML object. + * Upon an incoming PrivateXML packet @ref handlePrivateXML() will be called. + * + * @author Jakob Schroeter + */ + class GLOOX_API PrivateXMLHandler + { + public: + /** + * Describes the possible results of a 'store' or 'request' operation. + */ + enum PrivateXMLResult + { + PxmlStoreOk, /**< Storing was successful. */ + PxmlStoreError, /**< An error occurred while storing data in Private XML. */ + PxmlRequestError /**< An error occurred while requesting Private XML. */ + }; + + /** + * Virtual Destructor. + */ + virtual ~PrivateXMLHandler() {} + + /** + * Reimplement this function to receive the private XML that was requested earlier using + * @c PrivateXML::requestXML(). + * @param xml The private xml, i.e. the first child of the <query> tag. + * May be 0. You should not delete the object. + */ + virtual void handlePrivateXML( const Tag* xml ) = 0; + + /** + * This function is called to notify about the result of a 'store' or 'request' operation + * (successful requests are announced by means of handlePrivateXML()). + * @param uid The ID of the query. + * @param pxResult The result of the operation. + * @since 0.7 + */ + virtual void handlePrivateXMLResult( const std::string& uid, PrivateXMLResult pxResult ) = 0; + }; + +} + +#endif // PRIVATEXMLHANDLER_H__ diff --git a/libs/libgloox/pubsub.h b/libs/libgloox/pubsub.h new file mode 100644 index 0000000..dd06a8d --- /dev/null +++ b/libs/libgloox/pubsub.h @@ -0,0 +1,249 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#ifndef PUBSUB_H__ +#define PUBSUB_H__ + +#include +#include + +#include "gloox.h" +#include "jid.h" + +namespace gloox +{ + /** + * @brief Namespace holding all the Pubsub-related structures and definitions. + */ + namespace PubSub + { + + class Item; + + /** + * Describes the different node types. + */ + enum NodeType + { + NodeLeaf, /**< A node that contains published items only. It is NOT + * a container for other nodes. */ + NodeCollection, /**< A node that contains nodes and/or other collections but + * no published items. Collections make it possible to represent + * hierarchial node structures. */ + NodeInvalid /**< Invalid node type */ + }; + + /** + * Describes the different node affiliation types. + */ + enum AffiliationType + { + AffiliationNone, /**< No particular affiliation type. */ + AffiliationPublisher, /**< Entity is allowed to publish items. */ + AffiliationOwner, /**< Manager for the node. */ + AffiliationOutcast, /**< Entity is disallowed from subscribing or publishing. */ + AffiliationInvalid /**< Invalid Affiliation type. */ + }; + + /** + * Describes the different subscription types. + */ + enum SubscriptionType + { + SubscriptionNone, /**< The node MUST NOT send event notifications or payloads to the + * Entity. */ + SubscriptionSubscribed, /**< An entity is subscribed to a node. The node MUST send all event + * notifications (and, if configured, payloads) to the entity while it + * is in this state. */ + SubscriptionPending, /**< An entity has requested to subscribe to a node and the request + * has not yet been approved by a node owner. The node MUST NOT send + * event notifications or payloads to the entity while it is in this + * state. */ + SubscriptionUnconfigured, /**< An entity has subscribed but its subscription options have not yet + * been configured. The node MAY send event notifications or payloads + * to the entity while it is in this state. The service MAY timeout + * unconfigured subscriptions. */ + SubscriptionInvalid /**< Invalid subscription type. */ + }; + + /** + * Event types. + */ + enum EventType + { + EventCollection, /**< A Collection node has been created. */ + EventConfigure, /**< A node's configuration has changed. */ + EventDelete, /**< A node has been deleted. */ + EventItems, /**< An item has been created or modified. */ + EventItemsRetract, /**< An item has been deleted. */ + EventPurge, /**< A Leaf node has been purged. */ + EventSubscription, /**< A user's subscription has been processed. */ + EventUnknown /**< Unknown event. */ + }; + + /** + * Describes the different subscription types. + */ + enum SubscriptionObject + { + SubscriptionNodes, /**< Receive notification of new nodes only. */ + SubscriptionItems /**< Receive notification of new items only. */ + }; + + /** + * Describes the access types. + */ + enum AccessModel + { + AccessOpen, /**< Any entity may subscribe to the node (i.e., without the necessity + * for subscription approval) and any entity may retrieve items from the + * node (i.e., without being subscribed); this SHOULD be the default + * access model for generic pubsub services. */ + AccessPresence, /**< Any entity with a subscription of type "from" or "both" may subscribe + * to the node and retrieve items from the node; this access model applies + * mainly to instant messaging systems (see RFC 3921). */ + AccessRoster, /**< Any entity in the specified roster group(s) may subscribe to the node + * and retrieve items from the node; this access model applies mainly to + * instant messaging systems (see RFC 3921). */ + AccessAuthorize, /**< The node owner must approve all subscription requests, and only + * subscribers may retrieve items from the node. */ + AccessWhitelist, /**< An entity may be subscribed only through being added to a whitelist + * by the node owner (unsolicited subscription requests are rejected), and + * only subscribers may retrieve items from the node. In effect, the + * default affiliation is outcast. The node owner MUST automatically be + * on the whitelist. In order to add entities to the whitelist, the + * node owner SHOULD use the protocol specified in the Manage Affiliated + * Entities section of this document. */ + AccessDefault /**< Unspecified (default) Access Model (does not represent a real access + * type by itself). */ + }; + + /** + * Describes the different PubSub features (XEP-0060 Sect. 10). + */ + enum PubSubFeature + { + FeatureCollections = 1, /**< Collection nodes are supported. RECOMMENDED */ + FeatureConfigNode = 1<<1, /**< Configuration of node options is supported. RECOMMENDED */ + FeatureCreateAndConfig = 1<<2, /**< Simultaneous creation and configuration of nodes is + * supported. RECOMMENDED */ + FeatureCreateNodes = 1<<3, /**< Creation of nodes is supported. RECOMMENDED */ + FeatureDeleteAny = 1<<4, /**< Any publisher may delete an item (not only the originating + * publisher). OPTIONAL */ + FeatureDeleteNodes = 1<<5, /**< Deletion of nodes is supported. RECOMMENDED */ + FeatureGetPending = 1<<6, /**< Retrieval of pending subscription approvals is supported. + * OPTIONAL */ + FeatureInstantNodes = 1<<7, /**< Creation of instant nodes is supported. RECOMMENDED */ + FeatureItemIDs = 1<<8, /**< Publishers may specify item identifiers. RECOMMENDED */ + FeatureLeasedSubscription = 1<<9, /**< Time-based subscriptions are supported. OPTIONAL */ + FeatureManageSubscriptions = 1<<10, /**< Node owners may manage subscriptions. OPTIONAL */ + FeatureMetaData = 1<<11, /**< Node meta-data is supported. RECOMMENDED */ + FeatureModifyAffiliations = 1<<12, /**< Node owners may modify affiliations. OPTIONAL */ + FeatureMultiCollection = 1<<13, /**< A single leaf node may be associated with multiple + * collections. OPTIONAL */ + FeatureMultiSubscribe = 1<<14, /**< A single entity may subscribe to a node multiple times. + * OPTIONAL */ + FeaturePutcastAffiliation = 1<<15, /**< The outcast affiliation is supported. RECOMMENDED */ + FeaturePersistentItems = 1<<16, /**< Persistent items are supported. RECOMMENDED */ + FeaturePresenceNotifications = 1<<17, /**< Presence-based delivery of event notifications is supported. + * OPTIONAL */ + FeaturePublish = 1<<18, /**< Publishing items is supported (note: not valid for collection + * nodes). REQUIRED */ + FeaturePublisherAffiliation = 1<<19, /**< The publisher affiliation is supported. OPTIONAL */ + FeaturePurgeNodes = 1<<20, /**< Purging of nodes is supported. OPTIONAL */ + FeatureRetractItems = 1<<21, /**< Item retraction is supported. OPTIONAL */ + FeatureRetrieveAffiliations = 1<<22, /**< Retrieval of current affiliations is supported. + * RECOMMENDED */ + FeatureRetrieveDefault = 1<<23, /**< Retrieval of default node configuration is supported. + * RECOMMENDED */ + FeatureRetrieveItems = 1<<24, /**< Item retrieval is supported. RECOMMENDED */ + FeatureRetrieveSubscriptions = 1<<25, /**< Retrieval of current subscriptions is supported. + * RECOMMENDED */ + FeatureSubscribe = 1<<26, /**< Subscribing and unsubscribing are supported. REQUIRED */ + FeatureSubscriptionOptions = 1<<27, /**< Configuration of subscription options is supported. + * OPTIONAL */ + FeatureSubscriptionNotifs = 1<<28, /**< Notification of subscription state changes is supported. */ + FeatureUnknown = 1<<29 /**< Unrecognized feature */ + }; + +// [Persistent - Notification] +/* Publisher MUST include an <item/> element, which MAY be empty or contain a payload; if item ID is not provided by publisher, it MUST be generated by pubsub service */ + +// [Persistent - Payload] +/* Publisher MUST include an <item/> element that contains the payload; if item ID is not provided by publisher, it MUST be generated by pubsub service */ + +// [Transient - Notification] +/* Publisher MUST NOT include an <item/> element (therefore item ID is neither provided nor generated) but the notification will include an empty <items/> element */ + +// [Transient - Payload] +/* Publisher MUST include an <item/> element that contains the payload, but the item ID is OPTIONAL */ + + /** + * Describes a subscribed entity. + */ + struct Subscriber + { + Subscriber( const JID& _jid, + SubscriptionType _type, + const std::string& _subid = EmptyString) + : jid( _jid ), type( _type ), subid( _subid ) {} + JID jid; + SubscriptionType type; + std::string subid; + }; + + /** + * Describes an Affiliate. + */ + struct Affiliate + { + Affiliate( const JID& _jid, AffiliationType _type ) + : jid( _jid ), type( _type ) {} + JID jid; + AffiliationType type; + }; + + typedef std::list SubscriberList; + typedef std::list AffiliateList; + + /** + * Struct used to track info between requests. + * + */ + struct TrackedInfo + { + JID service; + std::string node; + std::string item; + std::string sid; + }; + + /** + * Struct used for subscription info. + */ + struct SubscriptionInfo + { + SubscriptionType type; + JID jid; + std::string subid; + }; + + typedef std::list SubscriptionList; + typedef std::map SubscriptionMap; + typedef std::map AffiliationMap; + typedef std::list ItemList; + + } + +} + +#endif // PUBSUB_H__ diff --git a/libs/libgloox/pubsubevent.cpp b/libs/libgloox/pubsubevent.cpp new file mode 100644 index 0000000..2ae86b8 --- /dev/null +++ b/libs/libgloox/pubsubevent.cpp @@ -0,0 +1,278 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#include "pubsubevent.h" +#include "tag.h" +#include "util.h" + +namespace gloox +{ + + namespace PubSub + { + + static const char* eventTypeValues[] = { + "collection", + "configuration", + "delete", + "items", + "items", + "purge", + "subscription" + }; + + Event::ItemOperation::ItemOperation( const ItemOperation& right ) + : retract( right.retract ), item( right.item ), + payload( right.payload ? right.payload->clone() : 0 ) + { + } + + Event::Event( const Tag* event ) + : StanzaExtension( ExtPubSubEvent ), m_type( PubSub::EventUnknown ), + m_subscriptionIDs( 0 ), m_config( 0 ), m_itemOperations( 0 ), m_subscription( false ) + { + if( !event || event->name() != "event" ) + return; + + const TagList& events = event->children(); + TagList::const_iterator it = events.begin(); + const Tag* tag = 0; + for( ; it != events.end(); ++it ) + { + tag = (*it); + PubSub::EventType type = (PubSub::EventType)util::lookup( tag->name(), eventTypeValues ); + + switch( type ) + { + case PubSub::EventCollection: + tag = tag->findChild( "node" ); + if( tag ) + { + m_node = tag->findAttribute( "id" ); + if( ( m_config = tag->findChild( "x" ) ) ) + m_config = m_config->clone(); + } + break; + + case PubSub::EventConfigure: + case PubSub::EventDelete: + case PubSub::EventPurge: + m_node = tag->findAttribute( "node" ); + if( type == PubSub::EventConfigure + && ( m_config = tag->findChild( "x" ) ) ) + m_config = m_config->clone(); + break; + + case PubSub::EventItems: + case PubSub::EventItemsRetract: + { + if( !m_itemOperations ) + m_itemOperations = new ItemOperationList(); + + m_node = tag->findAttribute( "node" ); + const TagList& items = tag->children(); + TagList::const_iterator itt = items.begin(); + for( ; itt != items.end(); ++itt ) + { + tag = (*itt); + bool retract = false; + if( tag->name() == "retract" ) + { + retract = true; + type = PubSub::EventItemsRetract; + } + ItemOperation* op = new ItemOperation( retract, + tag->findAttribute( "id" ), + tag->clone() ); + m_itemOperations->push_back( op ); + } + break; + } + + case EventSubscription: + { + m_node = tag->findAttribute( "node" ); + m_jid.setJID( tag->findAttribute( "jid" ) ); + m_subscription = tag->hasAttribute( "subscription", "subscribed" ); + break; + } + + case PubSub::EventUnknown: + if( type == PubSub::EventUnknown ) + { + if( tag->name() != "headers" || m_subscriptionIDs != 0 ) + { + m_valid = false; + return; + } + + m_subscriptionIDs = new StringList(); + + const TagList& headers = tag->children(); + TagList::const_iterator ith = headers.begin(); + for( ; ith != headers.end(); ++ith ) + { + const std::string& name = (*ith)->findAttribute( "name" ); + if( name == "pubsub#subid" ) + m_subscriptionIDs->push_back( (*ith)->cdata() ); + else if( name == "pubsub#collection" ) + m_collection = (*ith)->cdata(); + } + } + + default: + continue; + } + m_type = type; + } + + m_valid = true; + } + + Event::Event( const std::string& node, PubSub::EventType type ) + : StanzaExtension( ExtPubSubEvent ), m_type( type ), + m_node( node ), m_subscriptionIDs( 0 ), m_config( 0 ), + m_itemOperations( 0 ) + { + if( type != PubSub::EventUnknown ) + m_valid = true; + } + + Event::~Event() + { + delete m_subscriptionIDs; + delete m_config; + if( m_itemOperations ) + { + ItemOperationList::iterator it = m_itemOperations->begin(); + for( ; it != m_itemOperations->end(); ++it ) + { + delete (*it)->payload; + delete (*it); + } + delete m_itemOperations; + } + } + + void Event::addItem( ItemOperation* op ) + { + if( !m_itemOperations ) + m_itemOperations = new ItemOperationList(); + + m_itemOperations->push_back( op ); + } + + const std::string& Event::filterString() const + { + static const std::string filter = "/message/event[@xmlns='" + XMLNS_PUBSUB_EVENT + "']"; + return filter; + } + + Tag* Event::tag() const + { + if( !m_valid ) + return 0; + + Tag* event = new Tag( "event", XMLNS, XMLNS_PUBSUB_EVENT ); + Tag* child = new Tag( event, util::lookup( m_type, eventTypeValues ) ); + + Tag* item = 0; + + switch( m_type ) + { + case PubSub::EventCollection: + { + item = new Tag( child, "node", "id", m_node ); + item->addChildCopy( m_config ); + break; + } + + case PubSub::EventPurge: + case PubSub::EventDelete: + case PubSub::EventConfigure: + child->addAttribute( "node", m_node ); + if( m_type == PubSub::EventConfigure ) + child->addChildCopy( m_config ); + break; + + case PubSub::EventItems: + case PubSub::EventItemsRetract: + { + child->addAttribute( "node", m_node ); + if( m_itemOperations ) + { +// Tag* item; + ItemOperation* op; + ItemOperationList::const_iterator itt = m_itemOperations->begin(); + for( ; itt != m_itemOperations->end(); ++itt ) + { + op = (*itt); +// item = new Tag( child, op->retract ? "retract" : "item", "id", op->item ); + if( op->payload ) + child->addChildCopy( op->payload ); + } + } + break; + } + + case EventSubscription: + { + child->addAttribute( "node", m_node ); + child->addAttribute( "jid", m_jid.full() ); + child->addAttribute( "subscription", m_subscription ? "subscribed" : "none" ); + break; + } + + default: + delete event; + return 0; + } + + if( m_subscriptionIDs || !m_collection.empty() ) + { + Tag* headers = new Tag( event, "headers", XMLNS, "http://jabber.org/protocol/shim" ); + StringList::const_iterator it = m_subscriptionIDs->begin(); + for( ; it != m_subscriptionIDs->end(); ++it ) + { + (new Tag( headers, "header", "name", "pubsub#subid" ))->setCData( (*it) ); + } + + if( !m_collection.empty() ) + (new Tag( headers, "header", "name", "pubsub#collection" ) ) + ->setCData( m_collection ); + } + + return event; + } + + StanzaExtension* Event::clone() const + { + Event* e = new Event( m_node, m_type ); + e->m_subscriptionIDs = m_subscriptionIDs ? new StringList( *m_subscriptionIDs ) : 0; + e->m_config = m_config ? m_config->clone() : 0; + if( m_itemOperations ) + { + e->m_itemOperations = new ItemOperationList(); + ItemOperationList::const_iterator it = m_itemOperations->begin(); + for( ; it != m_itemOperations->end(); ++it ) + e->m_itemOperations->push_back( new ItemOperation( *(*it) ) ); + } + else + e->m_itemOperations = 0; + + e->m_collection = m_collection; + return e; + } + + } + +} diff --git a/libs/libgloox/pubsubevent.h b/libs/libgloox/pubsubevent.h new file mode 100644 index 0000000..10227bd --- /dev/null +++ b/libs/libgloox/pubsubevent.h @@ -0,0 +1,173 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#ifndef PUBSUBEVENT_H__ +#define PUBSUBEVENT_H__ + +#include "stanzaextension.h" +#include "pubsub.h" +#include "gloox.h" + +namespace gloox +{ + + class Tag; + + namespace PubSub + { + + /** + * @brief This is an implementation of a PubSub Notification as a StanzaExtension. + * + * @author Vincent Thomasset + * @since 1.0 + */ + class GLOOX_API Event : public StanzaExtension + { + public: + + /** + * Stores a retract or item notification. + */ + struct ItemOperation + { + /** + * Constructor. + * + * @param remove Whether this is a retract operation or not (ie item). + * @param itemid Item ID of this item. + * @param pld Payload for this object (in the case of a non transient + * item notification). + */ + ItemOperation( bool remove, const std::string& itemid, const Tag* pld = 0 ) + : retract( remove ), item( itemid ), payload( pld ) + {} + + /** + * Copy constructor. + * @param right The ItemOperation to copy from. + */ + ItemOperation( const ItemOperation& right ); + + bool retract; + std::string item; + const Tag* payload; + }; + + /** + * A list of ItemOperations. + */ + typedef std::list ItemOperationList; + + /** + * PubSub event notification Stanza Extension. + * @param event A tag to parse. + */ + Event( const Tag* event ); + + /** + * PubSub event notification Stanza Extension. + * @param node The node's ID for which the notification is sent. + * @param type The event's type. + */ + Event( const std::string& node, PubSub::EventType type ); + + /** + * Virtual destructor. + */ + virtual ~Event(); + + /** + * Returns the event's type. + * @return The event's type. + */ + PubSub::EventType type() const { return m_type; } + + /** + * Returns the list of subscription IDs for which this notification + * is valid. + * @return The list of subscription IDs. + */ + const StringList& subscriptions() const + { return m_subscriptionIDs ? *m_subscriptionIDs : m_emptyStringList; } + + /** + * Returns the list of ItemOperations for EventItems(Retract) notification. + * @return The list of ItemOperations. + */ + const ItemOperationList& items() const + { return m_itemOperations ? *m_itemOperations : m_emptyOperationList; } + + /** + * Add an item to the list of ItemOperations for EventItems(Retract) notification. + * After calling, the PubSub::Event object owns the ItemOperation and will free it. + * @param op An ItemOperation to add. + */ + void addItem( ItemOperation* op ); + + /** + * Returns the node's ID for which the notification is sent. + * @return The node's ID. + */ + const std::string& node() const { return m_node; } + + /** + * Returns the subscribe/unsubscribed JID. Only set for subscription notifications + * (type() == EventSubscription). + * @return The affected JID. + */ + const JID& jid() { return m_jid; } + + /** + * Returns the subscription state. Only set for subscription notifications + * (type() == EventSubscription). + * @return @b True if the subscription request was approved, @b false otherwise. + */ + bool subscription() { return m_subscription; } + + // reimplemented from StanzaExtension + const std::string& filterString() const; + + // reimplemented from StanzaExtension + StanzaExtension* newInstance( const Tag* tag ) const + { + return new Event( tag ); + } + + // reimplemented from StanzaExtension + Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const; + + private: + Event& operator=( const Event& ); + + PubSub::EventType m_type; + std::string m_node; + StringList* m_subscriptionIDs; + JID m_jid; + Tag* m_config; + ItemOperationList* m_itemOperations; + std::string m_collection; + bool m_subscription; + + const ItemOperationList m_emptyOperationList; + const StringList m_emptyStringList; + + }; + + } + +} + +#endif // PUBSUBEVENT_H__ diff --git a/libs/libgloox/pubsubitem.cpp b/libs/libgloox/pubsubitem.cpp new file mode 100644 index 0000000..a8e995a --- /dev/null +++ b/libs/libgloox/pubsubitem.cpp @@ -0,0 +1,63 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "pubsubitem.h" +#include "tag.h" + +namespace gloox +{ + + namespace PubSub + { + + Item::Item() + : m_payload( 0 ) + { + } + + Item::Item( const Tag* tag ) + : m_payload( 0 ) + { + if( !tag || tag->name() != "item" ) + return; + + m_id = tag->findAttribute( "id" ); + + if( tag->children().size() ) + m_payload = tag->children().front()->clone(); + } + + Item::Item( const Item& item ) + : m_payload( item.m_payload ? item.m_payload->clone() : 0 ) + { + m_id = item.m_id; + } + + Item::~Item() + { + delete m_payload; + } + + Tag* Item::tag() const + { + Tag* t = new Tag( "item" ); + t->addAttribute( "id", m_id ); + if( m_payload ) + t->addChild( m_payload->clone() ); + + return t; + } + + } + +} diff --git a/libs/libgloox/pubsubitem.h b/libs/libgloox/pubsubitem.h new file mode 100644 index 0000000..00406f3 --- /dev/null +++ b/libs/libgloox/pubsubitem.h @@ -0,0 +1,90 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef PUBSUBITEM_H__ +#define PUBSUBITEM_H__ + +#include "gloox.h" + +#include + +namespace gloox +{ + + class Tag; + + namespace PubSub + { + + /** + * @brief Abstracts a PubSub Item (XEP-0060). + * + * XEP Version: 1.12 + * + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API Item + { + public: + /** + * Constructs a new empty Item. + */ + Item(); + + /** + * Constructs a new Item from the given Tag. + * @param tag The Tag to parse. + */ + Item( const Tag* tag ); + + /** + * Copy constructor. + * @param item The Item to be copied. + */ + Item( const Item& item ); + + /** + * Destructor. + */ + ~Item(); + + /** + * Returns the Item's payload. + * @return The layload. + */ + const Tag* payload() const { return m_payload; } + + /** + * Returns the item ID. + * @return The item ID. + */ + const std::string& id() const { return m_id; } + + /** + * Creates and returns a Tag representation of the Item. + * @return An XML representation of the Item. + */ + Tag* tag() const; + + private: + Tag* m_payload; + std::string m_id; + + }; + + } + +} + +#endif // PUBSUBITEM_H__ diff --git a/libs/libgloox/pubsubmanager.cpp b/libs/libgloox/pubsubmanager.cpp new file mode 100644 index 0000000..9742ea1 --- /dev/null +++ b/libs/libgloox/pubsubmanager.cpp @@ -0,0 +1,1199 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#include "pubsubmanager.h" +#include "clientbase.h" +#include "dataform.h" +#include "iq.h" +#include "pubsub.h" +#include "pubsubresulthandler.h" +#include "pubsubitem.h" +#include "shim.h" +#include "util.h" +#include "error.h" + +namespace gloox +{ + + namespace PubSub + { + + static const std::string + XMLNS_PUBSUB_NODE_CONFIG = "http://jabber.org/protocol/pubsub#node_config", + XMLNS_PUBSUB_SUBSCRIBE_OPTIONS = "http://jabber.org/protocol/pubsub#subscribe_options"; + + /** + * Finds the associated PubSubFeature for a feature tag 'type' attribute, + * as received from a disco info query on a pubsub service (XEP-0060 sect. 10). + * @param feat Feature string to search for. + * @return the associated PubSubFeature. + */ +/* static PubSubFeature featureType( const std::string& str ) + { + static const char* values [] = { + "collections", + "config-node", + "create-and-configure", + "create-nodes", + "delete-any", + "delete-nodes", + "get-pending", + "instant-nodes", + "item-ids", + "leased-subscription", + "manage-subscriptions", + "meta-data", + "modify-affiliations", + "multi-collection", + "multi-subscribe", + "outcast-affiliation", + "persistent-items", + "presence-notifications", + "publish", + "publisher-affiliation", + "purge-nodes", + "retract-items", + "retrieve-affiliations", + "retrieve-default", + "retrieve-items", + "retrieve-subscriptions", + "subscribe", + "subscription-options", + "subscription-notifications", + "owner", + "event", + }; + return static_cast< PubSubFeature >( util::lookup2( str, values ) ); + } +*/ + + static const char* subscriptionValues[] = { + "none", "subscribed", "pending", "unconfigured" + }; + + static inline SubscriptionType subscriptionType( const std::string& subscription ) + { + return (SubscriptionType)util::lookup( subscription, subscriptionValues ); + } + + static inline const std::string subscriptionValue( SubscriptionType subscription ) + { + return util::lookup( subscription, subscriptionValues ); + } + + static const char* affiliationValues[] = { + "none", "publisher", "owner", "outcast" + }; + + static inline AffiliationType affiliationType( const std::string& affiliation ) + { + return (AffiliationType)util::lookup( affiliation, affiliationValues ); + } + + static inline const std::string affiliationValue( AffiliationType affiliation ) + { + return util::lookup( affiliation, affiliationValues ); + } + + // ---- Manager::PubSubOwner ---- + Manager::PubSubOwner::PubSubOwner( TrackContext context ) + : StanzaExtension( ExtPubSubOwner ), m_ctx( context ), m_form( 0 ) + { + } + + Manager::PubSubOwner::PubSubOwner( const Tag* tag ) + : StanzaExtension( ExtPubSubOwner ), m_ctx( InvalidContext ), m_form( 0 ) + { + const Tag* d = tag->findTag( "pubsub/delete" ); + if( d ) + { + m_ctx = DeleteNode; + m_node = d->findAttribute( "node" ); + return; + } + const Tag* p = tag->findTag( "pubsub/purge" ); + if( p ) + { + m_ctx = PurgeNodeItems; + m_node = p->findAttribute( "node" ); + return; + } + const Tag* c = tag->findTag( "pubsub/configure" ); + if( c ) + { + m_ctx = SetNodeConfig; + m_node = c->findAttribute( "node" ); + if( c->hasChild( "x", "xmlns", XMLNS_X_DATA ) ) + { + m_ctx = GetNodeConfig; + m_form = new DataForm( c->findChild( "x", "xmlns", XMLNS_X_DATA ) ); + } + return; + } + const Tag* de = tag->findTag( "pubsub/default" ); + if( de ) + { + m_ctx = DefaultNodeConfig; + return; + } + const Tag* s = tag->findTag( "pubsub/subscriptions" ); + if( s ) + { + m_ctx = GetSubscriberList; + m_node = s->findAttribute( "node" ); + const TagList& l = s->children(); + TagList::const_iterator it =l.begin(); + for( ; it != l.end(); ++it ) + { + if( (*it)->name() == "subscription" ) + { + Subscriber sub( (*it)->findAttribute( "jid" ), + subscriptionType( (*it)->findAttribute( "subscription" ) ), + (*it)->findAttribute( "subid" ) ); + m_subList.push_back( sub ); + } + } + return; + } + const Tag* a = tag->findTag( "pubsub/affiliations" ); + if( a ) + { + m_ctx = GetAffiliateList; + m_node = a->findAttribute( "node" ); + const TagList& l = a->children(); + TagList::const_iterator it =l.begin(); + for( ; it != l.end(); ++it ) + { + if( (*it)->name() == "affiliation" ) + { + Affiliate aff( (*it)->findAttribute( "jid" ), + affiliationType( (*it)->findAttribute( "affiliation" ) ) ); + m_affList.push_back( aff ); + } + } + return; + } + } + + Manager::PubSubOwner::~PubSubOwner() + { + delete m_form; + } + + const std::string& Manager::PubSubOwner::filterString() const + { + static const std::string filter = "/iq/pubsub[@xmlns='" + XMLNS_PUBSUB_OWNER + "']"; + return filter; + } + + Tag* Manager::PubSubOwner::tag() const + { + if( m_ctx == InvalidContext ) + return 0; + + Tag* t = new Tag( "pubsub" ); + t->setXmlns( XMLNS_PUBSUB_OWNER ); + Tag* c = 0; + + switch( m_ctx ) + { + case DeleteNode: + { + c = new Tag( t, "delete", "node", m_node ); + break; + } + case PurgeNodeItems: + { + c = new Tag( t, "purge", "node", m_node ); + break; + } + case GetNodeConfig: + case SetNodeConfig: + { + c = new Tag( t, "configure" ); + c->addAttribute( "node", m_node ); + if( m_form ) + c->addChild( m_form->tag() ); + break; + } + case GetSubscriberList: + case SetSubscriberList: + + { + c = new Tag( t, "subscriptions" ); + c->addAttribute( "node", m_node ); + if( m_subList.size() ) + { + Tag* s; + SubscriberList::const_iterator it = m_subList.begin(); + for( ; it != m_subList.end(); ++it ) + { + s = new Tag( c, "subscription" ); + s->addAttribute( "jid", (*it).jid.full() ); + s->addAttribute( "subscription", util::lookup( (*it).type, subscriptionValues ) ); + if( !(*it).subid.empty() ) + s->addAttribute( "subid", (*it).subid ); + } + } + break; + } + case GetAffiliateList: + case SetAffiliateList: + { + c = new Tag( t, "affiliations" ); + c->addAttribute( "node", m_node ); + if( m_affList.size() ) + { + Tag* a; + AffiliateList::const_iterator it = m_affList.begin(); + for( ; it != m_affList.end(); ++it ) + { + a = new Tag( c, "affiliation", "jid", (*it).jid.full() ); + a->addAttribute( "affiliation", util::lookup( (*it).type, affiliationValues ) ); + } + } + break; + } + case DefaultNodeConfig: + { + c = new Tag( t, "default" ); + break; + } + default: + break; + } + + return t; + } + // ---- ~Manager::PubSubOwner ---- + + // ---- Manager::PubSub ---- + Manager::PubSub::PubSub( TrackContext context ) + : StanzaExtension( ExtPubSub ), m_ctx( context ), m_maxItems( 0 ), + m_notify( false ) + { + m_options.df = 0; + } + + Manager::PubSub::PubSub( const Tag* tag ) + : StanzaExtension( ExtPubSub ), m_ctx( InvalidContext ), + m_maxItems( 0 ), m_notify( false ) + { + m_options.df = 0; + if( !tag ) + return; + + ConstTagList l = tag->findTagList( "pubsub/subscriptions/subscription" ); + if( l.size() ) + { + m_ctx = GetSubscriptionList; + ConstTagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + { + const std::string& node = (*it)->findAttribute( "node" ); + const std::string& sub = (*it)->findAttribute( "subscription" ); + const std::string& subid = (*it)->findAttribute( "subid" ); + SubscriptionInfo si; + si.jid.setJID( (*it)->findAttribute( "jid" ) ); + si.type = subscriptionType( sub ); + si.subid = subid; + SubscriptionList& lst = m_subscriptionMap[node]; + lst.push_back( si ); + } + return; + } + l = tag->findTagList( "pubsub/affiliations/affiliation" ); + if( l.size() ) + { + m_ctx = GetAffiliationList; + ConstTagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + { + const std::string& node = (*it)->findAttribute( "node" ); + const std::string& aff = (*it)->findAttribute( "affiliation" ); + m_affiliationMap[node] = affiliationType( aff ); + } + return; + } + const Tag* s = tag->findTag( "pubsub/subscribe" ); + if( s ) + { + m_ctx = Subscription; + m_node = s->findAttribute( "node" ); + m_jid = s->findAttribute( "jid" ); + } + const Tag* u = tag->findTag( "pubsub/unsubscribe" ); + if( u ) + { + m_ctx = Unsubscription; + m_node = u->findAttribute( "node" ); + m_jid = u->findAttribute( "jid" ); + m_subid = u->findAttribute( "subid" ); + } + const Tag* o = tag->findTag( "pubsub/options" ); + if( o ) + { + if( m_ctx == InvalidContext ) + m_ctx = GetSubscriptionOptions; + m_jid.setJID( o->findAttribute( "jid" ) ); + m_options.node = o->findAttribute( "node" ); + m_options.df = new DataForm( o->findChild( "x", "xmlns", XMLNS_X_DATA ) ); + } + const Tag* su = tag->findTag( "pubsub/subscription" ); + if( su ) + { + SubscriptionInfo si; + si.jid.setJID( su->findAttribute( "jid" ) ); + si.subid = su->findAttribute( "subid" ); + si.type = subscriptionType( su->findAttribute( "type" ) ); + SubscriptionList& lst = m_subscriptionMap[su->findAttribute( "node" )]; + lst.push_back( si ); + return; + } + const Tag* i = tag->findTag( "pubsub/items" ); + if( i ) + { + m_ctx = RequestItems; + m_node = i->findAttribute( "node" ); + m_subid = i->findAttribute( "subid" ); + m_maxItems = atoi( i->findAttribute( "max_items" ).c_str() ); + const TagList& l = i->children(); + TagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + m_items.push_back( new Item( (*it) ) ); + return; + } + const Tag* p = tag->findTag( "pubsub/publish" ); + if( p ) + { + m_ctx = PublishItem; + m_node = p->findAttribute( "node" ); + const TagList& l = p->children(); + TagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + m_items.push_back( new Item( (*it) ) ); + return; + } + const Tag* r = tag->findTag( "pubsub/retract" ); + if( r ) + { + m_ctx = DeleteItem; + m_node = r->findAttribute( "node" ); + m_notify = r->hasAttribute( "notify", "1" ) || r->hasAttribute( "notify", "true" ); + const TagList& l = p->children(); + TagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + m_items.push_back( new Item( (*it) ) ); + return; + } + const Tag* c = tag->findTag( "pubsub/create" ); + if( c ) + { + m_ctx = CreateNode; + m_node = c->findAttribute( "node" ); + const Tag* config = tag->findTag( "pubsub/configure" ); + if( config && config->hasChild( "x", XMLNS_X_DATA ) ) + m_options.df = new DataForm( config->findChild( "x", XMLNS_X_DATA ) ); + } + } + + Manager::PubSub::~PubSub() + { + delete m_options.df; + util::clearList( m_items ); + } + + const std::string& Manager::PubSub::filterString() const + { + static const std::string filter = "/iq/pubsub[@xmlns='" + XMLNS_PUBSUB + "']"; + return filter; + } + + Tag* Manager::PubSub::tag() const + { + if( m_ctx == InvalidContext ) + return 0; + + Tag* t = new Tag( "pubsub" ); + t->setXmlns( XMLNS_PUBSUB ); + + if( m_ctx == GetSubscriptionList ) + { + Tag* sub = new Tag( t, "subscriptions" ); + SubscriptionMap::const_iterator it = m_subscriptionMap.begin(); + for( ; it != m_subscriptionMap.end(); ++it ) + { + const SubscriptionList& lst = (*it).second; + SubscriptionList::const_iterator it2 = lst.begin(); + for( ; it2 != lst.end(); ++it2 ) + { + Tag* s = new Tag( sub, "subscription" ); + s->addAttribute( "node", (*it).first ); + s->addAttribute( "jid", (*it2).jid ); + s->addAttribute( "subscription", subscriptionValue( (*it2).type ) ); + s->addAttribute( "sid", (*it2).subid ); + } + } + } + else if( m_ctx == GetAffiliationList ) + { + + Tag* aff = new Tag( t, "affiliations" ); + AffiliationMap::const_iterator it = m_affiliationMap.begin(); + for( ; it != m_affiliationMap.end(); ++it ) + { + Tag* a = new Tag( aff, "affiliation" ); + a->addAttribute( "node", (*it).first ); + a->addAttribute( "affiliation", affiliationValue( (*it).second ) ); + } + } + else if( m_ctx == Subscription ) + { + Tag* s = new Tag( t, "subscribe" ); + s->addAttribute( "node", m_node ); + s->addAttribute( "jid", m_jid.full() ); + if( m_options.df ) + { + Tag* o = new Tag( t, "options" ); + o->addChild( m_options.df->tag() ); + } + } + else if( m_ctx == Unsubscription ) + { + Tag* u = new Tag( t, "unsubscribe" ); + u->addAttribute( "node", m_node ); + u->addAttribute( "jid", m_jid.full() ); + u->addAttribute( "subid", m_subid ); + } + else if( m_ctx == GetSubscriptionOptions + || m_ctx == SetSubscriptionOptions + || ( m_ctx == Subscription && m_options.df ) ) + { + Tag* o = new Tag( t, "options" ); + o->addAttribute( "node", m_options.node ); + o->addAttribute( "jid", m_jid.full() ); + if( m_options.df ) + o->addChild( m_options.df->tag() ); + } + else if( m_ctx == RequestItems ) + { + Tag* i = new Tag( t, "items" ); + i->addAttribute( "node", m_node ); + if( m_maxItems ) + i->addAttribute( "max_items", m_maxItems ); + i->addAttribute( "subid", m_subid ); + ItemList::const_iterator it = m_items.begin(); + for( ; it != m_items.end(); ++it ) + i->addChild( (*it)->tag() ); + } + else if( m_ctx == PublishItem ) + { + Tag* p = new Tag( t, "publish" ); + p->addAttribute( "node", m_node ); + ItemList::const_iterator it = m_items.begin(); + for( ; it != m_items.end(); ++it ) + p->addChild( (*it)->tag() ); + if( m_options.df ) + { + Tag* po = new Tag( "publish-options" ); + po->addChild( m_options.df->tag() ); + } + } + else if( m_ctx == DeleteItem ) + { + Tag* r = new Tag( t, "retract" ); + r->addAttribute( "node", m_node ); + if( m_notify ) + r->addAttribute( "notify", "true" ); + ItemList::const_iterator it = m_items.begin(); + for( ; it != m_items.end(); ++it ) + r->addChild( (*it)->tag() ); + } + else if( m_ctx == CreateNode ) + { + Tag* c = new Tag( t, "create" ); + c->addAttribute( "node", m_node ); + Tag* config = new Tag( t, "configure" ); + if( m_options.df ) + config->addChild( m_options.df->tag() ); + } + return t; + } + + StanzaExtension* Manager::PubSub::clone() const + { + PubSub* p = new PubSub(); + p->m_affiliationMap = m_affiliationMap; + p->m_subscriptionMap = m_subscriptionMap; + p->m_ctx = m_ctx; + + p->m_options.node = m_options.node; + p->m_options.df = m_options.df ? new DataForm( *(m_options.df) ) : 0; + + p->m_jid = m_jid; + p->m_node = m_node; + p->m_subid = m_subid; + ItemList::const_iterator it = m_items.begin(); + for( ; it != m_items.end(); ++it ) + p->m_items.push_back( new Item( *(*it) ) ); + + p->m_maxItems = m_maxItems; + p->m_notify = m_notify; + return p; + } + // ---- ~Manager::PubSub ---- + + // ---- Manager ---- + Manager::Manager( ClientBase* parent ) + : m_parent( parent ) + { + if( m_parent ) + { + m_parent->registerStanzaExtension( new PubSub() ); + m_parent->registerStanzaExtension( new PubSubOwner() ); + m_parent->registerStanzaExtension( new SHIM() ); + } + } + + const std::string Manager::getSubscriptionsOrAffiliations( const JID& service, + ResultHandler* handler, + TrackContext context ) + { + if( !m_parent || !handler || !service || context == InvalidContext ) + return EmptyString; + + const std::string& id = m_parent->getID(); + IQ iq( IQ::Get, service, id ); + iq.addExtension( new PubSub( context ) ); + + m_trackMapMutex.lock(); + m_resultHandlerTrackMap[id] = handler; + m_trackMapMutex.unlock(); + m_parent->send( iq, this, context ); + return id; + } + + const std::string Manager::subscribe( const JID& service, + const std::string& node, + ResultHandler* handler, + const JID& jid, + SubscriptionObject type, + int depth, + const std::string& expire + ) + { + if( !m_parent || !handler || !service || node.empty() ) + return EmptyString; + + const std::string& id = m_parent->getID(); + IQ iq( IQ::Set, service, id ); + PubSub* ps = new PubSub( Subscription ); + ps->setJID( jid ? jid : m_parent->jid() ); + ps->setNode( node ); + if( type != SubscriptionNodes || depth != 1 ) + { + DataForm* df = new DataForm( TypeSubmit ); + df->addField( DataFormField::TypeHidden, "FORM_TYPE", XMLNS_PUBSUB_SUBSCRIBE_OPTIONS ); + + if( type == SubscriptionItems ) + df->addField( DataFormField::TypeNone, "pubsub#subscription_type", "items" ); + + if( depth != 1 ) + { + DataFormField* field = df->addField( DataFormField::TypeNone, "pubsub#subscription_depth" ); + if( depth == 0 ) + field->setValue( "all" ); + else + field->setValue( util::int2string( depth ) ); + } + + if( !expire.empty() ) + { + DataFormField* field = df->addField( DataFormField::TypeNone, "pubsub#expire" ); + field->setValue( expire ); + } + + ps->setOptions( node, df ); + } + iq.addExtension( ps ); + + m_trackMapMutex.lock(); + m_resultHandlerTrackMap[id] = handler; + m_nopTrackMap[id] = node; + m_trackMapMutex.unlock(); + m_parent->send( iq, this, Subscription ); + return id; + } + + const std::string Manager::unsubscribe( const JID& service, + const std::string& node, + const std::string& subid, + ResultHandler* handler, + const JID& jid ) + { + if( !m_parent || !handler || !service ) + return EmptyString; + + const std::string& id = m_parent->getID(); + IQ iq( IQ::Set, service, id ); + PubSub* ps = new PubSub( Unsubscription ); + ps->setNode( node ); + ps->setJID( jid ? jid : m_parent->jid() ); + ps->setSubscriptionID( subid ); + iq.addExtension( ps ); + + m_trackMapMutex.lock(); + m_resultHandlerTrackMap[id] = handler; + m_trackMapMutex.unlock(); + // FIXME? need to track info for handler + m_parent->send( iq, this, Unsubscription ); + return id; + } + + const std::string Manager::subscriptionOptions( TrackContext context, + const JID& service, + const JID& jid, + const std::string& node, + ResultHandler* handler, + DataForm* df ) + { + if( !m_parent || !handler || !service ) + return EmptyString; + + const std::string& id = m_parent->getID(); + IQ iq( df ? IQ::Set : IQ::Get, service, id ); + PubSub* ps = new PubSub( context ); + ps->setJID( jid ? jid : m_parent->jid() ); + ps->setOptions( node, df ); + iq.addExtension( ps ); + + m_trackMapMutex.lock(); + m_resultHandlerTrackMap[id] = handler; + m_trackMapMutex.unlock(); + m_parent->send( iq, this, context ); + return id; + } + + const std::string Manager::requestItems( const JID& service, + const std::string& node, + const std::string& subid, + int maxItems, + ResultHandler* handler ) + { + if( !m_parent || !service || !handler ) + return EmptyString; + + const std::string& id = m_parent->getID(); + IQ iq( IQ::Get, service, id ); + PubSub* ps = new PubSub( RequestItems ); + ps->setNode( node ); + ps->setSubscriptionID( subid ); + ps->setMaxItems( maxItems ); + iq.addExtension( ps ); + + m_trackMapMutex.lock(); + m_resultHandlerTrackMap[id] = handler; + m_trackMapMutex.unlock(); + m_parent->send( iq, this, RequestItems ); + return id; + } + + const std::string Manager::requestItems( const JID& service, + const std::string& node, + const std::string& subid, + const ItemList& items, + ResultHandler* handler ) + { + if( !m_parent || !service || !handler ) + return EmptyString; + + const std::string& id = m_parent->getID(); + IQ iq( IQ::Get, service, id ); + PubSub* ps = new PubSub( RequestItems ); + ps->setNode( node ); + ps->setSubscriptionID( subid ); + ps->setItems( items ); + iq.addExtension( ps ); + + m_trackMapMutex.lock(); + m_resultHandlerTrackMap[id] = handler; + m_trackMapMutex.unlock(); + m_parent->send( iq, this, RequestItems ); + return id; + } + + const std::string Manager::publishItem( const JID& service, + const std::string& node, + ItemList& items, + DataForm* options, + ResultHandler* handler ) + { + if( !m_parent || !handler ) + { + util::clearList( items ); + return EmptyString; + } + + const std::string& id = m_parent->getID(); + IQ iq( IQ::Set, service, id ); + PubSub* ps = new PubSub( PublishItem ); + ps->setNode( node ); + ps->setItems( items ); + ps->setOptions( EmptyString, options ); + iq.addExtension( ps ); + + m_trackMapMutex.lock(); + m_resultHandlerTrackMap[id] = handler; + m_trackMapMutex.unlock(); + m_parent->send( iq, this, PublishItem ); + return id; + } + + const std::string Manager::deleteItem( const JID& service, + const std::string& node, + const ItemList& items, + bool notify, + ResultHandler* handler ) + { + if( !m_parent || !handler || !service ) + return EmptyString; + + const std::string& id = m_parent->getID(); + IQ iq( IQ::Set, service, id ); + PubSub* ps = new PubSub( DeleteItem ); + ps->setNode( node ); + ps->setItems( items ); + ps->setNotify( notify ); + iq.addExtension( ps ); + + m_trackMapMutex.lock(); + m_resultHandlerTrackMap[id] = handler; + m_trackMapMutex.unlock(); + m_parent->send( iq, this, DeleteItem ); + return id; + } + + const std::string Manager::createNode( const JID& service, + const std::string& node, + DataForm* config, + ResultHandler* handler ) + { + if( !m_parent || !handler || !service || node.empty() ) + return EmptyString; + + const std::string& id = m_parent->getID(); + IQ iq( IQ::Set, service, id ); + PubSub* ps = new PubSub( CreateNode ); + ps->setNode( node ); + ps->setOptions( EmptyString, config ); + iq.addExtension( ps ); + + m_trackMapMutex.lock(); + m_nopTrackMap[id] = node; + m_resultHandlerTrackMap[id] = handler; + m_trackMapMutex.unlock(); + m_parent->send( iq, this, CreateNode ); + return id; + } + + const std::string Manager::deleteNode( const JID& service, + const std::string& node, + ResultHandler* handler ) + { + if( !m_parent || !handler || !service || node.empty() ) + return EmptyString; + + const std::string& id = m_parent->getID(); + IQ iq( IQ::Set, service, id ); + PubSubOwner* pso = new PubSubOwner( DeleteNode ); + pso->setNode( node ); + iq.addExtension( pso ); + + m_trackMapMutex.lock(); + m_nopTrackMap[id] = node; + m_resultHandlerTrackMap[id] = handler; + m_trackMapMutex.unlock(); + m_parent->send( iq, this, DeleteNode ); + return id; + } + + const std::string Manager::getDefaultNodeConfig( const JID& service, + NodeType type, + ResultHandler* handler ) + { + if( !m_parent || !handler || !service ) + return EmptyString; + + const std::string& id = m_parent->getID(); + IQ iq( IQ::Get, service, id ); + PubSubOwner* pso = new PubSubOwner( DefaultNodeConfig ); + if( type == NodeCollection ) + { + DataForm* df = new DataForm( TypeSubmit ); + df->addField( DataFormField::TypeHidden, "FORM_TYPE", XMLNS_PUBSUB_NODE_CONFIG ); + df->addField( DataFormField::TypeNone, "pubsub#node_type", "collection" ); + pso->setConfig( df ); + } + iq.addExtension( pso ); + + m_trackMapMutex.lock(); + m_resultHandlerTrackMap[id] = handler; + m_trackMapMutex.unlock(); + m_parent->send( iq, this, DefaultNodeConfig ); + return id; + } + + const std::string Manager::nodeConfig( const JID& service, + const std::string& node, + DataForm* config, + ResultHandler* handler ) + { + if( !m_parent || !handler || !service || node.empty() ) + return EmptyString; + + const std::string& id = m_parent->getID(); + IQ iq( config ? IQ::Set : IQ::Get, service, id ); + PubSubOwner* pso = new PubSubOwner( config ? SetNodeConfig : GetNodeConfig ); + pso->setNode( node ); + if( config ) + pso->setConfig( config ); + iq.addExtension( pso ); + + m_trackMapMutex.lock(); + m_resultHandlerTrackMap[id] = handler; + m_trackMapMutex.unlock(); + m_parent->send( iq, this, config ? SetNodeConfig : GetNodeConfig ); + return id; + } + + const std::string Manager::subscriberList( TrackContext ctx, + const JID& service, + const std::string& node, + const SubscriberList& subList, + ResultHandler* handler ) + { + if( !m_parent || !handler || !service || node.empty() ) + return EmptyString; + + const std::string& id = m_parent->getID(); + IQ iq( ctx == SetSubscriberList ? IQ::Set : IQ::Get, service, id ); + PubSubOwner* pso = new PubSubOwner( ctx ); + pso->setNode( node ); + pso->setSubscriberList( subList ); + iq.addExtension( pso ); + + m_trackMapMutex.lock(); + m_nopTrackMap[id] = node; + m_resultHandlerTrackMap[id] = handler; + m_trackMapMutex.unlock(); + m_parent->send( iq, this, ctx ); + return id; + } + + const std::string Manager::affiliateList( TrackContext ctx, + const JID& service, + const std::string& node, + const AffiliateList& affList, + ResultHandler* handler ) + { + if( !m_parent || !handler || !service || node.empty() ) + return EmptyString; + + const std::string& id = m_parent->getID(); + IQ iq( ctx == SetAffiliateList ? IQ::Set : IQ::Get, service, id ); + PubSubOwner* pso = new PubSubOwner( ctx ); + pso->setNode( node ); + pso->setAffiliateList( affList ); + iq.addExtension( pso ); + + m_trackMapMutex.lock(); + m_nopTrackMap[id] = node; + m_resultHandlerTrackMap[id] = handler; + m_trackMapMutex.unlock(); + m_parent->send( iq, this, ctx ); + return id; + } + + const std::string Manager::purgeNode( const JID& service, + const std::string& node, + ResultHandler* handler ) + { + if( !m_parent || !handler || !service || node.empty() ) + return EmptyString; + + const std::string& id = m_parent->getID(); + IQ iq( IQ::Set, service, id ); + PubSubOwner* pso = new PubSubOwner( PurgeNodeItems ); + pso->setNode( node ); + iq.addExtension( pso ); + + m_trackMapMutex.lock(); + m_nopTrackMap[id] = node; + m_resultHandlerTrackMap[id] = handler; + m_trackMapMutex.unlock(); + m_parent->send( iq, this, PurgeNodeItems ); + return id; + } + + bool Manager::removeID( const std::string& id ) + { + m_trackMapMutex.lock(); + ResultHandlerTrackMap::iterator ith = m_resultHandlerTrackMap.find( id ); + if( ith == m_resultHandlerTrackMap.end() ) + { + m_trackMapMutex.unlock(); + return false; + } + m_resultHandlerTrackMap.erase( ith ); + m_trackMapMutex.unlock(); + return true; + } + + void Manager::handleIqID( const IQ& iq, int context ) + { + const JID& service = iq.from(); + const std::string& id = iq.id(); + + m_trackMapMutex.lock(); + ResultHandlerTrackMap::iterator ith = m_resultHandlerTrackMap.find( id ); + if( ith == m_resultHandlerTrackMap.end() ) + { + m_trackMapMutex.unlock(); + return; + } + ResultHandler* rh = (*ith).second; + m_resultHandlerTrackMap.erase( ith ); + m_trackMapMutex.unlock(); + + switch( iq.subtype() ) + { + case IQ::Error: + case IQ::Result: + { + const Error* error = iq.error(); + switch( context ) + { + case Subscription: + { + const PubSub* ps = iq.findExtension( ExtPubSub ); + if( !ps ) + return; + SubscriptionMap sm = ps->subscriptions(); + if( !sm.empty() ) + { + SubscriptionMap::const_iterator it = sm.begin(); + const SubscriptionList& lst = (*it).second; + if( lst.size() == 1 ) + { + SubscriptionList::const_iterator it2 = lst.begin(); + rh->handleSubscriptionResult( id, service, (*it).first, (*it2).subid, (*it2).jid, + (*it2).type, error ); + } + } + break; + } + case Unsubscription: + { + rh->handleUnsubscriptionResult( iq.id(), service, error ); + break; + } + case GetSubscriptionList: + { + const PubSub* ps = iq.findExtension( ExtPubSub ); + if( !ps ) + return; + + rh->handleSubscriptions( id, service, + ps->subscriptions(), + error ); + break; + } + case GetAffiliationList: + { + const PubSub* ps = iq.findExtension( ExtPubSub ); + if( !ps ) + return; + + rh->handleAffiliations( id, service, + ps->affiliations(), + error ); + break; + } + case RequestItems: + { + const PubSub* ps = iq.findExtension( ExtPubSub ); + if( !ps ) + return; + + rh->handleItems( id, service, ps->node(), + ps->items(), error ); + break; + } + case PublishItem: + { + const PubSub* ps = iq.findExtension( ExtPubSub ); + if( ps && ps->items().size()) + { + const ItemList il = ps->items(); + rh->handleItemPublication( id, service, "", + il, error ); + } + break; + } + case DeleteItem: + { + const PubSub* ps = iq.findExtension( ExtPubSub ); + if( ps ) + { + rh->handleItemDeletion( id, service, + ps->node(), + ps->items(), + error ); + } + break; + } + case DefaultNodeConfig: + { + const PubSubOwner* pso = iq.findExtension( ExtPubSubOwner ); + if( pso ) + { + rh->handleDefaultNodeConfig( id, service, + pso->config(), + error ); + } + break; + } + case GetSubscriptionOptions: + case GetSubscriberList: + case SetSubscriberList: + case GetAffiliateList: + case SetAffiliateList: + case GetNodeConfig: + case SetNodeConfig: + case CreateNode: + case DeleteNode: + case PurgeNodeItems: + { + switch( context ) + { + case GetSubscriptionOptions: + { + const PubSub* ps = iq.findExtension( ExtPubSub ); + if( ps ) + { + rh->handleSubscriptionOptions( id, service, + ps->jid(), + ps->node(), + ps->options(), + error ); + } + break; + } +// case GetSubscriberList: +// { +// const PubSub* ps = iq.findExtension( ExtPubSub ); +// if( ps ) +// { +// rh->handleSubscribers( service, ps->node(), ps->subscriptions() ); +// } +// break; +// } + case SetSubscriptionOptions: + case SetSubscriberList: + case SetAffiliateList: + case SetNodeConfig: + case CreateNode: + case DeleteNode: + case PurgeNodeItems: + { + m_trackMapMutex.lock(); + NodeOperationTrackMap::iterator it = m_nopTrackMap.find( id ); + if( it != m_nopTrackMap.end() ) + { + const std::string& node = (*it).second; + switch( context ) + { + case SetSubscriptionOptions: + rh->handleSubscriptionOptionsResult( id, service, JID( /* FIXME */ ), node, error ); + break; + case SetSubscriberList: + rh->handleSubscribersResult( id, service, node, 0, error ); + break; + case SetAffiliateList: + rh->handleAffiliatesResult( id, service, node, 0, error ); + break; + case SetNodeConfig: + rh->handleNodeConfigResult( id, service, node, error ); + break; + case CreateNode: + rh->handleNodeCreation( id, service, node, error ); + break; + case DeleteNode: + rh->handleNodeDeletion( id, service, node, error ); + break; + case PurgeNodeItems: + rh->handleNodePurge( id, service, node, error ); + break; + } + m_nopTrackMap.erase( it ); + } + m_trackMapMutex.unlock(); + break; + } + case GetAffiliateList: + { +// const PubSub + + /* const TagList& affiliates = query->children(); + AffiliateList affList; + TagList::const_iterator it = affiliates.begin(); + for( ; it != affiliates.end(); ++it ) + { + Affiliate aff( (*it)->findAttribute( "jid" ), + affiliationType( (*it)->findAttribute( "affiliation" ) ) ); + affList.push_back( aff ); + } + rh->handleAffiliates( service, query->findAttribute( "node" ), &affList ); + */ + break; + } + case GetNodeConfig: + { + const PubSubOwner* pso = iq.findExtension( ExtPubSubOwner ); + if( pso ) + { + rh->handleNodeConfig( id, service, + pso->node(), + pso->config(), + error ); + } + break; + } + default: + break; + } + + break; + } + } + break; + } + default: + break; + } + + } + + } + +} + diff --git a/libs/libgloox/pubsubmanager.h b/libs/libgloox/pubsubmanager.h new file mode 100644 index 0000000..4c448b1 --- /dev/null +++ b/libs/libgloox/pubsubmanager.h @@ -0,0 +1,828 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#ifndef PUBSUBMANAGER_H__ +#define PUBSUBMANAGER_H__ + +#include "pubsub.h" +#include "dataform.h" +#include "iqhandler.h" +#include "mutex.h" + +#include +#include + +namespace gloox +{ + + class ClientBase; + + namespace PubSub + { + + class ResultHandler; + + /** + * @brief This manager is used to interact with PubSub services (XEP-0060). + * + * @note PubSub support in gloox is still relatively young and you are most + * welcome to ask questions, criticize the API and so on. + * + * A ResultHandler is used to receive a request's result. Depending on the + * context, this can be a notification that an item has been succesfully + * deleted (or not), or the default node configuration for a service. + * + * To receive PubSub events: + * @li Tell ClientBase that you are interested in PubSub events by registering + * an empty PubSub::Event StanzaExtension + * @code + * m_client->registerStanzaExtension( new PubSub::Event() ); + * @endcode + * @li Implement a MessageHandler and register it with ClientBase, or use the MessageSession interface, at your choice, + * @li When receiving a Message, check it for a PubSub::Event + * @code + * const PubSub::Event* pse = msg.findExtension( ExtPubSubEvent ); + * if( pse ) + * { + * // use the Event + * } + * else + * { + * // no Event + * } + * @endcode + * + * To interact with PubSub services, you will need to + * instantiate a PubSub::Manager and + * implement the ResultHandler virtual interfaces to be notified of the + * result of requests. + * + * @note A null ResultHandler to a query is not allowed and is a no-op. + * + * XEP Version: 1.12 + * + * @author Jakob Schroeter + * @author Vincent Thomasset + * + * @since 1.0 + */ + class GLOOX_API Manager : public IqHandler + { + public: + + /** + * Initialize the manager. + * @param parent Client to which this manager belongs. + */ + Manager( ClientBase* parent ); + + /** + * Default virtual destructor. + */ + virtual ~Manager() {} + + /** + * Subscribe to a node. + * + * @param service Service hosting the node. + * @param node ID of the node to subscribe to. + * @param handler The ResultHandler. + * @param jid JID to subscribe. If empty, the client's JID will be used + * (ie. self subscription). + * @param type SubscriptionType of the subscription (Collections only). + * @param depth Subscription depth. For 'all', use 0 (Collections only). + * @param expire Subscription expiry. Defaults to the empty string. + * @return The IQ ID used in the request. + * + * @see ResultHandler::handleSubscriptionResult + */ + const std::string subscribe( const JID& service, const std::string& node, + ResultHandler* handler, const JID& jid = JID(), + SubscriptionObject type = SubscriptionNodes, + int depth = 1, const std::string& expire = EmptyString ); + + /** + * Unsubscribe from a node. + * + * @param service Service hosting the node. + * @param node ID of the node to unsubscribe from. + * @param subid An optional, additional subscription ID. + * @param handler ResultHandler receiving the result notification. + * @param jid JID to unsubscribe. If empty, the client's JID will be + * used (ie self unsubscription). + * @return The IQ ID used in the request. + * + * @see ResultHandler::handleUnsubscriptionResult + */ + const std::string unsubscribe( const JID& service, + const std::string& node, + const std::string& subid, + ResultHandler* handler, + const JID& jid = JID() ); + + /** + * Requests the subscription list from a service. + * + * @param service Service to query. + * @param handler The ResultHandler to handle the result. + * @return The IQ ID used in the request. + * + * @see ResultHandler::handleSubscriptions + */ + const std::string getSubscriptions( const JID& service, + ResultHandler* handler ) + { + return getSubscriptionsOrAffiliations( service, + handler, + GetSubscriptionList ); + } + + /** + * Requests the affiliation list from a service. + * + * @param service Service to query. + * @param handler The ResultHandler to handle the result. + * @return The IQ ID used in the request. + * + * @see ResultHandler::handleAffiliations + */ + const std::string getAffiliations( const JID& service, + ResultHandler* handler ) + { + return getSubscriptionsOrAffiliations( service, + handler, + GetAffiliationList ); + } + + /** + * Requests subscription options. + * + * @param service Service to query. + * @param jid Subscribed entity. + * @param node Node ID of the node. + * @param handler The SubscriptionListHandler to handle the result. + * @return The IQ ID used in the request. + * + * @see ResultHandler::handleSubscriptionOptions + */ + const std::string getSubscriptionOptions( const JID& service, + const JID& jid, + const std::string& node, + ResultHandler* handler) + { return subscriptionOptions( GetSubscriptionOptions, service, jid, node, handler, 0 ); } + + /** + * Modifies subscription options. + * + * @param service Service to query. + * @param jid Subscribed entity. + * @param node Node ID of the node. + * @param df New configuration. The DataForm will be owned and deleted by the Manager. + * @param handler The handler to handle the result. + * @return The IQ ID used in the request. + * + * @see ResultHandler::handleSubscriptionOptionsResult + */ + const std::string setSubscriptionOptions( const JID& service, + const JID& jid, + const std::string& node, + DataForm* df, + ResultHandler* handler ) + { return subscriptionOptions( SetSubscriptionOptions, service, jid, node, handler, df ); } + + /** + * Requests the affiliation list for a node. + * + * @param service Service to query. + * @param node Node ID of the node. + * @param handler The AffiliationListHandler to handle the result. + * + * @see ResultHandler::handleAffiliations + */ + void getAffiliations( const JID& service, + const std::string& node, + ResultHandler* handler ); + + /** + * Requests items from a node. + * @param service Service to query. + * @param node Node ID of the node. + * @param subid An optional subscription ID. + * @param maxItems The optional maximum number of items to return. + * @param handler The handler to handle the result. + * @return The ID used in the request. + */ + const std::string requestItems( const JID& service, + const std::string& node, + const std::string& subid, + int maxItems, + ResultHandler* handler); + + /** + * Requests specific items from a node. + * @param service Service to query. + * @param node Node ID of the node. + * @param subid An optional subscription ID. + * @param items The list of item IDs to request. + * @param handler The handler to handle the result. + * @return The ID used in the request. + */ + const std::string requestItems( const JID& service, + const std::string& node, + const std::string& subid, + const ItemList& items, + ResultHandler* handler); + + /** + * Publish an item to a node. The Tag to publish is destroyed + * by the function before returning. + * + * @param service Service hosting the node. + * @param node ID of the node to delete the item from. + * @param items One or more items to publish. The items will be owned and deleted by the Manager, + * even in the error case (empty string returned). + * @param options An optional DataForm containing publish options. The DataForm will be owned and deleted by the Manager. + * @param handler The handler to handle the result. + * @return The ID used in the request. + * + * @see ResultHandler::handleItemPublication + */ + const std::string publishItem( const JID& service, + const std::string& node, + ItemList& items, + DataForm* options, + ResultHandler* handler ); + + /** + * Delete an item from a node. + * + * @param service Service hosting the node. + * @param node ID of the node to delete the item from. + * @param items A list of items to delete (only ID filled in). + * @param notify Whether or not to notify subscribers about the deletion. + * @param handler The handler to handle the result. + * @return The ID used in the request. + * + * @see ResultHandler::handleItemDeletation + */ + const std::string deleteItem( const JID& service, + const std::string& node, + const ItemList& items, + bool notify, + ResultHandler* handler ); + + /** + * Creates a new node. + * + * @param service Service where to create the new node. + * @param node The ID of the new node. + * @param config An optional DataForm that holds the node configuration. + * The DataForm will be owned and deleted by the Manager. + * @param handler The handler to handle the result. + * @return The ID used in the request. + * + * @see ResultHandler::handleNodeCreation + */ + const std::string createNode( const JID& service, + const std::string& node, + DataForm* config, + ResultHandler* handler ); + + /** + * Deletes a node. + * + * @param service Service where to create the new node. + * @param node Node ID of the new node. + * @param handler The handler to handle the result. + * @return The ID used in the request. + * + * @see ResultHandler::handleNodeDeletion + */ + const std::string deleteNode( const JID& service, + const std::string& node, + ResultHandler* handler ); + + /** + * Retrieves the default configuration for a specific NodeType. + * + * @param service The queried service. + * @param type NodeType to get default configuration for. + * @param handler ResultHandler. + * @return The ID used in the request. + * + * @see ResultHandler::handleDefaultNodeConfig + */ + const std::string getDefaultNodeConfig( const JID& service, + NodeType type, + ResultHandler* handler ); + + /** + * Removes all the items from a node. + * + * @param service Service to query. + * @param node Node ID of the node. + * @param handler ResultHandler. + * @return The ID used in the request. + * + * @see ResultHandler::handleNodePurge + */ + const std::string purgeNode( const JID& service, + const std::string& node, + ResultHandler* handler ); + + /** + * Requests the subscriber list for a node. + * + * @param service Service to query. + * @param node Node ID of the node. + * @param handler ResultHandler. + * @return The ID used in the request. + * + * @see ResultHandler::handleSubscribers + */ + const std::string getSubscribers( const JID& service, + const std::string& node, + ResultHandler* handler ) + { return subscriberList( GetSubscriberList, service, + node, SubscriberList(), + handler ); } + + /** + * Modifies the subscriber list for a node. This function SHOULD only set the + * subscriber list to those which needs modification. + * + * @param service Service to query. + * @param node Node ID of the node. + * @param list The subscriber list. + * @param handler The ResultHandler. + * @return The ID used in the request. + * + * @see ResultHandler::handleSubscribers + */ + const std::string setSubscribers( const JID& service, + const std::string& node, + const SubscriberList& list, + ResultHandler* handler ) + { return subscriberList( SetSubscriberList, service, + node, list, handler ); } + + /** + * Requests the affiliate list for a node. + * + * @param service Service to query. + * @param node Node ID of the node. + * @param handler ResultHandler. + * @return The ID used in the request. + * + * @see ResultHandler::handleAffiliates + */ + const std::string getAffiliates( const JID& service, + const std::string& node, + ResultHandler* handler ) + { return affiliateList( GetAffiliateList, service, + node, AffiliateList(), + handler ); } + + /** + * Modifies the affiliate list for a node. + * + * @param service Service to query. + * @param node Node ID of the node. + * @param list ResultHandler. + * @param handler ResultHandler. + * @return The ID used in the request. + * + * @see ResultHandler::handleAffiliatesResult + */ + const std::string setAffiliates( const JID& service, + const std::string& node, + const AffiliateList& list, + ResultHandler* handler ) + { return affiliateList( SetAffiliateList, service, + node, list, handler ); } + + /** + * Retrieve the configuration (options) of a node. + * + * @param service Service hosting the node. + * @param node ID of the node. + * @param handler ResultHandler responsible to handle the request result. + * @return The ID used in the request. + * + * @see ResultHandler::handleNodeConfig + */ + const std::string getNodeConfig( const JID& service, + const std::string& node, + ResultHandler* handler ) + { return nodeConfig( service, node, 0, handler ); } + + /** + * Changes a node's configuration (options). + * + * @param service Service to query. + * @param node Node ID of the node. + * @param config The node's configuration DataForm. + * @param handler ResultHandler responsible to handle the request result. + * @return The ID used in the request. + * + * @see ResultHandler::handleNodeConfigResult + */ + const std::string setNodeConfig( const JID& service, + const std::string& node, + DataForm* config, + ResultHandler* handler ) + { return nodeConfig( service, node, config, handler ); } + + /** + * Removes an ID from our tracking lists. + * @param id The ID to remove. + * @return @b True if the ID was found and removed, @b false otherwise. + */ + bool removeID( const std::string& id ); + + // reimplemented from DiscoHandler + void handleDiscoInfoResult( IQ* iq, int context ); + + // reimplemented from DiscoHandler + void handleDiscoItemsResult( IQ* iq, int context ); + + // reimplemented from DiscoHandler + void handleDiscoError( IQ* iq, int context ); + + // reimplemented from DiscoHandler + bool handleDiscoSet( IQ* ) { return 0; } + + // reimplemented from IqHandler. + virtual bool handleIq( const IQ& iq ) { (void)iq; return false; } + + // reimplemented from IqHandler. + virtual void handleIqID( const IQ& iq, int context ); + + private: +#ifdef PUBSUBMANAGER_TEST + public: +#endif + + enum TrackContext + { + Subscription, + Unsubscription, + GetSubscriptionOptions, + SetSubscriptionOptions, + GetSubscriptionList, + GetSubscriberList, + SetSubscriberList, + GetAffiliationList, + GetAffiliateList, + SetAffiliateList, + GetNodeConfig, + SetNodeConfig, + DefaultNodeConfig, + GetItemList, + PublishItem, + DeleteItem, + CreateNode, + DeleteNode, + PurgeNodeItems, + NodeAssociation, + NodeDisassociation, + GetFeatureList, + DiscoServiceInfos, + DiscoNodeInfos, + DiscoNodeItems, + RequestItems, + InvalidContext + }; + + class PubSubOwner : public StanzaExtension + { + public: + /** + * Creates a new PubSubOwner object that can be used to request the given type. + * @param context The requets type. + */ + PubSubOwner( TrackContext context = InvalidContext ); + + /** + * Creates a new PubSubOwner object by parsing the given Tag. + * @param tag The Tag to parse. + */ + PubSubOwner( const Tag* tag ); + + /** + * Virtual destructor. + */ + virtual ~PubSubOwner(); + + /** + * Sets the node to use in e.g. subscription requests. + * @param node The node to use. + */ + void setNode( const std::string& node ) { m_node = node; } + + /** + * Returns the pubsub node. + * @return The pubsub node. + */ + const std::string& node() const { return m_node; } + + /** + * Sets an options DataForm. + * @param options The DataForm. + */ + void setConfig( DataForm* config ) + { m_form = config; } + + /** + * Returns the config DataForm. + * @return The config DataForm. + */ + const DataForm* config() const { return m_form; } + + /** + * Sets the subscriber list. + * @param subList The subscriber list. + */ + void setSubscriberList( const SubscriberList& subList ) + { m_subList = subList; } + + /** + * Sets the affiliate list. + * @param affList The affiliate list. + */ + void setAffiliateList( const AffiliateList& affList ) + { m_affList = affList; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new PubSubOwner( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + PubSubOwner* p = new PubSubOwner(); + p->m_node = m_node; + p->m_ctx = m_ctx; + p->m_form = m_form ? new DataForm( *m_form ) : 0; + p->m_subList = m_subList; + p->m_affList = m_affList; + return p; + } + + private: + std::string m_node; + TrackContext m_ctx; + DataForm* m_form; + SubscriberList m_subList; + AffiliateList m_affList; + }; + + class PubSub : public StanzaExtension + { + public: + /** + * Creates a new PubSub object that can be used to request the given type. + * @param context The requets type. + */ + PubSub( TrackContext context = InvalidContext ); + + /** + * Creates a new PubSub object by parsing the given Tag. + * @param tag The Tag to parse. + */ + PubSub( const Tag* tag ); + + /** + * Virtual destructor. + */ + virtual ~PubSub(); + + /** + * Sets the JID to use in e.g. subscription requests. + * @param jid The JID to use. + */ + void setJID( const JID& jid ) { m_jid = jid; } + + /** + * Returns the pubsub JID (not the service JID). + * @return The pubsub JID. + */ + const JID& jid() const { return m_jid; } + + /** + * Sets the node to use in e.g. subscription requests. + * @param node The node to use. + */ + void setNode( const std::string& node ) { m_node = node; } + + /** + * Returns the pubsub node. + * @return The pubsub node. + */ + const std::string& node() const { return m_node; } + + /** + * Sets the Subscription ID to use. + * @param subid The Subscription ID to use. + */ + void setSubscriptionID( const std::string& subid ) + { m_subid = subid; } + + /** + * Gets the Subscription ID to use. + * @return The Subscription ID to use. + */ + const std::string& subscriptionID() const { return m_subid; } + + /** + * Sets the subscription options. + * @param node The node to set the options for. + * @param df The DataForm holding the subscription options. + * Will be owned and deleted by the PubSub object + */ + void setOptions( const std::string& node, DataForm* df ) + { + m_options.node = node; + m_options.df = df; + } + + /** + * Returns the subscription options. + * @return The subscription options. + */ + const DataForm* options() const + { return m_options.df; } + + /** + * Returns the current Items. + * @return The current items. + */ + const ItemList& items() const { return m_items; } + + /** + * Sets the subscription IDs. + * @param ids Subscription IDs. + */ + void setItems( const ItemList& items ) + { m_items = items; } + + /** + * Sets the maximum number of items to request. + * @param maxItems The maximum number of items to request. + */ + void setMaxItems( int maxItems ) + { m_maxItems = maxItems; } + + /** + * Returns the subscriptions. + * @param The subscriptions. + */ + const SubscriptionMap& subscriptions() const + { return m_subscriptionMap; } + + /** + * Returns the affiliations. + * @param The affiliations. + */ + const AffiliationMap& affiliations() const + { return m_affiliationMap; } + + /** + * Sets whether or not a notify element should be included in a 'retract'. + * @param notify Indicates whether a notify attribute is included. + */ + void setNotify( bool notify ) { m_notify = notify; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new PubSub( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const; + + private: + AffiliationMap m_affiliationMap; + SubscriptionMap m_subscriptionMap; + TrackContext m_ctx; + + struct Options + { + std::string node; + DataForm* df; + }; + Options m_options; + JID m_jid; + std::string m_node; + std::string m_subid; + ItemList m_items; + int m_maxItems; + bool m_notify; + }; + + /** + * This function sets or requests a node's configuration form + * (depending on arguments). Does the actual work for requestNodeConfig + * and setNodeConfig. + * Requests or changes a node's configuration. + * @param service Service to query. + * @param node Node ID of the node. + * @param config If not NULL, the function will request the node config. + * Otherwise, it will set the config based on the form. + * @param handler ResultHandler responsible to handle the request result. + */ + const std::string nodeConfig( const JID& service, + const std::string& node, + DataForm* config, + ResultHandler* handler ); + + /** + * This function sets or requests a node's subscribers list form + * (depending on arguments). Does the actual work for + * requestSubscriberList and setSubscriberList. + * Requests or changes a node's configuration. + * @param ctx The operation to be performed. + * @param service Service to query. + * @param node Node ID of the node. + * @param config If not NULL, the function will request the node config. + * Otherwise, it will set the config based on the form. + * @param handler ResultHandler responsible to handle the request result. + * @return The ID used in the request. + */ + const std::string subscriberList( TrackContext ctx, + const JID& service, + const std::string& node, + const SubscriberList& config, + ResultHandler* handler ); + + /** + * This function sets or requests a node's affiliates list + * (depending on arguments). Does the actual work for + * requestAffiliateList and setAffiliateList. + * Requests or changes a node's configuration. + * @param ctx The operation to be performed. + * @param service Service to query. + * @param node Node ID of the node. + * @param config If not NULL, the function will request the node config. + * Otherwise, it will set the config based on the form. + * @param handler ResultHandler responsible to handle the request result. + * @return The ID used in the request. + */ + const std::string affiliateList( TrackContext ctx, + const JID& service, + const std::string& node, + const AffiliateList& config, + ResultHandler* handler ); + + const std::string subscriptionOptions( TrackContext context, + const JID& service, + const JID& jid, + const std::string& node, + ResultHandler* handler, + DataForm* df ); + + const std::string getSubscriptionsOrAffiliations( const JID& service, + ResultHandler* handler, + TrackContext context ); + + typedef std::map < std::string, std::string > NodeOperationTrackMap; + typedef std::map < std::string, ResultHandler* > ResultHandlerTrackMap; + + ClientBase* m_parent; + + NodeOperationTrackMap m_nopTrackMap; + ResultHandlerTrackMap m_resultHandlerTrackMap; + + util::Mutex m_trackMapMutex; + + }; + + } + +} + +#endif // PUBSUBMANAGER_H__ diff --git a/libs/libgloox/pubsubresulthandler.h b/libs/libgloox/pubsubresulthandler.h new file mode 100644 index 0000000..174febc --- /dev/null +++ b/libs/libgloox/pubsubresulthandler.h @@ -0,0 +1,391 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#ifndef PUBSUBRESULTHANDLER_H__ +#define PUBSUBRESULTHANDLER_H__ + +#include "error.h" +#include "jid.h" +#include "macros.h" +#include "pubsub.h" +#include "tag.h" + +#include +#include +#include + +namespace gloox +{ + + class Tag; + class Error; + class DataForm; + + namespace PubSub + { + /** + * @brief A virtual interface to receive item related requests results. + * + * Derive from this interface and pass it to item related requests. + * + * As a general rule, methods receive an Error pointer which will be null + * (when the request was successful) or describe the problem. Requests + * asking for information will have their "pointer to information" set to + * null when an error occured (that is they're mutually exclusive). In both + * cases, gloox takes care of deleting these objects. + * + * @author Vincent Thomasset + * @since 1.0 + */ + class GLOOX_API ResultHandler + { + public: + /** + * Virtual destructor. + */ + virtual ~ResultHandler() {} + + /** + * Receives the payload for an item. + * + * @param service Service hosting the queried node. + * @param node ID of the parent node. + * @param entry The complete item Tag (do not delete). + */ + virtual void handleItem( const JID& service, + const std::string& node, + const Tag* entry ) = 0; + + /** + * Receives the list of Items for a node. + * + * @param id The reply IQ's id. + * @param service Service hosting the queried node. + * @param node ID of the queried node (empty for the root node). + * @param itemList List of contained items. + * @param error Describes the error case if the request failed. + * + * @see Manager::requestItems() + */ + virtual void handleItems( const std::string& id, + const JID& service, + const std::string& node, + const ItemList& itemList, + const Error* error = 0 ) = 0; + + /** + * Receives the result for an item publication. + * + * @param id The reply IQ's id. + * @param service Service hosting the queried node. + * @param node ID of the queried node. If empty, the root node has been queried. + * @param itemList List of contained items. + * @param error Describes the error case if the request failed. + * + * @see Manager::publishItem + */ + virtual void handleItemPublication( const std::string& id, + const JID& service, + const std::string& node, + const ItemList& itemList, + const Error* error = 0 ) = 0; + + /** + * Receives the result of an item removal. + * + * @param id The reply IQ's id. + * @param service Service hosting the queried node. + * @param node ID of the queried node. If empty, the root node has been queried. + * @param itemList List of contained items. + * @param error Describes the error case if the request failed. + * + * @see Manager::deleteItem + */ + virtual void handleItemDeletion( const std::string& id, + const JID& service, + const std::string& node, + const ItemList& itemList, + const Error* error = 0 ) = 0; + + /** + * Receives the subscription results. In case a problem occured, the + * Subscription ID and SubscriptionType becomes irrelevant. + * + * @param id The reply IQ's id. + * @param service PubSub service asked for subscription. + * @param node Node asked for subscription. + * @param sid Subscription ID. + * @param jid Subscribed entity. + * @param subType Type of the subscription. + * @param error Subscription Error. + * + * @see Manager::subscribe + */ + virtual void handleSubscriptionResult( const std::string& id, + const JID& service, + const std::string& node, + const std::string& sid, + const JID& jid, + const SubscriptionType subType, + const Error* error = 0 ) = 0; + + /** + * Receives the unsubscription results. In case a problem occured, the + * subscription ID becomes irrelevant. + * + * @param id The reply IQ's id. + * @param service PubSub service. + * @param error Unsubscription Error. + * + * @see Manager::unsubscribe + */ + virtual void handleUnsubscriptionResult( const std::string& id, + const JID& service, + const Error* error = 0 ) = 0; + + /** + * Receives the subscription options for a node. + * + * @param id The reply IQ's id. + * @param service Service hosting the queried node. + * @param jid Subscribed entity. + * @param node ID of the node. + * @param options Options DataForm. + * @param error Subscription options retrieval Error. + * + * @see Manager::getSubscriptionOptions + */ + virtual void handleSubscriptionOptions( const std::string& id, + const JID& service, + const JID& jid, + const std::string& node, + const DataForm* options, + const Error* error = 0 ) = 0; + + /** + * Receives the result for a subscription options modification. + * + * @param id The reply IQ's id. + * @param service Service hosting the queried node. + * @param jid Subscribed entity. + * @param node ID of the queried node. + * @param error Subscription options modification Error. + * + * @see Manager::setSubscriptionOptions + */ + virtual void handleSubscriptionOptionsResult( const std::string& id, + const JID& service, + const JID& jid, + const std::string& node, + const Error* error = 0 ) = 0; + + + /** + * Receives the list of subscribers to a node. + * + * @param id The reply IQ's id. + * @param service Service hosting the node. + * @param node ID of the queried node. + * @param list Subscriber list. + * @param error Subscription options modification Error. + * + * @see Manager::getSubscribers + */ + virtual void handleSubscribers( const std::string& id, + const JID& service, + const std::string& node, + const SubscriberList* list, + const Error* error = 0 ) = 0; + + /** + * Receives the result of a subscriber list modification. + * + * @param id The reply IQ's id. + * @param service Service hosting the node. + * @param node ID of the queried node. + * @param list Subscriber list. + * @param error Subscriber list modification Error. + * + * @see Manager::setSubscribers + */ + virtual void handleSubscribersResult( const std::string& id, + const JID& service, + const std::string& node, + const SubscriberList* list, + const Error* error = 0 ) = 0; + + /** + * Receives the affiliate list for a node. + * + * @param id The reply IQ's id. + * @param service Service hosting the node. + * @param node ID of the queried node. + * @param list Affiliation list. + * @param error Affiliation list retrieval Error. + * + * @see Manager::getAffiliations + */ + virtual void handleAffiliates( const std::string& id, + const JID& service, + const std::string& node, + const AffiliateList* list, + const Error* error = 0 ) = 0; + + /** + * Handle the affiliate list for a specific node. + * + * @param id The reply IQ's id. + * @param service Service hosting the node. + * @param node ID of the node. + * @param list The Affiliate list. + * @param error Affiliation list modification Error. + * + * @see Manager::setAffiliations + */ + virtual void handleAffiliatesResult( const std::string& id, + const JID& service, + const std::string& node, + const AffiliateList* list, + const Error* error = 0 ) = 0; + + + /** + * Receives the configuration for a specific node. + * + * @param id The reply IQ's id. + * @param service Service hosting the node. + * @param node ID of the node. + * @param config Configuration DataForm. + * @param error Configuration retrieval Error. + * + * @see Manager::getNodeConfig + */ + virtual void handleNodeConfig( const std::string& id, + const JID& service, + const std::string& node, + const DataForm* config, + const Error* error = 0 ) = 0; + + /** + * Receives the result of a node's configuration modification. + * + * @param id The reply IQ's id. + * @param service Service hosting the node. + * @param node ID of the node. + * @param error Configuration modification Error. + * + * @see Manager::setNodeConfig + */ + virtual void handleNodeConfigResult( const std::string& id, + const JID& service, + const std::string& node, + const Error* error = 0 ) = 0; + + /** + * Receives the result of a node creation. + * + * @param id The reply IQ's id. + * @param service Service hosting the node. + * @param node ID of the node. + * @param error Node creation Error. + * + * @see Manager::setNodeConfig + */ + virtual void handleNodeCreation( const std::string& id, + const JID& service, + const std::string& node, + const Error* error = 0 ) = 0; + + /** + * Receives the result for a node removal. + * + * @param id The reply IQ's id. + * @param service Service hosting the node. + * @param node ID of the node. + * @param error Node removal Error. + * + * @see Manager::deleteNode + */ + virtual void handleNodeDeletion( const std::string& id, + const JID& service, + const std::string& node, + const Error* error = 0 ) = 0; + + + /** + * Receives the result of a node purge request. + * + * @param id The reply IQ's id. + * @param service Service hosting the node. + * @param node ID of the node. + * @param error Node purge Error. + * + * @see Manager::purgeNode + */ + virtual void handleNodePurge( const std::string& id, + const JID& service, + const std::string& node, + const Error* error = 0 ) = 0; + + /** + * Receives the Subscription list for a specific service. + * + * @param id The reply IQ's id. + * @param service The queried service. + * @param subMap The map of node's subscription. + * @param error Subscription list retrieval Error. + * + * @see Manager::getSubscriptions + */ + virtual void handleSubscriptions( const std::string& id, + const JID& service, + const SubscriptionMap& subMap, + const Error* error = 0) = 0; + + /** + * Receives the Affiliation map for a specific service. + * + * @param id The reply IQ's id. + * @param service The queried service. + * @param affMap The map of node's affiliation. + * @param error Affiliation list retrieval Error. + * + * @see Manager::getAffiliations + */ + virtual void handleAffiliations( const std::string& id, + const JID& service, + const AffiliationMap& affMap, + const Error* error = 0 ) = 0; + + /** + * Receives the default configuration for a specific node type. + * + * @param id The reply IQ's id. + * @param service The queried service. + * @param config Configuration form for the node type. + * @param error Default node config retrieval Error. + * + * @see Manager::getDefaultNodeConfig + */ + virtual void handleDefaultNodeConfig( const std::string& id, + const JID& service, + const DataForm* config, + const Error* error = 0 ) = 0; + + }; + + } + +} + +#endif // PUBSUBRESULTHANDLER_H__ + diff --git a/libs/libgloox/receipt.cpp b/libs/libgloox/receipt.cpp new file mode 100644 index 0000000..99cfba7 --- /dev/null +++ b/libs/libgloox/receipt.cpp @@ -0,0 +1,53 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#include "receipt.h" +#include "tag.h" +#include "util.h" + +namespace gloox +{ + + /* chat state type values */ + static const char* receiptValues [] = { + "request", + "received" + }; + + static inline Receipt::ReceiptType receiptType( const std::string& type ) + { + return (Receipt::ReceiptType)util::lookup( type, receiptValues ); + } + + Receipt::Receipt( const Tag* tag ) + : StanzaExtension( ExtReceipt ), + m_rcpt( receiptType( tag->name() ) ) + { + } + + const std::string& Receipt::filterString() const + { + static const std::string filter = + "/message/request[@xmlns='" + XMLNS_RECEIPTS + "']" + "|/message/received[@xmlns='" + XMLNS_RECEIPTS + "']"; + return filter; + } + + Tag* Receipt::tag() const + { + if( m_rcpt == Invalid ) + return 0; + + return new Tag( util::lookup( m_rcpt, receiptValues ), XMLNS, XMLNS_RECEIPTS ); + } + +} diff --git a/libs/libgloox/receipt.h b/libs/libgloox/receipt.h new file mode 100644 index 0000000..21141d5 --- /dev/null +++ b/libs/libgloox/receipt.h @@ -0,0 +1,95 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#ifndef RECEIPT_H__ +#define RECEIPT_H__ + +#include "gloox.h" +#include "stanzaextension.h" + +#include + +namespace gloox +{ + + class Tag; + + /** + * @brief An implementation of Message Receipts (XEP-0184) as a StanzaExtension. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API Receipt : public StanzaExtension + { + public: + /** + * Contains valid receipt types (XEP-0184). + */ + enum ReceiptType + { + Request, /**< Requests a receipt. */ + Received, /**< The receipt. */ + Invalid /**< Invalid type. */ + }; + + /** + * Constructs a new object from the given Tag. + * @param tag A Tag to parse. + */ + Receipt( const Tag* tag ); + + /** + * Constructs a new object of the given type. + * @param rcpt The receipt type. + */ + Receipt( ReceiptType rcpt ) + : StanzaExtension( ExtReceipt ), m_rcpt( rcpt ) + {} + + /** + * Virtual destructor. + */ + virtual ~Receipt() {} + + /** + * Returns the object's state. + * @return The object's state. + */ + ReceiptType rcpt() const { return m_rcpt; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new Receipt( tag ); + } + + // reimplemented from StanzaExtension + Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new Receipt( *this ); + } + + private: + ReceiptType m_rcpt; + + }; + +} + +#endif // RECEIPT_H__ diff --git a/libs/libgloox/registration.cpp b/libs/libgloox/registration.cpp new file mode 100644 index 0000000..c5f900a --- /dev/null +++ b/libs/libgloox/registration.cpp @@ -0,0 +1,391 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "registration.h" + +#include "clientbase.h" +#include "stanza.h" +#include "error.h" +#include "prep.h" +#include "oob.h" + +namespace gloox +{ + + // Registration::Query ---- + Registration::Query::Query( DataForm* form ) + : StanzaExtension( ExtRegistration ), m_form( form ), m_fields( 0 ), m_oob( 0 ), + m_del( false ), m_reg( false ) + { + } + + Registration::Query::Query( bool del ) + : StanzaExtension( ExtRegistration ), m_form( 0 ), m_fields( 0 ), m_oob( 0 ), m_del( del ), + m_reg( false ) + { + } + + Registration::Query::Query( int fields, const RegistrationFields& values ) + : StanzaExtension( ExtRegistration ), m_form( 0 ), m_fields( fields ), m_values( values ), + m_oob( 0 ), m_del( false ), m_reg( false ) + { + } + + Registration::Query::Query( const Tag* tag ) + : StanzaExtension( ExtRegistration ), m_form( 0 ), m_fields( 0 ), m_oob( 0 ), m_del( false ), + m_reg( false ) + { + if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_REGISTER ) + return; + + const TagList& l = tag->children(); + TagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + { + const std::string& name = (*it)->name(); + if( name == "instructions" ) + m_instructions = (*it)->cdata(); + else if( name == "remove" ) + m_del = true; + else if( name == "registered" ) + m_reg = true; + else if( name == "username" ) + { + m_fields |= FieldUsername; + m_values.username = (*it)->cdata(); + } + else if( name == "nick" ) + { + m_fields |= FieldNick; + m_values.nick = (*it)->cdata(); + } + else if( name == "password" ) + { + m_fields |= FieldPassword; + m_values.password = (*it)->cdata(); + } + else if( name == "name" ) + { + m_fields |= FieldName; + m_values.name = (*it)->cdata(); + } + else if( name == "first" ) + { + m_fields |= FieldFirst; + m_values.first = (*it)->cdata(); + } + else if( name == "last" ) + { + m_fields |= FieldLast; + m_values.last = (*it)->cdata(); + } + else if( name == "email" ) + { + m_fields |= FieldEmail; + m_values.email = (*it)->cdata(); + } + else if( name == "address" ) + { + m_fields |= FieldAddress; + m_values.address = (*it)->cdata(); + } + else if( name == "city" ) + { + m_fields |= FieldCity; + m_values.city = (*it)->cdata(); + } + else if( name == "state" ) + { + m_fields |= FieldState; + m_values.state = (*it)->cdata(); + } + else if( name == "zip" ) + { + m_fields |= FieldZip; + m_values.zip = (*it)->cdata(); + } + else if( name == "phone" ) + { + m_fields |= FieldPhone; + m_values.phone = (*it)->cdata(); + } + else if( name == "url" ) + { + m_fields |= FieldUrl; + m_values.url = (*it)->cdata(); + } + else if( name == "date" ) + { + m_fields |= FieldDate; + m_values.date = (*it)->cdata(); + } + else if( name == "misc" ) + { + m_fields |= FieldMisc; + m_values.misc = (*it)->cdata(); + } + else if( name == "text" ) + { + m_fields |= FieldText; + m_values.text = (*it)->cdata(); + } + else if( !m_form && name == "x" && (*it)->xmlns() == XMLNS_X_DATA ) + m_form = new DataForm( (*it) ); + else if( !m_oob && name == "x" && (*it)->xmlns() == XMLNS_X_OOB ) + m_oob = new OOB( (*it) ); + } + } + + Registration::Query::~Query() + { + delete m_form; + delete m_oob; + } + + const std::string& Registration::Query::filterString() const + { + static const std::string filter = "/iq/query[@xmlns='" + XMLNS_REGISTER + "']"; + return filter; + } + + Tag* Registration::Query::tag() const + { + Tag* t = new Tag( "query" ); + t->setXmlns( XMLNS_REGISTER ); + + if( !m_instructions.empty() ) + new Tag( t, "instructions", m_instructions ); + + if ( m_reg ) + new Tag( t, "registered" ); + + if( m_form ) + t->addChild( m_form->tag() ); + else if( m_oob ) + t->addChild( m_oob->tag() ); + else if( m_del ) + new Tag( t, "remove" ); + else if( m_fields ) + { + if( m_fields & FieldUsername ) + new Tag( t, "username", m_values.username ); + if( m_fields & FieldNick ) + new Tag( t, "nick", m_values.nick ); + if( m_fields & FieldPassword ) + new Tag( t, "password", m_values.password ); + if( m_fields & FieldName ) + new Tag( t, "name", m_values.name ); + if( m_fields & FieldFirst ) + new Tag( t, "first", m_values.first ); + if( m_fields & FieldLast ) + new Tag( t, "last", m_values.last ); + if( m_fields & FieldEmail ) + new Tag( t, "email", m_values.email ); + if( m_fields & FieldAddress ) + new Tag( t, "address", m_values.address ); + if( m_fields & FieldCity ) + new Tag( t, "city", m_values.city ); + if( m_fields & FieldState ) + new Tag( t, "state", m_values.state ); + if( m_fields & FieldZip ) + new Tag( t, "zip", m_values.zip ); + if( m_fields & FieldPhone ) + new Tag( t, "phone", m_values.phone ); + if( m_fields & FieldUrl ) + new Tag( t, "url", m_values.url ); + if( m_fields & FieldDate ) + new Tag( t, "date", m_values.date ); + if( m_fields & FieldMisc ) + new Tag( t, "misc", m_values.misc ); + if( m_fields & FieldText ) + new Tag( t, "text", m_values.text ); + } + + return t; + } + // ---- ~Registration::Query ---- + + // ---- Registration ---- + Registration::Registration( ClientBase* parent, const JID& to ) + : m_parent( parent ), m_to( to ), m_registrationHandler( 0 ) + { + init(); + } + + Registration::Registration( ClientBase* parent ) + : m_parent( parent ), m_registrationHandler( 0 ) + { + init(); + } + + void Registration::init() + { + if( m_parent ) + { + m_parent->registerIqHandler( this, ExtRegistration ); + m_parent->registerStanzaExtension( new Query() ); + } + } + + Registration::~Registration() + { + if( m_parent ) + { + m_parent->removeIqHandler( this, ExtRegistration ); + m_parent->removeIDHandler( this ); + m_parent->removeStanzaExtension( ExtRegistration ); + } + } + + void Registration::fetchRegistrationFields() + { + if( !m_parent || m_parent->state() != StateConnected ) + return; + + IQ iq( IQ::Get, m_to ); + iq.addExtension( new Query() ); + m_parent->send( iq, this, FetchRegistrationFields ); + } + + bool Registration::createAccount( int fields, const RegistrationFields& values ) + { + std::string username; + if( !m_parent || !prep::nodeprep( values.username, username ) ) + return false; + + IQ iq( IQ::Set, m_to ); + iq.addExtension( new Query( fields, values ) ); + m_parent->send( iq, this, CreateAccount ); + + return true; + } + + void Registration::createAccount( DataForm* form ) + { + if( !m_parent || !form ) + return; + + IQ iq( IQ::Set, m_to ); + iq.addExtension( new Query( form ) ); + m_parent->send( iq, this, CreateAccount ); + } + + void Registration::removeAccount() + { + if( !m_parent || !m_parent->authed() ) + return; + + IQ iq( IQ::Set, m_to ); + iq.addExtension( new Query( true ) ); + m_parent->send( iq, this, RemoveAccount ); + } + + void Registration::changePassword( const std::string& username, const std::string& password ) + { + if( !m_parent || !m_parent->authed() || username.empty() ) + return; + + int fields = FieldUsername | FieldPassword; + RegistrationFields rf; + rf.username = username; + rf.password = password; + createAccount( fields, rf ); + } + + void Registration::registerRegistrationHandler( RegistrationHandler* rh ) + { + m_registrationHandler = rh; + } + + void Registration::removeRegistrationHandler() + { + m_registrationHandler = 0; + } + + void Registration::handleIqID( const IQ& iq, int context ) + { + if( !m_registrationHandler ) + return; + + if( iq.subtype() == IQ::Result ) + { + switch( context ) + { + case FetchRegistrationFields: + { + const Query* q = iq.findExtension( ExtRegistration ); + if( !q ) + return; + + if( q->registered() ) + m_registrationHandler->handleAlreadyRegistered( iq.from() ); + + if( q->form() ) + m_registrationHandler->handleDataForm( iq.from(), *(q->form()) ); + + if( q->oob() ) + m_registrationHandler->handleOOB( iq.from(), *(q->oob()) ); + + m_registrationHandler->handleRegistrationFields( iq.from(), q->fields(), q->instructions() ); + break; + } + + case CreateAccount: + case ChangePassword: + case RemoveAccount: + m_registrationHandler->handleRegistrationResult( iq.from(), RegistrationSuccess ); + break; + } + } + else if( iq.subtype() == IQ::Error ) + { + const Error* e = iq.error(); + if( !e ) + return; + + switch( e->error() ) + { + case StanzaErrorConflict: + m_registrationHandler->handleRegistrationResult( iq.from(), RegistrationConflict ); + break; + case StanzaErrorNotAcceptable: + m_registrationHandler->handleRegistrationResult( iq.from(), RegistrationNotAcceptable ); + break; + case StanzaErrorBadRequest: + m_registrationHandler->handleRegistrationResult( iq.from(), RegistrationBadRequest ); + break; + case StanzaErrorForbidden: + m_registrationHandler->handleRegistrationResult( iq.from(), RegistrationForbidden ); + break; + case StanzaErrorRegistrationRequired: + m_registrationHandler->handleRegistrationResult( iq.from(), RegistrationRequired ); + break; + case StanzaErrorUnexpectedRequest: + m_registrationHandler->handleRegistrationResult( iq.from(), RegistrationUnexpectedRequest ); + break; + case StanzaErrorNotAuthorized: + m_registrationHandler->handleRegistrationResult( iq.from(), RegistrationNotAuthorized ); + break; + case StanzaErrorNotAllowed: + m_registrationHandler->handleRegistrationResult( iq.from(), RegistrationNotAllowed ); + break; + default: + m_registrationHandler->handleRegistrationResult( iq.from(), RegistrationUnknownError ); + break; + + } + } + + } + +} diff --git a/libs/libgloox/registration.h b/libs/libgloox/registration.h new file mode 100644 index 0000000..3fef509 --- /dev/null +++ b/libs/libgloox/registration.h @@ -0,0 +1,339 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef REGISTRATION_H__ +#define REGISTRATION_H__ + +#include "iqhandler.h" +#include "registrationhandler.h" +#include "dataform.h" +#include "jid.h" +#include "oob.h" + +#include +#include + +namespace gloox +{ + + class ClientBase; + class Stanza; + + /** + * Holds all the possible fields a server may require for registration according + * to Section 14.1, XEP-0077. + */ + struct RegistrationFields + { + std::string username; /**< Desired username. */ + std::string nick; /**< User's nickname. */ + std::string password; /**< User's password. */ + std::string name; /**< User's name. */ + std::string first; /**< User's first name.*/ + std::string last; /**< User's last name. */ + std::string email; /**< User's email address. */ + std::string address; /**< User's address. */ + std::string city; /**< User's city. */ + std::string state; /**< User's state. */ + std::string zip; /**< User's ZIP code. */ + std::string phone; /**< User's phone number. */ + std::string url; /**< User's homepage URL (or other URL). */ + std::string date; /**< Date (?) */ + std::string misc; /**< Misc (?) */ + std::string text; /**< Text (?)*/ + }; + + /** + * @brief This class is an implementation of XEP-0077 (In-Band Registration). + * + * Derive your object from @ref RegistrationHandler and implement the + * virtual functions offered by that interface. Then use it like this: + * @code + * void MyClass::myFunc() + * { + * m_client = new Client( "example.org" ); + * m_client->disableRoster(); // a roster is not necessary for registration + * m_client->registerConnectionListener( this ); + * + * m_reg = new Registration( c ); + * m_reg->registerRegistrationHandler( this ); + * + * m_client->connect(); + * } + * + * void MyClass::onConnect() + * { + * m_reg->fetchRegistrationFields(); + * } + * @endcode + * + * In RegistrationHandler::handleRegistrationFields() you should check which information the server + * requires to open a new account. You might not always get away with just username and password. + * Then call createAccount() with a filled-in RegistrationFields and an @c int representing the bit-wise + * ORed fields you want to have included in the registration attempt. For your convenience you can + * use the 'fields' argument of handleRegistrationFields(). ;) It's your responsibility to make + * sure at least those fields the server requested are filled in. + * + * Check @c tests/register_test.cpp for an example. + * + * @author Jakob Schroeter + * @since 0.2 + */ + class GLOOX_API Registration : public IqHandler + { + public: + + /** + * The possible fields of a XEP-0077 account registration. + */ + enum fieldEnum + { + FieldUsername = 1, + FieldNick = 2, + FieldPassword = 4, + FieldName = 8, + FieldFirst = 16, + FieldLast = 32, + FieldEmail = 64, + FieldAddress = 128, + FieldCity = 256, + FieldState = 512, + FieldZip = 1024, + FieldPhone = 2048, + FieldUrl = 4096, + FieldDate = 8192, + FieldMisc = 16384, + FieldText = 32768 + }; + + /** + * @brief A wrapping class for the XEP-0077 <query> element. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class Query : public StanzaExtension + { + public: + /** + * Creates a new object that can be used to carry out a registration. + * @param form A DataForm containing the registration terms. + */ + Query( DataForm* form ); + + /** + * Creates a new object that can be used to carry out a registration. + * @param del Whether or not to remove the account. + */ + Query( bool del = false ); + + /** + * Creates a new object that can be used to carry out a registration. + * @param fields Bit-wise ORed fieldEnum values describing the valid (i.e., set) + * fields in the @b values parameter. + * @param values Contains the registration fields. + */ + Query( int fields, const RegistrationFields& values ); + + /** + * Creates a new object from the given Tag. + * @param tag The Tag to parse. + */ + Query( const Tag* tag ); + + /** + * Virtual Destructor. + */ + virtual ~Query(); + + /** + * Returns the contained registration form, if any. + * @return The registration form. May be 0. + */ + const DataForm* form() const { return m_form; } + + /** + * Returns the registration instructions, if given + * @return The registration instructions. + */ + const std::string& instructions() const { return m_instructions; } + + /** + * Returns the registration fields, if set. + * @return The registration fields. + */ + int fields() const { return m_fields; } + + /** + * + */ + const RegistrationFields& values() const { return m_values; } + + /** + * Indicates whether the account is already registered. + * @return @b True if the <registered> element is present, @b false otherwise. + */ + bool registered() const { return m_reg; } + + /** + * Indicates whether the account shall be removed. + * @return @b True if the <remove> element is present, @b false otherwise. + */ + bool remove() const { return m_del; } + + /** + * Returns an optional OOB object. + * @return A pointer to an OOB object, if present, 0 otherwise. + */ + const OOB* oob() const { return m_oob; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new Query( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + Query* q = new Query(); + q->m_form = m_form ? new DataForm( *m_form ) : 0; + q->m_fields = m_fields; + q->m_values = m_values; + q->m_instructions = m_instructions; + q->m_oob = new OOB( *m_oob ); + q->m_del = m_del; + q->m_reg = m_reg; + return q; + } + + private: + DataForm* m_form; + int m_fields; + RegistrationFields m_values; + std::string m_instructions; + OOB* m_oob; + bool m_del; + bool m_reg; + }; + + /** + * Constructor. + * @param parent The ClientBase which is used for establishing a connection. + * @param to The server or service to authenticate with. If empty the currently connected + * server will be used. + */ + Registration( ClientBase* parent, const JID& to ); + + /** + * Constructor. Registration will be attempted with the ClientBase's connected host. + * @param parent The ClientBase which is used for establishing a connection. + */ + Registration( ClientBase* parent ); + + /** + * Virtual destructor. + */ + virtual ~Registration(); + + /** + * Use this function to request the registration fields the server requires. + * The required fields are returned asynchronously to the object registered as + * @ref RegistrationHandler by calling @ref RegistrationHandler::handleRegistrationFields(). + */ + void fetchRegistrationFields(); + + /** + * Attempts to register an account with the given credentials. Only the fields OR'ed in + * @c fields will be sent. This can only be called with an unauthenticated parent (@ref Client). + * @note It is recommended to use @ref fetchRegistrationFields to find out which fields the + * server requires. + * @param fields The fields to use to generate the registration request. OR'ed + * @ref fieldEnum values. + * @param values The struct contains the values which shall be used for the registration. + * @return Returns @b true if the registration request was sent successfully, @b false + * otherwise. In that case either there's no connected ClientBase available, or + * prepping of the username failed (i.e. the username is not valid for use in XMPP). + */ + bool createAccount( int fields, const RegistrationFields& values ); + + /** + * Attempts to register an account with the given credentials. This can only be called with an + * unauthenticated parent (@ref Client). + * @note According to XEP-0077, if the server sends both old-style fields and data form, + * implementations SHOULD prefer data forms. + * @param form The DataForm containing the registration credentials. + */ + void createAccount( DataForm* form ); + + /** + * Tells the server to remove the currently authenticated account from the server. + */ + void removeAccount(); + + /** + * Tells the server to change the password for the current account. + * @param username The username to change the password for. You might want to use + * Client::username() to get the current prepped username. + * @param password The new password. + */ + void changePassword( const std::string& username, const std::string& password ); + + /** + * Registers the given @c rh as RegistrationHandler. Only one handler is possible at a time. + * @param rh The RegistrationHandler to register. + */ + void registerRegistrationHandler( RegistrationHandler* rh ); + + /** + * Un-registers the current RegistrationHandler. + */ + void removeRegistrationHandler(); + + // reimplemented from IqHandler. + virtual bool handleIq( const IQ& iq ) { (void)iq; return false; } + + // reimplemented from IqHandler. + virtual void handleIqID( const IQ& iq, int context ); + + private: +#ifdef REGISTRATION_TEST + public: +#endif + + enum IdType + { + FetchRegistrationFields, + CreateAccount, + RemoveAccount, + ChangePassword + }; + + Registration operator=( const Registration& ); + + void init(); + + ClientBase* m_parent; + const JID m_to; + RegistrationHandler* m_registrationHandler; + }; + +} + +#endif // REGISTRATION_H__ diff --git a/libs/libgloox/registrationhandler.h b/libs/libgloox/registrationhandler.h new file mode 100644 index 0000000..f7422ad --- /dev/null +++ b/libs/libgloox/registrationhandler.h @@ -0,0 +1,128 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef REGISTRATIONHANDLER_H__ +#define REGISTRATIONHANDLER_H__ + +#include "oob.h" + +#include + +namespace gloox +{ + + class OOB; + class JID; + class DataForm; + + /** + * Possible results of a XEP-0077 operation. + */ + enum RegistrationResult + { + RegistrationSuccess = 0, /**< The last operation (account registration, account + * deletion or password change) was successful. */ + RegistrationNotAcceptable, /**< 406: Not all necessary information provided */ + RegistrationConflict, /**< 409: Username alreday exists. */ + RegistrationNotAuthorized, /**< Account removal: Unregistered entity waits too long + * before authentication or performs tasks other than + * authentication after registration.
+ * Password change: The server or service does not consider + * the channel safe enough to enable a password change. */ + RegistrationBadRequest, /**< Account removal: The <remove/> element was not + * the only child element of the <query/> element. + * Should not happen when only gloox functions are being + * used.
+ * Password change: The password change request does not + * contain complete information (both <username/> and + * <password/> are required). */ + RegistrationForbidden, /**< Account removal: The sender does not have sufficient + * permissions to cancel the registration. */ + RegistrationRequired, /**< Account removal: The entity sending the remove + * request was not previously registered. */ + RegistrationUnexpectedRequest, /**< Account removal: The host is an instant messaging + * server and the IQ get does not contain a 'from' + * address because the entity is not registered with + * the server.
+ * Password change: The host is an instant messaging + * server and the IQ set does not contain a 'from' + * address because the entity is not registered with + * the server. */ + RegistrationNotAllowed, /**< Password change: The server or service does not allow + * password changes. */ + RegistrationUnknownError /**< An unknown error condition occured. */ + }; + + /** + * @brief A virtual interface that receives events from an Registration object. + * + * Derived classes can be registered as RegistrationHandlers with an + * Registration object. Incoming results for operations initiated through + * the Registration object are forwarded to this handler. + * + * @author Jakob Schroeter + * @since 0.2 + */ + class GLOOX_API RegistrationHandler + { + public: + /** + * Virtual Destructor. + */ + virtual ~RegistrationHandler() {} + + /** + * Reimplement this function to receive results of the + * @ref Registration::fetchRegistrationFields() function. + * @param from The server or service the registration fields came from. + * @param fields The OR'ed fields the server requires. From @ref Registration::fieldEnum. + * @param instructions Any additional information the server sends along. + */ + virtual void handleRegistrationFields( const JID& from, int fields, + std::string instructions ) = 0; + + /** + * This function is called if @ref Registration::createAccount() was called on an authenticated + * stream and the server lets us know about this. + */ + virtual void handleAlreadyRegistered( const JID& from ) = 0; + + /** + * This funtion is called to notify about the result of an operation. + * @param from The server or service the result came from. + * @param regResult The result of the last operation. + */ + virtual void handleRegistrationResult( const JID& from, RegistrationResult regResult ) = 0; + + /** + * This function is called additionally to @ref handleRegistrationFields() if the server + * supplied a data form together with legacy registration fields. + * @param from The server or service the data form came from. + * @param form The DataForm containing registration information. + */ + virtual void handleDataForm( const JID& from, const DataForm& form ) = 0; + + /** + * This function is called if the server does not offer in-band registration + * but wants to refer the user to an external URL. + * @param from The server or service the referal came from. + * @param oob The OOB object describing the external URL. + */ + virtual void handleOOB( const JID& from, const OOB& oob ) = 0; + + }; + +} + +#endif // REGISTRATIONHANDLER_H__ diff --git a/libs/libgloox/resource.h b/libs/libgloox/resource.h new file mode 100644 index 0000000..c209d25 --- /dev/null +++ b/libs/libgloox/resource.h @@ -0,0 +1,106 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef RESOURCE_H__ +#define RESOURCE_H__ + +#include "presence.h" +#include "util.h" + +#include + +namespace gloox +{ + + class Presence; + + /** + * @brief Holds resource attributes. + * + * This holds the information of a single resource of a contact that is online. + * + * @author Jakob Schroeter + * @since 0.8 + */ + class GLOOX_API Resource + { + + friend class RosterItem; + + public: + /** + * Constructor. + * @param priority The resource's priority. + * @param msg The resource's status message. + * @param presence The resource's presence status. + */ + Resource( int priority, const std::string& msg, Presence::PresenceType presence ) + : m_priority( priority ), m_message( msg ), m_presence( presence ) {} + + /** + * Virtual destrcutor. + */ + virtual ~Resource() + { + util::clearList( m_extensions ); + } + + /** + * Lets you fetch the resource's priority. + * @return The resource's priority. + */ + int priority() const { return m_priority; } + + /** + * Lets you fetch the resource's status message. + * @return The resource's status message. + */ + const std::string& message() const { return m_message; } + + /** + * Lets you fetch the resource's last presence. + * @return The resource's presence status. + */ + Presence::PresenceType presence() const { return m_presence; } + + /** + * Returns the StanzaExtensions that were sent with the last presence stanza + * by the resource. + * @return A list of stanza extensions. + */ + const StanzaExtensionList& extensions() const { return m_extensions; } + + private: + void setPriority( int priority ) { m_priority = priority; } + void setMessage( std::string message ) { m_message = message; } + void setStatus( Presence::PresenceType presence ) { m_presence = presence; } + void setExtensions( const StanzaExtensionList& exts ) + { + StanzaExtensionList::const_iterator it = exts.begin(); + for( ; it != exts.end(); ++it ) + { + m_extensions.push_back( (*it)->clone() ); + } + } + + int m_priority; + std::string m_message; + std::string m_name; + Presence::PresenceType m_presence; + StanzaExtensionList m_extensions; + + }; + +} + +#endif // RESOURCE_H__ diff --git a/libs/libgloox/rosteritem.cpp b/libs/libgloox/rosteritem.cpp new file mode 100644 index 0000000..bfd0e8a --- /dev/null +++ b/libs/libgloox/rosteritem.cpp @@ -0,0 +1,177 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "rosteritem.h" +#include "rosteritemdata.h" +#include "util.h" + +namespace gloox +{ + + RosterItem::RosterItem( const std::string& jid, const std::string& name ) + : m_data( new RosterItemData( jid, name, StringList() ) ) + { + } + + RosterItem::RosterItem( const RosterItemData& data ) + : m_data( new RosterItemData( data ) ) + { + } + + RosterItem::~RosterItem() + { + delete m_data; + util::clearMap( m_resources ); + } + + void RosterItem::setName( const std::string& name ) + { + if( m_data ) + m_data->setName( name ); + } + + const std::string& RosterItem::name() const + { + if( m_data ) + return m_data->name(); + else + return EmptyString; + } + + const std::string& RosterItem::jid() const + { + if( m_data ) + return m_data->jid(); + else + return EmptyString; + } + + void RosterItem::setSubscription( const std::string& subscription, const std::string& ask ) + { + if( m_data ) + m_data->setSubscription( subscription, ask ); + } + + SubscriptionType RosterItem::subscription() const + { + if( m_data ) + return m_data->subscription(); + else + return S10nNone; + } + + void RosterItem::setGroups( const StringList& groups ) + { + if( m_data ) + m_data->setGroups( groups ); + } + + const StringList RosterItem::groups() const + { + if( m_data ) + return m_data->groups(); + else + return StringList(); + } + + bool RosterItem::changed() const + { + if( m_data ) + return m_data->changed(); + else + return false; + } + + void RosterItem::setSynchronized() + { + if( m_data ) + m_data->setSynchronized(); + } + + void RosterItem::setPresence( const std::string& resource, Presence::PresenceType presence ) + { + if( m_resources.find( resource ) == m_resources.end() ) + m_resources[resource] = new Resource( 0, EmptyString, presence ); + else + m_resources[resource]->setStatus( presence ); + } + + void RosterItem::setStatus( const std::string& resource, const std::string& msg ) + { + if( m_resources.find( resource ) == m_resources.end() ) + m_resources[resource] = new Resource( 0, msg, Presence::Unavailable ); + else + m_resources[resource]->setMessage( msg ); + } + + void RosterItem::setPriority( const std::string& resource, int priority ) + { + if( m_resources.find( resource ) == m_resources.end() ) + m_resources[resource] = new Resource( priority, EmptyString, Presence::Unavailable ); + else + m_resources[resource]->setPriority( priority ); + } + + const Resource* RosterItem::highestResource() const + { + int highestPriority = -255; + Resource* highestResource = 0; + ResourceMap::const_iterator it = m_resources.begin(); + for( ; it != m_resources.end() ; ++it ) + { + if( (*it).second->priority() > highestPriority ) + { + highestPriority = (*it).second->priority(); + highestResource = (*it).second; + } + } + return highestResource; + } + + void RosterItem::setExtensions( const std::string& resource, const StanzaExtensionList& exts ) + { + if( m_resources.find( resource ) == m_resources.end() ) + m_resources[resource] = new Resource( 0, EmptyString, Presence::Unavailable ); + + m_resources[resource]->setExtensions( exts ); + } + + void RosterItem::removeResource( const std::string& resource ) + { + ResourceMap::iterator it = m_resources.find( resource ); + if( it != m_resources.end() ) + { + delete (*it).second; + m_resources.erase( it ); + } + } + + bool RosterItem::online() const + { + return !m_resources.empty(); + } + + const Resource* RosterItem::resource( const std::string& res ) const + { + ResourceMap::const_iterator it = m_resources.find( res ); + return it != m_resources.end() ? (*it).second : 0; + } + + void RosterItem::setData( const RosterItemData& rid ) + { + delete m_data; + m_data = new RosterItemData( rid ); + } + +} diff --git a/libs/libgloox/rosteritem.h b/libs/libgloox/rosteritem.h new file mode 100644 index 0000000..27597c3 --- /dev/null +++ b/libs/libgloox/rosteritem.h @@ -0,0 +1,196 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef ROSTERITEM_H__ +#define ROSTERITEM_H__ + +#include "gloox.h" +#include "resource.h" +#include "presence.h" + +#include +#include + + +namespace gloox +{ + + class RosterItemData; + + /** + * @brief An abstraction of a roster item. + * + * For each RosterItem all resources that are available (online in some way) are stored in + * a ResourceMap. This map is accessible using the resources() function. + * + * @author Jakob Schroeter + * @since 0.3 + */ + class GLOOX_API RosterItem + { + friend class RosterManager; + + public: + /** + * A list of resources for the given JID. + */ + typedef std::map ResourceMap; + + /** + * Constructs a new item of the roster. + * @param jid The JID of the contact. + * @param name The displayed name of the contact. + */ + RosterItem( const std::string& jid, const std::string& name = EmptyString ); + + /** + * Constructs a new RosterItem using the data holding class. + * @param data The RosterItemData to construct the item from. The new + * item will own the data object. + */ + RosterItem( const RosterItemData& data ); + + /** + * Virtual destructor. + */ + virtual ~RosterItem(); + + /** + * Sets the displayed name of a contact/roster item. + * @param name The contact's new name. + */ + void setName( const std::string& name ); + + /** + * Retrieves the displayed name of a contact/roster item. + * @return The contact's name. + */ + const std::string& name() const; + + /** + * Returns the contact's bare JID. + * @return The contact's bare JID. + */ + const std::string& jid() const; + + /** + * Sets the current subscription status of the contact. + * @param subscription The current subscription. + * @param ask Whether a subscription request is pending. + */ + void setSubscription( const std::string& subscription, const std::string& ask ); + + /** + * Returns the current subscription type between the remote and the local entity. + * @return The subscription type. + */ + SubscriptionType subscription() const; + + /** + * Sets the groups this RosterItem belongs to. + * @param groups The groups to set for this item. + */ + void setGroups( const StringList& groups ); + + /** + * Returns the groups this RosterItem belongs to. + * @return The groups this item belongs to. + */ + const StringList groups() const; + + /** + * Whether the item has unsynchronized changes. + * @return @b True if the item has unsynchronized changes, @b false otherwise. + */ + bool changed() const; + + /** + * Indicates whether this item has at least one resource online (in any state). + * @return @b True if at least one resource is online, @b false otherwise. + */ + bool online() const; + + /** + * Returns the contact's resources. + * @return The contact's resources. + */ + const ResourceMap& resources() const { return m_resources; } + + /** + * Returns the Resource for a specific resource string. + * @param res The resource string. + * @return The Resource if found, 0 otherwise. + */ + const Resource* resource( const std::string& res ) const; + + /** + * Returns the Resource with the highest priority. + * @return The Resource with the highest priority. + */ + const Resource* highestResource() const; + + protected: + /** + * Sets the current presence of the resource. + * @param resource The resource to set the presence for. + * @param presence The current presence. + */ + void setPresence( const std::string& resource, Presence::PresenceType presence ); + + /** + * Sets the current status message of the resource. + * @param resource The resource to set the status message for. + * @param msg The current status message, i.e. from the presence info. + */ + void setStatus( const std::string& resource, const std::string& msg ); + + /** + * Sets the current priority of the resource. + * @param resource The resource to set the status message for. + * @param priority The resource's priority, i.e. from the presence info. + */ + void setPriority( const std::string& resource, int priority ); + + /** + * Sets the resource's presence extensions. + * @param resource The resource to set the extensions for. + * @param exts The extensions to set. + */ + void setExtensions( const std::string& resource, const StanzaExtensionList& exts ); + + /** + * Removes the 'changed' flag from the item. + */ + void setSynchronized(); + + /** + * This function is called to remove subsequent resources from a RosterItem. + * @param resource The resource to remove. + */ + void removeResource( const std::string& resource ); + + /** + * This function deletes the internal RosterItemData and replaces it with the provided + * one. The RosterItem will own the RosterItemData instance. + */ + void setData( const RosterItemData& rid ); + + private: + RosterItemData* m_data; + ResourceMap m_resources; + + }; + +} + +#endif // ROSTERITEM_H__ diff --git a/libs/libgloox/rosteritemdata.h b/libs/libgloox/rosteritemdata.h new file mode 100644 index 0000000..cd5e9a6 --- /dev/null +++ b/libs/libgloox/rosteritemdata.h @@ -0,0 +1,190 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef ROSTERITEMBASE_H__ +#define ROSTERITEMBASE_H__ + +#include "gloox.h" +#include "jid.h" +#include "tag.h" + +#include +#include + + +namespace gloox +{ + + /** + * @brief A class holding roster item data. + * + * You should not need to use this class directly. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API RosterItemData + { + + public: + /** + * Constructs a new item of the roster. + * @param jid The JID of the contact. + * @param name The displayed name of the contact. + * @param groups A list of groups the contact belongs to. + */ + RosterItemData( const std::string& jid, const std::string& name, + const StringList& groups ) + : m_jid( jid ), m_name( name ), m_groups( groups ), + m_subscription( S10nNone ), m_changed( false ), m_remove( false ) + {} + + /** + * Constructs a new item of the roster, scheduled for removal. + * @param jid The JID of the contact to remove. + */ + RosterItemData( const std::string& jid ) + : m_jid( jid ), m_subscription( S10nNone ), m_changed( false ), + m_remove( true ) + {} + + /** + * Virtual destructor. + */ + virtual ~RosterItemData() {} + + /** + * Returns the contact's bare JID. + * @return The contact's bare JID. + */ + const std::string& jid() const { return m_jid; } + + /** + * Sets the displayed name of a contact/roster item. + * @param name The contact's new name. + */ + void setName( const std::string& name ) + { + m_name = name; + m_changed = true; + } + + /** + * Retrieves the displayed name of a contact/roster item. + * @return The contact's name. + */ + const std::string& name() const { return m_name; } + + /** + * Sets the current subscription status of the contact. + * @param subscription The current subscription. + * @param ask Whether a subscription request is pending. + */ + void setSubscription( const std::string& subscription, const std::string& ask ) + { + m_sub = subscription; + m_ask = ask; + + if( subscription == "from" && ask.empty() ) + m_subscription = S10nFrom; + else if( subscription == "from" && !ask.empty() ) + m_subscription = S10nFromOut; + else if( subscription == "to" && ask.empty() ) + m_subscription = S10nTo; + else if( subscription == "to" && !ask.empty() ) + m_subscription = S10nToIn; + else if( subscription == "none" && ask.empty() ) + m_subscription = S10nNone; + else if( subscription == "none" && !ask.empty() ) + m_subscription = S10nNoneOut; + else if( subscription == "both" ) + m_subscription = S10nBoth; + } + + /** + * Returns the current subscription type between the remote and the local entity. + * @return The subscription type. + */ + SubscriptionType subscription() const { return m_subscription; } + + /** + * Sets the groups this RosterItem belongs to. + * @param groups The groups to set for this item. + */ + void setGroups( const StringList& groups ) + { + m_groups = groups; + m_changed = true; + } + + /** + * Returns the groups this RosterItem belongs to. + * @return The groups this item belongs to. + */ + const StringList& groups() const { return m_groups; } + + /** + * Whether the item has unsynchronized changes. + * @return @b True if the item has unsynchronized changes, @b false otherwise. + */ + bool changed() const { return m_changed; } + + /** + * Whether the item is scheduled for removal. + * @return @b True if the item is subject to a removal or scheduled for removal, @b false + * otherwise. + */ + bool remove() const { return m_remove; } + + /** + * Removes the 'changed' flag from the item. + */ + void setSynchronized() { m_changed = false; } + + /** + * Retruns a Tag representation of the roster item data. + * @return A Tag representation. + */ + Tag* tag() const + { + Tag* i = new Tag( "item" ); + i->addAttribute( "jid", m_jid ); + if( m_remove ) + i->addAttribute( "subscription", "remove" ); + else + { + i->addAttribute( "name", m_name ); + StringList::const_iterator it = m_groups.begin(); + for( ; it != m_groups.end(); ++it ) + new Tag( i, "group", (*it) ); + i->addAttribute( "subscription", m_sub ); + i->addAttribute( "ask", m_ask ); + } + return i; + } + + protected: + std::string m_jid; + std::string m_name; + StringList m_groups; + SubscriptionType m_subscription; + std::string m_sub; + std::string m_ask; + bool m_changed; + bool m_remove; + + }; + +} + +#endif // ROSTERITEMBASE_H__ diff --git a/libs/libgloox/rosterlistener.h b/libs/libgloox/rosterlistener.h new file mode 100644 index 0000000..ce93777 --- /dev/null +++ b/libs/libgloox/rosterlistener.h @@ -0,0 +1,173 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef ROSTERLISTENER_H__ +#define ROSTERLISTENER_H__ + +#include "rosteritem.h" + +#include +#include + +namespace gloox +{ + + class IQ; + class Presence; + + /** + * A map of JID/RosterItem pairs. + */ + typedef std::map Roster; + + /** + * @brief A virtual interface which can be reimplemented to receive roster updates. + * + * A class implementing this interface and being registered as RosterListener with the + * RosterManager object receives notifications about all the changes in the server-side + * roster. Only one RosterListener per Roster at a time is possible. + * + * @author Jakob Schroeter + * @since 0.3 + */ + class GLOOX_API RosterListener + { + public: + /** + * Virtual Destructor. + */ + virtual ~RosterListener() {} + + /** + * Reimplement this function if you want to be notified about new items + * on the server-side roster (items subject to a so-called Roster Push). + * This function will be called regardless who added the item, either this + * resource or another. However, it will not be called for JIDs for which + * presence is received without them being on the roster. + * @param jid The new item's full address. + */ + virtual void handleItemAdded( const JID& jid ) = 0; + + /** + * Reimplement this function if you want to be notified about items + * which authorised subscription. + * @param jid The authorising item's full address. + */ + virtual void handleItemSubscribed( const JID& jid ) = 0; + + /** + * Reimplement this function if you want to be notified about items that + * were removed from the server-side roster (items subject to a so-called Roster Push). + * This function will be called regardless who deleted the item, either this resource or + * another. + * @param jid The removed item's full address. + */ + virtual void handleItemRemoved( const JID& jid ) = 0; + + /** + * Reimplement this function if you want to be notified about items that + * were modified on the server-side roster (items subject to a so-called Roster Push). + * A roster push is initiated if a second resource of this JID modifies an item stored on the + * server-side contact list. This can include modifying the item's name, its groups, or the + * subscription status. These changes are pushed by the server to @b all connected resources. + * This is why this function will be called if you modify a roster item locally and synchronize + * it with the server. + * @param jid The modified item's full address. + */ + virtual void handleItemUpdated( const JID& jid ) = 0; + + /** + * Reimplement this function if you want to be notified about items which + * removed subscription authorization. + * @param jid The item's full address. + */ + virtual void handleItemUnsubscribed( const JID& jid ) = 0; + + /** + * Reimplement this function if you want to receive the whole server-side roster + * on the initial roster push. After successful authentication, RosterManager asks the + * server for the full server-side roster. Invocation of this method announces its arrival. + * Roster item status is set to 'unavailable' until incoming presence info updates it. A full + * roster push only happens once per connection. + * @param roster The full roster. + */ + virtual void handleRoster( const Roster& roster ) = 0; + + /** + * This function is called on every status change of an item in the roster. + * If the presence is of type Unavailable, then the resource has already been + * removed from the RosterItem. + * @param item The roster item. + * @param resource The resource that changed presence. + * @param presence The item's new presence. + * @param msg The status change message. + * @since 0.9 + */ + virtual void handleRosterPresence( const RosterItem& item, const std::string& resource, + Presence::PresenceType presence, const std::string& msg ) = 0; + + /** + * This function is called on every status change of a JID that matches the Client's + * own JID. + * If the presence is of type Unavailable, then the resource has already been + * removed from the RosterItem. + * @param item The self item. + * @param resource The resource that changed presence. + * @param presence The item's new presence. + * @param msg The status change message. + * @since 0.9 + */ + virtual void handleSelfPresence( const RosterItem& item, const std::string& resource, + Presence::PresenceType presence, const std::string& msg ) = 0; + + /** + * This function is called when an entity wishes to subscribe to this entity's presence. + * If the handler is registered as a asynchronous handler for subscription requests, + * the return value of this function is ignored. In this case you should use + * RosterManager::ackSubscriptionRequest() to answer the request. + * @param jid The requesting item's address. + * @param msg A message sent along with the request. + * @return Return @b true to allow subscription and subscribe to the remote entity's + * presence, @b false to ignore the request. + */ + virtual bool handleSubscriptionRequest( const JID& jid, const std::string& msg ) = 0; + + /** + * This function is called when an entity unsubscribes from this entity's presence. + * If the handler is registered as a asynchronous handler for subscription requests, + * the return value of this function is ignored. In this case you should use + * RosterManager::unsubscribe() if you want to unsubscribe yourself from the contct's + * presence and to remove the contact from the roster. + * @param jid The item's address. + * @param msg A message sent along with the request. + * @return Return @b true to unsubscribe from the remote entity, @b false to ignore. + */ + virtual bool handleUnsubscriptionRequest( const JID& jid, const std::string& msg ) = 0; + + /** + * This function is called whenever presence from an entity is received which is not in + * the roster. + * @param presence The full presence stanza. + */ + virtual void handleNonrosterPresence( const Presence& presence ) = 0; + + /** + * This function is called if the server returned an error. + * @param iq The error stanza. + */ + virtual void handleRosterError( const IQ& iq ) = 0; + }; + +} + +#endif // ROSTERLISTENER_H__ diff --git a/libs/libgloox/rostermanager.cpp b/libs/libgloox/rostermanager.cpp new file mode 100644 index 0000000..207d11e --- /dev/null +++ b/libs/libgloox/rostermanager.cpp @@ -0,0 +1,421 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "clientbase.h" +#include "rostermanager.h" +#include "disco.h" +#include "rosteritem.h" +#include "rosteritemdata.h" +#include "rosterlistener.h" +#include "privatexml.h" +#include "util.h" +#include "stanzaextension.h" +#include "capabilities.h" + + +namespace gloox +{ + + // ---- RosterManager::Query ---- + RosterManager::Query::Query( const JID& jid, const std::string& name, const StringList& groups ) + : StanzaExtension( ExtRoster ) + { + m_roster.push_back( new RosterItemData( jid.bare(), name, groups ) ); + } + + RosterManager::Query::Query( const JID& jid ) + : StanzaExtension( ExtRoster ) + { + m_roster.push_back( new RosterItemData( jid.bare() ) ); + } + + RosterManager::Query::Query( const Tag* tag ) + : StanzaExtension( ExtRoster ) + { + if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_ROSTER ) + return; + + const ConstTagList& l = tag->findTagList( "query/item" ); + ConstTagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + { + StringList groups; + const ConstTagList& g = (*it)->findTagList( "item/group" ); + ConstTagList::const_iterator it_g = g.begin(); + for( ; it_g != g.end(); ++it_g ) + groups.push_back( (*it_g)->cdata() ); + + const std::string sub = (*it)->findAttribute( "subscription" ); + if( sub == "remove" ) + m_roster.push_back( new RosterItemData( (*it)->findAttribute( "jid" ) ) ); + else + { + RosterItemData* rid = new RosterItemData( (*it)->findAttribute( "jid" ), + (*it)->findAttribute( "name" ), + groups ); + rid->setSubscription( sub, (*it)->findAttribute( "ask" ) ); + m_roster.push_back( rid ); + } + } + } + + RosterManager::Query::~Query() + { + util::clearList( m_roster ); + } + + const std::string& RosterManager::Query::filterString() const + { + static const std::string filter = "/iq/query[@xmlns='" + XMLNS_ROSTER + "']"; + return filter; + } + + Tag* RosterManager::Query::tag() const + { + Tag* t = new Tag( "query" ); + t->setXmlns( XMLNS_ROSTER ); + + RosterData::const_iterator it = m_roster.begin(); + for( ; it != m_roster.end(); ++it ) + t->addChild( (*it)->tag() ); + + return t; + } + + StanzaExtension* RosterManager::Query::clone() const + { + Query* q = new Query(); + RosterData::const_iterator it = m_roster.begin(); + for( ; it != m_roster.end(); ++it ) + { + q->m_roster.push_back( new RosterItemData( *(*it) ) ); + } + return q; + } + // ---- ~RosterManager::Query ---- + + // ---- RosterManager ---- + RosterManager::RosterManager( ClientBase* parent ) + : m_rosterListener( 0 ), m_parent( parent ), m_privateXML( 0 ), + m_syncSubscribeReq( false ) + { + if( m_parent ) + { + m_parent->registerIqHandler( this, ExtRoster ); + m_parent->registerPresenceHandler( this ); + m_parent->registerSubscriptionHandler( this ); + m_parent->registerStanzaExtension( new Query() ); + + m_self = new RosterItem( m_parent->jid().bare() ); + m_privateXML = new PrivateXML( m_parent ); + } + } + + RosterManager::~RosterManager() + { + if( m_parent ) + { + m_parent->removeIqHandler( this, ExtRoster ); + m_parent->removeIDHandler( this ); + m_parent->removePresenceHandler( this ); + m_parent->removeSubscriptionHandler( this ); + m_parent->removeStanzaExtension( ExtRoster ); + delete m_self; + delete m_privateXML; + } + + util::clearMap( m_roster ); + } + + Roster* RosterManager::roster() + { + return &m_roster; + } + + void RosterManager::fill() + { + if( !m_parent ) + return; + + util::clearMap( m_roster ); + m_privateXML->requestXML( "roster", XMLNS_ROSTER_DELIMITER, this ); + IQ iq( IQ::Get, JID(), m_parent->getID() ); + iq.addExtension( new Query() ); + m_parent->send( iq, this, RequestRoster ); + } + + bool RosterManager::handleIq( const IQ& iq ) + { + if( iq.subtype() != IQ::Set ) // FIXME add checks for 'from' attribute (empty or bare self jid?) + return false; + + // single roster item push + const Query* q = iq.findExtension( ExtRoster ); + if( q && q->roster().size() ) + mergePush( q->roster() ); + + IQ re( IQ::Result, JID(), iq.id() ); + m_parent->send( re ); + return true; + } + + void RosterManager::handleIqID( const IQ& iq, int context ) + { + if( iq.subtype() == IQ::Result ) // initial roster + { + const Query* q = iq.findExtension( ExtRoster ); + if( q ) + mergeRoster( q->roster() ); + + if( context == RequestRoster ) + { + if( m_parent ) + m_parent->rosterFilled(); + + if( m_rosterListener ) + m_rosterListener->handleRoster( m_roster ); + } + } + else if( iq.subtype() == IQ::Error ) + { + if( context == RequestRoster && m_parent ) + m_parent->rosterFilled(); + + if( m_rosterListener ) + m_rosterListener->handleRosterError( iq ); + } + } + + void RosterManager::handlePresence( const Presence& presence ) + { + if( presence.subtype() == Presence::Error ) + return; + + bool self = false; + Roster::iterator it = m_roster.find( presence.from().bare() ); + if( it != m_roster.end() || ( self = ( presence.from().bare() == m_self->jid() ) ) ) + { + RosterItem* ri = self ? m_self : (*it).second; + const std::string& resource = presence.from().resource(); + + if( presence.presence() == Presence::Unavailable ) + ri->removeResource( resource ); + else + { + ri->setPresence( resource, presence.presence() ); + ri->setStatus( resource, presence.status() ); + ri->setPriority( resource, presence.priority() ); + ri->setExtensions( resource, presence.extensions() ); + } + + if( m_rosterListener && !self ) + m_rosterListener->handleRosterPresence( *ri, resource, + presence.presence(), presence.status() ); + else if( m_rosterListener && self ) + m_rosterListener->handleSelfPresence( *ri, resource, + presence.presence(), presence.status() ); + } + else + { + if( m_rosterListener ) + m_rosterListener->handleNonrosterPresence( presence ); + } + } + + void RosterManager::subscribe( const JID& jid, const std::string& name, + const StringList& groups, const std::string& msg ) + { + if( !jid ) + return; + + add( jid, name, groups ); + + Subscription s( Subscription::Subscribe, jid.bareJID(), msg ); + m_parent->send( s ); + } + + + void RosterManager::add( const JID& jid, const std::string& name, const StringList& groups ) + { + if( !jid ) + return; + + IQ iq( IQ::Set, JID(), m_parent->getID() ); + iq.addExtension( new Query( jid, name, groups) ); + + m_parent->send( iq, this, AddRosterItem ); + } + + void RosterManager::unsubscribe( const JID& jid, const std::string& msg ) + { + Subscription p( Subscription::Unsubscribe, jid.bareJID(), msg ); + m_parent->send( p ); + } + + void RosterManager::cancel( const JID& jid, const std::string& msg ) + { + Subscription p( Subscription::Unsubscribed, jid.bareJID(), msg ); + m_parent->send( p ); + } + + void RosterManager::remove( const JID& jid ) + { + if( !jid ) + return; + + IQ iq( IQ::Set, JID(), m_parent->getID() ); + iq.addExtension( new Query( jid ) ); + + m_parent->send( iq, this, RemoveRosterItem ); + } + + void RosterManager::synchronize() + { + Roster::const_iterator it = m_roster.begin(); + for( ; it != m_roster.end(); ++it ) + { + if( !(*it).second->changed() ) + continue; + + IQ iq( IQ::Set, JID(), m_parent->getID() ); + iq.addExtension( new Query( (*it).second->jid(), (*it).second->name(), (*it).second->groups() ) ); + m_parent->send( iq, this, SynchronizeRoster ); + } + } + + void RosterManager::ackSubscriptionRequest( const JID& to, bool ack ) + { + Subscription p( ack ? Subscription::Subscribed + : Subscription::Unsubscribed, to.bareJID() ); + m_parent->send( p ); + } + + void RosterManager::handleSubscription( const Subscription& s10n ) + { + if( !m_rosterListener ) + return; + + switch( s10n.subtype() ) + { + case Subscription::Subscribe: + { + bool answer = m_rosterListener->handleSubscriptionRequest( s10n.from(), s10n.status() ); + if( m_syncSubscribeReq ) + { + ackSubscriptionRequest( s10n.from(), answer ); + } + break; + } + case Subscription::Subscribed: + { + m_rosterListener->handleItemSubscribed( s10n.from() ); + break; + } + + case Subscription::Unsubscribe: + { + Subscription p( Subscription::Unsubscribed, s10n.from().bareJID() ); + m_parent->send( p ); + + bool answer = m_rosterListener->handleUnsubscriptionRequest( s10n.from(), s10n.status() ); + if( m_syncSubscribeReq && answer ) + remove( s10n.from().bare() ); + break; + } + + case Subscription::Unsubscribed: + { + m_rosterListener->handleItemUnsubscribed( s10n.from() ); + break; + } + + default: + break; + } + } + + void RosterManager::registerRosterListener( RosterListener* rl, bool syncSubscribeReq ) + { + m_syncSubscribeReq = syncSubscribeReq; + m_rosterListener = rl; + } + + void RosterManager::removeRosterListener() + { + m_syncSubscribeReq = false; + m_rosterListener = 0; + } + + void RosterManager::setDelimiter( const std::string& delimiter ) + { + m_delimiter = delimiter; + Tag* t = new Tag( "roster", m_delimiter ); + t->addAttribute( XMLNS, XMLNS_ROSTER_DELIMITER ); + m_privateXML->storeXML( t, this ); + } + + void RosterManager::handlePrivateXML( const Tag* xml ) + { + if( xml ) + m_delimiter = xml->cdata(); + } + + void RosterManager::handlePrivateXMLResult( const std::string& /*uid*/, PrivateXMLResult /*result*/ ) + { + } + + RosterItem* RosterManager::getRosterItem( const JID& jid ) + { + Roster::const_iterator it = m_roster.find( jid.bare() ); + return it != m_roster.end() ? (*it).second : 0; + } + + void RosterManager::mergePush( const RosterData& data ) + { + RosterData::const_iterator it = data.begin(); + for( ; it != data.end(); ++it ) + { + Roster::iterator itr = m_roster.find( (*it)->jid() ); + if( itr != m_roster.end() ) + { + if( (*it)->remove() ) + { + if( m_rosterListener ) + m_rosterListener->handleItemRemoved( (*it)->jid() ); + delete (*itr).second; + m_roster.erase( itr ); + } + else + { + (*itr).second->setData( *(*it) ); + if( m_rosterListener ) + m_rosterListener->handleItemUpdated( (*it)->jid() ); + } + } + else if( !(*it)->remove() ) + { + m_roster.insert( std::make_pair( (*it)->jid(), new RosterItem( *(*it) ) ) ); + if( m_rosterListener ) + m_rosterListener->handleItemAdded( (*it)->jid() ); + } + } + } + + void RosterManager::mergeRoster( const RosterData& data ) + { + RosterData::const_iterator it = data.begin(); + for( ; it != data.end(); ++it ) + m_roster.insert( std::make_pair( (*it)->jid(), new RosterItem( *(*it) ) ) ); + } + +} diff --git a/libs/libgloox/rostermanager.h b/libs/libgloox/rostermanager.h new file mode 100644 index 0000000..0428873 --- /dev/null +++ b/libs/libgloox/rostermanager.h @@ -0,0 +1,283 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef ROSTERMANAGER_H__ +#define ROSTERMANAGER_H__ + +#include "subscriptionhandler.h" +#include "privatexmlhandler.h" +#include "iqhandler.h" +#include "presencehandler.h" +#include "rosterlistener.h" + +#include +#include +#include + +namespace gloox +{ + + class ClientBase; + class Stanza; + class PrivateXML; + class RosterItem; + + /** + * @brief This class implements Jabber/XMPP roster handling in the @b jabber:iq:roster namespace. + * + * It takes care of changing presence, subscriptions, etc. + * You can modify any number of RosterItems within the Roster at any time. These changes must be + * synchronized with the server by calling @ref synchronize(). Note that incoming Roster pushes + * initiated by other resources may overwrite changed values. + * Additionally, XEP-0083 (Nested Roster Groups) is implemented herein. + * + * @author Jakob Schroeter + * @since 0.3 + */ + class GLOOX_API RosterManager : public IqHandler, public PresenceHandler, public SubscriptionHandler, + public PrivateXMLHandler + { + public: + /** + * Creates a new RosterManager. + * @param parent The ClientBase which is used for communication. + */ + RosterManager( ClientBase* parent ); + + /** + * Virtual destructor. + */ + virtual ~RosterManager(); + + /** + * This function does the initial filling of the roster with + * the current server-side roster. + */ + void fill(); + + /** + * This function returns the roster. + * @return Returns a map of JIDs with their current presence. + */ + Roster* roster(); + + /** + * Use this function to subscribe to a new JID. The contact is added to the roster automatically + * (by compliant servers, as required by RFC 3921). + * @param jid The address to subscribe to. + * @param name The displayed name of the contact. + * @param groups A list of groups the contact belongs to. + * @param msg A message sent along with the request. + */ + void subscribe( const JID& jid, const std::string& name = EmptyString, + const StringList& groups = StringList(), + const std::string& msg = EmptyString ); + + /** + * Synchronizes locally modified RosterItems back to the server. + */ + void synchronize(); + + /** + * Use this function to add a contact to the roster. No subscription request is sent. + * @note Use @ref unsubscribe() to remove an item from the roster. + * @param jid The JID to add. + * @param name The displayed name of the contact. + * @param groups A list of groups the contact belongs to. + */ + void add( const JID& jid, const std::string& name, const StringList& groups ); + + /** + * Use this function to unsubscribe from a contact's presence. You will no longer + * receive presence from this contact. + * @param jid The address to unsubscribe from. + * @param msg A message to send along with the request. + * @since 0.9 + * @note Use remove() to remove a contact from the roster and to cancel its subscriptions. + */ + void unsubscribe( const JID& jid, const std::string& msg = EmptyString ); + + /** + * Use this function to cancel the contact's subscription to your presence. The contact will + * no longer receive presence from you. + * @param jid The contact's JID. + * @param msg A message to send along with the request. + * @since 0.9 + * @note Use remove() to remove a contact from the roster and to cancel its subscriptions. + */ + void cancel( const JID& jid, const std::string& msg = EmptyString ); + + /** + * Use this function to remove a contact from the roster. Subscription is implicitely + * cancelled. + * @param jid The contact's JID. + * @since 0.9 + */ + void remove( const JID& jid ); + + /** + * Use this function to acknowledge a subscription request if you requested asynchronous + * subscription request handling. + * @param to The JID to authorize/decline. + * @param ack Whether to authorize or decline the contact's request. + */ + void ackSubscriptionRequest( const JID& to, bool ack ); + + /** + * Use this function to retrieve the delimiter of Nested Roster Groups (XEP-0083). + * @return The group delimiter. + * @since 0.7 + */ + const std::string& delimiter() const { return m_delimiter; } + + /** + * Use this function to set the group delimiter (XEP-0083). + * @param delimiter The group delimiter. + * @since 0.7 + */ + void setDelimiter( const std::string& delimiter ); + + /** + * Lets you retrieve the RosterItem that belongs to the given JID. + * @param jid The JID to return the RosterItem for. + */ + RosterItem* getRosterItem( const JID& jid ); + + /** + * Register @c rl as object that receives updates on roster operations. + * For GUI applications it may be necessary to display a dialog or whatever to + * the user without blocking. If you want that, use asynchronous subscription + * requests. If you want to answer a request right away, make it synchronous. + * @param rl The object that receives roster updates. + * @param syncSubscribeReq Indicates whether (Un)SubscriptionRequests shall + * be handled synchronous (@b true) or asynchronous (@b false). Default: synchronous. + */ + void registerRosterListener( RosterListener* rl, bool syncSubscribeReq = true ); + + /** + * Complementary function to @ref registerRosterListener. Removes the current RosterListener. + * Roster events will not be delivered anywhere. + */ + void removeRosterListener(); + + // reimplemented from IqHandler. + virtual bool handleIq( const IQ& iq ); + + // reimplemented from IqHandler. + virtual void handleIqID( const IQ& iq, int context ); + + // reimplemented from PresenceHandler. + virtual void handlePresence( const Presence& presence ); + + // reimplemented from SubscriptionHandler. + virtual void handleSubscription( const Subscription& subscription ); + + // reimplemented from PrivateXMLHandler + virtual void handlePrivateXML( const Tag* xml ); + + // reimplemented from PrivateXMLHandler + virtual void handlePrivateXMLResult( const std::string& uid, PrivateXMLResult pxResult ); + + private: +#ifdef ROSTERMANAGER_TEST + public: +#endif + typedef std::list RosterData; + + /** + * @brief An implementation of StanzaExtension that helps in roster management. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class Query : public StanzaExtension + { + public: + /** + * Constructs a new object that can be used to add a contact to the roster. + * @param jid The contact's JID. + * @param name The contact's optional user-defined name. + * @param groups An optional list of groups the contact belongs to. + */ + Query( const JID& jid, const std::string& name, const StringList& groups ); + + /** + * Constructs a new object that can be used to remove a contact from the roster. + * @param jid The contact's JID. + */ + Query( const JID& jid ); + + /** + * Creates a new Query object from teh given Tag. + * @param tag The Tag to parse. + */ + Query( const Tag* tag = 0 ); + + /** + * Destructor. + */ + ~Query(); + + /** + * Retruns the internal roster that was created by the ctors (either from an + * incoming packet or passed arguments). + * This is not necessarily the full roster, but may be a single item. + * @return The (possibly partial) roster). + */ + const RosterData& roster() const { return m_roster; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new Query( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const; + + private: + RosterData m_roster; + + }; + + void mergePush( const RosterData& data ); + void mergeRoster( const RosterData& data ); + + RosterListener* m_rosterListener; + Roster m_roster; + ClientBase* m_parent; + PrivateXML* m_privateXML; + RosterItem* m_self; + + std::string m_delimiter; + bool m_syncSubscribeReq; + + enum RosterContext + { + RequestRoster, + AddRosterItem, + RemoveRosterItem, + SynchronizeRoster + }; + + }; + +} + +#endif // ROSTER_H__ diff --git a/libs/libgloox/search.cpp b/libs/libgloox/search.cpp new file mode 100644 index 0000000..097b529 --- /dev/null +++ b/libs/libgloox/search.cpp @@ -0,0 +1,223 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "search.h" + +#include "clientbase.h" +#include "dataform.h" +#include "iq.h" + +namespace gloox +{ + + // Search::Query ---- + Search::Query::Query( DataForm* form ) + : StanzaExtension( ExtSearch ), m_form( form ), m_fields( 0 ) + { + } + + Search::Query::Query( int fields, const SearchFieldStruct& values ) + : StanzaExtension( ExtSearch ), m_form( 0 ), m_fields( fields ), m_values( values ) + { + } + + Search::Query::Query( const Tag* tag ) + : StanzaExtension( ExtSearch ), m_form( 0 ), m_fields( 0 ) + { + if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_SEARCH ) + return; + + const TagList& l = tag->children(); + TagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + { + if( (*it)->name() == "instructions" ) + { + m_instructions = (*it)->cdata(); + } + else if( (*it)->name() == "item" ) + { + m_srl.push_back( new SearchFieldStruct( (*it) ) ); + } + else if( (*it)->name() == "first" ) + m_fields |= SearchFieldFirst; + else if( (*it)->name() == "last" ) + m_fields |= SearchFieldLast; + else if( (*it)->name() == "email" ) + m_fields |= SearchFieldEmail; + else if( (*it)->name() == "nick" ) + m_fields |= SearchFieldNick; + else if( !m_form && (*it)->name() == "x" && (*it)->xmlns() == XMLNS_X_DATA ) + m_form = new DataForm( (*it) ); + } + } + + Search::Query::~Query() + { + delete m_form; + SearchResultList::iterator it = m_srl.begin(); + for( ; it != m_srl.end(); ++it ) + delete (*it); + } + + const std::string& Search::Query::filterString() const + { + static const std::string filter = "/iq/query[@xmlns='" + XMLNS_SEARCH + "']"; + return filter; + } + + Tag* Search::Query::tag() const + { + Tag* t = new Tag( "query" ); + t->setXmlns( XMLNS_SEARCH ); + if( m_form ) + t->addChild( m_form->tag() ); + else if( m_fields ) + { + if( !m_instructions.empty() ) + new Tag( t, "instructions", m_instructions ); + if( m_fields & SearchFieldFirst ) + new Tag( t, "first", m_values.first() ); + if( m_fields & SearchFieldLast ) + new Tag( t, "last", m_values.last() ); + if( m_fields & SearchFieldNick ) + new Tag( t, "nick", m_values.nick() ); + if( m_fields & SearchFieldEmail ) + new Tag( t, "email", m_values.email() ); + } + else if( !m_srl.empty() ) + { + SearchResultList::const_iterator it = m_srl.begin(); + for( ; it != m_srl.end(); ++it ) + { + t->addChild( (*it)->tag() ); + } + } + return t; + } + // ---- ~Search::Query ---- + + // ---- Search ---- + Search::Search( ClientBase* parent ) + : m_parent( parent ) + { + if( m_parent ) + m_parent->registerStanzaExtension( new Query() ); + } + + Search::~Search() + { + if( m_parent ) + { + m_parent->removeIDHandler( this ); + m_parent->removeStanzaExtension( ExtRoster ); + } + } + + void Search::fetchSearchFields( const JID& directory, SearchHandler* sh ) + { + if( !m_parent || !directory || !sh ) + return; + + const std::string& id = m_parent->getID(); + IQ iq( IQ::Get, directory, id ); + iq.addExtension( new Query() ); + m_track[id] = sh; + m_parent->send( iq, this, FetchSearchFields ); + } + + void Search::search( const JID& directory, DataForm* form, SearchHandler* sh ) + { + if( !m_parent || !directory || !sh ) + return; + + const std::string& id = m_parent->getID(); + IQ iq( IQ::Set, directory, id ); + iq.addExtension( new Query( form ) ); + + m_track[id] = sh; + m_parent->send( iq, this, DoSearch ); + } + + void Search::search( const JID& directory, int fields, const SearchFieldStruct& values, SearchHandler* sh ) + { + if( !m_parent || !directory || !sh ) + return; + + const std::string& id = m_parent->getID(); + + IQ iq( IQ::Set, directory ); + iq.addExtension( new Query( fields, values ) ); + + m_track[id] = sh; + m_parent->send( iq, this, DoSearch ); + } + + void Search::handleIqID( const IQ& iq, int context ) + { + TrackMap::iterator it = m_track.find( iq.id() ); + if( it != m_track.end() ) + { + switch( iq.subtype() ) + { + case IQ::Result: + { + const Query* q = iq.findExtension( ExtSearch ); + if( !q ) + return; + + switch( context ) + { + case FetchSearchFields: + { + if( q->form() ) + { + (*it).second->handleSearchFields( iq.from(), q->form() ); + } + else + { + (*it).second->handleSearchFields( iq.from(), q->fields(), q->instructions() ); + } + break; + } + case DoSearch: + { + if( q->form() ) + { + (*it).second->handleSearchResult( iq.from(), q->form() ); + } + else + { + (*it).second->handleSearchResult( iq.from(), q->result() ); + } + break; + } + } + break; + } + case IQ::Error: + (*it).second->handleSearchError( iq.from(), iq.error() ); + break; + + default: + break; + } + + m_track.erase( it ); + } + + return; + } + +} diff --git a/libs/libgloox/search.h b/libs/libgloox/search.h new file mode 100644 index 0000000..677305d --- /dev/null +++ b/libs/libgloox/search.h @@ -0,0 +1,215 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef SEARCH_H__ +#define SEARCH_H__ + +#include "gloox.h" +#include "searchhandler.h" +#include "discohandler.h" +#include "iqhandler.h" +#include "stanzaextension.h" +#include "dataform.h" + +#include + +namespace gloox +{ + + class ClientBase; + class IQ; + class Disco; + + /** + * @brief An implementation of XEP-0055 (Jabber Search) + * + * To perform a search in a directory (e.g., a User Directory): + * + * @li Inherit from SearchHandler and implement the virtual functions. + * @li Create a new Search object. + * @li Ask the directory for the supported fields using fetchSearchFields(). Depending on the directory, + * the result can be either an integer (bit-wise ORed supported fields) or a DataForm. + * @li Search by either using a DataForm or the SearchFieldStruct. + * @li The results can be either a (empty) list of SearchFieldStructs or a DataForm. + * + * @author Jakob Schroeter + * @since 0.8.5 + */ + class GLOOX_API Search : public IqHandler + { + + public: + /** + * Creates a new Search object. + * @param parent The ClientBase to use. + */ + Search( ClientBase* parent ); + + /** + * Virtual Destructor. + */ + ~Search(); + + /** + * Use this function to check which fields the directory supports. + * @param directory The (user) directory to fetch the available/searchable fields from. + * @param sh The SearchHandler to notify about the results. + */ + void fetchSearchFields( const JID& directory, SearchHandler* sh ); + + /** + * Initiates a search on the given directory, with the given data form. The given SearchHandler + * is notified about the results. + * @param directory The (user) directory to search. + * @param form The DataForm contains the phrases the user wishes to search for. + * Search will delete the form eventually. + * @param sh The SearchHandler to notify about the results. + */ + void search( const JID& directory, DataForm* form, SearchHandler* sh ); + + /** + * Initiates a search on the given directory, with the given phrases. The given SearchHandler + * is notified about the results. + * @param directory The (user) directory to search. + * @param fields Bit-wise ORed FieldEnum values describing the valid (i.e., set) fields in + * the @b values parameter. + * @param values Contains the phrases to search for. + * @param sh The SearchHandler to notify about the results. + */ + void search( const JID& directory, int fields, const SearchFieldStruct& values, SearchHandler* sh ); + + // reimplemented from IqHandler. + virtual bool handleIq( const IQ& iq ) { (void)iq; return false; } + + // reimplemented from IqHandler. + virtual void handleIqID( const IQ& iq, int context ); + + protected: + enum IdType + { + FetchSearchFields, + DoSearch + }; + + typedef std::map TrackMap; + TrackMap m_track; + + ClientBase* m_parent; + Disco* m_disco; + + private: +#ifdef SEARCH_TEST + public: +#endif + /** + * @brief A wrapping class for the XEP-0055 <query> element. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class Query : public StanzaExtension + { + public: + /** + * Creates a new object that can be used to carry out a search. + * @param form A DataForm containing the search terms. + */ + Query( DataForm* form ); + + /** + * Creates a new object that can be used to carry out a search. + * @param fields Bit-wise ORed FieldEnum values describing the valid (i.e., set) + * fields in the @b values parameter. + * @param values Contains the phrases to search for. + */ + Query( int fields, const SearchFieldStruct& values ); + + /** + * Creates a new object that can be used to request search fields. + * Optionally, it can parse the given Tag. + * @param tag The Tag to parse. + */ + Query( const Tag* tag = 0 ); + + /** + * Virtual Destructor. + */ + virtual ~Query(); + + /** + * Returns the contained search form, if any. + * @return The search form. May be 0. + */ + const DataForm* form() const { return m_form; } + + /** + * Returns the search instructions, if given + * @return The search instructions. + */ + const std::string& instructions() const { return m_instructions; } + + /** + * Returns the search fields, if set. + * @return The search fields. + */ + int fields() const { return m_fields; } + + /** + * Returns the search's result, if available in legacy form. Use form() otherwise. + * @return The search's result. + */ + const SearchResultList& result() const { return m_srl; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new Query( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + Query* q = new Query(); + q->m_form = m_form ? new DataForm( *m_form ) : 0; + q->m_fields = m_fields; + q->m_values = m_values; + q->m_instructions = m_instructions; + SearchResultList::const_iterator it = m_srl.begin(); + for( ; it != m_srl.end(); ++it ) + q->m_srl.push_back( new SearchFieldStruct( *(*it) ) ); + return q; + } + + private: +#ifdef SEARCH_TEST + public: +#endif + DataForm* m_form; + int m_fields; + SearchFieldStruct m_values; + std::string m_instructions; + SearchResultList m_srl; + }; + + }; + +} + +#endif // SEARCH_H__ diff --git a/libs/libgloox/searchhandler.h b/libs/libgloox/searchhandler.h new file mode 100644 index 0000000..1a790e4 --- /dev/null +++ b/libs/libgloox/searchhandler.h @@ -0,0 +1,195 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef SEARCHHANDLER_H__ +#define SEARCHHANDLER_H__ + +#include "stanza.h" + +#include + +namespace gloox +{ + + class DataForm; + + /** + * Holds all the possible fields a server may require for searching according + * to Section 7, XEP-0055. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class SearchFieldStruct + { + public: + /** + * + */ + SearchFieldStruct() {} + + /** + * + */ + SearchFieldStruct( const std::string& first, const std::string& last, const std::string& nick, + const std::string& email ) + : m_first( first ), m_last( last ), m_nick( nick ), m_email( email ) + {} + + /** + * + */ + SearchFieldStruct( const Tag* tag ) + { + if( !tag || tag->name() != "item" || !tag->hasAttribute( "jid" ) ) + return; + + m_jid.setJID( tag->findAttribute( "jid" ) ); + const TagList& l = tag->children(); + TagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + { + if( (*it)->name() == "first" ) + m_first = (*it)->cdata(); + else if( (*it)->name() == "last" ) + m_last = (*it)->cdata(); + else if( (*it)->name() == "email" ) + m_email = (*it)->cdata(); + else if( (*it)->name() == "nick" ) + m_nick = (*it)->cdata(); + } + } + + /** + * + */ + ~SearchFieldStruct() {} + + /** + * + */ + const std::string first() const { return m_first; } + + /** + * + */ + const std::string last() const { return m_last; } + + /** + * + */ + const std::string email() const { return m_email; } + + /** + * + */ + const std::string nick() const { return m_nick; } + + /** + * + */ + Tag* tag() const + { + Tag* t = new Tag( "item" ); + t->addAttribute( "jid", m_jid.bare() ); + new Tag( t, "first", m_first ); + new Tag( t, "last", m_last ); + new Tag( t, "nick", m_nick ); + new Tag( t, "email", m_email ); + return t; + } + + private: + std::string m_first; /**< User's first name. */ + std::string m_last; /**< User's last name. */ + std::string m_nick; /**< User's nickname. */ + std::string m_email; /**< User's email. */ + JID m_jid; /**< User's JID. */ + }; + + /** + * The possible fields of a XEP-0055 user search. + */ + enum SearchFieldEnum + { + SearchFieldFirst = 1, /**< Search in first names. */ + SearchFieldLast = 2, /**< Search in last names. */ + SearchFieldNick = 4, /**< Search in nicknames. */ + SearchFieldEmail = 8 /**< Search in email addresses. */ + }; + + /** + * A list of directory entries returned by a search. + */ + typedef std::list SearchResultList; + + /** + * @brief A virtual interface that enables objects to receive Jabber Search (XEP-0055) results. + * + * A class implementing this interface can receive the result of a Jabber Search. + * + * @author Jakob Schroeter + * @since 0.8.5 + */ + class GLOOX_API SearchHandler + { + public: + /** + * Virtual Destructor. + */ + virtual ~SearchHandler() {} + + /** + * This function is called to announce the searchable fields a directory supports. It is the result + * of a call to @link gloox::Search::fetchSearchFields Search::fetchSearchFields() @endlink. + * @param directory The directory that was queried. + * @param fields Bit-wise ORed SearchFieldEnum values. + * @param instructions Plain-text instructions for the end user. + */ + virtual void handleSearchFields( const JID& directory, int fields, + const std::string& instructions ) = 0; + + /** + * This function is called to announce the searchable fields a directory supports. It is the result + * of a call to @link gloox::Search::fetchSearchFields Search::fetchSearchFields() @endlink. + * @param directory The directory that was queried. + * @param form A DataForm describing the valid searchable fields. Do not delete the form. + */ + virtual void handleSearchFields( const JID& directory, const DataForm* form ) = 0; + + /** + * This function is called to let the SearchHandler know about the results of the search. + * @param directory The searched directory. + * @param resultList A list of SearchFieldStructs. May be empty. + */ + virtual void handleSearchResult( const JID& directory, const SearchResultList& resultList ) = 0; + + /** + * This function is called to let the SearchHandler know about the result of the search. + * @param directory The searched directory. + * @param form A DataForm containing the search results. Do not delete the form. + */ + virtual void handleSearchResult( const JID& directory, const DataForm* form ) = 0; + + /** + * This function is called if a error occured as a result to a search or search field request. + * @param directory The queried/searched directory. + * @param error The error. May be 0. + */ + virtual void handleSearchError( const JID& directory, const Error* error ) = 0; + + }; + +} + +#endif // SEARCHHANDLER_H__ diff --git a/libs/libgloox/sha.cpp b/libs/libgloox/sha.cpp new file mode 100644 index 0000000..6e0266e --- /dev/null +++ b/libs/libgloox/sha.cpp @@ -0,0 +1,250 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#include "config.h" + +#include "sha.h" +#include "gloox.h" + +#include + +namespace gloox +{ + + SHA::SHA() + { + init(); + } + + SHA::~SHA() + { + } + + void SHA::init() + { + Length_Low = 0; + Length_High = 0; + Message_Block_Index = 0; + + H[0] = 0x67452301; + H[1] = 0xEFCDAB89; + H[2] = 0x98BADCFE; + H[3] = 0x10325476; + H[4] = 0xC3D2E1F0; + + m_finished = false; + m_corrupted = false; + } + + void SHA::reset() + { + init(); + } + + const std::string SHA::hex() + { + if( m_corrupted ) + return EmptyString; + + finalize(); + + char buf[41]; + for( int i = 0; i < 20; ++i ) + sprintf( buf + i * 2, "%02x", (unsigned char)( H[i >> 2] >> ( ( 3 - ( i & 3 ) ) << 3 ) ) ); + + return std::string( buf, 40 ); + } + + const std::string SHA::binary() + { + if( !m_finished ) + finalize(); + + unsigned char digest[20]; + for( int i = 0; i < 20; ++i ) + digest[i] = (unsigned char)( H[i >> 2] >> ( ( 3 - ( i & 3 ) ) << 3 ) ); + + return std::string( (char*)digest, 20 ); + } + + void SHA::finalize() + { + if( !m_finished ) + { + pad(); + m_finished = true; + } + } + + void SHA::feed( const unsigned char* data, unsigned length ) + { + if( !length ) + return; + + if( m_finished || m_corrupted ) + { + m_corrupted = true; + return; + } + + while( length-- && !m_corrupted ) + { + Message_Block[Message_Block_Index++] = ( *data & 0xFF ); + + Length_Low += 8; + Length_Low &= 0xFFFFFFFF; + if( Length_Low == 0 ) + { + Length_High++; + Length_High &= 0xFFFFFFFF; + if( Length_High == 0 ) + { + m_corrupted = true; + } + } + + if( Message_Block_Index == 64 ) + { + process(); + } + + ++data; + } + } + + void SHA::feed( const std::string& data ) + { + feed( (const unsigned char*)data.c_str(), (int)data.length() ); + } + + void SHA::process() + { + const unsigned K[] = { 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 + }; + int t; + unsigned temp; + unsigned W[80]; + unsigned A, B, C, D, E; + + for( t = 0; t < 16; t++ ) + { + W[t] = ((unsigned) Message_Block[t * 4]) << 24; + W[t] |= ((unsigned) Message_Block[t * 4 + 1]) << 16; + W[t] |= ((unsigned) Message_Block[t * 4 + 2]) << 8; + W[t] |= ((unsigned) Message_Block[t * 4 + 3]); + } + + for( t = 16; t < 80; ++t ) + { + W[t] = shift( 1, W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16] ); + } + + A = H[0]; + B = H[1]; + C = H[2]; + D = H[3]; + E = H[4]; + + for( t = 0; t < 20; ++t ) + { + temp = shift( 5, A ) + ( ( B & C ) | ( ( ~B ) & D ) ) + E + W[t] + K[0]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = shift( 30, B ); + B = A; + A = temp; + } + + for( t = 20; t < 40; ++t ) + { + temp = shift( 5, A ) + ( B ^ C ^ D ) + E + W[t] + K[1]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = shift( 30, B ); + B = A; + A = temp; + } + + for( t = 40; t < 60; ++t ) + { + temp = shift( 5, A ) + ( ( B & C ) | ( B & D ) | ( C & D ) ) + E + W[t] + K[2]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = shift( 30, B ); + B = A; + A = temp; + } + + for( t = 60; t < 80; ++t ) + { + temp = shift( 5, A ) + ( B ^ C ^ D ) + E + W[t] + K[3]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = shift( 30, B ); + B = A; + A = temp; + } + + H[0] = ( H[0] + A ) & 0xFFFFFFFF; + H[1] = ( H[1] + B ) & 0xFFFFFFFF; + H[2] = ( H[2] + C ) & 0xFFFFFFFF; + H[3] = ( H[3] + D ) & 0xFFFFFFFF; + H[4] = ( H[4] + E ) & 0xFFFFFFFF; + + Message_Block_Index = 0; + } + + void SHA::pad() + { + Message_Block[Message_Block_Index++] = 0x80; + + if( Message_Block_Index > 55 ) + { + while( Message_Block_Index < 64 ) + { + Message_Block[Message_Block_Index++] = 0; + } + + process(); + } + + while( Message_Block_Index < 56 ) + { + Message_Block[Message_Block_Index++] = 0; + } + + Message_Block[56] = static_cast( ( Length_High >> 24 ) & 0xFF ); + Message_Block[57] = static_cast( ( Length_High >> 16 ) & 0xFF ); + Message_Block[58] = static_cast( ( Length_High >> 8 ) & 0xFF ); + Message_Block[59] = static_cast( ( Length_High ) & 0xFF ); + Message_Block[60] = static_cast( ( Length_Low >> 24 ) & 0xFF ); + Message_Block[61] = static_cast( ( Length_Low >> 16 ) & 0xFF ); + Message_Block[62] = static_cast( ( Length_Low >> 8 ) & 0xFF ); + Message_Block[63] = static_cast( ( Length_Low ) & 0xFF ); + + process(); + } + + + unsigned SHA::shift( int bits, unsigned word ) + { + return ( ( word << bits ) & 0xFFFFFFFF) | ( ( word & 0xFFFFFFFF ) >> ( 32-bits ) ); + } + +} diff --git a/libs/libgloox/sha.h b/libs/libgloox/sha.h new file mode 100644 index 0000000..0eabd5e --- /dev/null +++ b/libs/libgloox/sha.h @@ -0,0 +1,98 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#ifndef SHA_H__ +#define SHA_H__ + +#include "macros.h" + +#include + +namespace gloox +{ + + /** + * @brief An implementation of SHA1. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API SHA + { + + public: + /** + * Constructs a new SHA object. + */ + SHA(); + + /** + * Virtual Destructor. + */ + virtual ~SHA(); + + /** + * Resets the internal state. + */ + void reset(); + + /** + * Finalizes the hash computation. + */ + void finalize(); + + /** + * Returns the message digest in hex notation. Finalizes the hash if finalize() + * has not been called before. + * @return The message digest. + */ + const std::string hex(); + + /** + * Returns the raw binary message digest. Finalizes the hash if finalize() + * has not been called before. + * @return The message raw binary digest. + */ + const std::string binary(); + + /** + * Provide input to SHA1. + * @param data The data to compute the digest of. + * @param length The size of the data in bytes. + */ + void feed( const unsigned char* data, unsigned length ); + + /** + * Provide input to SHA1. + * @param data The data to compute the digest of. + */ + void feed( const std::string& data ); + + private: + void process(); + void pad(); + inline unsigned shift( int bits, unsigned word ); + void init(); + + unsigned H[5]; + unsigned Length_Low; + unsigned Length_High; + unsigned char Message_Block[64]; + int Message_Block_Index; + bool m_finished; + bool m_corrupted; + + }; + +} + +#endif // SHA_H__ diff --git a/libs/libgloox/shim.cpp b/libs/libgloox/shim.cpp new file mode 100644 index 0000000..2aa1dc1 --- /dev/null +++ b/libs/libgloox/shim.cpp @@ -0,0 +1,71 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#include "shim.h" +#include "tag.h" + +namespace gloox +{ + + SHIM::SHIM( const HeaderList& hl ) + : StanzaExtension( ExtSHIM ), m_headers( hl ) + { + } + + SHIM::SHIM( const Tag* tag ) + : StanzaExtension( ExtSHIM ) + { + if( !tag || tag->name() != "headers" || tag->xmlns() != XMLNS_SHIM ) + return; + + const TagList& l = tag->children(); + TagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + { + if( (*it)->name() != "header" || !(*it)->hasAttribute( "name" ) ) + return; + + m_headers.insert( std::make_pair( (*it)->findAttribute( "name" ), (*it)->cdata() ) ); + } + } + + SHIM::~SHIM() + { + } + + const std::string& SHIM::filterString() const + { + static const std::string filter = "/presence/headers[@xmlns='" + XMLNS_SHIM + "']" + "|/message/headers[@xmlns='" + XMLNS_SHIM + "']" + "|/iq/*/headers[@xmlns='" + XMLNS_SHIM + "']"; + return filter; + } + + Tag* SHIM::tag() const + { + if( !m_headers.size() ) + return 0; + + Tag* t = new Tag( "headers" ); + t->setXmlns( XMLNS_SHIM ); + + HeaderList::const_iterator it = m_headers.begin(); + for( ; it != m_headers.end(); ++it ) + { + Tag* h = new Tag( t, "header" ); + h->addAttribute( "name", (*it).first ); + h->setCData( (*it).second ); + } + return t; + } + +} diff --git a/libs/libgloox/shim.h b/libs/libgloox/shim.h new file mode 100644 index 0000000..fbe0197 --- /dev/null +++ b/libs/libgloox/shim.h @@ -0,0 +1,91 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#ifndef SHIM_H__ +#define SHIM_H__ + +#include "stanzaextension.h" +#include "macros.h" + +#include +#include + +namespace gloox +{ + + class Tag; + + /** + * @brief An implementation/abstraction of Stanza Headers and Internet Metadata (SHIM, XEP-0131). + * + * XEP Version: 1.2 + * + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API SHIM : public StanzaExtension + { + public: + /** + * A list of SHIM headers (name & value). + */ + typedef std::map HeaderList; + + /** + * Creates a new SHIM object containing the given headers. + * @param hl The list of headers. + */ + SHIM( const HeaderList& hl ); + + /** + * Creates a new SHIM object from the given Tag. + * @param tag The Tag to parse. + */ + SHIM( const Tag* tag = 0 ); + + /** + * Returns the headers. + * @return The headers. + */ + const HeaderList& headers() const { return m_headers; } + + /** + * Virtual destructor. + */ + virtual ~SHIM(); + + // re-implemented from StanzaExtension + virtual const std::string& filterString() const; + + // re-implemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new SHIM( tag ); + } + + // re-implemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new SHIM( *this ); + } + + private: + HeaderList m_headers; + + }; + +} + +#endif // SHIM_H__ diff --git a/libs/libgloox/sihandler.h b/libs/libgloox/sihandler.h new file mode 100644 index 0000000..c0c69dd --- /dev/null +++ b/libs/libgloox/sihandler.h @@ -0,0 +1,70 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef SIHANDLER_H__ +#define SIHANDLER_H__ + +#include "macros.h" +#include "simanager.h" + +#include + +namespace gloox +{ + + class IQ; + class Tag; + class JID; + + /** + * @brief An abstract base class to handle results of outgoing SI requests, i.e. you requested a stream + * (using SIManager::requestSI()) to send a file to a remote entity. + * + * You should usually not need to use this class directly, unless your profile is not supported + * by gloox. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API SIHandler + { + + public: + /** + * Virtual destructor. + */ + virtual ~SIHandler() {} + + /** + * This function is called to handle results of outgoing SI requests, i.e. you requested a stream + * (using SIManager::requestSI()) to send a file to a remote entity. + * @param from The remote SI receiver. + * @param to The SI requestor. Usually oneself. Used in component scenario. + * @param sid The stream ID. + * @param si The request's complete SI. + */ + virtual void handleSIRequestResult( const JID& from, const JID& to, const std::string& sid, + const SIManager::SI& si ) = 0; + + /** + * This function is called to handle a request error or decline. + * @param iq The complete error stanza. + * @param sid The request's SID. + */ + virtual void handleSIRequestError( const IQ& iq, const std::string& sid ) = 0; + + }; + +} + +#endif // SIHANDLER_H__ diff --git a/libs/libgloox/simanager.cpp b/libs/libgloox/simanager.cpp new file mode 100644 index 0000000..0162c6d --- /dev/null +++ b/libs/libgloox/simanager.cpp @@ -0,0 +1,259 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "simanager.h" + +#include "siprofilehandler.h" +#include "sihandler.h" +#include "clientbase.h" +#include "disco.h" +#include "error.h" + +namespace gloox +{ + + // ---- SIManager::SI ---- + SIManager::SI::SI( const Tag* tag ) + : StanzaExtension( ExtSI ), m_tag1( 0 ), m_tag2( 0 ) + { + if( !tag || tag->name() != "si" || tag->xmlns() != XMLNS_SI ) + return; + + m_valid = true; + + m_id = tag->findAttribute( "id" ); + m_mimetype = tag->findAttribute( "mime-type" ); + m_profile = tag->findAttribute( "profile" ); + + Tag* c = tag->findChild( "file", "xmlns", XMLNS_SI_FT ); + if ( c ) + m_tag1 = c->clone(); + c = tag->findChild( "feature", "xmlns", XMLNS_FEATURE_NEG ); + if( c ) + m_tag2 = c->clone(); + } + + SIManager::SI::SI( Tag* tag1, Tag* tag2, const std::string& id, + const std::string& mimetype, const std::string& profile ) + : StanzaExtension( ExtSI ), m_tag1( tag1 ), m_tag2( tag2 ), + m_id( id ), m_mimetype( mimetype ), m_profile( profile ) + { + m_valid = true; + } + + SIManager::SI::~SI() + { + delete m_tag1; + delete m_tag2; + } + + const std::string& SIManager::SI::filterString() const + { + static const std::string filter = "/iq/si[@xmlns='" + XMLNS_SI + "']"; + return filter; + } + + Tag* SIManager::SI::tag() const + { + if( !m_valid ) + return 0; + + Tag* t = new Tag( "si" ); + t->setXmlns( XMLNS_SI ); + if( !m_id.empty() ) + t->addAttribute( "id", m_id ); + if( !m_mimetype.empty() ) + t->addAttribute( "mime-type", m_mimetype.empty() ? "binary/octet-stream" : m_mimetype ); + if( !m_profile.empty() ) + t->addAttribute( "profile", m_profile ); + if( m_tag1 ) + t->addChildCopy( m_tag1 ); + if( m_tag2 ) + t->addChildCopy( m_tag2 ); + + return t; + } + // ---- ~SIManager::SI ---- + + // ---- SIManager ---- + SIManager::SIManager( ClientBase* parent, bool advertise ) + : m_parent( parent ), m_advertise( advertise ) + { + if( m_parent ) + { + m_parent->registerStanzaExtension( new SI() ); + m_parent->registerIqHandler( this, ExtSI ); + if( m_parent->disco() && m_advertise ) + m_parent->disco()->addFeature( XMLNS_SI ); + } + } + + SIManager::~SIManager() + { + if( m_parent ) + { + m_parent->removeIqHandler( this, ExtSI ); + m_parent->removeIDHandler( this ); + if( m_parent->disco() && m_advertise ) + m_parent->disco()->removeFeature( XMLNS_SI ); + } + } + + const std::string SIManager::requestSI( SIHandler* sih, const JID& to, const std::string& profile, + Tag* child1, Tag* child2, const std::string& mimetype, + const JID& from, const std::string& sid ) + { + if( !m_parent || !sih ) + return EmptyString; + + const std::string& id = m_parent->getID(); + const std::string& sidToUse = sid.empty() ? m_parent->getID() : sid; + + IQ iq( IQ::Set, to, id ); + iq.addExtension( new SI( child1, child2, sidToUse, mimetype, profile ) ); + if( from ) + iq.setFrom( from ); + + TrackStruct t; + t.sid = sidToUse; + t.profile = profile; + t.sih = sih; + m_track[id] = t; + m_parent->send( iq, this, OfferSI ); + + return sidToUse; + } + + void SIManager::acceptSI( const JID& to, const std::string& id, Tag* child1, Tag* child2, const JID& from ) + { + IQ iq( IQ::Result, to, id ); + iq.addExtension( new SI( child1, child2 ) ); + if( from ) + iq.setFrom( from ); + + m_parent->send( iq ); + } + + void SIManager::declineSI( const JID& to, const std::string& id, SIError reason, const std::string& text ) + { + IQ iq( IQ::Error, to, id ); + Error* error; + if( reason == NoValidStreams || reason == BadProfile ) + { + Tag* appError = 0; + if( reason == NoValidStreams ) + appError = new Tag( "no-valid-streams", XMLNS, XMLNS_SI ); + else if( reason == BadProfile ) + appError = new Tag( "bad-profile", XMLNS, XMLNS_SI ); + error = new Error( StanzaErrorTypeCancel, StanzaErrorBadRequest, appError ); + } + else + { + error = new Error( StanzaErrorTypeCancel, StanzaErrorForbidden ); + if( !text.empty() ) + error->text( text ); + } + + iq.addExtension( error ); + m_parent->send( iq ); + } + + void SIManager::registerProfile( const std::string& profile, SIProfileHandler* sih ) + { + if( !sih || profile.empty() ) + return; + + m_handlers[profile] = sih; + + if( m_parent && m_advertise && m_parent->disco() ) + m_parent->disco()->addFeature( profile ); + } + + void SIManager::removeProfile( const std::string& profile ) + { + if( profile.empty() ) + return; + + m_handlers.erase( profile ); + + if( m_parent && m_advertise && m_parent->disco() ) + m_parent->disco()->removeFeature( profile ); + } + + bool SIManager::handleIq( const IQ& iq ) + { + TrackMap::iterator itt = m_track.find( iq.id() ); + if( itt != m_track.end() ) + return false; + + const SI* si = iq.findExtension( ExtSI ); + if( !si || si->profile().empty() ) + return false; + + HandlerMap::const_iterator it = m_handlers.find( si->profile() ); + if( it != m_handlers.end() && (*it).second ) + { + (*it).second->handleSIRequest( iq.from(), iq.to(), iq.id(), *si ); + return true; + } + + return false; + } + + void SIManager::handleIqID( const IQ& iq, int context ) + { + switch( iq.subtype() ) + { + case IQ::Result: + if( context == OfferSI ) + { + TrackMap::iterator it = m_track.find( iq.id() ); + if( it != m_track.end() ) + { + const SI* si = iq.findExtension( ExtSI ); + if( !si /*|| si->profile().empty()*/ ) + return; + +// Tag* si = iq.query(); +// Tag* ptag = 0; +// Tag* fneg = 0; +// if( si && si->name() == "si" && si->xmlns() == XMLNS_SI ) +// { +// ptag = si->findChildWithAttrib( XMLNS, (*it).second.profile ); +// fneg = si->findChild( "feature", XMLNS, XMLNS_FEATURE_NEG ); +// } + + // FIXME: remove above commented code and + // check corectness of last 3 params! + (*it).second.sih->handleSIRequestResult( iq.from(), iq.to(), (*it).second.sid, *si ); + m_track.erase( it ); + } + } + break; + case IQ::Error: + if( context == OfferSI ) + { + TrackMap::iterator it = m_track.find( iq.id() ); + if( it != m_track.end() ) + { + (*it).second.sih->handleSIRequestError( iq, (*it).second.sid ); + m_track.erase( it ); + } + } + break; + default: + break; + } + } + +} diff --git a/libs/libgloox/simanager.h b/libs/libgloox/simanager.h new file mode 100644 index 0000000..ace715a --- /dev/null +++ b/libs/libgloox/simanager.h @@ -0,0 +1,245 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef SIMANAGER_H__ +#define SIMANAGER_H__ + +#include "iqhandler.h" + +namespace gloox +{ + + class ClientBase; + class SIProfileHandler; + class SIHandler; + + /** + * @brief This class manages streams initiated using XEP-0095. + * + * You need only one SIManager object per ClientBase instance. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API SIManager : public IqHandler + { + + public: + /** + * SI error conditions. + */ + enum SIError + { + NoValidStreams, /**< None of the stream types are acceptable */ + BadProfile, /**< Profile is not understood. */ + RequestRejected /**< SI request was rejected. */ + }; + + class SI : public StanzaExtension + { + public: + /** + * Constructs a new SI object from the given Tag. + * @param tag The Tag to parse. + */ + SI( const Tag* tag = 0 ); + + /** + * Constructs a new SI object, wrapping the given Tags. + * @param tag1 Tag 1. + * @param tag2 Tag 2. + */ + SI( Tag* tag1, Tag* tag2, const std::string& id = EmptyString, + const std::string& mimetype = EmptyString, + const std::string& profile = EmptyString ); + + /** + * Virtual destructor. + */ + virtual ~SI(); + + /** + * Returns the current profile namespace. + * @return The profile namespace. + */ + const std::string& profile() const { return m_profile; }; + + /** + * Returns the mime-type. + * @return The mime-type. + */ + const std::string& mimetype() const { return m_mimetype; }; + + /** + * Returns the SI's ID. + * @return The SI's id. + */ + const std::string& id() const { return m_id; }; + + /** + * Returns the first SI child tag. + * @return The first SI child tag. + * @todo Use real objects. + */ + const Tag* tag1() const { return m_tag1; }; + + /** + * Returns the second SI child tag. + * @return The second SI child tag. + * @todo Use real objects. + */ + const Tag* tag2() const { return m_tag2; }; + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new SI( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + SI* s = new SI(); + s->m_tag1 = m_tag1 ? m_tag1->clone() : 0; + s->m_tag2 = m_tag2 ? m_tag2->clone() : 0; + s->m_id = m_id; + s->m_mimetype = m_mimetype; + s->m_profile = m_profile; + return s; + } + + private: + Tag* m_tag1; + Tag* m_tag2; + std::string m_id; + std::string m_mimetype; + std::string m_profile; + }; + + /** + * Constructor. + * @param parent The ClientBase to use for communication. + * @param advertise Whether to advertise SI capabilities by disco. Defaults to true. + */ + SIManager( ClientBase* parent, bool advertise = true ); + + /** + * Virtual destructor. + */ + virtual ~SIManager(); + + /** + * Starts negotiating a stream with a remote entity. + * @param sih The SIHandler to handle the result of this request. + * @param to The entity to talk to. + * @param profile The SI profile to use. See XEP-0095 for more info. + * @param child1 The first of the two allowed children of the SI offer. See + * XEP-0095 for more info. + * @param child2 The second of the two allowed children of the SI offer. See + * XEP-0095 for more info. Defaults to 0. + * @param mimetype The stream's/file's mime-type. Defaults to 'binary/octet-stream'. + * @param from An optional 'from' address to stamp outgoing requests with. + * Used in component scenario only. Defaults to empty JID. + * @param sid Optionally specify a stream ID (SID). If empty, one will be generated. + * @return The requested stream's ID (SID). Empty if SIHandler or ClientBase are invalid. + * @note The SIManager claims ownership of the Tags supplied to this function, and will + * delete them after use. + */ + const std::string requestSI( SIHandler* sih, const JID& to, const std::string& profile, Tag* child1, + Tag* child2 = 0, const std::string& mimetype = "binary/octet-stream", + const JID& from = JID(), const std::string& sid = EmptyString ); + + /** + * Call this function to accept an SI request previously announced by means of + * SIProfileHandler::handleSIRequest(). + * @param to The requestor. + * @param id The request's id, as passed to SIProfileHandler::handleSIRequest(). + * @param child1 The <feature/> child of the SI request. See XEP-0095 for details. + * @param child2 The profile-specific child of the SI request. May be 0. See XEP-0095 + * for details. + * @param from An optional 'from' address to stamp outgoing stanzas with. + * Used in component scenario only. Defaults to empty JID. + * @note The SIManager claims ownership of the Tags supplied to this function, and will + * delete them after use. + */ + void acceptSI( const JID& to, const std::string& id, Tag* child1, Tag* child2 = 0, const JID& from = JID() ); + + /** + * Call this function to decline an SI request previously announced by means of + * SIProfileHandler::handleSIRequest(). + * @param to The requestor. + * @param id The request's id, as passed to SIProfileHandler::handleSIRequest(). + * @param reason The reason for the reject. + * @param text An optional human-readable text explaining the decline. + */ + void declineSI( const JID& to, const std::string& id, SIError reason, + const std::string& text = EmptyString ); + + /** + * Registers the given SIProfileHandler to handle requests for the + * given SI profile namespace. The profile will be advertised by disco (unless disabled in + * the ctor). + * @param profile The complete profile namespace, e.g. + * http://jabber.org/protocol/si/profile/file-transfer. + * @param sih The profile handler. + */ + void registerProfile( const std::string& profile, SIProfileHandler* sih ); + + /** + * Un-registers the given profile. + * @param profile The profile's namespace to un-register. + */ + void removeProfile( const std::string& profile ); + + // reimplemented from IqHandler. + virtual bool handleIq( const IQ& iq ); + + // reimplemented from IqHandler. + virtual void handleIqID( const IQ& iq, int context ); + + private: +#ifdef SIMANAGER_TEST + public: +#endif + enum TrackContext + { + OfferSI + }; + + struct TrackStruct + { + std::string sid; + std::string profile; + SIHandler* sih; + }; + typedef std::map TrackMap; + TrackMap m_track; + + ClientBase* m_parent; + + typedef std::map HandlerMap; + HandlerMap m_handlers; + + bool m_advertise; + + }; + +} + +#endif // SIMANAGER_H__ diff --git a/libs/libgloox/siprofileft.cpp b/libs/libgloox/siprofileft.cpp new file mode 100644 index 0000000..4836efd --- /dev/null +++ b/libs/libgloox/siprofileft.cpp @@ -0,0 +1,312 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "siprofileft.h" + +#include "clientbase.h" +#include "siprofilefthandler.h" +#include "simanager.h" +#include "dataform.h" +#include "inbandbytestream.h" +#include "oob.h" +#include "socks5bytestream.h" +#include "socks5bytestreammanager.h" + +#include +#include + +namespace gloox +{ + + SIProfileFT::SIProfileFT( ClientBase* parent, SIProfileFTHandler* sipfth, SIManager* manager, + SOCKS5BytestreamManager* s5Manager ) + : m_parent( parent ), m_manager( manager ), m_handler( sipfth ), + m_socks5Manager( s5Manager ), m_delManager( false ), + m_delS5Manager( false ) + { + if( !m_manager ) + { + m_delManager = true; + m_manager = new SIManager( m_parent ); + } + + m_manager->registerProfile( XMLNS_SI_FT, this ); + + if( !m_socks5Manager ) + { + m_socks5Manager = new SOCKS5BytestreamManager( m_parent, this ); + m_delS5Manager = true; + } + } + + SIProfileFT::~SIProfileFT() + { + m_manager->removeProfile( XMLNS_SI_FT ); + + if( m_delManager ) + delete m_manager; + + if( m_socks5Manager && m_delS5Manager ) + delete m_socks5Manager; + } + + const std::string SIProfileFT::requestFT( const JID& to, const std::string& name, long size, + const std::string& hash, const std::string& desc, + const std::string& date, const std::string& mimetype, + int streamTypes, const JID& from, + const std::string& sid ) + { + if( name.empty() || size <= 0 || !m_manager ) + return EmptyString; + + Tag* file = new Tag( "file", XMLNS, XMLNS_SI_FT ); + file->addAttribute( "name", name ); + file->addAttribute( "size", size ); + if( !hash.empty() ) + file->addAttribute( "hash", hash ); + if( !date.empty() ) + file->addAttribute( "date", date ); + if( !desc.empty() ) + new Tag( file, "desc", desc ); + + Tag* feature = new Tag( "feature", XMLNS, XMLNS_FEATURE_NEG ); + DataForm df( TypeForm ); + DataFormField* dff = df.addField( DataFormField::TypeListSingle, "stream-method" ); + StringMultiMap sm; + if( streamTypes & FTTypeS5B ) + sm.insert( std::make_pair( "s5b", XMLNS_BYTESTREAMS ) ); + if( streamTypes & FTTypeIBB ) + sm.insert( std::make_pair( "ibb", XMLNS_IBB ) ); + if( streamTypes & FTTypeOOB ) + sm.insert( std::make_pair( "oob", XMLNS_IQ_OOB ) ); + dff->setOptions( sm ); + feature->addChild( df.tag() ); + + return m_manager->requestSI( this, to, XMLNS_SI_FT, file, feature, mimetype, from, sid ); + } + + void SIProfileFT::acceptFT( const JID& to, const std::string& sid, StreamType type, const JID& from ) + { + if( !m_manager ) + return; + + if( m_id2sid.find( sid ) == m_id2sid.end() ) + return; + + const std::string& id = m_id2sid[sid]; + + Tag* feature = new Tag( "feature", XMLNS, XMLNS_FEATURE_NEG ); + DataFormField* dff = new DataFormField( "stream-method" ); + switch( type ) + { + case FTTypeAll: + case FTTypeS5B: + dff->setValue( XMLNS_BYTESTREAMS ); + break; + case FTTypeIBB: + dff->setValue( XMLNS_IBB ); + if( m_handler ) + { + InBandBytestream* ibb = new InBandBytestream( m_parent, m_parent->logInstance(), to, + from ? from : m_parent->jid(), sid ); + m_handler->handleFTBytestream( ibb ); + } + break; + case FTTypeOOB: + dff->setValue( XMLNS_IQ_OOB ); + break; + } + DataForm df( TypeSubmit ); + df.addField( dff ); + feature->addChild( df.tag() ); + + m_manager->acceptSI( to, id, 0, feature, from ); + } + + void SIProfileFT::declineFT( const JID& to, const std::string& sid, SIManager::SIError reason, + const std::string& text ) + { + if( m_id2sid.find( sid ) == m_id2sid.end() || !m_manager ) + return; + + m_manager->declineSI( to, m_id2sid[sid], reason, text ); + } + + void SIProfileFT::dispose( Bytestream* bs ) + { + if( bs ) + { + if( bs->type() == Bytestream::S5B && m_socks5Manager ) + m_socks5Manager->dispose( static_cast( bs ) ); + else + delete bs; + } + } + + void SIProfileFT::cancel( Bytestream* bs ) + { + if( !bs ) + return; + + if( m_id2sid.find( bs->sid() ) == m_id2sid.end() || !m_manager ) + return; + + if( bs->type() == Bytestream::S5B && m_socks5Manager ) + m_socks5Manager->rejectSOCKS5Bytestream( bs->sid(), StanzaErrorServiceUnavailable ); + + dispose( bs ); + } + + void SIProfileFT::setStreamHosts( StreamHostList hosts ) + { + if( m_socks5Manager ) + m_socks5Manager->setStreamHosts( hosts ); + } + + void SIProfileFT::addStreamHost( const JID& jid, const std::string& host, int port ) + { + if( m_socks5Manager ) + m_socks5Manager->addStreamHost( jid, host, port ); + } + + void SIProfileFT::handleSIRequest( const JID& from, const JID& to, const std::string& id, + const SIManager::SI& si ) + { + if( si.profile() != XMLNS_SI_FT || !si.tag1() ) + return; + + if( m_handler ) + { + const Tag* t = si.tag1()->findChild( "desc" ); + const std::string& desc = t ? t->cdata() : EmptyString; + + const std::string& mt = si.mimetype(); + int types = 0; + + if( si.tag2() ) + { + const DataForm df( si.tag2()->findChild( "x", XMLNS, XMLNS_X_DATA ) ); + const DataFormField* dff = df.field( "stream-method" ); + + if( dff ) + { + const StringMultiMap& options = dff->options(); + StringMultiMap::const_iterator it = options.begin(); + for( ; it != options.end(); ++it ) + { + if( (*it).second == XMLNS_BYTESTREAMS ) + types |= FTTypeS5B; + else if( (*it).second == XMLNS_IBB ) + types |= FTTypeIBB; + else if( (*it).second == XMLNS_IQ_OOB ) + types |= FTTypeOOB; + } + } + } + + const std::string& sid = si.id(); + m_id2sid[sid] = id; + m_handler->handleFTRequest( from, to, sid, si.tag1()->findAttribute( "name" ), + atol( si.tag1()->findAttribute( "size" ).c_str() ), + si.tag1()->findAttribute( "hash" ), + si.tag1()->findAttribute( "date" ), + mt.empty() ? "binary/octet-stream" : mt, + desc, types ); + } + } + + void SIProfileFT::handleSIRequestResult( const JID& from, const JID& to, const std::string& sid, + const SIManager::SI& si ) + { + if( si.tag2() ) + { + const DataForm df( si.tag2()->findChild( "x", XMLNS, XMLNS_X_DATA ) ); + const DataFormField* dff = df.field( "stream-method" ); + + if( dff ) + { + if( m_socks5Manager && dff->value() == XMLNS_BYTESTREAMS ) + { + // check return value: + m_socks5Manager->requestSOCKS5Bytestream( from, SOCKS5BytestreamManager::S5BTCP, sid, to ); + } + else if( m_handler ) + { + if( dff->value() == XMLNS_IBB ) + { + InBandBytestream* ibb = new InBandBytestream( m_parent, m_parent->logInstance(), + to ? to : m_parent->jid(), from, sid ); + + m_handler->handleFTBytestream( ibb ); + } + else if( dff->value() == XMLNS_IQ_OOB ) + { + const std::string& url = m_handler->handleOOBRequestResult( from, to, sid ); + if( !url.empty() ) + { + const std::string& id = m_parent->getID(); + IQ iq( IQ::Set, from, id ); + if( to ) + iq.setFrom( to ); + + iq.addExtension( new OOB( url, EmptyString, true ) ); + m_parent->send( iq, this, OOBSent ); + } + } + } + } + } + } + + void SIProfileFT::handleIqID( const IQ& /*iq*/, int context ) + { + switch( context ) + { + case OOBSent: +// if( iq->subtype() == IQ::Error ) +// m_handler->handleOOBError + break; + } + } + + void SIProfileFT::handleSIRequestError( const IQ& iq, const std::string& sid ) + { + if( m_handler ) + m_handler->handleFTRequestError( iq, sid ); + } + + void SIProfileFT::handleIncomingBytestreamRequest( const std::string& sid, const JID& /*from*/ ) + { +// TODO: check for valid sid/from tuple + m_socks5Manager->acceptSOCKS5Bytestream( sid ); + } + + void SIProfileFT::handleIncomingBytestream( Bytestream* bs ) + { + if( m_handler ) + m_handler->handleFTBytestream( bs ); + } + + void SIProfileFT::handleOutgoingBytestream( Bytestream* bs ) + { + if( m_handler ) + m_handler->handleFTBytestream( bs ); + } + + void SIProfileFT::handleBytestreamError( const IQ& iq, const std::string& sid ) + { + if( m_handler ) + m_handler->handleFTRequestError( iq, sid ); + } + +} diff --git a/libs/libgloox/siprofileft.h b/libs/libgloox/siprofileft.h new file mode 100644 index 0000000..b83c859 --- /dev/null +++ b/libs/libgloox/siprofileft.h @@ -0,0 +1,341 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef SIPROFILEFT_H__ +#define SIPROFILEFT_H__ + +#include "iqhandler.h" +#include "socks5bytestreammanager.h" +#include "siprofilehandler.h" +#include "sihandler.h" +#include "simanager.h" +#include "bytestreamhandler.h" + +#include +#include + +namespace gloox +{ + + class ClientBase; + class InBandBytestream; + class IQ; + class JID; + class SIProfileFTHandler; + class SOCKS5Bytestream; + + /** + * @brief An implementation of the file transfer SI profile (XEP-0096). + * + * An SIProfileFT object acts as a 'plugin' to the SIManager. SIProfileFT + * manages most of the file transfer functionality. The naming comes from the fact that + * File Transfer (FT) is a profile of Stream Initiation (SI). + * + * Usage: + * + * Create a new SIProfileFT object. It needs a ClientBase -derived object (e.g. Client) + * as well as a SIProfileFTHandler -derived object that will receive file transfer-related events. + * If you already use SI and the SIManager somewhere else, you should pass a pointer to that + * SIManager object as third parameter to SIProfileFT's constructor. + * @code + * class MyFileTransferHandler : public SIProfileFTHandler + * { + * // ... + * }; + * + * Client* client = new Client( ... ); + * // ... + * MyFileTransferHandler* mh = new MyFileTransferHandler( ... ); + * + * SIProfileFT* ft = new SIProfileFT( client, mh ); + * @endcode + * + * You are now, basically, ready to send and receive files. + * + * A couple of notes: + * @li There are two (actually two and a half) possible "techniques" to transfer files + * using SI. The first is using a peer-to-peer SOCKS5 bytestream, optionally via a + * (special) SOCKS5 proxy. + * The second techniques is using an in-band bytestream, i.e. the data is encapsulated in XMPP stanzas + * and sent through the server. + * + * @li To be able to send files using the former method (SOCKS5 bytestreams), you may need + * access to a SOCKS5 bytestream proxy (called StreamHost). This is especially true if either + * or both of sender and receiver are behind NATs or are otherwise blocked from establishing + * direct TCP connections. You should use Disco to query a potential SOCKS5 proxy + * for its host and port parameters and feed that information into SIProfileFT: + * @code + * ft->addStreamHost( JID( "proxy.server.dom" ), "101.102.103.104", 6677 ); + * @endcode + * You should @b not hard-code this information (esp. host/IP and port) into your app since + * the proxy may go down occasionally or vanish completely. + * + * @li In addition to (or even instead of) using external SOCKS5 proxies, you can use a + * SOCKS5BytestreamServer object that gloox provides: + * @code + * SOCKS5BytestreamServer* server = new SOCKS5BytestreamServer( client->logInstance(), 1234 ); + * if( server->listen() != ConnNoError ) + * printf( "port in use\n" ); + * + * ft->addStreamHost( client->jid(), my_ip, 1234 ); + * ft->registerSOCKS5BytestreamServer( server ); + * @endcode + * This listening server should then be integrated into your mainloop to have its + * @link gloox::SOCKS5BytestreamServer::recv() recv() @endlink method called from time to time. + * It is safe to put the server into its own thread. + * + * @li When you finally receive a Bytestream via the SIProfileFTHandler, you will need + * to integrate this bytestream with your mainloop, or put it into a separate thread (if + * occasional blocking is not acceptable). You will need to call + * @link gloox::Bytestream::connect() connect() @endlink on that Bytestream. For SOCKS5 bytestreams, + * this function will try to connect to each of the given StreamHosts and block until it has established + * a connection with one of them (or until all attempts failed). Further, if you want to receive + * a file via the bytestream, you will have to call recv() on the object from time to time. + * For in-band bytestreams, @link gloox::Bytestream::connect() connect() @endlink will send an "open the + * bytestream" request to the contact. + * + * @li For both stream types, + * @link gloox::BytestreamDataHandler::handleBytestreamOpen() BytestreamDataHandler::handleBytestreamOpen() @endlink + * will announce the established bytestream. The stream then is ready to send and receive data. + * + * @li In general, both types of streams can be handled equally, i.e. there's no need to know whether + * the underlying stream really is a SOCKS5Bytestream or an InBandBytestream. + * @link gloox::Bytestream::type() Bytestream::type() @endlink tells anyway. Note, however, that + * sending large amounts of data using in-band bytestreams may trigger rate limiting in some servers. + * + * @li If you e.g. told Client to connect through a @link gloox::ConnectionHTTPProxy HTTP proxy @endlink + * or a @link gloox::ConnectionSOCKS5Proxy SOCKS5 proxy @endlink, or any other ConnectionBase -derived + * method, or even chains thereof, SIProfileFT will use the same connection types with the same + * configuration to connect to the Stream Host/SOCKS5 proxy. If this is inappropriate because you have + * e.g. a local SOCKS5 proxy inside your local network, use SOCKS5Bytestream::setConnectionImpl() to + * override the above default connection(s). + * + * @li Do @b not delete Bytestream objects manually. Use dispose() instead. + * + * @li When using the Client's JID as the first argument to addStreamHost() as in the code snippet + * above, make sure the JID is actually a full JID. If you let the server pick a resource, the call + * to Client::jid() needs to be made @b after the connection has been established and authenticated, + * because only then Client knows its full JID. This is generally a good idea, since the server + * may choose to change the resource, even if you provided one at login. + * + * @li The interal SOCKS5BytestreamServer will obviously not work across NATs. + * + * @li Using addStreamHost(), you can add as many potential StreamHosts as you like. However, you + * should add the best options (e.g. the local SOCKS5BytestreamServer) first. + * + * When cleaning up, delete the objectes you created above in the opposite order of + * creation: + * + * @code + * delete server + * delete ft; + * delete client; + * @endcode + * + * For usage examples see src/examples/ft_send.cpp and src/examples/ft_recv.cpp. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API SIProfileFT : public SIProfileHandler, public SIHandler, + public BytestreamHandler, public IqHandler + { + public: + /** + * Supported stream types. + */ + enum StreamType + { + FTTypeS5B = 1, /**< SOCKS5 Bytestreams. */ + FTTypeIBB = 2, /**< In-Band Bytestreams. */ + FTTypeOOB = 4, /**< Out-of-Band Data. */ + FTTypeAll = 0xFF /**< All types. */ + }; + + /** + * Constructor. + * @param parent The ClientBase to use for signaling. + * @param sipfth The SIProfileFTHandler to receive events. + * @param manager An optional SIManager to register with. If this is zero, SIProfileFT + * will create its own SIManager. You should pass a valid SIManager here if you are + * already using one with the @c parent ClientBase above. + * @param s5Manager An optional SOCKS5BytestreamManager to use. If this is zero, SIProfileFT + * will create its own SOCKS5BytestreamManager. You should pass a valid SOCKS5BytestreamManager + * here if you are already using one with the @c parent ClientBase above. + * @note If you passed a SIManager and/or SOCKS5BytestreamManager and/or InBandBytestreamManager + * to SIProfileFT's constructor, these objects will @b not be deleted on desctruction of SIProfileFT. + */ + SIProfileFT( ClientBase* parent, SIProfileFTHandler* sipfth, SIManager* manager = 0, + SOCKS5BytestreamManager* s5Manager = 0 ); + + /** + * Virtual destructor. + */ + virtual ~SIProfileFT(); + + /** + * Starts negotiating a file transfer with a remote entity. + * @param to The entity to send the file to. Must be a full JID. + * @param name The file's name. Mandatory and must not be empty. + * @param size The file's size. Mandatory and must be > 0. + * @param hash The file content's MD5 hash. + * @param desc A description. + * @param date The file's last modification date/time. See XEP-0082 for details. + * @param mimetype The file's mime-type. Defaults to 'binary/octet-stream' if empty. + * @param streamTypes ORed StreamType that can be used for this transfer. + * @param from An optional 'from' address to stamp outgoing requests with. + * Used in component scenario only. Defaults to empty JID. + * @param sid Optionally specify a stream ID (SID). If empty, one will be generated. + * @return The requested stream's ID (SID). Empty if conditions above (file name, size) + * are not met. + */ + const std::string requestFT( const JID& to, const std::string& name, long size, + const std::string& hash = EmptyString, + const std::string& desc = EmptyString, + const std::string& date = EmptyString, + const std::string& mimetype = EmptyString, + int streamTypes = FTTypeAll, + const JID& from = JID(), + const std::string& sid = EmptyString ); + + /** + * Call this function to accept a file transfer request previously announced by means of + * @link gloox::SIProfileFTHandler::handleFTRequest() SIProfileFTHandler::handleFTRequest() @endlink. + * @param to The requestor. + * @param sid The request's sid, as passed to SIProfileHandler::handleFTRequest(). + * @param type The desired stream type to use for this file transfer. Defaults to + * SOCKS5 Bytestream. You should not use @c FTTypeAll here. + * @param from An optional 'from' address to stamp outgoing stanzas with. + * Used in component scenario only. Defaults to empty JID. + */ + void acceptFT( const JID& to, const std::string& sid, + StreamType type = FTTypeS5B, const JID& from = JID() ); + + /** + * Call this function to decline a FT request previously announced by means of + * @link gloox::SIProfileFTHandler::handleFTRequest() SIProfileFTHandler::handleFTRequest() @endlink. + * @param to The requestor. + * @param sid The request's sid, as passed to SIProfileFTHandler::handleFTRequest(). + * @param reason The reason for the reject. + * @param text An optional human-readable text explaining the decline. + */ + void declineFT( const JID& to, const std::string& sid, SIManager::SIError reason, + const std::string& text = EmptyString ); + + /** + * Cancels the given bytestream. Most useful for SOCKS5 bytestreams where no proxies could be found. + * The given Bytestream will be deleted. + * @param bs The Bytestream to cancel. + * @note Can also be used with IBB. + */ + void cancel( Bytestream* bs ); + + /** + * To get rid of a bytestream (i.e., close and delete it), call this function. + * The remote entity will be notified about the closing of the stream. + * @param bs The bytestream to dispose. It will be deleted here. + */ + void dispose( Bytestream* bs ); + + /** + * Registers a handler that will be informed about incoming file transfer + * requests, i.e. when a remote entity wishes to send a file. + * @param sipfth A SIProfileFTHandler to register. Only one handler can be registered + * at any one time. + */ + void registerSIProfileFTHandler( SIProfileFTHandler* sipfth ) { m_handler = sipfth; } + + /** + * Removes the previously registered file transfer request handler. + */ + void removeSIProfileFTHandler() { m_handler = 0; } + + /** + * Sets a list of StreamHosts that will be used for subsequent SOCKS5 bytestream requests. + * @note At least one StreamHost is required. + * @param hosts A list of StreamHosts. + */ + void setStreamHosts( StreamHostList hosts ); + + /** + * Adds one StreamHost to the list of SOCKS5 StreamHosts. + * @param jid The StreamHost's JID. + * @param host The StreamHost's hostname. + * @param port The StreamHost's port. + */ + void addStreamHost( const JID& jid, const std::string& host, int port ); + + /** + * Tells the interal SOCKS5BytestreamManager which SOCKS5BytestreamServer handles + * peer-2-peer SOCKS5 bytestreams. + * @param server The SOCKS5BytestreamServer to use. + */ + void registerSOCKS5BytestreamServer( SOCKS5BytestreamServer* server ) + { if( m_socks5Manager ) m_socks5Manager->registerSOCKS5BytestreamServer( server ); } + + /** + * Un-registers any local SOCKS5BytestreamServer. + */ + void removeSOCKS5BytestreamServer() + { if( m_socks5Manager ) m_socks5Manager->removeSOCKS5BytestreamServer(); } + + // reimplemented from SIProfileHandler + virtual void handleSIRequest( const JID& from, const JID& to, const std::string& id, + const SIManager::SI& si ); + + // reimplemented from SIHandler + virtual void handleSIRequestResult( const JID& from, const JID& to, const std::string& sid, + const SIManager::SI& si ); + + // reimplemented from SIHandler + virtual void handleSIRequestError( const IQ& iq, const std::string& sid ); + + // reimplemented from BytestreamHandler + virtual void handleIncomingBytestreamRequest( const std::string& sid, const JID& from ); + + // reimplemented from BytestreamHandler + virtual void handleIncomingBytestream( Bytestream* bs ); + + // reimplemented from BytestreamHandler + virtual void handleOutgoingBytestream( Bytestream* bs ); + + // reimplemented from BytestreamHandler + virtual void handleBytestreamError( const IQ& iq, const std::string& sid ); + + // reimplemented from IqHandler. + virtual bool handleIq( const IQ& iq ) { (void)iq; return false; } + + // reimplemented from IqHandler. + virtual void handleIqID( const IQ& iq, int context ); + + private: + + enum TrackEnum + { + OOBSent + }; + + ClientBase* m_parent; + SIManager* m_manager; + SIProfileFTHandler* m_handler; + SOCKS5BytestreamManager* m_socks5Manager; + StreamHostList m_hosts; + StringMap m_id2sid; + bool m_delManager; + bool m_delS5Manager; + + }; + +} + +#endif // SIPROFILEFT_H__ diff --git a/libs/libgloox/siprofilefthandler.h b/libs/libgloox/siprofilefthandler.h new file mode 100644 index 0000000..3a7c0f4 --- /dev/null +++ b/libs/libgloox/siprofilefthandler.h @@ -0,0 +1,104 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef SIPROFILEFTHANDLER_H__ +#define SIPROFILEFTHANDLER_H__ + +#include "jid.h" + +#include + +namespace gloox +{ + + class JID; + class IQ; + class Bytestream; + + /** + * @brief An abstract base class to handle file transfer (FT) requests. + * + * See SIProfileFT for more information regarding file transfer. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API SIProfileFTHandler + { + + public: + /** + * Virtual destructor. + */ + virtual ~SIProfileFTHandler() {} + + /** + * This function is called to handle incoming file transfer requests, i.e. a remote entity requested + * to send a file to you. You should use either SIProfileFT::acceptFT() or + * SIProfileFT::declineFT() to accept or reject the request, respectively. + * @param from The file transfer requestor. + * @param to The file transfer recipient. Usuall oneself. Used in component scenario. + * @param sid The requested stream's ID. This sid MUST be supplied to SIProfileFT::acceptFT() + * and SIProfileFT::declineFT(), respectively. + * @param name The file name. + * @param size The file size. + * @param hash The file content's MD5 sum. + * @param date The file's last modification time. + * @param mimetype The file's mime-type. + * @param desc The file's description. + * @param stypes An ORed list of @link gloox::SIProfileFT::StreamType SIProfileFT::StreamType @endlink + * indicating the StreamTypes the initiator supports. + */ + virtual void handleFTRequest( const JID& from, const JID& to, const std::string& sid, + const std::string& name, long size, const std::string& hash, + const std::string& date, const std::string& mimetype, + const std::string& desc, int stypes ) = 0; + + /** + * This function is called to handle a request error or decline. + * @param iq The complete error stanza. + * @param sid The request's SID. + */ + virtual void handleFTRequestError( const IQ& iq, const std::string& sid ) = 0; + + /** + * This function is called to pass a negotiated bytestream (SOCKS5 or IBB). + * The bytestream is not yet open and not ready to send/receive data. + * @note To initialize the bytestream and to prepare it for data transfer + * do the following, preferable in that order: + * @li register a BytestreamDataHandler with the Bytestream, + * @li set up a separate thread for the bytestream or integrate it into + * your main loop, + * @li call its connect() method and check the return value. + * To not block your application while the data transfer and/or the connection + * attempts last, you most likely want to put the bytestream into its own + * thread or process (before calling connect() on it). It is safe to do so + * without additional synchronization. + * @param bs The bytestream. + */ + virtual void handleFTBytestream( Bytestream* bs ) = 0; + + /** + * This function is called if the contact chose OOB as the mechanism. + * @param from The remote contact's JID. + * @param to The local recipient's JID. Usually oneself. Used in component scenario. + * @param sid The stream's ID. + * @return The file's URL. + */ + virtual const std::string handleOOBRequestResult( const JID& from, const JID& to, const std::string& sid ) = 0; + + }; + +} + +#endif // SIPROFILEFTHANDLER_H__ diff --git a/libs/libgloox/siprofilehandler.h b/libs/libgloox/siprofilehandler.h new file mode 100644 index 0000000..9af75b4 --- /dev/null +++ b/libs/libgloox/siprofilehandler.h @@ -0,0 +1,62 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef SIPROFILEHANDLER_H__ +#define SIPROFILEHANDLER_H__ + +#include "jid.h" +#include "simanager.h" + +#include + +namespace gloox +{ + + class Tag; + class JID; + + /** + * @brief An abstract base class to handle SI requests for a specific profile, e.g. file transfer. + * + * You should usually not need to use this class directly, unless your profile is not supported + * by gloox. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API SIProfileHandler + { + + public: + /** + * Virtual destructor. + */ + virtual ~SIProfileHandler() {} + + /** + * This function is called to handle incoming SI requests, i.e. a remote entity requested + * a stream to send a file to you. You should use either SIManager::acceptSI() or + * SIManager::declineSI() to accept or reject the request, respectively. + * @param from The SI requestor. + * @param to The SI recipient, usually oneself. Used in component scenario. + * @param id The request's id (@b not the stream's id). This id MUST be supplied to either + * SIManager::acceptSI() or SIManager::declineSI(). + * @param si The request's complete SI. + */ + virtual void handleSIRequest( const JID& from, const JID& to, const std::string& id, const SIManager::SI& si ) = 0; + + }; + +} + +#endif // SIPROFILEHANDLER_H__ diff --git a/libs/libgloox/socks5bytestream.cpp b/libs/libgloox/socks5bytestream.cpp new file mode 100644 index 0000000..e4d80b3 --- /dev/null +++ b/libs/libgloox/socks5bytestream.cpp @@ -0,0 +1,155 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "socks5bytestream.h" +#include "bytestreamdatahandler.h" +#include "clientbase.h" +#include "connectionbase.h" +#include "connectionsocks5proxy.h" +#include "sha.h" +#include "logsink.h" + +namespace gloox +{ + + SOCKS5Bytestream::SOCKS5Bytestream( SOCKS5BytestreamManager* manager, ConnectionBase* connection, + LogSink& logInstance, const JID& initiator, const JID& target, + const std::string& sid ) + : Bytestream( Bytestream::S5B, logInstance, initiator, target, sid ), + m_manager( manager ), m_connection( 0 ), m_socks5( 0 ), m_connected( false ) + { + if( connection && connection->state() == StateConnected ) + m_open = true; + + setConnectionImpl( connection ); + } + + SOCKS5Bytestream::~SOCKS5Bytestream() + { + if( m_open ) + close(); + + if( m_socks5 ) + delete m_socks5; + } + + void SOCKS5Bytestream::setConnectionImpl( ConnectionBase* connection ) + { + if( m_socks5 ) + delete m_socks5; // deletes m_connection as well + + m_connection = connection; + + SHA sha; + sha.feed( m_sid ); + sha.feed( m_initiator.full() ); + sha.feed( m_target.full() ); + m_socks5 = new ConnectionSOCKS5Proxy( this, connection, m_logInstance, sha.hex(), 0 ); + } + + bool SOCKS5Bytestream::connect() + { + if( !m_connection || !m_socks5 || !m_manager ) + return false; + + if( m_open ) + return true; + + StreamHostList::const_iterator it = m_hosts.begin(); + for( ; it != m_hosts.end(); ++it ) + { + if( ++it == m_hosts.end() ) + m_connected = true; + --it; // FIXME ++it followed by --it is kinda ugly + m_connection->setServer( (*it).host, (*it).port ); + if( m_socks5->connect() == ConnNoError ) + { + m_proxy = (*it).jid; + m_connected = true; + return true; + } + } + + m_manager->acknowledgeStreamHost( false, JID(), EmptyString ); + return false; + } + + bool SOCKS5Bytestream::send( const std::string& data ) + { + if( !m_open || !m_connection || !m_socks5 || !m_manager ) + return false; + + return m_socks5->send( data ); + } + + ConnectionError SOCKS5Bytestream::recv( int timeout ) + { + if( !m_connection || !m_socks5 || !m_manager ) + return ConnNotConnected; + + return m_socks5->recv( timeout ); + } + + void SOCKS5Bytestream::activate() + { + m_open = true; + if( m_handler ) + m_handler->handleBytestreamOpen( this ); + } + + void SOCKS5Bytestream::close() + { + if( m_open && m_handler ) + { + m_open = false; + m_connected = false; + m_socks5->disconnect(); + m_handler->handleBytestreamClose( this ); + } + } + + void SOCKS5Bytestream::handleReceivedData( const ConnectionBase* /*connection*/, const std::string& data ) + { + if( !m_handler ) + return; + + if( !m_open ) + { + m_open = true; + m_handler->handleBytestreamOpen( this ); + } + +// if( !m_open && data.length() == 2 && data[0] == 0x05 && data[1] == 0x00 ) +// { +// printf( "received acknowleding zero byte, stream is now open\n" ); +// m_open = true; +// m_handler->handleBytestream5Open( this ); +// return; +// } + + if( m_open ) + m_handler->handleBytestreamData( this, data ); + } + + void SOCKS5Bytestream::handleConnect( const ConnectionBase* /*connection*/ ) + { + m_manager->acknowledgeStreamHost( true, m_proxy, m_sid ); + } + + void SOCKS5Bytestream::handleDisconnect( const ConnectionBase* /*connection*/, ConnectionError /*reason*/ ) + { + if( m_handler && m_connected ) + m_handler->handleBytestreamClose( this ); + } + +} diff --git a/libs/libgloox/socks5bytestream.h b/libs/libgloox/socks5bytestream.h new file mode 100644 index 0000000..d2a6201 --- /dev/null +++ b/libs/libgloox/socks5bytestream.h @@ -0,0 +1,136 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef SOCKS5BYTESTREAM_H__ +#define SOCKS5BYTESTREAM_H__ + +#include "bytestream.h" +#include "gloox.h" +#include "socks5bytestreammanager.h" +#include "connectiondatahandler.h" + +#include + +namespace gloox +{ + + class SOCKS5BytestreamDataHandler; + class ConnectionBase; + class LogSink; + + /** + * @brief An implementation of a single SOCKS5 Bytestream (XEP-0065). + * + * One instance of this class handles one bytestream. + * + * See SOCKS5BytestreamManager for a detailed description on how to implement + * SOCKS5 Bytestreams in your application. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API SOCKS5Bytestream : public ConnectionDataHandler, public Bytestream + { + friend class SOCKS5BytestreamManager; + + public: + /** + * Virtual destructor. + */ + virtual ~SOCKS5Bytestream(); + + /** + * This function starts the connection process. That is, it attempts to connect + * to each of the available StreamHosts. Once a working StreamHosts is found, the + * SOCKS5BytestreamManager is notified and the function returns. + * @return @b True if a connection to a StreamHost could be established, @b false + * otherwise. + * @note If @b false is returned you should hand this SOCKS5Bytestream object + * to SOCKS5BytestreamManager::dispose() for deletion. + * @note Make sure you have a SOCKS5BytestreamDataHandler registered (using + * registerSOCKS5BytestreamDataHandler()) before calling this function. + */ + virtual bool connect(); + + /** + * Closes the bytestream. + */ + virtual void close(); + + /** + * Use this function to send a chunk of data over an open bytestream. There is + * no limit for the size of the chunk (other than your machine's memory). + * If the stream is not open or has been closed again + * (by the remote entity or locally), nothing is sent and @b false is returned. + * @param data The block of data to send. + * @return @b True if the data has been sent (no guarantee of receipt), @b false + * in case of an error. + */ + virtual bool send( const std::string& data ); + + /** + * Call this function repeatedly to receive data from the socket. You should even do this + * if you use the bytestream to merely @b send data. + * @param timeout The timeout to use for select in microseconds. Default of -1 means blocking. + * @return The state of the connection. + */ + virtual ConnectionError recv( int timeout = -1 ); + + /** + * Sets the connection to use. + * @param connection The connection. The bytestream will own the connection, any + * previously set connection gets deleted. + */ + void setConnectionImpl( ConnectionBase* connection ); + + /** + * This function returns the concrete connection implementation currently in use. + * @return The concrete connection implementation. + * @since 0.9.7 + */ + ConnectionBase* connectionImpl( ) { return m_connection; } + + /** + * Use this function to set the available StreamHosts. Usually you should not need to + * use this function directly. + */ + void setStreamHosts( const StreamHostList& hosts ) { m_hosts = hosts; } + + // reimplemented from ConnectionDataHandler + virtual void handleReceivedData( const ConnectionBase* connection, const std::string& data ); + + // reimplemented from ConnectionDataHandler + virtual void handleConnect( const ConnectionBase* connection ); + + // reimplemented from ConnectionDataHandler + virtual void handleDisconnect( const ConnectionBase* connection, ConnectionError reason ); + + private: + SOCKS5Bytestream( SOCKS5BytestreamManager* manager, ConnectionBase* connection, + LogSink& logInstance, const JID& initiator, const JID& target, + const std::string& sid ); + void activate(); + + SOCKS5BytestreamManager* m_manager; + ConnectionBase* m_connection; + ConnectionBase* m_socks5; + JID m_proxy; + bool m_connected; + + StreamHostList m_hosts; + + }; + +} + +#endif // SOCKS5BYTESTREAM_H__ diff --git a/libs/libgloox/socks5bytestreammanager.cpp b/libs/libgloox/socks5bytestreammanager.cpp new file mode 100644 index 0000000..e885688 --- /dev/null +++ b/libs/libgloox/socks5bytestreammanager.cpp @@ -0,0 +1,486 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "bytestreamhandler.h" +#include "socks5bytestreammanager.h" +#include "socks5bytestreamserver.h" +#include "socks5bytestream.h" +#include "clientbase.h" +#include "disco.h" +#include "error.h" +#include "connectionbase.h" +#include "sha.h" +#include "util.h" + +#include + +namespace gloox +{ + + // ---- SOCKS5BytestreamManager::Query ---- + static const char* s5bModeValues[] = + { + "tcp", "udp" + }; + + static inline const char* modeString( SOCKS5BytestreamManager::S5BMode mode ) + { + return s5bModeValues[mode]; + } + + SOCKS5BytestreamManager::Query::Query() + : StanzaExtension( ExtS5BQuery ), m_type( TypeInvalid ) + { + } + + SOCKS5BytestreamManager::Query::Query( const std::string& sid, S5BMode mode, + const StreamHostList& hosts ) + : StanzaExtension( ExtS5BQuery ), m_sid( sid ), m_mode( mode ), m_hosts( hosts ), m_type( TypeSH ) + { + } + + SOCKS5BytestreamManager::Query::Query( const JID& jid, const std::string& sid, bool activate ) + : StanzaExtension( ExtS5BQuery ), m_sid( sid ), m_jid( jid ), m_type( activate ? TypeA : TypeSHU ) + { + } + + SOCKS5BytestreamManager::Query::Query( const Tag* tag ) + : StanzaExtension( ExtS5BQuery ), m_type( TypeInvalid ) + { + if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_BYTESTREAMS + /*|| !tag->hasAttribute( "sid" )*/ ) + return; + + m_sid = tag->findAttribute( "sid" ); + m_mode = static_cast( util::deflookup( tag->findAttribute( "mode" ), s5bModeValues, S5BTCP ) ); + + const TagList& l = tag->children(); + TagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + { + if( (*it)->name() == "streamhost" && (*it)->hasAttribute( "jid" ) + && (*it)->hasAttribute( "host" ) && (*it)->hasAttribute( "port" ) ) + { + m_type = TypeSH; + StreamHost sh; + sh.jid = (*it)->findAttribute( "jid" ); + sh.host = (*it)->findAttribute( "host" ); + sh.port = atoi( (*it)->findAttribute( "port" ).c_str() ); + m_hosts.push_back( sh ); + } + else if( (*it)->name() == "streamhost-used" ) + { + m_type = TypeSHU; + m_jid = (*it)->findAttribute( "jid" ); + } + else if( (*it)->name() == "activate" ) + { + m_type = TypeA; + m_jid = (*it)->cdata(); + } + } + } + + SOCKS5BytestreamManager::Query::~Query() + { + } + + const std::string& SOCKS5BytestreamManager::Query::filterString() const + { + static const std::string filter = "/iq/query[@xmlns='" + XMLNS_BYTESTREAMS + "']"; + return filter; + } + + Tag* SOCKS5BytestreamManager::Query::tag() const + { + if( m_type == TypeInvalid /*|| m_sid.empty()*/ ) + return 0; + + Tag* t = new Tag( "query" ); + t->setXmlns( XMLNS_BYTESTREAMS ); + t->addAttribute( "sid", m_sid ); + switch( m_type ) + { + case TypeSH: + { + t->addAttribute( "mode", util::deflookup( m_mode, s5bModeValues, "tcp" ) ); + StreamHostList::const_iterator it = m_hosts.begin(); + for( ; it != m_hosts.end(); ++it ) + { + Tag* s = new Tag( t, "streamhost" ); + s->addAttribute( "jid", (*it).jid.full() ); + s->addAttribute( "host", (*it).host ); + s->addAttribute( "port", (*it).port ); + } + break; + } + case TypeSHU: + { + Tag* s = new Tag( t, "streamhost-used" ); + s->addAttribute( "jid", m_jid.full() ); + break; + } + case TypeA: + { + Tag* c = new Tag( t, "activate" ); + c->setCData( m_jid.full() ); + break; + } + default: + break; + } + + return t; + } + // ---- ~SOCKS5BytestreamManager::Query ---- + + // ---- SOCKS5BytestreamManager ---- + SOCKS5BytestreamManager::SOCKS5BytestreamManager( ClientBase* parent, BytestreamHandler* s5bh ) + : m_parent( parent ), m_socks5BytestreamHandler( s5bh ), m_server( 0 ) + { + if( m_parent ) + { + m_parent->registerStanzaExtension( new Query() ); + m_parent->registerIqHandler( this, ExtS5BQuery ); + } + } + + SOCKS5BytestreamManager::~SOCKS5BytestreamManager() + { + if( m_parent ) + { + m_parent->removeIqHandler( this, ExtS5BQuery ); + m_parent->removeIDHandler( this ); + } + + util::clearMap( m_s5bMap ); + } + + void SOCKS5BytestreamManager::addStreamHost( const JID& jid, const std::string& host, int port ) + { + StreamHost sh; + sh.jid = jid; + sh.host = host; + sh.port = port; + m_hosts.push_back( sh ); + } + + bool SOCKS5BytestreamManager::requestSOCKS5Bytestream( const JID& to, S5BMode mode, + const std::string& sid, + const JID& from ) + { + if( !m_parent ) + { + m_parent->logInstance().warn( LogAreaClassS5BManager, + "No parent (ClientBase) set, cannot request bytestream." ); + return false; + } + + if( m_hosts.empty() ) + { + m_parent->logInstance().warn( LogAreaClassS5BManager, + "No stream hosts set, cannot request bytestream." ); + return false; + } + + const std::string& msid = sid.empty() ? m_parent->getID() : sid; + const std::string& id = m_parent->getID(); + IQ iq( IQ::Set, to, id ); + iq.addExtension( new Query( msid, mode, m_hosts ) ); + if( from ) + iq.setFrom( from ); + + if( m_server ) + { + SHA sha; + sha.feed( msid ); + if( from ) + sha.feed( from.full() ); + else + sha.feed( m_parent->jid().full() ); + sha.feed( to.full() ); + m_server->registerHash( sha.hex() ); + } + + AsyncS5BItem asi; + asi.sHosts = m_hosts; + asi.id = id; + asi.from = to; + asi.to = from ? from : m_parent->jid(); + asi.incoming = false; + m_asyncTrackMap[msid] = asi; + + m_trackMap[id] = msid; + m_parent->send( iq, this, S5BOpenStream ); + + return true; + } + + void SOCKS5BytestreamManager::acknowledgeStreamHost( bool success, const JID& jid, + const std::string& sid ) + { + AsyncTrackMap::const_iterator it = m_asyncTrackMap.find( sid ); + if( it == m_asyncTrackMap.end() || !m_parent ) + return; + + const AsyncS5BItem& item = (*it).second; + + IQ* iq = 0; + + if( item.incoming ) + { + iq = new IQ( IQ::Result, item.from.full(), item.id ); + if( item.to ) + iq->setFrom( item.to ); + + if( success ) + iq->addExtension( new Query( jid, sid, false ) ); + else + iq->addExtension( new Error( StanzaErrorTypeCancel, StanzaErrorItemNotFound ) ); + + m_parent->send( *iq ); + } + else + { + if( success ) + { + const std::string& id = m_parent->getID(); + iq = new IQ( IQ::Set, jid.full(), id ); + iq->addExtension( new Query( item.from, sid, true ) ); + + m_trackMap[id] = sid; + m_parent->send( *iq, this, S5BActivateStream ); + } + } + + delete iq; + } + + bool SOCKS5BytestreamManager::handleIq( const IQ& iq ) + { + const Query* q = iq.findExtension( ExtS5BQuery ); + if( !q || !m_socks5BytestreamHandler + || m_trackMap.find( iq.id() ) != m_trackMap.end() ) + return false; + + switch( iq.subtype() ) + { + case IQ::Set: + { + const std::string& sid = q->sid(); +// FIXME What is haveStream() good for? + if( /*haveStream( iq.from() ) ||*/ sid.empty() || q->mode() == S5BUDP ) + { + rejectSOCKS5Bytestream( iq.from(), iq.id(), StanzaErrorNotAcceptable ); + return true; + } + AsyncS5BItem asi; + asi.sHosts = q->hosts(); + asi.id = iq.id(); + asi.from = iq.from(); + asi.to = iq.to(); + asi.incoming = true; + m_asyncTrackMap[sid] = asi; + m_socks5BytestreamHandler->handleIncomingBytestreamRequest( sid, iq.from() ); + break; + } + case IQ::Error: + m_socks5BytestreamHandler->handleBytestreamError( iq, EmptyString ); + break; + default: + break; + } + + return true; + } + + const StreamHost* SOCKS5BytestreamManager::findProxy( const JID& from, const std::string& hostjid, + const std::string& sid ) + { + AsyncTrackMap::const_iterator it = m_asyncTrackMap.find( sid ); + if( it == m_asyncTrackMap.end() ) + return 0; + + if( (*it).second.from == from ) + { + StreamHostList::const_iterator it2 = (*it).second.sHosts.begin(); + for( ; it2 != (*it).second.sHosts.end(); ++it2 ) + { + if( (*it2).jid == hostjid ) + { + return &(*it2); + } + } + } + + return 0; + } + + bool SOCKS5BytestreamManager::haveStream( const JID& from ) + { + S5BMap::const_iterator it = m_s5bMap.begin(); + for( ; it != m_s5bMap.end(); ++it ) + { + if( (*it).second && (*it).second->target() == from ) + return true; + } + return false; + } + + void SOCKS5BytestreamManager::acceptSOCKS5Bytestream( const std::string& sid ) + { + AsyncTrackMap::iterator it = m_asyncTrackMap.find( sid ); + if( it == m_asyncTrackMap.end() || !m_socks5BytestreamHandler ) + return; + + SOCKS5Bytestream* s5b = new SOCKS5Bytestream( this, m_parent->connectionImpl()->newInstance(), + m_parent->logInstance(), + (*it).second.from, (*it).second.to, sid ); + s5b->setStreamHosts( (*it).second.sHosts ); + m_s5bMap[sid] = s5b; + m_socks5BytestreamHandler->handleIncomingBytestream( s5b ); + } + + void SOCKS5BytestreamManager::rejectSOCKS5Bytestream( const std::string& sid, StanzaError reason ) + { + AsyncTrackMap::iterator it = m_asyncTrackMap.find( sid ); + if( it != m_asyncTrackMap.end() ) + { + rejectSOCKS5Bytestream( (*it).second.from, (*it).second.id, reason ); + m_asyncTrackMap.erase( it ); + } + } + + void SOCKS5BytestreamManager::rejectSOCKS5Bytestream( const JID& from, + const std::string& id, + StanzaError reason ) + { + IQ iq( IQ::Error, from, id ); + + switch( reason ) + { + case StanzaErrorForbidden: + case StanzaErrorNotAcceptable: + { + iq.addExtension( new Error( StanzaErrorTypeAuth, reason ) ); + break; + } + case StanzaErrorFeatureNotImplemented: + case StanzaErrorNotAllowed: + default: + { + iq.addExtension( new Error( StanzaErrorTypeCancel, reason ) ); + break; + } + } + + m_parent->send( iq ); + } + + void SOCKS5BytestreamManager::handleIqID( const IQ& iq, int context ) + { + StringMap::iterator it = m_trackMap.find( iq.id() ); + if( it == m_trackMap.end() ) + return; + + switch( context ) + { + case S5BOpenStream: + { + switch( iq.subtype() ) + { + case IQ::Result: + { + const Query* q = iq.findExtension( ExtS5BQuery ); + if( q && m_socks5BytestreamHandler ) + { + const std::string& proxy = q->jid().full(); + const StreamHost* sh = findProxy( iq.from(), proxy, (*it).second ); + if( sh ) + { + SOCKS5Bytestream* s5b = 0; + bool selfProxy = ( proxy == m_parent->jid().full() && m_server ); + if( selfProxy ) + { + SHA sha; + sha.feed( (*it).second ); + sha.feed( iq.to().full() ); + sha.feed( iq.from().full() ); + s5b = new SOCKS5Bytestream( this, m_server->getConnection( sha.hex() ), + m_parent->logInstance(), + iq.to(), iq.from(), + (*it).second ); + } + else + { + s5b = new SOCKS5Bytestream( this, m_parent->connectionImpl()->newInstance(), + m_parent->logInstance(), + iq.to(), iq.from(), + (*it).second ); + s5b->setStreamHosts( StreamHostList( 1, *sh ) ); + } + m_s5bMap[(*it).second] = s5b; + m_socks5BytestreamHandler->handleOutgoingBytestream( s5b ); + if( selfProxy ) + s5b->activate(); + } + } + break; + } + case IQ::Error: + m_socks5BytestreamHandler->handleBytestreamError( iq, (*it).second ); + break; + default: + break; + } + break; + } + case S5BActivateStream: + { + switch( iq.subtype() ) + { + case IQ::Result: + { + S5BMap::const_iterator it5 = m_s5bMap.find( (*it).second ); + if( it5 != m_s5bMap.end() ) + (*it5).second->activate(); + break; + } + case IQ::Error: + m_socks5BytestreamHandler->handleBytestreamError( iq, (*it).second ); + break; + default: + break; + } + break; + } + default: + break; + } + m_trackMap.erase( it ); + } + + bool SOCKS5BytestreamManager::dispose( SOCKS5Bytestream* s5b ) + { + S5BMap::iterator it = m_s5bMap.find( s5b->sid() ); + if( it != m_s5bMap.end() ) + { + delete s5b; + m_s5bMap.erase( it ); + return true; + } + + return false; + } + +} diff --git a/libs/libgloox/socks5bytestreammanager.h b/libs/libgloox/socks5bytestreammanager.h new file mode 100644 index 0000000..49fbebb --- /dev/null +++ b/libs/libgloox/socks5bytestreammanager.h @@ -0,0 +1,307 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef SOCKS5BYTESTREAMMANAGER_H__ +#define SOCKS5BYTESTREAMMANAGER_H__ + +#include "iqhandler.h" +#include "jid.h" +#include "stanzaextension.h" + +namespace gloox +{ + + class BytestreamHandler; + class SOCKS5BytestreamServer; + class SOCKS5Bytestream; + class ClientBase; + + /** + * Describes a single StreamHost. + */ + struct StreamHost + { + JID jid; /**< The StreamHost's JID. */ + std::string host; /**< The StreamHost's IP or host name. */ + int port; /**< The StreamHost's port. */ +// std::string zeroconf; /**< A zeroconf identifier. */ + }; + + /** + * A list of StreamHosts. + */ + typedef std::list StreamHostList; + + /** + * @brief An SOCKS5BytestreamManager dispatches SOCKS5 Bytestreams. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API SOCKS5BytestreamManager : public IqHandler + { + + friend class SOCKS5Bytestream; + + public: + + /** + * Supported transport layer protocols. + */ + enum S5BMode + { + S5BTCP, /**< Use TCP on the transport layer. */ + S5BUDP, /**< Use UDP on the transport layer. Not currently supported. */ + S5BInvalid /**< Invalid mode. */ + }; + + /** + * Constructs a new SOCKS5BytestreamManager. + * @param parent The ClientBase to use for sending data. + * @param s5bh A SOCKS5BytestreamManager -derived object that will receive + * incoming and outgoing SOCKS5Bytestreams. + */ + SOCKS5BytestreamManager( ClientBase* parent, BytestreamHandler* s5bh ); + + /** + * Virtual destructor. + */ + virtual ~SOCKS5BytestreamManager(); + + /** + * Sets a list of StreamHosts that will be used for subsequent bytestream requests. + * @note At least one StreamHost is required. + * @param hosts A list of StreamHosts. + */ + void setStreamHosts( StreamHostList hosts ) { m_hosts = hosts; } + + /** + * Adds one StreamHost to the list of StreamHosts. + * @param jid The StreamHost's JID. + * @param host The StreamHost's hostname. + * @param port The StreamHost's port. + */ + void addStreamHost( const JID& jid, const std::string& host, int port ); + + /** + * This function requests a bytestream with the remote entity. + * Data can only be sent over an open stream. Use isOpen() to find out what the stream's + * current state is. However, successful opening/initiation will be announced by means of the + * BytestreamHandler interface. Multiple bytestreams (even per JID) can be initiated + * without waiting for success. + * @param to The recipient of the requested bytestream. + * @param mode The desired transport layer protocol. + * @param sid The bytestream's stream ID, if previously negotiated e.g. using SI (XEP-0095). + * @param from An optional 'from' address to stamp outgoing + * requests with. Only useful in component scenarios. Defaults to empty JID. + * @return @b False in case of an error, @b true otherwise. A return value of @b true does + * @b not indicate that the bytestream has been opened. This is announced by means of the + * BytestreamHandler. + */ + bool requestSOCKS5Bytestream( const JID& to, S5BMode mode, const std::string& sid = EmptyString, + const JID& from = JID() ); + + /** + * To get rid of a bytestream (i.e., close and delete it), call this function. You + * should then not use the bytestream any more. + * The remote entity will be notified of the closing of the stream. + * @param s5b The bytestream to dispose. It will be deleted here. + */ + bool dispose( SOCKS5Bytestream* s5b ); + + /** + * Use this function to accept an incoming bytestream. + * @param sid The stream's id as passed to BytestreamHandler::handleIncomingSOCKS5Bytestream(). + */ + void acceptSOCKS5Bytestream( const std::string& sid ); + + /** + * Use this function to reject an incoming bytestream. + * @param sid The stream's id as passed to BytestreamHandler::handleIncomingSOCKS5Bytestream(). + * @param reason The reason for the reject. + */ + void rejectSOCKS5Bytestream( const std::string& sid, StanzaError reason = StanzaErrorNotAcceptable ); + + /** + * Use this function to register an object that will receive new @b incoming bytestream + * requests from the SOCKS5BytestreamManager. Only one BytestreamHandler can be + * registered at any one time. + * @param s5bh The BytestreamHandler derived object to receive notifications. + */ + void registerBytestreamHandler( BytestreamHandler* s5bh ) + { m_socks5BytestreamHandler = s5bh; } + + /** + * Removes the registered BytestreamHandler. + */ + void removeBytestreamHandler() + { m_socks5BytestreamHandler = 0; } + + /** + * Tells the SOCKS5BytestreamManager which SOCKS5BytestreamServer handles peer-2-peer SOCKS5 + * bytestreams. + * @param server The SOCKS5BytestreamServer to use. + */ + void registerSOCKS5BytestreamServer( SOCKS5BytestreamServer* server ) { m_server = server; } + + /** + * Un-registers any local SOCKS5BytestreamServer. + */ + void removeSOCKS5BytestreamServer() { m_server = 0; } + + // reimplemented from IqHandler. + virtual bool handleIq( const IQ& iq ); + + // reimplemented from IqHandler. + virtual void handleIqID( const IQ& iq, int context ); + + private: +#ifdef SOCKS5BYTESTREAMMANAGER_TEST + public: +#endif + + class Query : public StanzaExtension + { + public: + /** + * Constructs a new empty Query object. + */ + Query(); + + /** + * Constructs a new Query (streamhost) object from the given parameters. + * @param sid The stream ID. + * @param mode The stream mode (TCP or UDP). + * @param hosts A list of stream hosts. + */ + Query( const std::string& sid, S5BMode mode, + const StreamHostList& hosts ); + + /** + * Constructs a new Query (streamhost-used or activate) object, including the given JID. + * @param jid The JID. + * @param sid The stream ID. + * @param activate Determines whether the object will be an 'activate' (@b true) or + * 'streamhost-used' (@b false) one. + */ + Query( const JID& jid, const std::string& sid, bool activate ); + + /** + * Constructs a new Query object from the given Tag. + * @param tag The Tag to parse. + */ + Query( const Tag* tag ); + + /** + * Virtual destructor. + */ + virtual ~Query(); + + /** + * Returns the current stream ID. + * @return The current stream ID. + */ + const std::string& sid() const { return m_sid; } + + /** + * Returns the current JID. + * @return The current JID. + */ + const JID& jid() const { return m_jid; } + + /** + * Returns the current mode. + * @return The current mode. + */ + S5BMode mode() const { return m_mode; } + + /** + * Returns the current list of stream hosts. + * @return The current list of stream hosts. + */ + const StreamHostList& hosts() const { return m_hosts; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new Query( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new Query( *this ); + } + + private: + enum QueryType + { + TypeSH, + TypeSHU, + TypeA, + TypeInvalid + }; + + std::string m_sid; + JID m_jid; + SOCKS5BytestreamManager::S5BMode m_mode; + StreamHostList m_hosts; + QueryType m_type; + + }; + + SOCKS5BytestreamManager& operator=( const SOCKS5BytestreamManager&); + void rejectSOCKS5Bytestream( const JID& from, const std::string& id, StanzaError reason = StanzaErrorNotAcceptable ); + bool haveStream( const JID& from ); + const StreamHost* findProxy( const JID& from, const std::string& hostjid, const std::string& sid ); + + void acknowledgeStreamHost( bool success, const JID& jid, const std::string& sid ); + + enum IBBActionType + { + S5BOpenStream, + S5BCloseStream, + S5BActivateStream + }; + + typedef std::map S5BMap; + S5BMap m_s5bMap; + + struct AsyncS5BItem + { + JID from; + JID to; + std::string id; + StreamHostList sHosts; + bool incoming; + }; + typedef std::map AsyncTrackMap; + AsyncTrackMap m_asyncTrackMap; + + ClientBase* m_parent; + BytestreamHandler* m_socks5BytestreamHandler; + SOCKS5BytestreamServer* m_server; + StreamHostList m_hosts; + StringMap m_trackMap; + + }; + +} + +#endif // SOCKS5BYTESTREAMMANAGER_H__ diff --git a/libs/libgloox/socks5bytestreamserver.cpp b/libs/libgloox/socks5bytestreamserver.cpp new file mode 100644 index 0000000..55f5fcc --- /dev/null +++ b/libs/libgloox/socks5bytestreamserver.cpp @@ -0,0 +1,220 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "socks5bytestreamserver.h" +#include "connectiontcpserver.h" +#include "mutexguard.h" +#include "util.h" + +namespace gloox +{ + + SOCKS5BytestreamServer::SOCKS5BytestreamServer( const LogSink& logInstance, int port, + const std::string& ip ) + : m_tcpServer( 0 ), m_logInstance( logInstance ), m_ip( ip ), m_port( port ) + { + m_tcpServer = new ConnectionTCPServer( this, m_logInstance, m_ip, m_port ); + } + + SOCKS5BytestreamServer::~SOCKS5BytestreamServer() + { + if( m_tcpServer ) + delete m_tcpServer; + + ConnectionMap::const_iterator it = m_connections.begin(); + for( ; it != m_connections.end(); ++it ) + delete (*it).first; + } + + ConnectionError SOCKS5BytestreamServer::listen() + { + if( m_tcpServer ) + return m_tcpServer->connect(); + + return ConnNotConnected; + } + + ConnectionError SOCKS5BytestreamServer::recv( int timeout ) + { + if( !m_tcpServer ) + return ConnNotConnected; + + ConnectionError ce = m_tcpServer->recv( timeout ); + if( ce != ConnNoError ) + return ce; + + ConnectionMap::const_iterator it = m_connections.begin(); + ConnectionMap::const_iterator it2; + while( it != m_connections.end() ) + { + it2 = it++; + (*it2).first->recv( timeout ); + } + + util::clearList( m_oldConnections ); + return ConnNoError; + } + + void SOCKS5BytestreamServer::stop() + { + if( m_tcpServer ) + { + m_tcpServer->disconnect(); + m_tcpServer->cleanup(); + } + } + + int SOCKS5BytestreamServer::localPort() const + { + if( m_tcpServer ) + return m_tcpServer->localPort(); + + return m_port; + } + + const std::string SOCKS5BytestreamServer::localInterface() const + { + if( m_tcpServer ) + return m_tcpServer->localInterface(); + + return m_ip; + } + + ConnectionBase* SOCKS5BytestreamServer::getConnection( const std::string& hash ) + { + util::MutexGuard mg( m_mutex ); + + ConnectionMap::iterator it = m_connections.begin(); + for( ; it != m_connections.end(); ++it ) + { + if( (*it).second.hash == hash ) + { + ConnectionBase* conn = (*it).first; + conn->registerConnectionDataHandler( 0 ); + m_connections.erase( it ); + return conn; + } + } + + return 0; + } + + void SOCKS5BytestreamServer::registerHash( const std::string& hash ) + { + util::MutexGuard mg( m_mutex ); + m_hashes.push_back( hash ); + } + + void SOCKS5BytestreamServer::removeHash( const std::string& hash ) + { + util::MutexGuard mg( m_mutex ); + m_hashes.remove( hash ); + } + + void SOCKS5BytestreamServer::handleIncomingConnection( ConnectionBase* /*server*/, ConnectionBase* connection ) + { + connection->registerConnectionDataHandler( this ); + ConnectionInfo ci; + ci.state = StateUnnegotiated; + m_connections[connection] = ci; + } + + void SOCKS5BytestreamServer::handleReceivedData( const ConnectionBase* connection, + const std::string& data ) + { + ConnectionMap::iterator it = m_connections.find( const_cast( connection ) ); + if( it == m_connections.end() ) + return; + + switch( (*it).second.state ) + { + case StateDisconnected: + (*it).first->disconnect(); + break; + case StateUnnegotiated: + { + char c[2]; + c[0] = 0x05; + c[1] = (char)(unsigned char)0xFF; + (*it).second.state = StateDisconnected; + + if( data.length() >= 3 && data[0] == 0x05 ) + { + unsigned int sz = ( data.length() - 2 < static_cast( data[1] ) ) + ? static_cast( data.length() - 2 ) + : static_cast( data[1] ); + for( unsigned int i = 2; i < sz + 2; ++i ) + { + if( data[i] == 0x00 ) + { + c[1] = 0x00; + (*it).second.state = StateAuthAccepted; + break; + } + } + } + (*it).first->send( std::string( c, 2 ) ); + break; + } + case StateAuthmethodAccepted: + // place to implement any future auth support + break; + case StateAuthAccepted: + { + std::string reply = data; + if( reply.length() < 2 ) + reply.resize( 2 ); + + reply[0] = 0x05; + reply[1] = 0x01; // general SOCKS server failure + (*it).second.state = StateDisconnected; + + if( data.length() == 47 && data[0] == 0x05 && data[1] == 0x01 && data[2] == 0x00 + && data[3] == 0x03 && data[4] == 0x28 && data[45] == 0x00 && data[46] == 0x00 ) + { + const std::string hash = data.substr( 5, 40 ); + + HashMap::const_iterator ith = m_hashes.begin(); + for( ; ith != m_hashes.end() && (*ith) != hash; ++ith ) + ; + + if( ith != m_hashes.end() ) + { + reply[1] = 0x00; + (*it).second.hash = hash; + (*it).second.state = StateDestinationAccepted; + } + } + (*it).first->send( reply ); + break; + } + case StateDestinationAccepted: + case StateActive: + // should not happen + break; + } + } + + void SOCKS5BytestreamServer::handleConnect( const ConnectionBase* /*connection*/ ) + { + // should never happen, TCP connection is already established + } + + void SOCKS5BytestreamServer::handleDisconnect( const ConnectionBase* connection, + ConnectionError /*reason*/ ) + { + m_connections.erase( const_cast( connection ) ); + m_oldConnections.push_back( connection ); + } + +} diff --git a/libs/libgloox/socks5bytestreamserver.h b/libs/libgloox/socks5bytestreamserver.h new file mode 100644 index 0000000..1910e8d --- /dev/null +++ b/libs/libgloox/socks5bytestreamserver.h @@ -0,0 +1,144 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef SOCKS5BYTESTREAMSERVER_H__ +#define SOCKS5BYTESTREAMSERVER_H__ + +#include "macros.h" +#include "connectionhandler.h" +#include "logsink.h" +#include "mutex.h" + +namespace gloox +{ + + class ConnectionTCPServer; + + /** + * @brief A server listening for SOCKS5 bytestreams. + * + * @note You can use a single SOCKS5BytestreamServer instance with multiple Client objects. + * + * @note It is safe to put a SOCKS5BytestreamServer instance into a separate thread. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API SOCKS5BytestreamServer : public ConnectionHandler, public ConnectionDataHandler + { + + friend class SOCKS5BytestreamManager; + + public: + /** + * Constructs a new SOCKS5BytestreamServer. + * @param logInstance A LogSink to use. + * @param port The local port to listen on. + * @param ip The local IP to bind to. If empty, the server will listen on all local interfaces. + */ + SOCKS5BytestreamServer( const LogSink& logInstance, int port, const std::string& ip = EmptyString ); + + /** + * Destructor. + */ + ~SOCKS5BytestreamServer(); + + /** + * Starts listening on the specified interface and port. + * @return Returns @c ConnNoError on success, @c ConnIoError on failure (e.g. if the port + * is already in use). + */ + ConnectionError listen(); + + /** + * Call this function repeatedly to check for incoming connections and to negotiate + * them. + * @param timeout The timeout to use for select in microseconds. + * @return The state of the listening socket. + */ + ConnectionError recv( int timeout ); + + /** + * Stops listening and unbinds from the interface and port. + */ + void stop(); + + /** + * Expose our TCP Connection localPort + * Returns the local port. + * @return The local port. + */ + int localPort() const; + + /** + * Expose our TCP Connection localInterface + * Returns the locally bound IP address. + * @return The locally bound IP address. + */ + const std::string localInterface() const; + + // reimplemented from ConnectionHandler + virtual void handleIncomingConnection( ConnectionBase* server, ConnectionBase* connection ); + + // reimplemented from ConnectionDataHandler + virtual void handleReceivedData( const ConnectionBase* connection, const std::string& data ); + + // reimplemented from ConnectionDataHandler + virtual void handleConnect( const ConnectionBase* connection ); + + // reimplemented from ConnectionDataHandler + virtual void handleDisconnect( const ConnectionBase* connection, ConnectionError reason ); + + private: + SOCKS5BytestreamServer& operator=( const SOCKS5BytestreamServer& ); + void registerHash( const std::string& hash ); + void removeHash( const std::string& hash ); + ConnectionBase* getConnection( const std::string& hash ); + + enum NegotiationState + { + StateDisconnected, + StateUnnegotiated, + StateAuthmethodAccepted, + StateAuthAccepted, + StateDestinationAccepted, + StateActive + }; + + struct ConnectionInfo + { + NegotiationState state; + std::string hash; + }; + + typedef std::map ConnectionMap; + ConnectionMap m_connections; + + typedef std::list ConnectionList; + ConnectionList m_oldConnections; + + typedef std::list HashMap; + HashMap m_hashes; + + ConnectionTCPServer* m_tcpServer; + + util::Mutex m_mutex; + const LogSink& m_logInstance; + std::string m_ip; + int m_port; + + }; + +} + +#endif // SOCKS5BYTESTREAMSERVER_H__ diff --git a/libs/libgloox/softwareversion.cpp b/libs/libgloox/softwareversion.cpp new file mode 100644 index 0000000..6705de1 --- /dev/null +++ b/libs/libgloox/softwareversion.cpp @@ -0,0 +1,74 @@ +/* + Copyright (c) 2008-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "softwareversion.h" +#include "tag.h" + +namespace gloox +{ + + SoftwareVersion::SoftwareVersion( const std::string& name, + const std::string& version, + const std::string& os ) + : StanzaExtension( ExtVersion ), m_name( name ), m_version( version ), m_os( os ) + { + } + + SoftwareVersion::SoftwareVersion( const Tag* tag ) + : StanzaExtension( ExtVersion ) + { + if( !tag ) + return; + + Tag* t = tag->findChild( "name" ); + if( t ) + m_name = t->cdata(); + + t = tag->findChild( "version" ); + if( t ) + m_version = t->cdata(); + + t = tag->findChild( "os" ); + if( t ) + m_os = t->cdata(); + } + + SoftwareVersion::~SoftwareVersion() + { + } + + const std::string& SoftwareVersion::filterString() const + { + static const std::string filter = "/iq/query[@xmlns='" + XMLNS_VERSION + "']"; + return filter; + } + + Tag* SoftwareVersion::tag() const + { + Tag* t = new Tag( "query" ); + t->setXmlns( XMLNS_VERSION ); + + if( !m_name.empty() ) + new Tag( t, "name", m_name ); + + if( !m_version.empty() ) + new Tag( t, "version", m_version ); + + if( !m_os.empty() ) + new Tag( t, "os", m_os ); + + return t; + } + +} diff --git a/libs/libgloox/softwareversion.h b/libs/libgloox/softwareversion.h new file mode 100644 index 0000000..2fe654d --- /dev/null +++ b/libs/libgloox/softwareversion.h @@ -0,0 +1,101 @@ +/* + Copyright (c) 2008-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef SOFTWAREVERSION_H__ +#define SOFTWAREVERSION_H__ + + +#include "stanzaextension.h" + +#include + +namespace gloox +{ + + class Tag; + + /** + * @brief This is an implementation of XEP-0092 as a StanzaExtension. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API SoftwareVersion : public StanzaExtension + { + + public: + /** + * Constructs a new object with the given resource string. + * @param name The software's name. + * @param version The software's version. + * @param os The software's operating system. + */ + SoftwareVersion( const std::string& name, const std::string& version, const std::string& os ); + + /** + * Constructs a new object from the given Tag. + * @param tag The Tag to parse. + */ + SoftwareVersion( const Tag* tag = 0 ); + + /** + * Virtual Destructor. + */ + virtual ~SoftwareVersion(); + + /** + * Returns the application's name. + * @return The application's name. + */ + const std::string& name() const { return m_name; } + + /** + * Returns the application's version. + * @return The application's version. + */ + const std::string& version() const { return m_version; } + + /** + * Returns the application's Operating System. + * @return The application's OS. + */ + const std::string& os() const { return m_os; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new SoftwareVersion( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new SoftwareVersion( *this ); + } + + private: + std::string m_name; + std::string m_version; + std::string m_os; + }; + +} + +#endif// SOFTWAREVERSION_H__ diff --git a/libs/libgloox/stanza.cpp b/libs/libgloox/stanza.cpp new file mode 100644 index 0000000..f0d979a --- /dev/null +++ b/libs/libgloox/stanza.cpp @@ -0,0 +1,127 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "stanza.h" +#include "error.h" +#include "jid.h" +#include "util.h" +#include "stanzaextension.h" +#include "stanzaextensionfactory.h" + +#include + +namespace gloox +{ + + Stanza::Stanza( const JID& to ) + : m_xmllang( "default" ), m_to( to ) + { + } + + Stanza::Stanza( Tag* tag ) + : m_xmllang( "default" ) + { + if( !tag ) + return; + + m_from.setJID( tag->findAttribute( "from" ) ); + m_to.setJID( tag->findAttribute( "to" ) ); + m_id = tag->findAttribute( "id" ); + } + + Stanza::~Stanza() + { + removeExtensions(); + } + + const Error* Stanza::error() const + { + return findExtension( ExtError ); + } + + void Stanza::addExtension( const StanzaExtension* se ) + { + m_extensionList.push_back( se ); + } + + const StanzaExtension* Stanza::findExtension( int type ) const + { + StanzaExtensionList::const_iterator it = m_extensionList.begin(); + for( ; it != m_extensionList.end() && (*it)->extensionType() != type; ++it ) ; + return it != m_extensionList.end() ? (*it) : 0; + } + + void Stanza::removeExtensions() + { + util::clearList( m_extensionList ); + } + + void Stanza::setLang( StringMap** map, + std::string& defaultLang, + const Tag* tag ) + { + const std::string& lang = tag ? tag->findAttribute( "xml:lang" ) : EmptyString; + setLang( map, defaultLang, tag ? tag->cdata() : EmptyString, lang ); + } + + void Stanza::setLang( StringMap** map, + std::string& defaultLang, + const std::string& data, + const std::string& xmllang ) + { + if( data.empty() ) + return; + + if( xmllang.empty() ) + defaultLang = data; + else + { + if( !*map ) + *map = new StringMap(); + (**map)[xmllang] = data; + } + } + + const std::string& Stanza::findLang( const StringMap* map, + const std::string& defaultData, + const std::string& lang ) + { + if( map && lang != "default" ) + { + StringMap::const_iterator it = map->find( lang ); + if( it != map->end() ) + return (*it).second; + } + return defaultData; + } + + void Stanza::getLangs( const StringMap* map, + const std::string& defaultData, + const std::string& name, + Tag* tag ) + { + if( !defaultData.empty() ) + new Tag( tag, name, defaultData ); + + if( !map ) + return; + + StringMap::const_iterator it = map->begin(); + for( ; it != map->end(); ++it ) + { + Tag* t = new Tag( tag, name, "xml:lang", (*it).first ); + t->setCData( (*it).second ); + } + } + +} diff --git a/libs/libgloox/stanza.h b/libs/libgloox/stanza.h new file mode 100644 index 0000000..45acdd6 --- /dev/null +++ b/libs/libgloox/stanza.h @@ -0,0 +1,174 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef STANZA_H__ +#define STANZA_H__ + +#include "gloox.h" +#include "tag.h" +#include "jid.h" +#include "stanzaextension.h" + +namespace gloox +{ + + class Error; + + /** + * @brief This is the base class for XMPP stanza abstractions. + * + * @author Jakob Schroeter + * @since 0.4 + */ + class GLOOX_API Stanza + { + public: + /** + * Virtual destructor. + */ + virtual ~Stanza(); + + /** + * Sets the 'from' address of the Stanza. This useful for @link gloox::Component Components @endlink. + * @param from The from address. + */ + void setFrom( const JID& from ) { m_from = from; } + + /** + * Returns the JID the stanza comes from. + * @return The origin of the stanza. + */ + const JID& from() const { return m_from; } + + /** + * Returns the receiver of the stanza. + * @return The stanza's destination. + */ + const JID& to() const { return m_to; } + + /** + * Returns the id of the stanza, if set. + * @return The ID of the stanza. + */ + const std::string& id() const { return m_id; } + + /** + * A convenience function that returns the stanza error condition, if any. + * @return The stanza error condition, may be 0. + */ + const Error* error() const; + + /** + * Retrieves the value of the xml:lang attribute of this stanza. + * Default is 'en'. + * @return The stanza's default language. + */ + const std::string& xmlLang() const { return m_xmllang; } + + /** + * Use this function to add a StanzaExtension to this Stanza. + * @param se The StanzaExtension to add. + * @note The Stanza will become the owner of the StanzaExtension and + * will take care of deletion. + * @since 0.9 + */ + void addExtension( const StanzaExtension* se ); + + /** + * Finds a StanzaExtension of a particular type. + * @param type StanzaExtensionType to search for. + * @return A pointer to the StanzaExtension, or 0 if none was found. + */ + const StanzaExtension* findExtension( int type ) const; + + /** + * Finds a StanzaExtension of a particular type. + * Example: + * @code + * const MyExtension* c = presence.findExtension( ExtMyExt ); + * @endcode + * @param type The extension type to look for. + * @return The static_cast' type, or 0 if none was found. + */ + template< class T > + inline const T* findExtension( int type ) const + { + return static_cast( findExtension( type ) ); + } + + /** + * Returns the list of the Stanza's extensions. + * @return The list of the Stanza's extensions. + */ + const StanzaExtensionList& extensions() const { return m_extensionList; } + + /** + * Removes (deletes) all the stanza's extensions. + */ + void removeExtensions(); + + /** + * Creates a Tag representation of the Stanza. The Tag is completely + * independent of the Stanza and will not be updated when the Stanza + * is modified. + * @return A pointer to a Tag representation. It is the job of the + * caller to delete the Tag. + */ + virtual Tag* tag() const = 0; + + protected: + /** + * Creates a new Stanza, taking from and to addresses from the given Tag. + * @param tag The Tag to create the Stanza from. + * @since 1.0 + */ + Stanza( Tag* tag ); + + /** + * Creates a new Stanza object and initializes the receiver's JID. + * @param to The receipient of the Stanza. + * @since 1.0 + */ + Stanza( const JID& to ); + + StanzaExtensionList m_extensionList; + std::string m_id; + std::string m_xmllang; + JID m_from; + JID m_to; + + static const std::string& findLang( const StringMap* map, + const std::string& defaultData, + const std::string& lang ); + + static void setLang( StringMap** map, + std::string& defaultLang, + const Tag* tag ); + + static void setLang( StringMap** map, + std::string& defaultLang, + const std::string& data, + const std::string& xmllang ); + + static void getLangs( const StringMap* map, + const std::string& defaultData, + const std::string& name, Tag* tag ); + + private: + Stanza( const Stanza& ); + + }; + +} + +#endif // STANZA_H__ diff --git a/libs/libgloox/stanzaextension.h b/libs/libgloox/stanzaextension.h new file mode 100644 index 0000000..89d71ee --- /dev/null +++ b/libs/libgloox/stanzaextension.h @@ -0,0 +1,241 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef STANZAEXTENSION_H__ +#define STANZAEXTENSION_H__ + +#include "macros.h" + +#include + +namespace gloox +{ + + class Tag; + + /** + * Supported Stanza extension types. + */ + enum StanzaExtensionType + { + ExtNone, /**< Invalid StanzaExtension. */ + ExtVCardUpdate, /**< Extension in the vcard-temp:x:update namespace, + * advertising a user avatar's SHA1 hash (XEP-0153). */ + ExtOOB, /**< An extension in the jabber:iq:oob or jabber:x:oob + * namespaces (XEP-0066). */ + ExtGPGSigned, /**< An extension containing a GPG/PGP signature + * (XEP-0027). */ + ExtGPGEncrypted, /**< An extension containing a GPG/PGP encrypted message + * (XEP-0027). */ + ExtReceipt, /**< An extension containing a Message Receipt/Request + * (XEP-0184). */ + ExtDelay, /**< An extension containing notice of delayed delivery + * (XEP-0203 & XEP-0091). */ + ExtAMP, /**< An extension containing advanced message processing + * rules (XEP-0079). */ + ExtError, /**< An extension containing an error. */ + ExtCaps, /**< An extension containing Entity Capabilities + * (XEP-0115). */ + ExtChatState, /**< An extension containing a chat state (XEP-0085). */ + ExtMessageEvent, /**< An extension containing a message event (XEP-0022). */ + ExtDataForm, /**< An extension containing a Data Form (XEP-0004). */ + ExtNickname, /**< An extension containing a User Nickname (XEP-0172). */ + ExtResourceBind, /**< A resource bind SE (RFC3921). */ + ExtSessionCreation, /**< A session establishing SE (RFC3921). */ + ExtVersion, /**< An extension containing a Version request/reply + * (XEP-0092). */ + ExtXHtmlIM, /**< An extension containing an XHTML message + * representation (XEP-0071) */ + ExtDiscoInfo, /**< An extension containing a disco#info element (XEP-0030). */ + ExtDiscoItems, /**< An extension containing a disco#items element (XEP-0030). */ + ExtAdhocCommand, /**< An extension containing a Adhoc Command (XEP-0050). */ + ExtPrivateXML, /**< An extension used for Private XML Storage (XEP-0048). */ + ExtRoster, /**< An extension dealing with the user's roster (RFC-3921). */ + ExtFeatureNeg, /**< An extension abstracting a Feature Negotiation element + * (XEP-0020). */ + ExtIBB, /**< An extension dealing with IBBs (XEP-0047). */ + ExtNonSaslAuth, /**< An extension for doing Non-SASL Authentication (XEP-0078). */ + ExtMUC, /**< An extension dealing with the muc namespace of XEP-0045. */ + ExtMUCOwner, /**< An extension dealing with the muc#owner namespace of XEP-0045. */ + ExtMUCAdmin, /**< An extension dealing with the muc#admin namespace of XEP-0045. */ + ExtMUCUser, /**< An extension dealing with the muc#user namespace of XEP-0045. */ + ExtMUCUnique, /**< An extension dealing with the muc#unique namespace of XEP-0045. */ + ExtPing, /**< An XMPP Ping (XEP-0199). */ + ExtSearch, /**< A XEP-0055 (Jabber Search) wrapper. */ + ExtRegistration, /**< A XEP-0077 (In-Band Registration) wrapper. */ + ExtJingle, /**< An extension dealing with Jingle (XEP-0166) */ + ExtVCard, /**< An extension dealing with vcard-temp (XEP-0054) */ + ExtPrivacy, /**< An extension dealing with Privacy Lists (XEP-0016) */ + ExtLastActivity, /**< An extension dealing with Last Activity (XEP-0012). */ + ExtFlexOffline, /**< An extension dealing with Flexible Offline Messages (XEP-0013). */ + ExtSI, /**< An extension dealing with Stream Initiation (XEP-0095). */ + ExtS5BQuery, /**< An extension dealing with stream host offers (XEP-0065) */ + ExtPubSub, /**< An extension dealing with PubSub requests (XEP-0060). */ + ExtPubSubOwner, /**< An extension dealing with PubSub owner requests (XEP-0060). */ + ExtPubSubEvent, /**< An extension for PubSub event notifications + * (XEP-0060) */ + ExtSHIM, /**< An extension dealing with Stanza Headers and Internet Metadata (XEP-0131). */ + ExtAttention, /**< An extension dealing with Attention (XEP-0224). */ + ExtUser /**< User-supplied extensions must use IDs above this. Do + * not hard-code ExtUser's value anywhere, it is subject + * to change. */ + }; + + /** + * @brief This class abstracts a stanza extension, which is usually + * an element in a specific namespace. + * + * This class is the base class for almost all protocol extensions in gloox. + * As such, it should be used whenever an add-on to the core XMPP spec + * needs to be made. For simple protocols it may suffice to create a sub-class + * of StanzaExtension. For protocols which require keeping of state, an additional + * persistent object acting like a manager may be needed. + * + * A Stanza can be extended by additional namespaced child elements. Obviously, + * it is not viable to include all the kinds of extensions possible. To avoid + * hard-coding of such extensions into gloox, StanzaExtension can be used to + * inform the core of gloox about additional supported extensions without it + * needing to know about the exact implementation. + * + * Note that a StanzaExtension can be used for both sending and receiving + * of custom protocols. When receiving, gloox requires an appropriate implementation + * of the pure virtuals filterString() and newInstance(). To be able to properly use + * the encapsulation, some getters may be necessary. Note that the object you will be + * dealing with usually is @em const. + * For sending StanzaExtensions, a custom constructor (as well as some setters, + * possibly) is needed. Additionally, an implementation of tag() is required. + * + * @li Sub-class StanzaExtension and re-implement filterString(). filterString() + * is supposed to return an XPath expression that matches the child element + * of a stanza that the protocol-to-implement uses. For example, consider this + * hypothetical XML format: The protocol is encapsulated inside a <stats> + * element in the 'ext:stats' namespace. It uses IQ stanzas for transmission. + * @code + * + * + * + * + * + * + * 10 + * + * + * @endcode + * The idea of filterString() and its XPath expression is to match the + * <stats> element such that it can be fed to your + * StanzaExtension-derived class' constructor when creating a new instance + * of it. For our @e stats protocol, filterString() would return something like: + * /iq/stats[\@xmlns='ext:stats'] + * + * @li When subclassing StanzaExtension, you have to initialize it with an int, the extension's + * type. You should choose a value that is not yet used in gloox, and unique to + * the given extension you implement. In general, you are free to use values + * above @link gloox::ExtUser ExtUser @endlink. See + * @link gloox::StanzaExtensionType StanzaExtensionType @endlink for existing values. + * + * @li The next step is to implement newInstance(). Whenever filterString()'s + * XPath expression matches a child element of an incoming stanza, newInstance() + * is called with the matched Tag. For our example above, this is the <stats> + * element (including its children): + * @code + * + * 10 + * + * @endcode + * The purpose of newInstance() is to return a new instance of your specialized + * StanzaExtension (implicitly cast to StanzaExtension). This way, gloox can deal + * entirely with the abstract base, StanzaExtension, and never ever needs to know + * which kind of extension it deals with. The most common implementation of + * newInstance() looks like this: + * @code + * StanzaExtension* StatsExtension::newInstance( const Tag* tag ) const + * { + * return new StatsExtension( tag ); + * } + * @endcode + * This of course implies that a constructor exists that takes a const Tag* as the + * only parameter. + * + * @li Finally, gloox must be able to serialize the StanzaExtension back + * into string'ified XML. This is done by means of the tag() function which + * must be reimplemented. The output Tag should -- like the input Tag -- be embeddable + * into the respective stanza. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API StanzaExtension + { + public: + /** + * Constructs an empty StanzaExtension. + * @param type Designates the extension's type. It should be one of StanzaExtensionType + * for built-in extensions, and it should be higher than ExtUser for custom types. + */ + StanzaExtension( int type ) : m_valid( false ), m_extensionType( type ) {} + + /** + * Virtual destructor. + */ + virtual ~StanzaExtension() {} + + /** + * Returns an XPath expression that describes a path to child elements of a + * stanza that an extension handles. + * + * @return The extension's filter string. + */ + virtual const std::string& filterString() const = 0; + + /** + * Returns a new Instance of the derived type. Usually, for a derived class FooExtension, + * the implementation of this function looks like: + * @code + * StanzaExtension* FooExtension::newInstance( const Tag* tag ) const + * { + * return new FooExtension( tag ); + * } + * @endcode + * @return The derived extension's new instance. + */ + virtual StanzaExtension* newInstance( const Tag* tag ) const = 0; + + /** + * Returns a Tag representation of the extension. + * @return A Tag representation of the extension. + */ + virtual Tag* tag() const = 0; + + /** + * Returns an identical copy of the current StanzaExtension. + * @return An identical copy of the current StanzaExtension. + */ + virtual StanzaExtension* clone() const = 0; + + /** + * Returns the extension's type. + * @return The extension's type. + */ + int extensionType() const { return m_extensionType; } + + protected: + bool m_valid; + + private: + int m_extensionType; + + }; + +} + +#endif // STANZAEXTENSION_H__ diff --git a/libs/libgloox/stanzaextensionfactory.cpp b/libs/libgloox/stanzaextensionfactory.cpp new file mode 100644 index 0000000..06d65e2 --- /dev/null +++ b/libs/libgloox/stanzaextensionfactory.cpp @@ -0,0 +1,85 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "stanzaextensionfactory.h" + +#include "gloox.h" +#include "util.h" +#include "stanza.h" +#include "stanzaextension.h" +#include "tag.h" + +namespace gloox +{ + + StanzaExtensionFactory::StanzaExtensionFactory() + { + } + + StanzaExtensionFactory::~StanzaExtensionFactory() + { + util::clearList( m_extensions ); + } + + void StanzaExtensionFactory::registerExtension( StanzaExtension* ext ) + { + if( !ext ) + return; + + SEList::iterator it = m_extensions.begin(); + SEList::iterator it2; + while( it != m_extensions.end() ) + { + it2 = it++; + if( ext->extensionType() == (*it2)->extensionType() ) + { + delete (*it2); + m_extensions.erase( it2 ); + } + } + m_extensions.push_back( ext ); + } + + bool StanzaExtensionFactory::removeExtension( int ext ) + { + SEList::iterator it = m_extensions.begin(); + for( ; it != m_extensions.end(); ++it ) + { + if( (*it)->extensionType() == ext ) + { + delete (*it); + m_extensions.erase( it ); + return true; + } + } + return false; + } + + void StanzaExtensionFactory::addExtensions( Stanza& stanza, Tag* tag ) + { + ConstTagList::const_iterator it; + SEList::const_iterator ite = m_extensions.begin(); + for( ; ite != m_extensions.end(); ++ite ) + { + const ConstTagList& match = tag->findTagList( (*ite)->filterString() ); + it = match.begin(); + for( ; it != match.end(); ++it ) + { + StanzaExtension* se = (*ite)->newInstance( (*it) ); + if( se ) + stanza.addExtension( se ); + } + } + } + +} diff --git a/libs/libgloox/stanzaextensionfactory.h b/libs/libgloox/stanzaextensionfactory.h new file mode 100644 index 0000000..ab53451 --- /dev/null +++ b/libs/libgloox/stanzaextensionfactory.h @@ -0,0 +1,90 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef STANZAEXTENSIONFACTORY_H__ +#define STANZAEXTENSIONFACTORY_H__ + +#include + +namespace gloox +{ + + class Tag; + class Stanza; + class StanzaExtension; + + /** + * @brief A Factory that creates StanzaExtensions from Tags. + * + * To supply a custom StanzaExtension, reimplement StanzaExtension's + * virtuals and pass an instance to registerExtension(). + * + * You should not need to use this class directly. Use ClientBase::registerStanzaExtension() + * instead. See StanzaExtension for more information about adding protocol implementations + * to gloox. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class StanzaExtensionFactory + { + + friend class ClientBase; + + public: + /** + * Use this function to inform StanzaExtensionFactory about available StanzaExtensions. + * By default, StanzaExtensionFactory does not know about any extensions. + * gloox-built-in extensions will usually be registered by their respective protocol + * implementations unless otherwise noted in the extension's API docs. + * @param ext An extension to register. + * @note The supplied StanzaExtension will be deleted in StanzaExtensionFactory's destructor. + * @note Only one instance per extension type can be registered. In case an extension is + * registered that is of the same type as an already registered extension, the new extension + * will replace the previously registered one. + */ + void registerExtension( StanzaExtension* ext ); + + /** + * Removes the given extension type. + * @param ext The extension type. + * @return @b True if the given type was found (and removed), @b false otherwise. + */ + bool removeExtension( int ext ); + + /** + * Creates a new StanzaExtensionFactory. + */ + StanzaExtensionFactory(); + + /** + * Non-virtual destructor. + */ + ~StanzaExtensionFactory(); + + /** + * This function creates StanzaExtensions from the given Tag and attaches them to the given Stanza. + * @param stanza The Stanza to attach the extensions to. + * @param tag The Tag to parse and create the StanzaExtension from. + */ + void addExtensions( Stanza& stanza, Tag* tag ); + + private: + typedef std::list SEList; + SEList m_extensions; + + }; + +} + +#endif // STANZAEXTENSIONFACTORY_H__ diff --git a/libs/libgloox/statisticshandler.h b/libs/libgloox/statisticshandler.h new file mode 100644 index 0000000..74ef964 --- /dev/null +++ b/libs/libgloox/statisticshandler.h @@ -0,0 +1,80 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef STATISTICSHANDLER_H__ +#define STATISTICSHANDLER_H__ + +#include "stanza.h" + +namespace gloox +{ + + /** + * A structure describing the current connection statistics. + */ + struct StatisticsStruct + { + long int totalBytesSent; /**< The total number of bytes sent over the wire. This does @b not + * include the TLS handshake nor any TLS-related overhead, but it does + * include anything sent before compression was switched on. */ + long int totalBytesReceived; /**< The total number of bytes received over the wire. This does @b not + * include the TLS handshake nor any TLS-related overhead, but it does + * include anything sent before compression was switched on. */ + long int compressedBytesSent; /**< Total number of bytes sent over the wire after compression was + * applied. */ + long int compressedBytesReceived; /**< Total number of bytes received over the wire before decompression + * was applied. */ + long int uncompressedBytesSent; /**< Total number of bytes sent over the wire before compression was + * applied. */ + long int uncompressedBytesReceived; /**< Total number of bytes received over the wire after decompression + * was applied. */ + long int totalStanzasSent; /**< The total number of Stanzas sent. */ + long int totalStanzasReceived; /**< The total number of Stanzas received. */ + long int iqStanzasSent; /**< The total number of IQ Stanzas sent. */ + long int iqStanzasReceived; /**< The total number of IQ Stanzas received. */ + long int messageStanzasSent; /**< The total number of Message Stanzas sent. */ + long int messageStanzasReceived; /**< The total number of Message Stanzas received. */ + long int s10nStanzasSent; /**< The total number of Subscription Stanzas sent. */ + long int s10nStanzasReceived; /**< The total number of Subscription Stanzas received. */ + long int presenceStanzasSent; /**< The total number of Presence Stanzas sent. */ + long int presenceStanzasReceived; /**< The total number of Presence Stanzas received. */ + bool encryption; /**< Whether or not the connection (to the server) is encrypted. */ + bool compression; /**< Whether or not the stream (to the server) gets compressed. */ + }; + + /** + * @brief A virtual interface which can be reimplemented to receive connection statistics. + * + * Derived classes can be registered as StatisticsHandlers with the ClientBase. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API StatisticsHandler + { + public: + /** + * Virtual Destructor. + */ + virtual ~StatisticsHandler() {} + + /** + * This function is called when a Stanza has been sent or received. + * @param stats The updated connection statistics. + */ + virtual void handleStatistics( const StatisticsStruct stats ) = 0; + }; + +} + +#endif // STATISTICSHANDLER_H__ diff --git a/libs/libgloox/subscription.cpp b/libs/libgloox/subscription.cpp new file mode 100644 index 0000000..bb310d6 --- /dev/null +++ b/libs/libgloox/subscription.cpp @@ -0,0 +1,77 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#include "subscription.h" +#include "util.h" + +namespace gloox +{ + + static const char* msgTypeStringValues[] = + { + "subscribe", "subscribed", "unsubscribe", "unsubscribed" + }; + + static inline const std::string typeString( Subscription::S10nType type ) + { + return util::lookup( type, msgTypeStringValues ); + } + + Subscription::Subscription( Tag* tag ) + : Stanza( tag ), m_subtype( Invalid ), m_stati( 0 ) + { + if( !tag || tag->name() != "presence" ) + return; + + m_subtype = (S10nType)util::lookup( tag->findAttribute( TYPE ), msgTypeStringValues ); + + const ConstTagList& c = tag->findTagList( "/presence/status" ); + ConstTagList::const_iterator it = c.begin(); + for( ; it != c.end(); ++it ) + setLang( &m_stati, m_status, (*it) ); + } + + Subscription::Subscription( S10nType type, const JID& to, const std::string& status, + const std::string& xmllang ) + : Stanza( to ), m_subtype( type ), m_stati( 0 ) + { + setLang( &m_stati, m_status, status, xmllang ); + } + + Subscription::~Subscription() + { + delete m_stati; + } + + Tag* Subscription::tag() const + { + if( m_subtype == Invalid ) + return 0; + + Tag* t = new Tag( "presence" ); + if( m_to ) + t->addAttribute( "to", m_to.full() ); + if( m_from ) + t->addAttribute( "from", m_from.full() ); + + t->addAttribute( "type", typeString( m_subtype ) ); + + getLangs( m_stati, m_status, "status", t ); + + StanzaExtensionList::const_iterator it = m_extensionList.begin(); + for( ; it != m_extensionList.end(); ++it ) + t->addChild( (*it)->tag() ); + + return t; + } + +} diff --git a/libs/libgloox/subscription.h b/libs/libgloox/subscription.h new file mode 100644 index 0000000..ae377d1 --- /dev/null +++ b/libs/libgloox/subscription.h @@ -0,0 +1,106 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#ifndef SUBSCRIPTION_H__ +#define SUBSCRIPTION_H__ + +#include "stanza.h" + +#include + +namespace gloox +{ + + class JID; + + /** + * @brief An abstraction of a subscription stanza. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API Subscription : public Stanza + { + + public: + + friend class ClientBase; + + /** + * Describes the different valid message types. + */ + enum S10nType + { + Subscribe, /**> A subscription request. */ + Subscribed, /**< A subscription notification. */ + Unsubscribe, /**< An unsubscription request. */ + Unsubscribed, /**< An unsubscription notification. */ + Invalid /**< The stanza is invalid. */ + }; + + /** + * Creates a Subscription request. + * @param type The presence type. + * @param to The intended receiver. Use an empty JID to create a broadcast packet. + * @param status An optional status message (e.g. "please authorize me"). + * @param xmllang An optional xml:lang for the status message. + */ + Subscription( S10nType type, const JID& to, const std::string& status = EmptyString, + const std::string& xmllang = EmptyString ); + /** + * Destructor. + */ + virtual ~Subscription(); + + /** + * Returns the subscription stanza's type. + * @return The subscription stanza's type. + * + */ + S10nType subtype() const { return m_subtype; } + + /** + * Returns the status text of a presence stanza for the given language if available. + * If the requested language is not available, the default status text (without a xml:lang + * attribute) will be returned. + * @param lang The language identifier for the desired language. It must conform to + * section 2.12 of the XML specification and RFC 3066. If empty, the default body + * will be returned, if any. + * @return The status text set by the sender. + */ + const std::string status( const std::string& lang = "default" ) const + { + return findLang( m_stati, m_status, lang ); + } + + // reimplemented from Stanza + virtual Tag* tag() const; + + private: +#ifdef SUBSCRIPTION_TEST + public: +#endif + /** + * Creates a Subscription request from the given Tag. The original Tag will be ripped off. + * @param tag The Tag to parse. + */ + Subscription( Tag* tag ); + + S10nType m_subtype; + StringMap* m_stati; + std::string m_status; + + }; + +} + +#endif // SUBSCRIPTION_H__ diff --git a/libs/libgloox/subscriptionhandler.h b/libs/libgloox/subscriptionhandler.h new file mode 100644 index 0000000..d891a7f --- /dev/null +++ b/libs/libgloox/subscriptionhandler.h @@ -0,0 +1,49 @@ +/* + Copyright (c) 2004-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef SUBSCRIPTIONHANDLER_H__ +#define SUBSCRIPTIONHANDLER_H__ + +#include "subscription.h" + +namespace gloox +{ + + /** + * @brief A virtual interface which can be reimplemented to receive incoming subscription stanzas. + * + * Derived classes can be registered as SubscriptionHandlers with the Client. + * Upon an incoming Subscription packet @ref handleSubscription() will be called. + * @author Jakob Schroeter + */ + class GLOOX_API SubscriptionHandler + { + public: + /** + * Virtual destructor. + */ + virtual ~SubscriptionHandler() {} + + /** + * Reimplement this function if you want to be notified about incoming + * subscriptions/subscription requests. + * @param subscription The complete Subscription stanza. + * @since 1.0 + */ + virtual void handleSubscription( const Subscription& subscription ) = 0; + + }; + +} + +#endif // SUBSCRIPTIONHANDLER_H__ diff --git a/libs/libgloox/tag.cpp b/libs/libgloox/tag.cpp new file mode 100644 index 0000000..8416157 --- /dev/null +++ b/libs/libgloox/tag.cpp @@ -0,0 +1,1372 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "tag.h" +#include "util.h" + +#include + +#include + +namespace gloox +{ + + // ---- Tag::Attribute ---- + Tag::Attribute::Attribute( Tag* parent, const std::string& name, const std::string& value, + const std::string& xmlns ) + : m_parent( parent ) + { + if( m_parent ) + m_parent->addAttribute( this ); + + init( name, value, xmlns ); + } + + Tag::Attribute::Attribute( const std::string& name, const std::string& value, + const std::string& xmlns ) + : m_parent( 0 ) + { + init( name, value, xmlns ); + } + + Tag::Attribute::Attribute( const Attribute& attr ) + : m_parent( attr.m_parent ), m_name( attr.m_name ), m_value( attr.m_value ), + m_xmlns( attr.m_xmlns ), m_prefix( attr.m_prefix ) + { + } + + void Tag::Attribute::init( const std::string& name, const std::string& value, + const std::string& xmlns ) + { + if( util::checkValidXMLChars( xmlns ) ) + m_xmlns = xmlns; + else + return; + + if( util::checkValidXMLChars( value ) ) + m_value = value; + else + return; + + if( util::checkValidXMLChars( name ) ) + m_name = name; + else + return; + } + + bool Tag::Attribute::setValue( const std::string& value ) + { + if( !util::checkValidXMLChars( value ) ) + return false; + + m_value = value; + return true; + } + + bool Tag::Attribute::setXmlns( const std::string& xmlns ) + { + if( !util::checkValidXMLChars( xmlns ) ) + return false; + + m_xmlns = xmlns; + return true; + } + + bool Tag::Attribute::setPrefix( const std::string& prefix ) + { + if( !util::checkValidXMLChars( prefix ) ) + return false; + + m_prefix = prefix; + return true; + } + + const std::string& Tag::Attribute::xmlns() const + { + if( !m_xmlns.empty() ) + return m_xmlns; + + if( m_parent ) + return m_parent->xmlns( m_prefix ); + + return EmptyString; + } + + const std::string& Tag::Attribute::prefix() const + { + if( !m_prefix.empty() ) + return m_prefix; + + if( m_parent ) + return m_parent->prefix( m_xmlns ); + + return EmptyString; + } + + const std::string Tag::Attribute::xml() const + { + if( m_name.empty() ) + return EmptyString; + + std::string xml; + xml += ' '; + if( !m_prefix.empty() ) + { + xml += m_prefix; + xml += ':'; + } + xml += m_name; + xml += "='"; + xml += util::escape( m_value ); + xml += '\''; + + return xml; + } + // ---- ~Tag::Attribute ---- + + // ---- Tag ---- + Tag::Tag( const std::string& name, const std::string& cdata ) + : m_parent( 0 ), m_children( 0 ), m_cdata( 0 ), + m_attribs( 0 ), m_nodes( 0 ), + m_xmlnss( 0 ) + { + addCData( cdata ); // implicitly UTF-8 checked + + if( util::checkValidXMLChars( name ) ) + m_name = name; + } + + Tag::Tag( Tag* parent, const std::string& name, const std::string& cdata ) + : m_parent( parent ), m_children( 0 ), m_cdata( 0 ), + m_attribs( 0 ), m_nodes( 0 ), + m_xmlnss( 0 ) + { + if( m_parent ) + m_parent->addChild( this ); + + addCData( cdata ); // implicitly UTF-8 checked + + if( util::checkValidXMLChars( name ) ) + m_name = name; + } + + Tag::Tag( const std::string& name, + const std::string& attrib, + const std::string& value ) + : m_parent( 0 ), m_children( 0 ), m_cdata( 0 ), + m_attribs( 0 ), m_nodes( 0 ), + m_name( name ), m_xmlnss( 0 ) + { + addAttribute( attrib, value ); // implicitly UTF-8 checked + + if( util::checkValidXMLChars( name ) ) + m_name = name; + } + + Tag::Tag( Tag* parent, const std::string& name, + const std::string& attrib, + const std::string& value ) + : m_parent( parent ), m_children( 0 ), m_cdata( 0 ), + m_attribs( 0 ), m_nodes( 0 ), + m_name( name ), m_xmlnss( 0 ) + { + if( m_parent ) + m_parent->addChild( this ); + + addAttribute( attrib, value ); // implicitly UTF-8 checked + + if( util::checkValidXMLChars( name ) ) + m_name = name; + } + + Tag::Tag( Tag* tag ) + : m_parent( 0 ), m_children( 0 ), m_cdata( 0 ), m_attribs( 0 ), + m_nodes( 0 ), m_xmlnss( 0 ) + { + if( !tag ) + return; + + m_children = tag->m_children; + m_cdata = tag->m_cdata; + m_attribs = tag->m_attribs; + m_nodes = tag->m_nodes; + m_name = tag->m_name; + m_xmlns = tag->m_xmlns; + m_xmlnss = tag->m_xmlnss; + + tag->m_nodes = 0; + tag->m_cdata = 0; + tag->m_attribs = 0; + tag->m_children = 0; + tag->m_xmlnss = 0; + + if( m_attribs ) + { + AttributeList::iterator it = m_attribs->begin(); + while( it != m_attribs->end() ) + (*it++)->m_parent = this; + } + + if( m_children ) + { + TagList::iterator it = m_children->begin(); + while( it != m_children->end() ) + (*it++)->m_parent = this; + } + } + + Tag::~Tag() + { + if( m_cdata ) + util::clearList( *m_cdata ); + if( m_attribs ) + util::clearList( *m_attribs ); + if( m_children ) + util::clearList( *m_children ); + if( m_nodes ) + util::clearList( *m_nodes ); + + delete m_cdata; + delete m_attribs; + delete m_children; + delete m_nodes; + delete m_xmlnss; + + m_parent = 0; + } + + bool Tag::operator==( const Tag& right ) const + { + if( m_name != right.m_name || m_xmlns != right.m_xmlns ) + return false; + + if( m_cdata && right.m_cdata ) + { + StringPList::const_iterator ct = m_cdata->begin(); + StringPList::const_iterator ct_r = right.m_cdata->begin(); + while( ct != m_cdata->end() && ct_r != right.m_cdata->end() && *(*ct) == *(*ct_r) ) + { + ++ct; + ++ct_r; + } + if( ct != m_cdata->end() ) + return false; + } + else if( m_cdata || right.m_cdata ) + return false; + + if( m_children && right.m_children ) + { + TagList::const_iterator it = m_children->begin(); + TagList::const_iterator it_r = right.m_children->begin(); + while( it != m_children->end() && it_r != right.m_children->end() && *(*it) == *(*it_r) ) + { + ++it; + ++it_r; + } + if( it != m_children->end() ) + return false; + } + else if( m_children || right.m_children ) + return false; + + if( m_attribs && right.m_attribs ) + { + AttributeList::const_iterator at = m_attribs->begin(); + AttributeList::const_iterator at_r = right.m_attribs->begin(); + while( at != m_attribs->end() && at_r != right.m_attribs->end() && *(*at) == *(*at_r) ) + { + ++at; + ++at_r; + } + if( at != m_attribs->end() ) + return false; + } + else if( m_attribs || right.m_attribs ) + return false; + + return true; + } + + const std::string Tag::xml() const + { + if( m_name.empty() ) + return EmptyString; + + std::string xml = "<"; + if( !m_prefix.empty() ) + { + xml += m_prefix; + xml += ':'; + } + xml += m_name; + if( m_attribs && !m_attribs->empty() ) + { + AttributeList::const_iterator it_a = m_attribs->begin(); + for( ; it_a != m_attribs->end(); ++it_a ) + { + xml += (*it_a)->xml(); + } + } + + if( !m_nodes || m_nodes->empty() ) + xml += "/>"; + else + { + xml += '>'; + NodeList::const_iterator it_n = m_nodes->begin(); + for( ; it_n != m_nodes->end(); ++it_n ) + { + switch( (*it_n)->type ) + { + case TypeTag: + xml += (*it_n)->tag->xml(); + break; + case TypeString: + xml += util::escape( *((*it_n)->str) ); + break; + } + } + xml += "'; + } + + return xml; + } + + bool Tag::addAttribute( Attribute* attr ) + { + if( !attr ) + return false; + + if( !(*attr) ) + { + delete attr; + return false; + } + + if( !m_attribs ) + m_attribs = new AttributeList(); + + AttributeList::iterator it = m_attribs->begin(); + for( ; it != m_attribs->end(); ++it ) + { + if( (*it)->name() == attr->name() + && ( (*it)->xmlns() == attr->xmlns() || (*it)->prefix() == attr->prefix() ) ) + { + delete (*it); + (*it) = attr; + return true; + } + } + + m_attribs->push_back( attr ); + + return true; + } + + bool Tag::addAttribute( const std::string& name, const std::string& value ) + { + if( name.empty() || value.empty() ) + return false; + + return addAttribute( new Attribute( name, value ) ); + } + + bool Tag::addAttribute( const std::string& name, int value ) + { + if( name.empty() ) + return false; + + return addAttribute( name, util::int2string( value ) ); + } + + bool Tag::addAttribute( const std::string& name, long value ) + { + if( name.empty() ) + return false; + + return addAttribute( name, util::long2string( value ) ); + } + + void Tag::setAttributes( const AttributeList& attributes ) + { + if( !m_attribs ) + m_attribs = new AttributeList( attributes ); + else + { + util::clearList( *m_attribs ); + *m_attribs = attributes; + } + + AttributeList::iterator it = m_attribs->begin(); + for( ; it != m_attribs->end(); ++it ) + (*it)->m_parent = this; + } + + void Tag::addChild( Tag* child ) + { + if( !child ) + return; + + if( !m_nodes ) + m_nodes = new NodeList(); + if( !m_children ) + m_children = new TagList(); + + m_children->push_back( child ); + child->m_parent = this; + m_nodes->push_back( new Node( TypeTag, child ) ); + } + + void Tag::addChildCopy( const Tag* child ) + { + if( !child ) + return; + + addChild( child->clone() ); + } + + bool Tag::setCData( const std::string& cdata ) + { + if( cdata.empty() || !util::checkValidXMLChars( cdata ) ) + return false; + + if( !m_cdata ) + m_cdata = new StringPList(); + else + util::clearList( *m_cdata ); + + if( !m_nodes ) + m_nodes = new NodeList(); + else + { + NodeList::iterator it = m_nodes->begin(); + NodeList::iterator t; + while( it != m_nodes->end() ) + { + if( (*it)->type == TypeString ) + { + t = it++; + delete (*t); + m_nodes->erase( t ); + } + } + } + + return addCData( cdata ); + } + + bool Tag::addCData( const std::string& cdata ) + { + if( cdata.empty() || !util::checkValidXMLChars( cdata ) ) + return false; + + if( !m_cdata ) + m_cdata = new StringPList(); + if( !m_nodes ) + m_nodes = new NodeList(); + + std::string* str = new std::string( cdata ); + m_cdata->push_back( str ); + m_nodes->push_back( new Node( TypeString, str ) ); + return true; + } + + const std::string Tag::cdata() const + { + if( !m_cdata ) + return EmptyString; + + std::string str; + StringPList::const_iterator it = m_cdata->begin(); + for( ; it != m_cdata->end(); ++it ) + str += *(*it); + + return str; + } + + const TagList& Tag::children() const + { + static const TagList empty; + return m_children ? *m_children : empty; + } + + const Tag::AttributeList& Tag::attributes() const + { + static const AttributeList empty; + return m_attribs ? *m_attribs : empty; + } + + bool Tag::setXmlns( const std::string& xmlns, const std::string& prefix ) + { + if( !util::checkValidXMLChars( xmlns ) || !util::checkValidXMLChars( prefix ) ) + return false; + + if( prefix.empty() ) + { + m_xmlns = xmlns; + return addAttribute( XMLNS, m_xmlns ); + } + else + { + if( !m_xmlnss ) + m_xmlnss = new StringMap(); + + (*m_xmlnss)[prefix] = xmlns; + + return addAttribute( XMLNS + ":" + prefix, xmlns ); + } + } + + const std::string& Tag::xmlns() const + { + return xmlns( m_prefix ); + } + + const std::string& Tag::xmlns( const std::string& prefix ) const + { + if( prefix.empty() ) + { + return hasAttribute( XMLNS ) ? findAttribute( XMLNS ) : m_xmlns; + } + + if( m_xmlnss ) + { + StringMap::const_iterator it = m_xmlnss->find( prefix ); + if( it != m_xmlnss->end() ) + return (*it).second; + } + + return m_parent ? m_parent->xmlns( prefix ) : EmptyString; + } + + bool Tag::setPrefix( const std::string& prefix ) + { + if( !util::checkValidXMLChars( prefix ) ) + return false; + + m_prefix = prefix; + return true; + } + + const std::string& Tag::prefix( const std::string& xmlns ) const + { + if( xmlns.empty() || !m_xmlnss ) + return EmptyString; + + StringMap::const_iterator it = m_xmlnss->begin(); + for( ; it != m_xmlnss->end(); ++it ) + { + if( (*it).second == xmlns ) + return (*it).first; + } + + return EmptyString; + } + + const std::string& Tag::findAttribute( const std::string& name ) const + { + if( !m_attribs ) + return EmptyString; + + AttributeList::const_iterator it = m_attribs->begin(); + for( ; it != m_attribs->end(); ++it ) + if( (*it)->name() == name ) + return (*it)->value(); + + return EmptyString; + } + + bool Tag::hasAttribute( const std::string& name, const std::string& value ) const + { + if( name.empty() || !m_attribs ) + return false; + + AttributeList::const_iterator it = m_attribs->begin(); + for( ; it != m_attribs->end(); ++it ) + if( (*it)->name() == name ) + return value.empty() || (*it)->value() == value; + + return false; + } + + bool Tag::hasChild( const std::string& name, const std::string& attr, + const std::string& value ) const + { + if( attr.empty() ) + return findChild( name ) ? true : false; + else + return findChild( name, attr, value ) ? true : false; + } + + Tag* Tag::findChild( const std::string& name ) const + { + if( !m_children ) + return 0; + + TagList::const_iterator it = m_children->begin(); + while( it != m_children->end() && (*it)->name() != name ) + ++it; + return it != m_children->end() ? (*it) : 0; + } + + Tag* Tag::findChild( const std::string& name, const std::string& attr, + const std::string& value ) const + { + if( !m_children || name.empty() ) + return 0; + + TagList::const_iterator it = m_children->begin(); + while( it != m_children->end() && ( (*it)->name() != name || !(*it)->hasAttribute( attr, value ) ) ) + ++it; + return it != m_children->end() ? (*it) : 0; + } + + bool Tag::hasChildWithCData( const std::string& name, const std::string& cdata ) const + { + if( !m_children || name.empty() || cdata.empty() ) + return 0; + + TagList::const_iterator it = m_children->begin(); + while( it != m_children->end() && ( (*it)->name() != name + || ( !cdata.empty() && (*it)->cdata() != cdata ) ) ) + ++it; + return it != m_children->end(); + } + + Tag* Tag::findChildWithAttrib( const std::string& attr, const std::string& value ) const + { + if( !m_children || attr.empty() ) + return 0; + + TagList::const_iterator it = m_children->begin(); + while( it != m_children->end() && !(*it)->hasAttribute( attr, value ) ) + ++it; + return it != m_children->end() ? (*it) : 0; + } + + Tag* Tag::clone() const + { + Tag* t = new Tag( m_name ); + t->m_xmlns = m_xmlns; + t->m_prefix = m_prefix; + + if( m_attribs ) + { + t->m_attribs = new AttributeList(); + Tag::AttributeList::const_iterator at = m_attribs->begin(); + Attribute* attr; + for( ; at != m_attribs->end(); ++at ) + { + attr = new Attribute( *(*at) ); + attr->m_parent = t; + t->m_attribs->push_back( attr ); + } + } + + if( m_xmlnss ) + { + t->m_xmlnss = new StringMap( *m_xmlnss ); + } + + if( m_nodes ) + { + Tag::NodeList::const_iterator nt = m_nodes->begin(); + for( ; nt != m_nodes->end(); ++nt ) + { + switch( (*nt)->type ) + { + case TypeTag: + t->addChild( (*nt)->tag->clone() ); + break; + case TypeString: + t->addCData( *((*nt)->str) ); + break; + } + } + } + + return t; + } + + TagList Tag::findChildren( const std::string& name, + const std::string& xmlns ) const + { + return m_children ? findChildren( *m_children, name, xmlns ) : TagList(); + } + + TagList Tag::findChildren( const TagList& list, const std::string& name, + const std::string& xmlns ) const + { + TagList ret; + TagList::const_iterator it = list.begin(); + for( ; it != list.end(); ++it ) + { + if( (*it)->name() == name && ( xmlns.empty() || (*it)->xmlns() == xmlns ) ) + ret.push_back( (*it) ); + } + return ret; + } + + void Tag::removeChild( const std::string& name, const std::string& xmlns ) + { + if( name.empty() || !m_children || !m_nodes ) + return; + + TagList l = findChildren( name, xmlns ); + TagList::iterator it = l.begin(); + TagList::iterator it2; + while( it != l.end() ) + { + it2 = it++; + NodeList::iterator itn = m_nodes->begin(); + for( ; itn != m_nodes->end(); ++itn ) + { + if( (*itn)->type == TypeTag && (*itn)->tag == (*it2) ) + { + delete (*itn); + m_nodes->erase( itn ); + break; + } + } + m_children->remove( (*it2) ); + delete (*it2); + } + } + + void Tag::removeChild( Tag* tag ) + { + if( m_children ) + m_children->remove( tag ); + + if( !m_nodes ) + return; + + NodeList::iterator it = m_nodes->begin(); + for( ; it != m_nodes->end(); ++it ) + { + if( (*it)->type == TypeTag && (*it)->tag == tag ) + { + delete (*it); + m_nodes->erase( it ); + return; + } + } + } + + void Tag::removeAttribute( const std::string& attr, const std::string& value, + const std::string& xmlns ) + { + if( attr.empty() || !m_attribs ) + return; + + AttributeList::iterator it = m_attribs->begin(); + AttributeList::iterator it2; + while( it != m_attribs->end() ) + { + it2 = it++; + if( (*it2)->name() == attr && ( value.empty() || (*it2)->value() == value ) + && ( xmlns.empty() || (*it2)->xmlns() == xmlns ) ) + { + delete (*it2); + m_attribs->erase( it2 ); + } + } + } + + const std::string Tag::findCData( const std::string& expression ) const + { + const ConstTagList& l = findTagList( expression ); + return !l.empty() ? l.front()->cdata() : EmptyString; + } + + const Tag* Tag::findTag( const std::string& expression ) const + { + const ConstTagList& l = findTagList( expression ); + return !l.empty() ? l.front() : 0; + } + + ConstTagList Tag::findTagList( const std::string& expression ) const + { + ConstTagList l; + if( expression == "/" || expression == "//" ) + return l; + + if( m_parent && expression.length() >= 2 && expression[0] == '/' + && expression[1] != '/' ) + return m_parent->findTagList( expression ); + + unsigned len = 0; + Tag* p = parse( expression, len ); +// if( p ) +// printf( "parsed tree: %s\n", p->xml().c_str() ); + l = evaluateTagList( p ); + delete p; + return l; + } + + ConstTagList Tag::evaluateTagList( Tag* token ) const + { + ConstTagList result; + if( !token ) + return result; + +// printf( "evaluateTagList called in Tag %s and Token %s (type: %s)\n", name().c_str(), +// token->name().c_str(), token->findAttribute( TYPE ).c_str() ); + + TokenType tokenType = (TokenType)atoi( token->findAttribute( TYPE ).c_str() ); + switch( tokenType ) + { + case XTUnion: + add( result, evaluateUnion( token ) ); + break; + case XTElement: + { +// printf( "in XTElement, token: %s\n", token->name().c_str() ); + if( token->name() == name() || token->name() == "*" ) + { +// printf( "found %s\n", name().c_str() ); + const TagList& tokenChildren = token->children(); + if( tokenChildren.size() ) + { + bool predicatesSucceeded = true; + TagList::const_iterator cit = tokenChildren.begin(); + for( ; cit != tokenChildren.end(); ++cit ) + { + if( (*cit)->hasAttribute( "predicate", "true" ) ) + { + predicatesSucceeded = evaluatePredicate( (*cit) ); + if( !predicatesSucceeded ) + return result; + } + } + + bool hasElementChildren = false; + cit = tokenChildren.begin(); + for( ; cit != tokenChildren.end(); ++cit ) + { + if( (*cit)->hasAttribute( "predicate", "true" ) || + (*cit)->hasAttribute( "number", "true" ) ) + continue; + + hasElementChildren = true; + +// printf( "checking %d children of token %s\n", tokenChildren.size(), token->name().c_str() ); + if( m_children && !m_children->empty() ) + { + TagList::const_iterator it = m_children->begin(); + for( ; it != m_children->end(); ++it ) + { + add( result, (*it)->evaluateTagList( (*cit) ) ); + } + } + else if( atoi( (*cit)->findAttribute( TYPE ).c_str() ) == XTDoubleDot && m_parent ) + { + (*cit)->addAttribute( TYPE, XTDot ); + add( result, m_parent->evaluateTagList( (*cit) ) ); + } + } + + if( !hasElementChildren ) + result.push_back( this ); + } + else + { +// printf( "adding %s to result set\n", name().c_str() ); + result.push_back( this ); + } + } +// else +// printf( "found %s != %s\n", token->name().c_str(), name().c_str() ); + + break; + } + case XTDoubleSlash: + { +// printf( "in XTDoubleSlash\n" ); + Tag* t = token->clone(); +// printf( "original token: %s\ncloned token: %s\n", token->xml().c_str(), n->xml().c_str() ); + t->addAttribute( TYPE, XTElement ); + add( result, evaluateTagList( t ) ); + const ConstTagList& res2 = allDescendants(); + ConstTagList::const_iterator it = res2.begin(); + for( ; it != res2.end(); ++it ) + { + add( result, (*it)->evaluateTagList( t ) ); + } + delete t; + break; + } + case XTDot: + { + const TagList& tokenChildren = token->children(); + if( !tokenChildren.empty() ) + { + add( result, evaluateTagList( tokenChildren.front() ) ); + } + else + result.push_back( this ); + break; + } + case XTDoubleDot: + { +// printf( "in XTDoubleDot\n" ); + if( m_parent ) + { + const TagList& tokenChildren = token->children(); + if( tokenChildren.size() ) + { + Tag* testtoken = tokenChildren.front(); + if( testtoken->name() == "*" ) + { + add( result, m_parent->evaluateTagList( testtoken ) ); + } + else + { + Tag* t = token->clone(); + t->addAttribute( TYPE, XTElement ); + t->m_name = m_parent->m_name; + add( result, m_parent->evaluateTagList( t ) ); + delete t; + } + } + else + { + result.push_back( m_parent ); + } + } + } + case XTInteger: + { + const TagList& l = token->children(); + if( !l.size() ) + break; + + const ConstTagList& res = evaluateTagList( l.front() ); + + int pos = atoi( token->name().c_str() ); +// printf( "checking index %d\n", pos ); + if( pos > 0 && pos <= (int)res.size() ) + { + ConstTagList::const_iterator it = res.begin(); + while ( --pos ) + { + ++it; + } + result.push_back( *it ); + } + break; + } + default: + break; + } + return result; + } + + bool Tag::evaluateBoolean( Tag* token ) const + { + if( !token ) + return false; + + bool result = false; + TokenType tokenType = (TokenType)atoi( token->findAttribute( TYPE ).c_str() ); + switch( tokenType ) + { + case XTAttribute: + if( token->name() == "*" && m_attribs && m_attribs->size() ) + result = true; + else + result = hasAttribute( token->name() ); + break; + case XTOperatorEq: + result = evaluateEquals( token ); + break; + case XTOperatorLt: + break; + case XTOperatorLtEq: + break; + case XTOperatorGtEq: + break; + case XTOperatorGt: + break; + case XTUnion: + case XTElement: + { + Tag* t = new Tag( "." ); + t->addAttribute( TYPE, XTDot ); + t->addChild( token ); + result = !evaluateTagList( t ).empty(); + t->removeChild( token ); + delete t; + break; + } + default: + break; + } + + return result; + } + + bool Tag::evaluateEquals( Tag* token ) const + { + if( !token || token->children().size() != 2 ) + return false; + + bool result = false; + TagList::const_iterator it = token->children().begin(); + Tag* ch1 = (*it); + Tag* ch2 = (*++it); + + TokenType tt1 = (TokenType)atoi( ch1->findAttribute( TYPE ).c_str() ); + TokenType tt2 = (TokenType)atoi( ch2->findAttribute( TYPE ).c_str() ); + switch( tt1 ) + { + case XTAttribute: + switch( tt2 ) + { + case XTInteger: + case XTLiteral: + result = ( findAttribute( ch1->name() ) == ch2->name() ); + break; + case XTAttribute: + result = ( hasAttribute( ch1->name() ) && hasAttribute( ch2->name() ) && + findAttribute( ch1->name() ) == findAttribute( ch2->name() ) ); + break; + default: + break; + } + break; + case XTInteger: + case XTLiteral: + switch( tt2 ) + { + case XTAttribute: + result = ( ch1->name() == findAttribute( ch2->name() ) ); + break; + case XTLiteral: + case XTInteger: + result = ( ch1->name() == ch2->name() ); + break; + default: + break; + } + break; + default: + break; + } + + return result; + } + + ConstTagList Tag::allDescendants() const + { + ConstTagList result; + + if( !m_children ) + return result; + + TagList::const_iterator it = m_children->begin(); + for( ; it != m_children->end(); ++it ) + { + result.push_back( (*it) ); + add( result, (*it)->allDescendants() ); + } + return result; + } + + ConstTagList Tag::evaluateUnion( Tag* token ) const + { + ConstTagList result; + if( !token ) + return result; + + const TagList& l = token->children(); + TagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + { + add( result, evaluateTagList( (*it) ) ); + } + return result; + } + + void Tag::closePreviousToken( Tag** root, Tag** current, Tag::TokenType& type, std::string& tok ) const + { + if( !tok.empty() ) + { + addToken( root, current, type, tok ); + type = XTElement; + tok = EmptyString; + } + } + + Tag* Tag::parse( const std::string& expression, unsigned& len, Tag::TokenType border ) const + { + Tag* root = 0; + Tag* current = root; + std::string token; + +// XPathError error = XPNoError; +// XPathState state = Init; +// int expected = 0; +// bool run = true; +// bool ws = false; + + Tag::TokenType type = XTElement; + + char c; + for( ; len < expression.length(); ++len ) + { + c = expression[len]; + if( type == XTLiteralInside && c != '\'' ) + { + token += c; + continue; + } + + switch( c ) + { + case '/': + closePreviousToken( &root, ¤t, type, token ); + + if( len < expression.length()-1 && expression[len+1] == '/' ) + { +// addToken( &root, ¤t, XTDoubleSlash, "//" ); + type = XTDoubleSlash; + ++len; + } +// else +// { +// if( !current ) +// addToken( &root, ¤t, XTSlash, "/" ); +// } + break; + case ']': + closePreviousToken( &root, ¤t, type, token ); + return root; + case '[': + { + closePreviousToken( &root, ¤t, type, token ); + Tag* t = parse( expression, ++len, XTRightBracket ); + if( !addPredicate( &root, ¤t, t ) ) + delete t; + break; + } + case '(': + { + closePreviousToken( &root, ¤t, type, token ); + Tag* t = parse( expression, ++len, XTRightParenthesis ); + if( current ) + { +// printf( "added %s to %s\n", t->xml().c_str(), current->xml().c_str() ); + t->addAttribute( "argument", "true" ); + current->addChild( t ); + } + else + { + root = t; +// printf( "made %s new root\n", t->xml().c_str() ); + } + break; + } + case ')': + closePreviousToken( &root, ¤t, type, token ); + ++len; + return root; + case '\'': + if( type == XTLiteralInside ) + if( expression[len - 2] == '\\' ) + token[token.length() - 2] = c; + else + type = XTLiteral; + else + type = XTLiteralInside; + break; + case '@': + type = XTAttribute; + break; + case '.': + token += c; + if( token.size() == 1 ) + { + if( len < expression.length()-1 && expression[len+1] == '.' ) + { + type = XTDoubleDot; + ++len; + token += c; + } + else + { + type = XTDot; + } + } + break; + case '*': +// if( !root || ( current && ( current->tokenType() == XTSlash +// || current->tokenType() == XTDoubleSlash ) ) ) +// { +// addToken( &root, ¤t, type, "*" ); +// break; +// } + addToken( &root, ¤t, type, "*" ); + type = XTElement; + break; + case '+': + case '>': + case '<': + case '=': + case '|': + { + closePreviousToken( &root, ¤t, type, token ); + std::string s( 1, c ); + Tag::TokenType ttype = getType( s ); + if( ttype <= border ) + return root; + Tag* t = parse( expression, ++len, ttype ); + addOperator( &root, ¤t, t, ttype, s ); + if( border == XTRightBracket ) + return root; + break; + } + default: + token += c; + } + } + + if( !token.empty() ) + addToken( &root, ¤t, type, token ); + +// if( error != XPNoError ) +// printf( "error: %d\n", error ); + return root; + } + + void Tag::addToken( Tag **root, Tag **current, Tag::TokenType type, + const std::string& token ) const + { + Tag* t = new Tag( token ); + if( t->isNumber() && !t->children().size() ) + type = XTInteger; + t->addAttribute( TYPE, type ); + + if( *root ) + { +// printf( "new current %s, type: %d\n", token.c_str(), type ); + (*current)->addChild( t ); + *current = t; + } + else + { +// printf( "new root %s, type: %d\n", token.c_str(), type ); + *current = *root = t; + } + } + + void Tag::addOperator( Tag** root, Tag** current, Tag* arg, + Tag::TokenType type, const std::string& token ) const + { + Tag* t = new Tag( token ); + t->addAttribute( TYPE, type ); +// printf( "new operator: %s (arg1: %s, arg2: %s)\n", t->name().c_str(), (*root)->xml().c_str(), +// arg->xml().c_str() ); + t->addAttribute( "operator", "true" ); + t->addChild( *root ); + t->addChild( arg ); + *current = *root = t; + } + + bool Tag::addPredicate( Tag **root, Tag **current, Tag* token ) const + { + if( !*root || !*current ) + return false; + + if( ( token->isNumber() && !token->children().size() ) || token->name() == "+" ) + { +// printf( "found Index %s, full: %s\n", token->name().c_str(), token->xml().c_str() ); + if( !token->hasAttribute( "operator", "true" ) ) + { + token->addAttribute( TYPE, XTInteger ); + } + if( *root == *current ) + { + *root = token; +// printf( "made Index new root\n" ); + } + else + { + (*root)->removeChild( *current ); + (*root)->addChild( token ); +// printf( "added Index somewhere between root and current\n" ); + } + token->addChild( *current ); +// printf( "added Index %s, full: %s\n", token->name().c_str(), token->xml().c_str() ); + } + else + { + token->addAttribute( "predicate", "true" ); + (*current)->addChild( token ); + } + + return true; + } + + Tag::TokenType Tag::getType( const std::string& c ) + { + if( c == "|" ) + return XTUnion; + if( c == "<" ) + return XTOperatorLt; + if( c == ">" ) + return XTOperatorGt; + if( c == "*" ) + return XTOperatorMul; + if( c == "+" ) + return XTOperatorPlus; + if( c == "=" ) + return XTOperatorEq; + + return XTNone; + } + + bool Tag::isWhitespace( const char c ) + { + return ( c == 0x09 || c == 0x0a || c == 0x0d || c == 0x20 ); + } + + bool Tag::isNumber() const + { + if( m_name.empty() ) + return false; + + std::string::size_type l = m_name.length(); + std::string::size_type i = 0; + while( i < l && isdigit( m_name[i] ) ) + ++i; + return i == l; + } + + void Tag::add( ConstTagList& one, const ConstTagList& two ) + { + ConstTagList::const_iterator it = two.begin(); + for( ; it != two.end(); ++it ) + if( std::find( one.begin(), one.end(), (*it) ) == one.end() ) + one.push_back( (*it) ); + } + +} diff --git a/libs/libgloox/tag.h b/libs/libgloox/tag.h new file mode 100644 index 0000000..3575bb7 --- /dev/null +++ b/libs/libgloox/tag.h @@ -0,0 +1,710 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef TAG_H__ +#define TAG_H__ + +#include "gloox.h" + +#include +#include +#include + +namespace gloox +{ + + class Tag; + + /** + * A list of Tags. + */ + typedef std::list TagList; + + /** + * A list of const Tags. + */ + typedef std::list ConstTagList; + + /** + * @brief This is an abstraction of an XML element. + * + * @note Use setXmlns() to set namespaces and namespace prefixes. + * + * @author Jakob Schroeter + * @since 0.4 + */ + class GLOOX_API Tag + { + + friend class Parser; + + public: + + /** + * An XML element's attribute. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class GLOOX_API Attribute + { + + friend class Tag; + + public: + /** + * Creates a new Attribute from @c name, @c value and optional @c xmlns and attaches + * it to the given Tag. + * + * In the future: If @c xmlns is not empty, and if it is different from the Tag's + * default namespace, an appropriate and unique namespace declaration (prefix) will + * be added to the Tag and the attribute will be prefixed accordingly. + * @param parent The Tag to attach the Attribute to. + * @param name The attribute's name. Invalid (non-UTF-8) input will be ignored. + * @param value The attribute's value. Invalid (non-UTF-8) input will be ignored. + * @param xmlns The attribute's namespace. Invalid (non-UTF-8) input will be ignored. + */ + Attribute( Tag* parent, const std::string& name, const std::string& value, + const std::string& xmlns = EmptyString ); + + /** + * Creates a new Attribute from @c name, @c value and optional @c xmlns. + * @param name The attribute's name. Invalid (non-UTF-8) input will be ignored. + * @param value The attribute's value. Invalid (non-UTF-8) input will be ignored. + * @param xmlns The attribute's namespace. Invalid (non-UTF-8) input will be ignored. + */ + Attribute( const std::string& name, const std::string& value, + const std::string& xmlns = EmptyString ); + + /** + * Copy constructor. + * @param attr The Attribute to copy. + */ + Attribute( const Attribute& attr ); + + /** + * Destructor. + */ + virtual ~Attribute() {} + + /** + * Returns the attribute's name. + * @return The attribute's name. + */ + const std::string& name() const { return m_name; } + + /** + * Returns the attribute's value. + * @return The attribute's value. + */ + const std::string& value() const { return m_value; } + + /** + * Sets the attribute's value. + * @param value The new value. + * @return @b True if the input is valid UTF-8, @b false otherwise. Invalid + * input will be ignored. + */ + bool setValue( const std::string& value ); + + /** + * Returns the attribute's namespace. + * @return The attribute's namespace. + */ + const std::string& xmlns() const; + + /** + * Sets the attribute's namespace. + * @param xmlns The new namespace. + * @return @b True if the input is valid UTF-8, @b false otherwise. Invalid + * input will be ignored. + */ + bool setXmlns( const std::string& xmlns ); + + /** + * Sets the attribute's namespace prefix. + * @param prefix The new namespace prefix. + * @return @b True if the input is valid UTF-8, @b false otherwise. Invalid + * input will be ignored. + */ + bool setPrefix( const std::string& prefix ); + + /** + * Returns the attribute's namespace prefix. + * @return The namespace prefix. + */ + const std::string& prefix() const; + + /** + * Returns a string representation of the attribute. + * @return A string representation. + */ + const std::string xml() const; + + /** + * Checks two Attributes for equality. + * @param right The Attribute to check against the current Attribute. + */ + bool operator==( const Attribute &right ) const + { return m_name == right.m_name && m_value == right.m_value && m_xmlns == right.m_xmlns; } + + /** + * Checks two Attributes for inequality. + * @param right The Attribute to check against the current Attribute. + */ + bool operator!=( const Attribute &right ) const + { return !( *this == right ); } + + /** + * Returns @b true if the Attribute is valid, @b false otherwise. + */ + operator bool() const { return !m_name.empty(); } + + private: + void init( const std::string& name, const std::string& value, + const std::string& xmlns ); + Tag* m_parent; + std::string m_name; + std::string m_value; + std::string m_xmlns; + std::string m_prefix; + + }; + + /** + * A list of XML element attributes. + */ + typedef std::list AttributeList; + + /** + * Creates a new tag with a given name (and XML character data, if given). + * @param name The name of the element. + * @param cdata The XML character data of the element. + */ + Tag( const std::string& name, const std::string& cdata = EmptyString ); + + /** + * Creates a new tag as a child tag of the given parent, with a given name (and + * XML character data, if given). + * @param parent The parent tag. + * @param name The name of the element. + * @param cdata The XML character data of the element. + */ + Tag( Tag* parent, const std::string& name, const std::string& cdata = EmptyString ); + + /** + * Creates a new tag with a given name and an attribute. + * @param name The name of the element. + * @param attrib The attribute name. + * @param value The attribute value. + */ + Tag( const std::string& name, const std::string& attrib, const std::string& value ); + + /** + * Creates a new tag as a child tag of the given parent, with a given name and + * an attribute. + * @param parent The parent tag. + * @param name The name of the element. + * @param attrib The attribute name. + * @param value The attribute value. + */ + Tag( Tag* parent, const std::string& name, const std::string& attrib, const std::string& value ); + + /** + * Virtual destructor. + */ + virtual ~Tag(); + + /** + * This function can be used to retrieve the complete XML of a tag as a string. + * It includes all the attributes, child nodes and character data. + * @return The complete XML. + */ + const std::string xml() const; + + /** + * Sets the Tag's namespace prefix. + * @param prefix The namespace prefix. + * @return @b True if the input is valid UTF-8, @b false otherwise. Invalid + * input will be ignored. + * @since 1.0 + */ + bool setPrefix( const std::string& prefix ); + + /** + * Returns the namespace prefix for this Tag, if any. + * @return The namespace prefix. + * @since 1.0 + */ + const std::string& prefix() const { return m_prefix; } + + /** + * Returns the namespace prefix for the given namespace. + * @return The namespace prefix for the given namespace. + * @since 1.0 + */ + const std::string& prefix( const std::string& xmlns ) const; + + /* * + * Adds an XML namespace declaration to the Tag. If @b def is false, a unique prefix will + * be created, else the default namespace is set (no prefix). + * @param xmlns The namespace value. + * @param def If @b true, this sets the default namespace; if @b false, a unique namespace + * prefix will be created (unless one already exists for the namespace) and used for + * all subsequent references to the same namespace. + * @since 1.0 + */ +// const std::string addXmlns( const std::string& xmlns, bool def ); + + /** + * Sets an XML namespace with a given prefix, or the default namespace if @c prefix + * is empty. + * @param xmlns The namespace value. + * @param prefix An optional namespace prefix. + * @return @b True if the input is valid UTF-8, @b false otherwise. Invalid + * input will be ignored. + * @since 1.0 + */ + bool setXmlns( const std::string& xmlns, const std::string& prefix = EmptyString ); + + /** + * Returns the namespace for this element. + * Namespace declarations in parent tags as well as prefixes will be taken into account. + * @return The namespace for this element. + * @since 1.0 + */ + const std::string& xmlns() const; + + /** + * Returns the namespace for the given prefix, or the default namespace if + * @c prefix is empty. Namespace declarations in parent tags will be taken into account. + * Consider the following XML: + * @code + * <foo:bar xmlns:foo='foobar'/> + * @endcode + * <bar/> is in the @c foobar namespace, having a prefix of @b foo. A call to prefix() + * will return 'foo'. A call to xmlns( "foo" ) or xmlns( prefix() ) will return 'foobar'. + * A call to xmlns() will also return 'foobar' (it is a shortcut to + * xmlns( prefix() ). + * @param prefix The namespace prefix to look up, or an empty string to fetch the + * default namespace. + * @return The namespace for the given prefix, or the empty string if no such prefix exists. + * The default namespace if an empty prefix is given. + * @since 1.0 + */ + const std::string& xmlns( const std::string& prefix ) const; + + /** + * Use this function to add a new attribute to the tag. The Tag will become the owner of the + * Attribute and take care of deletion. If an Attribute with the same name already exists, + * it will be replaced by the new one. + * @param attr A pointer to the attribute to add. + * @return @b True if the input is valid UTF-8, @b false otherwise. Invalid + * input will be ignored. + * @since 1.0 + * @note Do not use this function to set XML namespaces, use setXmlns() instead. + */ + bool addAttribute( Attribute* attr ); + + /** + * Use this function to add a new attribute to the tag. + * @param name The name of the attribute. + * @param value The value of the attribute. + * @note Do not use this function to set XML namespaces, use setXmlns() instead. + * @return @b True if the input is valid UTF-8, @b false otherwise. Invalid + * input will be ignored. + */ + bool addAttribute( const std::string& name, const std::string& value ); + + /** + * Use this function to add a new attribute to the tag. The value is an @c int here. + * @param name The name of the attribute. + * @param value The value of the attribute. + * @note Do not use this function to set XML namespaces, use setXmlns() instead. + * @return @b True if the input is valid UTF-8, @b false otherwise. Invalid + * input will be ignored. + * @since 0.8 + */ + bool addAttribute( const std::string& name, int value ); + + /** + * Use this function to add a new attribute to the tag. The value is a @c long here. + * @param name The name of the attribute. + * @param value The value of the attribute. + * @return @b True if the input is valid UTF-8, @b false otherwise. Invalid + * input will be ignored. + * @note Do not use this function to set XML namespaces, use setXmlns() instead. + * @since 0.9 + */ + bool addAttribute( const std::string& name, long value ); + + /** + * Sets the given attributes. Any existing attributes are lost. + * @param attributes The attributes to set. + * @return @b True if the input is valid UTF-8, @b false otherwise. Invalid + * input will be ignored. + * @note Do not use this function to set XML namespaces, use setXmlns() instead. + * @since 0.9 + */ + void setAttributes( const AttributeList& attributes ); + + /** + * Use this function to add a child node to the tag. The Tag will be owned by Tag. + * @param child The node to be inserted. + */ + void addChild( Tag* child ); + + /** + * Use this function to add a copy of the given element to the tag. + * @param child The node to be inserted. + * @since 0.9 + */ + void addChildCopy( const Tag* child ); + + /** + * Sets the XML character data for this Tag. + * @param cdata The new cdata. + * @return @b True if the input is valid UTF-8, @b false otherwise. Invalid + * input will be ignored. + */ + bool setCData( const std::string& cdata ); + + /** + * Adds the string to the existing XML character data for this Tag. + * @param cdata The additional cdata. + * @return @b True if the input is valid UTF-8, @b false otherwise. Invalid + * input will be ignored. + */ + bool addCData( const std::string& cdata ); + + /** + * Use this function to retrieve the name of an element. + * @return The name of the tag. + */ + const std::string& name() const { return m_name; } + + /** + * Use this function to retrieve the XML character data of an element. + * @return The cdata the element contains. + */ + const std::string cdata() const; + + /** + * Use this function to fetch a const list of attributes. + * @return A constant reference to the list of attributes. + */ + const AttributeList& attributes() const; + + /** + * Use this function to fetch a const list of child elements. + * @return A constant reference to the list of child elements. + */ + const TagList& children() const; + + /** + * This function can be used to retrieve the value of a Tag's attribute. + * @param name The name of the attribute to look for. + * @return The value of the attribute if found, an empty string otherwise. + */ + const std::string& findAttribute( const std::string& name ) const; + + /** + * Checks whether the tag has a attribute with given name and optional value. + * @param name The name of the attribute to check for. + * @param value The value of the attribute to check for. + * @return Whether the attribute exists (optionally with the given value). + */ + bool hasAttribute( const std::string& name, const std::string& value = EmptyString ) const; + + /** + * This function finds and returns the @b first element within the child elements of the current tag + * that has a matching tag name. + * @param name The name of the element to search for. + * @return The found Tag, or 0. + */ + Tag* findChild( const std::string& name ) const; + + /** + * This function finds and returns the @b first element within the child elements of the current tag, + * that has a certain name, and a certain attribute with a certain value. + * @param name The name of the element to search for. + * @param attr The name of the attribute of the child element. + * @param value The value of the attribute of the child element. + * @return The found Tag, or 0. + */ + Tag* findChild( const std::string& name, const std::string& attr, + const std::string& value = EmptyString ) const; + + /** + * This function checks whether the Tag has a child element with a given name, and optionally + * this child element is checked for having a given attribute with an optional value. + * @param name The name of the child element. + * @param attr The name of the attribute of the child element. + * @param value The value of the attribute of the child element. + * @return @b True if the given child element exists, @b false otherwise. + */ + bool hasChild( const std::string& name, const std::string& attr = EmptyString, + const std::string& value = EmptyString ) const; + + /** + * This function checks whether the Tag has a child element which posesses a given attribute + * with an optional value. The name of the child element does not matter. + * @param attr The name of the attribute of the child element. + * @param value The value of the attribute of the child element. + * @return The child if found, 0 otherwise. + */ + Tag* findChildWithAttrib( const std::string& attr, const std::string& value = EmptyString ) const; + + /** + * This function checks whether the Tag has a child element which posesses a given attribute + * with an optional value. The name of the child element does not matter. + * @param attr The name of the attribute of the child element. + * @param value The value of the attribute of the child element. + * @return @b True if any such child element exists, @b false otherwise. + */ + inline bool hasChildWithAttrib( const std::string& attr, + const std::string& value = EmptyString ) const + { return findChildWithAttrib( attr, value ) ? true : false; } + + /** + * Returns a list of child tags of the current tag with the given name. + * @param name The name of the tags to look for. + * @param xmlns An optional namespace to check for. + * @return A list of tags with the given name. + * @note The tags are still linked to the current Tag and should not be deleted. + * @since 0.9 + */ + TagList findChildren( const std::string& name, const std::string& xmlns = EmptyString ) const; + + /** + * Removes and deletes all child tags that have the given name and are, optionally, + * within the given namespace. + * @param name The name of the tag(s) to remove from the list of child tags. + * @param xmlns An optional namespace to check for. + */ + void removeChild( const std::string& name, const std::string& xmlns = EmptyString ); + + /** + * Removes the given Tag from the list of child Tags. + * @param tag The Tag to remove from the list of child Tags. + * @note The Tag @p tag is not deleted. + */ + void removeChild( Tag* tag ); + + /** + * Removes the attribute with the given name and optional value from this Tag. + * @param attr The attribute's name. + * @param value The attribute's optional value. + * @param xmlns An optional namespace to check for. + */ + void removeAttribute( const std::string& attr, const std::string& value = EmptyString, + const std::string& xmlns = EmptyString ); + + /** + * This function checks whether a child element with given name exists and has + * XML character data that equals the given cdata string. + * @param name The name of the child element. + * @param cdata The character data that has to exist in the child element. + * @return @b True if a child element with given cdata exists, @b false otherwise. + */ + bool hasChildWithCData( const std::string& name, const std::string& cdata ) const; + + /** + * Returns the tag's parent Tag. + * @return The Tag above the current Tag. May be @b 0. + */ + Tag* parent() const { return m_parent; } + + /** + * This function creates a deep copy of this Tag. + * @return An independent copy of the Tag. + * @since 0.7 + */ + Tag* clone() const; + + /** + * Evaluates the given XPath expression and returns the result Tag's character data, if any. + * If more than one Tag match, only the first one's character data is returned. + * @note Currently, XPath support is somewhat limited. However, it should be useable + * for basic expressions. For now, see src/tests/xpath/xpath_test.cpp for supported + * expressions. + * @param expression An XPath expression to evaluate. + * @return A matched Tag's character data, or the empty string. + * @since 1.0 + */ + const std::string findCData( const std::string& expression ) const; + + /** + * Evaluates the given XPath expression and returns the result Tag. If more than one + * Tag match, only the first one is returned. + * @note Currently, XPath support is somewhat limited. However, it should be useable + * for basic expressions. For now, see src/tests/xpath/xpath_test.cpp for supported + * expressions. + * @param expression An XPath expression to evaluate. + * @return A matched Tag, or 0. + * @since 0.9 + */ + const Tag* findTag( const std::string& expression ) const; + + /** + * Evaluates the given XPath expression and returns the matched Tags. + * @note Currently, XPath support is somewhat limited. However, it should be useable + * for basic expressions. For now, see src/tests/xpath/xpath_test.cpp for supported + * expressions. + * @param expression An XPath expression to evaluate. + * @return A list of matched Tags, or an empty TagList. + * @since 0.9 + */ + ConstTagList findTagList( const std::string& expression ) const; + + /** + * Checks two Tags for equality. Order of attributes and child tags does matter. + * @param right The Tag to check against the current Tag. + * @since 0.9 + */ + bool operator==( const Tag &right ) const; + + /** + * Checks two Tags for inequality. Order of attributes and child tags does matter. + * @param right The Tag to check against the current Tag. + * @since 0.9 + */ + bool operator!=( const Tag &right ) const { return !( *this == right ); } + + /** + * Returns @b true if the Tag is valid, @b false otherwise. + */ + operator bool() const { return !m_name.empty(); } + + private: + /** + * Creates a new Tag by stealing the original Tag's body (elements, attributes). The + * original Tag is pretty much useless afterwards. + * @param tag The Tag to rip off. + */ + Tag( Tag* tag ); + + /** + * XPath error conditions. + */ + enum XPathError + { + XPNoError, /**< No error occured. */ + XPExpectedLeftOperand, /**< Operator expected a left-hand operand. */ + XPUnexpectedToken + }; + + enum NodeType + { + TypeTag, /**< The Node is a Tag. */ + TypeString /**< The Node is a std::string. */ + }; + + struct Node + { + Node( NodeType _type, Tag* _tag ) : type( _type ), tag( _tag ) {} + Node( NodeType _type, std::string* _str ) : type( _type ), str( _str ) {} + ~Node() {} + + NodeType type; + union + { + Tag* tag; + std::string* str; + }; + }; + + typedef std::list NodeList; + + Tag* m_parent; + TagList* m_children; + StringPList* m_cdata; + AttributeList* m_attribs; + NodeList* m_nodes; + std::string m_name; + std::string m_xmlns; + StringMap* m_xmlnss; + std::string m_prefix; + + enum TokenType + { + XTNone, + XTLeftParenthesis, + XTRightParenthesis, + XTNodeSet, + XTInteger, + XTElement, + XTLeftBracket, + XTRightBracket, + XTFunction, + XTAsterisk, + XTAttribute, + XTLiteralInside, + XTLiteral, + XTDot, + XTDoubleDot, + XTOperatorOr, + XTOperatorAnd, + XTOperatorEq, + XTOperatorNe, + XTOperatorGt, + XTOperatorLt, + XTOperatorLtEq, + XTOperatorGtEq, + XTOperatorPlus, + XTOperatorMinus, + XTOperatorMul, + XTOperatorDiv, + XTOperatorMod, + XTUnion, + XTSlash, + XTDoubleSlash + }; + + /** + * Sets a list of namespaces. + * @param xmlnss The list of namespaces. + * @since 1.0 + */ + void setXmlns( StringMap* xmlns ) + { delete m_xmlnss; m_xmlnss = xmlns; } + + Tag* parse( const std::string& expression, unsigned& len, TokenType border = XTNone ) const; + + void closePreviousToken( Tag**, Tag**, TokenType&, std::string& ) const; + void addToken( Tag **root, Tag **current, TokenType type, const std::string& token ) const; + void addOperator( Tag **root, Tag **current, Tag* arg, TokenType type, + const std::string& token ) const; + bool addPredicate( Tag **root, Tag **current, Tag* token ) const; + + TagList findChildren( const TagList& list, const std::string& name, + const std::string& xmlns = EmptyString ) const; + ConstTagList evaluateTagList( Tag* token ) const; + ConstTagList evaluateUnion( Tag* token ) const; + ConstTagList allDescendants() const; + + static TokenType getType( const std::string& c ); + + static bool isWhitespace( const char c ); + bool isNumber() const; + + bool evaluateBoolean( Tag* token ) const; + bool evaluatePredicate( Tag* token ) const { return evaluateBoolean( token ); } + bool evaluateEquals( Tag* token ) const; + + static void add( ConstTagList& one, const ConstTagList& two ); + }; + +} + +#endif // TAG_H__ diff --git a/libs/libgloox/taghandler.h b/libs/libgloox/taghandler.h new file mode 100644 index 0000000..9766851 --- /dev/null +++ b/libs/libgloox/taghandler.h @@ -0,0 +1,51 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef TAGHANDLER_H__ +#define TAGHANDLER_H__ + +#include "tag.h" + +namespace gloox +{ + + /** + * @brief A virtual interface which can be reimplemented to receive non-XMPP Core stanzas. + * + * Derived classes can be registered as TagHandlers with the ClientBase. + * A TagHandler can handle arbitrary elements not defined by RFC 3920, XMPP Core. + * + * It can also be used to handle Tags emitted by Parser. + * + * @author Jakob Schroeter + */ + class GLOOX_API TagHandler + { + public: + /** + * Virtual Destructor. + */ + virtual ~TagHandler() {} + + /** + * This function is called when a registered XML element arrives. + * As with every handler in gloox, the Tag is going to be deleted after this function returned. + * If you need a copy afterwards, create it using Tag::clone(). + * @param tag The complete Tag. + */ + virtual void handleTag( Tag* tag ) = 0; + }; + +} + +#endif // TAGHANDLER_H__ diff --git a/libs/libgloox/tlsbase.h b/libs/libgloox/tlsbase.h new file mode 100644 index 0000000..101c493 --- /dev/null +++ b/libs/libgloox/tlsbase.h @@ -0,0 +1,149 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef TLSBASE_H__ +#define TLSBASE_H__ + +#include "gloox.h" +#include "mutex.h" +#include "tlshandler.h" + +namespace gloox +{ + + /** + * @brief An abstract base class for TLS implementations. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API TLSBase + { + public: + /** + * Constructor. + * @param th The TLSHandler to handle TLS-related events. + * @param server The server to use in certificate verification. + */ + TLSBase( TLSHandler* th, const std::string server ) + : m_handler( th ), m_server( server ), m_secure( false ), m_valid( false ), m_initLib( true ) + {} + + /** + * Virtual destructor. + */ + virtual ~TLSBase() {} + + /** + * Initializes the TLS module. This function must be called (and execute successfully) + * before the module can be used. + * @param clientKey The absolute path to the user's private key in PEM format. + * @param clientCerts A path to a certificate bundle in PEM format. + * @param cacerts A list of absolute paths to CA root certificate files in PEM format. + * @return @b False if initialization failed, @b true otherwise. + * @since 1.0 + */ + virtual bool init( const std::string& clientKey = EmptyString, + const std::string& clientCerts = EmptyString, + const StringList& cacerts = StringList() ) = 0; + + /** + * Enables/disables initialization of the underlying TLS library. By default, + * initialization is performed. You may want to switch it off if the TLS library + * is used elsewhere in your applicationas well and you have no control over the + * initialization. + * @param init Whether or not to intialize the underlying TLS library. + */ + void setInitLib( bool init ) { m_initLib = init; } + + /** + * Use this function to feed unencrypted data to the encryption implementation. + * The encrypted result will be pushed to the TLSHandler's handleEncryptedData() function. + * @param data The data to encrypt. + * @return Whether or not the data was used successfully. + */ + virtual bool encrypt( const std::string& data ) = 0; + + /** + * Use this function to feed encrypted data or received handshake data to the + * encryption implementation. Handshake data will be eaten, unencrypted data + * will be pushed to the TLSHandler's handleDecryptedData() function. + * @param data The data to decrypt. + * @return The number of bytes used from the input. + */ + virtual int decrypt( const std::string& data ) = 0; + + /** + * This function performs internal cleanup and will be called after a failed handshake attempt. + */ + virtual void cleanup() = 0; + + /** + * This functiopn performs the TLS handshake. Handshake data from the server side should be + * fed in using decrypt(). Handshake data that is to be sent to the other side is pushed through + * TLSBase's handleEncryptedData(). + * @return @b True if the handshake was successful or if more input is needed, @b false if the + * handshake failed. + */ + virtual bool handshake() = 0; + + /** + * Returns the state of the encryption. + * @return The state of the encryption. + */ + virtual bool isSecure() const { return m_secure; } + + /** + * Use this function to set a number of trusted root CA certificates which shall be + * used to verify a servers certificate. + * @param cacerts A list of absolute paths to CA root certificate files in PEM format. + */ + virtual void setCACerts( const StringList& cacerts ) = 0; + + /** + * This function is used to retrieve certificate and connection info of a encrypted connection. + * @return Certificate information. + */ + virtual const CertInfo& fetchTLSInfo() const { return m_certInfo; } + + /** + * Use this function to set the user's certificate and private key. The certificate will + * be presented to the server upon request and can be used for SASL EXTERNAL authentication. + * The user's certificate file should be a bundle of more than one certificate in PEM format. + * The first one in the file should be the user's certificate, each cert following that one + * should have signed the previous one. + * @note These certificates are not necessarily the same as those used to verify the server's + * certificate. + * @param clientKey The absolute path to the user's private key in PEM format. + * @param clientCerts A path to a certificate bundle in PEM format. + */ + virtual void setClientCert( const std::string& clientKey, const std::string& clientCerts ) = 0; + + protected: + TLSHandler* m_handler; + StringList m_cacerts; + std::string m_clientKey; + std::string m_clientCerts; + std::string m_server; + CertInfo m_certInfo; + util::Mutex m_mutex; + bool m_secure; + bool m_valid; + bool m_initLib; + + }; + +} + +#endif // TLSBASE_H__ diff --git a/libs/libgloox/tlsdefault.cpp b/libs/libgloox/tlsdefault.cpp new file mode 100644 index 0000000..2c09640 --- /dev/null +++ b/libs/libgloox/tlsdefault.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2007-2009 by Jakob Schroeter + * This file is part of the gloox library. http://camaya.net/gloox + * + * This software is distributed under a license. The full license + * agreement can be found in the file LICENSE in this distribution. + * This software may not be copied, modified, sold or distributed + * other than expressed in the named license agreement. + * + * This software is distributed without any warranty. + */ + +#include "tlsdefault.h" + +#include "tlshandler.h" + +#include "config.h" + +#if defined( HAVE_GNUTLS ) +# define HAVE_TLS +# include "tlsgnutlsclient.h" +# include "tlsgnutlsclientanon.h" +# include "tlsgnutlsserveranon.h" +#elif defined( HAVE_OPENSSL ) +# define HAVE_TLS +# include "tlsopensslclient.h" +#ifndef __SYMBIAN32__ +# include "tlsopensslserver.h" +#endif +#elif defined( HAVE_WINTLS ) +# define HAVE_TLS +# include "tlsschannel.h" +#endif + +namespace gloox +{ + + TLSDefault::TLSDefault( TLSHandler* th, const std::string server, Type type ) + : TLSBase( th, server ), m_impl( 0 ) + { + switch( type ) + { + case VerifyingClient: +#ifdef HAVE_GNUTLS + m_impl = new GnuTLSClient( th, server ); +#elif defined( HAVE_OPENSSL ) + m_impl = new OpenSSLClient( th, server ); +#elif defined( HAVE_WINTLS ) + m_impl = new SChannel( th, server ); +#endif + break; + case AnonymousClient: +#ifdef HAVE_GNUTLS + m_impl = new GnuTLSClientAnon( th ); +#endif + break; + case AnonymousServer: +#ifdef HAVE_GNUTLS + m_impl = new GnuTLSServerAnon( th ); +#endif + break; + case VerifyingServer: +#ifdef HAVE_OPENSSL +#ifndef __SYMBIAN32__ + m_impl = new OpenSSLServer( th ); +#endif +#endif + break; + default: + break; + } + } + + TLSDefault::~TLSDefault() + { + delete m_impl; + } + + bool TLSDefault::init( const std::string& clientKey, + const std::string& clientCerts, + const StringList& cacerts ) + { + return m_impl ? m_impl->init( clientKey, clientCerts, + cacerts ) : false; + } + + int TLSDefault::types() + { + int types = 0; +#ifdef HAVE_GNUTLS + types |= VerifyingClient; + types |= AnonymousClient; + types |= AnonymousServer; +#elif defined( HAVE_OPENSSL ) + types |= VerifyingClient; + types |= VerifyingServer; +#elif defined( HAVE_WINTLS ) + types |= VerifyingClient; +#endif + return types; + } + + bool TLSDefault::encrypt( const std::string& data ) + { + return m_impl ? m_impl->encrypt( data ) : false; + } + + int TLSDefault::decrypt( const std::string& data ) + { + return m_impl ? m_impl->decrypt( data ) : 0; + } + + void TLSDefault::cleanup() + { + if( m_impl ) + m_impl->cleanup(); + } + + bool TLSDefault::handshake() + { + return m_impl ? m_impl->handshake() : false; + } + + bool TLSDefault::isSecure() const + { + return m_impl ? m_impl->isSecure() : false; + } + + void TLSDefault::setCACerts( const StringList& cacerts ) + { + if( m_impl ) + m_impl->setCACerts( cacerts ); + } + + const CertInfo& TLSDefault::fetchTLSInfo() const + { + return m_impl ? m_impl->fetchTLSInfo() : m_certInfo; + } + + void TLSDefault::setClientCert( const std::string& clientKey, const std::string& clientCerts ) + { + if( m_impl ) + m_impl->setClientCert( clientKey, clientCerts ); + } + +} diff --git a/libs/libgloox/tlsdefault.h b/libs/libgloox/tlsdefault.h new file mode 100644 index 0000000..cee4940 --- /dev/null +++ b/libs/libgloox/tlsdefault.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2007-2009 by Jakob Schroeter + * This file is part of the gloox library. http://camaya.net/gloox + * + * This software is distributed under a license. The full license + * agreement can be found in the file LICENSE in this distribution. + * This software may not be copied, modified, sold or distributed + * other than expressed in the named license agreement. + * + * This software is distributed without any warranty. + */ + + +#ifndef TLSDEFAULT_H__ +#define TLSDEFAULT_H__ + +#include "tlsbase.h" + +namespace gloox +{ + + class TLSHandler; + + /** + * @brief This is an abstraction of the various TLS backends. + * + * You should use an instance of this class should you whish to use TLS encryption. + * TLS support for the main XMPP connection is managed by Client/ClientBase directly. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API TLSDefault : public TLSBase + { + public: + + /** + * Supported TLS types. + */ + enum Type + { + VerifyingClient = 1, /**< TLS client, verifying, available for all + * TLS implementations. */ + AnonymousClient = 2, /**< Anonymous TLS client (non-verifying), available with + * GnuTLS. */ + VerifyingServer = 4, /**< TLS server, verifying, currently not available. */ + AnonymousServer = 8 /**< Anonymous TLS server (non-verifying), available with + * GnuTLS. */ + }; + + /** + * Constructs a new TLS wrapper. + * @param th The TLSHandler to handle TLS-related events. + * @param server The server to use in certificate verification. + * @param type What you want to use this TLS object for. + */ + TLSDefault( TLSHandler* th, const std::string server, Type type = VerifyingClient ); + + /** + * Virtual Destructor. + */ + virtual ~TLSDefault(); + + // reimplemented from TLSBase + virtual bool init( const std::string& clientKey = EmptyString, + const std::string& clientCerts = EmptyString, + const StringList& cacerts = StringList() ); + + // reimplemented from TLSBase + virtual bool encrypt( const std::string& data ); + + // reimplemented from TLSBase + virtual int decrypt( const std::string& data ); + + // reimplemented from TLSBase + virtual void cleanup(); + + // reimplemented from TLSBase + virtual bool handshake(); + + // reimplemented from TLSBase + virtual bool isSecure() const; + + // reimplemented from TLSBase + virtual void setCACerts( const StringList& cacerts ); + + // reimplemented from TLSBase + virtual const CertInfo& fetchTLSInfo() const; + + // reimplemented from TLSBase + virtual void setClientCert( const std::string& clientKey, const std::string& clientCerts ); + + /** + * Returns an ORed list of supported TLS types. + * @return ORed TLSDefault::type members. + */ + static int types(); + + private: + TLSBase* m_impl; + + }; +} + +#endif // TLSDEFAULT_H__ diff --git a/libs/libgloox/tlsgnutlsbase.cpp b/libs/libgloox/tlsgnutlsbase.cpp new file mode 100644 index 0000000..d98c802 --- /dev/null +++ b/libs/libgloox/tlsgnutlsbase.cpp @@ -0,0 +1,178 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "tlsgnutlsbase.h" + +#ifdef HAVE_GNUTLS + +#include +#include +#include + +namespace gloox +{ + + GnuTLSBase::GnuTLSBase( TLSHandler* th, const std::string& server ) + : TLSBase( th, server ), m_session( new gnutls_session_t ), m_buf( 0 ), m_bufsize( 17000 ) + { + m_buf = (char*)calloc( m_bufsize + 1, sizeof( char ) ); + } + + GnuTLSBase::~GnuTLSBase() + { + free( m_buf ); + m_buf = 0; + cleanup(); + delete m_session; +// FIXME: It segfaults if more then one account uses +// encryption at same time, so we comment it for now. +// Do we need to deinit at all? +// gnutls_global_deinit(); + } + + bool GnuTLSBase::encrypt( const std::string& data ) + { + if( !m_secure ) + { + handshake(); + return true; + } + + ssize_t ret = 0; + std::string::size_type sum = 0; + do + { + ret = gnutls_record_send( *m_session, data.c_str() + sum, data.length() - sum ); + sum += ret; + } + while( ( ret == GNUTLS_E_AGAIN ) || ( ret == GNUTLS_E_INTERRUPTED ) || sum < data.length() ); + return true; + } + + int GnuTLSBase::decrypt( const std::string& data ) + { + m_recvBuffer += data; + + if( !m_secure ) + { + handshake(); + return static_cast( data.length() ); + } + + int sum = 0; + int ret = 0; + do + { + ret = static_cast( gnutls_record_recv( *m_session, m_buf, m_bufsize ) ); + + if( ret > 0 && m_handler ) + { + m_handler->handleDecryptedData( this, std::string( m_buf, ret ) ); + sum += ret; + } + } + while( ret > 0 ); + + return sum; + } + + void GnuTLSBase::cleanup() + { + if( !m_mutex.trylock() ) + return; + + TLSHandler* handler = m_handler; + m_handler = 0; + gnutls_bye( *m_session, GNUTLS_SHUT_RDWR ); + gnutls_db_remove_session( *m_session ); + gnutls_credentials_clear( *m_session ); + if( m_secure ) + gnutls_deinit( *m_session ); + + m_secure = false; + m_valid = false; + delete m_session; + m_session = 0; + m_session = new gnutls_session_t; + m_handler = handler; + + m_mutex.unlock(); + } + + bool GnuTLSBase::handshake() + { + if( !m_handler ) + return false; + + int ret = gnutls_handshake( *m_session ); + if( ret < 0 && gnutls_error_is_fatal( ret ) ) + { + gnutls_perror( ret ); + gnutls_db_remove_session( *m_session ); + gnutls_deinit( *m_session ); + m_valid = false; + + m_handler->handleHandshakeResult( this, false, m_certInfo ); + return false; + } + else if( ret == GNUTLS_E_AGAIN ) + { + return true; + } + + m_secure = true; + + getCertInfo(); + + m_handler->handleHandshakeResult( this, true, m_certInfo ); + return true; + } + + ssize_t GnuTLSBase::pullFunc( void* data, size_t len ) + { + ssize_t cpy = ( len > m_recvBuffer.length() ) ? ( m_recvBuffer.length() ) : ( len ); + if( cpy > 0 ) + { + memcpy( data, (const void*)m_recvBuffer.c_str(), cpy ); + m_recvBuffer.erase( 0, cpy ); + return cpy; + } + else + { + errno = EAGAIN; + return GNUTLS_E_AGAIN; + } + } + + ssize_t GnuTLSBase::pullFunc( gnutls_transport_ptr_t ptr, void* data, size_t len ) + { + return static_cast( ptr )->pullFunc( data, len ); + } + + ssize_t GnuTLSBase::pushFunc( const void* data, size_t len ) + { + if( m_handler ) + m_handler->handleEncryptedData( this, std::string( (const char*)data, len ) ); + + return len; + } + + ssize_t GnuTLSBase::pushFunc( gnutls_transport_ptr_t ptr, const void* data, size_t len ) + { + return static_cast( ptr )->pushFunc( data, len ); + } + +} + +#endif // HAVE_GNUTLS diff --git a/libs/libgloox/tlsgnutlsbase.h b/libs/libgloox/tlsgnutlsbase.h new file mode 100644 index 0000000..79ef968 --- /dev/null +++ b/libs/libgloox/tlsgnutlsbase.h @@ -0,0 +1,92 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef TLSGNUTLSBASE_H__ +#define TLSGNUTLSBASE_H__ + +#include "tlsbase.h" + +#include "config.h" + +#ifdef HAVE_GNUTLS + +#include +#include + +namespace gloox +{ + + /** + * @brief This is the common base class for (stream) encryption using GnuTLS. + * + * You should not need to use this class directly. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GnuTLSBase : public TLSBase + { + public: + /** + * Constructor. + * @param th The TLSHandler to handle TLS-related events. + * @param server The server to use in certificate verification. + */ + GnuTLSBase( TLSHandler* th, const std::string& server = EmptyString ); + + /** + * Virtual destructor. + */ + virtual ~GnuTLSBase(); + + // reimplemented from TLSBase + virtual bool encrypt( const std::string& data ); + + // reimplemented from TLSBase + virtual int decrypt( const std::string& data ); + + // reimplemented from TLSBase + virtual void cleanup(); + + // reimplemented from TLSBase + virtual bool handshake(); + + // reimplemented from TLSBase + virtual void setCACerts( const StringList& /*cacerts*/ ) {} + + // reimplemented from TLSBase + virtual void setClientCert( const std::string& /*clientKey*/, const std::string& /*clientCerts*/ ) {} + + protected: + virtual void getCertInfo() {} + + gnutls_session_t* m_session; + + std::string m_recvBuffer; + char* m_buf; + const int m_bufsize; + + ssize_t pullFunc( void* data, size_t len ); + static ssize_t pullFunc( gnutls_transport_ptr_t ptr, void* data, size_t len ); + + ssize_t pushFunc( const void* data, size_t len ); + static ssize_t pushFunc( gnutls_transport_ptr_t ptr, const void* data, size_t len ); + + }; + +} + +#endif // HAVE_GNUTLS + +#endif // TLSGNUTLSBASE_H__ diff --git a/libs/libgloox/tlsgnutlsclient.cpp b/libs/libgloox/tlsgnutlsclient.cpp new file mode 100644 index 0000000..c1d24c2 --- /dev/null +++ b/libs/libgloox/tlsgnutlsclient.cpp @@ -0,0 +1,223 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "tlsgnutlsclient.h" + +#ifdef HAVE_GNUTLS + +#include + +namespace gloox +{ + + GnuTLSClient::GnuTLSClient( TLSHandler* th, const std::string& server ) + : GnuTLSBase( th, server ) + { + } + + GnuTLSClient::~GnuTLSClient() + { + } + + void GnuTLSClient::cleanup() + { + GnuTLSBase::cleanup(); + init(); + } + + bool GnuTLSClient::init( const std::string& clientKey, + const std::string& clientCerts, + const StringList& cacerts ) + { + const int protocolPriority[] = { +#ifdef GNUTLS_TLS1_2 + GNUTLS_TLS1_2, +#endif + GNUTLS_TLS1_1, GNUTLS_TLS1, 0 }; + const int kxPriority[] = { GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, GNUTLS_KX_DHE_DSS, 0 }; + const int cipherPriority[] = { GNUTLS_CIPHER_AES_256_CBC, GNUTLS_CIPHER_AES_128_CBC, + GNUTLS_CIPHER_3DES_CBC, GNUTLS_CIPHER_ARCFOUR, 0 }; + const int compPriority[] = { GNUTLS_COMP_ZLIB, GNUTLS_COMP_NULL, 0 }; + const int macPriority[] = { GNUTLS_MAC_SHA, GNUTLS_MAC_MD5, 0 }; + + if( m_initLib && gnutls_global_init() != 0 ) + return false; + + if( gnutls_certificate_allocate_credentials( &m_credentials ) < 0 ) + return false; + + if( gnutls_init( m_session, GNUTLS_CLIENT ) != 0 ) + { + gnutls_certificate_free_credentials( m_credentials ); + return false; + } + + gnutls_protocol_set_priority( *m_session, protocolPriority ); + gnutls_cipher_set_priority( *m_session, cipherPriority ); + gnutls_compression_set_priority( *m_session, compPriority ); + gnutls_kx_set_priority( *m_session, kxPriority ); + gnutls_mac_set_priority( *m_session, macPriority ); + gnutls_credentials_set( *m_session, GNUTLS_CRD_CERTIFICATE, m_credentials ); + + gnutls_transport_set_ptr( *m_session, (gnutls_transport_ptr_t)this ); + gnutls_transport_set_push_function( *m_session, pushFunc ); + gnutls_transport_set_pull_function( *m_session, pullFunc ); + + m_valid = true; + return true; + } + + void GnuTLSClient::setCACerts( const StringList& cacerts ) + { + m_cacerts = cacerts; + + StringList::const_iterator it = m_cacerts.begin(); + for( ; it != m_cacerts.end(); ++it ) + gnutls_certificate_set_x509_trust_file( m_credentials, (*it).c_str(), GNUTLS_X509_FMT_PEM ); + } + + void GnuTLSClient::setClientCert( const std::string& clientKey, const std::string& clientCerts ) + { + m_clientKey = clientKey; + m_clientCerts = clientCerts; + + if( !m_clientKey.empty() && !m_clientCerts.empty() ) + { + gnutls_certificate_set_x509_key_file( m_credentials, m_clientCerts.c_str(), + m_clientKey.c_str(), GNUTLS_X509_FMT_PEM ); + } + } + + void GnuTLSClient::getCertInfo() + { + unsigned int status; + bool error = false; + + gnutls_certificate_free_ca_names( m_credentials ); + + if( gnutls_certificate_verify_peers2( *m_session, &status ) < 0 ) + error = true; + + m_certInfo.status = 0; + if( status & GNUTLS_CERT_INVALID ) + m_certInfo.status |= CertInvalid; + if( status & GNUTLS_CERT_SIGNER_NOT_FOUND ) + m_certInfo.status |= CertSignerUnknown; + if( status & GNUTLS_CERT_REVOKED ) + m_certInfo.status |= CertRevoked; + if( status & GNUTLS_CERT_SIGNER_NOT_CA ) + m_certInfo.status |= CertSignerNotCa; + const gnutls_datum_t* certList = 0; + unsigned int certListSize; + if( !error && ( ( certList = gnutls_certificate_get_peers( *m_session, &certListSize ) ) == 0 ) ) + error = true; + + gnutls_x509_crt_t* cert = new gnutls_x509_crt_t[certListSize+1]; + for( unsigned int i=0; !error && ( i 0 ) + && certListSize > 0 ) + certListSize--; + + bool chain = true; + for( unsigned int i=1; !error && ( i time( 0 ) ) + m_certInfo.status |= CertNotActive; + m_certInfo.date_from = t; + + t = (int)gnutls_x509_crt_get_expiration_time( cert[0] ); + if( t == -1 ) + error = true; + else if( t < time( 0 ) ) + m_certInfo.status |= CertExpired; + m_certInfo.date_to = t; + + char name[64]; + size_t nameSize = sizeof( name ); + gnutls_x509_crt_get_issuer_dn( cert[0], name, &nameSize ); + m_certInfo.issuer = name; + + nameSize = sizeof( name ); + gnutls_x509_crt_get_dn( cert[0], name, &nameSize ); + m_certInfo.server = name; + + const char* info; + info = gnutls_compression_get_name( gnutls_compression_get( *m_session ) ); + if( info ) + m_certInfo.compression = info; + + info = gnutls_mac_get_name( gnutls_mac_get( *m_session ) ); + if( info ) + m_certInfo.mac = info; + + info = gnutls_cipher_get_name( gnutls_cipher_get( *m_session ) ); + if( info ) + m_certInfo.cipher = info; + + info = gnutls_protocol_get_name( gnutls_protocol_get_version( *m_session ) ); + if( info ) + m_certInfo.protocol = info; + + if( !gnutls_x509_crt_check_hostname( cert[0], m_server.c_str() ) ) + m_certInfo.status |= CertWrongPeer; + + for( unsigned int i = 0; i < certListSize; ++i ) + gnutls_x509_crt_deinit( cert[i] ); + + delete[] cert; + + m_valid = true; + } + + static bool verifyCert( gnutls_x509_crt_t cert, unsigned result ) + { + return ! ( ( result & GNUTLS_CERT_INVALID ) + || gnutls_x509_crt_get_expiration_time( cert ) < time( 0 ) + || gnutls_x509_crt_get_activation_time( cert ) > time( 0 ) ); + } + + bool GnuTLSClient::verifyAgainst( gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer ) + { + unsigned int result; + gnutls_x509_crt_verify( cert, &issuer, 1, 0, &result ); + return verifyCert( cert, result ); + } + + bool GnuTLSClient::verifyAgainstCAs( gnutls_x509_crt_t cert, gnutls_x509_crt_t* CAList, int CAListSize ) + { + unsigned int result; + gnutls_x509_crt_verify( cert, CAList, CAListSize, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT, &result ); + return verifyCert( cert, result ); + } + +} + +#endif // HAVE_GNUTLS diff --git a/libs/libgloox/tlsgnutlsclient.h b/libs/libgloox/tlsgnutlsclient.h new file mode 100644 index 0000000..0d60e74 --- /dev/null +++ b/libs/libgloox/tlsgnutlsclient.h @@ -0,0 +1,81 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef TLSGNUTLSCLIENT_H__ +#define TLSGNUTLSCLIENT_H__ + +#include "tlsgnutlsbase.h" + +#include "config.h" + +#ifdef HAVE_GNUTLS + +#include +#include + +namespace gloox +{ + + /** + * @brief This class implements a TLS backend using GnuTLS. + * + * You should not need to use this class directly. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GnuTLSClient : public GnuTLSBase + { + public: + /** + * Constructor. + * @param th The TLSHandler to handle TLS-related events. + * @param server The server to use in certificate verification. + */ + GnuTLSClient( TLSHandler* th, const std::string& server ); + + /** + * Virtual destructor. + */ + virtual ~GnuTLSClient(); + + // reimplemented from TLSBase + virtual bool init( const std::string& clientKey = EmptyString, + const std::string& clientCerts = EmptyString, + const StringList& cacerts = StringList() ); + + // reimplemented from TLSBase + virtual void setCACerts( const StringList& cacerts ); + + // reimplemented from TLSBase + virtual void setClientCert( const std::string& clientKey, const std::string& clientCerts ); + + // reimplemented from TLSBase + virtual void cleanup(); + + private: + virtual void getCertInfo(); + + bool verifyAgainst( gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer ); + bool verifyAgainstCAs( gnutls_x509_crt_t cert, gnutls_x509_crt_t *CAList, int CAListSize ); + + gnutls_certificate_credentials m_credentials; + + }; + +} + +#endif // HAVE_GNUTLS + +#endif // TLSGNUTLSCLIENT_H__ diff --git a/libs/libgloox/tlsgnutlsclientanon.cpp b/libs/libgloox/tlsgnutlsclientanon.cpp new file mode 100644 index 0000000..d46464f --- /dev/null +++ b/libs/libgloox/tlsgnutlsclientanon.cpp @@ -0,0 +1,101 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "tlsgnutlsclientanon.h" + +#ifdef HAVE_GNUTLS + +#include + +namespace gloox +{ + + GnuTLSClientAnon::GnuTLSClientAnon( TLSHandler* th ) + : GnuTLSBase( th ) + { + } + + GnuTLSClientAnon::~GnuTLSClientAnon() + { + gnutls_anon_free_client_credentials( m_anoncred ); + } + + void GnuTLSClientAnon::cleanup() + { + GnuTLSBase::cleanup(); + init(); + } + + bool GnuTLSClientAnon::init( const std::string&, + const std::string&, + const StringList& ) + { + const int protocolPriority[] = { GNUTLS_TLS1, 0 }; + const int kxPriority[] = { GNUTLS_KX_ANON_DH, 0 }; + const int cipherPriority[] = { GNUTLS_CIPHER_AES_256_CBC, GNUTLS_CIPHER_AES_128_CBC, + GNUTLS_CIPHER_3DES_CBC, GNUTLS_CIPHER_ARCFOUR, 0 }; + const int compPriority[] = { GNUTLS_COMP_ZLIB, GNUTLS_COMP_NULL, 0 }; + const int macPriority[] = { GNUTLS_MAC_SHA, GNUTLS_MAC_MD5, 0 }; + + if( m_initLib && gnutls_global_init() != 0 ) + return false; + + if( gnutls_anon_allocate_client_credentials( &m_anoncred ) < 0 ) + return false; + + if( gnutls_init( m_session, GNUTLS_CLIENT ) != 0 ) + return false; + + gnutls_protocol_set_priority( *m_session, protocolPriority ); + gnutls_cipher_set_priority( *m_session, cipherPriority ); + gnutls_compression_set_priority( *m_session, compPriority ); + gnutls_kx_set_priority( *m_session, kxPriority ); + gnutls_mac_set_priority( *m_session, macPriority ); + gnutls_credentials_set( *m_session, GNUTLS_CRD_ANON, m_anoncred ); + + gnutls_transport_set_ptr( *m_session, (gnutls_transport_ptr_t)this ); + gnutls_transport_set_push_function( *m_session, pushFunc ); + gnutls_transport_set_pull_function( *m_session, pullFunc ); + + m_valid = true; + return true; + } + + void GnuTLSClientAnon::getCertInfo() + { + m_certInfo.status = CertOk; + + const char* info; + info = gnutls_compression_get_name( gnutls_compression_get( *m_session ) ); + if( info ) + m_certInfo.compression = info; + + info = gnutls_mac_get_name( gnutls_mac_get( *m_session ) ); + if( info ) + m_certInfo.mac = info; + + info = gnutls_cipher_get_name( gnutls_cipher_get( *m_session ) ); + if( info ) + m_certInfo.cipher = info; + + info = gnutls_protocol_get_name( gnutls_protocol_get_version( *m_session ) ); + if( info ) + m_certInfo.protocol = info; + + m_valid = true; + } + +} + +#endif // HAVE_GNUTLS diff --git a/libs/libgloox/tlsgnutlsclientanon.h b/libs/libgloox/tlsgnutlsclientanon.h new file mode 100644 index 0000000..3faec75 --- /dev/null +++ b/libs/libgloox/tlsgnutlsclientanon.h @@ -0,0 +1,70 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef TLSGNUTLSCLIENTANON_H__ +#define TLSGNUTLSCLIENTANON_H__ + +#include "tlsgnutlsbase.h" + +#include "config.h" + +#ifdef HAVE_GNUTLS + +#include +#include + +namespace gloox +{ + + /** + * @brief This class implements an anonymous TLS backend using GnuTLS. + * + * You should not need to use this class directly. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GnuTLSClientAnon : public GnuTLSBase + { + public: + /** + * Constructor. + * @param th The TLSHandler to handle TLS-related events. + */ + GnuTLSClientAnon( TLSHandler* th ); + + /** + * Virtual destructor. + */ + virtual ~GnuTLSClientAnon(); + + // reimplemented from TLSBase + virtual bool init( const std::string& clientKey = EmptyString, + const std::string& clientCerts = EmptyString, + const StringList& cacerts = StringList() ); + + // reimplemented from TLSBase + virtual void cleanup(); + + private: + virtual void getCertInfo(); + + gnutls_anon_client_credentials_t m_anoncred; + }; + +} + +#endif // HAVE_GNUTLS + +#endif // TLSGNUTLSCLIENTANON_H__ diff --git a/libs/libgloox/tlsgnutlsserveranon.cpp b/libs/libgloox/tlsgnutlsserveranon.cpp new file mode 100644 index 0000000..f851fdd --- /dev/null +++ b/libs/libgloox/tlsgnutlsserveranon.cpp @@ -0,0 +1,113 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "tlsgnutlsserveranon.h" + +#ifdef HAVE_GNUTLS + +#include + +namespace gloox +{ + + GnuTLSServerAnon::GnuTLSServerAnon( TLSHandler* th ) + : GnuTLSBase( th ), m_dhBits( 1024 ) + { + } + + GnuTLSServerAnon::~GnuTLSServerAnon() + { + gnutls_anon_free_server_credentials( m_anoncred ); + gnutls_dh_params_deinit( m_dhParams ); + } + + void GnuTLSServerAnon::cleanup() + { + GnuTLSBase::cleanup(); + init(); + } + + bool GnuTLSServerAnon::init( const std::string&, + const std::string&, + const StringList& ) + { + const int protocolPriority[] = { GNUTLS_TLS1, 0 }; + const int kxPriority[] = { GNUTLS_KX_ANON_DH, 0 }; + const int cipherPriority[] = { GNUTLS_CIPHER_AES_256_CBC, GNUTLS_CIPHER_AES_128_CBC, + GNUTLS_CIPHER_3DES_CBC, GNUTLS_CIPHER_ARCFOUR, 0 }; + const int compPriority[] = { GNUTLS_COMP_ZLIB, GNUTLS_COMP_NULL, 0 }; + const int macPriority[] = { GNUTLS_MAC_SHA, GNUTLS_MAC_MD5, 0 }; + + if( m_initLib && gnutls_global_init() != 0 ) + return false; + + if( gnutls_anon_allocate_server_credentials( &m_anoncred ) < 0 ) + return false; + + generateDH(); + gnutls_anon_set_server_dh_params( m_anoncred, m_dhParams ); + + if( gnutls_init( m_session, GNUTLS_SERVER ) != 0 ) + return false; + + gnutls_protocol_set_priority( *m_session, protocolPriority ); + gnutls_cipher_set_priority( *m_session, cipherPriority ); + gnutls_compression_set_priority( *m_session, compPriority ); + gnutls_kx_set_priority( *m_session, kxPriority ); + gnutls_mac_set_priority( *m_session, macPriority ); + gnutls_credentials_set( *m_session, GNUTLS_CRD_ANON, m_anoncred ); + + gnutls_dh_set_prime_bits( *m_session, m_dhBits ); + + gnutls_transport_set_ptr( *m_session, (gnutls_transport_ptr_t)this ); + gnutls_transport_set_push_function( *m_session, pushFunc ); + gnutls_transport_set_pull_function( *m_session, pullFunc ); + + m_valid = true; + return true; + } + + void GnuTLSServerAnon::generateDH() + { + gnutls_dh_params_init( &m_dhParams ); + gnutls_dh_params_generate2( m_dhParams, m_dhBits ); + } + + void GnuTLSServerAnon::getCertInfo() + { + m_certInfo.status = CertOk; + + const char* info; + info = gnutls_compression_get_name( gnutls_compression_get( *m_session ) ); + if( info ) + m_certInfo.compression = info; + + info = gnutls_mac_get_name( gnutls_mac_get( *m_session ) ); + if( info ) + m_certInfo.mac = info; + + info = gnutls_cipher_get_name( gnutls_cipher_get( *m_session ) ); + if( info ) + m_certInfo.cipher = info; + + info = gnutls_protocol_get_name( gnutls_protocol_get_version( *m_session ) ); + if( info ) + m_certInfo.protocol = info; + + m_valid = true; + } + +} + +#endif // HAVE_GNUTLS diff --git a/libs/libgloox/tlsgnutlsserveranon.h b/libs/libgloox/tlsgnutlsserveranon.h new file mode 100644 index 0000000..f66ae28 --- /dev/null +++ b/libs/libgloox/tlsgnutlsserveranon.h @@ -0,0 +1,75 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef TLSGNUTLSSERVERANON_H__ +#define TLSGNUTLSSERVERANON_H__ + +#include "tlsgnutlsbase.h" + +#include "config.h" + +#ifdef HAVE_GNUTLS + +#include +#include + +namespace gloox +{ + + /** + * @brief This class implements (stream) encryption using GnuTLS server-side. + * + * You should not need to use this class directly. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GnuTLSServerAnon : public GnuTLSBase + { + public: + /** + * Constructor. + * @param th The TLSHandler to handle TLS-related events. + */ + GnuTLSServerAnon( TLSHandler* th ); + + /** + * Virtual destructor. + */ + virtual ~GnuTLSServerAnon(); + + // reimplemented from TLSBase + virtual bool init( const std::string& clientKey = EmptyString, + const std::string& clientCerts = EmptyString, + const StringList& cacerts = StringList() ); + + // reimplemented from TLSBase + virtual void cleanup(); + + private: + virtual void getCertInfo(); + void generateDH(); + + gnutls_anon_server_credentials_t m_anoncred; + gnutls_dh_params_t m_dhParams; + + const int m_dhBits; + + }; + +} + +#endif // HAVE_GNUTLS + +#endif // TLSGNUTLSSERVERANON_H__ diff --git a/libs/libgloox/tlshandler.h b/libs/libgloox/tlshandler.h new file mode 100644 index 0000000..aa42fe7 --- /dev/null +++ b/libs/libgloox/tlshandler.h @@ -0,0 +1,68 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef TLSHANDLER_H__ +#define TLSHANDLER_H__ + +#include "macros.h" + +#include + +namespace gloox +{ + + struct CertInfo; + class TLSBase; + + /** + * @brief An interface that allows for interacting with TLS implementations derived from TLSBase. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API TLSHandler + { + public: + /** + * Virtual Destructor. + */ + virtual ~TLSHandler() {} + + /** + * Reimplement this function to receive encrypted data from a TLSBase implementation. + * @param base The encryption implementation which called this function. + * @param data The encrypted data (e.g. to send over the wire). + */ + virtual void handleEncryptedData( const TLSBase* base, const std::string& data ) = 0; + + /** + * Reimplement this function to receive decrypted data from a TLSBase implementation. + * @param base The encryption implementation which called this function. + * @param data The decrypted data (e.g. to parse). + */ + virtual void handleDecryptedData( const TLSBase* base, const std::string& data ) = 0; + + /** + * Reimplement this function to receive the result of a TLS handshake. + * @param base The encryption implementation which called this function. + * @param success Whether or not the handshake was successful. + * @param certinfo Information about the server's certificate. + */ + virtual void handleHandshakeResult( const TLSBase* base, bool success, CertInfo &certinfo ) = 0; + + }; + +} + +#endif // TLSHANDLER_H__ diff --git a/libs/libgloox/tlsopensslbase.cpp b/libs/libgloox/tlsopensslbase.cpp new file mode 100644 index 0000000..2936bad --- /dev/null +++ b/libs/libgloox/tlsopensslbase.cpp @@ -0,0 +1,333 @@ +/* + Copyright (c) 2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "tlsopensslbase.h" + +#ifdef HAVE_OPENSSL + +#include +#include +#include +#include + +#include + +namespace gloox +{ + + OpenSSLBase::OpenSSLBase( TLSHandler* th, const std::string& server ) + : TLSBase( th, server ), m_ssl( 0 ), m_ctx( 0 ), m_buf( 0 ), m_bufsize( 17000 ) + { + m_buf = (char*)calloc( m_bufsize + 1, sizeof( char ) ); + } + + OpenSSLBase::~OpenSSLBase() + { + m_handler = 0; + free( m_buf ); + SSL_CTX_free( m_ctx ); + SSL_shutdown( m_ssl ); + SSL_free( m_ssl ); + BIO_free( m_nbio ); + cleanup(); + } + + bool OpenSSLBase::init( const std::string& clientKey, + const std::string& clientCerts, + const StringList& cacerts ) + { + if( m_initLib ) + SSL_library_init(); + + SSL_COMP_add_compression_method( 193, COMP_zlib() ); + + OpenSSL_add_all_algorithms(); + + if( !setType() ) //inits m_ctx + return false; + + setClientCert( clientKey, clientCerts ); + setCACerts( cacerts ); + + if( !SSL_CTX_set_cipher_list( m_ctx, "HIGH:MEDIUM:AES:@STRENGTH" ) ) + return false; + + m_ssl = SSL_new( m_ctx ); + if( !m_ssl ) + return false; + + if( !BIO_new_bio_pair( &m_ibio, 0, &m_nbio, 0 ) ) + return false; + + SSL_set_bio( m_ssl, m_ibio, m_ibio ); + SSL_set_mode( m_ssl, SSL_MODE_AUTO_RETRY | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE ); + + ERR_load_crypto_strings(); + SSL_load_error_strings(); + + if( !privateInit() ) + return false; + + m_valid = true; + return true; + } + + bool OpenSSLBase::encrypt( const std::string& data ) + { + m_sendBuffer += data; + + if( !m_secure ) + { + handshake(); + return 0; + } + + doTLSOperation( TLSWrite ); + return true; + } + + int OpenSSLBase::decrypt( const std::string& data ) + { + m_recvBuffer += data; + + if( !m_secure ) + { + handshake(); + return 0; + } + + doTLSOperation( TLSRead ); + return true; + } + + void OpenSSLBase::setCACerts( const StringList& cacerts ) + { + m_cacerts = cacerts; + + StringList::const_iterator it = m_cacerts.begin(); + for( ; it != m_cacerts.end(); ++it ) + SSL_CTX_load_verify_locations( m_ctx, (*it).c_str(), 0 ); + } + + void OpenSSLBase::setClientCert( const std::string& clientKey, const std::string& clientCerts ) + { + m_clientKey = clientKey; + m_clientCerts = clientCerts; + + if( !m_clientKey.empty() && !m_clientCerts.empty() ) + { + if( SSL_CTX_use_certificate_chain_file( m_ctx, m_clientCerts.c_str() ) != 1 ) + { + // FIXME + } + if( SSL_CTX_use_RSAPrivateKey_file( m_ctx, m_clientKey.c_str(), SSL_FILETYPE_PEM ) != 1 ) + { + // FIXME + } + } + + if ( SSL_CTX_check_private_key( m_ctx ) != 1 ) + { + // FIXME + } + } + + void OpenSSLBase::cleanup() + { + if( !m_mutex.trylock() ) + return; + + m_secure = false; + m_valid = false; + + m_mutex.unlock(); + } + + void OpenSSLBase::doTLSOperation( TLSOperation op ) + { + if( !m_handler ) + return; + + int ret = 0; + bool onceAgain = false; + + do + { + switch( op ) + { + case TLSHandshake: + ret = handshakeFunction(); + break; + case TLSWrite: + ret = SSL_write( m_ssl, m_sendBuffer.c_str(), m_sendBuffer.length() ); + break; + case TLSRead: + ret = SSL_read( m_ssl, m_buf, m_bufsize ); + break; + } + + switch( SSL_get_error( m_ssl, ret ) ) + { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + pushFunc(); + break; + case SSL_ERROR_NONE: + if( op == TLSHandshake ) + m_secure = true; + else if( op == TLSWrite ) + m_sendBuffer.erase( 0, ret ); + else if( op == TLSRead ) + m_handler->handleDecryptedData( this, std::string( m_buf, ret ) ); + pushFunc(); + break; + default: + if( !m_secure ) + m_handler->handleHandshakeResult( this, false, m_certInfo ); + return; + break; + } + if( !onceAgain && !m_recvBuffer.length() ) + onceAgain = true; + else if( onceAgain ) + onceAgain = false; + } + while( ( ( onceAgain || m_recvBuffer.length() ) && ( !m_secure || op == TLSRead ) ) + || ( ( op == TLSWrite ) && ( ret > 0 ) )); + } + + int OpenSSLBase::openSSLTime2UnixTime( const char* time_string ) + { + char tstring[19]; + + // making seperate c string out of time string + int m = 0; + for( int n = 0; n < 12; n += 2 ) + { + tstring[m] = time_string[n]; + tstring[m + 1] = time_string[n + 1]; + tstring[m + 2] = 0; + m += 3; + } + + // converting to struct tm + tm time_st; + time_st.tm_year = ( atoi( &tstring[3 * 0] ) >= 70 ) ? atoi( &tstring[3 * 0] ) + : atoi( &tstring[3 * 0] ) + 100; + time_st.tm_mon = atoi( &tstring[3 * 1] ) - 1; + time_st.tm_mday = atoi( &tstring[3 * 2] ); + time_st.tm_hour = atoi( &tstring[3 * 3] ); + time_st.tm_min = atoi( &tstring[3 * 4] ); + time_st.tm_sec = atoi( &tstring[3 * 5] ); + + time_t unixt = mktime( &time_st ); + return unixt; + } + + bool OpenSSLBase::handshake() + { + + doTLSOperation( TLSHandshake ); + + if( !m_secure ) + return true; + + int res = SSL_get_verify_result( m_ssl ); + if( res != X509_V_OK ) + m_certInfo.status = CertInvalid; + else + m_certInfo.status = CertOk; + + X509* peer = SSL_get_peer_certificate( m_ssl ); + if( peer ) + { + char peer_CN[256]; + X509_NAME_get_text_by_NID( X509_get_issuer_name( peer ), NID_commonName, peer_CN, sizeof( peer_CN ) ); + m_certInfo.issuer = peer_CN; + X509_NAME_get_text_by_NID( X509_get_subject_name( peer ), NID_commonName, peer_CN, sizeof( peer_CN ) ); + m_certInfo.server = peer_CN; + m_certInfo.date_from = openSSLTime2UnixTime( (char*) (peer->cert_info->validity->notBefore->data) ); + m_certInfo.date_to = openSSLTime2UnixTime( (char*) (peer->cert_info->validity->notAfter->data) ); + std::string p( peer_CN ); + std::transform( p.begin(), p.end(), p.begin(), tolower ); + if( p != m_server ) + m_certInfo.status |= CertWrongPeer; + + if( ASN1_UTCTIME_cmp_time_t( X509_get_notBefore( peer ), time( 0 ) ) != -1 ) + m_certInfo.status |= CertNotActive; + + if( ASN1_UTCTIME_cmp_time_t( X509_get_notAfter( peer ), time( 0 ) ) != 1 ) + m_certInfo.status |= CertExpired; + } + else + { + m_certInfo.status = CertInvalid; + } + + const char* tmp; + tmp = SSL_get_cipher_name( m_ssl ); + if( tmp ) + m_certInfo.cipher = tmp; + + tmp = SSL_get_cipher_version( m_ssl ); + if( tmp ) + m_certInfo.protocol = tmp; + + tmp = SSL_COMP_get_name( SSL_get_current_compression( m_ssl ) ); + if( tmp ) + m_certInfo.compression = tmp; + + m_valid = true; + + m_handler->handleHandshakeResult( this, true, m_certInfo ); + return true; + } + + void OpenSSLBase::pushFunc() + { + int wantwrite; + size_t wantread; + int frombio; + int tobio; + + while( ( wantwrite = BIO_ctrl_pending( m_nbio ) ) > 0 ) + { + if( wantwrite > m_bufsize ) + wantwrite = m_bufsize; + + if( !wantwrite ) + break; + + frombio = BIO_read( m_nbio, m_buf, wantwrite ); + + if( m_handler ) + m_handler->handleEncryptedData( this, std::string( m_buf, frombio ) ); + } + + while( ( wantread = BIO_ctrl_get_read_request( m_nbio ) ) > 0 ) + { + if( wantread > m_recvBuffer.length() ) + wantread = m_recvBuffer.length(); + + if( !wantread ) + break; + + tobio = BIO_write( m_nbio, m_recvBuffer.c_str(), wantread ); + m_recvBuffer.erase( 0, tobio ); + } + } + +} + +#endif // HAVE_OPENSSL diff --git a/libs/libgloox/tlsopensslbase.h b/libs/libgloox/tlsopensslbase.h new file mode 100644 index 0000000..8e35f67 --- /dev/null +++ b/libs/libgloox/tlsopensslbase.h @@ -0,0 +1,108 @@ +/* + Copyright (c) 2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef TLSOPENSSLBASE_H__ +#define TLSOPENSSLBASE_H__ + +#include "tlsbase.h" + +#include "config.h" + +#ifdef HAVE_OPENSSL + +#include + +namespace gloox +{ + + /** + * This is a common base class for client and server-side TLS + * stream encryption implementations using OpenSSL. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class OpenSSLBase : public TLSBase + { + public: + /** + * Constructor. + * @param th The TLSHandler to handle TLS-related events. + * @param server The server to use in certificate verification. + */ + OpenSSLBase( TLSHandler* th, const std::string& server = EmptyString ); + + /** + * Virtual destructor. + */ + virtual ~OpenSSLBase(); + + // reimplemented from TLSBase + virtual bool init( const std::string& clientKey = EmptyString, + const std::string& clientCerts = EmptyString, + const StringList& cacerts = StringList() ); + + // reimplemented from TLSBase + virtual bool encrypt( const std::string& data ); + + // reimplemented from TLSBase + virtual int decrypt( const std::string& data ); + + // reimplemented from TLSBase + virtual void cleanup(); + + // reimplemented from TLSBase + virtual bool handshake(); + + // reimplemented from TLSBase + virtual void setCACerts( const StringList& cacerts ); + + // reimplemented from TLSBase + virtual void setClientCert( const std::string& clientKey, const std::string& clientCerts ); + + protected: + virtual bool setType() = 0; + virtual int handshakeFunction() = 0; + + SSL* m_ssl; + SSL_CTX* m_ctx; + BIO* m_ibio; + BIO* m_nbio; + + private: + void pushFunc(); + virtual bool privateInit() { return true; } + + enum TLSOperation + { + TLSHandshake, + TLSWrite, + TLSRead + }; + + void doTLSOperation( TLSOperation op ); + int openSSLTime2UnixTime( const char* time_string ); + + std::string m_recvBuffer; + std::string m_sendBuffer; + char* m_buf; + const int m_bufsize; + + }; + +} + +#endif // HAVE_OPENSSL + +#endif // TLSOPENSSLBASE_H__ diff --git a/libs/libgloox/tlsopensslclient.cpp b/libs/libgloox/tlsopensslclient.cpp new file mode 100644 index 0000000..8642a47 --- /dev/null +++ b/libs/libgloox/tlsopensslclient.cpp @@ -0,0 +1,47 @@ +/* + Copyright (c) 2005-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "tlsopensslclient.h" + +#ifdef HAVE_OPENSSL + +namespace gloox +{ + + OpenSSLClient::OpenSSLClient( TLSHandler* th, const std::string& server ) + : OpenSSLBase( th, server ) + { + } + + OpenSSLClient::~OpenSSLClient() + { + } + + bool OpenSSLClient::setType() + { + m_ctx = SSL_CTX_new( SSLv23_client_method() ); // FIXME: use TLSv1_client_method() as soon as OpenSSL/gtalk combo is fixed! + if( !m_ctx ) + return false; + + return true; + } + + int OpenSSLClient::handshakeFunction() + { + return SSL_connect( m_ssl ); + } + +} + +#endif // HAVE_OPENSSL diff --git a/libs/libgloox/tlsopensslclient.h b/libs/libgloox/tlsopensslclient.h new file mode 100644 index 0000000..0ad5640 --- /dev/null +++ b/libs/libgloox/tlsopensslclient.h @@ -0,0 +1,63 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef TLSOPENSSLCLIENT_H__ +#define TLSOPENSSLCLIENT_H__ + +#include "tlsopensslbase.h" + +#include "config.h" + +#ifdef HAVE_OPENSSL + +#include + +namespace gloox +{ + + /** + * This class implements a TLS client backend using OpenSSL. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class OpenSSLClient : public OpenSSLBase + { + public: + /** + * Constructor. + * @param th The TLSHandler to handle TLS-related events. + * @param server The server to use in certificate verification. + */ + OpenSSLClient( TLSHandler* th, const std::string& server ); + + /** + * Virtual destructor. + */ + virtual ~OpenSSLClient(); + + private: + // reimplemented from OpenSSLBase + virtual bool setType(); + + // reimplemented from OpenSSLBase + virtual int handshakeFunction(); + + }; + +} + +#endif // HAVE_OPENSSL + +#endif // TLSOPENSSLCLIENT_H__ diff --git a/libs/libgloox/tlsopensslserver.cpp b/libs/libgloox/tlsopensslserver.cpp new file mode 100644 index 0000000..ae0fab3 --- /dev/null +++ b/libs/libgloox/tlsopensslserver.cpp @@ -0,0 +1,266 @@ +/* + Copyright (c) 2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "tlsopensslserver.h" + +#ifdef HAVE_OPENSSL + +#ifndef __SYMBIAN32__ + +#ifndef HEADER_DH_H +#include +#endif + +namespace gloox +{ + + OpenSSLServer::OpenSSLServer( TLSHandler* th ) + : OpenSSLBase( th ) + { + } + + OpenSSLServer::~OpenSSLServer() + { + } + + bool OpenSSLServer::setType() + { + m_ctx = SSL_CTX_new( SSLv23_server_method() ); + if( !m_ctx ) + return false; + + return true; + } + + int OpenSSLServer::handshakeFunction() + { + return SSL_accept( m_ssl ); + } + + DH* getDH512() + { + static unsigned char dh512_p[] = + { + 0xF5,0x2A,0xFF,0x3C,0xE1,0xB1,0x29,0x40,0x18,0x11,0x8D,0x7C, + 0x84,0xA7,0x0A,0x72,0xD6,0x86,0xC4,0x03,0x19,0xC8,0x07,0x29, + 0x7A,0xCA,0x95,0x0C,0xD9,0x96,0x9F,0xAB,0xD0,0x0A,0x50,0x9B, + 0x02,0x46,0xD3,0x08,0x3D,0x66,0xA4,0x5D,0x41,0x9F,0x9C,0x7C, + 0xBD,0x89,0x4B,0x22,0x19,0x26,0xBA,0xAB,0xA2,0x5E,0xC3,0x55, + 0xE9,0x2A,0x05,0x5F, + }; + static unsigned char dh512_g[] = + { + 0x02, + }; + DH* dh = DH_new(); + + if( !dh ) + return 0; + + dh->p = BN_bin2bn( dh512_p, sizeof( dh512_p ), 0 ); + dh->g = BN_bin2bn( dh512_g, sizeof( dh512_g ), 0 ); + if( ( dh->p == 0 ) || ( dh->g == 0 ) ) + { + DH_free( dh ); + return 0; + } + + return dh; + } + DH* getDH1024() + { + static unsigned char dh1024_p[]={ + 0xF4,0x88,0xFD,0x58,0x4E,0x49,0xDB,0xCD,0x20,0xB4,0x9D,0xE4, + 0x91,0x07,0x36,0x6B,0x33,0x6C,0x38,0x0D,0x45,0x1D,0x0F,0x7C, + 0x88,0xB3,0x1C,0x7C,0x5B,0x2D,0x8E,0xF6,0xF3,0xC9,0x23,0xC0, + 0x43,0xF0,0xA5,0x5B,0x18,0x8D,0x8E,0xBB,0x55,0x8C,0xB8,0x5D, + 0x38,0xD3,0x34,0xFD,0x7C,0x17,0x57,0x43,0xA3,0x1D,0x18,0x6C, + 0xDE,0x33,0x21,0x2C,0xB5,0x2A,0xFF,0x3C,0xE1,0xB1,0x29,0x40, + 0x18,0x11,0x8D,0x7C,0x84,0xA7,0x0A,0x72,0xD6,0x86,0xC4,0x03, + 0x19,0xC8,0x07,0x29,0x7A,0xCA,0x95,0x0C,0xD9,0x96,0x9F,0xAB, + 0xD0,0x0A,0x50,0x9B,0x02,0x46,0xD3,0x08,0x3D,0x66,0xA4,0x5D, + 0x41,0x9F,0x9C,0x7C,0xBD,0x89,0x4B,0x22,0x19,0x26,0xBA,0xAB, + 0xA2,0x5E,0xC3,0x55,0xE9,0x2F,0x78,0xC7, + }; + static unsigned char dh1024_g[]={ + 0x02, + }; + DH* dh = DH_new(); + + if( !dh ) + return 0; + + dh->p = BN_bin2bn( dh1024_p, sizeof( dh1024_p ), 0 ); + dh->g = BN_bin2bn( dh1024_g, sizeof( dh1024_g ), 0 ); + if( ( dh->p == 0 ) || ( dh->g == 0 ) ) + { + DH_free( dh ); + return 0; + } + + return dh; + } + DH* getDH2048() + { + static unsigned char dh2048_p[]={ + 0xF6,0x42,0x57,0xB7,0x08,0x7F,0x08,0x17,0x72,0xA2,0xBA,0xD6, + 0xA9,0x42,0xF3,0x05,0xE8,0xF9,0x53,0x11,0x39,0x4F,0xB6,0xF1, + 0x6E,0xB9,0x4B,0x38,0x20,0xDA,0x01,0xA7,0x56,0xA3,0x14,0xE9, + 0x8F,0x40,0x55,0xF3,0xD0,0x07,0xC6,0xCB,0x43,0xA9,0x94,0xAD, + 0xF7,0x4C,0x64,0x86,0x49,0xF8,0x0C,0x83,0xBD,0x65,0xE9,0x17, + 0xD4,0xA1,0xD3,0x50,0xF8,0xF5,0x59,0x5F,0xDC,0x76,0x52,0x4F, + 0x3D,0x3D,0x8D,0xDB,0xCE,0x99,0xE1,0x57,0x92,0x59,0xCD,0xFD, + 0xB8,0xAE,0x74,0x4F,0xC5,0xFC,0x76,0xBC,0x83,0xC5,0x47,0x30, + 0x61,0xCE,0x7C,0xC9,0x66,0xFF,0x15,0xF9,0xBB,0xFD,0x91,0x5E, + 0xC7,0x01,0xAA,0xD3,0x5B,0x9E,0x8D,0xA0,0xA5,0x72,0x3A,0xD4, + 0x1A,0xF0,0xBF,0x46,0x00,0x58,0x2B,0xE5,0xF4,0x88,0xFD,0x58, + 0x4E,0x49,0xDB,0xCD,0x20,0xB4,0x9D,0xE4,0x91,0x07,0x36,0x6B, + 0x33,0x6C,0x38,0x0D,0x45,0x1D,0x0F,0x7C,0x88,0xB3,0x1C,0x7C, + 0x5B,0x2D,0x8E,0xF6,0xF3,0xC9,0x23,0xC0,0x43,0xF0,0xA5,0x5B, + 0x18,0x8D,0x8E,0xBB,0x55,0x8C,0xB8,0x5D,0x38,0xD3,0x34,0xFD, + 0x7C,0x17,0x57,0x43,0xA3,0x1D,0x18,0x6C,0xDE,0x33,0x21,0x2C, + 0xB5,0x2A,0xFF,0x3C,0xE1,0xB1,0x29,0x40,0x18,0x11,0x8D,0x7C, + 0x84,0xA7,0x0A,0x72,0xD6,0x86,0xC4,0x03,0x19,0xC8,0x07,0x29, + 0x7A,0xCA,0x95,0x0C,0xD9,0x96,0x9F,0xAB,0xD0,0x0A,0x50,0x9B, + 0x02,0x46,0xD3,0x08,0x3D,0x66,0xA4,0x5D,0x41,0x9F,0x9C,0x7C, + 0xBD,0x89,0x4B,0x22,0x19,0x26,0xBA,0xAB,0xA2,0x5E,0xC3,0x55, + 0xE9,0x32,0x0B,0x3B, + }; + static unsigned char dh2048_g[]={ + 0x02, + }; + DH* dh = DH_new(); + + if( !dh ) + return 0; + + dh->p = BN_bin2bn( dh2048_p, sizeof( dh2048_p ), 0 ); + dh->g = BN_bin2bn( dh2048_g, sizeof( dh2048_g ), 0 ); + if( ( dh->p == 0 ) || ( dh->g == 0 ) ) + { + DH_free( dh ); + return 0; + } + + return dh; + } + + DH* getDH4096() + { + static unsigned char dh4096_p[]={ + 0xFA,0x14,0x72,0x52,0xC1,0x4D,0xE1,0x5A,0x49,0xD4,0xEF,0x09, + 0x2D,0xC0,0xA8,0xFD,0x55,0xAB,0xD7,0xD9,0x37,0x04,0x28,0x09, + 0xE2,0xE9,0x3E,0x77,0xE2,0xA1,0x7A,0x18,0xDD,0x46,0xA3,0x43, + 0x37,0x23,0x90,0x97,0xF3,0x0E,0xC9,0x03,0x50,0x7D,0x65,0xCF, + 0x78,0x62,0xA6,0x3A,0x62,0x22,0x83,0xA1,0x2F,0xFE,0x79,0xBA, + 0x35,0xFF,0x59,0xD8,0x1D,0x61,0xDD,0x1E,0x21,0x13,0x17,0xFE, + 0xCD,0x38,0x87,0x9E,0xF5,0x4F,0x79,0x10,0x61,0x8D,0xD4,0x22, + 0xF3,0x5A,0xED,0x5D,0xEA,0x21,0xE9,0x33,0x6B,0x48,0x12,0x0A, + 0x20,0x77,0xD4,0x25,0x60,0x61,0xDE,0xF6,0xB4,0x4F,0x1C,0x63, + 0x40,0x8B,0x3A,0x21,0x93,0x8B,0x79,0x53,0x51,0x2C,0xCA,0xB3, + 0x7B,0x29,0x56,0xA8,0xC7,0xF8,0xF4,0x7B,0x08,0x5E,0xA6,0xDC, + 0xA2,0x45,0x12,0x56,0xDD,0x41,0x92,0xF2,0xDD,0x5B,0x8F,0x23, + 0xF0,0xF3,0xEF,0xE4,0x3B,0x0A,0x44,0xDD,0xED,0x96,0x84,0xF1, + 0xA8,0x32,0x46,0xA3,0xDB,0x4A,0xBE,0x3D,0x45,0xBA,0x4E,0xF8, + 0x03,0xE5,0xDD,0x6B,0x59,0x0D,0x84,0x1E,0xCA,0x16,0x5A,0x8C, + 0xC8,0xDF,0x7C,0x54,0x44,0xC4,0x27,0xA7,0x3B,0x2A,0x97,0xCE, + 0xA3,0x7D,0x26,0x9C,0xAD,0xF4,0xC2,0xAC,0x37,0x4B,0xC3,0xAD, + 0x68,0x84,0x7F,0x99,0xA6,0x17,0xEF,0x6B,0x46,0x3A,0x7A,0x36, + 0x7A,0x11,0x43,0x92,0xAD,0xE9,0x9C,0xFB,0x44,0x6C,0x3D,0x82, + 0x49,0xCC,0x5C,0x6A,0x52,0x42,0xF8,0x42,0xFB,0x44,0xF9,0x39, + 0x73,0xFB,0x60,0x79,0x3B,0xC2,0x9E,0x0B,0xDC,0xD4,0xA6,0x67, + 0xF7,0x66,0x3F,0xFC,0x42,0x3B,0x1B,0xDB,0x4F,0x66,0xDC,0xA5, + 0x8F,0x66,0xF9,0xEA,0xC1,0xED,0x31,0xFB,0x48,0xA1,0x82,0x7D, + 0xF8,0xE0,0xCC,0xB1,0xC7,0x03,0xE4,0xF8,0xB3,0xFE,0xB7,0xA3, + 0x13,0x73,0xA6,0x7B,0xC1,0x0E,0x39,0xC7,0x94,0x48,0x26,0x00, + 0x85,0x79,0xFC,0x6F,0x7A,0xAF,0xC5,0x52,0x35,0x75,0xD7,0x75, + 0xA4,0x40,0xFA,0x14,0x74,0x61,0x16,0xF2,0xEB,0x67,0x11,0x6F, + 0x04,0x43,0x3D,0x11,0x14,0x4C,0xA7,0x94,0x2A,0x39,0xA1,0xC9, + 0x90,0xCF,0x83,0xC6,0xFF,0x02,0x8F,0xA3,0x2A,0xAC,0x26,0xDF, + 0x0B,0x8B,0xBE,0x64,0x4A,0xF1,0xA1,0xDC,0xEE,0xBA,0xC8,0x03, + 0x82,0xF6,0x62,0x2C,0x5D,0xB6,0xBB,0x13,0x19,0x6E,0x86,0xC5, + 0x5B,0x2B,0x5E,0x3A,0xF3,0xB3,0x28,0x6B,0x70,0x71,0x3A,0x8E, + 0xFF,0x5C,0x15,0xE6,0x02,0xA4,0xCE,0xED,0x59,0x56,0xCC,0x15, + 0x51,0x07,0x79,0x1A,0x0F,0x25,0x26,0x27,0x30,0xA9,0x15,0xB2, + 0xC8,0xD4,0x5C,0xCC,0x30,0xE8,0x1B,0xD8,0xD5,0x0F,0x19,0xA8, + 0x80,0xA4,0xC7,0x01,0xAA,0x8B,0xBA,0x53,0xBB,0x47,0xC2,0x1F, + 0x6B,0x54,0xB0,0x17,0x60,0xED,0x79,0x21,0x95,0xB6,0x05,0x84, + 0x37,0xC8,0x03,0xA4,0xDD,0xD1,0x06,0x69,0x8F,0x4C,0x39,0xE0, + 0xC8,0x5D,0x83,0x1D,0xBE,0x6A,0x9A,0x99,0xF3,0x9F,0x0B,0x45, + 0x29,0xD4,0xCB,0x29,0x66,0xEE,0x1E,0x7E,0x3D,0xD7,0x13,0x4E, + 0xDB,0x90,0x90,0x58,0xCB,0x5E,0x9B,0xCD,0x2E,0x2B,0x0F,0xA9, + 0x4E,0x78,0xAC,0x05,0x11,0x7F,0xE3,0x9E,0x27,0xD4,0x99,0xE1, + 0xB9,0xBD,0x78,0xE1,0x84,0x41,0xA0,0xDF, + }; + static unsigned char dh4096_g[]={ + 0x02, + }; + DH* dh = DH_new(); + + if( !dh ) + return 0; + + dh->p = BN_bin2bn( dh4096_p, sizeof( dh4096_p ), 0 ); + dh->g = BN_bin2bn( dh4096_g, sizeof( dh4096_g ), 0 ); + if( ( dh->p == 0 ) || ( dh->g == 0 ) ) + { + DH_free( dh ); + return 0; + } + + return dh; + } + + DH* tmp_dh_callback( SSL* /*s*/, int is_export, int keylength ) + { + switch( keylength ) + { + case 512: + return getDH512(); + break; + case 1024: + return getDH1024(); + break; + case 2048: + return getDH2048(); + break; + case 4096: + return getDH4096(); + break; + default: + // unsupported DH param length requested + return 0; + break; + } + } + + RSA* tmp_rsa_callback( SSL* /*s*/, int is_export, int keylength ) + { + return RSA_generate_key( keylength, RSA_F4, 0, 0 ); + } + + bool OpenSSLServer::privateInit() + { + SSL_CTX_set_tmp_rsa_callback( m_ctx, tmp_rsa_callback ); + SSL_CTX_set_tmp_dh_callback( m_ctx, tmp_dh_callback ); + SSL_CTX_set_tmp_ecdh( m_ctx, EC_KEY_new_by_curve_name( NID_sect163r2 ) ); + SSL_CTX_set_options( m_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE ); + return true; + } + +} + +#endif // __SYMBIAN32__ + +#endif // HAVE_OPENSSL diff --git a/libs/libgloox/tlsopensslserver.h b/libs/libgloox/tlsopensslserver.h new file mode 100644 index 0000000..5b5d8e5 --- /dev/null +++ b/libs/libgloox/tlsopensslserver.h @@ -0,0 +1,64 @@ +/* + Copyright (c) 2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef TLSOPENSSLSERVER_H__ +#define TLSOPENSSLSERVER_H__ + +#include "tlsopensslbase.h" + +#include "config.h" + +#ifdef HAVE_OPENSSL + +#include + +namespace gloox +{ + + /** + * This class implements a TLS server backend using OpenSSL. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class OpenSSLServer : public OpenSSLBase + { + public: + /** + * Constructor. + * @param th The TLSHandler to handle TLS-related events. + */ + OpenSSLServer( TLSHandler* th ); + + /** + * Virtual destructor. + */ + virtual ~OpenSSLServer(); + + private: + // reimplemented from OpenSSLBase + virtual bool privateInit(); + // reimplemented from OpenSSLBase + virtual bool setType(); + + // reimplemented from OpenSSLBase + virtual int handshakeFunction(); + + }; + +} + +#endif // HAVE_OPENSSL + +#endif // TLSOPENSSLSERVER_H__ diff --git a/libs/libgloox/tlsschannel.cpp b/libs/libgloox/tlsschannel.cpp new file mode 100644 index 0000000..d06eddb --- /dev/null +++ b/libs/libgloox/tlsschannel.cpp @@ -0,0 +1,803 @@ +/* + * Copyright (c) 2007-2009 by Jakob Schroeter + * This file is part of the gloox library. http://camaya.net/gloox + * + * This software is distributed under a license. The full license + * agreement can be found in the file LICENSE in this distribution. + * This software may not be copied, modified, sold or distributed + * other than expressed in the named license agreement. + * + * This software is distributed without any warranty. + */ + +#include "tlsschannel.h" + +#ifdef HAVE_WINTLS + +#include // just for debugging output + +namespace gloox +{ + SChannel::SChannel( TLSHandler* th, const std::string& server ) + : TLSBase( th, server ), m_cleanedup( true ) + { + //printf(">> SChannel::SChannel()\n"); + } + + SChannel::~SChannel() + { + m_handler = 0; + cleanup(); + //printf(">> SChannel::~SChannel()\n"); + } + + bool SChannel::encrypt( const std::string& data ) + { + if( !m_handler ) + return false; + + //printf(">> SChannel::encrypt()\n"); + std::string data_copy = data; + + SecBuffer buffer[4]; + SecBufferDesc buffer_desc; + DWORD cbIoBufferLength = m_sizes.cbHeader + m_sizes.cbMaximumMessage + m_sizes.cbTrailer; + + PBYTE e_iobuffer = static_cast( LocalAlloc( LMEM_FIXED, cbIoBufferLength ) ); + + if( e_iobuffer == NULL ) + { + //printf("**** Out of memory (2)\n"); + cleanup(); + if( !m_secure ) + m_handler->handleHandshakeResult( this, false, m_certInfo ); + return false; + } + PBYTE e_message = e_iobuffer + m_sizes.cbHeader; + do + { + const size_t size = ( data_copy.size() > m_sizes.cbMaximumMessage ) + ? m_sizes.cbMaximumMessage + : data_copy.size(); + memcpy( e_message, data_copy.data(), size ); + if( data_copy.size() > m_sizes.cbMaximumMessage ) + data_copy.erase( 0, m_sizes.cbMaximumMessage ); + else + data_copy = EmptyString; + + buffer[0].pvBuffer = e_iobuffer; + buffer[0].cbBuffer = m_sizes.cbHeader; + buffer[0].BufferType = SECBUFFER_STREAM_HEADER; + + buffer[1].pvBuffer = e_message; + buffer[1].cbBuffer = size; + buffer[1].BufferType = SECBUFFER_DATA; + + buffer[2].pvBuffer = static_cast(buffer[1].pvBuffer) + buffer[1].cbBuffer; + buffer[2].cbBuffer = m_sizes.cbTrailer; + buffer[2].BufferType = SECBUFFER_STREAM_TRAILER; + + buffer[3].BufferType = SECBUFFER_EMPTY; + + buffer_desc.ulVersion = SECBUFFER_VERSION; + buffer_desc.cBuffers = 4; + buffer_desc.pBuffers = buffer; + + SECURITY_STATUS e_status = EncryptMessage( &m_context, 0, &buffer_desc, 0 ); + if( SUCCEEDED( e_status ) ) + { + std::string encrypted( reinterpret_cast(e_iobuffer), + buffer[0].cbBuffer + buffer[1].cbBuffer + buffer[2].cbBuffer ); + m_handler->handleEncryptedData( this, encrypted ); + //if (data_copy.size() <= m_sizes.cbMaximumMessage) data_copy = EmptyString; + } + else + { + LocalFree( e_iobuffer ); + if( !m_secure ) + m_handler->handleHandshakeResult( this, false, m_certInfo ); + cleanup(); + return false; + } + } + while( data_copy.size() > 0 ); + LocalFree( e_iobuffer ); + return true; + } + + int SChannel::decrypt( const std::string& data ) + { + + if( !m_handler ) + return 0; + + //printf(">> SChannel::decrypt()\n"); + if( m_secure ) + { + m_buffer += data; + + SecBuffer buffer[4]; + SecBufferDesc buffer_desc; + DWORD cbIoBufferLength = m_sizes.cbHeader + m_sizes.cbMaximumMessage + m_sizes.cbTrailer; + bool wantNewBufferSize = false; + + PBYTE e_iobuffer = static_cast( LocalAlloc( LMEM_FIXED, cbIoBufferLength ) ); + if( e_iobuffer == NULL ) + { + //printf("**** Out of memory (2)\n"); + cleanup(); + if( !m_secure ) + m_handler->handleHandshakeResult( this, false, m_certInfo ); + return 0; + } + SECURITY_STATUS e_status; + + do + { + if( wantNewBufferSize ) + { + e_iobuffer = static_cast( LocalReAlloc( e_iobuffer, cbIoBufferLength, 0 ) ); + wantNewBufferSize = false; + } + + // copy data chunk from tmp string into encryption memory buffer + memcpy( e_iobuffer, m_buffer.data(), m_buffer.size() > + cbIoBufferLength ? cbIoBufferLength : m_buffer.size() ); + + buffer[0].pvBuffer = e_iobuffer; + buffer[0].cbBuffer = static_cast( m_buffer.size() > cbIoBufferLength + ? cbIoBufferLength + : m_buffer.size() ); + buffer[0].BufferType = SECBUFFER_DATA; + buffer[1].cbBuffer = buffer[2].cbBuffer = buffer[3].cbBuffer = 0; + buffer[1].BufferType = buffer[2].BufferType = buffer[3].BufferType = SECBUFFER_EMPTY; + + buffer_desc.ulVersion = SECBUFFER_VERSION; + buffer_desc.cBuffers = 4; + buffer_desc.pBuffers = buffer; + + unsigned long processed_data = buffer[0].cbBuffer; + e_status = DecryptMessage( &m_context, &buffer_desc, 0, 0 ); + + // print_error(e_status, "decrypt() ~ DecryptMessage()"); + // for (int n=0; n<4; n++) + // printf("buffer[%d].cbBuffer: %d \t%d\n", n, buffer[n].cbBuffer, buffer[n].BufferType); + + // Locate data and (optional) extra buffers. + SecBuffer* pDataBuffer = NULL; + SecBuffer* pExtraBuffer = NULL; + for( int i = 1; i < 4; i++ ) + { + if( pDataBuffer == NULL && buffer[i].BufferType == SECBUFFER_DATA ) + { + pDataBuffer = &buffer[i]; + //printf("buffer[%d].BufferType = SECBUFFER_DATA\n",i); + } + if( pExtraBuffer == NULL && buffer[i].BufferType == SECBUFFER_EXTRA ) + { + pExtraBuffer = &buffer[i]; + } + } + if( e_status == SEC_E_OK ) + { + std::string decrypted( reinterpret_cast( pDataBuffer->pvBuffer ), + pDataBuffer->cbBuffer ); + m_handler->handleDecryptedData( this, decrypted ); + if( pExtraBuffer == NULL ) + { + m_buffer.erase( 0, processed_data ); + } + else + { + //std::cout << "m_buffer.size() = " << pExtraBuffer->cbBuffer << std::endl; + m_buffer.erase( 0, processed_data - pExtraBuffer->cbBuffer ); + //std::cout << "m_buffer.size() = " << m_buffer.size() << std::endl; + + cbIoBufferLength = m_sizes.cbHeader + m_sizes.cbMaximumMessage + m_sizes.cbTrailer; + wantNewBufferSize = true; + } + } + else if( e_status == SEC_E_INCOMPLETE_MESSAGE ) + { + if( cbIoBufferLength < 200000 && m_buffer.size() > cbIoBufferLength ) + { + cbIoBufferLength += 1000; + wantNewBufferSize = true; + } + else + { + cbIoBufferLength = m_sizes.cbHeader + m_sizes.cbMaximumMessage + m_sizes.cbTrailer; + wantNewBufferSize = true; + break; + } + } + else + { + //std::cout << "decrypt !!!ERROR!!!\n"; + if( !m_secure ) + m_handler->handleHandshakeResult( this, false, m_certInfo ); + cleanup(); + break; + } + } + while( m_buffer.size() != 0 ); + LocalFree( e_iobuffer ); + } + else + { + handshakeStage( data ); + } + //printf("<< SChannel::decrypt()\n"); + return 0; + } + + void SChannel::cleanup() + { + if( !m_mutex.trylock() ) + return; + + m_buffer = ""; + if( !m_cleanedup ) + { + m_valid = false; + m_secure = false; + m_cleanedup = true; + DeleteSecurityContext( &m_context ); + FreeCredentialsHandle( &m_credHandle ); + } + + m_mutex.unlock(); + } + + bool SChannel::handshake() + { + if( !m_handler ) + return false; + + //printf(">> SChannel::handshake()\n"); + SECURITY_STATUS error; + ULONG return_flags; + TimeStamp t; + SecBuffer obuf[1]; + SecBufferDesc obufs; + SCHANNEL_CRED tlscred; + ULONG request = ISC_REQ_ALLOCATE_MEMORY + | ISC_REQ_CONFIDENTIALITY + | ISC_REQ_EXTENDED_ERROR + | ISC_REQ_INTEGRITY + | ISC_REQ_REPLAY_DETECT + | ISC_REQ_SEQUENCE_DETECT + | ISC_REQ_STREAM + | ISC_REQ_MANUAL_CRED_VALIDATION; + + /* initialize TLS credential */ + memset( &tlscred, 0, sizeof( SCHANNEL_CRED ) ); + tlscred.dwVersion = SCHANNEL_CRED_VERSION; + tlscred.grbitEnabledProtocols = SP_PROT_TLS1; + /* acquire credentials */ + error = AcquireCredentialsHandle( 0, + UNISP_NAME, + SECPKG_CRED_OUTBOUND, + 0, + &tlscred, + 0, + 0, + &m_credHandle, + &t ); + //print_error(error, "handshake() ~ AcquireCredentialsHandle()"); + if( error != SEC_E_OK ) + { + cleanup(); + m_handler->handleHandshakeResult( this, false, m_certInfo ); + return false; + } + else + { + /* initialize buffers */ + obuf[0].cbBuffer = 0; + obuf[0].pvBuffer = 0; + obuf[0].BufferType = SECBUFFER_TOKEN; + /* initialize buffer descriptors */ + obufs.ulVersion = SECBUFFER_VERSION; + obufs.cBuffers = 1; + obufs.pBuffers = obuf; + /* negotiate security */ + SEC_CHAR* hname = const_cast( m_server.c_str() ); + + error = InitializeSecurityContextA( &m_credHandle, + 0, + hname, + request, + 0, + SECURITY_NETWORK_DREP, + 0, + 0, + &m_context, + &obufs, + &return_flags, + NULL ); + //print_error(error, "handshake() ~ InitializeSecurityContext()"); + + if( error == SEC_I_CONTINUE_NEEDED ) + { + m_cleanedup = false; + //std::cout << "obuf[1].cbBuffer: " << obuf[0].cbBuffer << "\n"; + std::string senddata( static_cast(obuf[0].pvBuffer), obuf[0].cbBuffer ); + FreeContextBuffer( obuf[0].pvBuffer ); + m_handler->handleEncryptedData( this, senddata ); + return true; + } + else + { + cleanup(); + m_handler->handleHandshakeResult( this, false, m_certInfo ); + return false; + } + } + } + + void SChannel::handshakeStage( const std::string& data ) + { + //printf(" >> handshake_stage\n"); + m_buffer += data; + + SECURITY_STATUS error; + ULONG a; + TimeStamp t; + SecBuffer ibuf[2], obuf[1]; + SecBufferDesc ibufs, obufs; + ULONG request = ISC_REQ_ALLOCATE_MEMORY + | ISC_REQ_CONFIDENTIALITY + | ISC_REQ_EXTENDED_ERROR + | ISC_REQ_INTEGRITY + | ISC_REQ_REPLAY_DETECT + | ISC_REQ_SEQUENCE_DETECT + | ISC_REQ_STREAM + | ISC_REQ_MANUAL_CRED_VALIDATION; + + SEC_CHAR* hname = const_cast( m_server.c_str() ); + + do + { + /* initialize buffers */ + ibuf[0].cbBuffer = static_cast( m_buffer.size() ); + ibuf[0].pvBuffer = static_cast( const_cast( m_buffer.c_str() ) ); + //std::cout << "Size: " << m_buffer.size() << "\n"; + ibuf[1].cbBuffer = 0; + ibuf[1].pvBuffer = 0; + obuf[0].cbBuffer = 0; + obuf[0].pvBuffer = 0; + + ibuf[0].BufferType = SECBUFFER_TOKEN; + ibuf[1].BufferType = SECBUFFER_EMPTY; + obuf[0].BufferType = SECBUFFER_EMPTY; + /* initialize buffer descriptors */ + ibufs.ulVersion = obufs.ulVersion = SECBUFFER_VERSION; + ibufs.cBuffers = 2; + obufs.cBuffers = 1; + ibufs.pBuffers = ibuf; + obufs.pBuffers = obuf; + + /* + * std::cout << "obuf[0].cbBuffer: " << obuf[0].cbBuffer << "\t" << obuf[0].BufferType << "\n"; + * std::cout << "ibuf[0].cbBuffer: " << ibuf[0].cbBuffer << "\t" << ibuf[0].BufferType << "\n"; + * std::cout << "ibuf[1].cbBuffer: " << ibuf[1].cbBuffer << "\t" << ibuf[1].BufferType << "\n"; + */ + + /* negotiate security */ + error = InitializeSecurityContextA( &m_credHandle, + &m_context, + hname, + request, + 0, + 0, + &ibufs, + 0, + 0, + &obufs, + &a, + &t ); + //print_error(error, "handshake() ~ InitializeSecurityContext()"); + if( error == SEC_E_OK ) + { + // EXTRA STUFF?? + if( ibuf[1].BufferType == SECBUFFER_EXTRA ) + { + m_buffer.erase( 0, m_buffer.size() - ibuf[1].cbBuffer ); + } + else + { + m_buffer = EmptyString; + } + setSizes(); + setCertinfos(); + + m_secure = true; + m_handler->handleHandshakeResult( this, true, m_certInfo ); + break; + } + else if( error == SEC_I_CONTINUE_NEEDED ) + { + /* + * std::cout << "obuf[0].cbBuffer: " << obuf[0].cbBuffer << "\t" << obuf[0].BufferType << "\n"; + * std::cout << "ibuf[0].cbBuffer: " << ibuf[0].cbBuffer << "\t" << ibuf[0].BufferType << "\n"; + * std::cout << "ibuf[1].cbBuffer: " << ibuf[1].cbBuffer << "\t" << ibuf[1].BufferType << "\n"; + */ + + // STUFF TO SEND?? + if( obuf[0].cbBuffer != 0 && obuf[0].pvBuffer != NULL ) + { + std::string senddata( static_cast(obuf[0].pvBuffer), obuf[0].cbBuffer ); + FreeContextBuffer( obuf[0].pvBuffer ); + m_handler->handleEncryptedData( this, senddata ); + } + // EXTRA STUFF?? + if( ibuf[1].BufferType == SECBUFFER_EXTRA ) + { + m_buffer.erase( 0, m_buffer.size() - ibuf[1].cbBuffer ); + // Call again if we aren't sending anything (otherwise the server will not send anything back + // and this function won't get called again to finish the processing). This is needed for + // NT4.0 which does not seem to process the entire buffer the first time around + if( obuf[0].cbBuffer == 0 ) + handshakeStage( EmptyString ); + } + else + { + m_buffer = EmptyString; + } + return; + } + else if( error == SEC_I_INCOMPLETE_CREDENTIALS ) + { + handshakeStage( EmptyString ); + } + else if( error == SEC_E_INCOMPLETE_MESSAGE ) + { + break; + } + else + { + cleanup(); + m_handler->handleHandshakeResult( this, false, m_certInfo ); + break; + } + } + while( true ); + } + + void SChannel::setCACerts( const StringList& /*cacerts*/ ) {} + + void SChannel::setClientCert( const std::string& /*clientKey*/, const std::string& /*clientCerts*/ ) {} + + void SChannel::setSizes() + { + if( QueryContextAttributes( &m_context, SECPKG_ATTR_STREAM_SIZES, &m_sizes ) == SEC_E_OK ) + { + //std::cout << "set_sizes success\n"; + } + else + { + //std::cout << "set_sizes no success\n"; + cleanup(); + m_handler->handleHandshakeResult( this, false, m_certInfo ); + } + } + + int SChannel::filetime2int( FILETIME t ) + { + SYSTEMTIME stUTC; + FileTimeToSystemTime(&t, &stUTC); + std::tm ts; + ts.tm_year = stUTC.wYear - 1900; + ts.tm_mon = stUTC.wMonth - 1; + ts.tm_mday = stUTC.wDay; + ts.tm_hour = stUTC.wHour; + ts.tm_min = stUTC.wMinute; + ts.tm_sec = stUTC.wSecond; + + time_t unixtime; + if ( (unixtime = mktime(&ts)) == -1 ) + unixtime = 0; + return (int)unixtime; + } + + void SChannel::validateCert() + { + bool valid = false; + HTTPSPolicyCallbackData policyHTTPS; + CERT_CHAIN_POLICY_PARA policyParameter; + CERT_CHAIN_POLICY_STATUS policyStatus; + + PCCERT_CONTEXT remoteCertContext = NULL; + PCCERT_CHAIN_CONTEXT chainContext = NULL; + CERT_CHAIN_PARA chainParameter; + PSTR serverName = const_cast( m_server.c_str() ); + + PWSTR uServerName = NULL; + DWORD csizeServerName; + + LPSTR Usages[] = { + szOID_PKIX_KP_SERVER_AUTH, + szOID_SERVER_GATED_CRYPTO, + szOID_SGC_NETSCAPE + }; + DWORD cUsages = sizeof( Usages ) / sizeof( LPSTR ); + + do + { + // Get server's certificate. + if( QueryContextAttributes( &m_context, SECPKG_ATTR_REMOTE_CERT_CONTEXT, + (PVOID)&remoteCertContext ) != SEC_E_OK ) + { + //printf("Error querying remote certificate\n"); + // !!! THROW SOME ERROR + break; + } + + // unicode conversation + // calculating unicode server name size + csizeServerName = MultiByteToWideChar( CP_ACP, 0, serverName, -1, NULL, 0 ); + uServerName = reinterpret_cast( LocalAlloc( LMEM_FIXED, + csizeServerName * sizeof( WCHAR ) ) ); + if( uServerName == NULL ) + { + //printf("SEC_E_INSUFFICIENT_MEMORY ~ Not enough memory!!!\n"); + break; + } + + // convert into unicode + csizeServerName = MultiByteToWideChar( CP_ACP, 0, serverName, -1, uServerName, csizeServerName ); + if( csizeServerName == 0 ) + { + //printf("SEC_E_WRONG_PRINCIPAL\n"); + break; + } + + // create the chain + ZeroMemory( &chainParameter, sizeof( chainParameter ) ); + chainParameter.cbSize = sizeof( chainParameter ); + chainParameter.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; + chainParameter.RequestedUsage.Usage.cUsageIdentifier = cUsages; + chainParameter.RequestedUsage.Usage.rgpszUsageIdentifier = Usages; + + if( !CertGetCertificateChain( NULL, remoteCertContext, NULL, remoteCertContext->hCertStore, + &chainParameter, 0, NULL, &chainContext ) ) + { +// DWORD status = GetLastError(); +// printf("Error 0x%x returned by CertGetCertificateChain!!!\n", status); + break; + } + + // validate the chain + ZeroMemory( &policyHTTPS, sizeof( HTTPSPolicyCallbackData ) ); + policyHTTPS.cbStruct = sizeof( HTTPSPolicyCallbackData ); + policyHTTPS.dwAuthType = AUTHTYPE_SERVER; + policyHTTPS.fdwChecks = 0; + policyHTTPS.pwszServerName = uServerName; + + memset( &policyParameter, 0, sizeof( policyParameter ) ); + policyParameter.cbSize = sizeof( policyParameter ); + policyParameter.pvExtraPolicyPara = &policyHTTPS; + + memset( &policyStatus, 0, sizeof( policyStatus ) ); + policyStatus.cbSize = sizeof( policyStatus ); + + if( !CertVerifyCertificateChainPolicy( CERT_CHAIN_POLICY_SSL, chainContext, &policyParameter, + &policyStatus ) ) + { +// DWORD status = GetLastError(); +// printf("Error 0x%x returned by CertVerifyCertificateChainPolicy!!!\n", status); + break; + } + + if( policyStatus.dwError ) + { + //printf("Trust Error!!!}n"); + break; + } + valid = true; + } + while( false ); + // cleanup + if( chainContext ) CertFreeCertificateChain( chainContext ); + m_certInfo.chain = valid; + } + + void SChannel::connectionInfos() + { + SecPkgContext_ConnectionInfo conn_info; + + memset( &conn_info, 0, sizeof( conn_info ) ); + + if( QueryContextAttributes( &m_context, SECPKG_ATTR_CONNECTION_INFO, &conn_info ) == SEC_E_OK ) + { + switch( conn_info.dwProtocol ) + { + case SP_PROT_TLS1_CLIENT: + m_certInfo.protocol = "TLSv1"; + break; + case SP_PROT_SSL3_CLIENT: + m_certInfo.protocol = "SSLv3"; + break; + default: + m_certInfo.protocol = "unknown"; + } + + switch( conn_info.aiCipher ) + { + case CALG_3DES: + m_certInfo.cipher = "3DES"; + break; + case CALG_AES_128: + m_certInfo.cipher = "AES_128"; + break; + case CALG_AES_256: + m_certInfo.cipher = "AES_256"; + break; + case CALG_DES: + m_certInfo.cipher = "DES"; + break; + case CALG_RC2: + m_certInfo.cipher = "RC2"; + break; + case CALG_RC4: + m_certInfo.cipher = "RC4"; + break; + default: + m_certInfo.cipher = EmptyString; + } + + switch( conn_info.aiHash ) + { + case CALG_MD5: + m_certInfo.mac = "MD5"; + break; + case CALG_SHA: + m_certInfo.mac = "SHA"; + break; + default: + m_certInfo.mac = EmptyString; + } + } + } + + void SChannel::certData() + { + PCCERT_CONTEXT remoteCertContext = NULL; + CHAR certString[1000]; + + // getting server's certificate + if( QueryContextAttributes( &m_context, SECPKG_ATTR_REMOTE_CERT_CONTEXT, + (PVOID)&remoteCertContext ) != SEC_E_OK ) + { + return; + } + + // setting certificat's lifespan + m_certInfo.date_from = filetime2int( remoteCertContext->pCertInfo->NotBefore ); + m_certInfo.date_to = filetime2int( remoteCertContext->pCertInfo->NotAfter ); + + if( !CertNameToStrA( remoteCertContext->dwCertEncodingType, + &remoteCertContext->pCertInfo->Subject, + CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG, + certString, sizeof( certString ) ) ) + { + return; + } + m_certInfo.server = certString; + + if( !CertNameToStrA( remoteCertContext->dwCertEncodingType, + &remoteCertContext->pCertInfo->Issuer, + CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG, + certString, sizeof( certString ) ) ) + { + return; + } + m_certInfo.issuer = certString; + } + + void SChannel::setCertinfos() + { + validateCert(); + connectionInfos(); + certData(); + } + +#if 0 + void SChannel::print_error( int errorcode, const char* place ) + { + printf( "Win error at %s.\n", place ); + switch( errorcode ) + { + case SEC_E_OK: + printf( "\tValue:\tSEC_E_OK\n" ); + printf( "\tDesc:\tNot really an error. Everything is fine.\n" ); + break; + case SEC_E_INSUFFICIENT_MEMORY: + printf( "\tValue:\tSEC_E_INSUFFICIENT_MEMORY\n" ); + printf( "\tDesc:\tThere is not enough memory available to complete the requested action.\n" ); + break; + case SEC_E_INTERNAL_ERROR: + printf( "\tValue:\tSEC_E_INTERNAL_ERROR\n" ); + printf( "\tDesc:\tAn error occurred that did not map to an SSPI error code.\n" ); + break; + case SEC_E_NO_CREDENTIALS: + printf( "\tValue:\tSEC_E_NO_CREDENTIALS\n" ); + printf( "\tDesc:\tNo credentials are available in the security package.\n" ); + break; + case SEC_E_NOT_OWNER: + printf( "\tValue:\tSEC_E_NOT_OWNER\n" ); + printf( "\tDesc:\tThe caller of the function does not have the necessary credentials.\n" ); + break; + case SEC_E_SECPKG_NOT_FOUND: + printf( "\tValue:\tSEC_E_SECPKG_NOT_FOUND\n" ); + printf( "\tDesc:\tThe requested security package does not exist. \n" ); + break; + case SEC_E_UNKNOWN_CREDENTIALS: + printf( "\tValue:\tSEC_E_UNKNOWN_CREDENTIALS\n" ); + printf( "\tDesc:\tThe credentials supplied to the package were not recognized.\n" ); + break; + case SEC_E_INCOMPLETE_MESSAGE: + printf( "\tValue:\tSEC_E_INCOMPLETE_MESSAGE\n" ); + printf( "\tDesc:\tData for the whole message was not read from the wire.\n" ); + break; + case SEC_E_INVALID_HANDLE: + printf( "\tValue:\tSEC_E_INVALID_HANDLE\n" ); + printf( "\tDesc:\tThe handle passed to the function is invalid.\n" ); + break; + case SEC_E_INVALID_TOKEN: + printf( "\tValue:\tSEC_E_INVALID_TOKEN\n" ); + printf( "\tDesc:\tThe error is due to a malformed input token, such as a token " + "corrupted in transit...\n" ); + break; + case SEC_E_LOGON_DENIED: + printf( "\tValue:\tSEC_E_LOGON_DENIED\n" ); + printf( "\tDesc:\tThe logon failed.\n" ); + break; + case SEC_E_NO_AUTHENTICATING_AUTHORITY: + printf( "\tValue:\tSEC_E_NO_AUTHENTICATING_AUTHORITY\n" ); + printf( "\tDesc:\tNo authority could be contacted for authentication...\n" ); + break; + case SEC_E_TARGET_UNKNOWN: + printf( "\tValue:\tSEC_E_TARGET_UNKNOWN\n" ); + printf( "\tDesc:\tThe target was not recognized.\n" ); + break; + case SEC_E_UNSUPPORTED_FUNCTION: + printf( "\tValue:\tSEC_E_UNSUPPORTED_FUNCTION\n" ); + printf( "\tDesc:\tAn invalid context attribute flag (ISC_REQ_DELEGATE or " + "ISC_REQ_PROMPT_FOR_CREDS)...\n" ); + break; + case SEC_E_WRONG_PRINCIPAL: + printf( "\tValue:\tSEC_E_WRONG_PRINCIPAL\n" ); + printf( "\tDesc:\tThe principal that received the authentication request " + "is not the same as the...\n" ); + break; + case SEC_I_COMPLETE_AND_CONTINUE: + printf( "\tValue:\tSEC_I_COMPLETE_AND_CONTINUE\n" ); + printf( "\tDesc:\tThe client must call CompleteAuthToken and then pass the output...\n" ); + break; + case SEC_I_COMPLETE_NEEDED: + printf( "\tValue:\tSEC_I_COMPLETE_NEEDED\n" ); + printf( "\tDesc:\tThe client must finish building the message and then " + "call the CompleteAuthToken function.\n" ); + break; + case SEC_I_CONTINUE_NEEDED: + printf( "\tValue:\tSEC_I_CONTINUE_NEEDED\n" ); + printf( "\tDesc:\tThe client must send the output token to the server " + "and wait for a return token...\n" ); + break; + case SEC_I_INCOMPLETE_CREDENTIALS: + printf( "\tValue:\tSEC_I_INCOMPLETE_CREDENTIALS\n" ); + printf( "\tDesc:\tThe server has requested client authentication, " + "and the supplied credentials either...\n" ); + break; + default: + printf( "\tValue:\t%d\n", errorcode ); + printf( "\tDesc:\tUnknown error code.\n" ); + } + } +#endif + +} + +#endif // HAVE_WINTLS diff --git a/libs/libgloox/tlsschannel.h b/libs/libgloox/tlsschannel.h new file mode 100644 index 0000000..8286835 --- /dev/null +++ b/libs/libgloox/tlsschannel.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2007-2009 by Jakob Schroeter + * This file is part of the gloox library. http://camaya.net/gloox + * + * This software is distributed under a license. The full license + * agreement can be found in the file LICENSE in this distribution. + * This software may not be copied, modified, sold or distributed + * other than expressed in the named license agreement. + * + * This software is distributed without any warranty. + */ + +#ifndef TLSSCHANNEL_H__ +#define TLSSCHANNEL_H__ + +#include "tlsbase.h" + +#include "config.h" + +#ifdef HAVE_WINTLS + +#include + +#define SECURITY_WIN32 +#include +#include +#include + +namespace gloox +{ + + /** + * This class implements a TLS backend using SChannel. + * + * @author Jakob Schroeter + * @since 0.9 + */ + class SChannel : public TLSBase + { + public: + /** + * Constructor. + * @param th The TLSHandler to handle TLS-related events. + * @param server The server to use in certificate verification. + */ + SChannel( TLSHandler* th, const std::string& server ); + + /** + * Virtual destructor. + */ + virtual ~SChannel(); + + // reimplemented from TLSBase + virtual bool init( const std::string& /*clientKey*/ = EmptyString, + const std::string& /*clientCerts*/ = EmptyString, + const StringList& /*cacerts*/ = StringList() ) + { return true; } + + // reimplemented from TLSBase + virtual bool encrypt( const std::string& data ); + + // reimplemented from TLSBase + virtual int decrypt( const std::string& data ); + + // reimplemented from TLSBase + virtual void cleanup(); + + // reimplemented from TLSBase + virtual bool handshake(); + + // reimplemented from TLSBase + virtual void setCACerts( const StringList& cacerts ); + + // reimplemented from TLSBase + virtual void setClientCert( const std::string& clientKey, const std::string& clientCerts ); + + private: + void handshakeStage( const std::string& data ); + void setSizes(); + + int filetime2int( FILETIME t ); + + void validateCert(); + void connectionInfos(); + void certData(); + void setCertinfos(); + CredHandle m_credHandle; + CtxtHandle m_context; + + SecPkgContext_StreamSizes m_sizes; + + size_t m_header_max; + size_t m_message_max; + size_t m_trailer_max; + + std::string m_buffer; + + bool m_cleanedup; + + // windows error outputs +// void print_error( int errorcode, const char* place = 0 ); + + }; +} + +#endif // HAVE_WINTLS + +#endif // TLSSCHANNEL_H__ diff --git a/libs/libgloox/uniquemucroom.cpp b/libs/libgloox/uniquemucroom.cpp new file mode 100644 index 0000000..1ed1784 --- /dev/null +++ b/libs/libgloox/uniquemucroom.cpp @@ -0,0 +1,109 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "uniquemucroom.h" +#include "clientbase.h" +#include "jid.h" +#include "sha.h" + +namespace gloox +{ + + // ---- UniqueMUCRoom::Unique ---- + UniqueMUCRoom::Unique::Unique( const Tag* tag ) + : StanzaExtension( ExtMUCUnique ) + { + if( !tag || tag->name() != "unique" || tag->xmlns() != XMLNS_MUC_UNIQUE ) + return; + + m_name = tag->cdata(); + } + + const std::string& UniqueMUCRoom::Unique::filterString() const + { + static const std::string filter = "/iq/unique[@xmlns='" + XMLNS_MUC_UNIQUE + "']"; + return filter; + } + + Tag* UniqueMUCRoom::Unique::tag() const + { + Tag* t = new Tag( "unique" ); + t->setXmlns( XMLNS_MUC_UNIQUE ); + if( !m_name.empty() ) + t->setCData( m_name ); + return t; + } + // ---- ~UniqueMUCRoom::Unique ---- + + // ---- UniqueMUCRoom ---- + UniqueMUCRoom::UniqueMUCRoom( ClientBase* parent, const JID& nick, MUCRoomHandler* mrh ) + : InstantMUCRoom( parent, nick, mrh ) + { + if( m_parent ) + { + m_parent->registerStanzaExtension( new Unique() ); + } + } + + UniqueMUCRoom::~UniqueMUCRoom() + { + if( m_parent ) + { + m_parent->removeIDHandler( this ); +// m_parent->removeStanzaExtension( ExtMUCUnique ); // don't remove, other rooms might need it + } + } + + void UniqueMUCRoom::join() + { + if( !m_parent || m_joined ) + return; + + IQ iq( IQ::Get, m_nick.server() ); + iq.addExtension( new Unique() ); + m_parent->send( iq, this, RequestUniqueName ); + } + + void UniqueMUCRoom::handleIqID( const IQ& iq, int context ) + { + switch( iq.subtype() ) + { + case IQ::Result: + if( context == RequestUniqueName ) + { + const Unique* u = iq.findExtension( ExtMUCUnique ); + if( u ) + { + if( !u->name().empty() ) + setName( u->name() ); + } + } + break; + case IQ::Error: + if( context == RequestUniqueName ) + { + SHA s; + s.feed( m_parent->jid().full() ); + s.feed( m_parent->getID() ); + setName( s.hex() ); + } + break; + default: + break; + } + + MUCRoom::join(); + } + +} diff --git a/libs/libgloox/uniquemucroom.h b/libs/libgloox/uniquemucroom.h new file mode 100644 index 0000000..bfc39e2 --- /dev/null +++ b/libs/libgloox/uniquemucroom.h @@ -0,0 +1,117 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef UNIQUEMUCROOM_H__ +#define UNIQUEMUCROOM_H__ + +#include "instantmucroom.h" +#include "stanzaextension.h" + +namespace gloox +{ + + /** + * @brief This class implements a unique MUC room. + * + * A unique MUC room is a room with a non-human-readable name. It is primarily intended + * to be used when converting one-to-one chats to multi-user chats. + * + * XEP version: 1.21 + * @author Jakob Schroeter + * @since 0.9 + */ + class GLOOX_API UniqueMUCRoom : public InstantMUCRoom + { + public: + /** + * Creates a new abstraction of a @b unique Multi-User Chat room. The room is not joined + * automatically. Use join() to join the room, use leave() to leave it. See MUCRoom for + * detailed info. + * @param parent The ClientBase object to use for the communication. + * @param nick The service to create the room on plus the desired nickname in the form + * @b service/nick. + * @param mrh The MUCRoomHandler that will listen to room events. May be 0 and may be specified + * later using registerMUCRoomHandler(). However, without one, MUC is no joy. + * @note To subsequently configure the room, use MUCRoom::registerMUCRoomConfigHandler(). + */ + UniqueMUCRoom( ClientBase* parent, const JID& nick, MUCRoomHandler* mrh ); + + /** + * Virtual Destructor. + */ + virtual ~UniqueMUCRoom(); + + // reimplemented from MUCRoom + virtual void join(); + + private: +#ifdef UNIQUEMUCROOM_TEST + public: +#endif + /** + * @brief A stanza extension wrapping MUC's <unique> element. + * + * @author Jakob Schroeter + * @since 1.0 + */ + class Unique : public StanzaExtension + { + public: + /** + * Creates a new object from the given Tag. + * @param tag The Tag to parse. + */ + Unique( const Tag* tag = 0 ); + + /** + *Virtual Destructor. + */ + virtual ~Unique() {} + + /** + * Returns the unique name created by the server. + * @return The server-created unique room name. + */ + const std::string& name() const { return m_name; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new Unique( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const + { + return new Unique( *this ); + } + + private: + std::string m_name; + }; + + // reimplemented from MUCRoom (IqHandler) + void handleIqID( const IQ& iq, int context ); + + }; + +} + +#endif // UNIQUEMUCROOM_H__ diff --git a/libs/libgloox/util.cpp b/libs/libgloox/util.cpp new file mode 100644 index 0000000..f50026c --- /dev/null +++ b/libs/libgloox/util.cpp @@ -0,0 +1,121 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#include "util.h" +#include "gloox.h" + +namespace gloox +{ + + namespace util + { + + int internalLog2( unsigned int n ) + { + int pos = 0; + if ( n >= 1<<16 ) { n >>= 16; pos += 16; } + if ( n >= 1<< 8 ) { n >>= 8; pos += 8; } + if ( n >= 1<< 4 ) { n >>= 4; pos += 4; } + if ( n >= 1<< 2 ) { n >>= 2; pos += 2; } + if ( n >= 1<< 1 ) { pos += 1; } + return ( (n == 0) ? (-1) : pos ); + } + + unsigned _lookup( const std::string& str, const char* values[], unsigned size, int def ) + { + unsigned i = 0; + for( ; i < size && str != values[i]; ++i ) + ; + return ( i == size && def >= 0 ) ? (unsigned)def : i; + } + + const std::string _lookup( unsigned code, const char* values[], unsigned size, const std::string& def ) + { + return code < size ? std::string( values[code] ) : def; + } + + unsigned _lookup2( const std::string& str, const char* values[], + unsigned size, int def ) + { + return 1 << _lookup( str, values, size, def <= 0 ? def : (int)internalLog2( def ) ); + } + + const std::string _lookup2( unsigned code, const char* values[], unsigned size, const std::string& def ) + { + const unsigned i = (unsigned)internalLog2( code ); + return i < size ? std::string( values[i] ) : def; + } + + static const char escape_chars[] = { '&', '<', '>', '\'', '"' }; + + static const std::string escape_seqs[] = { "amp;", "lt;", "gt;", "apos;", "quot;" }; + + static const unsigned escape_size = 5; + + const std::string escape( std::string what ) + { + for( size_t val, i = 0; i < what.length(); ++i ) + { + for( val = 0; val < escape_size; ++val ) + { + if( what[i] == escape_chars[val] ) + { + what[i] = '&'; + what.insert( i+1, escape_seqs[val] ); + i += escape_seqs[val].length(); + break; + } + } + } + return what; + } + + bool checkValidXMLChars( const std::string& data ) + { + if( data.empty() ) + return true; + + std::string::const_iterator it = data.begin(); + for( ; it != data.end() + && ( (unsigned char)(*it) == 0x09 + || (unsigned char)(*it) == 0x0a + || (unsigned char)(*it) == 0x0d + || ( (unsigned char)(*it) >= 0x20 + && (unsigned char)(*it) != 0xc0 + && (unsigned char)(*it) != 0xc1 + && (unsigned char)(*it) < 0xf5 ) ); ++it ) + ; + + return ( it == data.end() ); + } + + void replaceAll( std::string& target, const std::string& find, const std::string& replace ) + { + std::string::size_type findSize = find.size(); + std::string::size_type replaceSize = replace.size(); + + if( findSize == 0 ) + return; + + std::string::size_type index = target.find( find, 0 ); + + while( index != std::string::npos ) + { + target.replace( index, findSize, replace ); + index = target.find( find, index+replaceSize ); + } + } + + } + +} + diff --git a/libs/libgloox/util.h b/libs/libgloox/util.h new file mode 100644 index 0000000..63b3feb --- /dev/null +++ b/libs/libgloox/util.h @@ -0,0 +1,269 @@ +/* + Copyright (c) 2007-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#ifndef UTIL_H__ +#define UTIL_H__ + +#include "gloox.h" + +#include +#include +#include +#include +#include + +namespace gloox +{ + + /** + * @brief A namespace holding a couple utility functions. + */ + namespace util + { + + #define lookup( a, b ) _lookup( a, b, sizeof(b)/sizeof(char*) ) + #define lookup2( a, b ) _lookup2( a, b, sizeof(b)/sizeof(char*) ) + #define deflookup( a, b, c ) _lookup( a, b, sizeof(b)/sizeof(char*), c ) + #define deflookup2( a, b, c ) _lookup2( a, b, sizeof(b)/sizeof(char*), c ) + + /** + * Finds the enumerated value associated with a string value. + * @param str String to search for. + * @param values Array of String/Code pairs to look into. + * @param size The array's size. + * @param def Default value returned in case the lookup failed. + * @return The associated enum code. + */ + GLOOX_API unsigned _lookup( const std::string& str, const char* values[], + unsigned size, int def = -1 ); + + /** + * Finds the string associated with an enumerated type. + * @param code Code of the string to search for. + * @param values Array of String/Code pairs to look into. + * @param size The array's size. + * @param def Default value returned in case the lookup failed. + * @return The associated string (empty in case there's no match). + */ + GLOOX_API const std::string _lookup( unsigned code, const char* values[], + unsigned size, const std::string& def = EmptyString ); + + /** + * Finds the ORable enumerated value associated with a string value. + * @param str String to search for. + * @param values Array of String/Code pairs to look into. + * @param size The array's size. + * @param def The default value to return if the lookup failed. + * @return The associated enum code. + */ + GLOOX_API unsigned _lookup2( const std::string& str, const char* values[], + unsigned size, int def = -1 ); + + /** + * Finds the string associated with an ORable enumerated type. + * @param code Code of the string to search for. + * @param values Array of String/Code pairs to look into. + * @param size The array's size. + * @param def The default value to return if the lookup failed. + * @return The associated string (empty in case there's no match). + */ + GLOOX_API const std::string _lookup2( unsigned code, const char* values[], + unsigned size, const std::string& def = EmptyString ); + + /** + * A convenience function that executes the given function on each object in a given list. + * @param t The object to execute the function on. + * @param f The function to execute. + */ + template< typename T, typename F > + inline void ForEach( T& t, F f ) + { + for( typename T::iterator it = t.begin(); it != t.end(); ++it ) + ( (*it)->*f )(); + } + + /** + * A convenience function that executes the given function on each object in a given list, + * passing the given argument. + * @param t The object to execute the function on. + * @param f The function to execute. + * @param d An argument to pass to the function. + */ + template< typename T, typename F, typename D > + inline void ForEach( T& t, F f, D& d ) + { + for( typename T::iterator it = t.begin(); it != t.end(); ++it ) + ( (*it)->*f )( d ); + } + + /** + * A convenience function that executes the given function on each object in a given list, + * passing the given arguments. + * @param t The object to execute the function on. + * @param f The function to execute. + * @param d1 An argument to pass to the function. + * @param d2 An argument to pass to the function. + */ + template< typename T, typename F, typename D1, typename D2 > + inline void ForEach( T& t, F f, D1& d1, D2& d2 ) + { + for( typename T::iterator it = t.begin(); it != t.end(); ++it ) + ( (*it)->*f )( d1, d2 ); + } + + /** + * A convenience function that executes the given function on each object in a given list, + * passing the given arguments. + * @param t The object to execute the function on. + * @param f The function to execute. + * @param d1 An argument to pass to the function. + * @param d2 An argument to pass to the function. + * @param d3 An argument to pass to the function. + */ + template< typename T, typename F, typename D1, typename D2, typename D3 > + inline void ForEach( T& t, F f, D1& d1, D2& d2, D3& d3 ) + { + for( typename T::iterator it = t.begin(); it != t.end(); ++it ) + ( (*it)->*f )( d1, d2, d3 ); + } + + /** + * Delete all elements from a list of pointers. + * @param L List of pointers to delete. + */ + template< typename T > + inline void clearList( std::list< T* >& L ) + { + typename std::list< T* >::iterator it = L.begin(); + typename std::list< T* >::iterator it2; + while( it != L.end() ) + { + it2 = it++; + delete (*it2); + L.erase( it2 ); + } + } + + /** + * Delete all associated values from a map (not the key elements). + * @param M Map of pointer values to delete. + */ + template< typename Key, typename T > + inline void clearMap( std::map< Key, T* >& M ) + { + typename std::map< Key, T* >::iterator it = M.begin(); + typename std::map< Key, T* >::iterator it2; + while( it != M.end() ) + { + it2 = it++; + delete (*it2).second; + M.erase( it2 ); + } + } + + /** + * Delete all associated values from a map (not the key elements). + * Const key type version. + * @param M Map of pointer values to delete. + */ + template< typename Key, typename T > + inline void clearMap( std::map< const Key, T* >& M ) + { + typename std::map< const Key, T* >::iterator it = M.begin(); + typename std::map< const Key, T* >::iterator it2; + while( it != M.end() ) + { + it2 = it++; + delete (*it2).second; + M.erase( it2 ); + } + } + + /** + * Does some fancy escaping. (& --> &amp;, etc). + * @param what A string to escape. + * @return The escaped string. + */ + GLOOX_API const std::string escape( std::string what ); + + /** + * Checks whether the given input is valid UTF-8. + * @param data The data to check for validity. + * @return @@b True if the input is valid UTF-8, @b false otherwise. + */ + GLOOX_API bool checkValidXMLChars( const std::string& data ); + + /** + * Custom log2() implementation. + * @param n Figure to take the logarithm from. + * @return The logarithm to the basis of 2. + */ + GLOOX_API int internalLog2( unsigned int n ); + + /** + * Replace all instances of one substring of arbitrary length + * with another substring of arbitrary length. Replacement happens + * in place (so make a copy first if you don't want the original modified). + * @param target The string to process. Changes are made "in place". + * @param find The sub-string to find within the target string + * @param replace The sub-string to substitute for the find string. + * @todo Look into merging with util::escape() and Parser::decode(). + */ + GLOOX_API void replaceAll( std::string& target, const std::string& find, const std::string& replace ); + + /** + * Converts a long int to its string representation. + * @param value The long integer value. + * @param base The integer's base. + * @return The long int's string represenation. + */ + static inline const std::string long2string( long int value, const int base = 10 ) + { + int add = 0; + if( base < 2 || base > 16 || value == 0 ) + return "0"; + else if( value < 0 ) + { + ++add; + value = -value; + } + int len = (int)( log( (double)( value ? value : 1 ) ) / log( (double)base ) ) + 1; + const char digits[] = "0123456789ABCDEF"; + char* num = (char*)calloc( len + 1 + add, sizeof( char ) ); + num[len--] = '\0'; + if( add ) + num[0] = '-'; + while( value && len > -1 ) + { + num[len-- + add] = digits[(int)( value % base )]; + value /= base; + } + const std::string result( num ); + free( num ); + return result; + } + + /** + * Converts an int to its string representation. + * @param value The integer value. + * @return The int's string represenation. + */ + static inline const std::string int2string( int value ) + { + return long2string( value ); + } + + } + +} + +#endif // UTIL_H__ diff --git a/libs/libgloox/vcard.cpp b/libs/libgloox/vcard.cpp new file mode 100644 index 0000000..740dcbe --- /dev/null +++ b/libs/libgloox/vcard.cpp @@ -0,0 +1,555 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "vcard.h" +#include "tag.h" +#include "base64.h" + +namespace gloox +{ + + void VCard::insertField( Tag* vcard, const char* field, const std::string& var ) + { + if( field && !var.empty() ) + new Tag( vcard, field, var ); + } + + void VCard::insertField( Tag* vcard, const char* field, bool var ) + { + if( field && var ) + new Tag( vcard, field ); + } + + void VCard::checkField( const Tag* vcard, const char* field, std::string& var ) + { + if( field ) + { + Tag* child = vcard->findChild( field ); + if( child ) + var = child->cdata(); + } + } + + VCard::VCard() + : StanzaExtension( ExtVCard ), m_class( ClassNone ), m_prodid( "gloox" + GLOOX_VERSION ), + m_N( false ), m_PHOTO( false ), m_LOGO( false ) + { + m_valid = true; + } + + VCard::VCard( const Tag* vcard ) + : StanzaExtension( ExtVCard ), m_class( ClassNone ), m_prodid( "gloox" + GLOOX_VERSION ), + m_N( false ), m_PHOTO( false ), m_LOGO( false ) + { + if( !vcard || vcard->name() != "vCard" || vcard->xmlns() != XMLNS_VCARD_TEMP ) + return; + + m_valid = true; + + checkField( vcard, "FN", m_formattedname ); + checkField( vcard, "NICKNAME", m_nickname ); + checkField( vcard, "URL", m_url ); + checkField( vcard, "BDAY", m_bday ); + checkField( vcard, "JABBERID", m_jabberid ); + checkField( vcard, "TITLE", m_title ); + checkField( vcard, "ROLE", m_role ); + checkField( vcard, "NOTE", m_note ); + checkField( vcard, "DESC", m_desc ); + checkField( vcard, "MAILER", m_mailer ); + checkField( vcard, "TZ", m_tz ); + checkField( vcard, "PRODID", m_prodid ); + checkField( vcard, "REV", m_rev ); + checkField( vcard, "SORT-STRING", m_sortstring ); + checkField( vcard, "UID", m_uid ); + + TagList::const_iterator it = vcard->children().begin(); + for( ; it != vcard->children().end(); ++it ) + { + const Tag& tag = *(*it); + if( tag.name() == "N" ) + { + m_N = true; + const Tag * child = tag.findChild( "FAMILY" ); + if( child ) + m_name.family = child->cdata(); + if( ( child = tag.findChild( "GIVEN" ) ) ) + m_name.given = child->cdata(); + if( ( child = tag.findChild( "MIDDLE" ) ) ) + m_name.middle = child->cdata(); + if( ( child = tag.findChild( "PREFIX" ) ) ) + m_name.prefix = child->cdata(); + if( ( child = tag.findChild( "SUFFIX" ) ) ) + m_name.suffix = child->cdata(); + } + else if( tag.name() == "PHOTO" ) + { + if( tag.hasChild( "EXTVAL" ) ) + { + m_photo.extval = tag.findChild( "EXTVAL" )->cdata(); + m_PHOTO = true; + } + else if( tag.hasChild( "TYPE" ) && tag.hasChild( "BINVAL" ) ) + { + std::string binval = tag.findChild( "BINVAL" )->cdata(); + std::string::size_type pos = 0; + while( ( pos = binval.find( '\n' ) ) != std::string::npos ) + binval.erase( pos, 1 ); + m_photo.type = tag.findChild( "TYPE" )->cdata(); + m_photo.binval = Base64::decode64( binval ); + m_PHOTO = true; + } + } + else if( tag.name() == "LOGO" ) + { + if( tag.hasChild( "EXTVAL" ) ) + { + m_logo.extval = tag.findChild( "EXTVAL" )->cdata(); + m_LOGO = true; + } + else if( tag.hasChild( "TYPE" ) && tag.hasChild( "BINVAL" ) ) + { + std::string binval = tag.findChild( "BINVAL" )->cdata(); + std::string::size_type pos = 0; + while( ( pos = binval.find( '\n' ) ) != std::string::npos ) + binval.erase( pos, 1 ); + m_logo.type = tag.findChild( "TYPE" )->cdata(); + m_logo.binval = Base64::decode64( binval ); + m_LOGO = true; + } + } + else if( tag.name() == "EMAIL" && tag.hasChild( "USERID" ) ) + { + Email item; + item.userid = tag.findChild( "USERID" )->cdata(); + item.internet = tag.hasChild( "INTERNET" ); + item.x400 = tag.hasChild( "X400" ); + item.work = tag.hasChild( "WORK" ); + item.home = tag.hasChild( "HOME" ); + item.pref = tag.hasChild( "PREF" ); + m_emailList.push_back( item ); + } + else if( tag.name() == "ADR" ) + { + Address item; + checkField( &tag, "POBOX", item.pobox ); + checkField( &tag, "EXTADD", item.extadd ); + checkField( &tag, "STREET", item.street ); + checkField( &tag, "LOCALITY", item.locality ); + checkField( &tag, "REGION", item.region ); + checkField( &tag, "PCODE", item.pcode ); + checkField( &tag, "CTRY", item.ctry ); + item.postal = tag.hasChild( "POSTAL" ); + item.parcel = tag.hasChild( "PARCEL" ); + item.work = tag.hasChild( "WORK" ); + item.home = tag.hasChild( "HOME" ); + item.pref = tag.hasChild( "PREF" ); + item.dom = tag.hasChild( "DOM" ); + item.intl = !item.dom && tag.hasChild( "INTL" ); + m_addressList.push_back( item ); + } + else if( tag.name() == "LABEL" ) + { + Label item; + TagList::const_iterator it2 = tag.children().begin(); + for( ; it2 != tag.children().end(); ++it2 ) + { + if( (*it2)->name() == "LINE" ) + item.lines.push_back( (*it)->cdata() ); + item.postal = (*it2)->name() == "POSTAL"; + item.parcel = (*it2)->name() == "PARCEL"; + item.work = (*it2)->name() == "WORK"; + item.home = (*it2)->name() == "HOME"; + item.pref = (*it2)->name() == "PREF"; + item.dom = (*it2)->name() == "DOM"; + item.intl = !item.dom && (*it2)->name() == "INTL"; + } + m_labelList.push_back( item ); + } + else if( tag.name() == "TEL" && tag.hasChild( "NUMBER" ) ) + { + Telephone item; + item.number = tag.findChild( "NUMBER" )->cdata(); + item.work = tag.hasChild( "WORK" ); + item.home = tag.hasChild( "HOME" ); + item.voice = tag.hasChild( "VOICE" ); + item.fax = tag.hasChild( "FAX" ); + item.pager = tag.hasChild( "PAGER" ); + item.msg = tag.hasChild( "MSG" ); + item.cell = tag.hasChild( "CELL" ); + item.video = tag.hasChild( "VIDEO" ); + item.bbs = tag.hasChild( "BBS" ); + item.modem = tag.hasChild( "MODEM" ); + item.isdn = tag.hasChild( "ISDN" ); + item.pcs = tag.hasChild( "PCS" ); + item.pref = tag.hasChild( "PREF" ); + m_telephoneList.push_back( item ); + } + else if( tag.name() == "ORG" ) + { + TagList::const_iterator ito = tag.children().begin(); + for( ; ito != tag.children().end(); ++ito ) + { + if( (*ito)->name() == "ORGNAME" ) + m_org.name = (*ito)->cdata(); + else if( (*ito)->name() == "ORGUNIT" ) + m_org.units.push_back( (*ito)->cdata() ); + } + } + else if( tag.name() == "GEO" ) + { + checkField( &tag, "LON", m_geo.longitude ); + checkField( &tag, "LAT", m_geo.latitude ); + } + else if( tag.name() == "CLASS" ) + { + if( tag.hasChild( "PRIVATE" ) ) + m_class = ClassPrivate; + else if( tag.hasChild( "PUBLIC" ) ) + m_class = ClassPublic; + else if( tag.hasChild( "CONFIDENTIAL" ) ) + m_class = ClassConfidential; + } + + } + + } + + void VCard::setName( const std::string& family, const std::string& given, + const std::string& middle, const std::string& prefix, + const std::string& suffix ) + { + m_name.family = family; + m_name.given = given; + m_name.middle = middle; + m_name.prefix = prefix; + m_name.suffix = suffix; + m_N = true; + } + + void VCard::setPhoto( const std::string& extval ) + { + if( !extval.empty() ) + { + m_photo.extval= extval; + m_PHOTO = true; + } + } + + void VCard::setPhoto( const std::string& type, const std::string& binval ) + { + if( !type.empty() && !binval.empty() ) + { + m_photo.type = type; + m_photo.binval = binval; + m_PHOTO = true; + } + else + { + m_photo.type = EmptyString; + m_photo.binval = EmptyString; + m_photo.extval = EmptyString; + m_PHOTO = false; + } + } + + void VCard::setLogo( const std::string& extval ) + { + if( !extval.empty() ) + { + m_logo.extval = extval; + m_LOGO = true; + } + } + + void VCard::setLogo( const std::string& type, const std::string& binval ) + { + if( !type.empty() && !binval.empty() ) + { + m_logo.type = type; + m_logo.binval = binval; + m_LOGO = true; + } + else + { + m_logo.type = EmptyString; + m_logo.binval = EmptyString; + m_logo.extval = EmptyString; + m_LOGO = false; + } + } + + void VCard::addEmail( const std::string& userid, int type ) + { + if( userid.empty() ) + return; + + Email item; + item.userid = userid; + item.internet = ((type & AddrTypeInet) == AddrTypeInet); + item.x400 = ((type & AddrTypeX400) == AddrTypeX400); + item.work = ((type & AddrTypeWork) == AddrTypeWork); + item.home = ((type & AddrTypeHome) == AddrTypeHome); + item.pref = ((type & AddrTypePref) == AddrTypePref); + + m_emailList.push_back( item ); + } + + void VCard::addAddress( const std::string& pobox, const std::string& extadd, + const std::string& street, const std::string& locality, + const std::string& region, const std::string& pcode, + const std::string& ctry, int type ) + { + if( pobox.empty() && extadd.empty() && street.empty() && + locality.empty() && region.empty() && pcode.empty() && ctry.empty() ) + return; + + Address item; + item.pobox = pobox; + item.extadd = extadd; + item.street = street; + item.locality = locality; + item.region = region; + item.pcode = pcode; + item.ctry = ctry; + item.home = ((type & AddrTypeHome) == AddrTypeHome); + item.work = ((type & AddrTypeWork) == AddrTypeWork); + item.parcel = ((type & AddrTypeParcel) == AddrTypeParcel); + item.postal = ((type & AddrTypePostal) == AddrTypePostal); + item.dom = ((type & AddrTypeDom) == AddrTypeDom); + item.intl = !item.dom && ((type & AddrTypeIntl) == AddrTypeIntl); + item.pref = ((type & AddrTypePref) == AddrTypePref); + + m_addressList.push_back( item ); + } + + void VCard::addLabel( const StringList& lines, int type ) + { + if( lines.empty() ) + return; + + Label item; + item.lines = lines; + item.work = ((type & AddrTypeWork) == AddrTypeWork); + item.home = ((type & AddrTypeHome) == AddrTypeHome); + item.postal = ((type & AddrTypePostal) == AddrTypePostal); + item.parcel = ((type & AddrTypeParcel) == AddrTypeParcel); + item.pref = ((type & AddrTypePref) == AddrTypePref); + item.dom = ((type & AddrTypeDom) == AddrTypeDom); + item.intl = !item.dom && ((type & AddrTypeIntl) == AddrTypeIntl); + + m_labelList.push_back( item ); + } + + void VCard::addTelephone( const std::string& number, int type ) + { + if( number.empty() ) + return; + + Telephone item; + item.number = number; + item.work = ((type & AddrTypeWork) == AddrTypeWork); + item.home = ((type & AddrTypeHome) == AddrTypeHome); + item.voice = ((type & AddrTypeVoice) == AddrTypeVoice); + item.fax = ((type & AddrTypeFax) == AddrTypeFax); + item.pager = ((type & AddrTypePager) == AddrTypePager); + item.msg = ((type & AddrTypeMsg) == AddrTypeMsg); + item.cell = ((type & AddrTypeCell) == AddrTypeCell); + item.video = ((type & AddrTypeVideo) == AddrTypeVideo); + item.bbs = ((type & AddrTypeBbs) == AddrTypeBbs); + item.modem = ((type & AddrTypeModem) == AddrTypeModem); + item.isdn = ((type & AddrTypeIsdn) == AddrTypeIsdn); + item.pcs = ((type & AddrTypePcs) == AddrTypePcs); + item.pref = ((type & AddrTypePref) == AddrTypePref); + + m_telephoneList.push_back( item ); + } + + void VCard::setGeo( const std::string& lat, const std::string& lon ) + { + if( !lat.empty() && !lon.empty() ) + { + m_geo.latitude = lat; + m_geo.longitude = lon; + } + } + + void VCard::setOrganization( const std::string& orgname, const StringList& orgunits ) + { + if( !orgname.empty() ) + { + m_org.name = orgname; + m_org.units = orgunits; + } + } + + const std::string& VCard::filterString() const + { + static const std::string filter = "/iq/vCard[@xmlns='" + XMLNS_VCARD_TEMP + "']"; + return filter; + } + + Tag* VCard::tag() const + { + Tag* v = new Tag( "vCard" ); + v->setXmlns( XMLNS_VCARD_TEMP ); + + if( !m_valid ) + return v; + + v->addAttribute( "version", "3.0" ); + + insertField( v, "FN", m_formattedname ); + insertField( v, "NICKNAME", m_nickname ); + insertField( v, "URL", m_url ); + insertField( v, "BDAY", m_bday ); + insertField( v, "JABBERID", m_jabberid ); + insertField( v, "TITLE", m_title ); + insertField( v, "ROLE", m_role ); + insertField( v, "NOTE", m_note ); + insertField( v, "DESC", m_desc ); + insertField( v, "MAILER", m_mailer ); + insertField( v, "TZ", m_tz ); + insertField( v, "REV", m_rev ); + insertField( v, "SORT_STRING", m_sortstring ); + insertField( v, "UID", m_uid ); + + if( m_N ) + { + Tag* n = new Tag( v, "N" ); + insertField( n, "FAMILY", m_name.family ); + insertField( n, "GIVEN", m_name.given ); + insertField( n, "MIDDLE", m_name.middle ); + insertField( n, "PREFIX", m_name.prefix ); + insertField( n, "SUFFIX", m_name.suffix ); + } + + if( m_PHOTO ) + { + Tag* p = new Tag( v, "PHOTO" ); + if( !m_photo.extval.empty() ) + { + new Tag( p, "EXTVAL", m_photo.extval ); + } + else if( !m_photo.type.empty() && !m_photo.binval.empty() ) + { + new Tag( p, "TYPE", m_photo.type ); + new Tag( p, "BINVAL", Base64::encode64( m_photo.binval ) ); + } + } + + if( m_LOGO ) + { + Tag* l = new Tag( v, "LOGO" ); + if( !m_logo.extval.empty() ) + { + new Tag( l, "EXTVAL", m_logo.extval ); + } + else if( !m_logo.type.empty() && !m_logo.binval.empty() ) + { + new Tag( l, "TYPE", m_logo.type ); + new Tag( l, "BINVAL", Base64::encode64( m_logo.binval ) ); + } + } + + EmailList::const_iterator ite = m_emailList.begin(); + for( ; ite != m_emailList.end(); ++ite ) + { + Tag* e = new Tag( v, "EMAIL" ); + insertField( e, "INTERNET", (*ite).internet ); + insertField( e, "WORK", (*ite).work ); + insertField( e, "HOME", (*ite).home ); + insertField( e, "X400", (*ite).x400 ); + insertField( e, "PREF", (*ite).pref ); + insertField( e, "USERID", (*ite).userid ); + } + + AddressList::const_iterator ita = m_addressList.begin(); + for( ; ita != m_addressList.end(); ++ita ) + { + Tag* a = new Tag( v, "ADR" ); + insertField( a, "POSTAL", (*ita).postal ); + insertField( a, "PARCEL", (*ita).parcel ); + insertField( a, "HOME", (*ita).home ); + insertField( a, "WORK", (*ita).work ); + insertField( a, "PREF", (*ita).pref ); + insertField( a, "DOM", (*ita).dom ); + if( !(*ita).dom ) + insertField( a, "INTL", (*ita).intl ); + + insertField( a, "POBOX", (*ita).pobox ); + insertField( a, "EXTADD", (*ita).extadd ); + insertField( a, "STREET", (*ita).street ); + insertField( a, "LOCALITY", (*ita).locality ); + insertField( a, "REGION", (*ita).region ); + insertField( a, "PCODE", (*ita).pcode ); + insertField( a, "CTRY", (*ita).ctry ); + } + + TelephoneList::const_iterator itt = m_telephoneList.begin(); + for( ; itt != m_telephoneList.end(); ++itt ) + { + Tag* t = new Tag( v, "TEL" ); + insertField( t, "NUMBER", (*itt).number ); + insertField( t, "HOME", (*itt).home ); + insertField( t, "WORK", (*itt).work ); + insertField( t, "VOICE", (*itt).voice ); + insertField( t, "FAX", (*itt).fax ); + insertField( t, "PAGER", (*itt).pager ); + insertField( t, "MSG", (*itt).msg ); + insertField( t, "CELL", (*itt).cell ); + insertField( t, "VIDEO", (*itt).video ); + insertField( t, "BBS", (*itt).bbs ); + insertField( t, "MODEM", (*itt).modem ); + insertField( t, "ISDN", (*itt).isdn ); + insertField( t, "PCS", (*itt).pcs ); + insertField( t, "PREF", (*itt).pref ); + } + + if( !m_geo.latitude.empty() && !m_geo.longitude.empty() ) + { + Tag* g = new Tag( v, "GEO" ); + new Tag( g, "LAT", m_geo.latitude ); + new Tag( g, "LON", m_geo.longitude ); + } + + if( !m_org.name.empty() ) + { + Tag* o = new Tag( v, "ORG" ); + new Tag( o, "ORGNAME", m_org.name ); + StringList::const_iterator ito = m_org.units.begin(); + for( ; ito != m_org.units.end(); ++ito ) + new Tag( o, "ORGUNIT", (*ito) ); + } + + if( m_class != ClassNone ) + { + Tag* c = new Tag( v, "CLASS" ); + switch( m_class ) + { + case ClassPublic: + new Tag( c, "PUBLIC" ); + break; + case ClassPrivate: + new Tag( c, "PRIVATE" ); + break; + case ClassConfidential: + new Tag( c, "CONFIDENTIAL" ); + break; + default: + break; + } + } + + return v; + } +} diff --git a/libs/libgloox/vcard.h b/libs/libgloox/vcard.h new file mode 100644 index 0000000..3d28f19 --- /dev/null +++ b/libs/libgloox/vcard.h @@ -0,0 +1,627 @@ +/* + Copyright (c) 2006-2009 by Jakob Schroeter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef VCARD_H__ +#define VCARD_H__ + +#include "gloox.h" +#include "stanzaextension.h" + +namespace gloox +{ + + class Tag; + + /** + * @brief A VCard abstraction. + * + * See @link gloox::VCardManager VCardManager @endlink for info on how to + * fetch VCards. + * + * @author Jakob Schroeter + * @since 0.8 + */ + class GLOOX_API VCard : public StanzaExtension + { + public: + /** + * Addressing type indicators. + * @note @c AddrTypeDom and @c AddrTypeIntl are mutually exclusive. If both are present, + * @c AddrTypeDom takes precendence. + * @note Also note that not all adress types are applicable everywhere. For example, + * @c AddrTypeIsdn does not make sense for a postal address. Check XEP-0054 + * for details. + */ + enum AddressType + { + AddrTypeHome = 1, /**< Home address. */ + AddrTypeWork = 2, /**< Work address. */ + AddrTypePref = 4, /**< Preferred address. */ + AddrTypeX400 = 8, /**< X.400 address. */ + AddrTypeInet = 16, /**< Internet address. */ + AddrTypeParcel = 32, /**< Parcel address. */ + AddrTypePostal = 64, /**< Postal address. */ + AddrTypeDom = 128, /**< Domestic(?) address. */ + AddrTypeIntl = 256, /**< International(?) address. */ + AddrTypeVoice = 512, /**< Voice number. */ + AddrTypeFax = 1024, /**< Fax number. */ + AddrTypePager = 2048, /**< Pager. */ + AddrTypeMsg = 4096, /**< MSG(?) */ + AddrTypeCell = 8192, /**< Cell phone number. */ + AddrTypeVideo = 16384, /**< Video chat(?). */ + AddrTypeBbs = 32768, /**< BBS. */ + AddrTypeModem = 65536, /**< Modem. */ + AddrTypeIsdn = 131072, /**< ISDN. */ + AddrTypePcs = 262144 /**< PCS. */ + }; + + /** + * A person's full name. + */ + struct Name + { + std::string family; /**< Family name. */ + std::string given; /**< Given name. */ + std::string middle; /**< Middle name. */ + std::string prefix; /**< Name prefix. */ + std::string suffix; /**< Name suffix. */ + }; + + /** + * Classifies the VCard. + */ + enum VCardClassification + { + ClassNone = 0, /**< Not classified. */ + ClassPublic = 1, /**< Public. */ + ClassPrivate = 2, /**< Private. */ + ClassConfidential = 4 /**< Confidential. */ + }; + + /** + * Describes an email field. + */ + struct Email + { + std::string userid; /**< Email address. */ + bool home; /**< Whether this is a personal address. */ + bool work; /**< Whether this is a work address. */ + bool internet; /**< Whether this is an internet address(?). */ + bool pref; /**< Whether this is the preferred address. */ + bool x400; /**< Whether this is an X.400 address. */ + }; + + /** + * A list of email fields. + */ + typedef std::list EmailList; + + /** + * Describes a telephone number entry. + */ + struct Telephone + { + std::string number; /**< The phone number. */ + bool home; /**< Whether this is a personal number. */ + bool work; /**< Whether this is a work number. */ + bool voice; /**< Whether this is a voice number. */ + bool fax; /**< Whether this is a fax number. */ + bool pager; /**< Whether this is a pager. */ + bool msg; /**< MSG(?) */ + bool cell; /**< Whether this is a cell phone. */ + bool video; /**< Whether this is a video chat(?). */ + bool bbs; /**< Whether this is a BBS. */ + bool modem; /**< Whether this is a modem. */ + bool isdn; /**< Whether this is a ISDN line(?) */ + bool pcs; /**< PCS(?) */ + bool pref; /**< Whether this is the preferred number. */ + }; + + /** + * A list of telephone entries. + */ + typedef std::list TelephoneList; + + /** + * Describes an address entry. + */ + struct Address + { + std::string pobox; /**< Pobox. */ + std::string extadd; /**< Extended address. */ + std::string street; /**< Street. */ + std::string locality; /**< Locality. */ + std::string region; /**< Region. */ + std::string pcode; /**< Postal code. */ + std::string ctry; /**< Country. */ + bool home; /**< Whether this is a personal address. */ + bool work; /**< Whether this is a work address. */ + bool postal; /**< Whether this is a postal address(?). */ + bool parcel; /**< Whether this is a arcel address(?). */ + bool pref; /**< Whether this is the preferred address. */ + bool dom; /**< Whether this is a domestic(?) address. */ + bool intl; /**< Whether this is an international(?) address. */ + }; + + /** + * Describes an address label. + */ + struct Label + { + StringList lines; /**< A list of lines. */ + bool home; /**< Whether this is a personal address. */ + bool work; /**< Whether this is a work address. */ + bool postal; /**< Whether this is a postal address(?). */ + bool parcel; /**< Whether this is a arcel address(?). */ + bool pref; /**< Whether this is the preferred address. */ + bool dom; /**< Whether this is a domestic(?) address. */ + bool intl; /**< Whether this is an international(?) address. */ + }; + + /** + * Describes geo information. + */ + struct Geo + { + std::string latitude; /**< Longitude. */ + std::string longitude; /**< Latitude. */ + }; + + /** + * Describes organization information. + */ + struct Org + { + std::string name; /**< The organizations name. */ + StringList units; /**< A list of units in the organization + * (the VCard's owner belongs to?). */ + }; + + /** + * Describes photo/logo information. + */ + struct Photo + { + std::string extval; /**< The photo is not stored inside the VCard. This is a hint (URL?) + * where to look for it. */ + std::string binval; /**< This is the photo (binary). */ + std::string type; /**< This is a hint at the mime-type. May be forged! */ + }; + + /** + * A list of address entries. + */ + typedef std::list
AddressList; + + /** + * A list of address labels. + */ + typedef std::list