Changes in / [988d75c:ae8cc50]


Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • skype/skyped.py

    r988d75c rae8cc50  
    2626import locale
    2727import time
    28 import gobject
    2928import socket
    3029import getopt
     
    3433from traceback import print_exception
    3534import ssl
     35import select
     36import threading
    3637
    3738__version__ = "0.1.1"
     
    4243        if type != KeyboardInterrupt:
    4344                print_exception(type, value, tb)
    44         gobject.MainLoop().quit()
    45         options.conn.close()
     45        if options.conn: options.conn.close()
    4646        # shut down client if it's running
    4747        try:
     
    5353sys.excepthook = eh
    5454
    55 def input_handler(fd, io_condition):
    56         global options
     55def wait_for_lock(lock, timeout_to_print, timeout, msg):
     56        start = time.time()
     57        locked = lock.acquire(0)
     58        while not(locked):
     59                time.sleep(0.5)
     60                if timeout_to_print and (time.time() - timeout_to_print > start):
     61                        dprint("%s: Waited %f seconds" % \
     62                                        (msg, time.time() - start))
     63                        timeout_to_print = False
     64                if timeout and (time.time() - timeout > start):
     65                        dprint("%s: Waited %f seconds, giving up" % \
     66                                        (msg, time.time() - start))
     67                        return False
     68                locked = lock.acquire(0)
     69        return True
     70
     71def input_handler(fd):
     72        global options
     73        global skype
    5774        if options.buf:
    5875                for i in options.buf:
    5976                        skype.send(i.strip())
    6077                options.buf = None
     78                return True
    6179        else:
    62                 try:
    63                         input = fd.recv(1024)
    64                 except Exception, s:
    65                         dprint("Warning, receiving 1024 bytes failed (%s)." % s)
    66                         fd.close()
    67                         return False
    68                 for i in input.split("\n"):
    69                         skype.send(i.strip())
    70                 return True
     80                close_socket = False
     81                if wait_for_lock(options.lock, 3, 10, "input_handler"):
     82                        try:
     83                                        input = fd.recv(1024)
     84                                        options.lock.release()
     85                        except Exception, s:
     86                                dprint("Warning, receiving 1024 bytes failed (%s)." % s)
     87                                fd.close()
     88                                options.conn = False
     89                                options.lock.release()
     90                                return False
     91                        for i in input.split("\n"):
     92                                if i.strip() == "SET USERSTATUS OFFLINE":
     93                                        close_socket = True
     94                                skype.send(i.strip())
     95                return not(close_socket)
    7196
    7297def skype_idle_handler(skype):
     
    7499                c = skype.skype.Command("PING", Block=True)
    75100                skype.skype.SendCommand(c)
     101                dprint("... skype pinged")
    76102        except Skype4Py.SkypeAPIError, s:
    77103                dprint("Warning, pinging Skype failed (%s)." % (s))
     
    79105
    80106def send(sock, txt):
    81         from time import sleep
     107        global options
    82108        count = 1
    83109        done = False
    84         while (not done) and (count < 10):
    85                 try:
    86                         sock.send(txt)
    87                         done = True
    88                 except Exception, s:
    89                         count += 1
    90                         dprint("Warning, sending '%s' failed (%s). count=%d" % (txt, s, count))
    91                         sleep(1)
     110        while (not done) and (count < 10) and options.conn:
     111                if wait_for_lock(options.lock, 3, 10, "socket send"):
     112                        try:
     113                                if options.conn: sock.send(txt)
     114                                options.lock.release()
     115                                done = True
     116                        except Exception, s:
     117                                options.lock.release()
     118                                count += 1
     119                                dprint("Warning, sending '%s' failed (%s). count=%d" % (txt, s, count))
     120                                time.sleep(1)
    92121        if not done:
    93                 options.conn.close()
     122                if options.conn: options.conn.close()
     123                options.conn = False
     124        return done
    94125
    95126def bitlbee_idle_handler(skype):
     127        global options
     128        done = False
    96129        if options.conn:
    97130                try:
    98131                        e = "PING"
    99                         send(options.conn, "%s\n" % e)
     132                        done = send(options.conn, "%s\n" % e)
     133                        dprint("... pinged Bitlbee")
    100134                except Exception, s:
    101135                        dprint("Warning, sending '%s' failed (%s)." % (e, s))
    102                         options.conn.close()
    103         return True
    104 
    105 def server(host, port):
     136                        if options.conn: options.conn.close()
     137                        options.conn = False
     138                        done = False
     139        return done
     140
     141def server(host, port, skype):
    106142        global options
    107143        sock = socket.socket()
     
    109145        sock.bind((host, port))
    110146        sock.listen(1)
    111         gobject.io_add_watch(sock, gobject.IO_IN, listener)
    112 
    113 def listener(sock, *args):
    114         global options
     147        dprint("Waiting for connection...")
     148        listener(sock, skype)
     149
     150def listener(sock, skype):
     151        global options
     152        if not(wait_for_lock(options.lock, 3, 10, "listener")): return False
    115153        rawsock, addr = sock.accept()
    116154        options.conn = ssl.wrap_socket(rawsock,
     
    123161                        options.conn.handshake()
    124162                except Exception:
     163                        options.lock.release()
    125164                        dprint("Warning, handshake failed, closing connection.")
    126165                        return False
     
    136175                dprint("Warning, receiving 1024 bytes failed (%s)." % s)
    137176                options.conn.close()
     177                options.conn = False
     178                options.lock.release()
    138179                return False
    139180        if ret == 2:
    140181                dprint("Username and password OK.")
    141182                options.conn.send("PASSWORD OK\n")
    142                 gobject.io_add_watch(options.conn, gobject.IO_IN, input_handler)
     183                options.lock.release()
     184                serverloop(options, skype)
    143185                return True
    144186        else:
    145187                dprint("Username and/or password WRONG.")
    146188                options.conn.send("PASSWORD KO\n")
     189                options.conn.close()
     190                options.conn = False
     191                options.lock.release()
    147192                return False
    148193
     
    188233                        # everybody will be happy
    189234                        e = i.encode('UTF-8')
    190                         dprint('<< ' + e)
    191235                        if options.conn:
     236                                dprint('<< ' + e)
    192237                                try:
     238                                        # I called the send function really_send
    193239                                        send(options.conn, e + "\n")
    194240                                except Exception, s:
    195241                                        dprint("Warning, sending '%s' failed (%s)." % (e, s))
    196                                         options.conn.close()
     242                                        if options.conn: options.conn.close()
     243                                        options.conn = False
     244                        else:
     245                                dprint('---' + e)
    197246
    198247        def send(self, msg_text):
    199248                if not len(msg_text) or msg_text == "PONG":
     249                        if msg_text == "PONG": options.last_bitlbee_pong = time.time()
    200250                        return
    201251                try:
     
    252302        -v      --version       display version information""" % (self.cfgpath, self.host, self.port)
    253303                sys.exit(ret)
     304
     305def serverloop(options, skype):
     306        timeout = 1; # in seconds
     307        skype_ping_period = 5
     308        bitlbee_ping_period = 10
     309        bitlbee_pong_timeout = 30
     310        now = time.time()
     311        skype_ping_start_time = now
     312        bitlbee_ping_start_time = now
     313        options.last_bitlbee_pong = now
     314        in_error = []
     315        handler_ok = True
     316        while (len(in_error) == 0) and handler_ok and options.conn:
     317                ready_to_read, ready_to_write, in_error = \
     318                        select.select([options.conn], [], [options.conn], \
     319                                timeout)
     320                now = time.time()
     321                handler_ok = len(in_error) == 0
     322                if (len(ready_to_read) == 1) and handler_ok:
     323                        handler_ok = input_handler(ready_to_read.pop())
     324                        # don't ping bitlbee/skype if they already received data
     325                        now = time.time() # allow for the input_handler to take some time
     326                        bitlbee_ping_start_time = now
     327                        skype_ping_start_time = now
     328                        options.last_bitlbee_pong = now
     329                if (now - skype_ping_period > skype_ping_start_time) and handler_ok:
     330                        handler_ok = skype_idle_handler(skype)
     331                        skype_ping_start_time = now
     332                if now - bitlbee_ping_period > bitlbee_ping_start_time:
     333                        handler_ok = bitlbee_idle_handler(skype)
     334                        bitlbee_ping_start_time = now
     335                        if options.last_bitlbee_pong:
     336                                if (now - options.last_bitlbee_pong) > bitlbee_pong_timeout:
     337                                        dprint("Bitlbee pong timeout")
     338                                        # TODO is following line necessary? Should there be a options.conn.unwrap() somewhere?
     339                                        # options.conn.shutdown()
     340                                        if options.conn: options.conn.close()
     341                                        options.conn = False
     342                                else:
     343                                        dprint("%f seconds since last PONG" % (now - options.last_bitlbee_pong))
     344                        else:
     345                                options.last_bitlbee_pong = now
     346        dprint("Serverloop done")
    254347
    255348if __name__=='__main__':
     
    317410        else:
    318411                dprint('skyped is started on port %s' % options.port)
    319         server(options.host, options.port)
    320412        try:
    321413                skype = SkypeApi()
    322414        except Skype4Py.SkypeAPIError, s:
    323415                sys.exit("%s. Are you sure you have started Skype?" % s)
    324         gobject.timeout_add(2000, skype_idle_handler, skype)
    325         gobject.timeout_add(60000, bitlbee_idle_handler, skype)
    326         gobject.MainLoop().run()
     416        while 1:
     417                options.conn = False
     418                options.lock = threading.Lock()
     419                server(options.host, options.port, skype)
Note: See TracChangeset for help on using the changeset viewer.