I'll try to describe some facts about how gajim works, ie. what calls are needed to do things. Feel free to correct things. File names are written in italic, identifiers are written in bold monospace font. Callables other than classes get parentheses. If a function calls another, we get next level of indentation, but only if the call isn't effectively the last thing caller does.
Connection process
Description of how does gajim handle internally the process of connecting and authorizing to the xmpp server. This is a bit complicated and involves several layers (common/connection.py, common/xmpp/client_nb.py, common/xmpp/transports_nb.py).
common/connection.py
- Something creates Connection object. Connection is a class that controls the connection in a UI-independent way.
- Something calls Connection.connect(). It figures out the parameters of a connection (low-level things like server/port), then puts a callback (Connection._on_resolve()) to handle dns discovery answer.
- When the answer comes, Connection._on_resolve() calls Connection.connect_to_next_host() to connect to first server/port pair.
- Connection.connect_to_next_host(): if there's any server in Connection._hosts to check, try to connect to it by:
- creating NonBlockingClient object from common/xmpp/client_nb.py,
- setting basic callbacks (a lot of unclear things here, like caller, idlequeue). Most important one: on connection success (on_connect) Connection.on_connect_success() will be called.
- calling NonBlockingClient.connect() to initiate the connection.
common/xmpp/client_nb.py
I'll treat insignificant parts (e.g. transports) as black boxes. (NonBlockingClient is a NBCommonClient descendant, which in turn is a CommonClient descentant).
- NonBlockingClient.connect() calls NBCommonClient.connect().
- NBCommonClient.connect() initiates the connection using appropriate socket wrapper (either SOCKS/HTTP proxy or plain TCP -- called a «transport» here), and sets callbacks; most important one: NonBlockingClient._on_receive_document_attrs() will be called every time socket has received data; plugs a Dispatcher object (from common/xmpp/dispatcher_nb.py) into itself, so that NBCommonClient object gets methods to control the dispatcher (like RegisterHandler) (this is done in a messy way -- inside a callback that is known to be called before NBCommonClient.connect() is done). Also plugs the transport object into itself.
After that we wait for data and process them... Time passes...
- Transport gets data. Broadcasts an event using owner's Dispatcher, if possible (not important now). Then calls his on_receive callback, so...
- ...NonBlockingClient._on_receive_document_attrs() gets called, which in turn calls the Dispatcher.ProcessNonBlocking(), which calls the XML parser, which probably calls its stream_header_received callback, which is set up by Dispatcher to Dispatcher._check_stream_start, which only checks if the stream starts with appropriate element.
Interesting items:
- Connection._hosts - a list of dictionaries containing the result of dns query/user entered info about server/port.
- Dispatcher.Stream - the XML parser object from common/xmpp/simplexml.py
- IdleQueue - class polling list of socket file descriptors. It is used with non-blocking sockets. Instance of class implementing IdleObject interface can register its file descriptor by calling idlequeue.plug_idle(self, writable=True/False, readable=True/False). File descriptor has to be in self.fd. Then, self.pollin() is called on read event, and self.pollout() is called on write event on registered socket. Check on socket events is performed in IdleQueue.process() method which should be called in regular intervals in separated thread.
- IdleQueue.set_read_timeout(fd, timeout) will call read_timeout() method of plugged IdleObject? instance with file descriptor fd unless the instance is unplugged during the timeout or IdleQueue.remove_timeout(fd) is called with particular file descriptor.
- IdleQueue.set_alarm(alarm_cb, seconds) will call allarm_cb after seconds seconds.
- to plug an object into another (Plugin.Plugin()) - a way of adding interface of one object to another - self.Plugin(owner) means the owner will get caller's methods from self._exported_methods and calling object will be assigned as owner.<caller's class name>, e.g. following
will print
class Client(): pass class TcpTransport(Plugin): __init__(self): self._exported_methods=[self.send, self.onreceive] send(self): print 'send something' onreceive(self): print 'something received' if __name__ == '__main__': client = Client() tcpsocket = TcpTransport() tcpsocket.Plugin(client) print dir(client)
['__doc__', '__module__', 'send', 'onreceive', 'TcpTransport']
