Mercurial > noffle
view src/fetch.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 | d9035d08fe51 |
children |
line wrap: on
line source
/* fetch.c $Id: fetch.c 609 2003-07-23 09:32:25Z bears $ */ #if HAVE_CONFIG_H #include <config.h> #endif #include <stdio.h> #include <errno.h> #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 <signal.h> #include "client.h" #include "configfile.h" #include "content.h" #include "dynamicstring.h" #include "fetch.h" #include "fetchlist.h" #include "filter.h" #include "request.h" #include "group.h" #include "lock.h" #include "log.h" #include "outgoing.h" #include "protocol.h" #include "pseudo.h" #include "util.h" #include "portable.h" #define MAX_ARTICLE_CMDS_QUEUED 20 static struct Fetch { Bool ready; Str serv; } fetch = { FALSE, "" }; static Bool connectToServ( const char *name ) { Log_inf( "Fetch from '%s'", name ); if ( ! Client_connect( name ) ) { Log_err( "Could not connect to %s", name ); return FALSE; } return TRUE; } Bool Fetch_getNewGrps( void ) { time_t t; Str file; ASSERT( fetch.ready ); snprintf( file, MAXCHAR, "%s/lastupdate.%s", Cfg_spoolDir(), fetch.serv ); if ( ! Utl_getStamp( &t, file ) ) { Log_err( "Cannot read %s. Please run noffle --query groups", file ); return FALSE; } Log_inf( "Updating groupinfo" ); /* * If NEWGROUPS fails, it isn't necessarily fatal. You can do * a periodic noffle --query groups to refresh your group list. * So only return failure here if the status indicates the link * itself failed. * * In particular, older versions of NNTPcache have a Y2K bug that * stops NEWGROUPS working. */ return ( ! IS_FATAL( Client_getNewgrps( &t ) ) ); } /* Databases open on entry, closed on exit. */ static int fetchNewArts( const char *name, FetchMode mode ) { int next, first, last, refetch, stat; stat = Client_changeToGrp( name ); if ( stat != STAT_OK ) { Log_err( "Could not change to group %s", name ); if ( Lock_gotLock() ) Lock_closeDatabases(); return stat; } Client_rmtFirstLast( &first, &last ); Cont_read( name ); next = Grp_rmtNext( name ); if ( next == GRP_RMT_NEXT_NOT_SUBSCRIBED ) next = first; if ( next == last + 1 ) { Log_inf( "No new articles in %s", name ); if ( Cont_write() ) Grp_setFirstLast( name, Cont_first(), Cont_last() ); Lock_closeDatabases(); return STAT_OK; } if ( first == 0 && last == 0 ) { Log_inf( "No articles in %s", name ); if ( Cont_write() ) Grp_setFirstLast( name, Cont_first(), Cont_last() ); Lock_closeDatabases(); return STAT_OK; } if ( next > last + 1 ) { refetch = last - Cfg_maxFetch() + 1; if ( refetch < 0 ) refetch = 1; Log_err( "Article number inconsistent (%s rmt=%lu-%lu, next=%lu). " "Refetching from %lu", name, first, last, next, refetch ); Pseudo_cntInconsistent( name, first, last, next, refetch ); first = refetch; } else if ( next < first ) { Log_inf( "Missing articles (%s first=%lu next=%lu)", name, first, next ); Pseudo_missArts( name, first, next ); /* * If we are missing articles but there are none to fetch, * we must ensure we don't repeatedly generate missing * article warning on every fetch until there is something * to fetch. To guard against this, update the group remote * next now. */ Grp_setRmtNext( name, first ); next = first; } else first = next; if ( last - first > Cfg_maxFetch() ) { Log_ntc( "Cutting number of overviews to %lu", Cfg_maxFetch() ); first = last - Cfg_maxFetch() + 1; } Log_inf( "Getting remote overviews %lu-%lu for group %s", first, last, name ); Lock_closeDatabases(); return Client_getOver( name, first, last, mode ); } Bool Fetch_getNewArts( const char *name, FetchMode mode ) { if ( ! Lock_openDatabases() ) { Log_err( "Could not open message base" ); return FALSE; } Flt_init( fetch.serv ); /* Get filter data. Sorry, can't do it in Client_getOver(). * This is the lowest procedure not in the * noffle.c:doFetch() tree. */ return fetchNewArts( name, mode ); } Bool Fetch_updateGrps( void ) { FetchMode mode; int i, size; const char *name; ASSERT( fetch.ready ); if ( ! Lock_openDatabases() ) { Log_err( "Could not open message base" ); return FALSE; } Fetchlist_read(); size = Fetchlist_size(); for ( i = 0; i < size; ++i ) { Fetchlist_element( &name, &mode, i ); if ( strcmp( Grp_server( name ), fetch.serv ) == 0 ) { if ( IS_FATAL( fetchNewArts( name, mode ) ) ) return FALSE; if ( ! Lock_openDatabases() ) { Log_err( "Could not open message base" ); return FALSE; } } } Lock_closeDatabases(); return TRUE; } static int fetchMessageList( const char *list, int *artcnt, int artmax ) { const char *p; Str msgId; int stat; ASSERT( Lock_gotLock() ); stat = Client_retrieveArtList( list, artcnt, artmax ); if ( IS_FATAL( stat ) ) return stat; p = list; while ( ( p = Utl_getLn( msgId, p ) ) ) Req_remove( fetch.serv, msgId ); return STAT_OK; } Bool Fetch_getReq_( void ) { Str msgId; DynStr *list; DynStr *fetchList; const char *p; int count = 0, artcnt = 0, artmax = 0; Bool res; int stat; ASSERT( fetch.ready ); Log_dbg( LOG_DBG_FETCH, "Retrieving articles marked for download" ); list = new_DynStr( 10000 ); fetchList = new_DynStr( 1000 ); if ( list == NULL || fetchList == NULL ) { if ( list != NULL ) del_DynStr( list ); Log_err( "Out of memory in Fetch_get_Req_"); return FALSE; } /* * Get all waiting message IDs for this server. We copy into a master * list as the requests file will be closed and re-opened during the * fetch and the position therein will be lost. */ if ( ! Lock_openDatabases() ) { Log_err( "Could not open message base" ); return FALSE; } if ( Req_first( fetch.serv, msgId ) ) { do { DynStr_appLn( list, msgId ); artmax++; } while ( Req_next( msgId ) ); Log_inf( "%d TOTAL messages to download", artmax); } /* Retrieve in groups of up to size MAX_ARTICLE_CMDS_QUEUED. */ p = DynStr_str( list ); res = TRUE; while ( res && ( p = Utl_getLn( msgId, p ) ) != NULL ) { DynStr_appLn( fetchList, msgId ); if ( ++count % MAX_ARTICLE_CMDS_QUEUED == 0 ) { stat = fetchMessageList( DynStr_str( fetchList ), &artcnt, artmax ); res = ! IS_FATAL( stat ); DynStr_clear( fetchList ); } } stat = fetchMessageList( DynStr_str( fetchList ), &artcnt, artmax ); res = res && ! IS_FATAL( stat ); del_DynStr( fetchList ); del_DynStr( list ); Lock_closeDatabases(); return res; } static void saveArticleInFailedPostings( const char *msgId, const char *article ) { Utl_writeFile( Cfg_spoolDir(), "failed.posting", msgId, article ); } static void returnArticleToSender( const char *sender, const char *reason, const char *msgId, const char *article ) { int ret; Str cmd; FILE *f; SignalHandler lastHandler; Log_err( "Return article to '%s' by mail", sender ); snprintf( cmd, MAXCHAR, "%s -t -oi", SENDMAILPROG); lastHandler = signal( SIGPIPE, SIG_IGN ); f = popen( cmd, "w" ); if ( f == NULL ) { Log_err( "Invocation of '%s' failed (%s)", cmd, strerror( errno ) ); saveArticleInFailedPostings( msgId, article ); } else { fprintf( f, "To: %s\n" "From: %s\n" "Subject: [ NOFFLE: Posting failed ]\n" "\n" "\t[ NOFFLE: POSTING OF ARTICLE FAILED ]\n" "\n" "\t[ The posting of your article failed. ]\n" "\t[ Reason of failure at remote server: ]\n" "\n" "\t[ %s ]\n" "\n" "\t[ Full article text has been appended. ]\n" "\n" "%s" ".\n", sender, Cfg_mailFrom(), reason, article ); ret = pclose( f ); if ( ret != EXIT_SUCCESS ) { Log_err( "'%s' exit value %d", cmd, ret ); saveArticleInFailedPostings( msgId, article ); } signal( SIGPIPE, lastHandler ); } } Bool Fetch_postArts( void ) { DynStr *s; Str msgId, errStr, sender; const char *txt; Bool res; res = TRUE; s = new_DynStr( 10000 ); if ( Out_first( fetch.serv, msgId, s ) ) { Log_inf( "Posting articles" ); do { txt = DynStr_str( s ); if ( Client_postArt( msgId, txt, errStr ) != STAT_OK ) { res = FALSE; break; } /* * OK, no server communication SNAFU during post. Now, do we * get an error response? If so, try to return article to sender. */ Out_remove( fetch.serv, msgId ); if ( errStr[0] != '\0' ) { Utl_cpyStr( sender, Cfg_mailTo() ); if ( strcmp( sender, "" ) == 0 && ! Prt_searchHeader( txt, "REPLY-TO", sender ) && ! Prt_searchHeader( txt, "SENDER", sender ) && ! Prt_searchHeader( txt, "X-NOFFLE-X-SENDER", sender ) /* see server.c */ && ! Prt_searchHeader( txt, "FROM", sender ) ) { Log_err( "Article %s has no From/Sender/X-Sender field", msgId ); saveArticleInFailedPostings( msgId, txt ); } else returnArticleToSender( sender, errStr, msgId, txt ); } } while ( Out_next( msgId, s ) ); } del_DynStr( s ); return res; } Bool Fetch_init( const char *serv ) { Lock_closeDatabases(); if ( ! connectToServ( serv ) ) { Lock_openDatabases(); return FALSE; } Utl_cpyStr( fetch.serv, serv ); fetch.ready = TRUE; return TRUE; } void Fetch_close() { Client_disconnect(); fetch.ready = FALSE; Log_inf( "Fetch from '%s' finished", fetch.serv ); Lock_openDatabases(); }