view src/protocol.c @ 279:49b452b667a6 noffle

[svn] * src/util.c: localTimeDiff() cached its value and recalculated it every hour of clock time, regardless of the time the calculated was based on. This is potentially dangerous at daylight saving changes. So instead use the cached last result only when the new request is to be based on a time in the same hour as the cached result. * src/util.c: Replace the alternate Utl_mktimeGMT() implementation used when timegm() is not available. The previous version, as suggested by the glibc timegm() man page, used setenv() and unsetenv() for changing the environment. These aren't POSIX function, and the POSIX putenv() (a) is tricky to manage if the same var is being constantly update and memory isn't to leak, and (b) provides no way to remove an environment entry. So change to an implementation Wget uses. This should compile on not glibc systems - the previous version failed to build on Solaris.
author bears
date Sun, 17 Nov 2002 15:18:19 +0000
parents 3477050e8d10
children baa6408d1bbc
line wrap: on
line source

/*
  protocol.c

  $Id: protocol.c 403 2002-11-10 11:32:17Z bears $
*/

#if HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h> 
#include <ctype.h> 
#include <pwd.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include "common.h"
#include "configfile.h"
#include "dynamicstring.h"
#include "log.h"
#include "over.h"
#include "util.h"
#include "portable.h"
#include "protocol.h"

static void
readAlarm( int sig )
{
    UNUSED( sig );
    
    return;
}

Bool
Prt_getLn( Str line, FILE *f, int timeoutSeconds )
{
    size_t len;
    char *ret;
    SignalHandler oldHandler = NULL;
    int line_too_long;

    if ( timeoutSeconds >= 0 )
    {
        oldHandler = Utl_installSignalHandler( SIGALRM, readAlarm );
        if ( oldHandler == SIG_ERR )
        {
            Log_err( "Prt_getLn: signal failed." );
            return FALSE;
        }
        if ( alarm( timeoutSeconds ) != 0 )
            Log_err( "Prt_getLn: Alarm was already set." );
    }
    /*
      We also accept lines ending with "\n" instead of "\r\n", some
      clients wrongly send such lines.
    */
    ret = fgets( line, MAXCHAR, f );
    if ( timeoutSeconds >= 0 )
    {
        alarm( 0 );
        Utl_installSignalHandler( SIGALRM, oldHandler );
    }
    if ( ret == NULL )
        return FALSE;
    len = strlen( line );
    if ( len > 0 )
    {
        if ( line[ len - 1 ] == '\n' )
        {
            line[ len - 1 ] = '\0';
            if ( len > 1 && line[ len - 2 ] == '\r' )
                line[ len - 2 ] = '\0';
        }
        else
        {
        /* line too long, skip the rest */
            if( len == MAXCHAR )
            {
                Log_err( "Line too long:" );
                do
                {
                    line_too_long = fgetc( f );
                }
                while( line_too_long == EOF || line_too_long == '\n' );
            }
            else
            /* EOF occured in line, skip line. */
            {
                Log_err( "Ignoring incomplete line %s", line);
                return FALSE;
            }
        }
    }
    Log_dbg( LOG_DBG_PROTOCOL, "[R] %s", line );
    return TRUE;
}

Bool
Prt_getTxtLn( Str line, Bool *err, FILE *f, int timeoutSeconds )
{
    Str buf;

    if ( ! Prt_getLn( buf, f, timeoutSeconds ) )
    {
        Log_err( "Cannot get text line" );
        *err = TRUE;
        return FALSE;
    }
    *err = FALSE;
    if ( buf[ 0 ] == '.' )
    {
        if ( buf[ 1 ] == 0 )
            return FALSE;
        else
            strcpy( line, buf + 1 );
    }
    else
        strcpy( line, buf );
    return TRUE;
}

Bool
Prt_putTxtLn( const char* line, FILE *f )
{
    if ( line[ 0 ] == '.' )
    {
        Log_dbg( LOG_DBG_PROTOCOL, "[S] .%s", line );
        return ( fprintf( f, ".%s\r\n", line ) == (int)strlen( line ) + 3 );
    }
    else
    {
        Log_dbg( LOG_DBG_PROTOCOL, "[S] %s", line );
        return ( fprintf( f, "%s\r\n", line ) == (int)strlen( line ) + 2 );
    }
}

Bool
Prt_putEndOfTxt( FILE *f )
{
    Log_dbg( LOG_DBG_PROTOCOL, "[S] ." );
    return ( fprintf( f, ".\r\n" ) == 3 );
}

/*
  Write text buffer of lines each ending with '\n'.
  Replace '\n' by "\r\n".
*/
Bool
Prt_putTxtBuf( const char *buf, FILE *f )
{
    Str line;
    const char *pBuf;
    char *pLn;

    pBuf = buf;
    pLn = line;
    while ( *pBuf != '\0' )
    {
        if ( *pBuf == '\n' )
        {
            *pLn = '\0';
            if ( ! Prt_putTxtLn( line, f ) )
                return FALSE;
            pLn = line;
            ++pBuf;
        }
        else if ( pLn - line >= MAXCHAR - 1 )
        {
            /* Put it out raw to prevent String overflow */
            Log_err( "Writing VERY long line" );
            *pLn = '\0';
            if ( fprintf( f, "%s", line ) != (int)strlen( line ) )
                return FALSE;
            pLn = line;
        }   
        else
            *(pLn++) = *(pBuf++);
    }
    return TRUE;
}

Bool
Prt_getField( Str resultField, Str resultValue, const char* line )
{
    char *dst;
    const char *p;
    Str lineLower, t;
    
    Utl_cpyStr( lineLower, line );
    Utl_toLower( lineLower );
    p = Utl_stripWhiteSpace( lineLower );
    if ( p == lineLower )
    {
        dst = resultField;
        while ( ! isspace( *p ) && *p != ':' && *p != '\0' )
            *(dst++) = *(p++);
        *dst = '\0';
        while ( isspace( *p ) )
            ++p;    
        if ( *p == ':' )
        {
            ++p;
            strcpy( t, line + ( p - lineLower ) );
            p = Utl_stripWhiteSpace( t );
            strcpy( resultValue, p );
            return TRUE;
        }
    }
    else        /* continued header.  */
    {
            strcpy( resultValue, lineLower ); /* include starting whitespace */
            return TRUE;
    }
    return FALSE;
}

Bool
Prt_searchHeader( const char *artTxt, const char *which, Str result )
{
    const char *src, *p;
    char *dst;
    Str line, whichLower, field;
    int len;
    
    Utl_cpyStr( whichLower, which );
    Utl_toLower( whichLower );
    src = artTxt;
    while ( TRUE )
    {
        dst = line;
        len = 0;
        while ( *src != '\n' && len < MAXCHAR )
        {
            if ( *src == '\0' )
                return FALSE;
            *(dst++) = *(src++);
            ++len;
        }
        if ( *src == '\n' )
            ++src;
        *dst = '\0';
        p = Utl_stripWhiteSpace( line );
        if ( *p == '\0' )
            break;
        if ( Prt_getField( field, result, line )
             && strcmp( field, whichLower ) == 0 )
            return TRUE;
    }
    return FALSE;
}

static void
getDomain( Str domain, const char *from )
{
    const char *addTopLevel, *p1, *p2, *p, *domainStart;
    Str myDomain;

    if ( Utl_getFQDN( myDomain ) )
    {
        p = strstr( myDomain, "." );
        if ( p != NULL )
            domainStart = p + 1;
        else
            domainStart = myDomain;
    }
    else /* Take domain of From field */
    {
        myDomain[ 0 ] = '\0';
        p1 = strstr( from, "@" );
        if ( p1 != NULL )
        {
            p2 = strstr( p1, ">" );
            if ( p2 != NULL )
                Utl_cpyStrN( myDomain, p1 + 1, p2 - p1 - 1 );
        }
        if ( myDomain[ 0 ] == '\0' )
            Utl_cpyStr( myDomain, "unknown" );
        domainStart = myDomain;
    }
    /*
      If domain contains no dot (and is probably invalid anyway),
      we add ".local", because some servers insist on domainnames with dot
      in message ID.
    */
    addTopLevel = strstr( domainStart, "." ) == NULL ? ".local" : "";
    snprintf( domain, MAXCHAR, "%s%s", myDomain, addTopLevel );    
}

/* See RFC 850, section 2.1.7 */
Bool
Prt_isValidMsgId( const char *msgId )
{
    Str head, domain;
    int len, headLen;
    const char *p;
    const char * specials = "\t\r\n ()@<>"; /* hmm, check "\\\'\"[]" as well? */

    len = strlen( msgId );
    if ( len > 250 )
        return FALSE; /* see draft-ietf-usefor-article-06.txt, ch 5.3 */
    p = strchr( msgId, '@' );
    if ( msgId[ 0 ] != '<' || msgId[ len - 1 ] != '>' || p == NULL )
        return FALSE;
    Utl_cpyStr( domain, p + 1 );
    domain[ strlen( domain ) - 1 ] = '\0';
    headLen = p - msgId - 1;
    Utl_cpyStrN( head, msgId + 1, headLen );
    head[ headLen ] = '\0';
    for ( p = msgId ; *p != '\0' ; p++ )
    {
        if ( ! isascii( *p ) )
            return FALSE;
    }
    if ( strpbrk( head, specials ) )
        return FALSE; 
    if ( strpbrk( domain, specials ) )
        return FALSE;
    return TRUE;
}

void
Prt_genMsgId( Str msgId, const char *from, const char *suffix )
{
    Str domain, date;
    time_t t;
    static long count = 0;
    const char *pattern;

    getDomain( domain, from );
    time( &t );
    strftime( date, MAXCHAR, "%Y%m%d%H%M%S", gmtime( &t ) );
    if ( strchr( domain, '@' ) )
        pattern = "<%s.%X.%lx.%s%s>";
    else
        pattern = "<%s.%X.%lx.%s@%s>";
    snprintf( msgId, MAXCHAR, pattern , date, getpid(), count++ ,suffix, domain );
    ASSERT( Prt_isValidMsgId( msgId ) );
}

void
Prt_genPathHdr( Str pathHdr, const char *from )
{
    getDomain( pathHdr, from );
    Utl_catStr( pathHdr, "!not-for-mail" );
}

Bool
Prt_genFromHdr( Str fromHdr )
{
    Str name, domain;
    const char *nameval;
    struct passwd *pwd;

    /* First get the domain to use. If config empty, use FQDN */
    Utl_cpyStr( domain, Cfg_fromDomain() );

    if ( strlen( domain ) == 0 )
	if ( ! Utl_getFQDN( domain ) )
	    Utl_catStr( domain, "unknown" );

    /* Now get pwd for the username */
    pwd = getpwuid( getuid() );
    if ( pwd == NULL )
	return FALSE;

    /* Now for their name - use env NAME if available */
    nameval = getenv( "NAME" );
    if ( nameval != NULL )
	Utl_cpyStr( name, nameval );
    else
    {
	char *p;
	
	/*
	  Extract from GECOS field. Following the lead of the INN inews,
	  ignore leading stuff like "23-" "stuff]-" or "stuff -" as well
	  as trailing whitespace, or anything that comes after
	  a comma or semicolon.
	 */
	nameval = pwd->pw_gecos;
	p = strchr( nameval, '-' );
	if ( p != NULL && p > nameval &&
	     ( p[-1] == ']' || p[-1] == ' ' || isdigit( p[ -1 ] ) ) )
	    nameval = p;
	p = strrchr( nameval, ',' );
	if ( p != NULL )
	    *p = '\0';
	p = strchr( nameval, ';' );
	if ( p != NULL )
	    *p = '\0';
	Utl_cpyStr( name, nameval );
    }

    /* OK, build From: contents */
/*  deprecated.
    Utl_cpyStr( fromHdr, pwd->pw_name );
    Utl_catStr( fromHdr, "@" );
    Utl_catStr( fromHdr, domain );
    Utl_catStr( fromHdr, " (" );
    Utl_catStr( fromHdr, name );
    Utl_catStr( fromHdr, ")" );
*/
    Utl_cpyStr( fromHdr, "\"" );
    Utl_catStr( fromHdr, name );
    Utl_catStr( fromHdr, "\" <" );
    Utl_catStr( fromHdr, pwd->pw_name );
    Utl_catStr( fromHdr, "@" );
    Utl_catStr( fromHdr, domain );
    Utl_catStr( fromHdr, ">" );

    return TRUE;
}