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