~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to ivle/chat.py

  • Committer: dcoles
  • Date: 2008-07-03 04:20:54 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:803
Setup: Modularised setup.py so it is now no longer over 1000 lines. This should 
allow us to get in there and tidy up each module much easier. Also removed 
updatejails since this functionality seems to be duplicated with remakeuser.py 
and remakealluser.py scripts.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# IVLE - Informatics Virtual Learning Environment
2
 
# Copyright (C) 2007-2008 The University of Melbourne
3
 
#
4
 
# This program is free software; you can redistribute it and/or modify
5
 
# it under the terms of the GNU General Public License as published by
6
 
# the Free Software Foundation; either version 2 of the License, or
7
 
# (at your option) any later version.
8
 
#
9
 
# This program is distributed in the hope that it will be useful,
10
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 
# GNU General Public License for more details.
13
 
#
14
 
# You should have received a copy of the GNU General Public License
15
 
# along with this program; if not, write to the Free Software
16
 
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
 
 
18
 
# Module: Chat
19
 
# Author: Thomas Conway
20
 
# Date:   5/2/2008
21
 
 
22
 
import cjson
23
 
import cStringIO
24
 
import hashlib
25
 
import sys
26
 
import os
27
 
import socket
28
 
import traceback
29
 
 
30
 
SOCKETTIMEOUT = 60
31
 
BLOCKSIZE = 1024
32
 
 
33
 
class Terminate(Exception):
34
 
    """Exception thrown when server is to be shut down. It will attempt to
35
 
    send the final_response to the client and then exits"""
36
 
    def __init__(self, final_response=None):
37
 
        self.final_response = final_response
38
 
 
39
 
    def __str__(self):
40
 
        return repr(self.final_response)
41
 
 
42
 
class ProtocolError(Exception):
43
 
    """Exception thrown when client violates the the chat protocol"""
44
 
    pass
45
 
 
46
 
def start_server(port, magic, daemon_mode, handler, initializer = None):
47
 
    # Attempt to open the socket.
48
 
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
49
 
    s.bind(('', port))
50
 
    s.listen(1)
51
 
 
52
 
    # Excellent! It worked. Let's turn ourself into a daemon,
53
 
    # then get on with the job of being a python interpreter.
54
 
    if daemon_mode:
55
 
        if os.fork():   # launch child and...
56
 
            os._exit(0) # kill off parent
57
 
        os.setsid()
58
 
        if os.fork():   # launch child and...
59
 
            os._exit(0) # kill off parent again.
60
 
        os.umask(077)
61
 
 
62
 
        try:
63
 
            MAXFD = os.sysconf("SC_OPEN_MAX")
64
 
        except:
65
 
            MAXFD = 256
66
 
 
67
 
        # Close all file descriptors, except the socket.
68
 
        for i in xrange(MAXFD):
69
 
            if i == s.fileno():
70
 
                continue
71
 
            try:
72
 
                os.close(i)
73
 
            except OSError:
74
 
                pass
75
 
 
76
 
        si = os.open(os.devnull, os.O_RDONLY)
77
 
        os.dup2(si, sys.stdin.fileno())
78
 
 
79
 
        so = os.open(os.devnull, os.O_WRONLY)
80
 
        os.dup2(so, sys.stdout.fileno())
81
 
 
82
 
        se = os.open(os.devnull, os.O_WRONLY)
83
 
        os.dup2(se, sys.stderr.fileno())
84
 
 
85
 
    if initializer:
86
 
        initializer()
87
 
 
88
 
    while True:
89
 
        (conn, addr) = s.accept()
90
 
        conn.settimeout(SOCKETTIMEOUT)
91
 
        try:
92
 
            # Grab the input
93
 
            inp = recv_netstring(conn)
94
 
            env = cjson.decode(inp)
95
 
 
96
 
            # Check that the message is 
97
 
            digest = hashlib.md5(env['content'] + magic).hexdigest()
98
 
            if env['digest'] != digest:
99
 
                conn.close()
100
 
                continue
101
 
 
102
 
            content = cjson.decode(env['content'])
103
 
 
104
 
            response = handler(content)
105
 
 
106
 
            send_netstring(conn, cjson.encode(response))
107
 
 
108
 
            conn.close()
109
 
 
110
 
        except Terminate, t:
111
 
            # Try and send final response and then terminate
112
 
            if t.final_response:
113
 
                send_netstring(conn, cjson.encode(t.final_response))
114
 
            conn.close()
115
 
            sys.exit(0)
116
 
        except Exception:
117
 
            # Make a JSON object full of exceptional goodness
118
 
            tb_dump = cStringIO.StringIO()
119
 
            e_type, e_val, e_tb = sys.exc_info()
120
 
            traceback.print_tb(e_tb, file=tb_dump)
121
 
            json_exc = {
122
 
                "type": e_type.__name__,
123
 
                "value": str(e_val),
124
 
                "traceback": tb_dump.getvalue()
125
 
            }
126
 
            send_netstring(conn, cjson.encode(json_exc))
127
 
            conn.close()
128
 
 
129
 
 
130
 
def chat(host, port, msg, magic, decode = True):
131
 
    sok = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
132
 
    sok.connect((host, port))
133
 
    sok.settimeout(SOCKETTIMEOUT)
134
 
    content = cjson.encode(msg)
135
 
    digest = hashlib.md5(content + magic).hexdigest()
136
 
    env = {'digest':digest,'content':content}
137
 
    json = cjson.encode(env)
138
 
 
139
 
    send_netstring(sok, json)
140
 
    inp = recv_netstring(sok)
141
 
 
142
 
    sok.close()
143
 
 
144
 
    if decode:
145
 
        return cjson.decode(inp)
146
 
    else:
147
 
        return inp
148
 
 
149
 
 
150
 
def send_netstring(sok, data):
151
 
    netstring = "%d:%s,"%(len(data),data)
152
 
    sok.sendall(netstring)
153
 
 
154
 
 
155
 
def recv_netstring(sok):
156
 
    # Decode netstring
157
 
    size_buffer = []
158
 
    c = sok.recv(1)
159
 
    while c != ':':
160
 
        # Limit the Netstring to less than 10^10 bytes (~1GB).
161
 
        if len(size_buffer) >= 10:
162
 
            raise ProtocolError(
163
 
                    "Could not read Netstring size in first 9 bytes: '%s'"%(
164
 
                    ''.join(size_buffer)))
165
 
        size_buffer.append(c)
166
 
        c = sok.recv(1)
167
 
    try:
168
 
        # Message should be length plus ',' terminator
169
 
        recv_expected = int(''.join(size_buffer)) + 1
170
 
    except ValueError, e:
171
 
        raise ProtocolError("Could not decode Netstring size as int: '%s'"%(
172
 
                ''.join(size_buffer)))
173
 
 
174
 
    # Read data
175
 
    buf = []
176
 
    recv_data = sok.recv(min(recv_expected, BLOCKSIZE))
177
 
    recv_size = len(recv_data)
178
 
    while recv_size < recv_expected:
179
 
        buf.append(recv_data)
180
 
        recv_data = sok.recv(min(recv_expected-recv_size, 1024))
181
 
        recv_size = recv_size + len(recv_data)
182
 
    assert(recv_size == recv_expected)
183
 
 
184
 
    # Did we receive the correct amount?
185
 
    if recv_data[-1] != ',':
186
 
        raise ProtocolError("Netstring did not end with ','")
187
 
    buf.append(recv_data[:-1])
188
 
 
189
 
    return ''.join(buf)