client_base._pike
Go to the documentation of this file.
1 /* Copyright (C) 2000-2005 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 2008/07/17 16:45:00 astra Exp $
18  */
19 inherit "kernel/socket";
20 inherit "net/coal/binary";
21 #include <coal.h>
22 #include <classes.h>
23 #include <macros.h>
24 #include <client.h>
25 class client_base : public socket,binary{
26 public:
27 
28 
29 
30 
31 
32 #undef CLIENT_DEBUG
33 
34 #ifdef CLIENT_DEBUG
35 #define DEBUG_CLIENT(s, args...) werror(s+"\n", args)
36 #else
37 #define DEBUG_CLIENT(s, args...)
38 #endif
39 
40 private mapping mObjects; // objects
41 private string sLastPacket; // last package while communicating
42 private int iOID; // the object id of the current object
43 private int iTID; // the current transaction id
44 private int iWaitTID;
45  mapping mVariables; // session variables
46  array aEvents;
47  int __connected;
48  int __downloadBytes;
49  int __last_response;
50  function downloadStore;
51  mapping mEvents;
52 
53 private mixed miResult;
54 private int miCommand;
55 
56  Thread.Mutex cmd_mutex = Thread.Mutex();
57  Thread.Condition cmd_cond = Thread.Condition();
58  Thread.Queue resultQueue = Thread.Queue();
59  Thread.Queue cmdQueue = Thread.Queue();
60  object cmd_lock;
61 
62 string connected_server;
63 int connected_port;
64 
65 
66 class SteamObj
67 {
68 public:
69  private int oID;
70  private string identifier = 0;
71  private int cl = 0;
72  private int(0..1) nowait;
73  private mapping(string:function) functions=([]);
74 
75  int get_object_id() {
76  return oID;
77  }
78 
79  int get_object_class() {
80  if ( cl == 0 ) {
81  int wid = iWaitTID;
82  int id = set_object(oID);
83  mixed res = send_command(COAL_COMMAND, ({ "get_object_class" }));
84  if ( intp(res) )
85  cl = res;
86  set_object(id);
87  iWaitTID = wid;
88  }
89  return cl;
90  }
91 
92  object get_environment() {
93  return send_command(COAL_COMMAND, ({ "get_environment" }));
94  }
95 
96  string get_identifier() {
97  if ( !stringp(identifier) ) {
98  int wid = iWaitTID;
99  int id = set_object(oID);
100  identifier = send_command(COAL_COMMAND, ({ "get_identifier" }));
101  set_object(id);
102  iWaitTID = wid;
103  }
104  return identifier;
105  }
106 
107  void create(int id) {
108  oID = id;
109  }
110 
111  int status() {
112  return 1; // PSTAT_SAVE_OK
113  }
114 
115  int no_wait(void|int(0..1) _nowait)
116  {
117  if(!zero_type(_nowait) && nowait == !_nowait)
118  {
119  nowait=!!_nowait;
120  return !nowait;
121  }
122  else
123  return nowait;
124  }
125 
126  string function_name(function fun)
127  {
128  return search(functions, fun);
129  }
130 
131  string _sprintf()
132  {
133  return "OBJ#"+oID;
134  string describe="";
135  catch{ describe=`->("describe")(); };
136  return sprintf("%s:%d/%s", connected_server, connected_port, describe);
137  }
138 
139  function `->(string fun)
140  {
141  if(::`->(fun))
142  return ::`->(fun);
143  else
144  {
145  if(fun == "exec_code")
146  return 0;
147  else if (fun=="serialize_coal")
148  return 0;
149  if(!functions->fun)
150  functions[fun]=lambda(mixed|void ... args) {
151  return send_cmd(oID, fun, args, nowait);
152  };
153  return functions[fun];
154  }
155  }
156 
157  function find_function(string fun)
158  {
159  if(!functions->fun)
160  functions[fun]=lambda(mixed|void ... args) {
161  return send_cmd(oID, fun, args, nowait);
162  };
163  return functions[fun];
164  }
165 };
166 
167 
168 /**
169  *
170  *
171  * @param
172  * @return
173  * @see
174  */
175 int set_object(int|object|string id)
176 {
177  int oldID = iOID;
178 
179  if ( stringp(id) ) {
180  if ( objectp(mVariables[id]) )
181  iOID = mVariables[id]->get_object_id();
182  else
183  iOID = mVariables[id];
184  }
185  else if ( objectp(id) )
186  iOID = id->get_object_id();
187  else
188  iOID = id;
189  return oldID;
190 }
191 
192 /**
193  *
194  *
195  * @param
196  * @return
197  * @see
198  */
199 protected:
200  object find_obj(int|string id)
201 {
202  int oid;
203  if ( stringp(id) ) {
204  object fp = send_cmd( 0, "get_module", "filepath:tree" );
205  object obj = send_cmd( fp, "path_to_object", id );
206  if ( !objectp(obj) ) return 0;
207  oid = obj->get_object_id();
208  }
209  else oid = id;
210 
211  if ( !mObjects[oid] ) {
212  mObjects[oid] = SteamObj(oid);
213  //werror("Created:"+master()->describe_object(mObjects[id])+"\n");
214  }
215  return mObjects[oid];
216 }
217 
218 public:
219 
220 object find_object(int|string id) { return find_obj(id); }
221 
222 mixed get_variable(string key)
223 {
224  return mVariables[key];
225 }
226 
227 /**
228  *
229  *
230  * @param
231  * @return
232  * @see
233  */
234 int connect_server(string server, int port)
235 {
236  iTID = 1;
237  iOID = 0;
238 
239  sLastPacket = "";
240  __downloadBytes = 0;
241  mVariables = ([ ]);
242  mObjects = ([ ]);
243  aEvents = ({ });
244  mEvents = ([ ]);
245 
246  open_socket();
247  set_blocking();
248  if ( connect(server, port) ) {
249  MESSAGE("Connected to " + server + ":"+port +"\n");
250  connected_server=server;
251  connected_port=port;
252  __last_response = time(); // timestamp of last response
253  __connected = 1;
254  set_buffer(65536, "r");
255  set_buffer(65536, "w");
256  set_blocking();
257  thread_create(read_thread);
258  thread_create(handle_commands);
259  return 1;
260  }
261  return 0;
262 }
263 
264 void create()
265 {
266 }
267 
268 protected:
269  int write(string str)
270 {
271  __last_response = time();
272  return ::write(str);
273 }
274 
275 public:
276 
277 protected:
278  void handle_command(string func, mixed args) { }
279 
280 public:
281 
282 void handle_commands()
283 {
284  mixed res;
285  while ( res = cmdQueue->read() ) {
286  if ( arrayp(res) ) {
287  if ( arrayp(res[1]) ) {
288  mixed err = catch {
289  handle_command(res[1][0], res[1][1]);
290  };
291  if ( err != 0 )
292  werror("Fatal error while calling command: %O\n%O", err[0], err[1]);
293  }
294  }
295  }
296 }
297 
298 
299 void read_callback(object id, string data)
300 {
301  __last_response = time();
302 
303  if ( functionp(downloadStore) ) {
304  mixed err = catch {
305  downloadStore(data);
306  };
307  __downloadBytes -= strlen(data);
308  if ( __downloadBytes <= 0 ) {
309  downloadStore(0);
310  downloadStore = 0; // download finished
311  }
312  return;
313  }
314  sLastPacket += data;
315  if ( __downloadBytes > 0 ) {
316  if ( __downloadBytes <= strlen(sLastPacket) )
317  resultQueue->write(sLastPacket);
318  return;
319  }
320  mixed res;
321  res = receive_binary(sLastPacket);
322  while ( arrayp(res) ) {
323  int tid = res[0][0];
324  int cmd = res[0][1];
325 
326  if ( cmd == COAL_EVENT ) {
327  DEBUG_CLIENT("Event %O", res[1]);
328  }
329  DEBUG_CLIENT("RCVD Package(%d): Waiting for %d\n", tid, iWaitTID);
330  sLastPacket = res[2];
331  if ( tid == iWaitTID ) {
332  miResult = res[1];
333  miCommand = res[0][1];
334  resultQueue->write(miResult);
335  }
336  else if ( cmd == COAL_COMMAND ) {
337  cmdQueue->write(res);
338  }
339  res = receive_binary(sLastPacket);
340  }
341 }
342 
343 string download(int bytes, void|function store)
344 {
345  // actually the last command should have been the upload response,
346  // so there shouldnt be anything on the line except events
347  // which should have been already processed
348  // everything else should be download data
349  string data;
350  __downloadBytes = bytes;
351 
352  if ( functionp(store) ) {
353  data = copy_value(sLastPacket[..bytes]);
354  __downloadBytes -= strlen(data);
355  if ( strlen(data) > 0 )
356  store(data);
357  if ( __downloadBytes <= 0 ) {
358  store(0);
359  return "";
360  }
361  downloadStore = store;
362  return "";
363  }
364  downloadStore = 0;
365 
366  if ( strlen(sLastPacket) >= bytes ) {
367  data = copy_value(sLastPacket[..bytes]);
368  if ( bytes > strlen(sLastPacket) )
369  sLastPacket = sLastPacket[bytes+1..];
370  else
371  sLastPacket = "";
372  __downloadBytes = 0;
373  return data;
374  }
375 
376  miResult = resultQueue->read();
377  data = copy_value(sLastPacket[..bytes]);
378  if ( strlen(sLastPacket) > bytes )
379  sLastPacket = sLastPacket[bytes+1..];
380  else
381  sLastPacket = "";
382  __downloadBytes = 0;
383  return data;
384 }
385 
386 /**
387  *
388  *
389  * @param
390  * @return
391  * @see
392  */
393 void handle_error(mixed err)
394 {
395  throw(err);
396 }
397 
398 /**
399  *
400  *
401  * @param
402  * @return
403  * @see
404  */
405 mixed send_command(int cmd, array args, int|void no_wait)
406 {
407  if ( !no_wait ) iWaitTID = iTID;
408  aEvents = ({ });
409 
410 
411  string msg = coal_compose(iTID++, cmd, iOID, 0, args);
412  string nmsg = copy_value(msg);
413 
414  send_message(nmsg);
415  if ( no_wait ) return 0;
416 
417  mixed result = resultQueue->read();
418  if ( miCommand == COAL_ERROR ) {
419  handle_error(result);
420  }
421  return result;
422 }
423 
424 void subscribe_event(object obj, int eid, function callback)
425 {
426  int oid = set_object(obj);
427  send_command(COAL_SUBSCRIBE, ({ eid }) );
428  mEvents[eid] = callback;
429  set_object(oid);
430 }
431 
432 mixed send_cmd(object|int obj, string func, mixed|void args, void|int no_wait)
433 {
434  int oid = set_object(obj);
435  if ( zero_type(args) )
436  args = ({ });
437  else if ( !arrayp(args) )
438  args = ({ args });
439  mixed res = send_command(COAL_COMMAND, ({ func, args }), no_wait);
440  set_object(oid);
441  return res;
442 }
443 
444 mixed
445 login(string name, string pw, int features, string|void cname, int|void novars)
446 {
447  if ( !stringp(cname) )
448  cname = "steam-pike";
449 
450  mixed loginData;
451  if ( features != 0 )
452  loginData =send_command(COAL_LOGIN, ({ name, pw, cname, features, __id }));
453  else
454  loginData =
455  send_command(COAL_LOGIN,({ name, pw, cname,CLIENT_FEATURES_ALL, __id}));
456 
457  if ( arrayp(loginData) && sizeof(loginData) >= 9 ) {
458  mVariables["user"] = iOID;
459  foreach ( indices(loginData[8]), string key ) {
460  mVariables[key] = loginData[8][key];
461  }
462  mVariables["rootroom"] = loginData[6];
463  sLastPacket = "";
464  if ( novars != 1 ) {
465  foreach ( values(loginData[9]), object cl ) {
466  set_object(cl->get_object_id());
467  mVariables[send_cmd(cl,"get_identifier")] = cl;
468  }
469  }
470  return name;
471  }
472  return 0;
473 }
474 
475 mixed logout()
476 {
477  __connected = 0;
478  write(coal_compose(0, COAL_LOGOUT, 0, 0, 0));
479 }
480 
481 
482 void was_closed()
483 {
484  resultQueue->write("");
485  ::was_closed();
486 }
487 
488 
489 void write_error2file(mixed|string err, int recursive) {
490 
491  Stdio.File error_file;
492  string path;
493  array directory;
494  int file_counter =0;
495  int found=0;
496  path = getcwd();
497  directory = get_dir(path);
498  while (found==0){
499  int tmp_found=1;
500  tmp_found=Stdio.exist(path+"/install_error."+file_counter);
501  if (tmp_found==1){
502  file_counter = file_counter + 1;
503  }
504  else{
505  found = 1;
506  }
507  }
508 
509  if (recursive==1)
510  file_counter = file_counter -1;
511  error_file=Stdio.File (path+"/install_error."+file_counter ,"cwa");
512  if (stringp (err)){
513  error_file->write(err);
514  }
515  if(arrayp(err)){
516  foreach(err, mixed error){
517  if ( stringp(error) || intp(error) )
518  error_file->write((string)error);
519  else if ( objectp(error) )
520  error_file->write("<object...>\n");
521  else if ( arrayp(error) ){
522  write_error2file(error,1);
523  }
524  }
525  }
526  if (recursive!=0)
527  error_file->close();
528 }
529 
530 
531 /**
532  * Creates a new document object on the server.
533  *
534  * @param name the name of the new object
535  * @param where the container or room in which to create the new object
536  * @param mimetype (optional) the mime type of the new object (if not
537  * specified, the mime type will be determined by the object name)
538  * @param content (optional) the content for the new object (if not specified,
539  * the new object will not have any content)
540  * @return the newly created object (if an error occurs, an exception will be
541  * thrown instead)
542  */
543 object create_document ( string name, object where, void|string mimetype, void|string content )
544 {
545  if ( !stringp(name) || sizeof(name) < 1 )
546  throw( ({ "No name specified !" }) );
547  if ( !objectp(where) )
548  throw( ({ "No room or container specified !" }) );
549  object obj = send_cmd( where, "get_object_byname", name );
550  if ( objectp(obj) )
551  throw( ({ "Object \""+name+"\" already found !" }) );
552  object factory = send_cmd( 0, "get_factory", CLASS_DOCUMENT );
553  if ( !objectp(factory) )
554  throw( ({ "Document factory not found on server !" }) );
555  mapping params = ([ "name":name ]);
556  if ( stringp(mimetype) && sizeof(mimetype) > 0 )
557  params["mimetype"] = mimetype;
558  obj = send_cmd( factory, "execute", params );
559  if ( !objectp(obj) )
560  throw( ({ "Could not create document !" }) );
561  send_cmd( obj, "move", where );
562 
563  if ( stringp(content) )
564  send_cmd( obj, "set_content", content );
565 
566  return obj;
567 }
568 
569 
570 /**
571  * Creates a new room object on the server.
572  *
573  * @param name the name of the new object
574  * @param where the room in which to create the new object
575  * @return the newly created object (if an error occurs, an exception will be
576  * thrown instead)
577  */
578 object create_room ( string name, object where )
579 {
580  if ( !stringp(name) || sizeof(name) < 1 )
581  throw( ({ "No name specified !" }) );
582  if ( !objectp(where) )
583  throw( ({ "No room specified !" }) );
584  object obj = send_cmd( where, "get_object_byname", name );
585  if ( objectp(obj) )
586  throw( ({ "Object \""+name+"\" already found !" }) );
587  object factory = send_cmd( 0, "get_factory", CLASS_ROOM );
588  if ( !objectp(factory) )
589  throw( ({ "Room factory not found on server !" }) );
590  obj = send_cmd( factory, "execute", ([ "name":name ]) );
591  if ( !objectp(obj) )
592  throw( ({ "Could not create room !" }) );
593  send_cmd( obj, "move", where );
594  return obj;
595 }
596 
597 /**
598  * Creates a new container object on the server.
599  *
600  * @param name the name of the new object
601  * @param where the container or room in which to create the new object
602  * @return the newly created object (if an error occurs, an exception will be
603  * thrown instead)
604  */
605 object create_container ( string name, object where )
606 {
607  if ( !stringp(name) || sizeof(name) < 1 )
608  throw( ({ "No name specified !" }) );
609  if ( !objectp(where) )
610  throw( ({ "No room or container specified !" }) );
611  object obj = send_cmd( where, "get_object_byname", name );
612  if ( objectp(obj) )
613  throw( ({ "Object \""+name+"\" already found !" }) );
614  object factory = send_cmd( 0, "get_factory", CLASS_CONTAINER );
615  if ( !objectp(factory) )
616  throw( ({ "Container factory not found on server !" }) );
617  obj = send_cmd( factory, "execute", ([ "name":name ]) );
618  if ( !objectp(obj) )
619  throw( ({ "Could not create container !" }) );
620  send_cmd( obj, "move", where );
621  return obj;
622 }
623 
624 
625 };