Mercurial > noffle
view src/protocol.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 | 4426f4dc6e8b |
children |
line wrap: on
line source
/* protocol.c $Id: protocol.c 579 2003-06-25 09:40:02Z 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; } /* * Read and return the next line from f. * * The line is considered terminated by '\n' or '\r\n'. If present, * these are removed before the line is returned to the caller. NNTP * says text should be terminated by '\r\n', but just '\n' is * not unknown in the wild. * * Should the line be longer than can fit into the fixed MAXCHAR * buffer of a Str, then return the first MAXCHAR - 2 characters (so * the rest of the system can deal with that line plus a '\n') and * push back the last character, leaving it to be read as the first * character of a subsequent new 'next' line. This is a Bad Thing - * news transports shouldn't bugger around with even pathalogical * articles - but we'll have to live with it for now until someone * gets round to FIXME. * * Lines that are terminated by EOF are returned as normal. Only if * EOF occurs before any characters are read will this routine return * FALSE. */ Bool Prt_getLn( Str line, FILE *f, int timeoutSeconds ) { size_t len; char *ret; SignalHandler 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." ); } 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'; } else if ( len >= MAXCHAR - 1 ) { ungetc( line[ len - 1 ], f ); line[ len - 1 ] = '\0'; Log_err( "Prt_getLn: Input line too long, splitting." ); } 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 Utl_cpyStr( line, buf + 1 ); } else Utl_cpyStr( 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 ) { DynStr *line; const char *eol; Bool res = TRUE; line = new_DynStr(MAXCHAR); while ( res && *buf != '\0' ) { eol = strchr( buf, '\n' ); if ( eol != NULL ) { DynStr_appN( line, buf, eol - buf ); buf = eol + 1; res = Prt_putTxtLn( DynStr_str( line ), f ); DynStr_clear( line ); } else { res = Prt_putTxtLn( buf, f ); break; } } del_DynStr( line ); return res; } Bool Prt_getField( Str resultField, Str resultValue, Bool* isContinuation, const char* line ) { char *dst; const char *p; Str lineLower, t; ASSERT( isContinuation ); *isContinuation = FALSE; 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; Utl_cpyStr( t, line + ( p - lineLower ) ); p = Utl_stripWhiteSpace( t ); Utl_cpyStr( resultValue, p ); return TRUE; } else return FALSE; /* Not a header line */ } else { /* * If the line starts with white space, it can be a header * continuation. */ if( ! isspace( *line ) ) return FALSE; Utl_cpyStr( resultValue, line ); *isContinuation = TRUE; return TRUE; } /* NOTREACHED */ } Bool Prt_searchHeader( const char *artTxt, const char *which, Str result ) { const char *src, *p; char *dst; Str line, whichLower, field; int len; Bool continuation; 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, &continuation, 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; }