1 /*----------------------------------------------------------------------------
2 --
3 --  Module:           xtmDbTools
4 --
5 --  Project:          Xdiary
6 --  System:           xtm - X Desktop Calendar
7 --    Subsystem:      <>
8 --    Function block: <>
9 --
10 --  Description:
11 --    Interface to the database containing:
12 --      * Entries.
13 --      * Entries defined for a day.
14 --      * Standing entries.
15 --      * Private entries.
16 --
17 --  Filename:         xtmDbTools.c
18 --
19 --  Authors:          Roger Larsson, Ulrika Bornetun
20 --  Creation date:    1991-05-28
21 --
22 --
23 --  (C) Copyright Ulrika Bornetun, Roger Larsson (1995)
24 --      All rights reserved
25 --
26 --  Permission to use, copy, modify, and distribute this software and its
27 --  documentation for any purpose and without fee is hereby granted,
28 --  provided that the above copyright notice appear in all copies. Ulrika
29 --  Bornetun and Roger Larsson make no representations about the usability
30 --  of this software for any purpose. It is provided "as is" without express
31 --  or implied warranty.
32 ----------------------------------------------------------------------------*/
33 
34 /* SCCS module identifier. */
35 static char SCCSID[] = "@(#) Module: xtmDbTools.c, Version: 1.4, Date: 95/06/26 22:27:30";
36 
37 
38 /*----------------------------------------------------------------------------
39 --  Include files
40 ----------------------------------------------------------------------------*/
41 
42 /* netinet/in.h doesn't compile with _POSIX_SOURCE on some platforms. */
43 #ifdef _POSIX_SOURCE
44 #undef  _POSIX_SOURCE
45 #define HAD_POSIX
46 #endif
47 
48 #include <fcntl.h>
49 #include <stdio.h>
50 #include <unistd.h>
51 #include <ctype.h>
52 #include <sys/stat.h>
53 #include <sys/types.h>
54 
55 #include <X11/Intrinsic.h>
56 
57 #include "System.h"
58 #include "DirList.h"
59 
60 #include "xtmGlobal.h"
61 #include "xtmAccBase.h"
62 #include "xtmDbTools.h"
63 #include "xtmHoliday.h"
64 
65 #ifndef XD_NO_NET_ORDER
66 #  define NET_BYTE_ORDER 1
67 #endif
68 
69 #ifdef NET_BYTE_ORDER
70 #include <netinet/in.h>
71 #endif
72 
73 #ifdef HAD_POSIX
74 #ifndef _POSIX_SOURCE
75 #define _POSIX_SOURCE
76 #endif
77 #endif
78 
79 
80 /*----------------------------------------------------------------------------
81 --  Macro definitions
82 ----------------------------------------------------------------------------*/
83 
84 /* Try to lock a file this number of times. */
85 #define  MAX_LOCK_TRIES            20
86 
87 /* Number of cached db operations records. */
88 #define  MAX_DB_OP_CACHE           1
89 
90 /* Access control file. */
91 #define ACCESS_FILE                "dbAccess"
92 
93 /* Key for access control file. */
94 #define KEY_ACCESS_CONTROL         "Access"
95 
96 /* Exception handling. */
97 #define  raise  goto
98 
99 
100 /*----------------------------------------------------------------------------
101 --  Type declarations
102 ----------------------------------------------------------------------------*/
103 
104 /* Operations allowed on a database. */
105 typedef struct {
106   char    directory[ 150 ];
107   UINT32  operations;
108 } DB_OPERATIONS;
109 
110 
111 /*----------------------------------------------------------------------------
112 --  Global definitions
113 ----------------------------------------------------------------------------*/
114 
115 /* Name of module. */
116 static char  *module_name = "xtmDbTools";
117 
118 /* Size of the entry key and record. */
119 static int  xtm_db_entry_def_size = 170;
120 static int  xtm_db_entry_key_size = 4;
121 
122 /* Size of the date key and record. */
123 static int  xtm_db_date_def_size = 256;
124 static int  xtm_db_date_key_size = 4;
125 
126 /* Size of the standing entry key and record. */
127 static int  xtm_db_stand_entry_def_size = 48;
128 static int  xtm_db_stand_entry_key_size = 4;
129 
130 /* Size of the private entry key and record. */
131 static int  xtm_db_priv_entry_def_size = 110;
132 static int  xtm_db_priv_entry_key_size = 4;
133 
134 /* Use file locking? */
135 static Boolean use_lock = True;
136 
137 /* Real an privileged process IDs. */
138 static int real_gid       = -1;
139 static int privileged_gid = -1;
140 
141 /* Name of key and data files. */
142 static char  *db_filenames[] = {
143   ENTRY_DB_NAME,
144   ENTRY_DB_NAME_DIR,
145   ENTRY_DB_NAME_PAG,
146   DATE_DB_NAME,
147   DATE_DB_NAME_DIR,
148   DATE_DB_NAME_PAG,
149   STAND_ENTRY_DB_NAME,
150   STAND_ENTRY_DB_NAME_DIR,
151   STAND_ENTRY_DB_NAME_PAG,
152   PRIV_ENTRY_DB_NAME,
153   PRIV_ENTRY_DB_NAME_DIR,
154   PRIV_ENTRY_DB_NAME_PAG,
155 };
156 
157 /* Cache to hold operations allowed on a database. */
158 static Boolean        cache_valid = False;
159 static DB_OPERATIONS  db_op_cache[ MAX_DB_OP_CACHE ];
160 
161 
162 /*----------------------------------------------------------------------------
163 --  Function prototypes
164 ----------------------------------------------------------------------------*/
165 
166 static INT32
167   calcCrc( char  *text );
168 
169 static XTM_DB_STATUS
170   closeDatabase( XTM_DB_DATABASE database );
171 
172 static XTM_DB_STATUS
173   createFileLock( char    *filename,
174                   UINT32  operations,
175                   int     *lock_fd,
176                   int     *uid_locking );
177 
178 static LST_COMPARE
179   dateSortFunc( XTM_DB_DATE_DEF *element, TIM_TIME_REF  date );
180 
181 static XTM_DB_STATUS
182   deleteDate( XTM_DB_DATABASE  database,
183               TIM_TIME_REF     date );
184 
185 static XTM_DB_STATUS
186   deleteEntry( XTM_DB_DATABASE  database,
187                UINT32           id );
188 
189 static XTM_DB_STATUS
190   deleteEntryIdInDate( XTM_DB_DATABASE  database_ref,
191                        UINT32           entry_id,
192                        TIM_TIME_REF     date_stamp );
193 
194 static XTM_DB_STATUS
195   deletePrivData( XTM_DB_ENTRY_DATABASES  *databases,
196                   XTM_DB_ALL_ENTRY_REF    entry_ref );
197 
198 static XTM_DB_STATUS
199   deletePrivDb( XTM_DB_ENTRY_DATABASES  *databases,
200                 UINT32                  id );
201 
202 static XTM_DB_STATUS
203   deletePrivText( XTM_DB_ENTRY_DATABASES  *databases,
204                   UINT32                  id );
205 
206 static XTM_DB_STATUS
207   deleteStandEntry( XTM_DB_DATABASE  database,
208                     UINT32           id );
209 
210 static XTM_DB_STATUS
211   deleteTextFile( char    *directory,
212                   UINT32  id );
213 
214 static XTM_DB_STATUS
215   doInsertEntry( XTM_DB_ENTRY_DATABASES  *databases,
216                  XTM_DB_ALL_ENTRY_REF    entry_ref,
217                  char                    *text_ref,
218                  UINT32                  log_flags );
219 
220 static LST_COMPARE
221   entryIdSortFunc( XTM_DB_ENTRY_DEF *element, UINT32 id );
222 
223 static LST_COMPARE
224   entryTimeSortFunc( XTM_DB_ENTRY_DEF *element, TIM_TIME_REF  date );
225 
226 static XTM_DB_STATUS
227   fetchDate( XTM_DB_DATABASE   database,
228              TIM_TIME_REF      date,
229              XTM_DB_DATE_REF   entry );
230 
231 static XTM_DB_STATUS
232   fetchEntry( XTM_DB_DATABASE   database,
233               UINT32            id,
234               XTM_DB_ENTRY_REF  entry );
235 
236 static XTM_DB_STATUS
237   fetchFirstDate( XTM_DB_DATABASE   database,
238                   XTM_DB_DATE_REF   entry );
239 
240 static XTM_DB_STATUS
241   fetchFirstStand( XTM_DB_DATABASE         database,
242                    XTM_DB_STAND_ENTRY_REF  entry );
243 
244 static XTM_DB_STATUS
245   fetchNextDate( XTM_DB_DATABASE   database,
246                  XTM_DB_DATE_REF   entry );
247 
248 static XTM_DB_STATUS
249   fetchNextStand( XTM_DB_DATABASE         database,
250                   XTM_DB_STAND_ENTRY_REF  entry );
251 
252 static XTM_DB_STATUS
253   fetchPrivData( XTM_DB_ENTRY_DATABASES  *databases,
254                  UINT32                  id,
255                  XTM_DB_ALL_ENTRY_REF    entry_ref,
256                  char                    **text_ref );
257 
258 static XTM_DB_STATUS
259   fetchPrivDb( XTM_DB_ENTRY_DATABASES  *databases,
260                UINT32                  id,
261                char                    **text_ref );
262 
263 static XTM_DB_STATUS
264   fetchPrivText( XTM_DB_ENTRY_DATABASES  *databases,
265                  UINT32                  id,
266                  char                    **text_ref );
267 
268 static XTM_DB_STATUS
269   fetchStandEntry( XTM_DB_DATABASE         database,
270                    UINT32                  id,
271                    XTM_DB_STAND_ENTRY_REF  entry );
272 
273 static XTM_DB_STATUS
274   fetchTextFile( char     *directory,
275                  UINT32   id,
276                  char     **text );
277 
278 static void
279   freeFileLock( int  *lock_fd );
280 
281 static XTM_DB_STATUS
282   insertDate( XTM_DB_DATABASE  database,
283               XTM_DB_DATE_REF  entry );
284 
285 static XTM_DB_STATUS
286   insertEntry( XTM_DB_DATABASE   database,
287                XTM_DB_ENTRY_REF  entry );
288 
289 static XTM_DB_STATUS
290   insertEntryIdInDate( XTM_DB_DATABASE  database_ref,
291                        UINT32           entry_id,
292                        TIM_TIME_REF     date_stamp );
293 
294 static XTM_DB_STATUS
295   insertPrivData( XTM_DB_ENTRY_DATABASES  *databases,
296                   UINT32                  id,
297                   XTM_DB_ALL_ENTRY_REF    entry_ref,
298                   char                    *text_ref );
299 
300 static XTM_DB_STATUS
301   insertPrivDb( XTM_DB_ENTRY_DATABASES  *databases,
302                 UINT32                  id,
303                 char                    *text_ref );
304 
305 static XTM_DB_STATUS
306   insertPrivText( XTM_DB_ENTRY_DATABASES  *databases,
307                   UINT32                  id,
308                   char                    *text_ref );
309 
310 static XTM_DB_STATUS
311   insertStandEntry( XTM_DB_DATABASE        database,
312                     XTM_DB_STAND_ENTRY_REF entry );
313 
314 static XTM_DB_STATUS
315   insertTextFile( char    *directory,
316                   UINT32  id,
317                   char    *text );
318 
319 static XTM_DB_DATABASE
320   openDatabase( XTM_DB_OPEN_REQUEST  *open_request );
321 
322 static Boolean
323   updateDbLog( char    *directory,
324                UINT32  entry_id,
325                UINT32  flags );
326 
327 
328 
329 /*----------------------------------------------------------------------------
330 --  Functions
331 ----------------------------------------------------------------------------*/
332 
333 XTM_DB_STATUS
xtmDbChangesInLog(char * directory,TIM_TIME_REF since,XTM_DB_LOG_RECORD changes[],int max_changes,int * no_changes)334   xtmDbChangesInLog( char               *directory,
335                      TIM_TIME_REF       since,
336                      XTM_DB_LOG_RECORD  changes[],
337                      int                max_changes,
338                      int                *no_changes )
339 {
340 
341   /* Variables. */
342   Boolean            back_to_normal = False;
343   int                lock_fd = -1;
344   int                rec_index;
345   int                read_ref;
346   int                status;
347   int                uid_locking;
348   long               offset;
349   UINT32             operations;
350   char               filename[ 150 ];
351   FILE               *file_ref;
352   XTM_DB_LOG_HEADER  header;
353   XTM_DB_LOG_RECORD  log_record;
354   XTM_DB_STATUS      db_status;
355 
356 
357   /* Code. */
358 
359   /* Check the operations that can be done. */
360   xtmDbCheckDbOperations( directory, False, &operations );
361 
362   /* Go to privileged mode? */
363   if( flagIsSet( operations, XTM_DB_FLAG_MODE_SETID ) &&
364       getegid() != privileged_gid ) {
365     status = setgid( privileged_gid );
366     back_to_normal = True;
367   }
368 
369   /* Lock the file. */
370   sprintf( filename, "%s/%s", directory, LOCK_FILE );
371 
372   db_status = createFileLock( filename,
373                               XTM_DB_FLAG_MODE_READ,
374                               &lock_fd,
375                               &uid_locking );
376   if( db_status != XTM_DB_OK )
377     raise lock_exception;
378 
379 
380   /* Open the log file. */
381   sprintf( filename, "%s/%s", directory, LOG_FILE );
382 
383   file_ref = fopen( filename, "r" );
384   if( file_ref == NULL )
385     raise exception;
386 
387 
388   /* Fetch the header record. */
389   rewind( file_ref );
390 
391   fread( (void *) &header, sizeof( header ), 1, file_ref );
392 
393 #ifdef NET_BYTE_ORDER
394   header.last_changed    = ntohl( header.last_changed );
395   header.current_ref     = ntohl( header.current_ref );
396   header.records         = ntohl( header.records );
397   header.max_log_records = ntohl( header.max_log_records );
398 #endif
399 
400 
401   /* Where do we start? */
402   if( header.records < header.max_log_records ) {
403     read_ref = 0;
404   } else {
405     read_ref = header.current_ref + 1;
406     if( read_ref >= header.max_log_records )
407       read_ref = 0;
408   }
409 
410   rec_index = 0;
411 
412   /* Read all records in the log file. */
413   while( read_ref != header.current_ref && rec_index < max_changes ) {
414 
415     offset = sizeof( header ) + read_ref * sizeof( log_record );
416     fseek( file_ref, offset, 0 );
417 
418     fread( (void *) &log_record, sizeof( log_record ), 1, file_ref );
419 
420 #ifdef NET_BYTE_ORDER
421     log_record.time_stamp = ntohl( log_record.time_stamp );
422     log_record.entry_id   = ntohl( log_record.entry_id );
423     log_record.flags      = ntohl( log_record.flags );
424     log_record.changed_by = ntohl( log_record.changed_by );
425 #endif
426 
427     /* Do we have a match. */
428     if( (TIM_TIME_REF) log_record.time_stamp >= since ) {
429       memcpy( &changes[ rec_index ], &log_record, sizeof( log_record  ) );
430       rec_index++;
431     }
432 
433     /* Next record. */
434     read_ref++;
435     if( read_ref >= header.max_log_records )
436       read_ref = 0;
437 
438   } /* while */
439 
440   fclose( file_ref );
441 
442   *no_changes = rec_index;
443 
444 
445   /* Free the file lock. */
446   freeFileLock( &lock_fd );
447 
448   /* Back to normal mode? */
449   if( back_to_normal )
450     status = setgid( real_gid );
451 
452   return( XTM_DB_OK );
453 
454 
455   /* Exception handling. */
456   exception:
457     if( file_ref != NULL )
458       fclose( file_ref );
459 
460     if( lock_fd != -1 )
461       freeFileLock( &lock_fd );
462 
463     if( back_to_normal )
464       status = setgid( real_gid );
465 
466     return( XTM_DB_ERROR );
467 
468 
469   lock_exception:
470     if( back_to_normal )
471       status = setgid( real_gid );
472 
473     return( db_status );
474 
475 
476 
477 } /* xtmDbChangesInLog */
478 
479 
480 /*----------------------------------------------------------------------*/
481 
482 void
xtmDbCheckDbOperations(char * directory,Boolean force_check,UINT32 * operations)483   xtmDbCheckDbOperations( char     *directory,
484                           Boolean  force_check,
485                           UINT32   *operations )
486 {
487 
488   /* Variables. */
489   int            status;
490   char           filename[ 150 ];
491   XTM_DB_STATUS  db_status;
492   struct stat    file_status;
493 
494 
495   /* Code. */
496 
497   /* Check the cache. */
498   if( ! force_check &&
499         cache_valid &&
500         strcmp( directory, db_op_cache[ 0 ].directory ) == 0 ) {
501     *operations = db_op_cache[ 0 ].operations;
502     return;
503   }
504 
505   *operations = 0;
506 
507   /* Is this the owner? */
508   status = stat( directory, &file_status );
509   if( status == 0 && file_status.st_uid == getuid() ) {
510     flagSet( *operations, (XTM_DB_FLAG_MODE_READ | XTM_DB_FLAG_MODE_WRITE  |
511                            XTM_DB_FLAG_MODE_MSG  | XTM_DB_FLAG_MODE_PRIV) );
512 
513     return;
514   }
515 
516 
517   /* Check privileges within the directory itself. */
518   sprintf( filename, "%s/%s", directory, ID_FILE );
519 
520   if( access( filename, (R_OK | W_OK | F_OK) ) == 0 ) {
521     flagSet( *operations, (XTM_DB_FLAG_MODE_READ | XTM_DB_FLAG_MODE_WRITE) );
522 
523   } else if( access( filename, (R_OK | F_OK) ) == 0 ) {
524     flagSet( *operations, XTM_DB_FLAG_MODE_READ );
525   }
526 
527 
528   /* Access permissions for the Message directory. */
529   sprintf( filename, "%s/Message", directory );
530 
531   if( access( filename, (W_OK | F_OK) ) == 0 ) {
532     flagSet( *operations, XTM_DB_FLAG_MODE_MSG );
533   }
534 
535 
536   /* If we could not read or write, check the access list (if any). */
537   if( *operations == 0 && real_gid != privileged_gid ) {
538 
539     /* Move to privileged level. */
540     status = setgid( privileged_gid );
541 
542     /* Fetch the operations this user can do. */
543     db_status = xtmAbGetUserOperations( directory, getuid(), getgid(),
544                                         operations );
545     /* Back to user level. */
546     status = setgid( real_gid );
547 
548   } /* if */
549 
550 
551   /* Save in cache. */
552   cache_valid = True;
553   strcpy( db_op_cache[ 0 ].directory, directory );
554   db_op_cache[ 0 ].operations = *operations;
555 
556 
557   return;
558 
559 } /* xtmDbCheckDbOperations */
560 
561 
562 /*----------------------------------------------------------------------*/
563 
564 XTM_DB_STATUS
xtmDbCreateDatabase(XTM_DB_CREATE_REQUEST * create_request)565   xtmDbCreateDatabase( XTM_DB_CREATE_REQUEST  *create_request )
566 {
567 
568   /* Variables. */
569   int          file_mode;
570   int          status;
571   char         filename[ 200 ];
572   char         *data_file;
573   char         *key_file;
574   FILE         *file_ref;
575   struct stat  stat_data;
576 
577 
578   /* Code. */
579 
580   /* Get the name of the database. */
581   switch( create_request -> database ) {
582     case XTM_DB_ENTRY_DB:
583       key_file  = db_filenames[ 1 ];
584       data_file = db_filenames[ 2 ];
585       break;
586     case XTM_DB_DATE_DB:
587       key_file  = db_filenames[ 4 ];
588       data_file = db_filenames[ 5 ];
589       break;
590     case XTM_DB_STAND_ENTRY_DB:
591       key_file  = db_filenames[ 7 ];
592       data_file = db_filenames[ 8 ];
593       break;
594     case XTM_DB_PRIV_ENTRY_DB:
595       key_file  = db_filenames[ 10 ];
596       data_file = db_filenames[ 11 ];
597       break;
598     default:
599       return( XTM_DB_UNKNOWN );
600   } /* switch */
601 
602 
603   /* Fetch the directory data and define the mode for files. */
604   sprintf( filename, "%s", create_request -> directory );
605 
606   status = stat( filename, &stat_data );
607   if( status != 0 )
608     return( XTM_DB_ERROR );
609 
610   file_mode = stat_data.st_mode & (~(S_IXUSR | S_IXGRP | S_IXOTH));
611 
612 
613   /* Does the database exist? */
614   sprintf( filename, "%s/%s", create_request -> directory, key_file );
615 
616   status = stat( filename, &stat_data );
617   if( status == 0 )
618     return( XTM_DB_OK );
619 
620 
621   /* Create empty key file. */
622   sprintf( filename, "%s/%s", create_request -> directory, key_file );
623 
624   file_ref = fopen( filename, "w" );
625   if( file_ref == NULL )
626     return( XTM_DB_ERROR );
627   fclose( file_ref );
628 
629   /* Let the key file inherit the permissions from the directory. */
630   status = chmod( filename, file_mode );
631   if( status != 0 )
632     return( XTM_DB_ERROR );
633 
634 
635   /* Create empty data file. */
636   sprintf( filename, "%s/%s", create_request -> directory, data_file );
637 
638   file_ref = fopen( filename, "w" );
639   if( file_ref == NULL )
640     return( XTM_DB_ERROR );
641   fclose( file_ref );
642 
643   /* Let the data file inherit the permissions from the directory. */
644   status = chmod( filename, file_mode );
645   if( status != 0 )
646     return( XTM_DB_ERROR );
647 
648 
649   return( XTM_DB_OK );
650 
651 } /* xtmDbCreateDatabase */
652 
653 
654 /*----------------------------------------------------------------------*/
655 
656 XTM_DB_STATUS
xtmDbFetchFileInfo(char * filename,struct stat * file_info)657   xtmDbFetchFileInfo( char         *filename,
658                       struct stat  *file_info )
659 {
660 
661   /* Variables. */
662   Boolean  back_to_normal = False;
663   int      status;
664   UINT32   operations;
665 
666 
667   /* Code. */
668 
669   /* Check the operations that can be done. */
670   xtmDbCheckDbOperations( filename, False, &operations );
671 
672   /* Go to privileged mode? */
673   if( flagIsSet( operations, XTM_DB_FLAG_MODE_SETID ) &&
674       getegid() != privileged_gid ) {
675     status = setgid( privileged_gid );
676 
677     back_to_normal = True;
678   }
679 
680   /* Fetch file info. */
681   status = stat( filename, file_info );
682 
683   /* Back to normal mode? */
684   if( back_to_normal )
685     setgid( real_gid );
686 
687   if( status != 0 )
688     return( XTM_DB_ERROR );
689 
690 
691   return( XTM_DB_OK );
692 
693 } /* xtmDbFetchFileInfo */
694 
695 
696 /*----------------------------------------------------------------------*/
697 
698 XTM_DB_STATUS
xtmDbGenerateId(XTM_DB_ID_REQUEST * request,UINT32 * id)699   xtmDbGenerateId( XTM_DB_ID_REQUEST  *request,
700                    UINT32             *id )
701 {
702 
703   /* Variables. */
704   Boolean        back_to_normal = False;
705   int            items;
706   int            lock_fd = -1;
707   int            status;
708   int            uid_locking;
709   UINT32         operations;
710   char           buffer[ 50 ];
711   char           filename[ 150 ];
712   char           *char_ref;
713   FILE           *file_ref = NULL;
714   XTM_DB_STATUS  db_status;
715 
716 
717   /* Code. */
718 
719   *id = 0;
720 
721   /* Check the operations that can be done. */
722   xtmDbCheckDbOperations( request -> directory, False, &operations );
723 
724   /* Go to privileged mode? */
725   if( flagIsSet( operations, XTM_DB_FLAG_MODE_SETID ) &&
726       getegid() != privileged_gid ) {
727     status = setgid( privileged_gid );
728     back_to_normal = True;
729   }
730 
731   /* Lock the file. */
732   if( request -> lock_file ) {
733     sprintf( filename, "%s/%s", request -> directory, LOCK_FILE );
734 
735     db_status = createFileLock( filename,
736                                 XTM_DB_FLAG_MODE_WRITE,
737                                 &lock_fd,
738                                 &uid_locking );
739     if( db_status != XTM_DB_OK )
740       raise lock_exception;
741   }
742 
743   /* Open the identifier file. */
744   sprintf( filename, "%s/%s", request -> directory, ID_FILE );
745 
746   file_ref = fopen( filename, "r+" );
747 
748   if( file_ref == NULL )
749     raise exception;
750 
751   /* Fetch the old identifier. */
752   rewind( file_ref );
753 
754   char_ref = fgets( buffer, sizeof( buffer ), file_ref );
755   if( char_ref == NULL )
756     raise exception;
757 
758   items = sscanf( buffer, "%d", id );
759   if( items == 1 ) {
760     *id = *id + 1;
761 
762     rewind( file_ref );
763     fprintf( file_ref, "%8d\n", *id );
764   }
765 
766   fclose( file_ref );
767 
768   /* Free the file lock. */
769   freeFileLock( &lock_fd );
770 
771   /* Back to normal mode? */
772   if( back_to_normal )
773     status = setgid( real_gid );
774 
775   if( items == 1 )
776     return( XTM_DB_OK );
777   else
778     return( XTM_DB_ERROR );
779 
780 
781   /* Exception handling. */
782   exception:
783     if( file_ref != NULL )
784       fclose( file_ref );
785 
786     if( lock_fd != -1 )
787       freeFileLock( &lock_fd );
788 
789     if( back_to_normal )
790       status = setgid( real_gid );
791 
792     return( XTM_DB_ERROR );
793 
794 
795   lock_exception:
796     if( back_to_normal )
797       status = setgid( real_gid );
798 
799     return( db_status );
800 
801 } /* xtmDbGenerateId */
802 
803 
804 /*----------------------------------------------------------------------*/
805 
806 void
xtmDbGetEntryPermissions(UINT32 db_operations,int entry_owner,UINT32 entry_flags,UINT32 * can_do_flags)807   xtmDbGetEntryPermissions( UINT32  db_operations,
808                             int     entry_owner,
809                             UINT32  entry_flags,
810                             UINT32  *can_do_flags )
811 {
812 
813   /* Code. */
814 
815   *can_do_flags = 0;
816 
817   flagSet( *can_do_flags, XTM_DB_PROT_READ );
818   flagSet( *can_do_flags, XTM_DB_PROT_WRITE );
819 
820 
821   /* Can we read the entry. */
822   if( flagIsSet( entry_flags, XTM_DB_FLAG_PRIVATE ) &&
823       flagIsClear( db_operations, XTM_DB_FLAG_MODE_PRIV ) )
824     flagClear( *can_do_flags, XTM_DB_PROT_READ );
825 
826 
827   /* Can we write the entry. */
828   if( flagIsClear( db_operations, XTM_DB_FLAG_MODE_WRITE ) ||
829       (flagIsSet( entry_flags, XTM_DB_FLAG_PRIVATE ) &&
830        flagIsClear( db_operations, XTM_DB_FLAG_MODE_PRIV )) )
831     flagClear( *can_do_flags, XTM_DB_PROT_WRITE );
832 
833 
834   /* Can we change the entry? */
835   if( flagIsSet( *can_do_flags, XTM_DB_PROT_WRITE ) &&
836       (entry_owner == getuid() ||
837        flagIsClear( entry_flags, XTM_DB_FLAG_ONLY_OWNER_CHANGE )) )
838     flagSet( *can_do_flags, XTM_DB_PROT_CHANGE );
839 
840 
841   /* Can we delete the entry? */
842   if( flagIsSet( *can_do_flags, XTM_DB_PROT_WRITE ) &&
843       (entry_owner == getuid() ||
844        flagIsClear( entry_flags, XTM_DB_FLAG_ONLY_OWNER_DELETE )) )
845     flagSet( *can_do_flags, XTM_DB_PROT_DELETE );
846 
847 
848   return;
849 
850 } /* xtmDbGetEntryPermissions */
851 
852 
853 /*----------------------------------------------------------------------*/
854 
855 XTM_DB_STATUS
xtmDbInitializeAuxFiles(char * directory)856   xtmDbInitializeAuxFiles( char  *directory )
857 {
858 
859   /* Variables. */
860   int          file_mode;
861   int          status;
862   char         filename[ 150 ];
863   FILE         *file_ref;
864   struct stat  stat_data;
865 
866 
867   /* Code. */
868 
869   /* Fetch the directory data and define the mode for files. */
870   sprintf( filename, "%s", directory );
871 
872   status = stat( filename, &stat_data );
873   if( status != 0 )
874     return( XTM_DB_ERROR );
875 
876   file_mode = stat_data.st_mode & (~(S_IXUSR | S_IXGRP | S_IXOTH));
877 
878 
879   /* Does the id file already exist? */
880   sprintf( filename, "%s/%s", directory, ID_FILE );
881 
882   status = stat( filename, &stat_data );
883   if( status != 0 ) {
884 
885     /* Open the identifier file. */
886     file_ref = fopen( filename, "w" );
887     if( file_ref == NULL )
888       return( XTM_DB_ERROR );
889 
890     /* Write the start ID. */
891     fprintf( file_ref, "%8d\n", 11 );
892 
893     fclose( file_ref );
894 
895     /* Let the file inherit the permissions from the directory. */
896     status = chmod( filename, file_mode );
897     if( status != 0 )
898       return( XTM_DB_ERROR );
899 
900   } /* if */
901 
902 
903   /* Does the lock file already exist? */
904   sprintf( filename, "%s/%s", directory, LOCK_FILE );
905 
906   status = stat( filename, &stat_data );
907   if( status != 0 ) {
908 
909     /* Touch the lock file. */
910     file_ref = fopen( filename, "w" );
911     if( file_ref == NULL )
912       return( XTM_DB_ERROR );
913 
914     fclose( file_ref );
915 
916 
917     /* Let the file inherit the permissions from the directory. */
918     status = chmod( filename, file_mode );
919     if( status != 0 )
920       return( XTM_DB_ERROR );
921 
922   } /* if */
923 
924 
925   /* Does the access file already exist? */
926   sprintf( filename, "%s/%s", directory, ACCESS_FILE );
927 
928   status = stat( filename, &stat_data );
929   if( status != 0 ) {
930 
931     /* Touch the access file. */
932     file_ref = fopen( filename, "w" );
933     if( file_ref == NULL )
934       return( XTM_DB_ERROR );
935 
936     fclose( file_ref );
937 
938     /* Let the file inherit the permissions from the directory. */
939     status = chmod( filename, file_mode );
940     if( status != 0 )
941       return( XTM_DB_ERROR );
942 
943   } /* if */
944 
945 
946   return( XTM_DB_OK );
947 
948 } /* xtmDbInitializeAuxFiles */
949 
950 
951 /*----------------------------------------------------------------------*/
952 
953 void
xtmDbClearAccessCache()954   xtmDbClearAccessCache()
955 {
956 
957   /* Code. */
958 
959   cache_valid = False;
960 
961 
962   return;
963 
964 } /* xtmDbClearAccessCache */
965 
966 
967 /*----------------------------------------------------------------------*/
968 
969 void
xtmDbInitializeProcessId()970   xtmDbInitializeProcessId()
971 {
972 
973   /* Variables. */
974   int  status;
975 
976 
977   /* Code. */
978 
979   real_gid       = getgid();
980   privileged_gid = getegid();
981 
982   /* Go back to user level. */
983   status = setgid( getgid() );
984 
985 
986   return;
987 
988 } /* xtmDbInitializeProcessId */
989 
990 
991 /*----------------------------------------------------------------------*/
992 
993 void
xtmDbUseFileLock(Boolean use_file_lock)994   xtmDbUseFileLock( Boolean  use_file_lock )
995 {
996 
997   /* Code. */
998 
999   use_lock = use_file_lock;
1000 
1001 
1002   return;
1003 
1004 } /* xtmDbUseFileLock */
1005 
1006 
1007 /*----------------------------------------------------------------------*/
1008 
1009 Boolean
xtmDbDoesStandingMatch(XTM_DB_STAND_ENTRY_REF stand_ref,UINT32 flags,Boolean check_non_wday,TIM_TIME_REF date)1010   xtmDbDoesStandingMatch( XTM_DB_STAND_ENTRY_REF  stand_ref,
1011                           UINT32                  flags,
1012                           Boolean                 check_non_wday,
1013                           TIM_TIME_REF            date )
1014 {
1015 
1016   /* Variables. */
1017   Boolean         action_non_wday = False;
1018   Boolean         display_today = False;
1019   Boolean         workday = True;
1020   int             day_in_week;
1021   UINT32          stand_flags;
1022   TIM_DELTA_TYPE  delta;
1023   TIM_TIME_REF    today;
1024 
1025 
1026   /* Code. */
1027 
1028   day_in_week = TimIndexOfDayInIsoWeek( date );
1029   today       = TimLocalTime( TimMakeTimeNow() );
1030 
1031   today = TimMakeTime( TimIndexOfYear(  today ),
1032                        TimIndexOfMonth( today ),
1033                        TimIndexOfDay(   today ),
1034                        0, 0, 0 );
1035 
1036   if( TimIsSameDate( today, date ) == TIM_YES )
1037     display_today = True;
1038 
1039   workday = xtmHoIsWorkday( date );
1040 
1041   stand_flags = stand_ref -> flags;
1042 
1043   if( check_non_wday )
1044     action_non_wday= flagIsSet( stand_flags, XTM_DB_FLAG_SE_NWDAY_PREV |
1045                                              XTM_DB_FLAG_SE_NWDAY_NEXT |
1046                                              XTM_DB_FLAG_SE_NWDAY_SKIP );
1047 
1048 
1049   /* Only for standing and sticky entries. */
1050   if( flagIsClear( flags, XTM_DB_FETCH_STANDING ) &&
1051       flagIsClear( flags, XTM_DB_FETCH_STICKY ) )
1052     return( False );
1053 
1054 
1055   /* Fetch all standing entries? */
1056   if( flagIsSet( flags, XTM_DB_FETCH_ALL_STANDING ) )
1057     return( True );
1058 
1059 
1060   /* Sticky entries only valid today OR if from and to dates are defined,
1061      valid on the limit days. */
1062   if( flagIsSet( stand_ref -> flags, XTM_DB_FLAG_SE_STICKY ) ) {
1063 
1064     if( display_today ) {
1065 
1066       if( stand_ref -> from == 0 && stand_ref -> to == 0 )
1067         return( True );
1068 
1069       if( (stand_ref -> from != 0 && date < stand_ref -> from) ||
1070           (stand_ref -> to   != 0 && date > stand_ref -> to) )
1071         return( False );
1072       else
1073         return( True );
1074     }
1075 
1076     if( stand_ref -> from != 0 &&
1077         stand_ref -> from >  today &&
1078         stand_ref -> from == date )
1079       return( True );
1080 
1081     else if( stand_ref -> to != 0 &&
1082              stand_ref -> to <  today &&
1083              stand_ref -> from == date )
1084       return( True );
1085 
1086     else
1087       return( False );
1088 
1089   } /* if */
1090 
1091 
1092   /* Is the entry outside the defined range? */
1093   if( (stand_ref -> from != 0 && date < stand_ref -> from) ||
1094       (stand_ref -> to   != 0 && date > stand_ref -> to) )
1095     return( False );
1096 
1097 
1098   /* Valid this week? */
1099   {
1100 
1101     int     week_no;
1102     UINT32  week_flags;
1103     UINT32  skip_flag;
1104 
1105     week_no = TimIndexOfIsoWeek( date );
1106 
1107     skip_flag = (1 << (week_no % 30));
1108     if( week_no > 30 )
1109       week_flags = stand_ref -> skip_week[ 0 ];
1110     else
1111       week_flags = stand_ref -> skip_week[ 1 ];
1112 
1113     if( flagIsSet( week_flags, skip_flag ) )
1114       return( False );
1115 
1116   } /* block */
1117 
1118 
1119   /* Check action to take on holidays. */
1120   if( ! workday && action_non_wday )
1121     return( False );
1122 
1123 
1124   /* Special action for workdays? */
1125   if( action_non_wday ) {
1126 
1127     TIM_TIME_REF  check_date;
1128 
1129     /* Move to next workday. */
1130     if( flagIsSet( stand_flags, XTM_DB_FLAG_SE_NWDAY_NEXT ) ) {
1131 
1132       check_date = date;
1133       TimAddDays( &check_date, -1 );
1134 
1135       while( ! xtmHoIsWorkday( check_date ) ) {
1136         if( xtmDbDoesStandingMatch( stand_ref, flags, False, check_date ) )
1137           return( True );
1138 
1139         TimAddDays( &check_date, -1 );
1140       }
1141 
1142     } /* if */
1143 
1144     /* Move to previous workday. */
1145     if( flagIsSet( stand_flags, XTM_DB_FLAG_SE_NWDAY_PREV ) ) {
1146 
1147       check_date = date;
1148       TimAddDays( &check_date, 1 );
1149 
1150       while( ! xtmHoIsWorkday( check_date ) ) {
1151         if( xtmDbDoesStandingMatch( stand_ref, flags, False, check_date ) )
1152           return( True );
1153 
1154         TimAddDays( &check_date, 1 );
1155       }
1156 
1157     } /* if */
1158 
1159   } /* if */
1160 
1161 
1162 
1163   /* Is this the nth week day in month? */
1164   if( flagIsSet( stand_flags, XTM_DB_FLAG_SE_DAY_IN_MONTH ) ) {
1165 
1166     int           day_index;
1167     int           index;
1168     int           offset = 1;
1169     int           this_month;
1170     int           weeks = 1;
1171     TIM_TIME_REF  tmp_date;
1172 
1173     /* We check all valid days, might be more than one. */
1174     for( day_index = 0; day_index < 7; day_index++ ) {
1175 
1176       /* Valid day? */
1177       if( ! stand_ref -> valid_days[ day_index ] ||
1178           (day_index + 1) != day_in_week )
1179         continue;
1180 
1181       /* Correct day in month? */
1182       if( flagIsSet( stand_flags, XTM_DB_FLAG_SE_1ST ) ) {
1183         offset = (-1);
1184         weeks  = 1;
1185       } else if( flagIsSet( stand_flags, XTM_DB_FLAG_SE_2ND ) ) {
1186         offset = (-1);
1187         weeks  = 2;
1188       } else if( flagIsSet( stand_flags, XTM_DB_FLAG_SE_3RD ) ) {
1189         offset = (-1);
1190         weeks  = 3;
1191       } else if( flagIsSet( stand_flags, XTM_DB_FLAG_SE_1ST_LAST ) ) {
1192         offset = 1;
1193         weeks  = 1;
1194       } else if( flagIsSet( stand_flags, XTM_DB_FLAG_SE_2ND_LAST ) ) {
1195         offset = 1;
1196         weeks  = 2;
1197       } else if( flagIsSet( stand_flags, XTM_DB_FLAG_SE_3RD_LAST ) ) {
1198         offset = 1;
1199         weeks  = 3;
1200       } else {
1201         return( False );
1202       }
1203 
1204       tmp_date   = date;
1205       this_month = TimIndexOfMonth( date );
1206 
1207       for( index = 1; index <= weeks; index++ ) {
1208         TimAddDays( &tmp_date, offset * 7 );
1209         if( TimIndexOfMonth( tmp_date ) != this_month )
1210           break;
1211       }
1212 
1213       if( index == weeks )
1214         return( True );
1215 
1216     } /* loop */
1217 
1218     return( False );
1219 
1220   } /* if */
1221 
1222 
1223   /* Check single valid days. */
1224   if( stand_ref -> every_n == 0 &&
1225       stand_ref -> valid_days[ day_in_week - 1 ] )
1226     return( True );
1227 
1228 
1229   /* Valid every n days? */
1230   if( stand_ref -> valid_every == XTM_DB_VALID_DAY ) {
1231     TimDelta( stand_ref -> from, date, &delta );
1232 
1233     if( delta.days % stand_ref -> every_n == 0 )
1234       return( True );
1235     else
1236       return( False );
1237   }
1238 
1239 
1240   /* Valid every n weeks? */
1241   if( stand_ref -> valid_every == XTM_DB_VALID_WEEK ) {
1242     TimDelta( stand_ref -> from, date, &delta );
1243 
1244     if( delta.weeks % stand_ref -> every_n == 0 &&
1245         delta.days  % 7 == 0 )
1246       return( True );
1247     else
1248       return( False );
1249   } /* if */
1250 
1251 
1252   /* Valid every n months? */
1253   if( stand_ref -> valid_every == XTM_DB_VALID_MONTH ) {
1254 
1255     int  check_date;
1256     int  check_month;
1257     int  days_in_month;
1258     int  today_date;
1259     int  today_month;
1260 
1261     check_date    = TimIndexOfDay(   stand_ref -> from );
1262     check_month   = TimIndexOfMonth( stand_ref -> from );
1263     days_in_month = TimDaysInMonth(  date );
1264     today_date    = TimIndexOfDay(   date );
1265     today_month   = TimIndexOfMonth( date );
1266 
1267     if( abs( check_month - today_month ) % stand_ref -> every_n != 0 )
1268       return( False );
1269 
1270     if( check_date == today_date ||
1271         (check_date > days_in_month && today_date == days_in_month) )
1272       return( True );
1273     else
1274       return( False );
1275 
1276   } /* if */
1277 
1278 
1279   /* Valid last in month? */
1280   if( stand_ref -> valid_every == XTM_DB_VALID_MONTH_LAST ) {
1281 
1282     int  check_month;
1283     int  today_month;
1284 
1285     check_month = TimIndexOfMonth( stand_ref -> from );
1286     today_month = TimIndexOfMonth( date );
1287 
1288     if( abs( check_month - today_month ) % stand_ref -> every_n != 0 )
1289       return( False );
1290 
1291     if( TimIndexOfDay( date ) == TimDaysInMonth( date ) )
1292       return( True );
1293 
1294     return( False );
1295 
1296   } /* if */
1297 
1298 
1299   /* Valid every n years? */
1300   if( stand_ref -> valid_every == XTM_DB_VALID_YEAR ) {
1301 
1302     int  check_date;
1303     int  check_month;
1304     int  days_in_month;
1305     int  today_date;
1306     int  today_month;
1307 
1308     check_date    = TimIndexOfDay(   stand_ref -> from );
1309     check_month   = TimIndexOfMonth( stand_ref -> from );
1310     today_date    = TimIndexOfDay(   date );
1311     today_month   = TimIndexOfMonth( date );
1312     days_in_month = TimDaysInMonth(  date );
1313 
1314     if( abs( TimIndexOfYear( stand_ref -> from ) -
1315              TimIndexOfYear( date ) ) % stand_ref -> every_n != 0 )
1316       return( False );
1317 
1318     if( check_month == today_month ) {
1319       if( check_date == today_date ||
1320         (check_date > days_in_month && check_date == days_in_month) )
1321         return( True );
1322       else
1323         return( False );
1324     }
1325 
1326   } /* if */
1327 
1328 
1329   return( False );
1330 
1331 } /* xtmDbDoesStandingMatch */
1332 
1333 
1334 /*----------------------------------------------------------------------*/
1335 
1336 Boolean
xtmDbIsEntryDefined(XTM_DB_ENTRY_DATABASES * database,LST_DESC_TYPE * stand_entries,TIM_TIME_REF cal_date)1337   xtmDbIsEntryDefined( XTM_DB_ENTRY_DATABASES  *database,
1338                        LST_DESC_TYPE           *stand_entries,
1339                        TIM_TIME_REF            cal_date )
1340 {
1341 
1342   /* Variables. */
1343   int                   flags;
1344   int                   index;
1345   LST_DESC_TYPE         entries;
1346   LST_STATUS            lst_status;
1347   XTM_DB_ALL_ENTRY_DEF  entry_record;
1348   XTM_DB_DATE_DEF       date_record;
1349   XTM_DB_STATUS         db_status;
1350 
1351 
1352   /* Code. */
1353 
1354   cal_date = TimMakeTime( TimIndexOfYear(  cal_date ),
1355                           TimIndexOfMonth( cal_date ),
1356                           TimIndexOfDay(   cal_date ),
1357                           0, 0, 0 );
1358 
1359   /* Any normal entries defined this day? */
1360   db_status = fetchDate( database -> date_db, cal_date, &date_record );
1361   if( db_status == XTM_DB_OK )
1362     return( True );
1363 
1364   /* Check standing entries? */
1365   if( stand_entries == NULL ||
1366       (*stand_entries == NULL && *(stand_entries + 1) == NULL) )
1367     return( False );
1368 
1369 
1370   flags = (XTM_DB_FETCH_STICKY | XTM_DB_FETCH_STANDING);
1371 
1372   /* Standing entries are notes and appointments. */
1373   for( index = 0; index < 2; index++ ) {
1374 
1375     entries = *(stand_entries + index);
1376 
1377     /* Any entries defined? */
1378     if( entries == NULL || LstLinkElements( entries ) <= 0 )
1379       continue;
1380 
1381     /* Check the entries in the list. */
1382     lst_status = LstLinkCurrentFirst( entries );
1383     while( lst_status == LST_OK ) {
1384 
1385       lst_status = LstLinkGetCurrent( entries, &entry_record );
1386 
1387       /* Do we have a match? */
1388       if( flagIsClear( entry_record.stand_entry.flags,
1389                        XTM_DB_FLAG_SE_HIDE_IN_CALENDAR ) ) {
1390 
1391         if( xtmDbDoesStandingMatch( &entry_record.stand_entry,
1392                                     flags, True, cal_date ) )
1393           return( True );
1394 
1395       } /* if */
1396 
1397       /* Next entry. */
1398       lst_status = LstLinkCurrentNext( entries );
1399 
1400     } /* while */
1401 
1402   } /* loop */
1403 
1404 
1405   return( False );
1406 
1407 } /* xtmDbIsEntryDefined */
1408 
1409 
1410 /*----------------------------------------------------------------------*/
1411 
1412 Boolean
xtmToIsStandEntryDefined(XTM_GL_BASE_DATA_REF appl_data_ref,LST_DESC_TYPE * stand_entries,TIM_TIME_REF cal_date)1413   xtmToIsStandEntryDefined( XTM_GL_BASE_DATA_REF  appl_data_ref,
1414                             LST_DESC_TYPE         *stand_entries,
1415                             TIM_TIME_REF          cal_date )
1416 {
1417 
1418   /* Variables. */
1419   int                   flags;
1420   int                   index;
1421   LST_DESC_TYPE         entries;
1422   LST_STATUS            lst_status;
1423   XTM_DB_ALL_ENTRY_DEF  entry_record;
1424 
1425 
1426   /* Code. */
1427 
1428   if( stand_entries == NULL )
1429     return( False );
1430 
1431   flags = (XTM_DB_FETCH_STICKY | XTM_DB_FETCH_STANDING);
1432 
1433   /* Standing entries are notes and appointments. */
1434   for( index = 0; index < 2; index++ ) {
1435 
1436     entries = *(stand_entries + index);
1437 
1438     /* Any entries defined? */
1439     if( entries == NULL || LstLinkElements( entries ) <= 0 )
1440       continue;
1441 
1442     /* Check the entries in the list. */
1443     lst_status = LstLinkCurrentFirst( entries );
1444     while( lst_status == LST_OK ) {
1445 
1446       lst_status = LstLinkGetCurrent( entries, &entry_record );
1447 
1448       /* Do we have a match? */
1449       if( xtmDbDoesStandingMatch( &entry_record.stand_entry,
1450                                   flags, True, cal_date ) )
1451         return( True );
1452 
1453       /* Next entry. */
1454       lst_status = LstLinkCurrentNext( entries );
1455 
1456     } /* while */
1457 
1458   } /* loop */
1459 
1460   return( False );
1461 
1462 } /* xtmToIsStandEntryDefined */
1463 
1464 
1465 /*----------------------------------------------------------------------*/
1466 
1467 XTM_DB_STATUS
xtmDbCloseEntryDb(XTM_DB_ENTRY_DATABASES * entry_databases)1468   xtmDbCloseEntryDb( XTM_DB_ENTRY_DATABASES *entry_databases )
1469 {
1470 
1471   /* Variables. */
1472   int  status;
1473 
1474 
1475   /* Code. */
1476 
1477   /* Close entry database. */
1478   if( entry_databases -> entry_db != NULL )
1479     closeDatabase( entry_databases -> entry_db );
1480 
1481 
1482   /* Close date database. */
1483   if( entry_databases -> date_db != NULL )
1484     closeDatabase( entry_databases -> date_db );
1485 
1486 
1487   /* Close standing entries database. */
1488   if( entry_databases -> stand_entry_db != NULL )
1489     closeDatabase( entry_databases -> stand_entry_db );
1490 
1491 
1492   /* Close private entries database. */
1493   if( entry_databases -> private_db != NULL )
1494     closeDatabase( entry_databases -> private_db );
1495 
1496 
1497   /* Is the database locked? */
1498   if( entry_databases -> locked && use_lock )
1499     freeFileLock( &entry_databases -> lock_fd );
1500 
1501   entry_databases -> locked = False;
1502 
1503   /* Go to user mode? */
1504   if( entry_databases -> set_id )
1505     status = setgid( real_gid );
1506 
1507 
1508   return( XTM_DB_OK );
1509 
1510 } /* xtmDbCloseEntryDb */
1511 
1512 
1513 /*----------------------------------------------------------------------*/
1514 
1515 XTM_DB_STATUS
xtmDbOpenEntryDb(XTM_DB_OPEN_REQUEST * open_request,XTM_DB_ENTRY_DATABASES * entry_databases)1516   xtmDbOpenEntryDb( XTM_DB_OPEN_REQUEST     *open_request,
1517                     XTM_DB_ENTRY_DATABASES  *entry_databases )
1518 {
1519 
1520   /* Variables. */
1521   int                  db_dir_size;
1522   int                  status;
1523   UINT32               operations;
1524   char                 filename[ 150 ];
1525   XTM_DB_OPEN_REQUEST  single_open_req;
1526   XTM_DB_STATUS        db_status;
1527 
1528 
1529   /* Code. */
1530 
1531   strcpy( entry_databases -> name, open_request -> name );
1532 
1533   /* Check the operations that can be done. */
1534   xtmDbCheckDbOperations( open_request -> directory, False, &operations );
1535 
1536   single_open_req.directory    = open_request -> directory;
1537   single_open_req.lock_timeout = open_request -> lock_timeout;
1538   single_open_req.operations   = open_request -> operations;
1539 
1540 
1541   /* Go to privileged mode? */
1542   if( flagIsSet( operations, XTM_DB_FLAG_MODE_SETID ) )
1543     status = setgid( privileged_gid );
1544 
1545   /* Get a file lock. */
1546   entry_databases -> locked = False;
1547 
1548   sprintf( filename, "%s/%s", open_request -> directory, LOCK_FILE );
1549 
1550   db_status = createFileLock( filename,
1551                               open_request -> operations,
1552                               &entry_databases -> lock_fd,
1553                               &open_request -> uid_locking );
1554   if( db_status != XTM_DB_OK )
1555     return( db_status );
1556 
1557   entry_databases -> locked = True;
1558 
1559 
1560   /* Open the entry database. */
1561   single_open_req.database    = XTM_DB_ENTRY_DB;
1562   entry_databases -> entry_db = openDatabase( &single_open_req );
1563 
1564   if( entry_databases -> entry_db == NULL )
1565     return( XTM_DB_ERROR );
1566 
1567 
1568   /* Open the date database. */
1569   single_open_req.database   = XTM_DB_DATE_DB;
1570   entry_databases -> date_db = openDatabase( &single_open_req );
1571 
1572   if( entry_databases -> date_db == NULL )
1573     return( XTM_DB_ERROR );
1574 
1575 
1576   /* Open the standing entries database. */
1577   single_open_req.database          = XTM_DB_STAND_ENTRY_DB;
1578   entry_databases -> stand_entry_db = openDatabase( &single_open_req );
1579 
1580   if( entry_databases -> stand_entry_db == NULL )
1581     return( XTM_DB_ERROR );
1582 
1583 
1584   /* We don't open the private database (only if needed later). */
1585   entry_databases -> private_db = NULL;
1586 
1587 
1588   /* Save the database directory. */
1589   db_dir_size = sizeof( entry_databases -> database_dir );
1590 
1591   strncpy( entry_databases -> database_dir, open_request -> directory,
1592            db_dir_size - 1 );
1593 
1594   entry_databases -> database_dir[ db_dir_size - 1 ] = '\0';
1595 
1596 
1597   /* Operations allowed? */
1598   entry_databases -> operations = operations;
1599 
1600 
1601   /* Privileged mode? */
1602   if( flagIsSet( operations, XTM_DB_FLAG_MODE_SETID ) )
1603     entry_databases -> set_id = True;
1604   else
1605     entry_databases -> set_id = False;
1606 
1607 
1608   return( XTM_DB_OK );
1609 
1610 } /* xtmDbOpenEntryDb */
1611 
1612 
1613 /*----------------------------------------------------------------------*/
1614 
1615 XTM_DB_STATUS
xtmDbDeleteEntry(XTM_DB_ENTRY_DATABASES * databases,UINT32 id)1616   xtmDbDeleteEntry( XTM_DB_ENTRY_DATABASES  *databases,
1617                     UINT32                  id )
1618 {
1619 
1620   /* Variables. */
1621   XTM_DB_STATUS         status;
1622   XTM_DB_ALL_ENTRY_DEF  entry_record;
1623 
1624 
1625   /* Code. */
1626 
1627   /* All necessary databases must be open. */
1628   if( databases -> entry_db       == NULL ||
1629       databases -> date_db        == NULL ||
1630       databases -> stand_entry_db == NULL )
1631     return( XTM_DB_ERROR );
1632 
1633 
1634   /* Fetch the entry record (we need some data there). */
1635   status = fetchEntry( databases -> entry_db, id, &entry_record.entry );
1636   if( status != XTM_DB_OK )
1637     return( status );
1638 
1639 
1640   /* Delete entry in the private database? */
1641   (void) deletePrivData( databases, &entry_record );
1642 
1643 
1644   /* If an external file is used, delete it. */
1645   status = deleteTextFile( databases -> database_dir, id );
1646 
1647 
1648   /* Delete the entry record. */
1649   status = deleteEntry( databases -> entry_db, id );
1650   if( status != XTM_DB_OK )
1651     return( status );
1652 
1653 
1654   /* If this is a normal entry, delete the date reference. */
1655   if( entry_record.entry.entry_category == XTM_DB_ENTRY_LIST ) {
1656     status = deleteEntryIdInDate( databases -> date_db,
1657                                   id,
1658                                   entry_record.entry.date_stamp );
1659     if( status != XTM_DB_OK )
1660       return( status );
1661   } /* if */
1662 
1663 
1664   /* Delete the standing entry record? */
1665   if( entry_record.entry.entry_category == XTM_DB_REP_ENTRY_LIST ||
1666       entry_record.entry.entry_category == XTM_DB_STICKY_LIST ) {
1667 
1668     status = deleteStandEntry( databases -> stand_entry_db, id );
1669     if( status != XTM_DB_OK )
1670       return( status );
1671 
1672   } /* if */
1673 
1674 
1675   /* Update the log file. */
1676   updateDbLog( databases -> database_dir, id, XTM_DB_FLAG_LOG_DELETE );
1677 
1678 
1679   return( XTM_DB_OK );
1680 
1681 } /* xtmDbDeleteEntry */
1682 
1683 
1684 /*----------------------------------------------------------------------*/
1685 
1686 XTM_DB_STATUS
xtmDbFetchEntry(XTM_DB_ENTRY_DATABASES * databases,UINT32 id,XTM_DB_ALL_ENTRY_REF entry_ref,char ** text_ref)1687   xtmDbFetchEntry( XTM_DB_ENTRY_DATABASES  *databases,
1688                    UINT32                  id,
1689                    XTM_DB_ALL_ENTRY_REF    entry_ref,
1690                    char                    **text_ref )
1691 {
1692 
1693   /* Variables. */
1694   int            index;
1695   XTM_DB_STATUS  status;
1696 
1697 
1698   /* Code. */
1699 
1700   entry_ref -> all_text = NULL;
1701 
1702   strcpy( entry_ref -> db_name, databases -> name );
1703 
1704   /* Fetch the entry. */
1705   status = fetchEntry( databases -> entry_db, id, &entry_ref -> entry );
1706   if( status != XTM_DB_OK )
1707     return( status );
1708 
1709 
1710   /* Reset the standing part. */
1711   entry_ref -> stand_entry.id             = 0;
1712   entry_ref -> stand_entry.from           = 0;
1713   entry_ref -> stand_entry.to             = 0;
1714   entry_ref -> stand_entry.every_n        = 0;
1715   entry_ref -> stand_entry.valid_every    = 0;
1716   entry_ref -> stand_entry.skip_week[ 0 ] = 0;
1717   entry_ref -> stand_entry.skip_week[ 1 ] = 0;
1718 
1719   for( index = 0; index < 7; index++ )
1720     entry_ref -> stand_entry.valid_days[ index ] = 0;
1721 
1722 
1723   /* If this is a standing or sticky entry, fetch the extra info. */
1724   if( entry_ref -> entry.entry_category == XTM_DB_REP_ENTRY_LIST ||
1725       entry_ref -> entry.entry_category == XTM_DB_STICKY_LIST ) {
1726 
1727     status = fetchStandEntry( databases -> stand_entry_db, id,
1728                               &entry_ref -> stand_entry );
1729     if( status != XTM_DB_OK )
1730       return( status );
1731 
1732   } /* if */
1733 
1734 
1735   /* Is the entry in the private area? */
1736   if( flagIsSet( entry_ref -> entry.flags, XTM_DB_FLAG_IN_PRIV_DB ) ) {
1737 
1738     status = fetchPrivData( databases, id, entry_ref, text_ref );
1739     if( status == XTM_DB_OK )
1740       return( XTM_DB_OK );
1741 
1742   } /* if */
1743 
1744 
1745   /* Do we want to fetch the text separate? */
1746   if( text_ref == NULL )
1747     return( XTM_DB_OK );
1748 
1749 
1750   /* Fetch text in an external file? */
1751   *text_ref = NULL;
1752 
1753   if( flagIsSet( entry_ref -> entry.flags, XTM_DB_FLAG_EXT_FILE ) ) {
1754 
1755     status = fetchTextFile( databases -> database_dir, id, text_ref );
1756     if( status != XTM_DB_OK )
1757       return( status );
1758 
1759   } /* if */
1760 
1761 
1762   return( XTM_DB_OK );
1763 
1764 } /* xtmDbFetchEntry */
1765 
1766 
1767 /*----------------------------------------------------------------------*/
1768 
1769 XTM_DB_STATUS
xtmDbInsertEntry(XTM_DB_ENTRY_DATABASES * databases,XTM_DB_ALL_ENTRY_REF entry_ref,char * text_ref)1770   xtmDbInsertEntry( XTM_DB_ENTRY_DATABASES  *databases,
1771                     XTM_DB_ALL_ENTRY_REF    entry_ref,
1772                     char                    *text_ref )
1773 {
1774 
1775   /* Variables. */
1776   XTM_DB_STATUS  status;
1777 
1778 
1779   /* Code. */
1780 
1781   status = doInsertEntry( databases,
1782                           entry_ref, text_ref,
1783                           XTM_DB_FLAG_LOG_SAVE );
1784 
1785 
1786   return( status );
1787 
1788 } /* xtmDbInsertEntry */
1789 
1790 
1791 /*----------------------------------------------------------------------*/
1792 
1793 XTM_DB_STATUS
xtmDbFetchDates(XTM_DB_ENTRY_DATABASES * databases,LST_DESC_TYPE * list_ref)1794   xtmDbFetchDates( XTM_DB_ENTRY_DATABASES  *databases,
1795                    LST_DESC_TYPE           *list_ref )
1796 {
1797 
1798   /* Variables. */
1799   LST_STATUS        lst_status;
1800   XTM_DB_STATUS     status;
1801   XTM_DB_DATE_DEF   record;
1802 
1803 
1804   /* Code. */
1805 
1806   /* Create a linked list to containg the dates. */
1807   *list_ref = LstLinkNew( sizeof( record ), NULL );
1808 
1809   /* Fetch the first key in the database. */
1810   status = fetchFirstDate( databases -> date_db, &record );
1811 
1812   while( status == XTM_DB_OK ) {
1813 
1814     /* Insert the date in the sorted list. */
1815     lst_status = LstLinkSearchFirst( *list_ref,
1816                                      (void *)(uintptr_t)record.date,
1817                                      (EQUALS_FUNC_TYPE) dateSortFunc );
1818 
1819     if( lst_status == LST_OK )
1820       lst_status = LstLinkInsertCurrent( *list_ref, &record );
1821     else
1822       lst_status = LstLinkInsertLast( *list_ref, &record );
1823 
1824     /* The next date. */
1825     status = fetchNextDate( databases -> date_db, &record );
1826 
1827   } /* loop */
1828 
1829 
1830   return( XTM_DB_OK );
1831 
1832 } /* xtmDbFetchDates */
1833 
1834 
1835 /*----------------------------------------------------------------------*/
1836 
1837 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)1838   xtmDbFetchEntriesInDay( XTM_DB_ENTRY_DATABASES  *databases,
1839                           TIM_TIME_REF            date,
1840                           UINT32                  flags,
1841                           LST_DESC_TYPE           *entry_list_ref,
1842                           LST_DESC_TYPE           *note_list_ref )
1843 {
1844 
1845   /* Variables. */
1846   int                     index;
1847   char                    *text_ref;
1848   LST_STATUS              lst_status;
1849   XTM_DB_STATUS           status;
1850   XTM_DB_DATE_DEF         date_record;
1851   XTM_DB_ALL_ENTRY_DEF    entry_record;
1852   XTM_DB_STAND_ENTRY_DEF  stand_record;
1853 
1854 
1855   /* Code. */
1856 
1857   /* Create a linked list to containg the entries. */
1858   if( flagIsClear( flags, XTM_DB_FETCH_NO_NEW_LIST ) ) {
1859     *entry_list_ref = LstLinkNew( sizeof( XTM_DB_ALL_ENTRY_DEF ), NULL );
1860     *note_list_ref  = LstLinkNew( sizeof( XTM_DB_ALL_ENTRY_DEF ), NULL );
1861   }
1862 
1863   /* Fetch the entry id defined for this day. */
1864   status = fetchDate( databases -> date_db, date, &date_record );
1865 
1866 
1867   /* Fetch all entries defined this day. */
1868   if( status == XTM_DB_OK ) {
1869     for( index = 0; index < XTM_DB_DATE_ID_SIZE; index++ ) {
1870 
1871       /* Do we have an entry? */
1872       if( date_record.id[ index ] != 0 ) {
1873 
1874         /* Fetch the entry. */
1875         if( flagIsSet( flags, XTM_DB_FETCH_ALL_TEXT ) ) {
1876           status = xtmDbFetchEntry( databases,
1877                                     date_record.id[ index ],
1878                                     &entry_record, &text_ref );
1879           entry_record.all_text = text_ref;
1880         } else {
1881           status = xtmDbFetchEntry( databases,
1882                                     date_record.id[ index ],
1883                                     &entry_record, NULL );
1884         }
1885 
1886         if( status != XTM_DB_OK )
1887           continue;
1888 
1889         /* Is this a merged entry? */
1890         if( flagIsSet( flags, XTM_DB_FETCH_INCLUDE ) )
1891           flagSet( entry_record.entry.flags, XTM_DB_FLAG_INCLUDE );
1892 
1893 
1894         /* Insert in the appointment list or in the note list. */
1895         switch( entry_record.entry.entry_type ) {
1896           case XTM_DB_DAY_NOTE:
1897             lst_status = LstLinkSearchFirst(
1898                            *note_list_ref,
1899                            (void *)(uintptr_t)entry_record.entry.time_stamp,
1900                            (EQUALS_FUNC_TYPE) entryIdSortFunc );
1901 
1902             if( lst_status == LST_OK )
1903               lst_status = LstLinkInsertCurrent( *note_list_ref,
1904                                                  &entry_record );
1905             else
1906               lst_status = LstLinkInsertLast( *note_list_ref,
1907                   &entry_record );
1908             break;
1909 
1910           case XTM_DB_DAY_ENTRY:
1911             lst_status = LstLinkSearchFirst(
1912                            *entry_list_ref,
1913                            (void *)(uintptr_t)entry_record.entry.time_stamp,
1914                            (EQUALS_FUNC_TYPE) entryTimeSortFunc );
1915 
1916             if( lst_status == LST_OK )
1917               lst_status = LstLinkInsertCurrent( *entry_list_ref,
1918                                                  &entry_record );
1919             else
1920               lst_status = LstLinkInsertLast( *entry_list_ref,
1921                                               &entry_record );
1922             break;
1923 
1924           default:
1925             fprintf( stderr, "xtmDbTools: xdfe() Unknown entry type %d\n",
1926                      entry_record.entry.entry_type );
1927             return( XTM_DB_ERROR );
1928         } /* switch */
1929 
1930       } /* if */
1931 
1932     } /* loop */
1933 
1934   } /* if */
1935 
1936 
1937   /* Fetch all standing and sticky entries this date? */
1938   if( flagIsClear(flags, XTM_DB_FETCH_STANDING ) &&
1939       flagIsClear(flags, XTM_DB_FETCH_STICKY ) )
1940     return( XTM_DB_OK );
1941 
1942   status = fetchFirstStand( databases -> stand_entry_db, &stand_record );
1943 
1944   while( status == XTM_DB_OK ) {
1945 
1946     Boolean  match;
1947 
1948     /* Code. */
1949 
1950     /* Does the standing entry fit into the current date? */
1951     match = xtmDbDoesStandingMatch( &stand_record, flags, True, date );
1952 
1953     /* Did we have a match? */
1954     if( match ) {
1955 
1956       /* Fetch the entry record. */
1957       if( flagIsSet( flags, XTM_DB_FETCH_ALL_TEXT ) ) {
1958         status = xtmDbFetchEntry( databases,
1959                                   stand_record.id,
1960                                   &entry_record, &text_ref );
1961         entry_record.all_text = text_ref;
1962       } else {
1963         status = xtmDbFetchEntry( databases,
1964                                   stand_record.id,
1965                                   &entry_record, NULL );
1966       }
1967 
1968       if( status != XTM_DB_OK )
1969         return( status );
1970 
1971       /* Copy the standing entry information. */
1972       memcpy( &entry_record.stand_entry, &stand_record,
1973               sizeof( XTM_DB_STAND_ENTRY_DEF ) );
1974 
1975 
1976       /* Is this a merged entry? */
1977       if( flagIsSet( flags, XTM_DB_FETCH_INCLUDE ) )
1978         flagSet( entry_record.entry.flags, XTM_DB_FLAG_INCLUDE );
1979 
1980 
1981       /* Insert in the appointment list or in the note list. */
1982       switch( entry_record.entry.entry_type ) {
1983 
1984         case XTM_DB_DAY_NOTE:
1985           lst_status = LstLinkSearchFirst(
1986                          *note_list_ref,
1987                          (void *)(uintptr_t)entry_record.entry.time_stamp,
1988                          (EQUALS_FUNC_TYPE) entryIdSortFunc );
1989 
1990           if( lst_status == LST_OK )
1991             lst_status = LstLinkInsertCurrent( *note_list_ref,
1992                                                &entry_record );
1993           else
1994             lst_status = LstLinkInsertLast( *note_list_ref,
1995               &entry_record );
1996           break;
1997 
1998         case XTM_DB_DAY_ENTRY:
1999           lst_status = LstLinkSearchFirst(
2000                          *entry_list_ref,
2001                          (void *)(uintptr_t)entry_record.entry.time_stamp,
2002                          (EQUALS_FUNC_TYPE) entryTimeSortFunc );
2003 
2004           if( lst_status == LST_OK )
2005             lst_status = LstLinkInsertCurrent( *entry_list_ref,
2006                                                &entry_record );
2007           else
2008             lst_status = LstLinkInsertLast( *entry_list_ref,
2009               &entry_record );
2010           break;
2011 
2012       default:
2013         fprintf( stderr, "xtmDbTools: xdfe() Unknown stand entry type %d\n",
2014                  entry_record.entry.entry_type );
2015         return( XTM_DB_ERROR );
2016 
2017       } /* switch */
2018 
2019     } /* if */
2020 
2021     /* Next standing entry. */
2022     status = fetchNextStand( databases -> stand_entry_db, &stand_record );
2023 
2024   } /* while */
2025 
2026 
2027   return( XTM_DB_OK );
2028 
2029 } /* xtmDbFetchEntriesInDay */
2030 
2031 
2032 /*----------------------------------------------------------------------*/
2033 
2034 XTM_DB_STATUS
xtmDbFetchStandEntries(XTM_DB_ENTRY_DATABASES * databases,LST_DESC_TYPE * entry_list_ref,LST_DESC_TYPE * note_list_ref)2035   xtmDbFetchStandEntries( XTM_DB_ENTRY_DATABASES  *databases,
2036                           LST_DESC_TYPE           *entry_list_ref,
2037                           LST_DESC_TYPE           *note_list_ref )
2038 {
2039 
2040   /* Variables. */
2041   LST_STATUS              lst_status;
2042   XTM_DB_STATUS           status;
2043   XTM_DB_ALL_ENTRY_DEF    entry_record;
2044   XTM_DB_STAND_ENTRY_DEF  stand_record;
2045 
2046 
2047 
2048   /* Code. */
2049 
2050   /* Create a linked list to containg the entries. */
2051   *entry_list_ref = LstLinkNew( sizeof( XTM_DB_ALL_ENTRY_DEF ), NULL );
2052   *note_list_ref  = LstLinkNew( sizeof( XTM_DB_ALL_ENTRY_DEF ), NULL );
2053 
2054   /* Fetch all standing entries. */
2055   status = fetchFirstStand( databases -> stand_entry_db, &stand_record );
2056 
2057   while( status == XTM_DB_OK ) {
2058 
2059     /* Fetch the entry record. */
2060     status = fetchEntry( databases -> entry_db, stand_record.id,
2061                          &entry_record.entry );
2062     if( status != XTM_DB_OK )
2063       return( status );
2064 
2065     /* Copy the standing entry information. */
2066     memcpy( &entry_record.stand_entry, &stand_record,
2067             sizeof( XTM_DB_STAND_ENTRY_DEF ) );
2068 
2069     /* Insert in the appointment list or in the note list. */
2070     switch( entry_record.entry.entry_type ) {
2071 
2072       case XTM_DB_DAY_NOTE:
2073         lst_status = LstLinkSearchFirst(
2074                        *note_list_ref,
2075                        (void *)(uintptr_t)entry_record.entry.time_stamp,
2076                        (EQUALS_FUNC_TYPE) entryIdSortFunc );
2077 
2078         if( lst_status == LST_OK )
2079           lst_status = LstLinkInsertCurrent( *note_list_ref,
2080                                              &entry_record );
2081         else
2082           lst_status = LstLinkInsertLast( *note_list_ref,
2083                           &entry_record );
2084         break;
2085 
2086       case XTM_DB_DAY_ENTRY:
2087         lst_status = LstLinkSearchFirst(
2088                        *entry_list_ref,
2089                        (void *)(uintptr_t)entry_record.entry.time_stamp,
2090                        (EQUALS_FUNC_TYPE) entryTimeSortFunc );
2091 
2092         if( lst_status == LST_OK )
2093           lst_status = LstLinkInsertCurrent( *entry_list_ref,
2094                                              &entry_record );
2095         else
2096           lst_status = LstLinkInsertLast( *entry_list_ref,
2097                         &entry_record );
2098         break;
2099 
2100     default:
2101       fprintf( stderr, "xtmDbTools: xdfse() Unknown entry type %d\n",
2102                entry_record.entry.entry_type );
2103       return( XTM_DB_ERROR );
2104 
2105     } /* switch */
2106 
2107     /* Next standing entry. */
2108     status = fetchNextStand( databases -> stand_entry_db, &stand_record );
2109 
2110   } /* while */
2111 
2112 
2113   return( XTM_DB_OK );
2114 
2115 } /* xtmDbFetchStandEntries */
2116 
2117 
2118 /*----------------------------------------------------------------------*/
2119 
2120 XTM_DB_STATUS
xtmDbDeleteMessage(char * db_dir,UINT32 msg_id)2121   xtmDbDeleteMessage( char    *db_dir,
2122                       UINT32  msg_id )
2123 {
2124 
2125   /* Variables. */
2126   int          status;
2127   char         filename[ 200 ];
2128   char         message_dir[ 100 ];
2129   struct stat  file_info;
2130 
2131 
2132   /* Code. */
2133 
2134   /* Do we have a message directory? */
2135   sprintf( message_dir, "%s/Message", db_dir );
2136 
2137   status = stat( message_dir, &file_info );
2138   if( status != 0 )
2139     sprintf( message_dir, "%s", db_dir );
2140 
2141 
2142   /* Search the message file. */
2143   sprintf( filename, "%s/%s_*_%d", message_dir, XTM_DB_MESSAGE_FILE, msg_id );
2144 
2145   status = DirFindFirst( filename, filename );
2146   if( status != 0 )
2147     return( XTM_DB_ERROR );
2148 
2149   DirEnd();
2150 
2151   /* Try to delete the file and ignore all errors. */
2152   status = unlink( filename );
2153 
2154 
2155   return( XTM_DB_OK );
2156 
2157 } /* xtmDbDeleteMessage */
2158 
2159 
2160 /*----------------------------------------------------------------------*/
2161 
2162 XTM_DB_STATUS
xtmDbFetchMessage(char * db_dir,UINT32 msg_id,XTM_DB_MESSAGE_REF msg_info,char ** message,char ** text)2163   xtmDbFetchMessage( char                *db_dir,
2164                      UINT32              msg_id,
2165                      XTM_DB_MESSAGE_REF  msg_info,
2166                      char                **message,
2167                      char                **text )
2168 {
2169 
2170   /* Variables. */
2171   int          file_ref;
2172   int          position;
2173   int          status;
2174   char         filename[ 200 ];
2175   char         message_dir[ 100 ];
2176   char         *char_ref;
2177   struct stat  file_info;
2178 
2179 
2180   /* Code. */
2181 
2182   *message = NULL;
2183   *text    = NULL;
2184 
2185   /* Do we have a message directory? */
2186   sprintf( message_dir, "%s/Message", db_dir );
2187 
2188   status = stat( message_dir, &file_info );
2189   if( status != 0 )
2190     sprintf( message_dir, "%s", db_dir );
2191 
2192   /* Search the message file. */
2193   sprintf( filename, "%s/%s_*_%d", message_dir, XTM_DB_MESSAGE_FILE, msg_id );
2194 
2195   status = DirFindFirst( filename, filename );
2196   if( status != 0 )
2197     return( XTM_DB_ERROR );
2198 
2199   DirEnd();
2200 
2201   /* Open the file for read. */
2202   file_ref = open( filename, O_RDONLY );
2203   if( file_ref == -1 )
2204     return( XTM_DB_ERROR );
2205 
2206   /* Read the message record. */
2207   lseek( file_ref, 0, SEEK_SET );
2208   read( file_ref, msg_info, sizeof( XTM_DB_MESSAGE_DEF ) );
2209 
2210   /* Read the message string. */
2211   if( msg_info -> message_length > 0 ) {
2212     position = sizeof( XTM_DB_MESSAGE_DEF );
2213     lseek( file_ref, position, SEEK_SET );
2214 
2215     char_ref = SysMalloc( msg_info -> message_length + 1 );
2216     *(char_ref + msg_info -> message_length) = '\0';
2217 
2218     read( file_ref, char_ref, msg_info -> message_length );
2219 
2220     *message = char_ref;
2221   }
2222 
2223   /* Read the text string. */
2224   if( msg_info -> text_length > 0 ) {
2225     position = sizeof( XTM_DB_MESSAGE_DEF ) + msg_info -> message_length;
2226     lseek( file_ref, position, SEEK_SET );
2227 
2228     char_ref = SysMalloc( msg_info -> text_length + 1 );
2229     *(char_ref + msg_info -> text_length) = '\0';
2230 
2231     read( file_ref, char_ref, msg_info -> text_length );
2232 
2233     *text = char_ref;
2234   }
2235 
2236   close( file_ref );
2237 
2238   return( XTM_DB_OK );
2239 
2240 } /* xtmDbFetchMessage */
2241 
2242 
2243 /*----------------------------------------------------------------------*/
2244 
2245 XTM_DB_STATUS
xtmDbInsertMessage(char * db_dir,XTM_DB_MESSAGE_REF msg_info,char * message,char * text)2246   xtmDbInsertMessage( char                *db_dir,
2247                       XTM_DB_MESSAGE_REF  msg_info,
2248                       char                *message,
2249                       char                *text )
2250 {
2251 
2252   /* Variables. */
2253   Boolean      back_to_normal = False;
2254   int          file_mode;
2255   int          file_ref;
2256   int          status;
2257   UINT32       new_id;
2258   UINT32       id;
2259   UINT32       operations;
2260   char         message_dir[ 100 ];
2261   char         filename[ 200 ];
2262   char         file_pattern[ 150 ];
2263   struct stat  file_info;
2264 
2265 
2266   /* Code. */
2267 
2268   /* Do we have a message directory? */
2269   sprintf( message_dir, "%s/Message", db_dir );
2270 
2271   status = stat( message_dir, &file_info );
2272   if( status != 0 )
2273     sprintf( message_dir, "%s", db_dir );
2274 
2275 
2276   /* Fetch id to use. */
2277   sprintf( file_pattern, "%s/%s_*_*", message_dir, XTM_DB_MESSAGE_FILE );
2278 
2279   new_id = 1;
2280 
2281   status = DirFindFirst( file_pattern, filename );
2282   while( status == 0 ) {
2283 
2284     int   items;
2285     char  *char_ref;
2286 
2287     /* Filter out the id of the file. */
2288     char_ref = strrchr( filename, '_' );
2289     if( char_ref == NULL )
2290       return( XTM_DB_ERROR );
2291 
2292     char_ref++;
2293 
2294     items = sscanf( char_ref, "%d", &id );
2295     if( items != 1 ) {
2296       DirEnd();
2297       return( XTM_DB_ERROR );
2298     }
2299 
2300     if( id > new_id )
2301       new_id = id;
2302 
2303     /* Next Message. */
2304     status = DirFindNext( filename );
2305 
2306   } /* while */
2307 
2308   new_id++;
2309 
2310   /* Check the operations that can be done. */
2311   xtmDbCheckDbOperations( db_dir, False, &operations );
2312 
2313   /* Go to privileged mode? */
2314   if( flagIsSet( operations, XTM_DB_FLAG_MODE_SETID ) &&
2315       getegid() != privileged_gid ) {
2316     status = setgid( privileged_gid );
2317     back_to_normal = True;
2318   }
2319 
2320 
2321   /* Fetch the directory data and define the mode for files. */
2322   sprintf( filename, "%s", message_dir );
2323 
2324   status = stat( filename, &file_info );
2325   if( status != 0 )
2326     raise exception;
2327 
2328   file_mode = file_info.st_mode & (~(S_IXUSR | S_IXGRP | S_IXOTH));
2329   file_mode = (file_mode | S_IROTH);
2330 
2331   /* Open the file for write. */
2332   sprintf( filename, "%s/%s_%s_%d",
2333            message_dir, XTM_DB_MESSAGE_FILE, msg_info -> from, new_id );
2334 
2335   file_ref = open( filename, (O_CREAT | O_RDWR) );
2336   if( file_ref == -1 )
2337     raise exception;
2338 
2339   /* Write the message record. */
2340   write( file_ref, msg_info, sizeof( XTM_DB_MESSAGE_DEF ) );
2341 
2342   /* Write the message string. */
2343   write( file_ref, message, msg_info -> message_length );
2344 
2345   /* Write the message text. */
2346   write( file_ref, text, msg_info -> text_length );
2347 
2348   close( file_ref );
2349 
2350 
2351   /* Let the file inherit the permissions from the directory. */
2352   status = chmod( filename, file_mode );
2353   if( status != 0 )
2354     raise exception;
2355 
2356 
2357   /* Back to normal uid? */
2358   if( back_to_normal )
2359     status = setgid( real_gid );
2360 
2361 
2362   return( XTM_DB_OK );
2363 
2364 
2365   exception:
2366     if( back_to_normal )
2367       status = setgid( real_gid );
2368 
2369     return( XTM_DB_ERROR );
2370 
2371 } /* xtmDbInsertMessage */
2372 
2373 
2374 /*----------------------------------------------------------------------*/
2375 
2376 static INT32
calcCrc(char * text)2377   calcCrc( char  *text )
2378 {
2379 
2380   /* Variables. */
2381   UINT32  hash = 0;
2382   UINT32  mask = 0;
2383   char    *char_ref;
2384 
2385 
2386   /* Code. */
2387 
2388   if( text == NULL )
2389     return( 0 );
2390 
2391   char_ref = text;
2392   while( *char_ref != '\0' ) {
2393 
2394     hash = (hash << 4) + (*char_ref);
2395     if( (mask = hash & 0xf0000000) ) {
2396       hash = hash ^ (mask >> 24);
2397       hash = hash ^ mask;
2398     }
2399     char_ref++;
2400   }
2401 
2402 
2403   return( hash % 211 );
2404 
2405 } /* calcCrc */
2406 
2407 
2408 /*----------------------------------------------------------------------*/
2409 
2410 static XTM_DB_STATUS
closeDatabase(XTM_DB_DATABASE database)2411   closeDatabase( XTM_DB_DATABASE database )
2412 {
2413 
2414   /* Code. */
2415 
2416   dbm_close( (DBM *) database );
2417 
2418   return( XTM_DB_OK );
2419 
2420 } /* closeDatabase */
2421 
2422 
2423 /*----------------------------------------------------------------------*/
2424 
2425 static XTM_DB_STATUS
createFileLock(char * filename,UINT32 operations,int * lock_fd,int * uid_locking)2426   createFileLock( char    *filename,
2427                   UINT32  operations,
2428                   int     *lock_fd,
2429                   int     *uid_locking )
2430 {
2431 
2432   /* Variables. */
2433   int           lock_type;
2434   int           file_mode;
2435   int           status;
2436   int           tries;
2437   struct flock  lock;
2438 
2439 
2440   /* Code. */
2441 
2442   *uid_locking = 0;
2443   *lock_fd     = -1;
2444 
2445   if( ! use_lock )
2446     return( XTM_DB_OK );
2447 
2448   /* Type of lock? */
2449   file_mode = O_RDONLY;
2450   lock_type = F_RDLCK;
2451 
2452   if( flagIsSet( operations, XTM_DB_FLAG_MODE_READ ) ) {
2453     file_mode = O_RDONLY;
2454     lock_type = F_RDLCK;
2455   }
2456 
2457   if( flagIsSet( operations, XTM_DB_FLAG_MODE_WRITE ) ) {
2458     file_mode = O_RDWR;
2459     lock_type = F_WRLCK;
2460   }
2461 
2462 
2463   /* Fetch file descriptor for lock file. */
2464   *lock_fd = open( filename, file_mode );
2465   if( *lock_fd < 0 )
2466     return( XTM_DB_LOCKED );
2467 
2468 
2469   /* Lock the file. */
2470   lock.l_type   = lock_type;
2471   lock.l_whence = SEEK_SET;
2472   lock.l_start  = 0;
2473   lock.l_len    = 0;
2474 
2475   tries = 0;
2476 
2477   while( tries < MAX_LOCK_TRIES ) {
2478     status = fcntl( *lock_fd, F_SETLK, &lock );
2479     if( status != -1 )
2480       break;
2481 
2482     sleep( 1 );
2483     tries++;
2484   }
2485 
2486   /* If we could not lock the file, return with error. */
2487   if( status == -1 ) {
2488 
2489     /* Who has the lock? */
2490     lock.l_type   = lock_type;
2491     lock.l_whence = SEEK_SET;
2492     lock.l_start  = 0;
2493     lock.l_len    = 0;
2494 
2495     status = fcntl( *lock_fd, F_GETLK, &lock );
2496 
2497     close( *lock_fd );
2498 
2499     *uid_locking = lock.l_pid;
2500     *lock_fd     = -1;
2501 
2502     return( XTM_DB_LOCKED );
2503 
2504   } /* if */
2505 
2506   return( XTM_DB_OK );
2507 
2508 } /* createFileLock */
2509 
2510 
2511 /*----------------------------------------------------------------------*/
2512 
2513 static LST_COMPARE
dateSortFunc(XTM_DB_DATE_DEF * element,TIM_TIME_REF date)2514   dateSortFunc( XTM_DB_DATE_DEF *element, TIM_TIME_REF  date )
2515 {
2516 
2517   /* Code. */
2518 
2519   if( element -> date > date )
2520     return( LST_EQUAL );
2521   else
2522     return( LST_NOT_EQUAL );
2523 
2524 } /* dateSortFunc */
2525 
2526 
2527 /*----------------------------------------------------------------------*/
2528 
2529 static XTM_DB_STATUS
deleteDate(XTM_DB_DATABASE database,TIM_TIME_REF date)2530   deleteDate( XTM_DB_DATABASE  database,
2531               TIM_TIME_REF     date )
2532 {
2533 
2534   /* Variables. */
2535   int              status;
2536   datum            key;
2537   XTM_DB_DATE_KEY  key_record;
2538 
2539 
2540   /* Code. */
2541 
2542   if( database == NULL )
2543     return( XTM_DB_ERROR );
2544 
2545 #ifdef NET_BYTE_ORDER
2546   key_record.date = htonl( date );
2547 #else
2548   key_record.date = date;
2549 #endif
2550 
2551   /* Create the key for the entry. */
2552   key.dptr  = (char *) &key_record;
2553   key.dsize = xtm_db_date_key_size;
2554 
2555   /* Delete the entry. */
2556   status = dbm_delete( (DBM *) database, key );
2557   if( status != 0 )
2558     return( XTM_DB_ERROR );
2559 
2560   return( XTM_DB_OK );
2561 
2562 } /* deleteDate */
2563 
2564 
2565 /*----------------------------------------------------------------------*/
2566 
2567 static XTM_DB_STATUS
deleteEntry(XTM_DB_DATABASE database,UINT32 id)2568   deleteEntry( XTM_DB_DATABASE  database,
2569                UINT32           id )
2570 {
2571 
2572   /* Variables. */
2573   int               status;
2574   datum             key;
2575   XTM_DB_ENTRY_KEY  key_record;
2576 
2577 
2578   /* Code. */
2579 
2580   if( database == NULL )
2581     return( XTM_DB_ERROR );
2582 
2583   /* Create the key for the entry. */
2584 #ifdef NET_BYTE_ORDER
2585   key_record.id = htonl( id );
2586 #else
2587   key_record.id = id;
2588 #endif
2589 
2590   key.dptr  = (char *) &key_record;
2591   key.dsize = xtm_db_entry_key_size;
2592 
2593   /* Delete the entry. */
2594   status = dbm_delete( (DBM *) database, key );
2595   if( status != 0 )
2596     return( XTM_DB_ERROR );
2597 
2598 
2599   return( XTM_DB_OK );
2600 
2601 } /* deleteEntry */
2602 
2603 
2604 /*----------------------------------------------------------------------*/
2605 
2606 static XTM_DB_STATUS
deleteEntryIdInDate(XTM_DB_DATABASE database_ref,UINT32 entry_id,TIM_TIME_REF date_stamp)2607   deleteEntryIdInDate( XTM_DB_DATABASE  database_ref,
2608                        UINT32           entry_id,
2609                        TIM_TIME_REF     date_stamp )
2610 {
2611 
2612   /* Variables. */
2613   int              id_found;
2614   int              entry_index;
2615   int              index;
2616   XTM_DB_DATE_DEF  date_record;
2617   XTM_DB_STATUS    status;
2618 
2619 
2620   /* Code. */
2621 
2622   /* Try to fetch the date record. */
2623   status = fetchDate( database_ref, date_stamp, &date_record );
2624   if( status != XTM_DB_OK )
2625     return( status );
2626 
2627   /* Do we have the entry id in the date record. */
2628   id_found    = 0;
2629   entry_index = -1;
2630 
2631   for( index = 0; index < XTM_DB_DATE_ID_SIZE; index++ ) {
2632     if( date_record.id[ index ] != 0 )
2633       id_found++;
2634 
2635     if( date_record.id[ index ] == entry_id )
2636       entry_index = index;
2637   }
2638 
2639   if( entry_index == -1 )
2640     return( XTM_DB_ERROR );
2641 
2642   /* Entry is not saved anymore. */
2643   date_record.id[ entry_index ] = 0;
2644 
2645   /* Any entries left? */
2646   if( id_found <= 1 )
2647     status = deleteDate( database_ref, date_stamp );
2648   else
2649     status = insertDate( database_ref, &date_record );
2650 
2651   if( status != XTM_DB_OK )
2652     return( status );
2653 
2654 
2655   return( XTM_DB_OK );
2656 
2657 } /* deleteEntryIdInDate */
2658 
2659 
2660 /*----------------------------------------------------------------------*/
2661 
2662 static XTM_DB_STATUS
deletePrivData(XTM_DB_ENTRY_DATABASES * databases,XTM_DB_ALL_ENTRY_REF entry_ref)2663   deletePrivData( XTM_DB_ENTRY_DATABASES  *databases,
2664                   XTM_DB_ALL_ENTRY_REF    entry_ref )
2665 {
2666 
2667   /* Variables. */
2668   XTM_DB_STATUS  status;
2669 
2670 
2671   /* Code. */
2672 
2673   if( flagIsClear( entry_ref -> entry.flags, XTM_DB_FLAG_IN_PRIV_DB ) )
2674     return( XTM_DB_ERROR );
2675 
2676 
2677   /* Is the data in an external file? */
2678   if( flagIsSet( entry_ref -> entry.flags, XTM_DB_FLAG_PRIV_EXT_FILE ) )
2679     status = deletePrivText( databases, entry_ref -> entry.id );
2680   else
2681     status = deletePrivDb( databases, entry_ref -> entry.id );
2682 
2683   if( status != XTM_DB_OK )
2684     return( status );
2685 
2686 
2687   return( XTM_DB_OK );
2688 
2689 } /* deletePrivData */
2690 
2691 
2692 /*----------------------------------------------------------------------*/
2693 
2694 static XTM_DB_STATUS
deletePrivDb(XTM_DB_ENTRY_DATABASES * databases,UINT32 id)2695   deletePrivDb( XTM_DB_ENTRY_DATABASES  *databases,
2696                 UINT32                  id )
2697 {
2698 
2699   /* Variables. */
2700   int                  status;
2701   datum                key;
2702   XTM_DB_OPEN_REQUEST  single_open_req;
2703   XTM_DB_PRIV_KEY      key_record;
2704 
2705 
2706   /* Code. */
2707 
2708   /* Open the database for write. */
2709   if( databases -> private_db == NULL ) {
2710     single_open_req.database     = XTM_DB_PRIV_ENTRY_DB;
2711     single_open_req.directory    = databases -> database_dir;
2712     single_open_req.lock_timeout = 0;
2713 
2714     if( flagIsSet( databases -> operations, XTM_DB_FLAG_MODE_WRITE ) )
2715       single_open_req.operations = XTM_DB_FLAG_MODE_WRITE;
2716     else
2717       single_open_req.operations = XTM_DB_FLAG_MODE_READ;
2718 
2719     databases -> private_db = openDatabase( &single_open_req );
2720     if( databases -> private_db == NULL )
2721       return( XTM_DB_ERROR );
2722   }
2723 
2724   /* Create the key for the entry. */
2725 #ifdef NET_BYTE_ORDER
2726   key_record.id = htonl( id );
2727 #else
2728   key_record.id = id;
2729 #endif
2730 
2731   key.dptr  = (char *) &key_record;
2732   key.dsize = xtm_db_priv_entry_key_size;
2733 
2734   /* Delete the entry. */
2735   status = dbm_delete( (DBM *) databases -> private_db, key );
2736   if( status != 0 )
2737     return( XTM_DB_ERROR );
2738 
2739 
2740   return( XTM_DB_OK );
2741 
2742 } /* deletePrivDb */
2743 
2744 
2745 /*----------------------------------------------------------------------*/
2746 
2747 static XTM_DB_STATUS
deletePrivText(XTM_DB_ENTRY_DATABASES * databases,UINT32 id)2748   deletePrivText( XTM_DB_ENTRY_DATABASES  *databases,
2749                   UINT32                  id )
2750 {
2751 
2752   /* Variables. */
2753   int    status;
2754   char   filename[ 200 ];
2755 
2756 
2757   /* Code. */
2758 
2759   sprintf( filename, "%s/Private/%d.txt", databases -> database_dir, id );
2760 
2761   /* Try to delete the file and ignore all errors. */
2762   status = unlink( filename );
2763 
2764 
2765   return( XTM_DB_OK );
2766 
2767 } /* deletePrivText */
2768 
2769 
2770 /*----------------------------------------------------------------------*/
2771 
2772 static XTM_DB_STATUS
deleteStandEntry(XTM_DB_DATABASE database,UINT32 id)2773   deleteStandEntry( XTM_DB_DATABASE  database,
2774                     UINT32           id )
2775 {
2776 
2777   /* Variables. */
2778   int                     status;
2779   datum                   key;
2780   XTM_DB_STAND_ENTRY_KEY  key_record;
2781 
2782 
2783   /* Code. */
2784 
2785   if( database == NULL )
2786     return( XTM_DB_ERROR );
2787 
2788   /* Create the key for the entry. */
2789 #ifdef NET_BYTE_ORDER
2790   key_record.id = htonl( id );
2791 #else
2792   key_record.id = id;
2793 #endif
2794 
2795   key.dptr  = (char *) &key_record;
2796   key.dsize = xtm_db_stand_entry_key_size;
2797 
2798   /* Delete the entry. */
2799   status = dbm_delete( (DBM *) database, key );
2800   if( status != 0 )
2801     return( XTM_DB_ERROR );
2802 
2803   return( XTM_DB_OK );
2804 
2805 } /* deleteStandEntry */
2806 
2807 
2808 /*----------------------------------------------------------------------*/
2809 
2810 static XTM_DB_STATUS
deleteTextFile(char * directory,UINT32 id)2811   deleteTextFile( char    *directory,
2812                   UINT32  id )
2813 {
2814 
2815   /* Variables. */
2816   int    status;
2817   char   filename[ 200 ];
2818 
2819 
2820   /* Code. */
2821 
2822   sprintf( filename, "%s/%d.txt", directory, id );
2823 
2824   /* Try to delete the file and ignore all errors. */
2825   status = unlink( filename );
2826 
2827   return( XTM_DB_OK );
2828 
2829 } /* deleteTextFile */
2830 
2831 
2832 /*----------------------------------------------------------------------*/
2833 
2834 static XTM_DB_STATUS
doInsertEntry(XTM_DB_ENTRY_DATABASES * databases,XTM_DB_ALL_ENTRY_REF entry_ref,char * text_ref,UINT32 log_flags)2835   doInsertEntry( XTM_DB_ENTRY_DATABASES  *databases,
2836                  XTM_DB_ALL_ENTRY_REF    entry_ref,
2837                  char                    *text_ref,
2838                  UINT32                  log_flags )
2839 {
2840 
2841   /* Variables. */
2842   char           *char_ref;
2843   XTM_DB_STATUS  status;
2844 
2845 
2846   /* Code. */
2847 
2848   /* All necessary databases must be open. */
2849   if( databases -> entry_db       == NULL ||
2850       databases -> date_db        == NULL ||
2851       databases -> stand_entry_db == NULL )
2852     return( XTM_DB_ERROR );
2853 
2854 
2855   /* Delete any old file. */
2856   if( flagIsSet( entry_ref -> entry.flags, XTM_DB_FLAG_EXT_FILE ) ) {
2857     (void) deleteTextFile( databases -> database_dir,
2858                            entry_ref -> entry.id );
2859 
2860     flagClear( entry_ref -> entry.flags, XTM_DB_FLAG_EXT_FILE );
2861   }
2862 
2863 
2864   /* CRC for th entry. */
2865   entry_ref -> entry.crc = calcCrc( text_ref );
2866 
2867   /* Entry tag cannot contain spaces. */
2868   char_ref = entry_ref -> entry.tag;
2869   while( isspace( *char_ref ) )
2870     char_ref++;
2871 
2872   if( *char_ref == '\0' )
2873     entry_ref -> entry.tag[ 0 ] = '\0';
2874 
2875   /* No alarms for notes. */
2876   if( entry_ref -> entry.entry_type == XTM_DB_DAY_NOTE )
2877     flagClear( entry_ref -> entry.flags, XTM_DB_FLAG_ALARM );
2878 
2879 
2880   /* Save text in record. */
2881   if( text_ref != NULL ) {
2882     strncpy( entry_ref -> entry.text, text_ref, XTM_DB_RECORD_TEXT_LEN );
2883     entry_ref -> entry.text[ XTM_DB_RECORD_TEXT_LEN ] = '\0';
2884   }
2885 
2886 
2887   /* Is this a private entry? */
2888   flagClear( entry_ref -> entry.flags, XTM_DB_FLAG_IN_PRIV_DB );
2889 
2890   if( flagIsSet( entry_ref -> entry.flags, XTM_DB_FLAG_PRIVATE ) )
2891     (void) insertPrivData( databases, entry_ref -> entry.id, entry_ref,
2892                            text_ref );
2893 
2894 
2895   /* Insert the long entry text? */
2896   if( text_ref != NULL ) {
2897 
2898     /* Insert the text in a separate file? */
2899     if( strlen( text_ref ) > XTM_DB_RECORD_TEXT_LEN ) {
2900       flagSet( entry_ref -> entry.flags, XTM_DB_FLAG_EXT_FILE );
2901 
2902       status = insertTextFile( databases -> database_dir,
2903                                entry_ref -> entry.id,
2904                                text_ref );
2905       if( status != XTM_DB_OK )
2906         return( status );
2907     }
2908 
2909   } /* if */
2910 
2911 
2912   /* If a sticky note is done, make it a normal note. */
2913   if( entry_ref -> entry.entry_category == XTM_DB_STICKY_LIST &&
2914       flagIsSet( entry_ref -> entry.flags, XTM_DB_FLAG_NOTE_DONE ) )
2915     entry_ref -> entry.entry_category = XTM_DB_ENTRY_LIST;
2916 
2917 
2918   /* Replace/create the new entry. */
2919   status = insertEntry( databases -> entry_db, &entry_ref -> entry );
2920   if( status != XTM_DB_OK )
2921     return( status );
2922 
2923 
2924   /* Delete any standing entry (will be re-created if necessary). */
2925   status = deleteStandEntry( databases -> stand_entry_db,
2926                              entry_ref -> entry.id );
2927 
2928   /* Standing entry? */
2929   if( entry_ref -> entry.entry_category == XTM_DB_REP_ENTRY_LIST ) {
2930 
2931     flagSet(   entry_ref -> stand_entry.flags, XTM_DB_FLAG_SE_STANDING );
2932     flagClear( entry_ref -> stand_entry.flags, XTM_DB_FLAG_SE_STICKY );
2933 
2934     if( flagIsSet( entry_ref -> entry.flags, XTM_DB_FLAG_HIDE_IN_CALENDAR ) )
2935       flagSet( entry_ref -> stand_entry.flags,
2936                XTM_DB_FLAG_SE_HIDE_IN_CALENDAR );
2937 
2938 
2939     /* Create/replace the standing entry. */
2940     status = insertStandEntry( databases -> stand_entry_db,
2941                                &entry_ref -> stand_entry );
2942     if( status != XTM_DB_OK )
2943       return( status );
2944 
2945   } /* if */
2946 
2947 
2948   if( entry_ref -> entry.entry_category == XTM_DB_STICKY_LIST &&
2949       flagIsClear( entry_ref -> entry.flags, XTM_DB_FLAG_NOTE_DONE ) ) {
2950 
2951     flagClear( entry_ref -> stand_entry.flags, XTM_DB_FLAG_SE_STANDING );
2952     flagSet(   entry_ref -> stand_entry.flags, XTM_DB_FLAG_SE_STICKY );
2953 
2954     /* Create/replace the standing entry. */
2955     status = insertStandEntry( databases -> stand_entry_db,
2956                                &entry_ref -> stand_entry );
2957     if( status != XTM_DB_OK )
2958       return( status );
2959 
2960   } /* if */
2961 
2962 
2963   /* Is this a normal entry? */
2964   if( entry_ref -> entry.entry_category == XTM_DB_ENTRY_LIST ) {
2965     status = insertEntryIdInDate( databases -> date_db,
2966                                   entry_ref -> entry.id,
2967                                   entry_ref -> entry.date_stamp );
2968 
2969     if( status != XTM_DB_OK )
2970       return( status );
2971   } /* if */
2972 
2973 
2974   /* Update the log file. */
2975   updateDbLog( databases -> database_dir, entry_ref -> entry.id, log_flags );
2976 
2977 
2978   return( XTM_DB_OK );
2979 
2980 } /* doInsertEntry */
2981 
2982 
2983 /*----------------------------------------------------------------------*/
2984 
2985 static LST_COMPARE
entryIdSortFunc(XTM_DB_ENTRY_DEF * element,UINT32 id)2986   entryIdSortFunc( XTM_DB_ENTRY_DEF *element, UINT32 id )
2987 {
2988 
2989   /* Code. */
2990 
2991   if( element -> id > id )
2992     return( LST_EQUAL );
2993   else
2994     return( LST_NOT_EQUAL );
2995 
2996 } /* entryIdSortFunc */
2997 
2998 
2999 /*----------------------------------------------------------------------*/
3000 
3001 static LST_COMPARE
entryTimeSortFunc(XTM_DB_ENTRY_DEF * element,TIM_TIME_REF date)3002   entryTimeSortFunc( XTM_DB_ENTRY_DEF *element, TIM_TIME_REF  date )
3003 {
3004 
3005   /* Code. */
3006 
3007   if( element -> time_stamp > date )
3008     return( LST_EQUAL );
3009   else
3010     return( LST_NOT_EQUAL );
3011 
3012 } /* entryTimeSortFunc */
3013 
3014 
3015 /*----------------------------------------------------------------------*/
3016 
3017 static XTM_DB_STATUS
fetchDate(XTM_DB_DATABASE database,TIM_TIME_REF date,XTM_DB_DATE_REF entry)3018   fetchDate( XTM_DB_DATABASE   database,
3019              TIM_TIME_REF      date,
3020              XTM_DB_DATE_REF   entry )
3021 {
3022 
3023   /* Variables. */
3024   int              index;
3025   datum            key;
3026   datum            contents;
3027   XTM_DB_DATE_KEY  key_record;
3028 
3029 
3030   /* Code. */
3031 
3032   if( database == NULL )
3033     return( XTM_DB_ERROR );
3034 
3035   /* Create the key for the entry. */
3036 #ifdef NET_BYTE_ORDER
3037   key_record.date = htonl( date );
3038 #else
3039   key_record.date = date;
3040 #endif
3041 
3042   key.dptr  = (char *) &key_record;
3043   key.dsize = xtm_db_date_key_size;
3044 
3045   /* Fetch the entry. */
3046   contents = dbm_fetch( (DBM *) database, key );
3047   if( contents.dptr == NULL )
3048     return( XTM_DB_ERROR );
3049 
3050   /* Copy the contents. */
3051   memcpy( entry, contents.dptr, xtm_db_date_def_size );
3052 
3053 #ifdef NET_BYTE_ORDER
3054   entry -> date = ntohl( entry -> date );
3055 
3056   for( index = 0; index < XTM_DB_DATE_ID_SIZE; index++ )
3057     entry -> id[ index ] = ntohl( entry -> id[ index ] );
3058 #endif
3059 
3060   return( XTM_DB_OK );
3061 
3062 } /* fetchDate */
3063 
3064 
3065 /*----------------------------------------------------------------------*/
3066 
3067 static XTM_DB_STATUS
fetchEntry(XTM_DB_DATABASE database,UINT32 id,XTM_DB_ENTRY_REF entry)3068   fetchEntry( XTM_DB_DATABASE   database,
3069               UINT32            id,
3070               XTM_DB_ENTRY_REF  entry )
3071 {
3072 
3073   /* Variables. */
3074   int               index;
3075   datum             key;
3076   datum             contents;
3077   XTM_DB_ENTRY_KEY  key_record;
3078 
3079 
3080   /* Code. */
3081 
3082   if( database == NULL )
3083     return( XTM_DB_ERROR );
3084 
3085   /* Create the key for the entry. */
3086 #ifdef NET_BYTE_ORDER
3087   key_record.id = htonl( id );
3088 #else
3089   key_record.id = id;
3090 #endif
3091 
3092   key.dptr  = (char *) &key_record;
3093   key.dsize = xtm_db_entry_key_size;
3094 
3095   /* Fetch the entry. */
3096   contents = dbm_fetch( (DBM *) database, key );
3097   if( contents.dptr == NULL )
3098     return( XTM_DB_ERROR );
3099 
3100   /* Copy the contents. */
3101   memcpy( entry, contents.dptr, xtm_db_entry_def_size );
3102 
3103 #ifdef NET_BYTE_ORDER
3104   entry -> id          = ntohl( entry -> id );
3105   entry -> time_stamp  = ntohl( entry -> time_stamp );
3106   entry -> date_stamp  = ntohl( entry -> date_stamp );
3107   entry -> last_update = ntohl( entry -> last_update );
3108   entry -> owner       = ntohl( entry -> owner );
3109   entry -> duration    = ntohs( entry -> duration );
3110   entry -> flags       = ntohl( entry -> flags );
3111 
3112   for( index = 0; index < 5; index++ )
3113     entry -> alarm_offset[ index ] = ntohs( entry -> alarm_offset[ index ] );
3114 
3115   entry -> crc = ntohl( entry -> crc);
3116 #endif
3117 
3118   return( XTM_DB_OK );
3119 
3120 } /* fetchEntry */
3121 
3122 
3123 /*----------------------------------------------------------------------*/
3124 
3125 static XTM_DB_STATUS
fetchFirstDate(XTM_DB_DATABASE database,XTM_DB_DATE_REF entry)3126   fetchFirstDate( XTM_DB_DATABASE   database,
3127                   XTM_DB_DATE_REF   entry )
3128 {
3129 
3130   /* Variables. */
3131   datum          contents;
3132   TIM_TIME_REF   date;
3133   XTM_DB_STATUS  status;
3134 
3135 
3136   /* Code. */
3137 
3138   if( database == NULL )
3139     return( XTM_DB_ERROR );
3140 
3141   /* Fetch the entry. */
3142   contents = dbm_firstkey( (DBM *) database );
3143   if( contents.dptr == NULL )
3144     return( XTM_DB_ERROR );
3145 
3146   /* Fetch the data. */
3147   memcpy( &date, contents.dptr, xtm_db_date_key_size );
3148 
3149 #ifdef NET_BYTE_ORDER
3150   date = ntohl( date );
3151 #endif
3152 
3153   status = fetchDate( database, date, entry );
3154 
3155   return( status );
3156 
3157 } /* fetchFirstDate */
3158 
3159 
3160 /*----------------------------------------------------------------------*/
3161 
3162 static XTM_DB_STATUS
fetchFirstStand(XTM_DB_DATABASE database,XTM_DB_STAND_ENTRY_REF entry)3163   fetchFirstStand( XTM_DB_DATABASE         database,
3164                    XTM_DB_STAND_ENTRY_REF  entry )
3165 {
3166 
3167   /* Variables. */
3168   int            id;
3169   datum          contents;
3170   XTM_DB_STATUS  status;
3171 
3172 
3173   /* Code. */
3174 
3175   if( database == NULL )
3176     return( XTM_DB_ERROR );
3177 
3178   /* Fetch the entry. */
3179   contents = dbm_firstkey( (DBM *) database );
3180   if( contents.dptr == NULL )
3181     return( XTM_DB_ERROR );
3182 
3183   /* Fetch the data. */
3184   memcpy( &id, contents.dptr, xtm_db_stand_entry_key_size );
3185 
3186 #ifdef NET_BYTE_ORDER
3187   id = ntohl( id );
3188 #endif
3189 
3190   status = fetchStandEntry( database, id, entry );
3191 
3192   return( status );
3193 
3194 } /* fetchFirstStand */
3195 
3196 
3197 /*----------------------------------------------------------------------*/
3198 
3199 static XTM_DB_STATUS
fetchNextDate(XTM_DB_DATABASE database,XTM_DB_DATE_REF entry)3200   fetchNextDate( XTM_DB_DATABASE   database,
3201                  XTM_DB_DATE_REF   entry )
3202 {
3203 
3204   /* Variables. */
3205   datum          contents;
3206   TIM_TIME_REF   date;
3207   XTM_DB_STATUS  status;
3208 
3209 
3210   /* Code. */
3211 
3212   if( database == NULL )
3213     return( XTM_DB_ERROR );
3214 
3215   /* Fetch the entry. */
3216   contents = dbm_nextkey( (DBM *) database );
3217   if( contents.dptr == NULL )
3218     return( XTM_DB_ERROR );
3219 
3220   /* Fetch the data. */
3221   memcpy( &date, contents.dptr, xtm_db_date_key_size );
3222 
3223 #ifdef NET_BYTE_ORDER
3224   date = ntohl( date );
3225 #endif
3226 
3227   status = fetchDate( database, date, entry );
3228 
3229   return( status );
3230 
3231 } /* fetchNextDate */
3232 
3233 
3234 /*----------------------------------------------------------------------*/
3235 
3236 static XTM_DB_STATUS
fetchNextStand(XTM_DB_DATABASE database,XTM_DB_STAND_ENTRY_REF entry)3237   fetchNextStand( XTM_DB_DATABASE         database,
3238                   XTM_DB_STAND_ENTRY_REF  entry )
3239 {
3240 
3241   /* Variables. */
3242   int            id;
3243   datum          contents;
3244   XTM_DB_STATUS  status;
3245 
3246 
3247   /* Code. */
3248 
3249   if( database == NULL )
3250     return( XTM_DB_ERROR );
3251 
3252   /* Fetch the entry. */
3253   contents = dbm_nextkey( (DBM *) database );
3254   if( contents.dptr == NULL )
3255     return( XTM_DB_ERROR );
3256 
3257   /* Fetch the data. */
3258   memcpy( &id, contents.dptr, xtm_db_stand_entry_key_size );
3259 
3260 #ifdef NET_BYTE_ORDER
3261   id = ntohl( id );
3262 #endif
3263 
3264   status = fetchStandEntry( database, id, entry );
3265 
3266   return( XTM_DB_OK );
3267 
3268 } /* fetchNextStand */
3269 
3270 
3271 /*----------------------------------------------------------------------*/
3272 
3273 static XTM_DB_STATUS
fetchPrivData(XTM_DB_ENTRY_DATABASES * databases,UINT32 id,XTM_DB_ALL_ENTRY_REF entry_ref,char ** text_ref)3274   fetchPrivData( XTM_DB_ENTRY_DATABASES  *databases,
3275                  UINT32                  id,
3276                  XTM_DB_ALL_ENTRY_REF    entry_ref,
3277                  char                    **text_ref )
3278 {
3279 
3280   /* Variables. */
3281   char           *db_text;
3282   XTM_DB_STATUS  status;
3283 
3284 
3285   /* Code. */
3286 
3287   /* Default private text. */
3288   strcpy( entry_ref -> entry.text, "<Private>" );
3289 
3290   if( flagIsClear( entry_ref -> entry.flags, XTM_DB_FLAG_IN_PRIV_DB ) )
3291     return( XTM_DB_ERROR );
3292 
3293 
3294   /* Is the data in an external file? */
3295   if( flagIsSet( entry_ref -> entry.flags, XTM_DB_FLAG_PRIV_EXT_FILE ) )
3296     status = fetchPrivText( databases, id, &db_text );
3297   else
3298     status = fetchPrivDb( databases, id, &db_text );
3299 
3300   if( status != XTM_DB_OK )
3301     return( status );
3302 
3303 
3304   /* Save the fetched text. */
3305   strncpy( entry_ref -> entry.text, db_text, XTM_DB_RECORD_TEXT_LEN );
3306   entry_ref -> entry.text[ XTM_DB_RECORD_TEXT_LEN ] = '\0';
3307 
3308   /* Any 'extra' text? */
3309   if( flagIsSet( entry_ref -> entry.flags, XTM_DB_FLAG_PRIV_EXT_FILE ) ) {
3310     *text_ref = db_text;
3311   } else {
3312     SysFree( db_text );
3313 
3314     if( text_ref != NULL )
3315       *text_ref = NULL;
3316   }
3317 
3318   flagClear( entry_ref -> entry.flags, XTM_DB_FLAG_EXT_FILE );
3319 
3320 
3321   return( XTM_DB_OK );
3322 
3323 } /* fetchPrivData */
3324 
3325 
3326 /*----------------------------------------------------------------------*/
3327 
3328 static XTM_DB_STATUS
fetchPrivDb(XTM_DB_ENTRY_DATABASES * databases,UINT32 id,char ** text_ref)3329   fetchPrivDb( XTM_DB_ENTRY_DATABASES  *databases,
3330                UINT32                  id,
3331                char                    **text_ref )
3332 {
3333 
3334   /* Variables. */
3335   datum                key;
3336   datum                contents;
3337   XTM_DB_OPEN_REQUEST  single_open_req;
3338   XTM_DB_PRIV_DEF      data_record;
3339   XTM_DB_PRIV_KEY      key_record;
3340 
3341 
3342   /* Code. */
3343 
3344   *text_ref = NULL;
3345 
3346   /* Open the database for read. */
3347   if( databases -> private_db == NULL ) {
3348     single_open_req.database     = XTM_DB_PRIV_ENTRY_DB;
3349     single_open_req.directory    = databases -> database_dir;
3350     single_open_req.lock_timeout = 0;
3351 
3352     if( flagIsSet( databases -> operations, XTM_DB_FLAG_MODE_WRITE ) )
3353       single_open_req.operations = XTM_DB_FLAG_MODE_WRITE;
3354     else
3355       single_open_req.operations = XTM_DB_FLAG_MODE_READ;
3356 
3357     databases -> private_db = openDatabase( &single_open_req );
3358     if( databases -> private_db == NULL )
3359       return( XTM_DB_ERROR );
3360   }
3361 
3362   /* Create the key for the entry. */
3363 #ifdef NET_BYTE_ORDER
3364   key_record.id = htonl( id );
3365 #else
3366   key_record.id = id;
3367 #endif
3368 
3369   key.dptr  = (char *) &key_record;
3370   key.dsize = xtm_db_priv_entry_key_size;
3371 
3372 
3373   /* Fetch the text. */
3374   contents = dbm_fetch( (DBM *) databases -> private_db, key );
3375 
3376   if( contents.dptr == NULL )
3377     return( XTM_DB_ERROR );
3378 
3379 
3380   /* Store the text here. */
3381   memcpy( (char *) &data_record, contents.dptr, xtm_db_priv_entry_def_size );
3382 
3383 #ifdef NET_BYTE_ORDER
3384   data_record.id = ntohl( data_record.id );
3385 #endif
3386 
3387   *text_ref = SysMalloc( XTM_DB_RECORD_TEXT_LEN );
3388 
3389   strcpy( *text_ref, data_record.text );
3390 
3391 
3392   return( XTM_DB_OK );
3393 
3394 } /* fetchPrivDb */
3395 
3396 
3397 /*----------------------------------------------------------------------*/
3398 
3399 static XTM_DB_STATUS
fetchPrivText(XTM_DB_ENTRY_DATABASES * databases,UINT32 id,char ** text_ref)3400   fetchPrivText( XTM_DB_ENTRY_DATABASES  *databases,
3401                  UINT32                  id,
3402                  char                    **text_ref )
3403 {
3404 
3405   /* Variables. */
3406   int          char_read;
3407   int          status;
3408   char         filename[ 200 ];
3409   FILE         *file_ref;
3410   struct stat  file_status;
3411 
3412 
3413   /* Code. */
3414 
3415   *text_ref = NULL;
3416 
3417   /* All private entries hide in the Private directory. */
3418   /* 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*/
3419   sprintf( filename, "%s/Private/%d.txt", databases -> database_dir, id );
3420 
3421 
3422   /* We need the size of the file. */
3423   status = stat( filename, &file_status );
3424   if( status != 0 )
3425     return( XTM_DB_ERROR );
3426 
3427 
3428   /* Allocate space for the file. */
3429   *text_ref = SysMalloc( file_status.st_size + 5 );
3430   if( *text_ref == NULL )
3431     return( XTM_DB_ERROR );
3432 
3433 
3434   /* Open the file and read the contents. */
3435   file_ref = fopen( filename, "r" );
3436   if( file_ref == NULL )
3437     return( XTM_DB_ERROR );
3438 
3439   char_read = fread( *text_ref, 1, file_status.st_size + 1, file_ref );
3440 
3441   fclose( file_ref );
3442 
3443   *(*text_ref + char_read) = '\0';
3444 
3445 
3446   return( XTM_DB_OK );
3447 
3448 } /* fetchPrivText */
3449 
3450 
3451 /*----------------------------------------------------------------------*/
3452 
3453 static XTM_DB_STATUS
fetchStandEntry(XTM_DB_DATABASE database,UINT32 id,XTM_DB_STAND_ENTRY_REF entry)3454   fetchStandEntry( XTM_DB_DATABASE         database,
3455                    UINT32                  id,
3456                    XTM_DB_STAND_ENTRY_REF  entry )
3457 {
3458 
3459   /* Variables. */
3460   datum                   key;
3461   datum                   contents;
3462   XTM_DB_STAND_ENTRY_KEY  key_record;
3463 
3464 
3465   /* Code. */
3466 
3467   if( database == NULL )
3468     return( XTM_DB_ERROR );
3469 
3470   /* Create the key for the entry. */
3471 #ifdef NET_BYTE_ORDER
3472   key_record.id = htonl( id );
3473 #else
3474   key_record.id = id;
3475 #endif
3476 
3477   key.dptr  = (char *) &key_record;
3478   key.dsize = xtm_db_stand_entry_key_size;
3479 
3480   /* Fetch the entry. */
3481   contents = dbm_fetch( (DBM *) database, key );
3482   if( contents.dptr == NULL )
3483     return( XTM_DB_ERROR );
3484 
3485   /* Copy the contents. */
3486   memcpy( entry, contents.dptr, xtm_db_stand_entry_def_size );
3487 
3488 #ifdef NET_BYTE_ORDER
3489   entry -> id             = ntohl( entry -> id );
3490   entry -> from           = ntohl( entry -> from );
3491   entry -> to             = ntohl( entry -> to );
3492   entry -> flags          = ntohl( entry -> flags );
3493   entry -> every_n        = ntohs( entry -> every_n );
3494   entry -> skip_week[ 0 ] = ntohl( entry -> skip_week[ 0 ] );
3495   entry -> skip_week[ 1 ] = ntohl( entry -> skip_week[ 1 ] );
3496 #endif
3497 
3498 
3499   return( XTM_DB_OK );
3500 
3501 } /* fetchStandEntry */
3502 
3503 
3504 /*----------------------------------------------------------------------*/
3505 
3506 static XTM_DB_STATUS
fetchTextFile(char * directory,UINT32 id,char ** text)3507   fetchTextFile( char     *directory,
3508                  UINT32   id,
3509                  char     **text )
3510 {
3511 
3512   /* Variables. */
3513   Boolean      back_to_normal = False;
3514   int          char_read;
3515   int          status;
3516   UINT32       operations;
3517   char         filename[ 200 ];
3518   FILE         *file_ref;
3519   struct stat  file_status;
3520 
3521 
3522   /* Code. */
3523 
3524   *text = NULL;
3525 
3526   /* Check the operations that can be done. */
3527   xtmDbCheckDbOperations( directory, False, &operations );
3528 
3529   /* Go to privileged mode? */
3530   if( flagIsSet( operations, XTM_DB_FLAG_MODE_SETID ) &&
3531       getegid() != privileged_gid ) {
3532     status = setgid( privileged_gid );
3533     back_to_normal = True;
3534   }
3535 
3536   sprintf( filename, "%s/%d.txt", directory, id );
3537 
3538   /* Does the file exist? */
3539   status = stat( filename, &file_status );
3540   if( status != 0 )
3541     raise exception;
3542 
3543   /* Allocate space for the file. */
3544   *text = SysMalloc( file_status.st_size + 5 );
3545   if( *text == NULL )
3546     raise exception;
3547 
3548   /* Open the file and read the contents. */
3549   file_ref = fopen( filename, "r" );
3550   if( file_ref == NULL )
3551     raise exception;
3552 
3553   char_read = fread( *text, 1, file_status.st_size + 1, file_ref );
3554 
3555   fclose( file_ref );
3556 
3557   *(*text + char_read) = '\0';
3558 
3559   /* Back to normal mode? */
3560   if( back_to_normal )
3561     status = setgid( real_gid );
3562 
3563   return( XTM_DB_OK );
3564 
3565 
3566   /* Exception handling. */
3567   exception:
3568     if( back_to_normal )
3569       status = setgid( real_gid );
3570 
3571     return( XTM_DB_ERROR );
3572 
3573 
3574 } /* fetchTextFile */
3575 
3576 
3577 /*----------------------------------------------------------------------*/
3578 
3579 static void
freeFileLock(int * lock_fd)3580   freeFileLock( int  *lock_fd )
3581 {
3582 
3583   /* Variables. */
3584   int           status;
3585   struct flock  lock;
3586 
3587 
3588   /* Code. */
3589 
3590   if( ! use_lock || *lock_fd < 0 )
3591     return;
3592 
3593   /* Unlock the file. */
3594   lock.l_type   = F_UNLCK;
3595   lock.l_whence = SEEK_SET;
3596   lock.l_start  = 0;
3597   lock.l_len    = 0;
3598 
3599   status = fcntl( *lock_fd, F_SETLK, &lock );
3600 
3601   /* If we could not unlock the file, print error message and continue. */
3602   if( status == -1 )
3603     fprintf( stderr, "xtmDbTools: ffl(), Cannot unlock file.\n" );
3604 
3605   close( *lock_fd );
3606 
3607   *lock_fd = 0;
3608 
3609   return;
3610 
3611 } /* freeFileLock */
3612 
3613 
3614 /*----------------------------------------------------------------------*/
3615 
3616 static XTM_DB_STATUS
insertDate(XTM_DB_DATABASE database,XTM_DB_DATE_REF entry)3617   insertDate( XTM_DB_DATABASE  database,
3618               XTM_DB_DATE_REF  entry )
3619 {
3620 
3621   /* Variables. */
3622   int     flags;
3623   int     index;
3624   int     status;
3625   UINT32  date;
3626   datum   key;
3627   datum   contents;
3628 
3629 
3630   /* Code. */
3631 
3632   if( database == NULL )
3633     return( XTM_DB_ERROR );
3634 
3635   /* Fill unused data with NULL. */
3636   memset( entry -> unused, 0, sizeof( entry -> unused ) );
3637 
3638   flags = DBM_INSERT;
3639 
3640   /* Create the key for the entry. */
3641 #ifdef NET_BYTE_ORDER
3642   date = htonl( entry -> date );
3643 #else
3644   date = entry -> date;
3645 #endif
3646 
3647   key.dptr  = (char *) &date;
3648   key.dsize = xtm_db_date_key_size;
3649 
3650   /* Create the contents for the entry. */
3651   contents.dptr  = (char *) entry;
3652   contents.dsize = xtm_db_date_def_size;
3653 
3654 #ifdef NET_BYTE_ORDER
3655   entry -> date = htonl( entry -> date );
3656 
3657   for( index = 0; index < XTM_DB_DATE_ID_SIZE; index++ )
3658     entry -> id[ index ] = htonl( entry -> id[ index ] );
3659 #endif
3660 
3661   /* Insert/replace the entry. */
3662   status = dbm_store( (DBM *) database, key, contents, flags );
3663 
3664   if( status != 0 ) {
3665     flags = DBM_REPLACE;
3666 
3667     status = dbm_store( (DBM *) database, key, contents, flags );
3668   }
3669 
3670 #ifdef NET_BYTE_ORDER
3671   entry -> date = ntohl( entry -> date );
3672 
3673   for( index = 0; index < XTM_DB_DATE_ID_SIZE; index++ )
3674     entry -> id[ index ] = ntohl( entry -> id[ index ] );
3675 #endif
3676 
3677   if( status != 0 )
3678     return( XTM_DB_ERROR );
3679 
3680 
3681   return( XTM_DB_OK );
3682 
3683 } /* insertDate */
3684 
3685 
3686 /*----------------------------------------------------------------------*/
3687 
3688 static XTM_DB_STATUS
insertEntry(XTM_DB_DATABASE database,XTM_DB_ENTRY_REF entry)3689   insertEntry( XTM_DB_DATABASE   database,
3690                XTM_DB_ENTRY_REF  entry )
3691 {
3692 
3693   /* Variables. */
3694   int               flags;
3695   int               index;
3696   int               status;
3697   datum             key;
3698   datum             contents;
3699   XTM_DB_ENTRY_KEY  key_record;
3700 
3701 
3702   /* Code. */
3703 
3704   if( database == NULL )
3705     return( XTM_DB_ERROR );
3706 
3707   /* Fill unused data with NULL. */
3708   memset( entry -> unused, 0, sizeof( entry -> unused ) );
3709 
3710   flags = DBM_INSERT;
3711 
3712   /* Set the owner of the entry. */
3713   entry -> owner = getuid();
3714 
3715   /* Last update. */
3716   entry -> last_update = (UINT32) TimMakeTimeNow();
3717 
3718   /* Create the key for the entry. */
3719 #ifdef NET_BYTE_ORDER
3720   key_record.id = htonl( entry -> id );
3721 #else
3722   key_record.id = entry -> id;
3723 #endif
3724 
3725   key.dptr  = (char *) &key_record;
3726   key.dsize = xtm_db_entry_key_size;
3727 
3728   /* Create the contents for the entry. */
3729   contents.dptr  = (char *) entry;
3730   contents.dsize = xtm_db_entry_def_size;
3731 
3732 #ifdef NET_BYTE_ORDER
3733   entry -> id          = htonl( entry -> id );
3734   entry -> time_stamp  = htonl( entry -> time_stamp );
3735   entry -> date_stamp  = htonl( entry -> date_stamp );
3736   entry -> last_update = htonl( entry -> last_update );
3737   entry -> owner       = htonl( entry -> owner );
3738   entry -> duration    = htons( entry -> duration );
3739   entry -> flags       = htonl( entry -> flags );
3740 
3741   for( index = 0; index < 5; index++ )
3742     entry -> alarm_offset[ index ] = htons( entry -> alarm_offset[ index ] );
3743 
3744   entry -> crc = htonl( entry -> crc );
3745 #endif
3746 
3747   /* Insert/replace the entry. */
3748   status = dbm_store( (DBM *) database, key, contents, flags );
3749 
3750   if( status != 0 ) {
3751     flags = DBM_REPLACE;
3752 
3753     status = dbm_store( (DBM *) database, key, contents, flags );
3754   }
3755 
3756 #ifdef NET_BYTE_ORDER
3757   entry -> id          = ntohl( entry -> id );
3758   entry -> time_stamp  = ntohl( entry -> time_stamp );
3759   entry -> date_stamp  = ntohl( entry -> date_stamp );
3760   entry -> last_update = ntohl( entry -> last_update );
3761   entry -> owner       = ntohl( entry -> owner );
3762   entry -> duration    = ntohs( entry -> duration );
3763   entry -> flags       = ntohl( entry -> flags );
3764 
3765   for( index = 0; index < 5; index++ )
3766     entry -> alarm_offset[ index ] = ntohs( entry -> alarm_offset[ index ] );
3767 
3768   entry -> crc = ntohl( entry -> crc );
3769 #endif
3770 
3771   if( status != 0 )
3772     return( XTM_DB_ERROR );
3773 
3774   return( XTM_DB_OK );
3775 
3776 } /* insertEntry */
3777 
3778 
3779 /*----------------------------------------------------------------------*/
3780 
3781 static XTM_DB_STATUS
insertEntryIdInDate(XTM_DB_DATABASE database_ref,UINT32 entry_id,TIM_TIME_REF date_stamp)3782   insertEntryIdInDate( XTM_DB_DATABASE  database_ref,
3783                        UINT32           entry_id,
3784                        TIM_TIME_REF     date_stamp )
3785 {
3786 
3787   /* Variables. */
3788   int                   index;
3789   XTM_DB_DATE_DEF       date_record;
3790   XTM_DB_STATUS         status;
3791 
3792 
3793   /* Code. */
3794 
3795   /* Try to fetch the date record. */
3796   status = fetchDate( database_ref, date_stamp, &date_record );
3797 
3798   if( status != XTM_DB_OK ) {
3799     memset( &date_record, 0, xtm_db_date_def_size );
3800 
3801     date_record.date = date_stamp;
3802   }
3803 
3804   /* Do we have the entry id in the date record. */
3805   for( index = 0; index < XTM_DB_DATE_ID_SIZE; index++ ) {
3806     if( date_record.id[ index ] == entry_id )
3807       return( XTM_DB_OK );
3808   }
3809 
3810   /* Add the entry id to the date record. */
3811   for( index = 0; index < XTM_DB_DATE_ID_SIZE; index++ ) {
3812     if( date_record.id[ index ] == 0 )
3813       break;
3814   }
3815 
3816   if( index == XTM_DB_DATE_ID_SIZE )
3817     return( XTM_DB_ERROR );
3818 
3819   /* Insert the new entry id and save the date record. */
3820   date_record.id[ index ] = entry_id;
3821 
3822   status = insertDate( database_ref, &date_record );
3823   if( status != XTM_DB_OK )
3824     return( status );
3825 
3826   return( XTM_DB_OK );
3827 
3828 } /* insertEntryIdInDate */
3829 
3830 
3831 /*----------------------------------------------------------------------*/
3832 
3833 static XTM_DB_STATUS
insertPrivData(XTM_DB_ENTRY_DATABASES * databases,UINT32 id,XTM_DB_ALL_ENTRY_REF entry_ref,char * text_ref)3834   insertPrivData( XTM_DB_ENTRY_DATABASES  *databases,
3835                   UINT32                  id,
3836                   XTM_DB_ALL_ENTRY_REF    entry_ref,
3837                   char                    *text_ref )
3838 {
3839 
3840   /* Variables. */
3841   Boolean        in_db;
3842   XTM_DB_STATUS  status;
3843 
3844 
3845   /* Code. */
3846 
3847   /* Any privileges to do this? */
3848   if( flagIsClear( databases -> operations, XTM_DB_FLAG_MODE_PRIV ) ) {
3849     flagClear( entry_ref -> entry.flags, XTM_DB_FLAG_PRIVATE );
3850 
3851     return( XTM_DB_OK );
3852   }
3853 
3854 
3855   /* Does the data fit into a database entry? */
3856   if( text_ref == NULL ) {
3857     status = insertPrivDb( databases, id, entry_ref -> entry.text );
3858     in_db  = True;
3859   } else if( strlen( text_ref ) <= XTM_DB_RECORD_TEXT_LEN ) {
3860     status = insertPrivDb( databases, id, text_ref );
3861     in_db  = True;
3862   } else {
3863     status = insertPrivText( databases, id, text_ref );
3864     in_db  = False;
3865   }
3866 
3867   if( status != XTM_DB_OK )
3868     return( status );
3869 
3870 
3871   flagSet(   entry_ref -> entry.flags, XTM_DB_FLAG_IN_PRIV_DB );
3872   flagClear( entry_ref -> entry.flags, XTM_DB_FLAG_EXT_FILE );
3873 
3874   /* If inserted in external file, flag this. */
3875   if( ! in_db )
3876     flagSet( entry_ref -> entry.flags, XTM_DB_FLAG_PRIV_EXT_FILE );
3877 
3878 
3879   /* Do not leave the original text. */
3880   strcpy( entry_ref -> entry.text, "<Private>" );
3881   if( text_ref != NULL )
3882     *text_ref = '\0';
3883 
3884 
3885   return( XTM_DB_OK );
3886 
3887 } /* insertPrivData */
3888 
3889 
3890 /*----------------------------------------------------------------------*/
3891 
3892 static XTM_DB_STATUS
insertPrivDb(XTM_DB_ENTRY_DATABASES * databases,UINT32 id,char * text_ref)3893   insertPrivDb( XTM_DB_ENTRY_DATABASES  *databases,
3894                 UINT32                  id,
3895                 char                    *text_ref )
3896 {
3897 
3898   /* Variables. */
3899   int                  flags;
3900   int                  status;
3901   datum                key;
3902   datum                contents;
3903   XTM_DB_OPEN_REQUEST  single_open_req;
3904   XTM_DB_PRIV_DEF      data_record;
3905   XTM_DB_PRIV_KEY      key_record;
3906 
3907 
3908   /* Code. */
3909 
3910   /* Open the database for write. */
3911   if( databases -> private_db == NULL ) {
3912     single_open_req.database     = XTM_DB_PRIV_ENTRY_DB;
3913     single_open_req.directory    = databases -> database_dir;
3914     single_open_req.lock_timeout = 0;
3915 
3916     if( flagIsSet( databases -> operations, XTM_DB_FLAG_MODE_WRITE ) )
3917       single_open_req.operations = XTM_DB_FLAG_MODE_WRITE;
3918     else
3919       single_open_req.operations = XTM_DB_FLAG_MODE_READ;
3920 
3921     databases -> private_db = openDatabase( &single_open_req );
3922     if( databases -> private_db == NULL )
3923       return( XTM_DB_ERROR );
3924   }
3925 
3926   /* Fill unused data with NULL. */
3927   memset( data_record.unused, 0, sizeof( data_record.unused ) );
3928 
3929   flags = DBM_INSERT;
3930 
3931   /* Create the key and contents of the entry. */
3932 #ifdef NET_BYTE_ORDER
3933   key_record.id  = htonl( id );
3934   data_record.id = htonl( id );
3935 #else
3936   key_record.id  = id;
3937   data_record.id = id;
3938 #endif
3939 
3940   strcpy( data_record.text, text_ref );
3941 
3942   key.dptr  = (char *) &key_record;
3943   key.dsize = xtm_db_priv_entry_key_size;
3944 
3945   contents.dptr  = (char *) &data_record;
3946   contents.dsize = xtm_db_priv_entry_def_size;
3947 
3948 
3949   /* Try insert/replace entry. */
3950   status = dbm_store( (DBM *) databases -> private_db, key,
3951                       contents, flags );
3952 
3953   if( status != 0 ) {
3954     flags = DBM_REPLACE;
3955 
3956     status = dbm_store( (DBM *) databases -> private_db, key,
3957                         contents, flags );
3958   }
3959 
3960   if( status != 0 )
3961     return( XTM_DB_ERROR );
3962 
3963 
3964   return( XTM_DB_OK );
3965 
3966 } /* insertPrivDb */
3967 
3968 
3969 /*----------------------------------------------------------------------*/
3970 
3971 static XTM_DB_STATUS
insertPrivText(XTM_DB_ENTRY_DATABASES * databases,UINT32 id,char * text_ref)3972   insertPrivText( XTM_DB_ENTRY_DATABASES  *databases,
3973                   UINT32                  id,
3974                   char                    *text_ref )
3975 {
3976 
3977   /* Variables. */
3978   int          char_written;
3979   int          file_mode;
3980   int          status;
3981   char         filename[ 200 ];
3982   FILE         *file_ref;
3983   struct stat  file_status;
3984 
3985 
3986   /* Code. */
3987 
3988   /* Fetch protection of the directory. */
3989   sprintf( filename, "%s/Private", databases -> database_dir );
3990 
3991   status = stat( filename, &file_status );
3992   if( status != 0 )
3993     return( XTM_DB_ERROR );
3994 
3995   file_mode = file_status.st_mode & (~(S_IXUSR | S_IXGRP | S_IXOTH));
3996 
3997 
3998   /* All private entries hide in the Private directory. */
3999   sprintf( filename, "%s/Private/%d.txt", databases -> database_dir, id );
4000 
4001 
4002   /* Open the file and write the contents. */
4003   file_ref = fopen( filename, "w" );
4004   if( file_ref == NULL )
4005     return( XTM_DB_ERROR );
4006 
4007   char_written = fwrite( text_ref, 1, strlen( text_ref ), file_ref );
4008 
4009   fclose( file_ref );
4010 
4011 
4012   /* Let the file inherit the permissions from the directory. */
4013   status = chmod( filename, file_mode );
4014   if( status != 0 )
4015     return( XTM_DB_ERROR );
4016 
4017 
4018   if( char_written <= 0 )
4019     return( XTM_DB_ERROR );
4020 
4021 
4022   return( XTM_DB_OK );
4023 
4024 } /* insertPrivText */
4025 
4026 
4027 /*----------------------------------------------------------------------*/
4028 
4029 static XTM_DB_STATUS
insertStandEntry(XTM_DB_DATABASE database,XTM_DB_STAND_ENTRY_REF entry)4030   insertStandEntry( XTM_DB_DATABASE        database,
4031                     XTM_DB_STAND_ENTRY_REF entry )
4032 {
4033 
4034   /* Variables. */
4035   int                     flags;
4036   int                     status;
4037   datum                   key;
4038   datum                   contents;
4039   XTM_DB_STAND_ENTRY_KEY  key_record;
4040 
4041 
4042   /* Code. */
4043 
4044   if( database == NULL )
4045     return( XTM_DB_ERROR );
4046 
4047   /* Fill unused data with NULL. */
4048   memset( entry -> unused, 0, sizeof( entry -> unused ) );
4049 
4050   flags = DBM_REPLACE;
4051 
4052   /* Create the key for the entry. */
4053 #ifdef NET_BYTE_ORDER
4054   key_record.id = htonl( entry -> id );
4055 #else
4056   key_record.id = entry -> id;
4057 #endif
4058 
4059   key.dptr  = (char *) &key_record;
4060   key.dsize = xtm_db_stand_entry_key_size;
4061 
4062   /* Create the contents for the entry. */
4063   contents.dptr  = (char *) entry;
4064   contents.dsize = xtm_db_stand_entry_def_size;
4065 
4066 #ifdef NET_BYTE_ORDER
4067   entry -> id             = htonl( entry -> id );
4068   entry -> from           = htonl( entry -> from );
4069   entry -> to             = htonl( entry -> to );
4070   entry -> flags          = htonl( entry -> flags );
4071   entry -> every_n        = htons( entry -> every_n );
4072   entry -> skip_week[ 0 ] = htonl( entry -> skip_week[ 0 ] );
4073   entry -> skip_week[ 1 ] = htonl( entry -> skip_week[ 1 ] );
4074 #endif
4075 
4076   /* Insert/replace the entry. */
4077   status = dbm_store( (DBM *) database, key, contents, flags );
4078 
4079   if( status != 0 ) {
4080     flags = DBM_REPLACE;
4081 
4082     status = dbm_store( (DBM *) database, key, contents, flags );
4083   }
4084 
4085 #ifdef NET_BYTE_ORDER
4086   entry -> id             = ntohl( entry -> id );
4087   entry -> from           = ntohl( entry -> from );
4088   entry -> to             = ntohl( entry -> to );
4089   entry -> flags          = ntohl( entry -> flags );
4090   entry -> every_n        = ntohs( entry -> every_n );
4091   entry -> skip_week[ 0 ] = ntohl( entry -> skip_week[ 0 ] );
4092   entry -> skip_week[ 1 ] = ntohl( entry -> skip_week[ 1 ] );
4093 #endif
4094 
4095   if( status != 0 )
4096     return( XTM_DB_ERROR );
4097 
4098 
4099   return( XTM_DB_OK );
4100 
4101 } /* insertStandEntry */
4102 
4103 
4104 /*----------------------------------------------------------------------*/
4105 
4106 static XTM_DB_STATUS
insertTextFile(char * directory,UINT32 id,char * text)4107   insertTextFile( char    *directory,
4108                   UINT32  id,
4109                   char    *text )
4110 {
4111 
4112   /* Variables. */
4113   int          char_written;
4114   int          file_mode;
4115   int          status;
4116   char         filename[ 200 ];
4117   FILE         *file_ref;
4118   struct stat  stat_data;
4119 
4120 
4121   /* Code. */
4122 
4123   /* Fetch the directory data and define the mode for files. */
4124   sprintf( filename, "%s", directory );
4125 
4126   status = stat( filename, &stat_data );
4127   if( status != 0 )
4128     return( XTM_DB_ERROR );
4129 
4130   file_mode = stat_data.st_mode & (~(S_IXUSR | S_IXGRP | S_IXOTH));
4131 
4132 
4133   /* Open the file and write the contents. */
4134   sprintf( filename, "%s/%d.txt", directory, id );
4135 
4136   file_ref = fopen( filename, "w" );
4137   if( file_ref == NULL )
4138     return( XTM_DB_ERROR );
4139 
4140   char_written = fwrite( text, 1, strlen( text ), file_ref );
4141 
4142   fclose( file_ref );
4143 
4144 
4145   /* Let the file inherit the permissions from the directory. */
4146   status = chmod( filename, file_mode );
4147   if( status != 0 )
4148     return( XTM_DB_ERROR );
4149 
4150 
4151   if( char_written <= 0 )
4152     return( XTM_DB_ERROR );
4153 
4154   return( XTM_DB_OK );
4155 
4156 } /* insertTextFile */
4157 
4158 
4159 /*----------------------------------------------------------------------*/
4160 
4161 static XTM_DB_DATABASE
openDatabase(XTM_DB_OPEN_REQUEST * open_request)4162   openDatabase( XTM_DB_OPEN_REQUEST  *open_request )
4163 {
4164 
4165   /* Variables. */
4166   int   flags;
4167   char  filename[ 200 ];
4168   char  *key_file;
4169   DBM   *dbm_ref;
4170 
4171 
4172   /* Code. */
4173 
4174   /* Get the name of the database. */
4175   switch( open_request -> database ) {
4176     case XTM_DB_ENTRY_DB:
4177       key_file  = db_filenames[ 0 ];
4178       break;
4179     case XTM_DB_DATE_DB:
4180       key_file  = db_filenames[ 3 ];
4181       break;
4182     case XTM_DB_STAND_ENTRY_DB:
4183       key_file  = db_filenames[ 6 ];
4184       break;
4185     case XTM_DB_PRIV_ENTRY_DB:
4186       key_file  = db_filenames[ 9 ];
4187       break;
4188     default:
4189       return( NULL );
4190   } /* switch */
4191 
4192   /* Open the database. */
4193   sprintf( filename, "%s/%s", open_request -> directory, key_file );
4194 
4195   flags = 0;
4196 
4197   if( flagIsSet( open_request -> operations, XTM_DB_FLAG_MODE_READ ) )
4198     flagSet( flags, O_RDONLY );
4199 
4200   if( flagIsSet( open_request -> operations, XTM_DB_FLAG_MODE_WRITE ) )
4201     flagSet( flags, O_RDWR );
4202 
4203   dbm_ref = dbm_open( filename, flags, 0660 );
4204 
4205   return( (XTM_DB_DATABASE) dbm_ref );
4206 
4207 } /* openDatabase */
4208 
4209 
4210 /*----------------------------------------------------------------------*/
4211 
4212 static Boolean
updateDbLog(char * directory,UINT32 entry_id,UINT32 flags)4213   updateDbLog( char    *directory,
4214                UINT32  entry_id,
4215                UINT32  flags )
4216 {
4217 
4218   /* Variables. */
4219   int                file_mode;
4220   int                status;
4221   char               filename[ 200 ];
4222   long               offset;
4223   struct stat        stat_data;
4224   FILE               *file_ref;
4225   XTM_DB_LOG_HEADER  header;
4226   XTM_DB_LOG_RECORD  log_record;
4227 
4228 
4229   /* Code. */
4230 
4231   sprintf( filename, "%s/%s", directory, LOG_FILE );
4232 
4233   /* Open the log file for write. */
4234   file_ref = fopen( filename, "r+" );
4235 
4236   /* If the file did not exist, create it. */
4237   if( file_ref == NULL ) {
4238 
4239     /* Let the log file inherit the directory protection. */
4240     status = stat( directory, &stat_data );
4241     if( status != 0 )
4242       return( False );
4243 
4244     file_mode = stat_data.st_mode & (~(S_IXUSR | S_IXGRP | S_IXOTH));
4245 
4246     /* Open the file. */
4247     file_ref = fopen( filename, "w" );
4248     if( file_ref == NULL )
4249       return( False );
4250 
4251     /* We start all over again. */
4252     header.last_changed    = (UINT32) TimMakeTimeNow();
4253     header.current_ref     = 0;
4254     header.records         = 0;
4255     header.max_log_records = XTM_DB_MAX_LOG_RECORDS;
4256 
4257 #ifdef NET_BYTE_ORDER
4258     header.last_changed    = htonl( header.last_changed );
4259     header.current_ref     = htonl( header.current_ref );
4260     header.records         = htonl( header.records );
4261     header.max_log_records = htonl( header.max_log_records );
4262 #endif
4263 
4264     memset( header.unused, 0, sizeof( header.unused ) );
4265 
4266     /* Write the record to the file. */
4267     rewind( file_ref );
4268 
4269     fwrite( (void *) &header, sizeof( header ), 1, file_ref );
4270 
4271     fclose( file_ref );
4272 
4273     /* Set file protection. */
4274     status = chmod( filename, file_mode );
4275     if( status != 0 )
4276       return( False );
4277 
4278     /* Re-open the file for append. */
4279     file_ref = fopen( filename, "r+" );
4280     if( file_ref == NULL )
4281       return( False );
4282 
4283   } /* if */
4284 
4285 
4286   /* Fetch the header record. */
4287   rewind( file_ref );
4288 
4289   fread( (void *) &header, sizeof( header ), 1, file_ref );
4290 
4291 #ifdef NET_BYTE_ORDER
4292   header.last_changed    = ntohl( header.last_changed );
4293   header.current_ref     = ntohl( header.current_ref );
4294   header.records         = ntohl( header.records );
4295   header.max_log_records = ntohl( header.max_log_records );
4296 #endif
4297 
4298   /* Create the log record. */
4299   log_record.time_stamp = (UINT32) TimMakeTimeNow();
4300   log_record.entry_id   = entry_id;
4301   log_record.flags      = flags;
4302   log_record.changed_by = (INT32) getuid();
4303 
4304 #ifdef NET_BYTE_ORDER
4305   log_record.time_stamp = htonl( log_record.time_stamp );
4306   log_record.entry_id   = htonl( log_record.entry_id );
4307   log_record.flags      = htonl( log_record.flags );
4308   log_record.changed_by = htonl( log_record.changed_by );
4309 #endif
4310 
4311 
4312   /* Save the log record. */
4313   offset = sizeof( header ) + header.current_ref * sizeof( log_record );
4314   fseek( file_ref, offset, 0 );
4315 
4316   fwrite( (void *) &log_record, sizeof( log_record ), 1, file_ref );
4317 
4318 
4319   /* Calculate the new positions for the next record. */
4320   header.current_ref++;
4321   if( header.current_ref >= header.max_log_records )
4322     header.current_ref = 0;
4323 
4324   header.records++;
4325 
4326 #ifdef NET_BYTE_ORDER
4327   header.last_changed    = htonl( header.last_changed );
4328   header.current_ref     = htonl( header.current_ref );
4329   header.records         = htonl( header.records );
4330   header.max_log_records = htonl( header.max_log_records );
4331 #endif
4332 
4333   /* Save the header record. */
4334   rewind( file_ref );
4335 
4336   fwrite( (void *) &header, sizeof( header ), 1, file_ref );
4337 
4338 
4339   /* That's it. */
4340   fclose( file_ref );
4341 
4342 
4343   return( True );
4344 
4345 } /* updateDbLog */
4346