fritzy / SleekXMPP

Python 2.6+/3.1+ XMPP Library
http://groups.google.com/group/sleekxmpp-discussion
Other
1.1k stars 299 forks source link

Unable to transfer files using XEP-0047 (or others) #236

Closed SecurityForUs closed 11 years ago

SecurityForUs commented 11 years ago

Either the examples are out of date or something, but I cannot send or receive files via IBB (XEP-0047), or even trying to set up a stream initialization (XEP-0065) [also fails on handshake()].

What I'm trying to do is transfer files between Pidgin and SleekXMPP.

Is there newer code or something to be used?

I tried this using hte develop branch as well and it didn't do me any good, either.

SecurityForUs commented 11 years ago

Here's the output after trying to send a file through Psi+ to the SleekXMPP client:

DEBUG    at 2013-05-22 11:25:03,408 : RECV: <iq to="server_free@localhost/192.168.0.6" type="set" id="ab73a" from="admin@localhost/Psi+">
<si xmlns="http://jabber.org/protocol/si" profile="http://jabber.org/protocol/si/profile/file-transfer" id="ft_7930">
<file xmlns="http://jabber.org/protocol/si/profile/file-transfer" name="test" size="558">
<range />
</file>
<feature xmlns="http://jabber.org/protocol/feature-neg">
<x xmlns="jabber:x:data" type="form">
<field var="stream-method" type="list-single">
<option>
<value>http://jabber.org/protocol/ibb</value>
</option>
</field>
</x>
</feature>
</si>
</iq>
DEBUG    at 2013-05-22 11:25:03,456 : SEND: <iq to="admin@localhost/Psi+" type="error" id="ab73a">
<error type="cancel"><feature-not-implemented xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" /><text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">No handlers registered for this request.</text></error></iq>

I have an ibb_stream_start event handler set up, and it's being threaded (threaded=True).

I can't really show the code because its all loaded via plugins, but here's the general scheme of how I have it set up:

# I don't know why I am loading 0231 anymore
load_xep_plugins(xep_0047, xep_0030, xep_0231)

add_event_handler("ibb_stream_start", ibb_stream_start_func, **ibb_stream_start_opts)

The ibb_stream_start_opts is a kwarg of {'threaded' : True}.

legastero commented 11 years ago

Be sure to also load xep_0096 and xep_0095. Those control the overall stream session setup that needs to be negotiated first.

SecurityForUs commented 11 years ago

Is there a way to get XEP-0095 into master?

I installed SleekXMPP through pip after develop was acting up on me, and in that XEP-0095 isn't included.

SecurityForUs commented 11 years ago

Okay, I loaded those plugins (just git clone'd the develop branch), and now when a file is requested to be sent it shoots a si_request event. From there the request stalls (but doesn't error out).

DEBUG    at 2013-05-22 11:49:54,521 : RECV: <iq to="server_free@localhost/192.168.0.6" type="set" id="ab76a" from="admin@localhost/Psi+">
<si xmlns="http://jabber.org/protocol/si" profile="http://jabber.org/protocol/si/profile/file-transfer" id="ft_887f">
<file xmlns="http://jabber.org/protocol/si/profile/file-transfer" name="test" size="558">
<range />
</file>
<feature xmlns="http://jabber.org/protocol/feature-neg">
<x xmlns="jabber:x:data" type="form">
<field var="stream-method" type="list-single">
<option>
<value>http://jabber.org/protocol/ibb</value>
</option>
</field>
</x>
</feature>
</si>
</iq>
DEBUG    at 2013-05-22 11:49:54,542 : Event triggered: si_request

I removed the "accept_stream" option for XEP-0047, but would still like that in so I can see who sent the request for security/ACL reasons.

SecurityForUs commented 11 years ago

More details, I added an event handler for si_request, and get this output:

ERROR    at 2013-05-22 12:26:28,851 : 'NoneType' object has no attribute '__getitem__'
Traceback (most recent call last):
  File "/home/eric/.virtenvs/sysbotxmpp/lib/python2.7/site-packages/sleekxmpp/xmlstream/xmlstream.py", line 1611, in _threaded_event_wrapper
    func(*args)
  File "/home/eric/git/sysbotxmpp/plugins/event_handler/si_request/si_request.py", line 19, in run
    self.xmpp['xep_0095'].accept(sender, sid)
  File "/home/eric/.virtenvs/sysbotxmpp/lib/python2.7/site-packages/sleekxmpp/plugins/xep_0095/stream_initiation.py", line 145, in accept
    iq['id'] = stream['response_id']
TypeError: 'NoneType' object has no attribute '__getitem__'
DEBUG    at 2013-05-22 12:26:28,881 : SEND: <iq to="admin@localhost/Psi+" type="error" id="ab85a">
<error type="cancel"><undefined-condition xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" /><text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">SleekXMPP got into trouble.</text></error></iq>

Here is the code I'm using:

    def run(self, *args, **kwargs):
        #print "> args:",args
        iq = args[0] # This is the IQ provided in my last message
        sid = iq['id']
        sender = iq['from']
        self.xmpp['xep_0095'].accept(sender, sid) # Error happens when calling accept()

This event isn't threaded, but I don't see that being an issue with this as its complaining that the stream call failed (in stream_initiation.py):

    def accept(self, jid, sid, payload=None, ifrom=None, stream_handler=None):
        stream = self.api['get_pending'](ifrom, sid, jid)
legastero commented 11 years ago

So, using the develop branch, this is working for me to send a file to Sleek from Adium:

def on_si(iq):
    xmpp['xep_0095'].accept(iq['from'], iq['si']['id'])
xmpp.add_event_handler('si_request', on_si)

It looks like you've been using iq['id'] instead of iq['si']['id']

SecurityForUs commented 11 years ago

There was a couple of issues I had with it (one: thinking iq['to'] was the jid to pass in the first param).

I'm able to transfer files now, but I think using GPG will cause issues as it encrypts the body's data, in this case the file data.

Thanks for the heads up on that, now to figure out about this decryption ordeal.

SecurityForUs commented 11 years ago

Nevermind my last comment. I was sending a GPG file and didn't think about that.

Though, I do notice I get an error after I receive the data:

DEBUG    at 2013-05-22 13:12:55,013 : SEND: <iq to="admin@localhost/Psi+" type="result" id="abeea">
</iq>
DEBUG    at 2013-05-22 13:12:55,029 : RECV: <iq to="server_free@localhost/192.168.0.6" type="set" id="abefa" from="admin@localhost/Psi+">
<close xmlns="http://jabber.org/protocol/ibb" sid="ft_fc8e" />
</iq>
ERROR    at 2013-05-22 13:12:55,044 : Error processing stream handler: IBB Close
Traceback (most recent call last):
  File "/home/eric/.virtenvs/sysbotxmpp/lib/python2.7/site-packages/sleekxmpp/xmlstream/xmlstream.py", line 1645, in _event_runner
    handler.run(args[0])
  File "/home/eric/.virtenvs/sysbotxmpp/lib/python2.7/site-packages/sleekxmpp/xmlstream/handler/callback.py", line 76, in run
    self._pointer(payload)
  File "/home/eric/.virtenvs/sysbotxmpp/lib/python2.7/site-packages/sleekxmpp/plugins/xep_0047/ibb.py", line 210, in _handle_close
    stream = self.api['get_stream'](stanza['to'], sid, stanza['from'])
TypeError: 'module' object has no attribute '__getitem__'
ERROR    at 2013-05-22 13:12:55,046 : Error handling {jabber:client}iq stanza
Traceback (most recent call last):
  File "/home/eric/.virtenvs/sysbotxmpp/lib/python2.7/site-packages/sleekxmpp/xmlstream/xmlstream.py", line 1645, in _event_runner
    handler.run(args[0])
  File "/home/eric/.virtenvs/sysbotxmpp/lib/python2.7/site-packages/sleekxmpp/xmlstream/handler/callback.py", line 76, in run
    self._pointer(payload)
  File "/home/eric/.virtenvs/sysbotxmpp/lib/python2.7/site-packages/sleekxmpp/plugins/xep_0047/ibb.py", line 210, in _handle_close
    stream = self.api['get_stream'](stanza['to'], sid, stanza['from'])
TypeError: 'module' object has no attribute '__getitem__'
ERROR    at 2013-05-22 13:12:55,046 : 'module' object has no attribute '__getitem__'
Traceback (most recent call last):
  File "/home/eric/.virtenvs/sysbotxmpp/lib/python2.7/site-packages/sleekxmpp/xmlstream/xmlstream.py", line 1645, in _event_runner
    handler.run(args[0])
  File "/home/eric/.virtenvs/sysbotxmpp/lib/python2.7/site-packages/sleekxmpp/xmlstream/handler/callback.py", line 76, in run
    self._pointer(payload)
  File "/home/eric/.virtenvs/sysbotxmpp/lib/python2.7/site-packages/sleekxmpp/plugins/xep_0047/ibb.py", line 210, in _handle_close
    stream = self.api['get_stream'](stanza['to'], sid, stanza['from'])
TypeError: 'module' object has no attribute '__getitem__'
DEBUG    at 2013-05-22 13:12:55,078 : SEND: <iq to="admin@localhost/Psi+" type="error" id="abefa">
<error type="cancel"><undefined-condition xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" /><text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">SleekXMPP got into trouble.</text></error></iq>

Am I to do event['stream'].close() after the data's been read??

legastero commented 11 years ago

Ah, turns out there were two typos in the IBB plugin. Those have been fixed and pushed to develop.

SecurityForUs commented 11 years ago

Thanks, that fixed the receiving issue. Is there any special tricks or anything on sending? Here's the code I got so far:

        print "> who:",who
        print "> file:",file_

        try:
            stream = self.xmpp['xep_0047'].open_stream(who)
        except IqError, e:
            print "> Error:", e.iq['error'].get("text")

I get this:

> who: admin@localhost
> file: /tmp/blah
DEBUG    at 2013-05-22 13:40:57,874 : SEND: <iq to="admin@localhost" type="set" id="67360884-bf4f-4f79-8f82-579dd168772e-7"><open xmlns="http://jabber.org/protocol/ibb" stanza="iq" block-size="4096" sid="5e34a59e-33e6-44c8-876e-09f479d2e275" /></iq>
DEBUG    at 2013-05-22 13:40:57,876 : RECV: <iq to="server_free@localhost/192.168.0.6" type="error" id="67360884-bf4f-4f79-8f82-579dd168772e-7" from="admin@localhost"><error type="cancel"><service-unavailable xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" /></error></iq>
> Error: None
SecurityForUs commented 11 years ago

If I add the resource to the who JID (so admin@localhost/Psi+) I get an error saying it was rejected, but I was never given a popup for file transfer (so I'm not sure if this is more of an issue w/ my client or not).

legastero commented 11 years ago

Clients generally don't just accept arbitrary IBB/SOCKS5 stream requests, they have to be negotiated first.

xep['xep_0096'].request_file_transfer() is used to kick that off, and you'll get back the other client's preferred stream connection method.

legastero commented 11 years ago

And yes, a full JID with resource is required, otherwise you're talking to the user's server (ie, talking to their 'account', not a connected client).

SecurityForUs commented 11 years ago

Oh yeah hah, forgot about that nifty little shin-dig. Alright, cool! Put that in and see what happens.

at resource statement: makes sense; I'm still getting used to the finer details of the XMPP protocol heh.

SecurityForUs commented 11 years ago

Question...what is the flow that I have to do to send a file?

Here's my current code:

        data = self.xmpp['xep_0096'].request_file_transfer(who,name="blah",size="19888", allow_ranged=True)

        if data['type'] == "result":
            try:
                                # who = my Psi+ account (admin@localhost/Psi+), self.xmpp.login = client login w/ resource (server_free@localhost/local_ip
                acc = self.xmpp['xep_0095'].accept(who,data['si']['id'],self.xmpp.login)
                stream = self.xmpp['xep_0047'].open_stream(who, ifrom=self.xmpp.login, block_size=4096)
            except IqError, e:
                print "> Error:",e.iq['error'].get("text")

Here's the output:

DEBUG    at 2013-05-22 14:22:21,303 : SEND: <iq to="admin@localhost/Psi+" type="set" id="313236aa-3fe3-4973-8fa9-eb5619f7266f-7"><si xmlns="http://jabber.org/protocol/si" profile="http://jabber.org/protocol/si/profile/file-transfer" id="87349c1f5bab410b9d4a591c24238b47"><file xmlns="http://jabber.org/protocol/si/profile/file-transfer" name="blah" size="19888"><range /></file><feature xmlns="http://jabber.org/protocol/feature-neg"><x xmlns="jabber:x:data" type="form"><field var="stream-method" type="list-single"><option><value>http://jabber.org/protocol/ibb</value></option><option><value>http://jabber.org/protocol/bytestreams</value></option></field></x></feature></si></iq>
DEBUG    at 2013-05-22 14:22:26,292 : RECV: <iq to="server_free@localhost/192.168.0.6" type="result" id="313236aa-3fe3-4973-8fa9-eb5619f7266f-7" from="admin@localhost/Psi+">
<si xmlns="http://jabber.org/protocol/si">
<feature xmlns="http://jabber.org/protocol/feature-neg">
<x xmlns="jabber:x:data" type="submit">
<field var="stream-method">
<value>http://jabber.org/protocol/ibb</value>
</field>
</x>
</feature>
</si>
</iq>
ERROR    at 2013-05-22 14:22:26,319 : Error processing event handler: <bound method message.run of <message.message object at 0x1cb7950>>
Traceback (most recent call last):
  File "/home/eric/.virtenvs/sysbotxmpp/lib/python2.7/site-packages/sleekxmpp/xmlstream/xmlstream.py", line 1669, in _event_runner
    func(*args)
  File "/home/eric/git/sysbotxmpp/plugins/event_handler/message/message.py", line 98, in run
    resp = plugin.run(args, msg=body, arg_sep=self.xmpp_conf.arg_sep, sleekxmpp=self.xmpp)
  File "/home/eric/git/sysbotxmpp/plugins/cmd/send_file/send_file.py", line 62, in run
    acc = self.xmpp['xep_0095'].accept(who,data['si']['id'],self.xmpp.login)
  File "/home/eric/.virtenvs/sysbotxmpp/lib/python2.7/site-packages/sleekxmpp/plugins/xep_0095/stream_initiation.py", line 145, in accept
    iq['id'] = stream['response_id']
TypeError: 'NoneType' object has no attribute '__getitem__'

I've tried matching this best to the transactions between both when I send a file to the client.

Basically this works just fine up until the point after I accept it through Psi+. So basically:

Request transfer -> prompt user to accept -> accept -> error above.

legastero commented 11 years ago
  1. Request file transfer, receive preferred stream connection method
  2. Open stream via the received preferred method
  3. Send data
  4. Close stream

So in this case you don't have to call accept() because you're not the one accepting the stream, you're the one offering it.

SecurityForUs commented 11 years ago

Any ideas why I would be getting a Rejected response in SleekXMPP when I accept it in Psi+?

legastero commented 11 years ago

Ah, forgot that you have to carry the stream ID through.

so:

resp = xmpp['xep_0096'].request_file_transfer('foo@example.com/bar', name='stuff', size='1024', sid='thestreamid')
xmpp['xep_0047'].open_stream('foo@example.com/bar', sid='thestreamid')
...
SecurityForUs commented 11 years ago

That works, thanks.

However, shouldn't request_file_transfer return a sid? Due to the code in offer() that request_file_transfer calls?

https://github.com/fritzy/SleekXMPP/blob/develop/sleekxmpp/plugins/xep_0095/stream_initiation.py#L118-119

legastero commented 11 years ago

Technically, the sid returns back to you in the other client's response.

resp = xmpp['xep_0096'].request_file_transfer('foo@example.com/bar', name='stuff', size='1024')
xmpp['xep_0047'].open_stream('foo@example.com/bar', sid=resp['si']['id'])
SecurityForUs commented 11 years ago

Yeah so you wouldn't have to manually create the sid, then, just get it from the response of the request_file_transfer, right?

legastero commented 11 years ago

Right. You just have to be sure to include it so that everything is linked together to the user's approval.

SecurityForUs commented 11 years ago

Is there a reason why I wouldn't get it?

Using the example you provided, this is my return output from request_file_transfer:

DEBUG    at 2013-05-22 14:48:46,148 : RECV: <iq to="server_free@localhost/192.168.0.6" type="result" id="81623fe1-deb8-4319-86d4-7eefd1a4d177-7" from="admin@localhost/Psi+">
<si xmlns="http://jabber.org/protocol/si">
<feature xmlns="http://jabber.org/protocol/feature-neg">
<x xmlns="jabber:x:data" type="submit">
<field var="stream-method">
<value>http://jabber.org/protocol/ibb</value>
</field>
</x>
</feature>
</si>
</iq>
legastero commented 11 years ago

Ah, alright then; that's what I get for going on memory. Looks like I'll have to just explicitly require pre-generating a UUID and carrying it manually.

SecurityForUs commented 11 years ago

Thanks; quick question...is there a way to get the full JID (name@domain/resource) from the roster, or is there a way to do this some other way?

This is assuming I can't reference an IQ (i.e.: msg['from']) to pull the information from.

SecurityForUs commented 11 years ago

I've been trying to find an answer on Google and looking through the code and I can't seem to find a solution on getting the full JID of the roster (i.e.: including the resource). Is there any method to do this or am I paddling up a creek?

Here's the code I'm currently using:

        for r in self.xmpp.client_roster.keys():
            print "> id:",r
            print "> jid:",self.xmpp.client_roster[r].resources

Here's the output:

> id: admin@localhost
> jid: {}
> id: server_bump@localhost
> jid: {}

I understand the resources for server_bump is empty considering its not online, but when I'm logged into admin@localhost I thought I would be able to get the resources for that.

legastero commented 11 years ago

Right, client_roster[jid].resources should contain what you need. Unless you need the full JID for the client you're using, which would be xmpp.boundjid.

When are you running that code? It might be running too early, before presence information arrives.

SecurityForUs commented 11 years ago

I was calling it in my session_start handler (after self.get_roster & self.send_presence). However, I looked at the example for roster_browser and worked with that instead. What I ended up doing is creating an internal storage in my class for the roster, doing the threading code and was able to get the roster that way.

Personally I feel the biggest lacking piece of documentation about this software is the options to pass in event_handler. For example, I don't know why passing threaded=True to add_event_handler made it work, I just know it did. I'm not trying sound mean or be a dick, apologies if it came off that way.

legastero commented 11 years ago

Right, so the core of Sleek is the event processing queue. All matched stanza handlers, custom events, etc go through that and are processed in order. What's happening here is that the session_start event handler is running, and all of events related to the inbound presence updates which would update the roster data are stuck in the queue waiting for the session_start event handler to finish.

SecurityForUs commented 11 years ago

Makes sense, thanks. :) Going to close this as the issue is resolved, and now I'm stress-free over this. :D

bozdag commented 10 years ago

Can you please provide a full example of working code that sends a file ?

sidharthainnovateapps commented 10 years ago

Can you please provide a full example of working code that sends a file ? ------- my email id is sidhartha@innovateapps.com