/*---------------------------------------------------------------------------- -- -- Module: xtmDbTools -- -- Project: Xdiary -- System: xtm - X Desktop Calendar -- Subsystem: <> -- Function block: <> -- -- Description: -- Interface to the database containing: -- * Entries. -- * Entries defined for a day. -- * Standing entries. -- * Private entries. -- -- Filename: xtmDbTools.c -- -- Authors: Roger Larsson, Ulrika Bornetun -- Creation date: 1991-05-28 -- -- -- (C) Copyright Ulrika Bornetun, Roger Larsson (1995) -- All rights reserved -- -- Permission to use, copy, modify, and distribute this software and its -- documentation for any purpose and without fee is hereby granted, -- provided that the above copyright notice appear in all copies. Ulrika -- Bornetun and Roger Larsson make no representations about the usability -- of this software for any purpose. It is provided "as is" without express -- or implied warranty. ----------------------------------------------------------------------------*/ /* SCCS module identifier. */ static char SCCSID[] = "@(#) Module: xtmDbTools.c, Version: 1.4, Date: 95/06/26 22:27:30"; /*---------------------------------------------------------------------------- -- Include files ----------------------------------------------------------------------------*/ /* netinet/in.h doesn't compile with _POSIX_SOURCE on some platforms. */ #ifdef _POSIX_SOURCE #undef _POSIX_SOURCE #define HAD_POSIX #endif #include #include #include #include #include #include #include #include "System.h" #include "DirList.h" #include "xtmGlobal.h" #include "xtmAccBase.h" #include "xtmDbTools.h" #include "xtmHoliday.h" #ifndef XD_NO_NET_ORDER # define NET_BYTE_ORDER 1 #endif #ifdef NET_BYTE_ORDER #include #endif #ifdef HAD_POSIX #ifndef _POSIX_SOURCE #define _POSIX_SOURCE #endif #endif /*---------------------------------------------------------------------------- -- Macro definitions ----------------------------------------------------------------------------*/ /* Try to lock a file this number of times. */ #define MAX_LOCK_TRIES 20 /* Number of cached db operations records. */ #define MAX_DB_OP_CACHE 1 /* Access control file. */ #define ACCESS_FILE "dbAccess" /* Key for access control file. */ #define KEY_ACCESS_CONTROL "Access" /* Exception handling. */ #define raise goto /*---------------------------------------------------------------------------- -- Type declarations ----------------------------------------------------------------------------*/ /* Operations allowed on a database. */ typedef struct { char directory[ 150 ]; UINT32 operations; } DB_OPERATIONS; /*---------------------------------------------------------------------------- -- Global definitions ----------------------------------------------------------------------------*/ /* Name of module. */ static char *module_name = "xtmDbTools"; /* Size of the entry key and record. */ static int xtm_db_entry_def_size = 170; static int xtm_db_entry_key_size = 4; /* Size of the date key and record. */ static int xtm_db_date_def_size = 256; static int xtm_db_date_key_size = 4; /* Size of the standing entry key and record. */ static int xtm_db_stand_entry_def_size = 48; static int xtm_db_stand_entry_key_size = 4; /* Size of the private entry key and record. */ static int xtm_db_priv_entry_def_size = 110; static int xtm_db_priv_entry_key_size = 4; /* Use file locking? */ static Boolean use_lock = True; /* Real an privileged process IDs. */ static int real_gid = -1; static int privileged_gid = -1; /* Name of key and data files. */ static char *db_filenames[] = { ENTRY_DB_NAME, ENTRY_DB_NAME_DIR, ENTRY_DB_NAME_PAG, DATE_DB_NAME, DATE_DB_NAME_DIR, DATE_DB_NAME_PAG, STAND_ENTRY_DB_NAME, STAND_ENTRY_DB_NAME_DIR, STAND_ENTRY_DB_NAME_PAG, PRIV_ENTRY_DB_NAME, PRIV_ENTRY_DB_NAME_DIR, PRIV_ENTRY_DB_NAME_PAG, }; /* Cache to hold operations allowed on a database. */ static Boolean cache_valid = False; static DB_OPERATIONS db_op_cache[ MAX_DB_OP_CACHE ]; /*---------------------------------------------------------------------------- -- Function prototypes ----------------------------------------------------------------------------*/ static INT32 calcCrc( char *text ); static XTM_DB_STATUS closeDatabase( XTM_DB_DATABASE database ); static XTM_DB_STATUS createFileLock( char *filename, UINT32 operations, int *lock_fd, int *uid_locking ); static LST_COMPARE dateSortFunc( XTM_DB_DATE_DEF *element, TIM_TIME_REF date ); static XTM_DB_STATUS deleteDate( XTM_DB_DATABASE database, TIM_TIME_REF date ); static XTM_DB_STATUS deleteEntry( XTM_DB_DATABASE database, UINT32 id ); static XTM_DB_STATUS deleteEntryIdInDate( XTM_DB_DATABASE database_ref, UINT32 entry_id, TIM_TIME_REF date_stamp ); static XTM_DB_STATUS deletePrivData( XTM_DB_ENTRY_DATABASES *databases, XTM_DB_ALL_ENTRY_REF entry_ref ); static XTM_DB_STATUS deletePrivDb( XTM_DB_ENTRY_DATABASES *databases, UINT32 id ); static XTM_DB_STATUS deletePrivText( XTM_DB_ENTRY_DATABASES *databases, UINT32 id ); static XTM_DB_STATUS deleteStandEntry( XTM_DB_DATABASE database, UINT32 id ); static XTM_DB_STATUS deleteTextFile( char *directory, UINT32 id ); static XTM_DB_STATUS doInsertEntry( XTM_DB_ENTRY_DATABASES *databases, XTM_DB_ALL_ENTRY_REF entry_ref, char *text_ref, UINT32 log_flags ); static LST_COMPARE entryIdSortFunc( XTM_DB_ENTRY_DEF *element, UINT32 id ); static LST_COMPARE entryTimeSortFunc( XTM_DB_ENTRY_DEF *element, TIM_TIME_REF date ); static XTM_DB_STATUS fetchDate( XTM_DB_DATABASE database, TIM_TIME_REF date, XTM_DB_DATE_REF entry ); static XTM_DB_STATUS fetchEntry( XTM_DB_DATABASE database, UINT32 id, XTM_DB_ENTRY_REF entry ); static XTM_DB_STATUS fetchFirstDate( XTM_DB_DATABASE database, XTM_DB_DATE_REF entry ); static XTM_DB_STATUS fetchFirstStand( XTM_DB_DATABASE database, XTM_DB_STAND_ENTRY_REF entry ); static XTM_DB_STATUS fetchNextDate( XTM_DB_DATABASE database, XTM_DB_DATE_REF entry ); static XTM_DB_STATUS fetchNextStand( XTM_DB_DATABASE database, XTM_DB_STAND_ENTRY_REF entry ); static XTM_DB_STATUS fetchPrivData( XTM_DB_ENTRY_DATABASES *databases, UINT32 id, XTM_DB_ALL_ENTRY_REF entry_ref, char **text_ref ); static XTM_DB_STATUS fetchPrivDb( XTM_DB_ENTRY_DATABASES *databases, UINT32 id, char **text_ref ); static XTM_DB_STATUS fetchPrivText( XTM_DB_ENTRY_DATABASES *databases, UINT32 id, char **text_ref ); static XTM_DB_STATUS fetchStandEntry( XTM_DB_DATABASE database, UINT32 id, XTM_DB_STAND_ENTRY_REF entry ); static XTM_DB_STATUS fetchTextFile( char *directory, UINT32 id, char **text ); static void freeFileLock( int *lock_fd ); static XTM_DB_STATUS insertDate( XTM_DB_DATABASE database, XTM_DB_DATE_REF entry ); static XTM_DB_STATUS insertEntry( XTM_DB_DATABASE database, XTM_DB_ENTRY_REF entry ); static XTM_DB_STATUS insertEntryIdInDate( XTM_DB_DATABASE database_ref, UINT32 entry_id, TIM_TIME_REF date_stamp ); static XTM_DB_STATUS insertPrivData( XTM_DB_ENTRY_DATABASES *databases, UINT32 id, XTM_DB_ALL_ENTRY_REF entry_ref, char *text_ref ); static XTM_DB_STATUS insertPrivDb( XTM_DB_ENTRY_DATABASES *databases, UINT32 id, char *text_ref ); static XTM_DB_STATUS insertPrivText( XTM_DB_ENTRY_DATABASES *databases, UINT32 id, char *text_ref ); static XTM_DB_STATUS insertStandEntry( XTM_DB_DATABASE database, XTM_DB_STAND_ENTRY_REF entry ); static XTM_DB_STATUS insertTextFile( char *directory, UINT32 id, char *text ); static XTM_DB_DATABASE openDatabase( XTM_DB_OPEN_REQUEST *open_request ); static Boolean updateDbLog( char *directory, UINT32 entry_id, UINT32 flags ); /*---------------------------------------------------------------------------- -- Functions ----------------------------------------------------------------------------*/ XTM_DB_STATUS xtmDbChangesInLog( char *directory, TIM_TIME_REF since, XTM_DB_LOG_RECORD changes[], int max_changes, int *no_changes ) { /* Variables. */ Boolean back_to_normal = False; int lock_fd = -1; int rec_index; int read_ref; int status; int uid_locking; long offset; UINT32 operations; char filename[ 150 ]; FILE *file_ref; XTM_DB_LOG_HEADER header; XTM_DB_LOG_RECORD log_record; XTM_DB_STATUS db_status; /* Code. */ /* Check the operations that can be done. */ xtmDbCheckDbOperations( directory, False, &operations ); /* Go to privileged mode? */ if( flagIsSet( operations, XTM_DB_FLAG_MODE_SETID ) && getegid() != privileged_gid ) { status = setgid( privileged_gid ); back_to_normal = True; } /* Lock the file. */ sprintf( filename, "%s/%s", directory, LOCK_FILE ); db_status = createFileLock( filename, XTM_DB_FLAG_MODE_READ, &lock_fd, &uid_locking ); if( db_status != XTM_DB_OK ) raise lock_exception; /* Open the log file. */ sprintf( filename, "%s/%s", directory, LOG_FILE ); file_ref = fopen( filename, "r" ); if( file_ref == NULL ) raise exception; /* Fetch the header record. */ rewind( file_ref ); fread( (void *) &header, sizeof( header ), 1, file_ref ); #ifdef NET_BYTE_ORDER header.last_changed = ntohl( header.last_changed ); header.current_ref = ntohl( header.current_ref ); header.records = ntohl( header.records ); header.max_log_records = ntohl( header.max_log_records ); #endif /* Where do we start? */ if( header.records < header.max_log_records ) { read_ref = 0; } else { read_ref = header.current_ref + 1; if( read_ref >= header.max_log_records ) read_ref = 0; } rec_index = 0; /* Read all records in the log file. */ while( read_ref != header.current_ref && rec_index < max_changes ) { offset = sizeof( header ) + read_ref * sizeof( log_record ); fseek( file_ref, offset, 0 ); fread( (void *) &log_record, sizeof( log_record ), 1, file_ref ); #ifdef NET_BYTE_ORDER log_record.time_stamp = ntohl( log_record.time_stamp ); log_record.entry_id = ntohl( log_record.entry_id ); log_record.flags = ntohl( log_record.flags ); log_record.changed_by = ntohl( log_record.changed_by ); #endif /* Do we have a match. */ if( (TIM_TIME_REF) log_record.time_stamp >= since ) { memcpy( &changes[ rec_index ], &log_record, sizeof( log_record ) ); rec_index++; } /* Next record. */ read_ref++; if( read_ref >= header.max_log_records ) read_ref = 0; } /* while */ fclose( file_ref ); *no_changes = rec_index; /* Free the file lock. */ freeFileLock( &lock_fd ); /* Back to normal mode? */ if( back_to_normal ) status = setgid( real_gid ); return( XTM_DB_OK ); /* Exception handling. */ exception: if( file_ref != NULL ) fclose( file_ref ); if( lock_fd != -1 ) freeFileLock( &lock_fd ); if( back_to_normal ) status = setgid( real_gid ); return( XTM_DB_ERROR ); lock_exception: if( back_to_normal ) status = setgid( real_gid ); return( db_status ); } /* xtmDbChangesInLog */ /*----------------------------------------------------------------------*/ void xtmDbCheckDbOperations( char *directory, Boolean force_check, UINT32 *operations ) { /* Variables. */ int status; char filename[ 150 ]; XTM_DB_STATUS db_status; struct stat file_status; /* Code. */ /* Check the cache. */ if( ! force_check && cache_valid && strcmp( directory, db_op_cache[ 0 ].directory ) == 0 ) { *operations = db_op_cache[ 0 ].operations; return; } *operations = 0; /* Is this the owner? */ status = stat( directory, &file_status ); if( status == 0 && file_status.st_uid == getuid() ) { flagSet( *operations, (XTM_DB_FLAG_MODE_READ | XTM_DB_FLAG_MODE_WRITE | XTM_DB_FLAG_MODE_MSG | XTM_DB_FLAG_MODE_PRIV) ); return; } /* Check privileges within the directory itself. */ sprintf( filename, "%s/%s", directory, ID_FILE ); if( access( filename, (R_OK | W_OK | F_OK) ) == 0 ) { flagSet( *operations, (XTM_DB_FLAG_MODE_READ | XTM_DB_FLAG_MODE_WRITE) ); } else if( access( filename, (R_OK | F_OK) ) == 0 ) { flagSet( *operations, XTM_DB_FLAG_MODE_READ ); } /* Access permissions for the Message directory. */ sprintf( filename, "%s/Message", directory ); if( access( filename, (W_OK | F_OK) ) == 0 ) { flagSet( *operations, XTM_DB_FLAG_MODE_MSG ); } /* If we could not read or write, check the access list (if any). */ if( *operations == 0 && real_gid != privileged_gid ) { /* Move to privileged level. */ status = setgid( privileged_gid ); /* Fetch the operations this user can do. */ db_status = xtmAbGetUserOperations( directory, getuid(), getgid(), operations ); /* Back to user level. */ status = setgid( real_gid ); } /* if */ /* Save in cache. */ cache_valid = True; strcpy( db_op_cache[ 0 ].directory, directory ); db_op_cache[ 0 ].operations = *operations; return; } /* xtmDbCheckDbOperations */ /*----------------------------------------------------------------------*/ XTM_DB_STATUS xtmDbCreateDatabase( XTM_DB_CREATE_REQUEST *create_request ) { /* Variables. */ int file_mode; int status; char filename[ 200 ]; char *data_file; char *key_file; FILE *file_ref; struct stat stat_data; /* Code. */ /* Get the name of the database. */ switch( create_request -> database ) { case XTM_DB_ENTRY_DB: key_file = db_filenames[ 1 ]; data_file = db_filenames[ 2 ]; break; case XTM_DB_DATE_DB: key_file = db_filenames[ 4 ]; data_file = db_filenames[ 5 ]; break; case XTM_DB_STAND_ENTRY_DB: key_file = db_filenames[ 7 ]; data_file = db_filenames[ 8 ]; break; case XTM_DB_PRIV_ENTRY_DB: key_file = db_filenames[ 10 ]; data_file = db_filenames[ 11 ]; break; default: return( XTM_DB_UNKNOWN ); } /* switch */ /* Fetch the directory data and define the mode for files. */ sprintf( filename, "%s", create_request -> directory ); status = stat( filename, &stat_data ); if( status != 0 ) return( XTM_DB_ERROR ); file_mode = stat_data.st_mode & (~(S_IXUSR | S_IXGRP | S_IXOTH)); /* Does the database exist? */ sprintf( filename, "%s/%s", create_request -> directory, key_file ); status = stat( filename, &stat_data ); if( status == 0 ) return( XTM_DB_OK ); /* Create empty key file. */ sprintf( filename, "%s/%s", create_request -> directory, key_file ); file_ref = fopen( filename, "w" ); if( file_ref == NULL ) return( XTM_DB_ERROR ); fclose( file_ref ); /* Let the key file inherit the permissions from the directory. */ status = chmod( filename, file_mode ); if( status != 0 ) return( XTM_DB_ERROR ); /* Create empty data file. */ sprintf( filename, "%s/%s", create_request -> directory, data_file ); file_ref = fopen( filename, "w" ); if( file_ref == NULL ) return( XTM_DB_ERROR ); fclose( file_ref ); /* Let the data file inherit the permissions from the directory. */ status = chmod( filename, file_mode ); if( status != 0 ) return( XTM_DB_ERROR ); return( XTM_DB_OK ); } /* xtmDbCreateDatabase */ /*----------------------------------------------------------------------*/ XTM_DB_STATUS xtmDbFetchFileInfo( char *filename, struct stat *file_info ) { /* Variables. */ Boolean back_to_normal = False; int status; UINT32 operations; /* Code. */ /* Check the operations that can be done. */ xtmDbCheckDbOperations( filename, False, &operations ); /* Go to privileged mode? */ if( flagIsSet( operations, XTM_DB_FLAG_MODE_SETID ) && getegid() != privileged_gid ) { status = setgid( privileged_gid ); back_to_normal = True; } /* Fetch file info. */ status = stat( filename, file_info ); /* Back to normal mode? */ if( back_to_normal ) setgid( real_gid ); if( status != 0 ) return( XTM_DB_ERROR ); return( XTM_DB_OK ); } /* xtmDbFetchFileInfo */ /*----------------------------------------------------------------------*/ XTM_DB_STATUS xtmDbGenerateId( XTM_DB_ID_REQUEST *request, UINT32 *id ) { /* Variables. */ Boolean back_to_normal = False; int items; int lock_fd = -1; int status; int uid_locking; UINT32 operations; char buffer[ 50 ]; char filename[ 150 ]; char *char_ref; FILE *file_ref = NULL; XTM_DB_STATUS db_status; /* Code. */ *id = 0; /* Check the operations that can be done. */ xtmDbCheckDbOperations( request -> directory, False, &operations ); /* Go to privileged mode? */ if( flagIsSet( operations, XTM_DB_FLAG_MODE_SETID ) && getegid() != privileged_gid ) { status = setgid( privileged_gid ); back_to_normal = True; } /* Lock the file. */ if( request -> lock_file ) { sprintf( filename, "%s/%s", request -> directory, LOCK_FILE ); db_status = createFileLock( filename, XTM_DB_FLAG_MODE_WRITE, &lock_fd, &uid_locking ); if( db_status != XTM_DB_OK ) raise lock_exception; } /* Open the identifier file. */ sprintf( filename, "%s/%s", request -> directory, ID_FILE ); file_ref = fopen( filename, "r+" ); if( file_ref == NULL ) raise exception; /* Fetch the old identifier. */ rewind( file_ref ); char_ref = fgets( buffer, sizeof( buffer ), file_ref ); if( char_ref == NULL ) raise exception; items = sscanf( buffer, "%d", id ); if( items == 1 ) { *id = *id + 1; rewind( file_ref ); fprintf( file_ref, "%8d\n", *id ); } fclose( file_ref ); /* Free the file lock. */ freeFileLock( &lock_fd ); /* Back to normal mode? */ if( back_to_normal ) status = setgid( real_gid ); if( items == 1 ) return( XTM_DB_OK ); else return( XTM_DB_ERROR ); /* Exception handling. */ exception: if( file_ref != NULL ) fclose( file_ref ); if( lock_fd != -1 ) freeFileLock( &lock_fd ); if( back_to_normal ) status = setgid( real_gid ); return( XTM_DB_ERROR ); lock_exception: if( back_to_normal ) status = setgid( real_gid ); return( db_status ); } /* xtmDbGenerateId */ /*----------------------------------------------------------------------*/ void xtmDbGetEntryPermissions( UINT32 db_operations, int entry_owner, UINT32 entry_flags, UINT32 *can_do_flags ) { /* Code. */ *can_do_flags = 0; flagSet( *can_do_flags, XTM_DB_PROT_READ ); flagSet( *can_do_flags, XTM_DB_PROT_WRITE ); /* Can we read the entry. */ if( flagIsSet( entry_flags, XTM_DB_FLAG_PRIVATE ) && flagIsClear( db_operations, XTM_DB_FLAG_MODE_PRIV ) ) flagClear( *can_do_flags, XTM_DB_PROT_READ ); /* Can we write the entry. */ if( flagIsClear( db_operations, XTM_DB_FLAG_MODE_WRITE ) || (flagIsSet( entry_flags, XTM_DB_FLAG_PRIVATE ) && flagIsClear( db_operations, XTM_DB_FLAG_MODE_PRIV )) ) flagClear( *can_do_flags, XTM_DB_PROT_WRITE ); /* Can we change the entry? */ if( flagIsSet( *can_do_flags, XTM_DB_PROT_WRITE ) && (entry_owner == getuid() || flagIsClear( entry_flags, XTM_DB_FLAG_ONLY_OWNER_CHANGE )) ) flagSet( *can_do_flags, XTM_DB_PROT_CHANGE ); /* Can we delete the entry? */ if( flagIsSet( *can_do_flags, XTM_DB_PROT_WRITE ) && (entry_owner == getuid() || flagIsClear( entry_flags, XTM_DB_FLAG_ONLY_OWNER_DELETE )) ) flagSet( *can_do_flags, XTM_DB_PROT_DELETE ); return; } /* xtmDbGetEntryPermissions */ /*----------------------------------------------------------------------*/ XTM_DB_STATUS xtmDbInitializeAuxFiles( char *directory ) { /* Variables. */ int file_mode; int status; char filename[ 150 ]; FILE *file_ref; struct stat stat_data; /* Code. */ /* Fetch the directory data and define the mode for files. */ sprintf( filename, "%s", directory ); status = stat( filename, &stat_data ); if( status != 0 ) return( XTM_DB_ERROR ); file_mode = stat_data.st_mode & (~(S_IXUSR | S_IXGRP | S_IXOTH)); /* Does the id file already exist? */ sprintf( filename, "%s/%s", directory, ID_FILE ); status = stat( filename, &stat_data ); if( status != 0 ) { /* Open the identifier file. */ file_ref = fopen( filename, "w" ); if( file_ref == NULL ) return( XTM_DB_ERROR ); /* Write the start ID. */ fprintf( file_ref, "%8d\n", 11 ); fclose( file_ref ); /* Let the file inherit the permissions from the directory. */ status = chmod( filename, file_mode ); if( status != 0 ) return( XTM_DB_ERROR ); } /* if */ /* Does the lock file already exist? */ sprintf( filename, "%s/%s", directory, LOCK_FILE ); status = stat( filename, &stat_data ); if( status != 0 ) { /* Touch the lock file. */ file_ref = fopen( filename, "w" ); if( file_ref == NULL ) return( XTM_DB_ERROR ); fclose( file_ref ); /* Let the file inherit the permissions from the directory. */ status = chmod( filename, file_mode ); if( status != 0 ) return( XTM_DB_ERROR ); } /* if */ /* Does the access file already exist? */ sprintf( filename, "%s/%s", directory, ACCESS_FILE ); status = stat( filename, &stat_data ); if( status != 0 ) { /* Touch the access file. */ file_ref = fopen( filename, "w" ); if( file_ref == NULL ) return( XTM_DB_ERROR ); fclose( file_ref ); /* Let the file inherit the permissions from the directory. */ status = chmod( filename, file_mode ); if( status != 0 ) return( XTM_DB_ERROR ); } /* if */ return( XTM_DB_OK ); } /* xtmDbInitializeAuxFiles */ /*----------------------------------------------------------------------*/ void xtmDbClearAccessCache() { /* Code. */ cache_valid = False; return; } /* xtmDbClearAccessCache */ /*----------------------------------------------------------------------*/ void xtmDbInitializeProcessId() { /* Variables. */ int status; /* Code. */ real_gid = getgid(); privileged_gid = getegid(); /* Go back to user level. */ status = setgid( getgid() ); return; } /* xtmDbInitializeProcessId */ /*----------------------------------------------------------------------*/ void xtmDbUseFileLock( Boolean use_file_lock ) { /* Code. */ use_lock = use_file_lock; return; } /* xtmDbUseFileLock */ /*----------------------------------------------------------------------*/ Boolean xtmDbDoesStandingMatch( XTM_DB_STAND_ENTRY_REF stand_ref, UINT32 flags, Boolean check_non_wday, TIM_TIME_REF date ) { /* Variables. */ Boolean action_non_wday = False; Boolean display_today = False; Boolean workday = True; int day_in_week; UINT32 stand_flags; TIM_DELTA_TYPE delta; TIM_TIME_REF today; /* Code. */ day_in_week = TimIndexOfDayInIsoWeek( date ); today = TimLocalTime( TimMakeTimeNow() ); today = TimMakeTime( TimIndexOfYear( today ), TimIndexOfMonth( today ), TimIndexOfDay( today ), 0, 0, 0 ); if( TimIsSameDate( today, date ) == TIM_YES ) display_today = True; workday = xtmHoIsWorkday( date ); stand_flags = stand_ref -> flags; if( check_non_wday ) action_non_wday= flagIsSet( stand_flags, XTM_DB_FLAG_SE_NWDAY_PREV | XTM_DB_FLAG_SE_NWDAY_NEXT | XTM_DB_FLAG_SE_NWDAY_SKIP ); /* Only for standing and sticky entries. */ if( flagIsClear( flags, XTM_DB_FETCH_STANDING ) && flagIsClear( flags, XTM_DB_FETCH_STICKY ) ) return( False ); /* Fetch all standing entries? */ if( flagIsSet( flags, XTM_DB_FETCH_ALL_STANDING ) ) return( True ); /* Sticky entries only valid today OR if from and to dates are defined, valid on the limit days. */ if( flagIsSet( stand_ref -> flags, XTM_DB_FLAG_SE_STICKY ) ) { if( display_today ) { if( stand_ref -> from == 0 && stand_ref -> to == 0 ) return( True ); if( (stand_ref -> from != 0 && date < stand_ref -> from) || (stand_ref -> to != 0 && date > stand_ref -> to) ) return( False ); else return( True ); } if( stand_ref -> from != 0 && stand_ref -> from > today && stand_ref -> from == date ) return( True ); else if( stand_ref -> to != 0 && stand_ref -> to < today && stand_ref -> from == date ) return( True ); else return( False ); } /* if */ /* Is the entry outside the defined range? */ if( (stand_ref -> from != 0 && date < stand_ref -> from) || (stand_ref -> to != 0 && date > stand_ref -> to) ) return( False ); /* Valid this week? */ { int week_no; UINT32 week_flags; UINT32 skip_flag; week_no = TimIndexOfIsoWeek( date ); skip_flag = (1 << (week_no % 30)); if( week_no > 30 ) week_flags = stand_ref -> skip_week[ 0 ]; else week_flags = stand_ref -> skip_week[ 1 ]; if( flagIsSet( week_flags, skip_flag ) ) return( False ); } /* block */ /* Check action to take on holidays. */ if( ! workday && action_non_wday ) return( False ); /* Special action for workdays? */ if( action_non_wday ) { TIM_TIME_REF check_date; /* Move to next workday. */ if( flagIsSet( stand_flags, XTM_DB_FLAG_SE_NWDAY_NEXT ) ) { check_date = date; TimAddDays( &check_date, -1 ); while( ! xtmHoIsWorkday( check_date ) ) { if( xtmDbDoesStandingMatch( stand_ref, flags, False, check_date ) ) return( True ); TimAddDays( &check_date, -1 ); } } /* if */ /* Move to previous workday. */ if( flagIsSet( stand_flags, XTM_DB_FLAG_SE_NWDAY_PREV ) ) { check_date = date; TimAddDays( &check_date, 1 ); while( ! xtmHoIsWorkday( check_date ) ) { if( xtmDbDoesStandingMatch( stand_ref, flags, False, check_date ) ) return( True ); TimAddDays( &check_date, 1 ); } } /* if */ } /* if */ /* Is this the nth week day in month? */ if( flagIsSet( stand_flags, XTM_DB_FLAG_SE_DAY_IN_MONTH ) ) { int day_index; int index; int offset = 1; int this_month; int weeks = 1; TIM_TIME_REF tmp_date; /* We check all valid days, might be more than one. */ for( day_index = 0; day_index < 7; day_index++ ) { /* Valid day? */ if( ! stand_ref -> valid_days[ day_index ] || (day_index + 1) != day_in_week ) continue; /* Correct day in month? */ if( flagIsSet( stand_flags, XTM_DB_FLAG_SE_1ST ) ) { offset = (-1); weeks = 1; } else if( flagIsSet( stand_flags, XTM_DB_FLAG_SE_2ND ) ) { offset = (-1); weeks = 2; } else if( flagIsSet( stand_flags, XTM_DB_FLAG_SE_3RD ) ) { offset = (-1); weeks = 3; } else if( flagIsSet( stand_flags, XTM_DB_FLAG_SE_1ST_LAST ) ) { offset = 1; weeks = 1; } else if( flagIsSet( stand_flags, XTM_DB_FLAG_SE_2ND_LAST ) ) { offset = 1; weeks = 2; } else if( flagIsSet( stand_flags, XTM_DB_FLAG_SE_3RD_LAST ) ) { offset = 1; weeks = 3; } else { return( False ); } tmp_date = date; this_month = TimIndexOfMonth( date ); for( index = 1; index <= weeks; index++ ) { TimAddDays( &tmp_date, offset * 7 ); if( TimIndexOfMonth( tmp_date ) != this_month ) break; } if( index == weeks ) return( True ); } /* loop */ return( False ); } /* if */ /* Check single valid days. */ if( stand_ref -> every_n == 0 && stand_ref -> valid_days[ day_in_week - 1 ] ) return( True ); /* Valid every n days? */ if( stand_ref -> valid_every == XTM_DB_VALID_DAY ) { TimDelta( stand_ref -> from, date, &delta ); if( delta.days % stand_ref -> every_n == 0 ) return( True ); else return( False ); } /* Valid every n weeks? */ if( stand_ref -> valid_every == XTM_DB_VALID_WEEK ) { TimDelta( stand_ref -> from, date, &delta ); if( delta.weeks % stand_ref -> every_n == 0 && delta.days % 7 == 0 ) return( True ); else return( False ); } /* if */ /* Valid every n months? */ if( stand_ref -> valid_every == XTM_DB_VALID_MONTH ) { int check_date; int check_month; int days_in_month; int today_date; int today_month; check_date = TimIndexOfDay( stand_ref -> from ); check_month = TimIndexOfMonth( stand_ref -> from ); days_in_month = TimDaysInMonth( date ); today_date = TimIndexOfDay( date ); today_month = TimIndexOfMonth( date ); if( abs( check_month - today_month ) % stand_ref -> every_n != 0 ) return( False ); if( check_date == today_date || (check_date > days_in_month && today_date == days_in_month) ) return( True ); else return( False ); } /* if */ /* Valid last in month? */ if( stand_ref -> valid_every == XTM_DB_VALID_MONTH_LAST ) { int check_month; int today_month; check_month = TimIndexOfMonth( stand_ref -> from ); today_month = TimIndexOfMonth( date ); if( abs( check_month - today_month ) % stand_ref -> every_n != 0 ) return( False ); if( TimIndexOfDay( date ) == TimDaysInMonth( date ) ) return( True ); return( False ); } /* if */ /* Valid every n years? */ if( stand_ref -> valid_every == XTM_DB_VALID_YEAR ) { int check_date; int check_month; int days_in_month; int today_date; int today_month; check_date = TimIndexOfDay( stand_ref -> from ); check_month = TimIndexOfMonth( stand_ref -> from ); today_date = TimIndexOfDay( date ); today_month = TimIndexOfMonth( date ); days_in_month = TimDaysInMonth( date ); if( abs( TimIndexOfYear( stand_ref -> from ) - TimIndexOfYear( date ) ) % stand_ref -> every_n != 0 ) return( False ); if( check_month == today_month ) { if( check_date == today_date || (check_date > days_in_month && check_date == days_in_month) ) return( True ); else return( False ); } } /* if */ return( False ); } /* xtmDbDoesStandingMatch */ /*----------------------------------------------------------------------*/ Boolean xtmDbIsEntryDefined( XTM_DB_ENTRY_DATABASES *database, LST_DESC_TYPE *stand_entries, TIM_TIME_REF cal_date ) { /* Variables. */ int flags; int index; LST_DESC_TYPE entries; LST_STATUS lst_status; XTM_DB_ALL_ENTRY_DEF entry_record; XTM_DB_DATE_DEF date_record; XTM_DB_STATUS db_status; /* Code. */ cal_date = TimMakeTime( TimIndexOfYear( cal_date ), TimIndexOfMonth( cal_date ), TimIndexOfDay( cal_date ), 0, 0, 0 ); /* Any normal entries defined this day? */ db_status = fetchDate( database -> date_db, cal_date, &date_record ); if( db_status == XTM_DB_OK ) return( True ); /* Check standing entries? */ if( stand_entries == NULL || (*stand_entries == NULL && *(stand_entries + 1) == NULL) ) return( False ); flags = (XTM_DB_FETCH_STICKY | XTM_DB_FETCH_STANDING); /* Standing entries are notes and appointments. */ for( index = 0; index < 2; index++ ) { entries = *(stand_entries + index); /* Any entries defined? */ if( entries == NULL || LstLinkElements( entries ) <= 0 ) continue; /* Check the entries in the list. */ lst_status = LstLinkCurrentFirst( entries ); while( lst_status == LST_OK ) { lst_status = LstLinkGetCurrent( entries, &entry_record ); /* Do we have a match? */ if( flagIsClear( entry_record.stand_entry.flags, XTM_DB_FLAG_SE_HIDE_IN_CALENDAR ) ) { if( xtmDbDoesStandingMatch( &entry_record.stand_entry, flags, True, cal_date ) ) return( True ); } /* if */ /* Next entry. */ lst_status = LstLinkCurrentNext( entries ); } /* while */ } /* loop */ return( False ); } /* xtmDbIsEntryDefined */ /*----------------------------------------------------------------------*/ Boolean xtmToIsStandEntryDefined( XTM_GL_BASE_DATA_REF appl_data_ref, LST_DESC_TYPE *stand_entries, TIM_TIME_REF cal_date ) { /* Variables. */ int flags; int index; LST_DESC_TYPE entries; LST_STATUS lst_status; XTM_DB_ALL_ENTRY_DEF entry_record; /* Code. */ if( stand_entries == NULL ) return( False ); flags = (XTM_DB_FETCH_STICKY | XTM_DB_FETCH_STANDING); /* Standing entries are notes and appointments. */ for( index = 0; index < 2; index++ ) { entries = *(stand_entries + index); /* Any entries defined? */ if( entries == NULL || LstLinkElements( entries ) <= 0 ) continue; /* Check the entries in the list. */ lst_status = LstLinkCurrentFirst( entries ); while( lst_status == LST_OK ) { lst_status = LstLinkGetCurrent( entries, &entry_record ); /* Do we have a match? */ if( xtmDbDoesStandingMatch( &entry_record.stand_entry, flags, True, cal_date ) ) return( True ); /* Next entry. */ lst_status = LstLinkCurrentNext( entries ); } /* while */ } /* loop */ return( False ); } /* xtmToIsStandEntryDefined */ /*----------------------------------------------------------------------*/ XTM_DB_STATUS xtmDbCloseEntryDb( XTM_DB_ENTRY_DATABASES *entry_databases ) { /* Variables. */ int status; /* Code. */ /* Close entry database. */ if( entry_databases -> entry_db != NULL ) closeDatabase( entry_databases -> entry_db ); /* Close date database. */ if( entry_databases -> date_db != NULL ) closeDatabase( entry_databases -> date_db ); /* Close standing entries database. */ if( entry_databases -> stand_entry_db != NULL ) closeDatabase( entry_databases -> stand_entry_db ); /* Close private entries database. */ if( entry_databases -> private_db != NULL ) closeDatabase( entry_databases -> private_db ); /* Is the database locked? */ if( entry_databases -> locked && use_lock ) freeFileLock( &entry_databases -> lock_fd ); entry_databases -> locked = False; /* Go to user mode? */ if( entry_databases -> set_id ) status = setgid( real_gid ); return( XTM_DB_OK ); } /* xtmDbCloseEntryDb */ /*----------------------------------------------------------------------*/ XTM_DB_STATUS xtmDbOpenEntryDb( XTM_DB_OPEN_REQUEST *open_request, XTM_DB_ENTRY_DATABASES *entry_databases ) { /* Variables. */ int db_dir_size; int status; UINT32 operations; char filename[ 150 ]; XTM_DB_OPEN_REQUEST single_open_req; XTM_DB_STATUS db_status; /* Code. */ strcpy( entry_databases -> name, open_request -> name ); /* Check the operations that can be done. */ xtmDbCheckDbOperations( open_request -> directory, False, &operations ); single_open_req.directory = open_request -> directory; single_open_req.lock_timeout = open_request -> lock_timeout; single_open_req.operations = open_request -> operations; /* Go to privileged mode? */ if( flagIsSet( operations, XTM_DB_FLAG_MODE_SETID ) ) status = setgid( privileged_gid ); /* Get a file lock. */ entry_databases -> locked = False; sprintf( filename, "%s/%s", open_request -> directory, LOCK_FILE ); db_status = createFileLock( filename, open_request -> operations, &entry_databases -> lock_fd, &open_request -> uid_locking ); if( db_status != XTM_DB_OK ) return( db_status ); entry_databases -> locked = True; /* Open the entry database. */ single_open_req.database = XTM_DB_ENTRY_DB; entry_databases -> entry_db = openDatabase( &single_open_req ); if( entry_databases -> entry_db == NULL ) return( XTM_DB_ERROR ); /* Open the date database. */ single_open_req.database = XTM_DB_DATE_DB; entry_databases -> date_db = openDatabase( &single_open_req ); if( entry_databases -> date_db == NULL ) return( XTM_DB_ERROR ); /* Open the standing entries database. */ single_open_req.database = XTM_DB_STAND_ENTRY_DB; entry_databases -> stand_entry_db = openDatabase( &single_open_req ); if( entry_databases -> stand_entry_db == NULL ) return( XTM_DB_ERROR ); /* We don't open the private database (only if needed later). */ entry_databases -> private_db = NULL; /* Save the database directory. */ db_dir_size = sizeof( entry_databases -> database_dir ); strncpy( entry_databases -> database_dir, open_request -> directory, db_dir_size - 1 ); entry_databases -> database_dir[ db_dir_size - 1 ] = '\0'; /* Operations allowed? */ entry_databases -> operations = operations; /* Privileged mode? */ if( flagIsSet( operations, XTM_DB_FLAG_MODE_SETID ) ) entry_databases -> set_id = True; else entry_databases -> set_id = False; return( XTM_DB_OK ); } /* xtmDbOpenEntryDb */ /*----------------------------------------------------------------------*/ XTM_DB_STATUS xtmDbDeleteEntry( XTM_DB_ENTRY_DATABASES *databases, UINT32 id ) { /* Variables. */ XTM_DB_STATUS status; XTM_DB_ALL_ENTRY_DEF entry_record; /* Code. */ /* All necessary databases must be open. */ if( databases -> entry_db == NULL || databases -> date_db == NULL || databases -> stand_entry_db == NULL ) return( XTM_DB_ERROR ); /* Fetch the entry record (we need some data there). */ status = fetchEntry( databases -> entry_db, id, &entry_record.entry ); if( status != XTM_DB_OK ) return( status ); /* Delete entry in the private database? */ (void) deletePrivData( databases, &entry_record ); /* If an external file is used, delete it. */ status = deleteTextFile( databases -> database_dir, id ); /* Delete the entry record. */ status = deleteEntry( databases -> entry_db, id ); if( status != XTM_DB_OK ) return( status ); /* If this is a normal entry, delete the date reference. */ if( entry_record.entry.entry_category == XTM_DB_ENTRY_LIST ) { status = deleteEntryIdInDate( databases -> date_db, id, entry_record.entry.date_stamp ); if( status != XTM_DB_OK ) return( status ); } /* if */ /* Delete the standing entry record? */ if( entry_record.entry.entry_category == XTM_DB_REP_ENTRY_LIST || entry_record.entry.entry_category == XTM_DB_STICKY_LIST ) { status = deleteStandEntry( databases -> stand_entry_db, id ); if( status != XTM_DB_OK ) return( status ); } /* if */ /* Update the log file. */ updateDbLog( databases -> database_dir, id, XTM_DB_FLAG_LOG_DELETE ); return( XTM_DB_OK ); } /* xtmDbDeleteEntry */ /*----------------------------------------------------------------------*/ XTM_DB_STATUS xtmDbFetchEntry( XTM_DB_ENTRY_DATABASES *databases, UINT32 id, XTM_DB_ALL_ENTRY_REF entry_ref, char **text_ref ) { /* Variables. */ int index; XTM_DB_STATUS status; /* Code. */ entry_ref -> all_text = NULL; strcpy( entry_ref -> db_name, databases -> name ); /* Fetch the entry. */ status = fetchEntry( databases -> entry_db, id, &entry_ref -> entry ); if( status != XTM_DB_OK ) return( status ); /* Reset the standing part. */ entry_ref -> stand_entry.id = 0; entry_ref -> stand_entry.from = 0; entry_ref -> stand_entry.to = 0; entry_ref -> stand_entry.every_n = 0; entry_ref -> stand_entry.valid_every = 0; entry_ref -> stand_entry.skip_week[ 0 ] = 0; entry_ref -> stand_entry.skip_week[ 1 ] = 0; for( index = 0; index < 7; index++ ) entry_ref -> stand_entry.valid_days[ index ] = 0; /* If this is a standing or sticky entry, fetch the extra info. */ if( entry_ref -> entry.entry_category == XTM_DB_REP_ENTRY_LIST || entry_ref -> entry.entry_category == XTM_DB_STICKY_LIST ) { status = fetchStandEntry( databases -> stand_entry_db, id, &entry_ref -> stand_entry ); if( status != XTM_DB_OK ) return( status ); } /* if */ /* Is the entry in the private area? */ if( flagIsSet( entry_ref -> entry.flags, XTM_DB_FLAG_IN_PRIV_DB ) ) { status = fetchPrivData( databases, id, entry_ref, text_ref ); if( status == XTM_DB_OK ) return( XTM_DB_OK ); } /* if */ /* Do we want to fetch the text separate? */ if( text_ref == NULL ) return( XTM_DB_OK ); /* Fetch text in an external file? */ *text_ref = NULL; if( flagIsSet( entry_ref -> entry.flags, XTM_DB_FLAG_EXT_FILE ) ) { status = fetchTextFile( databases -> database_dir, id, text_ref ); if( status != XTM_DB_OK ) return( status ); } /* if */ return( XTM_DB_OK ); } /* xtmDbFetchEntry */ /*----------------------------------------------------------------------*/ XTM_DB_STATUS xtmDbInsertEntry( XTM_DB_ENTRY_DATABASES *databases, XTM_DB_ALL_ENTRY_REF entry_ref, char *text_ref ) { /* Variables. */ XTM_DB_STATUS status; /* Code. */ status = doInsertEntry( databases, entry_ref, text_ref, XTM_DB_FLAG_LOG_SAVE ); return( status ); } /* xtmDbInsertEntry */ /*----------------------------------------------------------------------*/ XTM_DB_STATUS xtmDbFetchDates( XTM_DB_ENTRY_DATABASES *databases, LST_DESC_TYPE *list_ref ) { /* Variables. */ LST_STATUS lst_status; XTM_DB_STATUS status; XTM_DB_DATE_DEF record; /* Code. */ /* Create a linked list to containg the dates. */ *list_ref = LstLinkNew( sizeof( record ), NULL ); /* Fetch the first key in the database. */ status = fetchFirstDate( databases -> date_db, &record ); while( status == XTM_DB_OK ) { /* Insert the date in the sorted list. */ lst_status = LstLinkSearchFirst( *list_ref, (void *)(uintptr_t)record.date, (EQUALS_FUNC_TYPE) dateSortFunc ); if( lst_status == LST_OK ) lst_status = LstLinkInsertCurrent( *list_ref, &record ); else lst_status = LstLinkInsertLast( *list_ref, &record ); /* The next date. */ status = fetchNextDate( databases -> date_db, &record ); } /* loop */ return( XTM_DB_OK ); } /* xtmDbFetchDates */ /*----------------------------------------------------------------------*/ XTM_DB_STATUS xtmDbFetchEntriesInDay( XTM_DB_ENTRY_DATABASES *databases, TIM_TIME_REF date, UINT32 flags, LST_DESC_TYPE *entry_list_ref, LST_DESC_TYPE *note_list_ref ) { /* Variables. */ int index; char *text_ref; LST_STATUS lst_status; XTM_DB_STATUS status; XTM_DB_DATE_DEF date_record; XTM_DB_ALL_ENTRY_DEF entry_record; XTM_DB_STAND_ENTRY_DEF stand_record; /* Code. */ /* Create a linked list to containg the entries. */ if( flagIsClear( flags, XTM_DB_FETCH_NO_NEW_LIST ) ) { *entry_list_ref = LstLinkNew( sizeof( XTM_DB_ALL_ENTRY_DEF ), NULL ); *note_list_ref = LstLinkNew( sizeof( XTM_DB_ALL_ENTRY_DEF ), NULL ); } /* Fetch the entry id defined for this day. */ status = fetchDate( databases -> date_db, date, &date_record ); /* Fetch all entries defined this day. */ if( status == XTM_DB_OK ) { for( index = 0; index < XTM_DB_DATE_ID_SIZE; index++ ) { /* Do we have an entry? */ if( date_record.id[ index ] != 0 ) { /* Fetch the entry. */ if( flagIsSet( flags, XTM_DB_FETCH_ALL_TEXT ) ) { status = xtmDbFetchEntry( databases, date_record.id[ index ], &entry_record, &text_ref ); entry_record.all_text = text_ref; } else { status = xtmDbFetchEntry( databases, date_record.id[ index ], &entry_record, NULL ); } if( status != XTM_DB_OK ) continue; /* Is this a merged entry? */ if( flagIsSet( flags, XTM_DB_FETCH_INCLUDE ) ) flagSet( entry_record.entry.flags, XTM_DB_FLAG_INCLUDE ); /* Insert in the appointment list or in the note list. */ switch( entry_record.entry.entry_type ) { case XTM_DB_DAY_NOTE: lst_status = LstLinkSearchFirst( *note_list_ref, (void *)(uintptr_t)entry_record.entry.time_stamp, (EQUALS_FUNC_TYPE) entryIdSortFunc ); if( lst_status == LST_OK ) lst_status = LstLinkInsertCurrent( *note_list_ref, &entry_record ); else lst_status = LstLinkInsertLast( *note_list_ref, &entry_record ); break; case XTM_DB_DAY_ENTRY: lst_status = LstLinkSearchFirst( *entry_list_ref, (void *)(uintptr_t)entry_record.entry.time_stamp, (EQUALS_FUNC_TYPE) entryTimeSortFunc ); if( lst_status == LST_OK ) lst_status = LstLinkInsertCurrent( *entry_list_ref, &entry_record ); else lst_status = LstLinkInsertLast( *entry_list_ref, &entry_record ); break; default: fprintf( stderr, "xtmDbTools: xdfe() Unknown entry type %d\n", entry_record.entry.entry_type ); return( XTM_DB_ERROR ); } /* switch */ } /* if */ } /* loop */ } /* if */ /* Fetch all standing and sticky entries this date? */ if( flagIsClear(flags, XTM_DB_FETCH_STANDING ) && flagIsClear(flags, XTM_DB_FETCH_STICKY ) ) return( XTM_DB_OK ); status = fetchFirstStand( databases -> stand_entry_db, &stand_record ); while( status == XTM_DB_OK ) { Boolean match; /* Code. */ /* Does the standing entry fit into the current date? */ match = xtmDbDoesStandingMatch( &stand_record, flags, True, date ); /* Did we have a match? */ if( match ) { /* Fetch the entry record. */ if( flagIsSet( flags, XTM_DB_FETCH_ALL_TEXT ) ) { status = xtmDbFetchEntry( databases, stand_record.id, &entry_record, &text_ref ); entry_record.all_text = text_ref; } else { status = xtmDbFetchEntry( databases, stand_record.id, &entry_record, NULL ); } if( status != XTM_DB_OK ) return( status ); /* Copy the standing entry information. */ memcpy( &entry_record.stand_entry, &stand_record, sizeof( XTM_DB_STAND_ENTRY_DEF ) ); /* Is this a merged entry? */ if( flagIsSet( flags, XTM_DB_FETCH_INCLUDE ) ) flagSet( entry_record.entry.flags, XTM_DB_FLAG_INCLUDE ); /* Insert in the appointment list or in the note list. */ switch( entry_record.entry.entry_type ) { case XTM_DB_DAY_NOTE: lst_status = LstLinkSearchFirst( *note_list_ref, (void *)(uintptr_t)entry_record.entry.time_stamp, (EQUALS_FUNC_TYPE) entryIdSortFunc ); if( lst_status == LST_OK ) lst_status = LstLinkInsertCurrent( *note_list_ref, &entry_record ); else lst_status = LstLinkInsertLast( *note_list_ref, &entry_record ); break; case XTM_DB_DAY_ENTRY: lst_status = LstLinkSearchFirst( *entry_list_ref, (void *)(uintptr_t)entry_record.entry.time_stamp, (EQUALS_FUNC_TYPE) entryTimeSortFunc ); if( lst_status == LST_OK ) lst_status = LstLinkInsertCurrent( *entry_list_ref, &entry_record ); else lst_status = LstLinkInsertLast( *entry_list_ref, &entry_record ); break; default: fprintf( stderr, "xtmDbTools: xdfe() Unknown stand entry type %d\n", entry_record.entry.entry_type ); return( XTM_DB_ERROR ); } /* switch */ } /* if */ /* Next standing entry. */ status = fetchNextStand( databases -> stand_entry_db, &stand_record ); } /* while */ return( XTM_DB_OK ); } /* xtmDbFetchEntriesInDay */ /*----------------------------------------------------------------------*/ XTM_DB_STATUS xtmDbFetchStandEntries( XTM_DB_ENTRY_DATABASES *databases, LST_DESC_TYPE *entry_list_ref, LST_DESC_TYPE *note_list_ref ) { /* Variables. */ LST_STATUS lst_status; XTM_DB_STATUS status; XTM_DB_ALL_ENTRY_DEF entry_record; XTM_DB_STAND_ENTRY_DEF stand_record; /* Code. */ /* Create a linked list to containg the entries. */ *entry_list_ref = LstLinkNew( sizeof( XTM_DB_ALL_ENTRY_DEF ), NULL ); *note_list_ref = LstLinkNew( sizeof( XTM_DB_ALL_ENTRY_DEF ), NULL ); /* Fetch all standing entries. */ status = fetchFirstStand( databases -> stand_entry_db, &stand_record ); while( status == XTM_DB_OK ) { /* Fetch the entry record. */ status = fetchEntry( databases -> entry_db, stand_record.id, &entry_record.entry ); if( status != XTM_DB_OK ) return( status ); /* Copy the standing entry information. */ memcpy( &entry_record.stand_entry, &stand_record, sizeof( XTM_DB_STAND_ENTRY_DEF ) ); /* Insert in the appointment list or in the note list. */ switch( entry_record.entry.entry_type ) { case XTM_DB_DAY_NOTE: lst_status = LstLinkSearchFirst( *note_list_ref, (void *)(uintptr_t)entry_record.entry.time_stamp, (EQUALS_FUNC_TYPE) entryIdSortFunc ); if( lst_status == LST_OK ) lst_status = LstLinkInsertCurrent( *note_list_ref, &entry_record ); else lst_status = LstLinkInsertLast( *note_list_ref, &entry_record ); break; case XTM_DB_DAY_ENTRY: lst_status = LstLinkSearchFirst( *entry_list_ref, (void *)(uintptr_t)entry_record.entry.time_stamp, (EQUALS_FUNC_TYPE) entryTimeSortFunc ); if( lst_status == LST_OK ) lst_status = LstLinkInsertCurrent( *entry_list_ref, &entry_record ); else lst_status = LstLinkInsertLast( *entry_list_ref, &entry_record ); break; default: fprintf( stderr, "xtmDbTools: xdfse() Unknown entry type %d\n", entry_record.entry.entry_type ); return( XTM_DB_ERROR ); } /* switch */ /* Next standing entry. */ status = fetchNextStand( databases -> stand_entry_db, &stand_record ); } /* while */ return( XTM_DB_OK ); } /* xtmDbFetchStandEntries */ /*----------------------------------------------------------------------*/ XTM_DB_STATUS xtmDbDeleteMessage( char *db_dir, UINT32 msg_id ) { /* Variables. */ int status; char filename[ 200 ]; char message_dir[ 100 ]; struct stat file_info; /* Code. */ /* Do we have a message directory? */ sprintf( message_dir, "%s/Message", db_dir ); status = stat( message_dir, &file_info ); if( status != 0 ) sprintf( message_dir, "%s", db_dir ); /* Search the message file. */ sprintf( filename, "%s/%s_*_%d", message_dir, XTM_DB_MESSAGE_FILE, msg_id ); status = DirFindFirst( filename, filename ); if( status != 0 ) return( XTM_DB_ERROR ); DirEnd(); /* Try to delete the file and ignore all errors. */ status = unlink( filename ); return( XTM_DB_OK ); } /* xtmDbDeleteMessage */ /*----------------------------------------------------------------------*/ XTM_DB_STATUS xtmDbFetchMessage( char *db_dir, UINT32 msg_id, XTM_DB_MESSAGE_REF msg_info, char **message, char **text ) { /* Variables. */ int file_ref; int position; int status; char filename[ 200 ]; char message_dir[ 100 ]; char *char_ref; struct stat file_info; /* Code. */ *message = NULL; *text = NULL; /* Do we have a message directory? */ sprintf( message_dir, "%s/Message", db_dir ); status = stat( message_dir, &file_info ); if( status != 0 ) sprintf( message_dir, "%s", db_dir ); /* Search the message file. */ sprintf( filename, "%s/%s_*_%d", message_dir, XTM_DB_MESSAGE_FILE, msg_id ); status = DirFindFirst( filename, filename ); if( status != 0 ) return( XTM_DB_ERROR ); DirEnd(); /* Open the file for read. */ file_ref = open( filename, O_RDONLY ); if( file_ref == -1 ) return( XTM_DB_ERROR ); /* Read the message record. */ lseek( file_ref, 0, SEEK_SET ); read( file_ref, msg_info, sizeof( XTM_DB_MESSAGE_DEF ) ); /* Read the message string. */ if( msg_info -> message_length > 0 ) { position = sizeof( XTM_DB_MESSAGE_DEF ); lseek( file_ref, position, SEEK_SET ); char_ref = SysMalloc( msg_info -> message_length + 1 ); *(char_ref + msg_info -> message_length) = '\0'; read( file_ref, char_ref, msg_info -> message_length ); *message = char_ref; } /* Read the text string. */ if( msg_info -> text_length > 0 ) { position = sizeof( XTM_DB_MESSAGE_DEF ) + msg_info -> message_length; lseek( file_ref, position, SEEK_SET ); char_ref = SysMalloc( msg_info -> text_length + 1 ); *(char_ref + msg_info -> text_length) = '\0'; read( file_ref, char_ref, msg_info -> text_length ); *text = char_ref; } close( file_ref ); return( XTM_DB_OK ); } /* xtmDbFetchMessage */ /*----------------------------------------------------------------------*/ XTM_DB_STATUS xtmDbInsertMessage( char *db_dir, XTM_DB_MESSAGE_REF msg_info, char *message, char *text ) { /* Variables. */ Boolean back_to_normal = False; int file_mode; int file_ref; int status; UINT32 new_id; UINT32 id; UINT32 operations; char message_dir[ 100 ]; char filename[ 200 ]; char file_pattern[ 150 ]; struct stat file_info; /* Code. */ /* Do we have a message directory? */ sprintf( message_dir, "%s/Message", db_dir ); status = stat( message_dir, &file_info ); if( status != 0 ) sprintf( message_dir, "%s", db_dir ); /* Fetch id to use. */ sprintf( file_pattern, "%s/%s_*_*", message_dir, XTM_DB_MESSAGE_FILE ); new_id = 1; status = DirFindFirst( file_pattern, filename ); while( status == 0 ) { int items; char *char_ref; /* Filter out the id of the file. */ char_ref = strrchr( filename, '_' ); if( char_ref == NULL ) return( XTM_DB_ERROR ); char_ref++; items = sscanf( char_ref, "%d", &id ); if( items != 1 ) { DirEnd(); return( XTM_DB_ERROR ); } if( id > new_id ) new_id = id; /* Next Message. */ status = DirFindNext( filename ); } /* while */ new_id++; /* Check the operations that can be done. */ xtmDbCheckDbOperations( db_dir, False, &operations ); /* Go to privileged mode? */ if( flagIsSet( operations, XTM_DB_FLAG_MODE_SETID ) && getegid() != privileged_gid ) { status = setgid( privileged_gid ); back_to_normal = True; } /* Fetch the directory data and define the mode for files. */ sprintf( filename, "%s", message_dir ); status = stat( filename, &file_info ); if( status != 0 ) raise exception; file_mode = file_info.st_mode & (~(S_IXUSR | S_IXGRP | S_IXOTH)); file_mode = (file_mode | S_IROTH); /* Open the file for write. */ sprintf( filename, "%s/%s_%s_%d", message_dir, XTM_DB_MESSAGE_FILE, msg_info -> from, new_id ); file_ref = open( filename, (O_CREAT | O_RDWR) ); if( file_ref == -1 ) raise exception; /* Write the message record. */ write( file_ref, msg_info, sizeof( XTM_DB_MESSAGE_DEF ) ); /* Write the message string. */ write( file_ref, message, msg_info -> message_length ); /* Write the message text. */ write( file_ref, text, msg_info -> text_length ); close( file_ref ); /* Let the file inherit the permissions from the directory. */ status = chmod( filename, file_mode ); if( status != 0 ) raise exception; /* Back to normal uid? */ if( back_to_normal ) status = setgid( real_gid ); return( XTM_DB_OK ); exception: if( back_to_normal ) status = setgid( real_gid ); return( XTM_DB_ERROR ); } /* xtmDbInsertMessage */ /*----------------------------------------------------------------------*/ static INT32 calcCrc( char *text ) { /* Variables. */ UINT32 hash = 0; UINT32 mask = 0; char *char_ref; /* Code. */ if( text == NULL ) return( 0 ); char_ref = text; while( *char_ref != '\0' ) { hash = (hash << 4) + (*char_ref); if( (mask = hash & 0xf0000000) ) { hash = hash ^ (mask >> 24); hash = hash ^ mask; } char_ref++; } return( hash % 211 ); } /* calcCrc */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS closeDatabase( XTM_DB_DATABASE database ) { /* Code. */ dbm_close( (DBM *) database ); return( XTM_DB_OK ); } /* closeDatabase */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS createFileLock( char *filename, UINT32 operations, int *lock_fd, int *uid_locking ) { /* Variables. */ int lock_type; int file_mode; int status; int tries; struct flock lock; /* Code. */ *uid_locking = 0; *lock_fd = -1; if( ! use_lock ) return( XTM_DB_OK ); /* Type of lock? */ file_mode = O_RDONLY; lock_type = F_RDLCK; if( flagIsSet( operations, XTM_DB_FLAG_MODE_READ ) ) { file_mode = O_RDONLY; lock_type = F_RDLCK; } if( flagIsSet( operations, XTM_DB_FLAG_MODE_WRITE ) ) { file_mode = O_RDWR; lock_type = F_WRLCK; } /* Fetch file descriptor for lock file. */ *lock_fd = open( filename, file_mode ); if( *lock_fd < 0 ) return( XTM_DB_LOCKED ); /* Lock the file. */ lock.l_type = lock_type; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; tries = 0; while( tries < MAX_LOCK_TRIES ) { status = fcntl( *lock_fd, F_SETLK, &lock ); if( status != -1 ) break; sleep( 1 ); tries++; } /* If we could not lock the file, return with error. */ if( status == -1 ) { /* Who has the lock? */ lock.l_type = lock_type; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; status = fcntl( *lock_fd, F_GETLK, &lock ); close( *lock_fd ); *uid_locking = lock.l_pid; *lock_fd = -1; return( XTM_DB_LOCKED ); } /* if */ return( XTM_DB_OK ); } /* createFileLock */ /*----------------------------------------------------------------------*/ static LST_COMPARE dateSortFunc( XTM_DB_DATE_DEF *element, TIM_TIME_REF date ) { /* Code. */ if( element -> date > date ) return( LST_EQUAL ); else return( LST_NOT_EQUAL ); } /* dateSortFunc */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS deleteDate( XTM_DB_DATABASE database, TIM_TIME_REF date ) { /* Variables. */ int status; datum key; XTM_DB_DATE_KEY key_record; /* Code. */ if( database == NULL ) return( XTM_DB_ERROR ); #ifdef NET_BYTE_ORDER key_record.date = htonl( date ); #else key_record.date = date; #endif /* Create the key for the entry. */ key.dptr = (char *) &key_record; key.dsize = xtm_db_date_key_size; /* Delete the entry. */ status = dbm_delete( (DBM *) database, key ); if( status != 0 ) return( XTM_DB_ERROR ); return( XTM_DB_OK ); } /* deleteDate */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS deleteEntry( XTM_DB_DATABASE database, UINT32 id ) { /* Variables. */ int status; datum key; XTM_DB_ENTRY_KEY key_record; /* Code. */ if( database == NULL ) return( XTM_DB_ERROR ); /* Create the key for the entry. */ #ifdef NET_BYTE_ORDER key_record.id = htonl( id ); #else key_record.id = id; #endif key.dptr = (char *) &key_record; key.dsize = xtm_db_entry_key_size; /* Delete the entry. */ status = dbm_delete( (DBM *) database, key ); if( status != 0 ) return( XTM_DB_ERROR ); return( XTM_DB_OK ); } /* deleteEntry */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS deleteEntryIdInDate( XTM_DB_DATABASE database_ref, UINT32 entry_id, TIM_TIME_REF date_stamp ) { /* Variables. */ int id_found; int entry_index; int index; XTM_DB_DATE_DEF date_record; XTM_DB_STATUS status; /* Code. */ /* Try to fetch the date record. */ status = fetchDate( database_ref, date_stamp, &date_record ); if( status != XTM_DB_OK ) return( status ); /* Do we have the entry id in the date record. */ id_found = 0; entry_index = -1; for( index = 0; index < XTM_DB_DATE_ID_SIZE; index++ ) { if( date_record.id[ index ] != 0 ) id_found++; if( date_record.id[ index ] == entry_id ) entry_index = index; } if( entry_index == -1 ) return( XTM_DB_ERROR ); /* Entry is not saved anymore. */ date_record.id[ entry_index ] = 0; /* Any entries left? */ if( id_found <= 1 ) status = deleteDate( database_ref, date_stamp ); else status = insertDate( database_ref, &date_record ); if( status != XTM_DB_OK ) return( status ); return( XTM_DB_OK ); } /* deleteEntryIdInDate */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS deletePrivData( XTM_DB_ENTRY_DATABASES *databases, XTM_DB_ALL_ENTRY_REF entry_ref ) { /* Variables. */ XTM_DB_STATUS status; /* Code. */ if( flagIsClear( entry_ref -> entry.flags, XTM_DB_FLAG_IN_PRIV_DB ) ) return( XTM_DB_ERROR ); /* Is the data in an external file? */ if( flagIsSet( entry_ref -> entry.flags, XTM_DB_FLAG_PRIV_EXT_FILE ) ) status = deletePrivText( databases, entry_ref -> entry.id ); else status = deletePrivDb( databases, entry_ref -> entry.id ); if( status != XTM_DB_OK ) return( status ); return( XTM_DB_OK ); } /* deletePrivData */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS deletePrivDb( XTM_DB_ENTRY_DATABASES *databases, UINT32 id ) { /* Variables. */ int status; datum key; XTM_DB_OPEN_REQUEST single_open_req; XTM_DB_PRIV_KEY key_record; /* Code. */ /* Open the database for write. */ if( databases -> private_db == NULL ) { single_open_req.database = XTM_DB_PRIV_ENTRY_DB; single_open_req.directory = databases -> database_dir; single_open_req.lock_timeout = 0; if( flagIsSet( databases -> operations, XTM_DB_FLAG_MODE_WRITE ) ) single_open_req.operations = XTM_DB_FLAG_MODE_WRITE; else single_open_req.operations = XTM_DB_FLAG_MODE_READ; databases -> private_db = openDatabase( &single_open_req ); if( databases -> private_db == NULL ) return( XTM_DB_ERROR ); } /* Create the key for the entry. */ #ifdef NET_BYTE_ORDER key_record.id = htonl( id ); #else key_record.id = id; #endif key.dptr = (char *) &key_record; key.dsize = xtm_db_priv_entry_key_size; /* Delete the entry. */ status = dbm_delete( (DBM *) databases -> private_db, key ); if( status != 0 ) return( XTM_DB_ERROR ); return( XTM_DB_OK ); } /* deletePrivDb */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS deletePrivText( XTM_DB_ENTRY_DATABASES *databases, UINT32 id ) { /* Variables. */ int status; char filename[ 200 ]; /* Code. */ sprintf( filename, "%s/Private/%d.txt", databases -> database_dir, id ); /* Try to delete the file and ignore all errors. */ status = unlink( filename ); return( XTM_DB_OK ); } /* deletePrivText */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS deleteStandEntry( XTM_DB_DATABASE database, UINT32 id ) { /* Variables. */ int status; datum key; XTM_DB_STAND_ENTRY_KEY key_record; /* Code. */ if( database == NULL ) return( XTM_DB_ERROR ); /* Create the key for the entry. */ #ifdef NET_BYTE_ORDER key_record.id = htonl( id ); #else key_record.id = id; #endif key.dptr = (char *) &key_record; key.dsize = xtm_db_stand_entry_key_size; /* Delete the entry. */ status = dbm_delete( (DBM *) database, key ); if( status != 0 ) return( XTM_DB_ERROR ); return( XTM_DB_OK ); } /* deleteStandEntry */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS deleteTextFile( char *directory, UINT32 id ) { /* Variables. */ int status; char filename[ 200 ]; /* Code. */ sprintf( filename, "%s/%d.txt", directory, id ); /* Try to delete the file and ignore all errors. */ status = unlink( filename ); return( XTM_DB_OK ); } /* deleteTextFile */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS doInsertEntry( XTM_DB_ENTRY_DATABASES *databases, XTM_DB_ALL_ENTRY_REF entry_ref, char *text_ref, UINT32 log_flags ) { /* Variables. */ char *char_ref; XTM_DB_STATUS status; /* Code. */ /* All necessary databases must be open. */ if( databases -> entry_db == NULL || databases -> date_db == NULL || databases -> stand_entry_db == NULL ) return( XTM_DB_ERROR ); /* Delete any old file. */ if( flagIsSet( entry_ref -> entry.flags, XTM_DB_FLAG_EXT_FILE ) ) { (void) deleteTextFile( databases -> database_dir, entry_ref -> entry.id ); flagClear( entry_ref -> entry.flags, XTM_DB_FLAG_EXT_FILE ); } /* CRC for th entry. */ entry_ref -> entry.crc = calcCrc( text_ref ); /* Entry tag cannot contain spaces. */ char_ref = entry_ref -> entry.tag; while( isspace( *char_ref ) ) char_ref++; if( *char_ref == '\0' ) entry_ref -> entry.tag[ 0 ] = '\0'; /* No alarms for notes. */ if( entry_ref -> entry.entry_type == XTM_DB_DAY_NOTE ) flagClear( entry_ref -> entry.flags, XTM_DB_FLAG_ALARM ); /* Save text in record. */ if( text_ref != NULL ) { strncpy( entry_ref -> entry.text, text_ref, XTM_DB_RECORD_TEXT_LEN ); entry_ref -> entry.text[ XTM_DB_RECORD_TEXT_LEN ] = '\0'; } /* Is this a private entry? */ flagClear( entry_ref -> entry.flags, XTM_DB_FLAG_IN_PRIV_DB ); if( flagIsSet( entry_ref -> entry.flags, XTM_DB_FLAG_PRIVATE ) ) (void) insertPrivData( databases, entry_ref -> entry.id, entry_ref, text_ref ); /* Insert the long entry text? */ if( text_ref != NULL ) { /* Insert the text in a separate file? */ if( strlen( text_ref ) > XTM_DB_RECORD_TEXT_LEN ) { flagSet( entry_ref -> entry.flags, XTM_DB_FLAG_EXT_FILE ); status = insertTextFile( databases -> database_dir, entry_ref -> entry.id, text_ref ); if( status != XTM_DB_OK ) return( status ); } } /* if */ /* If a sticky note is done, make it a normal note. */ if( entry_ref -> entry.entry_category == XTM_DB_STICKY_LIST && flagIsSet( entry_ref -> entry.flags, XTM_DB_FLAG_NOTE_DONE ) ) entry_ref -> entry.entry_category = XTM_DB_ENTRY_LIST; /* Replace/create the new entry. */ status = insertEntry( databases -> entry_db, &entry_ref -> entry ); if( status != XTM_DB_OK ) return( status ); /* Delete any standing entry (will be re-created if necessary). */ status = deleteStandEntry( databases -> stand_entry_db, entry_ref -> entry.id ); /* Standing entry? */ if( entry_ref -> entry.entry_category == XTM_DB_REP_ENTRY_LIST ) { flagSet( entry_ref -> stand_entry.flags, XTM_DB_FLAG_SE_STANDING ); flagClear( entry_ref -> stand_entry.flags, XTM_DB_FLAG_SE_STICKY ); if( flagIsSet( entry_ref -> entry.flags, XTM_DB_FLAG_HIDE_IN_CALENDAR ) ) flagSet( entry_ref -> stand_entry.flags, XTM_DB_FLAG_SE_HIDE_IN_CALENDAR ); /* Create/replace the standing entry. */ status = insertStandEntry( databases -> stand_entry_db, &entry_ref -> stand_entry ); if( status != XTM_DB_OK ) return( status ); } /* if */ if( entry_ref -> entry.entry_category == XTM_DB_STICKY_LIST && flagIsClear( entry_ref -> entry.flags, XTM_DB_FLAG_NOTE_DONE ) ) { flagClear( entry_ref -> stand_entry.flags, XTM_DB_FLAG_SE_STANDING ); flagSet( entry_ref -> stand_entry.flags, XTM_DB_FLAG_SE_STICKY ); /* Create/replace the standing entry. */ status = insertStandEntry( databases -> stand_entry_db, &entry_ref -> stand_entry ); if( status != XTM_DB_OK ) return( status ); } /* if */ /* Is this a normal entry? */ if( entry_ref -> entry.entry_category == XTM_DB_ENTRY_LIST ) { status = insertEntryIdInDate( databases -> date_db, entry_ref -> entry.id, entry_ref -> entry.date_stamp ); if( status != XTM_DB_OK ) return( status ); } /* if */ /* Update the log file. */ updateDbLog( databases -> database_dir, entry_ref -> entry.id, log_flags ); return( XTM_DB_OK ); } /* doInsertEntry */ /*----------------------------------------------------------------------*/ static LST_COMPARE entryIdSortFunc( XTM_DB_ENTRY_DEF *element, UINT32 id ) { /* Code. */ if( element -> id > id ) return( LST_EQUAL ); else return( LST_NOT_EQUAL ); } /* entryIdSortFunc */ /*----------------------------------------------------------------------*/ static LST_COMPARE entryTimeSortFunc( XTM_DB_ENTRY_DEF *element, TIM_TIME_REF date ) { /* Code. */ if( element -> time_stamp > date ) return( LST_EQUAL ); else return( LST_NOT_EQUAL ); } /* entryTimeSortFunc */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS fetchDate( XTM_DB_DATABASE database, TIM_TIME_REF date, XTM_DB_DATE_REF entry ) { /* Variables. */ int index; datum key; datum contents; XTM_DB_DATE_KEY key_record; /* Code. */ if( database == NULL ) return( XTM_DB_ERROR ); /* Create the key for the entry. */ #ifdef NET_BYTE_ORDER key_record.date = htonl( date ); #else key_record.date = date; #endif key.dptr = (char *) &key_record; key.dsize = xtm_db_date_key_size; /* Fetch the entry. */ contents = dbm_fetch( (DBM *) database, key ); if( contents.dptr == NULL ) return( XTM_DB_ERROR ); /* Copy the contents. */ memcpy( entry, contents.dptr, xtm_db_date_def_size ); #ifdef NET_BYTE_ORDER entry -> date = ntohl( entry -> date ); for( index = 0; index < XTM_DB_DATE_ID_SIZE; index++ ) entry -> id[ index ] = ntohl( entry -> id[ index ] ); #endif return( XTM_DB_OK ); } /* fetchDate */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS fetchEntry( XTM_DB_DATABASE database, UINT32 id, XTM_DB_ENTRY_REF entry ) { /* Variables. */ int index; datum key; datum contents; XTM_DB_ENTRY_KEY key_record; /* Code. */ if( database == NULL ) return( XTM_DB_ERROR ); /* Create the key for the entry. */ #ifdef NET_BYTE_ORDER key_record.id = htonl( id ); #else key_record.id = id; #endif key.dptr = (char *) &key_record; key.dsize = xtm_db_entry_key_size; /* Fetch the entry. */ contents = dbm_fetch( (DBM *) database, key ); if( contents.dptr == NULL ) return( XTM_DB_ERROR ); /* Copy the contents. */ memcpy( entry, contents.dptr, xtm_db_entry_def_size ); #ifdef NET_BYTE_ORDER entry -> id = ntohl( entry -> id ); entry -> time_stamp = ntohl( entry -> time_stamp ); entry -> date_stamp = ntohl( entry -> date_stamp ); entry -> last_update = ntohl( entry -> last_update ); entry -> owner = ntohl( entry -> owner ); entry -> duration = ntohs( entry -> duration ); entry -> flags = ntohl( entry -> flags ); for( index = 0; index < 5; index++ ) entry -> alarm_offset[ index ] = ntohs( entry -> alarm_offset[ index ] ); entry -> crc = ntohl( entry -> crc); #endif return( XTM_DB_OK ); } /* fetchEntry */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS fetchFirstDate( XTM_DB_DATABASE database, XTM_DB_DATE_REF entry ) { /* Variables. */ datum contents; TIM_TIME_REF date; XTM_DB_STATUS status; /* Code. */ if( database == NULL ) return( XTM_DB_ERROR ); /* Fetch the entry. */ contents = dbm_firstkey( (DBM *) database ); if( contents.dptr == NULL ) return( XTM_DB_ERROR ); /* Fetch the data. */ memcpy( &date, contents.dptr, xtm_db_date_key_size ); #ifdef NET_BYTE_ORDER date = ntohl( date ); #endif status = fetchDate( database, date, entry ); return( status ); } /* fetchFirstDate */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS fetchFirstStand( XTM_DB_DATABASE database, XTM_DB_STAND_ENTRY_REF entry ) { /* Variables. */ int id; datum contents; XTM_DB_STATUS status; /* Code. */ if( database == NULL ) return( XTM_DB_ERROR ); /* Fetch the entry. */ contents = dbm_firstkey( (DBM *) database ); if( contents.dptr == NULL ) return( XTM_DB_ERROR ); /* Fetch the data. */ memcpy( &id, contents.dptr, xtm_db_stand_entry_key_size ); #ifdef NET_BYTE_ORDER id = ntohl( id ); #endif status = fetchStandEntry( database, id, entry ); return( status ); } /* fetchFirstStand */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS fetchNextDate( XTM_DB_DATABASE database, XTM_DB_DATE_REF entry ) { /* Variables. */ datum contents; TIM_TIME_REF date; XTM_DB_STATUS status; /* Code. */ if( database == NULL ) return( XTM_DB_ERROR ); /* Fetch the entry. */ contents = dbm_nextkey( (DBM *) database ); if( contents.dptr == NULL ) return( XTM_DB_ERROR ); /* Fetch the data. */ memcpy( &date, contents.dptr, xtm_db_date_key_size ); #ifdef NET_BYTE_ORDER date = ntohl( date ); #endif status = fetchDate( database, date, entry ); return( status ); } /* fetchNextDate */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS fetchNextStand( XTM_DB_DATABASE database, XTM_DB_STAND_ENTRY_REF entry ) { /* Variables. */ int id; datum contents; XTM_DB_STATUS status; /* Code. */ if( database == NULL ) return( XTM_DB_ERROR ); /* Fetch the entry. */ contents = dbm_nextkey( (DBM *) database ); if( contents.dptr == NULL ) return( XTM_DB_ERROR ); /* Fetch the data. */ memcpy( &id, contents.dptr, xtm_db_stand_entry_key_size ); #ifdef NET_BYTE_ORDER id = ntohl( id ); #endif status = fetchStandEntry( database, id, entry ); return( XTM_DB_OK ); } /* fetchNextStand */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS fetchPrivData( XTM_DB_ENTRY_DATABASES *databases, UINT32 id, XTM_DB_ALL_ENTRY_REF entry_ref, char **text_ref ) { /* Variables. */ char *db_text; XTM_DB_STATUS status; /* Code. */ /* Default private text. */ strcpy( entry_ref -> entry.text, "" ); if( flagIsClear( entry_ref -> entry.flags, XTM_DB_FLAG_IN_PRIV_DB ) ) return( XTM_DB_ERROR ); /* Is the data in an external file? */ if( flagIsSet( entry_ref -> entry.flags, XTM_DB_FLAG_PRIV_EXT_FILE ) ) status = fetchPrivText( databases, id, &db_text ); else status = fetchPrivDb( databases, id, &db_text ); if( status != XTM_DB_OK ) return( status ); /* Save the fetched text. */ strncpy( entry_ref -> entry.text, db_text, XTM_DB_RECORD_TEXT_LEN ); entry_ref -> entry.text[ XTM_DB_RECORD_TEXT_LEN ] = '\0'; /* Any 'extra' text? */ if( flagIsSet( entry_ref -> entry.flags, XTM_DB_FLAG_PRIV_EXT_FILE ) ) { *text_ref = db_text; } else { SysFree( db_text ); if( text_ref != NULL ) *text_ref = NULL; } flagClear( entry_ref -> entry.flags, XTM_DB_FLAG_EXT_FILE ); return( XTM_DB_OK ); } /* fetchPrivData */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS fetchPrivDb( XTM_DB_ENTRY_DATABASES *databases, UINT32 id, char **text_ref ) { /* Variables. */ datum key; datum contents; XTM_DB_OPEN_REQUEST single_open_req; XTM_DB_PRIV_DEF data_record; XTM_DB_PRIV_KEY key_record; /* Code. */ *text_ref = NULL; /* Open the database for read. */ if( databases -> private_db == NULL ) { single_open_req.database = XTM_DB_PRIV_ENTRY_DB; single_open_req.directory = databases -> database_dir; single_open_req.lock_timeout = 0; if( flagIsSet( databases -> operations, XTM_DB_FLAG_MODE_WRITE ) ) single_open_req.operations = XTM_DB_FLAG_MODE_WRITE; else single_open_req.operations = XTM_DB_FLAG_MODE_READ; databases -> private_db = openDatabase( &single_open_req ); if( databases -> private_db == NULL ) return( XTM_DB_ERROR ); } /* Create the key for the entry. */ #ifdef NET_BYTE_ORDER key_record.id = htonl( id ); #else key_record.id = id; #endif key.dptr = (char *) &key_record; key.dsize = xtm_db_priv_entry_key_size; /* Fetch the text. */ contents = dbm_fetch( (DBM *) databases -> private_db, key ); if( contents.dptr == NULL ) return( XTM_DB_ERROR ); /* Store the text here. */ memcpy( (char *) &data_record, contents.dptr, xtm_db_priv_entry_def_size ); #ifdef NET_BYTE_ORDER data_record.id = ntohl( data_record.id ); #endif *text_ref = SysMalloc( XTM_DB_RECORD_TEXT_LEN ); strcpy( *text_ref, data_record.text ); return( XTM_DB_OK ); } /* fetchPrivDb */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS fetchPrivText( XTM_DB_ENTRY_DATABASES *databases, UINT32 id, char **text_ref ) { /* Variables. */ int char_read; int status; char filename[ 200 ]; FILE *file_ref; struct stat file_status; /* Code. */ *text_ref = NULL; /* All private entries hide in the Private directory. */ /* sprintf( filename, "%s/Private/%d.txt", databases -> private_db, id ); Orig. most certainly wrong. I'm not sure whether the line below is correct, but it might be. RXTN 040597*/ sprintf( filename, "%s/Private/%d.txt", databases -> database_dir, id ); /* We need the size of the file. */ status = stat( filename, &file_status ); if( status != 0 ) return( XTM_DB_ERROR ); /* Allocate space for the file. */ *text_ref = SysMalloc( file_status.st_size + 5 ); if( *text_ref == NULL ) return( XTM_DB_ERROR ); /* Open the file and read the contents. */ file_ref = fopen( filename, "r" ); if( file_ref == NULL ) return( XTM_DB_ERROR ); char_read = fread( *text_ref, 1, file_status.st_size + 1, file_ref ); fclose( file_ref ); *(*text_ref + char_read) = '\0'; return( XTM_DB_OK ); } /* fetchPrivText */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS fetchStandEntry( XTM_DB_DATABASE database, UINT32 id, XTM_DB_STAND_ENTRY_REF entry ) { /* Variables. */ datum key; datum contents; XTM_DB_STAND_ENTRY_KEY key_record; /* Code. */ if( database == NULL ) return( XTM_DB_ERROR ); /* Create the key for the entry. */ #ifdef NET_BYTE_ORDER key_record.id = htonl( id ); #else key_record.id = id; #endif key.dptr = (char *) &key_record; key.dsize = xtm_db_stand_entry_key_size; /* Fetch the entry. */ contents = dbm_fetch( (DBM *) database, key ); if( contents.dptr == NULL ) return( XTM_DB_ERROR ); /* Copy the contents. */ memcpy( entry, contents.dptr, xtm_db_stand_entry_def_size ); #ifdef NET_BYTE_ORDER entry -> id = ntohl( entry -> id ); entry -> from = ntohl( entry -> from ); entry -> to = ntohl( entry -> to ); entry -> flags = ntohl( entry -> flags ); entry -> every_n = ntohs( entry -> every_n ); entry -> skip_week[ 0 ] = ntohl( entry -> skip_week[ 0 ] ); entry -> skip_week[ 1 ] = ntohl( entry -> skip_week[ 1 ] ); #endif return( XTM_DB_OK ); } /* fetchStandEntry */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS fetchTextFile( char *directory, UINT32 id, char **text ) { /* Variables. */ Boolean back_to_normal = False; int char_read; int status; UINT32 operations; char filename[ 200 ]; FILE *file_ref; struct stat file_status; /* Code. */ *text = NULL; /* Check the operations that can be done. */ xtmDbCheckDbOperations( directory, False, &operations ); /* Go to privileged mode? */ if( flagIsSet( operations, XTM_DB_FLAG_MODE_SETID ) && getegid() != privileged_gid ) { status = setgid( privileged_gid ); back_to_normal = True; } sprintf( filename, "%s/%d.txt", directory, id ); /* Does the file exist? */ status = stat( filename, &file_status ); if( status != 0 ) raise exception; /* Allocate space for the file. */ *text = SysMalloc( file_status.st_size + 5 ); if( *text == NULL ) raise exception; /* Open the file and read the contents. */ file_ref = fopen( filename, "r" ); if( file_ref == NULL ) raise exception; char_read = fread( *text, 1, file_status.st_size + 1, file_ref ); fclose( file_ref ); *(*text + char_read) = '\0'; /* Back to normal mode? */ if( back_to_normal ) status = setgid( real_gid ); return( XTM_DB_OK ); /* Exception handling. */ exception: if( back_to_normal ) status = setgid( real_gid ); return( XTM_DB_ERROR ); } /* fetchTextFile */ /*----------------------------------------------------------------------*/ static void freeFileLock( int *lock_fd ) { /* Variables. */ int status; struct flock lock; /* Code. */ if( ! use_lock || *lock_fd < 0 ) return; /* Unlock the file. */ lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; status = fcntl( *lock_fd, F_SETLK, &lock ); /* If we could not unlock the file, print error message and continue. */ if( status == -1 ) fprintf( stderr, "xtmDbTools: ffl(), Cannot unlock file.\n" ); close( *lock_fd ); *lock_fd = 0; return; } /* freeFileLock */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS insertDate( XTM_DB_DATABASE database, XTM_DB_DATE_REF entry ) { /* Variables. */ int flags; int index; int status; UINT32 date; datum key; datum contents; /* Code. */ if( database == NULL ) return( XTM_DB_ERROR ); /* Fill unused data with NULL. */ memset( entry -> unused, 0, sizeof( entry -> unused ) ); flags = DBM_INSERT; /* Create the key for the entry. */ #ifdef NET_BYTE_ORDER date = htonl( entry -> date ); #else date = entry -> date; #endif key.dptr = (char *) &date; key.dsize = xtm_db_date_key_size; /* Create the contents for the entry. */ contents.dptr = (char *) entry; contents.dsize = xtm_db_date_def_size; #ifdef NET_BYTE_ORDER entry -> date = htonl( entry -> date ); for( index = 0; index < XTM_DB_DATE_ID_SIZE; index++ ) entry -> id[ index ] = htonl( entry -> id[ index ] ); #endif /* Insert/replace the entry. */ status = dbm_store( (DBM *) database, key, contents, flags ); if( status != 0 ) { flags = DBM_REPLACE; status = dbm_store( (DBM *) database, key, contents, flags ); } #ifdef NET_BYTE_ORDER entry -> date = ntohl( entry -> date ); for( index = 0; index < XTM_DB_DATE_ID_SIZE; index++ ) entry -> id[ index ] = ntohl( entry -> id[ index ] ); #endif if( status != 0 ) return( XTM_DB_ERROR ); return( XTM_DB_OK ); } /* insertDate */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS insertEntry( XTM_DB_DATABASE database, XTM_DB_ENTRY_REF entry ) { /* Variables. */ int flags; int index; int status; datum key; datum contents; XTM_DB_ENTRY_KEY key_record; /* Code. */ if( database == NULL ) return( XTM_DB_ERROR ); /* Fill unused data with NULL. */ memset( entry -> unused, 0, sizeof( entry -> unused ) ); flags = DBM_INSERT; /* Set the owner of the entry. */ entry -> owner = getuid(); /* Last update. */ entry -> last_update = (UINT32) TimMakeTimeNow(); /* Create the key for the entry. */ #ifdef NET_BYTE_ORDER key_record.id = htonl( entry -> id ); #else key_record.id = entry -> id; #endif key.dptr = (char *) &key_record; key.dsize = xtm_db_entry_key_size; /* Create the contents for the entry. */ contents.dptr = (char *) entry; contents.dsize = xtm_db_entry_def_size; #ifdef NET_BYTE_ORDER entry -> id = htonl( entry -> id ); entry -> time_stamp = htonl( entry -> time_stamp ); entry -> date_stamp = htonl( entry -> date_stamp ); entry -> last_update = htonl( entry -> last_update ); entry -> owner = htonl( entry -> owner ); entry -> duration = htons( entry -> duration ); entry -> flags = htonl( entry -> flags ); for( index = 0; index < 5; index++ ) entry -> alarm_offset[ index ] = htons( entry -> alarm_offset[ index ] ); entry -> crc = htonl( entry -> crc ); #endif /* Insert/replace the entry. */ status = dbm_store( (DBM *) database, key, contents, flags ); if( status != 0 ) { flags = DBM_REPLACE; status = dbm_store( (DBM *) database, key, contents, flags ); } #ifdef NET_BYTE_ORDER entry -> id = ntohl( entry -> id ); entry -> time_stamp = ntohl( entry -> time_stamp ); entry -> date_stamp = ntohl( entry -> date_stamp ); entry -> last_update = ntohl( entry -> last_update ); entry -> owner = ntohl( entry -> owner ); entry -> duration = ntohs( entry -> duration ); entry -> flags = ntohl( entry -> flags ); for( index = 0; index < 5; index++ ) entry -> alarm_offset[ index ] = ntohs( entry -> alarm_offset[ index ] ); entry -> crc = ntohl( entry -> crc ); #endif if( status != 0 ) return( XTM_DB_ERROR ); return( XTM_DB_OK ); } /* insertEntry */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS insertEntryIdInDate( XTM_DB_DATABASE database_ref, UINT32 entry_id, TIM_TIME_REF date_stamp ) { /* Variables. */ int index; XTM_DB_DATE_DEF date_record; XTM_DB_STATUS status; /* Code. */ /* Try to fetch the date record. */ status = fetchDate( database_ref, date_stamp, &date_record ); if( status != XTM_DB_OK ) { memset( &date_record, 0, xtm_db_date_def_size ); date_record.date = date_stamp; } /* Do we have the entry id in the date record. */ for( index = 0; index < XTM_DB_DATE_ID_SIZE; index++ ) { if( date_record.id[ index ] == entry_id ) return( XTM_DB_OK ); } /* Add the entry id to the date record. */ for( index = 0; index < XTM_DB_DATE_ID_SIZE; index++ ) { if( date_record.id[ index ] == 0 ) break; } if( index == XTM_DB_DATE_ID_SIZE ) return( XTM_DB_ERROR ); /* Insert the new entry id and save the date record. */ date_record.id[ index ] = entry_id; status = insertDate( database_ref, &date_record ); if( status != XTM_DB_OK ) return( status ); return( XTM_DB_OK ); } /* insertEntryIdInDate */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS insertPrivData( XTM_DB_ENTRY_DATABASES *databases, UINT32 id, XTM_DB_ALL_ENTRY_REF entry_ref, char *text_ref ) { /* Variables. */ Boolean in_db; XTM_DB_STATUS status; /* Code. */ /* Any privileges to do this? */ if( flagIsClear( databases -> operations, XTM_DB_FLAG_MODE_PRIV ) ) { flagClear( entry_ref -> entry.flags, XTM_DB_FLAG_PRIVATE ); return( XTM_DB_OK ); } /* Does the data fit into a database entry? */ if( text_ref == NULL ) { status = insertPrivDb( databases, id, entry_ref -> entry.text ); in_db = True; } else if( strlen( text_ref ) <= XTM_DB_RECORD_TEXT_LEN ) { status = insertPrivDb( databases, id, text_ref ); in_db = True; } else { status = insertPrivText( databases, id, text_ref ); in_db = False; } if( status != XTM_DB_OK ) return( status ); flagSet( entry_ref -> entry.flags, XTM_DB_FLAG_IN_PRIV_DB ); flagClear( entry_ref -> entry.flags, XTM_DB_FLAG_EXT_FILE ); /* If inserted in external file, flag this. */ if( ! in_db ) flagSet( entry_ref -> entry.flags, XTM_DB_FLAG_PRIV_EXT_FILE ); /* Do not leave the original text. */ strcpy( entry_ref -> entry.text, "" ); if( text_ref != NULL ) *text_ref = '\0'; return( XTM_DB_OK ); } /* insertPrivData */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS insertPrivDb( XTM_DB_ENTRY_DATABASES *databases, UINT32 id, char *text_ref ) { /* Variables. */ int flags; int status; datum key; datum contents; XTM_DB_OPEN_REQUEST single_open_req; XTM_DB_PRIV_DEF data_record; XTM_DB_PRIV_KEY key_record; /* Code. */ /* Open the database for write. */ if( databases -> private_db == NULL ) { single_open_req.database = XTM_DB_PRIV_ENTRY_DB; single_open_req.directory = databases -> database_dir; single_open_req.lock_timeout = 0; if( flagIsSet( databases -> operations, XTM_DB_FLAG_MODE_WRITE ) ) single_open_req.operations = XTM_DB_FLAG_MODE_WRITE; else single_open_req.operations = XTM_DB_FLAG_MODE_READ; databases -> private_db = openDatabase( &single_open_req ); if( databases -> private_db == NULL ) return( XTM_DB_ERROR ); } /* Fill unused data with NULL. */ memset( data_record.unused, 0, sizeof( data_record.unused ) ); flags = DBM_INSERT; /* Create the key and contents of the entry. */ #ifdef NET_BYTE_ORDER key_record.id = htonl( id ); data_record.id = htonl( id ); #else key_record.id = id; data_record.id = id; #endif strcpy( data_record.text, text_ref ); key.dptr = (char *) &key_record; key.dsize = xtm_db_priv_entry_key_size; contents.dptr = (char *) &data_record; contents.dsize = xtm_db_priv_entry_def_size; /* Try insert/replace entry. */ status = dbm_store( (DBM *) databases -> private_db, key, contents, flags ); if( status != 0 ) { flags = DBM_REPLACE; status = dbm_store( (DBM *) databases -> private_db, key, contents, flags ); } if( status != 0 ) return( XTM_DB_ERROR ); return( XTM_DB_OK ); } /* insertPrivDb */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS insertPrivText( XTM_DB_ENTRY_DATABASES *databases, UINT32 id, char *text_ref ) { /* Variables. */ int char_written; int file_mode; int status; char filename[ 200 ]; FILE *file_ref; struct stat file_status; /* Code. */ /* Fetch protection of the directory. */ sprintf( filename, "%s/Private", databases -> database_dir ); status = stat( filename, &file_status ); if( status != 0 ) return( XTM_DB_ERROR ); file_mode = file_status.st_mode & (~(S_IXUSR | S_IXGRP | S_IXOTH)); /* All private entries hide in the Private directory. */ sprintf( filename, "%s/Private/%d.txt", databases -> database_dir, id ); /* Open the file and write the contents. */ file_ref = fopen( filename, "w" ); if( file_ref == NULL ) return( XTM_DB_ERROR ); char_written = fwrite( text_ref, 1, strlen( text_ref ), file_ref ); fclose( file_ref ); /* Let the file inherit the permissions from the directory. */ status = chmod( filename, file_mode ); if( status != 0 ) return( XTM_DB_ERROR ); if( char_written <= 0 ) return( XTM_DB_ERROR ); return( XTM_DB_OK ); } /* insertPrivText */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS insertStandEntry( XTM_DB_DATABASE database, XTM_DB_STAND_ENTRY_REF entry ) { /* Variables. */ int flags; int status; datum key; datum contents; XTM_DB_STAND_ENTRY_KEY key_record; /* Code. */ if( database == NULL ) return( XTM_DB_ERROR ); /* Fill unused data with NULL. */ memset( entry -> unused, 0, sizeof( entry -> unused ) ); flags = DBM_REPLACE; /* Create the key for the entry. */ #ifdef NET_BYTE_ORDER key_record.id = htonl( entry -> id ); #else key_record.id = entry -> id; #endif key.dptr = (char *) &key_record; key.dsize = xtm_db_stand_entry_key_size; /* Create the contents for the entry. */ contents.dptr = (char *) entry; contents.dsize = xtm_db_stand_entry_def_size; #ifdef NET_BYTE_ORDER entry -> id = htonl( entry -> id ); entry -> from = htonl( entry -> from ); entry -> to = htonl( entry -> to ); entry -> flags = htonl( entry -> flags ); entry -> every_n = htons( entry -> every_n ); entry -> skip_week[ 0 ] = htonl( entry -> skip_week[ 0 ] ); entry -> skip_week[ 1 ] = htonl( entry -> skip_week[ 1 ] ); #endif /* Insert/replace the entry. */ status = dbm_store( (DBM *) database, key, contents, flags ); if( status != 0 ) { flags = DBM_REPLACE; status = dbm_store( (DBM *) database, key, contents, flags ); } #ifdef NET_BYTE_ORDER entry -> id = ntohl( entry -> id ); entry -> from = ntohl( entry -> from ); entry -> to = ntohl( entry -> to ); entry -> flags = ntohl( entry -> flags ); entry -> every_n = ntohs( entry -> every_n ); entry -> skip_week[ 0 ] = ntohl( entry -> skip_week[ 0 ] ); entry -> skip_week[ 1 ] = ntohl( entry -> skip_week[ 1 ] ); #endif if( status != 0 ) return( XTM_DB_ERROR ); return( XTM_DB_OK ); } /* insertStandEntry */ /*----------------------------------------------------------------------*/ static XTM_DB_STATUS insertTextFile( char *directory, UINT32 id, char *text ) { /* Variables. */ int char_written; int file_mode; int status; char filename[ 200 ]; FILE *file_ref; struct stat stat_data; /* Code. */ /* Fetch the directory data and define the mode for files. */ sprintf( filename, "%s", directory ); status = stat( filename, &stat_data ); if( status != 0 ) return( XTM_DB_ERROR ); file_mode = stat_data.st_mode & (~(S_IXUSR | S_IXGRP | S_IXOTH)); /* Open the file and write the contents. */ sprintf( filename, "%s/%d.txt", directory, id ); file_ref = fopen( filename, "w" ); if( file_ref == NULL ) return( XTM_DB_ERROR ); char_written = fwrite( text, 1, strlen( text ), file_ref ); fclose( file_ref ); /* Let the file inherit the permissions from the directory. */ status = chmod( filename, file_mode ); if( status != 0 ) return( XTM_DB_ERROR ); if( char_written <= 0 ) return( XTM_DB_ERROR ); return( XTM_DB_OK ); } /* insertTextFile */ /*----------------------------------------------------------------------*/ static XTM_DB_DATABASE openDatabase( XTM_DB_OPEN_REQUEST *open_request ) { /* Variables. */ int flags; char filename[ 200 ]; char *key_file; DBM *dbm_ref; /* Code. */ /* Get the name of the database. */ switch( open_request -> database ) { case XTM_DB_ENTRY_DB: key_file = db_filenames[ 0 ]; break; case XTM_DB_DATE_DB: key_file = db_filenames[ 3 ]; break; case XTM_DB_STAND_ENTRY_DB: key_file = db_filenames[ 6 ]; break; case XTM_DB_PRIV_ENTRY_DB: key_file = db_filenames[ 9 ]; break; default: return( NULL ); } /* switch */ /* Open the database. */ sprintf( filename, "%s/%s", open_request -> directory, key_file ); flags = 0; if( flagIsSet( open_request -> operations, XTM_DB_FLAG_MODE_READ ) ) flagSet( flags, O_RDONLY ); if( flagIsSet( open_request -> operations, XTM_DB_FLAG_MODE_WRITE ) ) flagSet( flags, O_RDWR ); dbm_ref = dbm_open( filename, flags, 0660 ); return( (XTM_DB_DATABASE) dbm_ref ); } /* openDatabase */ /*----------------------------------------------------------------------*/ static Boolean updateDbLog( char *directory, UINT32 entry_id, UINT32 flags ) { /* Variables. */ int file_mode; int status; char filename[ 200 ]; long offset; struct stat stat_data; FILE *file_ref; XTM_DB_LOG_HEADER header; XTM_DB_LOG_RECORD log_record; /* Code. */ sprintf( filename, "%s/%s", directory, LOG_FILE ); /* Open the log file for write. */ file_ref = fopen( filename, "r+" ); /* If the file did not exist, create it. */ if( file_ref == NULL ) { /* Let the log file inherit the directory protection. */ status = stat( directory, &stat_data ); if( status != 0 ) return( False ); file_mode = stat_data.st_mode & (~(S_IXUSR | S_IXGRP | S_IXOTH)); /* Open the file. */ file_ref = fopen( filename, "w" ); if( file_ref == NULL ) return( False ); /* We start all over again. */ header.last_changed = (UINT32) TimMakeTimeNow(); header.current_ref = 0; header.records = 0; header.max_log_records = XTM_DB_MAX_LOG_RECORDS; #ifdef NET_BYTE_ORDER header.last_changed = htonl( header.last_changed ); header.current_ref = htonl( header.current_ref ); header.records = htonl( header.records ); header.max_log_records = htonl( header.max_log_records ); #endif memset( header.unused, 0, sizeof( header.unused ) ); /* Write the record to the file. */ rewind( file_ref ); fwrite( (void *) &header, sizeof( header ), 1, file_ref ); fclose( file_ref ); /* Set file protection. */ status = chmod( filename, file_mode ); if( status != 0 ) return( False ); /* Re-open the file for append. */ file_ref = fopen( filename, "r+" ); if( file_ref == NULL ) return( False ); } /* if */ /* Fetch the header record. */ rewind( file_ref ); fread( (void *) &header, sizeof( header ), 1, file_ref ); #ifdef NET_BYTE_ORDER header.last_changed = ntohl( header.last_changed ); header.current_ref = ntohl( header.current_ref ); header.records = ntohl( header.records ); header.max_log_records = ntohl( header.max_log_records ); #endif /* Create the log record. */ log_record.time_stamp = (UINT32) TimMakeTimeNow(); log_record.entry_id = entry_id; log_record.flags = flags; log_record.changed_by = (INT32) getuid(); #ifdef NET_BYTE_ORDER log_record.time_stamp = htonl( log_record.time_stamp ); log_record.entry_id = htonl( log_record.entry_id ); log_record.flags = htonl( log_record.flags ); log_record.changed_by = htonl( log_record.changed_by ); #endif /* Save the log record. */ offset = sizeof( header ) + header.current_ref * sizeof( log_record ); fseek( file_ref, offset, 0 ); fwrite( (void *) &log_record, sizeof( log_record ), 1, file_ref ); /* Calculate the new positions for the next record. */ header.current_ref++; if( header.current_ref >= header.max_log_records ) header.current_ref = 0; header.records++; #ifdef NET_BYTE_ORDER header.last_changed = htonl( header.last_changed ); header.current_ref = htonl( header.current_ref ); header.records = htonl( header.records ); header.max_log_records = htonl( header.max_log_records ); #endif /* Save the header record. */ rewind( file_ref ); fwrite( (void *) &header, sizeof( header ), 1, file_ref ); /* That's it. */ fclose( file_ref ); return( True ); } /* updateDbLog */