comparison 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
comparison
equal deleted inserted replaced
187:166008a80f03 188:f1bacee93ca6
1 /* 1 /*
2 client.c 2 client.c
3 3
4 $Id: client.c 306 2001-09-01 15:57:45Z enz $ 4 $Id: client.c 307 2001-09-12 20:33:44Z bears $
5 */ 5 */
6 6
7 #if HAVE_CONFIG_H 7 #if HAVE_CONFIG_H
8 #include <config.h> 8 #include <config.h>
9 #endif 9 #endif
207 { 207 {
208 int result; 208 int result;
209 Str lastCmd; 209 Str lastCmd;
210 210
211 if ( ! getLn( client.lastStat ) ) 211 if ( ! getLn( client.lastStat ) )
212 result = STAT_PROGRAM_FAULT; 212 result = STAT_CONNECTION_LOST;
213 else if ( sscanf( client.lastStat, "%d", &result ) != 1 ) 213 else if ( sscanf( client.lastStat, "%d", &result ) != 1 )
214 { 214 {
215 Log_err( "Invalid server status: %s", client.lastStat ); 215 Log_err( "Invalid server status: %s", client.lastStat );
216 result = STAT_PROGRAM_FAULT; 216 result = STAT_PROGRAM_FAULT;
217 } 217 }
518 fclose( client.in ); 518 fclose( client.in );
519 fclose( client.out ); 519 fclose( client.out );
520 client.in = client.out = NULL; 520 client.in = client.out = NULL;
521 } 521 }
522 522
523 static Bool 523 static int
524 doGetGrps( const char *pattern, Bool *noServerPattern ) 524 doGetGrps( const char *pattern, Bool *noServerPattern )
525 { 525 {
526 Str cmd; 526 Str cmd;
527 int stat; 527 int stat;
528 DynStr *response; 528 DynStr *response;
534 Utl_catStr( cmd, pattern ); 534 Utl_catStr( cmd, pattern );
535 } 535 }
536 536
537 *noServerPattern = FALSE; 537 *noServerPattern = FALSE;
538 if ( ! putCmd( cmd ) ) 538 if ( ! putCmd( cmd ) )
539 return FALSE; 539 return STAT_CONNECTION_LOST;
540 stat = getStat(); 540 stat = getStat();
541 if ( stat == STAT_PROGRAM_FAULT ) 541 if ( IS_FATAL( stat ) )
542 return FALSE; 542 return stat;
543 543
544 /* 544 /*
545 * Try LIST instead of LIST ACTIVE in case server doesn't 545 * Try LIST instead of LIST ACTIVE in case server doesn't
546 * support LIST ACTIVE. 546 * support LIST ACTIVE.
547 */ 547 */
548 if ( pattern[ 0 ] != '\0' && stat != STAT_GRPS_FOLLOW ) 548 if ( pattern[ 0 ] != '\0' && stat != STAT_GRPS_FOLLOW )
549 { 549 {
550 *noServerPattern = TRUE; 550 *noServerPattern = TRUE;
551 if ( ! putCmd( "LIST" ) ) 551 if ( ! putCmd( "LIST" ) )
552 return FALSE; 552 return STAT_CONNECTION_LOST;
553 stat = getStat(); 553 stat = getStat();
554 } 554 }
555 if ( stat != STAT_GRPS_FOLLOW ) 555 if ( stat != STAT_GRPS_FOLLOW )
556 { 556 {
557 Log_err( "%s failed: %s", cmd, client.lastStat ); 557 Log_err( "%s failed: %s", cmd, client.lastStat );
558 return FALSE; 558 return stat;
559 } 559 }
560 560
561 response = collectTxt(); 561 response = collectTxt();
562 if ( response == NULL ) 562 if ( response == NULL )
563 return FALSE; 563 return STAT_CONNECTION_LOST;
564 564
565 processGrps( DynStr_str( response ), *noServerPattern ); 565 processGrps( DynStr_str( response ), *noServerPattern );
566 del_DynStr( response ); 566 del_DynStr( response );
567 return TRUE; 567 return STAT_OK;
568 } 568 }
569 569
570 Bool 570 int
571 Client_getGrps( void ) 571 Client_getGrps( void )
572 { 572 {
573 GroupEnum *ge; 573 GroupEnum *ge;
574 const char *pattern; 574 const char *pattern;
575 Bool doneOne, noServerPattern, res; 575 Bool doneOne, noServerPattern;
576 int res;
576 577
577 Log_inf( "Getting groups" ); 578 Log_inf( "Getting groups" );
578 579
579 doneOne = FALSE; 580 doneOne = FALSE;
580 res = TRUE; 581 res = STAT_OK;
581 ge = new_GetGrEn( client.serv ); 582 ge = new_GetGrEn( client.serv );
582 while ( res && ( pattern = GrEn_next( ge ) ) != NULL ) 583 while ( res == STAT_OK && ( pattern = GrEn_next( ge ) ) != NULL )
583 { 584 {
584 res = doGetGrps( pattern, &noServerPattern ); 585 res = doGetGrps( pattern, &noServerPattern );
585 doneOne = TRUE; 586 doneOne = TRUE;
586 if ( noServerPattern ) 587 if ( noServerPattern )
587 break; 588 break;
592 593
593 del_GrEn( ge ); 594 del_GrEn( ge );
594 return res; 595 return res;
595 } 596 }
596 597
597 static Bool 598 static int
598 doGetDsc( const char *pattern, Bool *noServerPattern ) 599 doGetDsc( const char *pattern, Bool *noServerPattern )
599 { 600 {
600 Str name, line, dsc, cmd; 601 Str name, line, dsc, cmd;
601 int stat; 602 int stat;
602 DynStr *response; 603 DynStr *response;
611 Utl_catStr( cmd, pattern ); 612 Utl_catStr( cmd, pattern );
612 } 613 }
613 614
614 *noServerPattern = FALSE; 615 *noServerPattern = FALSE;
615 if ( ! putCmd( cmd ) ) 616 if ( ! putCmd( cmd ) )
616 return FALSE; 617 return STAT_CONNECTION_LOST;
617 stat = getStat(); 618 stat = getStat();
618 if ( stat == STAT_PROGRAM_FAULT ) 619 if ( IS_FATAL( stat ) )
619 return FALSE; 620 return stat;
620 621
621 /* Try without pattern in case server doesn't support patterns. */ 622 /* Try without pattern in case server doesn't support patterns. */
622 if ( pattern[ 0 ] != '\0' && stat != STAT_GRPS_FOLLOW ) 623 if ( pattern[ 0 ] != '\0' && stat != STAT_GRPS_FOLLOW )
623 { 624 {
624 *noServerPattern = TRUE; 625 *noServerPattern = TRUE;
625 if ( !putCmd( "LIST NEWSGROUPS" ) ) 626 if ( !putCmd( "LIST NEWSGROUPS" ) )
626 return FALSE; 627 return STAT_CONNECTION_LOST;
627 stat = getStat(); 628 stat = getStat();
628 } 629 }
629 if ( stat != STAT_GRPS_FOLLOW ) 630 if ( stat != STAT_GRPS_FOLLOW )
630 { 631 {
631 Log_err( "%s failed: %s", cmd, client.lastStat ); 632 Log_err( "%s failed: %s", cmd, client.lastStat );
632 return FALSE; 633 return stat;
633 } 634 }
634 635
635 response = collectTxt(); 636 response = collectTxt();
636 if ( response == NULL ) 637 if ( response == NULL )
637 return FALSE; 638 return STAT_CONNECTION_LOST;
638 639
639 if ( ! Lock_openDatabases() ) 640 if ( ! Lock_openDatabases() )
640 return FALSE; 641 return STAT_NEWSBASE_FATAL;
641 642
642 lines = DynStr_str( response ); 643 lines = DynStr_str( response );
643 result = TRUE; 644 result = STAT_OK;
644 while ( ( lines = Utl_getLn( line, lines) ) != NULL ) 645 while ( ( lines = Utl_getLn( line, lines) ) != NULL )
645 { 646 {
646 if ( sscanf( line, "%s", name ) != 1 ) 647 if ( sscanf( line, "%s", name ) != 1 )
647 { 648 {
648 Log_err( "Unknown reply to LIST NEWSGROUPS: %s", line ); 649 Log_err( "Unknown reply to LIST NEWSGROUPS: %s", line );
649 result = FALSE; 650 result = STAT_PROGRAM_FAULT;
650 break; 651 break;
651 } 652 }
652 if ( *noServerPattern && ! isGetGroup( name ) ) 653 if ( *noServerPattern && ! isGetGroup( name ) )
653 continue; 654 continue;
654 strcpy( dsc, Utl_restOfLn( line, 1 ) ); 655 strcpy( dsc, Utl_restOfLn( line, 1 ) );
661 Lock_closeDatabases(); 662 Lock_closeDatabases();
662 del_DynStr( response ); 663 del_DynStr( response );
663 return result; 664 return result;
664 } 665 }
665 666
666 Bool 667 int
667 Client_getDsc( void ) 668 Client_getDsc( void )
668 { 669 {
669 GroupEnum *ge; 670 GroupEnum *ge;
670 const char *pattern; 671 const char *pattern;
671 Bool doneOne, noServerPattern, res; 672 Bool doneOne, noServerPattern;
673 int res;
672 674
673 Log_inf( "Querying group descriptions" ); 675 Log_inf( "Querying group descriptions" );
674 676
675 doneOne = FALSE; 677 doneOne = FALSE;
676 res = TRUE; 678 res = STAT_OK;
677 ge = new_GetGrEn( client.serv ); 679 ge = new_GetGrEn( client.serv );
678 while ( res && ( pattern = GrEn_next( ge ) ) != NULL ) 680 while ( res == STAT_OK && ( pattern = GrEn_next( ge ) ) != NULL )
679 { 681 {
680 res = doGetDsc( pattern, &noServerPattern ); 682 res = doGetDsc( pattern, &noServerPattern );
681 doneOne = TRUE; 683 doneOne = TRUE;
682 if ( noServerPattern ) 684 if ( noServerPattern )
683 break; 685 break;
684 } 686 }
685 687
686 if ( res && ! doneOne ) 688 if ( ! doneOne )
687 res = doGetDsc( "", &noServerPattern ); 689 res = doGetDsc( "", &noServerPattern );
688 690
689 del_GrEn( ge ); 691 del_GrEn( ge );
690 return res; 692 return res;
691 } 693 }
692 694
693 Bool 695 int
694 Client_getNewgrps( const time_t *lastTime ) 696 Client_getNewgrps( const time_t *lastTime )
695 { 697 {
696 Str s; 698 Str s;
697 const char *p; 699 const char *p;
698 DynStr *response; 700 DynStr *response;
705 According to newest IETF draft, this is still valid after 2000. 707 According to newest IETF draft, this is still valid after 2000.
706 (directly using %y in fmt string causes a Y2K compiler warning) 708 (directly using %y in fmt string causes a Y2K compiler warning)
707 */ 709 */
708 p = s + 2; 710 p = s + 2;
709 if ( ! putCmd( "NEWGROUPS %s GMT", p ) ) 711 if ( ! putCmd( "NEWGROUPS %s GMT", p ) )
710 return FALSE; 712 return STAT_CONNECTION_LOST;
711 stat = getStat(); 713 stat = getStat();
712 if ( stat != STAT_NEW_GRP_FOLLOW ) 714 if ( stat != STAT_NEW_GRP_FOLLOW )
713 { 715 {
714 Log_err( "NEWGROUPS command failed: %s", client.lastStat ); 716 Log_err( "NEWGROUPS command failed: %s", client.lastStat );
715 717 return stat;
716 /*
717 * If NEWGROUPS fails, it isn't necessarily fatal. You can do
718 * a periodic noffle --query groups to refresh your group list.
719 * So only return failure here if the status indicates the link
720 * itself failed.
721 *
722 * In particular, older versions of NNTPcache have a Y2K bug that
723 * stops NEWGROUPS working.
724 */
725 return ( stat != STAT_PROGRAM_FAULT );
726 } 718 }
727 719
728 response = collectTxt(); 720 response = collectTxt();
729 if ( response == NULL ) 721 if ( response == NULL )
730 return FALSE; 722 return STAT_CONNECTION_LOST;
731 723
732 processGrps( DynStr_str( response ), TRUE ); 724 processGrps( DynStr_str( response ), TRUE );
733 del_DynStr( response ); 725 del_DynStr( response );
734 return TRUE; 726 return STAT_OK;
735 } 727 }
736 728
737 static const char * 729 static const char *
738 readField( Str result, const char *p ) 730 readField( Str result, const char *p )
739 { 731 {
916 Log_dbg( LOG_DBG_FETCH, "Preparing '%s' in database", msgId ); 908 Log_dbg( LOG_DBG_FETCH, "Preparing '%s' in database", msgId );
917 Db_prepareEntry( ov, client.grp, Ov_numb( ov ) ); 909 Db_prepareEntry( ov, client.grp, Ov_numb( ov ) );
918 } 910 }
919 } 911 }
920 912
921 Bool 913 int
922 Client_getOver( const char *grp, int rmtFirst, int rmtLast, FetchMode mode ) 914 Client_getOver( const char *grp, int rmtFirst, int rmtLast, FetchMode mode )
923 { 915 {
924 size_t nbytes, nlines; 916 size_t nbytes, nlines;
925 int rmtNumb, groupsNumb, oldLast, cntMarked; 917 int rmtNumb, groupsNumb, oldLast, cntMarked;
926 Over *ov; 918 Over *ov;
927 Str line, subj, from, date, msgId, ref, groups; 919 Str line, subj, from, date, msgId, ref, groups;
928 DynStr *response, *newsgroups; 920 DynStr *response, *newsgroups;
929 const char *lines, *groupLines; 921 const char *lines, *groupLines;
930 char *p; 922 char *p;
931 FilterAction action; 923 FilterAction action;
924 int stat;
932 925
933 ASSERT( ! Lock_gotLock() ); 926 ASSERT( ! Lock_gotLock() );
934 ASSERT( strcmp( grp, "" ) != 0 ); 927 ASSERT( strcmp( grp, "" ) != 0 );
935 928
936 /* Do we need the article Newsgroups: for filtering? */ 929 /* Do we need the article Newsgroups: for filtering? */
937 if ( Flt_getNewsgroups() ) 930 if ( Flt_getNewsgroups() )
938 { 931 {
939 if ( ! putCmd( "XHDR Newsgroups %lu-%lu", rmtFirst, rmtLast ) ) 932 if ( ! putCmd( "XHDR Newsgroups %lu-%lu", rmtFirst, rmtLast ) )
940 return FALSE; 933 return STAT_CONNECTION_LOST;
941 if ( getStat() != STAT_HEAD_FOLLOWS ) 934 stat = getStat();
935 if ( stat != STAT_HEAD_FOLLOWS )
942 { 936 {
943 Log_err( "XHDR command failed: %s", client.lastStat ); 937 Log_err( "XHDR command failed: %s", client.lastStat );
944 return FALSE; 938 return stat;
945 } 939 }
946 940
947 Log_dbg( LOG_DBG_FETCH, 941 Log_dbg( LOG_DBG_FETCH,
948 "Requesting Newsgroups headers for remote %lu-%lu", 942 "Requesting Newsgroups headers for remote %lu-%lu",
949 rmtFirst, rmtLast ); 943 rmtFirst, rmtLast );
950 944
951 newsgroups = collectTxt(); 945 newsgroups = collectTxt();
952 if ( newsgroups == NULL ) 946 if ( newsgroups == NULL )
953 return FALSE; 947 return STAT_CONNECTION_LOST;
954 948
955 groupLines = DynStr_str( newsgroups ); 949 groupLines = DynStr_str( newsgroups );
956 } 950 }
957 else 951 else
958 { 952 {
961 } 955 }
962 956
963 if ( ! putCmd( "XOVER %lu-%lu", rmtFirst, rmtLast ) ) 957 if ( ! putCmd( "XOVER %lu-%lu", rmtFirst, rmtLast ) )
964 { 958 {
965 del_DynStr( newsgroups ); 959 del_DynStr( newsgroups );
966 return FALSE; 960 return STAT_CONNECTION_LOST;
967 } 961 }
968 962
969 if ( getStat() != STAT_OVERS_FOLLOW ) 963 stat = getStat();
964 if ( stat != STAT_OVERS_FOLLOW )
970 { 965 {
971 del_DynStr( newsgroups ); 966 del_DynStr( newsgroups );
972 Log_err( "XOVER command failed: %s", client.lastStat ); 967 Log_err( "XOVER command failed: %s", client.lastStat );
973 return FALSE; 968 return stat;
974 } 969 }
975 Log_dbg( LOG_DBG_FETCH, 970 Log_dbg( LOG_DBG_FETCH,
976 "Requesting overview for remote %lu-%lu", 971 "Requesting overview for remote %lu-%lu",
977 rmtFirst, rmtLast ); 972 rmtFirst, rmtLast );
978 973
979 response = collectTxt(); 974 response = collectTxt();
980 if ( response == NULL ) 975 if ( response == NULL )
981 { 976 {
982 del_DynStr( newsgroups ); 977 del_DynStr( newsgroups );
983 return FALSE; 978 return STAT_CONNECTION_LOST;
984 } 979 }
985 980
986 if ( ! Lock_openDatabases() ) 981 if ( ! Lock_openDatabases() )
987 { 982 {
988 del_DynStr( newsgroups ); 983 del_DynStr( newsgroups );
989 del_DynStr( response ); 984 del_DynStr( response );
990 return FALSE; 985 return STAT_NEWSBASE_FATAL;
991 } 986 }
992 987
993 Cont_read( grp ); 988 Cont_read( grp );
994 oldLast = Cont_last(); 989 oldLast = Cont_last();
995 cntMarked = 0; 990 cntMarked = 0;
1039 Cont_write(); 1034 Cont_write();
1040 Grp_setFirstLast( grp, Cont_first(), Cont_last() ); 1035 Grp_setFirstLast( grp, Cont_first(), Cont_last() );
1041 Lock_closeDatabases(); 1036 Lock_closeDatabases();
1042 del_DynStr( response ); 1037 del_DynStr( response );
1043 del_DynStr( newsgroups ); 1038 del_DynStr( newsgroups );
1044 return TRUE; 1039 return STAT_OK;
1045 } 1040 }
1046 1041
1047 static void 1042 static void
1048 retrievingFailed( const char* msgId, const char *reason ) 1043 retrievingFailed( const char* msgId, const char *reason )
1049 { 1044 {
1055 return; 1050 return;
1056 status = Db_status( msgId ); 1051 status = Db_status( msgId );
1057 Pseudo_retrievingFailed( msgId, reason ); 1052 Pseudo_retrievingFailed( msgId, reason );
1058 Db_setStatus( msgId, status | DB_RETRIEVING_FAILED ); 1053 Db_setStatus( msgId, status | DB_RETRIEVING_FAILED );
1059 Lock_closeDatabases(); 1054 Lock_closeDatabases();
1060 } 1055 return;
1061 1056 }
1062 static Bool 1057
1058 static int
1063 retrieveAndStoreArt( const char *msgId, int artcnt, int artmax ) 1059 retrieveAndStoreArt( const char *msgId, int artcnt, int artmax )
1064 { 1060 {
1065 Bool err; 1061 Bool err;
1066 DynStr *s = NULL; 1062 DynStr *s = NULL;
1067 1063
1077 txt = DynStr_str( s ); 1073 txt = DynStr_str( s );
1078 if ( ! Lock_openDatabases() ) 1074 if ( ! Lock_openDatabases() )
1079 { 1075 {
1080 del_DynStr( s ); 1076 del_DynStr( s );
1081 retrievingFailed( msgId, "Can't open message base" ); 1077 retrievingFailed( msgId, "Can't open message base" );
1082 return FALSE; 1078 return STAT_NEWSBASE_FATAL;
1083 } 1079 }
1084 1080
1085 err = ! Db_storeArt( msgId, txt ); 1081 err = ! Db_storeArt( msgId, txt );
1086 if ( ! err ) 1082 if ( ! err )
1087 { 1083 {
1102 } 1098 }
1103 Lock_closeDatabases(); 1099 Lock_closeDatabases();
1104 del_DynStr( s ); 1100 del_DynStr( s );
1105 } 1101 }
1106 else 1102 else
1103 {
1107 retrievingFailed( msgId, "Connection broke down" ); 1104 retrievingFailed( msgId, "Connection broke down" );
1108 return ! err; 1105 return STAT_CONNECTION_LOST;
1109 } 1106 }
1110 1107 return err ? STAT_NEWSBASE_FATAL : STAT_OK;
1111 Bool 1108 }
1109
1110 int
1112 Client_retrieveArt( const char *msgId ) 1111 Client_retrieveArt( const char *msgId )
1113 { 1112 {
1114 Bool res = FALSE; 1113 int res;
1115 1114
1116 ASSERT( Lock_gotLock() ); 1115 ASSERT( Lock_gotLock() );
1117 if ( ! Db_contains( msgId ) ) 1116 if ( ! Db_contains( msgId ) )
1118 { 1117 {
1119 Log_err( "Article '%s' not prepared in database. Skipping.", msgId ); 1118 Log_err( "Article '%s' not prepared in database. Skipping.", msgId );
1120 return TRUE; 1119 return STAT_PROGRAM_FAULT;
1121 } 1120 }
1122 if ( ! ( Db_status( msgId ) & DB_NOT_DOWNLOADED ) ) 1121 if ( ! ( Db_status( msgId ) & DB_NOT_DOWNLOADED ) )
1123 { 1122 {
1124 Log_inf( "Article '%s' already retrieved. Skipping.", msgId ); 1123 Log_inf( "Article '%s' already retrieved. Skipping.", msgId );
1125 return TRUE; 1124 return STAT_OK;
1126 } 1125 }
1127 1126
1128 Lock_closeDatabases(); 1127 Lock_closeDatabases();
1129 if ( ! putCmd( "ARTICLE %s", msgId ) ) 1128 if ( ! putCmd( "ARTICLE %s", msgId ) )
1129 {
1130 retrievingFailed( msgId, "Connection broke down" ); 1130 retrievingFailed( msgId, "Connection broke down" );
1131 else if ( getStat() != STAT_ART_FOLLOWS ) 1131 res = STAT_CONNECTION_LOST;
1132 }
1133 else if ( ( res = getStat() ) != STAT_ART_FOLLOWS )
1132 retrievingFailed( msgId, client.lastStat ); 1134 retrievingFailed( msgId, client.lastStat );
1133 else 1135 else
1134 res = retrieveAndStoreArt( msgId, 0, 0 ); 1136 res = retrieveAndStoreArt( msgId, 0, 0 );
1135 Lock_openDatabases(); 1137 if ( ! Lock_openDatabases() )
1138 res = STAT_NEWSBASE_FATAL;
1136 return res; 1139 return res;
1137 } 1140 }
1138 1141
1139 Bool 1142 int
1140 Client_retrieveArtList( const char *list, int *artcnt, int artmax ) 1143 Client_retrieveArtList( const char *list, int *artcnt, int artmax )
1141 { 1144 {
1142 Str msgId; 1145 Str msgId;
1143 DynStr *s; 1146 DynStr *s;
1144 const char *p; 1147 const char *p;
1145 Bool res; 1148 int res, msgStat;
1146 1149
1147 ASSERT( Lock_gotLock() ); 1150 ASSERT( Lock_gotLock() );
1148 Log_inf( "Retrieving article list" ); 1151 Log_inf( "Retrieving article list" );
1149 s = new_DynStr( (int)strlen( list ) ); 1152 s = new_DynStr( (int)strlen( list ) );
1150 p = list; 1153 p = list;
1154 res = STAT_OK;
1151 while ( ( p = Utl_getLn( msgId, p ) ) ) 1155 while ( ( p = Utl_getLn( msgId, p ) ) )
1152 if ( ! Db_contains( msgId ) ) 1156 if ( ! Db_contains( msgId ) )
1153 Log_err( "[%d/%d] Skipping retrieving of %s (not prepared in database)", 1157 {
1158 Log_err( "[%d/%d] Skipping retrieving of %s "
1159 "(not prepared in database)",
1154 ++(*artcnt), artmax, msgId ); 1160 ++(*artcnt), artmax, msgId );
1161 res = STAT_PROGRAM_FAULT;
1162 }
1155 else if ( ! ( Db_status( msgId ) & DB_NOT_DOWNLOADED ) ) 1163 else if ( ! ( Db_status( msgId ) & DB_NOT_DOWNLOADED ) )
1156 Log_inf( "[%d/%d] Skipping %s (already retrieved)", ++(*artcnt), artmax, msgId ); 1164 Log_inf( "[%d/%d] Skipping %s (already retrieved)",
1165 ++(*artcnt), artmax, msgId );
1157 else if ( ! putCmdNoFlush( "ARTICLE %s", msgId ) ) 1166 else if ( ! putCmdNoFlush( "ARTICLE %s", msgId ) )
1158 { 1167 {
1159 retrievingFailed( msgId, "Connection broke down" ); 1168 retrievingFailed( msgId, "Connection broke down" );
1160 del_DynStr( s ); 1169 del_DynStr( s );
1161 return FALSE; 1170 return STAT_CONNECTION_LOST;
1162 } 1171 }
1163 else 1172 else
1164 DynStr_appLn( s, msgId ); 1173 DynStr_appLn( s, msgId );
1165 1174
1166 Lock_closeDatabases(); 1175 Lock_closeDatabases();
1167 fflush( client.out ); 1176 fflush( client.out );
1168 Log_dbg( LOG_DBG_PROTOCOL, "[S FLUSH]" ); 1177 Log_dbg( LOG_DBG_PROTOCOL, "[S FLUSH]" );
1169 1178
1179 /*
1180 * We got something. Try to process all messages and return the
1181 * 'worst' error encountered (note we may have already hit a
1182 * STAT_PROGRAM_FAULT).
1183 */
1170 p = DynStr_str( s ); 1184 p = DynStr_str( s );
1171 res = TRUE; 1185 while ( ! IS_FATAL( res ) && ( p = Utl_getLn( msgId, p ) ) )
1172 while ( res && ( p = Utl_getLn( msgId, p ) ) ) 1186 {
1173 { 1187 msgStat = getStat();
1174 switch( getStat() ) 1188 if ( msgStat == STAT_ART_FOLLOWS )
1175 { 1189 msgStat = retrieveAndStoreArt( msgId, ++(*artcnt), artmax );
1176 case STAT_ART_FOLLOWS: 1190 else
1177 res = retrieveAndStoreArt( msgId, ++(*artcnt), artmax );
1178 break;
1179
1180 case STAT_PROGRAM_FAULT:
1181 res = FALSE;
1182 /* Fall through */
1183
1184 default:
1185 retrievingFailed( msgId, client.lastStat ); 1191 retrievingFailed( msgId, client.lastStat );
1186 break; 1192
1187 } 1193 if ( res == STAT_OK || ( ! IS_FATAL( res ) && IS_FATAL( msgStat ) ) )
1194 res = msgStat;
1188 } 1195 }
1189 del_DynStr( s ); 1196 del_DynStr( s );
1190 Lock_openDatabases(); 1197 if ( ! Lock_openDatabases() && ! IS_FATAL( res ) )
1198 res = STAT_NEWSBASE_FATAL;
1191 return res; 1199 return res;
1192 } 1200 }
1193 1201
1194 Bool 1202 int
1195 Client_changeToGrp( const char* name ) 1203 Client_changeToGrp( const char* name )
1196 { 1204 {
1197 unsigned int stat; 1205 unsigned int stat;
1198 int estimatedNumb, first, last; 1206 int estimatedNumb, first, last, res;
1199 Bool res;
1200 1207
1201 ASSERT( Lock_gotLock() ); 1208 ASSERT( Lock_gotLock() );
1202 if ( ! Grp_exists( name ) ) 1209 if ( ! Grp_exists( name ) )
1203 return FALSE; 1210 return STAT_NEWSBASE_FATAL;
1204 Lock_closeDatabases(); 1211 Lock_closeDatabases();
1205 res = putCmd( "GROUP %s", name ); 1212 stat = STAT_OK;
1206 res = res && ( getStat() == STAT_GRP_SELECTED ); 1213 if ( ! putCmd( "GROUP %s", name ) )
1207 if ( ! Lock_openDatabases() || ! res ) 1214 res = STAT_CONNECTION_LOST;
1208 return FALSE; 1215 if ( stat == STAT_OK )
1216 stat = getStat();
1217 if ( ! Lock_openDatabases() )
1218 return STAT_NEWSBASE_FATAL;
1219 if ( stat != STAT_GRP_SELECTED )
1220 return stat;
1209 if ( sscanf( client.lastStat, "%u %d %d %d", 1221 if ( sscanf( client.lastStat, "%u %d %d %d",
1210 &stat, &estimatedNumb, &first, &last ) != 4 ) 1222 &stat, &estimatedNumb, &first, &last ) != 4 )
1211 { 1223 {
1212 Log_err( "Bad server response to GROUP: %s", client.lastStat ); 1224 Log_err( "Bad server response to GROUP: %s", client.lastStat );
1213 return FALSE; 1225 return STAT_PROGRAM_FAULT;
1214 } 1226 }
1215 Utl_cpyStr( client.grp, name ); 1227 Utl_cpyStr( client.grp, name );
1216 client.rmtFirst = first; 1228 client.rmtFirst = first;
1217 client.rmtLast = last; 1229 client.rmtLast = last;
1218 return TRUE; 1230 return STAT_OK;
1219 } 1231 }
1220 1232
1221 void 1233 void
1222 Client_rmtFirstLast( int *first, int *last ) 1234 Client_rmtFirstLast( int *first, int *last )
1223 { 1235 {
1224 ASSERT( Lock_gotLock() ); 1236 ASSERT( Lock_gotLock() );
1225 *first = client.rmtFirst; 1237 *first = client.rmtFirst;
1226 *last = client.rmtLast; 1238 *last = client.rmtLast;
1227 } 1239 }
1228 1240
1229 Bool 1241 int
1230 Client_postArt( const char *msgId, const char *artTxt, Str errStr ) 1242 Client_postArt( const char *msgId, const char *artTxt, Str errStr )
1231 { 1243 {
1232 int stat; 1244 int stat;
1233 1245
1234 errStr[0] = '\0'; 1246 errStr[0] = '\0';
1235 1247
1236 if ( ! putCmd( "POST" ) ) 1248 if ( ! putCmd( "POST" ) )
1237 return FALSE; 1249 return STAT_CONNECTION_LOST;
1238 stat = getStat(); 1250 stat = getStat();
1239 if ( stat == STAT_PROGRAM_FAULT ) 1251 if ( IS_FATAL( stat ) )
1240 return FALSE; 1252 return stat;
1241 else if ( stat != STAT_SEND_ART ) 1253 else if ( stat != STAT_SEND_ART )
1242 { 1254 {
1243 Log_err( "Posting of %s not allowed: %s", msgId, client.lastStat ); 1255 Log_err( "Posting of %s not allowed: %s", msgId, client.lastStat );
1244 strcpy( errStr, client.lastStat ); 1256 strcpy( errStr, client.lastStat );
1245 return TRUE; 1257 return stat;
1246 } 1258 }
1247 putTxtBuf( artTxt ); 1259 putTxtBuf( artTxt );
1248 putEndOfTxt(); 1260 putEndOfTxt();
1249 stat = getStat(); 1261 stat = getStat();
1250 if ( stat == STAT_PROGRAM_FAULT ) 1262 if ( IS_FATAL( stat ) )
1251 return FALSE; 1263 return stat;
1252 else if ( stat != STAT_POST_OK ) 1264 else if ( stat != STAT_POST_OK )
1253 { 1265 {
1254 Log_err( "Posting of %s failed: %s", msgId, client.lastStat ); 1266 Log_err( "Posting of %s failed: %s", msgId, client.lastStat );
1255 strcpy( errStr, client.lastStat ); 1267 strcpy( errStr, client.lastStat );
1256 return TRUE; 1268 return stat;
1257 } 1269 }
1258 Log_inf( "Posted %s (Status: %s)", msgId, client.lastStat ); 1270 Log_inf( "Posted %s (Status: %s)", msgId, client.lastStat );
1259 return TRUE; 1271 return STAT_OK;
1260 } 1272 }