comparison src/expire.c @ 255:52f467c7213b noffle

[svn] * docs/noffle.1,src/Makefile.am,src/Makefile.in,src/content.c, src/content.h,src/database.c,src/database.h,src/expire.c, src/expire.h,src/noffle.c: Split out expire code from database.c, change to remove articles in place (rather than rebuild article database) and add separate command to rebuild article database from articles listed in overviews. This may help if the article database gets corrupted.
author bears
date Wed, 26 Jun 2002 14:15:44 +0100
parents
children
comparison
equal deleted inserted replaced
254:4c0f54d51591 255:52f467c7213b
1 /*
2 expire.c
3
4 $Id: expire.c 387 2002-06-26 13:15:44Z bears $
5
6 Handle expiring articles from the article base.
7 */
8
9 #if HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <stdio.h>
14 #include <errno.h>
15 #include <unistd.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include "configfile.h"
19 #include "content.h"
20 #include "database.h"
21 #include "expire.h"
22 #include "fetchlist.h"
23 #include "group.h"
24 #include "itemlist.h"
25 #include "log.h"
26 #include "protocol.h"
27 #include "pseudo.h"
28 #include "util.h"
29 #include "portable.h"
30
31 /*
32 * Find the maximum expire time in days for this article.
33 * Different groups may have different limits, so we need to
34 * check the limit for each group.
35 */
36 static int
37 calcExpireDays( const char *msgId )
38 {
39 const char *xref;
40 ItemList *refs;
41 const char *ref;
42 int res;
43
44 xref = Db_xref( msgId );
45 if ( xref[ 0 ] == '\0' )
46 return -1;
47
48 res = -1;
49 refs = new_Itl( xref, " :" );
50 for ( ref = Itl_first( refs ); ref != NULL; ref = Itl_next( refs ) )
51 {
52 int days;
53
54 days = Cfg_expire( ref );
55 if ( days == 0
56 || ( days > res && res != 0 ) )
57 res = days;
58
59 Itl_next( refs ); /* Throw away group number */
60 }
61 del_Itl( refs );
62
63 return res;
64 }
65
66 /* Does this article need to be expired? */
67 static Bool
68 articleExpired( const char *msgId, time_t now )
69 {
70 int expDays;
71 time_t lastAccess;
72 Str expires;
73 time_t texpires;
74
75 expDays = calcExpireDays( msgId );
76 if ( expDays == -1 )
77 {
78 Log_err( "Internal error: Failed expiry calculation on %s",
79 msgId );
80 return TRUE;
81 }
82
83 lastAccess = Db_lastAccess( msgId );
84 if ( lastAccess == -1 )
85 {
86 Log_err( "Internal error: Getting lastAccess of %s failed",
87 msgId );
88 return TRUE;
89 }
90
91 if ( Prt_searchHeader( Db_header( msgId ), "Expires", expires ) )
92 texpires = Utl_parseNewsDate( expires );
93 else
94 texpires = (time_t) -1;
95
96 if ( expDays > 0 &&
97 difftime( now, lastAccess ) > ( (double) expDays * 24 * 3600 ) )
98 {
99 #ifdef DEBUG
100 Str lastStr, nowStr;
101
102 Utl_cpyStr( lastStr, ctime( &lastAccess ) );
103 lastStr[ strlen( lastStr ) - 1 ] = '\0';
104 Utl_cpyStr( nowStr, ctime( &now ) );
105 nowStr[ strlen( nowStr ) - 1 ] = '\0';
106 Log_dbg( LOG_DBG_EXPIRE,
107 "Expiring %s: last access %s, time now %s",
108 msgId, lastStr, nowStr );
109 #endif
110 }
111 else if ( ( texpires != (time_t) -1 ) && now > texpires )
112 {
113 Log_dbg( LOG_DBG_EXPIRE,
114 "Expiring %s: Expires header activated", msgId );
115 }
116 else
117 return FALSE;
118
119 return TRUE;
120 }
121
122 /* Work though all overviews looking for articles to expire. */
123 void
124 Exp_expire( void )
125 {
126 const Over *ov;
127 int i;
128 int cntDel, cntLeft;
129 Str grp;
130 Bool autoUnsubscribe;
131 int autoUnsubscribeDays;
132 time_t now, maxAge = 0;
133 const char *msgId;
134
135 autoUnsubscribe = Cfg_autoUnsubscribe();
136 autoUnsubscribeDays = Cfg_autoUnsubscribeDays();
137 maxAge = Cfg_autoUnsubscribeDays() * 24 * 3600;
138 if ( ! Cont_firstGrp( grp ) )
139 return;
140 Log_inf( "Expiring articles" );
141 Fetchlist_read();
142 now = time( NULL );
143 do
144 {
145 if ( ! Grp_exists( grp ) )
146 Log_err( "Overview file for unknown group %s exists", grp );
147 else
148 {
149 cntDel = cntLeft = 0;
150 Cont_read( grp );
151 for ( i = Cont_first(); i <= Cont_last(); ++i )
152 {
153 if ( ! Cont_validNumb( i ) )
154 continue;
155
156 if ( ( ov = Cont_get( i ) ) )
157 {
158 msgId = Ov_msgId( ov );
159 /* Crossposted articles may have already been deleted. */
160 if ( ! Db_contains( msgId ) )
161 {
162 Cont_delete( i );
163 ++cntDel;
164 } else if ( articleExpired( msgId, now ) )
165 {
166 Cont_delete( i );
167 Db_delete( msgId );
168 ++cntDel;
169 }
170 else
171 ++cntLeft;
172 }
173 }
174
175 /*
176 * Auto unsubscribe where applicable if last article arrival
177 * time is maxAge newer than the last access time. This ensures
178 * the low traffic groups don't get expired simply because
179 * there's been nothing to read.
180 */
181 if ( ! Grp_local( grp )
182 && Fetchlist_contains( grp, NULL )
183 && autoUnsubscribe
184 && difftime( Grp_lastPostTime(grp),
185 Grp_lastAccess( grp ) ) > maxAge )
186 {
187 Log_ntc( "Auto-unsubscribing from %s after %d "
188 "days without access",
189 grp, autoUnsubscribeDays );
190 Pseudo_autoUnsubscribed( grp, autoUnsubscribeDays );
191 Fetchlist_remove( grp );
192 Grp_setRmtNext( grp, GRP_RMT_NEXT_NOT_SUBSCRIBED );
193 }
194 if ( Cont_write() )
195 Grp_setFirstLast( grp, Cont_first(), Cont_last() );
196 Log_inf( "%ld overviews deleted from group %s, %ld left (%ld-%ld)",
197 cntDel, grp, cntLeft, Grp_first( grp ), Grp_last( grp ) );
198 }
199 }
200 while ( Cont_nextGrp( grp ) );
201 Fetchlist_write();
202 Db_compact();
203 }