~ubuntu-branches/ubuntu/wily/steam/wily

« back to all changes in this revision

Viewing changes to server/classes/User.pike

  • Committer: Package Import Robot
  • Author(s): Felix Geyer
  • Date: 2013-10-29 19:51:18 UTC
  • mfrom: (1.1.4) (0.1.4 trusty-proposed)
  • Revision ID: package-import@ubuntu.com-20131029195118-b9bxciz5hwx5z459
Tags: 1:1.0.0.39-2ubuntu1
Add an epoch to the version number as there was an unrelated steam package
in the archive with a higher version number.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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: User.pike,v 1.8 2006/09/29 07:18:19 astra Exp $
18
 
 */
19
 
 
20
 
constant cvs_version="$Id: User.pike,v 1.8 2006/09/29 07:18:19 astra Exp $";
21
 
 
22
 
 
23
 
//! this is the user object. It keeps track of connections and membership
24
 
//! in groups.
25
 
 
26
 
inherit "/classes/Container" : __cont;
27
 
inherit "/base/member" :     __member;
28
 
 
29
 
#include <attributes.h>
30
 
#include <assert.h>
31
 
#include <macros.h>
32
 
#include <events.h>
33
 
#include <coal.h>
34
 
#include <classes.h>
35
 
#include <database.h>
36
 
#include <access.h>
37
 
#include <types.h>
38
 
#include <client.h>
39
 
#include <config.h>
40
 
 
41
 
//#define EVENT_USER_DEBUG
42
 
 
43
 
#ifdef EVENT_USER_DEBUG
44
 
#define DEBUG_EVENT(s, args...) werror(s+"\n", args)
45
 
#else
46
 
#define DEBUG_EVENT(s, args...)
47
 
#endif
48
 
 
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
55
 
 
56
 
private static string         sTicket;
57
 
private static array(string) aTickets;
58
 
private static int        iActiveCode;
59
 
 
60
 
        static mapping          mSockets;
61
 
        static mapping       mMoveEvents;
62
 
private static mapping     mSocketEvents;
63
 
 
64
 
bool userLoaded = false;
65
 
 
66
 
object this() { return __cont::this(); }
67
 
bool   check_swap() { return false; }
68
 
bool   check_upgrade() { return false; }
69
 
 
70
 
static void 
71
 
init()
72
 
{
73
 
    ::init();
74
 
    ::init_member();
75
 
    mSockets      = ([ ]);
76
 
    mSocketEvents = ([ ]);
77
 
    sTicket       = 0;
78
 
    
79
 
    /* the user name is a locked attribute */
80
 
    add_data_storage(STORE_USER, store_user_data, restore_user_data, 1);
81
 
}
82
 
 
83
 
/**
84
 
 * Constructor of the user object.
85
 
 *
86
 
 * @author <a href="mailto:astra@upb.de">Thomas Bopp</a>) 
87
 
 */
88
 
static void
89
 
create_object()
90
 
{
91
 
    ::create_object();
92
 
 
93
 
    sUserName  = "noone";
94
 
    sUserPass  = "steam";
95
 
    sPlainPass = 0;
96
 
 
97
 
    sTicket     = 0;
98
 
    aTickets    = ({ });
99
 
    iActiveCode = 0;
100
 
}
101
 
 
102
 
/**
103
 
 * Creating a duplicate of the user wont work.
104
 
 *  
105
 
 * @return throws an error
106
 
 * @author <a href="mailto:astra@upb.de">Thomas Bopp</a>) 
107
 
 */
108
 
object duplicate()
109
 
{
110
 
    THROW("User cannot be duplicated !\n", E_ERROR);
111
 
}
112
 
 
113
 
/**
114
 
 * register the object in the database.
115
 
 *  
116
 
 * @param name - the name of the object
117
 
 * @author Thomas Bopp (astra@upb.de) 
118
 
 */
119
 
static void database_registration(string name)
120
 
{
121
 
    MODULE_USERS->register(name, this());
122
 
}
123
 
 
124
 
/**
125
 
 * Destructor of the user.
126
 
 *  
127
 
 * @author Thomas Bopp (astra@upb.de) 
128
 
 * @see create
129
 
 */
130
 
static void
131
 
delete_object()
132
 
{
133
 
    mixed err;
134
 
 
135
 
    if ( this() == MODULE_USERS->lookup("root") )
136
 
        THROW("Cannot delete the root user !", E_ACCESS);
137
 
 
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) {
143
 
            err = catch {
144
 
                inv->delete();
145
 
            };
146
 
        }
147
 
        err = catch {
148
 
            mailbox->delete();
149
 
        };
150
 
    }
151
 
    err = catch {
152
 
      object workroom = do_query_attribute(USER_WORKROOM);
153
 
      if ( objectp(workroom) ) workroom->delete();
154
 
    };
155
 
    if ( err != 0 )
156
 
      FATAL( "Failed to delete workroom of \"%s\": %O\n%O\n", sUserName, err[0], err[1] );
157
 
    err = catch {
158
 
      object bookmarks = do_query_attribute(USER_BOOKMARKROOM);
159
 
      if ( objectp(bookmarks) ) bookmarks->delete();
160
 
    };
161
 
    if ( err != 0 )
162
 
      FATAL( "Failed to delete bookmars of \"%s\": %O\n%O\n", sUserName, err[0], err[1] );
163
 
    err = catch {
164
 
      object calendar = do_query_attribute(USER_CALENDAR);
165
 
      if ( objectp(calendar) ) calendar->delete();
166
 
    };
167
 
    if ( err != 0 )
168
 
      FATAL( "Failed to delete calendar of \"%s\": %O\n%O\n", sUserName, err[0], err[1] );
169
 
    
170
 
    __member::delete_object();
171
 
    __cont::delete_object();
172
 
}
173
 
 
174
 
/**
175
 
 * Dont update a users name.
176
 
 */
177
 
void update_identifier()
178
 
{
179
 
}
180
 
 
181
 
/**
182
 
 * Create all the exits to the groups the user is member of.
183
 
 *  
184
 
 * @author <a href="mailto:astra@upb.de">Thomas Bopp</a>) 
185
 
 */
186
 
void create_group_exits()
187
 
{
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 = ([ ]);
193
 
        
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(), ]);
200
 
            }
201
 
            mExits += exits;
202
 
        }
203
 
        foreach ( indices(mExits), object exit ) {
204
 
            bool       found_exit;
205
 
 
206
 
            if ( !objectp(exit) ) 
207
 
                continue;
208
 
            found_exit = false;
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) )
213
 
                       continue;
214
 
                    if ( exit_to->get_object_id() == exit->get_object_id() )
215
 
                        found_exit = true;
216
 
                }
217
 
            }
218
 
            if ( !found_exit ) {
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);
224
 
            }
225
 
        }
226
 
    }
227
 
}
228
 
 
229
 
static string new_session_id()
230
 
{
231
 
    string sid;
232
 
#if constant(Crypto.Random) 
233
 
    sid = sprintf("%x", hash(Crypto.Random.random_string(10)));
234
 
#else
235
 
    sid = sprintf("%x", hash(random(1000000) + time() + sUserName+sUserPass));
236
 
#endif
237
 
    return sid;
238
 
}
239
 
 
240
 
/**
241
 
 * Connect the user object to a steamsocket.
242
 
 *  
243
 
 * @param obj - the steamsocket to connect to
244
 
 * @return the time of the last login
245
 
 * @author Thomas Bopp 
246
 
 * @see disconnect
247
 
 * @see which_socket
248
 
 */
249
 
int
250
 
connect(object obj)
251
 
{
252
 
    int last_login, i;
253
 
    
254
 
    LOG("Connecting "+ get_identifier()+" with "+ obj->describe()+"\n");
255
 
 
256
 
    if ( !IS_SOCKET(CALLER) )
257
 
        THROW("Trying to connect user to non-steamsocket !", E_ACCESS);
258
 
    
259
 
    array aoSocket = values(mSockets);
260
 
    for ( i = sizeof(aoSocket) - 1; i >= 0; i-- ) {
261
 
        if ( aoSocket[i] == obj )
262
 
            return 0;
263
 
    }
264
 
    int features = obj->get_client_features();
265
 
    int prev_features = get_status();
266
 
    try_event(EVENT_LOGIN, this(), features, prev_features);
267
 
 
268
 
 
269
 
    string sid = new_session_id();
270
 
    while ( objectp(mSockets[sid]) )
271
 
        sid = new_session_id();
272
 
    mSockets[sid] = obj;
273
 
    mSockets[obj] = sid;
274
 
 
275
 
    m_delete(mSockets, 0);
276
 
    foreach ( indices(mSockets), sid) 
277
 
        if ( !objectp(mSockets[sid]) && !stringp(mSockets[sid]) ) 
278
 
            m_delete(mSockets, sid);
279
 
 
280
 
    last_login = do_query_attribute(USER_LAST_LOGIN);
281
 
    do_set_attribute(USER_LAST_LOGIN, time());
282
 
    
283
 
    if ( (prev_features & features) != features ) 
284
 
        run_event(EVENT_STATUS_CHANGED, this(), features, prev_features);
285
 
 
286
 
    run_event(EVENT_LOGIN, this(), features, prev_features);
287
 
 
288
 
    if ( objectp(oEnvironment) ) 
289
 
        oEnvironment->enter_system(this(), features, prev_features);
290
 
 
291
 
    return last_login;
292
 
}
293
 
 
294
 
string get_session_id() 
295
 
{
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 )
300
 
            return sid;
301
 
    }
302
 
    return "0";
303
 
}
304
 
 
305
 
bool join_group(object grp)
306
 
{
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);
311
 
  return res;
312
 
}
313
 
 
314
 
bool leave_group(object grp)
315
 
{
316
 
  try_event(EVENT_USER_LEAVE_GROUP, CALLER, grp);
317
 
  mixed res = ::leave_group(grp);
318
 
  run_event(EVENT_USER_LEAVE_GROUP, CALLER, grp);
319
 
  return res;
320
 
}
321
 
 
322
 
/**
323
 
 * Close the connection to socket and logout.
324
 
 *  
325
 
 * @param obj - the object to remove from active socket list
326
 
 * @author Thomas Bopp (astra@upb.de) 
327
 
 * @see disconnect
328
 
 */
329
 
static void
330
 
close_connection(object obj)
331
 
{
332
 
    if ( which_socket(obj) < 0 ) return;
333
 
    
334
 
    try_event(EVENT_LOGOUT, CALLER, obj);
335
 
 
336
 
    foreach(indices(mSockets), string sid)
337
 
        if ( mSockets[sid] == obj )
338
 
            m_delete(mSockets, sid);
339
 
 
340
 
    int cfeatures = obj->get_client_features();
341
 
    int features = get_status();
342
 
 
343
 
    if ( (cfeatures & features) != cfeatures ) 
344
 
        run_event(EVENT_STATUS_CHANGED, this(), cfeatures, features);
345
 
 
346
 
    ASSERTINFO(which_socket(obj) < 0, "Still connected to socket !");
347
 
    DEBUG_EVENT(sUserName+": logout event....");
348
 
    run_event(EVENT_LOGOUT, CALLER, obj);
349
 
}
350
 
 
351
 
/**
352
 
 * Disconnect the CALLER socket from this user object.
353
 
 *  
354
 
 * @author Thomas Bopp (astra@upb.de) 
355
 
 * @see connect
356
 
 */
357
 
void disconnect()
358
 
{
359
 
    object socket = CALLER;
360
 
    int             status;
361
 
 
362
 
    if ( which_socket(socket) == -1 )
363
 
      return; 
364
 
    
365
 
    if ( arrayp(mSocketEvents[socket]) ) {
366
 
        foreach ( mSocketEvents[socket], mixed event_data )
367
 
            if ( arrayp(event_data) )
368
 
                remove_event(@event_data);
369
 
    }
370
 
    // get the remaining status of the user
371
 
    status = 0;
372
 
    array aoSocket = values(mSockets);
373
 
    foreach ( aoSocket, object sock ) {
374
 
        if ( objectp(sock) && sock != socket ) {
375
 
            status |= sock->get_client_features();
376
 
        }
377
 
    }
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());
384
 
    
385
 
 
386
 
#ifdef MOVE_WORKROOM
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) ) 
390
 
    {
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) )
396
 
                move(workroom);
397
 
        }
398
 
    }
399
 
#endif
400
 
    close_connection(socket);
401
 
}
402
 
 
403
 
/**
404
 
 * find out if the object is one of the connected sockets
405
 
 *  
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) 
409
 
 * @see connect
410
 
 * @see disconnect
411
 
 */
412
 
static int 
413
 
which_socket(object obj)
414
 
{
415
 
    return search(values(mSockets), obj);
416
 
}
417
 
 
418
 
/**
419
 
 * Activate the login. Successfull activation code is required to do so!
420
 
 *  
421
 
 * @param int activation - the activation code
422
 
 * @return true or false
423
 
 * @author <a href="mailto:astra@upb.de">Thomas Bopp</a>) 
424
 
 */
425
 
bool activate_user(int|void activation)
426
 
{
427
 
    if ( activation == iActiveCode || _ADMIN->is_member(this_user()) ) {
428
 
        iActiveCode = 0;
429
 
        require_save(STORE_USER);
430
 
        return true;
431
 
    }
432
 
    return false;
433
 
}
434
 
 
435
 
/**
436
 
 * Set the activation code for an user - this is done by the factory.
437
 
 *  
438
 
 * @param int activation - the activation code.
439
 
 * @author <a href="mailto:astra@upb.de">Thomas Bopp</a>) 
440
 
 * @see activate_user
441
 
 */
442
 
void set_activation(int activation)
443
 
{
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);
449
 
}
450
 
 
451
 
/**
452
 
 * Find out if the user is inactivated.
453
 
 *  
454
 
 * @return activation code set or not.
455
 
 */
456
 
bool get_activation()
457
 
{
458
 
    return iActiveCode != 0;
459
 
}
460
 
 
461
 
/**
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.
466
 
 *  
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) 
471
 
 */
472
 
bool check_user_password(string pw)
473
 
{
474
 
    if ( !stringp(sUserPass) )
475
 
        return true;
476
 
 
477
 
    if ( !stringp(pw) ) 
478
 
        return false;
479
 
 
480
 
    if ( iActiveCode ) {
481
 
        MESSAGE("Trying to authenticate with inactivated user !");
482
 
        return false; // as long as the login is not activated
483
 
    }
484
 
    
485
 
    if ( stringp(sTicket) ) 
486
 
    {
487
 
        if ( verify_crypt_md5(pw, sTicket) ) {
488
 
            sTicket = 0; // ticket used
489
 
            return true;
490
 
        }
491
 
    }
492
 
    if ( arrayp(aTickets) && sizeof(aTickets) > 0 ) {
493
 
        array tickets = copy_value(aTickets);
494
 
        foreach(tickets, string ticket) {
495
 
            int t;
496
 
            sscanf(ticket, "%*s_%d", t);
497
 
            if ( t < time() ) {
498
 
                aTickets -= ({ ticket });
499
 
                require_save(STORE_USER);
500
 
            }
501
 
            else if ( pw == ticket )
502
 
                return true;
503
 
        }
504
 
    }
505
 
    // allow login with any session ID from a connected socket
506
 
    foreach ( indices(mSockets), string sid)
507
 
        if ( pw == sid )
508
 
            return true;
509
 
 
510
 
    if ( strlen(sUserPass) > 12 && lower_case(sUserPass[0..12]) == "{deactivated}" )
511
 
      return false;  // user account has been deactivated
512
 
 
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) );
518
 
    }
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);
523
 
    }
524
 
    if ( strlen(sUserPass) < 3 || sUserPass[0..2] != "$1$" ) 
525
 
      return crypt(pw, sUserPass); // normal crypt check
526
 
 
527
 
    return verify_crypt_md5(pw, sUserPass);
528
 
}
529
 
 
530
 
/**
531
 
 * Transform a string in some other characters.
532
 
 *  
533
 
 * @param string what the string to convert.
534
 
 * @return converted string.
535
 
 * @author <a href="mailto:astra@upb.de">Thomas Bopp</a>) 
536
 
 */
537
 
private static string tohex(string what)
538
 
{
539
 
    int i = 0;
540
 
    for ( int q = 0; q < strlen(what); q++ ) {
541
 
        i <<= 8;
542
 
        i |= what[strlen(what)-1-q];
543
 
    }
544
 
    return sprintf("%x", i);
545
 
}
546
 
 
547
 
/**
548
 
 * Get a ticket from the server - authenticate to the server with
549
 
 * this ticket once. Optional parameter t gives time the ticket
550
 
 * is valid.
551
 
 *  
552
 
 * @param void|int t - the validity of the ticket
553
 
 * @return the ticket
554
 
 * @see check_user_password
555
 
 */
556
 
final string get_ticket(void|int t)
557
 
{
558
 
    if ( !IS_SOCKET(CALLER) && !_SECURITY->access_write(0, this(), CALLER) )
559
 
        THROW("Invalid call to get_ticket() !", E_ACCESS);
560
 
 
561
 
    try_event(EVENT_USER_NEW_TICKET, CALLER, 0);
562
 
 
563
 
    string ticket = "        ";
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) ) {
569
 
        ticket += "_" + t;
570
 
        if(arrayp(aTickets))
571
 
          aTickets += ({ ticket });
572
 
        else
573
 
          aTickets = ({ ticket });
574
 
        run_event(EVENT_USER_NEW_TICKET, CALLER, "********");
575
 
        require_save(STORE_USER);
576
 
        return ticket;
577
 
    }
578
 
        
579
 
    sTicket = make_crypt_md5(ticket);
580
 
    sTicket = replace(sTicket, ({ "/", "\\" }), ({ "X", "Y" }));
581
 
    run_event(EVENT_USER_NEW_TICKET, CALLER, sTicket);
582
 
    return ticket;
583
 
}
584
 
 
585
 
static string oldpassword;
586
 
/**
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
592
 
 */
593
 
string get_old_password()
594
 
{
595
 
    if ( CALLER->this() != _Server->get_module("ldap"))
596
 
        THROW(sprintf("%O is not permitted to read the old password!", CALLER),
597
 
        E_ACCESS);
598
 
    //werror("get_old_password: %O\n", this_user());
599
 
    return oldpassword;
600
 
}
601
 
 
602
 
/**
603
 
 * Set the user password and save an md5 hash of it.
604
 
 *  
605
 
 * @param pw - the new password for the user
606
 
 * @return if successfull
607
 
 * @see check_user_pasword
608
 
 */
609
 
bool
610
 
set_user_password(string pw, int|void crypted, string|void oldpw)
611
 
{
612
 
    oldpassword=oldpw;
613
 
    try_event(EVENT_USER_CHANGE_PW, CALLER);
614
 
    if(crypted)
615
 
      sUserPass = pw; 
616
 
    else
617
 
      sUserPass = make_crypt_md5(pw);
618
 
    require_save(STORE_USER);
619
 
    run_event(EVENT_USER_CHANGE_PW, CALLER);
620
 
    oldpassword=0;
621
 
    return true;
622
 
}
623
 
 
624
 
bool
625
 
set_user_password_plain(string pw, int|void crypted)
626
 
{
627
 
    try_event(EVENT_USER_CHANGE_PW, CALLER);
628
 
    if(crypted)
629
 
      sPlainPass = pw; 
630
 
    else
631
 
      sPlainPass = make_crypt_md5(pw);
632
 
    require_save(STORE_USER);
633
 
    run_event(EVENT_USER_CHANGE_PW, CALLER);
634
 
    return true;
635
 
}
636
 
 
637
 
 
638
 
/**
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.
641
 
 *  
642
 
 * @return the users password.
643
 
 */
644
 
string
645
 
get_user_password(string|void pw)
646
 
{
647
 
    // security problem ? ask for read permissions at least - 
648
 
    // probably for admin?
649
 
    return copy_value(sUserPass);
650
 
}
651
 
 
652
 
/**
653
 
 * Get the user object of the user which is this object.
654
 
 *  
655
 
 */
656
 
object get_user_object()
657
 
{
658
 
  return this();
659
 
}
660
 
 
661
 
/**
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 )
664
 
 *  
665
 
 * @return the e-mail adress of this user
666
 
 */
667
 
string get_steam_email()
668
 
{
669
 
    return sUserName  + "@" + _Server->get_server_name();
670
 
}
671
 
 
672
 
/**
673
 
 * set the user name, which is only allowed for the factory.
674
 
 *  
675
 
 * @param string name - the new name of the user.
676
 
 */
677
 
void 
678
 
set_user_name(string name)
679
 
{
680
 
    if ( !_Server->is_factory(CALLER) && stringp(sUserName) )
681
 
        THROW("Calling object not trusted !", E_ACCESS);
682
 
    sUserName = name;
683
 
    do_set_attribute(OBJ_NAME, name);
684
 
    require_save(STORE_USER);
685
 
}
686
 
 
687
 
string
688
 
get_user_name()
689
 
{
690
 
  return copy_value(sUserName);
691
 
}
692
 
 
693
 
/**
694
 
 * Get the complete name of the user, that is first and lastname.
695
 
 * Last name attribute is called FULLNAME because of backwards compatibility.
696
 
 *  
697
 
 * @return the first and last name
698
 
 */
699
 
string get_name()
700
 
{
701
 
  string lname, fname;
702
 
  lname = do_query_attribute(USER_LASTNAME);
703
 
  fname = do_query_attribute(USER_FIRSTNAME);
704
 
  if ( !stringp(fname) )
705
 
    return lname;
706
 
  
707
 
  return fname + " " + lname;
708
 
}
709
 
 
710
 
 
711
 
/**
712
 
 * restore the use specific data
713
 
 *  
714
 
 * @param data - the unserialized data of the user
715
 
 * @author Thomas Bopp (astra@upb.de) 
716
 
 * @see store_user_data
717
 
 */
718
 
void 
719
 
restore_user_data(mixed data, string|void index)
720
 
{
721
 
    if ( CALLER != _Database ) 
722
 
      THROW("Invalid call to restore_user_data()", E_ACCESS);
723
 
 
724
 
    if ( equal(data, ([ ])) ) {
725
 
      FATAL("Empty load in restore_user_data()");
726
 
      return;
727
 
    }
728
 
    if ( userLoaded && !stringp(index) ) 
729
 
      steam_error("Loading already loaded user: " + sUserName + ":"+
730
 
                  get_object_id());
731
 
    if (zero_type(index)) // no index set restore all 
732
 
    {
733
 
        if ( !stringp(data->UserName) ) {
734
 
            FATAL("In: " + get_object_id() + ": "+ 
735
 
                  "Cannot restore user with 0-name, already got " +
736
 
                  sUserName);
737
 
            return;
738
 
        }
739
 
 
740
 
        sUserName    = data["UserName"];
741
 
        sUserPass    = data["UserPassword"];
742
 
        sPlainPass   = data["PlainPass"];
743
 
        sTicket      = data["UserTicket"];
744
 
        if ( !stringp(sPlainPass) )
745
 
            sPlainPass = "";
746
 
        aoGroups     = data["Groups"];
747
 
        iActiveCode  = data["Activation"];
748
 
        aTickets     = data["Tickets"];
749
 
        oActiveGrp = data["ActiveGroup"];
750
 
        if ( !arrayp(aTickets) )
751
 
            aTickets = ({ });
752
 
        userLoaded = true;
753
 
    }
754
 
    else
755
 
    {
756
 
        switch(index) {
757
 
          case "UserName" :
758
 
              if ( !stringp(data) ) {
759
 
                  FATAL("In: " + get_object_id() + 
760
 
                        " : Cannot restore user with null, previous name " +
761
 
                        sUserName);
762
 
                  return;
763
 
              }
764
 
            sUserName = data; 
765
 
            break;
766
 
          case "UserPassword" : sUserPass = data; break;
767
 
          case "PlainPass" :
768
 
              if (stringp(data))
769
 
                  sPlainPass = data;
770
 
              else
771
 
                  sPlainPass = "";
772
 
              break;
773
 
          case "UserTicket" : sTicket = data; break;
774
 
          case "Groups" : aoGroups = data; break;
775
 
          case "Activation" : iActiveCode = data;
776
 
          case "ActiveGroup" : oActiveGrp = data;
777
 
          case "Tickets" :
778
 
              if (arrayp(aTickets))
779
 
                  aTickets = data;
780
 
              else
781
 
                  aTickets = ({});
782
 
              break;
783
 
        }
784
 
    }
785
 
    //ASSERTINFO(arrayp(aoGroups),"Group is not an array !");
786
 
    if ( !arrayp(aoGroups) )
787
 
      aoGroups = ({ });
788
 
}
789
 
 
790
 
/**
791
 
 * returns the userdata that will be stored in the Database
792
 
 *  
793
 
 * @return array containing user data
794
 
 * @author Thomas Bopp (astra@upb.de) 
795
 
 * @see restore_user_data
796
 
 */
797
 
final mixed
798
 
store_user_data(string|void index)
799
 
{
800
 
    if ( CALLER != _Database )
801
 
      THROW("Invalid call to store_user_data()", E_ACCESS);
802
 
 
803
 
    if (zero_type(index))
804
 
    {
805
 
        return ([ 
806
 
            "UserName":sUserName,
807
 
            "UserPassword":sUserPass, 
808
 
            "PlainPass":sPlainPass,
809
 
            "Groups": aoGroups,
810
 
            "Activation": iActiveCode,
811
 
            "Tickets": aTickets,
812
 
            "ActiveGroup": oActiveGrp,
813
 
            "UserTicket" : sTicket,
814
 
        ]);
815
 
    } else {
816
 
        switch(index) {
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;
825
 
        default:
826
 
          steam_error("Invalid index in store_user_data(%O)\n", index);
827
 
        }            
828
 
    }
829
 
}
830
 
 
831
 
/**
832
 
 * the event listener function. The event is automatically send
833
 
 * to the client.
834
 
 *  
835
 
 * @param event - the type of event
836
 
 * @param args - the different args for each event
837
 
 * @return ok
838
 
 * @author Thomas Bopp (astra@upb.de) 
839
 
 * @see listen_event
840
 
 */
841
 
final int notify_event(int event, mixed ... args)
842
 
{
843
 
    int                 i;
844
 
    array(object) sockets;
845
 
 
846
 
    DEBUG_EVENT(sUserName+":notify_event("+event+",....)");
847
 
    sockets = values(mSockets);
848
 
    
849
 
    if ( !arrayp(sockets) || sizeof(sockets) == 0 )
850
 
        return EVENT_OK;
851
 
        
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]);
857
 
                continue;
858
 
            }
859
 
            if ( sockets[i]->get_client_features() & CLIENT_FEATURES_EVENTS ){
860
 
                LOG("Notifying socket " + i + " about event: " + event);
861
 
                sockets[i]->notify(event, @args);
862
 
            }
863
 
        }
864
 
    }
865
 
    return EVENT_OK;
866
 
}
867
 
 
868
 
/**
869
 
 * Get the annotations, eg e-mails of the user.
870
 
 *  
871
 
 * @return list of annotations
872
 
 */
873
 
array(object) get_annotations()
874
 
{
875
 
    object mb = do_query_attribute(USER_MAILBOX);
876
 
    if ( objectp(mb) ) {
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);
882
 
        }
883
 
        do_set_attribute(USER_MAILBOX, 0);
884
 
    }
885
 
    return ::get_annotations();
886
 
}
887
 
 
888
 
/**
889
 
 * Get the mails of a user.
890
 
 *  
891
 
 * @return array of objects of mail documents
892
 
 */
893
 
array(object) get_mails(void|int from_obj, void|int to_obj)
894
 
{
895
 
  array(object) mails = get_annotations();
896
 
  if ( sizeof(mails) == 0 )
897
 
    return mails;
898
 
  
899
 
  if ( !intp(to_obj) )
900
 
    to_obj = sizeof(mails);
901
 
  if ( !intp(from_obj) )
902
 
    from_obj = 1;
903
 
  return mails[from_obj-1..to_obj-1];
904
 
}
905
 
 
906
 
object get_mailbox()
907
 
{
908
 
    return this(); // the user functions as mailbox
909
 
}
910
 
 
911
 
 
912
 
/**
913
 
 * Mail the user some message by using steams internal mail system.
914
 
 *  
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>) 
919
 
 */
920
 
final object 
921
 
mail(string|object|mapping msg, string|mapping|void subject, void|string sender, void|string mimetype, void|mapping headers)
922
 
{
923
 
    object factory = _Server->get_factory(CLASS_DOCUMENT);
924
 
    object user = this_user();
925
 
    object message;
926
 
 
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";
934
 
 
935
 
    if ( objectp(msg) ) {
936
 
      message = msg;
937
 
    }
938
 
    else {
939
 
      message = factory->execute( ([ "name": subject,
940
 
                                     "mimetype": mimetype, 
941
 
                                    ]) );
942
 
      if ( mappingp(msg) ) 
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 );
949
 
      }
950
 
      message->set_content(msg);
951
 
    }
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
958
 
 
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)
962
 
        {
963
 
          if ( message->query_attribute(MAIL_MIMEHEADERS) )
964
 
            get_module("smtp")->send_mail_mime(do_query_attribute(USER_EMAIL), message);
965
 
          else {
966
 
            string from = user->get_steam_email();
967
 
            if ( (!stringp(from) || search(from, "@") == -1) )
968
 
              from = sender;
969
 
            else {
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;
975
 
            }
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 );
982
 
            }
983
 
            
984
 
            get_module("forward")->send_message( ({ do_query_attribute(USER_EMAIL) }), msgMessage );
985
 
          }
986
 
        }
987
 
    }
988
 
    return message;
989
 
}
990
 
 
991
 
/**
992
 
 * public tell (and private tell) will send a mail to the user
993
 
 * if there is no chat-socket connected.
994
 
 *  
995
 
 * @param msg - the msg to tell
996
 
 * @author Thomas Bopp (astra@upb.de) 
997
 
 * @see private_tell
998
 
 */
999
 
final bool 
1000
 
message(string msg)
1001
 
{
1002
 
    try_event(EVENT_TELL, this_user(), msg);
1003
 
 
1004
 
    // no steam client connected - so user would not see message
1005
 
 
1006
 
    run_event(EVENT_TELL, this_user(), msg);
1007
 
    return true;
1008
 
}
1009
 
 
1010
 
 
1011
 
/**
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.
1015
 
 *  
1016
 
 * @return features of the connected sockets.
1017
 
 * @author Thomas Bopp (astra@upb.de) 
1018
 
 */
1019
 
int get_status(void|int stats)
1020
 
{
1021
 
    int status                 = 0;
1022
 
 
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();
1028
 
        }
1029
 
        else
1030
 
            m_delete(mSockets, sid);
1031
 
    }
1032
 
    if ( zero_type(stats) )
1033
 
        return status;
1034
 
    return status & stats;
1035
 
}
1036
 
 
1037
 
/**
1038
 
 * check if a socket with some connection class exists
1039
 
 *  
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) 
1043
 
 */
1044
 
bool connected(string clientClass) 
1045
 
{
1046
 
    foreach ( values(mSockets), object socket ) {
1047
 
        if ( objectp(socket) ) {
1048
 
            if ( socket->get_client_class() == clientClass )
1049
 
                return true;
1050
 
        }
1051
 
    }
1052
 
    return false;
1053
 
}
1054
 
 
1055
 
/**
1056
 
 * Set the active group - can only be called by a socket of the user
1057
 
 *  
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
1061
 
 */
1062
 
void set_active_group(object grp) 
1063
 
{
1064
 
    if ( search(aoGroups, grp) == -1 ) 
1065
 
        THROW("Trying to activate a group the user is not member of !",
1066
 
              E_ACCESS);
1067
 
 
1068
 
    oActiveGrp = grp;
1069
 
    require_save(STORE_USER);
1070
 
}
1071
 
 
1072
 
/**
1073
 
 * Returns the currently active group of the user
1074
 
 *  
1075
 
 * @return The active group or the steam-user group.
1076
 
 * @see set_active_group
1077
 
 */
1078
 
object get_active_group()
1079
 
{
1080
 
    if ( !objectp(oActiveGrp) )
1081
 
        return _STEAMUSER;
1082
 
    return oActiveGrp;
1083
 
}
1084
 
 
1085
 
/**
1086
 
 * Called when a command is done. Only sockets can call this function.
1087
 
 *  
1088
 
 * @param t - time of the command
1089
 
 * @see get_idle
1090
 
 */
1091
 
void command_done(int t)
1092
 
{
1093
 
    if ( !IS_SOCKET(CALLER) )
1094
 
        THROW("Invalid call to command_done() !", E_ACCESS);
1095
 
    iCommandTime = t;
1096
 
}
1097
 
 
1098
 
/**
1099
 
 * Get the idle time of the user.
1100
 
 *  
1101
 
 * @return the time the user has not send a command
1102
 
 * @author <a href="mailto:astra@upb.de">Thomas Bopp</a>) 
1103
 
 * @see command_done
1104
 
 */
1105
 
int get_idle()
1106
 
{
1107
 
    return time() - iCommandTime;
1108
 
}
1109
 
 
1110
 
/**
1111
 
 * Check if it is possible to insert a given object in the user container.
1112
 
 *  
1113
 
 * @param object obj - the object to insert.
1114
 
 * @return true
1115
 
 * @author <a href="mailto:astra@upb.de">Thomas Bopp</a>) 
1116
 
 */
1117
 
static bool check_insert(object obj)
1118
 
{
1119
 
    return true;
1120
 
}
1121
 
 
1122
 
 
1123
 
void add_trail(object visit, int max_size)
1124
 
{
1125
 
    array aTrail = do_query_attribute("trail");
1126
 
    if ( !arrayp(aTrail) ) 
1127
 
        aTrail = ({ visit });
1128
 
    else {
1129
 
        if ( visit == aTrail[-1] )
1130
 
            return;
1131
 
        aTrail += ({ visit });
1132
 
        if ( sizeof(aTrail) > max_size )
1133
 
            aTrail = aTrail[sizeof(aTrail)-max_size..];
1134
 
    }
1135
 
    set_attribute("trail", aTrail);
1136
 
}
1137
 
 
1138
 
array(object) get_trail()
1139
 
{
1140
 
    return do_query_attribute("trail");
1141
 
}
1142
 
 
1143
 
object get_last_trail()
1144
 
{
1145
 
    array rooms =  do_query_attribute("trail");
1146
 
    if ( arrayp(rooms) )
1147
 
        return rooms[-1];
1148
 
    return 0;
1149
 
}
1150
 
 
1151
 
mixed move(object to)
1152
 
{
1153
 
    add_trail(to, 20);
1154
 
    return ::move(to);
1155
 
}
1156
 
 
1157
 
void confirm_contact()
1158
 
{
1159
 
  mapping confirmed = do_query_attribute(USER_CONTACTS_CONFIRMED) || ([ ]);
1160
 
  confirmed[this_user()] = 1;
1161
 
  do_set_attribute(USER_CONTACTS_CONFIRMED, confirmed);
1162
 
}
1163
 
 
1164
 
mixed set_attribute(string key, mixed val) 
1165
 
{
1166
 
  mixed res = ::set_attribute(key, val);
1167
 
  if ( key == USER_ID || key == USER_FIRSTNAME || key == USER_FULLNAME ||
1168
 
       key == USER_EMAIL )
1169
 
    catch(get_module("users")->update_user(this()));
1170
 
  return res;
1171
 
}
1172
 
 
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; }
1176
 
 
1177
 
/**
1178
 
 * Get a list of sockets of this user.
1179
 
 *  
1180
 
 * @return the list of sockets of the user
1181
 
 * @author Thomas Bopp (astra@upb.de) 
1182
 
 */
1183
 
array(object) get_sockets()
1184
 
{
1185
 
    return values(mSockets);
1186
 
}
1187
 
 
1188
 
string get_ip(string|int sname) 
1189
 
{
1190
 
    foreach(values(mSockets), object sock) {
1191
 
        
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();
1196
 
    }
1197
 
    return "0.0.0.0";
1198
 
}
1199
 
        
1200
 
string describe() 
1201
 
{
1202
 
    return "~"+sUserName+"(#"+get_object_id()+","+get_status()+","+get_ip(1)+
1203
 
        ")";
1204
 
}
1205
 
 
1206
 
 
1207