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

« back to all changes in this revision

Viewing changes to ivle/chat.py

  • Committer: mattgiuca
  • Date: 2007-12-19 06:01:34 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:85
common/studpath.py: Added url_to_jailpaths.
apps/server: Rewrote parts to now get the UserID and paths relative to jail,
    and pass those off to the interpreter. The interpreter is no longer
    python, but one relative to IVLE (our trampoline). (Currently a test one).
Added bin/tramptest trampoline, called by server temporarily. Just prints out
    its arguments.

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)