view src/expire.c @ 279:49b452b667a6 noffle

[svn] * src/util.c: localTimeDiff() cached its value and recalculated it every hour of clock time, regardless of the time the calculated was based on. This is potentially dangerous at daylight saving changes. So instead use the cached last result only when the new request is to be based on a time in the same hour as the cached result. * src/util.c: Replace the alternate Utl_mktimeGMT() implementation used when timegm() is not available. The previous version, as suggested by the glibc timegm() man page, used setenv() and unsetenv() for changing the environment. These aren't POSIX function, and the POSIX putenv() (a) is tricky to manage if the same var is being constantly update and memory isn't to leak, and (b) provides no way to remove an environment entry. So change to an implementation Wget uses. This should compile on not glibc systems - the previous version failed to build on Solaris.
author bears
date Sun, 17 Nov 2002 15:18:19 +0000 (2002-11-17)
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();
}