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.7 2010/01/25 19:18:18 astra Exp $
19 inherit "/classes/Container" : __cont;
20 inherit "/base/member" : __member;
21 #include <attributes.h>
32 #include <exception.h>
33 //! this is the user object. It keeps track of connections and membership
35 class User : public Container,member{
44 //#define EVENT_USER_DEBUG
46 #ifdef EVENT_USER_DEBUG
47 #define DEBUG_EVENT(s, args...) werror(s+"\n", args)
49 #define DEBUG_EVENT(s, args...)
52 /* Security relevant functions */
53 private string sUserPass; // the password for the user
54 private string sPlainPass;
55 private string sUserName; // the name of the user
56 private object oActiveGrp; // the active group
57 private int iCommandTime; // when the last command was send
59 private mapping mAttributeAccess = ([ ]); // set readable
61 private string sTicket;
62 private array aTickets;
63 private int iActiveCode;
67 private mapping mSocketEvents;
68 mapping mVirtualConnections;
70 bool userLoaded = false;
72 Thread.Mutex annotationMutex = Thread.Mutex();
74 bool check_swap() { return false; }
75 bool check_upgrade() { return false; }
84 mSocketEvents = ([ ]);
85 mVirtualConnections = ([ ]);
88 /* the user name is a locked attribute */
89 add_data_storage(STORE_USER, store_user_data, restore_user_data, 1);
95 * Constructor of the user object.
110 mAttributeAccess = ([ ]);
117 * Creating a duplicate of the user wont work.
119 * @return throws an error
121 object duplicate(void|mapping vars)
123 THROW("User cannot be duplicated !\n", E_ERROR);
127 * register the object in the database.
129 * @param name - the name of the object
132 void database_registration(string name)
139 * Destructor of the user.
149 THROW("Cannot delete the root user !", E_ACCESS);
151 WARN("DELETING user %O by %O through %O", sUserName, this_user(), CALLER);
152 object mailbox = do_query_attribute(USER_MAILBOX);
153 // delete the mailbox recursively
154 if ( objectp(mailbox) ) {
155 foreach(mailbox->get_inventory(), object inv) {
165 object workroom = do_query_attribute(USER_WORKROOM);
166 if ( objectp(workroom) ) workroom->delete();
169 FATAL( "Failed to delete workroom of \"%s\": %O\n%O\n", sUserName, err[0], err[1] );
171 object bookmarks = do_query_attribute(USER_BOOKMARKROOM);
172 if ( objectp(bookmarks) ) bookmarks->delete();
175 FATAL( "Failed to delete bookmars of \"%s\": %O\n%O\n", sUserName, err[0], err[1] );
177 object calendar = do_query_attribute(USER_CALENDAR);
178 if ( objectp(calendar) ) calendar->delete();
181 FATAL( "Failed to delete calendar of \"%s\": %O\n%O\n", sUserName, err[0], err[1] );
183 __member::delete_object();
184 __cont::delete_object();
190 * Dont update a users name.
192 void update_identifier()
197 * Dont update a users path (its ~username anyway)
204 * Create all the exits to the groups the user is member of.
207 void create_group_exits()
209 object workroom = do_query_attribute(USER_WORKROOM);
210 if ( objectp(workroom) ) {
211 array inv = workroom->get_inventory();
212 array groups = get_groups();
213 mapping mExits = ([ ]);
215 foreach ( groups, object grp ) {
216 if ( !objectp(grp) ) continue;
217 mapping exits = grp->query_attribute(GROUP_EXITS);
218 if ( !mappingp(exits) ) {
219 object workroom = grp->query_attribute(GROUP_WORKROOM);
220 exits = ([ workroom: workroom->get_identifier(), ]);
224 foreach ( indices(mExits), object exit ) {
227 if ( !objectp(exit) )
230 foreach ( inv, object o ) {
231 if ( o->get_object_class() & CLASS_EXIT ) {
232 object exit_to = o->get_link_object();
233 if ( !objectp(exit_to) )
235 if ( exit_to->get_object_id() == exit->get_object_id() )
240 object factory = _Server->get_factory(CLASS_EXIT);
241 object exit = factory->execute(
242 ([ "name": mExits[exit], "exit_to": exit, ]) );
243 exit->move(workroom);
250 string new_session_id()
253 #if constant(Crypto.Random)
254 sid = sprintf("%x", hash(Crypto.Random.random_string(10)));
256 sid = sprintf("%x", hash(random(1000000) + time() + sUserName+sUserPass));
264 * Connect the user object to a steamsocket.
266 * @param obj - the steamsocket to connect to
267 * @return the time of the last login
276 LOG("Connecting "+ get_identifier()+" with "+ obj->describe()+"\n");
278 if ( !IS_SOCKET(CALLER) )
279 THROW("Trying to connect user to non-steamsocket !", E_ACCESS);
281 array aoSocket = values(mSockets);
282 for ( i = sizeof(aoSocket) - 1; i >= 0; i-- ) {
283 if ( aoSocket[i] == obj )
286 int features = obj->get_client_features();
287 int prev_features = get_status();
290 string sid = new_session_id();
291 while ( objectp(mSockets[sid]) || objectp(mVirtualConnections[sid]) )
292 sid = new_session_id();
296 m_delete(mSockets, 0);
297 foreach ( indices(mSockets), sid)
298 if ( !objectp(mSockets[sid]) && !stringp(mSockets[sid]) )
299 m_delete(mSockets, sid);
301 last_login = do_query_attribute(USER_LAST_LOGIN);
302 do_set_attribute(USER_LAST_LOGIN, time());
304 if ( (prev_features & features) != features )
310 * Connect the user object to a virtual connection.
312 * @param obj - the virtual connection to connect to
313 * @return the time of the last login
314 * @see disconnect_virtual
316 int connect_virtual ( object connection ) {
318 LOG( "Connecting (virtual) " + get_identifier() + " with "
319 + connection->describe() + "\n" );
320 if ( has_value( mVirtualConnections, connection ) )
322 int features = connection->get_client_features();
323 int prev_features = get_status();
325 string sid = new_session_id();
326 while ( objectp(mSockets[sid]) || objectp(mVirtualConnections[sid]) )
327 sid = new_session_id();
328 mVirtualConnections[ sid ] = connection;
329 mVirtualConnections[ connection ] = sid;
331 m_delete( mVirtualConnections, 0 );
332 foreach ( indices(mVirtualConnections), sid )
333 if ( !objectp(mVirtualConnections[sid]) &&
334 !stringp(mVirtualConnections[sid]) )
335 m_delete( mVirtualConnections, sid );
337 last_login = do_query_attribute( USER_LAST_LOGIN );
338 do_set_attribute( USER_LAST_LOGIN, time() );
340 if ( (prev_features & features) != features )
345 string get_session_id()
347 if ( !IS_SOCKET(CALLER) )
348 THROW("Trying to steal session by non-socket !", E_ACCESS);
349 foreach( indices(mSockets), string sid) {
350 if ( mSockets[sid] == CALLER )
356 string get_virtual_session_id () {
357 mixed sid = mVirtualConnections[ CALLER ];
358 if ( stringp(sid) ) return sid;
362 bool join_group(object grp)
364 try_event(EVENT_USER_JOIN_GROUP, CALLER, grp);
365 mixed res = ::join_group(grp);
366 require_save(STORE_USER);
367 run_event(EVENT_USER_JOIN_GROUP, CALLER, grp);
371 bool leave_group(object grp)
373 try_event(EVENT_USER_LEAVE_GROUP, CALLER, grp);
374 mixed res = ::leave_group(grp);
375 run_event(EVENT_USER_LEAVE_GROUP, CALLER, grp);
380 * Close the connection to socket and logout.
382 * @param obj - the object to remove from active socket list
387 close_connection(object obj)
389 if ( which_socket(obj) < 0 ) return;
391 try_event(EVENT_LOGOUT, CALLER, obj);
393 foreach(indices(mSockets), string sid)
394 if ( mSockets[sid] == obj )
395 m_delete(mSockets, sid);
397 int cfeatures = obj->get_client_features();
398 int features = get_status();
400 if ( (cfeatures & features) != cfeatures )
402 ASSERTINFO(which_socket(obj) < 0, "Still connected to socket !");
403 DEBUG_EVENT(sUserName+": logout event....");
404 run_event(EVENT_LOGOUT, CALLER, obj);
410 * Close the connection to a virtual connection and logout.
412 * @param obj - the object to remove from active virtual connection list
413 * @see disconnect_virtual
416 void close_virtual_connection ( object connection ) {
417 if ( !has_value( mVirtualConnections, connection ) ) return;
419 try_event( EVENT_LOGOUT, CALLER, connection );
421 m_delete( mVirtualConnections, mVirtualConnections[connection] );
422 m_delete( mVirtualConnections, connection );
424 int cfeatures = connection->get_client_features();
425 int features = get_status();
427 if ( (cfeatures & features) != cfeatures )
429 ASSERTINFO( !has_value( mVirtualConnections, connection),
430 "Still connected to virtual connection !" );
431 DEBUG_EVENT( sUserName+": logout event...." );
432 run_event( EVENT_LOGOUT, CALLER, connection );
438 * Disconnect the CALLER socket from this user object.
444 object socket = CALLER;
447 if ( which_socket(socket) == -1 )
450 if ( arrayp(mSocketEvents[socket]) ) {
451 foreach ( mSocketEvents[socket], mixed event_data )
452 if ( arrayp(event_data) )
453 remove_event(@event_data);
455 // get the remaining status of the user
457 array aoSocket = values(mSockets);
458 foreach ( aoSocket, mixed sock ) {
459 if ( objectp(sock) && sock != socket ) {
460 status |= sock->get_client_features();
463 foreach ( values(mVirtualConnections), object conn ) {
465 status |= conn->get_client_features();
469 // if this is a client which allows movement of the user
470 // then move the user back to its workroom
471 if ( !(status & CLIENT_FEATURES_MOVE) )
473 object workroom = do_query_attribute(USER_WORKROOM);
474 if ( oEnvironment != workroom ) {
475 LOG("Closing down connection to user - moving to workroom !");
476 do_set_attribute(USER_LOGOUT_PLACE, oEnvironment);
477 if ( objectp(workroom) )
482 close_connection(socket);
486 * Disconnect the CALLER virtual connection from this user object.
488 * @see connect_virtual
490 void disconnect_virtual () {
491 object connection = CALLER;
493 if ( !has_value( mVirtualConnections, connection) )
496 // get the remaining status of the user
498 foreach ( values(mSockets), object sock ) {
500 status |= sock->get_client_features();
502 foreach ( values(mVirtualConnections), object conn ) {
503 if ( objectp(conn) && conn != connection )
504 status |= conn->get_client_features();
508 // if this is a client which allows movement of the user
509 // then move the user back to its workroom
510 if ( !(status & CLIENT_FEATURES_MOVE) ) {
511 object workroom = do_query_attribute( USER_WORKROOM );
512 if ( oEnvironment != workroom ) {
513 LOG("Closing down connection to user - moving to workroom !");
514 do_set_attribute( USER_LOGOUT_PLACE, oEnvironment );
515 if ( objectp(workroom) )
520 close_virtual_connection( connection );
524 * find out if the object is one of the connected sockets
526 * @param obj - the object to find out about
527 * @return the position of the socket in the socket array
533 which_socket(object obj)
535 return search(values(mSockets), obj);
541 * Activate the login. Successfull activation code is required to do so!
543 * @param int activation - the activation code
544 * @return true or false
546 bool activate_user(int|void activation)
548 if ( activation == iActiveCode || _ADMIN->is_member(this_user()) ) {
550 require_save(STORE_USER);
557 * Set the activation code for an user - this is done by the factory.
559 * @param int activation - the activation code.
562 void set_activation(int activation)
564 if ( CALLER != _Server->get_factory(CLASS_USER) &&
565 !_ADMIN->is_member(this_user()) )
566 THROW("Invalid call to set_activation !", E_ACCESS);
567 iActiveCode = activation;
568 require_save(STORE_USER);
572 * Find out if the user is inactivated.
574 * @return activation code set or not.
576 bool get_activation()
578 return iActiveCode != 0;
582 * Check if a given password is correct. Users can authenticate with their
583 * password or with temporary tickets. There are one time tickets and
584 * tickets which last for acertain time encoded in the ticket itself.
585 * Authentication will always fail if the user is not activated.
587 * @param pw - the password to check
588 * @param uid - the user object
589 * @return if the password matches or not
591 bool check_user_password(string pw)
593 if ( !stringp(sUserPass) ) {
594 if ( get_module("auth")->allow_zero_passwords() )
602 MESSAGE("Trying to authenticate with inactivated user !");
603 return false; // as long as the login is not activated
606 if ( stringp(sTicket) )
608 if ( verify_crypt_md5(pw, sTicket) ) {
609 sTicket = 0; // ticket used
613 if ( arrayp(aTickets) && sizeof(aTickets) > 0 ) {
614 array tickets = copy_value(aTickets);
615 foreach(tickets, string ticket) {
617 sscanf(ticket, "%*s_%d", t);
619 aTickets -= ({ ticket });
620 require_save(STORE_USER);
622 else if ( pw == ticket )
626 // allow login with any session ID from a connected socket
627 foreach ( indices(mSockets), string sid)
631 if ( !stringp(sUserPass) && !get_module("auth")->allow_zero_passwords() )
634 if ( strlen(sUserPass) > 5 && lower_case(sUserPass[0..4]) == "{sha}" )
635 return sUserPass[5..] == MIME.encode_base64( sha_hash(pw) );
636 if ( strlen(sUserPass) > 6 && lower_case(sUserPass[0..5]) == "{ssha}" ) {
637 string salt = MIME.decode_base64( sUserPass[6..] )[20..]; // last 8 bytes is the salt
638 return sUserPass[6..] == MIME.encode_base64( sha_hash(pw+salt) );
640 if ( strlen(sUserPass) > 7 && lower_case(sUserPass[0..6]) == "{crypt}" )
641 return crypt(pw, sUserPass[7..]);
642 if ( strlen(sUserPass) > 4 && lower_case(sUserPass[0..3]) == "{lm}" ) {
643 return sUserPass[4..] == LanManHash.lanman_hash(pw);
645 if ( strlen(sUserPass) < 3 || sUserPass[0..2] != "$1$" )
646 return crypt(pw, sUserPass); // normal crypt check
648 return verify_crypt_md5(pw, sUserPass);
652 * Get a ticket from the server - authenticate to the server with
653 * this ticket once. Optional parameter t gives time the ticket
656 * @param void|int t - the validity of the ticket
658 * @see check_user_password
660 final string get_ticket(void|int t)
662 THROW("Invalid call to get_ticket() !", E_ACCESS);
664 try_event(EVENT_USER_NEW_TICKET, CALLER, 0);
667 for ( int i = 0; i < 8; i++ )
668 ticket[i] = random(26) + 'a';
669 ticket = crypt(ticket + time());
670 ticket = String.string2hex(ticket);
671 if ( !zero_type(t) ) {
674 aTickets += ({ ticket });
676 aTickets = ({ ticket });
677 run_event(EVENT_USER_NEW_TICKET, CALLER, "********");
678 require_save(STORE_USER);
682 sTicket = make_crypt_md5(ticket);
683 run_event(EVENT_USER_NEW_TICKET, CALLER, "*********");
689 * temporary storage for old password while password is being changed.
690 * to allow places like ldap to pick get the old password, in case they need it
691 * to set the new one.
692 * @return oldpassword
693 * @see check_user_pasword
695 string get_old_password()
697 THROW(sprintf("%O is not permitted to read the old password!", CALLER),
699 //werror("get_old_password: %O\n", this_user());
704 * Set the user password and save an md5 hash of it.
706 * @param pw - the new password for the user
707 * @return if successfull
708 * @see check_user_pasword
711 set_user_password(string pw, int|void crypted, string|void oldpw)
714 try_event(EVENT_USER_CHANGE_PW, CALLER);
718 sUserPass = make_crypt_md5(pw);
719 require_save(STORE_USER);
720 run_event(EVENT_USER_CHANGE_PW, CALLER);
726 set_user_password_plain(string pw, int|void crypted)
728 try_event(EVENT_USER_CHANGE_PW, CALLER);
732 sPlainPass = make_crypt_md5(pw);
733 require_save(STORE_USER);
734 run_event(EVENT_USER_CHANGE_PW, CALLER);
740 * Get the password of the user which should be fine since
741 * we have an md5 hash. This is used to import/export users.
743 * @return the users password.
746 get_user_password(string|void pw)
748 // security problem ? ask for read permissions at least -
749 // probably for admin?
750 return copy_value(sUserPass);
754 * Get the user object of the user which is this object.
757 object get_user_object()
762 * Get the sTeam e-mail adress of this user. Usually its the users name
763 * on _Server->get_server_name() ( if sTeam runs smtp on port 25 )
765 * @return the e-mail adress of this user
767 string get_steam_email()
769 return sUserName + "@" + _Server->get_server_name();
773 * set the user name, which is only allowed for the factory.
775 * @param string name - the new name of the user.
778 set_user_name(string name)
780 if ( !_Server->is_factory(CALLER) && stringp(sUserName) )
781 THROW("Calling object not trusted !", E_ACCESS);
782 if ( !stringp(name) )
783 error("set_user_name(0) is not allowed!");
785 string old_name = sUserName;
788 do_set_attribute(OBJ_NAME, name);
790 object workroom = do_query_attribute(USER_WORKROOM);
791 if ( objectp(workroom) ) {
792 if ( workroom->query_attribute(OBJ_NAME) == old_name+"'s workarea" )
793 workroom->set_attribute( OBJ_NAME, name + "'s workarea" );
795 workroom->update_path();
798 require_save(STORE_USER);
804 return copy_value(sUserName);
808 * Get the complete name of the user, that is first and lastname.
809 * Last name attribute is called FULLNAME because of backwards compatibility.
811 * @return the first and last name
816 lname = do_query_attribute(USER_LASTNAME);
817 fname = do_query_attribute(USER_FIRSTNAME);
818 if ( !stringp(fname) )
821 return fname + " " + lname;
826 * restore the use specific data
828 * @param data - the unserialized data of the user
830 * @see store_user_data
834 restore_user_data(mixed data, string|void index)
836 if ( CALLER != _Database )
837 THROW("Invalid call to restore_user_data()", E_ACCESS);
839 if ( equal(data, ([ ])) ) {
840 FATAL("Empty load in restore_user_data()");
843 if ( userLoaded && !stringp(index) )
844 steam_error("Loading already loaded user: " + sUserName + ":"+
846 if (zero_type(index)) // no index set restore all
848 if ( !stringp(data->UserName) ) {
849 FATAL("In: " + get_object_id() + ": "+
850 "Cannot restore user with 0-name, already got " +
855 sUserName = data["UserName"];
856 sUserPass = data["UserPassword"];
857 sPlainPass = data["PlainPass"];
858 sTicket = data["UserTicket"];
859 if ( !stringp(sPlainPass) )
861 aoGroups = data["Groups"];
862 iActiveCode = data["Activation"];
863 aTickets = data["Tickets"];
864 oActiveGrp = data["ActiveGroup"];
865 mAttributeAccess = data["AttributeAccess"];
866 if ( !arrayp(aTickets) )
868 if (!mappingp(mAttributeAccess))
869 mAttributeAccess = ([ ]);
876 if ( !stringp(data) ) {
877 FATAL("In: " + get_object_id() +
878 " : Cannot restore user with null, previous name " +
884 case "UserPassword" : sUserPass = data; break;
891 case "AttributeAccess": mAttributeAccess = data; break;
892 case "UserTicket" : sTicket = data; break;
893 case "Groups" : aoGroups = data; break;
894 case "Activation" : iActiveCode = data; break;
895 case "ActiveGroup" : oActiveGrp = data; break;
897 if (arrayp(aTickets))
904 //ASSERTINFO(arrayp(aoGroups),"Group is not an array !");
905 if ( !arrayp(aoGroups) )
912 * returns the userdata that will be stored in the Database
914 * @return array containing user data
916 * @see restore_user_data
920 store_user_data(string|void index)
922 if ( CALLER != _Database )
923 THROW("Invalid call to store_user_data()", E_ACCESS);
925 if (zero_type(index))
928 "UserName":sUserName,
929 "UserPassword":sUserPass,
930 "PlainPass":sPlainPass,
932 "Activation": iActiveCode,
934 "ActiveGroup": oActiveGrp,
935 "UserTicket" : sTicket,
939 case "UserName": return sUserName;
940 case "UserPassword": return sUserPass;
941 case "PlainPass": return sPlainPass;
942 case "Groups": return aoGroups;
943 case "Activation": return iActiveCode;
944 case "Tickets": return aTickets;
945 case "ActiveGroup": return oActiveGrp;
946 case "UserTicket" : return sTicket;
947 case "AttributeAccess": return mAttributeAccess;
949 steam_error("Invalid index in store_user_data(%O)\n", index);
957 * the event listener function. The event is automatically send
960 * @param event - the type of event
961 * @param args - the different args for each event
965 final int notify_event(int event, mixed ... args)
970 DEBUG_EVENT(sUserName+":notify_event("+event+",....)");
971 sockets = values(mSockets);
973 if ( !arrayp(sockets) || sizeof(sockets) == 0 )
976 for ( i = sizeof(sockets) - 1; i >= 0; i-- ) {
977 if ( objectp(sockets[i]) ) {
978 if ( !objectp(sockets[i]->_fd) ) {
979 LOG("Closing connection...\n");
980 close_connection(sockets[i]);
983 if ( sockets[i]->get_client_features() & CLIENT_FEATURES_EVENTS ){
984 LOG("Notifying socket " + i + " about event: " + event);
985 sockets[i]->notify(event, @args);
993 bool do_add_annotation(object mail)
997 object lock = annotationMutex->lock();
999 object temp_objects = get_module("temp_objects");
1000 if (objectp(temp_objects)) {
1001 mixed mailtime = _Server->get_config("mail_expire");
1002 if ( !stringp(mailtime) && mailtime > 0 )
1003 temp_objects->add_temp_object(mail, time() + mailtime);
1005 result = ::do_add_annotation(mail);
1018 * Get the annotations, eg e-mails of the user.
1020 * @return list of annotations
1022 array get_annotations()
1024 object mb = do_query_attribute(USER_MAILBOX);
1025 if ( objectp(mb) ) {
1026 // import messages from mailbox
1027 foreach ( mb->get_inventory(), object importobj) {
1028 catch(add_annotation(importobj));
1029 importobj->set_acquire(0);
1031 do_set_attribute(USER_MAILBOX, 0);
1033 return ::get_annotations();
1037 * Get the mails of a user.
1039 * @return array of objects of mail documents
1041 array get_mails(void|int from_obj, void|int to_obj)
1043 array mails = get_annotations();
1044 if ( sizeof(mails) == 0 )
1047 if ( !intp(to_obj) )
1048 to_obj = sizeof(mails);
1049 if ( !intp(from_obj) )
1051 return mails[from_obj-1..to_obj-1];
1056 * Returns the user's emails, optionally filtered by object class,
1057 * attribute values or pagination.
1058 * The description of the filters and sort options can be found in the
1059 * filter_objects_array() function of the "searching" module.
1062 * Return the 10 newest mails whose subjects do not start with "{SPAM}",
1064 * get_mails_filtered(
1066 * ({ "-", "attribute", "OBJ_DESC", "prefix", "{SPAM}" }),
1067 * ({ "+", "class", CLASS_DOCUMENT }),
1070 * ({ ">", "attribute", "OBJ_CREATION_TIME" })
1073 * @param mail_folder (optional) mail folder from which to return the mails
1074 * (if not specified, then the inbox of the user is used)
1075 * @param filters (optional) an array of filters (each an array as described
1076 * in the "searching" module) that specify which objects to return
1077 * @param sort (optional) an array of sort entries (each an array as described
1078 * in the "searching" module) that specify the order of the items
1079 * @param offset (optional) only return the objects starting at (and including)
1081 * @param length (optional) only return a maximum of this many objects
1082 * @return a mapping ([ "objects":({...}), "total":nr, "length":nr,
1083 * "start":nr, "page":nr ]), where the "objects" value is an array of
1084 * objects that match the specified filters, sort order and pagination.
1085 * The other indices contain pagination information ("total" is the total
1086 * number of objects after filtering but before applying "length", "length"
1087 * is the requested number of items to return (as in the parameter list),
1088 * "start" is the start index of the result in the total number of objects,
1089 * and "page" is the page number (starting with 1) of pages with "length"
1090 * objects each, or 0 if invalid).
1092 mapping get_mails_paginated ( object|void mail_folder, array|void filters, array|void sort, int|void offset, int|void length )
1094 return get_module( "searching" )->paginate_object_array(
1095 mail_folder->get_annotations(), filters, sort, offset, length );
1099 * Returns the user's emails, optionally filtered, sorted and limited by
1100 * offset and length. This returns the same as the "objects" index in the
1101 * result of get_mails_paginated() and is here for compatibility reasons and
1102 * ease of use (if you don't need pagination information).
1104 * @see get_mails_paginated
1106 array get_mails_filtered ( object|void mail_folder, array|void filters, array|void sort, int|void offset, int|void length )
1108 return get_mails_paginated( mail_folder, filters, sort, offset, length )["objects"];
1111 object get_mailbox()
1116 * Get (or create if not existing) the sent mail folder of the user.
1118 * @param name optional name for the folder if it is created (default: "sent")
1119 * @return the sent mail folder of the user (if the user has none, it will
1120 * be created and returned)
1122 object create_sent_mail_folder ( void|string name ) {
1126 * Get the sent mail folder of the user.
1128 * @return the sent mail folder of the user, or 0 if the user has none
1130 object get_sent_mail_folder () {
1131 return query_attribute( USER_MAIL_SENT );
1135 * Set a sent mail folder for the user. If the user already has a sent mail
1136 * folder, then it will be turned into a regular mail folder of the user
1137 * and the new folder will be marked as the user's sent mail folder.
1139 * @param folder a mail folder to be set as the new sent mail folder of the
1141 * @return the new sent mail folder of the user
1143 object set_sent_mail_folder ( object folder ) {
1144 object old = query_attribute( USER_MAIL_SENT );
1145 object res = set_attribute( USER_MAIL_SENT, folder );
1146 if ( objectp(old) &&
1147 old->query_attribute( OBJ_TYPE ) == "container_mailbox_sent" )
1148 old->set_attribute( OBJ_TYPE, "container_mailbox" );
1149 if ( !objectp(res) ) return 0;
1150 res->set_attribute( OBJ_TYPE, "container_mailbox_sent" );
1151 if ( objectp(res) && search( get_annotations(), res ) < 0 )
1152 steam_user_error( "Cannot set as sent-mail folder because the object "
1153 + "is no annotation on the user object." );
1158 * Query whether the user is storing sent mails in a sent mail folder.
1159 * If the user has no sent mail folder then mails he sends won't be
1160 * stored, independant of this setting.
1162 * @see get_sent_mail_folder
1164 * @return 1 if the user is storing sent mails, or 0 if not
1166 bool is_storing_sent_mail () {
1167 return query_attribute( USER_MAIL_STORE_SENT );
1171 * Set whether the user shall store sent mails in a sent mail folder.
1172 * If the user has no sent mail folder then mails he sends won't be
1173 * stored, independant of this setting.
1175 * @see create_sent_mail_folder
1176 * @see set_sent_mail_folder
1178 * @param store set to 0 if the user shall not store sent mails, or to 1
1179 * if the user shall store sent mails
1180 * @return 1 if the user is now storing sent mails, or 0 if not
1182 bool set_is_storing_sent_mail ( bool store ) {
1183 return set_attribute( USER_MAIL_STORE_SENT, (int) store );
1188 * Mail the user some message by using steam's internal mail system.
1189 * If the sending user has activated sent mail storage, then a copy of the
1190 * mail will be stored in her sent mail folder.
1192 * @param msg the message body (can be a plaintext or html string, a document
1194 * @param subject an optional subject
1195 * @param sender an optional sender mail address
1196 * @param mimetype optional mime type of the message body
1197 * @param headers optional headers for the mail
1198 * @return the created mail object or 0.
1201 mail(string|object|mapping msg, string|mapping|void subject, void|string sender, void|string mimetype, void|mapping headers)
1203 object mail_obj = do_mail( msg, subject, sender, mimetype, headers );
1204 object sending_user = geteuid() || this_user();
1205 if ( objectp(mail_obj) && objectp(sending_user) &&
1206 sending_user->is_storing_sent_mail() &&
1207 objectp(sending_user->get_sent_mail_folder()) ) {
1208 object mail_copy = mail_obj->duplicate();
1209 if ( objectp(mail_copy) ) {
1210 mail_copy->sanction_object( sending_user, SANCTION_ALL );
1211 get_module( "table:read-documents" )->download_document( 0, mail_copy, UNDEFINED ); // mark as read
1212 foreach ( mail_copy->get_annotations(), object ann )
1213 get_module( "table:read-documents" )->download_document( 0, ann, UNDEFINED ); // mark as read
1214 sending_user->get_sent_mail_folder()->add_annotation( mail_copy );
1221 * Don't call this method, it is only here for User->mail() and Group->do_send_mail() !!!
1224 do_mail(string|object|mapping msg, string|mapping|void subject, void|string sender, void|string mimetype, void|mapping headers)
1226 return 0; // these users don't receive mails
1228 object factory = _Server->get_factory(CLASS_DOCUMENT);
1229 object user = geteuid() || this_user();
1233 if ( !objectp(user) ) user = _ROOT;
1234 if ( mappingp(subject) )
1235 subject = subject[do_query_attribute(USER_LANGUAGE)] || subject["english"];
1236 if ( objectp(msg) && !stringp(subject) )
1237 subject = msg->query_attribute( OBJ_DESC ) || msg->get_identifier();
1238 if ( !stringp(subject) )
1239 subject = "Message from " + user->get_identifier();
1240 if ( !stringp(mimetype) )
1241 mimetype = "text/html";
1243 if ( objectp(msg) ) {
1245 // OBJ_DESC is subject of messages
1246 string desc = msg->query_attribute(OBJ_DESC);
1247 if ( !stringp(desc) || desc == "" )
1248 msg->set_attribute(OBJ_DESC, msg->get_identifier());
1249 if ( !stringp(msg->query_attribute("mailto")) )
1252 message = factory->execute( ([ "name": replace(subject, "/", "_"),
1253 "mimetype": mimetype,
1255 if ( mappingp(msg) )
1256 msg = msg[do_query_attribute(USER_LANGUAGE)] || msg["english"];
1257 message->set_attribute(OBJ_DESC, subject);
1258 if ( lower_case(mimetype) == "text/html" && stringp(msg) ) {
1259 // check whether <html> and <body> tags are missing:
1260 msg = Messaging.fix_html( msg );
1262 message->set_content(msg);
1264 do_add_annotation(message);
1265 // give message to the user it was send to
1266 if ( objectp(this_user()) )
1267 message->sanction_object(this_user(), 0); // remove permissions of user
1268 message->set_acquire(0); // make sure only the user can read it
1270 if ( do_query_attribute(USER_FORWARD_MSG) == 1 ) {
1271 string email = do_query_attribute(USER_EMAIL);
1272 if ( stringp(email) && strlen(email) > 0 && search(email, "@") > 0)
1274 if ( message->query_attribute(MAIL_MIMEHEADERS) )
1275 get_module("smtp")->send_mail_mime(do_query_attribute(USER_EMAIL), message);
1277 string from = sender;
1278 if ( (!stringp(sender) || search(sender, "@") == -1) ) {
1279 from = Messaging.get_quoted_name( user ) +
1280 "<" + user->get_steam_email() + ">";
1282 msgMessage->set_subject( subject );
1283 if ( mappingp(headers) ) {
1284 mapping msgHeaders = message->query_attribute(MAIL_MIMEHEADERS_ADDITIONAL);
1285 if ( !mappingp(msgHeaders) ) msgHeaders = ([ ]);
1286 msgHeaders |= headers;
1287 message->set_attribute( MAIL_MIMEHEADERS_ADDITIONAL, msgHeaders );
1289 get_module("forward")->send_message( ({
1290 get_user_name() }), msgMessage );
1299 * public tell (and private tell) will send a mail to the user
1300 * if there is no chat-socket connected.
1302 * @param msg - the msg to tell
1309 try_event(EVENT_TELL, geteuid() || this_user(), msg);
1311 // no steam client connected - so user would not see message
1313 run_event(EVENT_TELL, geteuid() || this_user(), msg);
1321 * Get the current status of the user object. This goes through all
1322 * connected sockets and checks their features. The result of the function
1323 * are all features of the connected sockets.
1325 * @return features of the connected sockets.
1327 int get_status(void|int stats)
1331 foreach ( indices(mSockets), string sid ) {
1332 object socket = mSockets[sid];
1333 if ( objectp(socket) ) {
1334 status |= CLIENT_STATUS_CONNECTED;
1335 status |= socket->get_client_features();
1338 m_delete(mSockets, sid);
1340 foreach ( indices(mVirtualConnections), mixed sid ) {
1341 if ( !stringp(sid) ) continue;
1342 object connection = mVirtualConnections[sid];
1343 if ( objectp(connection) ) {
1344 status |= CLIENT_STATUS_CONNECTED;
1345 status |= connection->get_client_features();
1348 m_delete( mVirtualConnections, sid );
1350 if ( zero_type(stats) )
1352 return status & stats;
1356 * check if a socket with some connection class exists
1358 * @param clientClass - the client class to check
1359 * @return if a socket with the client class is present
1361 bool connected(string clientClass)
1363 foreach ( values(mSockets), object socket ) {
1364 if ( objectp(socket) ) {
1365 if ( socket->get_client_class() == clientClass )
1369 foreach ( values(mVirtualConnections), object connection ) {
1370 if ( objectp(connection) ) {
1371 if ( connection->get_client_class() == clientClass )
1379 * Set the active group - can only be called by a socket of the user
1381 * @param object grp - the group to be activated.
1382 * @see get_active_group
1384 void set_active_group(object grp)
1386 if ( search(aoGroups, grp) == -1 )
1387 THROW("Trying to activate a group the user is not member of !",
1391 require_save(STORE_USER);
1395 * Returns the currently active group of the user
1397 * @return The active group or the steam-user group.
1398 * @see set_active_group
1400 object get_active_group()
1402 if ( !objectp(oActiveGrp) )
1408 * Called when a command is done. Only sockets can call this function.
1410 * @param t - time of the command
1413 void command_done(int t)
1419 * Get the idle time of the user.
1421 * @return the time the user has not send a command
1426 return time() - iCommandTime;
1430 * Check if it is possible to insert a given object in the user container.
1432 * @param object obj - the object to insert.
1436 bool check_insert(object obj)
1444 void add_trail(object visit, int max_size)
1446 array aTrail = do_query_attribute("trail");
1447 if ( !arrayp(aTrail) )
1448 aTrail = ({ visit });
1450 if ( visit == aTrail[-1] )
1452 aTrail += ({ visit });
1453 if ( sizeof(aTrail) > max_size )
1454 aTrail = aTrail[sizeof(aTrail)-max_size..];
1456 set_attribute("trail", aTrail);
1461 return do_query_attribute("trail");
1464 object get_last_trail()
1466 array rooms = do_query_attribute("trail");
1467 if ( arrayp(rooms) )
1472 array get_attribute_readers(string key)
1474 return mAttributeAccess[key];
1477 void add_attribute_reader(string key, object group)
1479 if ( !arrayp(mAttributeAccess[key]) ) {
1480 mAttributeAccess[key] = ({ group });
1483 mAttributeAccess[key] += ({ group });
1485 require_save(STORE_USER, "AttributeAccess");
1488 void remove_attribute_reader(string key, object group)
1490 if ( arrayp(mAttributeAccess[key]) ) {
1491 mAttributeAccess[key] -= ({ group });
1492 require_save(STORE_USER, "AttributeAccess");
1497 void check_read_attribute(string key, object user)
1499 if (!objectp(user) || user==_ROOT)
1501 if ( mAttributeAccess[key] ) {
1504 array readers = mAttributeAccess[key];
1505 if ( arrayp(readers) && sizeof(readers) > 0 ) {
1506 if ( _ADMIN->is_member(user) )
1508 // check access for this_user(), because of restricted attributes
1509 foreach(readers, object reader) {
1510 if ( reader == _WORLDUSER || reader == user )
1512 if ( reader->get_object_class() & CLASS_GROUP )
1513 if ( reader->is_virtual_member(user) )
1516 THROW(sprintf("Access Denied for %s to read Attribute %s",
1517 user->get_user_name(),
1524 mixed query_attribute(string key)
1526 check_read_attribute(key, geteuid() || this_user());
1527 return ::query_attribute(key);
1532 bool do_set_attribute(string key, mixed|void val)
1534 mixed res = ::do_set_attribute(key, val);
1535 if ( key == USER_ID ||
1536 key == USER_FIRSTNAME ||
1537 key == USER_FULLNAME ||
1544 mixed move(object to)
1550 void confirm_contact()
1552 mapping confirmed = do_query_attribute(USER_CONTACTS_CONFIRMED) || ([ ]);
1553 confirmed[this_user()] = 1;
1554 do_set_attribute(USER_CONTACTS_CONFIRMED, confirmed);
1557 int __get_command_time() { return iCommandTime; }
1558 int get_object_class() { return ::get_object_class() | CLASS_USER; }
1559 final bool is_user() { return true; }
1562 * Get a list of sockets of this user.
1564 * @return the list of sockets of the user
1568 return values(mSockets);
1572 * Get a list of sockets of this user.
1574 * @return the list of sockets of the user
1576 array get_virtual_connections () {
1577 return values(mVirtualConnections);
1580 string get_ip(string|int sname)
1582 foreach(values(mSockets), object sock) {
1585 if ( stringp(sname) && sock->get_socket_name() == sname )
1586 return sock->get_ip();
1587 else if (sock->get_client_features() & sname )
1588 return sock->get_ip();
1590 foreach ( values(mVirtualConnections), mixed conn ) {
1591 if ( !objectp(conn) ) continue;
1592 if ( stringp(sname) && conn->get_connection_name() == sname )
1593 return conn->get_ip();
1594 else if ( conn->get_client_features() & sname )
1595 return conn->get_ip();
1602 return "~"+sUserName+"(#"+get_object_id()+","+get_status()+","+get_ip(1)+