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

« back to all changes in this revision

Viewing changes to ivle/chat.py

  • Committer: Matt Giuca
  • Date: 2010-07-28 06:09:00 UTC
  • Revision ID: matt.giuca@gmail.com-20100728060900-6a0lcuexcv1juh5r
ivle/webapp/submit/submit.html: Rewrote error message when an offering could not be found to submit to. This can have one of several causes, and the old error assumed it was because you weren't in a subject dir. Now enumerates the possible reasons. (LP: #526853)

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
try:
 
23
    import json
 
24
except ImportError:
 
25
    import simplejson as json
 
26
 
 
27
import cStringIO
 
28
import hashlib
 
29
import sys
 
30
import os
 
31
import socket
 
32
import traceback
 
33
 
 
34
SOCKETTIMEOUT = 60
 
35
BLOCKSIZE = 1024
 
36
 
 
37
class Terminate(Exception):
 
38
    """Exception thrown when server is to be shut down. It will attempt to
 
39
    send the final_response to the client and then exits"""
 
40
    def __init__(self, final_response=None):
 
41
        self.final_response = final_response
 
42
 
 
43
    def __str__(self):
 
44
        return repr(self.final_response)
 
45
 
 
46
class ProtocolError(Exception):
 
47
    """Exception thrown when client violates the the chat protocol"""
 
48
    pass
 
49
 
 
50
def start_server(port, magic, daemon_mode, handler, initializer = None):
 
51
    # Attempt to open the socket.
 
52
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
53
    s.bind(('', port))
 
54
    s.listen(1)
 
55
 
 
56
    # Excellent! It worked. Let's turn ourself into a daemon,
 
57
    # then get on with the job of being a python interpreter.
 
58
    if daemon_mode:
 
59
        if os.fork():   # launch child and...
 
60
            os._exit(0) # kill off parent
 
61
        os.setsid()
 
62
        if os.fork():   # launch child and...
 
63
            os._exit(0) # kill off parent again.
 
64
        os.umask(077)
 
65
 
 
66
        try:
 
67
            MAXFD = os.sysconf("SC_OPEN_MAX")
 
68
        except:
 
69
            MAXFD = 256
 
70
 
 
71
        # Close all file descriptors, except the socket.
 
72
        for i in xrange(MAXFD):
 
73
            if i == s.fileno():
 
74
                continue
 
75
            try:
 
76
                os.close(i)
 
77
            except OSError:
 
78
                pass
 
79
 
 
80
        si = os.open(os.devnull, os.O_RDONLY)
 
81
        os.dup2(si, sys.stdin.fileno())
 
82
 
 
83
        so = os.open(os.devnull, os.O_WRONLY)
 
84
        os.dup2(so, sys.stdout.fileno())
 
85
 
 
86
        se = os.open(os.devnull, os.O_WRONLY)
 
87
        os.dup2(se, sys.stderr.fileno())
 
88
 
 
89
    if initializer:
 
90
        initializer()
 
91
 
 
92
    while True:
 
93
        (conn, addr) = s.accept()
 
94
        conn.settimeout(SOCKETTIMEOUT)
 
95
        try:
 
96
            # Grab the input and try to decode
 
97
            inp = recv_netstring(conn)
 
98
            try:
 
99
                content = decode(inp, magic)
 
100
            except ProtocolError:
 
101
                conn.close()
 
102
                continue
 
103
 
 
104
            response = handler(content)
 
105
 
 
106
            send_netstring(conn, json.dumps(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, json.dumps(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, json.dumps(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
 
 
135
    out = encode(msg, magic)
 
136
 
 
137
    send_netstring(sok, out)
 
138
    inp = recv_netstring(sok)
 
139
 
 
140
    sok.close()
 
141
 
 
142
    if decode:
 
143
        return json.loads(inp)
 
144
    else:
 
145
        return inp
 
146
 
 
147
def encode(message, magic):
 
148
    """Converts a message into a JSON serialisation and uses a magic
 
149
    string to attach a HMAC digest.
 
150
    """
 
151
    # XXX: Any reason that we double encode?
 
152
    content = json.dumps(message)
 
153
 
 
154
    digest = hashlib.md5(content + magic).hexdigest()
 
155
    env = {'digest':digest,'content':content}
 
156
    return json.dumps(env)
 
157
 
 
158
 
 
159
def decode(message, magic):
 
160
    """Takes a message with an attached HMAC digest and validates the message.
 
161
    """
 
162
    msg = json.loads(message)
 
163
 
 
164
    # Check that the message is valid
 
165
    digest = hashlib.md5(msg['content'] + magic).hexdigest()
 
166
    if msg['digest'] != digest:
 
167
        raise ProtocolError("HMAC digest is invalid")
 
168
    content = json.loads(msg['content'])
 
169
 
 
170
    return content
 
171
 
 
172
 
 
173
def send_netstring(sok, data):
 
174
    """ Sends a netstring to a socket
 
175
    """
 
176
    netstring = "%d:%s,"%(len(data),data)
 
177
    sok.sendall(netstring)
 
178
 
 
179
 
 
180
def recv_netstring(sok):
 
181
    """ Attempts to recieve a Netstring from a socket.
 
182
    Throws a ProtocolError if the received data violates the Netstring 
 
183
    protocol.
 
184
    """
 
185
    # Decode netstring
 
186
    size_buffer = []
 
187
    c = sok.recv(1)
 
188
    while c != ':':
 
189
        # Limit the Netstring to less than 10^10 bytes (~1GB).
 
190
        if len(size_buffer) >= 10:
 
191
            raise ProtocolError(
 
192
                    "Could not read Netstring size in first 9 bytes: '%s'"%(
 
193
                    ''.join(size_buffer)))
 
194
        size_buffer.append(c)
 
195
        c = sok.recv(1)
 
196
    try:
 
197
        # Message should be length plus ',' terminator
 
198
        recv_expected = int(''.join(size_buffer)) + 1
 
199
    except ValueError, e:
 
200
        raise ProtocolError("Could not decode Netstring size as int: '%s'"%(
 
201
                ''.join(size_buffer)))
 
202
 
 
203
    # Read data
 
204
    buf = []
 
205
    recv_data = sok.recv(min(recv_expected, BLOCKSIZE))
 
206
    recv_size = len(recv_data)
 
207
    while recv_size < recv_expected:
 
208
        buf.append(recv_data)
 
209
        recv_data = sok.recv(min(recv_expected-recv_size, 1024))
 
210
        recv_size = recv_size + len(recv_data)
 
211
    assert(recv_size == recv_expected)
 
212
 
 
213
    # Did we receive the correct amount?
 
214
    if recv_data[-1] != ',':
 
215
        raise ProtocolError("Netstring did not end with ','")
 
216
    buf.append(recv_data[:-1])
 
217
 
 
218
    return ''.join(buf)