diff --git a/src/common/jingle_ft.py b/src/common/jingle_ft.py
index 06024ac4d399d8905359d34652c9cd0429d09a03..b5f7ed948e59fef50498ef959e01bf6bd426bcae 100644
--- a/src/common/jingle_ft.py
+++ b/src/common/jingle_ft.py
@@ -22,13 +22,13 @@ Handles  Jingle File Transfer (XEP 0234)
 import gajim
 import xmpp
 from jingle_content import contents, JingleContent
-from jingle_transport import JingleTransportICEUDP, JingleTransportSocks5
-from jingle_transport import JingleTransportIBB, TransportType
+from jingle_transport import *
 from common import helpers
 from common.socks5 import Socks5ReceiverClient, Socks5SenderClient
 from common.connection_handlers_events import FileRequestReceivedEvent
 import threading
 import logging
+from jingle_ftstates import *
 log = logging.getLogger('gajim.c.jingle_ft')
 
 STATE_NOT_STARTED = 0
@@ -68,8 +68,6 @@ class JingleFileTransfer(JingleContent):
         self.callbacks['transport-info'] += [self.__on_transport_info]
         self.callbacks['iq-result'] += [self.__on_iq_result]
 
-        self.state = STATE_NOT_STARTED
-
         self.use_security = use_security
 
         self.file_props = file_props
@@ -100,8 +98,21 @@ class JingleFileTransfer(JingleContent):
         self.session = session
         self.media = 'file'
         self.nominated_cand = {}
-        
-        
+
+        self.state = STATE_NOT_STARTED
+        self.states = {STATE_INITIALIZED   : StateInitialized(self),
+                       STATE_CAND_SENT     : StateCandSent(self),
+                       STATE_CAND_RECEIVED : StateCandReceived(self),
+                       STATE_TRANSFERING   : StateTransfering(self),
+                   STATE_TRANSPORT_REPLACE : StateTransportReplace(self),
+              STATE_CAND_SENT_AND_RECEIVED : StateCandSentAndRecv(self)
+                      }
+
+    def __state_changed(self, nextstate, args=None):
+        # Executes the next state action and sets the next state
+        st = self.states[nextstate]
+        st.action(args)
+        self.state = nextstate
 
     def __on_session_initiate(self, stanza, content, error, action):
         gajim.nec.push_incoming_event(FileRequestReceivedEvent(None,
@@ -148,7 +159,7 @@ class JingleFileTransfer(JingleContent):
             response.delChild(response.getQuery())
             con.connection.send(response)
             # We send the file
-            self.__start_IBB_transfer(con)
+            self.__state_changed(STATE_TRANSFERING)
             raise xmpp.NodeProcessed
 
         self.file_props['streamhosts'] = self.transport.remote_candidates
@@ -170,12 +181,11 @@ class JingleFileTransfer(JingleContent):
             fingerprint = 'client'
         if self.transport.type == TransportType.SOCKS5:
             gajim.socks5queue.connect_to_hosts(self.session.connection.name,
-                self.file_props['sid'], self.send_candidate_used,
+                self.file_props['sid'], self.on_connect,
                 self._on_connect_error, fingerprint=fingerprint,
                 receiving=False)
-        elif self.transport.type == TransportType.IBB:
-            self.state = STATE_TRANSFERING
-            self.__start_IBB_transfer(self.session.connection)
+            return
+        self.__state_changed(STATE_TRANSFERING)
         raise xmpp.NodeProcessed
 
     def __on_session_terminate(self, stanza, content, error, action):
@@ -203,16 +213,16 @@ class JingleFileTransfer(JingleContent):
                    not self.nominated_cand['peer-cand']:
                     if not self.weinitiate:
                         return
-                    self.session.transport_replace()
+                    self.__state_changed(STATE_TRANSPORT_REPLACE)
                 else:
                     response = stanza.buildReply('result')
                     response.delChild(response.getQuery())
                     self.session.connection.connection.send(response)
-                    self.start_transfer()
+                    self.__state_changed(STATE_TRANSFERING)
                     raise xmpp.NodeProcessed
             else:
-                self.state = STATE_CAND_RECEIVED
-
+                args = {'candError' : True}
+                self.__state_changed(STATE_CAND_RECEIVED, args)
             return
 
         if content.getTag('transport').getTag('activated'):
@@ -222,73 +232,39 @@ class JingleFileTransfer(JingleContent):
                 self.session.connection.name, 'client')
             return
 
-        streamhost_cid = content.getTag('transport').getTag('candidate-used').\
-            getAttr('cid')
-        streamhost_used = None
-        for cand in self.transport.candidates:
-            if cand['candidate_id'] == streamhost_cid:
-                streamhost_used = cand
-                break
-        if streamhost_used == None:
-            log.info("unknow streamhost")
-            return
-        # We save the candidate nominated by peer
-        self.nominated_cand['peer-cand'] = streamhost_used
+        args = {'content' : content,
+                'sendCand' : False}
         if self.state == STATE_CAND_SENT:
+            self.__state_changed(STATE_CAND_SENT_AND_RECEIVED, args)
             response = stanza.buildReply('result')
             response.delChild(response.getQuery())
             self.session.connection.connection.send(response)
-            self.start_transfer()
+            self.__state_changed(STATE_TRANSFERING)
             raise xmpp.NodeProcessed
         else:
-            self.state = STATE_CAND_RECEIVED
+            self.__state_changed(STATE_CAND_RECEIVED, args)
 
 
 
     def __on_iq_result(self, stanza, content, error, action):
         log.info("__on_iq_result")
 
-        if self.weinitiate and self.state == STATE_NOT_STARTED:
-            self.state = STATE_INITIALIZED
-            self.session.connection.files_props[self.file_props['sid']] = \
-                self.file_props
-            # Listen on configured port for file transfer
-            self._listen_host()
-        
-        elif not self.weinitiate and self.state == STATE_NOT_STARTED:
-            # session-accept iq-result
-            if not self.negotiated:
-                return
-            self.state = STATE_ACCEPTED
-            if not gajim.socks5queue.get_file_props(
-            self.session.connection.name, self.file_props['sid']):
-                gajim.socks5queue.add_file_props(self.session.connection.name,
-                    self.file_props)
-            fingerprint = None
-            if self.use_security:
-                fingerprint = 'client'
-            gajim.socks5queue.connect_to_hosts(self.session.connection.name,
-                self.file_props['sid'], self.send_candidate_used,
-                self._on_connect_error, fingerprint=fingerprint)
+        if self.state == STATE_NOT_STARTED:
+            self.__state_changed(STATE_INITIALIZED)
         elif self.state == STATE_CAND_SENT_AND_RECEIVED:
             if not self.nominated_cand['our-cand'] and \
             not self.nominated_cand['peer-cand']:
                 if not self.weinitiate:
                     return
-                self.session.transport_replace()
+                self.__state_changed(STATE_TRANSPORT_REPLACE)
                 return
             # initiate transfer
-            self.start_transfer()
+            self.__state_changed(STATE_TRANSFERING)
             
-    def __start_IBB_transfer(self, con):
-        con.files_props[self.file_props['sid']] = self.file_props
-        fp = open(self.file_props['file-name'], 'r')
-        con.OpenStream( self.transport.sid, self.session.peerjid, fp,
-            blocksize=4096)
-
     def __transport_setup(self, stanza=None, content=None, error=None,
     action=None):
         # Sets up a few transport specific things for the file transfer
+        # TODO: Do this inside of a state class
         if self.transport.type == TransportType.SOCKS5:
             self._listen_host()
             
@@ -296,48 +272,30 @@ class JingleFileTransfer(JingleContent):
             self.state = STATE_TRANSFERING
             
 
-    def send_candidate_used(self, streamhost):
+    def on_connect(self, streamhost):
         """
         send candidate-used stanza
         """
         log.info('send_candidate_used')
         if streamhost is None:
             return
+        args = {'streamhost' : streamhost,
+                'sendCand'   : True}
 
         self.nominated_cand['our-cand'] = streamhost
         if self.state == STATE_CAND_RECEIVED:
-            self.state = STATE_CAND_SENT_AND_RECEIVED
+            self.__state_changed(STATE_CAND_SENT_AND_RECEIVED, args)
         else:
-            self.state = STATE_CAND_SENT
-
-        content = xmpp.Node('content')
-        content.setAttr('creator', 'initiator')
-        content.setAttr('name', self.name)
-
-        transport = xmpp.Node('transport')
-        transport.setNamespace(xmpp.NS_JINGLE_BYTESTREAM)
-        transport.setAttr('sid', self.transport.sid)
-
-        candidateused = xmpp.Node('candidate-used')
-        candidateused.setAttr('cid', streamhost['cid'])
-
-        transport.addChild(node=candidateused)
-        content.addChild(node=transport)
-
-        self.session.send_transport_info(content)
+            self.__state_changed(STATE_CAND_SENT, args)
 
 
     def _on_connect_error(self, sid):
-        self.nominated_cand['our-cand'] = False
-        self.send_error_candidate()
-
+        log.info('connect error, sid=' + sid)
+        args = {'candError' : True}
         if self.state == STATE_CAND_RECEIVED:
-            self.state = STATE_CAND_SENT_AND_RECEIVED
+            self.__state_changed(STATE_CAND_SENT_AND_RECEIVED, args)
         else:
-            self.state = STATE_CAND_SENT
-
-
-        log.info('connect error, sid=' + sid)
+            self.__state_changed(STATE_CAND_SENT, args)
 
     def _fill_content(self, content):
         description_node = xmpp.simplexml.Node(
@@ -415,79 +373,6 @@ class JingleFileTransfer(JingleContent):
             return self.weinitiate
 
 
-    def start_transfer(self):
-
-        self.state = STATE_TRANSFERING
-        
-        # It tells wether we start the transfer as client or server
-        mode = None
-
-        if self.isOurCandUsed():
-            mode = 'client'
-            streamhost_used = self.nominated_cand['our-cand']
-        else:
-            mode = 'server'
-            streamhost_used = self.nominated_cand['peer-cand']
-            
-        if streamhost_used['type'] == 'proxy':
-            self.file_props['is_a_proxy'] = True
-            if self.weinitiate:
-                self.file_props['proxy_sender'] = streamhost_used['initiator']
-                self.file_props['proxy_receiver'] = streamhost_used['target']
-            else:
-                self.file_props['proxy_sender'] = streamhost_used['target']
-                self.file_props['proxy_receiver'] = streamhost_used['initiator']
-
-        if not self.weinitiate and streamhost_used['type'] == 'proxy':
-            r = gajim.socks5queue.readers
-            for reader in r:
-                if r[reader].host == streamhost_used['host'] and \
-                r[reader].connected:
-                    return
-
-        if self.weinitiate and streamhost_used['type'] == 'proxy':
-            s = gajim.socks5queue.senders
-            for sender in s:
-                if s[sender].host == streamhost_used['host'] and \
-                s[sender].connected:
-                    return
-
-        if streamhost_used['type'] == 'proxy': 
-            self.file_props['streamhost-used'] = True
-            streamhost_used['sid'] = self.file_props['sid']
-            self.file_props['streamhosts'] = []
-            self.file_props['streamhosts'].append(streamhost_used)
-            self.file_props['proxyhosts'] = []
-            self.file_props['proxyhosts'].append(streamhost_used)
-
-            if self.weinitiate:
-                gajim.socks5queue.idx += 1
-                idx = gajim.socks5queue.idx
-                sockobj = Socks5SenderClient(gajim.idlequeue, idx,
-                    gajim.socks5queue, _sock=None,
-                    host=str(streamhost_used['host']),
-                    port=int(streamhost_used['port']), fingerprint=None,
-                    connected=False, file_props=self.file_props)
-            else:
-                sockobj = Socks5ReceiverClient(gajim.idlequeue, streamhost_used,
-                    sid=self.file_props['sid'],
-                    file_props=self.file_props, fingerprint=None)
-            sockobj.proxy = True
-            sockobj.streamhost = streamhost_used
-            gajim.socks5queue.add_sockobj(self.session.connection.name,
-                sockobj, 'sender')
-            streamhost_used['idx'] = sockobj.queue_idx
-            # If we offered the nominated candidate used, we activate
-            # the proxy
-            if not self.isOurCandUsed():
-                gajim.socks5queue.on_success[self.file_props['sid']] = \
-                self.transport._on_proxy_auth_ok
-            # TODO: add on failure
-        else:
-            jid = gajim.get_jid_without_resource(self.session.ourjid)
-            gajim.socks5queue.send_file(self.file_props,
-                self.session.connection.name, mode)
-
 def get_content(desc):
     return JingleFileTransfer
 
diff --git a/src/common/jingle_ftstates.py b/src/common/jingle_ftstates.py
new file mode 100644
index 0000000000000000000000000000000000000000..72e5a485637053dcd550122f9b94d1152f2b1b18
--- /dev/null
+++ b/src/common/jingle_ftstates.py
@@ -0,0 +1,244 @@
+##
+## Copyright (C) 2006 Gajim Team
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published
+## by the Free Software Foundation; version 2 only.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+
+import gajim
+import xmpp
+from jingle_transport import *
+
+class JingleFileTransferStates:
+
+    # This class implements the state machine design pattern
+
+    def __init__(self, jingleft):
+
+        self.jft = jingleft
+
+    def action(self, args=None):
+        '''
+        This method MUST be overriden by a subclass
+        '''
+        raise Exception('This is an abstract method!!')
+
+
+class StateInitialized(JingleFileTransferStates):
+
+    '''
+    This state initializes the file transfer
+    '''
+
+    def action(self, args=None):
+        if self.jft.weinitiate:
+            # update connection's fileprops
+            self.jft.session.connection.files_props[self.jft.file_props['sid']] = \
+                self.jft.file_props
+            # Listen on configured port for file transfer
+            self.jft._listen_host() # TODO: Rename this to listen_host()
+        else:
+            # Add file_props to the queue
+            if not gajim.socks5queue.get_file_props(
+            self.jft.session.connection.name, self.jft.file_props['sid']):
+                    gajim.socks5queue.add_file_props(
+                            self.jft.session.connection.name, 
+                            self.jft.file_props)
+            fingerprint = None
+            if self.jft.use_security:
+                fingerprint = 'client'
+            # Connect to the candidate host, on success call on_connect method
+            gajim.socks5queue.connect_to_hosts(
+                            self.jft.session.connection.name,
+                            self.jft.file_props['sid'], self.jft.on_connect,
+                            self.jft._on_connect_error, fingerprint=fingerprint)
+
+
+class StateCandSent(JingleFileTransferStates):
+
+    '''
+    This state sends our nominated candidate
+    '''
+
+    def _sendCand(self, args):
+        if 'candError' in args:
+            self.jft.nominated_cand['our-cand'] = False
+            self.jft.send_error_candidate()
+            return
+        # Send candidate used
+        streamhost = args['streamhost']
+        self.jft.nominated_cand['our-cand'] = streamhost
+
+        content = xmpp.Node('content')
+        content.setAttr('creator', 'initiator')
+        content.setAttr('name', self.jft.name)
+
+        transport = xmpp.Node('transport')
+        transport.setNamespace(xmpp.NS_JINGLE_BYTESTREAM)
+        transport.setAttr('sid', self.jft.transport.sid)
+
+        candidateused = xmpp.Node('candidate-used')
+        candidateused.setAttr('cid', streamhost['cid'])
+
+        transport.addChild(node=candidateused)
+        content.addChild(node=transport)
+
+        self.jft.session.send_transport_info(content)
+
+
+    def action(self, args=None):
+        self._sendCand(args)
+
+class  StateCandReceived(JingleFileTransferStates):
+
+    '''
+    This state happens when we receive a candidate.
+    It takes the arguments: canError if we receive a candidate-error
+    '''
+
+    def _recvCand(self, args):
+        if 'candError' in args:
+            return
+        content = args['content']
+        streamhost_cid = content.getTag('transport').getTag('candidate-used').\
+            getAttr('cid')
+        streamhost_used = None
+        for cand in self.jft.transport.candidates:
+            if cand['candidate_id'] == streamhost_cid:
+                streamhost_used = cand
+                break
+        if streamhost_used == None:
+            log.info("unknow streamhost")
+            return
+        # We save the candidate nominated by peer
+        self.jft.nominated_cand['peer-cand'] = streamhost_used
+
+
+
+    def action(self, args=None):
+        self._recvCand(args)
+
+class StateCandSentAndRecv( StateCandSent, StateCandReceived):
+
+    '''
+    This state happens when we have received and sent the candidates.
+    It takes the boolean argument: sendCand in order to decide whether
+    we should execute the action of when we receive or send a candidate.
+    '''
+
+    def action(self, args=None):
+
+        if args['sendCand']:
+            self._sendCand(args)
+        else:
+            self._recvCand(args)
+
+class StateTransportReplace(JingleFileTransferStates):
+
+    '''
+    This state initiates transport replace
+    '''
+
+    def action(self, args=None):
+        self.jft.session.transport_replace()
+
+class StateTransfering(JingleFileTransferStates):
+
+    '''
+    This state will start the transfer depeding on the type of transport
+    we have.
+    '''
+
+    def __start_IBB_transfer(self, con):
+        con.files_props[self.jft.file_props['sid']] = \
+                        self.jft.file_props
+        fp = open(self.jft.file_props['file-name'], 'r')
+        con.OpenStream( self.jft.transport.sid, 
+                self.jft.session.peerjid, fp, blocksize=4096)
+
+    def __start_SOCK5_transfer(self):
+        # It tells wether we start the transfer as client or server
+        mode = None
+
+        if self.jft.isOurCandUsed():
+            mode = 'client'
+            streamhost_used = self.jft.nominated_cand['our-cand']
+        else:
+            mode = 'server'
+            streamhost_used = self.jft.nominated_cand['peer-cand']
+            
+        if streamhost_used['type'] == 'proxy':
+            self.jft.file_props['is_a_proxy'] = True
+            # This needs to be changed when requesting
+            if self.jft.weinitiate:
+                self.jft.file_props['proxy_sender'] = streamhost_used['initiator']
+                self.jft.file_props['proxy_receiver'] = streamhost_used['target']
+            else:
+                self.jft.file_props['proxy_sender'] = streamhost_used['target']
+                self.jft.file_props['proxy_receiver'] = streamhost_used['initiator']
+
+        # This needs to be changed when requesting
+        if not self.jft.weinitiate and streamhost_used['type'] == 'proxy':
+            r = gajim.socks5queue.readers
+            for reader in r:
+                if r[reader].host == streamhost_used['host'] and \
+                r[reader].connected:
+                    return
+
+        # This needs to be changed when requesting
+        if self.jft.weinitiate and streamhost_used['type'] == 'proxy':
+            s = gajim.socks5queue.senders
+            for sender in s:
+                if s[sender].host == streamhost_used['host'] and \
+                s[sender].connected:
+                    return
+
+        if streamhost_used['type'] == 'proxy': 
+            self.jft.file_props['streamhost-used'] = True
+            streamhost_used['sid'] = self.jft.file_props['sid']
+            self.jft.file_props['streamhosts'] = []
+            self.jft.file_props['streamhosts'].append(streamhost_used)
+            self.jft.file_props['proxyhosts'] = []
+            self.jft.file_props['proxyhosts'].append(streamhost_used)
+
+            # This needs to be changed when requesting
+            if self.jft.weinitiate:
+                gajim.socks5queue.idx += 1
+                idx = gajim.socks5queue.idx
+                sockobj = Socks5SenderClient(gajim.idlequeue, idx,
+                    gajim.socks5queue, _sock=None,
+                    host=str(streamhost_used['host']),
+                    port=int(streamhost_used['port']), fingerprint=None,
+                    connected=False, file_props=self.jft.file_props)
+            else:
+                sockobj = Socks5ReceiverClient(gajim.idlequeue, streamhost_used,
+                    sid=self.jft.file_props['sid'],
+                    file_props=self.jft.file_props, fingerprint=None)
+            sockobj.proxy = True
+            sockobj.streamhost = streamhost_used
+            gajim.socks5queue.add_sockobj(self.jft.session.connection.name,
+                sockobj, 'sender')
+            streamhost_used['idx'] = sockobj.queue_idx
+            # If we offered the nominated candidate used, we activate
+            # the proxy
+            if not self.jft.isOurCandUsed():
+                gajim.socks5queue.on_success[self.jft.file_props['sid']] = \
+                self.jft.transport._on_proxy_auth_ok
+            # TODO: add on failure
+        else:
+            jid = gajim.get_jid_without_resource(self.jft.session.ourjid)
+            gajim.socks5queue.send_file(self.jft.file_props,
+                self.jft.session.connection.name, mode)
+
+    def action(self, args=None):
+        if self.jft.transport.type == TransportType.IBB:
+            self.__start_IBB_transfer(self.jft.session.connection)
+
+        elif self.jft.transport.type == TransportType.SOCKS5:
+            self.__start_SOCK5_transfer()
diff --git a/src/common/jingle_session.py b/src/common/jingle_session.py
index a83cb191ab8176e9a0c942ec02e02c004f006a6a..b3faafe2d8e23495091f04e52db05cf0443bae5c 100644
--- a/src/common/jingle_session.py
+++ b/src/common/jingle_session.py
@@ -381,7 +381,6 @@ class JingleSession(object):
             self.modify_content(creator, name, transport)
             cont = self.contents[(creator, name)]
             cont.transport = transport
-            cont.state =  STATE_TRANSPORT_REPLACE
             
         stanza, jingle = self.__make_jingle('transport-replace')
         self.__append_contents(jingle)