view src/protocol.c @ 193:021d145e34e9 noffle

[svn] * src/fetch.c: Only leave articles in the requested list if the error fetching them was fatal. Otherwise article requests will accumulate indefinitely (e.g retrieving through NNTPcache when it can't find the body of an article, now or event. Yes, this happened to me; I had nearly 2000 requests backed up and never being cleared). * src/group.c: The weekend's change introduced code that causes a bus error on Sparc ( *(time_t *)p = xxx ). Replace with a safe memcpy, and also use memcpy when reading the Entry and time items to remove warnings on Sparc compilation.
author bears
date Mon, 22 Oct 2001 14:41:43 +0100
parents fed1334d766b
children 76460d98b2fb
line wrap: on
line source

/*
  protocol.c

  $Id: protocol.c 300 2001-08-05 08:24:22Z bears $
*/

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

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

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

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

    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 && line[ len - 1 ] == '\n' )
    {
        line[ len - 1 ] = '\0';
        if ( len > 1 && line[ len - 2 ] == '\r' )
            line[ len - 2 ] = '\0';
    }
    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 );
    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;
    }
    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 Bool
getFQDN( 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;
}

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

    if ( 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;

    len = strlen( msgId );
    p = strstr( msgId, "@" );
    if ( msgId[ 0 ] != '<' || msgId[ len - 1 ] != '>' || p == NULL )
        return FALSE;
    strcpy( domain, p + 1 );
    domain[ strlen( domain ) - 1 ] = '\0';
    headLen = p - msgId - 1;
    Utl_cpyStrN( head, msgId + 1, headLen );
    head[ headLen ] = '\0';
    /*
      To do: check for special characters in head and domain (non-printable
      or '@', '<', '>'). Maybe compare domain with a config option 
      and replace it by the config option, if not equal.
     */
    if ( strstr( domain, "." ) == NULL )
        return FALSE;
    return TRUE;
}

void
Prt_genMsgId( Str msgId, const char *from, const char *suffix )
{
    Str domain, date;
    time_t t;
    static Bool randSeeded = FALSE;

    if ( ! randSeeded )
    {
	struct timeval tv;
	struct timezone tz;

	if ( gettimeofday( &tv, &tz ) == 0 )
	    srand( (unsigned int) tv.tv_usec );
	randSeeded = TRUE;
    }
    getDomain( domain, from );
    time( &t );
    strftime( date, MAXCHAR, "%Y%m%d%H%M%S", gmtime( &t ) );
    snprintf( msgId, MAXCHAR, "<%s.%X.%s@%s>", date, rand(), 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 ( ! 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 */
    Utl_cpyStr( fromHdr, pwd->pw_name );
    Utl_catStr( fromHdr, "@" );
    Utl_catStr( fromHdr, domain );
    Utl_catStr( fromHdr, " (" );
    Utl_catStr( fromHdr, name );
    Utl_catStr( fromHdr, ")" );

    return TRUE;
}