Mercurial > noffle
view src/util.c @ 500:614a3177b15c noffle tip
Add mail-from option.
Some modern mail systems will try and ensure the sender email is a legitimate
address. Which will fail if there isn't such an address.
author | Jim Hague <jim.hague@acm.org> |
---|---|
date | Wed, 14 Aug 2013 12:04:39 +0100 |
parents | 95697d7c97a1 |
children |
line wrap: on
line source
/* util.c $Id: util.c 608 2003-07-23 09:31:01Z bears $ */ #if HAVE_CONFIG_H #include <config.h> #endif #if TIME_WITH_SYS_TIME #include <sys/time.h> #include <time.h> #else #if HAVE_SYS_TIME_H #include <sys/time.h> #else #include <time.h> #endif #endif #include <errno.h> #include <ctype.h> #include <netdb.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/utsname.h> #include <fcntl.h> #include <stdlib.h> #include <unistd.h> #include "configfile.h" #include "log.h" #include "portable.h" #include "util.h" #include "wildmat.h" #if defined(UTIL_TEST) #define Log_err printf #endif static const char * nextWhiteSpace( const char *p ) { while ( *p && ! isspace( *p ) ) ++p; return p; } static const char * nextNonWhiteSpace( const char *p ) { while ( *p && isspace( *p ) ) ++p; return p; } const char * Utl_restOfLn( const char *line, unsigned int token ) { unsigned int i; const char *p; p = line; for ( i = 0; i < token; ++i ) { p = nextNonWhiteSpace( p ); p = nextWhiteSpace( p ); } p = nextNonWhiteSpace( p ); return p; } const char * Utl_getLn( Str result, const char *pos ) { int len = 0; const char *p = pos; if ( ! p ) return NULL; while ( *p != '\n' ) { if ( *p == '\0' ) { if ( len > 0 ) Log_err( "Line not terminated by newline: '%s'", pos ); return NULL; } *(result++) = *(p++); ++len; if ( len >= MAXCHAR - 1 ) { *result = '\0'; Log_err( "Utl_getLn: line too long: %s", result ); return ++p; } } *result = '\0'; return ++p; } const char * Utl_ungetLn( const char *str, const char *p ) { if ( str == p ) return FALSE; --p; if ( *p != '\n' ) { Log_err( "Utl_ungetLn: not at beginning of line" ); return NULL; } --p; while ( TRUE ) { if ( p == str ) return p; if ( *p == '\n' ) return p + 1; --p; } } const char * Utl_getHeaderLn( Str result, const char *p ) { const char * res = Utl_getLn( result, p ); Bool not_too_long_header = TRUE; /* Look for followon line if this isn't a blank line. */ if ( res != NULL && result[ 0 ] != '\0' && ! isspace( result[ 0 ] ) ) for(;;) { Str nextLine; const char *here; here = res; nextLine[ 0 ] = '\0'; res = Utl_getLn( nextLine, res ); if ( res == NULL || nextLine[ 0 ] == '\0' || ! isspace( nextLine[ 0 ] ) ) { res = here; break; } else { if ( not_too_long_header && ( MAXCHAR > ( strlen( result ) + strlen( nextLine ) + 1 ) ) ) { Utl_catStr( result, "\n" ); Utl_catStr( result, nextLine ); } else { Log_err( "Utl_getHeaderLn: skipped continued header: %s", nextLine ); not_too_long_header = FALSE; /* Now let poor little noffle skip the header continuations. */ /* We really need to up the size limit of headers much */ /* higher than MAXCHAR = 2048. mliss */ } } } return res; } void Utl_toLower( Str line ) { char *p; p = line; while ( *p ) { *p = tolower( *p ); ++p; } } char * Utl_stripWhiteSpace( char *line ) { char *p; while ( isspace( *line ) ) ++line; p = line + strlen( line ) - 1; while ( p >= line && isspace( *p ) ) { *p = '\0'; --p; } return line; } void Utl_stripComment( char *line ) { for ( ; *line != '\0'; line++ ) if ( *line =='#' ) { *line = '\0'; break; } } void Utl_cpyStr( Str dst, const char *src ) { dst[ 0 ] = '\0'; strncat( dst, src, MAXCHAR ); } void Utl_cpyStrN( Str dst, const char *src, int n ) { if ( n > MAXCHAR ) n = MAXCHAR; dst[ 0 ] = '\0'; strncat( dst, src, (size_t)n ); } void Utl_catStr( Str dst, const char *src ) { strncat( dst, src, MAXCHAR - strlen( dst ) ); } void Utl_catStrN( Str dst, const char *src, int n ) { size_t un; ASSERT( n >= 0 ); un = (size_t)n; if ( un > MAXCHAR - strlen( dst ) ) un = MAXCHAR - strlen( dst ); strncat( dst, src, un ); } void Utl_stamp( Str file ) { FILE *f; time_t t; Str tmpfname; snprintf( tmpfname, MAXCHAR, "%s/.#%d.stamp.update", Cfg_spoolDir(), (int) getpid() ); time( &t ); if ( ! ( f = fopen( tmpfname, "w" ) ) ) { Log_err( "Could not open %s for writing (%s)", tmpfname, strerror( errno ) ); return; } fprintf( f, "%lu\n", t ); if ( fclose( f ) != 0 ) { Log_err( "Error stamping into file %s: %s", tmpfname, strerror( errno ) ); } else { if ( rename( tmpfname, file ) < 0 ) Log_err( "Rename of stamp file %s to %s failed: %s", tmpfname, file, strerror( errno ) ); } } Bool Utl_getStamp( time_t *result, Str file ) { FILE *f; if ( ! ( f = fopen( file, "r" ) ) ) return FALSE; if ( fscanf( f, "%lu", (unsigned long *) result ) != 1 ) { Log_err( "File %s corrupted", file ); fclose( f ); return FALSE; } fclose( f ); return TRUE; } static const char *DOTW[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL }; static const char *MON[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL }; /* * Calculate the difference between local time and GMT at GMT time t. * * This is INN's 'always-works' method. It assumes the time differences * is < 24hrs. Sounds reasonable to me. It also assumes it can ignore seconds. * Returns localtime - GMT seconds. It will also trash the localtime/ * gmtime/etc. static buffer. * * Do some basic caching by returning the previous result if the new request * is in the same hour as the previous one. */ static int localTimeDiff( time_t t ) { struct tm local, gmt, *tm; static time_t resHourStart = 0; static int res = 0; if ( labs( t - resHourStart ) < 60 ) return res; /* Calculating for new hour */ resHourStart = ( t / 60 ) * 60; tm = localtime( &t ); if ( tm == NULL ) return 0; local = *tm; tm = gmtime( &t ); if ( tm == NULL ) return 0; gmt = *tm; res = local.tm_yday - gmt.tm_yday; if ( res < -1 ) res = -1; /* Year rollover? */ else if ( res > 1 ) res = 1; res *= 24; res += local.tm_hour - gmt.tm_hour; res *= 60; res += local.tm_min - gmt.tm_min; res *= 60; return res; } time_t Utl_mktimeGMT( struct tm *t ) { #if HAVE_TIMEGM return timegm( t ); #else /* This comment and implmentation is taken from Wget's mktime_from_utc(). * We could * * Converts struct tm to time_t, assuming the data in tm is UTC rather * than local timezone. * * mktime is similar but assumes struct tm, also known as the * "broken-down" form of time, is in local time zone. mktime_from_utc * uses mktime to make the conversion understanding that an offset * will be introduced by the local time assumption. * * mktime_from_utc then measures the introduced offset by applying * gmtime to the initial result and applying mktime to the resulting * "broken-down" form. The difference between the two mktime results * is the measured offset which is then subtracted from the initial * mktime result to yield a calendar time which is the value returned. * * tm_isdst in struct tm is set to 0 to force mktime to introduce a * consistent offset (the non DST offset) since tm and tm+o might be * on opposite sides of a DST change. * * Some implementations of mktime return -1 for the nonexistent * localtime hour at the beginning of DST. In this event, use * mktime(tm - 1hr) + 3600. * * Schematically * mktime(tm) --> t+o * gmtime(t+o) --> tm+o * mktime(tm+o) --> t+2o * t+o - (t+2o - t+o) = t * * Note that glibc contains a function of the same purpose named * `timegm' (reverse of gmtime). But obviously, it is not universally * available, and unfortunately it is not straightforwardly * extractable for use here. Perhaps configure should detect timegm * and use it where available. * * Contributed by Roger Beeman <beeman@cisco.com>, with the help of * Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO. * Further improved by Roger with assistance from Edward J. Sabol * based on input by Jamie Zawinski. */ time_t tl, tb; struct tm *tg; tl = mktime (t); if (tl == -1) { t->tm_hour--; tl = mktime (t); if (tl == -1) return -1; /* can't deal with output from strptime */ tl += 3600; } tg = gmtime (&tl); tg->tm_isdst = 0; tb = mktime (tg); if (tb == -1) { tg->tm_hour--; tb = mktime (tg); if (tb == -1) return -1; /* can't deal with output from gmtime */ tb += 3600; } return (tl - (tb - tl)); #endif } void Utl_newsDate( time_t t, Str res ) { struct tm *local; long tzdiff, hoffset, moffset; tzdiff = localTimeDiff( t ) / 60; local = localtime( &t ); if ( local == NULL ) { Utl_cpyStr( res, "** localtime failure **" ); return; } hoffset = tzdiff / 60; moffset = tzdiff % 60; if ( moffset < 0 ) moffset = - moffset; sprintf( res, "%s, %d %s %4d %02d:%02d:%02d %+03ld%02ld", DOTW[local->tm_wday], local->tm_mday, MON[local->tm_mon], local->tm_year + 1900, local->tm_hour, local->tm_min, local->tm_sec, hoffset, moffset ); } time_t Utl_parseNewsDate( const char *s ) { struct tm tm; int wday, offset, tzoffset; char *p; time_t res; memset( &tm, 0, sizeof( tm ) ); wday = -1; tm.tm_isdst = -1; s = nextNonWhiteSpace( s ); /* Is this the day number, or a weekday? */ if ( ! isdigit( *s ) ) { if ( strlen( s ) < 4 ) return (time_t) -1; for ( wday = 0; DOTW[ wday ] != NULL; wday++ ) if ( strncasecmp( DOTW[ wday ], s, 3 ) == 0 ) break; if( DOTW[ wday ] == NULL || s[3] != ',' ) return (time_t) -1; s += 4; } /* Get the day number */ tm.tm_mday = (int) strtol( s, &p, 10 ); if ( p == s ) return (time_t) -1; s = p; /* Look for month name */ s = nextNonWhiteSpace( s ); if ( strlen( s ) < 4 ) return (time_t) -1; for ( tm.tm_mon = 0; MON[ tm.tm_mon ] != NULL; tm.tm_mon++ ) if ( strncasecmp( MON[ tm.tm_mon ], s, 3 ) == 0 ) break; if ( MON[ tm.tm_mon ] == NULL ) return (time_t) -1; s += 3; /* Year next */ tm.tm_year = (int) strtol( s, &p, 10 ); if ( p == s || ( tm.tm_year >= 100 && tm.tm_year < 1900 ) ) return (time_t) -1; if ( tm.tm_year >= 1900 ) tm.tm_year -= 1900; s = p; /* Hours */ tm.tm_hour = (int) strtol( s, &p, 10 ); if ( p == s || *p != ':' ) return (time_t) -1; s = ++p; /* Minutes */ tm.tm_min = (int) strtol( s, &p, 10 ); if ( p == s || ( *p != ':' && *p != ' ' ) ) return (time_t) -1; s = p; /* Seconds */ if ( *s == ':' ) { s++; tm.tm_sec = (int) strtol( s, &p, 10 ); if ( p == s ) return (time_t) -1; s = p; } /* GMT/UT or timezone offset */ tzoffset = 0; s = nextNonWhiteSpace( s ); if ( strncasecmp( s, "GMT", 3) == 0 ) s += 3; else if ( strncasecmp( s, "UT", 2 ) == 0 ) s += 2; else { offset = (int) strtol( s, &p, 10 ); if ( p == s ) return (time_t) -1; s = p; tzoffset = ( offset / 100 ) * 60 + ( offset % 100 ); } /* Check for following junk */ if ( *s != '\0' && ! isspace( *s ) ) return (time_t) -1; res = Utl_mktimeGMT( &tm ); if ( res == (time_t) -1 ) return res; if ( wday >= 0 && wday != tm.tm_wday ) return (time_t) -1; /* And now adjust for tzoffset */ res -= tzoffset * 60; return res; } void Utl_allocAndCpy( char **dst, const char *src ) { size_t len = strlen( src ); if ( ! ( *dst = malloc( len + 1 ) ) ) Log_fatal( "Cannot allocate string with length %lu", len ); memcpy( *dst, src, len + 1 ); } SignalHandler Utl_installSignalHandler( int sig, SignalHandler handler ) { struct sigaction act, oldAct; act.sa_handler = handler; sigemptyset( &act.sa_mask ); act.sa_flags = 0; if ( sig != SIGALRM ) act.sa_flags |= SA_RESTART; if ( sigaction( sig, &act, &oldAct ) < 0 ) return SIG_ERR; return oldAct.sa_handler; } static Bool getHostFQDN( Str result ) { struct hostent *myHostEnt; struct utsname myName; if ( uname( &myName ) >= 0 && ( myHostEnt = gethostbyname( myName.nodename ) ) ) { Utl_cpyStr( result, myHostEnt->h_name ); return TRUE; } return FALSE; } Bool Utl_getFQDN( Str result ) { /* get hostname from Cfg-File */ Utl_cpyStr( result, Cfg_hostnameMsgId() ); if ( strlen( result ) != 0 ) return TRUE; return getHostFQDN( result ); } Bool Utl_createDir( const char *dir ) { struct stat st; int err; err = stat( dir, &st ); if ( err == 0 && S_ISDIR( st.st_mode ) ) return TRUE; err = mkdir( dir, 0755 ); if ( err != 0 ) { Log_dbg( LOG_DBG_NEWSBASE, "mkdir: %s", strerror( errno ) ); return FALSE; } return TRUE; } Bool Utl_writeFile( const char *dirPrefix, const char *dir, const char *file, const char *text) { Str dirPath; Str filePath; FILE *f; if ( dirPrefix != NULL ) { if ( ! Utl_createDir( dirPrefix ) ) return FALSE; snprintf( dirPath, MAXCHAR, "%s/%s", dirPrefix, dir ); } else Utl_cpyStr( dirPath, dir ); if ( ! Utl_createDir( dirPath ) ) return FALSE; snprintf( filePath, MAXCHAR, "%s/%s", dirPath, file); if ( ! ( f = fopen( filePath, "w" ) ) ) { Log_err( "Cannot open %s", filePath ); return FALSE; } fprintf( f, "%s", text ); fclose( f ); return TRUE; } #if defined(UTIL_TEST) /* Test code borrowed from wildmat.c. Yep, still uses gets(). */ extern char *gets(); int main() { Str line; time_t t; printf( "Util date tester. Enter date to test.\n" ); printf( "A blank line exits the program.\n" ); for ( ; ; ) { t = time( NULL ); Utl_newsDate( t, line ); printf( "\n(%s) Enter date: ", line ); (void) fflush( stdout ); if ( gets( line ) == NULL || line[0] == '\0' ) break; t = Utl_parseNewsDate( line ); if ( t == (time_t) -1 ) printf( "Date parse failed\n" ); else { Utl_newsDate( t, line ); printf( "Utl_newsDate -> '%s'\n", line ); } } exit(0); /* NOTREACHED */ } #endif