view src/expire.c @ 478:2d5fb05adbc3 noffle

[svn] update
author godisch
date Wed, 28 Jul 2004 17:18:45 +0100
parents 52f467c7213b
children
line wrap: on
line source

/*
  expire.c

  $Id: expire.c 387 2002-06-26 13:15:44Z bears $

  Handle expiring articles from the article base.
*/

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

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "configfile.h"
#include "content.h"
#include "database.h"
#include "expire.h"
#include "fetchlist.h"
#include "group.h"
#include "itemlist.h"
#include "log.h"
#include "protocol.h"
#include "pseudo.h"
#include "util.h"
#include "portable.h"

/*
 * Find the maximum expire time in days for this article.
 * Different groups may have different limits, so we need to
 * check the limit for each group.
 */
static int
calcExpireDays( const char *msgId )
{
    const char *xref;
    ItemList *refs;
    const char *ref;
    int res;

    xref = Db_xref( msgId );
    if ( xref[ 0 ] == '\0' )
	return -1;

    res = -1;
    refs = new_Itl( xref, " :" );
    for ( ref = Itl_first( refs ); ref != NULL; ref = Itl_next( refs ) )
    {
	int days;

	days = Cfg_expire( ref );
	if ( days == 0
	     || ( days > res && res != 0 ) )
	    res = days;
	
	Itl_next( refs );	/* Throw away group number */
    }
    del_Itl( refs );

    return res;
}

/* Does this article need to be expired? */
static Bool
articleExpired( const char *msgId, time_t now )
{
    int expDays;
    time_t lastAccess;
    Str expires;
    time_t texpires;

    expDays = calcExpireDays( msgId );
    if ( expDays == -1 )
    {
	Log_err( "Internal error: Failed expiry calculation on %s",
		 msgId );
	return TRUE;
    }
    
    lastAccess = Db_lastAccess( msgId );
    if ( lastAccess == -1 )
    {
	Log_err( "Internal error: Getting lastAccess of %s failed",
		 msgId );
	return TRUE;
    }
    
    if ( Prt_searchHeader( Db_header( msgId ), "Expires", expires ) )
	texpires = Utl_parseNewsDate( expires );
    else
	texpires = (time_t) -1;
	    
    if ( expDays > 0 &&
	 difftime( now, lastAccess ) > ( (double) expDays * 24 * 3600 ) )
    {
#ifdef DEBUG
	Str lastStr, nowStr;

	Utl_cpyStr( lastStr, ctime( &lastAccess ) );
	lastStr[ strlen( lastStr ) - 1 ] = '\0';
	Utl_cpyStr( nowStr, ctime( &now ) );
	nowStr[ strlen( nowStr ) - 1 ] = '\0';
	Log_dbg( LOG_DBG_EXPIRE,
		 "Expiring %s: last access %s, time now %s",
		 msgId, lastStr, nowStr );
#endif
    }
    else if ( ( texpires != (time_t) -1 ) && now > texpires )
    {
	Log_dbg( LOG_DBG_EXPIRE,
		 "Expiring %s: Expires header activated", msgId );
    }
    else
	return FALSE;

    return TRUE;
}

/* Work though all overviews looking for articles to expire. */
void
Exp_expire( void )
{
    const Over *ov;
    int i;
    int cntDel, cntLeft;
    Str grp;
    Bool autoUnsubscribe;
    int autoUnsubscribeDays;
    time_t now, maxAge = 0;
    const char *msgId;

    autoUnsubscribe = Cfg_autoUnsubscribe();
    autoUnsubscribeDays = Cfg_autoUnsubscribeDays();
    maxAge = Cfg_autoUnsubscribeDays() * 24 * 3600;
    if ( ! Cont_firstGrp( grp ) )
        return;
    Log_inf( "Expiring articles" );
    Fetchlist_read();
    now = time( NULL );
    do
    {
	if ( ! Grp_exists( grp ) )
            Log_err( "Overview file for unknown group %s exists", grp );
        else
        {
            cntDel = cntLeft = 0;
            Cont_read( grp );
            for ( i = Cont_first(); i <= Cont_last(); ++i )
	    {
		if ( ! Cont_validNumb( i ) )
		    continue;
		
                if ( ( ov = Cont_get( i ) ) )
                {
                    msgId = Ov_msgId( ov );
		    /* Crossposted articles may have already been deleted. */
		    if ( ! Db_contains( msgId ) )
		    {
			Cont_delete( i );
			++cntDel;
		    } else if ( articleExpired( msgId, now ) )
                    {
                        Cont_delete( i );
			Db_delete( msgId );
                        ++cntDel;
                    }
                    else
                        ++cntLeft;
                }
	    }

	    /*
	     * Auto unsubscribe where applicable if last article arrival
	     * time is maxAge newer than the last access time. This ensures
	     * the low traffic groups don't get expired simply because
	     * there's been nothing to read.
	     */
            if ( ! Grp_local( grp )
                 && Fetchlist_contains( grp, NULL )
                 && autoUnsubscribe
                 && difftime( Grp_lastPostTime(grp),
			      Grp_lastAccess( grp ) ) > maxAge )
            {
		Log_ntc( "Auto-unsubscribing from %s after %d "
			 "days without access",
			 grp, autoUnsubscribeDays );
		Pseudo_autoUnsubscribed( grp, autoUnsubscribeDays );
		Fetchlist_remove( grp );
		Grp_setRmtNext( grp, GRP_RMT_NEXT_NOT_SUBSCRIBED );
            }
            if ( Cont_write() )
                Grp_setFirstLast( grp, Cont_first(), Cont_last() );
            Log_inf( "%ld overviews deleted from group %s, %ld left (%ld-%ld)",
                     cntDel, grp, cntLeft, Grp_first( grp ), Grp_last( grp ) );
        }
    }
    while ( Cont_nextGrp( grp ) );
    Fetchlist_write();
    Db_compact();
}