1 /*
2   util.c
3 
4   $Id: util.c,v 1.22 2003/01/31 15:35:14 bears Exp $
5 */
6 
7 #if HAVE_CONFIG_H
8 #include <config.h>
9 #endif
10 
11 #if TIME_WITH_SYS_TIME
12 #include <sys/time.h>
13 #include <time.h>
14 #else
15 #if HAVE_SYS_TIME_H
16 #include <sys/time.h>
17 #else
18 #include <time.h>
19 #endif
20 #endif
21 
22 #include <errno.h>
23 #include <ctype.h>
24 #include <netdb.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/utsname.h>
28 #include <fcntl.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include "configfile.h"
32 #include "log.h"
33 #include "portable.h"
34 #include "util.h"
35 #include "wildmat.h"
36 
37 #if defined(UTIL_TEST)
38 #define	Log_err	printf
39 #endif
40 
41 static const char *
nextWhiteSpace(const char * p)42 nextWhiteSpace( const char *p )
43 {
44     while ( *p && ! isspace( *p ) )
45         ++p;
46     return p;
47 }
48 
49 static const char *
nextNonWhiteSpace(const char * p)50 nextNonWhiteSpace( const char *p )
51 {
52     while ( *p && isspace( *p ) )
53         ++p;
54     return p;
55 }
56 
57 const char *
Utl_restOfLn(const char * line,unsigned int token)58 Utl_restOfLn( const char *line, unsigned int token )
59 {
60     unsigned int i;
61     const char *p;
62 
63     p = line;
64     for ( i = 0; i < token; ++i )
65     {
66         p = nextNonWhiteSpace( p );
67         p = nextWhiteSpace( p );
68     }
69     p = nextNonWhiteSpace( p );
70     return p;
71 }
72 
73 const char *
Utl_getLn(Str result,const char * pos)74 Utl_getLn( Str result, const char *pos )
75 {
76     int len = 0;
77     const char *p = pos;
78 
79     if ( ! p )
80         return NULL;
81     while ( *p != '\n' )
82     {
83         if ( *p == '\0' )
84         {
85             if ( len > 0 )
86                 Log_err( "Line not terminated by newline: '%s'", pos );
87             return NULL;
88         }
89         *(result++) = *(p++);
90         ++len;
91         if ( len >= MAXCHAR - 1 )
92         {
93             *result = '\0';
94             Log_err( "Utl_getLn: line too long: %s", result );
95             return ++p;
96         }
97     }
98     *result = '\0';
99     return ++p;
100 
101 }
102 
103 const char *
Utl_ungetLn(const char * str,const char * p)104 Utl_ungetLn( const char *str, const char *p )
105 {
106     if ( str == p )
107         return FALSE;
108     --p;
109     if ( *p != '\n' )
110     {
111         Log_err( "Utl_ungetLn: not at beginning of line" );
112         return NULL;
113     }
114     --p;
115     while ( TRUE )
116     {
117         if ( p == str )
118             return p;
119         if ( *p == '\n' )
120             return p + 1;
121         --p;
122     }
123 }
124 
125 const char *
Utl_getHeaderLn(Str result,const char * p)126 Utl_getHeaderLn( Str result, const char *p )
127 {
128     const char * res = Utl_getLn( result, p );
129     Bool not_too_long_header = TRUE;
130 
131     /* Look for followon line if this isn't a blank line. */
132     if ( res != NULL && result[ 0 ] != '\0' && ! isspace( result[ 0 ] ) )
133 	for(;;)
134 	{
135 	    Str nextLine;
136 	    const char *here;
137 
138 	    here = res;
139 	    nextLine[ 0 ] = '\0';
140 	    res = Utl_getLn( nextLine, res );
141 	    if ( res == NULL || nextLine[ 0 ] == '\0'
142 		 || ! isspace( nextLine[ 0 ] ) )
143 	    {
144 		res = here;
145 		break;
146 	    }
147 	    else
148 	    {
149                 if ( not_too_long_header &&
150                      ( MAXCHAR > ( strlen( result ) + strlen( nextLine ) + 1 ) ) )
151                 {
152                     Utl_catStr( result, "\n"   );
153 		    Utl_catStr( result, nextLine );
154                 }
155                 else
156                 {
157                     Log_err( "Utl_getHeaderLn: skipped continued header: %s", nextLine );
158                     not_too_long_header = FALSE;
159                     /* Now let poor little noffle skip the header continuations. */
160                     /* We really need to up the size limit of headers much */
161                     /* higher than MAXCHAR = 2048. mliss */
162                 }
163 	    }
164 	}
165 
166     return res;
167 }
168 
169 void
Utl_toLower(Str line)170 Utl_toLower( Str line )
171 {
172     char *p;
173 
174     p = line;
175     while ( *p )
176     {
177         *p = tolower( *p );
178         ++p;
179     }
180 }
181 
182 char *
Utl_stripWhiteSpace(char * line)183 Utl_stripWhiteSpace( char *line )
184 {
185     char *p;
186 
187     while ( isspace( *line ) )
188         ++line;
189     p = line + strlen( line ) - 1;
190     while ( p >= line && isspace( *p ) )
191     {
192         *p = '\0';
193         --p;
194     }
195     return line;
196 }
197 
198 void
Utl_stripComment(char * line)199 Utl_stripComment( char *line )
200 {
201     for ( ; *line != '\0'; line++ )
202 	if ( *line =='#' )
203 	{
204 	    *line = '\0';
205 	    break;
206 	}
207 }
208 
209 void
Utl_cpyStr(Str dst,const char * src)210 Utl_cpyStr( Str dst, const char *src )
211 {
212     dst[ 0 ] = '\0';
213     strncat( dst, src, MAXCHAR );
214 }
215 
216 void
Utl_cpyStrN(Str dst,const char * src,int n)217 Utl_cpyStrN( Str dst, const char *src, int n )
218 {
219     if ( n > MAXCHAR )
220     	n = MAXCHAR;
221     dst[ 0 ] = '\0';
222     strncat( dst, src, (size_t)n );
223 }
224 
225 void
Utl_catStr(Str dst,const char * src)226 Utl_catStr( Str dst, const char *src )
227 {
228     strncat( dst, src, MAXCHAR - strlen( dst ) );
229 }
230 
231 void
Utl_catStrN(Str dst,const char * src,int n)232 Utl_catStrN( Str dst, const char *src, int n )
233 {
234     size_t un;
235 
236     ASSERT( n >= 0 );
237     un = (size_t)n;
238     if ( un > MAXCHAR - strlen( dst ) )
239     	 un = MAXCHAR - strlen( dst );
240     strncat( dst, src, un );
241 }
242 
243 void
Utl_stamp(Str file)244 Utl_stamp( Str file )
245 {
246     FILE *f;
247     time_t t;
248     Str tmpfname;
249 
250     snprintf( tmpfname, MAXCHAR, "%s/.#%d.stamp.update",
251               Cfg_spoolDir(), (int) getpid() );
252     time( &t );
253     if ( ! ( f = fopen( tmpfname, "w" ) ) )
254     {
255         Log_err( "Could not open %s for writing (%s)",
256                  tmpfname, strerror( errno ) );
257         return;
258     }
259     fprintf( f, "%lu\n", t );
260     if ( fclose( f ) != 0 )
261     {
262          Log_err( "Error stamping into file %s: %s",
263 		 tmpfname, strerror( errno ) );
264 
265     }
266     else
267     {
268         if ( rename( tmpfname, file ) < 0 )
269             Log_err( "Rename of stamp file %s to %s failed: %s",
270 		     tmpfname, file, strerror( errno ) );
271     }
272 }
273 
274 Bool
Utl_getStamp(time_t * result,Str file)275 Utl_getStamp( time_t *result, Str file )
276 {
277     FILE *f;
278 
279     if ( ! ( f = fopen( file, "r" ) ) )
280         return FALSE;
281     if ( fscanf( f, "%lu", (unsigned long *) result ) != 1 )
282     {
283         Log_err( "File %s corrupted", file );
284         fclose( f );
285         return FALSE;
286     }
287     fclose( f );
288     return TRUE;
289 }
290 
291 static const char *DOTW[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri",
292 			      "Sat", NULL };
293 static const char *MON[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
294 			     "Aug", "Sep", "Oct", "Nov", "Dec", NULL };
295 
296 /*
297  * Calculate the difference between local time and GMT at GMT time t.
298  *
299  * This is INN's 'always-works' method. It assumes the time differences
300  * is < 24hrs. Sounds reasonable to me. It also assumes it can ignore seconds.
301  * Returns localtime - GMT seconds. It will also trash the localtime/
302  * gmtime/etc. static buffer.
303  *
304  * Do some basic caching by returning the previous result if the new request
305  * is in the same hour as the previous one.
306  */
307 static int
localTimeDiff(time_t t)308 localTimeDiff( time_t t )
309 {
310     struct tm local, gmt, *tm;
311     static time_t resHourStart = 0;
312     static int res = 0;
313 
314     if ( labs( t - resHourStart ) < 60 )
315 	return res;
316 
317     /* Calculating for new hour */
318     resHourStart = ( t / 60 ) * 60;
319 
320     tm = localtime( &t );
321     if ( tm == NULL )
322 	return 0;
323     local = *tm;
324     tm = gmtime( &t );
325     if ( tm == NULL )
326 	return 0;
327     gmt = *tm;
328 
329     res = local.tm_yday - gmt.tm_yday;
330     if ( res < -1 )
331 	res = -1;		/* Year rollover? */
332     else if ( res > 1 )
333 	res = 1;
334 
335     res *= 24;
336     res += local.tm_hour - gmt.tm_hour;
337     res *= 60;
338     res += local.tm_min - gmt.tm_min;
339     res *= 60;
340 
341     return res;
342 }
343 
344 time_t
Utl_mktimeGMT(struct tm * t)345 Utl_mktimeGMT( struct tm *t )
346 {
347 #if HAVE_TIMEGM
348     return timegm( t );
349 #else
350     /* This comment and implmentation is taken from Wget's mktime_from_utc().
351      * We could
352      *
353      * Converts struct tm to time_t, assuming the data in tm is UTC rather
354      * than local timezone.
355      *
356      * mktime is similar but assumes struct tm, also known as the
357      * "broken-down" form of time, is in local time zone.  mktime_from_utc
358      * uses mktime to make the conversion understanding that an offset
359      * will be introduced by the local time assumption.
360      *
361      * mktime_from_utc then measures the introduced offset by applying
362      * gmtime to the initial result and applying mktime to the resulting
363      * "broken-down" form.  The difference between the two mktime results
364      * is the measured offset which is then subtracted from the initial
365      * mktime result to yield a calendar time which is the value returned.
366      *
367      * tm_isdst in struct tm is set to 0 to force mktime to introduce a
368      * consistent offset (the non DST offset) since tm and tm+o might be
369      * on opposite sides of a DST change.
370      *
371      * Some implementations of mktime return -1 for the nonexistent
372      * localtime hour at the beginning of DST.  In this event, use
373      * mktime(tm - 1hr) + 3600.
374      *
375      * Schematically
376      * mktime(tm)   --> t+o
377      * gmtime(t+o)  --> tm+o
378      * mktime(tm+o) --> t+2o
379      * t+o - (t+2o - t+o) = t
380      *
381      * Note that glibc contains a function of the same purpose named
382      * `timegm' (reverse of gmtime).  But obviously, it is not universally
383      * available, and unfortunately it is not straightforwardly
384      * extractable for use here.  Perhaps configure should detect timegm
385      * and use it where available.
386      *
387      * Contributed by Roger Beeman <beeman@cisco.com>, with the help of
388      * Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO.
389      * Further improved by Roger with assistance from Edward J. Sabol
390      * based on input by Jamie Zawinski.
391      */
392     time_t tl, tb;
393     struct tm *tg;
394 
395     tl = mktime (t);
396     if (tl == -1)
397     {
398 	t->tm_hour--;
399 	tl = mktime (t);
400 	if (tl == -1)
401 	    return -1; /* can't deal with output from strptime */
402 	tl += 3600;
403     }
404     tg = gmtime (&tl);
405     tg->tm_isdst = 0;
406     tb = mktime (tg);
407     if (tb == -1)
408     {
409 	tg->tm_hour--;
410 	tb = mktime (tg);
411 	if (tb == -1)
412 	    return -1; /* can't deal with output from gmtime */
413 	tb += 3600;
414     }
415     return (tl - (tb - tl));
416 #endif
417 }
418 
419 void
Utl_newsDate(time_t t,Str res)420 Utl_newsDate( time_t t, Str res )
421 {
422     struct tm *local;
423     long tzdiff, hoffset, moffset;
424 
425     tzdiff = localTimeDiff( t ) / 60;
426 
427     local = localtime( &t );
428     if ( local == NULL )
429     {
430 	Utl_cpyStr( res, "** localtime failure **" );
431 	return;
432     }
433 
434     hoffset = tzdiff / 60;
435     moffset = tzdiff % 60;
436     if ( moffset < 0 )
437 	moffset = - moffset;
438 
439     sprintf( res, "%s, %d %s %4d %02d:%02d:%02d %+03ld%02ld",
440 	     DOTW[local->tm_wday], local->tm_mday,
441 	     MON[local->tm_mon], local->tm_year + 1900,
442 	     local->tm_hour, local->tm_min, local->tm_sec,
443 	     hoffset, moffset );
444 }
445 
446 time_t
Utl_parseNewsDate(const char * s)447 Utl_parseNewsDate( const char *s )
448 {
449     struct tm tm;
450     int wday, offset, tzoffset;
451     char *p;
452     time_t res;
453 
454     memset( &tm, 0, sizeof( tm ) );
455     wday = -1;
456     tm.tm_isdst = -1;
457 
458     s = nextNonWhiteSpace( s );
459 
460     /* Is this the day number, or a weekday? */
461     if ( ! isdigit( *s ) )
462     {
463 	if ( strlen( s ) < 4 )
464 	    return (time_t) -1;
465 
466 	for ( wday = 0; DOTW[ wday ] != NULL; wday++ )
467 	    if ( strncasecmp( DOTW[ wday ], s, 3 ) == 0 )
468 		break;
469 
470 	if( DOTW[ wday ] == NULL || s[3] != ',' )
471 	    return (time_t) -1;
472 
473 	s += 4;
474     }
475 
476     /* Get the day number */
477     tm.tm_mday = (int) strtol( s, &p, 10 );
478     if ( p == s )
479 	return (time_t) -1;
480     s = p;
481 
482     /* Look for month name */
483     s = nextNonWhiteSpace( s );
484     if ( strlen( s ) < 4 )
485 	return (time_t) -1;
486     for ( tm.tm_mon = 0; MON[ tm.tm_mon ] != NULL; tm.tm_mon++ )
487 	if ( strncasecmp( MON[ tm.tm_mon ], s, 3 ) == 0 )
488 	    break;
489 
490     if ( MON[ tm.tm_mon ] == NULL )
491 	return (time_t) -1;
492     s += 3;
493 
494     /* Year next */
495     tm.tm_year = (int) strtol( s, &p, 10 );
496     if ( p == s || ( tm.tm_year >= 100 && tm.tm_year < 1900  ) )
497 	return (time_t) -1;
498     if ( tm.tm_year >= 1900 )
499 	tm.tm_year -= 1900;
500     s = p;
501 
502     /* Hours */
503     tm.tm_hour = (int) strtol( s, &p, 10 );
504     if ( p == s || *p != ':' )
505 	return (time_t) -1;
506     s = ++p;
507 
508     /* Minutes */
509     tm.tm_min = (int) strtol( s, &p, 10 );
510     if ( p == s || ( *p != ':' && *p != ' ' ) )
511 	return (time_t) -1;
512     s = p;
513 
514     /* Seconds */
515     if ( *s == ':' )
516     {
517 	s++;
518 	tm.tm_sec = (int) strtol( s, &p, 10 );
519 	if ( p == s )
520 	    return (time_t) -1;
521 	s = p;
522     }
523 
524     /* GMT/UT or timezone offset */
525     tzoffset = 0;
526     s = nextNonWhiteSpace( s );
527     if ( strncasecmp( s, "GMT", 3) == 0 )
528 	s += 3;
529     else if ( strncasecmp( s, "UT", 2 ) == 0 )
530 	s += 2;
531     else
532     {
533 	offset = (int) strtol( s, &p, 10 );
534 	if ( p == s )
535 	    return (time_t) -1;
536 	s = p;
537 	tzoffset = ( offset / 100 ) * 60 + ( offset % 100 );
538     }
539 
540     /* Check for following junk */
541     if ( *s != '\0' && ! isspace( *s ) )
542 	return (time_t) -1;
543 
544     res = Utl_mktimeGMT( &tm );
545     if ( res == (time_t) -1 )
546 	return res;
547 
548     if ( wday >= 0 && wday != tm.tm_wday )
549 	return (time_t) -1;
550 
551     /* And now adjust for tzoffset */
552     res -= tzoffset * 60;
553 
554     return res;
555 }
556 
557 void
Utl_allocAndCpy(char ** dst,const char * src)558 Utl_allocAndCpy( char **dst, const char *src )
559 {
560     size_t len = strlen( src );
561     if ( ! ( *dst = malloc( len + 1 ) ) )
562         Log_fatal( "Cannot allocate string with length %lu", len );
563     memcpy( *dst, src, len + 1 );
564 }
565 
566 SignalHandler
Utl_installSignalHandler(int sig,SignalHandler handler)567 Utl_installSignalHandler( int sig, SignalHandler handler )
568 {
569     struct sigaction act, oldAct;
570 
571     act.sa_handler = handler;
572     sigemptyset( &act.sa_mask );
573     act.sa_flags = 0;
574     if ( sig != SIGALRM )
575         act.sa_flags |= SA_RESTART;
576     if ( sigaction( sig, &act, &oldAct ) < 0 )
577         return SIG_ERR;
578     return oldAct.sa_handler;
579 }
580 
581 static Bool
getHostFQDN(Str result)582 getHostFQDN( Str result )
583 {
584     struct hostent *myHostEnt;
585     struct utsname myName;
586 
587     if ( uname( &myName ) >= 0
588          && ( myHostEnt = gethostbyname( myName.nodename ) ) )
589     {
590         Utl_cpyStr( result, myHostEnt->h_name );
591         return TRUE;
592     }
593     return FALSE;
594 }
595 
596 Bool
Utl_getFQDN(Str result)597 Utl_getFQDN( Str result )
598 {
599     /* get hostname from Cfg-File */
600     Utl_cpyStr( result, Cfg_hostnameMsgId() );
601     if ( strlen( result ) != 0 )
602 	return TRUE;
603     return getHostFQDN( result );
604 }
605 
606 #if defined(UTIL_TEST)
607 
608 /* Test code borrowed from wildmat.c. Yep, still uses gets(). */
609 extern char	*gets();
610 
611 int
main()612 main()
613 {
614     Str line;
615     time_t t;
616 
617     printf( "Util date tester.  Enter date to test.\n" );
618     printf( "A blank line exits the program.\n" );
619 
620     for ( ; ; )
621     {
622 	t = time( NULL );
623 	Utl_newsDate( t, line );
624 	printf( "\n(%s) Enter date:  ", line );
625 	(void) fflush( stdout );
626 	if ( gets( line ) == NULL || line[0] == '\0' )
627 	    break;
628 
629 	t = Utl_parseNewsDate( line );
630 	if ( t == (time_t) -1 )
631 	    printf( "Date parse failed\n" );
632 	else
633 	{
634 	    Utl_newsDate( t, line );
635 	    printf( "Utl_newsDate -> '%s'\n", line );
636 	}
637     }
638 
639     exit(0);
640     /* NOTREACHED */
641 }
642 #endif
643