Mercurial > noffle
view src/fetch.c @ 197:24d4cd032da5 noffle
[svn] * AUTHORS,INSTALL,NEWS,README,TODO,docs/NOTES,src/client.c,src/protocol.c,
src/protocol.h,src/server.c: Merge with latest 1.0 branch. I do not
intend to do any further merges from the 1.0 branch; future changes to
1.0 should be carried over by hand if necessary.
* src/configfile.c,src/content.c,src/control.c,src/database.c,
src/dynamicstring.c,src/fetch.c,src/filter.c,src/group.c,
src/itemlist.c,src/lock.c,src/outgoing.c,src/over.c,src/post.c,
src/pseudo.c,src/request.c,src/util.c: Minor includes rearrangement.
author | bears |
---|---|
date | Wed, 31 Oct 2001 11:44:53 +0000 |
parents | 021d145e34e9 |
children | ffb1848a39db |
line wrap: on
line source
/* fetch.c $Id: fetch.c 316 2001-10-31 11:44:53Z 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 "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 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 ); 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 ); 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; } 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 returnArticleToSender( const char *sender, const char *reason, const char *article ) { int ret; Str cmd; FILE *f; sig_t 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 ) ); else { fprintf( f, "To: %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, reason, article ); ret = pclose( f ); if ( ret != EXIT_SUCCESS ) Log_err( "'%s' exit value %d", cmd, ret ); 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, "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 ); else returnArticleToSender( sender, errStr, 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(); }