Source

FlSock / FlSock.py

# -*- coding: utf-8 -*-

import asynchat, asyncore, socket, optparse


parser = optparse.OptionParser()
parser.add_option('-i', dest='host', default='',
                  help='address to listen, default all available')
parser.add_option('-p', dest='port', default=8881,
                  help='port, default 8881', type='int')
parser.add_option('-c', dest='channels', default=100,
                  help='max channels, default 100', type='int')
parser.add_option('-m', dest='max_length', default=1024,
                  help='max message length, default 1024', type='int')
parser.add_option('-d', dest='domains', default='*',
                  help='allowed domains comma separated, default *')


class FlSockChannel(asynchat.async_chat):
    
    ac_in_buffer_size = 1024
    ac_out_buffer_size = 1024
    
    def __init__(self, server, sock, opts):
        asynchat.async_chat.__init__(self, sock)
        self.data = ''
        self.state = 'mode'
        self.opts = opts
        self.server = server
        self.room = None
        self.set_terminator(1)
    
    def collect_incoming_data(self, data):
        self.data = self.data + data
        if len(self.data) > self.opts.max_length:
            self.handle_close()
    
    def found_terminator(self):
        if self.state == 'mode':
            if self.data == '<':
                self.push('<?xml version="1.0"?>')
                self.push('<!DOCTYPE cross-domain-policy')
                self.push('SYSTEM "http://www.macromedia.com/xml'\
                          '/dtds/cross-domain-policy.dtd">')
                self.push('<cross-domain-policy>')
                for domain in self.opts.domains.split(','):
                    self.push('<allow-access-from domain="%s"'\
                              ' to-ports="%i" />' % (domain, self.opts.port))
                self.push('</cross-domain-policy>')
                self.close_when_done()
            elif self.data == 'r':
                self.state = 'room'
                self.set_terminator(chr(1))
            elif self.data == 'm':
                self.state = 'message'
                self.set_terminator(chr(1))
        elif self.state == 'message':
            self.state = 'mode'
            if self.room:
                for channel in self.server.rooms[self.room]:
                    if channel is not self:
                        channel.push(self.data + chr(1))
            self.data = ''
            self.set_terminator(1)
        elif self.state == 'room':
            self.state = 'mode'
            self.leave_room()
            self.room = self.data
            self.join_room()
            self.set_terminator(1)
        self.data = ''

    def join_room(self):
        if self.room not in self.server.rooms:
            self.server.rooms[self.room] = []
        self.server.rooms[self.room].append(self)
    
    def leave_room(self):
        if self.room:
            self.server.rooms[self.room].remove(self)
            if not self.server.rooms[self.room]:
                del self.server.rooms[self.room]
    
    def handle_close(self):
        self.leave_room()
        self.close()


class FlSockDispatcher(asyncore.dispatcher):
    
    def __init__(self, opts):
        asyncore.dispatcher.__init__(self)
        self.opts = opts
        self.rooms = {}
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.set_reuse_addr()
        self.bind((opts.host, opts.port))
        self.listen(opts.channels)
    
    def handle_accept(self):
        conn, addr = self.accept()
        FlSockChannel(self, conn, self.opts)


if __name__ == '__main__':
    opts, args = parser.parse_args()
    s = FlSockDispatcher(opts)
    asyncore.loop()