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: users.pike,v 1.2 2010/01/25 19:18:18 astra Exp $
19 inherit "/kernel/secure_mapping.pike";
23 #include <attributes.h>
24 //! This module keeps track of the users in the database. -
25 //! Therefor it maps a nickname to the related sTeam user object.
26 class users : public secure_mapping.pike{
34 private function myfDb;
35 private string mysDbTable;
36 private array test_objects = ({ });
41 [myfDb , mysDbTable] = _Database->connect_db_mapping();
42 if( search(myfDb()->list_tables(), "i_userlookup" ) == -1 ) {
43 MESSAGE("Creating i_userlookup table in users module.");
45 array types = ({ ([ "name": "login", "type":"char(255) not null", ]),
46 ([ "name": "email", "type":"char(255)", ]),
47 ([ "name": "firstname", "type":"char(255)", ]),
48 ([ "name": "lastname", "type":"char(255)", ]),
49 ([ "name": "user_id", "type":"char(255)", ]),
50 ([ "name": "ob_id", "type":"char(255) not null",
53 myfDb()->create_table("i_userlookup", types);
56 myfDb()->big_query("create table i_userlookup "
57 "(login char(255) not null, email char(255), firstname char(255), lastname char(255), user_id char(255), ob_id char(255) not null, UNIQUE(ob_id))");
59 foreach( get_users(), object u) {
64 FATAL("Failed to create userlookup table: %O\n%O", err[0],err[1]);
68 int unregister_user(object u)
70 WARN("users module: unregister from invalid %O", CALLER);
71 THROW("Unauthorized call to users module: unregister()", E_ACCESS);
73 unregister(u->get_user_name());
77 int unregister(string key)
79 int result = ::unregister(key);
80 // delete this user from i_userlookup, too
81 myfDb()->big_query("delete from i_userlookup where login='"+
82 myfDb()->quote_index(key) + "'");
89 void update_user(object u)
92 WARN("users module: update_user from invalid %O", CALLER);
93 THROW("Unauthorized call to users module: update_user!", E_ACCESS);
95 Sql.sql_result res = myfDb()->big_query("select * from i_userlookup where ob_id='"+u->get_object_id()+"'");
98 if ( res->fetch_row() ) {
99 query = sprintf("update i_userlookup set login='%s',email='%s',firstname='%s',lastname='%s',user_id='%s' where ob_id='%s'",
100 myfDb()->quote(u->get_user_name()||""),
101 myfDb()->quote(u->query_attribute(USER_EMAIL)||""),
102 myfDb()->quote(u->query_attribute(USER_FIRSTNAME)||""),
103 myfDb()->quote(u->query_attribute(USER_FULLNAME)||""),
104 myfDb()->quote(u->query_attribute(USER_ID)||""),
105 (string)u->get_object_id());
109 sprintf("insert into i_userlookup values('%s','%s','%s','%s','%s','%s')",
110 myfDb()->quote(u->get_user_name()||""),
111 myfDb()->quote(u->query_attribute(USER_EMAIL)||""),
112 myfDb()->quote(u->query_attribute(USER_FIRSTNAME)||""),
113 myfDb()->quote(u->query_attribute(USER_FULLNAME)||""),
114 myfDb()->quote(u->query_attribute(USER_ID)||""),
115 (string)u->get_object_id());
117 myfDb()->big_query(query);
120 FATAL("Failed to insert user: %O\n%O", err[0], err[1]);
125 int register(string uname, object user)
127 WARN("users module: register from invalid %O", CALLER);
128 THROW("Unauthorized call to users module: register()", E_ACCESS);
130 mixed err = catch(update_user(user));
132 FATAL("While updating user: %O\n%O", err[0], err[1]);
134 return ::register(uname, user);
138 * Lookup a user by his login
140 * @param string login the login of the user to search
141 * @param bool like optional parameter to specify "like" instead of "="
142 * @return array of matching users
144 array lookup_login(string login, void|bool like)
146 return _Persistence->lookup_users( ([ "login":login ]), false,
151 * Lookup a user by his name (firstname, lastname)
153 * @param string firstname the firstname of the user to search
154 * @param string lastname the last name of the user to search
155 * @param bool like optional parameter to specify "like" instead of "="
156 * @return array of matching users
158 array lookup_name(string firstname, string lastname, void|bool like)
160 return _Persistence->lookup_users( ([ "firstname":firstname,
161 "lastname":lastname ]), false,
165 array search_name(string firstname, string lastname, void|bool like)
167 return _Persistence->lookup_users( ([ "firstname":firstname,
168 "lastname":lastname ]), true,
173 * Lookup a user by his lastname
175 * @param string lastname the last name of the user to search
176 * @return array of matching users
178 array lookup_lastname(string lastname, void|bool like)
180 return _Persistence->lookup_users( ([ "lastname":lastname ]), false,
185 * Lookup a user by his first name
187 * @param string firstname the first name of the user to search
188 * @return array of matching users
190 array lookup_firstname(string firstname, void|bool like)
192 return _Persistence->lookup_users( ([ "firstname":firstname ]), false,
197 * Search users by a term
199 * @param string term the search term
200 * @return array of matching users
202 array search_users(string term, void|bool like)
204 return _Persistence->lookup_users( ([ "firstname":term, "lastname":term,
205 "login":term, "email":term ]), true,
209 int check_read_attribute(object user, string attribute)
211 mixed err = catch(user->check_read_attribute(attribute,
212 geteuid()||this_user()));
214 if ( sizeof(err) == 3)
216 FATAL("Error while checking for readable attribute %O of %O\n%O:%O",
217 attribute, user, err[0], err[1]);
223 array check_user(int id, void|string|array attribute)
225 // check if user data are still ok ?!
226 object user = find_object(id);
228 if ( arrayp(attribute) ) {
229 foreach(attribute, string a)
230 if ( check_read_attribute(user, a) == 0 )
234 if ( check_read_attribute(user, attribute) == 0 )
244 * Lookup a user by id
246 * @param string id the id to lookup
247 * @return array of matching users
249 array lookup_id(string id)
251 Sql.sql_result res = myfDb()->big_query("select ob_id from i_userlookup where user_id='"+id+"'");
258 array result = ({ });
259 while ( row = res->fetch_row() ) {
260 result += check_user((int)row[0], USER_ID);
267 * Lookup a user by e-mail
269 * @param string e-mail the e-mail to lookup
270 * @param bool like exact search flag
271 * @return array of matching users
273 array lookup_email(string email, void|bool like)
275 if (!stringp(email) || strlen(email) == 0)
278 return _Persistence->lookup_users( ([ "email":email ]), false,
283 * Lookup a user by login
285 * @param string index the login to lookup
286 * @return matching user
288 object lookup(string index)
290 return _Persistence->lookup_user(index);
293 string rename_user(object user, string new_name)
295 if ( CALLER != get_factory(CLASS_USER) )
296 steam_error("Invalid call to rename_user() !");
297 object other = get_value(new_name);
298 if ( objectp(other) && other != user )
299 steam_error("There is already a user with the name '"+new_name+"' !");
300 string name = user->get_user_name();
302 set_value(new_name, user);
303 _Persistence->user_renamed( user, name, new_name );
309 array index = index();
312 foreach ( index, string idx ) {
313 object obj = get_value(idx);
320 object get_user(string name )
322 return get_value(name);
326 * Drops a user from any caches, so that it will receive fresh data on the
327 * next lookup. This can be useful if you know that user data has changed in
328 * one of the persistence layers and you want the user object to update its
329 * data accordingly (before the regular update after the cache times out).
331 * @param identifier the identifier (user's login name) of the user that shall
332 * be dropped from cache
333 * @return 1 if the user was dropped from the object cache,
334 * 0 if it wasn't found in the object cache (regardless of this return value,
335 * the user might have been dropped from any persistence layer caches)
337 int uncache_user ( string identifier )
339 return _Persistence->uncache_user( identifier );
342 int check_updates () {
343 int result = 0; // set to 1 if an update needs a server restart
346 string sent_mail_update = "activated sent-mail for all users";
347 if ( !_Server->get_update( sent_mail_update ) ) {
348 array users = get_users();
349 int nr_users = sizeof(users);
350 MESSAGE( "Activating sent-mail for all %d users...", nr_users );
351 string update_log = "=Sent-Mails=\n\n" + ctime(time())
352 + sprintf( "\nCreating sent-mail folders and activating sent-mail "
353 + "storage for all %d users.\n\n", nr_users );
356 foreach ( users, object user ) {
357 if ( user == _GUEST ) continue; // skip guest user
358 string username = user->get_identifier();
359 update_log += "* " + username + " : folder ";
361 mixed err = catch( res = user->create_sent_mail_folder() );
363 update_log += "'''could not be created''' (" + err[0] + "), storage ";
364 werror("Could not create sent-mail folder for user "+username+"\n");
367 else if ( !objectp(res) ) {
368 update_log += "'''could not be created''', storage ";
369 werror("Could not create sent-mail folder for user "+username+"\n");
373 update_log += "created, storage ";
374 err = catch( res = user->set_is_storing_sent_mail( 1 ) );
376 update_log += "'''could not be activated''' (" + err[0] + ")\n";
377 werror("Could not activate sent-mail storage for user "+username+"\n");
380 else if ( !user->is_storing_sent_mail() ) {
381 update_log += "'''could not be activated'''\n";
382 werror("Could not activate sent-mail storage for user "+username+"\n");
386 update_log += "activated\n";
388 if ( (count % 100) == 0 )
389 MESSAGE("Activated sent-mails for %d of %d users...", count, nr_users);
391 update_log += "\n" + ctime(time()) + "\n";
392 update_log += "\n" + failed + " errors occurred.\n";
393 object update = get_factory(CLASS_DOCUMENT)->execute(
394 ([ "name":sent_mail_update, "mimetype":"text/wiki" ]) );
395 if ( objectp(update) ) {
396 update->set_content( update_log );
397 _Server->add_update( update );
398 MESSAGE( "Finished activating sent-mail for users." );
401 MESSAGE( "Failed to store sent-mail update." );
402 werror( "Failed to store sent-mail update.\n" );
409 string get_identifier() { return "users"; }
410 string get_table_name() { return "users"; }
413 object factory = get_factory(CLASS_USER);
419 uname = "test_" + ((string)time()) + "_" + ((string)uname_count++);
420 user = USER( uname );
421 } while ( objectp(user) );
422 user = factory->execute( (["name": uname, "pw":"test", "email": "xyz",]) );
423 if ( objectp(user) ) test_objects += ({ user });
426 array users = lookup_email("xyz");
427 Test.test("User searching 1", search(users, user) >= 0);
428 Test.test("User lookup", lookup(uname) == user);
430 user->set_attribute(USER_FIRSTNAME, "xyzz");
431 users = lookup_firstname("xyzz");
432 Test.test("User searching 2", search(users, user) >= 0);
435 void test_cleanup () {
436 if ( arrayp(test_objects) ) {
437 foreach ( test_objects, object obj )
438 catch( obj->delete() );