Mercurial > noffle
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 } |