1
/* Copyright (C) 2000-2006 Thomas Bopp, Thorsten Hampel, Ludger Merkens
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.
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.
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
17
* $Id: User.pike,v 1.8 2006/09/29 07:18:19 astra Exp $
20
constant cvs_version="$Id: User.pike,v 1.8 2006/09/29 07:18:19 astra Exp $";
23
//! this is the user object. It keeps track of connections and membership
26
inherit "/classes/Container" : __cont;
27
inherit "/base/member" : __member;
29
#include <attributes.h>
41
//#define EVENT_USER_DEBUG
43
#ifdef EVENT_USER_DEBUG
44
#define DEBUG_EVENT(s, args...) werror(s+"\n", args)
46
#define DEBUG_EVENT(s, args...)
49
/* Security relevant functions */
50
private static string sUserPass; // the password for the user
51
private static string sPlainPass;
52
private static string sUserName; // the name of the user
53
private static object oActiveGrp; // the active group
54
private static int iCommandTime; // when the last command was send
56
private static string sTicket;
57
private static array(string) aTickets;
58
private static int iActiveCode;
60
static mapping mSockets;
61
static mapping mMoveEvents;
62
private static mapping mSocketEvents;
64
bool userLoaded = false;
66
object this() { return __cont::this(); }
67
bool check_swap() { return false; }
68
bool check_upgrade() { return false; }
76
mSocketEvents = ([ ]);
79
/* the user name is a locked attribute */
80
add_data_storage(STORE_USER, store_user_data, restore_user_data, 1);
84
* Constructor of the user object.
86
* @author <a href="mailto:astra@upb.de">Thomas Bopp</a>)
103
* Creating a duplicate of the user wont work.
105
* @return throws an error
106
* @author <a href="mailto:astra@upb.de">Thomas Bopp</a>)
110
THROW("User cannot be duplicated !\n", E_ERROR);
114
* register the object in the database.
116
* @param name - the name of the object
117
* @author Thomas Bopp (astra@upb.de)
119
static void database_registration(string name)
121
MODULE_USERS->register(name, this());
125
* Destructor of the user.
127
* @author Thomas Bopp (astra@upb.de)
135
if ( this() == MODULE_USERS->lookup("root") )
136
THROW("Cannot delete the root user !", E_ACCESS);
138
MODULE_USERS->unregister(sUserName);
139
object mailbox = do_query_attribute(USER_MAILBOX);
140
// delete the mailbox recursively
141
if ( objectp(mailbox) ) {
142
foreach(mailbox->get_inventory(), object inv) {
152
object workroom = do_query_attribute(USER_WORKROOM);
153
if ( objectp(workroom) ) workroom->delete();
156
FATAL( "Failed to delete workroom of \"%s\": %O\n%O\n", sUserName, err[0], err[1] );
158
object bookmarks = do_query_attribute(USER_BOOKMARKROOM);
159
if ( objectp(bookmarks) ) bookmarks->delete();
162
FATAL( "Failed to delete bookmars of \"%s\": %O\n%O\n", sUserName, err[0], err[1] );
164
object calendar = do_query_attribute(USER_CALENDAR);
165
if ( objectp(calendar) ) calendar->delete();
168
FATAL( "Failed to delete calendar of \"%s\": %O\n%O\n", sUserName, err[0], err[1] );
170
__member::delete_object();
171
__cont::delete_object();
175
* Dont update a users name.
177
void update_identifier()
182
* Create all the exits to the groups the user is member of.
184
* @author <a href="mailto:astra@upb.de">Thomas Bopp</a>)
186
void create_group_exits()
188
object workroom = do_query_attribute(USER_WORKROOM);
189
if ( objectp(workroom) ) {
190
array(object) inv = workroom->get_inventory();
191
array(object) groups = get_groups();
192
mapping mExits = ([ ]);
194
foreach ( groups, object grp ) {
195
if ( !objectp(grp) ) continue;
196
mapping exits = grp->query_attribute(GROUP_EXITS);
197
if ( !mappingp(exits) ) {
198
object workroom = grp->query_attribute(GROUP_WORKROOM);
199
exits = ([ workroom: workroom->get_identifier(), ]);
203
foreach ( indices(mExits), object exit ) {
206
if ( !objectp(exit) )
209
foreach ( inv, object o ) {
210
if ( o->get_object_class() & CLASS_EXIT ) {
211
object exit_to = o->get_link_object();
212
if ( !objectp(exit_to) )
214
if ( exit_to->get_object_id() == exit->get_object_id() )
219
object factory = _Server->get_factory(CLASS_EXIT);
220
object exit = factory->execute(
221
([ "name": mExits[exit], "exit_to": exit, ]) );
222
exit->sanction_object(this(), SANCTION_ALL);
223
exit->move(workroom);
229
static string new_session_id()
232
#if constant(Crypto.Random)
233
sid = sprintf("%x", hash(Crypto.Random.random_string(10)));
235
sid = sprintf("%x", hash(random(1000000) + time() + sUserName+sUserPass));
241
* Connect the user object to a steamsocket.
243
* @param obj - the steamsocket to connect to
244
* @return the time of the last login
245
* @author Thomas Bopp
254
LOG("Connecting "+ get_identifier()+" with "+ obj->describe()+"\n");
256
if ( !IS_SOCKET(CALLER) )
257
THROW("Trying to connect user to non-steamsocket !", E_ACCESS);
259
array aoSocket = values(mSockets);
260
for ( i = sizeof(aoSocket) - 1; i >= 0; i-- ) {
261
if ( aoSocket[i] == obj )
264
int features = obj->get_client_features();
265
int prev_features = get_status();
266
try_event(EVENT_LOGIN, this(), features, prev_features);
269
string sid = new_session_id();
270
while ( objectp(mSockets[sid]) )
271
sid = new_session_id();
275
m_delete(mSockets, 0);
276
foreach ( indices(mSockets), sid)
277
if ( !objectp(mSockets[sid]) && !stringp(mSockets[sid]) )
278
m_delete(mSockets, sid);
280
last_login = do_query_attribute(USER_LAST_LOGIN);
281
do_set_attribute(USER_LAST_LOGIN, time());
283
if ( (prev_features & features) != features )
284
run_event(EVENT_STATUS_CHANGED, this(), features, prev_features);
286
run_event(EVENT_LOGIN, this(), features, prev_features);
288
if ( objectp(oEnvironment) )
289
oEnvironment->enter_system(this(), features, prev_features);
294
string get_session_id()
296
if ( !IS_SOCKET(CALLER) )
297
THROW("Trying to steal session by non-socket !", E_ACCESS);
298
foreach( indices(mSockets), string sid) {
299
if ( mSockets[sid] == CALLER )
305
bool join_group(object grp)
307
try_event(EVENT_USER_JOIN_GROUP, CALLER, grp);
308
mixed res = ::join_group(grp);
309
require_save(STORE_USER);
310
run_event(EVENT_USER_JOIN_GROUP, CALLER, grp);
314
bool leave_group(object grp)
316
try_event(EVENT_USER_LEAVE_GROUP, CALLER, grp);
317
mixed res = ::leave_group(grp);
318
run_event(EVENT_USER_LEAVE_GROUP, CALLER, grp);
323
* Close the connection to socket and logout.
325
* @param obj - the object to remove from active socket list
326
* @author Thomas Bopp (astra@upb.de)
330
close_connection(object obj)
332
if ( which_socket(obj) < 0 ) return;
334
try_event(EVENT_LOGOUT, CALLER, obj);
336
foreach(indices(mSockets), string sid)
337
if ( mSockets[sid] == obj )
338
m_delete(mSockets, sid);
340
int cfeatures = obj->get_client_features();
341
int features = get_status();
343
if ( (cfeatures & features) != cfeatures )
344
run_event(EVENT_STATUS_CHANGED, this(), cfeatures, features);
346
ASSERTINFO(which_socket(obj) < 0, "Still connected to socket !");
347
DEBUG_EVENT(sUserName+": logout event....");
348
run_event(EVENT_LOGOUT, CALLER, obj);
352
* Disconnect the CALLER socket from this user object.
354
* @author Thomas Bopp (astra@upb.de)
359
object socket = CALLER;
362
if ( which_socket(socket) == -1 )
365
if ( arrayp(mSocketEvents[socket]) ) {
366
foreach ( mSocketEvents[socket], mixed event_data )
367
if ( arrayp(event_data) )
368
remove_event(@event_data);
370
// get the remaining status of the user
372
array aoSocket = values(mSockets);
373
foreach ( aoSocket, object sock ) {
374
if ( objectp(sock) && sock != socket ) {
375
status |= sock->get_client_features();
378
// if the user has no more chat and awareness clients notify
379
// the environment about logged out
380
if ( objectp(oEnvironment) &&
381
!(status & CLIENT_FEATURES_CHAT) &&
382
!(status & CLIENT_FEATURES_AWARENESS) )
383
oEnvironment->leave_system(this());
387
// if this is a client which allows movement of the user
388
// then move the user back to its workroom
389
if ( !(status & CLIENT_FEATURES_MOVE) )
391
object workroom = do_query_attribute(USER_WORKROOM);
392
if ( oEnvironment != workroom ) {
393
LOG("Closing down connection to user - moving to workroom !");
394
set_attribute(USER_LOGOUT_PLACE, oEnvironment);
395
if ( objectp(workroom) )
400
close_connection(socket);
404
* find out if the object is one of the connected sockets
406
* @param obj - the object to find out about
407
* @return the position of the socket in the socket array
408
* @author Thomas Bopp (astra@upb.de)
413
which_socket(object obj)
415
return search(values(mSockets), obj);
419
* Activate the login. Successfull activation code is required to do so!
421
* @param int activation - the activation code
422
* @return true or false
423
* @author <a href="mailto:astra@upb.de">Thomas Bopp</a>)
425
bool activate_user(int|void activation)
427
if ( activation == iActiveCode || _ADMIN->is_member(this_user()) ) {
429
require_save(STORE_USER);
436
* Set the activation code for an user - this is done by the factory.
438
* @param int activation - the activation code.
439
* @author <a href="mailto:astra@upb.de">Thomas Bopp</a>)
442
void set_activation(int activation)
444
if ( CALLER != _Server->get_factory(CLASS_USER) &&
445
!_ADMIN->is_member(this_user()) )
446
THROW("Invalid call to set_activation !", E_ACCESS);
447
iActiveCode = activation;
448
require_save(STORE_USER);
452
* Find out if the user is inactivated.
454
* @return activation code set or not.
456
bool get_activation()
458
return iActiveCode != 0;
462
* Check if a given password is correct. Users can authenticate with their
463
* password or with temporary tickets. There are one time tickets and
464
* tickets which last for acertain time encoded in the ticket itself.
465
* Authentication will always fail if the user is not activated.
467
* @param pw - the password to check
468
* @param uid - the user object
469
* @return if the password matches or not
470
* @author Thomas Bopp (astra@upb.de)
472
bool check_user_password(string pw)
474
if ( !stringp(sUserPass) )
481
MESSAGE("Trying to authenticate with inactivated user !");
482
return false; // as long as the login is not activated
485
if ( stringp(sTicket) )
487
if ( verify_crypt_md5(pw, sTicket) ) {
488
sTicket = 0; // ticket used
492
if ( arrayp(aTickets) && sizeof(aTickets) > 0 ) {
493
array tickets = copy_value(aTickets);
494
foreach(tickets, string ticket) {
496
sscanf(ticket, "%*s_%d", t);
498
aTickets -= ({ ticket });
499
require_save(STORE_USER);
501
else if ( pw == ticket )
505
// allow login with any session ID from a connected socket
506
foreach ( indices(mSockets), string sid)
510
if ( strlen(sUserPass) > 12 && lower_case(sUserPass[0..12]) == "{deactivated}" )
511
return false; // user account has been deactivated
513
if ( strlen(sUserPass) > 5 && lower_case(sUserPass[0..4]) == "{sha}" )
514
return sUserPass[5..] == MIME.encode_base64( sha_hash(pw) );
515
if ( strlen(sUserPass) > 6 && lower_case(sUserPass[0..5]) == "{ssha}" ) {
516
string salt = MIME.decode_base64( sUserPass[6..] )[20..]; // last 8 bytes is the salt
517
return sUserPass[6..] == MIME.encode_base64( sha_hash(pw+salt) );
519
if ( strlen(sUserPass) > 7 && lower_case(sUserPass[0..6]) == "{crypt}" )
520
return crypt(pw, sUserPass[7..]);
521
if ( strlen(sUserPass) > 4 && lower_case(sUserPass[0..3]) == "{lm}" ) {
522
return sUserPass[4..] == LanManHash.lanman_hash(pw);
524
if ( strlen(sUserPass) < 3 || sUserPass[0..2] != "$1$" )
525
return crypt(pw, sUserPass); // normal crypt check
527
return verify_crypt_md5(pw, sUserPass);
531
* Transform a string in some other characters.
533
* @param string what the string to convert.
534
* @return converted string.
535
* @author <a href="mailto:astra@upb.de">Thomas Bopp</a>)
537
private static string tohex(string what)
540
for ( int q = 0; q < strlen(what); q++ ) {
542
i |= what[strlen(what)-1-q];
544
return sprintf("%x", i);
548
* Get a ticket from the server - authenticate to the server with
549
* this ticket once. Optional parameter t gives time the ticket
552
* @param void|int t - the validity of the ticket
554
* @see check_user_password
556
final string get_ticket(void|int t)
558
if ( !IS_SOCKET(CALLER) && !_SECURITY->access_write(0, this(), CALLER) )
559
THROW("Invalid call to get_ticket() !", E_ACCESS);
561
try_event(EVENT_USER_NEW_TICKET, CALLER, 0);
564
for ( int i = 0; i < 8; i++ )
565
ticket[i] = random(26) + 'a';
566
ticket = crypt(ticket + time());
567
ticket = replace(ticket, ({ "/", "\\" }), ({ "X", "Y" }));
568
if ( !zero_type(t) ) {
571
aTickets += ({ ticket });
573
aTickets = ({ ticket });
574
run_event(EVENT_USER_NEW_TICKET, CALLER, "********");
575
require_save(STORE_USER);
579
sTicket = make_crypt_md5(ticket);
580
sTicket = replace(sTicket, ({ "/", "\\" }), ({ "X", "Y" }));
581
run_event(EVENT_USER_NEW_TICKET, CALLER, sTicket);
585
static string oldpassword;
587
* temporary storage for old password while password is being changed.
588
* to allow places like ldap to pick get the old password, in case they need it
589
* to set the new one.
590
* @return oldpassword
591
* @see check_user_pasword
593
string get_old_password()
595
if ( CALLER->this() != _Server->get_module("ldap"))
596
THROW(sprintf("%O is not permitted to read the old password!", CALLER),
598
//werror("get_old_password: %O\n", this_user());
603
* Set the user password and save an md5 hash of it.
605
* @param pw - the new password for the user
606
* @return if successfull
607
* @see check_user_pasword
610
set_user_password(string pw, int|void crypted, string|void oldpw)
613
try_event(EVENT_USER_CHANGE_PW, CALLER);
617
sUserPass = make_crypt_md5(pw);
618
require_save(STORE_USER);
619
run_event(EVENT_USER_CHANGE_PW, CALLER);
625
set_user_password_plain(string pw, int|void crypted)
627
try_event(EVENT_USER_CHANGE_PW, CALLER);
631
sPlainPass = make_crypt_md5(pw);
632
require_save(STORE_USER);
633
run_event(EVENT_USER_CHANGE_PW, CALLER);
639
* Get the password of the user which should be fine since
640
* we have an md5 hash. This is used to import/export users.
642
* @return the users password.
645
get_user_password(string|void pw)
647
// security problem ? ask for read permissions at least -
648
// probably for admin?
649
return copy_value(sUserPass);
653
* Get the user object of the user which is this object.
656
object get_user_object()
662
* Get the sTeam e-mail adress of this user. Usually its the users name
663
* on _Server->get_server_name() ( if sTeam runs smtp on port 25 )
665
* @return the e-mail adress of this user
667
string get_steam_email()
669
return sUserName + "@" + _Server->get_server_name();
673
* set the user name, which is only allowed for the factory.
675
* @param string name - the new name of the user.
678
set_user_name(string name)
680
if ( !_Server->is_factory(CALLER) && stringp(sUserName) )
681
THROW("Calling object not trusted !", E_ACCESS);
683
do_set_attribute(OBJ_NAME, name);
684
require_save(STORE_USER);
690
return copy_value(sUserName);
694
* Get the complete name of the user, that is first and lastname.
695
* Last name attribute is called FULLNAME because of backwards compatibility.
697
* @return the first and last name
702
lname = do_query_attribute(USER_LASTNAME);
703
fname = do_query_attribute(USER_FIRSTNAME);
704
if ( !stringp(fname) )
707
return fname + " " + lname;
712
* restore the use specific data
714
* @param data - the unserialized data of the user
715
* @author Thomas Bopp (astra@upb.de)
716
* @see store_user_data
719
restore_user_data(mixed data, string|void index)
721
if ( CALLER != _Database )
722
THROW("Invalid call to restore_user_data()", E_ACCESS);
724
if ( equal(data, ([ ])) ) {
725
FATAL("Empty load in restore_user_data()");
728
if ( userLoaded && !stringp(index) )
729
steam_error("Loading already loaded user: " + sUserName + ":"+
731
if (zero_type(index)) // no index set restore all
733
if ( !stringp(data->UserName) ) {
734
FATAL("In: " + get_object_id() + ": "+
735
"Cannot restore user with 0-name, already got " +
740
sUserName = data["UserName"];
741
sUserPass = data["UserPassword"];
742
sPlainPass = data["PlainPass"];
743
sTicket = data["UserTicket"];
744
if ( !stringp(sPlainPass) )
746
aoGroups = data["Groups"];
747
iActiveCode = data["Activation"];
748
aTickets = data["Tickets"];
749
oActiveGrp = data["ActiveGroup"];
750
if ( !arrayp(aTickets) )
758
if ( !stringp(data) ) {
759
FATAL("In: " + get_object_id() +
760
" : Cannot restore user with null, previous name " +
766
case "UserPassword" : sUserPass = data; break;
773
case "UserTicket" : sTicket = data; break;
774
case "Groups" : aoGroups = data; break;
775
case "Activation" : iActiveCode = data;
776
case "ActiveGroup" : oActiveGrp = data;
778
if (arrayp(aTickets))
785
//ASSERTINFO(arrayp(aoGroups),"Group is not an array !");
786
if ( !arrayp(aoGroups) )
791
* returns the userdata that will be stored in the Database
793
* @return array containing user data
794
* @author Thomas Bopp (astra@upb.de)
795
* @see restore_user_data
798
store_user_data(string|void index)
800
if ( CALLER != _Database )
801
THROW("Invalid call to store_user_data()", E_ACCESS);
803
if (zero_type(index))
806
"UserName":sUserName,
807
"UserPassword":sUserPass,
808
"PlainPass":sPlainPass,
810
"Activation": iActiveCode,
812
"ActiveGroup": oActiveGrp,
813
"UserTicket" : sTicket,
817
case "UserName": return sUserName;
818
case "UserPassword": return sUserPass;
819
case "PlainPass": return sPlainPass;
820
case "Groups": return aoGroups;
821
case "Activation": return iActiveCode;
822
case "Tickets": return aTickets;
823
case "ActiveGroup": return oActiveGrp;
824
case "UserTicket" : return sTicket;
826
steam_error("Invalid index in store_user_data(%O)\n", index);
832
* the event listener function. The event is automatically send
835
* @param event - the type of event
836
* @param args - the different args for each event
838
* @author Thomas Bopp (astra@upb.de)
841
final int notify_event(int event, mixed ... args)
844
array(object) sockets;
846
DEBUG_EVENT(sUserName+":notify_event("+event+",....)");
847
sockets = values(mSockets);
849
if ( !arrayp(sockets) || sizeof(sockets) == 0 )
852
for ( i = sizeof(sockets) - 1; i >= 0; i-- ) {
853
if ( objectp(sockets[i]) ) {
854
if ( !objectp(sockets[i]->_fd) ) {
855
LOG("Closing connection...\n");
856
close_connection(sockets[i]);
859
if ( sockets[i]->get_client_features() & CLIENT_FEATURES_EVENTS ){
860
LOG("Notifying socket " + i + " about event: " + event);
861
sockets[i]->notify(event, @args);
869
* Get the annotations, eg e-mails of the user.
871
* @return list of annotations
873
array(object) get_annotations()
875
object mb = do_query_attribute(USER_MAILBOX);
877
// import messages from mailbox
878
foreach ( mb->get_inventory(), object importobj) {
879
catch(add_annotation(importobj));
880
importobj->set_acquire(0);
881
importobj->sanction_object(this(), SANCTION_ALL);
883
do_set_attribute(USER_MAILBOX, 0);
885
return ::get_annotations();
889
* Get the mails of a user.
891
* @return array of objects of mail documents
893
array(object) get_mails(void|int from_obj, void|int to_obj)
895
array(object) mails = get_annotations();
896
if ( sizeof(mails) == 0 )
900
to_obj = sizeof(mails);
901
if ( !intp(from_obj) )
903
return mails[from_obj-1..to_obj-1];
908
return this(); // the user functions as mailbox
913
* Mail the user some message by using steams internal mail system.
915
* @param string msg - the message body.
916
* @param string|void subject - an optional subject.
917
* @return the created mail object or 0.
918
* @author <a href="mailto:astra@upb.de">Thomas Bopp</a>)
921
mail(string|object|mapping msg, string|mapping|void subject, void|string sender, void|string mimetype, void|mapping headers)
923
object factory = _Server->get_factory(CLASS_DOCUMENT);
924
object user = this_user();
927
if ( !objectp(user) ) user = _ROOT;
928
if ( mappingp(subject) )
929
subject = subject[do_query_attribute("language")||"english"];
930
if ( !stringp(subject) )
931
subject = "Message from " + user->get_identifier();
932
if ( !stringp(mimetype) )
933
mimetype = "text/html";
935
if ( objectp(msg) ) {
939
message = factory->execute( ([ "name": subject,
940
"mimetype": mimetype,
943
msg = msg[do_query_attribute("language")||"english"];
944
message->set_attribute(OBJ_DESC, subject);
945
message->set_attribute("mailto", this());
946
if ( lower_case(mimetype) == "text/html" ) {
947
// check whether <html> and <body> tags are missing:
948
msg = Messaging.fix_html( msg );
950
message->set_content(msg);
952
do_add_annotation(message);
953
// give message to the user it was send to
954
message->sanction_object(this(), SANCTION_ALL);
955
if ( objectp(this_user()) )
956
message->sanction_object(this_user(), 0); // remove permissions of user
957
message->set_acquire(0); // make sure only the user can read it
959
if ( do_query_attribute(USER_FORWARD_MSG) == 1 ) {
960
string email = do_query_attribute(USER_EMAIL);
961
if ( stringp(email) && strlen(email) > 0 && search(email, "@") > 0)
963
if ( message->query_attribute(MAIL_MIMEHEADERS) )
964
get_module("smtp")->send_mail_mime(do_query_attribute(USER_EMAIL), message);
966
string from = user->get_steam_email();
967
if ( (!stringp(from) || search(from, "@") == -1) )
970
from = "<" + from + ">";
971
if ( stringp(user->query_attribute(USER_LASTNAME)) )
972
from = user->query_attribute(USER_LASTNAME) + " " + from;
973
if ( stringp(user->query_attribute(USER_FIRSTNAME)) )
974
from = user->query_attribute(USER_FIRSTNAME) + " " + from;
976
object msgMessage = Messaging.Message(message, 0, this(), from);
977
if ( mappingp(headers) ) {
978
mapping msgHeaders = message->query_attribute(MAIL_MIMEHEADERS);
979
if ( !mappingp(msgHeaders) ) msgHeaders = ([ ]);
980
msgHeaders |= headers;
981
message->set_attribute( MAIL_MIMEHEADERS, msgHeaders );
984
get_module("forward")->send_message( ({ do_query_attribute(USER_EMAIL) }), msgMessage );
992
* public tell (and private tell) will send a mail to the user
993
* if there is no chat-socket connected.
995
* @param msg - the msg to tell
996
* @author Thomas Bopp (astra@upb.de)
1002
try_event(EVENT_TELL, this_user(), msg);
1004
// no steam client connected - so user would not see message
1006
run_event(EVENT_TELL, this_user(), msg);
1012
* Get the current status of the user object. This goes through all
1013
* connected sockets and checks their features. The result of the function
1014
* are all features of the connected sockets.
1016
* @return features of the connected sockets.
1017
* @author Thomas Bopp (astra@upb.de)
1019
int get_status(void|int stats)
1023
foreach ( indices(mSockets), string sid ) {
1024
object socket = mSockets[sid];
1025
if ( objectp(socket) ) {
1026
status |= CLIENT_STATUS_CONNECTED;
1027
status |= socket->get_client_features();
1030
m_delete(mSockets, sid);
1032
if ( zero_type(stats) )
1034
return status & stats;
1038
* check if a socket with some connection class exists
1040
* @param clientClass - the client class to check
1041
* @return if a socket with the client class is present
1042
* @author Thomas Bopp (astra@upb.de)
1044
bool connected(string clientClass)
1046
foreach ( values(mSockets), object socket ) {
1047
if ( objectp(socket) ) {
1048
if ( socket->get_client_class() == clientClass )
1056
* Set the active group - can only be called by a socket of the user
1058
* @param object grp - the group to be activated.
1059
* @author <a href="mailto:astra@upb.de">Thomas Bopp</a>)
1060
* @see get_active_group
1062
void set_active_group(object grp)
1064
if ( search(aoGroups, grp) == -1 )
1065
THROW("Trying to activate a group the user is not member of !",
1069
require_save(STORE_USER);
1073
* Returns the currently active group of the user
1075
* @return The active group or the steam-user group.
1076
* @see set_active_group
1078
object get_active_group()
1080
if ( !objectp(oActiveGrp) )
1086
* Called when a command is done. Only sockets can call this function.
1088
* @param t - time of the command
1091
void command_done(int t)
1093
if ( !IS_SOCKET(CALLER) )
1094
THROW("Invalid call to command_done() !", E_ACCESS);
1099
* Get the idle time of the user.
1101
* @return the time the user has not send a command
1102
* @author <a href="mailto:astra@upb.de">Thomas Bopp</a>)
1107
return time() - iCommandTime;
1111
* Check if it is possible to insert a given object in the user container.
1113
* @param object obj - the object to insert.
1115
* @author <a href="mailto:astra@upb.de">Thomas Bopp</a>)
1117
static bool check_insert(object obj)
1123
void add_trail(object visit, int max_size)
1125
array aTrail = do_query_attribute("trail");
1126
if ( !arrayp(aTrail) )
1127
aTrail = ({ visit });
1129
if ( visit == aTrail[-1] )
1131
aTrail += ({ visit });
1132
if ( sizeof(aTrail) > max_size )
1133
aTrail = aTrail[sizeof(aTrail)-max_size..];
1135
set_attribute("trail", aTrail);
1138
array(object) get_trail()
1140
return do_query_attribute("trail");
1143
object get_last_trail()
1145
array rooms = do_query_attribute("trail");
1146
if ( arrayp(rooms) )
1151
mixed move(object to)
1157
void confirm_contact()
1159
mapping confirmed = do_query_attribute(USER_CONTACTS_CONFIRMED) || ([ ]);
1160
confirmed[this_user()] = 1;
1161
do_set_attribute(USER_CONTACTS_CONFIRMED, confirmed);
1164
mixed set_attribute(string key, mixed val)
1166
mixed res = ::set_attribute(key, val);
1167
if ( key == USER_ID || key == USER_FIRSTNAME || key == USER_FULLNAME ||
1169
catch(get_module("users")->update_user(this()));
1173
int __get_command_time() { return iCommandTime; }
1174
int get_object_class() { return ::get_object_class() | CLASS_USER; }
1175
final bool is_user() { return true; }
1178
* Get a list of sockets of this user.
1180
* @return the list of sockets of the user
1181
* @author Thomas Bopp (astra@upb.de)
1183
array(object) get_sockets()
1185
return values(mSockets);
1188
string get_ip(string|int sname)
1190
foreach(values(mSockets), object sock) {
1192
if ( stringp(sname) && sock->get_socket_name() == sname )
1193
return sock->get_ip();
1194
else if (sock->get_client_features() & sname )
1195
return sock->get_ip();
1202
return "~"+sUserName+"(#"+get_object_id()+","+get_status()+","+get_ip(1)+