view src/fetch.c @ 155:22b81617d427 noffle

[svn] applied patch from Matija Nalis: better handling of inconsistent counters at remote server; do not read overview on each group command
author enz
date Tue, 05 Dec 2000 19:50:09 +0000
parents b2a4f839affd
children 09ca6eb5c7ff
line wrap: on
line source

/*
  fetch.c

  $Id: fetch.c 236 2000-12-05 19:50:09Z enz $
*/

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

#include <stdio.h>
#include "fetch.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 "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;
}

void
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;
    }
    Log_inf( "Updating groupinfo" );
    Client_getNewgrps( &t );
}

/* Databases open on entry, closed on exit. */
static void
fetchNewArts( const char *name, FetchMode mode )
{
    int next, first, last, refetch;

    if ( ! Client_changeToGrp( name ) )
    {
        Log_err( "Could not change to group %s", name );
	if ( Lock_gotLock() )
	    Lock_closeDatabases();
        return;
    }
    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;
    }
    if ( first == 0 && last == 0 )
    {
        Log_inf( "No articles in %s", name );
        Cont_write();
        Grp_setFirstLast( name, Cont_first(), Cont_last() );
	Lock_closeDatabases();
        return;
    }
    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 );
    }
    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();
    Client_getOver( name, first, last, mode );
}

void
Fetch_getNewArts( const char *name, FetchMode mode )
{
    if ( ! Lock_openDatabases() )
    {
        Log_err( "Could not open message base" );
        return;
    }
    fetchNewArts( name, mode );
}

void
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;
    }
    Fetchlist_read();
    size = Fetchlist_size();
    for ( i = 0; i < size; ++i )
    {
        Fetchlist_element( &name, &mode, i );
        if ( strcmp( Grp_server( name ), fetch.serv ) == 0 )
	{
            fetchNewArts( name, mode );
	    if ( ! Lock_openDatabases() )
	    {
		Log_err( "Could not open message base" );
		return;
	    }
	}
    }
    Lock_closeDatabases();
}

static void
fetchMessageList( const char *list, int *artcnt, int artmax )
{
    const char *p;
    Str msgId;

    ASSERT( Lock_gotLock() );
    Client_retrieveArtList( list, artcnt, artmax );
    p = list;
    while ( ( p = Utl_getLn( msgId, p ) ) )
        Req_remove( fetch.serv, msgId );
}

void
Fetch_getReq_( void )
{
    Str msgId;
    DynStr *list;
    DynStr *fetchList;
    const char *p;
    int count = 0, artcnt = 0, artmax = 0;

    ASSERT( fetch.ready );
    Log_dbg( "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;
    }

    /*
     * 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;
    }

    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 );
    while ( ( p = Utl_getLn( msgId, p ) ) != NULL )
    {
	DynStr_appLn( fetchList, msgId );
	if ( ++count % MAX_ARTICLE_CMDS_QUEUED == 0 )
	{
	    fetchMessageList( DynStr_str( fetchList ), &artcnt, artmax );
	    DynStr_clear( fetchList );
	}
    }
    fetchMessageList( DynStr_str( fetchList ), &artcnt, artmax );

    del_DynStr( fetchList );
    del_DynStr( list );
    Lock_closeDatabases();
}

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 );
    }
}

void
Fetch_postArts( void )
{
    DynStr *s;
    Str msgId, errStr, sender;
    const char *txt;

    s = new_DynStr( 10000 );
    if ( Out_first( fetch.serv, msgId, s ) )
    {
        Log_inf( "Posting articles" );
        do
        {
            txt = DynStr_str( s );
            Out_remove( fetch.serv, msgId );
            if ( ! Client_postArt( msgId, txt, errStr ) )
            {
                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 );
}

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();
}