Select Git revision
Per Cederqvist authored
session.c 21.71 KiB
/*
* $Id: session.c,v 0.36 1996/08/03 01:32:12 ceder Exp $
* Copyright (C) 1991, 1992, 1993, 1994, 1996 Lysator Academic Computer Association.
*
* This file is part of the LysKOM server.
*
* LysKOM is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* LysKOM is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* along with LysKOM; see the file COPYING. If not, write to
* Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
* or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
* MA 02139, USA.
*
* Please mail bug reports to bug-lyskom@lysator.liu.se.
*/
/*
* session.c
*
* Session control and miscellaneous.
*/
static char *rcsid = "$Id: session.c,v 0.36 1996/08/03 01:32:12 ceder Exp $";
#include "rcs.h"
USE(rcsid);
#include <stdio.h>
#include <setjmp.h>
#ifdef HAVE_STDARG_H
# include <stdarg.h>
#endif
#include <time.h>
#include <sys/types.h>
#include "ldifftime.h"
#include "s-string.h"
#include "kom-types.h"
#include "services.h"
#include "com.h"
#include "async.h"
#include "connections.h"
#include "internal-connections.h"
#include "manipulate.h"
#include "lyskomd.h"
#include "kom-errno.h"
#include "log.h"
#include "cache.h"
#include "send-async.h"
#include "config.h"
#include "server/smalloc.h"
#include "param.h"
#include "kom-memory.h"
#include "string-malloc.h"
/*
* Create an oldstyle username, user%host.domain@host.domain.
* The RESULT must be initialized to a string (such as EMPTY_STRING).
* A new string will be allocated in RESULT. (The old will be freed).
*/
static void
create_oldstyle_username(String *result,
Connection *connection)
{
if ( s_strcpy(result, connection->username) != OK )
restart_kom("create_oldstyle_username(): strcpy\n");
if ( s_strcat(result, s_fcrea_str((const unsigned char *)"@")) != OK )
restart_kom("create_oldstyle_username: s_strcat\n");
if ( s_strcat(result, connection->hostname) != OK )
restart_kom("prot_a_parse_packet: s_strcat II\n");
}
/*
* This function is called whenever a person leaves a conf,
* i e when he change_conference():s or logout():s.
*/
extern void
leave_conf(void)
{
Membership * mship;
if (active_connection->cwc != 0 )
{
if ((mship = locate_membership( active_connection->cwc, ACT_P ))
!= NULL )
{
time(&mship->last_time_read);
mark_person_as_changed (active_connection->pers_no);
}
else
{
log("ERROR: leave_conf(): Can't find membership of cwc.\n");
}
cached_unlock_conf( active_connection->cwc );
active_connection->cwc = 0;
}
}
/*
* Log in as user pers_no. If ACTPERS is a supervisor of pers_no the login
* will succeed regardless of the passwd. An logout() will automatically
* be performed if ACTPERS is logged in.
*/
extern Success
login_old (Pers_no pers_no,
const String passwd)
{
Person *pers_p;
GET_P_STAT(pers_p, pers_no, FAILURE);
#if 0
if ( !logins_allowed && !pers_p->privileges.wheel)
{
kom_errno = KOM_LOGIN_DISALLOWED;
return FAILURE;
}
#endif
if ( !is_supervisor(pers_no, NULL, ACTPERS, ACT_P)
&& chk_passwd(pers_p->pwd, passwd) == FAILURE )
{
kom_errno = KOM_PWD;
return FAILURE;
}
logout(); /*+++ How many tries are allowed before disconnection? */
active_connection->flags.invisible = FALSE;
ACTPERS = pers_no;
ACT_P = pers_p;
cached_lock_person(pers_no);
pers_p->last_login = time(&active_connection->session_start);
++pers_p->sessions;
s_strcpy(&pers_p->username, active_connection->username);
/* A Person should have separate fields for ident_user and hostname.
But for now, we use the syntax username(ident_user)@hostname. */
if (!s_empty(active_connection->ident_user))
{
if ( s_strcat(&pers_p->username,
s_fcrea_str((const unsigned char *)"(")) != OK )
restart_kom("login: s_strcat (\n");
if ( s_strcat(&pers_p->username, active_connection->ident_user) != OK )
restart_kom("login: s_strcat ident_user\n");
if ( s_strcat(&pers_p->username,
s_fcrea_str((const unsigned char *)")")) != OK )
restart_kom("login: s_strcat )\n");
}
if ( s_strcat(&pers_p->username,
s_fcrea_str((const unsigned char *)"@")) != OK )
restart_kom("prot_a_parse_packet: s_strcat\n");
if ( s_strcat(&pers_p->username, active_connection->hostname) != OK )
restart_kom("prot_a_parse_packet: s_strcat II\n");
mark_person_as_changed( pers_no );
if (param.log_login == TRUE)
{
char *id = s_crea_c_str (pers_p->username);
log ("Login %d %s\n", ACTPERS, id);
string_free (id);
}
async_login(ACTPERS, active_connection->session_no);
return OK;
}
/*
* Log in as user pers_no. If ACTPERS is a supervisor of pers_no the login
* will succeed regardless of the passwd. An logout() will automatically
* be performed if ACTPERS is logged in.
*/
extern Success
login (Pers_no pers_no,
const String passwd,
Bool invisible)
{
Person *pers_p;
Bool same_person;
GET_P_STAT(pers_p, pers_no, FAILURE);
#if 0
if ( !logins_allowed && !pers_p->privileges.wheel)
{
kom_errno = KOM_LOGIN_DISALLOWED;
return FAILURE;
}
#endif
if ( !is_supervisor(pers_no, NULL, ACTPERS, ACT_P)
&& chk_passwd(pers_p->pwd, passwd) == FAILURE )
{
kom_errno = KOM_PWD;
return FAILURE;
}
same_person = (ACTPERS == pers_no) ? TRUE : FALSE;
logout(); /*+++ How many tries are allowed before disconnection? */
if (invisible)
active_connection->flags.invisible = TRUE;
else
active_connection->flags.invisible = FALSE;
/* Don't count this as a new session if the person already was
logged on. The elisp-client version 0.38.1 logs on invisibly
automatically to indicate that the user is inactve. */
if (same_person == FALSE)
++pers_p->sessions;
ACTPERS = pers_no;
ACT_P = pers_p;
cached_lock_person(pers_no);
pers_p->last_login = time(&active_connection->session_start);
s_strcpy(&pers_p->username, active_connection->username);
/* A Person should have separate fields for ident_user and hostname.
But for now, we use the syntax username(ident_user)@hostname. */
if (!s_empty(active_connection->ident_user))
{
if ( s_strcat(&pers_p->username,
s_fcrea_str((const unsigned char *)"(")) != OK )
restart_kom("login: s_strcat (\n");
if ( s_strcat(&pers_p->username, active_connection->ident_user) != OK )
restart_kom("login: s_strcat ident_user\n");
if ( s_strcat(&pers_p->username,
s_fcrea_str((const unsigned char *)")")) != OK )
restart_kom("login: s_strcat )\n");
}
if ( s_strcat(&pers_p->username,
s_fcrea_str((const unsigned char *)"@")) != OK )
restart_kom("prot_a_parse_packet: s_strcat\n");
if ( s_strcat(&pers_p->username, active_connection->hostname) != OK )
restart_kom("prot_a_parse_packet: s_strcat II\n");
mark_person_as_changed( pers_no );
if (param.log_login == TRUE && same_person == FALSE)
{
char *id = s_crea_c_str (pers_p->username);
log ("Login %d %s\n", ACTPERS, id);
string_free (id);
}
if (!active_connection->flags.invisible)
async_login(ACTPERS, active_connection->session_no);
return OK;
}
/*
* Log out. Does not disconnect the client. The person is also automatically
* logged out if the connection is closed. Takes no action if noone is logged
* in on this line. Never fails.
*/
extern Success
logout( void )
{
if ( ACTPERS != 0 ) /* Is he logged in? Then log him out. */
{
if (!active_connection->flags.invisible)
{
async_logout( ACTPERS, active_connection->session_no );
}
leave_conf();
ACT_P->total_time_present +=
ldifftime(time(&ACT_P->last_login),
active_connection->session_start);
cached_unlock_person( ACTPERS );
mark_person_as_changed( ACTPERS );
}
s_clear(&active_connection -> what_am_i_doing);
active_connection->person = NULL;
active_connection->cwc = 0;
active_connection->pers_no = 0;
active_connection->ena_level = 0;
return OK;
}
/*
* Change Conference.
*
* You are not allowed to change to a conference unless you are a
* member in the conference.
*
* You can change_conference(0) to indicate that you are no longer in
* a certain conference.
*
* There are two reasons to use this call:
* 1) Other persons want to know what you are doing.
* 2) When the server checks that you have permission to
* read a certain text it first checks if you current working
* conference is a recipient of the text, since that is a
* cheap test. If that test fails it will have to read in the
* conference structs for the recipients of the text until it
* finds an open one.
*
* In the future the server might read in the conference in advance when
* someone change_conference's to it to allow faster response times.
*/
extern Success
change_conference (Conf_no conference)
{
Who_info info;
init_who_info(&info);
CHK_LOGIN(FAILURE);
if ( conference != 0 )
{
CHK_EXIST(conference, FAILURE);
if ( locate_membership( conference, ACT_P) == NULL )
{
kom_errno = KOM_NOT_MEMBER;
return FAILURE;
}
}
leave_conf();
active_connection->cwc = conference;
if ( conference != 0 )
cached_lock_conf( conference );
if (!active_connection->flags.invisible)
{
info.person = ACTPERS;
info.what_am_i_doing = active_connection->what_am_i_doing;
info.working_conference = conference;
info.session_no = active_connection->session_no;
/* Bug compatibility. */
create_oldstyle_username(&info.username, active_connection);
async_i_am_on(info);
s_clear(&info.username);
}
return OK;
}
/*
* Tell the server what you are doing. This string is sent to anyone
* who does a 'who_is_on()'.
*/
extern Success
change_what_i_am_doing (String what_am_i_doing)
{
Who_info info;
init_who_info(&info);
if ( s_strlen( what_am_i_doing ) > param.what_do_len )
{
s_clear ( &what_am_i_doing );
kom_errno = KOM_LONG_STR;
return FAILURE;
}
s_clear ( &active_connection->what_am_i_doing );
active_connection->what_am_i_doing = what_am_i_doing;
if (!active_connection->flags.invisible)
{
info.person = ACTPERS;
info.what_am_i_doing = active_connection->what_am_i_doing;
create_oldstyle_username(&info.username, active_connection);
info.working_conference = active_connection->cwc;
info.session_no = active_connection->session_no;
async_i_am_on(info);
s_clear(&info.username);
}
return OK;
}
/*
* Get info about what all the currently logged in persons are doing.
*/
extern Success
who_is_on( Who_info_list *result )
{
Connection *cptr;
int no_of_clients = 0;
int i;
Session_no session;
cptr = active_connection;
for ( session = 0; (session = traverse_connections(session)) != 0; )
{
cptr = get_conn_by_number(session);
if ( cptr->person != NULL && cptr->flags.invisible == FALSE )
++no_of_clients;
}
result->no_of_persons = no_of_clients;
result->info = tmp_alloc ( no_of_clients * sizeof(Who_info));
for ( session = 0, i = 0;
i < no_of_clients && (session = traverse_connections(session)) != 0; )
{
cptr = get_conn_by_number(session);
if ( cptr->person != NULL && cptr->flags.invisible == FALSE )
{
init_who_info(&result->info[ i ]);
result->info[ i ].person = cptr->pers_no;
result->info[ i ].what_am_i_doing = cptr->what_am_i_doing;
/* Backward compatibility: Old clients want the username to be
user%host@host. result->info[i].username is free()d in
prot_a_output_who_info_list() in prot-a-output.c. */
create_oldstyle_username(&result->info[i].username, cptr);
result->info[ i ].working_conference = cptr->cwc;
result->info[ i ].session_no = cptr->session_no;
++i;
}
}
if ( i != no_of_clients )
log("who_is_on: i == %d, no_of_clients == %d\n",
i, no_of_clients);
return OK;
}
/*
* Get info about what all the currently logged in persons are doing.
*/
extern Success
who_is_on_ident( Who_info_ident_list *result )
{
Connection *cptr;
int no_of_clients = 0;
int i;
Session_no session;
cptr = active_connection;
for ( session = 0; (session = traverse_connections(session)) != 0; )
{
cptr = get_conn_by_number(session);
if ( cptr->person != NULL && cptr->flags.invisible == FALSE )
++no_of_clients;
}
result->no_of_persons = no_of_clients;
result->info = tmp_alloc ( no_of_clients * sizeof(Who_info_ident));
for ( session = 0, i = 0;
i < no_of_clients && (session = traverse_connections(session)) != 0; )
{
cptr = get_conn_by_number(session);
if ( cptr->person != NULL && cptr->flags.invisible == FALSE )
{
init_who_info_ident(&result->info[i]);
result->info[i].person = cptr->pers_no;
result->info[i].what_am_i_doing = cptr->what_am_i_doing;
result->info[i].username = cptr->username;
result->info[i].hostname = cptr->hostname;
result->info[i].ident_user = cptr->ident_user;
result->info[i].working_conference = cptr->cwc;
result->info[i].session_no = cptr->session_no;
++i;
}
}
if ( i != no_of_clients )
log("who_is_on_ident: i == %d, no_of_clients == %d\n",
i, no_of_clients);
return OK;
}
/*
* Get info about what all the currently logged in persons are doing.
*/
extern Success
who_is_on_dynamic(int want_visible,
int want_invisible,
Dynamic_session_info_list *result)
{
Connection *cptr;
long no_of_clients = 0;
long i;
Session_no session;
int include_it = 0;
time_t tn;
tn = time(NULL);
cptr = active_connection;
for ( session = 0; (session = traverse_connections(session)) != 0; )
{
cptr = get_conn_by_number(session);
if (cptr->person == NULL || cptr->flags.invisible == TRUE)
include_it = want_invisible;
else
include_it = want_visible;
if (include_it != 0)
++no_of_clients;
}
result->no_of_sessions = no_of_clients;
result->sessions = tmp_alloc(no_of_clients * sizeof(Dynamic_session_info));
for ( session = 0, i = 0;
i < no_of_clients && (session = traverse_connections(session)) != 0; )
{
cptr = get_conn_by_number(session);
if (cptr->person == NULL || cptr->flags.invisible == TRUE)
include_it = want_invisible;
else
include_it = want_visible;
if (include_it != 0)
{
init_dynamic_session_info(&result->sessions[i]);
result->sessions[i].session = cptr->session_no;
result->sessions[i].person = cptr->pers_no;
result->sessions[i].working_conference = cptr->cwc;
result->sessions[i].idle_time = ldifftime(tn, cptr->active_time);
result->sessions[i].flags = cptr->flags;
/* Special case: sessions that are not logged in are
always invisible. */
if (cptr->person == NULL)
result->sessions[i].flags.invisible = TRUE;
result->sessions[i].what_am_i_doing = cptr->what_am_i_doing;
++i;
}
}
if ( i != no_of_clients )
log("who_is_on_dynamic: i == %ld, no_of_clients == %ld\n",
i, no_of_clients);
return OK;
}
extern Success
get_session_info (Session_no session_no,
Session_info *result)
{
Connection *cptr;
CHK_LOGIN(FAILURE);
cptr = get_conn_by_number(session_no);
if ( cptr != NULL )
{
init_session_info(result);
result->person = cptr->pers_no;
result->what_am_i_doing = cptr->what_am_i_doing;
result->working_conference = cptr->cwc;
result->session = cptr->session_no;
result->connection_time = cptr->session_start;
result->idle_time = ldifftime(time(NULL), cptr->active_time);
/* Backward compatibility. result->username is free()d in
prot_a_reply() prot-a.c. */
create_oldstyle_username(&result->username, cptr);
return OK;
}
else
{
kom_errno = KOM_UNDEF_SESSION;
return FAILURE;
}
}
extern Success
get_static_session_info (Session_no session_no,
Static_session_info *result)
{
Connection *cptr;
CHK_LOGIN(FAILURE);
cptr = get_conn_by_number(session_no);
if ( cptr != NULL )
{
init_static_session_info(result);
result->username = cptr->username;
result->hostname = cptr->hostname;
result->ident_user = cptr->ident_user;
result->connection_time = cptr->session_start;
return OK;
}
else
{
kom_errno = KOM_UNDEF_SESSION;
return FAILURE;
}
}
extern Success
get_session_info_ident (Session_no session_no,
Session_info_ident *result)
{
Connection *cptr;
CHK_LOGIN(FAILURE);
cptr = get_conn_by_number(session_no);
if ( cptr != NULL )
{
init_session_info_ident(result);
result->person = cptr->pers_no;
result->what_am_i_doing = cptr->what_am_i_doing;
result->working_conference = cptr->cwc;
result->session = cptr->session_no;
result->connection_time = cptr->session_start;
result->idle_time = ldifftime(time(NULL), cptr->active_time);
result->username = cptr->username;
result->hostname = cptr->hostname;
result->ident_user = cptr->ident_user;
return OK;
}
else
{
kom_errno = KOM_UNDEF_SESSION;
return FAILURE;
}
}
extern Success
who_am_i (Session_no *session_no)
{
*session_no = active_connection->session_no;
return OK;
}
extern Success
disconnect (Session_no session_no)
{
Connection *cptr;
if ( session_no != active_connection->session_no )
CHK_LOGIN(FAILURE);
cptr = get_conn_by_number(session_no);
if ( cptr != NULL )
{
if ( is_supervisor(cptr->pers_no, NULL, ACTPERS, ACT_P) == TRUE
|| session_no == active_connection->session_no )
{
add_to_kill_list(cptr);
return OK;
}
else
{
kom_errno = KOM_PERM;
return FAILURE;
}
}
else
{
kom_errno = KOM_UNDEF_SESSION;
return FAILURE;
}
}
/* Get less info */
extern Success
who_is_on_old( Who_info_list_old *result )
{
Connection *cptr;
int no_of_clients = 0;
int i;
Session_no session = 0;
while ( (session = traverse_connections(session)) != 0)
{
cptr = get_conn_by_number(session);
if ( cptr->person != NULL && cptr->flags.invisible == FALSE )
++no_of_clients;
}
result->no_of_persons = no_of_clients;
result->info = tmp_alloc ( no_of_clients * sizeof(Who_info));
for (session = 0, i = 0;
i < no_of_clients && (session = traverse_connections(session)) != 0;)
{
cptr = get_conn_by_number(session);
if ( cptr->person != NULL && cptr->flags.invisible == FALSE )
{
init_who_info_old(&result->info[ i ]);
result->info[ i ].person = cptr->pers_no;
result->info[ i ].what_am_i_doing = cptr->what_am_i_doing;
result->info[ i ].working_conference = cptr->cwc;
++i;
}
}
if ( i != no_of_clients )
log("who_is_on_old: i == %d, no_of_clients == %d\n",
i, no_of_clients);
return OK;
}
/*
* Ask the server what it thinks the time is.
*/
extern Success
get_time( time_t *clock )
{
time(clock);
return OK;
}
/*
* Set ena_level. 0 means don't use any privileges. ///
*/
extern Success
enable (u_char ena_level)
{
CHK_LOGIN(FAILURE);
active_connection->ena_level = ena_level;
return OK;
}
extern Success
set_client_version (const String client_name,
const String client_version)
{
if (s_strlen(client_name) > param.conf_name_len
|| s_strlen(client_version) > param.conf_name_len)
{
kom_errno = KOM_LONG_STR;
return FAILURE;
}
s_strcpy(&active_connection->client_name, client_name);
s_strcpy(&active_connection->client_version, client_version);
return OK;
}
extern Success
get_client_name (Session_no session_no,
String *result)
{
Connection *cptr;
CHK_LOGIN(FAILURE);
cptr = get_conn_by_number(session_no);
if ( cptr != NULL )
{
*result = cptr->client_name;
return OK;
}
else
{
kom_errno = KOM_UNDEF_SESSION;
return FAILURE;
}
}
extern Success
get_client_version (Session_no session_no,
String *result)
{
Connection *cptr;
CHK_LOGIN(FAILURE);
cptr = get_conn_by_number(session_no);
if ( cptr != NULL )
{
*result = cptr->client_version;
return OK;
}
else
{
kom_errno = KOM_UNDEF_SESSION;
return FAILURE;
}
}
extern Success
accept_async(Number_list *num_list)
{
int i;
/*
* Check agains maliciously long arrays
*/
if (num_list->data == NULL && num_list->length > 0)
{
kom_errno = KOM_LONG_ARRAY;
return FAILURE;
}
/*
* Clear the accept list
*/
for (i = 0; i < ay_dummy_last; i++)
{
active_connection->want_async[i] = FALSE;
}
/*
* Enter the new accept list -- silently ignore requests for
* messages that this version of the server doesn't understand.
*/
for (i = 0; i < num_list->length; i++)
{
if (num_list->data[i] >= 0 &&
num_list->data[i] < ay_dummy_last)
active_connection->want_async[num_list->data[i]] = TRUE;
}
return OK;
}
extern Success
query_async(Number_list *result)
{
/* (This static buffer is mentioned in async.h). */
static long temp[ay_dummy_last];
int i;
result->length = 0;
for (i = 0; i < ay_dummy_last; i++)
{
if (active_connection->want_async[i] != FALSE)
{
temp[result->length] = i;
result->length += 1;
}
}
result->data = temp;
return OK;
}
extern Success
user_active(void)
{
time(&active_connection->active_time);
active_connection->flags.user_active_used = TRUE;
return OK;
}