comparison src/noffle.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 93d5d8b098da
children d6fedc09b052
comparison
equal deleted inserted replaced
254:4c0f54d51591 255:52f467c7213b
8 not as server. If noffle runs as server, locking is performed while 8 not as server. If noffle runs as server, locking is performed while
9 executing NNTP commands, but temporarily released if no new command is 9 executing NNTP commands, but temporarily released if no new command is
10 received for some seconds (to allow multiple clients connect at the same 10 received for some seconds (to allow multiple clients connect at the same
11 time). 11 time).
12 12
13 $Id: noffle.c 382 2002-06-05 22:03:44Z mirkol $ 13 $Id: noffle.c 387 2002-06-26 13:15:44Z bears $
14 */ 14 */
15 15
16 #if HAVE_CONFIG_H 16 #if HAVE_CONFIG_H
17 #include <config.h> 17 #include <config.h>
18 #endif 18 #endif
28 #include "common.h" 28 #include "common.h"
29 #include "content.h" 29 #include "content.h"
30 #include "control.h" 30 #include "control.h"
31 #include "configfile.h" 31 #include "configfile.h"
32 #include "database.h" 32 #include "database.h"
33 #include "expire.h"
33 #include "fetch.h" 34 #include "fetch.h"
34 #include "fetchlist.h" 35 #include "fetchlist.h"
35 #include "filter.h" 36 #include "filter.h"
36 #include "group.h" 37 #include "group.h"
37 #include "itemlist.h" 38 #include "itemlist.h"
234 Client_getDsc(); 235 Client_getDsc();
235 Fetch_close(); 236 Fetch_close();
236 } 237 }
237 } 238 }
238 239
239 /* Expire all overviews not in database */
240 static void
241 expireContents( void )
242 {
243 const Over *ov;
244 int i;
245 int cntDel, cntLeft;
246 Str grp;
247 Bool autoUnsubscribe;
248 int autoUnsubscribeDays;
249 time_t maxAge = 0;
250 const char *msgId;
251
252 autoUnsubscribe = Cfg_autoUnsubscribe();
253 autoUnsubscribeDays = Cfg_autoUnsubscribeDays();
254 maxAge = Cfg_autoUnsubscribeDays() * 24 * 3600;
255 if ( ! Cont_firstGrp( grp ) )
256 return;
257 Log_inf( "Expiring overviews not in database" );
258 Fetchlist_read();
259 do
260 {
261 if ( ! Grp_exists( grp ) )
262 Log_err( "Overview file for unknown group %s exists", grp );
263 else
264 {
265 cntDel = cntLeft = 0;
266 Cont_read( grp );
267 for ( i = Cont_first(); i <= Cont_last(); ++i )
268 if ( ( ov = Cont_get( i ) ) )
269 {
270 msgId = Ov_msgId( ov );
271 if ( ! Db_contains( msgId ) )
272 {
273 Cont_delete( i );
274 ++cntDel;
275 }
276 else
277 ++cntLeft;
278 }
279
280 /*
281 * Auto unsubscribe where applicable if last article arrival
282 * time is maxAge newer than the last access time. This ensures
283 * the low traffic groups don't get expired simply because
284 * there's been nothing to read.
285 */
286 if ( ! Grp_local( grp )
287 && Fetchlist_contains( grp, NULL )
288 && autoUnsubscribe
289 && difftime( Grp_lastPostTime(grp),
290 Grp_lastAccess( grp ) ) > maxAge )
291 {
292 Log_ntc( "Auto-unsubscribing from %s after %d "
293 "days without access",
294 grp, autoUnsubscribeDays );
295 Pseudo_autoUnsubscribed( grp, autoUnsubscribeDays );
296 Fetchlist_remove( grp );
297 Grp_setRmtNext( grp, GRP_RMT_NEXT_NOT_SUBSCRIBED );
298 }
299 if ( Cont_write() )
300 Grp_setFirstLast( grp, Cont_first(), Cont_last() );
301 Log_inf( "%ld overviews deleted from group %s, %ld left (%ld-%ld)",
302 cntDel, grp, cntLeft, Grp_first( grp ), Grp_last( grp ) );
303 }
304 }
305 while ( Cont_nextGrp( grp ) );
306 Fetchlist_write();
307 }
308
309 static void 240 static void
310 doExpire( void ) 241 doExpire( void )
311 { 242 {
312 Db_close(); 243 Exp_expire();
313 Db_expire(); 244 }
314 if ( ! Db_open() ) 245
315 return; 246 static void
316 expireContents(); 247 doRebuild( void )
248 {
249 if ( ! Db_rebuild() )
250 fprintf( stderr, "Rebuild failed.\n" );
317 } 251 }
318 252
319 static void 253 static void
320 doCreateLocalGroup( const char * name ) 254 doCreateLocalGroup( const char * name )
321 { 255 {
372 int i; 306 int i;
373 307
374 Log_inf( "Deleting group '%s'", name ); 308 Log_inf( "Deleting group '%s'", name );
375 309
376 /* 310 /*
377 Delete all articles that are only in the group. Check the 311 Delete all articles that are only in the group or are
378 article Xref for more than one group. 312 crossposted only to groups that do not exist on this
313 server.
379 */ 314 */
380 Cont_read( name ); 315 Cont_read( name );
381 for ( i = Cont_first(); i <= Cont_last(); i++ ) 316 for ( i = Cont_first(); i <= Cont_last(); i++ )
382 { 317 {
383 const Over *over; 318 const Over *over;
384 Bool toDelete; 319 Bool toDelete;
385 Str msgId; 320 Str msgId;
386 321
322 if ( ! Cont_validNumb( i ) )
323 continue;
324
387 over = Cont_get( i ); 325 over = Cont_get( i );
388 toDelete = TRUE; 326 toDelete = TRUE;
389 if ( over != NULL ) 327 if ( over != NULL )
390 { 328 {
391 ItemList * xref; 329 ItemList *xrefs;
330 const char *xref;
331 int localXrefs = 0;
392 332
393 Utl_cpyStr( msgId, Ov_msgId( over ) ); 333 Utl_cpyStr( msgId, Ov_msgId( over ) );
394 xref = new_Itl( Db_xref( msgId ), " " ); 334 xrefs = new_Itl( Db_xref( msgId ), " " );
395 if ( Itl_count( xref ) > 1 ) 335 for ( xref = Itl_first( xrefs );
336 xref != NULL;
337 xref = Itl_next( xrefs) )
338 {
339 Str xgrp;
340 int no;
341
342 if ( sscanf( xref, "%s:%d", xgrp, &no ) != 2 )
343 {
344 /* Malformed xref - leave article just in case */
345 Log_err( "Malformed Xref: entry in %s: %s",
346 msgId, xref);
347 toDelete = FALSE;
348 break;
349 }
350
351 if ( Cont_exists( xgrp ) )
352 ++localXrefs;
353 }
354
355 if ( localXrefs > 1 )
396 toDelete = FALSE; 356 toDelete = FALSE;
397 del_Itl( xref ); 357 del_Itl( xrefs );
398 } 358 }
399 Cont_delete( i ); 359 Cont_delete( i );
400 if ( toDelete ) 360 if ( toDelete )
401 Db_delete( msgId ); 361 Db_delete( msgId );
402 } 362 }
563 { 523 {
564 static const char *msg = 524 static const char *msg =
565 "Usage: noffle <option>\n" 525 "Usage: noffle <option>\n"
566 "Option is one of the following:\n" 526 "Option is one of the following:\n"
567 " -a | --article <msg id>|all Show article(s) in database\n" 527 " -a | --article <msg id>|all Show article(s) in database\n"
528 " -B | --rebuild Rebuild article database\n"
568 " -c | --cancel <msg id> Remove article from database\n" 529 " -c | --cancel <msg id> Remove article from database\n"
569 " -C | --create <grp> Create a local group\n" 530 " -C | --create <grp> Create a local group\n"
570 " -d | --database Show content of article database\n" 531 " -d | --database Show content of article database\n"
571 " -D | --delete <grp> Delete a group\n" 532 " -D | --delete <grp> Delete a group\n"
572 " -e | --expire Expire articles\n" 533 " -e | --expire Expire articles\n"
795 { "--modify", "-m" }, 756 { "--modify", "-m" },
796 { "--offline", "-o" }, 757 { "--offline", "-o" },
797 { "--online", "-n" }, 758 { "--online", "-n" },
798 { "--post", "-p" }, 759 { "--post", "-p" },
799 { "--query", "-q" }, 760 { "--query", "-q" },
761 { "--rebuild", "-B" },
800 { "--server", "-r" }, 762 { "--server", "-r" },
801 { "--requested", "-R" }, 763 { "--requested", "-R" },
802 { "--subscribe-over", "-s" }, 764 { "--subscribe-over", "-s" },
803 { "--subscribe-full", "-S" }, 765 { "--subscribe-full", "-S" },
804 { "--subscribe-thread", "-t" }, 766 { "--subscribe-thread", "-t" },
868 result = EXIT_FAILURE; 830 result = EXIT_FAILURE;
869 } 831 }
870 else 832 else
871 doArt( *argv ); 833 doArt( *argv );
872 break; 834 break;
835 case 'B':
836 doRebuild();
837 break;
873 case 'c': 838 case 'c':
874 if ( *argv == NULL ) 839 if ( *argv == NULL )
875 { 840 {
876 fprintf( stderr, "Option -c needs argument.\n" ); 841 fprintf( stderr, "Option -c needs argument.\n" );
877 result = EXIT_FAILURE; 842 result = EXIT_FAILURE;