Issues (229)

server/lib/igeclient/IClient.py (1 issue)

Severity
1
#
2
#  Copyright 2001 - 2016 Ludek Smid [http://www.ospace.net/]
3
#
4
#  This file is part of Outer Space.
5
#
6
#  Outer Space is free software; you can redistribute it and/or modify
7
#  it under the terms of the GNU General Public License as published by
8
#  the Free Software Foundation; either version 2 of the License, or
9
#  (at your option) any later version.
10
#
11
#  Outer Space is distributed in the hope that it will be useful,
12
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
#  GNU General Public License for more details.
15
#
16
#  You should have received a copy of the GNU General Public License
17
#  along with Outer Space; if not, write to the Free Software
18
#  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19
#
20
21
import ige
22
import ige.Authentication
23
from ige.IMarshal import IMarshal, IPacket
24
from ige import ServerStatusException, log
25
import httplib, urllib
26
import exceptions
27
import time
28
from binascii import hexlify
29
import threading
30
31
from ige.Const import OID_ADMIN
32
33
MSG_CMD_BEGIN = -1000
34
MSG_CMD_END = -1001
35
36
class IClientException(Exception):
37
    pass
38
39
class IClient:
40
41
    def __init__(self, server, proxy, msgHandler, idleHandler, clientIdent, keepAliveTime = 180):
42
        self.clientIdent = clientIdent
43
        self.gameID = None
44
        self.server = server
45
        self.logged = 0
46
        self.sid = None
47
        self.httpConn = None
48
        self.keepAliveTime = keepAliveTime
49
        self.proxy = proxy
50
        self.msgHandler = msgHandler
51
        self.idleHandler = idleHandler
52
        self.lastCommand = time.time()
53
        self.hostID = 'FILLMEWITHREALVALUE'
54
        self.statsBytesIn = 0
55
        self.statsBytesOut = 0
56
        self.lastClientVersion = None
57
58
    def connect(self):
59
        # to enable sending commands
60
        self.connected = 1
61
        # create connection
62
        log.debug('Connecting to the server', self.server)
63
        # send hello message
64
        log.debug('Sending hello')
65
        try:
66
            self.sid, self.challenge = self.hello(self.clientIdent)
67
        except:
68
            log.warning('Cannot connect to the server.')
69
            self.connected = 0
70
            raise IClientException('Cannot connect to the server.')
71
        log.debug(self.sid, self.challenge)
72
73
    def login(self, gameID, login, password):
74
        self.gameID = gameID.encode("ascii")
75
        # hash password with challenge
76
        passwd = ige.Authentication.encode(password, self.challenge)
77
        #@log.debug(login, password, passwd)
78
        try:
79
            IProxy('login', None, self)(login, passwd, self.hostID)
80
        except:
81
            log.warning('login failed')
82
            return 0
83
        log.debug('login succeeded')
84
        self.logged = 1
85
        return 1
86
87
    def logout(self):
88
        self.logged = 0
89
        return IProxy('logout', None, self)()
90
91
    def shutdown(self):
92
        self.logged = 0
93
        return IProxy('shutdown', None, self)()
94
95
    def hello(self, clientID):
96
        return IProxy('hello', None, self)(clientID)
97
98
    def getVersion(self):
99
        return IProxy('getVersion', None, self)()
100
101
    def getAccountData(self):
102
        return IProxy('getAccountData', None, self)()
103
104
    def getRegisteredGames(self):
105
        return IProxy('getRegisteredGames', None, self)()
106
107
    def cleanupSessions(self):
108
        return IProxy('cleanupSessions', None, self)()
109
110
    def reloadAccounts(self):
111
        return IProxy('reloadAccounts', None, self)()
112
113
    def createAccount(self, login, password, nick, email):
114
        safePassword = ige.Authentication.encode(password, self.challenge)
115
        return IProxy('createAccount', None, self)(login, safePassword, nick, email)
116
117
    def exportAccounts(self):
118
        return IProxy('exportAccounts', None, self)()
119
120
    def changePassword(self, old, new):
121
        safeOld = ige.Authentication.encode(old, self.challenge)
122
        safeNew = ige.Authentication.encode(new, self.challenge)
123
        return IProxy('changePassword', None, self)(safeOld, safeNew)
124
125
    def getBookingAnswers(self):
126
        return IProxy('getBookingAnswers', None, self)()
127
128
    def getBookingOffers(self):
129
        return IProxy('getBookingOffers', None, self)()
130
131
    def toggleBooking(self, bookID, password):
132
        return IProxy('toggleBooking', None, self)(bookID, password)
133
134
    def createPrivateBooking(self, bookID, password):
135
        return IProxy('createPrivateBooking', None, self)(bookID, password)
136
137
    def deletePrivateBooking(self, bookID):
138
        return IProxy('deletePrivateBooking', None, self)(bookID)
139
140
    def selectPlayer(self, playerID):
141
        return IProxy('%s.selectPlayer' % self.gameID, None, self)(playerID)
142
143
    def selectAdmin(self):
144
        return IProxy('%s.selectPlayer' % self.gameID, None, self)(OID_ADMIN)
145
146
    def createNewPlayer(self, galaxyID):
147
        return IProxy('%s.createNewPlayer' % self.gameID, None, self)(galaxyID)
148
149
    def takeOverAIPlayer(self, playerID):
150
        return IProxy('%s.takeOverAIPlayer' % self.gameID, None, self)(playerID)
151
152
    def takeOverPirate(self, playerID, password):
153
        return IProxy('%s.takeOverPirate' % self.gameID, None, self)(playerID, password)
154
155
    def getActivePositions(self):
156
        return IProxy('%s.getActivePositions' % self.gameID, None, self)()
157
158
    def getStartingPositions(self):
159
        return IProxy('%s.getStartingPositions' % self.gameID, None, self)()
160
161
    def processTurn(self):
162
        return IProxy('%s.processTurn' % self.gameID, None, self)()
163
164
    def processTurns(self, turns):
165
        return IProxy('%s.processTurn' % self.gameID, None, self)(turns)
166
167
    def backup(self, basename):
168
        return IProxy('%s.backup' % self.gameID, None, self)(basename)
169
170
    def commitDatabases(self):
171
        return IProxy('%s.commitDatabases' % self.gameID, None, self)()
172
173
    def getTurnData(self):
174
        return IProxy('%s.getTurnData' % self.gameID, None, self)()
175
176
    def turnFinished(self):
177
        return IProxy('%s.turnFinished' % self.gameID, None, self)()
178
179
    def doKeepAlive(self):
180
        return ((time.time() - self.lastCommand) > self.keepAliveTime) and self.logged
181
182
    def keepAlive(self):
183
        return IProxy('ping', None, self)()
184
185
    def __nonzero__(self):
186
        return 1
187
188
    def __getattr__(self, name):
189
        if self.gameID:
190
            return IProxy('%s.execute' % self.gameID, name, self)
191
        else:
192
            return IProxy(name, None, self)
193
194
class ProtocolException(Exception):
195
196
    def __init__(self, url, errcode, errmsg, headers):
197
        self.url = url
198
        self.errcode = errcode
199
        self.errmsg = errmsg
200
        self.headers = headers
201
202
    def __repr__(self):
203
        return "<ProtocolError for %s: %s %s>" % (self.url, self.errcode, self.errmsg)
204
205
    def __str__(self):
206
        return '%s %s' % (self.errcode, self.errmsg)
207
208
class IProxy:
209
210
    def __init__(self, method, command, client):
211
        self.client = client
212
        self.method = method
213
        self.command = command
214
        self.marshal = IMarshal()
215
216
    def __call__(self, *args):
217
        if self.client.msgHandler:
218
            self.client.msgHandler(MSG_CMD_BEGIN, None)
219
        # retry 'turn in progress' and server restart situations
220
        retries = 10
221
        ok = 0
222
        while retries > 0:
223
            try:
224
                result = self.processCall(args)
225
                ok = 1
226
                break
227
            except ServerStatusException, e:
228
                log.warning("Cannot complete request - retrying...")
229
                retries -= 1
230
                time.sleep(1)
231
            # this was commented out
232
            except Exception, e:
233
                log.warning("Cannot complete request")
234
                if self.client.msgHandler:
235
                    self.client.msgHandler(MSG_CMD_END, None)
236
                raise e
237
        if self.client.msgHandler:
238
            self.client.msgHandler(MSG_CMD_END, None)
239
        if ok:
240
            return result
0 ignored issues
show
The variable result does not seem to be defined in case the while loop on line 222 is not entered. Are you sure this can never be the case?
Loading history...
241
        else:
242
            raise IClientException('Cannot send request to the server')
243
244
    def processCall(self, args):
245
        if not self.client.connected:
246
            raise IClientException('Not connected.')
247
        # record time of command
248
        self.client.lastCommand = time.time()
249
        # packet init
250
        packet = IPacket()
251
        packet.sid = self.client.sid
252
        packet.method = self.method
253
        if self.command:
254
            packet.params = [self.command]
255
            packet.params.extend(args)
256
        else:
257
            packet.params = args
258
        log.debug('calling', packet.method, packet.params)
259
        # encode
260
        # V11
261
        # data = self.marshal.encode(packet).encode('utf-8')
262
        data = self.marshal.encode(packet)
263
        self.client.statsBytesOut += len(data)
264
        #@log.debug('->', data)
265
        # make call
266
        # init connection
267
        if self.client.proxy:
268
            # use urllib
269
            if not self.client.httpConn:
270
                log.debug('Using proxy', self.client.proxy)
271
                self.client.httpConn = urllib.FancyURLopener({'http': self.client.proxy})
272
        else:
273
            if self.client.httpConn:
274
                h = self.client.httpConn
275
            else:
276
                h = httplib.HTTPConnection(self.client.server)
277
                self.client.httpConn = h
278
        try:
279
            if self.client.proxy:
280
                fh = self.client.httpConn.open('http://%s/IGERPC' % self.client.server, data)
281
                # use thread to read response and invoke idle handler
282
                # regularly
283
                reader = Reader(fh.read)
284
                reader.start()
285
                while reader.isAlive():
286
                    reader.join(0.1)
287
                    if self.client.idleHandler:
288
                        self.client.idleHandler()
289
                if reader.exception:
290
                    raise reader.exception
291
                else:
292
                    rspData = reader.result
293
                # end of thread dispatcher
294
                fh.close()
295
            else:
296
                h.putrequest('POST', '/IGERPC')
297
                # required by HTTP/1.1
298
                h.putheader('Host', self.client.server)
299
                # required by IGE-RPC
300
                h.putheader("Content-Type", "text/plain")
301
                h.putheader("Content-Length", str(len(data)))
302
                h.endheaders()
303
                h.send(data)
304
                # use thread to read response and invoke idle handler
305
                # regularly
306
                reader = Reader(h.getresponse)
307
                reader.start()
308
                while reader.isAlive():
309
                    reader.join(0.1)
310
                    if self.client.idleHandler:
311
                        self.client.idleHandler()
312
                if reader.exception:
313
                    raise reader.exception
314
                else:
315
                    response = reader.result
316
                # end of thread dispatcher
317
                if response.status != 200:
318
                    raise ProtocolException(self.client.server + '/IGERPC',
319
                        response.status, response.reason, response.msg)
320
                # use thread to read response and invoke idle handler
321
                # regularly
322
                reader = Reader(response.read)
323
                reader.start()
324
                while reader.isAlive():
325
                    reader.join(0.1)
326
                    if self.client.idleHandler:
327
                        self.client.idleHandler()
328
                if reader.exception:
329
                    raise reader.exception
330
                else:
331
                    rspData = reader.result
332
                # end of thread dispatcher
333
        except Exception, e:
334
            log.warning('Cannot send request to the server')
335
            self.client.logged = 0
336
            self.client.connected = 0
337
            raise IClientException('Cannot send request to the server')
338
        #@log.debug('<-', rspData)
339
        # V11
340
        # packet = self.marshal.decode(unicode(rspData, "utf-8"))
341
        self.client.statsBytesIn += len(rspData)
342
        packet = self.marshal.decode(rspData)
343
        if packet.exception:
344
            #@log.debug('raising exception', packet.exception)
345
            exception = eval('%s()' % packet.exception[0])
346
            exception.args = packet.exception[1]
347
            raise exception
348
        # process messages in packet.messages
349
        if self.client.msgHandler and packet.messages:
350
            for message in packet.messages:
351
                log.debug('got message', message)
352
                self.client.msgHandler(*message)
353
        elif packet.messages:
354
            log.debug('throwing away messages', packet.messages)
355
        log.debug("Stats: %d B IN / %d B OUT" % (self.client.statsBytesIn, self.client.statsBytesOut))
356
        return packet.result
357
358
class Reader(threading.Thread):
359
360
    def __init__(self, callable):
361
        threading.Thread.__init__(self)
362
        self.callable = callable
363
        self.result = None
364
        self.exception = None
365
366
    def run(self):
367
        try:
368
            self.result = self.callable()
369
        except Exception, e:
370
            self.exception = e
371