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

« back to all changes in this revision

Viewing changes to ivle/chat.py

  • Committer: William Grant
  • Date: 2010-02-23 06:11:58 UTC
  • Revision ID: grantw@unimelb.edu.au-20100223061158-05cqg88387ymjtlj
Redo console CSS, and replace the examples on the help page with something more like the modern console that also doesn't break the real console with ID conflicts.

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)