client_base._pike
Go to the documentation of this file.
1 /* Copyright (C) 2000-2004 Thomas Bopp, Thorsten Hampel, Ludger Merkens
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  *
17  * $Id: client_base.pike,v 1.2 2009/08/07 15:22:37 nicke Exp $
18  */
19 inherit Stdio.File;
20 inherit "/net/coal/binary";
21 inherit "serialize";
22 #include <coal.h>
23 #include <exception.h>
24 #include <client.h>
25 #include <macros.h>
26 class client_base : public binary,serialize{
27 public:
28 
29 
30 
31 
32 
33 #define CLIENT_FEATURES ((1<<31)-1)
34 
35 private string sLastPacket; // last package while communicating
36 private int iOID; // the object id of the current object
37 private int iTID; // the current transaction id
38 private int iWaitTID;
39  mapping mVariables; // session variables
40  array aEvents;
41 private mapping mVariableReq; // requests for setting variables
42 private mapping mVariableReqInv; // inverse requests
43 private mapping mObjects; // objects
44 private mixed miResult;
45 private string sLoginName;
46  int __connected;
47 private int doThrow;
48 
49 object decryptRSA;
50 object encryptRSA;
51 string decryptBuffer = "";
52 
53 #define ERR_CONTINUE 0
54 #define ERR_FATAL 1
55 #define ERR_THROW 2
56 
57 // mutex stuff for threads
58 private Thread.Mutex command_mutex = Thread.Mutex();
59 private object read_lock, write_lock;
60 private Thread.Condition read_cond = Thread.Condition();
61 private Thread.Condition write_cond = Thread.Condition();
62 
63 int set_object(object|int id);
64 mixed send_command(int cmd, array args, int|void do_wait);
65 
66 
67 class SteamObj
68 {
69 public:
70  private int oID;
71  private string identifier = 0;
72  private int cl = 0;
73 
74  int get_object_id() {
75  return oID;
76  }
77  int get_object_class() {
78  if ( cl == 0 ) {
79  int wid = iWaitTID;
80  int id = set_object(oID);
81  mixed res = send_command(COAL_COMMAND, ({ "get_object_class" }));
82  if ( intp(res) )
83  cl = res;
84  set_object(id);
85  iWaitTID = wid;
86  }
87  return cl;
88  }
89  string get_identifier() {
90  if ( !stringp(identifier) ) {
91  int wid = iWaitTID;
92  int id = set_object(oID);
93  identifier = send_command(COAL_COMMAND, ({ "get_identifier" }));
94  set_object(id);
95  iWaitTID = wid;
96  }
97  return identifier;
98  }
99  void create(int id) {
100  oID = id;
101  }
102 }
103 
104 /**
105  *
106  *
107  * @param
108  * @return
109  * @see
110  *
111  */
112 protected:
113  object find_obj(int id)
114 {
115  if ( !mObjects[id] ) {
116  mObjects[id] = SteamObj(id);
117  }
118  return mObjects[id];
119 }
120 
121 public:
122 
123 /**
124  *
125  *
126  * @param
127  * @return
128  * @see
129  */
130 protected:
131  string translate_exception(int exc)
132 {
133  string ex = "";
134  if ( exc & E_ERROR )
135  ex += "error,";
136  if ( exc & E_LOCAL )
137  ex += "local,";
138  if ( exc & E_MEMORY )
139  ex += "memory fault,";
140  if ( exc & E_EVENT )
141  ex += "event,";
142  if ( exc & E_ACCESS )
143  ex += "access failure,";
144  if ( exc & E_PASSWORD )
145  ex += "wrong password,";
146  if ( exc & E_NOTEXIST )
147  ex += "non existing,";
148  if ( exc & E_FUNCTION )
149  ex += "function,";
150  if ( exc & E_FORMAT )
151  ex += "invalid format,";
152  if ( exc & E_OBJECT )
153  ex += "object,";
154  if ( exc & E_TYPE )
155  ex += "wrong type,";
156  if ( exc & E_MOVE )
157  ex += "move error,";
158  if ( exc & E_LOOP )
159  ex += "command will cause endless loop,";
160  if ( exc & E_LOCK )
161  ex += "object locked,";
162 
163  return ex;
164 }
165 
166 public:
167 
168 /**
169  *
170  *
171  * @param
172  * @return
173  * @see
174  */
175 protected:
176  string
177 get_login()
178 {
179  return sLoginName;
180 }
181 
182 public:
183 
184 /**
185  *
186  *
187  * @param
188  * @return
189  * @see
190  */
191 mixed
192 get_variable(int|string key)
193 {
194  return mVariables[key];
195 }
196 
197 /**
198  *
199  *
200  * @param
201  * @return
202  * @see
203  */
204 protected:
205  mixed
206 set_variable(int|string key, mixed val)
207 {
208  mVariables[key] = val;
209 }
210 
211 public:
212 
213 /**
214  *
215  *
216  * @param
217  * @return
218  * @see
219  */
220 int connect_server(string server, int port)
221 {
222  iTID = 1;
223  iOID = 0;
224  sLastPacket = "";
225  mVariableReq = ([ ]);
226  mVariableReqInv = ([ ]);
227  mVariables = ([ ]);
228  mObjects = ([ ]);
229  aEvents = ({ });
230  sLoginName = "not logged in";
231 
232  open_socket();
233  set_blocking();
234  if ( connect(server, port) ) {
235  LOG("Connected to " + server + ":"+port +"\n");
236  __connected = 1;
237  set_buffer(65536, "r");
238  set_buffer(65536, "w");
239  set_blocking();
240  thread_create(read_thread);
241  return 1;
242  }
243  return 0;
244 }
245 
246 /**
247  *
248  *
249  * @param
250  * @return
251  * @see
252  */
253 int receive_message(int id, string str)
254 {
255  int slen, tid, cmd, lid, len, n;
256  mixed args;
257 
258  if ( !stringp(str) )
259  return -1;
260  slen = strlen(str);
261  if ( slen == 0 )
262  return -1;
263  for ( n = 0; n < slen-10; n++ )
264  if ( str[n] == COMMAND_BEGIN_MASK )
265  break;
266  if ( n >= slen-18 )
267  return -1;
268  str = str[n..];
269 
270  len = (int)((str[1]<<24)+(str[2]<<16)+(str[3]<<8) + str[4]);
271 
272  if ( len > slen )
273  return -1;
274 
275  tid = (int)((str[5] << 24) + (str[6]<<16) + (str[7]<<8) + str[8]);
276  cmd = (int)str[9];
277  lid = (int)((str[10] << 24) + (str[11]<<16) + (str[12]<<8) + str[13]);
278 
279  wstr = str;
280  args = receive_args(18);
281  if ( arrayp(args) )
282  args = args[0];
283  wstr = "";
284 
285  miResult = args;
286  iOID = lid;
287 
288  doThrow = 0;
289  if ( cmd == COAL_ERROR ) {
290  sLastPacket = "";
291  int errRes = handle_error(miResult);
292  switch(errRes) {
293  case ERR_FATAL:
294  exit(1);
295  break;
296  case ERR_THROW:
297  doThrow = 1;
298  break;
299  case ERR_CONTINUE:
300  break;
301  }
302  }
303  else if ( cmd == COAL_EVENT ) {
304  miResult += ({ lid });
305  handle_event(miResult);
306  }
307  else {
308  //LOG(sprintf("%d:%O\n", tid,miResult));
309  }
310 
311  if ( mVariableReq[tid] != 0 ) {
312  LOG("Setting variable "+ mVariableReq[tid] + "\n");
313  mVariables[mVariableReq[tid]] = args[0]; // ?
314  mVariableReq[tid] = 0;
315  }
316  if ( mVariableReqInv[tid] != 0 ) {
317  LOG("Setting variable "+ args[0] + ":"+
318  sprintf("%O", mVariableReqInv[tid])+"\n");
319  mVariables[args[0]] = mVariableReqInv[tid];
320  mVariableReqInv[tid] = 0;
321  }
322  if ( slen > len )
323  sLastPacket = str[len..];
324  else
325  sLastPacket = "";
326  return tid;
327 }
328 
329 /**
330  *
331  *
332  * @param
333  * @return
334  * @see
335  */
336 int handle_error(mixed err)
337 {
338  return ERR_THROW;
339 }
340 
341 /**
342  *
343  *
344  * @param
345  * @return
346  * @see
347  */
348 int handle_event(mixed event)
349 {
350  aEvents += ({ event });
351 }
352 
353 /**
354  *
355  *
356  * @param
357  * @return
358  * @see
359  */
360 void read_thread()
361 {
362  int rcv;
363  string resp;
364  mixed err;
365 
366  read_lock = command_mutex->lock();
367  read_cond->wait(read_lock);
368  sLastPacket = "";
369  while( __connected )
370  {
371  rcv = 0;
372  while ( rcv != iWaitTID && __connected ) {
373  resp = ::read(8192,1);
374  if ( !stringp(resp) ) {
375  __connected = 0;
376  return;
377  }
378  LOG("*");
379  if ( objectp(decryptRSA) ) {
380  resp = decryptBuffer + resp;
381  decryptBuffer = "";
382  int i = 0;
383  int l = strlen(resp);
384  string decryptBlock = "";
385  while ( i+64 <= l ) {
386  decryptBlock += decryptRSA->decrypt(resp[i..i+63]);
387  i+=64;
388  }
389  if ( (l%64) != 0 )
390  decryptBuffer = resp[(l-(l%64))..];
391  resp = decryptBlock;
392  }
393  sLastPacket += resp;
394  rcv = 0;
395  while ( rcv >= 0 && err == 0 && rcv != iWaitTID ) {
396  rcv = receive_message(0, sLastPacket);
397  }
398  }
399  write_cond->signal();
400  read_cond->wait(read_lock);
401  }
402 }
403 
404 /**
405  *
406  *
407  * @param
408  * @return
409  * @see
410  */
411 mixed send_command(int cmd, array args, int|void no_wait)
412 {
413  if ( !no_wait ) iWaitTID = iTID;
414  aEvents = ({ });
415 
416  string msg = coal_compose(iTID++, cmd, iOID, 0, args);
417  string nmsg = copy_value(msg);
418 
419  if ( objectp(encryptRSA) ) {
420  nmsg = "";
421  int l = strlen(msg);
422  int i = 0;
423  while ( i < l ) {
424  if ( i+32 > l )
425  nmsg += encryptRSA->encrypt(msg[i..]);
426  else
427  nmsg += encryptRSA->encrypt(msg[i..i+31]);
428  i+=32;
429  }
430  }
431 
432  write(nmsg);
433  if ( !no_wait ) {
434  read_cond->signal();
435  write_cond->wait(write_lock);
436  if ( doThrow ) {
437  throw(({"sTeam Server error:\n"+
438  translate_exception(miResult[0])+"\n"+
439  serialize(miResult)+"\n", backtrace() }));
440  }
441  return miResult;
442  }
443  return 0;
444 }
445 
446 /**
447  *
448  *
449  * @param
450  * @return
451  * @see
452  */
453 mixed send_cmd(object|int obj, string func, mixed|void args, void|int no_wait)
454 {
455  int oid = set_object(obj);
456  if ( zero_type(args) )
457  args = ({ });
458  else if ( !arrayp(args) )
459  args = ({ args });
460 
461  mixed res = send_command(COAL_COMMAND, ({ func, args }), no_wait);
462  set_object(oid);
463  return res;
464 }
465 
466 /**
467  *
468  *
469  * @param
470  * @return
471  * @see
472  */
473 mixed login(string name, string pw, int features, string|void cname)
474 {
475  if ( objectp(write_lock) )
476  destruct(write_lock);
477  write_lock = command_mutex->lock();
478 
479  if ( !stringp(cname) )
480  cname = "steam-pike";
481 
482  mixed loginData;
483  if ( features == 0 )
484  loginData = send_command(COAL_LOGIN,({name, pw, cname, features }));
485  else
486  loginData = send_command(COAL_LOGIN,({name, pw, cname,CLIENT_FEATURES}));
487  if ( arrayp(loginData) && sizeof(loginData) >= 9 ) {
488  mVariables["user"] = iOID;
489  sLoginName = loginData[0];
490  foreach ( indices(loginData[8]), string key ) {
491  mVariables[key] = loginData[8][key];
492  }
493  mVariables["rootroom"] = loginData[6];
494  sLastPacket = "";
495  foreach ( values(loginData[9]), object cl ) {
496  set_object(cl->get_object_id());
497  mVariables[send_command(COAL_COMMAND, ({"get_identifier",({ })}))]
498  = cl;
499  }
500  return name;
501  }
502  return 0;
503 }
504 
505 mixed server_hello(string cert)
506 {
507  return send_command(COAL_SERVERHELLO, ({ cert }));
508 }
509 
510 
511 mixed logout()
512 {
513  if ( objectp(write_lock) )
514  destruct(write_lock);
515  write_lock = command_mutex->lock();
516  __connected = 0;
517  write(coal_compose(iTID++, COAL_LOGOUT,0, 0, ({ })));
518 }
519 
520 /**
521  *
522  *
523  * @param
524  * @return
525  * @see
526  */
527 protected:
528  int set_object(int|object id)
529 {
530  int old_id = iOID;
531  if ( objectp(id) )
532  iOID = id->get_object_id();
533  else
534  iOID = id;
535  return old_id;
536 }
537 
538 public:
539 
540 protected:
541  int get_object()
542 {
543  return iOID;
544 }
545 
546 public:
547 
548 /**
549  *
550  *
551  * @param
552  * @return
553  * @see
554  */
555 protected:
556  array
557 get_events()
558 {
559  return aEvents;
560 }
561 
562 public:
563 
564 /**
565  *
566  *
567  * @param
568  * @return
569  * @see
570  */
571 int get_commands()
572 {
573  return iTID;
574 }
575 
576 void create(object|void id)
577 {
578  // do nothing...
579 }
580 
581 
582 };