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

1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
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
1197 by Matt Giuca
ivle.chat, ivle.database, ivle.makeuser: Replaced use of md5 library with
24
import hashlib
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
25
import sys
26
import os
27
import socket
28
import traceback
29
1621 by David Coles
Modify chat protocol to use Netstrings to track message boundaries rather than
30
SOCKETTIMEOUT = 60
31
BLOCKSIZE = 1024
32
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
33
class Terminate(Exception):
1621 by David Coles
Modify chat protocol to use Netstrings to track message boundaries rather than
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"""
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
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
1630 by David Coles
ivle.console should capture ivle.chat.ProtocolErrors as a ConsoleError
42
class ProtocolError(Exception):
43
    """Exception thrown when client violates the the chat protocol"""
44
    pass
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
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
1099.1.176 by William Grant
ivle.chat now closes all file descriptors when daemonising.
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
1099.1.178 by William Grant
ivle.chat now opens /dev/null as std{in,out,err} when daemonising.
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
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
85
    if initializer:
86
        initializer()
87
88
    while True:
89
        (conn, addr) = s.accept()
1621 by David Coles
Modify chat protocol to use Netstrings to track message boundaries rather than
90
        conn.settimeout(SOCKETTIMEOUT)
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
91
        try:
92
            # Grab the input
1621 by David Coles
Modify chat protocol to use Netstrings to track message boundaries rather than
93
            inp = recv_netstring(conn)
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
94
            env = cjson.decode(inp)
1621 by David Coles
Modify chat protocol to use Netstrings to track message boundaries rather than
95
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
96
            # Check that the message is 
1197 by Matt Giuca
ivle.chat, ivle.database, ivle.makeuser: Replaced use of md5 library with
97
            digest = hashlib.md5(env['content'] + magic).hexdigest()
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
98
            if env['digest'] != digest:
99
                conn.close()
100
                continue
101
102
            content = cjson.decode(env['content'])
103
104
            response = handler(content)
105
1621 by David Coles
Modify chat protocol to use Netstrings to track message boundaries rather than
106
            send_netstring(conn, cjson.encode(response))
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
107
108
            conn.close()
109
110
        except Terminate, t:
111
            # Try and send final response and then terminate
112
            if t.final_response:
1621 by David Coles
Modify chat protocol to use Netstrings to track message boundaries rather than
113
                send_netstring(conn, cjson.encode(t.final_response))
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
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
            }
1621 by David Coles
Modify chat protocol to use Netstrings to track message boundaries rather than
126
            send_netstring(conn, cjson.encode(json_exc))
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
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))
1621 by David Coles
Modify chat protocol to use Netstrings to track message boundaries rather than
133
    sok.settimeout(SOCKETTIMEOUT)
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
134
    content = cjson.encode(msg)
1197 by Matt Giuca
ivle.chat, ivle.database, ivle.makeuser: Replaced use of md5 library with
135
    digest = hashlib.md5(content + magic).hexdigest()
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
136
    env = {'digest':digest,'content':content}
1621 by David Coles
Modify chat protocol to use Netstrings to track message boundaries rather than
137
    json = cjson.encode(env)
138
139
    send_netstring(sok, json)
140
    inp = recv_netstring(sok)
141
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
142
    sok.close()
143
144
    if decode:
145
        return cjson.decode(inp)
146
    else:
147
        return inp
148
1621 by David Coles
Modify chat protocol to use Netstrings to track message boundaries rather than
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:
1630 by David Coles
ivle.console should capture ivle.chat.ProtocolErrors as a ConsoleError
162
            raise ProtocolError(
163
                    "Could not read Netstring size in first 9 bytes: '%s'"%(
164
                    ''.join(size_buffer)))
1621 by David Coles
Modify chat protocol to use Netstrings to track message boundaries rather than
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:
1630 by David Coles
ivle.console should capture ivle.chat.ProtocolErrors as a ConsoleError
171
        raise ProtocolError("Could not decode Netstring size as int: '%s'"%(
172
                ''.join(size_buffer)))
1621 by David Coles
Modify chat protocol to use Netstrings to track message boundaries rather than
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] != ',':
1630 by David Coles
ivle.console should capture ivle.chat.ProtocolErrors as a ConsoleError
186
        raise ProtocolError("Netstring did not end with ','")
1621 by David Coles
Modify chat protocol to use Netstrings to track message boundaries rather than
187
    buf.append(recv_data[:-1])
188
189
    return ''.join(buf)