cerberus 0.6 → 0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Authors.txt +4 -0
- data/Changelog.txt +11 -0
- data/Copyright.txt +16 -0
- data/Rakefile +16 -25
- data/Readme.markdown +1 -3
- data/bin/cerberus +5 -0
- data/lib/cerberus/builder/bjam.rb +1 -3
- data/lib/cerberus/builder/maven2.rb +0 -1
- data/lib/cerberus/builder/rake.rb +1 -1
- data/lib/cerberus/builder/rspec.rb +0 -1
- data/lib/cerberus/builder/ruby_base.rb +0 -1
- data/lib/cerberus/component_lazy_loader.rb +6 -2
- data/lib/cerberus/config.example.yml +1 -0
- data/lib/cerberus/constants.rb +2 -2
- data/lib/cerberus/manager.rb +7 -1
- data/lib/cerberus/publisher/mail.rb +4 -4
- data/lib/cerberus/publisher/twitter.rb +0 -1
- data/lib/cerberus/scm/base.rb +19 -0
- data/lib/cerberus/scm/bzr.rb +62 -0
- data/lib/cerberus/scm/cvs.rb +2 -7
- data/lib/cerberus/scm/darcs.rb +2 -11
- data/lib/cerberus/scm/git.rb +6 -14
- data/lib/cerberus/scm/perforce.rb +4 -4
- data/lib/cerberus/scm/svn.rb +2 -7
- data/lib/cerberus/utils.rb +5 -0
- data/lib/vendor/irc/README +23 -0
- data/lib/vendor/irc/lib/IRC.rb +164 -0
- data/lib/vendor/irc/lib/IRCChannel.rb +33 -0
- data/lib/vendor/irc/lib/IRCConnection.rb +134 -0
- data/lib/vendor/irc/lib/IRCEvent.rb +91 -0
- data/lib/vendor/irc/lib/IRCUser.rb +23 -0
- data/lib/vendor/irc/lib/IRCUtil.rb +49 -0
- data/lib/vendor/irc/lib/eventmap.yml +247 -0
- data/lib/vendor/twitter/CHANGES +129 -0
- data/lib/vendor/twitter/MIT-LICENSE +20 -0
- data/lib/vendor/twitter/README +37 -0
- data/lib/vendor/twitter/TODO +7 -0
- data/lib/vendor/twitter/lib/twitter.rb +34 -0
- data/lib/vendor/twitter/lib/twitter/client.rb +24 -0
- data/lib/vendor/twitter/lib/twitter/client/account.rb +24 -0
- data/lib/vendor/twitter/lib/twitter/client/auth.rb +27 -0
- data/lib/vendor/twitter/lib/twitter/client/base.rb +93 -0
- data/lib/vendor/twitter/lib/twitter/client/blocks.rb +35 -0
- data/lib/vendor/twitter/lib/twitter/client/favorites.rb +53 -0
- data/lib/vendor/twitter/lib/twitter/client/friendship.rb +35 -0
- data/lib/vendor/twitter/lib/twitter/client/graph.rb +37 -0
- data/lib/vendor/twitter/lib/twitter/client/messaging.rb +79 -0
- data/lib/vendor/twitter/lib/twitter/client/profile.rb +29 -0
- data/lib/vendor/twitter/lib/twitter/client/search.rb +27 -0
- data/lib/vendor/twitter/lib/twitter/client/status.rb +51 -0
- data/lib/vendor/twitter/lib/twitter/client/timeline.rb +72 -0
- data/lib/vendor/twitter/lib/twitter/client/user.rb +65 -0
- data/lib/vendor/twitter/lib/twitter/config.rb +77 -0
- data/lib/vendor/twitter/lib/twitter/console.rb +31 -0
- data/lib/vendor/twitter/lib/twitter/core.rb +137 -0
- data/lib/vendor/twitter/lib/twitter/ext.rb +2 -0
- data/lib/vendor/twitter/lib/twitter/ext/stdlib.rb +52 -0
- data/lib/vendor/twitter/lib/twitter/extras.rb +39 -0
- data/lib/vendor/twitter/lib/twitter/meta.rb +56 -0
- data/lib/vendor/twitter/lib/twitter/model.rb +360 -0
- data/lib/vendor/twitter/lib/twitter/version.rb +19 -0
- data/lib/vendor/twitter/spec/twitter/client/account_spec.rb +28 -0
- data/lib/vendor/twitter/spec/twitter/client/auth_spec.rb +34 -0
- data/lib/vendor/twitter/spec/twitter/client/base_spec.rb +242 -0
- data/lib/vendor/twitter/spec/twitter/client/blocks_spec.rb +76 -0
- data/lib/vendor/twitter/spec/twitter/client/favorites_spec.rb +183 -0
- data/lib/vendor/twitter/spec/twitter/client/friendship_spec.rb +76 -0
- data/lib/vendor/twitter/spec/twitter/client/graph_spec.rb +67 -0
- data/lib/vendor/twitter/spec/twitter/client/messaging_spec.rb +135 -0
- data/lib/vendor/twitter/spec/twitter/client/profile_spec.rb +91 -0
- data/lib/vendor/twitter/spec/twitter/client/search_spec.rb +68 -0
- data/lib/vendor/twitter/spec/twitter/client/status_spec.rb +119 -0
- data/lib/vendor/twitter/spec/twitter/client/timeline_spec.rb +79 -0
- data/lib/vendor/twitter/spec/twitter/client/user_spec.rb +203 -0
- data/lib/vendor/twitter/spec/twitter/client_spec.rb +2 -0
- data/lib/vendor/twitter/spec/twitter/config_spec.rb +86 -0
- data/lib/vendor/twitter/spec/twitter/console_spec.rb +15 -0
- data/lib/vendor/twitter/spec/twitter/core_spec.rb +127 -0
- data/lib/vendor/twitter/spec/twitter/ext/stdlib_spec.rb +59 -0
- data/lib/vendor/twitter/spec/twitter/extras_spec.rb +46 -0
- data/lib/vendor/twitter/spec/twitter/meta_spec.rb +90 -0
- data/lib/vendor/twitter/spec/twitter/model_spec.rb +508 -0
- data/lib/vendor/twitter/spec/twitter/version_spec.rb +19 -0
- data/lib/vendor/xmpp4r/CHANGELOG +83 -0
- data/lib/vendor/xmpp4r/COPYING +340 -0
- data/lib/vendor/xmpp4r/LICENSE +59 -0
- data/lib/vendor/xmpp4r/README.rdoc +110 -0
- data/lib/vendor/xmpp4r/README_ruby19.txt +43 -0
- data/lib/vendor/xmpp4r/Rakefile +262 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/advanced/adventure/README +56 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/advanced/adventure/adventure.rb +23 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/advanced/adventure/adventuremuc.rb +136 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/advanced/adventure/cube.xml +15 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/advanced/adventure/tower.xml +69 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/advanced/adventure/world.rb +424 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/advanced/fileserve.conf +11 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/advanced/fileserve.rb +346 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/advanced/getonline.rb +56 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/advanced/gtkmucclient.rb +315 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/advanced/migrate.rb +88 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/advanced/minimuc.rb +266 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/advanced/pep-aggregator/index.xsl +235 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/advanced/pep-aggregator/pep-aggregator.rb +147 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/advanced/recvfile.rb +84 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/advanced/rosterdiscovery.rb +129 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/advanced/sendfile.conf +10 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/advanced/sendfile.rb +72 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr.rb +51 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr_jabber.rb +43 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr_test.rb +10 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/advanced/versionpoll.rb +90 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/advanced/xmpping.rb +146 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/advanced/xmppingrc.sample +14 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/basic/change_password.rb +41 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/basic/client.rb +70 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/basic/component.rb +11 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/basic/echo.rb +37 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/basic/jabbersend.rb +41 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/basic/mass_sender.rb +68 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/basic/muc_owner_config.rb +12 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/basic/mucinfo.rb +41 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/basic/mucsimplebot.rb +82 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/basic/register.rb +42 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/basic/remove_registration.rb +18 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/basic/roster.rb +44 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/basic/rosterprint.rb +50 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/basic/rosterrename.rb +34 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/basic/rosterwatch.rb +171 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/basic/send_vcard.rb +67 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/basic/tune_client.rb +56 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/basic/tune_server.rb +58 -0
- data/lib/vendor/xmpp4r/data/doc/xmpp4r/examples/basic/versionbot.rb +75 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r.rb +116 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/base64.rb +32 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/bytestreams.rb +15 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/bytestreams/helper/filetransfer.rb +319 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/bytestreams/helper/ibb/base.rb +257 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/bytestreams/helper/ibb/initiator.rb +31 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/bytestreams/helper/ibb/target.rb +47 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/bytestreams/helper/socks5bytestreams/base.rb +152 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/bytestreams/helper/socks5bytestreams/initiator.rb +86 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/bytestreams/helper/socks5bytestreams/server.rb +198 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/bytestreams/helper/socks5bytestreams/socks5.rb +65 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb +73 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb.orig +62 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/bytestreams/iq/bytestreams.rb +170 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/bytestreams/iq/si.rb +206 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/callbacks.rb +124 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/caps.rb +1 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/caps/c.rb +53 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/caps/helper/generator.rb +160 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/caps/helper/helper.rb +87 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/client.rb +317 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/command/helper/responder.rb +53 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/command/iq/command.rb +154 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/component.rb +103 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/connection.rb +219 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/dataforms.rb +5 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/dataforms/x/data.rb +297 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/debuglog.rb +42 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/delay.rb +5 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/delay/x/delay.rb +99 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/discovery.rb +7 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/discovery/helper/responder.rb +165 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/discovery/iq/discoinfo.rb +211 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/discovery/iq/discoitems.rb +147 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/errors.rb +281 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/feature_negotiation.rb +5 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/feature_negotiation/iq/feature.rb +28 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/framework/base.rb +55 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/framework/bot.rb +148 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/httpbinding.rb +5 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/httpbinding/client.rb +285 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/idgenerator.rb +37 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/iq.rb +221 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/jid.rb +167 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/message.rb +148 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/muc.rb +14 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/muc/helper/mucbrowser.rb +92 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/muc/helper/mucclient.rb +462 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/muc/helper/simplemucclient.rb +332 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/muc/iq/mucadmin.rb +23 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/muc/iq/mucadminitem.rb +20 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/muc/iq/mucowner.rb +15 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/muc/item.rb +143 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/muc/x/muc.rb +70 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/muc/x/mucuserinvite.rb +60 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/muc/x/mucuseritem.rb +36 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/presence.rb +232 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/pubsub.rb +8 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/pubsub/children/configuration.rb +86 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/pubsub/children/event.rb +49 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/pubsub/children/item.rb +35 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/pubsub/children/items.rb +44 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/pubsub/children/node_config.rb +48 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/pubsub/children/publish.rb +24 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/pubsub/children/subscription.rb +62 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/pubsub/children/subscription_config.rb +67 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/pubsub/children/unsubscribe.rb +48 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/pubsub/helper/nodebrowser.rb +130 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/pubsub/helper/nodehelper.rb +156 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/pubsub/helper/servicehelper.rb +417 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/pubsub/iq/pubsub.rb +19 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/query.rb +15 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/rexmladdons.rb +151 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/roster.rb +7 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/roster/helper/roster.rb +519 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/roster/iq/roster.rb +215 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/roster/x/roster.rb +138 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/rpc.rb +2 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/rpc/helper/client.rb +123 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/rpc/helper/server.rb +74 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/rpc/helper/xmlrpcaddons.rb +67 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/rpc/iq/rpc.rb +23 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/sasl.rb +237 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/semaphore.rb +38 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/stream.rb +497 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/streamparser.rb +77 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/tune.rb +2 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/tune/helper/helper.rb +58 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/tune/tune.rb +113 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/vcard.rb +6 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/vcard/helper/vcard.rb +84 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/vcard/iq/vcard.rb +109 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/version.rb +7 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/version/helper/responder.rb +72 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/version/helper/simpleresponder.rb +44 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/version/iq/version.rb +105 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/x.rb +37 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/xhtml.rb +1 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/xhtml/html.rb +115 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/xmpp4r.rb +18 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/xmppelement.rb +168 -0
- data/lib/vendor/xmpp4r/lib/xmpp4r/xmppstanza.rb +162 -0
- data/lib/vendor/xmpp4r/setup.rb +1586 -0
- data/lib/vendor/xmpp4r/test/bytestreams/tc_ibb.rb +186 -0
- data/lib/vendor/xmpp4r/test/bytestreams/tc_socks5bytestreams.rb +113 -0
- data/lib/vendor/xmpp4r/test/caps/tc_helper.rb +156 -0
- data/lib/vendor/xmpp4r/test/dataforms/tc_data.rb +81 -0
- data/lib/vendor/xmpp4r/test/delay/tc_xdelay.rb +51 -0
- data/lib/vendor/xmpp4r/test/discovery/tc_responder.rb +91 -0
- data/lib/vendor/xmpp4r/test/lib/assert_equal_xml.rb +14 -0
- data/lib/vendor/xmpp4r/test/lib/clienttester.rb +120 -0
- data/lib/vendor/xmpp4r/test/muc/tc_muc_mucclient.rb +830 -0
- data/lib/vendor/xmpp4r/test/muc/tc_muc_simplemucclient.rb +114 -0
- data/lib/vendor/xmpp4r/test/muc/tc_mucowner.rb +50 -0
- data/lib/vendor/xmpp4r/test/pubsub/tc_helper.rb +662 -0
- data/lib/vendor/xmpp4r/test/pubsub/tc_nodeconfig.rb +54 -0
- data/lib/vendor/xmpp4r/test/pubsub/tc_subscriptionconfig.rb +41 -0
- data/lib/vendor/xmpp4r/test/roster/tc_helper.rb +514 -0
- data/lib/vendor/xmpp4r/test/roster/tc_iqqueryroster.rb +173 -0
- data/lib/vendor/xmpp4r/test/roster/tc_xroster.rb +73 -0
- data/lib/vendor/xmpp4r/test/rpc/tc_helper.rb +96 -0
- data/lib/vendor/xmpp4r/test/tc_callbacks.rb +129 -0
- data/lib/vendor/xmpp4r/test/tc_class_names.rb +146 -0
- data/lib/vendor/xmpp4r/test/tc_client.rb +30 -0
- data/lib/vendor/xmpp4r/test/tc_errors.rb +146 -0
- data/lib/vendor/xmpp4r/test/tc_idgenerator.rb +30 -0
- data/lib/vendor/xmpp4r/test/tc_iq.rb +113 -0
- data/lib/vendor/xmpp4r/test/tc_iqquery.rb +31 -0
- data/lib/vendor/xmpp4r/test/tc_jid.rb +204 -0
- data/lib/vendor/xmpp4r/test/tc_message.rb +116 -0
- data/lib/vendor/xmpp4r/test/tc_presence.rb +150 -0
- data/lib/vendor/xmpp4r/test/tc_rexml.rb +139 -0
- data/lib/vendor/xmpp4r/test/tc_stream.rb +229 -0
- data/lib/vendor/xmpp4r/test/tc_streamComponent.rb +95 -0
- data/lib/vendor/xmpp4r/test/tc_streamError.rb +131 -0
- data/lib/vendor/xmpp4r/test/tc_streamSend.rb +59 -0
- data/lib/vendor/xmpp4r/test/tc_streamparser.rb +112 -0
- data/lib/vendor/xmpp4r/test/tc_xmppstanza.rb +135 -0
- data/lib/vendor/xmpp4r/test/ts_xmpp4r.rb +53 -0
- data/lib/vendor/xmpp4r/test/tune/tc_helper_recv.rb +84 -0
- data/lib/vendor/xmpp4r/test/tune/tc_helper_send.rb +74 -0
- data/lib/vendor/xmpp4r/test/tune/tc_tune.rb +79 -0
- data/lib/vendor/xmpp4r/test/vcard/tc_helper.rb +49 -0
- data/lib/vendor/xmpp4r/test/vcard/tc_iqvcard.rb +62 -0
- data/lib/vendor/xmpp4r/test/version/tc_helper.rb +60 -0
- data/lib/vendor/xmpp4r/test/version/tc_iqqueryversion.rb +97 -0
- data/lib/vendor/xmpp4r/test/xhtml/tc_html.rb +41 -0
- data/lib/vendor/xmpp4r/tools/gen_requires.bash +31 -0
- data/lib/vendor/xmpp4r/tools/xmpp4r-gemspec-test.rb +11 -0
- data/lib/vendor/xmpp4r/xmpp4r.gemspec +291 -0
- data/test/jabber_publisher_test.rb +1 -1
- data/test/mail_publisher_test.rb +2 -2
- data/test/mock/twitter.rb +0 -1
- data/test/rspec_builder_test.rb +39 -2
- data/test/rss_publisher_test.rb +1 -1
- data/test/twitter_publisher_test.rb +1 -1
- metadata +268 -53
- data/lib/cerberus/publisher/gmailer.rb +0 -17
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
require 'xmlrpc/server'
|
|
2
|
+
require 'xmlrpc/parser'
|
|
3
|
+
require 'xmlrpc/create'
|
|
4
|
+
require 'xmlrpc/config'
|
|
5
|
+
require 'xmlrpc/utils' # ParserWriterChooseMixin
|
|
6
|
+
|
|
7
|
+
require 'xmpp4r/dataforms/x/data'
|
|
8
|
+
require 'xmpp4r/rpc/iq/rpc'
|
|
9
|
+
require 'xmpp4r/rpc/helper/xmlrpcaddons'
|
|
10
|
+
|
|
11
|
+
module Jabber
|
|
12
|
+
module RPC
|
|
13
|
+
|
|
14
|
+
##
|
|
15
|
+
# XMLRPC Server
|
|
16
|
+
class Server < XMLRPC::BasicServer
|
|
17
|
+
|
|
18
|
+
include XMLRPC::ParserWriterChooseMixin
|
|
19
|
+
include XMLRPC::ParseContentType
|
|
20
|
+
|
|
21
|
+
##
|
|
22
|
+
# new - creates a new server
|
|
23
|
+
#
|
|
24
|
+
def initialize(stream,class_delim=".")
|
|
25
|
+
super(class_delim)
|
|
26
|
+
@stream = stream
|
|
27
|
+
@stream.add_iq_callback(120,"Helpers::RPCServer") { |iq|
|
|
28
|
+
if iq.type == :set and iq.type != :result
|
|
29
|
+
handle_iq(iq)
|
|
30
|
+
true
|
|
31
|
+
else
|
|
32
|
+
false
|
|
33
|
+
end
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
# handles incoming iqs
|
|
39
|
+
# iq:: [Jabber::IQ] - the jabber iq
|
|
40
|
+
def handle_iq(iq)
|
|
41
|
+
if iq.type == :set
|
|
42
|
+
if iq.query.kind_of?(IqQueryRPC)
|
|
43
|
+
data = iq.query
|
|
44
|
+
response = IqQueryRPC.new
|
|
45
|
+
data.elements.each { |rpc|
|
|
46
|
+
if rpc
|
|
47
|
+
response.typed_add(handle_rpc_requests(rpc))
|
|
48
|
+
end
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
respiq = iq.answer(false)
|
|
52
|
+
respiq.type = :result
|
|
53
|
+
respiq.add(response)
|
|
54
|
+
@stream.send(respiq)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
##
|
|
62
|
+
# handles the rpc requests
|
|
63
|
+
# takes rpcdata:: [String]
|
|
64
|
+
def handle_rpc_requests(rpcdata)
|
|
65
|
+
resp = process(rpcdata.to_s)
|
|
66
|
+
if resp == nil or resp.size <= 0
|
|
67
|
+
raise Jabber::ErrorResponse.new(:forbidden)
|
|
68
|
+
else
|
|
69
|
+
return resp
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end # RPCServer
|
|
73
|
+
end # Helpers
|
|
74
|
+
end # Jabber
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
require "xmlrpc/parser"
|
|
2
|
+
require "xmlrpc/create"
|
|
3
|
+
require "xmlrpc/config"
|
|
4
|
+
require "xmlrpc/utils" # ParserWriterChooseMixin
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
module XMLRPC
|
|
8
|
+
class Create
|
|
9
|
+
|
|
10
|
+
# Avoids warnings
|
|
11
|
+
remove_method(:methodCall)
|
|
12
|
+
remove_method(:methodResponse)
|
|
13
|
+
|
|
14
|
+
##
|
|
15
|
+
# create a Method Call
|
|
16
|
+
# name:: [String] name of the method
|
|
17
|
+
# params:: [Array] params of the method as a array
|
|
18
|
+
def methodCall(name, *params)
|
|
19
|
+
name = name.to_s
|
|
20
|
+
|
|
21
|
+
if name !~ /[a-zA-Z0-9_.:\/]+/
|
|
22
|
+
raise ArgumentError, "Wrong XML-RPC method-name"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
parameter = params.collect { |param|
|
|
26
|
+
@writer.ele("param", conv2value(param))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
tree = @writer.document(
|
|
30
|
+
@writer.ele("methodCall",
|
|
31
|
+
@writer.tag("methodName", name),
|
|
32
|
+
@writer.ele("params", *parameter)
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
@writer.document_to_str(tree) + "\n"
|
|
37
|
+
end
|
|
38
|
+
##
|
|
39
|
+
# create a response to a method call
|
|
40
|
+
# is_ret:: [TrueClass] is this a return (true) or a error (false)
|
|
41
|
+
# params:: [Array] a array of params
|
|
42
|
+
|
|
43
|
+
def methodResponse(is_ret, *params)
|
|
44
|
+
|
|
45
|
+
if is_ret
|
|
46
|
+
begin
|
|
47
|
+
resp = params.collect do |param|
|
|
48
|
+
@writer.ele("param", conv2value(param))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
resp = [@writer.ele("params", *resp)]
|
|
52
|
+
rescue Exception => e
|
|
53
|
+
error = XMLRPC::FaultException.new(XMLRPC::BasicServer::ERR_UNCAUGHT_EXCEPTION, "Uncaught exception '#{e.message}' serialising params into response")
|
|
54
|
+
resp = @writer.ele("fault", conv2value(error.to_h))
|
|
55
|
+
end
|
|
56
|
+
else
|
|
57
|
+
if params.size != 1 or params[0] === XMLRPC::FaultException
|
|
58
|
+
raise ArgumentError, "no valid fault-structure given"
|
|
59
|
+
end
|
|
60
|
+
resp = @writer.ele("fault", conv2value(params[0].to_h))
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
tree = @writer.document(@writer.ele("methodResponse", resp))
|
|
64
|
+
@writer.document_to_str(tree) + "\n"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# =XMPP4R - XMPP Library for Ruby
|
|
2
|
+
# License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
|
|
3
|
+
# Website::http://home.gna.org/xmpp4r/
|
|
4
|
+
|
|
5
|
+
require 'xmpp4r/query'
|
|
6
|
+
|
|
7
|
+
module Jabber
|
|
8
|
+
module RPC
|
|
9
|
+
class IqQueryRPC < IqQuery
|
|
10
|
+
NS_RPC = 'jabber:iq:rpc'
|
|
11
|
+
name_xmlns 'query', NS_RPC
|
|
12
|
+
|
|
13
|
+
# TODO: Is typed_add with a String right here?
|
|
14
|
+
def typed_add(e)
|
|
15
|
+
if e.kind_of? String
|
|
16
|
+
typed_add(REXML::Document.new(e).root)
|
|
17
|
+
else
|
|
18
|
+
super
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# =XMPP4R - XMPP Library for Ruby
|
|
2
|
+
# License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
|
|
3
|
+
# Website::http://home.gna.org/xmpp4r/
|
|
4
|
+
|
|
5
|
+
require 'digest/md5'
|
|
6
|
+
require 'xmpp4r/base64'
|
|
7
|
+
|
|
8
|
+
module Jabber
|
|
9
|
+
##
|
|
10
|
+
# Helpers for SASL authentication (RFC2222)
|
|
11
|
+
#
|
|
12
|
+
# You might not need to use them directly, they are
|
|
13
|
+
# invoked by Jabber::Client#auth
|
|
14
|
+
module SASL
|
|
15
|
+
NS_SASL = 'urn:ietf:params:xml:ns:xmpp-sasl'
|
|
16
|
+
|
|
17
|
+
##
|
|
18
|
+
# Factory function to obtain a SASL helper for the specified mechanism
|
|
19
|
+
def SASL.new(stream, mechanism)
|
|
20
|
+
case mechanism
|
|
21
|
+
when 'DIGEST-MD5'
|
|
22
|
+
DigestMD5.new(stream)
|
|
23
|
+
when 'PLAIN'
|
|
24
|
+
Plain.new(stream)
|
|
25
|
+
when 'ANONYMOUS'
|
|
26
|
+
Anonymous.new(stream)
|
|
27
|
+
else
|
|
28
|
+
raise "Unknown SASL mechanism: #{mechanism}"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
##
|
|
33
|
+
# SASL mechanism base class (stub)
|
|
34
|
+
class Base
|
|
35
|
+
def initialize(stream)
|
|
36
|
+
@stream = stream
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def generate_auth(mechanism, text=nil)
|
|
42
|
+
auth = REXML::Element.new 'auth'
|
|
43
|
+
auth.add_namespace NS_SASL
|
|
44
|
+
auth.attributes['mechanism'] = mechanism
|
|
45
|
+
auth.text = text
|
|
46
|
+
auth
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def generate_nonce
|
|
50
|
+
Digest::MD5.hexdigest(Time.new.to_f.to_s)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
##
|
|
55
|
+
# SASL PLAIN authentication helper (RFC2595)
|
|
56
|
+
class Plain < Base
|
|
57
|
+
##
|
|
58
|
+
# Authenticate via sending password in clear-text
|
|
59
|
+
def auth(password)
|
|
60
|
+
auth_text = "#{@stream.jid.strip}\x00#{@stream.jid.node}\x00#{password}"
|
|
61
|
+
error = nil
|
|
62
|
+
@stream.send(generate_auth('PLAIN', Base64::encode64(auth_text).gsub(/\s/, ''))) { |reply|
|
|
63
|
+
if reply.name != 'success'
|
|
64
|
+
error = reply.first_element(nil).name
|
|
65
|
+
end
|
|
66
|
+
true
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
raise error if error
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
##
|
|
74
|
+
# SASL Anonymous authentication helper
|
|
75
|
+
class Anonymous < Base
|
|
76
|
+
##
|
|
77
|
+
# Authenticate by sending nothing with the ANONYMOUS token
|
|
78
|
+
def auth(password)
|
|
79
|
+
auth_text = "#{@stream.jid.node}"
|
|
80
|
+
error = nil
|
|
81
|
+
@stream.send(generate_auth('ANONYMOUS', Base64::encode64(auth_text).gsub(/\s/, ''))) { |reply|
|
|
82
|
+
if reply.name != 'success'
|
|
83
|
+
error = reply.first_element(nil).name
|
|
84
|
+
end
|
|
85
|
+
true
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
raise error if error
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
##
|
|
93
|
+
# SASL DIGEST-MD5 authentication helper (RFC2831)
|
|
94
|
+
class DigestMD5 < Base
|
|
95
|
+
##
|
|
96
|
+
# Sends the wished auth mechanism and wait for a challenge
|
|
97
|
+
#
|
|
98
|
+
# (proceed with DigestMD5#auth)
|
|
99
|
+
def initialize(stream)
|
|
100
|
+
super
|
|
101
|
+
|
|
102
|
+
challenge = {}
|
|
103
|
+
error = nil
|
|
104
|
+
@stream.send(generate_auth('DIGEST-MD5')) { |reply|
|
|
105
|
+
if reply.name == 'challenge' and reply.namespace == NS_SASL
|
|
106
|
+
challenge = decode_challenge(reply.text)
|
|
107
|
+
else
|
|
108
|
+
error = reply.first_element(nil).name
|
|
109
|
+
end
|
|
110
|
+
true
|
|
111
|
+
}
|
|
112
|
+
raise error if error
|
|
113
|
+
|
|
114
|
+
@nonce = challenge['nonce']
|
|
115
|
+
@realm = challenge['realm']
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def decode_challenge(challenge)
|
|
119
|
+
text = Base64::decode64(challenge)
|
|
120
|
+
res = {}
|
|
121
|
+
|
|
122
|
+
state = :key
|
|
123
|
+
key = ''
|
|
124
|
+
value = ''
|
|
125
|
+
|
|
126
|
+
text.scan(/./) do |ch|
|
|
127
|
+
if state == :key
|
|
128
|
+
if ch == '='
|
|
129
|
+
state = :value
|
|
130
|
+
else
|
|
131
|
+
key += ch
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
elsif state == :value
|
|
135
|
+
if ch == ','
|
|
136
|
+
res[key] = value
|
|
137
|
+
key = ''
|
|
138
|
+
value = ''
|
|
139
|
+
state = :key
|
|
140
|
+
elsif ch == '"' and value == ''
|
|
141
|
+
state = :quote
|
|
142
|
+
else
|
|
143
|
+
value += ch
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
elsif state == :quote
|
|
147
|
+
if ch == '"'
|
|
148
|
+
state = :value
|
|
149
|
+
else
|
|
150
|
+
value += ch
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
res[key] = value unless key == ''
|
|
155
|
+
|
|
156
|
+
Jabber::debuglog("SASL DIGEST-MD5 challenge:\n#{text.inspect}\n#{res.inspect}")
|
|
157
|
+
|
|
158
|
+
res
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
##
|
|
162
|
+
# * Send a response
|
|
163
|
+
# * Wait for the server's challenge (which aren't checked)
|
|
164
|
+
# * Send a blind response to the server's challenge
|
|
165
|
+
def auth(password)
|
|
166
|
+
response = {}
|
|
167
|
+
response['nonce'] = @nonce
|
|
168
|
+
response['charset'] = 'utf-8'
|
|
169
|
+
response['username'] = @stream.jid.node
|
|
170
|
+
response['realm'] = @realm || @stream.jid.domain
|
|
171
|
+
response['cnonce'] = generate_nonce
|
|
172
|
+
response['nc'] = '00000001'
|
|
173
|
+
response['qop'] = 'auth'
|
|
174
|
+
response['digest-uri'] = "xmpp/#{@stream.jid.domain}"
|
|
175
|
+
response['response'] = response_value(@stream.jid.node, @stream.jid.domain, response['digest-uri'], password, @nonce, response['cnonce'], response['qop'])
|
|
176
|
+
response.each { |key,value|
|
|
177
|
+
unless %w(nc qop response charset).include? key
|
|
178
|
+
response[key] = "\"#{value}\""
|
|
179
|
+
end
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
response_text = response.collect { |k,v| "#{k}=#{v}" }.join(',')
|
|
183
|
+
Jabber::debuglog("SASL DIGEST-MD5 response:\n#{response_text}")
|
|
184
|
+
|
|
185
|
+
r = REXML::Element.new('response')
|
|
186
|
+
r.add_namespace NS_SASL
|
|
187
|
+
r.text = Base64::encode64(response_text).gsub(/\s/, '')
|
|
188
|
+
|
|
189
|
+
success_already = false
|
|
190
|
+
error = nil
|
|
191
|
+
@stream.send(r) { |reply|
|
|
192
|
+
if reply.name == 'success'
|
|
193
|
+
success_already = true
|
|
194
|
+
elsif reply.name != 'challenge'
|
|
195
|
+
error = reply.first_element(nil).name
|
|
196
|
+
end
|
|
197
|
+
true
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return if success_already
|
|
201
|
+
raise error if error
|
|
202
|
+
|
|
203
|
+
# TODO: check the challenge from the server
|
|
204
|
+
|
|
205
|
+
r.text = nil
|
|
206
|
+
@stream.send(r) { |reply|
|
|
207
|
+
if reply.name != 'success'
|
|
208
|
+
error = reply.first_element(nil).name
|
|
209
|
+
end
|
|
210
|
+
true
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
raise error if error
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
private
|
|
217
|
+
|
|
218
|
+
##
|
|
219
|
+
# Function from RFC2831
|
|
220
|
+
def h(s); Digest::MD5.digest(s); end
|
|
221
|
+
##
|
|
222
|
+
# Function from RFC2831
|
|
223
|
+
def hh(s); Digest::MD5.hexdigest(s); end
|
|
224
|
+
|
|
225
|
+
##
|
|
226
|
+
# Calculate the value for the response field
|
|
227
|
+
def response_value(username, realm, digest_uri, passwd, nonce, cnonce, qop)
|
|
228
|
+
a1_h = h("#{username}:#{realm}:#{passwd}")
|
|
229
|
+
a1 = "#{a1_h}:#{nonce}:#{cnonce}"
|
|
230
|
+
#a2 = "AUTHENTICATE:#{digest_uri}#{(qop == 'auth') ? '' : ':00000000000000000000000000000000'}"
|
|
231
|
+
a2 = "AUTHENTICATE:#{digest_uri}"
|
|
232
|
+
|
|
233
|
+
hh("#{hh(a1)}:#{nonce}:00000001:#{cnonce}:#{qop}:#{hh(a2)}")
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# =XMPP4R - XMPP Library for Ruby
|
|
2
|
+
# License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
|
|
3
|
+
# Website::http://home.gna.org/xmpp4r/
|
|
4
|
+
|
|
5
|
+
module Jabber
|
|
6
|
+
##
|
|
7
|
+
# This class implements semaphore for threads synchronization.
|
|
8
|
+
class Semaphore
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
# Initialize new semaphore
|
|
12
|
+
#
|
|
13
|
+
# val:: [Integer] number of threads, that can enter to section
|
|
14
|
+
def initialize(val=0)
|
|
15
|
+
@tickets = val
|
|
16
|
+
@lock = Mutex.new
|
|
17
|
+
@cond = ConditionVariable.new
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
##
|
|
21
|
+
# Waits until are available some free tickets
|
|
22
|
+
def wait
|
|
23
|
+
@lock.synchronize {
|
|
24
|
+
@cond.wait(@lock) while !(@tickets > 0)
|
|
25
|
+
@tickets -= 1
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
##
|
|
30
|
+
# Unlocks guarded section, increments number of free tickets
|
|
31
|
+
def run
|
|
32
|
+
@lock.synchronize {
|
|
33
|
+
@tickets += 1
|
|
34
|
+
@cond.signal
|
|
35
|
+
}
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
# =XMPP4R - XMPP Library for Ruby
|
|
2
|
+
# License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
|
|
3
|
+
# Website::http://home.gna.org/xmpp4r/
|
|
4
|
+
|
|
5
|
+
require 'xmpp4r/callbacks'
|
|
6
|
+
require 'socket'
|
|
7
|
+
require 'thread'
|
|
8
|
+
require 'xmpp4r/semaphore'
|
|
9
|
+
require 'xmpp4r/streamparser'
|
|
10
|
+
require 'xmpp4r/presence'
|
|
11
|
+
require 'xmpp4r/message'
|
|
12
|
+
require 'xmpp4r/iq'
|
|
13
|
+
require 'xmpp4r/debuglog'
|
|
14
|
+
require 'xmpp4r/idgenerator'
|
|
15
|
+
|
|
16
|
+
module Jabber
|
|
17
|
+
##
|
|
18
|
+
# The stream class manages a connection stream (a file descriptor using which
|
|
19
|
+
# XML messages are read and sent)
|
|
20
|
+
#
|
|
21
|
+
# You may register callbacks for the three Jabber stanzas
|
|
22
|
+
# (message, presence and iq) and use the send and send_with_id
|
|
23
|
+
# methods.
|
|
24
|
+
#
|
|
25
|
+
# To ensure the order of received stanzas, callback blocks are
|
|
26
|
+
# launched in the parser thread. If further blocking operations
|
|
27
|
+
# are intended in those callbacks, run your own thread there.
|
|
28
|
+
class Stream
|
|
29
|
+
DISCONNECTED = 1
|
|
30
|
+
CONNECTED = 2
|
|
31
|
+
|
|
32
|
+
# file descriptor used
|
|
33
|
+
attr_reader :fd
|
|
34
|
+
|
|
35
|
+
# connection status
|
|
36
|
+
attr_reader :status
|
|
37
|
+
|
|
38
|
+
##
|
|
39
|
+
# Initialize a new stream
|
|
40
|
+
def initialize
|
|
41
|
+
@fd = nil
|
|
42
|
+
@status = DISCONNECTED
|
|
43
|
+
@xmlcbs = CallbackList.new
|
|
44
|
+
@stanzacbs = CallbackList.new
|
|
45
|
+
@messagecbs = CallbackList.new
|
|
46
|
+
@iqcbs = CallbackList.new
|
|
47
|
+
@presencecbs = CallbackList.new
|
|
48
|
+
@send_lock = Mutex.new
|
|
49
|
+
@last_send = Time.now
|
|
50
|
+
@exception_block = nil
|
|
51
|
+
@threadblocks = []
|
|
52
|
+
@wakeup_thread = nil
|
|
53
|
+
@streamid = nil
|
|
54
|
+
@streamns = 'jabber:client'
|
|
55
|
+
@features_sem = Semaphore.new
|
|
56
|
+
@parser_thread = nil
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
##
|
|
60
|
+
# Start the XML parser on the fd
|
|
61
|
+
def start(fd)
|
|
62
|
+
@stream_mechanisms = []
|
|
63
|
+
@stream_features = {}
|
|
64
|
+
|
|
65
|
+
@fd = fd
|
|
66
|
+
@parser = StreamParser.new(@fd, self)
|
|
67
|
+
@parser_thread = Thread.new do
|
|
68
|
+
Thread.current.abort_on_exception = true
|
|
69
|
+
begin
|
|
70
|
+
@parser.parse
|
|
71
|
+
Jabber::debuglog("DISCONNECTED\n")
|
|
72
|
+
|
|
73
|
+
if @exception_block
|
|
74
|
+
Thread.new { close!; @exception_block.call(nil, self, :disconnected) }
|
|
75
|
+
else
|
|
76
|
+
close!
|
|
77
|
+
end
|
|
78
|
+
rescue Exception => e
|
|
79
|
+
Jabber::debuglog("EXCEPTION:\n#{e.class}\n#{e.message}\n#{e.backtrace.join("\n")}")
|
|
80
|
+
|
|
81
|
+
if @exception_block
|
|
82
|
+
Thread.new do
|
|
83
|
+
Thread.current.abort_on_exception = true
|
|
84
|
+
close
|
|
85
|
+
@exception_block.call(e, self, :start)
|
|
86
|
+
end
|
|
87
|
+
else
|
|
88
|
+
Jabber::debuglog "Exception caught in Parser thread! (#{e.class})\n#{e.backtrace.join("\n")}"
|
|
89
|
+
close!
|
|
90
|
+
raise
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
@status = CONNECTED
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def stop
|
|
99
|
+
@parser_thread.kill
|
|
100
|
+
@parser = nil
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
##
|
|
104
|
+
# Mounts a block to handle exceptions if they occur during the
|
|
105
|
+
# poll send. This will likely be the first indication that
|
|
106
|
+
# the socket dropped in a Jabber Session.
|
|
107
|
+
#
|
|
108
|
+
# The block has to take three arguments:
|
|
109
|
+
# * the Exception
|
|
110
|
+
# * the Jabber::Stream object (self)
|
|
111
|
+
# * a symbol where it happened, namely :start, :parser, :sending and :end
|
|
112
|
+
def on_exception(&block)
|
|
113
|
+
@exception_block = block
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
##
|
|
117
|
+
# This method is called by the parser when a failure occurs
|
|
118
|
+
def parse_failure(e)
|
|
119
|
+
Jabber::debuglog("EXCEPTION:\n#{e.class}\n#{e.message}\n#{e.backtrace.join("\n")}")
|
|
120
|
+
|
|
121
|
+
# A new thread has to be created because close will cause the thread
|
|
122
|
+
# to commit suicide(???)
|
|
123
|
+
if @exception_block
|
|
124
|
+
# New thread, because close will kill the current thread
|
|
125
|
+
Thread.new do
|
|
126
|
+
Thread.current.abort_on_exception = true
|
|
127
|
+
close
|
|
128
|
+
@exception_block.call(e, self, :parser)
|
|
129
|
+
end
|
|
130
|
+
else
|
|
131
|
+
Jabber::debuglog "Stream#parse_failure was called by XML parser. Dumping " +
|
|
132
|
+
"backtrace...\n" + e.exception + "\n#{e.backtrace.join("\n")}"
|
|
133
|
+
close
|
|
134
|
+
raise
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
##
|
|
139
|
+
# This method is called by the parser upon receiving <tt></stream:stream></tt>
|
|
140
|
+
def parser_end
|
|
141
|
+
if @exception_block
|
|
142
|
+
Thread.new do
|
|
143
|
+
Thread.current.abort_on_exception = true
|
|
144
|
+
close
|
|
145
|
+
@exception_block.call(nil, self, :close)
|
|
146
|
+
end
|
|
147
|
+
else
|
|
148
|
+
close
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
##
|
|
153
|
+
# Returns if this connection is connected to a Jabber service
|
|
154
|
+
# return:: [Boolean] Connection status
|
|
155
|
+
def is_connected?
|
|
156
|
+
return @status == CONNECTED
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
##
|
|
160
|
+
# Returns if this connection is NOT connected to a Jabber service
|
|
161
|
+
#
|
|
162
|
+
# return:: [Boolean] Connection status
|
|
163
|
+
def is_disconnected?
|
|
164
|
+
return @status == DISCONNECTED
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
##
|
|
168
|
+
# Processes a received REXML::Element and executes
|
|
169
|
+
# registered thread blocks and filters against it.
|
|
170
|
+
#
|
|
171
|
+
# element:: [REXML::Element] The received element
|
|
172
|
+
def receive(element)
|
|
173
|
+
Jabber::debuglog("RECEIVED:\n#{element.to_s}")
|
|
174
|
+
|
|
175
|
+
if element.namespace('').to_s == '' # REXML namespaces are always strings
|
|
176
|
+
element.add_namespace(@streamns)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
case element.prefix
|
|
180
|
+
when 'stream'
|
|
181
|
+
case element.name
|
|
182
|
+
when 'stream'
|
|
183
|
+
stanza = element
|
|
184
|
+
@streamid = element.attributes['id']
|
|
185
|
+
@streamns = element.namespace('') if element.namespace('')
|
|
186
|
+
|
|
187
|
+
# Hack: component streams are basically client streams.
|
|
188
|
+
# Someday we may want to create special stanza classes
|
|
189
|
+
# for components/s2s deriving from normal stanzas but
|
|
190
|
+
# posessing these namespaces
|
|
191
|
+
@streamns = 'jabber:client' if @streamns == 'jabber:component:accept'
|
|
192
|
+
|
|
193
|
+
unless element.attributes['version'] # isn't XMPP compliant, so
|
|
194
|
+
Jabber::debuglog("FEATURES: server not XMPP compliant, will not wait for features")
|
|
195
|
+
@features_sem.run # don't wait for <stream:features/>
|
|
196
|
+
end
|
|
197
|
+
when 'features'
|
|
198
|
+
stanza = element
|
|
199
|
+
element.each { |e|
|
|
200
|
+
if e.name == 'mechanisms' and e.namespace == 'urn:ietf:params:xml:ns:xmpp-sasl'
|
|
201
|
+
e.each_element('mechanism') { |mech|
|
|
202
|
+
@stream_mechanisms.push(mech.text)
|
|
203
|
+
}
|
|
204
|
+
else
|
|
205
|
+
@stream_features[e.name] = e.namespace
|
|
206
|
+
end
|
|
207
|
+
}
|
|
208
|
+
Jabber::debuglog("FEATURES: received")
|
|
209
|
+
@features_sem.run
|
|
210
|
+
else
|
|
211
|
+
stanza = element
|
|
212
|
+
end
|
|
213
|
+
else
|
|
214
|
+
# Any stanza, classes are registered by XMPPElement::name_xmlns
|
|
215
|
+
begin
|
|
216
|
+
stanza = XMPPStanza::import(element)
|
|
217
|
+
rescue NoNameXmlnsRegistered
|
|
218
|
+
stanza = element
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Iterate through blocked threads (= waiting for an answer)
|
|
223
|
+
#
|
|
224
|
+
# We're dup'ping the @threadblocks here, so that we won't end up in an
|
|
225
|
+
# endless loop if Stream#send is being nested. That means, the nested
|
|
226
|
+
# threadblock won't receive the stanza currently processed, but the next
|
|
227
|
+
# one.
|
|
228
|
+
threadblocks = @threadblocks.dup
|
|
229
|
+
threadblocks.each { |threadblock|
|
|
230
|
+
exception = nil
|
|
231
|
+
r = false
|
|
232
|
+
begin
|
|
233
|
+
r = threadblock.call(stanza)
|
|
234
|
+
rescue Exception => e
|
|
235
|
+
exception = e
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
if r == true
|
|
239
|
+
@threadblocks.delete(threadblock)
|
|
240
|
+
threadblock.wakeup
|
|
241
|
+
return
|
|
242
|
+
elsif exception
|
|
243
|
+
@threadblocks.delete(threadblock)
|
|
244
|
+
threadblock.raise(exception)
|
|
245
|
+
end
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
Jabber::debuglog("PROCESSING:\n#{stanza.to_s} (#{stanza.class})")
|
|
249
|
+
return true if @xmlcbs.process(stanza)
|
|
250
|
+
return true if @stanzacbs.process(stanza)
|
|
251
|
+
case stanza
|
|
252
|
+
when Message
|
|
253
|
+
return true if @messagecbs.process(stanza)
|
|
254
|
+
when Iq
|
|
255
|
+
return true if @iqcbs.process(stanza)
|
|
256
|
+
when Presence
|
|
257
|
+
return true if @presencecbs.process(stanza)
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
##
|
|
262
|
+
# This is used by Jabber::Stream internally to
|
|
263
|
+
# keep track of any blocks which were passed to
|
|
264
|
+
# Stream#send.
|
|
265
|
+
class ThreadBlock
|
|
266
|
+
def initialize(block)
|
|
267
|
+
@block = block
|
|
268
|
+
@waiter = Semaphore.new
|
|
269
|
+
@exception = nil
|
|
270
|
+
end
|
|
271
|
+
def call(*args)
|
|
272
|
+
@block.call(*args)
|
|
273
|
+
end
|
|
274
|
+
def wait
|
|
275
|
+
@waiter.wait
|
|
276
|
+
raise @exception if @exception
|
|
277
|
+
end
|
|
278
|
+
def wakeup
|
|
279
|
+
# TODO: Handle threadblock removal if !alive?
|
|
280
|
+
@waiter.run
|
|
281
|
+
end
|
|
282
|
+
def raise(exception)
|
|
283
|
+
@exception = exception
|
|
284
|
+
@waiter.run
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def send_data(data)
|
|
289
|
+
@send_lock.synchronize do
|
|
290
|
+
@last_send = Time.now
|
|
291
|
+
@fd << data
|
|
292
|
+
@fd.flush
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
##
|
|
297
|
+
# Sends XML data to the socket and (optionally) waits
|
|
298
|
+
# to process received data.
|
|
299
|
+
#
|
|
300
|
+
# Do not invoke this in a callback but in a seperate thread
|
|
301
|
+
# because we may not suspend the parser-thread (in whose
|
|
302
|
+
# context callbacks are executed).
|
|
303
|
+
#
|
|
304
|
+
# xml:: [String] The xml data to send
|
|
305
|
+
# &block:: [Block] The optional block
|
|
306
|
+
def send(xml, &block)
|
|
307
|
+
Jabber::debuglog("SENDING:\n#{xml}")
|
|
308
|
+
@threadblocks.unshift(threadblock = ThreadBlock.new(block)) if block
|
|
309
|
+
begin
|
|
310
|
+
# Temporarily remove stanza's namespace to
|
|
311
|
+
# reduce bandwidth consumption
|
|
312
|
+
if xml.kind_of? XMPPStanza and xml.namespace == 'jabber:client'
|
|
313
|
+
xml.delete_namespace
|
|
314
|
+
send_data(xml.to_s)
|
|
315
|
+
xml.add_namespace(@streamns)
|
|
316
|
+
else
|
|
317
|
+
send_data(xml.to_s)
|
|
318
|
+
end
|
|
319
|
+
rescue Exception => e
|
|
320
|
+
Jabber::debuglog("EXCEPTION:\n#{e.class}\n#{e.message}\n#{e.backtrace.join("\n")}")
|
|
321
|
+
|
|
322
|
+
if @exception_block
|
|
323
|
+
Thread.new do
|
|
324
|
+
Thread.current.abort_on_exception = true
|
|
325
|
+
close!
|
|
326
|
+
@exception_block.call(e, self, :sending)
|
|
327
|
+
end
|
|
328
|
+
else
|
|
329
|
+
Jabber::debuglog "Exception caught while sending! (#{e.class})\n#{e.backtrace.join("\n")}"
|
|
330
|
+
close!
|
|
331
|
+
raise
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
# The parser thread might be running this (think of a callback running send())
|
|
335
|
+
# If this is the case, we mustn't stop (or we would cause a deadlock)
|
|
336
|
+
if block and Thread.current != @parser_thread
|
|
337
|
+
threadblock.wait
|
|
338
|
+
elsif block
|
|
339
|
+
Jabber::debuglog("WARNING:\nCannot stop current thread in Jabber::Stream#send because it is the parser thread!")
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
##
|
|
344
|
+
# Send an XMMP stanza with an Jabber::XMPPStanza#id. The id will be
|
|
345
|
+
# generated by Jabber::IdGenerator if not already set.
|
|
346
|
+
#
|
|
347
|
+
# The block will be called once: when receiving a stanza with the
|
|
348
|
+
# same Jabber::XMPPStanza#id. There is no need to return true to
|
|
349
|
+
# complete this! Instead the return value of the block will be
|
|
350
|
+
# returned. This is a direct result of unique request/response
|
|
351
|
+
# stanza identification via the id attribute.
|
|
352
|
+
#
|
|
353
|
+
# The block may be omitted. Then, the result will be the response
|
|
354
|
+
# stanza.
|
|
355
|
+
#
|
|
356
|
+
# Be aware that if a stanza with <tt>type='error'</tt> is received
|
|
357
|
+
# the function does not yield but raises an ServerError with
|
|
358
|
+
# the corresponding error element.
|
|
359
|
+
#
|
|
360
|
+
# Please see Stream#send for some implementational details.
|
|
361
|
+
#
|
|
362
|
+
# Please read the note about nesting at Stream#send
|
|
363
|
+
# xml:: [XMPPStanza]
|
|
364
|
+
def send_with_id(xml, &block)
|
|
365
|
+
if xml.id.nil?
|
|
366
|
+
xml.id = Jabber::IdGenerator.instance.generate_id
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
res = nil
|
|
370
|
+
error = nil
|
|
371
|
+
send(xml) do |received|
|
|
372
|
+
if received.kind_of? XMPPStanza and received.id == xml.id
|
|
373
|
+
if received.type == :error
|
|
374
|
+
error = (received.error ? received.error : ErrorResponse.new)
|
|
375
|
+
true
|
|
376
|
+
elsif block_given?
|
|
377
|
+
res = yield(received)
|
|
378
|
+
true
|
|
379
|
+
else
|
|
380
|
+
res = received
|
|
381
|
+
true
|
|
382
|
+
end
|
|
383
|
+
else
|
|
384
|
+
false
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
unless error.nil?
|
|
389
|
+
raise ServerError.new(error)
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
res
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
##
|
|
396
|
+
# Adds a callback block to process received XML messages
|
|
397
|
+
#
|
|
398
|
+
# priority:: [Integer] The callback's priority, the higher, the sooner
|
|
399
|
+
# ref:: [String] The callback's reference
|
|
400
|
+
# &block:: [Block] The optional block
|
|
401
|
+
def add_xml_callback(priority = 0, ref = nil, &block)
|
|
402
|
+
@xmlcbs.add(priority, ref, block)
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
##
|
|
406
|
+
# Delete an XML-messages callback
|
|
407
|
+
#
|
|
408
|
+
# ref:: [String] The reference of the callback to delete
|
|
409
|
+
def delete_xml_callback(ref)
|
|
410
|
+
@xmlcbs.delete(ref)
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
##
|
|
414
|
+
# Adds a callback block to process received Messages
|
|
415
|
+
#
|
|
416
|
+
# priority:: [Integer] The callback's priority, the higher, the sooner
|
|
417
|
+
# ref:: [String] The callback's reference
|
|
418
|
+
# &block:: [Block] The optional block
|
|
419
|
+
def add_message_callback(priority = 0, ref = nil, &block)
|
|
420
|
+
@messagecbs.add(priority, ref, block)
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
##
|
|
424
|
+
# Delete an Message callback
|
|
425
|
+
#
|
|
426
|
+
# ref:: [String] The reference of the callback to delete
|
|
427
|
+
def delete_message_callback(ref)
|
|
428
|
+
@messagecbs.delete(ref)
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
##
|
|
432
|
+
# Adds a callback block to process received Stanzas
|
|
433
|
+
#
|
|
434
|
+
# priority:: [Integer] The callback's priority, the higher, the sooner
|
|
435
|
+
# ref:: [String] The callback's reference
|
|
436
|
+
# &block:: [Block] The optional block
|
|
437
|
+
def add_stanza_callback(priority = 0, ref = nil, &block)
|
|
438
|
+
@stanzacbs.add(priority, ref, block)
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
##
|
|
442
|
+
# Delete a Stanza callback
|
|
443
|
+
#
|
|
444
|
+
# ref:: [String] The reference of the callback to delete
|
|
445
|
+
def delete_stanza_callback(ref)
|
|
446
|
+
@stanzacbs.delete(ref)
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
##
|
|
450
|
+
# Adds a callback block to process received Presences
|
|
451
|
+
#
|
|
452
|
+
# priority:: [Integer] The callback's priority, the higher, the sooner
|
|
453
|
+
# ref:: [String] The callback's reference
|
|
454
|
+
# &block:: [Block] The optional block
|
|
455
|
+
def add_presence_callback(priority = 0, ref = nil, &block)
|
|
456
|
+
@presencecbs.add(priority, ref, block)
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
##
|
|
460
|
+
# Delete a Presence callback
|
|
461
|
+
#
|
|
462
|
+
# ref:: [String] The reference of the callback to delete
|
|
463
|
+
def delete_presence_callback(ref)
|
|
464
|
+
@presencecbs.delete(ref)
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
##
|
|
468
|
+
# Adds a callback block to process received Iqs
|
|
469
|
+
#
|
|
470
|
+
# priority:: [Integer] The callback's priority, the higher, the sooner
|
|
471
|
+
# ref:: [String] The callback's reference
|
|
472
|
+
# &block:: [Block] The optional block
|
|
473
|
+
def add_iq_callback(priority = 0, ref = nil, &block)
|
|
474
|
+
@iqcbs.add(priority, ref, block)
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
##
|
|
478
|
+
# Delete an Iq callback
|
|
479
|
+
#
|
|
480
|
+
# ref:: [String] The reference of the callback to delete
|
|
481
|
+
#
|
|
482
|
+
def delete_iq_callback(ref)
|
|
483
|
+
@iqcbs.delete(ref)
|
|
484
|
+
end
|
|
485
|
+
##
|
|
486
|
+
# Closes the connection to the Jabber service
|
|
487
|
+
def close
|
|
488
|
+
close!
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
def close!
|
|
492
|
+
@parser_thread.kill if @parser_thread
|
|
493
|
+
@fd.close if @fd and !@fd.closed?
|
|
494
|
+
@status = DISCONNECTED
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
end
|