protocoll._pike
Go to the documentation of this file.
1 /* Copyright (C) 2000-2006 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: protocoll.pike,v 1.8 2009/08/07 15:22:36 nicke Exp $
18  */
19 inherit "binary";
20 inherit "login";
21  inherit Events.Listener;
22 #include <attributes.h>
23 #include <coal.h>
24 #include <assert.h>
25 #include <macros.h>
26 #include <events.h>
27 #include <functions.h>
28 #include <attributes.h>
29 #include <classes.h>
30 #include <database.h>
31 #include <config.h>
32 #include <client.h>
33 class protocoll : public binary,login{
34 public:
35 
36 
37 
38 
39 
40 //#define DEBUG_PROTOCOL
41 
42 #ifdef DEBUG_PROTOCOL
43 #define PROTO_LOG(s, args...) werror(s+"\n", args)
44 #else
45 #define PROTO_LOG(s, args...)
46 #endif
47 
48 void send_message(string str);
49 void close_connection();
50 void register_send_function(function f, function e);
51 void set_id(int i);
52 
53  mapping mCommandServer;
54  int iTransfer;
55  int iTransferSize;
56  object oTransfer;
57  string session_id;
58 
59 // slow command logging
60  int slow = _Server->get_config("log_slow_commands");
61 
62 
63 
64 //events
65  mapping mEvents = ([ ]);
66 
67 
68 class SocketListener {
69 public:
70  bool myEvents = true;
71  bool mapEvents = false;
72  object mySocket;
73  string session;
74 
75  void create(int event, object obj, object socket, bool mapE, bool receiveSelf, string s)
76  {
77  ::create(event, PHASE_NOTIFY, obj, notify, oUser);
78  session = s;
79  myEvents = receiveSelf;
80  mapEvents = mapE;
81  mySocket = socket;
82  }
83 
84  void notify(int event, mixed args, object eventObj) {
85  object target;
86  object socket = this_socket();
87 
88  if ( zero_type(::get_object()) ) {
89  destruct(this_object());
90  return;
91  }
92  if ( mySocket == socket && !myEvents ) {
93  return;
94  }
95  target = args[0];
96  if ( mapEvents )
97  SEND_COAL(time(), COAL_EVENT, target->get_object_id(),
98  target->get_object_class(),
99  ({ event, eventObj->get_params(), session_id,
100  socket == mySocket }));
101  else
102  SEND_COAL(time(), COAL_EVENT, target->get_object_id(),
103  target->get_object_class(),
104  ({ event, args[1..], session_id,
105  socket == mySocket}));
106  }
107  mapping save() {
108  // do not save !
109  return 0;
110  }
111 }
112 
113 /**
114  * COAL_event is not used at all since there are usually
115  * no events coming from a client.
116  *
117  * @param int t_id - the transaction id
118  * @param object obj - the context object
119  * @param mixed args - parameter array
120  * @return ok or failed
121  */
122 int
123 COAL_event(int t_id, object obj, mixed args)
124 {
125  return _COAL_OK;
126 }
127 
128 int COAL_getobject(int t_id, object obj, mixed args)
129 {
130  mapping attributes = obj->query_attributes();
131 
132  // sends only attributes yet
133  send_message( coal_compose(t_id, COAL_SENDOBJECT,
134  obj->get_object_id(), obj->get_object_class(),
135  ({ attributes }) ) );
136  return _COAL_OK;
137 }
138 
139 /**
140  * COAL_command: Call a function inside steam. The args are
141  * an array with one or two parameters. The first on is the function
142  * to call and the second one is an array again containing all the
143  * parameters to be passed to the function call.
144  *
145  * @param int t_id - the transaction id
146  * @param object obj - the context object
147  * @param mixed args - parameter array
148  * @return ok or failed
149  */
150 int
151 COAL_command(int t_id, object obj, mixed args)
152 {
153  int cmd;
154  function f;
155  mixed res;
156 
157  if ( !objectp(obj) ) return E_NOTEXIST | E_OBJECT;
158  if ( obj->status && obj->status() == PSTAT_DELETED ) return E_DELETED;
159 
160  if ( sizeof(args) >= 2 )
161  [ cmd, args ] = args;
162  else {
163  cmd = args[0];
164  args = ({ });
165  }
166  if ( functionp(obj->get_object) ) {
167  f = obj->find_function( cmd );
168  obj = obj->get_object();
169  }
170  else if ( !functionp( f = obj[cmd] ) )
171 
172  if ( !functionp(f) )
173  THROW("Function: " + cmd + " not found inside ("+obj->get_object_id()+
174  ")", E_FUNCTION|E_NOTEXIST);
175 
176  if ( !arrayp(args) ) args = ({ args });
177 
178  int oid = obj->get_object_id();
179  int oclass = obj->get_object_class();
180 
181  int tt = get_time_millis();
182  res = f(@args);
183  tt = get_time_millis() - tt;
184  if ( slow && tt > slow )
185  get_module("log")->log("slow_requests", LOG_LEVEL_INFO,
186  "%s Functioncall of %s in %O took %d ms",
187  timelib.event_time(time()), cmd, obj, tt);
188 
189  if ( objectp(oUser) )
190  oUser->command_done(time());
191 
192  if ( objectp(res) &&
193  functionp(res->is_async_return) &&
194  res->is_async_return() )
195  {
196  res->resultFunc = coal_send_result;
197  res->tid = t_id;
198  res->cmd = COAL_COMMAND;
199  res->oid = oid;
200  res->oclass = oclass;
201  }
202  else {
203  SEND_COAL(t_id, COAL_COMMAND, oid, oclass, res);
204  }
205 
206  return _COAL_OK;
207 }
208 
209 protected:
210  void coal_send_result(object ret, mixed res)
211 {
212  SEND_COAL(ret->tid, ret->cmd, ret->oid, ret->oclass, res);
213 }
214 
215 public:
216 
217 /**
218  * COAL_query_commands: returns a list of callable commands of the
219  * given object.
220  *
221  * @param int t_id - the transaction id
222  * @param object obj - the context object
223  * @param mixed args - parameter array
224  * @return ok or failed
225  * @see
226  */
227 int
228 COAL_query_commands(int t_id, object obj, mixed args)
229 {
230  if ( !objectp(obj) )
231  return E_NOTEXIST | E_OBJECT;
232  THROW("query_commands is unsupported", E_ERROR_PROTOCOL);
233 
234  return _COAL_OK;
235 }
236 
237 /**
238  * Set the client features of this connection.
239  *
240  * @param int t_id - the transaction id
241  * @param object obj - the context object
242  * @param mixed args - parameter array
243  * @return ok or failed.
244  */
245 int
246 COAL_set_client(int t_id, object obj, mixed args)
247 {
248  if ( sizeof(args) != 1 || !intp(args[0]) )
249  return E_FORMAT | E_TYPE;
250  iClientFeatures = args[0];
251  SEND_COAL(t_id, COAL_SET_CLIENT, 0, 0, ({ }));
252  return _COAL_OK;
253 }
254 
255 /**
256  *
257  *
258  * @param
259  * @return
260  * @see
261  */
262 int COAL_ping(int t_id, object obj, mixed args)
263 {
264  SEND_COAL(t_id, COAL_PONG, 0, 0, ({ }));
265  return _COAL_OK;
266 }
267 
268 int COAL_pong(int t_id, object obj, mixed args)
269 {
270  // clients are not supposed to send pongs
271 }
272 
273 /**
274  * Login the server with name and password. Optional the parameters client-name
275  * and features can be used to login. If no features and name is given the
276  * server will use "steam" and all client features. Otherwise the file client.h
277  * describes all possible features. Right now only CLIENT_FEATURES_EVENTS
278  * (enables the socket to get events) and CLIENT_FEATURES_MOVE (moves the
279  * user to his workroom when disconnecting and back to the last logout place
280  * when connecting). Apart from that the features can be checked at the user
281  * object by calling the function get_status(). It will return a bit vector
282  * of all set features. This enables clients to check if a user hears a chat
283  * for example.
284  *
285  * @param t_id - id of the transfer
286  * @param obj_id - the relevant object
287  * @param args - the arguments, { user, password } optional two other
288  * parameters could be used: { user, password, client-name,
289  * client-features }
290  * @return ok or error code
291  * @see COAL_logout
292  * @see database.lookup_user
293  */
294 int
295 COAL_login(int t_id, object obj, mixed args)
296 {
297  object uid;
298  string u_name, u_pass;
299 
300  if ( sizeof(args) < 2 || !stringp(args[0]) || !stringp(args[1]) )
301  return E_FORMAT | E_TYPE;
302 
303  u_name = args[0]; /* first argument is the username */
304  u_pass = args[1];
305  PROTO_LOG("login("+u_name+")");
306  sClientClass = CLIENT_CLASS_STEAM;
307  if ( sizeof(args) > 3 ) {
308  sClientClass = args[2];
309  if ( !intp(args[3]) )
310  THROW("Third argument is not an integer", E_TYPE);
311  iClientFeatures = args[3];
312  }
313  else
314  iClientFeatures = CLIENT_STATUS_CONNECTED;
315 
316  if ( sizeof(args) == 5 )
317  set_id(args[4]);
318 
319  mixed err = catch(uid = get_module("auth")->authenticate(u_name, u_pass));
320  if ( err )
321  {
322  FATAL("COAL: failed to authenticate: %O\n", err[0], err[1]);
323  return E_ACCESS | E_PASSWORD;
324  }
325  if ( !objectp(uid) )
326  return E_ACCESS | E_PASSWORD;
327 
328  if ( functionp(uid->is_async_return) && uid->is_async_return() ) {
329  uid->resultFunc = async_login_user;
330  uid->tid = t_id;
331  }
332  else {
333  do_login_user(uid, t_id);
334  }
335  return _COAL_OK;
336 }
337 
338 protected:
339  void async_login_user(object async, object uid)
340 {
341  do_login_user(uid, async->tid);
342 }
343 
344 public:
345 
346 protected:
347  void do_login_user(object uid, int t_id)
348 {
349  // allready connected to user - relogin
350  logout_user();
351 
352  int last_login = login_user(uid);
353  object server = master()->get_server();
354 
355  session_id = uid->get_session_id();
356  send_message( coal_compose(t_id, COAL_LOGIN, uid->get_object_id(),
357  uid->get_object_class(),
358  ({ uid->get_user_name(),
359  server->get_version(),
360  server->get_last_reboot(),
361  last_login,
362  version(),
363  _Database,
364  MODULE_OBJECTS->lookup("rootroom"),
365  MODULE_GROUPS->lookup("sTeam"),
366  _Server->get_modules(),
367  _Server->get_classes(),
368  _Server->get_configs(),
369  session_id,
370  COAL_VERSION,
371  })) );
372 }
373 
374 public:
375 
376 int COAL_hello(int t_id, object obj, mixed args)
377 {
378  string name, cert;
379 
380  if ( sizeof(args) < 2 || !stringp(args[0]) || !stringp(args[1]) )
381  return E_FORMAT | E_TYPE;
382 
383  object cluster = get_module("Cluster");
384  if ( !objectp(cluster) )
385  steam_error("Standalone sTeam-Server !");
386 
387 
388  name = args[0]; /* first argument is the username */
389  cert = args[1];
390  object server = cluster->hello(name, cert);
391  if ( !objectp(server) )
392  steam_error("Unable to verify Authentication !");
393  login_user(server); // establish connection with server object
394  sClientClass = CLIENT_CLASS_SERVER;
395  send_message( coal_compose(t_id, COAL_LOGIN, 0,
396  server->get_id(),
397  ({ name, server->get_version(),
398  server->get_last_reboot(),
399  0,
400  version(), _Database,
401  MODULE_OBJECTS->lookup("rootroom"),
402  MODULE_GROUPS->lookup("sTeam"),
403  _Server->get_modules(),
404  _Server->get_classes(),
405  _Server->get_configs(),
406  })) );
407  return _COAL_OK;
408 }
409 
410 int COAL_relogin(int t_id, object obj, mixed args)
411 {
412  object uid;
413  string u_name, u_pass;
414  int last_login;
415 
416  if ( sizeof(args) < 2 || !stringp(args[0]) || !stringp(args[1]) )
417  return E_FORMAT | E_TYPE;
418 
419  u_name = args[0]; /* first argument is the username */
420  u_pass = args[1];
421  sClientClass = CLIENT_CLASS_STEAM;
422  if ( sizeof(args) > 3 ) {
423  sClientClass = args[2];
424  if ( !intp(args[3]) )
425  THROW("Third argument is not an integer", E_TYPE);
426  iClientFeatures = args[3];
427  }
428  else
429  iClientFeatures = CLIENT_STATUS_CONNECTED;
430 
431  if ( sizeof(args) == 5 )
432  set_id(args[4]);
433 
434  uid = get_module("auth")->authenticate( u_name, u_pass );
435 
436  if ( !objectp(uid) )
437  return E_ACCESS | E_PASSWORD;
438 
439  logout_user();
440 
441  last_login = login_user(uid);
442 
443  send_message( coal_compose(t_id, COAL_LOGIN, uid->get_object_id(),
444  uid->get_object_class(), ({ })) );
445  return _COAL_OK;
446 }
447 
448 /**
449  * called when logging out
450  *
451  * @param t_id - the current transaction id
452  * @param obj - the relevant object (not used in this case)
453  * @return ok - works all the time
454  * @see COAL_login
455  */
456 int
457 COAL_logout(int t_id, object obj, mixed args)
458 {
459  PROTO_LOG("Logging out...\n");
460  close_connection();
461  logout_user();
462  return _COAL_OK;
463 }
464 
465 /**
466  * COAL_file_download
467  *
468  * @param t_id - the transaction id of the command
469  * @param obj - the relevant object
470  * @param args - arguments for the download (ignored)
471  * @return error code or ok
472  * @see
473  */
474 int
475 COAL_file_download(int t_id, object obj, mixed args)
476 {
477  function send;
478  string type;
479 
480  if ( !objectp(obj) )
481  return E_NOTEXIST | E_OBJECT;
482  else if ( obj->get_content_size() == 0 ) {
483  SEND_COAL(t_id, COAL_FILE_UPLOAD, obj->get_object_id(),
484  obj->get_object_class(), ({ obj->get_content_size() }));
485  return _COAL_OK;
486  }
487 
488 
489  if ( !arrayp(args) )
490  args = ({ });
491 
492  type = obj->query_attribute(DOC_MIME_TYPE);
493  PROTO_LOG("mime:"+type);
494  obj = obj->get_object();
495 
496  if ( !functionp(obj->get_content_callback) ) {
497  object index;
498  if ( obj->get_object_class() & CLASS_CONTAINER ) {
499 
500  index = obj->get_object_byname("index.html");
501  if ( !objectp(index) )
502  index = obj->get_object_byname("index.htm");
503  if ( !objectp(index) )
504  index = obj->get_object_byname("index.xml");
505  }
506  if ( !objectp(index) )
507  return E_ERROR;
508  obj = index->get_object();
509  }
510 
511  if ( sizeof(args) == 0 )
512  send = obj->get_content_callback( ([ "raw": 1, ]) );
513  else
514  send = obj->get_content_callback(args[0]);
515 
516  PROTO_LOG("Now acknowledging download !");
517  SEND_COAL(t_id, COAL_FILE_UPLOAD, obj->get_object_id(),
518  obj->get_object_class(), ({ obj->get_content_size() }));
519  type = "";
520 
521  iTransfer = COAL_TRANSFER_SEND;
522  register_send_function(send, download_finished);
523  return _COAL_OK;
524 }
525 
526 protected:
527  void receive_message(string str) { }
528 
529 public:
530 
531 /**
532  * download finished will set the mode back to no-transfer
533  *
534  * @see COAL_file_download
535  */
536 private:
537 private void download_finished()
538 {
539  PROTO_LOG("transfer finished...");
540  iTransfer = COAL_TRANSFER_NONE;
541  receive_message("");
542 }
543 
544 public:
545 
546 /**
547  * COAL_file_upload
548  *
549  * @param t_id - the transaction id of the command
550  * @param obj - the relevant object
551  * @param args - arguments for the upload (1 arg, url and size)
552  * @return error code or ok
553  * @see COAL_file_download
554  */
555 int
556 COAL_file_upload(int t_id, object obj, mixed args)
557 {
558  string url;
559  int size;
560  object path = 0;
561 
562  /*
563  * find the object or create it...
564  */
565  if ( !arrayp(args) ||
566  (sizeof(args) != 1 && sizeof(args) != 2 && sizeof(args) != 3) )
567  {
568  return E_FORMAT | E_TYPE;
569  }
570  switch ( sizeof(args) ) {
571  case 3:
572  [ path, url, size ] = args;
573  break;
574  case 2:
575  [url, size] = args;
576  break;
577  case 1:
578  [ url ] = args;
579  size = -1;
580  }
581 
582  if ( objectp(path) ) {
583  obj = _FILEPATH->resolve_path(path, url);
584  }
585  else {
586  obj = _FILEPATH->path_to_object(url);
587  }
588  if ( !objectp(obj) ) {
589  object factory, cont;
590 
591  factory = _Server->get_factory(CLASS_DOCUMENT);
592  cont = _FILEPATH->path_to_environment(url);
593  obj = factory->execute((["url":url,]));
594  if ( objectp(path) )
595  obj->move(path);
596  PROTO_LOG("object created="+master()->stupid_describe(obj,255));
597  }
598  else
599  PROTO_LOG("found object.="+master()->stupid_describe(obj,255));
600 
601  if ( !functionp(obj->receive_content) )
602  return E_NOTEXIST | E_OBJECT;
603  PROTO_LOG("sending ok...");
604  SEND_COAL(t_id, COAL_FILE_DOWNLOAD, 0, 0, ({ obj }));
605  iTransfer = COAL_TRANSFER_RCV;
606  iTransferSize = size;
607  oTransfer = ((program)"/kernel/DocFile")(obj, "wct");
608  obj->set_attribute(DOC_LAST_ACCESSED, time());
609  obj->set_attribute(DOC_LAST_MODIFIED, time());
610  return _COAL_OK;
611 }
612 
613 /**
614  * COAL_upload_start - start an upload and
615  * call upload_package subsequently.
616  *
617  * @param t_id - the transaction id of the command
618  * @param obj - the relevant object
619  * @param args - arguments for the upload (1 arg, url and size)
620  * @return error code or ok
621  * @see COAL_file_download
622  */
623 int
624 COAL_upload_start(int t_id, object obj, mixed args)
625 {
626  string|object url;
627  int size;
628  /* find the object or create it... */
629 
630  if ( !arrayp(args) || sizeof(args) != 1 )
631  return E_FORMAT | E_TYPE;
632  size = 0;
633  [ url ] = args;
634 
635  if ( objectp(url) )
636  obj = url;
637  else
638  obj = _FILEPATH->path_to_object(url);
639 
640  if ( !objectp(obj) ) {
641  object factory, cont;
642 
643  factory = _Server->get_factory(CLASS_DOCUMENT);
644  if ( !objectp(factory) ) LOG("Unable to find document factory !\n");
645  cont = _FILEPATH->path_to_environment(url);
646  obj = factory->execute((["url":url,]));
647  PROTO_LOG("object created="+master()->stupid_describe(obj,255));
648  }
649  else
650  PROTO_LOG("found object.="+master()->stupid_describe(obj,255));
651 
652  if ( !functionp(obj->receive_content) )
653  return E_NOTEXIST | E_OBJECT;
654  SEND_COAL(t_id, COAL_FILE_DOWNLOAD, 0, 0, ({ obj }) );
655  iTransfer = 0;
656  // only set upload function, but dont set transfer mode,
657  // this means the protocoll is not blocking anymore !
658  oTransfer = ((program)"/kernel/DocFile")(obj, "wct");
659  obj->set_attribute(DOC_LAST_ACCESSED, time());
660  obj->set_attribute(DOC_LAST_MODIFIED, time());
661  return _COAL_OK;
662 }
663 
664 /**
665  * Upload a package to steam. Before this command can be used
666  * there has to be a call to upload start before to define
667  * a callback function receiving the data.
668  *
669  * @param t_id - the transaction id of the command.
670  * @param obj - the relevant object.
671  * @param args - arguments for the query containing the content.
672  * @return ok or failed.
673  * @see
674  */
675 int COAL_upload_package(int t_id, object obj, mixed args)
676 {
677  if ( !objectp(oTransfer) )
678  THROW("No upload function - start upload with COAL_UPLOAD_START !",
679  E_ERROR);
680  PROTO_LOG("uploading...");
681  if ( sizeof(args) != 1 )
682  return E_FORMAT | E_TYPE;
683  PROTO_LOG("upload_package()");
684  if ( !stringp(args[0]) || args[0] == 0 ) {
685  oTransfer->close();
686  destruct(oTransfer);
687  oTransfer = 0;
688  PROTO_LOG("Finished upload !\n");
689  // at this point send back that we are finished, so client can logout
690  SEND_COAL(t_id, COAL_UPLOAD_FINISHED, 0, 0, ({ obj }));
691  return _COAL_OK;
692  }
693  PROTO_LOG("Received package: " + strlen(args[0]));
694  oTransfer->write(args[0]);
695  return _COAL_OK;
696 }
697 
698 
699 int COAL_log(int t_id, object obj, mixed args)
700 {
701  if ( sizeof(args) != 1 )
702  return E_FORMAT | E_TYPE;
703 }
704 
705 int COAL_retr_log(int t_id, object obj, mixed args)
706 {
707 }
708 
709 protected:
710  int add_event(object obj, int event, bool receiveSelf, bool mapEvents)
711 {
712  if ( !mappingp(mEvents[event]) )
713  mEvents[event] = ([ ]);
714  if ( objectp(mEvents[event][obj]) )
715  return event;
716 
717  SocketListener l = SocketListener(event, obj, this_object(), mapEvents,
718  receiveSelf, session_id);
719 
720  mEvents[event][obj] = l;
721  object listener = obj->listen_event(l);
722  if ( listener->get_listener_id() != l->get_listener_id() ) {
723  steam_error("Found previous listener !");
724  }
725  return event;
726 }
727 
728 public:
729 
730 int COAL_subscribe(int t_id, object obj, mixed args)
731 {
732  int id, i;
733  array new_events;
734  bool receiveSelf;
735  bool mapEvents;
736 
737  receiveSelf = true;
738 
739  if ( sizeof(args) != 1 && sizeof(args) != 2 )
740  return E_FORMAT | E_TYPE;
741 
742  mixed events = args[0];
743  if ( sizeof(args) >= 2 )
744  mapEvents = args[1];
745  if ( sizeof(args) >= 3 )
746  receiveSelf = args[2];
747 
748 
749  new_events = ({ });
750 
751 
752  if ( !arrayp(events) ) {
753  int mask = events & 0xf0000000;
754  for ( i = 0; i < 28; i++ ) {
755  if ( (id = events & (1<<i)) > 0 )
756  new_events+=({ add_event(obj,id|mask,receiveSelf,mapEvents) });
757  }
758  }
759  else {
760  for ( i = 0; i < sizeof(events); i++ )
761  new_events+=({ add_event(obj, events[i], receiveSelf, mapEvents)});
762  }
763  SEND_COAL(t_id, COAL_SUBSCRIBE, obj->get_object_id(),
764  obj->get_object_class(), ({ new_events }));
765  return _COAL_OK;
766 }
767 
768 mapping get_events()
769 {
770  return mEvents;
771 }
772 
773 int COAL_unsubscribe(int t_id, object obj, mixed args)
774 {
775  int events_removed = 0;
776  array events;
777 
778  if ( !arrayp(args) )
779  events = ({ args });
780  else
781  events = args;
782 
783  PROTO_LOG("Unsubscribing events (%s) = %O", obj->describe(), events);
784  for ( int i = 0; i < sizeof(events); i++ ) {
785  if ( !mappingp(mEvents[events[i]]) )
786  continue;
787  object listener = mEvents[events[i]][obj];
788  if ( objectp(listener) ) {
789  object target = listener->get_object();
790  if ( objectp(target) )
791  target->ignore_event(listener);
792  destruct(listener);
793  m_delete(mEvents[events[i]], obj);
794  events_removed++;
795  }
796  else
797  FATAL("Cannot remove listener for %d on %s", events[i],
798  obj->describe());
799  }
800  SEND_COAL(t_id, COAL_UNSUBSCRIBE, obj->get_object_id(),
801  obj->get_object_class(), ({ events_removed }));
802  return _COAL_OK;
803 }
804 
805 int COAL_reg_service(int t_id, object obj, mixed args)
806 {
807  if ( !objectp(obj) || obj == _Server )
808  obj = get_module("ServiceManager");
809  obj->register_service(send_service, notify_service, @args);
810  SEND_COAL(t_id, COAL_REG_SERVICE, obj->get_object_id(),
811  obj->get_object_class(), ({ }));
812  return _COAL_OK;
813 }
814 
815 protected:
816  void send_service(mixed args)
817 {
818  catch(SEND_COAL(0, COAL_COMMAND, 0, 0, ({ "call_service", args })));
819 }
820 
821 public:
822 
823 protected:
824  void notify_service(object event, string name)
825 {
826  mapping args = event->get_params();
827  args->name = name;
828  catch(SEND_COAL(0, COAL_COMMAND, 0, 0, ({ "notify", args })));
829 }
830 
831 public:
832 
833 
834 
835 /**
836  * Initialize the protocoll.
837  */
838 void
839 private:
840 init_protocoll()
841 {
842  mCommandServer = ([
843  COAL_EVENT: COAL_event,
844  COAL_COMMAND: COAL_command,
845  COAL_LOGIN: COAL_login,
846  COAL_LOGOUT: COAL_logout,
847  COAL_FILE_UPLOAD: COAL_file_upload,
848  COAL_FILE_DOWNLOAD: COAL_file_download,
849  COAL_SET_CLIENT: COAL_set_client,
850  COAL_UPLOAD_PACKAGE: COAL_upload_package,
851  COAL_UPLOAD_START: COAL_upload_start,
852  COAL_PING: COAL_ping,
853  COAL_PONG: COAL_pong,
854  COAL_LOG: COAL_log,
855  COAL_RETR_LOG: COAL_retr_log,
856  COAL_SUBSCRIBE: COAL_subscribe,
857  COAL_UNSUBSCRIBE: COAL_unsubscribe,
858  COAL_REG_SERVICE: COAL_reg_service,
859  COAL_RELOGIN: COAL_relogin,
860  COAL_SERVERHELLO: COAL_hello,
861  COAL_GETOBJECT: COAL_getobject,
862  ]);
863  // any connection is guest user first!
864  login_user(_Persistence->lookup_user("guest"));
865 }
866 
867 public:
868 
869 /**
870  * send a message to the client - this function can only be called
871  * by the connected user-object
872  *
873  * @param tid - transaction id
874  * @param cmd - the command
875  * @param obj - the relevant object
876  * @param args - the arguments for the command
877  * @see coal_compose
878  */
879 final void
880 send_client_message(int tid, int cmd, object obj, mixed ... args)
881 {
882  if ( !is_user_object(CALLER) )
883  return;
884  if ( tid == USE_LAST_TID )
885  tid = iLastTID;
886  SEND_COAL(tid, cmd, obj->get_object_id(), obj->get_object_class(), args);
887 }
888 
889 /**
890  * Compose a coal command by passing a number of parameters.
891  *
892  * @param int t_id - the transaction id
893  * @param int cmd - the coal command to call
894  * @param int o_id - the object id of the context object
895  * @param int class_id - the class of the context object
896  * @param mixed args - the parameters
897  * @return composed string
898  */
899 string coal_compose(int t_id, int cmd, int o_id, int class_id, mixed args)
900 {
901  return ::coal_compose(t_id, cmd, o_id, class_id, args);
902 }
903 
904 
905 protected:
906  void logout_user()
907 {
908  foreach(indices(mEvents), int event)
909  if ( mappingp(mEvents[event]) )
910  foreach(values(mEvents[event]), object listener)
911  destruct(listener);
912  ::logout_user();
913 }
914 
915 public:
916 
917 
918 };