Compare commits
	
		
			173 Commits
		
	
	
		
			1.1.10
			...
			hildjj-dev
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					48def71d0c | ||
| 
						 | 
					12e8bb6ddc | ||
| 
						 | 
					c8c20fff71 | ||
| 
						 | 
					75a18b5ffe | ||
| 
						 | 
					06a690a259 | ||
| 
						 | 
					52feabbe76 | ||
| 
						 | 
					14c9e9a9cc | ||
| 
						 | 
					a22ca228cc | ||
| 
						 | 
					d0666a5eb6 | ||
| 
						 | 
					931d49560a | ||
| 
						 | 
					2a4e435228 | ||
| 
						 | 
					3655827ef2 | ||
| 
						 | 
					c5046b9c91 | ||
| 
						 | 
					4598031dd2 | ||
| 
						 | 
					12e0e1a16b | ||
| 
						 | 
					5e9266ba90 | ||
| 
						 | 
					0d448b8221 | ||
| 
						 | 
					e6c95f0a2a | ||
| 
						 | 
					63b58edda1 | ||
| 
						 | 
					af9632519c | ||
| 
						 | 
					d367fb938d | ||
| 
						 | 
					77f2a339e1 | ||
| 
						 | 
					4190027a78 | ||
| 
						 | 
					ef48a8c4d9 | ||
| 
						 | 
					829b225053 | ||
| 
						 | 
					747a6e94e6 | ||
| 
						 | 
					cebc798e72 | ||
| 
						 | 
					7c485c6a8b | ||
| 
						 | 
					e2e8c4b5dc | ||
| 
						 | 
					675c0112ac | ||
| 
						 | 
					4dd2c15775 | ||
| 
						 | 
					9f6decdbc1 | ||
| 
						 | 
					fc07e23ff8 | ||
| 
						 | 
					4ea328b9f2 | ||
| 
						 | 
					84a2fc382b | ||
| 
						 | 
					098714b3c4 | ||
| 
						 | 
					cf2c94d974 | ||
| 
						 | 
					657102e938 | ||
| 
						 | 
					44e7585bf8 | ||
| 
						 | 
					94488fa2ea | ||
| 
						 | 
					a2c60a4911 | ||
| 
						 | 
					ee9c4abd08 | ||
| 
						 | 
					b5b1c932c7 | ||
| 
						 | 
					b8f04983e1 | ||
| 
						 | 
					90807dd973 | ||
| 
						 | 
					ef974114ea | ||
| 
						 | 
					f6e1fecdf8 | ||
| 
						 | 
					94e8b2becf | ||
| 
						 | 
					a6ca6701a0 | ||
| 
						 | 
					c4edb9724b | ||
| 
						 | 
					b5c669bdff | ||
| 
						 | 
					e449dce65c | ||
| 
						 | 
					73ce9a5ecc | ||
| 
						 | 
					671f680bb3 | ||
| 
						 | 
					dfff19ffbf | ||
| 
						 | 
					a4abdf9fa6 | ||
| 
						 | 
					6c57bb0553 | ||
| 
						 | 
					d385b9e708 | ||
| 
						 | 
					c2ae1ee891 | ||
| 
						 | 
					67147570e9 | ||
| 
						 | 
					fb3e6b7e35 | ||
| 
						 | 
					cf28d4586d | ||
| 
						 | 
					f65eb5eeea | ||
| 
						 | 
					26fa9bd87e | ||
| 
						 | 
					0016d9a638 | ||
| 
						 | 
					a88b9737ff | ||
| 
						 | 
					df9ac58d05 | ||
| 
						 | 
					357406d801 | ||
| 
						 | 
					19a78f63f4 | ||
| 
						 | 
					c7ec6a72cd | ||
| 
						 | 
					e68b07dbce | ||
| 
						 | 
					e20610ab80 | ||
| 
						 | 
					1ca0c46333 | ||
| 
						 | 
					e510875f64 | ||
| 
						 | 
					f52a10b061 | ||
| 
						 | 
					7d382a2bfd | ||
| 
						 | 
					09bec1c4fe | ||
| 
						 | 
					ff28b0a005 | ||
| 
						 | 
					8a03bd72ae | ||
| 
						 | 
					a249f8736a | ||
| 
						 | 
					f0e1fc5aad | ||
| 
						 | 
					f09adf0014 | ||
| 
						 | 
					c6ac64ed2d | ||
| 
						 | 
					04dc68f5f6 | ||
| 
						 | 
					92be051450 | ||
| 
						 | 
					5c25208fb5 | ||
| 
						 | 
					779c258e27 | ||
| 
						 | 
					962dfad216 | ||
| 
						 | 
					f7a710e55b | ||
| 
						 | 
					814a50e36f | ||
| 
						 | 
					230465b946 | ||
| 
						 | 
					d11a67702e | ||
| 
						 | 
					4e12e228cb | ||
| 
						 | 
					4a94aeba49 | ||
| 
						 | 
					14aa831169 | ||
| 
						 | 
					295d23ccf3 | ||
| 
						 | 
					75d904ed01 | ||
| 
						 | 
					aebcf6ff82 | ||
| 
						 | 
					8c2ece3bca | ||
| 
						 | 
					80a90a6221 | ||
| 
						 | 
					f81d5e4bd6 | ||
| 
						 | 
					2324c90232 | ||
| 
						 | 
					2f65fdbc76 | ||
| 
						 | 
					59ff08174f | ||
| 
						 | 
					2f4149c7d0 | ||
| 
						 | 
					b84e359770 | ||
| 
						 | 
					fb4275648c | ||
| 
						 | 
					475ccfa8dc | ||
| 
						 | 
					267c24c8ef | ||
| 
						 | 
					06a9d9fc30 | ||
| 
						 | 
					1383ca19b5 | ||
| 
						 | 
					4c3ff2abab | ||
| 
						 | 
					7c6ef18e4f | ||
| 
						 | 
					f8856467d5 | ||
| 
						 | 
					3bd84b8d27 | ||
| 
						 | 
					bc8b5774ac | ||
| 
						 | 
					8009b0485e | ||
| 
						 | 
					8742a56b3e | ||
| 
						 | 
					a792bcdafe | ||
| 
						 | 
					167d1ce97b | ||
| 
						 | 
					695cd95657 | ||
| 
						 | 
					44ce01a70b | ||
| 
						 | 
					c2189b4ecd | ||
| 
						 | 
					c9b2cf6043 | ||
| 
						 | 
					16ec0f151a | ||
| 
						 | 
					c42f1ad4c7 | ||
| 
						 | 
					a3ec1af205 | ||
| 
						 | 
					2e580304f9 | ||
| 
						 | 
					5492e9028d | ||
| 
						 | 
					060c9ab679 | ||
| 
						 | 
					78f0325398 | ||
| 
						 | 
					1efe049959 | ||
| 
						 | 
					2393148908 | ||
| 
						 | 
					c7594b3ef0 | ||
| 
						 | 
					b210870f48 | ||
| 
						 | 
					5d6019a962 | ||
| 
						 | 
					eb5df1aa37 | ||
| 
						 | 
					546066d677 | ||
| 
						 | 
					3234596974 | ||
| 
						 | 
					5820d49cd4 | ||
| 
						 | 
					1ab66e5767 | ||
| 
						 | 
					aab2682f9a | ||
| 
						 | 
					55d332bcc8 | ||
| 
						 | 
					f89df6e70c | ||
| 
						 | 
					250d28e870 | ||
| 
						 | 
					19f65c8510 | ||
| 
						 | 
					f70b49882f | ||
| 
						 | 
					a7b092a305 | ||
| 
						 | 
					daa73a3f3c | ||
| 
						 | 
					0b51afe87a | ||
| 
						 | 
					2b298766c9 | ||
| 
						 | 
					10664d723b | ||
| 
						 | 
					c012208a8f | ||
| 
						 | 
					0953896d2d | ||
| 
						 | 
					cf9e89d0ae | ||
| 
						 | 
					48dd01b0bb | ||
| 
						 | 
					7247efe055 | ||
| 
						 | 
					8def3758e4 | ||
| 
						 | 
					1851ab6f5f | ||
| 
						 | 
					289b052338 | ||
| 
						 | 
					26147f5ae0 | ||
| 
						 | 
					ae01f1071a | ||
| 
						 | 
					dcdf5dcd09 | ||
| 
						 | 
					c59a6d0f51 | ||
| 
						 | 
					2cd936318d | ||
| 
						 | 
					2f38857681 | ||
| 
						 | 
					39505ae1ff | ||
| 
						 | 
					44ee0633f2 | ||
| 
						 | 
					b52d2768b0 | ||
| 
						 | 
					cf24b870b1 | ||
| 
						 | 
					69cffce7dc | ||
| 
						 | 
					a14979375b | ||
| 
						 | 
					40ef4a16b1 | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -7,3 +7,4 @@ docs/_build/
 | 
			
		||||
.tox/
 | 
			
		||||
.coverage
 | 
			
		||||
sleekxmpp.egg-info/
 | 
			
		||||
.ropeproject/
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								LICENSE
									
									
									
									
									
								
							@@ -69,8 +69,8 @@ modification, are permitted provided that the following conditions are met:
 | 
			
		||||
    * Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
      notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
      documentation and/or other materials provided with the distribution.
 | 
			
		||||
    * Neither the name of Red Innovation nor the names of its contributors 
 | 
			
		||||
      may be used to endorse or promote products derived from this software 
 | 
			
		||||
    * Neither the name of Red Innovation nor the names of its contributors
 | 
			
		||||
      may be used to endorse or promote products derived from this software
 | 
			
		||||
      without specific prior written permission.
 | 
			
		||||
 | 
			
		||||
THIS SOFTWARE IS PROVIDED BY RED INNOVATION ``AS IS'' AND ANY
 | 
			
		||||
@@ -167,3 +167,28 @@ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | 
			
		||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 | 
			
		||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 | 
			
		||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 | 
			
		||||
socksipy: A Python SOCKS client module.
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
Copyright 2006 Dan-Haim. All rights reserved.
 | 
			
		||||
 | 
			
		||||
Redistribution and use in source and binary forms, with or without modification,
 | 
			
		||||
are permitted provided that the following conditions are met:
 | 
			
		||||
1. Redistributions of source code must retain the above copyright notice, this
 | 
			
		||||
   list of conditions and the following disclaimer.
 | 
			
		||||
2. Redistributions in binary form must reproduce the above copyright notice,
 | 
			
		||||
   this list of conditions and the following disclaimer in the documentation
 | 
			
		||||
   and/or other materials provided with the distribution.
 | 
			
		||||
3. Neither the name of Dan Haim nor the names of his contributors may be used
 | 
			
		||||
   to endorse or promote products derived from this software without specific
 | 
			
		||||
   prior written permission.
 | 
			
		||||
 | 
			
		||||
THIS SOFTWARE IS PROVIDED BY DAN HAIM "AS IS" AND ANY EXPRESS OR IMPLIED
 | 
			
		||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 | 
			
		||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 | 
			
		||||
EVENT SHALL DAN HAIM OR HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 | 
			
		||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
			
		||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA
 | 
			
		||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | 
			
		||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 | 
			
		||||
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE.
 | 
			
		||||
 
 | 
			
		||||
@@ -69,8 +69,8 @@ use ASCII. We can get Python to use UTF-8 as the default encoding by including:
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    if sys.version_info < (3, 0):
 | 
			
		||||
        reload(sys)
 | 
			
		||||
        sys.setdefaultencoding('utf8')
 | 
			
		||||
        from sleekxmpp.util.misc_ops import setdefaultencoding
 | 
			
		||||
        setdefaultencoding('utf8')
 | 
			
		||||
 | 
			
		||||
.. warning::
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,8 @@ import sleekxmpp
 | 
			
		||||
# throughout SleekXMPP, we will set the default encoding
 | 
			
		||||
# ourselves to UTF-8.
 | 
			
		||||
if sys.version_info < (3, 0):
 | 
			
		||||
    reload(sys)
 | 
			
		||||
    sys.setdefaultencoding('utf8')
 | 
			
		||||
    from sleekxmpp.util.misc_ops import setdefaultencoding
 | 
			
		||||
    setdefaultencoding('utf8')
 | 
			
		||||
else:
 | 
			
		||||
    raw_input = input
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,8 @@ import sleekxmpp
 | 
			
		||||
# throughout SleekXMPP, we will set the default encoding
 | 
			
		||||
# ourselves to UTF-8.
 | 
			
		||||
if sys.version_info < (3, 0):
 | 
			
		||||
    reload(sys)
 | 
			
		||||
    sys.setdefaultencoding('utf8')
 | 
			
		||||
    from sleekxmpp.util.misc_ops import setdefaultencoding
 | 
			
		||||
    setdefaultencoding('utf8')
 | 
			
		||||
else:
 | 
			
		||||
    raw_input = input
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,8 @@ import sleekxmpp
 | 
			
		||||
# throughout SleekXMPP, we will set the default encoding
 | 
			
		||||
# ourselves to UTF-8.
 | 
			
		||||
if sys.version_info < (3, 0):
 | 
			
		||||
    reload(sys)
 | 
			
		||||
    sys.setdefaultencoding('utf8')
 | 
			
		||||
    from sleekxmpp.util.misc_ops import setdefaultencoding
 | 
			
		||||
    setdefaultencoding('utf8')
 | 
			
		||||
else:
 | 
			
		||||
    raw_input = input
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -28,8 +28,8 @@ from stanza import Action
 | 
			
		||||
# throughout SleekXMPP, we will set the default encoding
 | 
			
		||||
# ourselves to UTF-8.
 | 
			
		||||
if sys.version_info < (3, 0):
 | 
			
		||||
    reload(sys)
 | 
			
		||||
    sys.setdefaultencoding('utf8')
 | 
			
		||||
    from sleekxmpp.util.misc_ops import setdefaultencoding
 | 
			
		||||
    setdefaultencoding('utf8')
 | 
			
		||||
else:
 | 
			
		||||
    raw_input = input
 | 
			
		||||
 | 
			
		||||
@@ -56,8 +56,8 @@ class ActionBot(sleekxmpp.ClientXMPP):
 | 
			
		||||
            StanzaPath('iq@type=set/action'),
 | 
			
		||||
            self._handle_action))
 | 
			
		||||
 | 
			
		||||
        self.add_event_handler('custom_action', 
 | 
			
		||||
                self._handle_action_event, 
 | 
			
		||||
        self.add_event_handler('custom_action',
 | 
			
		||||
                self._handle_action_event,
 | 
			
		||||
                threaded=True)
 | 
			
		||||
 | 
			
		||||
        register_stanza_plugin(Iq, Action)
 | 
			
		||||
 
 | 
			
		||||
@@ -26,8 +26,8 @@ from stanza import Action
 | 
			
		||||
# throughout SleekXMPP, we will set the default encoding
 | 
			
		||||
# ourselves to UTF-8.
 | 
			
		||||
if sys.version_info < (3, 0):
 | 
			
		||||
    reload(sys)
 | 
			
		||||
    sys.setdefaultencoding('utf8')
 | 
			
		||||
    from sleekxmpp.util.misc_ops import setdefaultencoding
 | 
			
		||||
    setdefaultencoding('utf8')
 | 
			
		||||
else:
 | 
			
		||||
    raw_input = input
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ import getpass
 | 
			
		||||
from optparse import OptionParser
 | 
			
		||||
 | 
			
		||||
import sleekxmpp
 | 
			
		||||
from sleekxmpp.exceptions import IqError, IqTimeout
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Python versions before 3.0 do not use UTF-8 encoding
 | 
			
		||||
@@ -22,8 +23,8 @@ import sleekxmpp
 | 
			
		||||
# throughout SleekXMPP, we will set the default encoding
 | 
			
		||||
# ourselves to UTF-8.
 | 
			
		||||
if sys.version_info < (3, 0):
 | 
			
		||||
    reload(sys)
 | 
			
		||||
    sys.setdefaultencoding('utf8')
 | 
			
		||||
    from sleekxmpp.util.misc_ops import setdefaultencoding
 | 
			
		||||
    setdefaultencoding('utf8')
 | 
			
		||||
else:
 | 
			
		||||
    raw_input = input
 | 
			
		||||
 | 
			
		||||
@@ -83,50 +84,54 @@ class Disco(sleekxmpp.ClientXMPP):
 | 
			
		||||
        self.get_roster()
 | 
			
		||||
        self.send_presence()
 | 
			
		||||
 | 
			
		||||
        if self.get in self.info_types:
 | 
			
		||||
            # By using block=True, the result stanza will be
 | 
			
		||||
            # returned. Execution will block until the reply is
 | 
			
		||||
            # received. Non-blocking options would be to listen
 | 
			
		||||
            # for the disco_info event, or passing a handler
 | 
			
		||||
            # function using the callback parameter.
 | 
			
		||||
            info = self['xep_0030'].get_info(jid=self.target_jid,
 | 
			
		||||
                                             node=self.target_node,
 | 
			
		||||
                                             block=True)
 | 
			
		||||
        if self.get in self.items_types:
 | 
			
		||||
            # The same applies from above. Listen for the
 | 
			
		||||
            # disco_items event or pass a callback function
 | 
			
		||||
            # if you need to process a non-blocking request.
 | 
			
		||||
            items = self['xep_0030'].get_items(jid=self.target_jid,
 | 
			
		||||
                                               node=self.target_node,
 | 
			
		||||
                                               block=True)
 | 
			
		||||
        try:
 | 
			
		||||
            if self.get in self.info_types:
 | 
			
		||||
                # By using block=True, the result stanza will be
 | 
			
		||||
                # returned. Execution will block until the reply is
 | 
			
		||||
                # received. Non-blocking options would be to listen
 | 
			
		||||
                # for the disco_info event, or passing a handler
 | 
			
		||||
                # function using the callback parameter.
 | 
			
		||||
                info = self['xep_0030'].get_info(jid=self.target_jid,
 | 
			
		||||
                                                 node=self.target_node,
 | 
			
		||||
                                                 block=True)
 | 
			
		||||
            elif self.get in self.items_types:
 | 
			
		||||
                # The same applies from above. Listen for the
 | 
			
		||||
                # disco_items event or pass a callback function
 | 
			
		||||
                # if you need to process a non-blocking request.
 | 
			
		||||
                items = self['xep_0030'].get_items(jid=self.target_jid,
 | 
			
		||||
                                                   node=self.target_node,
 | 
			
		||||
                                                   block=True)
 | 
			
		||||
            else:
 | 
			
		||||
                logging.error("Invalid disco request type.")
 | 
			
		||||
                return
 | 
			
		||||
        except IqError as e:
 | 
			
		||||
            logging.error("Entity returned an error: %s" % e.iq['error']['condition'])
 | 
			
		||||
        except IqTimeout:
 | 
			
		||||
            logging.error("No response received.")
 | 
			
		||||
        else:
 | 
			
		||||
            logging.error("Invalid disco request type.")
 | 
			
		||||
            self.disconnect()
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        header = 'XMPP Service Discovery: %s' % self.target_jid
 | 
			
		||||
        print(header)
 | 
			
		||||
        print('-' * len(header))
 | 
			
		||||
        if self.target_node != '':
 | 
			
		||||
            print('Node: %s' % self.target_node)
 | 
			
		||||
            header = 'XMPP Service Discovery: %s' % self.target_jid
 | 
			
		||||
            print(header)
 | 
			
		||||
            print('-' * len(header))
 | 
			
		||||
            if self.target_node != '':
 | 
			
		||||
                print('Node: %s' % self.target_node)
 | 
			
		||||
                print('-' * len(header))
 | 
			
		||||
 | 
			
		||||
        if self.get in self.identity_types:
 | 
			
		||||
            print('Identities:')
 | 
			
		||||
            for identity in info['disco_info']['identities']:
 | 
			
		||||
                print('  - %s' % str(identity))
 | 
			
		||||
            if self.get in self.identity_types:
 | 
			
		||||
                print('Identities:')
 | 
			
		||||
                for identity in info['disco_info']['identities']:
 | 
			
		||||
                    print('  - %s' % str(identity))
 | 
			
		||||
 | 
			
		||||
        if self.get in self.feature_types:
 | 
			
		||||
            print('Features:')
 | 
			
		||||
            for feature in info['disco_info']['features']:
 | 
			
		||||
                print('  - %s' % feature)
 | 
			
		||||
            if self.get in self.feature_types:
 | 
			
		||||
                print('Features:')
 | 
			
		||||
                for feature in info['disco_info']['features']:
 | 
			
		||||
                    print('  - %s' % feature)
 | 
			
		||||
 | 
			
		||||
        if self.get in self.items_types:
 | 
			
		||||
            print('Items:')
 | 
			
		||||
            for item in items['disco_items']['items']:
 | 
			
		||||
                print('  - %s' % str(item))
 | 
			
		||||
 | 
			
		||||
        self.disconnect()
 | 
			
		||||
            if self.get in self.items_types:
 | 
			
		||||
                print('Items:')
 | 
			
		||||
                for item in items['disco_items']['items']:
 | 
			
		||||
                    print('  - %s' % str(item))
 | 
			
		||||
        finally:
 | 
			
		||||
            self.disconnect()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
 
 | 
			
		||||
@@ -24,8 +24,8 @@ from sleekxmpp.exceptions import XMPPError
 | 
			
		||||
# throughout SleekXMPP, we will set the default encoding
 | 
			
		||||
# ourselves to UTF-8.
 | 
			
		||||
if sys.version_info < (3, 0):
 | 
			
		||||
    reload(sys)
 | 
			
		||||
    sys.setdefaultencoding('utf8')
 | 
			
		||||
    from sleekxmpp.util.misc_ops import setdefaultencoding
 | 
			
		||||
    setdefaultencoding('utf8')
 | 
			
		||||
else:
 | 
			
		||||
    raw_input = input
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,8 @@ import sleekxmpp
 | 
			
		||||
# throughout SleekXMPP, we will set the default encoding
 | 
			
		||||
# ourselves to UTF-8.
 | 
			
		||||
if sys.version_info < (3, 0):
 | 
			
		||||
    reload(sys)
 | 
			
		||||
    sys.setdefaultencoding('utf8')
 | 
			
		||||
    from sleekxmpp.util.misc_ops import setdefaultencoding
 | 
			
		||||
    setdefaultencoding('utf8')
 | 
			
		||||
else:
 | 
			
		||||
    raw_input = input
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -22,8 +22,8 @@ from sleekxmpp.componentxmpp import ComponentXMPP
 | 
			
		||||
# throughout SleekXMPP, we will set the default encoding
 | 
			
		||||
# ourselves to UTF-8.
 | 
			
		||||
if sys.version_info < (3, 0):
 | 
			
		||||
    reload(sys)
 | 
			
		||||
    sys.setdefaultencoding('utf8')
 | 
			
		||||
    from sleekxmpp.util.misc_ops import setdefaultencoding
 | 
			
		||||
    setdefaultencoding('utf8')
 | 
			
		||||
else:
 | 
			
		||||
    raw_input = input
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -25,8 +25,8 @@ from sleekxmpp.xmlstream import cert
 | 
			
		||||
# throughout SleekXMPP, we will set the default encoding
 | 
			
		||||
# ourselves to UTF-8.
 | 
			
		||||
if sys.version_info < (3, 0):
 | 
			
		||||
    reload(sys)
 | 
			
		||||
    sys.setdefaultencoding('utf8')
 | 
			
		||||
    from sleekxmpp.util.misc_ops import setdefaultencoding
 | 
			
		||||
    setdefaultencoding('utf8')
 | 
			
		||||
else:
 | 
			
		||||
    raw_input = input
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,8 @@ import sleekxmpp
 | 
			
		||||
# throughout SleekXMPP, we will set the default encoding
 | 
			
		||||
# ourselves to UTF-8.
 | 
			
		||||
if sys.version_info < (3, 0):
 | 
			
		||||
    reload(sys)
 | 
			
		||||
    sys.setdefaultencoding('utf8')
 | 
			
		||||
    from sleekxmpp.util.misc_ops import setdefaultencoding
 | 
			
		||||
    setdefaultencoding('utf8')
 | 
			
		||||
else:
 | 
			
		||||
    raw_input = input
 | 
			
		||||
 | 
			
		||||
@@ -85,7 +85,7 @@ class IBBReceiver(sleekxmpp.ClientXMPP):
 | 
			
		||||
    def stream_opened(self, stream):
 | 
			
		||||
        # NOTE: IBB streams are bi-directional, so the original sender is
 | 
			
		||||
        # now the opened stream's receiver.
 | 
			
		||||
        print('Stream opened: %s from ' % (stream.sid, stream.receiver))
 | 
			
		||||
        print('Stream opened: %s from %s' % (stream.sid, stream.receiver))
 | 
			
		||||
 | 
			
		||||
        # You could run a loop reading from the stream using stream.recv(),
 | 
			
		||||
        # or use the ibb_stream_data event.
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,8 @@ import sleekxmpp
 | 
			
		||||
# throughout SleekXMPP, we will set the default encoding
 | 
			
		||||
# ourselves to UTF-8.
 | 
			
		||||
if sys.version_info < (3, 0):
 | 
			
		||||
    reload(sys)
 | 
			
		||||
    sys.setdefaultencoding('utf8')
 | 
			
		||||
    from sleekxmpp.util.misc_ops import setdefaultencoding
 | 
			
		||||
    setdefaultencoding('utf8')
 | 
			
		||||
else:
 | 
			
		||||
    raw_input = input
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,8 @@ import sleekxmpp
 | 
			
		||||
# throughout SleekXMPP, we will set the default encoding
 | 
			
		||||
# ourselves to UTF-8.
 | 
			
		||||
if sys.version_info < (3, 0):
 | 
			
		||||
    reload(sys)
 | 
			
		||||
    sys.setdefaultencoding('utf8')
 | 
			
		||||
    from sleekxmpp.util.misc_ops import setdefaultencoding
 | 
			
		||||
    setdefaultencoding('utf8')
 | 
			
		||||
else:
 | 
			
		||||
    raw_input = input
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,8 @@ import sleekxmpp
 | 
			
		||||
# throughout SleekXMPP, we will set the default encoding
 | 
			
		||||
# ourselves to UTF-8.
 | 
			
		||||
if sys.version_info < (3, 0):
 | 
			
		||||
    reload(sys)
 | 
			
		||||
    sys.setdefaultencoding('utf8')
 | 
			
		||||
    from sleekxmpp.util.misc_ops import setdefaultencoding
 | 
			
		||||
    setdefaultencoding('utf8')
 | 
			
		||||
else:
 | 
			
		||||
    raw_input = input
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,8 @@ import sleekxmpp
 | 
			
		||||
# throughout SleekXMPP, we will set the default encoding
 | 
			
		||||
# ourselves to UTF-8.
 | 
			
		||||
if sys.version_info < (3, 0):
 | 
			
		||||
    reload(sys)
 | 
			
		||||
    sys.setdefaultencoding('utf8')
 | 
			
		||||
    from sleekxmpp.util.misc_ops import setdefaultencoding
 | 
			
		||||
    setdefaultencoding('utf8')
 | 
			
		||||
else:
 | 
			
		||||
    raw_input = input
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,8 +12,8 @@ from sleekxmpp.xmlstream import ET, tostring
 | 
			
		||||
# throughout SleekXMPP, we will set the default encoding
 | 
			
		||||
# ourselves to UTF-8.
 | 
			
		||||
if sys.version_info < (3, 0):
 | 
			
		||||
    reload(sys)
 | 
			
		||||
    sys.setdefaultencoding('utf8')
 | 
			
		||||
    from sleekxmpp.util.misc_ops import setdefaultencoding
 | 
			
		||||
    setdefaultencoding('utf8')
 | 
			
		||||
else:
 | 
			
		||||
    raw_input = input
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14,8 +14,8 @@ from sleekxmpp.xmlstream.handler import Callback
 | 
			
		||||
# throughout SleekXMPP, we will set the default encoding
 | 
			
		||||
# ourselves to UTF-8.
 | 
			
		||||
if sys.version_info < (3, 0):
 | 
			
		||||
    reload(sys)
 | 
			
		||||
    sys.setdefaultencoding('utf8')
 | 
			
		||||
    from sleekxmpp.util.misc_ops import setdefaultencoding
 | 
			
		||||
    setdefaultencoding('utf8')
 | 
			
		||||
else:
 | 
			
		||||
    raw_input = input
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -22,8 +22,8 @@ from sleekxmpp.exceptions import IqError, IqTimeout
 | 
			
		||||
# throughout SleekXMPP, we will set the default encoding
 | 
			
		||||
# ourselves to UTF-8.
 | 
			
		||||
if sys.version_info < (3, 0):
 | 
			
		||||
    reload(sys)
 | 
			
		||||
    sys.setdefaultencoding('utf8')
 | 
			
		||||
    from sleekxmpp.util.misc_ops import setdefaultencoding
 | 
			
		||||
    setdefaultencoding('utf8')
 | 
			
		||||
else:
 | 
			
		||||
    raw_input = input
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -24,8 +24,8 @@ from sleekxmpp.exceptions import IqError, IqTimeout
 | 
			
		||||
# throughout SleekXMPP, we will set the default encoding
 | 
			
		||||
# ourselves to UTF-8.
 | 
			
		||||
if sys.version_info < (3, 0):
 | 
			
		||||
    reload(sys)
 | 
			
		||||
    sys.setdefaultencoding('utf8')
 | 
			
		||||
    from sleekxmpp.util.misc_ops import setdefaultencoding
 | 
			
		||||
    setdefaultencoding('utf8')
 | 
			
		||||
else:
 | 
			
		||||
    raw_input = input
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,8 +21,8 @@ import sleekxmpp
 | 
			
		||||
# throughout SleekXMPP, we will set the default encoding
 | 
			
		||||
# ourselves to UTF-8.
 | 
			
		||||
if sys.version_info < (3, 0):
 | 
			
		||||
    reload(sys)
 | 
			
		||||
    sys.setdefaultencoding('utf8')
 | 
			
		||||
    from sleekxmpp.util.misc_ops import setdefaultencoding
 | 
			
		||||
    setdefaultencoding('utf8')
 | 
			
		||||
else:
 | 
			
		||||
    raw_input = input
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -26,8 +26,8 @@ from sleekxmpp.exceptions import XMPPError
 | 
			
		||||
# throughout SleekXMPP, we will set the default encoding
 | 
			
		||||
# ourselves to UTF-8.
 | 
			
		||||
if sys.version_info < (3, 0):
 | 
			
		||||
    reload(sys)
 | 
			
		||||
    sys.setdefaultencoding('utf8')
 | 
			
		||||
    from sleekxmpp.util.misc_ops import setdefaultencoding
 | 
			
		||||
    setdefaultencoding('utf8')
 | 
			
		||||
else:
 | 
			
		||||
    raw_input = input
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -29,8 +29,8 @@ from sleekxmpp.xmlstream import JID
 | 
			
		||||
# throughout SleekXMPP, we will set the default encoding
 | 
			
		||||
# ourselves to UTF-8.
 | 
			
		||||
if sys.version_info < (3, 0):
 | 
			
		||||
    reload(sys)
 | 
			
		||||
    sys.setdefaultencoding('utf8')
 | 
			
		||||
    from sleekxmpp.util.misc_ops import setdefaultencoding
 | 
			
		||||
    setdefaultencoding('utf8')
 | 
			
		||||
else:
 | 
			
		||||
    raw_input = input
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								setup.py
									
									
									
									
									
								
							@@ -50,6 +50,7 @@ packages     = [ 'sleekxmpp',
 | 
			
		||||
                 'sleekxmpp/test',
 | 
			
		||||
                 'sleekxmpp/roster',
 | 
			
		||||
                 'sleekxmpp/util',
 | 
			
		||||
                 'sleekxmpp/util/sasl',
 | 
			
		||||
                 'sleekxmpp/xmlstream',
 | 
			
		||||
                 'sleekxmpp/xmlstream/matcher',
 | 
			
		||||
                 'sleekxmpp/xmlstream/handler',
 | 
			
		||||
@@ -59,16 +60,20 @@ packages     = [ 'sleekxmpp',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0009',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0009/stanza',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0012',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0013',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0016',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0027',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0030',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0030/stanza',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0033',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0047',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0049',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0050',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0054',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0059',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0060',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0060/stanza',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0065',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0066',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0077',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0078',
 | 
			
		||||
@@ -76,6 +81,7 @@ packages     = [ 'sleekxmpp',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0084',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0085',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0086',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0091',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0092',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0107',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0108',
 | 
			
		||||
@@ -95,8 +101,15 @@ packages     = [ 'sleekxmpp',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0221',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0224',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0231',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0235',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0249',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0257',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0258',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0279',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0280',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0297',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0308',
 | 
			
		||||
                 'sleekxmpp/plugins/xep_0313',
 | 
			
		||||
                 'sleekxmpp/features',
 | 
			
		||||
                 'sleekxmpp/features/feature_mechanisms',
 | 
			
		||||
                 'sleekxmpp/features/feature_mechanisms/stanza',
 | 
			
		||||
@@ -104,9 +117,8 @@ packages     = [ 'sleekxmpp',
 | 
			
		||||
                 'sleekxmpp/features/feature_bind',
 | 
			
		||||
                 'sleekxmpp/features/feature_session',
 | 
			
		||||
                 'sleekxmpp/features/feature_rosterver',
 | 
			
		||||
                 'sleekxmpp/features/feature_preapproval',
 | 
			
		||||
                 'sleekxmpp/thirdparty',
 | 
			
		||||
                 'sleekxmpp/thirdparty/suelta',
 | 
			
		||||
                 'sleekxmpp/thirdparty/suelta/mechanisms',
 | 
			
		||||
                 ]
 | 
			
		||||
 | 
			
		||||
setup(
 | 
			
		||||
 
 | 
			
		||||
@@ -43,8 +43,8 @@ log = logging.getLogger(__name__)
 | 
			
		||||
# In order to make sure that Unicode is handled properly
 | 
			
		||||
# in Python 2.x, reset the default encoding.
 | 
			
		||||
if sys.version_info < (3, 0):
 | 
			
		||||
    reload(sys)
 | 
			
		||||
    sys.setdefaultencoding('utf8')
 | 
			
		||||
    from sleekxmpp.util.misc_ops import setdefaultencoding
 | 
			
		||||
    setdefaultencoding('utf8')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BaseXMPP(XMLStream):
 | 
			
		||||
@@ -68,8 +68,13 @@ class BaseXMPP(XMLStream):
 | 
			
		||||
        #: An identifier for the stream as given by the server.
 | 
			
		||||
        self.stream_id = None
 | 
			
		||||
 | 
			
		||||
        #: The JabberID (JID) used by this connection.
 | 
			
		||||
        self.boundjid = JID(jid)
 | 
			
		||||
        #: The JabberID (JID) requested for this connection.
 | 
			
		||||
        self.requested_jid = JID(jid, cache_lock=True)
 | 
			
		||||
 | 
			
		||||
        #: The JabberID (JID) used by this connection,
 | 
			
		||||
        #: as set after session binding. This may even be a
 | 
			
		||||
        #: different bare JID than what was requested.
 | 
			
		||||
        self.boundjid = JID(jid, cache_lock=True)
 | 
			
		||||
 | 
			
		||||
        self._expected_server_name = self.boundjid.host
 | 
			
		||||
        self._redirect_attempts = 0
 | 
			
		||||
@@ -109,6 +114,17 @@ class BaseXMPP(XMLStream):
 | 
			
		||||
        #: ``'to'`` and ``'from'`` JIDs of stanzas.
 | 
			
		||||
        self.is_component = False
 | 
			
		||||
 | 
			
		||||
        #: Messages may optionally be tagged with ID values. Setting
 | 
			
		||||
        #: :attr:`use_message_ids` to `True` will assign all outgoing
 | 
			
		||||
        #: messages an ID. Some plugin features require enabling
 | 
			
		||||
        #: this option.
 | 
			
		||||
        self.use_message_ids = False
 | 
			
		||||
 | 
			
		||||
        #: Presence updates may optionally be tagged with ID values.
 | 
			
		||||
        #: Setting :attr:`use_message_ids` to `True` will assign all
 | 
			
		||||
        #: outgoing messages an ID.
 | 
			
		||||
        self.use_presence_ids = False
 | 
			
		||||
 | 
			
		||||
        #: The API registry is a way to process callbacks based on
 | 
			
		||||
        #: JID+node combinations. Each callback in the registry is
 | 
			
		||||
        #: marked with:
 | 
			
		||||
@@ -196,6 +212,10 @@ class BaseXMPP(XMLStream):
 | 
			
		||||
        self.stream_version = xml.get('version', '')
 | 
			
		||||
        self.peer_default_lang = xml.get('{%s}lang' % XML_NS, None)
 | 
			
		||||
 | 
			
		||||
        if not self.is_component and not self.stream_version:
 | 
			
		||||
            log.warning('Legacy XMPP 0.9 protocol detected.')
 | 
			
		||||
            self.event('legacy_protocol')
 | 
			
		||||
 | 
			
		||||
    def process(self, *args, **kwargs):
 | 
			
		||||
        """Initialize plugins and begin processing the XML stream.
 | 
			
		||||
 | 
			
		||||
@@ -221,13 +241,6 @@ class BaseXMPP(XMLStream):
 | 
			
		||||
        - The send queue processor
 | 
			
		||||
        - The scheduler
 | 
			
		||||
        """
 | 
			
		||||
        if 'xep_0115' in self.plugin:
 | 
			
		||||
            name = 'xep_0115'
 | 
			
		||||
            if not hasattr(self.plugin[name], 'post_inited'):
 | 
			
		||||
                if hasattr(self.plugin[name], 'post_init'):
 | 
			
		||||
                    self.plugin[name].post_init()
 | 
			
		||||
                self.plugin[name].post_inited = True
 | 
			
		||||
 | 
			
		||||
        for name in self.plugin:
 | 
			
		||||
            if not hasattr(self.plugin[name], 'post_inited'):
 | 
			
		||||
                if hasattr(self.plugin[name], 'post_init'):
 | 
			
		||||
@@ -652,7 +665,7 @@ class BaseXMPP(XMLStream):
 | 
			
		||||
    def set_jid(self, jid):
 | 
			
		||||
        """Rip a JID apart and claim it as our own."""
 | 
			
		||||
        log.debug("setting jid to %s", jid)
 | 
			
		||||
        self.boundjid.full = jid
 | 
			
		||||
        self.boundjid = JID(jid, cache_lock=True)
 | 
			
		||||
 | 
			
		||||
    def getjidresource(self, fulljid):
 | 
			
		||||
        if '/' in fulljid:
 | 
			
		||||
@@ -733,6 +746,8 @@ class BaseXMPP(XMLStream):
 | 
			
		||||
        item = self.roster[pres['to']][pres['from']]
 | 
			
		||||
        if item['whitelisted']:
 | 
			
		||||
            item.authorize()
 | 
			
		||||
            if roster.auto_subscribe:
 | 
			
		||||
                item.subscribe()
 | 
			
		||||
        elif roster.auto_authorize:
 | 
			
		||||
            item.authorize()
 | 
			
		||||
            if roster.auto_subscribe:
 | 
			
		||||
 
 | 
			
		||||
@@ -64,7 +64,6 @@ class ClientXMPP(BaseXMPP):
 | 
			
		||||
                 escape_quotes=True, sasl_mech=None, lang='en'):
 | 
			
		||||
        BaseXMPP.__init__(self, jid, 'jabber:client')
 | 
			
		||||
 | 
			
		||||
        self.set_jid(jid)
 | 
			
		||||
        self.escape_quotes = escape_quotes
 | 
			
		||||
        self.plugin_config = plugin_config
 | 
			
		||||
        self.plugin_whitelist = plugin_whitelist
 | 
			
		||||
@@ -95,7 +94,7 @@ class ClientXMPP(BaseXMPP):
 | 
			
		||||
        self.bound = False
 | 
			
		||||
        self.bindfail = False
 | 
			
		||||
 | 
			
		||||
        self.add_event_handler('connected', self._handle_connected)
 | 
			
		||||
        self.add_event_handler('connected', self._reset_connection_state)
 | 
			
		||||
        self.add_event_handler('session_bind', self._handle_session_bind)
 | 
			
		||||
 | 
			
		||||
        self.register_stanza(StreamFeatures)
 | 
			
		||||
@@ -113,9 +112,10 @@ class ClientXMPP(BaseXMPP):
 | 
			
		||||
        self.register_plugin('feature_starttls')
 | 
			
		||||
        self.register_plugin('feature_bind')
 | 
			
		||||
        self.register_plugin('feature_session')
 | 
			
		||||
        self.register_plugin('feature_rosterver')
 | 
			
		||||
        self.register_plugin('feature_preapproval')
 | 
			
		||||
        self.register_plugin('feature_mechanisms',
 | 
			
		||||
                pconfig={'use_mech': sasl_mech} if sasl_mech else None)
 | 
			
		||||
        self.register_plugin('feature_rosterver')
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def password(self):
 | 
			
		||||
@@ -252,7 +252,7 @@ class ClientXMPP(BaseXMPP):
 | 
			
		||||
            self._handle_roster(response)
 | 
			
		||||
            return response
 | 
			
		||||
 | 
			
		||||
    def _handle_connected(self, event=None):
 | 
			
		||||
    def _reset_connection_state(self, event=None):
 | 
			
		||||
        #TODO: Use stream state here
 | 
			
		||||
        self.authenticated = False
 | 
			
		||||
        self.sessionstarted = False
 | 
			
		||||
@@ -272,6 +272,8 @@ class ClientXMPP(BaseXMPP):
 | 
			
		||||
                    # Don't continue if the feature requires
 | 
			
		||||
                    # restarting the XML stream.
 | 
			
		||||
                    return True
 | 
			
		||||
        log.debug('Finished processing stream features.')
 | 
			
		||||
        self.event('stream_negotiated')
 | 
			
		||||
 | 
			
		||||
    def _handle_roster(self, iq):
 | 
			
		||||
        """Update the roster after receiving a roster stanza.
 | 
			
		||||
@@ -286,15 +288,17 @@ class ClientXMPP(BaseXMPP):
 | 
			
		||||
        if iq['roster']['ver']:
 | 
			
		||||
            roster.version = iq['roster']['ver']
 | 
			
		||||
        items = iq['roster']['items']
 | 
			
		||||
        for jid in items:
 | 
			
		||||
            item = items[jid]
 | 
			
		||||
            roster[jid]['name'] = item['name']
 | 
			
		||||
            roster[jid]['groups'] = item['groups']
 | 
			
		||||
            roster[jid]['from'] = item['subscription'] in ['from', 'both']
 | 
			
		||||
            roster[jid]['to'] = item['subscription'] in ['to', 'both']
 | 
			
		||||
            roster[jid]['pending_out'] = (item['ask'] == 'subscribe')
 | 
			
		||||
 | 
			
		||||
            roster[jid].save(remove=(item['subscription'] == 'remove'))
 | 
			
		||||
        valid_subscriptions = ('to', 'from', 'both', 'none', 'remove')
 | 
			
		||||
        for jid, item in items.items():
 | 
			
		||||
            if item['subscription'] in valid_subscriptions:
 | 
			
		||||
                roster[jid]['name'] = item['name']
 | 
			
		||||
                roster[jid]['groups'] = item['groups']
 | 
			
		||||
                roster[jid]['from'] = item['subscription'] in ('from', 'both')
 | 
			
		||||
                roster[jid]['to'] = item['subscription'] in ('to', 'both')
 | 
			
		||||
                roster[jid]['pending_out'] = (item['ask'] == 'subscribe')
 | 
			
		||||
 | 
			
		||||
                roster[jid].save(remove=(item['subscription'] == 'remove'))
 | 
			
		||||
 | 
			
		||||
        self.event("roster_update", iq)
 | 
			
		||||
        if iq['type'] == 'set':
 | 
			
		||||
 
 | 
			
		||||
@@ -11,5 +11,6 @@ __all__ = [
 | 
			
		||||
    'feature_mechanisms',
 | 
			
		||||
    'feature_bind',
 | 
			
		||||
    'feature_session',
 | 
			
		||||
    'feature_rosterver'
 | 
			
		||||
    'feature_rosterver',
 | 
			
		||||
    'feature_preapproval'
 | 
			
		||||
]
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.jid import JID
 | 
			
		||||
from sleekxmpp.stanza import Iq, StreamFeatures
 | 
			
		||||
from sleekxmpp.features.feature_bind import stanza
 | 
			
		||||
from sleekxmpp.xmlstream import register_stanza_plugin
 | 
			
		||||
@@ -48,7 +49,7 @@ class FeatureBind(BasePlugin):
 | 
			
		||||
            iq['bind']['resource'] = self.xmpp.boundjid.resource
 | 
			
		||||
        response = iq.send(now=True)
 | 
			
		||||
 | 
			
		||||
        self.xmpp.set_jid(response['bind']['jid'])
 | 
			
		||||
        self.xmpp.boundjid = JID(response['bind']['jid'], cache_lock=True)
 | 
			
		||||
        self.xmpp.bound = True
 | 
			
		||||
        self.xmpp.event('session_bind', self.xmpp.boundjid, direct=True)
 | 
			
		||||
        self.xmpp.session_bind_event.set()
 | 
			
		||||
 
 | 
			
		||||
@@ -6,12 +6,12 @@
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import sys
 | 
			
		||||
import ssl
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.thirdparty import suelta
 | 
			
		||||
from sleekxmpp.thirdparty.suelta.exceptions import SASLCancelled, SASLError
 | 
			
		||||
from sleekxmpp.thirdparty.suelta.exceptions import SASLPrepFailure
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.util import sasl
 | 
			
		||||
from sleekxmpp.util.stringprep_profiles import StringPrepError
 | 
			
		||||
from sleekxmpp.stanza import StreamFeatures
 | 
			
		||||
from sleekxmpp.xmlstream import RestartStream, register_stanza_plugin
 | 
			
		||||
from sleekxmpp.plugins import BasePlugin
 | 
			
		||||
@@ -31,42 +31,29 @@ class FeatureMechanisms(BasePlugin):
 | 
			
		||||
    stanza = stanza
 | 
			
		||||
    default_config = {
 | 
			
		||||
        'use_mech': None,
 | 
			
		||||
        'use_mechs': None,
 | 
			
		||||
        'min_mech': None,
 | 
			
		||||
        'sasl_callback': None,
 | 
			
		||||
        'security_callback': None,
 | 
			
		||||
        'encrypted_plain': True,
 | 
			
		||||
        'unencrypted_plain': False,
 | 
			
		||||
        'unencrypted_digest': False,
 | 
			
		||||
        'unencrypted_cram': False,
 | 
			
		||||
        'unencrypted_scram': True,
 | 
			
		||||
        'order': 100
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def plugin_init(self):
 | 
			
		||||
        if not self.use_mech and not self.xmpp.boundjid.user:
 | 
			
		||||
        if not self.use_mech and not self.xmpp.requested_jid.user:
 | 
			
		||||
            self.use_mech = 'ANONYMOUS'
 | 
			
		||||
 | 
			
		||||
        def tls_active():
 | 
			
		||||
            return 'starttls' in self.xmpp.features
 | 
			
		||||
 | 
			
		||||
        def basic_callback(mech, values):
 | 
			
		||||
            creds = self.xmpp.credentials
 | 
			
		||||
            for value in values:
 | 
			
		||||
                if value == 'username':
 | 
			
		||||
                    values['username'] = self.xmpp.boundjid.user
 | 
			
		||||
                elif value == 'password':
 | 
			
		||||
                    values['password'] = creds['password']
 | 
			
		||||
                elif value == 'email':
 | 
			
		||||
                    jid = self.xmpp.boundjid.bare
 | 
			
		||||
                    values['email'] = creds.get('email', jid)
 | 
			
		||||
                elif value in creds:
 | 
			
		||||
                    values[value] = creds[value]
 | 
			
		||||
            mech.fulfill(values)
 | 
			
		||||
 | 
			
		||||
        if self.sasl_callback is None:
 | 
			
		||||
            self.sasl_callback = basic_callback
 | 
			
		||||
            self.sasl_callback = self._default_credentials
 | 
			
		||||
 | 
			
		||||
        if self.security_callback is None:
 | 
			
		||||
            self.security_callback = self._default_security
 | 
			
		||||
 | 
			
		||||
        self.mech = None
 | 
			
		||||
        self.sasl = suelta.SASL(self.xmpp.boundjid.domain, 'xmpp',
 | 
			
		||||
                                username=self.xmpp.boundjid.user,
 | 
			
		||||
                                sec_query=suelta.sec_query_allow,
 | 
			
		||||
                                request_values=self.sasl_callback,
 | 
			
		||||
                                tls_active=tls_active,
 | 
			
		||||
                                mech=self.use_mech)
 | 
			
		||||
 | 
			
		||||
        self.mech_list = set()
 | 
			
		||||
        self.attempted_mechs = set()
 | 
			
		||||
 | 
			
		||||
@@ -99,6 +86,51 @@ class FeatureMechanisms(BasePlugin):
 | 
			
		||||
                restart=True,
 | 
			
		||||
                order=self.order)
 | 
			
		||||
 | 
			
		||||
    def _default_credentials(self, required_values, optional_values):
 | 
			
		||||
        creds = self.xmpp.credentials
 | 
			
		||||
        result = {}
 | 
			
		||||
        values = required_values.union(optional_values)
 | 
			
		||||
        for value in values:
 | 
			
		||||
            if value == 'username':
 | 
			
		||||
                result[value] = self.xmpp.requested_jid.user
 | 
			
		||||
            elif value == 'password':
 | 
			
		||||
                result[value] = creds['password']
 | 
			
		||||
            elif value == 'authzid':
 | 
			
		||||
                result[value] = creds.get('authzid', '')
 | 
			
		||||
            elif value == 'email':
 | 
			
		||||
                jid = self.xmpp.requested_jid.bare
 | 
			
		||||
                result[value] = creds.get('email', jid)
 | 
			
		||||
            elif value == 'channel_binding':
 | 
			
		||||
                if sys.version_info >= (3, 3):
 | 
			
		||||
                    result[value] = self.xmpp.socket.get_channel_binding()
 | 
			
		||||
                else:
 | 
			
		||||
                    result[value] = None
 | 
			
		||||
            elif value == 'host':
 | 
			
		||||
                result[value] = self.xmpp.requested_jid.domain
 | 
			
		||||
            elif value == 'realm':
 | 
			
		||||
                result[value] = self.xmpp.requested_jid.domain
 | 
			
		||||
            elif value == 'service-name':
 | 
			
		||||
                result[value] = self.xmpp._service_name
 | 
			
		||||
            elif value == 'service':
 | 
			
		||||
                result[value] = 'xmpp'
 | 
			
		||||
            elif value in creds:
 | 
			
		||||
                result[value] = creds[value]
 | 
			
		||||
        return result
 | 
			
		||||
 | 
			
		||||
    def _default_security(self, values):
 | 
			
		||||
        result = {}
 | 
			
		||||
        for value in values:
 | 
			
		||||
            if value == 'encrypted':
 | 
			
		||||
                if 'starttls' in self.xmpp.features:
 | 
			
		||||
                    result[value] = True
 | 
			
		||||
                elif isinstance(self.xmpp.socket, ssl.SSLSocket):
 | 
			
		||||
                    result[value] = True
 | 
			
		||||
                else:
 | 
			
		||||
                    result[value] = False
 | 
			
		||||
            else:
 | 
			
		||||
                result[value] = self.config.get(value, False)
 | 
			
		||||
        return result
 | 
			
		||||
 | 
			
		||||
    def _handle_sasl_auth(self, features):
 | 
			
		||||
        """
 | 
			
		||||
        Handle authenticating using SASL.
 | 
			
		||||
@@ -111,37 +143,61 @@ class FeatureMechanisms(BasePlugin):
 | 
			
		||||
            # server has incorrectly offered it again.
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
        if not self.use_mech:
 | 
			
		||||
            self.mech_list = set(features['mechanisms'])
 | 
			
		||||
        else:
 | 
			
		||||
            self.mech_list = set([self.use_mech])
 | 
			
		||||
        enforce_limit = False
 | 
			
		||||
        limited_mechs = self.use_mechs
 | 
			
		||||
 | 
			
		||||
        if limited_mechs is None:
 | 
			
		||||
            limited_mechs = set()
 | 
			
		||||
        elif limited_mechs and not isinstance(limited_mechs, set):
 | 
			
		||||
            limited_mechs = set(limited_mechs)
 | 
			
		||||
            enforce_limit = True
 | 
			
		||||
 | 
			
		||||
        if self.use_mech:
 | 
			
		||||
            limited_mechs.add(self.use_mech)
 | 
			
		||||
            enforce_limit = True
 | 
			
		||||
 | 
			
		||||
        if enforce_limit:
 | 
			
		||||
            self.use_mechs = limited_mechs
 | 
			
		||||
 | 
			
		||||
        self.mech_list = set(features['mechanisms'])
 | 
			
		||||
 | 
			
		||||
        return self._send_auth()
 | 
			
		||||
 | 
			
		||||
    def _send_auth(self):
 | 
			
		||||
        mech_list = self.mech_list - self.attempted_mechs
 | 
			
		||||
        self.mech = self.sasl.choose_mechanism(mech_list)
 | 
			
		||||
 | 
			
		||||
        if mech_list and self.mech is not None:
 | 
			
		||||
            resp = stanza.Auth(self.xmpp)
 | 
			
		||||
            resp['mechanism'] = self.mech.name
 | 
			
		||||
            try:
 | 
			
		||||
                resp['value'] = self.mech.process()
 | 
			
		||||
            except SASLCancelled:
 | 
			
		||||
                self.attempted_mechs.add(self.mech.name)
 | 
			
		||||
                self._send_auth()
 | 
			
		||||
            except SASLError:
 | 
			
		||||
                self.attempted_mechs.add(self.mech.name)
 | 
			
		||||
                self._send_auth()
 | 
			
		||||
            except SASLPrepFailure:
 | 
			
		||||
                log.exception("A credential value did not pass SASLprep.")
 | 
			
		||||
                self.xmpp.disconnect()
 | 
			
		||||
            else:
 | 
			
		||||
                resp.send(now=True)
 | 
			
		||||
        else:
 | 
			
		||||
        try:
 | 
			
		||||
            self.mech = sasl.choose(mech_list,
 | 
			
		||||
                                    self.sasl_callback,
 | 
			
		||||
                                    self.security_callback,
 | 
			
		||||
                                    limit=self.use_mechs,
 | 
			
		||||
                                    min_mech=self.min_mech)
 | 
			
		||||
        except sasl.SASLNoAppropriateMechanism:
 | 
			
		||||
            log.error("No appropriate login method.")
 | 
			
		||||
            self.xmpp.event("no_auth", direct=True)
 | 
			
		||||
            self.attempted_mechs = set()
 | 
			
		||||
            return self.xmpp.disconnect()
 | 
			
		||||
 | 
			
		||||
        resp = stanza.Auth(self.xmpp)
 | 
			
		||||
        resp['mechanism'] = self.mech.name
 | 
			
		||||
        try:
 | 
			
		||||
            resp['value'] = self.mech.process()
 | 
			
		||||
        except sasl.SASLCancelled:
 | 
			
		||||
            self.attempted_mechs.add(self.mech.name)
 | 
			
		||||
            self._send_auth()
 | 
			
		||||
        except sasl.SASLFailed:
 | 
			
		||||
            self.attempted_mechs.add(self.mech.name)
 | 
			
		||||
            self._send_auth()
 | 
			
		||||
        except sasl.SASLMutualAuthFailed:
 | 
			
		||||
            log.error("Mutual authentication failed! " + \
 | 
			
		||||
                      "A security breach is possible.")
 | 
			
		||||
            self.attempted_mechs.add(self.mech.name)
 | 
			
		||||
            self.xmpp.disconnect()
 | 
			
		||||
        except StringPrepError:
 | 
			
		||||
            log.exception("A credential value did not pass SASLprep.")
 | 
			
		||||
            self.xmpp.disconnect()
 | 
			
		||||
        else:
 | 
			
		||||
            resp.send(now=True)
 | 
			
		||||
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def _handle_challenge(self, stanza):
 | 
			
		||||
@@ -149,20 +205,33 @@ class FeatureMechanisms(BasePlugin):
 | 
			
		||||
        resp = self.stanza.Response(self.xmpp)
 | 
			
		||||
        try:
 | 
			
		||||
            resp['value'] = self.mech.process(stanza['value'])
 | 
			
		||||
        except SASLCancelled:
 | 
			
		||||
        except sasl.SASLCancelled:
 | 
			
		||||
            self.stanza.Abort(self.xmpp).send()
 | 
			
		||||
        except SASLError:
 | 
			
		||||
        except sasl.SASLFailed:
 | 
			
		||||
            self.stanza.Abort(self.xmpp).send()
 | 
			
		||||
        except sasl.SASLMutualAuthFailed:
 | 
			
		||||
            log.error("Mutual authentication failed! " + \
 | 
			
		||||
                      "A security breach is possible.")
 | 
			
		||||
            self.attempted_mechs.add(self.mech.name)
 | 
			
		||||
            self.xmpp.disconnect()
 | 
			
		||||
        else:
 | 
			
		||||
            resp.send(now=True)
 | 
			
		||||
 | 
			
		||||
    def _handle_success(self, stanza):
 | 
			
		||||
        """SASL authentication succeeded. Restart the stream."""
 | 
			
		||||
        self.attempted_mechs = set()
 | 
			
		||||
        self.xmpp.authenticated = True
 | 
			
		||||
        self.xmpp.features.add('mechanisms')
 | 
			
		||||
        self.xmpp.event('auth_success', stanza, direct=True)
 | 
			
		||||
        raise RestartStream()
 | 
			
		||||
        try:
 | 
			
		||||
            final = self.mech.process(stanza['value'])
 | 
			
		||||
        except sasl.SASLMutualAuthFailed:
 | 
			
		||||
            log.error("Mutual authentication failed! " + \
 | 
			
		||||
                      "A security breach is possible.")
 | 
			
		||||
            self.attempted_mechs.add(self.mech.name)
 | 
			
		||||
            self.xmpp.disconnect()
 | 
			
		||||
        else:
 | 
			
		||||
            self.attempted_mechs = set()
 | 
			
		||||
            self.xmpp.authenticated = True
 | 
			
		||||
            self.xmpp.features.add('mechanisms')
 | 
			
		||||
            self.xmpp.event('auth_success', stanza, direct=True)
 | 
			
		||||
            raise RestartStream()
 | 
			
		||||
 | 
			
		||||
    def _handle_fail(self, stanza):
 | 
			
		||||
        """SASL authentication failed. Disconnect and shutdown."""
 | 
			
		||||
 
 | 
			
		||||
@@ -8,8 +8,7 @@
 | 
			
		||||
 | 
			
		||||
import base64
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.thirdparty.suelta.util import bytes
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.util import bytes
 | 
			
		||||
from sleekxmpp.xmlstream import StanzaBase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,8 +8,7 @@
 | 
			
		||||
 | 
			
		||||
import base64
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.thirdparty.suelta.util import bytes
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.util import bytes
 | 
			
		||||
from sleekxmpp.xmlstream import StanzaBase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,8 +8,7 @@
 | 
			
		||||
 | 
			
		||||
import base64
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.thirdparty.suelta.util import bytes
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.util import bytes
 | 
			
		||||
from sleekxmpp.xmlstream import StanzaBase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,8 +6,10 @@
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.xmlstream import StanzaBase
 | 
			
		||||
import base64
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.util import bytes
 | 
			
		||||
from sleekxmpp.xmlstream import StanzaBase
 | 
			
		||||
 | 
			
		||||
class Success(StanzaBase):
 | 
			
		||||
 | 
			
		||||
@@ -16,9 +18,21 @@ class Success(StanzaBase):
 | 
			
		||||
 | 
			
		||||
    name = 'success'
 | 
			
		||||
    namespace = 'urn:ietf:params:xml:ns:xmpp-sasl'
 | 
			
		||||
    interfaces = set()
 | 
			
		||||
    interfaces = set(['value'])
 | 
			
		||||
    plugin_attrib = name
 | 
			
		||||
 | 
			
		||||
    def setup(self, xml):
 | 
			
		||||
        StanzaBase.setup(self, xml)
 | 
			
		||||
        self.xml.tag = self.tag_name()
 | 
			
		||||
 | 
			
		||||
    def get_value(self):
 | 
			
		||||
        return base64.b64decode(bytes(self.xml.text))
 | 
			
		||||
 | 
			
		||||
    def set_value(self, values):
 | 
			
		||||
        if values:
 | 
			
		||||
            self.xml.text = bytes(base64.b64encode(values)).decode('utf-8')
 | 
			
		||||
        else:
 | 
			
		||||
            self.xml.text = '='
 | 
			
		||||
 | 
			
		||||
    def del_value(self):
 | 
			
		||||
        self.xml.text = ''
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								sleekxmpp/features/feature_preapproval/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								sleekxmpp/features/feature_preapproval/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012  Nathanael C. Fritz
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins.base import register_plugin
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.features.feature_preapproval.preapproval import FeaturePreApproval
 | 
			
		||||
from sleekxmpp.features.feature_preapproval.stanza import PreApproval
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
register_plugin(FeaturePreApproval)
 | 
			
		||||
							
								
								
									
										42
									
								
								sleekxmpp/features/feature_preapproval/preapproval.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								sleekxmpp/features/feature_preapproval/preapproval.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012  Nathanael C. Fritz
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.stanza import Iq, StreamFeatures
 | 
			
		||||
from sleekxmpp.features.feature_preapproval import stanza
 | 
			
		||||
from sleekxmpp.xmlstream import register_stanza_plugin
 | 
			
		||||
from sleekxmpp.plugins.base import BasePlugin
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
log = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FeaturePreApproval(BasePlugin):
 | 
			
		||||
 | 
			
		||||
    name = 'feature_preapproval'
 | 
			
		||||
    description = 'RFC 6121: Stream Feature: Subscription Pre-Approval'
 | 
			
		||||
    dependences = set()
 | 
			
		||||
    stanza = stanza
 | 
			
		||||
 | 
			
		||||
    def plugin_init(self):
 | 
			
		||||
        self.xmpp.register_feature('preapproval',
 | 
			
		||||
                self._handle_preapproval,
 | 
			
		||||
                restart=False,
 | 
			
		||||
                order=9001)
 | 
			
		||||
 | 
			
		||||
        register_stanza_plugin(StreamFeatures, stanza.PreApproval)
 | 
			
		||||
 | 
			
		||||
    def _handle_preapproval(self, features):
 | 
			
		||||
        """Save notice that the server support subscription pre-approvals.
 | 
			
		||||
 | 
			
		||||
        Arguments:
 | 
			
		||||
            features -- The stream features stanza.
 | 
			
		||||
        """
 | 
			
		||||
        log.debug("Server supports subscription pre-approvals.")
 | 
			
		||||
        self.xmpp.features.add('preapproval')
 | 
			
		||||
							
								
								
									
										17
									
								
								sleekxmpp/features/feature_preapproval/stanza.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								sleekxmpp/features/feature_preapproval/stanza.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012  Nathanael C. Fritz
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.xmlstream import ElementBase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PreApproval(ElementBase):
 | 
			
		||||
 | 
			
		||||
    name = 'sub'
 | 
			
		||||
    namespace = 'urn:xmpp:features:pre-approval'
 | 
			
		||||
    interfaces = set()
 | 
			
		||||
    plugin_attrib = 'preapproval'
 | 
			
		||||
@@ -54,13 +54,9 @@ class FeatureSTARTTLS(BasePlugin):
 | 
			
		||||
            return False
 | 
			
		||||
        elif not self.xmpp.use_tls:
 | 
			
		||||
            return False
 | 
			
		||||
        elif self.xmpp.ssl_support:
 | 
			
		||||
        else:
 | 
			
		||||
            self.xmpp.send(features['starttls'], now=True)
 | 
			
		||||
            return True
 | 
			
		||||
        else:
 | 
			
		||||
            log.warning("The module tlslite is required to log in" + \
 | 
			
		||||
                        " to some servers, and has not been found.")
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
    def _handle_starttls_proceed(self, proceed):
 | 
			
		||||
        """Restart the XML stream when TLS is accepted."""
 | 
			
		||||
 
 | 
			
		||||
@@ -16,9 +16,11 @@ from __future__ import unicode_literals
 | 
			
		||||
import re
 | 
			
		||||
import socket
 | 
			
		||||
import stringprep
 | 
			
		||||
import threading
 | 
			
		||||
import encodings.idna
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.util import stringprep_profiles
 | 
			
		||||
from sleekxmpp.thirdparty import OrderedDict
 | 
			
		||||
 | 
			
		||||
#: These characters are not allowed to appear in a JID.
 | 
			
		||||
ILLEGAL_CHARS = '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r' + \
 | 
			
		||||
@@ -63,6 +65,24 @@ JID_UNESCAPE_TRANSFORMATIONS = {'\\20': ' ',
 | 
			
		||||
                                '\\40': '@',
 | 
			
		||||
                                '\\5c': '\\'}
 | 
			
		||||
 | 
			
		||||
JID_CACHE = OrderedDict()
 | 
			
		||||
JID_CACHE_LOCK = threading.Lock()
 | 
			
		||||
JID_CACHE_MAX_SIZE = 1024
 | 
			
		||||
 | 
			
		||||
def _cache(key, parts, locked):
 | 
			
		||||
    JID_CACHE[key] = (parts, locked)
 | 
			
		||||
    if len(JID_CACHE) > JID_CACHE_MAX_SIZE:
 | 
			
		||||
        with JID_CACHE_LOCK:
 | 
			
		||||
            while len(JID_CACHE) > JID_CACHE_MAX_SIZE:
 | 
			
		||||
                found = None
 | 
			
		||||
                for key, item in JID_CACHE.iteritems():
 | 
			
		||||
                    if not item[1]: # if not locked
 | 
			
		||||
                        found = key
 | 
			
		||||
                        break
 | 
			
		||||
                if not found: # more than MAX_SIZE locked
 | 
			
		||||
                    # warn?
 | 
			
		||||
                    break
 | 
			
		||||
                del JID_CACHE[found]
 | 
			
		||||
 | 
			
		||||
# pylint: disable=c0103
 | 
			
		||||
#: The nodeprep profile of stringprep used to validate the local,
 | 
			
		||||
@@ -72,7 +92,7 @@ nodeprep = stringprep_profiles.create(
 | 
			
		||||
    bidi=True,
 | 
			
		||||
    mappings=[
 | 
			
		||||
        stringprep_profiles.b1_mapping,
 | 
			
		||||
        stringprep_profiles.c12_mapping],
 | 
			
		||||
        stringprep.map_table_b2],
 | 
			
		||||
    prohibited=[
 | 
			
		||||
        stringprep.in_table_c11,
 | 
			
		||||
        stringprep.in_table_c12,
 | 
			
		||||
@@ -412,29 +432,48 @@ class JID(object):
 | 
			
		||||
 | 
			
		||||
    # pylint: disable=W0212
 | 
			
		||||
    def __init__(self, jid=None, **kwargs):
 | 
			
		||||
        self._jid = (None, None, None)
 | 
			
		||||
        locked = kwargs.get('cache_lock', False)
 | 
			
		||||
        in_local = kwargs.get('local', None)
 | 
			
		||||
        in_domain = kwargs.get('domain', None)
 | 
			
		||||
        in_resource = kwargs.get('resource', None)
 | 
			
		||||
        parts = None
 | 
			
		||||
        if in_local or in_domain or in_resource:
 | 
			
		||||
            parts = (in_local, in_domain, in_resource)
 | 
			
		||||
 | 
			
		||||
        if jid is None or jid == '':
 | 
			
		||||
            jid = (None, None, None)
 | 
			
		||||
        elif not isinstance(jid, JID):
 | 
			
		||||
            jid = _parse_jid(jid)
 | 
			
		||||
        else:
 | 
			
		||||
            jid = jid._jid
 | 
			
		||||
        # only check cache if there is a jid string, or parts, not if there
 | 
			
		||||
        # are both
 | 
			
		||||
        self._jid = None
 | 
			
		||||
        key = None
 | 
			
		||||
        if (jid is not None) and (parts is None):
 | 
			
		||||
            if isinstance(jid, JID):
 | 
			
		||||
                # it's already good to go, and there are no additions
 | 
			
		||||
                self._jid = jid._jid
 | 
			
		||||
                return
 | 
			
		||||
            key = jid
 | 
			
		||||
            self._jid, locked = JID_CACHE.get(jid, (None, locked))
 | 
			
		||||
        elif jid is None and parts is not None:
 | 
			
		||||
            key = parts
 | 
			
		||||
            self._jid, locked = JID_CACHE.get(parts, (None, locked))
 | 
			
		||||
        if not self._jid:
 | 
			
		||||
            if not jid:
 | 
			
		||||
                parsed_jid = (None, None, None)
 | 
			
		||||
            elif not isinstance(jid, JID):
 | 
			
		||||
                parsed_jid = _parse_jid(jid)
 | 
			
		||||
            else:
 | 
			
		||||
                parsed_jid = jid._jid
 | 
			
		||||
 | 
			
		||||
        local, domain, resource = jid
 | 
			
		||||
            local, domain, resource = parsed_jid
 | 
			
		||||
 | 
			
		||||
        local = kwargs.get('local', local)
 | 
			
		||||
        domain = kwargs.get('domain', domain)
 | 
			
		||||
        resource = kwargs.get('resource', resource)
 | 
			
		||||
            if 'local' in kwargs:
 | 
			
		||||
                local = _escape_node(in_local)
 | 
			
		||||
            if 'domain' in kwargs:
 | 
			
		||||
                domain = _validate_domain(in_domain)
 | 
			
		||||
            if 'resource' in kwargs:
 | 
			
		||||
                resource = _validate_resource(in_resource)
 | 
			
		||||
 | 
			
		||||
        if 'local' in kwargs:
 | 
			
		||||
            local = _escape_node(local)
 | 
			
		||||
        if 'domain' in kwargs:
 | 
			
		||||
            domain = _validate_domain(domain)
 | 
			
		||||
        if 'resource' in kwargs:
 | 
			
		||||
            resource = _validate_resource(resource)
 | 
			
		||||
 | 
			
		||||
        self._jid = (local, domain, resource)
 | 
			
		||||
            self._jid = (local, domain, resource)
 | 
			
		||||
            if key:
 | 
			
		||||
                _cache(key, self._jid, locked)
 | 
			
		||||
 | 
			
		||||
    def unescape(self):
 | 
			
		||||
        """Return an unescaped JID object.
 | 
			
		||||
@@ -498,7 +537,9 @@ class JID(object):
 | 
			
		||||
                             ``resource``, ``full``, ``jid``, or ``bare``.
 | 
			
		||||
        :param value: The new string value of the JID component.
 | 
			
		||||
        """
 | 
			
		||||
        if name == 'resource':
 | 
			
		||||
        if name == '_jid':
 | 
			
		||||
            super(JID, self).__setattr__('_jid', value)
 | 
			
		||||
        elif name == 'resource':
 | 
			
		||||
            self._jid = JID(self, resource=value)._jid
 | 
			
		||||
        elif name in ('user', 'username', 'local', 'node'):
 | 
			
		||||
            self._jid = JID(self, local=value)._jid
 | 
			
		||||
@@ -509,8 +550,6 @@ class JID(object):
 | 
			
		||||
        elif name == 'bare':
 | 
			
		||||
            parsed = JID(value)._jid
 | 
			
		||||
            self._jid = (parsed[0], parsed[1], self._jid[2])
 | 
			
		||||
        elif name == '_jid':
 | 
			
		||||
            super(JID, self).__setattr__('_jid', value)
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        """Use the full JID as the string value."""
 | 
			
		||||
 
 | 
			
		||||
@@ -18,15 +18,19 @@ __all__ = [
 | 
			
		||||
    'xep_0004',  # Data Forms
 | 
			
		||||
    'xep_0009',  # Jabber-RPC
 | 
			
		||||
    'xep_0012',  # Last Activity
 | 
			
		||||
    'xep_0013',  # Flexible Offline Message Retrieval
 | 
			
		||||
    'xep_0016',  # Privacy Lists
 | 
			
		||||
    'xep_0027',  # Current Jabber OpenPGP Usage
 | 
			
		||||
    'xep_0030',  # Service Discovery
 | 
			
		||||
    'xep_0033',  # Extended Stanza Addresses
 | 
			
		||||
    'xep_0045',  # Multi-User Chat (Client)
 | 
			
		||||
    'xep_0047',  # In-Band Bytestreams
 | 
			
		||||
    'xep_0049',  # Private XML Storage
 | 
			
		||||
    'xep_0050',  # Ad-hoc Commands
 | 
			
		||||
    'xep_0054',  # vcard-temp
 | 
			
		||||
    'xep_0059',  # Result Set Management
 | 
			
		||||
    'xep_0060',  # Pubsub (Client)
 | 
			
		||||
    'xep_0065',  # SOCKS5 Bytestreams
 | 
			
		||||
    'xep_0066',  # Out of Band Data
 | 
			
		||||
    'xep_0077',  # In-Band Registration
 | 
			
		||||
#   'xep_0078',  # Non-SASL auth. Don't automatically load
 | 
			
		||||
@@ -35,6 +39,7 @@ __all__ = [
 | 
			
		||||
    'xep_0084',  # User Avatar
 | 
			
		||||
    'xep_0085',  # Chat State Notifications
 | 
			
		||||
    'xep_0086',  # Legacy Error Codes
 | 
			
		||||
    'xep_0091',  # Legacy Delayed Delivery
 | 
			
		||||
    'xep_0092',  # Software Version
 | 
			
		||||
    'xep_0106',  # JID Escaping
 | 
			
		||||
    'xep_0107',  # User Mood
 | 
			
		||||
@@ -59,9 +64,17 @@ __all__ = [
 | 
			
		||||
    'xep_0223',  # Persistent Storage of Private Data via Pubsub
 | 
			
		||||
    'xep_0224',  # Attention
 | 
			
		||||
    'xep_0231',  # Bits of Binary
 | 
			
		||||
    'xep_0235',  # OAuth Over XMPP
 | 
			
		||||
    'xep_0242',  # XMPP Client Compliance 2009
 | 
			
		||||
    'xep_0249',  # Direct MUC Invitations
 | 
			
		||||
    'xep_0256',  # Last Activity in Presence
 | 
			
		||||
    'xep_0257',  # Client Certificate Management for SASL EXTERNAL
 | 
			
		||||
    'xep_0258',  # Security Labels in XMPP
 | 
			
		||||
    'xep_0270',  # XMPP Compliance Suites 2010
 | 
			
		||||
    'xep_0279',  # Server IP Check
 | 
			
		||||
    'xep_0280',  # Message Carbons
 | 
			
		||||
    'xep_0297',  # Stanza Forwarding
 | 
			
		||||
    'xep_0302',  # XMPP Compliance Suites 2012
 | 
			
		||||
    'xep_0308',  # Last Message Correction
 | 
			
		||||
    'xep_0313',  # Message Archive Management
 | 
			
		||||
]
 | 
			
		||||
 
 | 
			
		||||
@@ -201,7 +201,8 @@ class Form(ElementBase):
 | 
			
		||||
        del self['instructions']
 | 
			
		||||
        if instructions in [None, '']:
 | 
			
		||||
            return
 | 
			
		||||
        instructions = instructions.split('\n')
 | 
			
		||||
        if not isinstance(instructions, list):
 | 
			
		||||
            instructions = instructions.split('\n')
 | 
			
		||||
        for instruction in instructions:
 | 
			
		||||
            inst = ET.Element('{%s}instructions' % self.namespace)
 | 
			
		||||
            inst.text = instruction
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								sleekxmpp/plugins/xep_0013/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								sleekxmpp/plugins/xep_0013/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permissio
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins.base import register_plugin
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins.xep_0013.stanza import Offline
 | 
			
		||||
from sleekxmpp.plugins.xep_0013.offline import XEP_0013
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
register_plugin(XEP_0013)
 | 
			
		||||
							
								
								
									
										134
									
								
								sleekxmpp/plugins/xep_0013/offline.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								sleekxmpp/plugins/xep_0013/offline.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,134 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permissio
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
import sleekxmpp
 | 
			
		||||
from sleekxmpp.stanza import Message, Iq
 | 
			
		||||
from sleekxmpp.exceptions import XMPPError
 | 
			
		||||
from sleekxmpp.xmlstream.handler import Collector
 | 
			
		||||
from sleekxmpp.xmlstream.matcher import StanzaPath
 | 
			
		||||
from sleekxmpp.xmlstream import register_stanza_plugin
 | 
			
		||||
from sleekxmpp.plugins import BasePlugin
 | 
			
		||||
from sleekxmpp.plugins.xep_0013 import stanza
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
log = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class XEP_0013(BasePlugin):
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    XEP-0013 Flexible Offline Message Retrieval
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    name = 'xep_0013'
 | 
			
		||||
    description = 'XEP-0013: Flexible Offline Message Retrieval'
 | 
			
		||||
    dependencies = set(['xep_0030'])
 | 
			
		||||
    stanza = stanza
 | 
			
		||||
 | 
			
		||||
    def plugin_init(self):
 | 
			
		||||
        register_stanza_plugin(Iq, stanza.Offline)
 | 
			
		||||
        register_stanza_plugin(Message, stanza.Offline)
 | 
			
		||||
 | 
			
		||||
    def get_count(self, **kwargs):
 | 
			
		||||
        return self.xmpp['xep_0030'].get_info(
 | 
			
		||||
                node='http://jabber.org/protocol/offline',
 | 
			
		||||
                local=False,
 | 
			
		||||
                **kwargs)
 | 
			
		||||
 | 
			
		||||
    def get_headers(self, **kwargs):
 | 
			
		||||
        return self.xmpp['xep_0030'].get_items(
 | 
			
		||||
                node='http://jabber.org/protocol/offline',
 | 
			
		||||
                local=False,
 | 
			
		||||
                **kwargs)
 | 
			
		||||
 | 
			
		||||
    def view(self, nodes, ifrom=None, block=True, timeout=None, callback=None):
 | 
			
		||||
        if not isinstance(nodes, (list, set)):
 | 
			
		||||
            nodes = [nodes]
 | 
			
		||||
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'get'
 | 
			
		||||
        iq['from'] = ifrom
 | 
			
		||||
        offline = iq['offline']
 | 
			
		||||
        for node in nodes:
 | 
			
		||||
            item = stanza.Item()
 | 
			
		||||
            item['node'] = node
 | 
			
		||||
            item['action'] = 'view'
 | 
			
		||||
            offline.append(item)
 | 
			
		||||
 | 
			
		||||
        collector = Collector(
 | 
			
		||||
            'Offline_Results_%s' % iq['id'],
 | 
			
		||||
            StanzaPath('message/offline'))
 | 
			
		||||
        self.xmpp.register_handler(collector)
 | 
			
		||||
 | 
			
		||||
        if not block and callback is not None:
 | 
			
		||||
            def wrapped_cb(iq):
 | 
			
		||||
                results = collector.stop()
 | 
			
		||||
                if iq['type'] == 'result':
 | 
			
		||||
                    iq['offline']['results'] = results
 | 
			
		||||
                callback(iq)
 | 
			
		||||
            return iq.send(block=block, timeout=timeout, callback=wrapped_cb)
 | 
			
		||||
        else:
 | 
			
		||||
            try:
 | 
			
		||||
                resp = iq.send(block=block, timeout=timeout, callback=callback)
 | 
			
		||||
                resp['offline']['results'] = collector.stop()
 | 
			
		||||
                return resp
 | 
			
		||||
            except XMPPError as e:
 | 
			
		||||
                collector.stop()
 | 
			
		||||
                raise e
 | 
			
		||||
 | 
			
		||||
    def remove(self, nodes, ifrom=None, block=True, timeout=None, callback=None):
 | 
			
		||||
        if not isinstance(nodes, (list, set)):
 | 
			
		||||
            nodes = [nodes]
 | 
			
		||||
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'set'
 | 
			
		||||
        iq['from'] = ifrom
 | 
			
		||||
        offline = iq['offline']
 | 
			
		||||
        for node in nodes:
 | 
			
		||||
            item = stanza.Item()
 | 
			
		||||
            item['node'] = node
 | 
			
		||||
            item['action'] = 'remove'
 | 
			
		||||
            offline.append(item)
 | 
			
		||||
 | 
			
		||||
        return iq.send(block=block, timeout=timeout, callback=callback)
 | 
			
		||||
 | 
			
		||||
    def fetch(self, ifrom=None, block=True, timeout=None, callback=None):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'set'
 | 
			
		||||
        iq['from'] = ifrom
 | 
			
		||||
        iq['offline']['fetch'] = True
 | 
			
		||||
 | 
			
		||||
        collector = Collector(
 | 
			
		||||
            'Offline_Results_%s' % iq['id'],
 | 
			
		||||
            StanzaPath('message/offline'))
 | 
			
		||||
        self.xmpp.register_handler(collector)
 | 
			
		||||
 | 
			
		||||
        if not block and callback is not None:
 | 
			
		||||
            def wrapped_cb(iq):
 | 
			
		||||
                results = collector.stop()
 | 
			
		||||
                if iq['type'] == 'result':
 | 
			
		||||
                    iq['offline']['results'] = results
 | 
			
		||||
                callback(iq)
 | 
			
		||||
            return iq.send(block=block, timeout=timeout, callback=wrapped_cb)
 | 
			
		||||
        else:
 | 
			
		||||
            try:
 | 
			
		||||
                resp = iq.send(block=block, timeout=timeout, callback=callback)
 | 
			
		||||
                resp['offline']['results'] = collector.stop()
 | 
			
		||||
                return resp
 | 
			
		||||
            except XMPPError as e:
 | 
			
		||||
                collector.stop()
 | 
			
		||||
                raise e
 | 
			
		||||
 | 
			
		||||
    def purge(self, ifrom=None, block=True, timeout=None, callback=None):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'set'
 | 
			
		||||
        iq['from'] = ifrom
 | 
			
		||||
        iq['offline']['purge'] = True
 | 
			
		||||
        return iq.send(block=block, timeout=timeout, callback=callback)
 | 
			
		||||
							
								
								
									
										53
									
								
								sleekxmpp/plugins/xep_0013/stanza.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								sleekxmpp/plugins/xep_0013/stanza.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permissio
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.jid import JID
 | 
			
		||||
from sleekxmpp.xmlstream import ElementBase, register_stanza_plugin
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Offline(ElementBase):
 | 
			
		||||
    name = 'offline'
 | 
			
		||||
    namespace = 'http://jabber.org/protocol/offline'
 | 
			
		||||
    plugin_attrib = 'offline'
 | 
			
		||||
    interfaces = set(['fetch', 'purge', 'results'])
 | 
			
		||||
    bool_interfaces = interfaces
 | 
			
		||||
 | 
			
		||||
    def setup(self, xml=None):
 | 
			
		||||
        ElementBase.setup(self, xml)
 | 
			
		||||
        self._results = []
 | 
			
		||||
 | 
			
		||||
    # The results interface is meant only as an easy
 | 
			
		||||
    # way to access the set of collected message responses
 | 
			
		||||
    # from the query.
 | 
			
		||||
 | 
			
		||||
    def get_results(self):
 | 
			
		||||
        return self._results
 | 
			
		||||
 | 
			
		||||
    def set_results(self, values):
 | 
			
		||||
        self._results = values
 | 
			
		||||
 | 
			
		||||
    def del_results(self):
 | 
			
		||||
        self._results = []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Item(ElementBase):
 | 
			
		||||
    name = 'item'
 | 
			
		||||
    namespace = 'http://jabber.org/protocol/offline'
 | 
			
		||||
    plugin_attrib = 'item'
 | 
			
		||||
    interfaces = set(['action', 'node', 'jid'])
 | 
			
		||||
 | 
			
		||||
    actions = set(['view', 'remove'])
 | 
			
		||||
 | 
			
		||||
    def get_jid(self):
 | 
			
		||||
        return JID(self._get_attr('jid'))
 | 
			
		||||
 | 
			
		||||
    def set_jid(self, value):
 | 
			
		||||
        self._set_attr('jid', str(value))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
register_stanza_plugin(Offline, Item, iterable=True)
 | 
			
		||||
							
								
								
									
										16
									
								
								sleekxmpp/plugins/xep_0016/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								sleekxmpp/plugins/xep_0016/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins.base import register_plugin
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins.xep_0016 import stanza
 | 
			
		||||
from sleekxmpp.plugins.xep_0016.stanza import Privacy
 | 
			
		||||
from sleekxmpp.plugins.xep_0016.privacy import XEP_0016
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
register_plugin(XEP_0016)
 | 
			
		||||
							
								
								
									
										110
									
								
								sleekxmpp/plugins/xep_0016/privacy.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								sleekxmpp/plugins/xep_0016/privacy.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,110 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sleekxmpp import Iq
 | 
			
		||||
from sleekxmpp.xmlstream import register_stanza_plugin
 | 
			
		||||
from sleekxmpp.plugins import BasePlugin
 | 
			
		||||
from sleekxmpp.plugins.xep_0016 import stanza
 | 
			
		||||
from sleekxmpp.plugins.xep_0016.stanza import Privacy, Item
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class XEP_0016(BasePlugin):
 | 
			
		||||
 | 
			
		||||
    name = 'xep_0016'
 | 
			
		||||
    description = 'XEP-0016: Privacy Lists'
 | 
			
		||||
    dependencies = set(['xep_0030'])
 | 
			
		||||
    stanza = stanza
 | 
			
		||||
 | 
			
		||||
    def plugin_init(self):
 | 
			
		||||
        register_stanza_plugin(Iq, Privacy)
 | 
			
		||||
 | 
			
		||||
    def plugin_end(self):
 | 
			
		||||
        self.xmpp['xep_0030'].del_feature(feature=Privacy.namespace)
 | 
			
		||||
 | 
			
		||||
    def session_bind(self, jid):
 | 
			
		||||
        self.xmpp['xep_0030'].add_feature(Privacy.namespace)
 | 
			
		||||
 | 
			
		||||
    def get_privacy_lists(self, block=True, timeout=None, callback=None):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'get'
 | 
			
		||||
        iq.enable('privacy')
 | 
			
		||||
        return iq.send(block=block, timeout=timeout, callback=callback)
 | 
			
		||||
 | 
			
		||||
    def get_list(self, name, block=True, timeout=None, callback=None):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'get'
 | 
			
		||||
        iq['privacy']['list']['name'] = name
 | 
			
		||||
        return iq.send(block=block, timeout=timeout, callback=callback)
 | 
			
		||||
 | 
			
		||||
    def get_active(self, block=True, timeout=None, callback=None):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'get'
 | 
			
		||||
        iq['privacy'].enable('active')
 | 
			
		||||
        return iq.send(block=block, timeout=timeout, callback=callback)
 | 
			
		||||
 | 
			
		||||
    def get_default(self, block=True, timeout=None, callback=None):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'get'
 | 
			
		||||
        iq['privacy'].enable('default')
 | 
			
		||||
        return iq.send(block=block, timeout=timeout, callback=callback)
 | 
			
		||||
 | 
			
		||||
    def activate(self, name, block=True, timeout=None, callback=None):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'set'
 | 
			
		||||
        iq['privacy']['active']['name'] = name
 | 
			
		||||
        return iq.send(block=block, timeout=timeout, callback=callback)
 | 
			
		||||
 | 
			
		||||
    def deactivate(self, block=True, timeout=None, callback=None):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'set'
 | 
			
		||||
        iq['privacy'].enable('active')
 | 
			
		||||
        return iq.send(block=block, timeout=timeout, callback=callback)
 | 
			
		||||
 | 
			
		||||
    def make_default(self, name, block=True, timeout=None, callback=None):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'set'
 | 
			
		||||
        iq['privacy']['default']['name'] = name
 | 
			
		||||
        return iq.send(block=block, timeout=timeout, callback=callback)
 | 
			
		||||
 | 
			
		||||
    def remove_default(self, block=True, timeout=None, callback=None):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'set'
 | 
			
		||||
        iq['privacy'].enable('default')
 | 
			
		||||
        return iq.send(block=block, timeout=timeout, callback=callback)
 | 
			
		||||
 | 
			
		||||
    def edit_list(self, name, rules, block=True, timeout=None, callback=None):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'set'
 | 
			
		||||
        iq['privacy']['list']['name'] = name
 | 
			
		||||
        priv_list = iq['privacy']['list']
 | 
			
		||||
 | 
			
		||||
        if not rules:
 | 
			
		||||
            rules = []
 | 
			
		||||
 | 
			
		||||
        for rule in rules:
 | 
			
		||||
            if isinstance(rule, Item):
 | 
			
		||||
                priv_list.append(rule)
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            priv_list.add_item(
 | 
			
		||||
                rule['value'],
 | 
			
		||||
                rule['action'],
 | 
			
		||||
                rule['order'],
 | 
			
		||||
                itype=rule.get('type', None),
 | 
			
		||||
                iq=rule.get('iq', None),
 | 
			
		||||
                message=rule.get('message', None),
 | 
			
		||||
                presence_in=rule.get('presence_in',
 | 
			
		||||
                    rule.get('presence-in', None)),
 | 
			
		||||
                presence_out=rule.get('presence_out',
 | 
			
		||||
                    rule.get('presence-out', None)))
 | 
			
		||||
 | 
			
		||||
    def remove_list(self, name, block=True, timeout=None, callback=None):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'set'
 | 
			
		||||
        iq['privacy']['list']['name'] = name
 | 
			
		||||
        return iq.send(block=block, timeout=timeout, callback=callback)
 | 
			
		||||
							
								
								
									
										103
									
								
								sleekxmpp/plugins/xep_0016/stanza.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								sleekxmpp/plugins/xep_0016/stanza.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,103 @@
 | 
			
		||||
from sleekxmpp.xmlstream import ET, ElementBase, register_stanza_plugin
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Privacy(ElementBase):
 | 
			
		||||
    name = 'query'
 | 
			
		||||
    namespace = 'jabber:iq:privacy'
 | 
			
		||||
    plugin_attrib = 'privacy'
 | 
			
		||||
    interfaces = set()
 | 
			
		||||
 | 
			
		||||
    def add_list(self, name):
 | 
			
		||||
        priv_list = List()
 | 
			
		||||
        priv_list['name'] = name
 | 
			
		||||
        self.append(priv_list)
 | 
			
		||||
        return priv_list
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Active(ElementBase):
 | 
			
		||||
    name = 'active'
 | 
			
		||||
    namespace = 'jabber:iq:privacy'
 | 
			
		||||
    plugin_attrib = name
 | 
			
		||||
    interfaces = set(['name'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Default(ElementBase):
 | 
			
		||||
    name = 'default'
 | 
			
		||||
    namespace = 'jabber:iq:privacy'
 | 
			
		||||
    plugin_attrib = name
 | 
			
		||||
    interfaces = set(['name'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class List(ElementBase):
 | 
			
		||||
    name = 'list'
 | 
			
		||||
    namespace = 'jabber:iq:privacy'
 | 
			
		||||
    plugin_attrib = name
 | 
			
		||||
    plugin_multi_attrib = 'lists'
 | 
			
		||||
    interfaces = set(['name'])
 | 
			
		||||
 | 
			
		||||
    def add_item(self, value, action, order, itype=None, iq=False,
 | 
			
		||||
                 message=False, presence_in=False, presence_out=False):
 | 
			
		||||
        item = Item()
 | 
			
		||||
        item.values = {'type': itype,
 | 
			
		||||
                       'value': value,
 | 
			
		||||
                       'action': action,
 | 
			
		||||
                       'order': order,
 | 
			
		||||
                       'message': message,
 | 
			
		||||
                       'iq': iq,
 | 
			
		||||
                       'presence_in': presence_in,
 | 
			
		||||
                       'presence_out': presence_out}
 | 
			
		||||
        self.append(item)
 | 
			
		||||
        return item
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Item(ElementBase):
 | 
			
		||||
    name = 'item'
 | 
			
		||||
    namespace = 'jabber:iq:privacy'
 | 
			
		||||
    plugin_attrib = name
 | 
			
		||||
    plugin_multi_attrib = 'items'
 | 
			
		||||
    interfaces = set(['type', 'value', 'action', 'order', 'iq',
 | 
			
		||||
                      'message', 'presence_in', 'presence_out'])
 | 
			
		||||
    bool_interfaces = set(['message', 'iq', 'presence_in', 'presence_out'])
 | 
			
		||||
 | 
			
		||||
    type_values = ('', 'jid', 'group', 'subscription')
 | 
			
		||||
    action_values = ('allow', 'deny')
 | 
			
		||||
 | 
			
		||||
    def set_type(self, value):
 | 
			
		||||
        if value and value not in self.type_values:
 | 
			
		||||
            raise ValueError('Unknown type value: %s' % value)
 | 
			
		||||
        else:
 | 
			
		||||
            self._set_attr('type', value)
 | 
			
		||||
 | 
			
		||||
    def set_action(self, value):
 | 
			
		||||
        if value not in self.action_values:
 | 
			
		||||
            raise ValueError('Unknown action value: %s' % value)
 | 
			
		||||
        else:
 | 
			
		||||
            self._set_attr('action', value)
 | 
			
		||||
 | 
			
		||||
    def set_presence_in(self, value):
 | 
			
		||||
        keep = True if value else False
 | 
			
		||||
        self._set_sub_text('presence-in', '', keep=keep)
 | 
			
		||||
 | 
			
		||||
    def get_presence_in(self):
 | 
			
		||||
        pres = self.xml.find('{%s}presence-in' % self.namespace)
 | 
			
		||||
        return pres is not None
 | 
			
		||||
 | 
			
		||||
    def del_presence_in(self):
 | 
			
		||||
        self._del_sub('{%s}presence-in' % self.namespace)
 | 
			
		||||
 | 
			
		||||
    def set_presence_out(self, value):
 | 
			
		||||
        keep = True if value else False
 | 
			
		||||
        self._set_sub_text('presence-in', '', keep=keep)
 | 
			
		||||
 | 
			
		||||
    def get_presence_out(self):
 | 
			
		||||
        pres = self.xml.find('{%s}presence-in' % self.namespace)
 | 
			
		||||
        return pres is not None
 | 
			
		||||
 | 
			
		||||
    def del_presence_out(self):
 | 
			
		||||
        self._del_sub('{%s}presence-in' % self.namespace)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
register_stanza_plugin(Privacy, Active)
 | 
			
		||||
register_stanza_plugin(Privacy, Default)
 | 
			
		||||
register_stanza_plugin(Privacy, List, iterable=True)
 | 
			
		||||
register_stanza_plugin(List, Item, iterable=True)
 | 
			
		||||
@@ -288,7 +288,7 @@ class XEP_0030(BasePlugin):
 | 
			
		||||
                'cached': cached}
 | 
			
		||||
        return self.api['has_identity'](jid, node, ifrom, data)
 | 
			
		||||
 | 
			
		||||
    def get_info(self, jid=None, node=None, local=False,
 | 
			
		||||
    def get_info(self, jid=None, node=None, local=None,
 | 
			
		||||
                       cached=None, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Retrieve the disco#info results from a given JID/node combination.
 | 
			
		||||
@@ -324,18 +324,21 @@ class XEP_0030(BasePlugin):
 | 
			
		||||
            callback -- Optional callback to execute when a reply is
 | 
			
		||||
                        received instead of blocking and waiting for
 | 
			
		||||
                        the reply.
 | 
			
		||||
            timeout_callback -- Optional callback to execute when no result 
 | 
			
		||||
                        has been received in timeout seconds.
 | 
			
		||||
        """
 | 
			
		||||
        if jid is not None and not isinstance(jid, JID):
 | 
			
		||||
            jid = JID(jid)
 | 
			
		||||
            if self.xmpp.is_component:
 | 
			
		||||
                if jid.domain == self.xmpp.boundjid.domain:
 | 
			
		||||
                    local = True
 | 
			
		||||
            else:
 | 
			
		||||
                if str(jid) == str(self.xmpp.boundjid):
 | 
			
		||||
                    local = True
 | 
			
		||||
            jid = jid.full
 | 
			
		||||
        elif jid in (None, ''):
 | 
			
		||||
            local = True
 | 
			
		||||
        if local is None:
 | 
			
		||||
            if jid is not None and not isinstance(jid, JID):
 | 
			
		||||
                jid = JID(jid)
 | 
			
		||||
                if self.xmpp.is_component:
 | 
			
		||||
                    if jid.domain == self.xmpp.boundjid.domain:
 | 
			
		||||
                        local = True
 | 
			
		||||
                else:
 | 
			
		||||
                    if str(jid) == str(self.xmpp.boundjid):
 | 
			
		||||
                        local = True
 | 
			
		||||
                jid = jid.full
 | 
			
		||||
            elif jid in (None, ''):
 | 
			
		||||
                local = True
 | 
			
		||||
 | 
			
		||||
        if local:
 | 
			
		||||
            log.debug("Looking up local disco#info data " + \
 | 
			
		||||
@@ -363,7 +366,8 @@ class XEP_0030(BasePlugin):
 | 
			
		||||
        iq['disco_info']['node'] = node if node else ''
 | 
			
		||||
        return iq.send(timeout=kwargs.get('timeout', None),
 | 
			
		||||
                       block=kwargs.get('block', True),
 | 
			
		||||
                       callback=kwargs.get('callback', None))
 | 
			
		||||
                       callback=kwargs.get('callback', None),
 | 
			
		||||
                       timeout_callback=kwargs.get('timeout_callback', None))
 | 
			
		||||
 | 
			
		||||
    def set_info(self, jid=None, node=None, info=None):
 | 
			
		||||
        """
 | 
			
		||||
@@ -404,8 +408,10 @@ class XEP_0030(BasePlugin):
 | 
			
		||||
            iterator -- If True, return a result set iterator using
 | 
			
		||||
                        the XEP-0059 plugin, if the plugin is loaded.
 | 
			
		||||
                        Otherwise the parameter is ignored.
 | 
			
		||||
            timeout_callback -- Optional callback to execute when no result 
 | 
			
		||||
                        has been received in timeout seconds.
 | 
			
		||||
        """
 | 
			
		||||
        if local or jid is None:
 | 
			
		||||
        if local or local is None and jid is None:
 | 
			
		||||
            items = self.api['get_items'](jid, node,
 | 
			
		||||
                    kwargs.get('ifrom', None),
 | 
			
		||||
                    kwargs)
 | 
			
		||||
@@ -422,7 +428,8 @@ class XEP_0030(BasePlugin):
 | 
			
		||||
        else:
 | 
			
		||||
            return iq.send(timeout=kwargs.get('timeout', None),
 | 
			
		||||
                           block=kwargs.get('block', True),
 | 
			
		||||
                           callback=kwargs.get('callback', None))
 | 
			
		||||
                           callback=kwargs.get('callback', None),
 | 
			
		||||
                           timeout_callback=kwargs.get('timeout_callback', None))
 | 
			
		||||
 | 
			
		||||
    def set_items(self, jid=None, node=None, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
 
 | 
			
		||||
@@ -156,6 +156,7 @@ class XEP_0045(BasePlugin):
 | 
			
		||||
        entry = pr['muc'].getStanzaValues()
 | 
			
		||||
        entry['show'] = pr['show']
 | 
			
		||||
        entry['status'] = pr['status']
 | 
			
		||||
        entry['alt_nick'] = pr['nick']
 | 
			
		||||
        if pr['type'] == 'unavailable':
 | 
			
		||||
            if entry['nick'] in self.rooms[entry['room']]:
 | 
			
		||||
                del self.rooms[entry['room']][entry['nick']]
 | 
			
		||||
@@ -244,11 +245,11 @@ class XEP_0045(BasePlugin):
 | 
			
		||||
        stanza = self.xmpp.makePresence(pto="%s/%s" % (room, nick), pstatus=pstatus, pshow=pshow, pfrom=pfrom)
 | 
			
		||||
        x = ET.Element('{http://jabber.org/protocol/muc}x')
 | 
			
		||||
        if password:
 | 
			
		||||
            passelement = ET.Element('password')
 | 
			
		||||
            passelement = ET.Element('{http://jabber.org/protocol/muc}password')
 | 
			
		||||
            passelement.text = password
 | 
			
		||||
            x.append(passelement)
 | 
			
		||||
        if maxhistory:
 | 
			
		||||
            history = ET.Element('history')
 | 
			
		||||
            history = ET.Element('{http://jabber.org/protocol/muc}history')
 | 
			
		||||
            if maxhistory ==  "0":
 | 
			
		||||
                history.attrib['maxchars'] = maxhistory
 | 
			
		||||
            else:
 | 
			
		||||
@@ -270,10 +271,10 @@ class XEP_0045(BasePlugin):
 | 
			
		||||
            iq['from'] = ifrom
 | 
			
		||||
        iq['to'] = room
 | 
			
		||||
        query = ET.Element('{http://jabber.org/protocol/muc#owner}query')
 | 
			
		||||
        destroy = ET.Element('destroy')
 | 
			
		||||
        destroy = ET.Element('{http://jabber.org/protocol/muc#owner}destroy')
 | 
			
		||||
        if altroom:
 | 
			
		||||
            destroy.attrib['jid'] = altroom
 | 
			
		||||
        xreason = ET.Element('reason')
 | 
			
		||||
        xreason = ET.Element('{http://jabber.org/protocol/muc#owner}reason')
 | 
			
		||||
        xreason.text = reason
 | 
			
		||||
        destroy.append(xreason)
 | 
			
		||||
        query.append(destroy)
 | 
			
		||||
@@ -293,9 +294,9 @@ class XEP_0045(BasePlugin):
 | 
			
		||||
            raise TypeError
 | 
			
		||||
        query = ET.Element('{http://jabber.org/protocol/muc#admin}query')
 | 
			
		||||
        if nick is not None:
 | 
			
		||||
            item = ET.Element('item', {'affiliation':affiliation, 'nick':nick})
 | 
			
		||||
            item = ET.Element('{http://jabber.org/protocol/muc#admin}item', {'affiliation':affiliation, 'nick':nick})
 | 
			
		||||
        else:
 | 
			
		||||
            item = ET.Element('item', {'affiliation':affiliation, 'jid':jid})
 | 
			
		||||
            item = ET.Element('{http://jabber.org/protocol/muc#admin}item', {'affiliation':affiliation, 'jid':jid})
 | 
			
		||||
        query.append(item)
 | 
			
		||||
        iq = self.xmpp.makeIqSet(query)
 | 
			
		||||
        iq['to'] = room
 | 
			
		||||
@@ -316,7 +317,7 @@ class XEP_0045(BasePlugin):
 | 
			
		||||
        x = ET.Element('{http://jabber.org/protocol/muc#user}x')
 | 
			
		||||
        invite = ET.Element('{http://jabber.org/protocol/muc#user}invite', {'to': jid})
 | 
			
		||||
        if reason:
 | 
			
		||||
            rxml = ET.Element('reason')
 | 
			
		||||
            rxml = ET.Element('{http://jabber.org/protocol/muc#user}reason')
 | 
			
		||||
            rxml.text = reason
 | 
			
		||||
            invite.append(rxml)
 | 
			
		||||
        x.append(invite)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
import re
 | 
			
		||||
import base64
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.util import bytes
 | 
			
		||||
from sleekxmpp.exceptions import XMPPError
 | 
			
		||||
from sleekxmpp.xmlstream import ElementBase
 | 
			
		||||
from sleekxmpp.thirdparty.suelta.util import bytes
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
VALID_B64 = re.compile(r'[A-Za-z0-9\+\/]*=*')
 | 
			
		||||
@@ -14,7 +14,7 @@ def to_b64(data):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def from_b64(data):
 | 
			
		||||
    return bytes(base64.b64decode(bytes(data))).decode('utf-8')
 | 
			
		||||
    return bytes(base64.b64decode(bytes(data)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Open(ElementBase):
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								sleekxmpp/plugins/xep_0049/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								sleekxmpp/plugins/xep_0049/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins.base import register_plugin
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins.xep_0049.stanza import PrivateXML
 | 
			
		||||
from sleekxmpp.plugins.xep_0049.private_storage import XEP_0049
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
register_plugin(XEP_0049)
 | 
			
		||||
							
								
								
									
										53
									
								
								sleekxmpp/plugins/xep_0049/private_storage.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								sleekxmpp/plugins/xep_0049/private_storage.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from sleekxmpp import Iq
 | 
			
		||||
from sleekxmpp.plugins import BasePlugin
 | 
			
		||||
from sleekxmpp.xmlstream.handler import Callback
 | 
			
		||||
from sleekxmpp.xmlstream.matcher import StanzaPath
 | 
			
		||||
from sleekxmpp.xmlstream import register_stanza_plugin
 | 
			
		||||
from sleekxmpp.plugins.xep_0049 import stanza, PrivateXML
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
log = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class XEP_0049(BasePlugin):
 | 
			
		||||
 | 
			
		||||
    name = 'xep_0049'
 | 
			
		||||
    description = 'XEP-0049: Private XML Storage'
 | 
			
		||||
    dependencies = set([])
 | 
			
		||||
    stanza = stanza
 | 
			
		||||
 | 
			
		||||
    def plugin_init(self):
 | 
			
		||||
        register_stanza_plugin(Iq, PrivateXML)
 | 
			
		||||
 | 
			
		||||
    def register(self, stanza):
 | 
			
		||||
        register_stanza_plugin(PrivateXML, stanza, iterable=True)
 | 
			
		||||
 | 
			
		||||
    def store(self, data, ifrom=None, block=True, timeout=None, callback=None):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'set'
 | 
			
		||||
        iq['from'] = ifrom
 | 
			
		||||
 | 
			
		||||
        if not isinstance(data, list):
 | 
			
		||||
            data = [data]
 | 
			
		||||
 | 
			
		||||
        for elem in data:
 | 
			
		||||
            iq['private'].append(elem)
 | 
			
		||||
 | 
			
		||||
        return iq.send(block=block, timeout=timeout, callback=callback)
 | 
			
		||||
 | 
			
		||||
    def retrieve(self, name, ifrom=None, block=True, timeout=None, callback=None):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'get'
 | 
			
		||||
        iq['from'] = ifrom
 | 
			
		||||
        iq['private'].enable(name)
 | 
			
		||||
        return iq.send(block=block, timeout=timeout, callback=callback)
 | 
			
		||||
							
								
								
									
										17
									
								
								sleekxmpp/plugins/xep_0049/stanza.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								sleekxmpp/plugins/xep_0049/stanza.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.xmlstream import ET, ElementBase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PrivateXML(ElementBase):
 | 
			
		||||
 | 
			
		||||
    name = 'query'
 | 
			
		||||
    namespace = 'jabber:iq:private'
 | 
			
		||||
    plugin_attrib = 'private'
 | 
			
		||||
    interfaces = set()
 | 
			
		||||
@@ -187,12 +187,6 @@ class XEP_0050(BasePlugin):
 | 
			
		||||
            jid = JID(jid)
 | 
			
		||||
        item_jid = jid.full
 | 
			
		||||
 | 
			
		||||
        # Client disco uses only the bare JID
 | 
			
		||||
        if self.xmpp.is_component:
 | 
			
		||||
            jid = jid.full
 | 
			
		||||
        else:
 | 
			
		||||
            jid = jid.bare
 | 
			
		||||
 | 
			
		||||
        self.xmpp['xep_0030'].add_identity(category='automation',
 | 
			
		||||
                                           itype='command-list',
 | 
			
		||||
                                           name='Ad-Hoc commands',
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,7 @@
 | 
			
		||||
import base64
 | 
			
		||||
import datetime as dt
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.thirdparty.suelta.util import bytes
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.util import bytes
 | 
			
		||||
from sleekxmpp.xmlstream import ElementBase, ET, register_stanza_plugin, JID
 | 
			
		||||
from sleekxmpp.plugins import xep_0082
 | 
			
		||||
 | 
			
		||||
@@ -542,6 +541,7 @@ register_stanza_plugin(VCardTemp, Logo, iterable=True)
 | 
			
		||||
register_stanza_plugin(VCardTemp, Mailer, iterable=True)
 | 
			
		||||
register_stanza_plugin(VCardTemp, Note, iterable=True)
 | 
			
		||||
register_stanza_plugin(VCardTemp, Nickname, iterable=True)
 | 
			
		||||
register_stanza_plugin(VCardTemp, Org, iterable=True)
 | 
			
		||||
register_stanza_plugin(VCardTemp, Photo, iterable=True)
 | 
			
		||||
register_stanza_plugin(VCardTemp, ProdID, iterable=True)
 | 
			
		||||
register_stanza_plugin(VCardTemp, Rev, iterable=True)
 | 
			
		||||
 
 | 
			
		||||
@@ -97,8 +97,8 @@ class XEP_0054(BasePlugin):
 | 
			
		||||
 | 
			
		||||
    def publish_vcard(self, vcard=None, jid=None, block=True, ifrom=None,
 | 
			
		||||
                      callback=None, timeout=None):
 | 
			
		||||
        self.api['set_vcard'](jid, None, ifrom, vcard)
 | 
			
		||||
        if self.xmpp.is_component:
 | 
			
		||||
            self.api['set_vcard'](jid, None, ifrom, vcard)
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
 
 | 
			
		||||
@@ -25,11 +25,14 @@ class ResultIterator():
 | 
			
		||||
    An iterator for Result Set Managment
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, query, interface, amount=10, start=None, reverse=False):
 | 
			
		||||
    def __init__(self, query, interface, results='substanzas', amount=10,
 | 
			
		||||
                       start=None, reverse=False):
 | 
			
		||||
        """
 | 
			
		||||
        Arguments:
 | 
			
		||||
           query     -- The template query
 | 
			
		||||
           interface -- The substanza of the query, for example disco_items
 | 
			
		||||
           results   -- The query stanza's interface which provides a
 | 
			
		||||
                        countable list of query results.
 | 
			
		||||
           amount    -- The max amounts of items to request per iteration
 | 
			
		||||
           start     -- From which item id to start
 | 
			
		||||
           reverse   -- If True, page backwards through the results
 | 
			
		||||
@@ -46,6 +49,7 @@ class ResultIterator():
 | 
			
		||||
        self.amount = amount
 | 
			
		||||
        self.start = start
 | 
			
		||||
        self.interface = interface
 | 
			
		||||
        self.results = results
 | 
			
		||||
        self.reverse = reverse
 | 
			
		||||
        self._stop = False
 | 
			
		||||
 | 
			
		||||
@@ -85,7 +89,7 @@ class ResultIterator():
 | 
			
		||||
               r[self.interface]['rsm']['first_index']:
 | 
			
		||||
                count = int(r[self.interface]['rsm']['count'])
 | 
			
		||||
                first = int(r[self.interface]['rsm']['first_index'])
 | 
			
		||||
                num_items = len(r[self.interface]['substanzas'])
 | 
			
		||||
                num_items = len(r[self.interface][self.results])
 | 
			
		||||
                if first + num_items == count:
 | 
			
		||||
                    self._stop = True
 | 
			
		||||
 | 
			
		||||
@@ -123,7 +127,7 @@ class XEP_0059(BasePlugin):
 | 
			
		||||
    def session_bind(self, jid):
 | 
			
		||||
        self.xmpp['xep_0030'].add_feature(Set.namespace)
 | 
			
		||||
 | 
			
		||||
    def iterate(self, stanza, interface):
 | 
			
		||||
    def iterate(self, stanza, interface, results='substanzas'):
 | 
			
		||||
        """
 | 
			
		||||
        Create a new result set iterator for a given stanza query.
 | 
			
		||||
 | 
			
		||||
@@ -135,5 +139,7 @@ class XEP_0059(BasePlugin):
 | 
			
		||||
                         result set management stanza should be
 | 
			
		||||
                         appended. For example, for disco#items queries
 | 
			
		||||
                         the interface 'disco_items' should be used.
 | 
			
		||||
            results   -- The name of the interface containing the
 | 
			
		||||
                         query results (typically just 'substanzas').
 | 
			
		||||
        """
 | 
			
		||||
        return ResultIterator(stanza, interface)
 | 
			
		||||
        return ResultIterator(stanza, interface, results)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								sleekxmpp/plugins/xep_0065/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								sleekxmpp/plugins/xep_0065/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
from sleekxmpp.plugins.base import register_plugin
 | 
			
		||||
from sleekxmpp.plugins.xep_0065.proxy import XEP_0065
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
register_plugin(XEP_0065)
 | 
			
		||||
							
								
								
									
										359
									
								
								sleekxmpp/plugins/xep_0065/proxy.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										359
									
								
								sleekxmpp/plugins/xep_0065/proxy.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,359 @@
 | 
			
		||||
import sys
 | 
			
		||||
import logging
 | 
			
		||||
import struct
 | 
			
		||||
 | 
			
		||||
from threading import Thread, Event
 | 
			
		||||
from hashlib import sha1
 | 
			
		||||
from select import select
 | 
			
		||||
from uuid import uuid4
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins.xep_0065 import stanza
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins.base import base_plugin
 | 
			
		||||
from sleekxmpp.xmlstream.handler import Callback
 | 
			
		||||
from sleekxmpp.xmlstream.matcher import StanzaPath
 | 
			
		||||
from sleekxmpp.thirdparty.socks import socksocket, PROXY_TYPE_SOCKS5
 | 
			
		||||
 | 
			
		||||
# Registers the sleekxmpp logger
 | 
			
		||||
log = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class XEP_0065(base_plugin):
 | 
			
		||||
    """
 | 
			
		||||
    XEP-0065 Socks5 Bytestreams
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    description = "Socks5 Bytestreams"
 | 
			
		||||
    dependencies = set(['xep_0030', ])
 | 
			
		||||
    xep = '0065'
 | 
			
		||||
    name = 'xep_0065'
 | 
			
		||||
 | 
			
		||||
    # A dict contains for each SID, the proxy thread currently
 | 
			
		||||
    # running.
 | 
			
		||||
    proxy_threads = {}
 | 
			
		||||
 | 
			
		||||
    def plugin_init(self):
 | 
			
		||||
        """ Initializes the xep_0065 plugin and all event callbacks.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        # Shortcuts to access to the xep_0030 plugin.
 | 
			
		||||
        self.disco = self.xmpp['xep_0030']
 | 
			
		||||
 | 
			
		||||
        # Handler for the streamhost stanza.
 | 
			
		||||
        self.xmpp.registerHandler(
 | 
			
		||||
            Callback('Socks5 Bytestreams',
 | 
			
		||||
                     StanzaPath('iq@type=set/socks/streamhost'),
 | 
			
		||||
                     self._handle_streamhost))
 | 
			
		||||
 | 
			
		||||
        # Handler for the streamhost-used stanza.
 | 
			
		||||
        self.xmpp.registerHandler(
 | 
			
		||||
            Callback('Socks5 Bytestreams',
 | 
			
		||||
                     StanzaPath('iq@type=result/socks/streamhost-used'),
 | 
			
		||||
                     self._handle_streamhost_used))
 | 
			
		||||
 | 
			
		||||
    def get_socket(self, sid):
 | 
			
		||||
        """ Returns the socket associated to the SID.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        proxy = self.proxy_threads.get(sid)
 | 
			
		||||
        if proxy:
 | 
			
		||||
            return proxy.s
 | 
			
		||||
 | 
			
		||||
    def handshake(self, to, streamer=None):
 | 
			
		||||
        """ Starts the handshake to establish the socks5 bytestreams
 | 
			
		||||
        connection.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        # Discovers the proxy.
 | 
			
		||||
        self.streamer = streamer or self.discover_proxy()
 | 
			
		||||
 | 
			
		||||
        # Requester requests network address from the proxy.
 | 
			
		||||
        streamhost = self.get_network_address(self.streamer)
 | 
			
		||||
        self.proxy_host = streamhost['socks']['streamhost']['host']
 | 
			
		||||
        self.proxy_port = streamhost['socks']['streamhost']['port']
 | 
			
		||||
 | 
			
		||||
        # Generates the SID for this new handshake.
 | 
			
		||||
        sid = uuid4().hex
 | 
			
		||||
 | 
			
		||||
        # Requester initiates S5B negotation with Target by sending
 | 
			
		||||
        # IQ-set that includes the JabberID and network address of
 | 
			
		||||
        # StreamHost as well as the StreamID (SID) of the proposed
 | 
			
		||||
        # bytestream.
 | 
			
		||||
        iq = self.xmpp.Iq(sto=to, stype='set')
 | 
			
		||||
        iq['socks']['sid'] = sid
 | 
			
		||||
        iq['socks']['streamhost']['jid'] = self.streamer
 | 
			
		||||
        iq['socks']['streamhost']['host'] = self.proxy_host
 | 
			
		||||
        iq['socks']['streamhost']['port'] = self.proxy_port
 | 
			
		||||
 | 
			
		||||
        # Sends the new IQ.
 | 
			
		||||
        return iq.send()
 | 
			
		||||
 | 
			
		||||
    def discover_proxy(self):
 | 
			
		||||
        """ Auto-discovers (using XEP 0030) the available bytestream
 | 
			
		||||
        proxy on the XMPP server.
 | 
			
		||||
 | 
			
		||||
        Returns the JID of the proxy.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        # Gets all disco items.
 | 
			
		||||
        disco_items = self.disco.get_items(self.xmpp.server)
 | 
			
		||||
 | 
			
		||||
        for item in disco_items['disco_items']['items']:
 | 
			
		||||
            # For each items, gets the disco info.
 | 
			
		||||
            disco_info = self.disco.get_info(item[0])
 | 
			
		||||
 | 
			
		||||
            # Gets and verifies if the identity is a bytestream proxy.
 | 
			
		||||
            identities = disco_info['disco_info']['identities']
 | 
			
		||||
            for identity in identities:
 | 
			
		||||
                if identity[0] == 'proxy' and identity[1] == 'bytestreams':
 | 
			
		||||
                    # Returns when the first occurence is found.
 | 
			
		||||
                    return '%s' % disco_info['from']
 | 
			
		||||
 | 
			
		||||
    def get_network_address(self, streamer):
 | 
			
		||||
        """ Gets the streamhost information of the proxy.
 | 
			
		||||
 | 
			
		||||
        streamer : The jid of the proxy.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        iq = self.xmpp.Iq(sto=streamer, stype='get')
 | 
			
		||||
        iq['socks']  # Adds the query eleme to the iq.
 | 
			
		||||
 | 
			
		||||
        return iq.send()
 | 
			
		||||
 | 
			
		||||
    def _handle_streamhost(self, iq):
 | 
			
		||||
        """ Handles all streamhost stanzas.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        # Registers the streamhost info.
 | 
			
		||||
        self.streamer = iq['socks']['streamhost']['jid']
 | 
			
		||||
        self.proxy_host = iq['socks']['streamhost']['host']
 | 
			
		||||
        self.proxy_port = iq['socks']['streamhost']['port']
 | 
			
		||||
 | 
			
		||||
        # Sets the SID, the requester and the target.
 | 
			
		||||
        sid = iq['socks']['sid']
 | 
			
		||||
        requester = '%s' % iq['from']
 | 
			
		||||
        target = '%s' % self.xmpp.boundjid
 | 
			
		||||
 | 
			
		||||
        # Next the Target attempts to open a standard TCP socket on
 | 
			
		||||
        # the network address of the Proxy.
 | 
			
		||||
        self.proxy_thread = Proxy(sid, requester, target, self.proxy_host,
 | 
			
		||||
                                  self.proxy_port, self.on_recv)
 | 
			
		||||
        self.proxy_thread.start()
 | 
			
		||||
 | 
			
		||||
        # Registers the new thread in the proxy_thread dict.
 | 
			
		||||
        self.proxy_threads[sid] = self.proxy_thread
 | 
			
		||||
 | 
			
		||||
        # Wait until the proxy is connected
 | 
			
		||||
        self.proxy_thread.connected.wait()
 | 
			
		||||
 | 
			
		||||
        # Replies to the incoming iq with a streamhost-used stanza.
 | 
			
		||||
        res_iq = iq.reply()
 | 
			
		||||
        res_iq['socks']['sid'] = sid
 | 
			
		||||
        res_iq['socks']['streamhost-used']['jid'] = self.streamer
 | 
			
		||||
 | 
			
		||||
        # Sends the IQ
 | 
			
		||||
        return res_iq.send()
 | 
			
		||||
 | 
			
		||||
    def _handle_streamhost_used(self, iq):
 | 
			
		||||
        """ Handles all streamhost-used stanzas.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        # Sets the SID, the requester and the target.
 | 
			
		||||
        sid = iq['socks']['sid']
 | 
			
		||||
        requester = '%s' % self.xmpp.boundjid
 | 
			
		||||
        target  = '%s' % iq['from']
 | 
			
		||||
 | 
			
		||||
        # The Requester will establish a connection to the SOCKS5
 | 
			
		||||
        # proxy in the same way the Target did.
 | 
			
		||||
        self.proxy_thread = Proxy(sid, requester, target, self.proxy_host,
 | 
			
		||||
                                  self.proxy_port, self.on_recv)
 | 
			
		||||
        self.proxy_thread.start()
 | 
			
		||||
 | 
			
		||||
        # Registers the new thread in the proxy_thread dict.
 | 
			
		||||
        self.proxy_threads[sid] = self.proxy_thread
 | 
			
		||||
 | 
			
		||||
        # Wait until the proxy is connected
 | 
			
		||||
        self.proxy_thread.connected.wait()
 | 
			
		||||
 | 
			
		||||
        # Requester sends IQ-set to StreamHost requesting that
 | 
			
		||||
        # StreamHost activate the bytestream associated with the
 | 
			
		||||
        # StreamID.
 | 
			
		||||
        self.activate(iq['socks']['sid'], target)
 | 
			
		||||
 | 
			
		||||
    def activate(self, sid, to):
 | 
			
		||||
        """ IQ-set to StreamHost requesting that StreamHost activate
 | 
			
		||||
        the bytestream associated with the StreamID.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        # Creates the activate IQ.
 | 
			
		||||
        act_iq = self.xmpp.Iq(sto=self.streamer, stype='set')
 | 
			
		||||
        act_iq['socks']['sid'] = sid
 | 
			
		||||
        act_iq['socks']['activate'] = to
 | 
			
		||||
 | 
			
		||||
        # Send the IQ.
 | 
			
		||||
        act_iq.send()
 | 
			
		||||
 | 
			
		||||
    def deactivate(self, sid):
 | 
			
		||||
        """ Closes the Proxy thread associated to this SID.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        proxy = self.proxy_threads.get(sid)
 | 
			
		||||
        if proxy:
 | 
			
		||||
            proxy.s.close()
 | 
			
		||||
            del self.proxy_threads[sid]
 | 
			
		||||
 | 
			
		||||
    def close(self):
 | 
			
		||||
        """ Closes all Proxy threads.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        for sid, proxy in self.proxy_threads.items():
 | 
			
		||||
            proxy.s.close()
 | 
			
		||||
            del self.proxy_threads[sid]
 | 
			
		||||
 | 
			
		||||
    def send(self, sid, data):
 | 
			
		||||
        """ Sends the data over the Proxy socket associated to the
 | 
			
		||||
        SID.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        proxy = self.proxy_threads.get(sid)
 | 
			
		||||
        if proxy:
 | 
			
		||||
            proxy.s.sendall(data)
 | 
			
		||||
 | 
			
		||||
    def on_recv(self, sid, data):
 | 
			
		||||
        """ Calls when data is recv from the Proxy socket associated
 | 
			
		||||
        to the SID.
 | 
			
		||||
 | 
			
		||||
        Triggers a socks_closed event if the socket is closed. The sid
 | 
			
		||||
        is passed to this event.
 | 
			
		||||
 | 
			
		||||
        Triggers a socks_recv event if there's available data. A dict
 | 
			
		||||
        that contains the sid and the data is passed to this event.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        proxy = self.proxy_threads.get(sid)
 | 
			
		||||
        if proxy:
 | 
			
		||||
            if not data:
 | 
			
		||||
                self.xmpp.event('socks_closed', sid)
 | 
			
		||||
            else:
 | 
			
		||||
                self.xmpp.event('socks_recv', {'sid': sid, 'data': data})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Proxy(Thread):
 | 
			
		||||
    """ Establishes in a thread a connection between the client and
 | 
			
		||||
    the server-side Socks5 proxy.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, sid, requester, target, proxy, proxy_port,
 | 
			
		||||
                 on_recv):
 | 
			
		||||
        """ Initializes the proxy thread.
 | 
			
		||||
 | 
			
		||||
        sid        : The StreamID. <str>
 | 
			
		||||
        requester  : The JID of the requester. <str>
 | 
			
		||||
        target     : The JID of the target. <str>
 | 
			
		||||
        proxy_host : The hostname or the IP of the proxy. <str>
 | 
			
		||||
        proxy_port : The port of the proxy. <str> or <int>
 | 
			
		||||
        on_recv    : A callback called when data are received from the
 | 
			
		||||
                     socket. <Callable>
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        # Initializes the thread.
 | 
			
		||||
        Thread.__init__(self)
 | 
			
		||||
 | 
			
		||||
        # Because the xep_0065 plugin uses the proxy_port as string,
 | 
			
		||||
        # the Proxy class accepts the proxy_port argument as a string
 | 
			
		||||
        # or an integer. Here, we force to use the port as an integer.
 | 
			
		||||
        proxy_port = int(proxy_port)
 | 
			
		||||
 | 
			
		||||
        # Creates a connected event to warn when to proxy is
 | 
			
		||||
        # connected.
 | 
			
		||||
        self.connected = Event()
 | 
			
		||||
 | 
			
		||||
        # Registers the arguments.
 | 
			
		||||
        self.sid = sid
 | 
			
		||||
        self.requester = requester
 | 
			
		||||
        self.target = target
 | 
			
		||||
        self.proxy = proxy
 | 
			
		||||
        self.proxy_port = proxy_port
 | 
			
		||||
        self.on_recv = on_recv
 | 
			
		||||
 | 
			
		||||
    def run(self):
 | 
			
		||||
        """ Starts the thread.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        # Creates the socks5 proxy socket
 | 
			
		||||
        self.s = socksocket()
 | 
			
		||||
        self.s.setproxy(PROXY_TYPE_SOCKS5, self.proxy, port=self.proxy_port)
 | 
			
		||||
 | 
			
		||||
        # The hostname MUST be SHA1(SID + Requester JID + Target JID)
 | 
			
		||||
        # where the output is hexadecimal-encoded (not binary).
 | 
			
		||||
        digest = sha1()
 | 
			
		||||
        digest.update(self.sid)  # SID
 | 
			
		||||
        digest.update(self.requester)  # Requester JID
 | 
			
		||||
        digest.update(self.target)  # Target JID
 | 
			
		||||
 | 
			
		||||
        # Computes the digest in hex.
 | 
			
		||||
        dest = '%s' % digest.hexdigest()
 | 
			
		||||
 | 
			
		||||
        # The port MUST be 0.
 | 
			
		||||
        self.s.connect((dest, 0))
 | 
			
		||||
        log.info('Socket connected.')
 | 
			
		||||
        self.connected.set()
 | 
			
		||||
 | 
			
		||||
        # Blocks until the socket need to be closed.
 | 
			
		||||
        self.listen()
 | 
			
		||||
 | 
			
		||||
        # Closes the socket.
 | 
			
		||||
        self.s.close()
 | 
			
		||||
        log.info('Socket closed.')
 | 
			
		||||
 | 
			
		||||
    def listen(self):
 | 
			
		||||
        """ Listen for data on the socket. When receiving data, call
 | 
			
		||||
        the callback on_recv callable.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        socket_open = True
 | 
			
		||||
        while socket_open:
 | 
			
		||||
            ins = []
 | 
			
		||||
            try:
 | 
			
		||||
                # Wait any read available data on socket. Timeout
 | 
			
		||||
                # after 5 secs.
 | 
			
		||||
                ins, out, err = select([self.s, ], [], [], 5)
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                # There's an error with the socket (maybe the socket
 | 
			
		||||
                # has been closed and the file descriptor is bad).
 | 
			
		||||
                log.debug('Socket error: %s' % e)
 | 
			
		||||
                break
 | 
			
		||||
 | 
			
		||||
            for s in ins:
 | 
			
		||||
                data = self.recv_size(self.s)
 | 
			
		||||
                if not data:
 | 
			
		||||
                    socket_open = False
 | 
			
		||||
 | 
			
		||||
                self.on_recv(self.sid, data)
 | 
			
		||||
 | 
			
		||||
    def recv_size(self, the_socket):
 | 
			
		||||
        total_len = 0
 | 
			
		||||
        total_data = []
 | 
			
		||||
        size = sys.maxint
 | 
			
		||||
        size_data = sock_data = ''
 | 
			
		||||
        recv_size = 8192
 | 
			
		||||
 | 
			
		||||
        while total_len < size:
 | 
			
		||||
            sock_data = the_socket.recv(recv_size)
 | 
			
		||||
            if not sock_data:
 | 
			
		||||
                return ''.join(total_data)
 | 
			
		||||
 | 
			
		||||
            if not total_data:
 | 
			
		||||
                if len(sock_data) > 4:
 | 
			
		||||
                    size_data += sock_data
 | 
			
		||||
                    size = struct.unpack('>i', size_data[:4])[0]
 | 
			
		||||
                    recv_size = size
 | 
			
		||||
                    if recv_size > 524288:
 | 
			
		||||
                        recv_size = 524288
 | 
			
		||||
                    total_data.append(size_data[4:])
 | 
			
		||||
                else:
 | 
			
		||||
                    size_data += sock_data
 | 
			
		||||
            else:
 | 
			
		||||
                total_data.append(sock_data)
 | 
			
		||||
            total_len = sum([len(i) for i in total_data])
 | 
			
		||||
        return ''.join(total_data)
 | 
			
		||||
							
								
								
									
										41
									
								
								sleekxmpp/plugins/xep_0065/stanza.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								sleekxmpp/plugins/xep_0065/stanza.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
from sleekxmpp import Iq
 | 
			
		||||
from sleekxmpp.xmlstream import ElementBase, register_stanza_plugin
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# The protocol namespace defined in the Socks5Bytestream (0065) spec.
 | 
			
		||||
namespace = 'http://jabber.org/protocol/bytestreams'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class StreamHost(ElementBase):
 | 
			
		||||
    """ The streamhost xml element.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    namespace = namespace
 | 
			
		||||
    name = 'streamhost'
 | 
			
		||||
    plugin_attrib = 'streamhost'
 | 
			
		||||
    interfaces = set(('host', 'jid', 'port'))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class StreamHostUsed(ElementBase):
 | 
			
		||||
    """ The streamhost-used xml element.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    namespace = namespace
 | 
			
		||||
    name = 'streamhost-used'
 | 
			
		||||
    plugin_attrib = 'streamhost-used'
 | 
			
		||||
    interfaces = set(('jid',))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Socks5(ElementBase):
 | 
			
		||||
    """ The query xml element.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    namespace = namespace
 | 
			
		||||
    name = 'query'
 | 
			
		||||
    plugin_attrib = 'socks'
 | 
			
		||||
    interfaces = set(('sid', 'activate'))
 | 
			
		||||
    sub_interfaces = set(('activate',))
 | 
			
		||||
 | 
			
		||||
register_stanza_plugin(Iq, Socks5)
 | 
			
		||||
register_stanza_plugin(Socks5, StreamHost)
 | 
			
		||||
register_stanza_plugin(Socks5, StreamHostUsed)
 | 
			
		||||
@@ -11,6 +11,7 @@ import hashlib
 | 
			
		||||
import random
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.jid import JID
 | 
			
		||||
from sleekxmpp.exceptions import IqError, IqTimeout
 | 
			
		||||
from sleekxmpp.stanza import Iq, StreamFeatures
 | 
			
		||||
from sleekxmpp.xmlstream import ElementBase, ET, register_stanza_plugin
 | 
			
		||||
@@ -44,16 +45,27 @@ class XEP_0078(BasePlugin):
 | 
			
		||||
                restart=False,
 | 
			
		||||
                order=self.order)
 | 
			
		||||
 | 
			
		||||
        self.xmpp.add_event_handler('legacy_protocol',
 | 
			
		||||
                self._handle_legacy_protocol)
 | 
			
		||||
 | 
			
		||||
        register_stanza_plugin(Iq, stanza.IqAuth)
 | 
			
		||||
        register_stanza_plugin(StreamFeatures, stanza.AuthFeature)
 | 
			
		||||
 | 
			
		||||
    def plugin_end(self):
 | 
			
		||||
        self.xmpp.del_event_handler('legacy_protocol',
 | 
			
		||||
                self._handle_legacy_protocol)
 | 
			
		||||
        self.xmpp.unregister_feature('auth', self.order)
 | 
			
		||||
 | 
			
		||||
    def _handle_auth(self, features):
 | 
			
		||||
        # If we can or have already authenticated with SASL, do nothing.
 | 
			
		||||
        if 'mechanisms' in features['features']:
 | 
			
		||||
            return False
 | 
			
		||||
        return self.authenticate()
 | 
			
		||||
 | 
			
		||||
    def _handle_legacy_protocol(self, event):
 | 
			
		||||
        self.authenticate()
 | 
			
		||||
 | 
			
		||||
    def authenticate(self):
 | 
			
		||||
        if self.xmpp.authenticated:
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
@@ -62,13 +74,13 @@ class XEP_0078(BasePlugin):
 | 
			
		||||
        # Step 1: Request the auth form
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'get'
 | 
			
		||||
        iq['to'] = self.xmpp.boundjid.host
 | 
			
		||||
        iq['auth']['username'] = self.xmpp.boundjid.user
 | 
			
		||||
        iq['to'] = self.xmpp.requested_jid.host
 | 
			
		||||
        iq['auth']['username'] = self.xmpp.requested_jid.user
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            resp = iq.send(now=True)
 | 
			
		||||
        except IqError:
 | 
			
		||||
            log.info("Authentication failed: %s", resp['error']['condition'])
 | 
			
		||||
        except IqError as err:
 | 
			
		||||
            log.info("Authentication failed: %s", err.iq['error']['condition'])
 | 
			
		||||
            self.xmpp.event('failed_auth', direct=True)
 | 
			
		||||
            self.xmpp.disconnect()
 | 
			
		||||
            return True
 | 
			
		||||
@@ -81,13 +93,14 @@ class XEP_0078(BasePlugin):
 | 
			
		||||
        # Step 2: Fill out auth form for either password or digest auth
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'set'
 | 
			
		||||
        iq['auth']['username'] = self.xmpp.boundjid.user
 | 
			
		||||
        iq['auth']['username'] = self.xmpp.requested_jid.user
 | 
			
		||||
 | 
			
		||||
        # A resource is required, so create a random one if necessary
 | 
			
		||||
        if self.xmpp.boundjid.resource:
 | 
			
		||||
            iq['auth']['resource'] = self.xmpp.boundjid.resource
 | 
			
		||||
        else:
 | 
			
		||||
            iq['auth']['resource'] = '%s' % random.random()
 | 
			
		||||
        resource = self.xmpp.requested_jid.resource
 | 
			
		||||
        if not resource:
 | 
			
		||||
            resource = uuid.uuid4()
 | 
			
		||||
 | 
			
		||||
        iq['auth']['resource'] = resource
 | 
			
		||||
 | 
			
		||||
        if 'digest' in resp['auth']['fields']:
 | 
			
		||||
            log.debug('Authenticating via jabber:iq:auth Digest')
 | 
			
		||||
@@ -109,16 +122,22 @@ class XEP_0078(BasePlugin):
 | 
			
		||||
            result = iq.send(now=True)
 | 
			
		||||
        except IqError as err:
 | 
			
		||||
            log.info("Authentication failed")
 | 
			
		||||
            self.xmpp.disconnect()
 | 
			
		||||
            self.xmpp.event("failed_auth", direct=True)
 | 
			
		||||
            self.xmpp.disconnect()
 | 
			
		||||
        except IqTimeout:
 | 
			
		||||
            log.info("Authentication failed")
 | 
			
		||||
            self.xmpp.disconnect()
 | 
			
		||||
            self.xmpp.event("failed_auth", direct=True)
 | 
			
		||||
            self.xmpp.disconnect()
 | 
			
		||||
 | 
			
		||||
        self.xmpp.features.add('auth')
 | 
			
		||||
 | 
			
		||||
        self.xmpp.authenticated = True
 | 
			
		||||
 | 
			
		||||
        self.xmpp.boundjid = JID(self.xmpp.requested_jid,
 | 
			
		||||
                resource=resource,
 | 
			
		||||
                cache_lock=True)
 | 
			
		||||
        self.xmpp.event('session_bind', self.xmpp.boundjid, direct=True)
 | 
			
		||||
 | 
			
		||||
        log.debug("Established Session")
 | 
			
		||||
        self.xmpp.sessionstarted = True
 | 
			
		||||
        self.xmpp.session_started_event.set()
 | 
			
		||||
 
 | 
			
		||||
@@ -69,6 +69,8 @@ class XEP_0084(BasePlugin):
 | 
			
		||||
        metadata = MetaData()
 | 
			
		||||
        if items is None:
 | 
			
		||||
            items = []
 | 
			
		||||
        if not isinstance(items, (list, set)):
 | 
			
		||||
            items = [items]
 | 
			
		||||
        for info in items:
 | 
			
		||||
            metadata.add_info(info['id'], info['type'], info['bytes'],
 | 
			
		||||
                    height=info.get('height', ''),
 | 
			
		||||
 
 | 
			
		||||
@@ -7,8 +7,8 @@
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from base64 import b64encode, b64decode
 | 
			
		||||
from sleekxmpp.thirdparty.suelta.util import bytes
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.util import bytes
 | 
			
		||||
from sleekxmpp.xmlstream import ET, ElementBase, register_stanza_plugin
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -52,4 +52,5 @@ class XEP_0085(BasePlugin):
 | 
			
		||||
    def _handle_chat_state(self, msg):
 | 
			
		||||
        state = msg['chat_state']
 | 
			
		||||
        log.debug("Chat State: %s, %s", state, msg['from'].jid)
 | 
			
		||||
        self.xmpp.event('chatstate', msg)
 | 
			
		||||
        self.xmpp.event('chatstate_%s' % state, msg)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								sleekxmpp/plugins/xep_0091/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								sleekxmpp/plugins/xep_0091/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins.base import register_plugin
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins.xep_0091 import stanza
 | 
			
		||||
from sleekxmpp.plugins.xep_0091.stanza import LegacyDelay
 | 
			
		||||
from sleekxmpp.plugins.xep_0091.legacy_delay import XEP_0091
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
register_plugin(XEP_0091)
 | 
			
		||||
							
								
								
									
										29
									
								
								sleekxmpp/plugins/xep_0091/legacy_delay.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								sleekxmpp/plugins/xep_0091/legacy_delay.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.stanza import Message, Presence
 | 
			
		||||
from sleekxmpp.xmlstream import register_stanza_plugin
 | 
			
		||||
from sleekxmpp.plugins import BasePlugin
 | 
			
		||||
from sleekxmpp.plugins.xep_0091 import stanza
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class XEP_0091(BasePlugin):
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    XEP-0091: Legacy Delayed Delivery
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    name = 'xep_0091'
 | 
			
		||||
    description = 'XEP-0091: Legacy Delayed Delivery'
 | 
			
		||||
    dependencies = set()
 | 
			
		||||
    stanza = stanza
 | 
			
		||||
 | 
			
		||||
    def plugin_init(self):
 | 
			
		||||
        register_stanza_plugin(Message, stanza.LegacyDelay)
 | 
			
		||||
        register_stanza_plugin(Presence, stanza.LegacyDelay)
 | 
			
		||||
							
								
								
									
										46
									
								
								sleekxmpp/plugins/xep_0091/stanza.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								sleekxmpp/plugins/xep_0091/stanza.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import datetime as dt
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.jid import JID
 | 
			
		||||
from sleekxmpp.xmlstream import ElementBase
 | 
			
		||||
from sleekxmpp.plugins import xep_0082
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LegacyDelay(ElementBase):
 | 
			
		||||
 | 
			
		||||
    name = 'x'
 | 
			
		||||
    namespace = 'jabber:x:delay'
 | 
			
		||||
    plugin_attrib = 'legacy_delay'
 | 
			
		||||
    interfaces = set(('from', 'stamp', 'text'))
 | 
			
		||||
 | 
			
		||||
    def get_from(self):
 | 
			
		||||
        return JID(self._get_attr('from'))
 | 
			
		||||
 | 
			
		||||
    def set_from(self, value):
 | 
			
		||||
        self._set_attr('from', str(value))
 | 
			
		||||
 | 
			
		||||
    def get_stamp(self):
 | 
			
		||||
        timestamp = self._get_attr('stamp')
 | 
			
		||||
        return xep_0082.parse('%sZ' % timestamp)
 | 
			
		||||
 | 
			
		||||
    def set_stamp(self, value):
 | 
			
		||||
        if isinstance(value, dt.datetime):
 | 
			
		||||
            value = value.astimezone(xep_0082.tzutc)
 | 
			
		||||
            value = xep_0082.format_datetime(value)
 | 
			
		||||
        self._set_attr('stamp', value[0:19].replace('-', ''))
 | 
			
		||||
 | 
			
		||||
    def get_text(self):
 | 
			
		||||
        return self.xml.text
 | 
			
		||||
 | 
			
		||||
    def set_text(self, value):
 | 
			
		||||
        self.xml.text = value
 | 
			
		||||
 | 
			
		||||
    def del_text(self):
 | 
			
		||||
        self.xml.text = ''
 | 
			
		||||
@@ -143,6 +143,11 @@ class XEP_0115(BasePlugin):
 | 
			
		||||
        if str(existing_verstring) == str(pres['caps']['ver']):
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        existing_caps = self.get_caps(verstring=pres['caps']['ver'])
 | 
			
		||||
        if existing_caps is not None:
 | 
			
		||||
            self.assign_verstring(pres['from'], pres['caps']['ver'])
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        if pres['caps']['hash'] not in self.hashes:
 | 
			
		||||
            try:
 | 
			
		||||
                log.debug("Unknown caps hash: %s", pres['caps']['hash'])
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ import threading
 | 
			
		||||
 | 
			
		||||
from sleekxmpp import JID
 | 
			
		||||
from sleekxmpp.stanza import Presence
 | 
			
		||||
from sleekxmpp.exceptions import XMPPError
 | 
			
		||||
from sleekxmpp.xmlstream import register_stanza_plugin
 | 
			
		||||
from sleekxmpp.xmlstream.matcher import StanzaPath
 | 
			
		||||
from sleekxmpp.xmlstream.handler import Callback
 | 
			
		||||
@@ -103,15 +104,18 @@ class XEP_0153(BasePlugin):
 | 
			
		||||
        if own_jid:
 | 
			
		||||
            self.xmpp.roster[jid].send_last_presence()
 | 
			
		||||
 | 
			
		||||
        iq = self.xmpp['xep_0054'].get_vcard(jid=jid.bare, ifrom=ifrom)
 | 
			
		||||
        try:
 | 
			
		||||
            iq = self.xmpp['xep_0054'].get_vcard(jid=jid.bare, ifrom=ifrom)
 | 
			
		||||
 | 
			
		||||
        data = iq['vcard_temp']['PHOTO']['BINVAL']
 | 
			
		||||
        if not data:
 | 
			
		||||
            new_hash = ''
 | 
			
		||||
        else:
 | 
			
		||||
            new_hash = hashlib.sha1(data).hexdigest()
 | 
			
		||||
            data = iq['vcard_temp']['PHOTO']['BINVAL']
 | 
			
		||||
            if not data:
 | 
			
		||||
                new_hash = ''
 | 
			
		||||
            else:
 | 
			
		||||
                new_hash = hashlib.sha1(data).hexdigest()
 | 
			
		||||
 | 
			
		||||
        self.api['set_hash'](jid, args=new_hash)
 | 
			
		||||
            self.api['set_hash'](jid, args=new_hash)
 | 
			
		||||
        except XMPPError:
 | 
			
		||||
            log.debug('Could not retrieve vCard for %s' % jid)
 | 
			
		||||
 | 
			
		||||
    def _recv_presence(self, pres):
 | 
			
		||||
        if not pres.match('presence/vcard_temp_update'):
 | 
			
		||||
 
 | 
			
		||||
@@ -48,7 +48,7 @@ class XEP_0191(BasePlugin):
 | 
			
		||||
    def get_blocked(self, ifrom=None, block=True, timeout=None, callback=None):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'get'
 | 
			
		||||
        iq['from'] = 'ifrom'
 | 
			
		||||
        iq['from'] = ifrom
 | 
			
		||||
        iq.enable('blocklist')
 | 
			
		||||
        return iq.send(block=block, timeout=timeout, callback=callback)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14,14 +14,17 @@ from sleekxmpp.plugins import xep_0082
 | 
			
		||||
 | 
			
		||||
class Delay(ElementBase):
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    name = 'delay'
 | 
			
		||||
    namespace = 'urn:xmpp:delay'
 | 
			
		||||
    plugin_attrib = 'delay'
 | 
			
		||||
    interfaces = set(('from', 'stamp', 'text'))
 | 
			
		||||
 | 
			
		||||
    def get_from(self):
 | 
			
		||||
        return JID(self._get_attr('from'))
 | 
			
		||||
 | 
			
		||||
    def set_from(self, value):
 | 
			
		||||
        self._set_attr('from', str(value))
 | 
			
		||||
 | 
			
		||||
    def get_stamp(self):
 | 
			
		||||
        timestamp = self._get_attr('stamp')
 | 
			
		||||
        return xep_0082.parse(timestamp)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								sleekxmpp/plugins/xep_0235/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								sleekxmpp/plugins/xep_0235/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins.base import register_plugin
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins.xep_0235 import stanza
 | 
			
		||||
from sleekxmpp.plugins.xep_0235.stanza import OAuth
 | 
			
		||||
from sleekxmpp.plugins.xep_0235.oauth import XEP_0235
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
register_plugin(XEP_0235)
 | 
			
		||||
							
								
								
									
										32
									
								
								sleekxmpp/plugins/xep_0235/oauth.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								sleekxmpp/plugins/xep_0235/oauth.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from sleekxmpp import Message
 | 
			
		||||
from sleekxmpp.plugins import BasePlugin
 | 
			
		||||
from sleekxmpp.xmlstream import register_stanza_plugin
 | 
			
		||||
from sleekxmpp.plugins.xep_0235 import stanza, OAuth
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class XEP_0235(BasePlugin):
 | 
			
		||||
 | 
			
		||||
    name = 'xep_0235'
 | 
			
		||||
    description = 'XEP-0235: OAuth Over XMPP'
 | 
			
		||||
    dependencies = set(['xep_0030'])
 | 
			
		||||
    stanza = stanza
 | 
			
		||||
 | 
			
		||||
    def plugin_init(self):
 | 
			
		||||
        register_stanza_plugin(Message, OAuth)
 | 
			
		||||
 | 
			
		||||
    def session_bind(self, jid):
 | 
			
		||||
        self.xmpp['xep_0030'].add_feature('urn:xmpp:oauth:0')
 | 
			
		||||
 | 
			
		||||
    def plugin_end(self):
 | 
			
		||||
        self.xmpp['xep_0030'].del_feature(feature='urn:xmpp:oauth:0')
 | 
			
		||||
							
								
								
									
										80
									
								
								sleekxmpp/plugins/xep_0235/stanza.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								sleekxmpp/plugins/xep_0235/stanza.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,80 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import hmac
 | 
			
		||||
import hashlib
 | 
			
		||||
import urllib
 | 
			
		||||
import base64
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.xmlstream import ET, ElementBase, JID
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OAuth(ElementBase):
 | 
			
		||||
 | 
			
		||||
    name = 'oauth'
 | 
			
		||||
    namespace = 'urn:xmpp:oauth:0'
 | 
			
		||||
    plugin_attrib = 'oauth'
 | 
			
		||||
    interfaces = set(['oauth_consumer_key', 'oauth_nonce', 'oauth_signature',
 | 
			
		||||
                      'oauth_signature_method', 'oauth_timestamp',
 | 
			
		||||
                      'oauth_token', 'oauth_version'])
 | 
			
		||||
    sub_interfaces = interfaces
 | 
			
		||||
 | 
			
		||||
    def generate_signature(self, stanza, sfrom, sto, consumer_secret,
 | 
			
		||||
                                 token_secret, method='HMAC-SHA1'):
 | 
			
		||||
        self['oauth_signature_method'] = method
 | 
			
		||||
 | 
			
		||||
        request = urllib.quote('%s&%s' % (sfrom, sto), '')
 | 
			
		||||
        parameters = urllib.quote('&'.join([
 | 
			
		||||
            'oauth_consumer_key=%s' % self['oauth_consumer_key'],
 | 
			
		||||
            'oauth_nonce=%s' % self['oauth_nonce'],
 | 
			
		||||
            'oauth_signature_method=%s' % self['oauth_signature_method'],
 | 
			
		||||
            'oauth_timestamp=%s' % self['oauth_timestamp'],
 | 
			
		||||
            'oauth_token=%s' % self['oauth_token'],
 | 
			
		||||
            'oauth_version=%s' % self['oauth_version']
 | 
			
		||||
        ]), '')
 | 
			
		||||
 | 
			
		||||
        sigbase = '%s&%s&%s' % (stanza, request, parameters)
 | 
			
		||||
 | 
			
		||||
        consumer_secret = urllib.quote(consumer_secret, '')
 | 
			
		||||
        token_secret = urllib.quote(token_secret, '')
 | 
			
		||||
        key = '%s&%s' % (consumer_secret, token_secret)
 | 
			
		||||
 | 
			
		||||
        if method == 'HMAC-SHA1':
 | 
			
		||||
            sig = base64.b64encode(hmac.new(key, sigbase, hashlib.sha1).digest())
 | 
			
		||||
        elif method == 'PLAINTEXT':
 | 
			
		||||
            sig = key
 | 
			
		||||
 | 
			
		||||
        self['oauth_signature'] = sig
 | 
			
		||||
        return sig
 | 
			
		||||
 | 
			
		||||
    def verify_signature(self, stanza, sfrom, sto, consumer_secret,
 | 
			
		||||
                               token_secret):
 | 
			
		||||
        method = self['oauth_signature_method']
 | 
			
		||||
 | 
			
		||||
        request = urllib.quote('%s&%s' % (sfrom, sto), '')
 | 
			
		||||
        parameters = urllib.quote('&'.join([
 | 
			
		||||
            'oauth_consumer_key=%s' % self['oauth_consumer_key'],
 | 
			
		||||
            'oauth_nonce=%s' % self['oauth_nonce'],
 | 
			
		||||
            'oauth_signature_method=%s' % self['oauth_signature_method'],
 | 
			
		||||
            'oauth_timestamp=%s' % self['oauth_timestamp'],
 | 
			
		||||
            'oauth_token=%s' % self['oauth_token'],
 | 
			
		||||
            'oauth_version=%s' % self['oauth_version']
 | 
			
		||||
        ]), '')
 | 
			
		||||
 | 
			
		||||
        sigbase = '%s&%s&%s' % (stanza, request, parameters)
 | 
			
		||||
 | 
			
		||||
        consumer_secret = urllib.quote(consumer_secret, '')
 | 
			
		||||
        token_secret = urllib.quote(token_secret, '')
 | 
			
		||||
        key = '%s&%s' % (consumer_secret, token_secret)
 | 
			
		||||
 | 
			
		||||
        if method == 'HMAC-SHA1':
 | 
			
		||||
            sig = base64.b64encode(hmac.new(key, sigbase, hashlib.sha1).digest())
 | 
			
		||||
        elif method == 'PLAINTEXT':
 | 
			
		||||
            sig = key
 | 
			
		||||
 | 
			
		||||
        return self['oauth_signature'] == sig
 | 
			
		||||
							
								
								
									
										21
									
								
								sleekxmpp/plugins/xep_0242.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								sleekxmpp/plugins/xep_0242.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins import BasePlugin, register_plugin
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class XEP_0242(BasePlugin):
 | 
			
		||||
 | 
			
		||||
    name = 'xep_0242'
 | 
			
		||||
    description = 'XEP-0242: XMPP Client Compliance 2009'
 | 
			
		||||
    dependencies = set(['xep_0030', 'xep_0115', 'xep_0054',
 | 
			
		||||
                        'xep_0045', 'xep_0085', 'xep_0016',
 | 
			
		||||
                        'xep_0191'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
register_plugin(XEP_0242)
 | 
			
		||||
							
								
								
									
										17
									
								
								sleekxmpp/plugins/xep_0257/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								sleekxmpp/plugins/xep_0257/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins.base import register_plugin
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins.xep_0257 import stanza
 | 
			
		||||
from sleekxmpp.plugins.xep_0257.stanza import Certs, AppendCert
 | 
			
		||||
from sleekxmpp.plugins.xep_0257.stanza import DisableCert, RevokeCert
 | 
			
		||||
from sleekxmpp.plugins.xep_0257.client_cert_management import XEP_0257
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
register_plugin(XEP_0257)
 | 
			
		||||
							
								
								
									
										65
									
								
								sleekxmpp/plugins/xep_0257/client_cert_management.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								sleekxmpp/plugins/xep_0257/client_cert_management.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from sleekxmpp import Iq
 | 
			
		||||
from sleekxmpp.plugins import BasePlugin
 | 
			
		||||
from sleekxmpp.xmlstream import register_stanza_plugin
 | 
			
		||||
from sleekxmpp.plugins.xep_0257 import stanza, Certs
 | 
			
		||||
from sleekxmpp.plugins.xep_0257 import AppendCert, DisableCert, RevokeCert
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
log = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class XEP_0257(BasePlugin):
 | 
			
		||||
 | 
			
		||||
    name = 'xep_0257'
 | 
			
		||||
    description = 'XEP-0258: Client Certificate Management for SASL EXTERNAL'
 | 
			
		||||
    dependencies = set(['xep_0030'])
 | 
			
		||||
    stanza = stanza
 | 
			
		||||
 | 
			
		||||
    def plugin_init(self):
 | 
			
		||||
        register_stanza_plugin(Iq, Certs)
 | 
			
		||||
        register_stanza_plugin(Iq, AppendCert)
 | 
			
		||||
        register_stanza_plugin(Iq, DisableCert)
 | 
			
		||||
        register_stanza_plugin(Iq, RevokeCert)
 | 
			
		||||
 | 
			
		||||
    def get_certs(self, ifrom=None, block=True, timeout=None, callback=None):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'get'
 | 
			
		||||
        iq['from'] = ifrom
 | 
			
		||||
        iq.enable('sasl_certs')
 | 
			
		||||
        return iq.send(block=block, timeout=timeout, callback=callback)
 | 
			
		||||
 | 
			
		||||
    def add_cert(self, name, cert, allow_management=True, ifrom=None,
 | 
			
		||||
                       block=True, timeout=None, callback=None):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'set'
 | 
			
		||||
        iq['from'] = ifrom
 | 
			
		||||
        iq['sasl_cert_append']['name'] = name
 | 
			
		||||
        iq['sasl_cert_append']['x509cert'] = cert
 | 
			
		||||
        iq['sasl_cert_append']['cert_management'] = allow_management
 | 
			
		||||
        return iq.send(block=block, timeout=timeout, callback=callback)
 | 
			
		||||
 | 
			
		||||
    def disable_cert(self, name, ifrom=None, block=True,
 | 
			
		||||
                           timeout=None, callback=None):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'set'
 | 
			
		||||
        iq['from'] = ifrom
 | 
			
		||||
        iq['sasl_cert_disable']['name'] = name
 | 
			
		||||
        return iq.send(block=block, timeout=timeout, callback=callback)
 | 
			
		||||
 | 
			
		||||
    def revoke_cert(self, name, ifrom=None, block=True,
 | 
			
		||||
                           timeout=None, callback=None):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'set'
 | 
			
		||||
        iq['from'] = ifrom
 | 
			
		||||
        iq['sasl_cert_revoke']['name'] = name
 | 
			
		||||
        return iq.send(block=block, timeout=timeout, callback=callback)
 | 
			
		||||
							
								
								
									
										87
									
								
								sleekxmpp/plugins/xep_0257/stanza.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								sleekxmpp/plugins/xep_0257/stanza.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,87 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.xmlstream import ElementBase, ET, register_stanza_plugin
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Certs(ElementBase):
 | 
			
		||||
    name = 'query'
 | 
			
		||||
    namespace = 'urn:xmpp:saslcert:1'
 | 
			
		||||
    plugin_attrib = 'sasl_certs'
 | 
			
		||||
    interfaces = set()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CertItem(ElementBase):
 | 
			
		||||
    name = 'item'
 | 
			
		||||
    namespace = 'urn:xmpp:saslcert:1'
 | 
			
		||||
    plugin_attrib = 'item'
 | 
			
		||||
    plugin_multi_attrib = 'items'
 | 
			
		||||
    interfaces = set(['name', 'x509cert', 'users'])
 | 
			
		||||
    sub_interfaces = set(['name', 'x509cert'])
 | 
			
		||||
 | 
			
		||||
    def get_users(self):
 | 
			
		||||
        resources = self.xml.findall('{%s}users/{%s}resource' % (
 | 
			
		||||
            self.namespace, self.namespace))
 | 
			
		||||
        return set([res.text for res in resources])
 | 
			
		||||
 | 
			
		||||
    def set_users(self, values):
 | 
			
		||||
        users = self.xml.find('{%s}users' % self.namespace)
 | 
			
		||||
        if users is None:
 | 
			
		||||
            users = ET.Element('{%s}users' % self.namespace)
 | 
			
		||||
            self.xml.append(users)
 | 
			
		||||
        for resource in values:
 | 
			
		||||
            res = ET.Element('{%s}resource' % self.namespace)
 | 
			
		||||
            res.text = resource
 | 
			
		||||
            users.append(res)
 | 
			
		||||
 | 
			
		||||
    def del_users(self):
 | 
			
		||||
        users = self.xml.find('{%s}users' % self.namespace)
 | 
			
		||||
        if users is not None:
 | 
			
		||||
            self.xml.remove(users)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AppendCert(ElementBase):
 | 
			
		||||
    name = 'append'
 | 
			
		||||
    namespace = 'urn:xmpp:saslcert:1'
 | 
			
		||||
    plugin_attrib = 'sasl_cert_append'
 | 
			
		||||
    interfaces = set(['name', 'x509cert', 'cert_management'])
 | 
			
		||||
    sub_interfaces = set(['name', 'x509cert'])
 | 
			
		||||
 | 
			
		||||
    def get_cert_management(self):
 | 
			
		||||
        manage = self.xml.find('{%s}no-cert-management' % self.namespace)
 | 
			
		||||
        return manage is None
 | 
			
		||||
 | 
			
		||||
    def set_cert_management(self, value):
 | 
			
		||||
        self.del_cert_management()
 | 
			
		||||
        if not value:
 | 
			
		||||
            manage = ET.Element('{%s}no-cert-management' % self.namespace)
 | 
			
		||||
            self.xml.append(manage)
 | 
			
		||||
 | 
			
		||||
    def del_cert_management(self):
 | 
			
		||||
        manage = self.xml.find('{%s}no-cert-management' % self.namespace)
 | 
			
		||||
        if manage is not None:
 | 
			
		||||
            self.xml.remove(manage)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DisableCert(ElementBase):
 | 
			
		||||
    name = 'disable'
 | 
			
		||||
    namespace = 'urn:xmpp:saslcert:1'
 | 
			
		||||
    plugin_attrib = 'sasl_cert_disable'
 | 
			
		||||
    interfaces = set(['name'])
 | 
			
		||||
    sub_interfaces = interfaces
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RevokeCert(ElementBase):
 | 
			
		||||
    name = 'revoke'
 | 
			
		||||
    namespace = 'urn:xmpp:saslcert:1'
 | 
			
		||||
    plugin_attrib = 'sasl_cert_revoke'
 | 
			
		||||
    interfaces = set(['name'])
 | 
			
		||||
    sub_interfaces = interfaces
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
register_stanza_plugin(Certs, CertItem, iterable=True)
 | 
			
		||||
@@ -8,8 +8,7 @@
 | 
			
		||||
 | 
			
		||||
from base64 import b64encode, b64decode
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.thirdparty.suelta.util import bytes
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.util import bytes
 | 
			
		||||
from sleekxmpp.xmlstream import ElementBase, ET, register_stanza_plugin
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								sleekxmpp/plugins/xep_0279/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								sleekxmpp/plugins/xep_0279/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins.base import register_plugin
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins.xep_0279 import stanza
 | 
			
		||||
from sleekxmpp.plugins.xep_0279.stanza import IPCheck
 | 
			
		||||
from sleekxmpp.plugins.xep_0279.ipcheck import XEP_0279
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
register_plugin(XEP_0279)
 | 
			
		||||
							
								
								
									
										39
									
								
								sleekxmpp/plugins/xep_0279/ipcheck.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								sleekxmpp/plugins/xep_0279/ipcheck.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from sleekxmpp import Iq
 | 
			
		||||
from sleekxmpp.plugins import BasePlugin
 | 
			
		||||
from sleekxmpp.xmlstream import register_stanza_plugin
 | 
			
		||||
from sleekxmpp.plugins.xep_0279 import stanza, IPCheck
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class XEP_0279(BasePlugin):
 | 
			
		||||
 | 
			
		||||
    name = 'xep_0279'
 | 
			
		||||
    description = 'XEP-0279: Server IP Check'
 | 
			
		||||
    dependencies = set(['xep_0030'])
 | 
			
		||||
    stanza = stanza
 | 
			
		||||
 | 
			
		||||
    def plugin_init(self):
 | 
			
		||||
        register_stanza_plugin(Iq, IPCheck)
 | 
			
		||||
 | 
			
		||||
    def session_bind(self, jid):
 | 
			
		||||
        self.xmpp['xep_0030'].add_feature('urn:xmpp:sic:0')
 | 
			
		||||
 | 
			
		||||
    def plugin_end(self):
 | 
			
		||||
        self.xmpp['xep_0030'].del_feature(feature='urn:xmpp:sic:0')
 | 
			
		||||
 | 
			
		||||
    def check_ip(self, ifrom=None, block=True, timeout=None, callback=None):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'get'
 | 
			
		||||
        iq['from'] = ifrom
 | 
			
		||||
        iq.enable('ip_check')
 | 
			
		||||
        return iq.send(block=block, timeout=timeout, callback=callback)
 | 
			
		||||
							
								
								
									
										30
									
								
								sleekxmpp/plugins/xep_0279/stanza.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								sleekxmpp/plugins/xep_0279/stanza.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.xmlstream import ElementBase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class IPCheck(ElementBase):
 | 
			
		||||
 | 
			
		||||
    name = 'ip'
 | 
			
		||||
    namespace = 'urn:xmpp:sic:0'
 | 
			
		||||
    plugin_attrib = 'ip_check'
 | 
			
		||||
    interfaces = set(['ip_check'])
 | 
			
		||||
    is_extension = True
 | 
			
		||||
 | 
			
		||||
    def get_ip_check(self):
 | 
			
		||||
        return self.xml.text
 | 
			
		||||
 | 
			
		||||
    def set_ip_check(self, value):
 | 
			
		||||
        if value:
 | 
			
		||||
            self.xml.text = value
 | 
			
		||||
        else:
 | 
			
		||||
            self.xml.text = ''
 | 
			
		||||
 | 
			
		||||
    def del_ip_check(self):
 | 
			
		||||
        self.xml.text = ''
 | 
			
		||||
							
								
								
									
										17
									
								
								sleekxmpp/plugins/xep_0280/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								sleekxmpp/plugins/xep_0280/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permissio
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins.base import register_plugin
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins.xep_0280.stanza import ReceivedCarbon, SentCarbon
 | 
			
		||||
from sleekxmpp.plugins.xep_0280.stanza import PrivateCarbon
 | 
			
		||||
from sleekxmpp.plugins.xep_0280.stanza import CarbonEnable, CarbonDisable
 | 
			
		||||
from sleekxmpp.plugins.xep_0280.carbons import XEP_0280
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
register_plugin(XEP_0280)
 | 
			
		||||
							
								
								
									
										81
									
								
								sleekxmpp/plugins/xep_0280/carbons.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								sleekxmpp/plugins/xep_0280/carbons.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,81 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permissio
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
import sleekxmpp
 | 
			
		||||
from sleekxmpp.stanza import Message, Iq
 | 
			
		||||
from sleekxmpp.xmlstream.handler import Callback
 | 
			
		||||
from sleekxmpp.xmlstream.matcher import StanzaPath
 | 
			
		||||
from sleekxmpp.xmlstream import register_stanza_plugin
 | 
			
		||||
from sleekxmpp.plugins import BasePlugin
 | 
			
		||||
from sleekxmpp.plugins.xep_0280 import stanza
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
log = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class XEP_0280(BasePlugin):
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    XEP-0280 Message Carbons
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    name = 'xep_0280'
 | 
			
		||||
    description = 'XEP-0280: Message Carbons'
 | 
			
		||||
    dependencies = set(['xep_0030', 'xep_0297'])
 | 
			
		||||
    stanza = stanza
 | 
			
		||||
 | 
			
		||||
    def plugin_init(self):
 | 
			
		||||
        self.xmpp.register_handler(
 | 
			
		||||
            Callback('Carbon Received',
 | 
			
		||||
                     StanzaPath('message/carbon_received'),
 | 
			
		||||
                     self._handle_carbon_received))
 | 
			
		||||
        self.xmpp.register_handler(
 | 
			
		||||
            Callback('Carbon Sent',
 | 
			
		||||
                     StanzaPath('message/carbon_sent'),
 | 
			
		||||
                     self._handle_carbon_sent))
 | 
			
		||||
 | 
			
		||||
        register_stanza_plugin(Message, stanza.ReceivedCarbon)
 | 
			
		||||
        register_stanza_plugin(Message, stanza.SentCarbon)
 | 
			
		||||
        register_stanza_plugin(Message, stanza.PrivateCarbon)
 | 
			
		||||
        register_stanza_plugin(Iq, stanza.CarbonEnable)
 | 
			
		||||
        register_stanza_plugin(Iq, stanza.CarbonDisable)
 | 
			
		||||
 | 
			
		||||
        register_stanza_plugin(stanza.ReceivedCarbon,
 | 
			
		||||
                               self.xmpp['xep_0297'].stanza.Forwarded)
 | 
			
		||||
        register_stanza_plugin(stanza.SentCarbon,
 | 
			
		||||
                               self.xmpp['xep_0297'].stanza.Forwarded)
 | 
			
		||||
 | 
			
		||||
    def plugin_end(self):
 | 
			
		||||
        self.xmpp.remove_handler('Carbon Received')
 | 
			
		||||
        self.xmpp.remove_handler('Carbon Sent')
 | 
			
		||||
        self.xmpp.plugin['xep_0030'].del_feature(feature='urn:xmpp:carbons:2')
 | 
			
		||||
 | 
			
		||||
    def session_bind(self, jid):
 | 
			
		||||
        self.xmpp.plugin['xep_0030'].add_feature('urn:xmpp:carbons:2')
 | 
			
		||||
 | 
			
		||||
    def _handle_carbon_received(self, msg):
 | 
			
		||||
        self.xmpp.event('carbon_received', msg)
 | 
			
		||||
 | 
			
		||||
    def _handle_carbon_sent(self, msg):
 | 
			
		||||
        self.xmpp.event('carbon_sent', msg)
 | 
			
		||||
 | 
			
		||||
    def enable(self, ifrom=None, block=True, timeout=None, callback=None):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'set'
 | 
			
		||||
        iq['from'] = ifrom
 | 
			
		||||
        iq.enable('carbon_enable')
 | 
			
		||||
        return iq.send(block=block, timeout=timeout, callback=callback)
 | 
			
		||||
 | 
			
		||||
    def disable(self, ifrom=None, block=True, timeout=None, callback=None):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'set'
 | 
			
		||||
        iq['from'] = ifrom
 | 
			
		||||
        iq.enable('carbon_disable')
 | 
			
		||||
        return iq.send(block=block, timeout=timeout, callback=callback)
 | 
			
		||||
							
								
								
									
										64
									
								
								sleekxmpp/plugins/xep_0280/stanza.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								sleekxmpp/plugins/xep_0280/stanza.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permissio
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.xmlstream import ElementBase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ReceivedCarbon(ElementBase):
 | 
			
		||||
    name = 'received'
 | 
			
		||||
    namespace = 'urn:xmpp:carbons:2'
 | 
			
		||||
    plugin_attrib = 'carbon_received'
 | 
			
		||||
    interfaces = set(['carbon_received'])
 | 
			
		||||
    is_extension = True
 | 
			
		||||
 | 
			
		||||
    def get_carbon_received(self):
 | 
			
		||||
        return self['forwarded']['stanza']
 | 
			
		||||
 | 
			
		||||
    def del_carbon_received(self):
 | 
			
		||||
        del self['forwarded']['stanza']
 | 
			
		||||
 | 
			
		||||
    def set_carbon_received(self, stanza):
 | 
			
		||||
        self['forwarded']['stanza'] = stanza
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SentCarbon(ElementBase):
 | 
			
		||||
    name = 'sent'
 | 
			
		||||
    namespace = 'urn:xmpp:carbons:2'
 | 
			
		||||
    plugin_attrib = 'carbon_sent'
 | 
			
		||||
    interfaces = set(['carbon_sent'])
 | 
			
		||||
    is_extension = True
 | 
			
		||||
 | 
			
		||||
    def get_carbon_sent(self):
 | 
			
		||||
        return self['forwarded']['stanza']
 | 
			
		||||
 | 
			
		||||
    def del_carbon_sent(self):
 | 
			
		||||
        del self['forwarded']['stanza']
 | 
			
		||||
 | 
			
		||||
    def set_carbon_sent(self, stanza):
 | 
			
		||||
        self['forwarded']['stanza'] = stanza
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PrivateCarbon(ElementBase):
 | 
			
		||||
    name = 'private'
 | 
			
		||||
    namespace = 'urn:xmpp:carbons:2'
 | 
			
		||||
    plugin_attrib = 'carbon_private'
 | 
			
		||||
    interfaces = set()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CarbonEnable(ElementBase):
 | 
			
		||||
    name = 'enable'
 | 
			
		||||
    namespace = 'urn:xmpp:carbons:2'
 | 
			
		||||
    plugin_attrib = 'carbon_enable'
 | 
			
		||||
    interfaces = set()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CarbonDisable(ElementBase):
 | 
			
		||||
    name = 'disable'
 | 
			
		||||
    namespace = 'urn:xmpp:carbons:2'
 | 
			
		||||
    plugin_attrib = 'carbon_disable'
 | 
			
		||||
    interfaces = set()
 | 
			
		||||
							
								
								
									
										16
									
								
								sleekxmpp/plugins/xep_0297/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								sleekxmpp/plugins/xep_0297/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins.base import register_plugin
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins.xep_0297 import stanza
 | 
			
		||||
from sleekxmpp.plugins.xep_0297.stanza import Forwarded
 | 
			
		||||
from sleekxmpp.plugins.xep_0297.forwarded import XEP_0297
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
register_plugin(XEP_0297)
 | 
			
		||||
							
								
								
									
										64
									
								
								sleekxmpp/plugins/xep_0297/forwarded.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								sleekxmpp/plugins/xep_0297/forwarded.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from sleekxmpp import Iq, Message, Presence
 | 
			
		||||
from sleekxmpp.plugins import BasePlugin
 | 
			
		||||
from sleekxmpp.xmlstream import register_stanza_plugin
 | 
			
		||||
from sleekxmpp.xmlstream.handler import Callback
 | 
			
		||||
from sleekxmpp.xmlstream.matcher import StanzaPath
 | 
			
		||||
from sleekxmpp.plugins.xep_0297 import stanza, Forwarded
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class XEP_0297(BasePlugin):
 | 
			
		||||
 | 
			
		||||
    name = 'xep_0297'
 | 
			
		||||
    description = 'XEP-0297: Stanza Forwarding'
 | 
			
		||||
    dependencies = set(['xep_0030', 'xep_0203'])
 | 
			
		||||
    stanza = stanza
 | 
			
		||||
 | 
			
		||||
    def plugin_init(self):
 | 
			
		||||
        register_stanza_plugin(Message, Forwarded)
 | 
			
		||||
 | 
			
		||||
        # While these are marked as iterable, that is just for
 | 
			
		||||
        # making it easier to extract the forwarded stanza. There
 | 
			
		||||
        # still can be only a single forwarded stanza.
 | 
			
		||||
        register_stanza_plugin(Forwarded, Message, iterable=True)
 | 
			
		||||
        register_stanza_plugin(Forwarded, Presence, iterable=True)
 | 
			
		||||
        register_stanza_plugin(Forwarded, Iq, iterable=True)
 | 
			
		||||
 | 
			
		||||
        register_stanza_plugin(Forwarded, self.xmpp['xep_0203'].stanza.Delay)
 | 
			
		||||
 | 
			
		||||
        self.xmpp.register_handler(
 | 
			
		||||
            Callback('Forwarded Stanza',
 | 
			
		||||
                StanzaPath('message/forwarded'),
 | 
			
		||||
                self._handle_forwarded))
 | 
			
		||||
 | 
			
		||||
    def session_bind(self, jid):
 | 
			
		||||
        self.xmpp['xep_0030'].add_feature('urn:xmpp:forward:0')
 | 
			
		||||
 | 
			
		||||
    def plugin_end(self):
 | 
			
		||||
        self.xmpp['xep_0030'].del_feature(feature='urn:xmpp:forward:0')
 | 
			
		||||
        self.xmpp.remove_handler('Forwarded Stanza')
 | 
			
		||||
 | 
			
		||||
    def forward(self, stanza=None, mto=None, mbody=None, mfrom=None, delay=None):
 | 
			
		||||
        stanza.stream = None
 | 
			
		||||
 | 
			
		||||
        msg = self.xmpp.Message()
 | 
			
		||||
        msg['to'] = mto
 | 
			
		||||
        msg['from'] = mfrom
 | 
			
		||||
        msg['body'] = mbody
 | 
			
		||||
        msg['forwarded']['stanza'] = stanza
 | 
			
		||||
        if delay is not None:
 | 
			
		||||
            msg['forwarded']['delay']['stamp'] = delay
 | 
			
		||||
        msg.send()
 | 
			
		||||
 | 
			
		||||
    def _handle_forwarded(self, msg):
 | 
			
		||||
        self.xmpp.event('forwarded_stanza', msg)
 | 
			
		||||
							
								
								
									
										36
									
								
								sleekxmpp/plugins/xep_0297/stanza.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								sleekxmpp/plugins/xep_0297/stanza.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permission.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.stanza import Message, Presence, Iq
 | 
			
		||||
from sleekxmpp.xmlstream import ElementBase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Forwarded(ElementBase):
 | 
			
		||||
    name = 'forwarded'
 | 
			
		||||
    namespace = 'urn:xmpp:forward:0'
 | 
			
		||||
    plugin_attrib = 'forwarded'
 | 
			
		||||
    interfaces = set(['stanza'])
 | 
			
		||||
 | 
			
		||||
    def get_stanza(self):
 | 
			
		||||
        for stanza in self:
 | 
			
		||||
            if isinstance(stanza, (Message, Presence, Iq)):
 | 
			
		||||
                return stanza
 | 
			
		||||
        return ''
 | 
			
		||||
 | 
			
		||||
    def set_stanza(self, value):
 | 
			
		||||
        self.del_stanza()
 | 
			
		||||
        self.append(value)
 | 
			
		||||
 | 
			
		||||
    def del_stanza(self):
 | 
			
		||||
        found_stanzas = []
 | 
			
		||||
        for stanza in self:
 | 
			
		||||
            if isinstance(stanza, (Message, Presence, Iq)):
 | 
			
		||||
                found_stanzas.append(stanza)
 | 
			
		||||
        for stanza in found_stanzas:
 | 
			
		||||
            self.iterables.remove(stanza)
 | 
			
		||||
            self.xml.remove(stanza.xml)
 | 
			
		||||
							
								
								
									
										15
									
								
								sleekxmpp/plugins/xep_0308/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								sleekxmpp/plugins/xep_0308/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permissio
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins.base import register_plugin
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins.xep_0308.stanza import Replace
 | 
			
		||||
from sleekxmpp.plugins.xep_0308.correction import XEP_0308
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
register_plugin(XEP_0308)
 | 
			
		||||
							
								
								
									
										52
									
								
								sleekxmpp/plugins/xep_0308/correction.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								sleekxmpp/plugins/xep_0308/correction.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permissio
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
import sleekxmpp
 | 
			
		||||
from sleekxmpp.stanza import Message
 | 
			
		||||
from sleekxmpp.xmlstream.handler import Callback
 | 
			
		||||
from sleekxmpp.xmlstream.matcher import StanzaPath
 | 
			
		||||
from sleekxmpp.xmlstream import register_stanza_plugin
 | 
			
		||||
from sleekxmpp.plugins import BasePlugin
 | 
			
		||||
from sleekxmpp.plugins.xep_0308 import stanza, Replace
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
log = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class XEP_0308(BasePlugin):
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    XEP-0308 Last Message Correction
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    name = 'xep_0308'
 | 
			
		||||
    description = 'XEP-0308: Last Message Correction'
 | 
			
		||||
    dependencies = set(['xep_0030'])
 | 
			
		||||
    stanza = stanza
 | 
			
		||||
 | 
			
		||||
    def plugin_init(self):
 | 
			
		||||
        self.xmpp.register_handler(
 | 
			
		||||
            Callback('Message Correction',
 | 
			
		||||
                     StanzaPath('message/replace'),
 | 
			
		||||
                     self._handle_correction))
 | 
			
		||||
 | 
			
		||||
        register_stanza_plugin(Message, Replace)
 | 
			
		||||
 | 
			
		||||
        self.xmpp.use_message_ids = True
 | 
			
		||||
 | 
			
		||||
    def plugin_end(self):
 | 
			
		||||
        self.xmpp.remove_handler('Message Correction')
 | 
			
		||||
        self.xmpp.plugin['xep_0030'].del_feature(feature=Replace.namespace)
 | 
			
		||||
 | 
			
		||||
    def session_bind(self, jid):
 | 
			
		||||
        self.xmpp.plugin['xep_0030'].add_feature(Replace.namespace)
 | 
			
		||||
 | 
			
		||||
    def _handle_correction(self, msg):
 | 
			
		||||
        self.xmpp.event('message_correction', msg)
 | 
			
		||||
							
								
								
									
										16
									
								
								sleekxmpp/plugins/xep_0308/stanza.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								sleekxmpp/plugins/xep_0308/stanza.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permissio
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.xmlstream import ElementBase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Replace(ElementBase):
 | 
			
		||||
    name = 'replace'
 | 
			
		||||
    namespace = 'urn:xmpp:message-correct:0'
 | 
			
		||||
    plugin_attrib = 'replace'
 | 
			
		||||
    interfaces = set(['id'])
 | 
			
		||||
							
								
								
									
										15
									
								
								sleekxmpp/plugins/xep_0313/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								sleekxmpp/plugins/xep_0313/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permissio
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins.base import register_plugin
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.plugins.xep_0313.stanza import Result, MAM, Preferences
 | 
			
		||||
from sleekxmpp.plugins.xep_0313.mam import XEP_0313
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
register_plugin(XEP_0313)
 | 
			
		||||
							
								
								
									
										92
									
								
								sleekxmpp/plugins/xep_0313/mam.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								sleekxmpp/plugins/xep_0313/mam.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,92 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permissio
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
import sleekxmpp
 | 
			
		||||
from sleekxmpp.stanza import Message, Iq
 | 
			
		||||
from sleekxmpp.exceptions import XMPPError
 | 
			
		||||
from sleekxmpp.xmlstream.handler import Collector
 | 
			
		||||
from sleekxmpp.xmlstream.matcher import StanzaPath
 | 
			
		||||
from sleekxmpp.xmlstream import register_stanza_plugin
 | 
			
		||||
from sleekxmpp.plugins import BasePlugin
 | 
			
		||||
from sleekxmpp.plugins.xep_0313 import stanza
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
log = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class XEP_0313(BasePlugin):
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    XEP-0313 Message Archive Management
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    name = 'xep_0313'
 | 
			
		||||
    description = 'XEP-0313: Message Archive Management'
 | 
			
		||||
    dependencies = set(['xep_0030', 'xep_0050', 'xep_0059', 'xep_0297'])
 | 
			
		||||
    stanza = stanza
 | 
			
		||||
 | 
			
		||||
    def plugin_init(self):
 | 
			
		||||
        register_stanza_plugin(Iq, stanza.MAM)
 | 
			
		||||
        register_stanza_plugin(Iq, stanza.Preferences)
 | 
			
		||||
        register_stanza_plugin(Message, stanza.Result)
 | 
			
		||||
        register_stanza_plugin(stanza.MAM, self.xmpp['xep_0059'].stanza.Set)
 | 
			
		||||
 | 
			
		||||
    def retrieve(self, jid=None, start=None, end=None, with_jid=None, ifrom=None,
 | 
			
		||||
                 block=True, timeout=None, callback=None, iterator=False):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        query_id = iq['id']
 | 
			
		||||
 | 
			
		||||
        iq['to'] = jid
 | 
			
		||||
        iq['from'] = ifrom
 | 
			
		||||
        iq['type'] = 'get'
 | 
			
		||||
        iq['mam']['queryid'] = query_id
 | 
			
		||||
        iq['mam']['start'] = start
 | 
			
		||||
        iq['mam']['end'] = end
 | 
			
		||||
        iq['mam']['with'] = with_jid
 | 
			
		||||
 | 
			
		||||
        collector = Collector(
 | 
			
		||||
            'MAM_Results_%s' % query_id,
 | 
			
		||||
            StanzaPath('message/mam_result@queryid=%s' % query_id))
 | 
			
		||||
        self.xmpp.register_handler(collector)
 | 
			
		||||
 | 
			
		||||
        if iterator:
 | 
			
		||||
            return self.xmpp['xep_0059'].iterate(iq, 'mam', 'results')
 | 
			
		||||
        elif not block and callback is not None:
 | 
			
		||||
            def wrapped_cb(iq):
 | 
			
		||||
                results = collector.stop()
 | 
			
		||||
                if iq['type'] == 'result':
 | 
			
		||||
                    iq['mam']['results'] = results
 | 
			
		||||
                callback(iq)
 | 
			
		||||
            return iq.send(block=block, timeout=timeout, callback=wrapped_cb)
 | 
			
		||||
        else:
 | 
			
		||||
            try:
 | 
			
		||||
                resp = iq.send(block=block, timeout=timeout, callback=callback)
 | 
			
		||||
                resp['mam']['results'] = collector.stop()
 | 
			
		||||
                return resp
 | 
			
		||||
            except XMPPError as e:
 | 
			
		||||
                collector.stop()
 | 
			
		||||
                raise e
 | 
			
		||||
 | 
			
		||||
    def set_preferences(self, jid=None, default=None, always=None, never=None,
 | 
			
		||||
                        ifrom=None, block=True, timeout=None, callback=None):
 | 
			
		||||
        iq = self.xmpp.Iq()
 | 
			
		||||
        iq['type'] = 'set'
 | 
			
		||||
        iq['to'] = jid
 | 
			
		||||
        iq['from'] = ifrom
 | 
			
		||||
        iq['mam_prefs']['default'] = default
 | 
			
		||||
        iq['mam_prefs']['always'] = always
 | 
			
		||||
        iq['mam_prefs']['never'] = never
 | 
			
		||||
        return iq.send(block=block, timeout=timeout, callback=callback)
 | 
			
		||||
 | 
			
		||||
    def get_configuration_commands(self, jid, **kwargs):
 | 
			
		||||
        return self.xmpp['xep_0030'].get_items(
 | 
			
		||||
                jid=jid,
 | 
			
		||||
                node='urn:xmpp:mam#configure',
 | 
			
		||||
                **kwargs)
 | 
			
		||||
							
								
								
									
										131
									
								
								sleekxmpp/plugins/xep_0313/stanza.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								sleekxmpp/plugins/xep_0313/stanza.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,131 @@
 | 
			
		||||
"""
 | 
			
		||||
    SleekXMPP: The Sleek XMPP Library
 | 
			
		||||
    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
 | 
			
		||||
    This file is part of SleekXMPP.
 | 
			
		||||
 | 
			
		||||
    See the file LICENSE for copying permissio
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import datetime as dt
 | 
			
		||||
 | 
			
		||||
from sleekxmpp.jid import JID
 | 
			
		||||
from sleekxmpp.xmlstream import ElementBase, ET
 | 
			
		||||
from sleekxmpp.plugins import xep_0082
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MAM(ElementBase):
 | 
			
		||||
    name = 'query'
 | 
			
		||||
    namespace = 'urn:xmpp:mam:tmp'
 | 
			
		||||
    plugin_attrib = 'mam'
 | 
			
		||||
    interfaces = set(['queryid', 'start', 'end', 'with', 'results'])
 | 
			
		||||
    sub_interfaces = set(['start', 'end', 'with'])
 | 
			
		||||
 | 
			
		||||
    def setup(self, xml=None):
 | 
			
		||||
        ElementBase.setup(self, xml)
 | 
			
		||||
        self._results = []
 | 
			
		||||
 | 
			
		||||
    def get_start(self):
 | 
			
		||||
        timestamp = self._get_attr('start')
 | 
			
		||||
        return xep_0082.parse(timestamp)
 | 
			
		||||
 | 
			
		||||
    def set_start(self, value):
 | 
			
		||||
        if isinstance(value, dt.datetime):
 | 
			
		||||
            value = xep_0082.format_datetime(value)
 | 
			
		||||
        self._set_attr('start', value)
 | 
			
		||||
 | 
			
		||||
    def get_end(self):
 | 
			
		||||
        timestamp = self._get_sub_text('end')
 | 
			
		||||
        return xep_0082.parse(timestamp)
 | 
			
		||||
 | 
			
		||||
    def set_end(self, value):
 | 
			
		||||
        if isinstance(value, dt.datetime):
 | 
			
		||||
            value = xep_0082.format_datetime(value)
 | 
			
		||||
        self._set_sub_text('end', value)
 | 
			
		||||
 | 
			
		||||
    def get_with(self):
 | 
			
		||||
        return JID(self._get_sub_text('with'))
 | 
			
		||||
 | 
			
		||||
    def set_with(self, value):
 | 
			
		||||
        self._set_sub_text('with', str(value))
 | 
			
		||||
 | 
			
		||||
    # The results interface is meant only as an easy
 | 
			
		||||
    # way to access the set of collected message responses
 | 
			
		||||
    # from the query.
 | 
			
		||||
 | 
			
		||||
    def get_results(self):
 | 
			
		||||
        return self._results
 | 
			
		||||
 | 
			
		||||
    def set_results(self, values):
 | 
			
		||||
        self._results = values
 | 
			
		||||
 | 
			
		||||
    def del_results(self):
 | 
			
		||||
        self._results = []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Preferences(ElementBase):
 | 
			
		||||
    name = 'prefs'
 | 
			
		||||
    namespace = 'urn:xmpp:mam:tmp'
 | 
			
		||||
    plugin_attrib = 'mam_prefs'
 | 
			
		||||
    interfaces = set(['default', 'always', 'never'])
 | 
			
		||||
    sub_interfaces = set(['always', 'never'])
 | 
			
		||||
 | 
			
		||||
    def get_always(self):
 | 
			
		||||
        results = set()
 | 
			
		||||
 | 
			
		||||
        jids = self.xml.findall('{%s}always/{%s}jid' % (
 | 
			
		||||
            self.namespace, self.namespace))
 | 
			
		||||
 | 
			
		||||
        for jid in jids:
 | 
			
		||||
            results.add(JID(jid.text))
 | 
			
		||||
 | 
			
		||||
        return results
 | 
			
		||||
 | 
			
		||||
    def set_always(self, value):
 | 
			
		||||
        self._set_sub_text('always', '', keep=True)
 | 
			
		||||
        always = self.xml.find('{%s}always' % self.namespace)
 | 
			
		||||
        always.clear()
 | 
			
		||||
 | 
			
		||||
        if not isinstance(value, (list, set)):
 | 
			
		||||
            value = [value]
 | 
			
		||||
 | 
			
		||||
        for jid in value:
 | 
			
		||||
            jid_xml = ET.Element('{%s}jid' % self.namespace)
 | 
			
		||||
            jid_xml.text = str(jid)
 | 
			
		||||
            always.append(jid_xml)
 | 
			
		||||
 | 
			
		||||
    def get_never(self):
 | 
			
		||||
        results = set()
 | 
			
		||||
 | 
			
		||||
        jids = self.xml.findall('{%s}never/{%s}jid' % (
 | 
			
		||||
            self.namespace, self.namespace))
 | 
			
		||||
 | 
			
		||||
        for jid in jids:
 | 
			
		||||
            results.add(JID(jid.text))
 | 
			
		||||
 | 
			
		||||
        return results
 | 
			
		||||
 | 
			
		||||
    def set_never(self, value):
 | 
			
		||||
        self._set_sub_text('never', '', keep=True)
 | 
			
		||||
        never = self.xml.find('{%s}never' % self.namespace)
 | 
			
		||||
        never.clear()
 | 
			
		||||
 | 
			
		||||
        if not isinstance(value, (list, set)):
 | 
			
		||||
            value = [value]
 | 
			
		||||
 | 
			
		||||
        for jid in value:
 | 
			
		||||
            jid_xml = ET.Element('{%s}jid' % self.namespace)
 | 
			
		||||
            jid_xml.text = str(jid)
 | 
			
		||||
            never.append(jid_xml)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Result(ElementBase):
 | 
			
		||||
    name = 'result'
 | 
			
		||||
    namespace = 'urn:xmpp:mam:tmp'
 | 
			
		||||
    plugin_attrib = 'mam_result'
 | 
			
		||||
    interfaces = set(['forwarded', 'queryid', 'id'])
 | 
			
		||||
 | 
			
		||||
    def get_forwarded(self):
 | 
			
		||||
        return self.parent()['forwarded']
 | 
			
		||||
 | 
			
		||||
    def del_forwarded(self):
 | 
			
		||||
        del self.parent()['forwarded']
 | 
			
		||||
@@ -479,11 +479,11 @@ class RosterItem(object):
 | 
			
		||||
            self.xmpp.event('roster_subscription_removed', presence)
 | 
			
		||||
 | 
			
		||||
    def handle_probe(self, presence):
 | 
			
		||||
        if self['to']:
 | 
			
		||||
        if self['from']:
 | 
			
		||||
            self.send_last_presence()
 | 
			
		||||
        if self['pending_out']:
 | 
			
		||||
            self.subscribe()
 | 
			
		||||
        if not self['to']:
 | 
			
		||||
        if not self['from']:
 | 
			
		||||
            self._unsubscribed()
 | 
			
		||||
 | 
			
		||||
    def reset(self):
 | 
			
		||||
 
 | 
			
		||||
@@ -154,7 +154,7 @@ class Iq(RootStanza):
 | 
			
		||||
        StanzaBase.reply(self, clear)
 | 
			
		||||
        return self
 | 
			
		||||
 | 
			
		||||
    def send(self, block=True, timeout=None, callback=None, now=False):
 | 
			
		||||
    def send(self, block=True, timeout=None, callback=None, now=False, timeout_callback=None):
 | 
			
		||||
        """
 | 
			
		||||
        Send an <iq> stanza over the XML stream.
 | 
			
		||||
 | 
			
		||||
@@ -181,15 +181,32 @@ class Iq(RootStanza):
 | 
			
		||||
            now      -- Indicates if the send queue should be skipped and send
 | 
			
		||||
                        the stanza immediately. Used during stream
 | 
			
		||||
                        initialization. Defaults to False.
 | 
			
		||||
            timeout_callback -- Optional reference to a stream handler function.
 | 
			
		||||
                        Will be executed when the timeout expires before a 
 | 
			
		||||
                        response has been received with the originally-sent IQ 
 | 
			
		||||
                        stanza.  Only called if there is a callback parameter
 | 
			
		||||
                        (and therefore are in async mode).
 | 
			
		||||
        """
 | 
			
		||||
        if timeout is None:
 | 
			
		||||
            timeout = self.stream.response_timeout
 | 
			
		||||
        if callback is not None and self['type'] in ('get', 'set'):
 | 
			
		||||
            handler_name = 'IqCallback_%s' % self['id']
 | 
			
		||||
            handler = Callback(handler_name,
 | 
			
		||||
                               MatcherId(self['id']),
 | 
			
		||||
                               callback,
 | 
			
		||||
                               once=True)
 | 
			
		||||
            if timeout_callback:
 | 
			
		||||
                self.callback = callback
 | 
			
		||||
                self.timeout_callback = timeout_callback
 | 
			
		||||
                self.stream.schedule('IqTimeout_%s' % self['id'], 
 | 
			
		||||
                                     timeout, 
 | 
			
		||||
                                     self._fire_timeout, 
 | 
			
		||||
                                     repeat=False)            
 | 
			
		||||
                handler = Callback(handler_name,
 | 
			
		||||
                                   MatcherId(self['id']),
 | 
			
		||||
                                   self._handle_result,
 | 
			
		||||
                                   once=True)
 | 
			
		||||
            else:
 | 
			
		||||
                handler = Callback(handler_name,
 | 
			
		||||
                                   MatcherId(self['id']),
 | 
			
		||||
                                   callback,
 | 
			
		||||
                                   once=True)
 | 
			
		||||
            self.stream.register_handler(handler)
 | 
			
		||||
            StanzaBase.send(self, now=now)
 | 
			
		||||
            return handler_name
 | 
			
		||||
@@ -206,6 +223,16 @@ class Iq(RootStanza):
 | 
			
		||||
        else:
 | 
			
		||||
            return StanzaBase.send(self, now=now)
 | 
			
		||||
 | 
			
		||||
    def _handle_result(self, iq):
 | 
			
		||||
        # we got the IQ, so don't fire the timeout
 | 
			
		||||
        self.stream.scheduler.remove('IqTimeout_%s' % self['id'])
 | 
			
		||||
        self.callback(iq)
 | 
			
		||||
 | 
			
		||||
    def _fire_timeout(self):
 | 
			
		||||
        # don't fire the handler for the IQ, if it finally does come in
 | 
			
		||||
        self.stream.remove_handler('IqCallback_%s' % self['id'])
 | 
			
		||||
        self.timeout_callback(self)
 | 
			
		||||
 | 
			
		||||
    def _set_stanza_values(self, values):
 | 
			
		||||
        """
 | 
			
		||||
        Set multiple stanza interface values using a dictionary.
 | 
			
		||||
 
 | 
			
		||||
@@ -63,6 +63,17 @@ class Message(RootStanza):
 | 
			
		||||
    lang_interfaces = sub_interfaces
 | 
			
		||||
    types = set(['normal', 'chat', 'headline', 'error', 'groupchat'])
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Initialize a new <message /> stanza with an optional 'id' value.
 | 
			
		||||
 | 
			
		||||
        Overrides StanzaBase.__init__.
 | 
			
		||||
        """
 | 
			
		||||
        StanzaBase.__init__(self, *args, **kwargs)
 | 
			
		||||
        if self['id'] == '':
 | 
			
		||||
            if self.stream is not None and self.stream.use_message_ids:
 | 
			
		||||
                self['id'] = self.stream.new_id()
 | 
			
		||||
 | 
			
		||||
    def get_type(self):
 | 
			
		||||
        """
 | 
			
		||||
        Return the message type.
 | 
			
		||||
 
 | 
			
		||||
@@ -72,6 +72,17 @@ class Presence(RootStanza):
 | 
			
		||||
                 'subscribed', 'unsubscribe', 'unsubscribed'])
 | 
			
		||||
    showtypes = set(['dnd', 'chat', 'xa', 'away'])
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Initialize a new <presence /> stanza with an optional 'id' value.
 | 
			
		||||
 | 
			
		||||
        Overrides StanzaBase.__init__.
 | 
			
		||||
        """
 | 
			
		||||
        StanzaBase.__init__(self, *args, **kwargs)
 | 
			
		||||
        if self['id'] == '':
 | 
			
		||||
            if self.stream is not None and self.stream.use_presence_ids:
 | 
			
		||||
                self['id'] = self.stream.new_id()
 | 
			
		||||
 | 
			
		||||
    def exception(self, e):
 | 
			
		||||
        """
 | 
			
		||||
        Override exception passback for presence.
 | 
			
		||||
 
 | 
			
		||||
@@ -130,7 +130,10 @@ class RosterItem(ElementBase):
 | 
			
		||||
    def get_groups(self):
 | 
			
		||||
        groups = []
 | 
			
		||||
        for group in self.xml.findall('{%s}group' % self.namespace):
 | 
			
		||||
            groups.append(group.text)
 | 
			
		||||
            if group.text:
 | 
			
		||||
                groups.append(group.text)
 | 
			
		||||
            else:
 | 
			
		||||
                groups.append('')
 | 
			
		||||
        return groups
 | 
			
		||||
 | 
			
		||||
    def set_groups(self, values):
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user