1 /* $Id$
2    Written 1999 by Tobias Ernst and released do the Public Domain.
3    This file is part of NLTOOLS, the nodelist processor of the Husky fidonet
4    software project.
5 
6    Main program of the NLTOOLS.
7 */
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <assert.h>
12 #include <ctype.h>
13 #include <time.h>
14 #include <errno.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 
18 #include <huskylib/compiler.h>
19 
20 #ifdef HAS_IO_H
21 # include <io.h>
22 #endif
23 
24 #ifdef HAS_UNISTD_H
25 # include <unistd.h>
26 #endif
27 
28 #include <huskylib/dirlayer.h>
29 
30 #include <fidoconf/fidoconf.h>
31 #include <huskylib/xstr.h>
32 #include <fidoconf/common.h>
33 #include <huskylib/log.h>
34 
35 #ifdef USE_HPTZIP
36 # include <hptzip/hptzip.h>
37 #endif
38 
39 #include "nlstring.h"
40 #include "nlfind.h"
41 #include "nldate.h"
42 #include "julian.h"
43 #include "ulc.h"
44 #include "version.h"
45 
46 /* store the nldiff command name */
47 static char *differ = NULL;
48 
49 /*
50 # if 0
51 */
52 /* This is not needed now because I imported fexist.c from smapi, and the
53    routines in fexist.c are a little more intelligent than this one! */
54 /*
55 static int nl_fexist(char *filename)
56 {
57    FILE *f = fopen(filename, "rb");
58    if (f == NULL)
59       return 0;
60    fclose(f);
61    return 1;
62 }
63 
64 #else
65 #define nl_fexist fexist
66 #endif
67 */
68 
mk_uncompressdir(char * nldir)69 static char *mk_uncompressdir( char *nldir )
70 {
71   char *upath = NULL;
72   size_t l;
73   FILE *f;
74   l = strlen( nldir ) + 12;
75   /* construct a temporary directory name below nodelist dir */
76   xstrscat( &upath, nldir, "nlupdate.tmp", NULL );
77   adaptcase( upath );
78 
79   /* try to create it */
80   mymkdir( upath );
81 
82   w_log( LL_DIR, "Use temp dir: '%s'", upath );
83 
84   /* check if we can write to the directory */
85   nfree( upath );
86   xscatprintf( &upath, "%s%s%c%s", nldir, "nlupdate.tmp", PATH_DELIM, "nlupdate.flg" );
87   f = fopen( upath, "w" );
88   if( f == NULL )
89   {
90     fprintf( stderr, "%s\n", upath );
91     upath[l] = '\0';
92     w_log( LL_ERROR, "Cannot create/access temporary directory (%s): %s", upath,
93            strerror( errno ) );
94     free( upath );
95     return NULL;
96   }
97   fclose( f );
98   remove( upath );
99   upath[l + 1] = '\0';
100 
101   return upath;
102 }
103 
destroy_uncompressdir(char * upath)104 int destroy_uncompressdir( char *upath )
105 {
106   size_t l = strlen( upath );
107   char *dfile;
108   husky_DIR *hdir;
109 
110   hdir = husky_opendir( upath );
111   if( hdir == NULL )
112   {
113     w_log( LL_ERROR, "Cannot read temporary directory (%s): %s", upath, strerror( errno ) );
114     free( upath );
115     return 0;
116   }
117 
118   while( ( dfile = husky_readdir( hdir ) ) != NULL )
119   {
120     if( ( strcmp( dfile, "." ) ) && ( strcmp( dfile, ".." ) ) )
121     {
122       strcpy( upath + l, dfile );
123       /* there is room for this because of the
124          addition of FILENAME_MAX in mk_uncompressdir */
125 
126       if( remove( upath ) )
127       {
128         w_log( LL_ERROR, "Cannot delete file '%s': %s", upath, strerror( errno ) );
129       }
130       else
131         w_log( LL_DEL, "File '%s' removed", upath );
132     }
133   }
134   husky_closedir( hdir );
135 
136   upath[l - 1] = '\0';
137 
138   if( rmdir( upath ) )
139   {
140     w_log( LL_ERROR, "Cannot remove temporary directory '%s': %s", upath, strerror( errno ) );
141     free( upath );
142     return 0;
143   }
144 
145   free( upath );
146   return 1;
147 }
148 
uncompress(s_fidoconfig * config,char * directory,char * filename,char * tempdir)149 static int uncompress( s_fidoconfig * config, char *directory, char *filename, char *tempdir )
150 {
151   char *tmpfilename = NULL;
152   char cmd[256];
153   FILE *f;
154   int j, found, exitcode;
155   UINT i;
156 
157   xstrscat( &tmpfilename, directory, filename, NULL );
158 
159   /* open the packet so we can see what archiver has been used */
160   f = fopen( tmpfilename, "rb" );
161   if( f == NULL )
162   {
163     w_log( LL_ERROR, "Cannot access %s: %s", tmpfilename, strerror( errno ) );
164     return 0;
165   }
166 
167   /* find what unpacker to use */
168   for( i = 0, found = 0; ( i < config->unpackCount ) && !found; i++ )
169   {
170     fseek( f, config->unpack[i].offset, config->unpack[i].offset >= 0 ? SEEK_SET : SEEK_END );
171     if( ferror( f ) )
172     {
173       w_log( LL_ERROR, "Seek error on %s: %s", tmpfilename, strerror( errno ) );
174       break;
175     }
176     for( found = 1, j = 0; j < config->unpack[i].codeSize; j++ )
177     {
178       if( ( getc( f ) & config->unpack[i].mask[j] ) != config->unpack[i].matchCode[j] )
179         found = 0;
180     }
181     if( found )
182       break;
183   }
184 
185   fclose( f );
186 
187   if( !found )
188   {
189     w_log( LL_ERROR, "Did not find an appropriate unpacker for %s", tmpfilename );
190     nfree( tmpfilename );
191     return 0;
192   }
193 
194   /* create the unpack command */
195   /* unpack_command(cmd,config->unpack[i].call, directory, filename, tempdir); */
196   fillCmdStatement( cmd, config->unpack[i].call, tmpfilename, "", tempdir );
197 
198   /* execute the unpacker */
199   w_log( LL_EXEC, "Executing '%s'", cmd );
200 
201   if( fc_stristr( config->unpack[i].call, ZIPINTERNAL ) )
202   {
203     exitcode = 1;
204 #ifdef USE_HPTZIP
205     exitcode = UnPackWithZlib( tmpfilename, NULL, tempdir );
206 #endif
207   }
208   else
209   {
210     exitcode = cmdcall( cmd );
211   }
212 
213   if( exitcode != 0 )
214   {
215     w_log( LL_EXEC, "Unpacker returned exit code '%d'", exitcode );
216     nfree( tmpfilename );
217     return 0;
218   }
219   adaptcase_refresh_dir( tempdir );
220 
221   nfree( tmpfilename );
222   return 1;
223 }
224 
get_uncompressed_filename(s_fidoconfig * config,char * directory,char * filename,char * tempdir,int expday,int * reason)225 static char *get_uncompressed_filename( s_fidoconfig * config,
226                                         char *directory, char *filename,
227                                         char *tempdir, int expday, int *reason )
228 {
229   size_t l = strlen( filename );
230   char *rv;
231 
232   w_log( LL_FUNC, "get_uncompressed_filename()" );
233 
234   if( reason )
235   {
236     *reason = 0;                /* means: all sorts of errors */
237   }
238 
239   assert( l >= 4 );             /* a match should never happen w/o ".???" at the end */
240 
241   if( isdigit( filename[l - 3] ) && expday == atoi( filename + l - 3 ) )
242   {
243     /* the file is not compressed and the day number in the file
244        name matches the expected day number */
245 
246     w_log( LL_DEBUG, "File '%s' is not compressed", filename );
247 
248     if( ( rv = malloc( strlen( directory ) + l + 1 ) ) == NULL )
249     {
250       w_log( LL_ERROR, "Out of memory" );
251       w_log( LL_FUNC, "get_uncompressed_filename() failed" );
252       return NULL;
253     }
254     strcpy( rv, directory );
255     strcat( rv, filename );
256     w_log( LL_FUNC, "get_uncompressed_filename() OK" );
257     return rv;
258   }
259   else if( expday % 100 == atoi( filename + l - 2 ) )
260   {
261     /* the file is compressed and its name looks like it could match */
262 
263     w_log( LL_DEBUG, "File '%s' is compressed", filename );
264 
265     if( !uncompress( config, directory, filename, tempdir ) )
266     {
267       w_log( LL_DEBUG, "Uncompress failed" );
268       return NULL;
269     }
270 
271     if( ( rv = malloc( strlen( tempdir ) + l + 1 ) ) == NULL )
272     {
273       w_log( LL_ERROR, "Out of memory" );
274       w_log( LL_FUNC, "get_uncompressed_filename() failed" );
275       return NULL;
276     }
277     strcpy( rv, tempdir );
278     strcat( rv, filename );
279     sprintf( rv + strlen( tempdir ) + l - 3, "%03d", expday % 1000 );
280 
281     adaptcase( rv );
282     w_log( LL_DEBUG, "Expected uncompressed filename after adaptcase(): %s", rv );
283     if( access( rv, F_OK | R_OK ) )
284     {
285       w_log( LL_WARN, "Uncompressed file '%s' does not exist", rv );
286       free( rv );
287       w_log( LL_FUNC, "get_uncompressed_filename() failed" );
288       return NULL;
289     }
290     w_log( LL_FUNC, "get_uncompressed_filename() OK" );
291     return rv;
292   }
293   else
294   {
295     /* no match */
296 
297     w_log( LL_DEBUG, "File suffix %d does not match exp day %d", atoi( filename + l - 2 ), expday );
298 
299     if( reason )
300     {
301       *reason = 1;              /* means: file extension simply doesn't match */
302     }
303 
304     w_log( LL_FUNC, "get_uncompressed_filename() failed" );
305     return NULL;
306   }
307 }
308 
basedir(const char * stemname)309 static char *basedir( const char *stemname )
310 {
311   char *rv;
312   size_t l;
313   int j;
314 
315   l = strlen( stemname );
316 
317   if( ( rv = malloc( l + 1 ) ) == NULL )
318   {
319     return NULL;
320   }
321   strcpy( rv, stemname );
322 
323   for( j = l - 1; j >= 0 && rv[j] != '/' && rv[j] != '\\' && rv[j] != ':'; j-- )
324   {
325     rv[j] = '\0';
326   }
327 
328   return rv;
329 }
330 
call_differ(char * nodelist,char * nodediff)331 static int call_differ( char *nodelist, char *nodediff )
332 {
333   char *cmd = NULL;
334   int rv;
335 
336   xscatprintf( &cmd, "%s -n %s %s", differ, nodelist, nodediff );
337   w_log( LL_EXEC, "Executing diff processor (%s)", cmd );
338   rv = system( cmd );
339   nfree( cmd );
340   if( rv != 0 )
341   {
342     w_log( LL_ERROR, "Diff processor returned exit code '%d'", rv );
343     return 0;
344   }
345   return 1;
346 }
347 
348 
349 
350 /* Purpose of try_full_update:
351 
352    fullbase + match might contain a full update for rawnl, with julian
353    being the julian day number that we expect to find in fullbase + match.
354    try to unpack the update and see if it really contains this day number, and
355    if so, use it.
356 
357    Return values: 0: no hit, no error
358                   1: fatal error
359                   2: hit, but non-fatal error
360                   3: hit
361 
362 */
363 
try_full_update(s_fidoconfig * config,char * rawnl,char * fullbase,nlist * fulllist,int j,char * tmpdir,long julian,int nl)364 static int try_full_update( s_fidoconfig * config, char *rawnl, char *fullbase,
365                             nlist * fulllist, int j, char *tmpdir, long julian, int nl )
366 {
367   char *ufn;
368   char *newfn;
369   int ndnr;
370   int rv = 0;
371   int reason;
372 
373   w_log( LL_FUNC, "try_full_update()" );
374 
375   if( fulllist->julians[j] == -1 )
376   {
377     w_log( LL_FUNC, "try_full_update() failed (fulllist->julians[j]==-1)" );
378     return 0;                   /* this full update has an unknown date number */
379   }
380 
381   decode_julian_date( julian, NULL, NULL, NULL, &ndnr );
382 
383   ufn = get_uncompressed_filename( config, fullbase, fulllist->matches[j], tmpdir, ndnr, &reason );
384 
385   w_log( LL_FILENAME, "Uncomressed filename: %s", ufn );
386 
387   if( !reason )
388     fulllist->julians[j] = -1;
389 
390   if( ufn != NULL )
391   {
392     if( julian == ( fulllist->julians[j] = parse_nodelist_date( ufn ) ) )
393     {
394       w_log( LL_INFO, "Found full update: %s", ufn );
395 
396       newfn = malloc( strlen( config->nodelistDir ) +
397                       strlen( config->nodelists[nl].nodelistName ) + 5 );
398 
399       if( newfn == NULL )
400       {
401         w_log( LL_ERROR, "Out of memory" );
402         w_log( LL_FUNC, "try_full_update() failed" );
403         return 1;
404       }
405       else
406       {
407         strcpy( newfn, config->nodelistDir );
408         strcat( newfn, config->nodelists[nl].nodelistName );
409         sprintf( newfn + strlen( newfn ), ".%03d", ndnr );
410 
411         w_log( LL_FILE, "Copy '%s' to '%s'", ufn, newfn );
412 
413         if( copy_file( ufn, newfn, 1 ) )
414         {
415           w_log( LL_ERROR, "Error copying '%s' to '%s'", ufn, newfn );
416           w_log( LL_FUNC, "try_full_update() failed" );
417           return 1;
418         }
419         else
420         {
421           rv = 3;
422           if( rawnl != NULL )
423           {
424             w_log( LL_FILE, "Replacing '%s' with '%s'", rawnl, newfn );
425 
426             if( remove( rawnl ) )
427             {
428               w_log( LL_ERROR, "Cannot remove '%s'", rawnl );
429               rv = 2;           /* error, but still proceed */
430             }
431           }
432           else
433           {
434             w_log( LL_CREAT, "Creating '%s'", newfn );
435           }
436 
437         }
438         free( newfn );
439       }
440     }
441     else
442     {
443       w_log( LL_ALERT, "%s does not contain nodelist for day %03d", ufn, ndnr );
444     }
445 
446     free( ufn );
447   }
448   w_log( LL_FUNC, "try_full_update() OK" );
449   return rv;
450 }
451 
452 
453 
454 /* Do_update finds an update for a already existing unpacked nodelist. it first
455    parses the day number of the existing nodelist instance, then increases it
456    by steps of 7, and looks for matching diff or full updates. If there is no
457    existing instance of the nodelst, you must use create_instance instead.
458 
459    today is passed as argument, because we only want to call julian_today()
460    once. Imagine nlupdate being started at 23:59:59 .... */
461 
do_update(s_fidoconfig * config,int nl,char * rawnl,long today,char * tmpdir)462 static int do_update( s_fidoconfig * config, int nl, char *rawnl, long today, char *tmpdir )
463 {
464   long julian, i;
465   int ndnr;
466   int checkdiff = 1;
467   int j = 0;
468   int rv = 1;
469   char *diffbase = NULL, *fullbase = NULL;
470   nlist *difflist = NULL, *fulllist = NULL;
471   int hit;
472   char *ufn = NULL, *cfn = NULL;
473 
474   w_log( LL_FUNC, "do_update()" );
475 
476   if( ( julian = parse_nodelist_date( rawnl ) ) == -1 )
477     return 0;
478 
479   if( today - julian < 7 )
480   {
481     w_log( LL_INFO, "%s younger than 7 days, no update necessary", rawnl );
482     return 1;
483   }
484 
485   /* build the list of candidate nodediff files */
486   if( config->nodelists[nl].diffUpdateStem != NULL )
487   {
488     if( ( diffbase = basedir( config->nodelists[nl].diffUpdateStem ) ) != NULL )
489     {
490       difflist = find_nodelistfiles( diffbase,
491                                      config->nodelists[nl].diffUpdateStem + strlen( diffbase ), 1 );
492     }
493     else
494       difflist = NULL;
495   }
496 
497   /* build the list of candidate full update files */
498   if( config->nodelists[nl].fullUpdateStem )
499   {
500     if( ( fullbase = basedir( config->nodelists[nl].fullUpdateStem ) ) != NULL )
501     {
502       fulllist = find_nodelistfiles( fullbase,
503                                      config->nodelists[nl].fullUpdateStem + strlen( fullbase ), 1 );
504     }
505     else
506       fullbase = NULL;
507   }
508 
509   if( !fulllist && !difflist )
510   {
511     if( config->nodelists[nl].fullUpdateStem )
512     {
513       if( !access( config->nodelists[nl].fullUpdateStem, F_OK ) )
514       {
515         w_log( LL_INFO, "Full update '%s' not an FTS5000 compatible",
516                config->nodelists[nl].fullUpdateStem );
517         xstrscat( &ufn, config->nodelistDir, config->nodelists[nl].nodelistName, NULL );
518 
519         w_log( LL_FILE, "Copy '%s' to '%s'", config->nodelists[nl].fullUpdateStem, ufn );
520 
521         if( copy_file( config->nodelists[nl].fullUpdateStem, ufn, 1 ) )
522         {
523           w_log( LL_ERROR, "Error copying '%s' to '%s'", config->nodelists[nl].fullUpdateStem,
524                  ufn );
525           w_log( LL_FUNC, "do_update() failed" );
526           nfree( ufn );
527           return 0;
528         }
529         else
530         {
531           w_log( LL_FUNC, "do_update() OK" );
532           nfree( ufn );
533           return 1;
534         }
535       }
536       else
537       {
538         w_log( LL_INFO, "Full update '%s' not found", config->nodelists[nl].fullUpdateStem );
539         w_log( LL_FUNC, "do_update() failed" );
540         return 0;
541       }
542     }
543   }
544 
545   /* search for diffs or full updates */
546 
547   for( i = julian + 7; i <= today && rawnl; i += 7 )
548   {
549     decode_julian_date( i, NULL, NULL, NULL, &ndnr );
550     hit = 0;
551 
552     if( checkdiff && difflist != NULL ) /* search for diffs */
553     {
554       for( j = 0; j < difflist->n && !hit; j++ )
555       {
556 
557         w_log( LL_DEBUG, "Checking %s%s", diffbase, difflist->matches[j] );
558 
559         ufn = get_uncompressed_filename( config, diffbase,
560                                          difflist->matches[j], tmpdir, ndnr, NULL );
561 
562         w_log( LL_DEBUG, "ufn is: %s", ufn );
563 
564         if( ufn != NULL )
565         {
566 
567           /* nodediffs contain the header of the nodelist to which
568              they apply as the first line, so we need to compare the
569              parsed date with julian, not with i */
570 
571           if( julian == parse_nodelist_date( ufn ) )
572           {
573             w_log( LL_INFO, "Found diff update: %s", ufn );
574 
575             if( call_differ( rawnl, ufn ) )
576             {
577               hit = 1;
578               difflist->applied[j] = 1;
579             }
580           }
581           else
582           {
583             w_log( LL_INFO, "%s is not applicable to %s", rawnl, ufn );
584           }
585           free( ufn );
586         }
587       }
588     }
589 
590     if( !hit )
591     {
592       if( ( fulllist == NULL ) && config->nodelists[nl].fullUpdateStem )
593       {
594         /* non-standard full update nodelist file */
595 
596 #if 0
597         if( add_match( fulllist, config->nodelists[nl].fullUpdateStem ) )
598         {
599           switch ( try_full_update( config, rawnl, fullbase, fulllist, j, tmpdir, i, nl ) )
600           {
601           case 0:              /* no hit, no error */
602             break;
603           case 1:
604             i = today;          /* fatal error; stop searching! */
605             rv = 0;
606             break;
607           case 2:
608             rv = 0;             /* hit, minor error, but don't exit loop! */
609             hit = 1;
610             break;
611           case 3:
612             hit = 1;            /* hit, no errors; */
613             break;
614           }
615         }
616 #endif
617       }
618       else if( fulllist != NULL )
619       {                         /* search for fulls */
620         for( j = 0; j < fulllist->n && !hit; j++ )
621         {
622 
623           w_log( LL_DEBUG, "Checking %s%s", fullbase, fulllist->matches[j] );
624 
625           switch ( try_full_update( config, rawnl, fullbase, fulllist, j, tmpdir, i, nl ) )
626           {
627           case 0:              /* no hit, no error */
628             break;
629           case 1:
630             i = today;          /* fatal error; stop searching! */
631             rv = 0;
632             break;
633           case 2:
634             rv = 0;             /* hit, minor error, but don't exit loop! */
635             hit = 1;
636             break;
637           case 3:
638             hit = 1;            /* hit, no errors; */
639             break;
640           }
641         }
642       }
643     }
644 
645     if( hit )                   /* analyse the new nodelist */
646     {
647       if( rawnl != NULL )
648         free( rawnl );
649       rawnl = findNodelist( config, nl );
650       if( rawnl == NULL )
651       {
652         w_log( LL_ERROR,
653                "Instance of nodelist %s vanished during update!?",
654                config->nodelists[nl].nodelistName );
655       }
656       else if( ( i = parse_nodelist_date( rawnl ) ) == -1 )
657       {
658         free( rawnl );
659         rawnl = NULL;
660       }
661       else if( i <= julian )
662       {
663         w_log( LL_ERROR, "Nodelist %s still as old as before!?", rawnl );
664         rv = 0;
665         i = today;              /* exit loop :-( */
666       }
667       else
668       {
669         julian = i;
670       }
671       checkdiff = 1;
672     }
673     else
674     {
675       checkdiff = 0;
676     }
677   }
678 
679   if( config->nodelists[nl].delAppliedDiff )
680     for( j = 0; j < difflist->n; j++ )
681       if( difflist->applied[j] )
682       {
683         xstrscat( &cfn, diffbase, difflist->matches[j], NULL );
684         if( remove( cfn ) )
685         {
686           w_log( LL_ERROR, "Cannot delete file '%s': %s", cfn, strerror( errno ) );
687         }
688         else
689           w_log( LL_DEL, "File '%s' removed", cfn );
690         nfree( cfn );
691       }
692 
693   if( fullbase != NULL )
694     free( fullbase );
695   if( diffbase != NULL )
696     free( diffbase );
697   free_nlist( fulllist );
698   fulllist = NULL;
699 
700   /* check if nodelist is extraordinarily old, just for information */
701   if( today - julian > 14 )
702   {
703     w_log( LL_ALERT, "%s is more than two weeks old, but still no way to update it", rawnl );
704   }
705 
706   free( rawnl );
707   return rv;
708 }
709 
710 /* Create_instance is used instead of do_instance if there is not yet any
711    unpacked instance of the nodelist in the nodelist directory. In this case,
712    of course, diff updates are useless, and we only search for full
713    updates. Create_instance will start at the current day number, and then
714    decrease it in steps of 1 until it finds a matching full update.
715 
716    today is passed as argument, because we only want to call julian_today()
717    once. Imagine nlupdate being started at 23:59:59 .... */
718 
create_instance(s_fidoconfig * config,int nl,long today,char * tmpdir)719 static int create_instance( s_fidoconfig * config, int nl, long today, char *tmpdir )
720 {
721   long i;
722   int j;
723   int rv = 0;
724   char *fullbase = NULL;
725   nlist *fulllist = NULL;
726   int hit;
727 
728   /* build the list of candidate full update files */
729 
730   w_log( LL_FUNC, "create_instance()" );
731 
732   if( config->nodelists[nl].fullUpdateStem )
733   {
734     if( ( fullbase = basedir( config->nodelists[nl].fullUpdateStem ) ) != NULL )
735     {
736       fulllist = find_nodelistfiles( fullbase,
737                                      config->nodelists[nl].fullUpdateStem + strlen( fullbase ), 1 );
738     }
739     else
740       fullbase = NULL;
741   }
742   else
743   {
744     w_log( LL_ERR, "fullupdate not present" );
745     w_log( LL_FUNC, "create_instance() failed" );
746     return 0;
747   }
748 
749   if( fulllist == NULL )
750   {                             /* non-standard full update nodelist file */
751     if( !access( config->nodelists[nl].fullUpdateStem, F_OK ) )
752     {
753       w_log( LL_INFO, "Full update '%s' not an FTS5000 compatible",
754              config->nodelists[nl].fullUpdateStem );
755       nfree( fullbase );
756       xstrscat( &fullbase, config->nodelistDir, config->nodelists[nl].nodelistName, NULL );
757 
758       w_log( LL_FILE, "Copy '%s' to '%s'", config->nodelists[nl].fullUpdateStem, fullbase );
759 
760       if( copy_file( config->nodelists[nl].fullUpdateStem, fullbase, 1 ) )
761       {
762         w_log( LL_ERROR, "Error copying '%s' to '%s'", config->nodelists[nl].fullUpdateStem,
763                fullbase );
764         w_log( LL_FUNC, "create_instance() failed" );
765         nfree( fullbase );
766         return 0;
767       }
768 
769       nfree( fullbase );
770       w_log( LL_FUNC, "create_instance() OK" );
771       return 1;
772     }
773     else
774     {
775       w_log( LL_INFO, "Full update '%s' not found", config->nodelists[nl].fullUpdateStem );
776       w_log( LL_FUNC, "create_instance() failed" );
777       return 0;
778     }
779   }
780 
781   /* search for diffs or full updates */
782   hit = 0;
783   for( i = today; i > today - 10 * 366 && !rv; i-- )
784   {
785     if( ( fulllist != NULL ) && ( !hit ) )
786     {                           /* search for fulls */
787       for( j = 0; j < fulllist->n && !hit; j++ )
788       {
789         switch ( try_full_update( config, NULL, fullbase, fulllist, j, tmpdir, i, nl ) )
790         {
791         case 0:                /* no hit, no error */
792           break;
793         case 1:
794           i = today - 10 * 366; /* fatal error; stop searching! */
795           rv = 0;
796           break;
797         case 2:
798         case 3:
799           rv = 1;               /* hit */
800           break;
801         }
802       }
803     }
804   }
805 
806   if( fullbase != NULL )
807     free( fullbase );
808 
809   w_log( LL_FUNC, "create_instance() rc=%d", rv );
810   return rv;
811 }
812 
813 
process(s_fidoconfig * config)814 static int process( s_fidoconfig * config )
815 {
816   UINT i;
817   int rv = 0;
818   char *nodelist;
819   char *tmpdir;
820   long today = julian_today(  );
821 
822   if( config->nodelistDir == NULL )
823   {
824     w_log( LL_CRIT, "No nodelist directory configured in fidoconfig" );
825     return 8;
826   }
827 
828   if( config->nodelistCount < 1 )
829   {
830     w_log( LL_CRIT, "No nodelist configured in fidoconfig" );
831     return 8;
832   }
833 
834   if( ( tmpdir = mk_uncompressdir( config->nodelistDir ) ) != NULL )
835   {
836     for( i = 0; i < config->nodelistCount; i++ )
837     {
838       nodelist = findNodelist( config, i );
839 
840       if( nodelist == NULL )
841       {
842         w_log( LL_ALERT, "No instance of nodelist %s found.", config->nodelists[i].nodelistName );
843 
844         /* New: If there is no instance, we can still try
845            to find a full update! */
846 
847         if( config->nodelists[i].fullUpdateStem != NULL )
848         {
849           w_log( LL_INFO, "Check fullupdate: %s", config->nodelists[i].fullUpdateStem );
850           if( !create_instance( config, i, today, tmpdir ) )
851           {
852             w_log( LL_ALERT,
853                    "no full update for nodelist %s found.", config->nodelists[i].nodelistName );
854             if( rv < 4 )
855               rv = 4;
856           }
857           else
858           {
859             nodelist = findNodelist( config, i );
860             if( nodelist == NULL )
861             {
862               w_log( LL_ERROR, "Unpacked full update, but "
863                      "still no instance of nodelist %s !?", config->nodelists[i].nodelistName );
864               if( rv < 8 )
865                 rv = 8;
866             }
867           }
868         }
869         else
870         {
871           w_log( LL_ALERT, "Don't know how to create instance"
872                  " of nodelist %s (no full update configured)", config->nodelists[i].nodelistName );
873           if( rv < 4 )
874             rv = 4;
875         }
876       }
877 
878       if( nodelist != NULL )
879       {
880         if( config->nodelists[i].diffUpdateStem == NULL &&
881             config->nodelists[i].fullUpdateStem == NULL )
882         {
883           w_log( LL_ALERT, "Don't know how to update "
884                  "nodelist %s", config->nodelists[i].nodelistName );
885           if( rv < 4 )
886             rv = 4;
887           free( nodelist );
888         }
889         else
890         {
891           w_log( LL_INFO, "Trying to update %s", nodelist );
892 
893           if( !do_update( config, i, nodelist, today, tmpdir ) )
894           {
895             if( rv < 8 )
896               rv = 8;
897           }
898         }
899       }
900     }
901 
902     destroy_uncompressdir( tmpdir );
903   }
904 
905   w_log( LL_FUNC, "%s::process() done", __FILE__ );
906   return rv;
907 }
908 
usage()909 void usage()
910 {
911   printf(
912       "USAGE:\n"
913       "\tnlupdate [-h] [-v] [-q] [-c config]\n"
914       "where:\n"
915       "\t-h\t - print this help and exit;\n"
916       "\t-v\t - print version information and exit;\n"
917       "\t-q\t - quiet mode (supress normal output, print errors only).\n"
918       "\t-c config - read configuration from alternate fidoconfig file.\n"
919         );
920 }
921 
main(int argc,char ** argv)922 int main( int argc, char **argv )
923 {
924   s_fidoconfig *config;
925   int rv;
926   int l = 0, i, flag_quiet = 0;
927   char *versionStr, *configfile = NULL;
928 
929   versionStr = GenVersionStr( "nlupdate", VER_MAJOR, VER_MINOR, VER_PATCH, VER_BRANCH, cvs_date );
930 
931   for( i = 1; i < argc; i++ )
932   {
933     int j, plen;
934     if( argv[i][0] == '-' )
935     {
936       int plen = sstrlen( argv[i] );
937       for( j = 1; j < plen; j++ )
938         switch ( argv[i][j] )
939         {
940         case 'h':
941           usage(  );
942           return 0;
943         case 'v':
944           printversion(  );
945           return 0;
946         case 'c':
947           if( plen > ++j )
948           { configfile = argv[i] + j;
949             j=plen;
950           }else if( argc > ++i )
951             configfile = argv[i];
952           else
953           {
954             w_log( LL_ERR, "Fatal: parameter after \"-c\" is required\n" );
955             return 1;
956           }
957           break;
958         case 'q':
959           flag_quiet = 1;
960         }
961     }
962   }
963 
964   if( !flag_quiet )
965     printversion(  );
966 
967   config = readConfig( configfile );
968 
969   /* construct the name of the nldiff command */
970   if( argc )
971   {
972     l = strlen( argv[0] );
973     if( l )
974     {
975       for( l--; l >= 0 && argv[0][l] != '/' && argv[0][l] != '\\' && argv[0][l] != ':'; l-- );
976 
977       l++;
978     }
979   }
980   differ = malloc( l + 7 );
981   if( l )
982     memcpy( differ, argv[0], l );
983   strcpy( differ + l, "nldiff" );
984 
985   /* run the main program */
986   if( config != NULL )
987   {
988     {
989       char errloglevels[3] = { LL_ERR, LL_CRIT, '\0' };
990       initLog( config->logFileDir, config->logEchoToScreen, config->loglevels,
991              flag_quiet? errloglevels : config->screenloglevels );
992     }
993     openLog( LOGNAME, versionStr );
994     w_log( LL_START, "Start" );
995 
996     rv = process( config );
997 
998     w_log( LL_STOP, "End" );
999     closeLog(  );
1000     disposeConfig( config );
1001     free( differ );
1002     return rv;
1003 
1004   }
1005   else
1006   {
1007     w_log( LL_ERR, "Fatal: Cannot open fidoconfig.\n" );
1008     free( differ );
1009     return 8;
1010   }
1011 }
1012