Mercurial > noffle
diff src/client.c @ 188:f1bacee93ca6 noffle
[svn] * src/client.c,src/client.h,src/fetch.c,src/noffle.c,src/server.c:
robustness - instead of retruning simple Passed/Failed statuses from
connection functions, return an integer status instead. This allows
Noffle to distinguish between a connection failure, an unrecoverable
protocol error and a recoverable problem. As a concrete instance, Noffle
will no longer abort the connection if a group is removed from the
upstream server. Also beef up error detection a bit.
* src/content.c: instead of overwriting the existing content file(s) when
updating - which leaves a window where Noffle is vulnerable to failure
which will leave the content file corrupted (yes, it happened to me),
write a new content file and rename it over the old file only when it
has been written and closed with no errors reported.
author | bears |
---|---|
date | Wed, 12 Sep 2001 21:33:44 +0100 |
parents | 166008a80f03 |
children | 28488e0e3630 |
line wrap: on
line diff
--- a/src/client.c Sat Sep 01 16:57:45 2001 +0100 +++ b/src/client.c Wed Sep 12 21:33:44 2001 +0100 @@ -1,7 +1,7 @@ /* client.c - $Id: client.c 306 2001-09-01 15:57:45Z enz $ + $Id: client.c 307 2001-09-12 20:33:44Z bears $ */ #if HAVE_CONFIG_H @@ -209,7 +209,7 @@ Str lastCmd; if ( ! getLn( client.lastStat ) ) - result = STAT_PROGRAM_FAULT; + result = STAT_CONNECTION_LOST; else if ( sscanf( client.lastStat, "%d", &result ) != 1 ) { Log_err( "Invalid server status: %s", client.lastStat ); @@ -520,7 +520,7 @@ client.in = client.out = NULL; } -static Bool +static int doGetGrps( const char *pattern, Bool *noServerPattern ) { Str cmd; @@ -536,10 +536,10 @@ *noServerPattern = FALSE; if ( ! putCmd( cmd ) ) - return FALSE; + return STAT_CONNECTION_LOST; stat = getStat(); - if ( stat == STAT_PROGRAM_FAULT ) - return FALSE; + if ( IS_FATAL( stat ) ) + return stat; /* * Try LIST instead of LIST ACTIVE in case server doesn't @@ -549,37 +549,38 @@ { *noServerPattern = TRUE; if ( ! putCmd( "LIST" ) ) - return FALSE; + return STAT_CONNECTION_LOST; stat = getStat(); } if ( stat != STAT_GRPS_FOLLOW ) { Log_err( "%s failed: %s", cmd, client.lastStat ); - return FALSE; + return stat; } response = collectTxt(); if ( response == NULL ) - return FALSE; + return STAT_CONNECTION_LOST; processGrps( DynStr_str( response ), *noServerPattern ); del_DynStr( response ); - return TRUE; + return STAT_OK; } -Bool +int Client_getGrps( void ) { GroupEnum *ge; const char *pattern; - Bool doneOne, noServerPattern, res; + Bool doneOne, noServerPattern; + int res; Log_inf( "Getting groups" ); doneOne = FALSE; - res = TRUE; + res = STAT_OK; ge = new_GetGrEn( client.serv ); - while ( res && ( pattern = GrEn_next( ge ) ) != NULL ) + while ( res == STAT_OK && ( pattern = GrEn_next( ge ) ) != NULL ) { res = doGetGrps( pattern, &noServerPattern ); doneOne = TRUE; @@ -594,7 +595,7 @@ return res; } -static Bool +static int doGetDsc( const char *pattern, Bool *noServerPattern ) { Str name, line, dsc, cmd; @@ -613,40 +614,40 @@ *noServerPattern = FALSE; if ( ! putCmd( cmd ) ) - return FALSE; + return STAT_CONNECTION_LOST; stat = getStat(); - if ( stat == STAT_PROGRAM_FAULT ) - return FALSE; + if ( IS_FATAL( stat ) ) + return stat; /* Try without pattern in case server doesn't support patterns. */ if ( pattern[ 0 ] != '\0' && stat != STAT_GRPS_FOLLOW ) { *noServerPattern = TRUE; if ( !putCmd( "LIST NEWSGROUPS" ) ) - return FALSE; + return STAT_CONNECTION_LOST; stat = getStat(); } if ( stat != STAT_GRPS_FOLLOW ) { Log_err( "%s failed: %s", cmd, client.lastStat ); - return FALSE; + return stat; } response = collectTxt(); if ( response == NULL ) - return FALSE; + return STAT_CONNECTION_LOST; if ( ! Lock_openDatabases() ) - return FALSE; + return STAT_NEWSBASE_FATAL; lines = DynStr_str( response ); - result = TRUE; + result = STAT_OK; while ( ( lines = Utl_getLn( line, lines) ) != NULL ) { if ( sscanf( line, "%s", name ) != 1 ) { Log_err( "Unknown reply to LIST NEWSGROUPS: %s", line ); - result = FALSE; + result = STAT_PROGRAM_FAULT; break; } if ( *noServerPattern && ! isGetGroup( name ) ) @@ -663,19 +664,20 @@ return result; } -Bool +int Client_getDsc( void ) { GroupEnum *ge; const char *pattern; - Bool doneOne, noServerPattern, res; + Bool doneOne, noServerPattern; + int res; Log_inf( "Querying group descriptions" ); doneOne = FALSE; - res = TRUE; + res = STAT_OK; ge = new_GetGrEn( client.serv ); - while ( res && ( pattern = GrEn_next( ge ) ) != NULL ) + while ( res == STAT_OK && ( pattern = GrEn_next( ge ) ) != NULL ) { res = doGetDsc( pattern, &noServerPattern ); doneOne = TRUE; @@ -683,14 +685,14 @@ break; } - if ( res && ! doneOne ) + if ( ! doneOne ) res = doGetDsc( "", &noServerPattern ); del_GrEn( ge ); return res; } -Bool +int Client_getNewgrps( const time_t *lastTime ) { Str s; @@ -707,31 +709,21 @@ */ p = s + 2; if ( ! putCmd( "NEWGROUPS %s GMT", p ) ) - return FALSE; + return STAT_CONNECTION_LOST; stat = getStat(); if ( stat != STAT_NEW_GRP_FOLLOW ) { Log_err( "NEWGROUPS command failed: %s", client.lastStat ); - - /* - * If NEWGROUPS fails, it isn't necessarily fatal. You can do - * a periodic noffle --query groups to refresh your group list. - * So only return failure here if the status indicates the link - * itself failed. - * - * In particular, older versions of NNTPcache have a Y2K bug that - * stops NEWGROUPS working. - */ - return ( stat != STAT_PROGRAM_FAULT ); + return stat; } response = collectTxt(); if ( response == NULL ) - return FALSE; + return STAT_CONNECTION_LOST; processGrps( DynStr_str( response ), TRUE ); del_DynStr( response ); - return TRUE; + return STAT_OK; } static const char * @@ -918,7 +910,7 @@ } } -Bool +int Client_getOver( const char *grp, int rmtFirst, int rmtLast, FetchMode mode ) { size_t nbytes, nlines; @@ -929,6 +921,7 @@ const char *lines, *groupLines; char *p; FilterAction action; + int stat; ASSERT( ! Lock_gotLock() ); ASSERT( strcmp( grp, "" ) != 0 ); @@ -937,11 +930,12 @@ if ( Flt_getNewsgroups() ) { if ( ! putCmd( "XHDR Newsgroups %lu-%lu", rmtFirst, rmtLast ) ) - return FALSE; - if ( getStat() != STAT_HEAD_FOLLOWS ) + return STAT_CONNECTION_LOST; + stat = getStat(); + if ( stat != STAT_HEAD_FOLLOWS ) { Log_err( "XHDR command failed: %s", client.lastStat ); - return FALSE; + return stat; } Log_dbg( LOG_DBG_FETCH, @@ -950,7 +944,7 @@ newsgroups = collectTxt(); if ( newsgroups == NULL ) - return FALSE; + return STAT_CONNECTION_LOST; groupLines = DynStr_str( newsgroups ); } @@ -963,14 +957,15 @@ if ( ! putCmd( "XOVER %lu-%lu", rmtFirst, rmtLast ) ) { del_DynStr( newsgroups ); - return FALSE; + return STAT_CONNECTION_LOST; } - if ( getStat() != STAT_OVERS_FOLLOW ) + stat = getStat(); + if ( stat != STAT_OVERS_FOLLOW ) { del_DynStr( newsgroups ); Log_err( "XOVER command failed: %s", client.lastStat ); - return FALSE; + return stat; } Log_dbg( LOG_DBG_FETCH, "Requesting overview for remote %lu-%lu", @@ -980,14 +975,14 @@ if ( response == NULL ) { del_DynStr( newsgroups ); - return FALSE; + return STAT_CONNECTION_LOST; } if ( ! Lock_openDatabases() ) { del_DynStr( newsgroups ); del_DynStr( response ); - return FALSE; + return STAT_NEWSBASE_FATAL; } Cont_read( grp ); @@ -1041,7 +1036,7 @@ Lock_closeDatabases(); del_DynStr( response ); del_DynStr( newsgroups ); - return TRUE; + return STAT_OK; } static void @@ -1057,9 +1052,10 @@ Pseudo_retrievingFailed( msgId, reason ); Db_setStatus( msgId, status | DB_RETRIEVING_FAILED ); Lock_closeDatabases(); + return; } -static Bool +static int retrieveAndStoreArt( const char *msgId, int artcnt, int artmax ) { Bool err; @@ -1079,7 +1075,7 @@ { del_DynStr( s ); retrievingFailed( msgId, "Can't open message base" ); - return FALSE; + return STAT_NEWSBASE_FATAL; } err = ! Db_storeArt( msgId, txt ); @@ -1104,61 +1100,74 @@ del_DynStr( s ); } else + { retrievingFailed( msgId, "Connection broke down" ); - return ! err; + return STAT_CONNECTION_LOST; + } + return err ? STAT_NEWSBASE_FATAL : STAT_OK; } -Bool +int Client_retrieveArt( const char *msgId ) { - Bool res = FALSE; + int res; ASSERT( Lock_gotLock() ); if ( ! Db_contains( msgId ) ) { Log_err( "Article '%s' not prepared in database. Skipping.", msgId ); - return TRUE; + return STAT_PROGRAM_FAULT; } if ( ! ( Db_status( msgId ) & DB_NOT_DOWNLOADED ) ) { Log_inf( "Article '%s' already retrieved. Skipping.", msgId ); - return TRUE; + return STAT_OK; } Lock_closeDatabases(); if ( ! putCmd( "ARTICLE %s", msgId ) ) + { retrievingFailed( msgId, "Connection broke down" ); - else if ( getStat() != STAT_ART_FOLLOWS ) + res = STAT_CONNECTION_LOST; + } + else if ( ( res = getStat() ) != STAT_ART_FOLLOWS ) retrievingFailed( msgId, client.lastStat ); else res = retrieveAndStoreArt( msgId, 0, 0 ); - Lock_openDatabases(); + if ( ! Lock_openDatabases() ) + res = STAT_NEWSBASE_FATAL; return res; } -Bool +int Client_retrieveArtList( const char *list, int *artcnt, int artmax ) { Str msgId; DynStr *s; const char *p; - Bool res; + int res, msgStat; ASSERT( Lock_gotLock() ); Log_inf( "Retrieving article list" ); s = new_DynStr( (int)strlen( list ) ); p = list; + res = STAT_OK; while ( ( p = Utl_getLn( msgId, p ) ) ) if ( ! Db_contains( msgId ) ) - Log_err( "[%d/%d] Skipping retrieving of %s (not prepared in database)", + { + Log_err( "[%d/%d] Skipping retrieving of %s " + "(not prepared in database)", ++(*artcnt), artmax, msgId ); + res = STAT_PROGRAM_FAULT; + } else if ( ! ( Db_status( msgId ) & DB_NOT_DOWNLOADED ) ) - Log_inf( "[%d/%d] Skipping %s (already retrieved)", ++(*artcnt), artmax, msgId ); + Log_inf( "[%d/%d] Skipping %s (already retrieved)", + ++(*artcnt), artmax, msgId ); else if ( ! putCmdNoFlush( "ARTICLE %s", msgId ) ) { retrievingFailed( msgId, "Connection broke down" ); del_DynStr( s ); - return FALSE; + return STAT_CONNECTION_LOST; } else DynStr_appLn( s, msgId ); @@ -1167,55 +1176,58 @@ fflush( client.out ); Log_dbg( LOG_DBG_PROTOCOL, "[S FLUSH]" ); + /* + * We got something. Try to process all messages and return the + * 'worst' error encountered (note we may have already hit a + * STAT_PROGRAM_FAULT). + */ p = DynStr_str( s ); - res = TRUE; - while ( res && ( p = Utl_getLn( msgId, p ) ) ) + while ( ! IS_FATAL( res ) && ( p = Utl_getLn( msgId, p ) ) ) { - switch( getStat() ) - { - case STAT_ART_FOLLOWS: - res = retrieveAndStoreArt( msgId, ++(*artcnt), artmax ); - break; - - case STAT_PROGRAM_FAULT: - res = FALSE; - /* Fall through */ - - default: + msgStat = getStat(); + if ( msgStat == STAT_ART_FOLLOWS ) + msgStat = retrieveAndStoreArt( msgId, ++(*artcnt), artmax ); + else retrievingFailed( msgId, client.lastStat ); - break; - } + + if ( res == STAT_OK || ( ! IS_FATAL( res ) && IS_FATAL( msgStat ) ) ) + res = msgStat; } del_DynStr( s ); - Lock_openDatabases(); + if ( ! Lock_openDatabases() && ! IS_FATAL( res ) ) + res = STAT_NEWSBASE_FATAL; return res; } -Bool +int Client_changeToGrp( const char* name ) { unsigned int stat; - int estimatedNumb, first, last; - Bool res; + int estimatedNumb, first, last, res; ASSERT( Lock_gotLock() ); if ( ! Grp_exists( name ) ) - return FALSE; + return STAT_NEWSBASE_FATAL; Lock_closeDatabases(); - res = putCmd( "GROUP %s", name ); - res = res && ( getStat() == STAT_GRP_SELECTED ); - if ( ! Lock_openDatabases() || ! res ) - return FALSE; + stat = STAT_OK; + if ( ! putCmd( "GROUP %s", name ) ) + res = STAT_CONNECTION_LOST; + if ( stat == STAT_OK ) + stat = getStat(); + if ( ! Lock_openDatabases() ) + return STAT_NEWSBASE_FATAL; + if ( stat != STAT_GRP_SELECTED ) + return stat; if ( sscanf( client.lastStat, "%u %d %d %d", &stat, &estimatedNumb, &first, &last ) != 4 ) { Log_err( "Bad server response to GROUP: %s", client.lastStat ); - return FALSE; + return STAT_PROGRAM_FAULT; } Utl_cpyStr( client.grp, name ); client.rmtFirst = first; client.rmtLast = last; - return TRUE; + return STAT_OK; } void @@ -1226,7 +1238,7 @@ *last = client.rmtLast; } -Bool +int Client_postArt( const char *msgId, const char *artTxt, Str errStr ) { int stat; @@ -1234,27 +1246,27 @@ errStr[0] = '\0'; if ( ! putCmd( "POST" ) ) - return FALSE; + return STAT_CONNECTION_LOST; stat = getStat(); - if ( stat == STAT_PROGRAM_FAULT ) - return FALSE; + if ( IS_FATAL( stat ) ) + return stat; else if ( stat != STAT_SEND_ART ) { Log_err( "Posting of %s not allowed: %s", msgId, client.lastStat ); strcpy( errStr, client.lastStat ); - return TRUE; + return stat; } putTxtBuf( artTxt ); putEndOfTxt(); stat = getStat(); - if ( stat == STAT_PROGRAM_FAULT ) - return FALSE; + if ( IS_FATAL( stat ) ) + return stat; else if ( stat != STAT_POST_OK ) { Log_err( "Posting of %s failed: %s", msgId, client.lastStat ); strcpy( errStr, client.lastStat ); - return TRUE; + return stat; } Log_inf( "Posted %s (Status: %s)", msgId, client.lastStat ); - return TRUE; + return STAT_OK; }