diff --git a/src/server/admin.c b/src/server/admin.c new file mode 100644 index 0000000000000000000000000000000000000000..287149dc7e9b924c5b9dbf355dcb2dfbacd174b2 --- /dev/null +++ b/src/server/admin.c @@ -0,0 +1,157 @@ +/* + * admin.c + * + * Administrative calls. + */ +#include <stdlib.h> +#include "lyskomd.h" +#include <kom-types.h> +#include <services.h> +#include "manipulate.h" +#include "smalloc.h" +#include "cache.h" +#include "log.h" +#include "admin.h" +#include <kom-errno.h> +#include "com.h" +#include "connections.h" +#include "send-async.h" +#include "config.h" + +Info kom_info = +{ +#include "version.incl" + , /* version */ + 1, /* conf_pres_conf */ + 2, /* pers_pres_conf */ + 3, /* motd_conf */ + 4, /* kom_news_conf */ + 0 /* motd_of_lyskom */ +}; + +/* + * Return info about this server. This can (and should) be done + * before logging in. modt_of_lyskom should be displayed before + * prompting for username if it isn't 0. + */ +extern Success +get_info( Info *result ) +{ + *result = kom_info; + return OK; +} + +/* /// */ +extern Success +set_motd_of_lyskom (Text_no motd) +{ + Text_stat *old_motd = NULL; + Text_stat *new_motd = NULL; + + CHK_LOGIN(FAILURE); + + if ( !ENA(admin, 1) ) + { + kom_errno = KOM_PERM; + return FAILURE; + } + + /* Check that the new motd exists before deleting the old*/ + + if ( motd != 0 ) + { + GET_T_STAT(new_motd, motd, FAILURE); + if ( new_motd->no_of_marks >= MAX_MARKS_TEXT ) + { + log( + "LIMIT: set_motd_of_lyskom(): New motd very marked.\n"); + return FAILURE; + } + } + + /* Unmark the previous motd if it exists. */ + + if ( kom_info.motd_of_lyskom != 0 + && (old_motd = cached_get_text_stat(kom_info.motd_of_lyskom)) != NULL) + { + if ( old_motd->no_of_marks > 0 ) + { + --old_motd->no_of_marks; + mark_text_as_changed( kom_info.motd_of_lyskom ); + } + else + { + log("ERROR: do_set_motd(): Old motd not marked\n"); + } + } + + /* Mark the new motd */ + + if ( motd != 0 ) + { + ++new_motd->no_of_marks; + mark_text_as_changed( motd ); + } + + kom_info.motd_of_lyskom = motd; + + return OK; +} + + + + +/* + * Force all clients to read a message. + * Sends an asynchronous message. This is obsoleted by send_message. + */ +extern Success +broadcast (String message) +{ + CHK_LOGIN(FAILURE); + + async_broadcast(ACTPERS, message); + return OK; +} + +/* + * Send a message + */ +extern Success +send_message (Pers_no recipient, + const String message) +{ + CHK_LOGIN(FAILURE); + + return async_send_message(recipient, ACTPERS, message); +} + + + +/* + * Make LysKOM sync its files. + */ +extern Success +sync (void) +{ + cache_sync_all(); + dump_statistics(); + return OK; +} + +/* + * Close LysKOM. exit_val is (currently) not used. The database is synced. + */ +extern Success +shutdown (int exit_val) +{ + CHK_LOGIN(FAILURE); + if ( !ENA(admin, 1) ) + { + kom_errno = KOM_PERM; + return FAILURE; + } + + go_and_die = TRUE; + return OK; +} diff --git a/src/server/admin.h b/src/server/admin.h new file mode 100644 index 0000000000000000000000000000000000000000..ba4d0cd1a6bdc0cc6a3da1b4258d1bee209a07b4 --- /dev/null +++ b/src/server/admin.h @@ -0,0 +1,4 @@ +/* + * admin.h + */ +extern Info kom_info; diff --git a/src/server/cache.h b/src/server/cache.h new file mode 100644 index 0000000000000000000000000000000000000000..2e3f9c175bd98e08902c0c0d5346ab659adf8706 --- /dev/null +++ b/src/server/cache.h @@ -0,0 +1,212 @@ +/* + * This file contains the cached data that the server stores. + * .h file created by ceder 1990-04-18 + */ + +/* cache_sync_all only returns when everything is written to disk. */ +extern void +cache_sync_all(void); + + +/* First, some things which I want here. /ceder */ + +extern struct matching_info *match_table; + +extern void +cached_lock_conf(Conf_no conf_no); + +extern Success +init_cache(void); + +extern void +cached_unlock_conf(Conf_no conf); + +extern void +cached_lock_person(Pers_no pers); + +extern void +cached_unlock_person(Pers_no pers); + +extern Success +cached_delete_person(Pers_no pres); + +extern Success +cached_delete_text(Text_no text); + +extern Success +build_matching_info(void); + +extern void /* Write out everything. */ +cache_sync(void); + +/* S}d{r ja. Tack f|r mig. /ceder */ + +#define MAX_CACHED_PERSONS 5 /* To be increased after debugging */ +#define MAX_CACHED_WHATEVER /* To be added */ + + +/* + * Name caching routines + */ + +/* + * change_name changes the cached conference name. It is only called when + * a conference name is changed or a conference is deleted. + * + * cached_change_name(foo, EMPTY_STRING); should be used when a conference + * is deleted. (Eller ska det vara annorlunda? S} g|r jag nu... /ceder) + */ +extern void +cached_change_name(Conf_no name_num, + String new_name ); + + +/* add_name adds a name to the list of cached conference names. It is only + * used when a new conference is created. + */ +extern void +cached_add_name( char * new_name ); + +/* load all the conference names from disk and store them in memory */ +extern void +cached_load_names( void ); + +#if 0 +/* get one name (I want this call! /ceder) */ +/* I no longer want it. */ +extern String +cached_get_name( Conf_no conf_no ); /* Returns NULL if the conference */ + /* doesn't exist. */ +#endif + +extern Bool +cached_conf_exists( Conf_no conf_no ); + +/* + * Calls for the cacheing of conf_type: + * cached_get_conf_type() returns the type, and + * cached_set_conf_type() sets the type. + */ + +/* Get conference type */ +/* Undefined result if the conf doesn't exist. */ + +extern Conf_type +cached_get_conf_type (Conf_no conf_no); + +/* + * Set the type. This call is used whenever a conference is created or the + * conf_type is changed. + */ + +extern void +cached_set_conf_type (Conf_no conf_no, + Conf_type type); + +/* + * Get/set garb_nice for a certain conference. + */ +extern Garb_nice +cached_get_garb_nice (Conf_no conf_no); + +extern void +cached_set_garb_nice (Conf_no conf_no, + Garb_nice nice); + +extern Local_text_no +cached_get_highest_local_no (Conf_no conf_no); + + + +/* + * Person caching routines + */ +#if 0 +extern Person * +cached_read_person( Pers_no pers_no ); +#endif +/* + * Various function calls to tell the cache that something is changed. + */ + +void +mark_person_as_changed(Pers_no pers_no); + + +void +mark_conference_as_changed(Conf_no conf_no); + + +void +mark_text_as_changed(Text_no text_no); + + + + +/* + * Person-related calls + */ + +extern Success +cached_create_person( Pers_no person ); + + + +extern Person * +cached_get_person_stat( Pers_no person ); /* Returns NULL if person + doesn't exist */ + + + +/* + * Conference-related calls + */ +extern Conf_no +cached_create_conf( String name ); + + +extern Success +cached_delete_conf( Conf_no conf ); + +extern Success +cached_lookup_name(const String name, + Conf_list_old *result); + +extern Conference * +cached_get_conf_stat( Conf_no conf_no ); + + +/* + * Calls to handle texts + */ + +extern String +cached_get_text( Text_no text );/* The string should be free'd by the caller */ + +extern Text_stat * +cached_get_text_stat( Text_no text ); + + +extern Text_no +cached_create_text( String message); + +/* + * traverse_text can be used to cycle through all existing texts. 0 will + * be returned once in the "cycle". + */ +extern Text_no +traverse_text(Text_no seed); + +/* + * traverse_person can be used to cycle through all existing persons. 0 will + * be returned once in the "cycle". + */ +extern Pers_no +traverse_person(Pers_no seed); + +/* + * traverse_conference can be used to cycle through all existing conferences. + * 0 will be returned once in the "cycle". + */ +extern Conf_no +traverse_conference(Conf_no seed); diff --git a/src/server/conference.c b/src/server/conference.c new file mode 100644 index 0000000000000000000000000000000000000000..2bc4985d9d9eb7c7b1eec3e550b50cf5e48b69e0 --- /dev/null +++ b/src/server/conference.c @@ -0,0 +1,916 @@ +/* + * conference.c + * + * All atomic calls that deals with conferences. + */ + +#include <time.h> +#include <ctype.h> +#include <stdlib.h> +#include "lyskomd.h" +#include <kom-types.h> +#include <services.h> +#include "manipulate.h" +#include <kom-errno.h> +#include "smalloc.h" +#include "cache.h" +#include "log.h" +#include <config.h> +#include "com.h" +#include "connections.h" +#include "send-async.h" +#include <debug.h> +#include "parser.h" +#include "internal-connections.h" + +/* From ram-cache.h. This will go away when we use regexp-matching. This is + * only temporary. +++*** + */ +extern Conf_no *conf_table; + +/* + * Static functions + */ + +/* + * Delete a conference. Delete all references to this conf. + */ + +static void +do_delete_conf (Conf_no conf_no, + Conference * conf_c) /* Not NULL */ +{ + int i; + + if ( do_set_presentation(conf_no, conf_c, 0) != OK ) + { + log("ERROR: do_delete_conf() - couldn't unmark presentation.\n"); + } + + if ( do_set_etc_motd(conf_no, conf_c, 0) != OK ) + { + log("ERROR: do_delete_conf() - couldn't unmark motd.\n"); + } + + /* Delete all members */ + /* Note that because of the way do_sub_member is written it is important */ + /* that the loop is executed this way. */ + for ( i = conf_c->members.no_of_members - 1; i >= 0; i-- ) + { + do_sub_member( conf_no, conf_c, conf_c->members.members + i, + conf_c->members.members[ i ].member, NULL, NULL); + } + + sfree( conf_c->members.members ); + conf_c->members.members = NULL; + + /* Delete the name */ + + s_clear( &conf_c->name ); + cached_change_name( conf_no, EMPTY_STRING); + + /* texts */ + + /* + * The texts are not deleted at once, but since they now have no recipient + * they will not live long. + */ + + /* ??? Note that there will still be a recipient in the texts, but it + will not exist in reality. This might cause problems. A do_sub_recpt() + should maybe be used. */ + /* Yes, do! Also, send out asynchronous message about deleted conf. */ + + sfree( conf_c->texts.texts ); + conf_c->texts.texts = NULL; + + cached_delete_conf( conf_no ); + + return; +} + +/* + * Functions that are exported to the server. + */ + +/* + * Return TRUE if NAME is not already used. + */ + +Bool +legal_name( String name ) +{ + if (name.len == 0 || name.len > CONF_NAME_LEN ) + { + return FALSE; + } + + while( name.len-- ) + { + if ( !isascii( *name.string ) || !isprint( *name.string ) + /*???|| *name.string == '"'*/ ) + { + return FALSE; + } + name.string++; + } + + return TRUE; +} + +/* + * Return TRUE if name is unique, or if the only match is conf_no. Use 0 + * as conf_no if it should not be allowed to be changed. + */ +Bool +unique_name( const String name, Conf_no conf_no ) +{ + Parse_info parse_info; + Parse_token *name_token; + Parse_token *existing_token; + Bool exact_match_found; + Bool diff_found; + int i; + int n; + + parse_info = parse(name, match_table, FALSE, TRUE, + s_fcrea_str(" \t"), DEFAULT_COLLAT_TAB); + + if ( parse_info.no_of_matches == 0 ) /* Doesn't match any name. */ + { + sfree(parse_info.indexes); + return TRUE; + } + + if ( parse_info.no_of_matches == -1 ) /* Error. */ + { + log("unique_name(): parse returned error.\n"); + sfree(parse_info.indexes); + return FALSE; + } + + if ( parse_info.no_of_matches == 1 && parse_info.indexes[ 0 ] == -1 ) + { + /* Empty name is not allowed. */ + sfree(parse_info.indexes); + return FALSE; + } + + /* The name matches some conference. Check if they are equal. */ + + name_token = tokenize(name, s_fcrea_str(" \t")); + + exact_match_found = FALSE; + + for ( i = 0; !exact_match_found && i < parse_info.no_of_matches; i++ ) + { + existing_token = match_table[ parse_info.indexes[ i ] ].tokens; + diff_found = FALSE; + + for ( n = 0; + (!diff_found && !s_empty(existing_token[ n ].word) + && !s_empty(name_token[ n ].word)); + ++n) + { + if ( !s_usr_streq(existing_token[ n ].word, + name_token[ n ].word, + DEFAULT_COLLAT_TAB) ) + { + diff_found = TRUE; + } + } + + if (! s_empty(existing_token[ n ].word) + || ! s_empty(name_token[ n ].word) ) + { + /* The length (number of words) differed. */ + diff_found = TRUE; + } + + if ( !diff_found && conf_no != 0 + && conf_table[ parse_info.indexes[ i ] ] != conf_no ) + exact_match_found = TRUE; + } + + sfree(parse_info.indexes); + free_tokens(name_token); + return exact_match_found ? FALSE : TRUE; +} + +/* + * Create a conference. + */ +Conf_no +do_create_conf(String name, + Pers_no creator, + Conf_no supervisor, + Conf_no super_conf, + Conf_type type) +{ + Conf_no conf_no; + Conference * conf_c; + + /* Allocate memory for conf_c */ + + conf_no = cached_create_conf( name ); + + if ( (conf_c = cached_get_conf_stat( conf_no ) ) == NULL) + { + restart_kom("create_conf() - can't get conf_stat"); + } + + conf_c->creator = creator; + conf_c->creation_time = time(NULL); + conf_c->presentation= 0; /* No presentation yet */ + conf_c->supervisor = supervisor; + conf_c->permitted_submitters = 0; + conf_c->super_conf = super_conf; + conf_c->type = type; + conf_c->last_written= conf_c->creation_time; + conf_c->msg_of_day = 0; + conf_c->nice = DEFAULT_NICE; + conf_c->members = EMPTY_MEMBER_LIST; + conf_c->texts = EMPTY_TEXT_LIST; + + mark_conference_as_changed( conf_no ); + + cached_set_conf_type( conf_no, type ); + + return conf_no; +} + + +/* + * Return TRUE if ACTPERS is a supervisor to CONF. + */ + +Bool +is_supervisor(Conf_no conf, + Conference * conf_c)/* Conference status for CONF, can be NULL */ +{ + if (!ACTPERS) + return FALSE; + + if (ENA(wheel, 8)) + return TRUE; + + if (ACTPERS == conf) /* A person is ALWAYS supervisor to */ + return TRUE; /* his/her own mailbox! */ + + + if ( conf_c == NULL ) /* Get conference if unknown to the caller */ + GET_C_STAT( conf_c, conf, FALSE ); + + if ( !conf_c->supervisor ) + return FALSE; + + if (ACTPERS == conf_c->supervisor) + return TRUE; + + if ( locate_membership(conf_c->supervisor, ACT_P) != NULL) + return TRUE; + + return FALSE; +} + + +/* + * Atomic functions + */ + +/* + * Change name of a person or conference. You must be supervisor + * of the conference to use this call. + */ +extern Success +change_name (Conf_no conf_no, + String new_name) +{ + Conference * conf_c; + Access acc; + + CHK_LOGIN(FAILURE); + GET_C_STAT(conf_c, conf_no, FAILURE); + + acc = access_perm(conf_no, conf_c); + + if ( acc <= none ) + { + kom_errno = KOM_UNDEF_CONF; + return FAILURE; + } + + if ( !ACT_P->privileges.change_name + || (acc != unlimited && !ENA(admin, 3))) + { + kom_errno = KOM_PERM; + return FAILURE; + } + + if ( !legal_name( new_name ) ) + { + kom_errno = KOM_BAD_NAME; + return FAILURE; + } + + if ( !unique_name( new_name, conf_no ) ) + { + kom_errno = KOM_CONF_EXISTS; + return FAILURE; + } + + async_new_name(conf_no, conf_c->name, new_name); + + s_strcpy(&conf_c->name, new_name); + mark_conference_as_changed(conf_no); + + cached_change_name(conf_no, new_name); + + return OK; +} + +/* + * Create a conference: Default modes: + * ACTPERS becomes supervisor and super_conf. + * Anyone can submitt texts. + * Noone (not even the creator) is a member in the conf. + * + * If ANYONE_CAN_CREATE_NEW_CONFS (#defined in config.h) is not true + * you must have the 'create_conf' capability. + * + * It is currently not allowed to have a conference that is secret + * and not rd_prot. This restriction might be lifted in the future + * (but I don't understand the use of such a conference...) + */ + +extern Conf_no +create_conf(String name, + Conf_type type) +{ + Conf_no conf_no; + + CHK_LOGIN(0); + + if (!ANYONE_CAN_CREATE_NEW_CONFS && !ENA(create_conf, 0) ) + { + kom_errno = KOM_PERM; + return 0; + } + + if ( !legal_name( name ) ) + { + kom_errno = KOM_BAD_NAME; + return 0; + } + + if ( !unique_name( name, 0 ) ) + { + kom_errno = KOM_CONF_EXISTS; + return 0; + } + + if ( type.letter_box ) /* A letter_box can only be created via */ + { /* create_person. */ + kom_errno = KOM_PERM; + return 0; + } + + if ( type.secret && !type.rd_prot ) + { + kom_errno = KOM_SECRET_PUBLIC; + return 0; + } + + conf_no = do_create_conf(name, ACTPERS, ACTPERS, ACTPERS, type); + + if ( conf_no != 0) + { + ACT_P->created_confs++; + mark_person_as_changed( ACTPERS ); + } + + return conf_no; +} + +/* + * Log out a person from any connection he might be logged on to. + */ +static void +logout_person(Pers_no pers_no) +{ + Session_no i = 0; + Connection *real_active_connection; + + real_active_connection = active_connection; + + while ( (i = traverse_connections(i)) != 0) + { + active_connection = get_conn_by_number(i); + + if ( active_connection->pers_no == pers_no ) + logout(); + } + + active_connection = real_active_connection; +} + +/* + * Delete a conference or person. You must be supervisor of the + * conference to be allowed to delete it. + */ +extern Success +delete_conf (Conf_no conf_no ) +{ + Conference * conf_c; + Access acc; + + CHK_LOGIN(FAILURE); + GET_C_STAT(conf_c, conf_no, FAILURE); + + acc = access_perm(conf_no, conf_c); + + if ( acc != unlimited ) + { + kom_errno = (acc <= none) ? KOM_UNDEF_CONF : KOM_PERM ; + return FAILURE; + } + + if ( conf_c->type.letter_box ) + { + logout_person(ACTPERS); /* Log out this person from any connection */ + + if ( do_delete_pers( conf_no ) != OK ) + { + log("ERROR: delete_conf(): can't delete person.\n"); + } + } + + do_delete_conf( conf_no, conf_c ); + + return OK; +} + +/* + * Map conference name to number. Can be done without logging in. + * Secret conferences will not be returned unless ACTPERS is supervisor + * of, or member in, that conference. + */ +extern Success +lookup_name (const String name, + Conf_list_old * result) +{ + Conf_no * no, * no_copy; + Conf_type * type, *type_copy; + int i; + + if ( cached_lookup_name( name, result ) != OK ) + return FAILURE; + + no = no_copy = result->conf_nos; + type = type_copy = result->type_of_conf; + + for ( i = result->no_of_conf_nos; i > 0; i-- ) + { + if ( fast_access_perm( *no ) <= none ) + --result->no_of_conf_nos; + else + { + *no_copy++ = *no; + *type_copy++ = *type; + } + + ++no; + ++type; + } + return OK; +} + + +/* + * Get status for a conference. + */ +extern Success +get_conf_stat (Conf_no conf_no, + Conference * result ) +{ + Conference * conf_c; + Access acc; + + GET_C_STAT(conf_c, conf_no, FAILURE); + + acc = access_perm( conf_no, conf_c); + + if ( acc == error ) + return FAILURE; + + if ( acc == none ) + { + kom_errno = KOM_UNDEF_CONF; + return FAILURE; + } + + *result = *conf_c; + + return OK; +} + +extern Success +get_conf_stat_old (Conf_no conf_no, + int mask, + Conference * result ) +{ + Conference * conf_c; + Access acc; + + GET_C_STAT(conf_c, conf_no, FAILURE); + + acc = access_perm( conf_no, conf_c); + + if ( acc == error ) + return FAILURE; + + if ( acc == none ) + { + kom_errno = KOM_UNDEF_CONF; + return FAILURE; + } + + *result = *conf_c; + + if ( !(mask & 1) ) + result->name = EMPTY_STRING; + + return OK; +} + + + +/* + * Set or delete the presentation of a conference. If text_no == 0 there + * will be no presentation. + * + * The text's mark-count will be increased so that it will not be deleted + * when it gets old. + * + * BUGS: This should probably be a Info_type 'presentation to conf no #' + * for at least two reasons: + * 1) Given the text, there is no way to know that it + * is a presentation unless what the user writes + * tells you so. + * 2) Presentations should always be readable by everyone. + * But to be readable an open conference needs to be + * recipient of the text. Since all presentations should + * go to the presentation-confs (that is the clients + * responsibility!) this is no big problem, but it depends + * somewhat on how the text-garber is implemented. + */ + +extern Success +set_presentation (Conf_no conf_no, + Text_no text_no ) +{ + Conference * conf_c; + Access acc; + + CHK_LOGIN(FAILURE); + GET_C_STAT(conf_c, conf_no, FAILURE); + + acc = access_perm(conf_no, conf_c); + + if ( acc < unlimited ) + { + kom_errno = (acc <= none) ? KOM_UNDEF_CONF : KOM_PERM; + return FAILURE; + } + + return do_set_presentation(conf_no, conf_c, text_no); +} + +/* + * Set a message-of-the-day for a conference. There should normally + * be no motd unless something extraordinary happens. If there is + * a motd the client should show it as soon as possible. + * + * Only the supervisor can change the motd. + * + * BUG: There is no asynchronous message that reports new motds. + */ +extern Success +set_etc_motd( Conf_no conf_no, + Text_no text_no ) +{ + Conference * conf_c; + Access acc; + BUGDECL; + + CHK_LOGIN(FAILURE); + GET_C_STAT(conf_c, conf_no, FAILURE); + + if ( (acc = access_perm(conf_no, conf_c)) < unlimited ) + { + kom_errno = (acc <= none ) ? KOM_UNDEF_CONF : KOM_PERM; + BUG(("set_etc_motd failed. Conf %ld Text %ld Acc %ld < %ld (%d).\n", + (u_long)conf_no, (u_long)text_no, (u_long)acc, + (u_long)unlimited, acc < unlimited)); + return FAILURE; + } + + return do_set_etc_motd(conf_no, conf_c, text_no); +} + +/* + * Set a new supervisor for a conference. May only be used by + * the old supervisor. + * + * NEW_SUPER is either a person or a conference number. If it is a + * conference number it means that all the members in NEW_SUPER become + * supervisors to CONF_NO. (NEW_SUPER should normally be rd_prot to + * prevent anyone from makeing themselves supervisors). + */ +extern Success +set_supervisor( Conf_no conf_no, + Conf_no new_super ) +{ + Conference * conf_c; + + CHK_LOGIN(FAILURE); + GET_C_STAT(conf_c, conf_no, FAILURE); + if (new_super != 0) + CHK_EXIST(new_super, FAILURE); + + if ( !is_supervisor(conf_no, conf_c) + && !ENA(admin, 6) ) + { + kom_errno = conf_c->type.secret ? KOM_UNDEF_CONF : KOM_PERM; + return FAILURE; + } + + conf_c->supervisor = new_super; + mark_conference_as_changed( conf_no ); + + return OK; +} + +/* + * Allow certain users to submit texts to CONF_NO. + * + * If NEW_PERM_SUB == 0 anyone may submit texts. + * If it is a person only that person can submit texts to CONF_NO. + * If it is a conference only the members in that conference can + * submit texts. + * + * If anyone tries to submit a text to a conference he is not allowed + * to submit texts to the text will silently be redirected to the + * superconf. If he is not allowed to submit to that conference either + * it will be redirected again and so forth, but there is a limit + * (MAX_SUPER_CONF_LOOP) on how many redirections will be done. + * + * It is possible to have a secret conference as super_conf. Users + * will then be able to send texts to it, but they will not see + * where the text went... + */ +extern Success +set_permitted_submitters (Conf_no conf_no, + Conf_no new_perm_sub ) +{ + Conference * conf_c; + + CHK_LOGIN(FAILURE); + if (new_perm_sub != 0) + CHK_EXIST(new_perm_sub, FAILURE); + GET_C_STAT(conf_c, conf_no, FAILURE); + + if ( !is_supervisor(conf_no, conf_c) + && !ENA(admin, 6) ) + { + kom_errno = conf_c->type.secret ? KOM_UNDEF_CONF : KOM_PERM; + return FAILURE; + } + + conf_c->permitted_submitters = new_perm_sub; + mark_conference_as_changed( conf_no ); + + return OK; +} + +/* + * Set the super_conf of CONF_NO. This call may only be used of a + * supervisor of CONF_NO. + * + * See documentation for set_permitted_submitters(). + */ +extern Success +set_super_conf (Conf_no conf_no, + Conf_no new_super_conf ) +{ + Conference * conf_c; + + CHK_LOGIN(FAILURE); + GET_C_STAT(conf_c, conf_no, FAILURE); + CHK_EXIST(new_super_conf, FAILURE); + + if ( !is_supervisor(conf_no, conf_c) + && !ENA(admin, 5) ) + { + kom_errno = conf_c->type.secret ? KOM_UNDEF_CONF : KOM_PERM; + return FAILURE; + } + + conf_c->super_conf = new_super_conf; + mark_conference_as_changed( conf_no ); + + return OK; +} + +/* + * Set the type of a conference. Only the supervisor of a conference can + * set the conf_type. The letter_box flag can not be changed. + * + * BUG: It is allowed to set the type to 'secret' for persons. I don't + * think we want it to be that way. + */ +extern Success +set_conf_type (Conf_no conf_no, + Conf_type type ) +{ + Conference * conf_c; + Access acc; + + CHK_LOGIN(FAILURE); + GET_C_STAT(conf_c, conf_no, FAILURE); + + if ( type.secret && !type.rd_prot ) + { + kom_errno = KOM_SECRET_PUBLIC; + return 0; + } + + acc = access_perm(conf_no, conf_c) ; + + if ( acc < unlimited ) + { + kom_errno = ( acc <= none ) ? KOM_UNDEF_CONF : KOM_PERM ; + return FAILURE; + } + + if ( type.letter_box != conf_c->type.letter_box ) + { + kom_errno = KOM_LETTER_BOX; + return FAILURE; + } + + conf_c->type = type; + mark_conference_as_changed( conf_no ); + + cached_set_conf_type( conf_no, type ); + + return OK; +} + + +/* + * Set garb_nice for a conference. This controls how long messages + * to a conference will live. The argument is probably the number of + * days a message will live. Only the supervisor of the conference + * may change the garb_nice. + */ +extern Success +set_garb_nice( Conf_no conf_no, + Garb_nice nice ) /* number of days */ +{ + Conference * conf_c; + Access acc; + + CHK_LOGIN(FAILURE); + GET_C_STAT(conf_c, conf_no, FAILURE); + + acc = access_perm(conf_no, conf_c); + + if ( acc < unlimited ) + { + kom_errno = (acc <= none) ? KOM_UNDEF_CONF : KOM_PERM; + return FAILURE; + } + + conf_c->nice = nice; + mark_conference_as_changed( conf_no ); + + return OK; +} + +/* + * Change presentation of a conference. If text_no is 0, there will be + * no presentation. + */ + +Success +do_set_presentation(Conf_no conf_no, + Conference * conf_c, + Text_no text_no) +{ + Text_stat * old_pres; + Text_stat * new_pres = NULL; /* Initialized to stop gcc complaining + * about this being used uninitialized. + * This initialization is in fact not needed + * since it would not be used uninitialized.*/ + + /* Check that the new presentation exists before deleting the old*/ + + if ( text_no != 0 ) + { + GET_T_STAT(new_pres, text_no, FAILURE); + if ( new_pres->no_of_marks >= MAX_MARKS_TEXT ) + { + log( + "LIMIT: do_set_presentation(): New presentation very marked.\n"); + return FAILURE; + } + } + + /* Unmark the previous presentation if it exists. */ + + if ( conf_c->presentation != 0 + && (old_pres = cached_get_text_stat(conf_c->presentation)) != NULL) + { + if ( old_pres->no_of_marks > 0 ) + { + --old_pres->no_of_marks; + mark_text_as_changed( conf_c->presentation ); + } + else + { + log("ERROR: do_set_presentation(): Old presentation not marked\n"); + } + } + + /* Mark the new presentation */ + + if ( text_no != 0 ) + { + ++new_pres->no_of_marks; + mark_text_as_changed( text_no ); + } + + conf_c->presentation = text_no; + mark_conference_as_changed( conf_no ); + + return OK; +} + +/* + * Change motd of a conference. If text_no is 0, there will be + * no motd. + */ + +Success +do_set_etc_motd(Conf_no conf_no, + Conference * conf_c, + Text_no text_no) +{ + Text_stat * old_motd; + Text_stat * new_motd = NULL; /* To stop gcc complaining. */ + + /* Check that the new motd exists before deleting the old*/ + + if ( text_no != 0 ) + { + GET_T_STAT(new_motd, text_no, FAILURE); + if ( new_motd->no_of_marks >= MAX_MARKS_TEXT ) + { + log( + "LIMIT: do_set_motd(): New motd very marked.\n"); + return FAILURE; + } + } + + /* Unmark the previous motd if it exists. */ + + if ( conf_c->msg_of_day != 0 + && (old_motd = cached_get_text_stat(conf_c->msg_of_day)) != NULL) + { + if ( old_motd->no_of_marks > 0 ) + { + --old_motd->no_of_marks; + mark_text_as_changed( conf_c->msg_of_day ); + } + else + { + log("ERROR: do_set_motd(): Old motd not marked\n"); + } + } + + /* Mark the new motd */ + + if ( text_no != 0 ) + { + ++new_motd->no_of_marks; + mark_text_as_changed( text_no ); + } + + conf_c->msg_of_day = text_no; + mark_conference_as_changed( conf_no ); + + return OK; +} + + diff --git a/src/server/connections.c b/src/server/connections.c new file mode 100644 index 0000000000000000000000000000000000000000..4c172f004e28aa7f53d1a5eb7359a091bd97e285 --- /dev/null +++ b/src/server/connections.c @@ -0,0 +1,811 @@ +/* + * connections.c + * + * Denna fil inneh}ller niv}n ovanf|r isc. + * + * Created by Willf|r 31/3-90. Mostly written by ceder. + */ + +#include <errno.h> +#include <setjmp.h> +#include <sys/types.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <time.h> +#include <sys/time.h> + +#include <kom-types.h> +#include "lyskomd.h" +#include "s-string.h" +#include <config.h> +#include <debug.h> +#include "end-of-atomic.h" +#include "log.h" +#include <services.h> +#include "isc.h" +#include <kom-errno.h> +#include "com.h" +#include "connections.h" +#include "prot-a-parse.h" +#include "prot-a-output.h" +#include "prot-a.h" +#include "smalloc.h" +#include "prot-a-parse-arg.h" +#include "isc-parse.h" +#include "cache.h" +#include "send-async.h" +#include "mux.h" +#include "mux-parse.h" +#include "internal-connections.h" + +ISCMCB * kom_server_mcb = NULL; +Connection * active_connection = NULL; + +Bool go_and_die = FALSE; +Bool do_sync_db = FALSE; + +jmp_buf parse_env; + + +const Fnc_descriptor fnc_defs[]={ +#include "fnc-def-init.incl" +}; + +u_long service_statistics[sizeof (fnc_defs) / sizeof (Fnc_descriptor)]; + +BUGDECL; + + + +void +logout_client(Connection *cp) +{ + Connection *real_active_connection; + + if ( active_connection != NULL ) + { + log("BUGCHK: logout_client(%d): connection %d is active.\n", + cp->session_no, active_connection->session_no); + } + + switch (cp->magic) + { + case CONN_MAGIC_ALLOC: + break; + + case CONN_MAGIC_FREE: + log("LOGOUT_CLIENT: Trying to free freed Connection - ignored!\n"); + return; + + default: + restart_kom("LOGOUT_CLIENT: Bad magic number\n"); + } + + if ( cp->pers_no != 0 ) + { + real_active_connection = active_connection; /* Shouldn't be needed +++ */ + active_connection = cp; + logout(); + active_connection = real_active_connection; + } + + s_clear(&cp->unparsed); + s_clear(&cp->what_am_i_doing); + s_clear(&cp->username); + + switch(cp->protocol) + { + case 0: /* Hasn't yet allocated any protocol. */ + break; + + case 'A': + prot_a_destruct(cp); + break; + + default: + restart_kom("logout_client(): Bad protocol.\n"); + } + + mux_close(cp); /* Close fd's. Clear mux and isc structures. */ + + kill_client(cp); /* Free the Connection */ +} + + + + +/* + * Call a function in services.c. A pointer to the result is returned. + * The pointer points to static data which is overwritten on each call. + */ +static Success +call_function (Connection * client, + Result_holder * res) /* This is a union. */ +{ + Success status=FAILURE; /* OK if the call was successful. */ + + if ( active_connection != NULL ) + { + log("call_function(%d): active_connection = %d", + client->session_no, active_connection->session_no); + } + + active_connection = client; + + service_statistics[client->function]++; + +#include "call-switch.incl" + + active_connection = NULL; + + return status; +} + + +static void +parse_packet(Connection *client) +{ + if ( client->protocol == '\0' ) /* Not known yet. */ + { + client->protocol = parse_char(client); + switch(client->protocol) + { + case 'A': + prot_a_init(client); + break; + + default: + client->protocol = '\0'; + mux_printf(client, + "%%%%LysKOM unsupported protocol.\n"); + mux_flush(client); + BUG(("%%%%Unsupported protocol.\n")); + longjmp(parse_env, ISC_LOGOUT); + } + } + + switch(client->protocol) + { + case 'A': + prot_a_parse_packet(client); + break; + + default: + restart_kom("parse_packet(): Bad protocol.\n"); + break; + } +} + +/* + * Free all parsed areas which are no longer needed. Re-initialize all + * parse_pos fields so that the parse will expect a new function. + * + * This function is called + * when a parse error occurs + * when a parse is complete and the function has executed. + */ +static void +free_parsed(Connection *client) +{ + s_clear(&client->c_string0); + s_clear(&client->c_string1); + client->string0 = EMPTY_STRING; /* So that no one frees it. */ + sfree( client->c_misc_info_p); + client->c_misc_info_p = NULL; + sfree( client->c_local_text_no_p); + client->c_local_text_no_p = NULL; + client->parse_pos = 0; + client->fnc_parse_pos = 0; + client->array_parse_pos = 0; + client->struct_parse_pos = 0; + client->string_parse_pos = 0; +} + +/* + * Send a reply to a call. + */ +static void +reply(Connection *client, + Success status, + Result_holder *result) +{ + switch(client->protocol) + { + case 'A': + prot_a_reply(client, status, result); + break; + + default: + restart_kom("reply(): Bad protocol.\n"); + break; + } +} + + +/* + * Try to parse enough data from client->unparsed to call a function. + * If more data is needed set client->more_to_parse to FALSE. + */ +static void +parse_unparsed(Connection *client) +{ + String tmp_str = EMPTY_STRING; + Success status; + Result_holder result; + + switch ( setjmp(parse_env) ) + { + case 0 : + /* Parse message. If message is complete call function and reply. */ + parse_packet(client); + client->last_request = time(NULL); + status = call_function(client, &result); + reply(client, status, &result); + free_parsed(client); + end_of_atomic(FALSE); + break; + + case ISC_PROTOCOL_ERR: + s_clear(&client->string0); + free_parsed(client); + mux_printf(client, "%% LysKOM protocol error.\n"); + mux_flush(client); + BUG(("%%%% Protocol error.\n")); + s_clear(&client->unparsed); + client->first_to_parse = 0; + client->more_to_parse = FALSE; + end_of_atomic(FALSE); + break; + + case ISC_MSG_INCOMPLETE: + client->more_to_parse = FALSE; + break; + + case ISC_LOGOUT: + client->kill_me = TRUE; + break; + } + + /* Delete the parsed part of 'unparsed' */ + + if ( s_substr(&tmp_str, client->unparsed, + client->first_to_parse, END_OF_STRING) != OK ) + restart_kom("parse_unparsed: s_substr\n"); + + s_clear(&client->unparsed); + client->unparsed = tmp_str; + client->first_to_parse = 0; +} + + +/* + * There is a message in the event. Parse it. + */ +static void +parse_message(Connection *cp, String tmp_str) +{ + VBUGSTR(tmp_str); + + cp->more_to_parse = TRUE; + + if ( s_strcat(&cp->unparsed, tmp_str) != OK ) + restart_kom("parse_message(): s_strcat\n"); + + tmp_str = EMPTY_STRING; + + /* Parse this packet, but leave the last token if it isn't complete. */ + + parse_unparsed(cp); + + return; +} + +static void +mux_handle_packet(Mux *mux) +{ + Mux_client *mcp = NULL; + Connection *cp = NULL; + + switch(mux->parse.function) + { + case 0: /* ping */ + isc_printf(mux->scb, "0\n"); + isc_flush(mux->scb); + break; + + case 1: /* login */ + + mcp = mux_getclientbyid(mux, mux->parse.num); + if (mcp) + { /* ERROR %d: Already logged in\n */ + isc_printf(mux->scb, "4\n"); + isc_flush(mux->scb); + return; + } + + cp = new_client(); + cp->mux = mux; + cp->hostname = mux->parse.string; + mux->parse.string = EMPTY_STRING; + + mux_addclient(mux, mux->parse.num, cp); + + + BUG(("\n[Client %d from %.*s via MUX %s", + cp->session_no, + cp->hostname.len, + cp->hostname.string, + mux->scb->info.tcp.hostname)); + BUG((" is connecting]\n")); + + break; + + case 2: /* logout */ + mcp = mux_getclientbyid(mux, mux->parse.num); + if (!mcp) + { /* ERROR %d: No such mux-session */ + isc_printf(mux->scb, "4\n"); + isc_flush(mux->scb); + return; + } + + cp = mcp->conn; + + BUG(("\n[Client %d via MUX(%s)", + cp->session_no, + mux->scb->info.tcp.hostname)); + BUG((" is logging out by request]\n")); + +#if 0 + logout_client(cp); + end_of_atomic(FALSE); /* ??? */ +#else + cp->kill_me = TRUE; +#endif + break; + + case 3: /* msg */ + mcp = mux_getclientbyid(mux, mux->parse.num); + if (!mcp) + { /* ERROR %d: No such mux-session */ + isc_printf(mux->scb, "4\n"); + isc_flush(mux->scb); + return; + } + + cp = mcp->conn; + + VBUG(("\n[Message client %d via MUX #%d]\n", + cp->session_no, mux->scb)); + + parse_message(cp, mux->parse.string); + break; + + default: + + VBUG(("\nMUX Protocol error from MUX #%d]\n", + mux->scb)); + + isc_printf(mux->scb, "5\n"); + isc_flush(mux->scb); + break; + + } +} + +void +mux_logout(Mux *mp) +{ + int i; + + for (i = 0; i < mp->client_c; i++) + if (mp->client_v[i].conn) + { + BUG(("\n[Client %d via MUX(%s)", + mp->client_v[i].conn->session_no, + mp->scb->info.tcp.hostname)); + BUG((" is logging out by MUX shutdown]\n")); + logout_client(mp->client_v[i].conn); + end_of_atomic(FALSE); + } + + isc_close(mp->scb); + mux_destruct(mp); +} + + +static void +mux_parse_unparsed(Mux *mux) +{ + String tmp_str; + + while ( mux->parse.more_to_parse ) + { + switch ( setjmp(mux_parse_env) ) + { + case 0 : + /* Parse message. Take apropriate action if message is complete. */ + mux_parse_packet(mux); + mux_handle_packet(mux); + mux_free_parsed(mux); + break; + + case MUX_PROTOCOL_ERR: + s_clear(&mux->parse.string); + mux_free_parsed(mux); + isc_printf(mux->scb, "5\n"); + isc_flush(mux->scb); + BUG(("%%%% Mux protocol error.\n")); + s_clear(&mux->parse.unparsed); + mux->parse.first_to_parse = 0; + mux->parse.more_to_parse = FALSE; + break; + + case MUX_MSG_INCOMPLETE: + mux->parse.more_to_parse = FALSE; + break; + + case MUX_LOGOUT: + mux_logout(mux); + return; + } + + /* Delete the parsed part of 'unparsed' */ + + tmp_str = EMPTY_STRING; + + if ( s_substr(&tmp_str, mux->parse.unparsed, + mux->parse.first_to_parse, END_OF_STRING) != OK ) + restart_kom("mux_parse_unparsed: s_substr\n"); + + s_clear(&mux->parse.unparsed); + mux->parse.unparsed = tmp_str; + mux->parse.first_to_parse = 0; + } +} + + +static void +mux_parse_message(Mux *mp, String tmp_str) +{ + VBUGSTR(tmp_str); + + BUG(("mux_parse_message(): mp->parse.unparsed = { len = %d, " + "string = %p }\n", mp->parse.unparsed.len, + mp->parse.unparsed.string)); + + if ( s_strcat(&mp->parse.unparsed, tmp_str) != OK ) + restart_kom("mux_parse_message(): s_strcat\n"); + + tmp_str = EMPTY_STRING; + + /* Parse this packet, but leave the last token if it isn't complete. */ + + mux_parse_unparsed(mp); + + return; +} + +/* + * parse data in client->unparsed and mux->unparsed if there + * is data that has not yet been taken care of. Return TRUE + * if there was no data to take care of. + */ +Bool +parse_forgotten() +{ + Bool flg = FALSE; /* Was there anything? */ + Connection *ci; + Session_no session_no = 0; + + if ( traverse_connections(0) == 0 ) + return TRUE; + + while ( ( session_no = traverse_connections( session_no ) ) != 0 ) + { + ci = get_conn_by_number (session_no); + + if ( ci->more_to_parse ) + { + parse_unparsed(ci); + flg = TRUE; + } + } + + return !flg; +} + + +/* Return 1 if the named file exists, 0 otherwise */ +static int +fexists(char *filename) +{ + extern int stat(char *filename, struct stat *bufp); + struct stat buf; + int code; + + code = !stat(filename, &buf); + errno = 0; + + return code; +} + + +void +dump_statistics(void) +{ + static time_t last_dump = NO_TIME; + int i; + time_t now; + FILE *fp; + extern char statisticfile[1024]; /* Defined in ramkomd.c */ + + if ( (fp = fopen(statisticfile, "a")) == NULL ) + { + log(__FILE__ ": dump_statistics(): can't open file %s\n", + statisticfile); + return; + } + + time(&now); + + if (last_dump == NO_TIME) + { + fprintf(fp, "RESTART\n"); + last_dump = now; + } + + fprintf(fp, "TIME: %s", ctime(&now)); + fprintf(fp, "SECONDS: %d\n", (int)difftime(now, last_dump)); + fprintf(fp, "STATISTICS:"); + for ( i = 0; i < sizeof(service_statistics) / sizeof(u_long); i++) + { + fprintf(fp, " %lu", service_statistics[i]); + service_statistics[i]=0; + } + + fprintf(fp, "\n"); + fclose(fp); + + last_dump = now; +} + + +/* + * check_kill_flg must NEVER be called inside an atomic call! + */ +static void +check_kill_flg(void) +{ + Session_no i = 0; + Connection *conn; + + if ( active_connection != NULL ) + { + restart_kom("check_kill_flg: active_connection == %d", + active_connection->session_no); + } + + while ( (i = traverse_connections (i)) != 0 ) + { + conn = get_conn_by_number(i); + + if ( conn->kill_me == TRUE ) + { + logout_client(conn); + end_of_atomic(FALSE); + } + } +} + +static void +login_request(ISCECB *event) +{ + Connection * cp; + + if (isc_sessions(kom_server_mcb) >= MAX_NO_OF_CONNECTIONS) + { + BUG(("Connection attempt rejected.\n")); + isc_printf(event->session, "%s", "%% No connections left.\n"); + isc_flush(event->session); + isc_close(event->session); + + async_rejected_connection(); + return; + } + + /* Supress logins if /etc/nologin exists */ + if (fexists("/etc/nologin")) + { + isc_printf(event->session, "%s", "%% No logins allowed.\n"); + isc_flush(event->session); + isc_close(event->session); + return; + } + + if (event->session->info.tcp.listen == listen_mux) + { + /* Multiplexer requesting connection */ + + /* + ** Create, and setup the MUX for the MUX case + */ + event->session->udg = mux_create(MUX_TYPE_MUX, event->session); + + BUG(("MUX #%d at %s connecting.\n", + event->session, event->session->info.tcp.hostname)); + } + else + { + /* Client requesting connection */ + + /* + ** Create, and setup the MUX for the CLIENT case + */ + event->session->udg = mux_create(MUX_TYPE_CLIENT, + event->session); + + cp = new_client(); + cp->mux = event->session->udg; + cp->hostname = s_fcrea_str(event->session->info.tcp.hostname); + + mux_addclient(event->session->udg, 0, cp); + + BUG(("\n[Client %d from %s", cp->session_no, + event->session->info.tcp.hostname)); + BUG((" is connecting]\n")); + } +} + +static void +logout_request(ISCECB *event) +{ + Connection * cp; + + if (event->session->udg->type == MUX_TYPE_MUX) + { + BUG(("\n[MUX #%d at %s", event->session, + event->session->info.tcp.hostname)); + BUG((" is logging out]\n")); + + /* + ** Logout any remaining clients + */ + mux_logout(event->session->udg); + } + else + { + cp = event->session->udg->client_v[0].conn; + + BUG(("\n[Client %d from %s", cp->session_no, + event->session->info.tcp.hostname)); + BUG((" is logging out]\n")); + + cp->kill_me = TRUE; + } +} + + +static void +message_request(ISCECB *event) +{ + Connection *cp; + String tmp_str; + + if (event->session->udg->type == MUX_TYPE_MUX) + { + event->session->udg->parse.more_to_parse = TRUE; + tmp_str.string = event->msg->buffer; + tmp_str.len = event->msg->length; + mux_parse_message(event->session->udg, tmp_str); + } + else + { + cp = event->session->udg->client_v[0].conn; + VBUG(("\n[Message from client %d]\n", + cp->session_no)); + /* + ** Pass the message on to the parser + */ + tmp_str.string = event->msg->buffer; + tmp_str.len = event->msg->length; + parse_message(cp, tmp_str); + } +} + +static long +timevaldiff(struct timeval a, + struct timeval b) +{ + return 1000000 * (a.tv_sec - b.tv_sec) + a.tv_usec - b.tv_usec; +} + +void +toploop(void) +{ + ISCECB * event; + Bool pending_input = TRUE; /* Should be TRUE whenever there + is or might be pending input + from any client in the unparsed + buffer. */ + long timeout = TIMEOUT; /* In milliseconds. */ + struct timeval before, after; + + while ( !go_and_die ) + { + if (do_sync_db) + { + cache_sync(); /* A noop in diskomd. Actually + stops the server and dumps + everything in ramkomd. */ + dump_statistics(); + do_sync_db = 0; + } + + gettimeofday(&before, NULL); + event = isc_getnextevent(kom_server_mcb, + pending_input ? 0 : timeout ); + gettimeofday(&after, NULL); + + timeout -= timevaldiff(after, before) / 1000; + + if ( timeout < 0 ) + timeout = 0; + + if ( event == NULL ) + restart_kom("toploop(): Got NULL event - shouldn't happen...\n"); + + switch ( event->event ) + { + case ISC_EVENT_ERROR: + if (errno != EINTR) + { + log("toploop: ISC_EVENT_ERROR (error = '%s'):\n '%s'\n", + strerror(errno), event->msg ? event->msg->buffer + : "(unknown)"); + } + break; + + case ISC_EVENT_TIMEOUT: + BUG((">")); +#ifdef DEBUG + fflush(stdout); +#endif + if ( pending_input == FALSE ) + timeout = end_of_atomic(TRUE); /* Idle. Do some cleaning up. */ + else + { + if ( timeout == 0 ) + end_of_atomic(FALSE); + pending_input = !parse_forgotten(); + } + + + break; + + case ISC_EVENT_LOGIN: + pending_input = TRUE; + login_request(event); + break; + + case ISC_EVENT_LOGOUT: + pending_input = TRUE; + logout_request(event); + break; + + case ISC_EVENT_MESSAGE: + pending_input = TRUE; + message_request(event); + break; + + default: + pending_input = TRUE; + log("ERROR: toploop(): Unknown ISC_EVENT\n"); + break; + } + + isc_dispose(event); + + check_kill_flg(); + } +} diff --git a/src/server/connections.h b/src/server/connections.h new file mode 100644 index 0000000000000000000000000000000000000000..815ef3afda3404d404451548a7a6d2d4620f70c2 --- /dev/null +++ b/src/server/connections.h @@ -0,0 +1,179 @@ + /* + * connections.h -- The top level of the communication packet. + * + * Requires kom-types.h, com.h + */ + + +extern Bool go_and_die; /* Set this to TRUE to kill the server. */ +extern Bool do_sync_db; /* Set this to TRUE to force a sync via + signal. */ + +#define CONN_MAGIC_ALLOC 0x18F4AE74 +#define CONN_MAGIC_FREE 0x67A7B144 + +typedef struct connection { + unsigned long magic; /* Hey, this is like magic! */ + + struct connection * prev; + struct connection * next; + + /* The following are used by services.c */ + + Pers_no pers_no; /* 0 if not yet logged in. */ + Person * person; /* NULL if not yet logged in. */ + time_t session_start; /* the time when the current session + was started */ + Conf_no cwc; /* Current working conference */ + String what_am_i_doing; + u_char ena_level; /* Enable level */ + String username; /* Userid and hostname. */ + String hostname; /* Real hostname */ + + /* The following are used by server/connections.c */ + + struct mux *mux; + u_char protocol; + + int parse_pos; + int fnc_parse_pos; + int array_parse_pos; + int struct_parse_pos; + int string_parse_pos; + + int ref_no; + Call_header function; /* Function to call. */ + + /* Gather data in these variables. */ + + long num0; + long num1; + long num2; + long num3; + String c_string0; /* These strings are used for strings + * that are *NOT* freed by services.c. + * They are freed by free_parsed() in + * connections.c */ + String c_string1; + String string0; /* This string is used for strings that + * are freed by the routines in services.c */ + Misc_info *c_misc_info_p; /* Freead by free_parsed(). */ + Local_text_no *c_local_text_no_p; /* Freead by free_parsed(). */ + Priv_bits priv_bits; + Conf_type conf_type; + + /* Protocol independent things. */ + + String unparsed; + String_size first_to_parse; /* Index into unparsed. */ + Bool more_to_parse; /* Any chance that there is anything + left in unparsed? */ + time_t last_request; /* Set every time a packet is + received from the client. */ + + Session_no session_no; /* A unique number. */ + Bool kill_me; /* Yeah! */ +} Connection; +/* + * It is guaranteed that in the Connection struct the pers_no and person + * fields are either both 0/NULL, or both non-0/non-NULL. + */ + + +/* + * Data for the isc-server. + */ +extern struct isc_mcb *kom_server_mcb; + +#if 0 +extern Connection *all_connections; +#endif + +/* + * active_connection points to the info about the client who initiated this + * call. This is set by connectins.c whenever a complete message is parsed + * and a function in services.c is called. + */ +Connection * active_connection; + + +/* + * This enum describes the result of a function in services.c. + */ +typedef enum { + number, /* E.g. Pers_no, Conf_no, Text_no, time_t. + This is somewhat special in that the + function indicates an error by returning + 0 and not FAILURE. */ + success, /* Only return a Success. */ + /* Functions of the following types returns a Success _and_ data + of the indicated type. The corresponding types are found in + kom-types.h */ + person, + membership, + conf_list, + conf_no_list, + conference, + string, + mark_list, + text_stat, + text_list, + who_info_list, + who_info_list_old, + info, + membership_list, + member_list, + time_date, + session_info, + session_no +} Res_type; + +/* + * The result from a call is stored in this union. + */ +typedef union { + u_long number; + Person person; + Membership membership; + Conf_list_old conf_list; + Conf_no_list conf_no_list; + Conference conference; + Mark_list mark_list; + String string; + Text_stat text_stat; + Text_list text_list; + Info info; + Who_info_list who_info_list; + Who_info_list_old who_info_list_old; + Membership_list membership_list; + Member_list member_list; + time_t time_date; + Session_info session_info; + Session_no session_no; +} Result_holder; + + +typedef struct { + Res_type result; + /* The function that is used to parse args for this function: */ + void (*parser)(Connection *client); +} Fnc_descriptor; + +extern const Fnc_descriptor fnc_defs[]; + +/* + * This array holds number of calls to each service. It is dumped to the + * log file every time the server syncs. + */ +extern u_long service_statistics[]; + + +#ifdef _SETJMP_ +extern jmp_buf parse_env; +#endif + +extern void +toploop(void); + +extern void +dump_statistics(void); diff --git a/src/server/dbck-cache.c b/src/server/dbck-cache.c new file mode 100644 index 0000000000000000000000000000000000000000..075087222c3a33decf40e9e86809e02e549d8475 --- /dev/null +++ b/src/server/dbck-cache.c @@ -0,0 +1,740 @@ +/* + * This module contains some very simple simulations of the routines in + * cache.c. + * + * Extracted from ram-cache.c and rewritten by ceder. + * + * New database format with texts in their own file by Inge Wallin. + * Also save time as a time_t instead of a struct tm. + */ + +/* + * All functions that can fail sets kom_errno to a suitable value + * if they fail. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/file.h> + +#include <kom-errno.h> +#include <kom-types.h> +#include "s-collat-tables.h" +#include "smalloc.h" +#include "cache.h" +#include <debug.h> +#include "lyskomd.h" +#include "parser.h" +#include "ram-parse.h" +#include "log.h" +#include "ram-output.h" +#include "com.h" +#include "connections.h" +#include "send-async.h" +#include "memory.h" + +#ifdef TIME_SYNC +# include <sys/resource.h> +#endif + +#define EXPORT + +#include "limits.h" + +Person * pers_arr[ MAX_CONF ]; +Conference * conf_arr[ MAX_CONF ]; +Conf_type conf_type_arr[ MAX_CONF ]; +String name_list [ MAX_CONF ]; /* "cache" list */ + /* Global var auto init to NULL */ +int next_free_num = 1; + +Text_stat * text_arr[ MAX_TEXT ]; +int next_text_num = 1; + +/* Defined in ramkomd.c */ +extern char datafilename[1024]; +extern char backupfilename[1024]; +extern char textfilename[1024]; + + +static FILE *text_file = NULL; +static FILE *new_text_file = NULL; /* Used when garbage collecting. */ + +BUGDECL; + + +/* Macros */ + +#define TRACE2(format, arg) if ( buglevel > 2 ) printf(format, arg) +#define TRACE1(format) if ( buglevel > 2 ) printf(format) +#define TRACESTR(str) if ( buglevel > 2 ) s_puts(str) +#define INRANGE(str, num, retval) if ( num == 0 || num >= next_free_num ) \ +{ \ + return retval; \ + printf("%s: conf_no %d out of range 1 ... %d ", str, num, next_free_num); \ + fflush(stdout); \ + fflush(stderr); \ + abort(); \ +} + +#define TEXT_RANGE(str, num, retval) if ( num == 0 || num >= next_text_num ) \ +{ \ + return retval;\ + printf("Text_no out of range 1 ... %d ", next_text_num); \ + printf(str); \ + fflush(stdout); \ + fflush(stderr); \ + abort(); \ +} + + +extern void +cached_set_conf_type (Conf_no conf_no, + Conf_type type) +{ + return; /* No need to do anything in this simulation. */ +} + + +extern Conf_type +cached_get_conf_type (Conf_no conf_no) +{ + return conf_arr [ conf_no ]->type; +} + + + +#define IMPL(retval) if (1) { fflush(stderr); fflush(stdout); abort(); } + + +/* + * Various function calls to tell the cache that something is changed. + */ + +void +mark_person_as_changed(Pers_no pers_no) +{ + TRACE2("Person %d is changed\n", pers_no); + return; +} + + +void +mark_conference_as_changed(Conf_no conf_no) +{ + TRACE2("Conf. %d is changed\n", conf_no); + return; +} + +void +mark_text_as_changed( Text_no text_no ) +{ + TRACE2("Text %d is changed.\n", text_no); + TEXT_RANGE("mark_text_as_changed\n", text_no, (void)0); +} + + +/* + * Person-related calls + */ + + +extern Success +cached_create_person( Pers_no person ) +{ + TRACE2("Person %d is being created.\n", person); + INRANGE("cached_create_person\n", person, FAILURE); + + if ( pers_arr[ person ] != NULL ) + { + TRACE1("pers_arr not NULL"); + fflush(stdout); + fflush(stderr); + abort(); + } + + pers_arr[ person ] = alloc_person(); + return OK; +} + + +extern Person * +cached_get_person_stat( Pers_no person ) +{ + INRANGE("cached_get_person_stat\n", person, NULL); + TRACE2("cached_get_person_stat %d\n", person); + kom_errno = KOM_UNDEF_PERS; + + return pers_arr[ person ]; +} + + + +/* + * Conference-related calls + */ +extern Conf_no /* Also cache the name */ +cached_create_conf (String name) +{ + Conference * conf_c; + Conf_no conf_no; + + TRACE1("cached_create_conf( "); + TRACESTR(name); + TRACE1(" )\n"); + + if ( next_free_num >= MAX_CONF ) + { + kom_errno = KOM_INDEX_OUT_OF_RANGE; + return 0; + } + + conf_no = next_free_num++; + conf_c = alloc_conference(); + + conf_c->name = EMPTY_STRING; + s_strcpy(&conf_c->name, name); + + TRACE2(" number %d\n", conf_no); + + conf_arr[ conf_no ] = conf_c; + pers_arr[ conf_no ] = NULL; + + return conf_no; +} + +extern Success +cached_delete_conf( Conf_no conf ) +{ + INRANGE("cached_delete_conf()", conf, FAILURE); + if ( conf_arr[ conf ] == NULL ) + { + kom_errno = KOM_UNDEF_CONF; + return FAILURE; + } + free_conference(conf_arr[ conf ]); + conf_arr[ conf ] = NULL; + return OK; +} + +Success +cached_delete_person(Pers_no pers) +{ + INRANGE("cached_delete_person()", pers, FAILURE); + if ( pers_arr[ pers ] == NULL ) + { + kom_errno = KOM_UNDEF_PERS; + return FAILURE; + } + + pers_arr[ pers ] = NULL; + return OK; +} + +Success +cached_delete_text(Text_no text) +{ + TEXT_RANGE("cached_delete_text()", text, FAILURE); + if ( text_arr[ text ] == NULL ) + { + kom_errno = KOM_NO_SUCH_TEXT; + return FAILURE; + } + free_text_stat(text_arr[ text ]); + text_arr[ text ] = NULL; + return OK; + +} + + +extern Conference * +cached_get_conf_stat( Conf_no conf_no ) +{ + INRANGE("cached_get_conf_stat\n", conf_no, NULL); + TRACE2("cached_get_conf_stat %d\n", conf_no); + kom_errno = KOM_UNDEF_CONF; + + return conf_arr[ conf_no ]; +} + +/* + * Return TRUE if conf_no exists. + */ +Bool +cached_conf_exists(Conf_no conf_no) +{ + return conf_arr[ conf_no ] != NULL ? TRUE : FALSE; +} + + +/* + * Calls to handle texts + */ + +extern String +cached_get_text( Text_no text ) +{ + String the_string; + + TEXT_RANGE("cached_get_text\n", text, EMPTY_STRING); + TRACE2("cached_get_text %d\n", text); + + if ( text_arr[ text ] == NULL ) + return EMPTY_STRING; + else + { + the_string.string = tmp_alloc( text_arr[text]->no_of_chars ); + the_string.len = text_arr[text]->no_of_chars; + fseek(text_file, text_arr[ text ]->file_pos, SEEK_SET); + if ( fread(the_string.string, sizeof(char), the_string.len, text_file) + != the_string.len ) + { + log("WARNING: cached_get_text: " + "couldn't read enough characters of text %d\n", + text); + } + + return the_string; + } +} + +extern Text_stat * /* NULL on error */ +cached_get_text_stat( Text_no text ) +{ + kom_errno = KOM_NO_SUCH_TEXT; + TEXT_RANGE("cached_get_text_stat\n", text, NULL); + TRACE2("cached_get_text_stat %d\n", text); + + return text_arr[ text ]; +} + + + +/* + * The text is set up with an empty misc-field. The misc field is + * later initialized by create_text. + */ + +#if 0 +extern Text_no +cached_create_text( String message) +{ + Text_no tno; + + tno = next_text_num++; + + TRACE2("cached_create_text (len=%d)\n", message.len); + + if ( tno >= MAX_TEXT ) + { + kom_errno = KOM_INDEX_OUT_OF_RANGE; + next_text_num = MAX_TEXT; + + return 0; + } + + text_arr[ tno ] = alloc_text_stat(); + text_arr[ tno ]->no_of_misc = 0; + text_arr[ tno ]->misc_items = NULL; + text_arr[ tno ]->no_of_marks = 0; + text_arr[ tno ]->no_of_lines = 0; + text_arr[ tno ]->no_of_chars = 0; + fseek(text_file, 0, SEEK_END); + text_arr[ tno ]->file_pos = ftell(text_file); + + if ( fwrite(message.string, sizeof(char), message.len, text_file) + != message.len ) { + log("WARNING: cached_create_text: Couldn't write the text %d\n", + tno); + } + + fflush(text_file); + + TRACE2("cached_create_text -> %d\n", tno); + + return tno; +} +#endif + +extern void +cached_flush_text(Text_no text_no, + String message) +{ + if ( text_no >= next_text_num ) + { + log("cached_flush_text(%lu, string): only text %lu exists.", + (u_long) text_no, (u_long) next_text_num); + return; + } + + fseek(new_text_file, 0, SEEK_END); + text_arr[ text_no ]->file_pos = ftell(new_text_file); + + if ( fwrite(message.string, sizeof(char), message.len, new_text_file) + != message.len ) + { + log("WARNING: cached_flush_text: Couldn't write the text %d\n", + text_no); + } + + fflush(new_text_file); +} + + +Text_no +traverse_text(Text_no seed) +{ + seed++; + + while ( seed < next_text_num && text_arr[ seed ] == NULL ) + seed++; + + return (seed == next_text_num) ? 0 : seed ; +} + +Pers_no +traverse_person(Pers_no seed) +{ + seed++; + + while ( seed < next_free_num && pers_arr[ seed ] == NULL ) + seed++; + + return (seed == next_free_num) ? 0 : seed ; +} + +Conf_no +traverse_conference(Conf_no seed) +{ + seed++; + + while ( seed < next_free_num && conf_arr[ seed ] == NULL ) + seed++; + + return (seed == next_free_num) ? 0 : seed ; +} + +extern Garb_nice +cached_get_garb_nice (Conf_no conf_no) +{ + return conf_arr [ conf_no ]->nice; +} + +extern Local_text_no +cached_get_highest_local_no (Conf_no conf_no) +{ + return ( conf_arr [ conf_no ]->texts.first_local_no + - 1 + conf_arr [ conf_no ]->texts.no_of_texts ); +} + +/* Lock a person struct in memory. Increase a referenc count. */ +void +cached_lock_person(Pers_no pers_no) +{} + +/* Decrease reference count. If zero, unlock person. */ +void +cached_unlock_person(Pers_no pers_no) +{} + +/* Lock a conf struct in memory. Increase a referenc count. */ +void +cached_lock_conf(Conf_no conf_no) +{} + +/* Decrease reference count. If zero, unlock conf. */ +void +cached_unlock_conf(Conf_no conf_no) +{} + + + + +static Bool +is_clean(const char *fn) +{ + FILE *fp; + + if ( (fp = fopen(fn, "rb")) == NULL ) + return FALSE; + + if ( getc(fp) == 'C' && getc(fp) == 'L' && getc(fp) == 'E' + && getc(fp) == 'A' && getc(fp) == 'N' ) + { + fclose(fp); + return TRUE; + } + else + { + fclose(fp); + return FALSE; + } +} + +#ifdef TIME_SYNC +static long +timerdiff(struct timeval a, + struct timeval b) +{ + return a.tv_sec - b.tv_sec; +} +#endif + +extern void /* Write out everything. */ +cache_sync(void) +{ + FILE *fp; + int i; + +#ifdef TIME_SYNC + struct rusage start, after_confs, after_persons, after_text_stats, + after_text_masses; + time_t st, ac, ap, ats, atm; + void getrusage(int who, struct rusage *rusage); +#endif + +#ifdef TIME_SYNC + getrusage(0, &start); + time(&st); +#endif + + if ( is_clean(datafilename) ) + { + if ( rename(datafilename, backupfilename) != 0 ) + log("WARNING: cache_sync: can't backup.\n"); + } + else + log("cache_sync: datafile not clean. No backup taken.\n"); + + if ( (fp=fopen(datafilename, "w") ) == NULL ) + { + log("WARNING: cache_sync: can't open file to save in.\n"); + return; + } + + fprintf(fp, "DIRTY\n"); /* DIRTY-FLAG */ + + fprintf(fp, "%d\n", next_free_num); /* NEXT_FREE_NUM */ + + for ( i = 1; i < next_free_num; i++ ) /* CONFS */ + { + if ( conf_arr[ i ] == NULL ) + fprintf(fp, "@"); + else + { + fprintf(fp, "+ "); + foutput_conference(fp, conf_arr[ i ]); + } + putc('\n', fp); + } +#ifdef TIME_SYNC + getrusage(0, &after_confs); + time(&ac); +#endif + + for ( i = 1; i < next_free_num; i++ ) /* PERSONS */ + { + if ( pers_arr[ i ] == NULL ) + fprintf(fp, "@"); + else + { + fprintf(fp, "+ %dH", PASSWD_LEN); + fwrite(pers_arr[ i ]->pwd, PASSWD_LEN, 1, fp); + foutput_person(fp, pers_arr[ i ]); + } + putc('\n', fp); + } +#ifdef TIME_SYNC + getrusage(0, &after_persons); + time(&ap); +#endif + + fprintf(fp, "%d\n", next_text_num); /* NEXT_TEXT_NUM */ + + for ( i = 1; i < next_text_num; i++ ) /* TEXT_STATS */ + { + if ( text_arr[ i ] == NULL ) + fprintf(fp, "@"); + else + { + fprintf(fp, "+ "); + foutput_text_stat(fp, text_arr[ i ]); + } + putc('\n', fp); + } + +#ifdef TIME_SYNC + getrusage(0, &after_text_stats); + time(&ats); + + getrusage(0, &after_text_masses); + time(&atm); +#endif + + rewind(fp); + fprintf(fp, "CLEAN"); + fclose(fp); + +#ifdef TIME_SYNC + log("Sync ready.\n" + "User: %4ld sec (%4ld conf, %4ld pers, %4ld stat, %4ld text)\n" + "Sys: %4ld sec (%4ld conf, %4ld pers, %4ld stat, %4ld text)\n" + "Real: %4ld sec (%4ld conf, %4ld pers, %4ld stat, %4ld text)\n" + "Page faults: %4ld. Swapped: %4ld. Outblocked: %4ld.\n", + + timerdiff(after_text_masses.ru_utime, start.ru_utime), + timerdiff(after_confs.ru_utime, start.ru_utime), + timerdiff(after_persons.ru_utime, after_confs.ru_utime), + timerdiff(after_text_stats.ru_utime, after_persons.ru_utime), + timerdiff(after_text_masses.ru_utime, after_text_stats.ru_utime), + + timerdiff(after_text_masses.ru_stime, start.ru_stime), + timerdiff(after_confs.ru_stime, start.ru_stime), + timerdiff(after_persons.ru_stime, after_confs.ru_stime), + timerdiff(after_text_stats.ru_stime, after_persons.ru_stime), + timerdiff(after_text_masses.ru_stime, after_text_stats.ru_stime), + + (u_long)difftime(atm, st), + (u_long)difftime(ac, st), + (u_long)difftime(ap, ac), + (u_long)difftime(ats, ap), + (u_long)difftime(atm, ats), + + after_text_masses.ru_majflt - start.ru_majflt, + after_text_masses.ru_nswap - start.ru_nswap, + after_text_masses.ru_oublock - start.ru_oublock); +#endif +} + +extern void +cache_open_new_text_file(void) +{ + if ( ( new_text_file = fopen(textfilename, "w")) == NULL ) + { + log("Can't open file to save new texts. Goodbye.\n"); + exit(1); + } +} + +extern Success +init_cache(void) +{ + FILE *fp = NULL; + int i; + extern int vflag; /* from dbck.c */ + + new_text_file = NULL; + + if ( (text_file = fopen(textfilename, "rb")) == NULL ) + { + restart_kom("ERROR: init_cache: can't open text file.\n"); + } + + if ( is_clean(datafilename) ) + { + if ( (fp = fopen(datafilename, "rb")) == NULL ) + { + log("WARNING: init_cache: can't open datafile.\n"); + return FAILURE; + } + log("MSG: init_cache: using datafile.\n"); + } + else if ( is_clean(backupfilename) ) + { + if ( (fp = fopen(backupfilename, "rb")) == NULL ) + { + log("WARNING: init_cache: can't open backupfile.\n"); + return FAILURE; + } + log("MSG: init_cache: using backup file.\n"); + } + else + { + log("WARNING: init_cache: can't find old data base.\n"); + return FAILURE; + } + + fseek(fp, 6, SEEK_SET); /* skip clean/dirty flag. */ + + next_free_num = fparse_long(fp); /* NEXT_FREE_NUM */ + + if ( vflag ) + log("Reading %d conferences, starting at pos %d.\n", + next_free_num-1, ftell(fp)); + + for ( i = 1; i < next_free_num; i++ ) /* CONFS */ + { + fskipwhite(fp); + switch(getc(fp)) + { + case '@': + conf_arr[ i ] = NULL; + break; + + case '+': + conf_arr[ i ] = alloc_conference(); + if ( fparse_conference(fp, conf_arr[ i ]) != OK ) + restart_kom("init_cache(): fparse_conference failed. " + "i == %d\n", i); + + name_list[i] = EMPTY_STRING; + s_strcpy(&name_list[i], conf_arr[ i ]->name); + + break; + } + } + + if ( vflag ) + log("Reading %d persons, starting at pos %d.\n", + next_free_num-1, ftell(fp)); + + for ( i = 1; i < next_free_num; i++ ) /* PERSONS */ + { + fskipwhite(fp); + switch(getc(fp)) + { + case '@': + pers_arr[ i ] = NULL; + break; + + case '+': + pers_arr[ i ] = alloc_person(); + if ( fparse_person(fp, pers_arr[ i ]) != OK ) + restart_kom("init_cache: fparse_person failed. i == %d\n", i); + + break; + } + } + + next_text_num = fparse_long(fp); /* NEXT_TEXT_NUM */ + + if ( vflag ) + log("Reading %d texts, starting at pos %d.\n", + next_text_num-1, ftell(fp)); + + for ( i = 1; i < next_text_num; i++ ) /* TEXT_STATS */ + { + fskipwhite(fp); + switch(getc(fp)) + { + case '@': + text_arr[ i ] = NULL; + break; + + case '+': + text_arr[ i ] = alloc_text_stat(); + if ( fparse_text_stat(fp, text_arr[ i ]) != OK ) + restart_kom("init_cache(): fparse_text_stat failed. i == %d\n", + i); + break; + } + } + + log("Read %d confs/persons and %d texts, eof at %d\n", + next_free_num-1, next_text_num-1, ftell(fp)); + + fclose(fp); + + return OK; +} diff --git a/src/server/dbck-cache.h b/src/server/dbck-cache.h new file mode 100644 index 0000000000000000000000000000000000000000..0db145163b82898c714ec5033cfcb4bf1fa1d309 --- /dev/null +++ b/src/server/dbck-cache.h @@ -0,0 +1,8 @@ +extern void +cached_flush_text(Text_no text_no, + String message); + +extern void +cache_open_new_text_file(void); + + diff --git a/src/server/dbck.c b/src/server/dbck.c new file mode 100644 index 0000000000000000000000000000000000000000..71dac1cd68a38d2b18cd2e8c439a5d547d102342 --- /dev/null +++ b/src/server/dbck.c @@ -0,0 +1,1223 @@ +/* + * dbck.c - A simple database checker and corrector. + * + * Author: Per Cederqvist. + */ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#include <kom-types.h> +#include <config.h> +#include "cache.h" +#include "log.h" +#include "lyskomd.h" +#include "misc-parser.h" +#include "smalloc.h" +#include <debug.h> +#include "dbck-cache.h" + +char datafilename[1024]; /* Full pathname to the database file */ +char backupfilename[1024]; /* Full pathname to the backup file */ +char textfilename[1024]; +char textbackupfilename[1024]; /* unshrinked text-file. */ +static const char *dbase_dir = NULL; /* Directory where database resides */ + +#define TEXTBACKUPFILE_NAME "db/backup-texts" + +int vflag=0; /* Verbose - list statistics also. */ +int iflag=0; /* Interactive - prompt user and repair. */ +int rflag=0; /* Repair simple error without confirmation. */ +int gflag=0; /* Garbage collect text-file. */ + +int modifications = 0; + +typedef struct { + int created_confs; +} Person_scratchpad; + +static const Person_scratchpad EMPTY_PERSON_SCRATCHPAD = { 0 }; + +#include "limits.h" + +static Person_scratchpad *person_scratchpad[MAX_CONF]; + +#ifdef DEBUG +int buglevel = 0; +#endif + +extern void +log (const char * format, ...) +{ + va_list AP; + + va_start(AP, format); + + vfprintf(stdout, format, AP); + + va_end(AP); +} + +extern void +restart_kom (const char * format, ...) +{ + va_list AP; + + va_start(AP, format); + + vfprintf(stdout, format, AP); + + va_end(AP); + exit(1); +} + +static Person_scratchpad * +alloc_person_scratchpad(void) +{ + Person_scratchpad *p; + + p = smalloc(sizeof(Person_scratchpad)); + *p = EMPTY_PERSON_SCRATCHPAD; + return p; +} + + +static Bool +is_comment_to(Text_no comment, + Text_stat *parent) +{ + int i; + + for ( i = 0; i < parent->no_of_misc; i++ ) + { + switch( parent->misc_items[ i ].type ) + { + case comm_in: + if ( parent->misc_items[ i ].datum.commented_in == comment ) + return TRUE; + break; + default: + break; + } + } + + return FALSE; +} + +static Bool +is_commented_in(Text_no parent, + Text_stat *child) +{ + int i; + + for ( i = 0; i < child->no_of_misc; i++ ) + { + switch( child->misc_items[ i ].type ) + { + case comm_to: + if ( child->misc_items[ i ].datum.comment_to == parent ) + return TRUE; + break; + default: + break; + } + } + + return FALSE; +} + +static Bool +is_footnote_to(Text_no footnote, + Text_stat *parent) +{ + int i; + + for ( i = 0; i < parent->no_of_misc; i++ ) + { + switch( parent->misc_items[ i ].type ) + { + case footn_in: + if ( parent->misc_items[ i ].datum.footnoted_in == footnote ) + return TRUE; + break; + default: + break; + } + } + + return FALSE; +} + +static Bool +is_footnoted_in(Text_no parent, + Text_stat *child) +{ + int i; + + for ( i = 0; i < child->no_of_misc; i++ ) + { + switch( child->misc_items[ i ].type ) + { + case footn_to: + if ( child->misc_items[ i ].datum.footnote_to == parent ) + return TRUE; + break; + default: + break; + } + } + + return FALSE; +} + +Member * +locate_member(Pers_no pers_no, + Conference * conf_c) +{ + Member * member; + int i; + + for(member = conf_c->members.members, i = conf_c->members.no_of_members; + i > 0; i--, member++) + { + if ( member->member == pers_no ) + { + return member; + } + } + + return NULL; +} + +/* + * Delete a misc_info. + * If it is a recpt, cc_recpt, comm_to or footn_to delete any + * loc_no, rec_time, sent_by or sent_at that might follow it. + * + * Note that the Misc_info is not reallocated. + */ + +static void +delete_misc (Text_stat *tstat, + Misc_info *misc) /* Pointer to first misc_item to delete. */ +{ + int del = 1; /* Number of items to delete. */ + /* Always delete at least one item. */ + Bool ready; + + /* Check range of misc */ + + if (misc < tstat->misc_items + || misc >= tstat->misc_items + tstat->no_of_misc ) + { + restart_kom("delete_misc() - misc out of range"); + } + + ready = FALSE; + + while (ready == FALSE + && misc + del < tstat->misc_items + tstat->no_of_misc ) + { + switch ( misc[ del ].type ) + { + case loc_no: + case rec_time: + case sent_by: + case sent_at: + del++; + break; + + case recpt: + case cc_recpt: + case footn_to: + case footn_in: + case comm_to: + case comm_in: + ready = TRUE; + break; + +#ifndef COMPILE_CHECKS + default: + restart_kom("delete_misc() - illegal misc found.\n"); +#endif + } + } + + tstat->no_of_misc -= del; + + /* Move items beyond the deleted ones. */ + + while ( misc < tstat->misc_items + tstat->no_of_misc ) + { + misc[ 0 ] = misc[ del ]; + misc++; + } +} + +static int +confirm(char *question) +{ + if ( iflag ) + { + fputs(question, stdout); + fputs(" (y/n) ", stdout); + while(1) + switch(getchar()) + { + case 'y': + case 'Y': + return 1; + case 'n': + case 'N': + case EOF: + return 0; + default: + break; + } + } + else + return 0; +} + + +static long +check_misc_infos(Text_no tno, + Text_stat *tstat) +{ + const Misc_info * misc = tstat->misc_items; + Misc_info * previous; + Misc_info_group group; + Conference *c; + Text_stat *t; + + long error=0; + + while (previous = (Misc_info *)misc, + group = parse_next_misc(&misc, + tstat->misc_items + tstat->no_of_misc), + group.type != m_end_of_list && group.type != m_error ) + { + switch ( group.type ) + { + case m_recpt: + c = cached_get_conf_stat (group.recipient); + if ( c == NULL && group.recipient == 0 ) + { + log ("Conference 0 is recipient to text %lu.\n", (u_long)tno); + if (rflag || confirm("Repair by deleting misc_item? ")) + { + delete_misc(tstat, previous); + mark_text_as_changed(tno); + modifications++; + log("Repaired: Conference 0 is no longer a recipient.\n"); + misc = previous; + } + else + error++; + + break; + } + + if ( c == NULL ) + break; + + /* Check loc_no */ + if ( group.local_no < c->texts.first_local_no ) + { + log("Text %lu: Recipient %lu<%lu> loc_no is less than %lu\n", + (u_long)tno, (u_long)group.recipient, + (u_long)group.local_no, + (u_long)c->texts.first_local_no); + error++; + } + else if ( c->texts.first_local_no + c->texts.no_of_texts - 1 + < group.local_no ) + { + log("Text %lu: Recipient %lu<%lu> loc_no" + " is greater than %lu\n", + (u_long)tno, (u_long)group.recipient, + (u_long)group.local_no, + (u_long)(c->texts.first_local_no + + c->texts.no_of_texts - 1)); + error++; + } + else if ( c->texts.texts[group.local_no + - c->texts.first_local_no] != tno ) + { + log("Text %lu: Recipient %lu<%lu>: that local number " + "is mapped to %lu.\n", + (u_long)tno, (u_long)group.recipient, + (u_long)group.local_no, + (u_long)c->texts.texts[group.local_no + - c->texts.first_local_no]); + error++; + } + + break; + + case m_cc_recpt: + c = cached_get_conf_stat (group.cc_recipient); + if ( c == NULL && group.cc_recipient == 0 ) + { + log ("Conference 0 is cc_recipient to text %lu.\n", + (u_long)tno); + if (rflag || confirm("Repair by deleting misc_item? ")) + { + delete_misc(tstat, previous); + mark_text_as_changed(tno); + modifications++; + log("Repaired: Conference 0 is no longer " + "a cc_recipient.\n"); + misc = previous; + } + else + error++; + + break; + } + + if ( c == NULL ) + break; + + /* Check loc_no */ + if ( group.local_no < c->texts.first_local_no ) + { + log("Text %lu: CC_Recipient %lu<%lu> is less than %lu\n", + (u_long)tno, (u_long)group.cc_recipient, + (u_long)group.local_no, + (u_long)c->texts.first_local_no); + error++; + } + else if ( c->texts.first_local_no + c->texts.no_of_texts - 1 + < group.local_no ) + { + log("Text %lu: CC_Recipient %lu<%lu> loc_no is " + "greater than %lu\n", + (u_long)tno, (u_long)group.cc_recipient, + (u_long)group.local_no, + (u_long)(c->texts.first_local_no + + c->texts.no_of_texts - 1)); + error++; + } + else if ( c->texts.texts[group.local_no + - c->texts.first_local_no] != tno ) + { + log("Text %lu: CC_Recipient %lu<%lu>: that local " + "number is mapped to %lu.\n", + (u_long)tno, (u_long)group.cc_recipient, + (u_long)group.local_no, + (u_long)c->texts.texts[group.local_no + - c->texts.first_local_no]); + error++; + } + + break; + + case m_comm_to: + t = cached_get_text_stat(group.comment_to); + + if ( t == NULL ) + { + log("Text %lu is a comment to %lu, which doesn't exist.\n", + (u_long)tno, (u_long)group.comment_to); + + if (rflag || confirm("Repair by deleting misc_item? ")) + { + delete_misc(tstat, previous); + mark_text_as_changed(tno); + modifications++; + log("Repaired: Comment-link deleted.\n"); + misc = previous; + } + else + error++; + + error++; + } + else if (!is_comment_to(tno, t)) + { + log("Text %lu is a comment to %lu, but not the reverse.\n", + (u_long)tno, (u_long)group.comment_to); + error++; + } + + break; + + case m_comm_in: + t = cached_get_text_stat(group.commented_in); + + if ( t == NULL ) + { + log("Text %lu is commented in %lu, which doesn't exist.\n", + (u_long)tno, (u_long)group.commented_in); + + if (rflag || confirm("Repair by deleting misc_item? ")) + { + delete_misc(tstat, previous); + mark_text_as_changed(tno); + modifications++; + log("Repaired: Comment-link deleted.\n"); + misc = previous; + } + else + error++; + } + else if (!is_commented_in(tno, t)) + { + log("Text %lu is a comment to %lu, but not the reverse.\n", + (u_long)tno, (u_long)group.commented_in); + error++; + } + + break; + + case m_footn_to: + t = cached_get_text_stat(group.footnote_to); + + if ( t == NULL ) + { + log("Text %lu is a footnote to %lu, which doesn't exist.\n", + (u_long)tno, (u_long)group.footnote_to); + + if (rflag || confirm("Repair by deleting misc_item? ")) + { + delete_misc(tstat, previous); + mark_text_as_changed(tno); + modifications++; + log("Repaired: Footnote-link deleted.\n"); + misc = previous; + } + else + error++; + } + else if (!is_footnote_to(tno, t)) + { + log("Text %lu is a footnote to %lu, but not the reverse.\n", + (u_long)tno, (u_long)group.footnote_to); + error++; + } + + break; + + case m_footn_in: + t = cached_get_text_stat(group.footnoted_in); + + if ( t == NULL ) + { + log("Text %lu is footnoted in %lu, which doesn't exist.\n", + (u_long)tno, (u_long)group.footnoted_in); + + if (rflag || confirm("Repair by deleting misc_item? ")) + { + delete_misc(tstat, previous); + mark_text_as_changed(tno); + modifications++; + log("Repaired: Footnote-link deleted.\n"); + misc = previous; + } + else + error++; + } + else if (!is_footnoted_in(tno, t)) + { + log("Text %lu is a footnot to %lu, but not the reverse.\n", + (u_long)tno, (u_long)group.footnoted_in); + error++; + } + + break; + + default: + log("check_misc_infos(): parse_next_misc returned type %lu\n", + (u_long)group.type); + break; + } + } + + if ( group.type == m_error ) + { + log("Text %lu has a bad misc_info_list.\n", (u_long)tno); + error++; + } + + return error; +} + + + + +static long +check_texts(void) +{ + Text_no ct = 0; + Text_stat *ctp=NULL; + long errors = 0; + Text_no number_of_texts = 0; + u_long bytes=0; + u_long max_bytes=0; + Text_no max_text=0; + + while ( ct = traverse_text(ct) ) + { + number_of_texts++; + + ctp = cached_get_text_stat( ct ); + if ( ctp == NULL ) + { + log("Text %lu nonexistent.\n", ct); + errors++; + } + else + { + bytes += ctp->no_of_chars; + if ( ctp->no_of_chars > max_bytes ) + { + max_bytes = ctp->no_of_chars; + max_text = ct; + } + + /* no_of_marks is not yet checked. */ + errors += check_misc_infos(ct, ctp); + } + } + + if (vflag) + { + if ( number_of_texts == 0 ) + log("WARNING: No texts found.\n"); + else + { + log("Total of %lu texts (total %lu bytes, " + "average %lu bytes/text).\n", + (u_long)number_of_texts, + (u_long)bytes, + (u_long)(bytes/number_of_texts)); + log("Longest text is %lu (%lu bytes).\n", + (u_long)max_text, (u_long)max_bytes); + } + } + + return errors; +} + +static Bool +adjust_text_list(Text_list *text_list) +{ + u_long zeroes; + u_long i; + + for (zeroes = 0; + zeroes < text_list->no_of_texts && text_list->texts[ zeroes ] == 0; + zeroes++) + ; + + if ( zeroes > 0 ) + { + text_list->no_of_texts -= zeroes; + text_list->first_local_no += zeroes; + + for ( i = 0; i < text_list->no_of_texts; i++) + text_list->texts[ i ] = text_list->texts[ i + zeroes ]; + + text_list->texts = srealloc(text_list->texts, + (text_list->no_of_texts + * sizeof(Text_no))); + } + + return zeroes > 0; +} + +static int +check_created_texts(Pers_no pno, + Text_list *created) +{ + u_long i; + Text_stat *t; + int error=0; + + for ( i=0; i < created->no_of_texts; i++ ) + { + if (created->texts[i] != 0) + { + t = cached_get_text_stat(created->texts[i]); + if ( t != NULL && t->author != pno) + { + log("Person %lu is author of text %lu whose author is %lu.\n", + (u_long)pno, (u_long)created->texts[i], + (u_long)t->author); + error++; + } + + if ( t == NULL ) + { + log("Person %lu is author of text %lu, which doesn't exist.\n", + (u_long)pno, (u_long)created->texts[i]); + if ( rflag || + confirm("Repair by setting to text_no to 0 in local map")) + { + created->texts[i] = 0; + mark_person_as_changed(pno); + modifications++; + log("Repaired: created_texts corrected.\n"); + } + else + error++; + } + } + } + + if ( created->no_of_texts > 0 && created->texts[0] == 0 ) + { + log("Person %lu has a bad created_texts array. Starts with a 0.\n", + (u_long)pno); + if ( rflag || confirm ("Repair by adjusting created_texts")) + { + adjust_text_list(created); + mark_person_as_changed(pno); + modifications++; + log("Repaired: created_texts adjusted.\n"); + } + else + error++; + } + + return error; +} + +static int +check_membership(Pers_no pno, + const Membership *mship) +{ + int error=0; + Conference *conf; + int i; + Local_text_no last=0; + + conf = cached_get_conf_stat(mship->conf_no); + if ( conf == NULL ) + { + log("Person %lu is a member in the non-existing conference %lu.\n", + (u_long)pno, (u_long)mship->conf_no); + error++; + } + else + { + /* Check read texts */ + if ( mship->last_text_read > + conf->texts.first_local_no + conf->texts.no_of_texts - 1) + { + log("Person %lu has read text %lu in conf %lu, " + "which only has %lu texts.\n", + (u_long)pno, + (u_long)mship->last_text_read, + (u_long)mship->conf_no, + (u_long)(conf->texts.first_local_no + + conf->texts.no_of_texts - 1)); + error++; + } + + for ( last = i = 0; i < mship->no_of_read; i++) + { + if ( mship->read_texts[i] <= last ) + { + log("Person %lu's membership in %lu is corrupt:" + " read text number %lu<%lu> <= %lu.\n", + (u_long)pno, (u_long)mship->conf_no, + (u_long)mship->read_texts[i], (u_long)i, (u_long)last); + error++; + } + + last = mship->read_texts[i]; + } + + /* Check that he is a member */ + if ( locate_member(pno, conf) == NULL ) + { + log("Person %lu is a member in %lu in which he isn't a member.\n", + (u_long)pno, (u_long)mship->conf_no); + error++; + } + } + + return error; +} + + + +static int +check_membership_list(Pers_no pno, + const Membership_list *mlist) +{ + int errors=0; + int i; + + for (i = 0; i < mlist->no_of_confs; i++) + errors += check_membership(pno, &mlist->confs[i]); + + return errors; +} + + +static int +check_persons(void) +{ + Pers_no cp = 0; + Person *pstat=NULL; + Conference *cstat=NULL; + long errors = 0; + Pers_no number_of_persons=0; + + while ( cp = traverse_person(cp) ) + { + number_of_persons++; + pstat = cached_get_person_stat (cp); + cstat = cached_get_conf_stat (cp); + + if ( pstat == NULL ) + { + log("Person %lu nonexistent.\n", (u_long)cp); + errors++; + } + else if (cstat == NULL) + { + log("Person %lu has no conference.\n", (u_long)cp); + errors++; + } + else if (!cstat->type.letter_box) + { + log("Person %lu's conference is not a letter_box.\n", + (u_long)cp); + errors++; + } + else + { + errors += (check_created_texts(cp, &pstat->created_texts) + + check_membership_list(cp, &pstat->conferences)); + } + } + + if (vflag) + log("Total of %lu persons.\n", number_of_persons); + + return errors; +} + +static Bool +is_recipient(Conf_no conf_no, + Text_stat * t_stat) +{ + int i; + + for ( i = 0; i < t_stat->no_of_misc; i++ ) + { + switch( t_stat->misc_items[ i ].type ) + { + case recpt: + if ( t_stat->misc_items[ i ].datum.recipient == conf_no ) + { + return TRUE; + } + break; + + case cc_recpt: + if ( t_stat->misc_items[ i ].datum.cc_recipient == conf_no ) + { + return TRUE; + } + break; + + case rec_time: + case comm_to: + case comm_in: + case footn_to: + case footn_in: + case sent_by: + case sent_at: + case loc_no: + break; + +#ifndef COMPILE_CHECKS + default: + restart_kom("is_recipient(): illegal misc_item\n"); +#endif + } + } + + return FALSE; +} + +static int +check_texts_in_conf(Conf_no cc, + Text_list *tlist) +{ + u_long i; + Text_stat *t; + int error=0; + + for ( i=0; i < tlist->no_of_texts; i++ ) + { + if (tlist->texts[i] != 0) + { + t = cached_get_text_stat(tlist->texts[i]); + if ( t == NULL ) + { + log("Text %lu<%lu> in conference %lu is non-existent.\n", + (u_long)tlist->texts[i], + (u_long)i + tlist->first_local_no, + (u_long)cc); + + if (rflag + || confirm("Repair by setting Text_no to 0 in the map?") ) + { + tlist->texts[i]=0; + mark_conference_as_changed(cc); + modifications++; + log("Repaired: %lu is no longer a recipient.\n", + (u_long)cc); + } + else + error++; + } + else + { + if ( !is_recipient(cc, t) ) + { + log("Text %lu<%lu> in conference %lu doesn't " + "have the conference as recipient.\n", + (u_long)tlist->texts[i], + (u_long)i + tlist->first_local_no, + (u_long)cc); + + if (confirm("Repair by setting Text_no to 0 in the map?") ) + { + tlist->texts[i]=0; + mark_conference_as_changed(cc); + modifications++; + log("Repaired: %lu is no longer a recipient.\n", + (u_long)cc); + } + else + error++; + } + } + } + } + + if ( tlist->no_of_texts > 0 && tlist->texts[0] == 0 ) + { + log("Conference %lu has a bad Text_list. Starts with a 0.\n", + (u_long)cc); + if ( rflag || confirm ("Repair by adjusting text_list")) + { + adjust_text_list(tlist); + mark_conference_as_changed(cc); + modifications++; + log("Repaired: text_list adjusted.\n"); + } + else + error++; + } + + return error; +} + +Membership * +locate_membership(Conf_no conf_no, + Person * pers_p) +{ + Membership * confp; + int i; + + for(confp = pers_p->conferences.confs, i = pers_p->conferences.no_of_confs; + i > 0; i--, confp++) + { + if ( confp->conf_no == conf_no ) + { + return confp; + } + } + + return NULL; +} + +static int +check_member(Conf_no cc, + Member *memb) +{ + Person *pp; + int error=0; + + pp = cached_get_person_stat(memb->member); + if ( pp == NULL ) + { + log("Person %lu, who is supposed to be a member in conf %lu, " + "is nonexistent.\n", + (u_long)memb->member, (u_long)cc); + error++; + } + else + { + if ( locate_membership(cc, pp) == NULL ) + { + log("Person %lu is not a member in conf %lu.\n", + (u_long)memb->member, + (u_long)cc); + error++; + } + } + + return error; +} + +static int +check_member_list(Conf_no cc, + const Member_list *mlist) +{ + int errors=0; + int i; + + for (i = 0; i < mlist->no_of_members; i++) + errors += check_member(cc, &mlist->members[i]); + + return errors; +} + + +static int +check_confs(void) +{ + Conf_no cc = 0; + Person *pstat=NULL; + Conference *cstat=NULL; + long errors = 0; + Conf_no number_of_confs = 0; + + while ( (cc = traverse_conference(cc)) != 0 ) + { + number_of_confs++; + cstat = cached_get_conf_stat (cc); + + if ( cstat == NULL ) + { + log("Conference %lu nonexistent.\n", (u_long)cc); + errors++; + } + else + { + if (cstat->type.letter_box) + { + pstat = cached_get_person_stat(cc); + if (pstat == NULL) + { + log("Mailbox %lu has no person.\n", (u_long)cc); + errors++; + } + } + else /* not letter_box */ + { + /* Remember that the creator might no longer exist. */ + if ( person_scratchpad[ cstat->creator ] != NULL ) + ++person_scratchpad[ cstat->creator ]->created_confs; + } + + errors += (check_texts_in_conf(cc, &cstat->texts) + + check_member_list(cc, &cstat->members)); + } + } + + if ( vflag ) + log("Total of %lu conferences.\n", (u_long)number_of_confs); + + return errors; +} + +static void +init_person_scratch(void) +{ + Pers_no pno = 0; + + while( (pno = traverse_person(pno)) != 0 ) + { + person_scratchpad[pno] = alloc_person_scratchpad(); + } +} + +static long +post_check_persons(void) +{ + long errors = 0; + + Pers_no pers_no = 0; + Person *pstat; + + while ( (pers_no = traverse_person(pers_no)) != 0 ) + { + if ( (pstat = cached_get_person_stat(pers_no)) == NULL ) + { + log("INTERNAL DBCK ERROR: post_check_persons(): can't " + "cached_get_person_stat(%d).\n", pers_no); + } + + if ( person_scratchpad[pers_no]->created_confs + != pstat->created_confs ) + { + log("Person %d has created %d conferences, not %d (as said in " + "his person-stat).\n", + pers_no, person_scratchpad[pers_no]->created_confs, + pstat->created_confs); + if ( rflag || confirm("Repair by altering person-stat? ") ) + { + pstat->created_confs + = person_scratchpad[pers_no]->created_confs; + mark_person_as_changed(pers_no); + log("Person-stat corrected.\n"); + } + else + errors++; + } + } + + return errors; +} + + +/* + * Returns 0 if the database seems to be correct. + */ +static long +check_data_base(void) +{ + long errors; + + init_person_scratch(); + errors = check_texts() + check_persons() + check_confs(); + return errors + post_check_persons(); +} + +static void +init_data_base(void) +{ + if (dbase_dir == NULL) + dbase_dir = DEFAULT_DBASE_DIR; + + sprintf(datafilename, "%s/%s", dbase_dir, DATAFILE_NAME); + sprintf(backupfilename, "%s/%s", dbase_dir, BACKUPFILE_NAME); + sprintf(textfilename, "%s/%s", dbase_dir, TEXTFILE_NAME); + sprintf(textbackupfilename, "%s/%s", dbase_dir, TEXTBACKUPFILE_NAME); + + if ( vflag ) + { + log("Database = %s\n", datafilename); + log("Backup = %s\n", backupfilename); + log("Text = %s\n", textfilename); + log("Textback = %s\n", textbackupfilename); + } + + if ( init_cache() == FAILURE ) + restart_kom("Can't find database.\n"); +} + +void +garb_text_file(void) +{ + Text_no tno = 0; + String text; + + log("Renaming %s to %s", textfilename, textbackupfilename); + rename(textfilename, textbackupfilename); + log("Writing texts to (new) %s", textfilename); + fflush(stdout); + fflush(stderr); + cache_open_new_text_file(); + + while ( (tno = traverse_text(tno)) != 0 ) + { + text = cached_get_text(tno); + cached_flush_text(tno, text); + free_tmp(); + } + log("Writing datafile with new indexes.\n"); + fflush(stdout); + fflush(stderr); + cache_sync(); + log("Ready."); +} + + + + + +int +main (int argc, + char **argv) +{ + int i; + int errors; + BUGDECL; + + for (i = 1; i < argc && argv[i][0] == '-'; i++) + { + switch (argv[i][1]) + { +#ifdef DEBUG + case 'd': + buglevel++; + break; +#endif + + case 'D': /* Database directory */ + dbase_dir = argv[i]+2; + break; + + case 'i': /* Running interactively. */ + iflag++; /* Will ask user and try to repair. */ + break; + + case 'r': /* Repair simple errors wihtout asking. */ + rflag++; + break; + + case 'v': /* Verbose: report more than errors. */ + vflag++; + break; + + case 'g': /* Garbage collect: compress text-file. */ + gflag++; + break; + + default: + restart_kom("usage: dbck [options]\n"); + } + } + + s_set_storage_management(smalloc, srealloc, sfree); + + init_data_base(); + errors = check_data_base(); + if ( iflag ) + log("Total of %d error%s remains.\n", errors, errors == 1 ? "" : "s"); + else if ( vflag && errors > 0 ) + log("%d error%s found.\n", errors, errors == 1 ? "" : "s"); + + if ( modifications > 0 ) + { + log("%d modification%s made. Syncing...\n", + modifications, modifications == 1 ? "" : "s"); + fflush(stdout); + fflush(stderr); + cache_sync(); + log("ready.\n"); + } + + if ( modifications == 0 && errors == 0 && gflag ) + { + log("No errors found. Compressing textfile.\n"); + fflush(stdout); + fflush(stderr); + garb_text_file(); + log("ready.\n"); + } + + return errors != 0; +} diff --git a/src/server/disk-cache.h b/src/server/disk-cache.h new file mode 100644 index 0000000000000000000000000000000000000000..ecc0e46c150f71e879c70f222d1ee8fb06950736 --- /dev/null +++ b/src/server/disk-cache.h @@ -0,0 +1,4 @@ +/* Returns TRUE when the sync is ready. */ +Bool +sync_part(void); + diff --git a/src/server/disk-end-of-atomic.c b/src/server/disk-end-of-atomic.c new file mode 100644 index 0000000000000000000000000000000000000000..76218d5018f4f032421aa442c01aac24916c8552 --- /dev/null +++ b/src/server/disk-end-of-atomic.c @@ -0,0 +1,36 @@ +/* + * end-of-atomic.c + * + * This is the magic function which is called at the end of every atomic + * call to the server. It is responisble for + * Free:ing all tmp_alloc:ated memory + * Throw out some cached data if necessary + * Forget some old texts if necessary + * Save some items to disk if saving + * + * idle is TRUE if the server has no pending calls. That might be a good time + * to forget old texts. + */ + +#include <kom-types.h> +#include "smalloc.h" +#include "text-garb.h" +#include "config.h" +#include "disk-cache.h" + +long +end_of_atomic( Bool idle ) +{ + int timeout = TIMEOUT; + + free_tmp(); + + if ( idle ) + if ( garb_text() == FALSE ) + timeout = GARBTIMEOUT; + + if ( sync_part() == FALSE ) + timeout = SYNCTIMEOUT; + + return timeout; +} diff --git a/src/server/end-of-atomic.h b/src/server/end-of-atomic.h new file mode 100644 index 0000000000000000000000000000000000000000..18877d0d7d9686585127a37ec9abd2b73ad846fe --- /dev/null +++ b/src/server/end-of-atomic.h @@ -0,0 +1,10 @@ +/* + * end-of-atomic.c + */ + +/* + * Return value X: end_of_atomic() should be called again within the + * next X milliseconds. + */ +long +end_of_atomic( Bool idle ); diff --git a/src/server/exp.h b/src/server/exp.h new file mode 100644 index 0000000000000000000000000000000000000000..477e54a39f69d48f3174da5f8b024d430ce58712 --- /dev/null +++ b/src/server/exp.h @@ -0,0 +1,2 @@ +#define EXPORT +#define INTERNAL static diff --git a/src/server/internal-connections.c b/src/server/internal-connections.c new file mode 100644 index 0000000000000000000000000000000000000000..a7e061390e07d2b5f165b9a44b976f1596030519 --- /dev/null +++ b/src/server/internal-connections.c @@ -0,0 +1,203 @@ +/* + * internal-connections.c + * + * Abstract routines on the data type Connection. + */ + +#include "exp.h" +#include <kom-types.h> +#include "smalloc.h" +#include "com.h" +#include "connections.h" +#include "internal-connections.h" +#include "lyskomd.h" +#include "config.h" + +INTERNAL Connection *all_connections = NULL; + +/* Used in get_conn_by_number to speed things up. */ + +INTERNAL Connection *last_conn = NULL; + +INTERNAL const Connection EMPTY_CONNECTION = + ((Connection){0, NULL, NULL, 0, NULL, NO_TIME, 0, + EMPTY_STRING_i, 0, EMPTY_STRING_i, EMPTY_STRING_i, + NULL, '\0', + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, EMPTY_STRING_i, EMPTY_STRING_i, + EMPTY_STRING_i, NULL, NULL, DEFAULT_PRIV_BITS_i, + NULL_CONF_TYPE_i, EMPTY_STRING_i, 0, FALSE, NO_TIME, + 0, FALSE}); + +INTERNAL Connection * +alloc_connection(void) +{ + Connection *res; + + res = smalloc ( sizeof(Connection) ); + *res = EMPTY_CONNECTION; + return res; +} + +EXPORT Connection * +new_client(void) +{ + Connection *c; + static Session_no no_of_connection_attempts = 0; + + c = alloc_connection(); + + c->magic = CONN_MAGIC_ALLOC; + + if (all_connections != NULL) + { + all_connections->prev->next = c; + c->prev = all_connections->prev; + all_connections->prev = c; + c->next = all_connections; + } + else + { + c->prev = c; + c->next = c; + } + + all_connections = c; + + c->session_no = ++no_of_connection_attempts; + c->protocol = '\0'; /* Reserved to mean 'no protocol specified yet' */ + c->unparsed = EMPTY_STRING; + c->first_to_parse = 0; + c->more_to_parse = TRUE; + c->last_request = NO_TIME; + c->pers_no = 0; + c->person = NULL; + time( & c->session_start ); + c->cwc = 0; + c->what_am_i_doing = EMPTY_STRING; + c->ena_level = 0; + c->username = EMPTY_STRING; + c->kill_me = FALSE; + + return c; +} + +/* + * Only used from logout_client + */ +EXPORT void +kill_client(Connection *cp) +{ + if ( all_connections == NULL ) + restart_kom("kill_client(): No clients in all_connections list\n"); + + if (all_connections == all_connections->next) + { + if (all_connections->prev != all_connections) + restart_kom("kill_client(): all_connections corrupt (LINK)\n"); + + if (all_connections != cp) + restart_kom("kill_client(): all_connections corrupt (SINGLE)\n"); + + all_connections = NULL; + } + else + { + if (cp->prev == NULL || cp->next == NULL) + restart_kom("kill_client(): all_connections corrupt (NULL)\n"); + + cp->prev->next = cp->next; + cp->next->prev = cp->prev; + + if (all_connections == cp) + all_connections = cp->next; + } + + if ( last_conn == cp ) + last_conn = NULL; + + cp->magic = CONN_MAGIC_FREE; + sfree(cp); +} + +#ifdef DEFENSIVE_CHECKS + +INTERNAL void +check_conn_exists(Connection *foo) +{ + /* Check that foo really is active. */ + + Connection *c = all_connections; + Bool found = FALSE; + + do + { + if ( c == foo ) + found = TRUE; + + c = c->next; + } + while ( c != all_connections && found == FALSE ); + + if ( found == FALSE ) + restart_kom("get_conn_by_number: foo == %d not found in" + " all_connections.\n", last_conn->session_no); +} + +#endif + +/* + * Session_nos must NOT be recycled, or this code might break! + */ +EXPORT Connection * +get_conn_by_number (Session_no session_no) +{ + Connection *end; + + if ( last_conn == NULL ) + last_conn = all_connections; + else if ( all_connections == NULL ) + { + restart_kom("get_conn_by_number(%d): last_conn = %d and " + "all_connections == NULL", session_no, + last_conn->session_no); + } +#ifdef DEFENSIVE_CHECKS + else + check_conn_exists(last_conn); +#endif + + end = last_conn; + + do + { + if ( last_conn->session_no == session_no ) + return last_conn; + + last_conn = last_conn->next; + } + while ( last_conn != end); + + return NULL; +} + + +EXPORT Session_no +traverse_connections (Session_no session_no) +{ + Connection *prev = NULL; + + if ( all_connections == NULL ) + return 0; + + prev = get_conn_by_number ( session_no ); + + /* prev is NULL if session_no is 0, or if session_no was logged out. */ + + if ( prev == NULL ) + return all_connections->session_no; + else if ( prev->next == all_connections ) + return 0; /* Full circle. */ + else + return prev->next->session_no; +} diff --git a/src/server/internal-connections.h b/src/server/internal-connections.h new file mode 100644 index 0000000000000000000000000000000000000000..a2885b2f95a68444b4654052a0f7deeb2d311a49 --- /dev/null +++ b/src/server/internal-connections.h @@ -0,0 +1,19 @@ +/* + * internal-connections.c + * + * Abstract routines on the data type Connection. + */ + +extern Connection * +new_client(void); + +extern void +kill_client(Connection *cp); + + +extern Connection * +get_conn_by_number (Session_no session_no); + + +extern Session_no +traverse_connections (Session_no session_no); diff --git a/src/server/internal-services.h b/src/server/internal-services.h new file mode 100644 index 0000000000000000000000000000000000000000..82eb12352b6a4a6a36cf9092b06a898c791f38c5 --- /dev/null +++ b/src/server/internal-services.h @@ -0,0 +1,34 @@ +/* + * server/services.h + * + * F|rv{xla inte denna fil med services.h! + * + * I denna fil ligger en del funktioner typ do_delete_text som anropas + * b}de av servern (n{r en text blir f|r gammal) och av klienten (n{r + * en av{ndare kom p} att han inte ville ha texten kvar) + * + * Funktionerna i denna fil har kontrollerar inte att ACTPERS har r{tt att + * g|ra det som g|rs. + */ + + +extern Bool logins_allowed; /* Defined in server/services.c */ + +/* + * Create a conference. + * + * Returns (Conf_no) 0 if there is an error. + */ + +extern Conf_no +do_create_conf(String name, + Pers_no creator, + Conf_no supervisor, + Conf_no super_conf, + Conf_type type); + + + +extern Success +do_delete_text(Text_no text_no, + Text_stat *text_s); diff --git a/src/server/isc-interface.h b/src/server/isc-interface.h new file mode 100644 index 0000000000000000000000000000000000000000..56d2b007a264d4648295486382b3a237d208a198 --- /dev/null +++ b/src/server/isc-interface.h @@ -0,0 +1,6 @@ +/* + * Wrapper around <isc.h> to ensure that ISC_UDGTYPE is always correct. + */ + +#define ISC_UDGTYPE struct mux +#include <isc.h> diff --git a/src/server/isc-parse.c b/src/server/isc-parse.c new file mode 100644 index 0000000000000000000000000000000000000000..1ca6b6e07acf2b53c841003f7275eef90542bce1 --- /dev/null +++ b/src/server/isc-parse.c @@ -0,0 +1,32 @@ +/* + * Generic parse routines. + */ + +#include <setjmp.h> +#include <string.h> +#include <stdio.h> +#include "lyskomd.h" +#include <kom-types.h> +#include "com.h" +#include "connections.h" +#include "isc-parse.h" + +int +parse_char(Connection *client) +{ + if ( client->unparsed.len <= client->first_to_parse ) + longjmp(parse_env, ISC_MSG_INCOMPLETE); + + return client->unparsed.string[ client->first_to_parse++ ]; +} + + +int +parse_nonwhite_char(Connection *client) +{ + int c; + + while ( strchr(" \t\n\r", c=parse_char(client) ) != NULL ) + ; + return c; +} diff --git a/src/server/isc-parse.h b/src/server/isc-parse.h new file mode 100644 index 0000000000000000000000000000000000000000..254bf1a8a466871c5f4c7d52cb04e886785b5e8f --- /dev/null +++ b/src/server/isc-parse.h @@ -0,0 +1,9 @@ +#define ISC_PROTOCOL_ERR 1 +#define ISC_MSG_INCOMPLETE 2 +#define ISC_LOGOUT 3 + +int +parse_char(Connection *client); + +int +parse_nonwhite_char(Connection *client); diff --git a/src/server/kom-types.c b/src/server/kom-types.c new file mode 100644 index 0000000000000000000000000000000000000000..bf2f48ec5d6f03eb39a735861d06bc6a547ffc76 --- /dev/null +++ b/src/server/kom-types.c @@ -0,0 +1,69 @@ +/* + * kom-types.c + * Definition of constants and other thing that + * can't or shouldn't be defined in header files. + * + * + * Copyright (C) 1990 Lysator Computer Club + * Linkoping University, Sweden + * + * Everyone is granted permission to copy, modify and redistribute + * this code, provided the people they give it to can. + * + * + * Author: Thomas Bellman + * Lysator Computer Club + * Linkoping University + * Sweden + * + * email: bellman@Lysator.LiU.SE + */ + + +#include <stdio.h> + +#include <kom-types.h> +#include <config.h> + +#define EXPORT + + +EXPORT const Conf_list_old EMPTY_CONF_LIST_OLD = EMPTY_CONF_LIST_OLD_i; + +EXPORT const Pers_list EMPTY_PERS_LIST = EMPTY_PERS_LIST_i; + +EXPORT const Mark_list EMPTY_MARK_LIST = EMPTY_MARK_LIST_i; + +EXPORT const Conf_type NULL_CONF_TYPE = NULL_CONF_TYPE_i; + +EXPORT const Membership EMPTY_MEMBERSHIP = EMPTY_MEMBERSHIP_i; + +EXPORT const Membership_list EMPTY_MEMBERSHIP_LIST = EMPTY_MEMBERSHIP_LIST_i; + +EXPORT const Text_list EMPTY_TEXT_LIST = EMPTY_TEXT_LIST_i; + +EXPORT const Member_list EMPTY_MEMBER_LIST = EMPTY_MEMBER_LIST_i; + +EXPORT const Session_info EMPTY_SESSION_INFO = EMPTY_SESSION_INFO_i; + +EXPORT const Who_info_old EMPTY_WHO_INFO_OLD = EMPTY_WHO_INFO_OLD_i; + +EXPORT const Who_info_list_old EMPTY_WHO_INFO_LIST_OLD = + EMPTY_WHO_INFO_LIST_OLD_i; + +EXPORT const Who_info EMPTY_WHO_INFO = EMPTY_WHO_INFO_i; + +EXPORT const Who_info_list EMPTY_WHO_INFO_LIST = EMPTY_WHO_INFO_LIST_i; + +EXPORT const Priv_bits DEFAULT_PRIV_BITS = DEFAULT_PRIV_BITS_i; + +EXPORT const Personal_flags DEFAULT_PERSONAL_FLAGS = DEFAULT_PERSONAL_FLAGS_i; + + +EXPORT const Person EMPTY_PERSON = EMPTY_PERSON_i; + +EXPORT const Conference EMPTY_CONFERENCE = EMPTY_CONFERENCE_i; + +EXPORT const Small_conf EMPTY_SMALL_CONF = EMPTY_SMALL_CONF_i; + +EXPORT const Text_stat EMPTY_TEXT_STAT = EMPTY_TEXT_STAT_i; diff --git a/src/server/log.c b/src/server/log.c new file mode 100644 index 0000000000000000000000000000000000000000..51f90cb6aaf51718d2b797306fcf0dde21722c03 --- /dev/null +++ b/src/server/log.c @@ -0,0 +1,40 @@ +/* + * log.c + * + * File created by ceder 1990-05-25. + */ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include "log.h" + +/* + * Add a string to the log file. + */ + +extern void +log (const char * format, ...) +{ + va_list AP; + + va_start(AP, format); + logv(format, AP); + va_end(AP); +} + + +extern void +logv (const char *format, va_list AP) +{ + time_t clock; + + + time(&clock); + + fprintf(stderr, "--LOGG-> %s: ", ctime(&clock)); + vfprintf(stderr, format, AP); + + fflush(stderr); +} diff --git a/src/server/log.h b/src/server/log.h new file mode 100644 index 0000000000000000000000000000000000000000..8bf6d8dcc955dd53a20eeb942fa51c1dcb8d78d4 --- /dev/null +++ b/src/server/log.h @@ -0,0 +1,22 @@ +/* + * log.h + * + * File created by ceder 1990-05-25. + */ + +#ifndef __LYSKOM__LOG_H__ +#define __LYSKOM__LOG_H__ + +/* + * Add a string to the log file. + */ + +extern void +log (const char * format, ...); + +#ifdef _STDARG_H + extern void + logv (const char * format, va_list AP); +#endif + +#endif diff --git a/src/server/logII.c b/src/server/logII.c new file mode 100644 index 0000000000000000000000000000000000000000..1f96c471497efa7e554971050f0442004dcbf543 --- /dev/null +++ b/src/server/logII.c @@ -0,0 +1,89 @@ +/* + * log.c + * + * File created by ceder 1990-05-25. + */ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> + +enum +{ + MESSAGE, /* No action when message_counter is 0. */ + WARNING, /* Sync&Restart when message_counter is 0. */ + RESTART, /* Sync&Restart immediately. */ + CRASH /* Don't sync, but restart immediately. */ + } log_class; + +typedef enum log_class Log_class; + + +extern void +log (Log_class class, + const char *file, + const char *function, + int *message_counter, + const char *format, ...) +{ + va_list AP; + time_t clock; + Bool log_it = TRUE; + Bool restart = FALSE; + + va_start(AP, format); + time(&clock); + + switch(log_class) + { + case MESSAGE: + if ( message_counter != NULL && message_counter > 0 ) + message_counter--; + else + log_it = FALSE; + break; + case WARNING: + if ( message_counter != NULL ) + if ( *message_counter > 0 ) + message_counter--; + else + restart = TRUE; + break; + case RESTART: + case CRASH: + break; +#ifndef COMPILE_CHECKS + default: + fprintf(stderr, "Deep Internal Error: log() called" + "with log_class %d.\n", log_class); +#endif + } + + if ( log_it == TRUE ) + { + fprintf(stderr, "--> %-19s %-29s %s", ctime(&clock)); + vfprintf(stderr, format, AP); + } + + if ( log_class == RESTART || (log_class == WARNING + && message_counter != NULL + && *message_counter <= 0)) + { + fprintf(stderr, "++> Syncing.\n"); + cache_sync(); + } + + if ( restart == TRUE || log_class == RESTART || log_class == CRASH ) + { + fprintf(stderr, "+++> Restarting LysKOM.\n"); +/* #include "CloseFileDescriptors" */ + execl("/usr/lyskom/bin/ramkomd", "ramkomd", NULL); + fprintf(stderr, "+++> execl() failed.\n"); + exit(1); + } + + va_end(AP); +} + + diff --git a/src/server/lyskomd.h b/src/server/lyskomd.h new file mode 100644 index 0000000000000000000000000000000000000000..420e3135abbd0e7259d6a4cbc67f5fc1063695b7 --- /dev/null +++ b/src/server/lyskomd.h @@ -0,0 +1,19 @@ +/* +** Defined in ramkomd.c /pen +*/ +extern int listen_client; +extern int listen_mux; + + +/* + * restart_kom is used to try to recover from an impossible error. + * This function is in fact never called, unless some cosmic radiation + * changes some pointers or suchlike. + * + * The msg string is sent as a mail to kom@lysator.liu.se + * (At least one member in that group should not read his mail using kom...) + */ + + +extern void +restart_kom(const char * format, ...); diff --git a/src/server/manipulate.h b/src/server/manipulate.h new file mode 100644 index 0000000000000000000000000000000000000000..bbb4d03c758b118ce8892aa7f104e541a57edd34 --- /dev/null +++ b/src/server/manipulate.h @@ -0,0 +1,653 @@ + +/* + * manipulate.h + * + * Skapad av ceder n}gon g}ng efter 1990-05-25, men f|re 1990-07-05. + */ + +/* + * Definition of macros used in services.c and manipulate.c + */ + +/* pers_no of the connected person. (0 if not logged in) */ +#define ACTPERS (active_connection->pers_no) + +/* person of the connected person. (NULL if not logged in) */ +#define ACT_P (active_connection->person) + +/* many functions can only be used if ACTPERS is logged in */ + +#define CHK_LOGIN(errortype) \ +{ \ + if ( ! ACTPERS ) \ + { \ + kom_errno = KOM_LOGIN; \ + return errortype; \ + } \ +} + + +/* Conference 0 does never exist. */ + +#define CONF_ZERO(conf, errortype) \ +{ \ + if ( (conf) == 0 ) \ + { \ + kom_errno = KOM_CONF_ZERO; \ + return errortype; \ + } \ +} + + +/* Check that a conference exists */ + +#define CHK_EXIST(conf, errortype) \ +{ \ + CONF_ZERO(conf, errortype); \ + if ( !cached_conf_exists( conf )) \ + { \ + kom_errno = KOM_UNDEF_CONF; \ + return errortype; \ + } \ +} + + +/* do a cached_get_person_stat */ + +#define GET_P_STAT(p_stat_p, pers_no, failure) \ +{ \ + if ( ((p_stat_p) = cached_get_person_stat( pers_no )) == NULL) \ + { \ + return (failure); \ + } \ +} + +/* do a cached_get_conf_stat */ + +#define GET_C_STAT(c_stat_p, conf_no, failure) \ +{ \ + if ( ((c_stat_p) = cached_get_conf_stat( conf_no )) == NULL) \ + { \ + return (failure); \ + } \ +} + +/* do a cached_get_text_stat */ + +#define GET_T_STAT(t_stat_p, text_no, failure) \ +{ \ + if ( ((t_stat_p) = cached_get_text_stat( text_no )) == NULL) \ + { \ + return (failure); \ + } \ +} + + +/* + * ENA returns TRUE if ACTPERS has his privtype bit set, and has + * enabled to at least req_lev. + */ + +#define ENA(privtype, req_lev) \ +(active_connection->ena_level >=(req_lev) && (ACT_P)->privileges.privtype) + + + +/* + * Add a misc item to a text_status. + * The item is put last on the list. Thus this macro should not be used + * to add rec_time. + * + * Text_stat * text_stat_pointer Textstatus to modify + * Info_type type_of_misc Type om misc_item to add + * tag_name Tagname in Info_datum + * value_of_misc Value to set tag_name to. + */ +#define ADD_MISC(text_stat_pointer, type_of_misc, tag_name, value_of_misc) \ +{ \ + /* Allocate space */ \ + text_stat_pointer->misc_items \ + = srealloc(text_stat_pointer->misc_items, \ + (++(text_stat_pointer->no_of_misc)) * sizeof(Misc_info));\ + \ + /* Set type */ \ + text_stat_pointer->misc_items[ text_stat_pointer->no_of_misc-1 ] \ + .type = type_of_misc; \ + \ + /* Set value */ \ + text_stat_pointer->misc_items[ text_stat_pointer->no_of_misc-1 ] \ + .datum.tag_name = value_of_misc; \ +} + + +/* + * IMPL - returns with error "not implemented yet" + */ + +#define IMPL(code) \ +{ \ + kom_errno = KOM_NOT_IMPL; \ + return code; \ +} + +/* + * A value of the following type are returned from access_perm() and + * fast_access_perm(). They are used to see how much ACTPERS is allowed to + * read/modify the data of a given conference. + * + * access_perm() alway returns the "highest" from this list. + * fast_access_perm() avoids reading the conference struct. It will only + * return "unlimited" if the conference is secret and ACTPERS + * is not a member of the conference. fast_access_perm() is used + * in lookup_name() which sometimes have to check the access + * permissions of _all_ conferences. If access_perm() was used + * _all_ conference statuses would have to be read in into the + * cache in a single atomic call, and that would mean that the + * cache would overflow if there are many conferences. + * Now only the conference statuses for secret conferences of + * which ACTPERS is not a member have to be read in. + */ + +typedef enum { + error, /* The conf doesn't exist or other error. */ + none, /* A secret conference. */ + read_protected, /* An rd_prot conference. */ + limited, /* An open conference. */ + member, /* ACTPERS is a member of the conference. */ + unlimited /* ACTPERS is supervisor of the conference. */ +} Access; + + + +/* + * Functions which manipulate the CONFERENCE struct. + */ + +extern Bool +legal_passwd(const String pwd); + +extern Success +do_set_passwd( Password pwd, + const String new_pwd); + + +extern Success +chk_passwd( Password pwd, + const String s ); + + +/* + * Locate the Member struct in CONF_C for person PERS_NO + */ + +extern Member * +locate_member(Pers_no pers_no, + Conference * conf_c); + + +/* + * Return TRUE if ACTPERS is a supervisor to CONF. + */ + +extern Bool +is_supervisor(Conf_no conf, + Conference * conf_c);/* Conference status for CONF or NULL */ + + + +/* + * Add text_no to the list of texts in a conference. Return the local number + * the text gets in this conference. + */ + +extern Local_text_no +add_text_in_conf(Conference * conf_c, + Text_no text_no); + + +/* + * Set the global text_no of a certain local text_no if that local text_no + * exists in the conf. No action is taken if the local text_no doesn't exist. + * This function is probably only used to set the number to zero when a text + * is deleted. + */ + +extern void +set_loc_no (Conference * conf_c, + Local_text_no loc_no, + Text_no text_no); + + + +/* + * This function is called whenever a person leaves a conf, + * i e when he pepsi():s or logout():s. + */ + +extern void +leave_conf(void); + + +/* + * Change presentation of a conference. If text_no is 0, there will be + * no presentation. + */ + +extern Success +do_set_presentation(Conf_no conf_no, + Conference * conf_c, + Text_no text_no); + + +/* + * Change motd of a conference. If text_no is 0, there will be + * no motd. + */ + +extern Success +do_set_etc_motd(Conf_no conf_no, + Conference * conf_c, + Text_no text_no); + + + +/* + * Delete a conference. Delete all references to this conf. + */ + +extern void +do_delete_conf (Conf_no conf_no, + Conference * conf_c); /* Not NULL */ + + +/* + * Functions that manupulate the PERSON struct + */ + +/* + * Find the data about PERS_P:s membership in CONF_NO. + * Return NULL if not found + */ + +extern Membership * +locate_membership(Conf_no conf_no, + Person * pers_p); + + + +/* + * Change user_area of a person. If text_no is 0, there will be + * no user_area. + */ + +extern Success +do_set_user_area(Pers_no pers_no, + Person * pers_p, + Text_no text_no); + +/* + * Mark a text. No check is done if pers_p is really allowed to mark it. + * Use mark_type==0 to unmark the text. + */ + +Success +do_mark_text(Pers_no pers_no, + Person *pers_p, /* May be NULL */ + Text_no text_no, + Text_stat *text_s, /* May be NULL */ + u_char mark_type); + +/* + * Return a pointer to a Mark if pers_no has marked the text text_no. + * Otherwise, return NULL. + */ +Mark * +locate_mark(Pers_no pers_no, + Person *pers_p, /* May be NULL. */ + Text_no text_no); + +/* + * Delete a person. (The mailbox is not deleted);. + */ + +extern Success +do_delete_pers (Pers_no pers_no); + + +/* + * Functions which work on CONFERENCES and/or PERSONS. + */ + +/* + * Add a member to a conference. All errorchecking should already + * be done when this function is called. The person must not already + * be a member of the conference. It is _not_ an error to make WHERE bigger + * than the number of conferences the person is a member in. + */ + +extern void +do_add_member(Conf_no conf_no, /* Conference to add a new member to. */ + Conference * conf_c, /* Conf. status. Must NOT be NULL. */ + Pers_no pers_no, /* Person to be added. */ + Person * pers_p, /* Pers. status. Must NOT be NULL. */ + u_char priority, /* Prioritylevel to assign to this conf */ + u_short where); /* Sequence number in the list */ + +/* + * Return TRUE if ACTPERS has enough privileges to access VICTIM's data. + * VICTIM is a person or a conference. + * Meaning of return values: + * unlimited: ACTPERS is supervisor of VICTIM, or ACTPERS is admin, + * or ACTPERS is VICTIM + * none: VICTIM is secret, and ACTPERS is not a member + * member: ACTPERS is a member in VICTIM, but doesn't have unlimited + * access. + * limited: otherwise. + * error: see kom_errno + */ + +extern Access +access_perm(Conf_no victim, + Conference * victim_c); /* May be NULL */ + +/* + * Fast version of access_perm. This function does not check if ATCPERS is a + * supervisor of the conference. This function should be used i calls where + * a lot of conferences are checked to avoid readin in conferences from the + * cache. + * + * BUG: If a person is supervisor of a secret conf he is not member in the + * result will be 'none'. + * + * unlimited: ACTPERS is admin, or ACTPERS is VICTIM + * none: VICTIM is secret, and ACTPERS is not a member + * member: ACTPERS is a member in VICTIM, but doesn't have unlimited + * access. + * limited: otherwise. + * error: see kom_errno + */ +Access +fast_access_perm(Conf_no victim); + + +/* + * Return TRUE if NAME is a legal name + */ + +extern Bool +legal_name( String name ); + + +/* + * Return TRUE if NAME is not already used + */ + +extern Bool +unique_name( const String name, Conf_no conf_no ); + + + +/* + * Change the priority of a certain conference in a person. + */ +extern void +do_change_priority (Membership * mship, + u_char priority, + u_short where, + Pers_no pers_no, + Person * pers_p); + + +/* + * Delete a member from a conference. + * No checks are made on the parameters. + * The dynamically allocated areas conf_c->members.members and + * pers_p->confs are NOT reallocated since they will anyhow sooner or later + * be flushed from core. + */ + +extern Success +do_sub_member(Conf_no conf_no, /* Conf to delete member from. */ + Conference * conf_c, /* May be NULL */ + Member * member, /* May be NULL */ + Pers_no pers_no, /* Person to be deleted. */ + Person * pers_p, /* May be NULL */ + Membership * mship); /* Pointer to the persons membership in + conf., or NULL if not known. */ + + +/* + * Functions which deal with TEXTS + */ + +/* + * Count how many recipients and cc_recipients a text has. + */ + +extern int +count_recipients( Text_stat *t_stat ); + + +/* + * Count how many footnotes a text has. + */ + +extern int +count_footn( Text_stat *t_stat ); + + +/* + * Count how many commments a text has. + */ + +extern int +count_comment( Text_stat *t_stat ); + + +/* + * Check if ACTPERS is allowed to read this text. + * Returns TRUE if he is allowed to read it. + */ + +extern Bool +text_read_access(Text_no text_no, + Text_stat * text_stat); + + + +/* + * Check if CONF_NO is a recipient of the text whose text_stat is given. + */ + +extern Bool +is_recipient(Conf_no conf_no, + Text_stat * t_stat); + + + +/* + * Check if comment is a comment to parent. + */ +Bool +is_comment_to(Text_no comment, + Text_stat *parent); + + +/* + * Check if footnote is a footnote to parent. + */ +Bool +is_footnote_to(Text_no footnote, + Text_stat *parent); + + +/* + * Return the conference which the text goes to. This is normally conf_no, but + * it may be a super_conf. If ACTPERS is not allowed to submit a text to + * conf_no or a super_conf, return 0. + */ + +extern Conf_no +submit_to(Conf_no conf_no, /* The conference the user is trying to */ + /* submit a text to. */ + Conference * conf_c); /* May be NULL */ + + +/* + * Insert a rec_time misc item to a text_status. + * The item is put at position POS on the list. (0 == first) + * Take no action if the misc_item at POS is a rec_time. + * This function is only used when a person marks his letters as read. + * + * Text_stat * text_stat_pointer Textstatus to modify + * int pos Where to insert rec_time + */ + +extern void +do_add_rec_time (Text_stat * text_stat_ptr, + int pos); + + +/* + * Say that FOOTNOTE is a footnote to TEXT. + */ + +extern Success +do_add_footnote(Text_no footnote, + Text_no text); + + + +/* + * Say that COMMENT is a comment to TEXT. + */ + +extern Success +do_add_comment(Text_no comment, + Text_no text); + + + +/* + * Say that RECEIVER is a recipient of TEXT. + */ +extern Success +do_add_recpt(Text_no text, + Text_stat * text_s, /* May be NULL */ + Conf_no receiver); + + + +extern Success +do_add_cc_recpt(Text_no new_text, + Text_stat *text_s, /* May be NULL */ + Conf_no receiver); + + + +/* + * Return number of lines in a text + */ + +extern u_short +count_lines( String str ); + + + +/* + * Delete misc_info at location loc. + * If it is a recpt, cc_recpt, comm_to or footn_to delete any + * loc_no, rec_time, sent_by or sent_at that might follow it. + * + * Note that the Misc_info is not reallocated. + */ + +extern void +do_delete_misc (u_short * no_of_misc, + Misc_info * misc, + int loc); + + +/* + * Delete a recipient from a text + */ +extern Success +do_sub_recpt (Text_no text_no, + Text_stat * text_s, + Conf_no conf_no, + Conference * conf_s ); + + +/* + * Delete the link between comment and comment_to. + */ +void +do_sub_comment (Text_no comment, + Text_stat * text_s, + Text_no comment_to, + Text_stat * parent_s ); + +/* + * Delete the link between footnote and footnote_to. + */ +void +do_sub_footnote (Text_no footnote, + Text_stat * text_s, + Text_no footnote_to, + Text_stat * parent_s ); + + +/* + * Check if ACTPERS has sent this text to conference CONF_NO + */ +extern Bool +is_sender(Text_stat * text_s, + Conf_no conf_no); + + +/* + * Check if ACTPERS has sent this text as a comment to parent. + */ +Bool +is_comm_sender(Text_stat * text_s, + Text_no parent); + + +/* + * Check if ACTPERS has sent this text as a footnote to parent. + */ +extern Bool +is_footn_sender(Text_stat * text_s, + Text_no parent); + + +/* + * Check if ACTPERS is allowed to add a footnote to a text. Sets errno if + * there is an error. + */ + +extern Success +check_footn(Text_stat * t_stat); + + +/* + * Check if ACTPERS is allowed to add a comment to a text. Sets errno if + * there is an error. Note that it is allowed to comment a text even if + * you are not allowed to read it. + */ + +extern Success +check_comm(Text_stat * t_stat); + + +/* + * Check if pers_no is a member in any of the recipients or cc_recipients + * of text_s + */ +extern Bool +is_member_in_recpt(Person * person, + Text_stat * text_s); +extern void +forced_leave_conf(Pers_no pers_no, + Conf_no conf_no); + diff --git a/src/server/membership.c b/src/server/membership.c new file mode 100644 index 0000000000000000000000000000000000000000..8a682111fcdfeb163add046e0c939686f811eb03 --- /dev/null +++ b/src/server/membership.c @@ -0,0 +1,1169 @@ +/* + * membership.c + * + * All atomic calls that controlls who is a member in what. + * (The person/conf relation). + */ +#include <time.h> +#include <stdlib.h> +#include "lyskomd.h" +#include <kom-types.h> +#include <services.h> +#include "manipulate.h" +#include "smalloc.h" +#include "cache.h" +#include "log.h" +#include "minmax.h" +#include "com.h" +#include "isc.h" +#include "connections.h" +#include "send-async.h" +#include <kom-errno.h> +#include "internal-connections.h" + +#define DEBUG_MARK_AS_READ + +#ifdef DEBUG_MARK_AS_READ +# include <stdio.h> +# include <string.h> +# include "ram-output.h" +#endif + + + + + +/* + * Copy all information that ACTPERS is authorized to know about ORIG_P's + * membership in all conferences to CENSOR_P. + * + * This function is used in get_membership(). + */ +static void +copy_public_confs (Person * censor_p, /* The censored Person-struct */ + Person * orig_p, /* The uncensored Person-struct */ + Bool want_read) /* Does ACTPERS want to know + * which texts are read?*/ +{ + int i; /* Number of mships lefte in ORIG_P */ + Membership * censor_m; /* Pointer in CENSOR_P */ + Membership * orig_m; /* Pointer in ORIG_P */ + + + /* Copy all information except the secret. */ + + censor_p->conferences.confs + = tmp_alloc( orig_p->conferences.no_of_confs * sizeof(Membership)); + censor_p->conferences.no_of_confs = 0; + + censor_m = censor_p->conferences.confs; + orig_m = orig_p->conferences.confs; + + for ( i = 0; i < orig_p->conferences.no_of_confs; i++, orig_m++ ) + { + if ( fast_access_perm( orig_m->conf_no ) > none ) + { + *censor_m = *orig_m; + + if ( orig_p->flags.unread_is_secret || !want_read ) + { + censor_m->no_of_read = 0; + censor_m->read_texts = NULL; + } + + if ( orig_p->flags.unread_is_secret ) + { + censor_m->last_time_read = NO_TIME; + censor_m->last_text_read = 0; + } + + ++censor_m; + ++censor_p->conferences.no_of_confs; + } + } +} + +/* + * Change the priority of a certain conference in a person. + */ +static void +do_change_priority (Membership * mship, + u_char priority, + u_short where, + Pers_no pers_no, + Person * pers_p) +{ + Membership tmp_conf; + + mship->priority = priority; + + /* Check range of where */ + + if ( where >= pers_p->conferences.no_of_confs ) + { + where = pers_p->conferences.no_of_confs - 1; + } + + /* And now move the conference to slot number 'where' */ + + if ( mship < pers_p->conferences.confs + where ) + { + tmp_conf = *mship; + while ( mship < pers_p->conferences.confs + where) + { + *mship = *(mship + 1); + mship++; + } + *mship = tmp_conf; + } + else + { + tmp_conf = *mship; + while ( mship > pers_p->conferences.confs + where) + { + *mship = *(mship - 1); + mship--; + } + *mship = tmp_conf; + } + + mark_person_as_changed( pers_no ); +} + + +/* + * Insert a rec_time misc item to a text_status. + * The item is put at position POS on the list. (0 == first) + * Take no action if the misc_item at POS is a rec_time. + * This function is only used when a person marks his letters as read. + * + * Text_stat * text_stat_pointer Textstatus to modify + * int pos Where to insert rec_time + */ + +static void +do_add_rec_time (Text_stat * text_stat_ptr, + int pos) +{ + int i; + + /* Defensive checks */ + if ( pos < 0 || pos > text_stat_ptr->no_of_misc ) + { + restart_kom("do_add_rec_time() - illegal pos\n"); + } + + /* Check that no rec_time exists */ + + if ( pos < text_stat_ptr->no_of_misc + && text_stat_ptr->misc_items[ pos ].type == rec_time ) + { + return; + } + + /* Allocate space */ + text_stat_ptr->misc_items + = srealloc(text_stat_ptr->misc_items, + (++(text_stat_ptr->no_of_misc)) * sizeof(Misc_info)); + + /* Move items. */ + + for ( i = text_stat_ptr->no_of_misc - 1; i > pos; i-- ) + { + text_stat_ptr->misc_items[ i ] = text_stat_ptr->misc_items[ i - 1 ]; + } + + /* Set type */ + text_stat_ptr->misc_items[ pos ].type = rec_time; + + /* Set value */ + time( & text_stat_ptr->misc_items[ pos ].datum.received_at); +} + +/* + * add_rec_time adds a 'rec_time' misc_item to text number LOC_NO in + * conference CONF_NO. The item will follow a recpt or cc_recpt to ACTPERS. + * No action is taken if ACTPERS is not a recipient of the text, or the text + * no longer exists, or the text has already been received. + */ +static void +add_rec_time(Conf_no conf_no, + Conference * conf_c, /* The structure for conf_no */ + Local_text_no local_no) +{ + Bool found; + Text_no text_no; + Text_stat * t_stat; + int i; + + if ( local_no >= conf_c->texts.first_local_no + conf_c->texts.no_of_texts + || local_no < conf_c->texts.first_local_no ) + { + return; /* No longer exists in conf. */ + } + + text_no = conf_c->texts.texts[ local_no - conf_c->texts.first_local_no ]; + + if ( text_no == 0 ) + { + return; /* Text is deleted. */ + } + + GET_T_STAT(t_stat, text_no, (void)0); + + /* locate the misc_item which says that ACTPERS is a recipient */ + + for ( found = FALSE, i = 0; !found && i < t_stat->no_of_misc; i++ ) + { + switch ( t_stat->misc_items[ i ].type ) + { + case recpt: + if ( t_stat->misc_items[ i ].datum.recipient == ACTPERS ) + { + do_add_rec_time( t_stat, i + 2 ); /* Add after loc_no */ + found = TRUE; + } + break; + + case cc_recpt: + if ( t_stat->misc_items[ i ].datum.cc_recipient == ACTPERS ) + { + do_add_rec_time( t_stat, i + 2 ); + found = TRUE; + } + break; + + case comm_to: + case comm_in: + case footn_to: + case footn_in: + case loc_no: + case rec_time: + case sent_by: + case sent_at: + break; + } + } + + if( found == FALSE ) + { + log("ERROR: add_rec_time(): found==FALSE\n"); + } + + mark_text_as_changed( text_no); + return; +} + +/* + * Check if there are some texts immediately following last_text_read + * that are read or deleted. If so, update last_text_read and delete them + * from read_texts. + * + * This is only used from mark_as_read(). + */ +static void +adjust_read( Membership * m, + const Conference * conf) +{ + u_short i; + Local_text_no * locp; + Local_text_no conf_max; /* Highest used local_text_no in conf */ + Local_text_no conf_min; /* Lowest used local_text_no in conf */ + Bool ready; +#ifdef DEFENSIVE_CHECKS + Local_text_no prev; +#endif + + i = 0; /* Number of texts in read_texts in Membership which are + included in last_text_read. */ + + /* (conf_min <= x <= conf_max) if x is an existing local_text_no */ + conf_max = conf->texts.first_local_no + conf->texts.no_of_texts - 1; + conf_min = conf->texts.first_local_no; + + do + { + ready = TRUE; + + /* Increase m->last_text_read if + * * the user has read the text (last_text_read+1) + * * text (last_text_read+1) is deleted. + */ + + if ( m->last_text_read < conf_max) /* Does (last_text_read+1) exist? */ + { + if ( m->no_of_read > i /* Read? */ + && m->read_texts[ i ] == m->last_text_read + 1 ) + { + i++; + m->last_text_read++; + ready = FALSE; + } + else + { + /* Is the text deleted? */ + if ( m->last_text_read + 1 < conf_min /* Deleted and older + than any text that + exists in the + conference. */ + || (conf->texts.texts /* Deleted? */ + [ m->last_text_read + 1 - conf_min ] + == 0 ) ) + { + m->last_text_read++; + ready = FALSE; + } + } + } + } while ( ready == FALSE ); + + /* Delete the first i entries in read_texts */ + if ( i > 0 ) + { + m->no_of_read -= i; + + for (locp = m->read_texts; + locp < m->read_texts + m->no_of_read; + locp++) + { + *locp = *(locp + i); + } + } + +#ifdef DEFENSIVE_CHECKS + + /* Check that the items in read_texts really ARE sorted in ascending order. + If not, there is probably a bug in this routine or in mark_as_read */ + + prev = m->last_text_read; + + for ( i = 0; i < m->no_of_read; i++) + { + if ( prev >= m->read_texts[ i ] ) + { + log("Bug in adjust_read. Conference %lu, Priority %lu\n" + "\tprev = %lu, i = %lu, m->read_texts[i] = %lu\n", + (u_long)m->conf_no, (u_long)m->priority, + (u_long)prev, (u_long)i, (u_long)m->read_texts[i]); + } + + prev = m->read_texts[ i ]; + } +#endif +} + +/* + * insert TEXT in the list of read_texts in M. The texts are sorted. + * m->no_of_read is updated. m->read_texts is never reallocated, and must + * thus be big enough to hold the new number. + * + * Returns FAILURE if the text is already read. + * + * This is only used from mark_as_read(). + */ + +static Success +insert_loc_no(Local_text_no text, + Membership * m) +{ + Local_text_no * seek, * move; + + + if ( text <= m->last_text_read ) + { + return FAILURE; /* This text was already read. */ + } + + for ( seek = m->read_texts; seek < m->read_texts + m->no_of_read; seek++) + { + if ( text == *seek ) + { + return FAILURE; /* This text was already read. */ + } + + if ( text < *seek ) + { /* The text should be entered here. */ + for ( move = m->read_texts + m->no_of_read; move > seek; move--) + { + *move = *(move - 1); + } + + *seek = text; + ++(m->no_of_read); + + return OK; + } + } + + *seek = text; /* The text had a higher number than any */ + ++(m->no_of_read); /* previously read text. */ + + return OK; +} + +/* + * End of static functions + */ + +/* + * Functions that are exported to the server. + */ + +/* + * Add a member to a conference. All errorchecking should already + * be done when this function is called. The person must not already + * be a member of the conference. It is _not_ an error to make WHERE bigger + * than the number of conferences the person is a member in. + */ + +void +do_add_member(Conf_no conf_no, /* Conference to add a new member to. */ + Conference * conf_c, /* Conf. status. Must NOT be NULL. */ + Pers_no pers_no, /* Person to be added. */ + Person * pers_p, /* Pers. status. Must NOT be NULL. */ + u_char priority, /* Prioritylevel to assign to this conf */ + u_short where) /* Sequence number in the list */ +{ + Membership * mship; + Member * member; + + /* First add the conference in the person-struct. + * Make room for it. + */ + + pers_p->conferences.confs = srealloc( pers_p->conferences.confs, + ++(pers_p->conferences.no_of_confs) + * sizeof(Membership)); + + /* Fill in the room */ + + /* Find last slot */ + mship = pers_p->conferences.confs + pers_p->conferences.no_of_confs - 1 ; + + /* Move all data beyond WHERE */ + while ( mship > pers_p->conferences.confs + where ) + { + *mship = *(mship - 1); + mship--; + } + + *mship = EMPTY_MEMBERSHIP; + + mship->conf_no = conf_no; + mship->priority = priority; + mship->last_time_read = time(NULL); + mship->last_text_read = 0; + mship->no_of_read = 0; + mship->read_texts = NULL; + + /* Make room for the person in the conference */ + + conf_c->members.members = srealloc( conf_c->members.members, + ++(conf_c->members.no_of_members) + * sizeof(Member)); + + /* New members go to the end of the list */ + + member = (conf_c->members.members + + conf_c->members.no_of_members - 1); + member->member = pers_no; + + mark_conference_as_changed( conf_no ); + mark_person_as_changed( pers_no ); + + return; +} + +/* + * Send an asynchronous message to person pers_no (if he is logged on) + * and tell him that he is no longer a member of conf_no. Also calls + * leave_conf(). + */ +extern void +forced_leave_conf(Pers_no pers_no, + Conf_no conf_no) +{ + Connection *real_active_connection; + Session_no i = 0; + + real_active_connection = active_connection; + + while ( (i = traverse_connections(i)) != 0 ) + { + active_connection = get_conn_by_number(i); + + if ( active_connection->pers_no == pers_no ) + { + async_forced_leave_conf(active_connection, conf_no); + + if ( active_connection->cwc == conf_no ) + leave_conf(); + } + } + + active_connection = real_active_connection; +} + +/* + * Delete a member from a conference. + * No checks are made on the parameters. + * The dynamically allocated areas conf_c->members.members and + * pers_p->confs are NOT reallocated since they will anyhow sooner or later + * be flushed from core. + */ + +Success +do_sub_member(Conf_no conf_no, /* Conf to delete member from. */ + Conference * conf_c, /* May be NULL */ + Member * member, /* May be NULL */ + Pers_no pers_no, /* Person to be deleted. */ + Person * pers_p, /* May be NULL */ + Membership * mship) /* Pointer to the persons membership in + conf., or NULL if not known. */ +{ + if ( conf_c == NULL ) + GET_C_STAT(conf_c, conf_no, FAILURE); + + if ( pers_p == NULL ) + GET_P_STAT(pers_p, pers_no, FAILURE); + + if ( mship == NULL && (mship = locate_membership(conf_no, pers_p)) == NULL) + restart_kom("do_sub_member() - can't find mship\n"); + + if ( member == NULL && (member = locate_member(pers_no, conf_c)) == NULL) + restart_kom("do_sub_member() - can't find member.\n"); + + forced_leave_conf(pers_no, conf_no); + + /* Delete from Person */ + + sfree( mship->read_texts ); + --pers_p->conferences.no_of_confs; + while ( mship + < pers_p->conferences.confs + pers_p->conferences.no_of_confs ) + { + *mship = *(mship + 1); + ++mship; + } + + /* Delete from Conference */ + + --conf_c->members.no_of_members; + while ( member < conf_c->members.members + conf_c->members.no_of_members ) + { + *member = *(member + 1); + ++member; + } + + mark_person_as_changed( pers_no ); + mark_conference_as_changed( conf_no ); + + return OK; +} + + +/* + * VICTIM is a person or a conference. + * Meaning of return values: + * unlimited: ACTPERS is supervisor of VICTIM, or ACTPERS is admin, + * or ACTPERS is VICTIM + * none: VICTIM is secret, and ACTPERS is not a member + * member: ACTPERS is a member in VICTIM, but doesn't have unlimited + * access. + * read_protected: The conference is rd_prot and ACTPERS is not a member. + * limited: otherwise. + * error: see kom_errno + */ + +Access +access_perm(Conf_no victim, + Conference * victim_c) /* May be NULL */ +{ + if (victim == ACTPERS) + return unlimited; + + if (victim_c == NULL) + GET_C_STAT(victim_c, victim, error); + + if ( ACTPERS != 0 && ENA(admin, 2) ) + return unlimited; + + if ( is_supervisor(victim, victim_c)) + return unlimited; + + if ( ACTPERS != 0 && locate_membership( victim, ACT_P ) != NULL ) + return member; + + if ( victim_c->type.secret ) + return none; + + if ( victim_c->type.rd_prot ) + return read_protected; + + return limited; +} + + +/* + * Fast version of access_perm. See comment in file server/manipulate.h + * where Access is defined. + */ +Access +fast_access_perm(Conf_no victim) +{ + Conf_type conf_type; + + if ( ACTPERS != 0 && (ENA(admin, 2) || ENA(wheel,8) || ACTPERS == victim) ) + return unlimited; + + if ( ACTPERS != 0 && locate_membership( victim, ACT_P ) != NULL ) + return member; + + if ( (conf_type=cached_get_conf_type( victim )).secret ) + return access_perm(victim, NULL); /* Only read in conference struct + * when really necessary. */ + + if ( conf_type.rd_prot ) + return read_protected; + + return limited; +} + +/* + * Locate the Member struct in CONF_C for person PERS_NO + */ + +Member * +locate_member(Pers_no pers_no, + Conference * conf_c) +{ + Member * member; + int i; + + for(member = conf_c->members.members, i = conf_c->members.no_of_members; + i > 0; i--, member++) + { + if ( member->member == pers_no ) + { + return member; + } + } + + return NULL; +} + + +/* + * Find the data about PERS_P:s membership in CONF_NO. + * Return NULL if not found + */ + +Membership * +locate_membership(Conf_no conf_no, + Person * pers_p) +{ + Membership * confp; + int i; + + for(confp = pers_p->conferences.confs, i = pers_p->conferences.no_of_confs; + i > 0; i--, confp++) + { + if ( confp->conf_no == conf_no ) + { + return confp; + } + } + + return NULL; +} + +/* + * Atomic functions. + */ + +/* + * Unsubscribe from a conference. + * + * You must be supervisor of either conf_no or pers_no to be allowed to + * do this. + * + * BUGS: There is no passive membership. + */ +extern Success +sub_member( Conf_no conf_no, + Pers_no pers_no ) +{ + Conference * conf_c; + Membership * mship; + Person * pers_p; + + CHK_LOGIN(FAILURE); + GET_C_STAT(conf_c, conf_no, FAILURE); + GET_P_STAT(pers_p, pers_no, FAILURE); + + if( (mship = locate_membership(conf_no, pers_p) ) == NULL) + { + kom_errno = conf_c->type.secret ? KOM_UNDEF_CONF : KOM_NOT_MEMBER; + return FAILURE; + } + + if ( !is_supervisor(conf_no, conf_c) && !is_supervisor(pers_no, NULL) + && !ENA(admin, 4) ) + { + kom_errno = conf_c->type.secret ? KOM_UNDEF_CONF : KOM_PERM; + return FAILURE; + } + + return do_sub_member(conf_no, conf_c, NULL, pers_no, pers_p, mship); +} + + +/* + * Add a member to a conference (join a conference) or + * Change the priority of a conference. + * + * Anyone may add anyone as a member as long as the new member is not + * secret and the conference is not rd_prot. This might be a bug. + * + * PRIORITY is the assigned priority for the conference. WHERE says + * where on the list the person wants the conference. 0 is first. WHERE + * is automatically set to the number of conferences that PERS_NO is member + * in if WHERE is too big, so it is not an error to give WHERE == ~0 as + * a parameter. + * + * You can only re-prioritize if you are supervisor of pers_no. + */ +extern Success +add_member(Conf_no conf_no, + Pers_no pers_no, + u_char priority, + u_short where) /* Range of where is [0..] */ +{ + Conference * conf_c, * pers_c; + Person * pers_p; + Membership * mship; + + + CHK_LOGIN(FAILURE); + GET_C_STAT(conf_c, conf_no, FAILURE); + GET_P_STAT(pers_p, pers_no, FAILURE); + + if ( access_perm(conf_no, conf_c) < limited && !ENA(admin, 4) ) + { + kom_errno = conf_c->type.secret ? KOM_UNDEF_CONF : KOM_ACCESS; + return FAILURE; + } + + /* Is he already a member? */ + + if ( (mship = locate_membership( conf_no, pers_p )) != NULL) + { + /* He is already a member. Only change the priority. */ + + GET_C_STAT(pers_c, pers_no, FAILURE); + if( !is_supervisor( pers_no, pers_c ) ) /* Noone else can change */ + /* one's priorities. */ + { + kom_errno = KOM_PERM; + return FAILURE; + } + + do_change_priority( mship, priority, where, pers_no, pers_p); + } + else + { + do_add_member(conf_no, conf_c, pers_no, pers_p, priority, where); + } + + return OK; +} + +#ifdef DEBUG_MARK_AS_READ +static int +check_membership(Pers_no pno, + const Conference *conf, + const Membership *mship) +{ + int error=0; + int i; + Local_text_no last=0; + int log_no=0; + + /* Check read texts */ + if ( mship->last_text_read > + conf->texts.first_local_no + conf->texts.no_of_texts - 1) + { + if ( log_no++ < 80 ) + log("membership.c: check_membership():" + "(%d) Person %lu has read text %lu in conf %lu," + "which only has %lu texts.\n", + log_no, + (u_long)pno, + (u_long)mship->last_text_read, + (u_long)mship->conf_no, + (u_long)(conf->texts.first_local_no + + conf->texts.no_of_texts - 1)); + error++; + } + + last = mship->last_text_read; + + for ( i = 0; i < mship->no_of_read; i++) + { + if ( mship->read_texts[i] <= last ) + { + error++; + } + + last = mship->read_texts[i]; + } + + return error; +} +#endif + +/* + * mark_as_read() is used to tell LysKOM which texts you have read. + * You can mark several texts in one chunk, but the chunk should not + * be too big to prevent users from having to re-read texts in case of + * a [server/client/network]-crash. + * + * The texts are marked per conference. If there are several recipients + * to a text it should be mark_as_read() in all the recipients. + * + * If conference is ACTPERS mailbox it will add a rec_time item in the + * misc_items field. + * + * It is only possible to mark texts as read in a conference you are + * member in. + * + * Attempts to mark non-existing texts as read are ignored if the text + * has existed. If the text has not yet been created KOM_NO_SUCH_LOCAL_TEXT + * will be returned in kom_errno. + * + * If CONFERENCE is the current working conference of ACTPERS, and the text + * has not previously been marked as read, ACT_P->read_texts will be + * increased. If the client cooperates this will be correct. If the + * client pepsi()s to all recipients of a text before marking it as read + * the read_texts field will be too big. (If anyone cares about that, + * feel free to rewrite this code as long as it doen't get too CPU- + * intensive.) + */ +extern Success +mark_as_read (Conf_no conference, + int no_of_texts, + const Local_text_no * text_arr ) +{ + int i; + Membership * m; + int allocflg = 0; /* read_texts is not re-allocated yet */ + Conference * conf_c; + Success retval = OK; +#ifdef DEBUG_MARK_AS_READ + const Local_text_no * const text_arr_start = text_arr; + Membership original; + int loop; + static int log_no = 0; +#endif + + + CHK_LOGIN(FAILURE); + GET_C_STAT(conf_c, conference, FAILURE); + + if ( (m = locate_membership( conference, ACT_P)) == NULL) + { + kom_errno = KOM_NOT_MEMBER; + return FAILURE; + } + +#ifdef DEBUG_MARK_AS_READ + if ( m->read_texts == NULL && m->no_of_read != 0 ) + { + log("mark_as_read(): m->read_texts == NULL && m->no_of_read == %lu (corrected).", + (u_long)m->no_of_read); + m->no_of_read = 0; + } + + original = *m; + original.read_texts = smalloc(m->no_of_read * sizeof(Local_text_no)); + memcpy(original.read_texts, m->read_texts, + m->no_of_read * sizeof(Local_text_no)); +#endif + + for( i = no_of_texts; i > 0; i--, text_arr++ ) + { + if ( *text_arr >= ( conf_c->texts.first_local_no + + conf_c->texts.no_of_texts )) + { + kom_errno = KOM_NO_SUCH_LOCAL_TEXT; + err_stat = no_of_texts - i; + retval = FAILURE; + break; /* Exit for-loop */ + } + + if ( *text_arr == 0 ) + { + kom_errno = KOM_LOCAL_TEXT_ZERO; + err_stat = no_of_texts - i; + retval = FAILURE; + break; /* Exit for-loop */ + } + + /* Is it a letter to ACTPERS? If so, add a rec_time item. */ + + if ( conference == ACTPERS ) + add_rec_time( conference, conf_c, *text_arr ); + + /* Update the Membership struct */ + + if ( *text_arr == m->last_text_read + 1 ) + { + ++m->last_text_read; + if ( active_connection->cwc == conference ) + ++ACT_P->read_texts; + } + else + { + if ( allocflg == 0 ) + { + /* Realloc as much as is needed, and probably more. */ + /* Better than to execute srealloc 100 times... */ + + m->read_texts = srealloc( m->read_texts, + (m->no_of_read + i) + * sizeof(Local_text_no)); + allocflg = 1; + } + + if ( insert_loc_no( *text_arr, m ) == OK + && active_connection->cwc == conference ) + { + ++ACT_P->read_texts; + } + } + } + + adjust_read( m, conf_c ); /* Delete initial part + of read_texts in the membership. */ + /* Realloc to correct size */ + + m->read_texts = srealloc( m->read_texts, + (m->no_of_read) * sizeof(Local_text_no)); + + mark_conference_as_changed ( conference ); + + if ( active_connection->cwc == conference ) + mark_person_as_changed( ACTPERS ); +#ifdef DEBUG_MARK_AS_READ + /* Check that the membership is correct. Otherwise log all info. */ + if ( check_membership(ACTPERS, conf_c, m) > 0 && log_no++ < 40 ) + { + log("mark_as_read(): (Msg no %d) Person %lu " + "has a corrupt membership:\n", + log_no, (u_long)ACTPERS); + log("Dump of data follows: <original membership>" + " <updated membership> <texts to mark>\n"); + foutput_membership(stderr, &original); + putc('\n', stderr); + foutput_membership(stderr, m); + fprintf(stderr, "\n%lu { ", (u_long)no_of_texts); + for ( loop = 0; loop < no_of_texts; loop++ ) + { + fprintf(stderr, "\n%lu ", (u_long)text_arr_start[loop]); + } + fprintf(stderr, "}\n"); + } + + sfree(original.read_texts); +#endif + return retval; +} + +/* + * Ask what conferences a person is a member of. + * + * /// The following might be slightly misleading: /// /ceder + * Only ask for information about at most NO_OF_CONFS conferences + * starting with conferece FIRST. WANT_READ_TEXTS has the same + * function as GETP_READ_TEXTS in the get_person_stat call. + * /// + */ +extern Success +get_membership (Pers_no pers_no, + u_short first, + u_short no_of_confs, + Bool want_read_texts, + Membership_list * memberships ) +{ + Person * p_orig; + Person temp_pers; + Conference * pers_c; + Access acc; + int i; + + CHK_LOGIN (FAILURE); + + GET_P_STAT (p_orig, pers_no, FAILURE); + GET_C_STAT (pers_c, pers_no, FAILURE); + + acc = access_perm (pers_no, pers_c); + + if (acc == error) + return FAILURE; + + if (acc == none) + { + kom_errno = KOM_UNDEF_PERS; + return FAILURE; + } + + /* Make a copy of the struct. */ + + temp_pers = *p_orig; + + /* Delete all secret information. */ + + if ( acc != unlimited ) + copy_public_confs (&temp_pers, p_orig, want_read_texts); + else if ( !want_read_texts ) + { + /* Delete info about read texts. */ + temp_pers.conferences.confs + = tmp_alloc(temp_pers.conferences.no_of_confs + * sizeof(Membership)); + + memcpy(temp_pers.conferences.confs, + p_orig->conferences.confs, + (temp_pers.conferences.no_of_confs + * sizeof(Membership) )); + + for ( i = 0; i < temp_pers.conferences.no_of_confs; i++ ) + temp_pers.conferences.confs[ i ].read_texts = NULL; + } + + + *memberships = temp_pers.conferences; + + if ( first >= memberships->no_of_confs ) + { + kom_errno = KOM_INDEX_OUT_OF_RANGE; + return FAILURE; + } + + memberships->confs += first; + memberships->no_of_confs = min( memberships->no_of_confs - first, + no_of_confs); + + return OK; +} + +/* + * +++/// + * + * first starts at 0. + */ +extern Success +get_members (Conf_no conf_no, + u_short first, + u_short no_of_members, + Member_list * members ) +{ + Conference * conf_c; + Access acc; + + GET_C_STAT(conf_c, conf_no, FAILURE); + + acc = access_perm( conf_no, conf_c); + + if ( acc == error ) + return FAILURE; + + if ( acc == none ) + { + kom_errno = KOM_UNDEF_CONF; + return FAILURE; + } + + *members = conf_c->members; + + if ( first >= members->no_of_members ) + { + kom_errno = KOM_INDEX_OUT_OF_RANGE; + return FAILURE; + } + + members->members += first; + members->no_of_members = min (no_of_members, + members->no_of_members - first); + + return OK; +} + +/* + * Get a list of all conferences where it is possible that a person has + * unread articles. + */ + +Success +get_unread_confs(Pers_no pers_no, + Conf_no_list *result) +{ + Person *pers_p; + Membership *confs; + u_short n; + + GET_P_STAT(pers_p, pers_no, FAILURE); + + result->conf_nos = tmp_alloc (pers_p->conferences.no_of_confs + * sizeof(Conf_no)); + result->no_of_confs = 0; + + for ( n = 0, confs = pers_p->conferences.confs; + n < pers_p->conferences.no_of_confs; + n++, confs++ ) + { + if ( confs->last_text_read + < cached_get_highest_local_no( confs->conf_no ) ) + { + result->conf_nos[ result->no_of_confs++ ] = confs->conf_no; + } + } + return OK; +} + + +/* + * Tell the server that I want to mark/unmark texts as read so that I + * get (approximately) no_of_unread unread texts in conf_no. + */ +extern Success +set_unread (Conf_no conf_no, + Text_no no_of_unread) +{ + Membership *mship; + Conference *conf_c; + Local_text_no highest; + + CHK_LOGIN(FAILURE); + + GET_C_STAT(conf_c, conf_no, FAILURE); + + if ( (mship = locate_membership(conf_no, ACT_P)) == NULL ) + { + kom_errno = KOM_NOT_MEMBER; + return FAILURE; + } + + highest = conf_c->texts.first_local_no + conf_c->texts.no_of_texts - 1; + + mship->last_text_read = ((highest > no_of_unread) + ? (highest - no_of_unread) : 0); + + sfree(mship->read_texts); + mship->read_texts = NULL; + mship->no_of_read = 0; + + mark_person_as_changed(ACTPERS); + return OK; +} + diff --git a/src/server/memory.c b/src/server/memory.c new file mode 100644 index 0000000000000000000000000000000000000000..79b43da82be5a93e77ac546b0d431080134df427 --- /dev/null +++ b/src/server/memory.c @@ -0,0 +1,385 @@ +/* + * Memory allocators/deallocators. + * + * These functions should be used instead of smalloc/srealloc. + */ + +#include <kom-types.h> +#include <string.h> +#include "smalloc.h" +#include "memory.h" +#include "exp.h" +#include "lyskomd.h" + +static int text_list_cnt = 0; +static int mark_list_cnt = 0; +static int membership_list_cnt = 0; +static int membership_cnt = 0; +static int person_cnt = 0; +static int member_list_cnt = 0; +static int conference_cnt = 0; +static int text_stat_cnt = 0; + +EXPORT Text_list * +alloc_text_list(void) +{ + Text_list *t; + + text_list_cnt++; + + t = smalloc(sizeof(Text_list)); + *t = EMPTY_TEXT_LIST; + return t; +} + +EXPORT Mark_list * +alloc_mark_list(void) +{ + Mark_list *m; + + mark_list_cnt++; + + m = smalloc(sizeof(Mark_list)); + *m = EMPTY_MARK_LIST; + return m; +} + +EXPORT Membership_list * +alloc_membership_list(void) +{ + Membership_list *m; + + membership_list_cnt++; + + m = smalloc(sizeof(Membership_list)); + *m = EMPTY_MEMBERSHIP_LIST; + return m; +} + +EXPORT Membership * +alloc_membership(void) +{ + Membership *m; + + membership_cnt++; + + m = smalloc(sizeof(Membership)); + *m = EMPTY_MEMBERSHIP; + + return m; +} + +EXPORT Person * +alloc_person(void) +{ + Person *p; + + person_cnt++; + + p = smalloc(sizeof(Person)); + *p = EMPTY_PERSON; + return p; +} + +EXPORT Member_list * +alloc_member_list(void) +{ + Member_list *m; + + member_list_cnt++; + + m = smalloc(sizeof(Member_list)); + *m = EMPTY_MEMBER_LIST; + + return m; +} + +EXPORT Conference * +alloc_conference(void) +{ + Conference *c; + + conference_cnt++; + + c = smalloc(sizeof(Conference)); + *c = EMPTY_CONFERENCE; + return c; +} + +EXPORT Text_stat * +alloc_text_stat(void) +{ + Text_stat *t; + + text_stat_cnt++; + + t = smalloc(sizeof(Text_stat)); + *t = EMPTY_TEXT_STAT; + return t; +} + +EXPORT void +free_text_list(Text_list *text_list) +{ + if ( text_list == NULL ) + return; + + text_list_cnt--; + + sfree(text_list->texts); + text_list->texts = NULL; + text_list->no_of_texts = 0; +} + +EXPORT void +free_mark_list(Mark_list *mark_list) +{ + if ( mark_list == NULL ) + return; + + mark_list_cnt--; + + sfree(mark_list->marks); + mark_list->marks = NULL; + mark_list->no_of_marks = 0; +} + +EXPORT void +free_membership_list(Membership_list *mlist) +{ + int i; + + if ( mlist == NULL ) + return; + + membership_list_cnt--; + + for ( i = 0; i < mlist->no_of_confs; i++ ) + { + free_membership(&mlist->confs[i]); + } + + mlist->confs = NULL; + mlist->no_of_confs = 0; +} + +EXPORT void +free_membership(Membership *mship) +{ + if ( mship == NULL ) + return; + + membership_cnt--; + + sfree(mship->read_texts); + mship->read_texts = NULL; + mship->no_of_read = 0; +} + +EXPORT void +clear_person(Person *person) +{ + s_clear(&person->username); + free_text_list(&person->created_texts); + free_mark_list(&person->marks); + free_membership_list(&person->conferences); + *person = EMPTY_PERSON; +} + +EXPORT void +free_person(Person *person) +{ + if ( person == NULL ) + return; + + person_cnt--; + + clear_person(person); + + sfree(person); +} + + +EXPORT void +free_member_list(Member_list *m) +{ + if ( m == NULL ) + return; + + member_list_cnt--; + + sfree(m->members); + m->members = NULL; + m->no_of_members = 0; +} + +EXPORT void +clear_conference(Conference *confp) +{ + s_clear(&confp->name); + free_member_list(&confp->members); + free_text_list(&confp->texts); + *confp = EMPTY_CONFERENCE; +} + +EXPORT void +free_conference(Conference *confp) +{ + if ( confp == NULL ) + return; + + conference_cnt--; + clear_conference(confp); + sfree(confp); +} + +EXPORT void +clear_text_stat(Text_stat *t) +{ + int i; + + for ( i = 0; i < t->no_of_misc; i++ ) + { + switch ( t->misc_items[ i ].type ) + { + case recpt: + case cc_recpt: + case comm_to: + case comm_in: + case footn_to: + case footn_in: + case loc_no: + case rec_time: + case sent_by: + case sent_at: + /* No need to free anything for these. */ + break; + + default: + restart_kom(__FILE__ ": free_text_stat: unknown Info_type %d.", + t->misc_items[ i ].type ); + } + } + + sfree(t->misc_items); + *t = EMPTY_TEXT_STAT; +} + +EXPORT void +free_text_stat(Text_stat *t) +{ + if ( t == NULL ) + return; + + text_stat_cnt--; + clear_text_stat(t); + sfree(t); +} + + +EXPORT Person * +copy_person(Person *p) +{ + Person *c; + + c = alloc_person(); + *c = *p; + c->username = EMPTY_STRING; + s_strcpy(&c->username, p->username); + + c->created_texts = copy_text_list(p->created_texts); + c->marks = copy_mark_list(p->marks); + c->conferences = copy_membership_list(p->conferences); + return c; +} + +EXPORT Conference * +copy_conf(Conference *o) +{ + Conference *c; + + c = alloc_conference(); + *c = *o; + c->name = EMPTY_STRING; + s_strcpy(&c->name, o->name); + c->members = copy_member_list(o->members); + c->texts = copy_text_list(o->texts); + return c; +} + +EXPORT Text_stat * +copy_text_stat(Text_stat *t) +{ + Text_stat *c; + + c = alloc_text_stat(); + *c = *t; + c->misc_items = smalloc(c->no_of_misc * sizeof(Misc_info)); + memcpy(c->misc_items, t->misc_items, c->no_of_misc * sizeof(Misc_info)); + return c; +} + + +EXPORT Text_list +copy_text_list(Text_list tl) +{ + Text_list r; + + r = tl; + r.texts = smalloc(r.no_of_texts * sizeof(Text_no)); + memcpy(r.texts, tl.texts, r.no_of_texts * sizeof(Text_no)); + + return r; +} + +EXPORT Mark_list +copy_mark_list(Mark_list ml) +{ + Mark_list r; + + r.no_of_marks = ml.no_of_marks; + r.marks = smalloc(r.no_of_marks * sizeof(Mark)); + memcpy(r.marks, ml.marks, r.no_of_marks * sizeof(Mark)); + return r; +} + +static Membership +copy_membership(Membership m) +{ + Membership res; + + res = m; + res.read_texts = smalloc(m.no_of_read * sizeof(Local_text_no)); + memcpy(res.read_texts, m.read_texts, m.no_of_read * sizeof(Local_text_no)); + return res; +} + +EXPORT Membership_list +copy_membership_list(Membership_list ml) +{ + Membership_list r; + int i; + + r.no_of_confs = ml.no_of_confs; + r.confs = smalloc(ml.no_of_confs * sizeof(Membership)); + + for ( i = 0; i < r.no_of_confs; i++ ) + { + r.confs[i] = copy_membership(ml.confs[i]); + } + + return r; +} + +EXPORT Member_list +copy_member_list(Member_list ml) +{ + Member_list res; + + res.no_of_members = ml.no_of_members; + res.members = smalloc(res.no_of_members * sizeof ( Member )); + memcpy(res.members, ml.members, res.no_of_members * sizeof ( Member )); + return res; +} + + diff --git a/src/server/memory.h b/src/server/memory.h new file mode 100644 index 0000000000000000000000000000000000000000..2953425616d67666e0ec6b4e33f3f770d43b95ca --- /dev/null +++ b/src/server/memory.h @@ -0,0 +1,82 @@ + +extern Text_list * +alloc_text_list(void); + +extern Mark_list * +alloc_mark_list(void); + +extern Membership_list * +alloc_membership_list(void); + +extern Membership * +alloc_membership(void); + +extern Person * +alloc_person(void); + +extern Member_list * +alloc_member_list(void); + +extern Conference * +alloc_conference(void); + +extern Text_stat * +alloc_text_stat(void); + +extern Small_conf * +alloc_small_conf(void); + +extern void +free_text_list(Text_list *text_list); + +extern void +free_mark_list(Mark_list *mark_list); + +extern void +free_membership_list(Membership_list *mlist); + +extern void +free_membership(Membership *mship); + +extern void +free_person(Person *person); + +extern void +free_member_list(Member_list *m); + +extern void +free_conference(Conference *confp); + +extern void +free_text_stat(Text_stat *t); + +extern Person * +copy_person(Person *p); + +extern Conference * +copy_conf(Conference *c); + +extern Text_stat * +copy_text_stat(Text_stat *c); + + +extern void +clear_conference(Conference *c); + +extern void +clear_person(Person *p); + +extern void +clear_text_stat(Text_stat *t); + +extern Text_list +copy_text_list(Text_list tl); + +extern Mark_list +copy_mark_list(Mark_list ml); + +extern Membership_list +copy_membership_list(Membership_list ml); + +extern Member_list +copy_member_list(Member_list ml); diff --git a/src/server/minmax.h b/src/server/minmax.h new file mode 100644 index 0000000000000000000000000000000000000000..57ad9ccf091781b6d55705aa9ec6cb350a333e36 --- /dev/null +++ b/src/server/minmax.h @@ -0,0 +1,6 @@ +/* + * I can't believe that this really doesn't exist in a standard library! + */ + +#define min(a, b) ((a) > (b) ? (b) : (a)) +#define max(a, b) ((a) > (b) ? (a) : (b)) diff --git a/src/server/missing-ansi.c b/src/server/missing-ansi.c new file mode 100644 index 0000000000000000000000000000000000000000..d3acdff9c389bb60ef8905cb6cd1a651c0ad97f7 --- /dev/null +++ b/src/server/missing-ansi.c @@ -0,0 +1,83 @@ +/* + * Some missing functions that are needed. + * + * Written by ceder. + */ + +#include <string.h> +#include <time.h> + +#if defined(__sequent__) && !defined(DEBUG_MALLOC) + +void * +memcpy (void * s1, const void * s2, size_t n) +{ + extern bcopy(const char *b1, char *b2, unsigned int length); + + /* bcopy takes the parameters the other way round. */ + + bcopy(s2, s1, n); + return s1; /* Since ANSI says so */ +} + +#endif + +#if defined(__sequent__) || defined(__sun__) + +extern double +difftime(time_t t1, time_t t2) +{ + return (double) t1 - (double) t2; +} + +#endif + +#ifdef __sequent__ + +/* + * This version in NOT 100 % ANSI-conformant, but it is enough to run LysKOM + * (I hope) /ceder + */ + +extern time_t +mktime (const struct tm * temeptr ) +{ + int nyears; + int nleaps; + int ndays; + + nyears = temeptr->tm_year - 70; + nleaps = (nyears+2) / 4; + + ndays = 365 * nyears + nleaps + temeptr->tm_yday; + + return (time_t) ( temeptr->tm_sec + + 60 * ( temeptr->tm_min + + 60 * ( temeptr->tm_hour + + 24 * ndays ))); +} + +#endif + + +#ifdef __sequent__ +#include <errno.h> +#include <stdio.h> + +extern char *sys_errlist[]; +extern int sys_nerr; + +const char *strerror(int eno) +{ + static char buf[200]; + + + if (eno < 0 || eno >= sys_nerr) + { + sprintf(buf, "error #%d", eno); + return buf; + } + else + return sys_errlist[eno]; +} +#endif diff --git a/src/server/mux-parse.c b/src/server/mux-parse.c new file mode 100644 index 0000000000000000000000000000000000000000..20502d7980207403b68220b8fbcfa5e1a2229947 --- /dev/null +++ b/src/server/mux-parse.c @@ -0,0 +1,226 @@ +/* + ** mux-parse.c Handle the MUX protocol + * This is a hack. Clean this mess up with lacgen. + */ + +#include <stddef.h> +#include <stdarg.h> +#include <string.h> +#include "lyskomd.h" +#include <kom-types.h> +#include "com.h" +#include "connections.h" +#include "isc.h" +#include "mux.h" +#include "smalloc.h" +#include <setjmp.h> +#include <string.h> +#include <stdio.h> +#include "mux-parse.h" +#include "minmax.h" +#include "config.h" +#include <s-string.h> + +jmp_buf mux_parse_env; + +static String +mux_get_token(Mux *mux) +{ + String result; + String_size old_first; + + old_first = mux->parse.first_to_parse; + + result = s_strtok(mux->parse.unparsed, &mux->parse.first_to_parse, + s_fcrea_str(" \t\n\r")); + + /* Check that there was at least one trailing blank. */ + + if ( mux->parse.first_to_parse >= s_strlen(mux->parse.unparsed) ) + { + mux->parse.first_to_parse = old_first; + longjmp(mux_parse_env, MUX_MSG_INCOMPLETE); + } + + return result; +} + +static long +mux_parse_long(Mux *mux) +{ + String token; + String_size end; + long res; + + token = mux_get_token(mux); + res = s_strtol(token, &end, PROTOCOL_NUMBER_BASE); + if (end != s_strlen(token)) + longjmp(mux_parse_env, MUX_PROTOCOL_ERR); + + return res; +} + + +/* + * Parse a string. At most 'maxlen' characters are allowed. If the + * mux sends a longer string only the first 'maxlen+1' characters + * are read. The following characters are discarded. + */ +static void +mux_parse_string(Mux *mux, + String *result, + int maxlen) +{ + String_size hptr; /* Pointer to 'H' */ + String_size mux_len; /* The len the mux is sending. */ + String_size truncated_len; /* How much the server will receive. */ + String_size to_skip; + String tmp; + + switch ( mux->parse.string_parse_pos ) + { + case 0: + /* Get number and discard trailing 'H' */ + result->len = s_strtol(s_fsubstr(mux->parse.unparsed, + mux->parse.first_to_parse, + END_OF_STRING), + &hptr, PROTOCOL_NUMBER_BASE); + + if ( hptr == -1 + || mux->parse.first_to_parse + hptr + >= s_strlen(mux->parse.unparsed) ) + { + longjmp(mux_parse_env, MUX_MSG_INCOMPLETE); + } + + /* Check that + a) there is a trailing H + b) there was at least one digit before the H */ + + if ( mux->parse.unparsed.string[ mux->parse.first_to_parse + + hptr ] != 'H' + || hptr <= 0 ) + { + longjmp(mux_parse_env, MUX_PROTOCOL_ERR); + } + + mux->parse.first_to_parse += 1 + hptr; + mux->parse.string_parse_pos = 1; + /* Fall through */ + case 1: + /* Check that the entire string is transmitted. */ + /* (Don't care about the trailing part that will be skipped if the + * string is longer than maxlen) */ + truncated_len = min(maxlen + 1, result->len); + mux_len = result->len; + + if ( mux->parse.first_to_parse + truncated_len + > s_strlen(mux->parse.unparsed) ) + { + longjmp(mux_parse_env, MUX_MSG_INCOMPLETE); + } + + result->string = (mux->parse.unparsed.string + + mux->parse.first_to_parse); + result->len = truncated_len; + tmp = EMPTY_STRING; + s_strcpy(&tmp, *result); /* Copy the string. */ + *result = tmp; + result->len = mux_len; + + mux->parse.first_to_parse += truncated_len; + mux->parse.string_parse_pos = 2; + /* Fall through */ + case 2: + /* Was the string too long? If so, skip the truncated data. */ + + mux_len = result->len; + truncated_len = min(maxlen+1, result->len); + + if ( mux_len > truncated_len ) + { + to_skip = min(mux_len - truncated_len, + mux->parse.unparsed.len - mux->parse.first_to_parse); + mux_len -= to_skip; + mux->parse.first_to_parse += to_skip; + } + + result->len = mux_len; + + if ( mux_len > truncated_len ) + longjmp(mux_parse_env, MUX_MSG_INCOMPLETE); + /* Fall through */ + default: + mux->parse.string_parse_pos = 0; + } +} + + + + + +static Bool +mux_is_legal_fnc(int fnc) +{ + switch(fnc) + { + case 0: + case 1: + case 2: + case 3: + return TRUE; + + default: + return FALSE; + } +} + +static void +mux_parse_client_message(Mux *mux) +{ + switch(mux->parse.parse_pos_2) + { + case 0: + mux->parse.num = mux_parse_long(mux); + mux->parse.parse_pos_2 = 1; + /* Fall through */ + case 1: + mux_parse_string(mux, &mux->parse.string, 10000); + /* Fall through */ + default: + mux->parse.parse_pos_2 = 0; + } +} + +void +mux_parse_packet(Mux *mux) +{ + switch(mux->parse.parse_pos) + { + case 0: /* Get fnc_no */ + mux->parse.function = mux_parse_long(mux); + if ( !mux_is_legal_fnc(mux->parse.function) ) + longjmp(mux_parse_env, MUX_PROTOCOL_ERR); + mux->parse.parse_pos = 1; + /* Fall through */ + case 1: + /* Call the function that parses the arguments for this call. */ + switch(mux->parse.function) + { + case 0: + break; + + case 2: + mux->parse.num = mux_parse_long(mux); + break; + + case 1: + case 3: + mux_parse_client_message(mux); + break; + } + /* Fall through */ + default: + mux->parse.parse_pos = 0; + } +} diff --git a/src/server/mux-parse.h b/src/server/mux-parse.h new file mode 100644 index 0000000000000000000000000000000000000000..5e09d796b6b564e763dd08e6467c00bfa231bf64 --- /dev/null +++ b/src/server/mux-parse.h @@ -0,0 +1,15 @@ +/* + ** mux-parse.c Handle the MUX protocol + * This is a hack. + */ + +#ifdef _SETJMP_ +extern jmp_buf mux_parse_env; +#endif + +#define MUX_PROTOCOL_ERR 1 +#define MUX_MSG_INCOMPLETE 2 +#define MUX_LOGOUT 3 + +extern void +mux_parse_packet(Mux *mux); diff --git a/src/server/mux.c b/src/server/mux.c new file mode 100644 index 0000000000000000000000000000000000000000..16b7ee5c3d21d40aab248cbef67b6b9da3d15aa9 --- /dev/null +++ b/src/server/mux.c @@ -0,0 +1,401 @@ +/* +** mux.c +*/ + +#include <stddef.h> +#include <stdarg.h> +#include <string.h> +#include "s-string.h" +#include "lyskomd.h" +#include <kom-types.h> +#include "com.h" +#include "connections.h" +#include "isc.h" +#include "mux.h" +#include "smalloc.h" + + +extern _printf( int (*sputc)(int chr), const char *fmt, va_list AP); + + +/* +** MUX support functions +*/ + +Mux * +mux_create(Mux_type type, ISCSCB *scb) +{ + Mux *mp; + + + mp = (Mux *) smalloc(sizeof(Mux)); + + mp->magic = MUX_MAGIC_ALLOC; + mp->type = type; + mp->scb = scb; + mp->client_c = 0; + mp->client_v = NULL; + + mp->parse.parse_pos = 0; + mp->parse.parse_pos_2 = 0; + mp->parse.string_parse_pos = 0; + mp->parse.first_to_parse = 0; + mp->parse.more_to_parse = TRUE; + mp->parse.function = 0; + mp->parse.string = EMPTY_STRING; + mp->parse.unparsed = EMPTY_STRING; + + return mp; +} + +void +mux_free_parsed(Mux *mp) +{ + s_clear(&mp->parse.string); + mp->parse.parse_pos = 0; + mp->parse.parse_pos_2 = 0; + mp->parse.string_parse_pos = 0; +} + +void +mux_destruct(Mux *mp) +{ + switch (mp->magic) + { + case MUX_MAGIC_ALLOC: + break; + + case MUX_MAGIC_FREE: + restart_kom("MUX_DESTRUCT: Trying to destruct freed Mux block\n"); + + default: + restart_kom("MUX_DESTRUCT: Bad magic number\n"); + } + + mp->magic = MUX_MAGIC_FREE; + + mux_free_parsed(mp); + s_clear(&mp->parse.unparsed); + sfree(mp->client_v); + sfree(mp); +} + + + +int +mux_clients(Mux *mp) +{ + int i; + int c; + + + switch (mp->magic) + { + case MUX_MAGIC_ALLOC: + break; + + case MUX_MAGIC_FREE: + restart_kom("MUX_CLIENTS: Trying to work on freed Mux block\n"); + + default: + restart_kom("MUX_CLIENTS: Bad magic number\n"); + } + + for (i = 0, c = 0; i < mp->client_c; i++) + if (mp->client_v[i].id != -1) + c++; + + return c; +} + + +void +mux_addclient(Mux *mp, int id, Connection *cp) +{ + int i; + + + switch (mp->magic) + { + case MUX_MAGIC_ALLOC: + break; + + case MUX_MAGIC_FREE: + restart_kom("MUX_ADDCLIENT: Trying to work on freed Mux block\n"); + + default: + restart_kom("MUX_ADDCLIENT: Bad magic number\n"); + } + + for (i = 0; i < mp->client_c; i++) + if (mp->client_v[i].id == -1) + break; + + if (i == mp->client_c) + { + mp->client_c++; + if (mp->client_v == NULL) + mp->client_v = (Mux_client *) smalloc(sizeof(Mux_client)); + else + mp->client_v = (Mux_client *) srealloc(mp->client_v, + sizeof(Mux_client) * + (mp->client_c)); + } + + mp->client_v[i].conn = cp; + mp->client_v[i].id = id; + mp->client_v[i].outmsg = isc_allocmsg(MUX_OUTMSGSIZE); +} + + + +void +mux_delclient(Mux *mp, int id) +{ + int i; + + + switch (mp->magic) + { + case MUX_MAGIC_ALLOC: + break; + + case MUX_MAGIC_FREE: + restart_kom("MUX_DELCLIENT: Trying to work on freed Mux block\n"); + + default: + restart_kom("MUX_DELCLIENT: Bad magic number\n"); + } + + for (i = 0; i < mp->client_c; i++) + if (mp->client_v[i].id == id) + { + mp->client_v[i].id = -1; + mp->client_v[i].conn = NULL; + isc_freemsg(mp->client_v[i].outmsg); + return; + } +} + + +Mux_client * +mux_getclientbyid(Mux *mp, int id) +{ + int i; + + + switch (mp->magic) + { + case MUX_MAGIC_ALLOC: + break; + + case MUX_MAGIC_FREE: + restart_kom("MUX_GETCLIENTBYID: Trying to work on freed Mux block\n"); + + default: + restart_kom("MUX_GETCLIENTBYID: Bad magic number\n"); + } + + + for (i = 0; i < mp->client_c; i++) + if (mp->client_v[i].id == id) + return &mp->client_v[i]; + + return NULL; +} + + + +Mux_client * +mux_getclientbyconn(Mux *mp, Connection *cp) +{ + int i; + + + switch (mp->magic) + { + case MUX_MAGIC_ALLOC: + break; + + case MUX_MAGIC_FREE: + restart_kom("MUX_GETCLIENTBYCONN: Trying to work on freed Mux block\n"); + + default: + restart_kom("MUX_GETCLIENTBYCONN: Bad magic number\n"); + } + + for (i = 0; i < mp->client_c; i++) + if (mp->client_v[i].conn == cp) + return &mp->client_v[i]; + + return NULL; +} + + +/* +** MUX output functions +*/ + + +int +mux_write(Connection * cp, + const char * buf, + size_t size) +{ + Mux * mp = cp->mux; + Mux_client * mcp; + size_t osize; + size_t wlen; + size_t clen; + + + mcp = mux_getclientbyconn(mp, cp); + if (mcp == NULL) + /* BETTER ERROR HANDLING SHOULD GO HERE */ + restart_kom("mux_write: STALE CLIENT ID\n"); + + osize = size; + while (size > 0) + { + while ((wlen = mcp->outmsg->size - mcp->outmsg->length) == 0) + mux_flush(cp); + + clen = size > wlen ? wlen : size; + memcpy(mcp->outmsg->buffer + mcp->outmsg->length, buf, clen); + mcp->outmsg->length += clen; + size -= clen; + buf += clen; + } + + return osize; +} + + + + +int +mux_putc(int chr, + Connection * cp) +{ + Mux * mp = cp->mux; + Mux_client * mcp; + + + mcp = mux_getclientbyconn(mp, cp); + if (mcp == NULL) + /* BETTER ERROR HANDLING SHOULD GO HERE */ + restart_kom("mux_putc: STALE CLIENT ID\n"); + + while (mcp->outmsg->length == mcp->outmsg->size) + mux_flush(cp); + + mcp->outmsg->buffer[mcp->outmsg->length++] = chr; + + return chr; +} + + + +static Connection *send_cp; + +static int +send_putc(int chr) +{ + return mux_putc(chr, send_cp); +} + + + +int +mux_printf(Connection * cp, + const char * format, + ...) +{ + va_list AP; + int retval = -1; + + + va_start(AP, format); + + send_cp = cp; + retval = _printf(&send_putc, format, AP); + + va_end(AP); + + return retval; +} + + +void +mux_flush(Connection * cp) +{ + Mux * mp = cp->mux; + Mux_client * mcp; + + + mcp = mux_getclientbyconn(mp, cp); + if (mcp == NULL) + /* BETTER ERROR HANDLING SHOULD GO HERE */ + restart_kom("mux_flush: STALE CLIENT ID\n"); + + if (mcp->outmsg->length == 0) + return; + + switch (mp->type) + { + case MUX_TYPE_CLIENT: + isc_send(mp->scb, mcp->outmsg); + isc_flush(mp->scb); + mcp->outmsg = isc_allocmsg(MUX_OUTMSGSIZE); + break; + + case MUX_TYPE_MUX: + isc_printf(mp->scb, "3 %d %dH", + mcp->id, mcp->outmsg->length); + isc_write(mp->scb, mcp->outmsg->buffer, mcp->outmsg->length); + isc_printf(mp->scb, "\n"); + isc_flush(mp->scb); + mcp->outmsg->length = 0; + break; + + default: + restart_kom("mux_flush: ILLEGAL MUX TYPE: %d\n", mp->type); + } +} + + +void +mux_close(Connection * cp) +{ + Mux * mp = cp->mux; + Mux_client * mcp; + + + mcp = mux_getclientbyconn(mp, cp); + if (mcp == NULL) + /* BETTER ERROR HANDLING SHOULD GO HERE */ + restart_kom("mux_close: STALE CLIENT ID\n"); + + mux_flush(cp); + + switch (mp->type) + { + case MUX_TYPE_CLIENT: + isc_close(mp->scb); + mux_destruct(mp); + cp->mux = NULL; + break; + + case MUX_TYPE_MUX: + isc_printf(mp->scb, "2 %d\n", mcp->id); + isc_flush(mp->scb); + + mux_delclient(mp, mcp->id); + cp->mux = NULL; + break; + + default: + restart_kom("mux_close: ILLEGAL MUX TYPE: %d\n", mp->type); + } +} + diff --git a/src/server/mux.h b/src/server/mux.h new file mode 100644 index 0000000000000000000000000000000000000000..f4e469d608218bc5f9886f152d273fea605d3dfb --- /dev/null +++ b/src/server/mux.h @@ -0,0 +1,113 @@ +/* +** mux.h +*/ + +#ifndef __LYSKOM__MUX_H__ +#define __LYSKOM__MUX_H__ + +#define MUX_OUTMSGSIZE 1024 + +#define MUX_MAGIC_ALLOC 0x56AE17DB +#define MUX_MAGIC_FREE 0xF24A6BE7 + +typedef enum +{ + MUX_TYPE_CLIENT, + MUX_TYPE_MUX +} Mux_type; + + + +typedef struct +{ + int id; /* MUX client id */ + Connection * conn; /* LysKOM virtual Connection */ + + struct isc_msg * outmsg; +} Mux_client; + +typedef struct +{ + int parse_pos; + int parse_pos_2; + int string_parse_pos; + int function; /* Function to call. */ + + /* Gather data in these variables. */ + + long num; + String string; + String unparsed; + String_size first_to_parse; /* Index into unparsed. */ + Bool more_to_parse; /* Any chance that there is anything + left in unparsed? */ +} Mux_parse; + +typedef struct mux +{ + unsigned long magic; /* Magic number :-) */ + Mux_type type; + struct isc_scb * scb; + + Mux_parse parse; + + int client_c; + Mux_client * client_v; +} Mux; + + +/* +** MUX support functions +*/ + +extern Mux * +mux_create(Mux_type type, IscSession *scb); + +extern void +mux_destruct(Mux *mp); + +extern int +mux_clients(Mux *mp); + +extern void +mux_addclient(Mux *mp, int id, Connection *cp); + +extern void +mux_delclient(Mux *mp, int id); + +extern Mux_client * +mux_getclientbyid(Mux *mp, int id); + +extern Mux_client * +mux_getclientbyconn(Mux *mp, Connection *cp); + + + +/* +** MUX output functions +*/ + +extern int +mux_write(Connection * cp, + const char * buf, + size_t size); + +extern int +mux_printf(Connection * cp, + const char * format, + ...); + +extern int +mux_putc(int chr, + Connection * cp); + +extern void +mux_flush(Connection * cp); + +extern void +mux_close(Connection * cp); + +extern void +mux_free_parsed(Mux *mp); + +#endif diff --git a/src/server/person.c b/src/server/person.c new file mode 100644 index 0000000000000000000000000000000000000000..e1e79c1e9aa946f7af768caca4d94998b6111609 --- /dev/null +++ b/src/server/person.c @@ -0,0 +1,715 @@ +/* + * person.c + * + * All atomic calls that deals with persons. + */ +#include <time.h> +#include <string.h> +#include <stdlib.h> +#include "lyskomd.h" +#include <kom-types.h> +#include <services.h> +#include "manipulate.h" +#include <kom-errno.h> +#include "smalloc.h" +#include "cache.h" +#include "log.h" +#include "minmax.h" +#include <config.h> +#include "parser.h" +#include "com.h" +#include "connections.h" +#include "send-async.h" +#include <debug.h> +/* + * Static functions. + */ +static Bool +legal_passwd(const String pwd) +{ + return TRUE; /* ??? */ +} + +static Success +do_set_passwd( Password pwd, + const String new_pwd) +{ + if ( !legal_passwd(new_pwd) ) + return FAILURE; + + *pwd++ = new_pwd.len; /*** Maybe too easy crypt... */ + strncpy(pwd, new_pwd.string, min( PASSWD_LEN-1, new_pwd.len )); + return OK; +} + + + +/* + * Mark a text. No check is done if pers_p is really allowed to mark it. + * Use mark_type==0 to unmark the text. + */ + +static Success +do_mark_text(Pers_no pers_no, + Person *pers_p, /* May be NULL */ + Text_no text_no, + Text_stat *text_s, /* May be NULL */ + u_char mark_type) +{ + Mark *markp; + Mark *old_mark = NULL; + int i; + BUGDECL; + + if ( pers_p == NULL ) + GET_P_STAT(pers_p, pers_no, FAILURE); + + /* Locate the mark. */ + + for ( i = pers_p->marks.no_of_marks, markp = pers_p->marks.marks; + i > 0 && old_mark == NULL; + i--, markp++ ) + { + if ( markp->text_no == text_no ) + old_mark = markp; + } + + if (old_mark != NULL && mark_type != 0) + { + /* Change marktype of an already marked text. (No need to check + that the text exists). */ + BUG(("do_mark_text(): Change type of mark.\n")); + + old_mark->mark_type = mark_type; + mark_person_as_changed(pers_no); + } + else if (old_mark != NULL) + { + /* Delete this mark. */ + BUG(("do_mark_text(): Delete mark.\n")); + + while ( old_mark < ( pers_p->marks.marks + + pers_p->marks.no_of_marks - 1 )) + { + *old_mark = *(old_mark + 1); + old_mark++; + } + + pers_p->marks.no_of_marks--; + mark_person_as_changed(pers_no); + + /* ...and don't forget to decrease the ref_cnt in the text_stat... */ + + if ( (text_s = cached_get_text_stat(text_no)) != NULL ) + { + if ( text_s->no_of_marks == 0 ) + { + log("WNG: do_mark_text(): Text %d has no_of_marks==0,\ + but person %d had marked the text.", + text_no, pers_no); + } + else + { + text_s->no_of_marks--; + mark_text_as_changed(text_no); + } + } + /* ...but it is not an error if someone has deleted the text. */ + } + else if ( mark_type != 0 ) + { + /* A new mark. Check that the text exists. */ + BUG(("do_mark_text(): new mark.\n")); + + if ( text_s == NULL ) + GET_T_STAT(text_s, text_no, FAILURE); + + if (text_s->no_of_marks >= MAX_MARKS_TEXT + || pers_p->marks.no_of_marks >= MAX_MARKS_PERSON) + { + kom_errno = KOM_MARK_LIMIT; + return FAILURE; + } + + text_s->no_of_marks++; + mark_text_as_changed(text_no); + + pers_p->marks.marks = srealloc(pers_p->marks.marks, + ++pers_p->marks.no_of_marks * sizeof(Mark)); + pers_p->marks.marks[pers_p->marks.no_of_marks - 1].text_no = text_no; + pers_p->marks.marks[pers_p->marks.no_of_marks - 1].mark_type + = mark_type; + mark_person_as_changed(pers_no); + } + else + { + /* An attempt to delete a non-existent mark. This is a noop. */ + BUG(("do_mark_text(): delete non-existent mark.\n")); + } + + return OK; +} + + +/* + * Change user_area of a person. If text_no is 0, there will be + * no user_area. + */ + +static Success +do_set_user_area(Pers_no pers_no, + Person * pers_p, + Text_no text_no) +{ + Text_stat * old_user_area; + Text_stat * new_user_area = NULL; /* To stop gcc complaining. */ + + /* Check that the new user_area exists before deleting the old */ + + if ( text_no != 0 ) + { + GET_T_STAT(new_user_area, text_no, FAILURE); + if ( new_user_area->no_of_marks >= MAX_MARKS_TEXT ) + { + log("LIMIT: do_set_user_area(): New user_area very marked.\n"); + return FAILURE; + } + } + + /* Unmark the previous user_area if it exists. */ + + if ( pers_p->user_area != 0 + && (old_user_area = cached_get_text_stat(text_no)) != NULL) + { + if ( old_user_area->no_of_marks > 0 ) + { + --old_user_area->no_of_marks; + mark_text_as_changed( text_no ); + } + else + { + log("ERROR: do_set_user_area(): Old user_area not marked\n"); + } + } + + /* Mark the new user_area */ + + if ( text_no != 0 ) + { + ++new_user_area->no_of_marks; + mark_text_as_changed( text_no ); + } + + pers_p->user_area = text_no; + mark_person_as_changed( pers_no ); + + return OK; +} +/* + * End of static functions. + */ + +/* + * Functions that are exported to the server. + */ + +/* + * Delete a person. (The mailbox is not deleted). + */ + +Success +do_delete_pers (Pers_no pers_no) +{ + Person * pers_p; + int i; + + GET_P_STAT(pers_p, pers_no, FAILURE); + + /* Cancel membership in all confs. */ + /* Note that because of the way do_sub_member is written the */ + /* loop must be executed this way. */ + for ( i = pers_p->conferences.no_of_confs - 1; i >= 0; i-- ) + { + if ( do_sub_member( pers_p->conferences.confs[ i ].conf_no, NULL, NULL, + pers_no, pers_p, + pers_p->conferences.confs + i) != OK) + { + log("ERROR: do_delete_pers(): can't sub_member\n"); + } + } + + for ( i = pers_p->marks.no_of_marks; i > 0; i-- ) + { + if ( do_mark_text(pers_no, pers_p, + pers_p->marks.marks[0].text_no, NULL, 0) != OK ) + { + log("WNG: do_delete_pers(): can't unmark text %d (i == %d)\n", + pers_p->marks.marks[0].text_no, i); + } + } + + if ( do_set_user_area(pers_no, pers_p, 0) != OK ) + { + log("WNG: do_delete_pers(): can't unmark user_area\n"); + } + + s_clear( &pers_p->username ); + sfree( pers_p->created_texts.texts ); + pers_p->created_texts.texts = NULL; + pers_p->created_texts.no_of_texts = 0; + + cached_delete_person(pers_no); + /* ??? Eller r{cker det med cached_delete_conf() ??? */ + + /* ASYNC */ + return OK; +} + +/* + * Check a password + */ +Success +chk_passwd( Password pwd, + const String s ) +{ + int i; + + if ( s.len != *pwd ) + return FAILURE; + + for ( i = 0; i < s.len; i++ ) + if ( pwd[ i + 1 ] != s.string[ i ] ) + return FAILURE; + + return OK; +} + + +/* + * Mark a text. + * + * If the you already have marked the text the mark-type will be changed. + * If mark_type == 0 the mark will be deleted. You can only mark texts + * that you are allowed to read. You are always allowed to read all + * texts that you have marked even if you are no longer a recipient + * of the text. + */ + +extern Success +mark_text( Text_no text_no, /* Will fail if the user is not */ + u_char mark_type ) /* allowed to read the text. */ +{ + Text_stat *text_s = NULL; + BUGDECL; + + CHK_LOGIN(FAILURE); + + if ( mark_type != 0 ) + { + /* Check that the user is allowed to read the text. */ + GET_T_STAT(text_s, text_no, FAILURE); + if ( !text_read_access(text_no, text_s) ) + { + kom_errno = KOM_PERM; + return FAILURE; + } + } + + BUG(("Person %d markerar text %d med typ %d\n", + ACTPERS, text_no, mark_type)); + + return do_mark_text(ACTPERS, ACT_P, text_no, text_s, mark_type); +} + +/* + * Create a new person. + * + * If CREATE_PERSON_BEFORE_LOGIN (#define'd in config.h) is true + * anyone can create themself. Otherwise you must be logged in to + * create a new person. + * + * If you are not logged in an auto-login as the created person is + * performed. (This is so that it is impossible to create a million + * persons (and so destroy the database) anonymously. This way it will + * at least be possible to see from which machine the attack came). + * + * If you are logged in you will be supervisor and creator of the new + * person, otherwise the new person will be supervisor and creator. + * + * The new person will be rd_prot, but not secret. His privileges will + * be as defined in DEFAULT_PRIV_BITS in kom-types.c. + * + * This function returns the Pers_no of the new person, or 0 if an error + * occured. (0 is an illegal Pers_no). + */ +extern Pers_no +create_person (String name, + const String passwd ) +{ + Pers_no new_user; + Conference * mailbox; + Person * pers_p; + + + if (!CREATE_PERSON_BEFORE_LOGIN && ACTPERS == 0 ) + { + kom_errno = KOM_LOGIN; + return 0; + } + +#if 0 + if ( !logins_allowed && !ACTPERS ) /* A new user can't create himself */ + { /* if he isn't allowed to login. */ + kom_errno = KOM_LOGIN_DISALLOWED; + return 0; + } +#endif + + if ( !legal_name( name ) ) + { + kom_errno = KOM_BAD_NAME; + return 0; + } + + if ( !unique_name( name, 0 ) ) + { + kom_errno = KOM_CONF_EXISTS; + return 0; + } + + if ( !legal_passwd( passwd ) ) + { + kom_errno = KOM_PWD; + return 0; + } + + new_user = cached_create_conf( name ); + + if ( (mailbox = cached_get_conf_stat( new_user ) ) == NULL) + { + restart_kom("create_person() - can't get conf_stat"); + } + + + mailbox->creator = ACTPERS ? ACTPERS : new_user; + mailbox->creation_time = time(NULL); + mailbox->presentation = 0; /* No presentation yet */ + mailbox->supervisor = ACTPERS ? ACTPERS : new_user; + mailbox->permitted_submitters = 0; + mailbox->super_conf = ACTPERS; + mailbox->type.rd_prot = 1; + mailbox->type.original = 0; + mailbox->type.secret = 0; + mailbox->type.letter_box = 1; + mailbox->last_written = mailbox->creation_time; + mailbox->msg_of_day = 0; + mailbox->nice = DEFAULT_NICE; + mailbox->members = EMPTY_MEMBER_LIST; + mailbox->texts = EMPTY_TEXT_LIST; + + mark_conference_as_changed( new_user ); + + cached_set_conf_type(new_user, mailbox->type); + + /* allocate Person */ + + if ( cached_create_person( new_user ) == FAILURE ) + { + cached_delete_conf( new_user ); + mark_conference_as_changed( new_user ); /*&*/ + return 0; + } + + if ( ( pers_p = cached_get_person_stat( new_user )) == NULL ) + { + cached_delete_conf( new_user ); + mark_conference_as_changed( new_user ); /*&*/ + restart_kom("create_person() - can't get pers_stat"); + } + + + /* Fill in Person */ + *pers_p = EMPTY_PERSON; + if ( do_set_passwd( pers_p->pwd, passwd) != OK ) + restart_kom("create_person(): can't set passwd\n"); + + mark_person_as_changed( new_user ); + + do_add_member( new_user, mailbox, new_user, pers_p, UCHAR_MAX, 0 ); + + if ( ACTPERS ) + { + ACT_P->created_persons++; + mark_person_as_changed( ACTPERS ); + } + else + { + /* Auto login */ + login( new_user, passwd); + } + + return new_user; +} + + +/* + * Get the person status of PERSON. + */ +extern Success +get_person_stat (Pers_no person, + Person * result) +{ + Person *p_orig; + Conference *pers_c; + Access acc; + + CHK_LOGIN(FAILURE); + + GET_P_STAT(p_orig, person, FAILURE); + GET_C_STAT(pers_c, person, FAILURE); + + acc = access_perm(person, pers_c); + + if ( acc == error ) + return FAILURE; + + if ( acc == none ) + { + kom_errno = KOM_UNDEF_PERS; + return FAILURE; + } + + *result = *p_orig; /* Make a copy of the struct. */ + + /* The user area is normally secret. */ + + if( acc != unlimited ) + result->user_area = 0; + + return OK; +} + +/* As get_person_stat, but only return the name if bit 0 in mask is set. */ + +extern Success +get_person_stat_old (Pers_no person, + int mask, + Person * result) +{ + Person *p_orig; + Conference *pers_c; + Access acc; + + CHK_LOGIN(FAILURE); + + GET_P_STAT(p_orig, person, FAILURE); + GET_C_STAT(pers_c, person, FAILURE); + + acc = access_perm(person, pers_c); + + if ( acc == error ) + return FAILURE; + + if ( acc == none ) + { + kom_errno = KOM_UNDEF_PERS; + return FAILURE; + } + + *result = *p_orig; /* Make a copy of the struct. */ + + /* The user area is normally secret. */ + + if( acc != unlimited ) + result->user_area = 0; + + if ( !(mask & 1 ) ) + result->username = EMPTY_STRING; + + return OK; +} + + +/* /// */ +extern Success +get_created_texts(Pers_no pers_no, + Local_text_no first, + u_long len, + Text_list * result) +{ + Person *pers_p; + Access acc; + Local_text_no new_first; + + CHK_LOGIN(FAILURE); + + GET_P_STAT(pers_p, pers_no, FAILURE); + + acc = access_perm(pers_no, NULL); + + if ( acc == error ) + return FAILURE; + + if ( acc == none ) + { + kom_errno = KOM_UNDEF_PERS; + return FAILURE; + } + + *result = pers_p->created_texts; + new_first = max(result->first_local_no, first); + + if ( new_first >= result->first_local_no + result->no_of_texts) + { + kom_errno = KOM_NO_SUCH_LOCAL_TEXT; + return FAILURE; + } + + result->texts += new_first - result->first_local_no; + result->no_of_texts = min(len, (result->no_of_texts + - (new_first - result->first_local_no))); + result->first_local_no = new_first; + + return OK; +} + + + +/* + * Set privilege bits of a person. You must have the wheel bit set + * to be allowed to do this. + */ +extern Success +set_priv_bits( Pers_no person, + Priv_bits privileges ) +{ + Person *p; + + CHK_LOGIN(FAILURE); + + GET_P_STAT(p, person, FAILURE); + + if ( ENA(wheel, 6) ) + { + p->privileges = privileges; + mark_person_as_changed( person ); + return OK; + } + else + { + kom_errno = KOM_PERM; + return FAILURE; + } +} + +/* + * Set password. You may set the password of yourself and all persons that + * you are supervisor of. OLD_PWD is your password. + */ +extern Success +set_passwd (Pers_no person, + const String old_pwd, /* of the one who is changing the pwd, + not necessarily the person whose pwd + is changed. */ + const String new_pwd) /* of person */ +{ + Person *p; + + CHK_LOGIN(FAILURE); + + GET_P_STAT(p, person, FAILURE); + + if ( person != ACTPERS && !ENA(wheel, 7) + && !is_supervisor(person, NULL) ) + { + kom_errno = KOM_PERM; + return FAILURE; /* Not allowed to change the other persons pwd */ + } + + if ( chk_passwd( ACT_P->pwd, old_pwd ) == FAILURE ) + { + kom_errno = KOM_PWD; + return FAILURE; + } + + if ( do_set_passwd( p->pwd, new_pwd ) != OK) + { + kom_errno = KOM_PWD; + return FAILURE; + } + + mark_person_as_changed( person ); + + return OK; +} + +/* + * Set user_area. The text is a text which has previously been created + * with create_text(). It typically contains option-settings for the + * clients, e. g. if you want to be interrupted when new mail arrives. + * The format of this text is not yet defined. + * + * set_user_area(0) to clear the user area. + */ +Success +set_user_area(Text_no text_no) +{ + return do_set_user_area(ACTPERS, ACT_P, text_no); +} + +/* + * Ask which texts a person has read in a certain conference. + * Can be done before loggin. Will return EMPTY_MEMBERSHIP if VICTIM + * has his unread_is_secret-flag set. This can not be distinguished + * from the case when VICTIM has not read any texts in CONF_NO. This + * is a feature, not a bug. (At least I think so. /ceder+++) + * + * (*** Well, I've changed my mind. This call is full of security holes. + * /ceder) + * + * Will fail if VICTIM is not a member of CONF_NO. + */ +extern Success +query_read_texts(Pers_no victim, + Conf_no conf_no, + Membership * result) /* Points to area to store result in */ +{ + Person * pers_p; + Membership * membp; + Access victim_acc; + + GET_P_STAT( pers_p, victim, FAILURE); + + victim_acc = access_perm(victim, NULL); + if ( victim_acc <= none ) + { + kom_errno = KOM_UNDEF_PERS; + return FAILURE; + } + + if ( access_perm(conf_no, NULL) <= none ) + { + kom_errno = KOM_UNDEF_CONF; + return FAILURE; + } + + if ( (membp = locate_membership( victim, pers_p )) == NULL) + { + kom_errno = KOM_NOT_MEMBER; + return FAILURE; + } + + *result = *membp; + + if ( pers_p->flags.unread_is_secret && + victim_acc != unlimited) + { + result->last_time_read = NO_TIME; + result->last_text_read = 0; + result->no_of_read = 0; + result->read_texts = NULL; + } + + return OK; +} + diff --git a/src/server/prot-a-output.c b/src/server/prot-a-output.c new file mode 100644 index 0000000000000000000000000000000000000000..1eb461aad5a55fa51f2b5c950e6b8f2df010d5a3 --- /dev/null +++ b/src/server/prot-a-output.c @@ -0,0 +1,501 @@ +/* + * prot-a-output.c - write objects through a mux connection. + * + * Written by ceder 1990-07-13 + */ + +#include <kom-types.h> +#include <stdio.h> +#include <sys/file.h> +#include <time.h> +#include "lyskomd.h" +#include "com.h" +#include "connections.h" +#include "prot-a-output.h" +#include "isc.h" +#include "mux.h" + +void +prot_a_output_person (Connection *fp, + Person *person) +{ + prot_a_output_string (fp, person->username); + prot_a_output_priv_bits (fp, person->privileges); + prot_a_output_personal_flags (fp, person->flags); + + prot_a_output_time(fp, person->last_login); + + mux_printf (fp, " %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", + (u_long) person -> user_area, + (u_long) person -> total_time_present, /* This is not a time, + * but a number of seconds. + */ + (u_long) person -> sessions, + (u_long) person -> created_lines, + (u_long) person -> created_bytes, + (u_long) person -> read_texts, + (u_long) person -> no_of_text_fetches, + (u_long) person -> created_persons, + (u_long) person -> created_confs, + (u_long) person -> created_texts.first_local_no, + (u_long) person -> created_texts.no_of_texts, + (u_long) person -> marks.no_of_marks, + (u_long) person -> conferences.no_of_confs); +} + +void +prot_a_output_membership(Connection *fp, + const Membership *mship) +{ + int i; + + prot_a_output_time(fp, mship->last_time_read ); + + mux_printf(fp, " %lu %lu %lu %lu", + (u_long)mship->conf_no, + (u_long)mship->priority, + (u_long)mship->last_text_read, + (u_long)mship->no_of_read); + + if ( mship->read_texts != NULL && mship->no_of_read > 0) + { + mux_printf(fp, " {"); + for ( i = 0; i < mship->no_of_read; i++) + mux_printf(fp, " %lu", (u_long)mship->read_texts[ i ]); + + mux_printf(fp, " }"); + } + else + mux_printf(fp, " *"); +} + + +void +prot_a_output_membership_list (Connection * fp, + Membership_list mlist) +{ + int i; + + mux_printf(fp, " %lu", (u_long)mlist.no_of_confs); + + if ( mlist.confs != NULL && mlist.no_of_confs > 0 ) + { + mux_printf(fp, " {"); + for ( i = 0; i < mlist.no_of_confs; i++) + prot_a_output_membership(fp, mlist.confs + i); + mux_printf(fp, " }"); + } + else + mux_printf(fp, " *"); +} + + +void +prot_a_output_conf_list(Connection *fp, + Conf_list_old conf_list) +{ + int i; + + mux_printf(fp, " %lu", (u_long)conf_list.no_of_conf_nos); + if ( conf_list.conf_nos != NULL && conf_list.no_of_conf_nos > 0 ) + { + mux_printf(fp, " {"); + for ( i = 0; i < conf_list.no_of_conf_nos; i++ ) + mux_printf(fp, " %lu", (u_long)conf_list.conf_nos[ i ]); + mux_printf(fp, " }"); + } + else + mux_printf(fp, " *"); + + if ( conf_list.type_of_conf != NULL && conf_list.no_of_conf_nos > 0 ) + { + mux_printf(fp, " {"); + for ( i = 0; i < conf_list.no_of_conf_nos; i++ ) + prot_a_output_conf_type(fp, conf_list.type_of_conf[ i ]); + mux_printf(fp, " }"); + } + else + mux_printf(fp, " *"); +} + +void +prot_a_output_conf_no_list(Connection *fp, + Conf_no_list conf_no_list) +{ + int i; + + mux_printf(fp, " %lu", (u_long)conf_no_list.no_of_confs); + if ( conf_no_list.conf_nos != NULL && conf_no_list.no_of_confs > 0 ) + { + mux_printf(fp, " {"); + for ( i = 0; i < conf_no_list.no_of_confs; i++ ) + mux_printf(fp, " %lu", (u_long)conf_no_list.conf_nos[ i ]); + mux_printf(fp, " }"); + } + else + mux_printf(fp, " *"); +} + + +void +prot_a_output_conference (Connection *fp, + Conference *conf_c) +{ + prot_a_output_string(fp, conf_c->name); + + prot_a_output_conf_type(fp, conf_c->type); + + prot_a_output_time(fp, conf_c -> creation_time ); + prot_a_output_time(fp, conf_c -> last_written ); + + mux_printf (fp, " %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", + (u_long) conf_c -> creator, + (u_long) conf_c -> presentation, + (u_long) conf_c -> supervisor, + (u_long) conf_c -> permitted_submitters, + (u_long) conf_c -> super_conf, + (u_long) conf_c -> msg_of_day, + (u_long) conf_c -> nice, + (u_long) conf_c -> members.no_of_members, + (u_long) conf_c -> texts.first_local_no, + (u_long) conf_c -> texts.no_of_texts); +} + + +void +prot_a_output_mark_list(Connection *fp, + Mark_list mark_list) +{ + int i; + + mux_printf(fp, " %lu", (u_long)mark_list.no_of_marks); + + if ( mark_list.marks != NULL && mark_list.no_of_marks > 0 ) + { + mux_printf(fp, " {"); + for ( i = 0; i < mark_list.no_of_marks; i++ ) + prot_a_output_mark(fp, mark_list.marks[ i ]); + mux_printf(fp, " }"); + } + else + mux_printf(fp, " *"); + +} + + +void +prot_a_output_text_stat(Connection *fp, + Text_stat *t_stat) +{ + int i; + + prot_a_output_time(fp, t_stat->creation_time); + + mux_printf(fp, " %lu %lu %lu %lu %lu", + (u_long)t_stat->author, + (u_long)t_stat->no_of_lines, + (u_long)t_stat->no_of_chars, + (u_long)t_stat->no_of_marks, + (u_long)t_stat->no_of_misc); + + if ( t_stat->misc_items != NULL && t_stat->no_of_misc > 0 ) + { + mux_printf(fp, " {"); + for ( i = 0; i < t_stat->no_of_misc; i++ ) + prot_a_output_misc_info(fp, t_stat->misc_items[ i ]); + mux_printf(fp, " }"); + } + else + mux_printf(fp, " *"); +} + + +void +prot_a_output_text_list(Connection *fp, + Text_list text_list) +{ + int i; + + mux_printf(fp, " %lu %lu", + (u_long)text_list.first_local_no, + (u_long)text_list.no_of_texts); + + if ( text_list.texts != NULL && text_list.no_of_texts > 0 ) + { + mux_printf(fp, " {"); + for ( i = 0; i < text_list.no_of_texts; i++ ) + mux_printf(fp, " %lu", (u_long)text_list.texts[ i ]); + mux_printf(fp, " }"); + } + else + mux_printf(fp, " *"); +} + +void +prot_a_output_who_info(Connection *fp, + Who_info *info) +{ + mux_printf(fp, " %lu %lu %lu", + (u_long)info->person, + (u_long)info->working_conference, + (u_long)info->session_no); + + prot_a_output_string(fp, info->what_am_i_doing); + prot_a_output_string(fp, info->username); +} + + +void +prot_a_output_who_info_list(Connection *fp, + Who_info_list info) +{ + int i; + + mux_printf(fp, " %lu", (u_long)info.no_of_persons); + + if ( info.info != NULL && info.no_of_persons > 0 ) + { + mux_printf(fp, " {"); + for ( i = 0; i < info.no_of_persons; i++ ) + { + prot_a_output_who_info(fp, &info.info[ i ]); + } + mux_printf(fp, " }"); + } + else + mux_printf(fp, " *"); +} + +void +prot_a_output_who_info_list_old(Connection *fp, + Who_info_list_old info) +{ + int i; + + mux_printf(fp, " %lu", (u_long)info.no_of_persons); + + if ( info.info != NULL && info.no_of_persons > 0 ) + { + mux_printf(fp, " {"); + for ( i = 0; i < info.no_of_persons; i++ ) + { + mux_printf(fp, " %lu %lu", + (u_long)info.info[ i ].person, + (u_long)info.info[ i ].working_conference); + + prot_a_output_string(fp, info.info[ i ].what_am_i_doing); + } + mux_printf(fp, " }"); + } + else + mux_printf(fp, " *"); +} + +void +prot_a_output_session_info(Connection *fp, + Session_info *info) +{ + mux_printf(fp, " %lu %lu %lu", + (u_long)info->person, + (u_long)info->working_conference, + (u_long)info->session); + + prot_a_output_string(fp, info->what_am_i_doing); + prot_a_output_string(fp, info->username); + + mux_printf(fp, " %lu", (u_long)info->idle_time); + prot_a_output_time(fp, info->connection_time); +} + +void +prot_a_output_info(Connection *fp, + Info *info) +{ + mux_printf(fp, " %lu %lu %lu %lu %lu %lu", + (u_long)info->version, + (u_long)info->conf_pres_conf, + (u_long)info->pers_pres_conf, + (u_long)info->motd_conf, + (u_long)info->kom_news_conf, + (u_long)info->motd_of_lyskom); +} + + +extern void +prot_a_output_string(Connection *fp, + String str) +{ + mux_printf(fp, " %luH", (u_long)str.len); + mux_write(fp, str.string, str.len); +} + + +extern void +prot_a_output_priv_bits(Connection *fp, + Priv_bits bits) +{ + mux_putc(' ', fp); + mux_putc(bits.wheel + '0', fp); + mux_putc(bits.admin + '0', fp); + mux_putc(bits.statistic + '0', fp); + mux_putc(bits.create_pers + '0', fp); + mux_putc(bits.create_conf + '0', fp); + mux_putc(bits.change_name + '0', fp); + mux_putc(bits.flg7 + '0', fp); + mux_putc(bits.flg8 + '0', fp); + mux_putc(bits.flg9 + '0', fp); + mux_putc(bits.flg10 + '0', fp); + mux_putc(bits.flg11 + '0', fp); + mux_putc(bits.flg12 + '0', fp); + mux_putc(bits.flg13 + '0', fp); + mux_putc(bits.flg14 + '0', fp); + mux_putc(bits.flg15 + '0', fp); + mux_putc(bits.flg16 + '0', fp); +} + + +extern void +prot_a_output_personal_flags(Connection *fp, + Personal_flags flags) +{ + mux_putc(' ', fp); + mux_putc(flags.unread_is_secret + '0', fp); + mux_putc(flags.flg2 + '0', fp); + mux_putc(flags.flg3 + '0', fp); + mux_putc(flags.flg4 + '0', fp); + mux_putc(flags.flg5 + '0', fp); + mux_putc(flags.flg6 + '0', fp); + mux_putc(flags.flg7 + '0', fp); + mux_putc(flags.flg8 + '0', fp); +} + +extern void +prot_a_output_conf_type(Connection *fp, + Conf_type type) +{ + mux_putc(' ', fp); + mux_putc(type.rd_prot + '0', fp); + mux_putc(type.original + '0', fp); + mux_putc(type.secret + '0', fp); + mux_putc(type.letter_box + '0', fp); +} + + +extern void +prot_a_output_member_list(Connection *fp, + Member_list m_list) +{ + int i; + + mux_printf(fp, " %lu", (u_long)m_list.no_of_members); + if ( m_list.members != NULL && m_list.no_of_members > 0 ) + { + mux_printf(fp, " {"); + for ( i = 0; i < m_list.no_of_members; i++ ) + prot_a_output_member(fp, m_list.members[ i ]); + mux_printf(fp, " }"); + } + else + mux_printf(fp, " *"); +} + + +void +prot_a_output_member(Connection *fp, + Member member) +{ + mux_printf(fp, " %lu", (u_long)member.member); +} + + +extern void +prot_a_output_mark(Connection *fp, + Mark mark) +{ + mux_printf(fp, " %lu %lu", (u_long)mark.text_no, (u_long)mark.mark_type); +} + + +extern void +prot_a_output_misc_info(Connection *fp, + Misc_info misc) +{ + mux_printf(fp, " %lu", (u_long)misc.type); + + switch(misc.type) + { + case recpt: + mux_printf(fp, " %lu", (u_long)misc.datum.recipient); + break; + + case cc_recpt: + mux_printf(fp, " %lu", (u_long)misc.datum.cc_recipient); + break; + + case loc_no: + mux_printf(fp, " %lu", (u_long)misc.datum.local_no); + break; + + case rec_time: + prot_a_output_time(fp, misc.datum.received_at); + break; + + case comm_to: + mux_printf(fp, " %lu", (u_long)misc.datum.comment_to); + break; + + case comm_in: + mux_printf(fp, " %lu", (u_long)misc.datum.commented_in); + break; + + case footn_to: + mux_printf(fp, " %lu", (u_long)misc.datum.footnote_to); + break; + + case footn_in: + mux_printf(fp, " %lu", (u_long)misc.datum.footnoted_in); + break; + + case sent_by: + mux_printf(fp, " %lu", (u_long)misc.datum.sender); + break; + + case sent_at: + prot_a_output_time(fp, misc.datum.sent_at); + break; + +#ifndef COMPILE_CHECKS + default: + restart_kom("prot_a_output_misc_info: Illegal misc\n"); +#endif + } +} + + +void +prot_a_output_time(Connection *fp, + time_t clock) +{ + struct tm *time; + + time = localtime( &clock ); + + mux_printf(fp, " %lu %lu %lu %lu %lu %lu %lu %lu %lu", + (u_long) time -> tm_sec, + (u_long) time -> tm_min, + (u_long) time -> tm_hour, + (u_long) time -> tm_mday, + (u_long) time -> tm_mon, + (u_long) time -> tm_year, + (u_long) time -> tm_wday, + (u_long) time -> tm_yday, + (u_long) time -> tm_isdst); +} + + +void +prot_a_output_session_no(Connection *fp, + Session_no session_no) +{ + mux_printf(fp, " %lu", (u_long) session_no); +} diff --git a/src/server/prot-a-output.h b/src/server/prot-a-output.h new file mode 100644 index 0000000000000000000000000000000000000000..bde9cd429803a060fdf7edc3beb92a3698499483 --- /dev/null +++ b/src/server/prot-a-output.h @@ -0,0 +1,99 @@ + +extern void +prot_a_output_person(Connection *fp, + Person *person); + + +extern void +prot_a_output_membership(Connection *fp, + const Membership *mship); + +extern void +prot_a_output_membership_list(Connection *fp, + Membership_list mlist); + +extern void +prot_a_output_conf_list(Connection *fp, + Conf_list_old conf_list); + +extern void +prot_a_output_conf_no_list(Connection *fp, + Conf_no_list conf_no_list); + +extern void +prot_a_output_conference(Connection *fp, + Conference *conf_c); + +extern void +prot_a_output_mark_list(Connection *fp, + Mark_list mark_list); + +extern void +prot_a_output_text_stat(Connection *fp, + Text_stat *t_stat); + +extern void +prot_a_output_text_list(Connection *fp, + Text_list text_list); + +extern void +prot_a_output_info(Connection *fp, + Info *info); + +extern void +prot_a_output_who_info(Connection *fp, + Who_info *info); + +extern void +prot_a_output_who_info_list(Connection *fp, + Who_info_list info); + +extern void +prot_a_output_who_info_list_old(Connection *fp, + Who_info_list_old info); + +void +prot_a_output_session_info(Connection *fp, + Session_info *info); + +extern void +prot_a_output_string(Connection *fp, + String str); + +extern void +prot_a_output_priv_bits(Connection *fp, + Priv_bits bits); + +extern void +prot_a_output_personal_flags(Connection *fp, + Personal_flags flags); + + +extern void +prot_a_output_conf_type(Connection *fp, + Conf_type type); + +extern void +prot_a_output_member_list(Connection *fp, + Member_list m_list); + +extern void +prot_a_output_mark(Connection *fp, + Mark mark); + +extern void +prot_a_output_misc_info(Connection *fp, + Misc_info misc); + +extern void +prot_a_output_member(Connection *fp, + Member member); + +void +prot_a_output_time(Connection *fp, + time_t clock); + +void +prot_a_output_session_no(Connection *fp, + Session_no session_no); + diff --git a/src/server/prot-a-parse.c b/src/server/prot-a-parse.c new file mode 100644 index 0000000000000000000000000000000000000000..34a0f45063c2b2e94fffd9260a8c75b8a6ef4f86 --- /dev/null +++ b/src/server/prot-a-parse.c @@ -0,0 +1,249 @@ +/* + * prot-a-parse.c - parse protocol-A messages. + * + * BUG: Not all functions are used, I think. /ceder + */ + +#include <setjmp.h> +#include <string.h> + +#include <kom-types.h> +#include "s-string.h" +#include "lyskomd.h" +#include "com.h" +#include "connections.h" +#include "prot-a-parse.h" +#include "isc-parse.h" +#include "smalloc.h" +#include "minmax.h" +#include "prot-a.h" +#include "config.h" + +long +prot_a_parse_long(Connection *client) +{ + String token; + String_size end; + long res; + + token = prot_a_get_token(client); + res = s_strtol(token, &end, PROTOCOL_NUMBER_BASE); + if (end != s_strlen(token)) + longjmp(parse_env, ISC_PROTOCOL_ERR); + + return res; +} + +void +prot_a_parse_priv_bits(Connection *client, + Priv_bits *res) +{ + String token; + + token = prot_a_get_token(client); + + if ( s_strlen(token) != 16 ) + longjmp(parse_env, ISC_PROTOCOL_ERR); + + res->wheel = token.string[ 0 ] != '0'; + res->admin = token.string[ 1 ] != '0'; + res->statistic = token.string[ 2 ] != '0'; + res->create_pers = token.string[ 3 ] != '0'; + res->create_conf = token.string[ 4 ] != '0'; + res->change_name = token.string[ 5 ] != '0'; + res->flg7 = token.string[ 6 ] != '0'; + res->flg8 = token.string[ 7 ] != '0'; + res->flg9 = token.string[ 8 ] != '0'; + res->flg10 = token.string[ 9 ] != '0'; + res->flg11 = token.string[ 10 ] != '0'; + res->flg12 = token.string[ 11 ] != '0'; + res->flg13 = token.string[ 12 ] != '0'; + res->flg14 = token.string[ 13 ] != '0'; + res->flg15 = token.string[ 14 ] != '0'; + res->flg16 = token.string[ 15 ] != '0'; +} + +void +prot_a_parse_conf_type(Connection *client, + Conf_type *res) +{ + String token; + + token = prot_a_get_token(client); + + if ( s_strlen(token) != 4 ) + longjmp(parse_env, ISC_PROTOCOL_ERR); + + res->rd_prot = token.string[ 0 ] != '0'; + res->original = token.string[ 1 ] != '0'; + res->secret = token.string[ 2 ] != '0'; + res->letter_box = token.string[ 3 ] != '0'; +} + +/* + * Parse a string. At most 'maxlen' characters are allowed. If the + * client sends a longer string only the first 'maxlen+1' characters + * are read. The following characters are discarded. + */ +void +prot_a_parse_string(Connection *client, + String *result, + int maxlen) +{ + String_size hptr; /* Pointer to 'H' */ + String_size client_len; /* The len the client is sending. */ + String_size truncated_len; /* How much the server will receive. */ + String_size to_skip; + String tmp; + + switch ( client->string_parse_pos ) + { + case 0: + /* Get number and discard trailing 'H' */ + result->len = s_strtol(s_fsubstr(client->unparsed, + client->first_to_parse, + END_OF_STRING), + &hptr, PROTOCOL_NUMBER_BASE); + + if ( hptr == -1 + || client->first_to_parse + hptr + >= s_strlen(client->unparsed) ) + { + longjmp(parse_env, ISC_MSG_INCOMPLETE); + } + + /* Check that + a) there is a trailing H + b) there was at least one digit before the H */ + + if ( client->unparsed.string[ client->first_to_parse + + hptr ] != 'H' + || hptr <= 0 ) + { + longjmp(parse_env, ISC_PROTOCOL_ERR); + } + + client->first_to_parse += 1 + hptr; + client->string_parse_pos = 1; + /* Fall through */ + case 1: + /* Check that the entire string is transmitted. */ + /* (Don't care about the trailing part that will be skipped if the + * string is longer than maxlen) */ + truncated_len = min(maxlen + 1, result->len); + client_len = result->len; + + if ( client->first_to_parse + truncated_len + > s_strlen(client->unparsed) ) + { + longjmp(parse_env, ISC_MSG_INCOMPLETE); + } + + result->string = client->unparsed.string + client->first_to_parse; + result->len = truncated_len; + tmp = EMPTY_STRING; + s_strcpy(&tmp, *result); /* Copy the string. */ + *result = tmp; + result->len = client_len; + + client->first_to_parse += truncated_len; + client->string_parse_pos = 2; + /* Fall through */ + case 2: + /* Was the string too long? If so, skip the truncated data. */ + + client_len = result->len; + truncated_len = min(maxlen+1, result->len); + + if ( client_len > truncated_len ) + { + to_skip = min(client_len - truncated_len, + client->unparsed.len - client->first_to_parse); + client_len -= to_skip; + client->first_to_parse += to_skip; + } + + result->len = client_len; + + if ( client_len > truncated_len ) + longjmp(parse_env, ISC_MSG_INCOMPLETE); + /* Fall through */ + default: + client->string_parse_pos = 0; + } +} + + +extern void +prot_a_parse_misc_info(Connection *client, + Misc_info *result) +{ + switch ( client->struct_parse_pos ) + { + case 0: + result->type = prot_a_parse_long(client); + client->struct_parse_pos = 1; + /* Fall through */ + case 1: + switch( result->type ) + { + case recpt: + result->datum.recipient = prot_a_parse_long(client); + break; + + case cc_recpt: + result->datum.cc_recipient = prot_a_parse_long(client); + break; + + case loc_no: + result->datum.local_no = prot_a_parse_long(client); + break; + + case comm_to: + result->datum.comment_to = prot_a_parse_long(client); + break; + + case footn_to: + result->datum.footnote_to = prot_a_parse_long(client); + break; + + case comm_in: + case footn_in: + case rec_time: + case sent_by: + case sent_at: + /* Fall through */ + +#ifndef COMPILE_CHECKS + default: +#endif + longjmp(parse_env, ISC_PROTOCOL_ERR); + } + default: + client->struct_parse_pos = 0; + } +} + + +String +prot_a_get_token(Connection *client) +{ + String result; + String_size old_first; + + old_first = client->first_to_parse; + + result = s_strtok(client->unparsed, &client->first_to_parse, + s_fcrea_str(" \t\n\r")); + + /* Check that there was at least one trailing blank. */ + + if ( client->first_to_parse >= s_strlen(client->unparsed) ) + { + client->first_to_parse = old_first; + longjmp(parse_env, ISC_MSG_INCOMPLETE); + } + + return result; +} + diff --git a/src/server/prot-a-parse.h b/src/server/prot-a-parse.h new file mode 100644 index 0000000000000000000000000000000000000000..888ac02c128808c2fb5a370c4f93eea44dcbe1f7 --- /dev/null +++ b/src/server/prot-a-parse.h @@ -0,0 +1,28 @@ +extern long +prot_a_parse_long(Connection *client); + +extern void +prot_a_parse_priv_bits(Connection *client, + Priv_bits *result); + +extern void +prot_a_parse_conf_type(Connection *client, + Conf_type *result); + + +extern void +prot_a_parse_string(Connection *client, + String *result, + int maxlen); + +extern void +prot_a_parse_misc_info(Connection *client, + Misc_info *result); + +/* + * Return next token from the input stream. Note that the String returned + * by this call points into data that might be freed by the next call to + * get_token or any function which reads from the stream. + */ +extern String +prot_a_get_token(Connection *client); diff --git a/src/server/prot-a-send-async.c b/src/server/prot-a-send-async.c new file mode 100644 index 0000000000000000000000000000000000000000..d7490fc0b4a824b4d2e3900fba30f7d9898cd7e0 --- /dev/null +++ b/src/server/prot-a-send-async.c @@ -0,0 +1,148 @@ +/* + * Asynchronous messages in protocol A. + */ + +#include <stdio.h> +#include "lyskomd.h" +#include <kom-types.h> +#include <services.h> +#include "async.h" +#include "com.h" +#include "connections.h" +#include "prot-a-send-async.h" +#include "prot-a-output.h" +#include "manipulate.h" +#include "log.h" +#include "isc.h" +#include "mux.h" + +static void +async_header(Connection *fp, + int no_of_tokens, + Async fnc) +{ + mux_printf(fp, ":%lu %lu", (u_long)no_of_tokens, (u_long)fnc); +} + + + + +static void +async_trailer(Connection *fp) +{ + mux_putc('\n', fp); + mux_flush(fp); +} + + +void +prot_a_async_new_text(Connection *cptr, + Text_no text_no, + Text_stat *text_s) +{ + async_header(cptr, 16, ay_new_text); + mux_printf(cptr, " %lu", text_no); + prot_a_output_text_stat(cptr, text_s); + async_trailer(cptr); +} + + +void +prot_a_async_i_am_on(Connection *cptr, + Who_info info) +{ + async_header(cptr, 5, ay_i_am_on); + prot_a_output_who_info(cptr, &info); + async_trailer(cptr); +} + +void +prot_a_async_i_am_off(Connection *cptr, + Pers_no pers_no) +{ + async_header(cptr, 1, ay_i_am_off); + mux_printf(cptr, " %lu", pers_no); + async_trailer(cptr); +} + + +void +prot_a_async_logout(Connection *cptr, + Pers_no pers_no, + Session_no session_no) +{ + async_header(cptr, 2, ay_logout); + mux_printf(cptr, " %lu %lu", (u_long)pers_no, (u_long)session_no); + async_trailer(cptr); +} + +void +prot_a_async_new_name(Connection *cptr, + Conf_no conf_no, + String old_name, + String new_name) +{ + async_header(cptr, 3, ay_new_name); + mux_printf(cptr, " %lu", (u_long)conf_no); + prot_a_output_string(cptr, old_name); + prot_a_output_string(cptr, new_name); + async_trailer(cptr); +} + +void +prot_a_async_sync_db(Connection *cptr) +{ + async_header(cptr, 0, ay_sync_db); + async_trailer(cptr); +} + +void +prot_a_async_forced_leave_conf(Connection *cptr, + Conf_no conf_no) +{ + async_header(cptr, 1, ay_leave_conf); + mux_printf(cptr, " %lu", (u_long)conf_no); + async_trailer(cptr); +} + +void +prot_a_async_login(Connection *cptr, + Pers_no pers_no, + int session_no) +{ + async_header(cptr, 2, ay_login); + mux_printf(cptr, " %lu %lu", (u_long)pers_no, (u_long)session_no); + async_trailer(cptr); +} + + + +void +prot_a_async_broadcast(Connection *cptr, + Pers_no pers_no, + String message) +{ + async_header(cptr, 2, ay_broadcast); + mux_printf(cptr, " %lu", (u_long)pers_no); + prot_a_output_string(cptr, message); + async_trailer(cptr); +} + +void +prot_a_async_rejected_connection(Connection *cptr) +{ + async_header(cptr, 0, ay_rejected_connection); + async_trailer(cptr); +} + +void +prot_a_async_send_message(Connection *cptr, + Pers_no recipient, + Pers_no sender, + String message) +{ + async_header(cptr, 3, ay_send_message); + mux_printf(cptr, " %lu %lu", (u_long)recipient, (u_long)sender); + prot_a_output_string(cptr, message); + async_trailer(cptr); +} diff --git a/src/server/prot-a-send-async.h b/src/server/prot-a-send-async.h new file mode 100644 index 0000000000000000000000000000000000000000..7ad5a91a1903a122c1acc7d486c244dde188de4f --- /dev/null +++ b/src/server/prot-a-send-async.h @@ -0,0 +1,52 @@ +/* + * Asynchronous messages in protocol A. + */ + +void +prot_a_async_new_text(Connection *cptr, + Text_no text_no, + Text_stat *text_s); + +void +prot_a_async_i_am_on(Connection *cptr, + Who_info info); +void +prot_a_async_i_am_off(Connection *cptr, + Pers_no pers_no); +void +prot_a_async_logout(Connection *cptr, + Pers_no pers_no, + Session_no session_no); + +void +prot_a_async_new_name(Connection *cptr, + Conf_no conf_no, + String old_name, + String new_name); + +void +prot_a_async_sync_db(Connection *cptr); + +void +prot_a_async_forced_leave_conf(Connection *cptr, + Conf_no conf_no); + +void +prot_a_async_login(Connection *cptr, + Pers_no pers_no, + int session_no); + + +void +prot_a_async_broadcast(Connection *cptr, + Pers_no pers_no, + String message); + +void +prot_a_async_rejected_connection(Connection *cptr); + +void +prot_a_async_send_message(Connection *cptr, + Pers_no recipient, + Pers_no sender, + String message); diff --git a/src/server/prot-a.c b/src/server/prot-a.c new file mode 100644 index 0000000000000000000000000000000000000000..c0ad2848dda58952ebaf85b3f3ee24b093551a31 --- /dev/null +++ b/src/server/prot-a.c @@ -0,0 +1,317 @@ +/* + * Protocol A. + */ + +#include <stdio.h> +#include <setjmp.h> + +#include <kom-errno.h> +#include <kom-types.h> +#include "smalloc.h" +#include "lyskomd.h" +#include "com.h" +#include "connections.h" +#include "isc-parse.h" +#include "prot-a.h" +#include "prot-a-output.h" +#include "prot-a-parse.h" +#include <debug.h> +#include "isc.h" +#include "mux.h" + +BUGDECL; + +void +prot_a_reply(Connection *client, + Bool status, + Result_holder *res) +{ + /* + * The function is called. Now return the answer. + */ + + if ( status == OK ) + { + mux_printf(client, "=%d", client->ref_no); + + switch ( fnc_defs[ client->function ].result ) + { + case number: + mux_printf(client, " %ld", (u_long)res->number); + BUG(("=%d\n", res->number)); + break; + + case success: + BUG(("=Success\n")); + break; + + case person: + prot_a_output_person(client, &res->person); + BUG(("={Person struct not listed}\n")); + break; + + case membership: + prot_a_output_membership(client, &res->membership); + BUG(("={Membership struct not listed}\n")); + break; + + case conf_list: + prot_a_output_conf_list(client, res->conf_list); + BUG(("={Conf_list not listed}\n")); + break; + + case conf_no_list: + prot_a_output_conf_no_list(client, res->conf_no_list); + BUG(("={Conf_no_list not listed}\n")); + break; + + case conference: + prot_a_output_conference(client, &res->conference); + BUG(("={Conference struct not listed}\n")); + break; + + case string: + prot_a_output_string(client, res->string); + BUG(("={%dH", res->string.len)); + BUGSTR(res->string); + BUG(("}\n")); + break; + + case mark_list: + prot_a_output_mark_list(client, res->mark_list); + BUG(("={Mark_list not listed}\n")); + break; + + case text_stat: + prot_a_output_text_stat(client, &res->text_stat); + BUG(("={Text_stat struct not listed}\n")); + break; + + case text_list: + prot_a_output_text_list(client, res->text_list); + BUG(("={Text_list not listed}\n")); + break; + + case who_info_list: + prot_a_output_who_info_list(client, res->who_info_list); + BUG(("={Who_info_list not listed}\n")); + break; + + case who_info_list_old: + prot_a_output_who_info_list_old(client, + res->who_info_list_old); + BUG(("={Old who_info_list not listed}\n")); + break; + + case session_info: + prot_a_output_session_info(client, + &res->session_info); + BUG(("={Session_info not listed}\n")); + break; + + case info: + prot_a_output_info(client, &res->info); + BUG(("={Who_info struct not listed}\n")); + break; + + case membership_list: + prot_a_output_membership_list (client, res->membership_list); + BUG (("={Membership_list not listed}\n")); + break; + + case member_list: + prot_a_output_member_list (client, res->member_list); + BUG(("={Member_list not listed}\n")); + break; + + case time_date: + prot_a_output_time (client, res->time_date); + BUG(("=(time_t)%d\n", res->time_date)); + break; + + case session_no: + prot_a_output_session_no (client, res->session_no); + BUG(("=(Session_no)%d\n", res->session_no)); + break; + } + mux_putc('\n', client); + } + else + { + /* Failure. Give a reply with the error message. */ + mux_printf(client, "%%%d %d %d\n", + client->ref_no, kom_errno, err_stat); + BUG(("%%Err %d\n", kom_errno)); + } + + mux_flush(client); +} + +/* + * Check if it is a legal function. + * + * BUG: This should be generated from fncdef.txt. + */ + + +/* + * Set up all data structures that are private to protocol A. This + * function is called from connections.c whenever a connection says + * that it is of type A. + */ +void +prot_a_init(Connection *conn) +{ + conn->parse_pos = 0; + conn->fnc_parse_pos = 0; + conn->array_parse_pos = 0; + conn->struct_parse_pos = 0; + conn->string_parse_pos = 0; + conn->ref_no = 0; + conn->function = 0; + conn->num0 = 0; + conn->num1 = 0; + conn->num2 = 0; + conn->num3 = 0; + conn->c_string0 = EMPTY_STRING; + conn->c_string1 = EMPTY_STRING; + conn->string0 = EMPTY_STRING; + conn->c_misc_info_p = NULL; + conn->c_local_text_no_p = NULL; + conn->priv_bits = DEFAULT_PRIV_BITS; + conn->conf_type = NULL_CONF_TYPE; +} + +void +prot_a_destruct(Connection *conn) +{ + + /* + * All strings et c should already by free:d - that is done at the + * end of each atomic call. But free them anyhow, just in case + * this client is forced of. + */ + + s_clear(&conn->string0); + s_clear(&conn->c_string0); + s_clear(&conn->c_string1); + sfree(conn->c_misc_info_p); + sfree(conn->c_local_text_no_p); +} + + +static Bool +prot_a_is_legal_fnc(Call_header fnc) +{ + switch(fnc) + { + case call_fnc_login: + case call_fnc_logout: + case call_fnc_pepsi: + case call_fnc_change_name: + case call_fnc_change_what_i_am_doing: + case call_fnc_create_person: + case call_fnc_get_person_stat_old: + case call_fnc_set_priv_bits: + case call_fnc_set_passwd: + case call_fnc_query_read_texts: + case call_fnc_create_conf: + case call_fnc_delete_conf: + case call_fnc_lookup_name: + case call_fnc_get_conf_stat_old: + case call_fnc_add_member: + case call_fnc_sub_member: + case call_fnc_set_presentation: + case call_fnc_set_etc_motd: + case call_fnc_set_supervisor: + case call_fnc_set_permitted_submitters: + case call_fnc_set_super_conf: + case call_fnc_set_conf_type: + case call_fnc_set_garb_nice: + case call_fnc_get_marks: + case call_fnc_mark_text: + case call_fnc_get_text: + case call_fnc_get_text_stat: + case call_fnc_mark_as_read: + case call_fnc_create_text: + case call_fnc_delete_text: + case call_fnc_add_recipient: + case call_fnc_sub_recipient: + case call_fnc_add_comment: + case call_fnc_sub_comment: + case call_fnc_get_map: + case call_fnc_get_time: + case call_fnc_get_info: + case call_fnc_add_footnote: + case call_fnc_sub_footnote: + case call_fnc_who_is_on_old: + case call_fnc_set_unread: + case call_fnc_set_motd_of_lyskom: + case call_fnc_enable: + case call_fnc_sync: + case call_fnc_shutdown: + case call_fnc_broadcast: + case call_fnc_get_membership: + case call_fnc_get_created_texts: + case call_fnc_get_members: + case call_fnc_get_person_stat: + case call_fnc_get_conf_stat: + case call_fnc_who_is_on: + case call_fnc_get_unread_confs: + case call_fnc_send_message: + case call_fnc_get_session_info: + case call_fnc_disconnect: + case call_fnc_who_am_i: + return TRUE; + + default: + return FALSE; + } +} + + +void +prot_a_parse_packet(Connection *client) +{ + if ( s_empty(client->username) ) + { + /* Connection not established yet */ + + prot_a_parse_string(client, &client->c_string0, USERNAME_LEN); + + client->username = client->c_string0; /* Kludge to deal with */ + client->c_string0 = EMPTY_STRING; /* "A5B" as first input. */ + /* Protokoll B will not suffer from this... */ + + if ( s_strcat(&client->username, s_fcrea_str("@")) != OK ) + restart_kom("prot_a_parse_packet: s_strcat\n"); + + if ( s_strcat(&client->username, client->hostname) != OK ) + restart_kom("prot_a_parse_packet: s_strcat II\n"); + + mux_printf(client, "LysKOM\n"); + mux_flush(client); + BUG(("[Client %d is logged on]\n", client->session_no)); + } + + switch(client->parse_pos) + { + case 0: /* Get ref_no */ + client->ref_no = prot_a_parse_long(client); + client->parse_pos = 1; + /* Fall through */ + case 1: /* Get fnc_no */ + client->function = prot_a_parse_long(client); + if ( !prot_a_is_legal_fnc(client->function) ) + longjmp(parse_env, ISC_PROTOCOL_ERR); + client->parse_pos = 2; + /* Fall through */ + case 2: + /* Call the function that parses the arguments for this call. */ + fnc_defs[client->function].parser(client); + /* Fall through */ + default: + client->parse_pos = 0; + } + +} diff --git a/src/server/prot-a.h b/src/server/prot-a.h new file mode 100644 index 0000000000000000000000000000000000000000..d9b3dd5d709df632f61a783eca9cba5c38e438a3 --- /dev/null +++ b/src/server/prot-a.h @@ -0,0 +1,14 @@ + +void +prot_a_init(Connection *conn); + +void +prot_a_destruct(Connection *conn); + +void +prot_a_reply(Connection *client, + Bool status, + Result_holder *result); + +void +prot_a_parse_packet(Connection *client); diff --git a/src/server/ram-output.c b/src/server/ram-output.c new file mode 100644 index 0000000000000000000000000000000000000000..bc409fca6d31b01af02b24e5aa4c1a4c6dc4aa56 --- /dev/null +++ b/src/server/ram-output.c @@ -0,0 +1,383 @@ +/* + * ram-output.c - write objects to disk. + * + * This is a hack. It shouldn't be used except for debugging and as a + * temporary substitute for what Willf|r is (or should:-) be doing. + * + * Written by ceder 1990-07-13. Rewritten 1990-08-31. + * Some functions rewritten for speed by Inge Wallin. + * (It worked - now saving is twice as fast.) + */ + +#include <kom-types.h> +#include <stdio.h> +#include <sys/file.h> +#include <time.h> +#include "lyskomd.h" +#include "ram-output.h" +#include "com.h" +#include "isc.h" +#include "connections.h" +#include "prot-a-output.h" +#include "log.h" + +void +foutput_person (FILE *fp, + Person *person) +{ + foutput_string (fp, person->username); + foutput_priv_bits (fp, person->privileges); + foutput_personal_flags (fp, person->flags); + + foutput_text_list (fp, person->created_texts); + foutput_mark_list (fp, person->marks); + foutput_membership_list (fp, person->conferences); + + foutput_time(fp, person->last_login); + + fprintf (fp, " %lu %lu %lu %lu %lu %lu %lu %lu %lu", + (u_long) person -> user_area, + (u_long) person -> total_time_present, /* This is not a time, + * but a number of seconds. + */ + (u_long) person -> sessions, + (u_long) person -> created_lines, + (u_long) person -> created_bytes, + (u_long) person -> read_texts, + (u_long) person -> no_of_text_fetches, + (u_long) person -> created_persons, + (u_long) person -> created_confs); +} + + + +void +foutput_conference (FILE *fp, + Conference *conf_c) +{ + foutput_string(fp, conf_c->name); + foutput_member_list(fp, conf_c->members); + foutput_text_list(fp, conf_c->texts); + foutput_conf_type(fp, conf_c->type); + + foutput_time(fp, conf_c -> creation_time ); + foutput_time(fp, conf_c -> last_written ); + + fprintf (fp, " %lu %lu %lu %lu %lu %lu %lu", + (u_long) conf_c -> creator, + (u_long) conf_c -> presentation, + (u_long) conf_c -> supervisor, + (u_long) conf_c -> permitted_submitters, + (u_long) conf_c -> super_conf, + (u_long) conf_c -> msg_of_day, + (u_long) conf_c -> nice); +} + + +void +foutput_text_stat(FILE *fp, + Text_stat *t_stat) +{ + int i; + + foutput_time(fp, t_stat->creation_time); + + foutput_ulong((u_long) t_stat->author, fp); + foutput_ulong((u_long) t_stat->file_pos, fp); + foutput_ulong((u_long) t_stat->no_of_lines, fp); + foutput_ulong((u_long) t_stat->no_of_chars, fp); + foutput_ulong((u_long) t_stat->no_of_marks, fp); + foutput_ulong((u_long) t_stat->no_of_misc, fp); + + if ( t_stat->misc_items != NULL && t_stat->no_of_misc > 0 ) + { + fputs(" {", fp); + for ( i = 0; i < t_stat->no_of_misc; i++ ) + foutput_misc_info(fp, t_stat->misc_items[ i ]); + fputs(" }", fp); + } + else + fputs(" *", fp); +} + + +extern void +foutput_string(FILE *fp, + String str) +{ + foutput_ulong((u_long)str.len, fp); + putc('H', fp); + + if (str.len) + fwrite(str.string, str.len, 1, fp); +} + +extern void +foutput_priv_bits(FILE *fp, + Priv_bits bits) +{ + putc(' ', fp); + putc(bits.wheel + '0', fp); + putc(bits.admin + '0', fp); + putc(bits.statistic + '0', fp); + putc(bits.create_pers + '0', fp); + putc(bits.create_conf + '0', fp); + putc(bits.change_name + '0', fp); + putc(bits.flg7 + '0', fp); + putc(bits.flg8 + '0', fp); + putc(bits.flg9 + '0', fp); + putc(bits.flg10 + '0', fp); + putc(bits.flg11 + '0', fp); + putc(bits.flg12 + '0', fp); + putc(bits.flg13 + '0', fp); + putc(bits.flg14 + '0', fp); + putc(bits.flg15 + '0', fp); + putc(bits.flg16 + '0', fp); +} + +extern void +foutput_personal_flags(FILE *fp, + Personal_flags flags) +{ + putc(' ', fp); + putc(flags.unread_is_secret + '0', fp); + putc(flags.flg2 + '0', fp); + putc(flags.flg3 + '0', fp); + putc(flags.flg4 + '0', fp); + putc(flags.flg5 + '0', fp); + putc(flags.flg6 + '0', fp); + putc(flags.flg7 + '0', fp); + putc(flags.flg8 + '0', fp); +} + + +void +foutput_text_list(FILE *fp, + Text_list text_list) +{ + int i; + + foutput_ulong((u_long)text_list.first_local_no, fp); + foutput_ulong((u_long)text_list.no_of_texts, fp); + + if ( text_list.texts != NULL && text_list.no_of_texts > 0 ) + { + fputs(" {", fp); + for ( i = 0; i < text_list.no_of_texts; i++ ) + foutput_ulong((u_long)text_list.texts[ i ], fp); + fputs(" }", fp); + } + else + fprintf(fp, " *"); +} + + +void +foutput_mark_list(FILE *fp, + Mark_list mark_list) +{ + int i; + + fprintf(fp, " %lu", (u_long)mark_list.no_of_marks); + + if ( mark_list.marks != NULL && mark_list.no_of_marks > 0 ) + { + fprintf(fp, " {"); + for ( i = 0; i < mark_list.no_of_marks; i++ ) + foutput_mark(fp, mark_list.marks[ i ]); + fprintf(fp, " }"); + } + else + fprintf(fp, " *"); + +} + + +extern void +foutput_mark(FILE *fp, + Mark mark) +{ + fprintf(fp, " %lu %lu", (u_long)mark.text_no, (u_long)mark.mark_type); +} + +void +foutput_membership_list (FILE * fp, + Membership_list mlist) +{ + int i; + + fprintf(fp, " %lu", (u_long)mlist.no_of_confs); + + if ( mlist.confs != NULL && mlist.no_of_confs > 0 ) + { + fprintf(fp, " {"); + for ( i = 0; i < mlist.no_of_confs; i++) + foutput_membership(fp, mlist.confs + i); + fprintf(fp, " }"); + } + else + fprintf(fp, " *"); +} + + + +void +foutput_membership(FILE *fp, + Membership *mship) +{ + int i; + + foutput_time(fp, mship->last_time_read ); + + if ( mship->read_texts == NULL && mship->no_of_read != 0 ) + { + log("foutput_membership(): no_of_read forced to 0" + " in someones membership in %lu.\n", + mship->conf_no); + mship->no_of_read = 0; + } + + fprintf(fp, " %lu %lu %lu %lu", + (u_long)mship->conf_no, + (u_long)mship->priority, + (u_long)mship->last_text_read, + (u_long)mship->no_of_read); + + if ( mship->read_texts != NULL && mship->no_of_read > 0) + { + fprintf(fp, " {"); + for ( i = 0; i < mship->no_of_read; i++) + fprintf(fp, " %lu", (u_long)mship->read_texts[ i ]); + + fprintf(fp, " }"); + } + else + fprintf(fp, " *"); +} + +void +foutput_time(FILE *fp, + time_t clock) +{ + foutput_ulong((u_long) clock, fp); +} + + +extern void +foutput_member_list(FILE *fp, + Member_list m_list) +{ + int i; + + fprintf(fp, " %lu", (u_long)m_list.no_of_members); + if ( m_list.members != NULL && m_list.no_of_members > 0 ) + { + fprintf(fp, " {"); + for ( i = 0; i < m_list.no_of_members; i++ ) + foutput_member(fp, m_list.members[ i ]); + fprintf(fp, " }"); + } + else + fprintf(fp, " *"); +} + + +void +foutput_member(FILE *fp, + Member member) +{ + fprintf(fp, " %lu", (u_long)member.member); +} + +extern void +foutput_conf_type(FILE *fp, + Conf_type type) +{ + putc(' ', fp); + putc(type.rd_prot + '0', fp); + putc(type.original + '0', fp); + putc(type.secret + '0', fp); + putc(type.letter_box + '0', fp); +} + + +extern void +foutput_misc_info(FILE *fp, + Misc_info misc) +{ + foutput_ulong((u_long)misc.type, fp); + + switch(misc.type) + { + case recpt: + foutput_ulong((u_long)misc.datum.recipient, fp); + break; + + case cc_recpt: + foutput_ulong((u_long)misc.datum.cc_recipient, fp); + break; + + case loc_no: + foutput_ulong((u_long)misc.datum.local_no, fp); + break; + + case rec_time: + foutput_time(fp, misc.datum.received_at); + break; + + case comm_to: + foutput_ulong((u_long)misc.datum.comment_to, fp); + break; + + case comm_in: + foutput_ulong((u_long)misc.datum.commented_in, fp); + break; + + case footn_to: + foutput_ulong((u_long)misc.datum.footnote_to, fp); + break; + + case footn_in: + foutput_ulong((u_long)misc.datum.footnoted_in, fp); + break; + + case sent_by: + foutput_ulong((u_long)misc.datum.sender, fp); + break; + + case sent_at: + foutput_time(fp, misc.datum.sent_at); + break; + +#ifndef COMPILE_CHECKS + default: + restart_kom("prot_a_output_misc_info: Illegal misc\n"); +#endif + } +} + + +/* + * Output the unsigned long L in the fastest way possible to the file + * FP. Ok, it's ugly, but it's fast (or is it?). + */ + +void +foutput_ulong (u_long l, + FILE *fp) +{ + static char buf[20]; + char *cp; + + putc(' ', fp); + if (l < 10) + putc("0123456789"[l], fp); + else { + cp = buf + 19; + while (l > 0) { + *cp-- = (l % 10) + '0'; + l /= 10; + } + fwrite(cp + 1, buf + 19 - cp, 1, fp); + } +} diff --git a/src/server/ram-output.h b/src/server/ram-output.h new file mode 100644 index 0000000000000000000000000000000000000000..994f104243b85204a21891779e0b890b874c2e38 --- /dev/null +++ b/src/server/ram-output.h @@ -0,0 +1,72 @@ +/* + * ram-output.c - write objects to disk. + * + * This is a hack. It shouldn't be used except for debugging and as a + * temporary substitute for what Willf|r is (or should:-) be doing. + * + * Written by ceder 1990-07-13. Rewritten 1990-08-31. + */ + +void +foutput_person (FILE *fp, + Person *person); + + +void +foutput_conference (FILE *fp, + Conference *conf_c); + +void +foutput_text_stat(FILE *fp, + Text_stat *t_stat); + +extern void +foutput_string(FILE *fp, + String str); +extern void +foutput_priv_bits(FILE *fp, + Priv_bits bits); +extern void +foutput_personal_flags(FILE *fp, + Personal_flags flags); + +void +foutput_text_list(FILE *fp, + Text_list text_list); + +void +foutput_mark_list(FILE *fp, + Mark_list mark_list); + +extern void +foutput_mark(FILE *fp, + Mark mark); +void +foutput_membership_list (FILE * fp, + Membership_list mlist); + +void +foutput_membership(FILE *fp, + Membership *mship); +void +foutput_time(FILE *fp, + time_t clock); + +extern void +foutput_member_list(FILE *fp, + Member_list m_list); + +void +foutput_member(FILE *fp, + Member member); +extern void +foutput_conf_type(FILE *fp, + Conf_type type); + +extern void +foutput_misc_info(FILE *fp, + Misc_info misc); + +extern void +foutput_ulong (u_long l, + FILE *fp); diff --git a/src/server/ram-parse.c b/src/server/ram-parse.c new file mode 100644 index 0000000000000000000000000000000000000000..f1e47b29a0ae3b65b99d29ce909e7a2ed69e193c --- /dev/null +++ b/src/server/ram-parse.c @@ -0,0 +1,1173 @@ +#define DISKERR /* Some corrections + that are needed by diskomd 0.29. + */ +/* + * ram-parse.c -- parse objects from disk file. + */ + +/* + * BUGS: Not all functions are needed. + * The method for checking errors in fparse_long is ugly. + * Errors in fparse_long are not always checked for. + */ +#include <stdio.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/file.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include "lyskomd.h" +#include <kom-types.h> +#include <kom-errno.h> +#include <services.h> +#include "ram-parse.h" +#include "log.h" +#include "smalloc.h" +#include "com.h" +#include "connections.h" + +#define REALLOC(ptr, size) srealloc(ptr, size) + +static int fparse_long_errors = 0; + +void +fskipwhite(FILE *fp) +{ + int c; + + while ( (c = getc(fp)) != EOF && isascii(c) && isspace(c) ) + ; + + ungetc(c, fp); +} + +extern u_long +fparse_long(FILE *fp) +{ + u_long res = 0; + int foo = 0; + int c; + + fskipwhite(fp); + while ( (c = getc(fp)) != EOF && isascii(c) && isdigit(c)) + { + foo = 1; + res = 10 * res + c - '0'; + } + + if ( foo == 0 ) + { + log("fparse_long() failed at pos %d.\n", ftell(fp)); + ++fparse_long_errors; + } + + ungetc(c, fp); + + return res; +} + +extern time_t +fparse_time(FILE *fp) +{ + return fparse_long(fp); +} + + + +extern Success +fparse_conference(FILE *fp, + Conference *result) +{ + if ( fparse_long_errors != 0 ) + { + log("fparse_conference(): fparse_long_errors == %d on entry. Reset.\n", + fparse_long_errors); + fparse_long_errors = 0; + } + + if ( fparse_string(fp, &result->name) != OK ) + { + log("fparse_conference(): Can't parse name.\n"); + return FAILURE; + } + + if ( fparse_member_list(fp, &result->members) != OK + || fparse_text_list(fp, &result->texts) != OK + || fparse_conf_type(fp, &result->type) != OK ) + { + log("fparse_conference: file is corrupt.\n"); + return FAILURE; + } + + result->creation_time = fparse_time(fp); + result->last_written = fparse_time(fp); + + result->creator = fparse_long(fp); + result->presentation = fparse_long(fp); + result->supervisor = fparse_long(fp); + result->permitted_submitters = fparse_long(fp); + result->super_conf = fparse_long(fp); + result->msg_of_day = fparse_long(fp); + + if ( fparse_long_errors != 0 ) + { + log("fparse_conference(): %d fparse_long_errors before 'nice'. " + "Reset.\n", + fparse_long_errors); + fparse_long_errors = 0; + return FAILURE; + } + + result->nice = fparse_long(fp); + +#ifdef DISKERR + if ( fparse_long_errors != 0 ) + { + log("fparse_conference(): Error parsing 'nice' at pos %d. " + "nice set to 77 and error ignored.\n", ftell(fp)); + fparse_long_errors = 0; + result->nice = 77; + } + + if ( result->nice < 10 ) + { + char *name; + int new_nice; + + name = s_crea_c_str(result->name); + new_nice = 10 * result->nice + 7; + + if ( name == NULL ) + { + log("fparse_conference(): nice in (null) was %d, set to %d.\n", + result->nice, new_nice); + } + else + { + log("fparse_conference(): nice in %s was %d, set to %d.\n", + name, result->nice, new_nice); + sfree(name); + } + + result->nice = new_nice; + } +#endif + + fskipwhite(fp); + + return OK; +} + + +Success +fparse_person(FILE *fp, + Person *person) +{ + String pwd = EMPTY_STRING; + + if ( fparse_long_errors != 0 ) + { + log("fparse_person(): fparse_long_errors == %d on entry. Reset.\n", + fparse_long_errors); + fparse_long_errors = 0; + } + + if ( fparse_string(fp, &pwd) != OK ) + { + log("fparse_person(): Failed to parse password.\n"); + return FAILURE; + } + + memcpy(person->pwd, pwd.string, PASSWD_LEN); + s_clear(&pwd); + + if ( fparse_string(fp, &person->username) != OK + || fparse_priv_bits(fp, &person->privileges) != OK + || fparse_personal_flags(fp, &person->flags) != OK + || fparse_text_list(fp, &person->created_texts) != OK + || fparse_mark_list(fp, &person->marks) != OK + || fparse_membership_list(fp, &person->conferences) != OK ) + { + log("fparse_person(): parse error.\n"); + return FAILURE; + } + + person->last_login = fparse_time(fp); + + person->user_area = fparse_long(fp); + person->total_time_present = fparse_long(fp); + person->sessions = fparse_long(fp); + person->created_lines = fparse_long(fp); + person->created_bytes = fparse_long(fp); + person->read_texts = fparse_long(fp); + person->no_of_text_fetches = fparse_long(fp); + person->created_persons = fparse_long(fp); + + if ( fparse_long_errors != 0 ) + { + log("fparse_person(): %d fparse_long_errors before 'created_" + "confs'. Reset.\n", + fparse_long_errors); + fparse_long_errors = 0; + return FAILURE; + } + + person->created_confs = fparse_long(fp); + +#ifdef DISKERR + if ( fparse_long_errors != 0 ) + { + log("fparse_person(): Error parsing 'created_confs' at pos %d. " + "created_confs set to 0 and error ignored.\n", ftell(fp)); + fparse_long_errors = 0; + person->created_confs = 0; + } +#endif + + fskipwhite(fp); + + return OK; +} + +Success +fparse_membership(FILE *fp, + Membership *mship) +{ + int i; + + mship->last_time_read = fparse_time(fp); + + mship->conf_no = fparse_long(fp); + mship->priority = fparse_long(fp); + mship->last_text_read = fparse_long(fp); + mship->no_of_read = fparse_long(fp); + + if ( mship->no_of_read > 0) + { + fskipwhite(fp); + switch(getc(fp)) + { + case '{': + mship->read_texts + = REALLOC(mship->read_texts, (mship->no_of_read + * sizeof(Local_text_no))); + if ( mship->read_texts == NULL ) + { + kom_errno = KOM_OUT_OF_MEMORY; + return FAILURE; + } + + for ( i = 0; i < mship->no_of_read; i++) + mship->read_texts[ i ] = fparse_long(fp); + + fskipwhite(fp); + if ( getc(fp) != '}' ) + { + kom_errno = KOM_SERVER_IS_CRAZY; + log("fparse_membership(): expected '}' at pos %d.\n", + ftell(fp)); + return FAILURE; + } + break; + + case '*': + if ( mship->read_texts != NULL ) + { + sfree(mship->read_texts); + mship->read_texts = NULL; + } + log("fparse_membership(): empty read_texts " + "with %lu elements (corrected)\n", + mship->no_of_read); + mship->no_of_read = 0; + break; + default: + log("fparse_membership(): expected '*' or '{' at pos %d.\n", + ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + } + else + { + fskipwhite(fp); + if ( getc(fp) != '*' ) + { + log("fparse_membership(): expected '*' at pos %d.\n", + ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + + if ( mship->read_texts != NULL ) + { + sfree(mship->read_texts); + mship->read_texts = NULL; + } + } + + return OK; +} + + +extern Success +fparse_membership_list(FILE *fp, + Membership_list *result) +{ + int i; + + /* First free all the read_texts. */ + + if ( result->confs != NULL ) + { + for ( i = 0; i < result->no_of_confs; i++) + sfree(result->confs[ i ].read_texts); + } + + result->no_of_confs = fparse_long(fp); + + if ( result->no_of_confs > 0 ) + { + fskipwhite(fp); + switch(getc(fp)) + { + case '{': + result->confs = REALLOC(result->confs, + (result->no_of_confs + * sizeof(Membership))); + if ( result->confs == NULL && result->no_of_confs > 0 ) + { + kom_errno = KOM_OUT_OF_MEMORY; + return FAILURE; + } + + for ( i = 0; i < result->no_of_confs; i++) + { + result->confs[ i ] = EMPTY_MEMBERSHIP; + if ( fparse_membership(fp, &result->confs[i]) != OK ) + return FAILURE; + } + + fskipwhite(fp); + if ( getc(fp) != '}' ) + { + log("fparse_membership_list(): expected '}' at pos %d.\n", + ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + + break; + case '*': + if ( result->confs != NULL ) + { + sfree(result->confs); + result->confs = NULL; + } + log("fparse_membership_list(): empty list with %lu " + "elements (corrected).\n", + (u_long)result->no_of_confs); + + result->no_of_confs = 0; + break; + default: + log("fparse_membership_list(): expected '*' or '{' at pos %d.\n", + ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + } + else + { + fskipwhite(fp); + if ( getc(fp) != '*' ) + { + log("fparse_membership_list(): expected '*' at pos %d.\n", + ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + if ( result->confs != NULL ) + { + sfree(result->confs); + result->confs = NULL; + } + } + return OK; +} + + +extern Success +fparse_conf_list(FILE *fp, + Conf_list_old *result) +{ + int i; + + result->no_of_conf_nos = fparse_long(fp); + if ( result->no_of_conf_nos > 0 ) + { + fskipwhite(fp); + switch(getc(fp)) + { + case '{': + result->conf_nos = REALLOC(result->conf_nos, + (result->no_of_conf_nos + * sizeof(Conf_no))); + if ( result->conf_nos == NULL ) + { + kom_errno = KOM_OUT_OF_MEMORY; + return FAILURE; + } + + for ( i = 0; i < result->no_of_conf_nos; i++) + result->conf_nos[ i ] = fparse_long(fp); + + fskipwhite(fp); + if ( getc(fp) != '}' ) + { + log("fparse_conf_list(): expected '}' at pos %d.\n", + ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + + break; + case '*': + if ( result->conf_nos != NULL ) + { + sfree(result->conf_nos); + result->conf_nos = NULL; + } + break; + default: + log("fparse_conf_list(): expected '*' or '{' at pos %d.\n", + ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + } + else + { + fskipwhite(fp); + if ( getc(fp) != '*' ) + { + log("fparse_conf_list(): expected '*' at pos %d.\n", + ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + if ( result->conf_nos != NULL ) + { + sfree(result->conf_nos); + result->conf_nos = NULL; + } + } + + if ( result->no_of_conf_nos > 0 ) + { + fskipwhite(fp); + switch(getc(fp)) + { + case '{': + result->type_of_conf = REALLOC(result->type_of_conf, + (result->no_of_conf_nos + * sizeof(Conf_type))); + + if ( result->type_of_conf == NULL ) + { + kom_errno = KOM_OUT_OF_MEMORY; + return FAILURE; + } + + for ( i = 0; i < result->no_of_conf_nos; i++) + if ( fparse_conf_type(fp, &result->type_of_conf[i]) != OK ) + { + return FAILURE; + } + + fskipwhite(fp); + if ( getc(fp) != '}' ) + { + log("fparse_conf_list(): expected '}' at pos %d.\n", + ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + + break; + case '*': + if ( result->type_of_conf != NULL ) + { + sfree(result->type_of_conf); + result->type_of_conf = NULL; + } + break; + default: + log("fparse_conf_list(): expected '*' or '+' at pos %d.\n", + ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + } + else + { + fskipwhite(fp); + if ( getc(fp) != '*' ) + { + log("fparse_conf_list(): expected '*' at pos %d.\n", + ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + + if ( result->type_of_conf != NULL ) + { + sfree(result->type_of_conf); + result->type_of_conf = NULL; + } + } + return OK; +} + + +extern Success +fparse_mark_list(FILE *fp, + Mark_list *result) +{ + int i; + + result->no_of_marks = fparse_long(fp); + + if ( result->no_of_marks > 0 ) + { + fskipwhite(fp); + switch(getc(fp)) + { + case '{': + result->marks = REALLOC(result->marks, + (result->no_of_marks + * sizeof(Mark))); + + if ( result->marks == NULL ) + { + kom_errno = KOM_OUT_OF_MEMORY; + return FAILURE; + } + + for ( i = 0; i < result->no_of_marks; i++) + if ( fparse_mark(fp, &result->marks[ i ] ) != OK ) + { + return FAILURE; + } + + fskipwhite(fp); + if ( getc(fp) != '}' ) + { + log("fparse_mark_list(): expected '}' at pos %d.\n", + ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + + break; + + case '*': + if ( result->marks != NULL ) + { + sfree(result->marks); + result->marks = NULL; + } + break; + + default: + log("fparse_mark_list(): expected '*' or '{' at pos %d.\n", + ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + } + else + { + fskipwhite(fp); + if ( getc(fp) != '*' ) + { + log("fparse_mark_list(): expected '*' at pos %d.\n", ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + if ( result->marks != NULL ) + { + sfree(result->marks); + result->marks = NULL; + } + } + return OK; +} + + +extern Success +fparse_text_stat(FILE *fp, + Text_stat *result) +{ + int i; + int c; + + if ( fparse_long_errors != 0 ) + { + log("fparse_text_stat(): fparse_long_errors == %d on entry. Reset.\n", + fparse_long_errors); + fparse_long_errors = 0; + } + + result->creation_time = fparse_time(fp); + + result->author = fparse_long(fp); + result->file_pos = fparse_long(fp); + result->no_of_lines = fparse_long(fp); + result->no_of_chars = fparse_long(fp); + result->no_of_marks = fparse_long(fp); + result->no_of_misc = fparse_long(fp); + + if ( fparse_long_errors != 0 ) + { + log("fparse_text_stat(): %d fparse_long_errors before 'misc_items'. " + "Reset.\n", + fparse_long_errors); + fparse_long_errors = 0; + return FAILURE; + } + + if ( result->no_of_misc > 0 ) + { + fskipwhite(fp); + switch( c = getc(fp) ) + { + case '{': + result->misc_items = REALLOC(result->misc_items, + (result->no_of_misc + * sizeof(Misc_info))); + + if ( result->misc_items == NULL ) + { + kom_errno = KOM_OUT_OF_MEMORY; + return FAILURE; + } + + for ( i = 0; i < result->no_of_misc; i++) + if ( fparse_misc_info(fp, &result->misc_items[ i ]) != OK ) + return FAILURE; + + fskipwhite(fp); + if ( (c = getc(fp)) != '}' ) + { +#ifdef DISKERR + if ( c == '@' || c == '+' ) + { + ungetc(c, fp); + log("fparse_text_stat(): got '%c' when expecting '}'.\n." + "Character ungetc'd and interpreted as a '}'." + " (pos %d).\n", c, ftell(fp)); + } + else +#endif + { + log("fparse_text_stat(): expected '}' at pos %d.\n", + ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + } + + break; + + case '@': + case '+': + ungetc(c, fp); + log("fparse_text_stat(): got '%c' when expecting '{' or '*'\n." + "Character ungetc'd and interpreted as a '*'. (pos %d).\n", + c, ftell(fp)); + /* Fall through */ + case '*': + if ( result->misc_items != NULL ) + { + sfree(result->misc_items); + result->misc_items = NULL; + } + break; + + default: + log("fparse_text_stat(): expected '*' or '}' at pos %d.\n", + ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + } + else + { + fskipwhite(fp); + if ( (c = getc(fp)) != '*' ) + { +#ifdef DISKERR + if ( c == '@' || c == '+' ) + { + ungetc(c, fp); + log("fparse_text_stat(): got '%c' when expecting '*'.\n." + "Character ungetc'd and interpreted as a '*'." + " (pos %d).\n", c, ftell(fp)); + } + else +#endif + { + log("fparse_text_stat(): expected '*' at pos %d.\n", + ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + } + + if ( result->misc_items != NULL ) + { + sfree(result->misc_items); + result->misc_items = NULL; + } + } + + fskipwhite(fp); + + return OK; +} + + +extern Success +fparse_text_list(FILE *fp, + Text_list *result) +{ + int i; + + result->first_local_no = fparse_long(fp); + result->no_of_texts = fparse_long(fp); + + if ( result->no_of_texts > 0 ) + { + fskipwhite(fp); + switch(getc(fp)) + { + case '{': + result->texts = REALLOC(result->texts, + (result->no_of_texts + * sizeof(Text_no))); + + if ( result->texts == NULL ) + { + kom_errno = KOM_OUT_OF_MEMORY; + return FAILURE; + } + + for ( i = 0; i < result->no_of_texts; i++) + result->texts[ i ] = fparse_long(fp); + + fskipwhite(fp); + if ( getc(fp) != '}' ) + { + log("fparse_text_list(): expected '}' at pos %d.\n", + ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + + break; + + case '*': + if ( result->texts != NULL ) + { + sfree(result->texts); + result->texts = NULL; + } + break; + + default: + log("fparse_text_list(): expected '*' or '{' at pos %d.\n", + ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + } + else + { + fskipwhite(fp); + if ( getc(fp) != '*' ) + { + log("fparse_text_list(): expected '*' at pos %d.\n", + ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + if ( result->texts != NULL ) + { + sfree(result->texts); + result->texts = NULL; + } + } + return OK; +} + + +extern Success +fparse_info(FILE *fp, + Info *result) +{ + result->version = fparse_long(fp); + result->conf_pres_conf = fparse_long(fp); + result->pers_pres_conf = fparse_long(fp); + result->motd_conf = fparse_long(fp); + result->kom_news_conf = fparse_long(fp); + result->motd_of_lyskom = fparse_long(fp); + return OK; +} + + +extern Success +fparse_string(FILE *fp, + String *result) +{ +#if 0 + String_size i; + int c; +#endif + + result->len = fparse_long(fp); + + if ( getc(fp) != 'H' ) + { + log("fparse_string(): expected 'H' at pos %d.\n", + ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + + result->string = REALLOC(result->string, result->len); + + if ( result->string == NULL ) + { + kom_errno = KOM_OUT_OF_MEMORY; + return FAILURE; + } + + if ( fread(result->string, sizeof(char), result->len, fp) + != result->len ) + { + log("fparse_string(): unexpected eof at pos %d.\n", ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + + return OK; +} + +extern Success +fparse_member_list(FILE *fp, + Member_list *result) +{ + int i; + + result->no_of_members = fparse_long(fp); + if ( result->no_of_members > 0 ) + { + fskipwhite(fp); + switch(getc(fp)) + { + case '{': + result->members = REALLOC(result->members, + (result->no_of_members + * sizeof(Member))); + + if ( result->members == NULL ) + { + kom_errno = KOM_OUT_OF_MEMORY; + return FAILURE; + } + + for ( i = 0; i < result->no_of_members; i++) + if ( fparse_member(fp, &result->members[ i ]) != OK ) + { + return FAILURE; + } + + + fskipwhite(fp); + if ( getc(fp) != '}' ) + { + log("fparse_member_list(): expected '}' at pos %d.\n", + ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + + break; + + case '*': + if ( result->members != NULL ) + { + sfree(result->members); + result->members = NULL; + } + break; + + default: + log("fparse_member_list(): expected '*' or '{' at pos %d.\n", + ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + } + else + { + fskipwhite(fp); + if ( getc(fp) != '*' ) + { + log("fparse_member_list(): expected '*' at pos %d.\n", ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + if ( result->members != NULL ) + { + sfree(result->members); + result->members = NULL; + } + } + return OK; +} + + +extern Success +fparse_member(FILE *fp, + Member *result) +{ + result->member = fparse_long(fp); + return OK; +} + +extern Success +fparse_mark(FILE *fp, + Mark *result) +{ + result->text_no = fparse_long(fp); + result->mark_type = fparse_long(fp); + return OK; +} + + +extern Success +fparse_priv_bits(FILE *fp, + Priv_bits *result) +{ + fskipwhite(fp); + + result->wheel = getc(fp) != '0'; + result->admin = getc(fp) != '0'; + result->statistic = getc(fp) != '0'; + result->create_pers = getc(fp) != '0'; + result->create_conf = getc(fp) != '0'; + result->change_name = getc(fp) != '0'; + result->flg7 = getc(fp) != '0'; + result->flg8 = getc(fp) != '0'; + result->flg9 = getc(fp) != '0'; + result->flg10 = getc(fp) != '0'; + result->flg11 = getc(fp) != '0'; + result->flg12 = getc(fp) != '0'; + result->flg13 = getc(fp) != '0'; + result->flg14 = getc(fp) != '0'; + result->flg15 = getc(fp) != '0'; + result->flg16 = getc(fp) != '0'; + + return OK; +} + + +extern Success +fparse_personal_flags(FILE *fp, + Personal_flags *result) +{ + fskipwhite(fp); + + result->unread_is_secret = getc(fp) != '0'; + result->flg2 = getc(fp) != '0'; + result->flg3 = getc(fp) != '0'; + result->flg4 = getc(fp) != '0'; + result->flg5 = getc(fp) != '0'; + result->flg6 = getc(fp) != '0'; + result->flg7 = getc(fp) != '0'; + result->flg8 = getc(fp) != '0'; + + return OK; +} + +extern Success +fparse_conf_type(FILE *fp, + Conf_type *result) +{ + fskipwhite(fp); + + result->rd_prot = getc(fp) != '0'; + result->original = getc(fp) != '0'; + result->secret = getc(fp) != '0'; + result->letter_box = getc(fp) != '0'; + + return OK; +} + + +extern Success +fparse_who_info(FILE *fp, + Who_info *result) +{ + result->person = fparse_long(fp); + result->working_conference = fparse_long(fp); + if ( fparse_string(fp, &result->what_am_i_doing) != OK ) + { + log("fparse_who_info(): parse error.\n"); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + + return OK; +} + + +extern Success +fparse_who_info_list(FILE *fp, + Who_info_list *result) +{ + int i; + + fskipwhite(fp); + + result->no_of_persons = fparse_long(fp); + + if ( result->no_of_persons > 0 ) + { + fskipwhite(fp); + switch(getc(fp)) + { + case '{': + result->info = REALLOC(result->info, + (result->no_of_persons + * sizeof(Who_info))); + + if ( result->info == NULL ) + { + kom_errno = KOM_OUT_OF_MEMORY; + return FAILURE; + } + + for ( i = 0; i < result->no_of_persons; i++) + { + result->info[ i ] = EMPTY_WHO_INFO; + if ( fparse_who_info(fp, &result->info[ i ]) != OK ) + return FAILURE; + } + + + fskipwhite(fp); + if ( getc(fp) != '}' ) + { + log("fparse_who_info_list(): expected '}' at pos %d.\n", + ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + + break; + + case '*': + if ( result->info != NULL ) + { + sfree(result->info); + result->info = NULL; + } + break; + + default: + log("fparse_who_info_list(): expected '*' or '{' at pos %d.\n", + ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + } + else + { + fskipwhite(fp); + if ( getc(fp) != '*' ) + { + log("fparse_who_info_list(): expected '*' at pos %d.\n", + ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + if ( result->info != NULL ) + { + sfree(result->info); + result->info = NULL; + } + } + return OK; +} + +extern Success +fparse_misc_info(FILE *fp, + Misc_info *result) +{ + result->type = fparse_long(fp); + + switch(result->type) + { + case recpt: + result->datum.recipient = fparse_long(fp); + break; + + case cc_recpt: + result->datum.cc_recipient = fparse_long(fp); + break; + + case loc_no: + result->datum.local_no = fparse_long(fp); + break; + + case rec_time: + result->datum.received_at = fparse_time(fp); + break; + + case comm_to: + result->datum.comment_to = fparse_long(fp); + break; + + case comm_in: + result->datum.commented_in = fparse_long(fp); + break; + + case footn_to: + result->datum.footnote_to = fparse_long(fp); + break; + + case footn_in: + result->datum.footnoted_in = fparse_long(fp); + break; + + case sent_by: + result->datum.sender = fparse_long(fp); + break; + + case sent_at: + result->datum.sent_at = fparse_time(fp); + break; + + default: + log("fparse_misc_info(): illegal info_type %d at pos %d.\n", + result->type, ftell(fp)); + kom_errno = KOM_SERVER_IS_CRAZY; + return FAILURE; + } + + return OK; +} diff --git a/src/server/ram-parse.h b/src/server/ram-parse.h new file mode 100644 index 0000000000000000000000000000000000000000..4f704ded083f1b23e617a7b266fe46e662000273 --- /dev/null +++ b/src/server/ram-parse.h @@ -0,0 +1,102 @@ +/* + * ram-parse.h -- parse objects from disk file. + */ + +extern u_long +fparse_long(FILE *fp); + +extern void +fskipwhite(FILE *fp); + +extern time_t +fparse_time(FILE *fp); + +extern Success +fparse_conference(FILE *fp, + Conference *result); + + +Success +fparse_person(FILE *fp, + Person *person); + +Success +fparse_membership(FILE *fp, + Membership *mship); + + +extern Success +fparse_membership_list(FILE *fp, + Membership_list *result); + + +extern Success +fparse_conf_list(FILE *fp, + Conf_list_old *result); + + +extern Success +fparse_mark_list(FILE *fp, + Mark_list *result); + + +extern Success +fparse_text_stat(FILE *fp, + Text_stat *result); + + +extern Success +fparse_text_list(FILE *fp, + Text_list *result); + + +extern Success +fparse_info(FILE *fp, + Info *result); + + +extern Success +fparse_string(FILE *fp, + String *result); + +extern Success +fparse_member_list(FILE *fp, + Member_list *result); + + +extern Success +fparse_member(FILE *fp, + Member *result); + +extern Success +fparse_mark(FILE *fp, + Mark *result); + + +extern Success +fparse_priv_bits(FILE *fp, + Priv_bits *result); + + +extern Success +fparse_personal_flags(FILE *fp, + Personal_flags *result); + +extern Success +fparse_conf_type(FILE *fp, + Conf_type *result); + + +extern Success +fparse_who_info(FILE *fp, + Who_info *result); + + +extern Success +fparse_who_info_list(FILE *fp, + Who_info_list *result); + +extern Success +fparse_misc_info(FILE *fp, + Misc_info *result); + diff --git a/src/server/ram-smalloc.c b/src/server/ram-smalloc.c new file mode 100644 index 0000000000000000000000000000000000000000..0efbb45598fe09b85905ce024cb4c9f55ca7729e --- /dev/null +++ b/src/server/ram-smalloc.c @@ -0,0 +1,177 @@ +/* + * smalloc.c + * + * Contains memory allocator routines + * + * TEST VERSION by ceder + */ + +#include <stdio.h> +#include <stdarg.h> +#include <sys/types.h> +#include <malloc.h> +#include <stdlib.h> +#include "smalloc.h" +#include "lyskomd.h" +#include "log.h" + +static u_long no_of_allocated_blocks = 0; + +/* + * "safe" malloc. Handles the case when malloc returns NULL. + * smalloc cannot fail. + */ +void * +smalloc(size_t size) +{ + unsigned int *p; + + + p = (unsigned int *) malloc(size + 2*sizeof(unsigned int) + 2); + if (p == NULL) + restart_kom("Can't allocate %lu bytes.\n", (u_long)size); + + ++no_of_allocated_blocks; + + *p++ = SMALLOC_MAGIC_ALLOC; + *p++ = size; + ((unsigned char *) p)[size] = 0x89; + ((unsigned char *) p)[size+1] = 0xA7; + + return (void *) p; +} + + +void +sfree(void * ptr) /* it is legal to sfree a NULL pointer */ +{ + unsigned int *ip; + +#if 0 + if ( ptr == NULL) + log("SFREE: Freeing a NULL pointer - should be bad! /pen\n"); +#endif + + if ( ptr != NULL ) + { + ip = (unsigned int *) ptr; + ip -= 2; + switch (*ip) + { + case SMALLOC_MAGIC_ALLOC: + if (((unsigned char *) (ip+2))[ip[1]] != 0x89 || + ((unsigned char *) (ip+2))[ip[1]+1] != 0xA7) + restart_kom("SFREE: Buffer overflow, bsize = %ul\n", ip[1]); + --no_of_allocated_blocks; + *ip = SMALLOC_MAGIC_FREE; + free( ip ); + break; + + case SMALLOC_MAGIC_FREE: + restart_kom("SFREE: Trying to free already freed block\n"); + + default: + restart_kom("SFREE: Illegal magic number\n"); + } + } +} + +extern void * +srealloc(void * ptr, size_t size) /* Never fails. It is legal to */ +{ /* realloc the NULL ptr. */ + unsigned int * ip; + unsigned int * new_ptr; + + if ( ptr == NULL ) + return smalloc(size); + + ip = (unsigned int *) ptr; + ip -= 2; + switch (*ip) + { + case SMALLOC_MAGIC_ALLOC: + break; + + case SMALLOC_MAGIC_FREE: + restart_kom("SREALLOC: Trying to realloc freed block\n"); + + default: + restart_kom("SREALLOC: Illegal magic number\n"); + } + + if (((unsigned char *) (ip+2))[ip[1]] != 0x89 || + ((unsigned char *) (ip+2))[ip[1]+1] != 0xA7) + restart_kom("SREALLOC: Buffer overflow, osize = %ul, nsize = %ul\n", + ip[1], size); + + *ip = SMALLOC_MAGIC_FREE; + if ( (new_ptr = (unsigned int *) realloc((void *) ip, + size+2*sizeof(unsigned int)+2) ) == NULL ) + { + restart_kom("Out of memory - can't realloc. ptr = %d size = %d. ", + (int)ptr, size); + } + + *new_ptr++ = SMALLOC_MAGIC_ALLOC; + *new_ptr++ = size; + + ((unsigned char *) new_ptr)[size] = 0x89; + ((unsigned char *) new_ptr)[size+1] = 0xA7; + + return (void *) new_ptr; +} + + +/* + * Allocate temporary memory, which is automatically freed after this + * atomic call. + */ + +static void **tmp_alloc_table = NULL; +static int tmp_alloc_table_size = 0; /* Size */ +static int tmp_alloc_table_use = 0; /* Used size */ + +void * +tmp_alloc(u_long size) +{ + if ( tmp_alloc_table_size <= tmp_alloc_table_use ) + { + /* Need to increas table. */ + tmp_alloc_table = srealloc (tmp_alloc_table, + ((++tmp_alloc_table_size) + * sizeof (void *))); + log("tmp_alloc: internal table size now %d elements.\n", + tmp_alloc_table_size); + } + + return (tmp_alloc_table[ tmp_alloc_table_use++ ] + = smalloc (size)); +} + + +/* + * Free all core which is allocated with tmp_alloc(). This is called from + * end_of_atomic(). + */ +void +free_tmp(void) +{ + int i; + + for ( i = 0; i < tmp_alloc_table_use; i++ ) + { + sfree ( tmp_alloc_table[ i ] ); + tmp_alloc_table[ i ] = NULL; + } + + tmp_alloc_table_use = 0; +} + +void +free_all_tmp(void) +{ + free_tmp(); + sfree( tmp_alloc_table ); + tmp_alloc_table = NULL; + tmp_alloc_table_size = 0; +} diff --git a/src/server/ramkomd.c b/src/server/ramkomd.c new file mode 100644 index 0000000000000000000000000000000000000000..214008968518893a4c698b6bb3172d0e63461ab2 --- /dev/null +++ b/src/server/ramkomd.c @@ -0,0 +1,269 @@ +/* + * Detta {r serverns huvudprogram. Det kommer f|rhoppningsvis bli st|rre + * {n det {r just nu... + * + * Created by Willf|r 31/3-90 + * + * It has grown! /ceder + */ + + +#include <getopt.h> +#include <stdlib.h> +#include <signal.h> +#include <ctype.h> +#include <netdb.h> +#include <string.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <netinet/in.h> +#include <unistd.h> +#include "std-disclaimer.h" +#include <kom-types.h> +#include <kom-errno.h> +#include "s-string.h" +#include "internal-services.h" +#include "lyskomd.h" +#include "isc.h" +#include "smalloc.h" +#include "cache.h" +#include "log.h" +#include "com.h" +#include "connections.h" +#include "config.h" + +Kom_err kom_errno; +u_long err_stat; +Bool send_async_messages = TRUE; + + +#ifdef DEBUG +int buglevel = 0; +Bool never_save = FALSE; +#endif + + +extern int chdir (char * path); + + +char datafilename[1024]; /* Full pathname to the database file */ +char backupfilename[1024]; /* Full pathname to the backup file */ +char textfilename[1024]; /* Full pathname to the text file. */ +char statisticfile[1024]; /* Dito to statistics file. */ +char pidfile[1024]; /* Dito pid-file. */ + +char ip_client_port[80]; /* Port to listen to for clients */ +char ip_mux_port[80]; /* Port to listen to for mux:es */ + +int listen_client = -1; /* ISC listen identifier */ +int listen_mux = -1; /* -"- */ + + +static char *dbase_dir = NULL; /* Directory where database resides */ + +static void +server_init( char * client_port, char * mux_port) +{ + /* + ** Setup some parameters here + */ + isc_setlogfn(&logv); + isc_setallocfn(&smalloc, &srealloc, &sfree); + /* isc_setabortfn(&restart_kom); */ + + kom_server_mcb = isc_initialize(NULL); + + if ( kom_server_mcb == NULL ) + restart_kom("server_init: can't isc_setup()\n"); + + if ((listen_client = isc_listentcp(kom_server_mcb, client_port)) < 0) + restart_kom("server_init: can't isc_listen(CLIENT)\n"); + + if ((listen_mux = isc_listentcp(kom_server_mcb, mux_port)) < 0) + restart_kom("server_init: can't isc_listen(MUX)\n"); + + + /* + * Ignore SIGPIPE, which the server gets if it tries to write to a + * socket and the client has died. The server will anyhow handle + * this situation correct. + */ + signal(SIGPIPE, SIG_IGN); +} + +static void +init_data_base(const char *dbase_dir) +{ + String a = EMPTY_STRING, b = EMPTY_STRING, c = EMPTY_STRING, + d = EMPTY_STRING; + + if (dbase_dir == NULL) + dbase_dir = DEFAULT_DBASE_DIR; + + sprintf(datafilename, "%s/%s", dbase_dir, DATAFILE_NAME); + sprintf(backupfilename, "%s/%s", dbase_dir, BACKUPFILE_NAME); + sprintf(textfilename, "%s/%s", dbase_dir, TEXTFILE_NAME); + sprintf(statisticfile, "%s/%s", dbase_dir, STATISTIC_NAME); + sprintf(pidfile, "%s/%s", dbase_dir, PID_NAME); + + log("Database = %s\n", datafilename); + log("Backup = %s\n", backupfilename); + + if ( init_cache() == FAILURE ) + { + log ( "Setting up first four conferences.\n"); + + if ( s_crea_str(&a, "Presentation (av nya) m|ten") != OK + || s_crea_str(&b, "Presentation (av nya) medlemmar") != OK + || s_crea_str(&c, "Lappar (p}) d|rren") != OK + || s_crea_str(&d, "Nyheter om LysKOM") != OK) + { + restart_kom("init_data_base: can't create strings\n"); + } + + if ( ! ( do_create_conf(a, 0, 0, 0, NULL_CONF_TYPE) + && do_create_conf(b, 0, 0, 0, NULL_CONF_TYPE) + && do_create_conf(c, 0, 0, 0, NULL_CONF_TYPE) + && do_create_conf(d, 0, 0, 0, NULL_CONF_TYPE))) + { + restart_kom("init_data_base: Kan ej skapa m|ten\n"); + } + } +} + +void +sighandler_hup (void) +{ + log ("Signal HUP received. Shutting down server.\n"); + go_and_die = TRUE; +} + +void +sighandler_quit (void) +{ + log ("Signal QUIT received - syncing...\n"); + cache_sync_all(); + log ("Dumping core now.\n"); + chdir ("/usr/lyskom/etc"); + abort(); +} + +void +sighandler_usr1 (void) +{ + do_sync_db = TRUE; +} + +void +sighandler_usr2 (void) +{ + int child; + extern int fork(void); + + log ("Signal USR2 received - will dump core now. (Check that child dies.)\n"); + if ((child = fork()) == 0) + { + chdir ("/usr/lyskom/etc"); + abort(); + log ("Abort() failed!!!"); + exit(1); + } + else if (child < 0) + { + log ("Couldn't fork.\n"); + } + else + { + wait (NULL); + } +} + +void +save_pid(void) +{ + FILE *fp; + + if ( (fp = fopen(pidfile, "w")) == NULL ) + return; + + fprintf(fp, "%d", getpid()); + fclose(fp); +} + + +int +main (int argc, + char **argv) +{ + int i; + + + strcpy(ip_client_port, DEFAULT_CLIENT_SERVICE); + strcpy(ip_mux_port, DEFAULT_MUX_SERVICE); + + for (i = 1; i < argc && argv[i][0] == '-'; i++) + switch (argv[i][1]) + { + case 'd': + buglevel++; + break; + + case 'q': + never_save = TRUE; + break; + + case 'D': /* Database directory */ + dbase_dir = argv[i]+2; + break; + + case 'p': /* TCP/IP port number for clients */ + strcpy(ip_client_port, argv[i]+2); + break; + + case 'P': /* TCP/IP port number for MUXes */ + strcpy(ip_mux_port, argv[i]+2); + break; + + case 'a': + send_async_messages = FALSE; + break; + + default: + restart_kom("usage: ramkomd [options]\n"); + } + + if (i < argc) + { + restart_kom("usage: ramkomd\n"); + } + + s_set_storage_management(smalloc, srealloc, sfree); + + signal(SIGHUP, sighandler_hup); + signal(SIGQUIT, sighandler_quit); + signal(SIGUSR1, sighandler_usr1); + signal(SIGUSR2, sighandler_usr2); + + server_init(ip_client_port, ip_mux_port); + init_data_base(dbase_dir); + save_pid(); + toploop(); + cache_sync_all(); + free_all_tmp(); + + log("ramkomd terminated normally.\n"); + return 0; +} + +void +restart_kom(const char * format, ...) +{ + va_list AP; + + va_start(AP, format); + logv(format, AP); + va_end(AP); + + log("Previous message is fatal. Will dump core now.\n"); + abort(); +} diff --git a/src/server/server-config.c b/src/server/server-config.c new file mode 100644 index 0000000000000000000000000000000000000000..8d83113b5770b2d9e73c21d6bf66184325862574 --- /dev/null +++ b/src/server/server-config.c @@ -0,0 +1,100 @@ +/* + * config.c + * + * This is in a .c file to make it possible to change a value without having + * to recompile the entire server. + */ + + +/* Where to save things. These are used by ramkomd. */ + +const char * DEFAULT_DBASE_DIR = "/usr/lyskom"; + +const char *DATAFILE_NAME = "db/ramkomd-data"; +const char *BACKUPFILE_NAME = "db/ramkomd-backup"; +const char *TEXTFILE_NAME = "db/ramkomd-texts"; +const char *STATISTIC_NAME = "etc/ramkomd-logg"; +const char *PID_NAME = "etc/pid"; + +/* Communications */ + +const char * DEFAULT_CLIENT_SERVICE = "lyskom-client"; /* Can be number! */ +const char * DEFAULT_MUX_SERVICE = "lyskom-mux"; /* Can be number! */ + +#if 0 +const int IN_CLIENT_PORT = 4896; /* Which port does LysKOM listen to? */ +const int IN_MUX_PORT = 4895; /* Which port does LysKOM listen to? */ +#endif + +/* + * The following should always be true: + * 0 <= SYNCTIMEOUT <= GARBTIMEOUT <= TIMEOUT + * Times in milliseconds. + * + * GARBTIMEOUT and SYNCTIMEOUT are only used in diskomd. + */ +const int TIMEOUT = 100; /* Timeout to select() when totally idle. + * Should be much larger. Is this small + to work around the famous select bug + in isc. */ +const int GARBTIMEOUT = 100; /* Timeout to select() when garbing texts + but not syncing. */ +const int SYNCTIMEOUT = 10; /* Timeout to select() when syncing. */ + +/* Times in minutes. */ +const int GARB_INTERVAL = 60*24; /* Once a day. */ +const int SYNC_INTERVAL = 30; /* 30 = Twice per hour. */ +const int SYNC_RETRY_INTERVAL = 5; /* 5 = Wait 5 minutes and retry after + an error. */ + +/* String limits */ + +const int CONF_NAME_LEN = 60; /* Conference (and Person) name */ +const int PWD_LEN = 128; /* Password. It is not guaranteed that all + chars are significant. */ +const int WHAT_DO_LEN = 60; /* what_am_i_doing */ +const int USERNAME_LEN = 128; /* Max login-id from clients */ +const int TEXT_LEN =131072;/* Max len of a text. */ +const int BROADCAST_LEN = 1024; /* Max len of a broadcast message */ + +/* Text_stat limits */ + +const int MAX_MARKS_PERSON = 2048; /* Max marks per person */ +const int MAX_MARKS_TEXT = 1024; /* Max marks per text */ +const int MAX_RECIPIENTS = 256; /* Max recipients/cc_recipients per text */ +const int MAX_COMM = 128; /* Max number of comments to a text */ +const int MAX_FOOT = 32; /* Max number of footnotes to a text */ +const int MAX_CREA_MISC = 512; /* Sum of recipients, cc_recipients, comm_to + * and footn_to must not exceed MAX_CREA_MISC + * when the text is created. */ + +/* + * Some other limits + */ + +#ifdef __sequent__ +const int MAX_NO_OF_CONNECTIONS = 12; +#else +const int MAX_NO_OF_CONNECTIONS = 32; /* the maximum number of persons + * that can use Kom simultaneously */ +#endif + +const int MARK_AS_READ_CHUNK = 128; /* You can't mark more than this many + * texts as read in one call. */ + +/* + * Max number of nested super_confs a message will be forwarded before + * the server gives up. + */ +const int MAX_SUPER_CONF_LOOP = 17; + + +const int DEFAULT_NICE = 77; /* Number of days a text normally lives. */ + + + +/* Max entries in the per-client transmit queue */ +const int MAXQUEUEDSIZE = 300; + +/* Max entries in the per-client transmit queue to send at any one time */ +const int MAXDEQUEUELEN = 10; diff --git a/src/server/tmp-limits.h b/src/server/tmp-limits.h new file mode 100644 index 0000000000000000000000000000000000000000..0b14f1c6b27c662aa044a4eb4bb7273dc004a6d3 --- /dev/null +++ b/src/server/tmp-limits.h @@ -0,0 +1,8 @@ +/* + * limits.h + * + * These arbitrary limits should be lifted in future versions. + */ + +#define MAX_CONF 1026 +#define MAX_TEXT 99999