Mercurial > noffle
comparison src/server.c @ 127:3c71e28c8eef noffle
[svn] Release-1-0 mergedocs/NOTES
author | bears |
---|---|
date | Tue, 25 Jul 2000 13:14:54 +0100 |
parents | f50cc311e29a |
children | 8b9366fc1361 |
comparison
equal
deleted
inserted
replaced
126:7c7a7c96d35b | 127:3c71e28c8eef |
---|---|
1 /* | 1 /* |
2 server.c | 2 server.c |
3 | 3 |
4 $Id: server.c 167 2000-06-30 07:10:45Z enz $ | 4 $Id: server.c 183 2000-07-25 12:14:54Z 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 |
50 #include "portable.h" | 50 #include "portable.h" |
51 | 51 |
52 struct | 52 struct |
53 { | 53 { |
54 Bool running; | 54 Bool running; |
55 time_t lastServerOpen; | |
55 int artPtr; | 56 int artPtr; |
56 Str grp; /* selected group, "" if none */ | 57 Str grp; /* selected group, "" if none */ |
57 } server = { FALSE, 0, "" }; | 58 Bool readAlarmFlag; |
59 } server = { FALSE, 0L, 0, "", FALSE }; | |
58 | 60 |
59 typedef struct Cmd | 61 typedef struct Cmd |
60 { | 62 { |
61 const char *name; | 63 const char *name; |
62 const char *syntax; | 64 const char *syntax; |
85 static Bool doXpat( char *arg, const Cmd *cmd ); | 87 static Bool doXpat( char *arg, const Cmd *cmd ); |
86 static Bool doXOver( char *arg, const Cmd *cmd ); | 88 static Bool doXOver( char *arg, const Cmd *cmd ); |
87 static Bool notImplemented( char *arg, const Cmd *cmd ); | 89 static Bool notImplemented( char *arg, const Cmd *cmd ); |
88 static void putStat( unsigned int stat, const char *fmt, ... ); | 90 static void putStat( unsigned int stat, const char *fmt, ... ); |
89 | 91 |
92 static void closeServer( void ); | |
93 static Bool initServer( void ); | |
94 | |
90 Cmd commands[] = | 95 Cmd commands[] = |
91 { | 96 { |
92 { "article", "ARTICLE [msg-id|n]", &doArt }, | 97 { "article", "ARTICLE [msg-id|n]", &doArt }, |
93 { "body", "BODY [msg-id|n]", &doBody }, | 98 { "body", "BODY [msg-id|n]", &doBody }, |
94 { "head", "HEAD [msg-id|n]", &doHead }, | 99 { "head", "HEAD [msg-id|n]", &doHead }, |
105 { "next", "NEXT", &doNext }, | 110 { "next", "NEXT", &doNext }, |
106 { "post", "POST", &doPost }, | 111 { "post", "POST", &doPost }, |
107 { "quit", "QUIT", &doQuit }, | 112 { "quit", "QUIT", &doQuit }, |
108 { "slave", "SLAVE (ignored)", &doSlave }, | 113 { "slave", "SLAVE (ignored)", &doSlave }, |
109 { "stat", "STAT [msg-id|n]", &doStat }, | 114 { "stat", "STAT [msg-id|n]", &doStat }, |
110 { "xhdr", "XHDR over-field [m[-[n]]]", &doXhdr }, | 115 { "xhdr", "XHDR over-field [msg-id|m[-[n]]]", &doXhdr }, |
111 { "xpat", "XPAT over-field m[-[n]] pat", &doXpat }, | 116 { "xpat", "XPAT over-field msg-id|m[-[n]] pat", &doXpat }, |
112 { "xover", "XOVER [m[-[n]]]", &doXOver } | 117 { "xover", "XOVER [m[-[n]]]", &doXOver } |
113 }; | 118 }; |
114 | 119 |
115 /* | 120 /* |
116 Notice interest in reading this group. | 121 Notice interest in reading this group. |
200 putSyntax( const Cmd *cmd ) | 205 putSyntax( const Cmd *cmd ) |
201 { | 206 { |
202 putStat( STAT_SYNTAX_ERR, "Syntax error. Usage: %s", cmd->syntax ); | 207 putStat( STAT_SYNTAX_ERR, "Syntax error. Usage: %s", cmd->syntax ); |
203 } | 208 } |
204 | 209 |
205 static Bool | 210 static void |
206 getLn( Str line ) | 211 readAlarm( int sig ) |
207 { | 212 { |
208 return Prt_getLn( line, stdin ); | 213 UNUSED( sig ); |
214 | |
215 server.readAlarmFlag = TRUE; | |
216 return; | |
217 } | |
218 | |
219 static sig_t | |
220 installSignalHandler( int sig, sig_t handler ) | |
221 { | |
222 struct sigaction act, oldAct; | |
223 | |
224 act.sa_handler = handler; | |
225 sigemptyset( &act.sa_mask ); | |
226 act.sa_flags = 0; | |
227 if ( sig != SIGALRM ) | |
228 act.sa_flags |= SA_RESTART; | |
229 if ( sigaction( sig, &act, &oldAct ) < 0 ) | |
230 return SIG_ERR; | |
231 return oldAct.sa_handler; | |
232 } | |
233 | |
234 /* | |
235 * Returns: < 0 on error, 0 on timeout, > 0 on success. | |
236 * If timeout is zero, wait indefinitely. | |
237 */ | |
238 static int | |
239 waitCmdLn( Str line, int timeoutSeconds ) | |
240 { | |
241 sig_t oldHandler; | |
242 int r; | |
243 | |
244 ASSERT( timeoutSeconds >= 0 ); | |
245 | |
246 /* Special case - no timeout. */ | |
247 if ( timeoutSeconds == 0 ) | |
248 { | |
249 r = Prt_getLn( line, stdin ); | |
250 return ( r ) ? 1 : -1; | |
251 } | |
252 | |
253 server.readAlarmFlag = FALSE; | |
254 oldHandler = installSignalHandler( SIGALRM, readAlarm ); | |
255 if ( oldHandler == SIG_ERR ) | |
256 { | |
257 Log_err( "client.c:connectWithTimeout: signal failed." ); | |
258 return -1; | |
259 } | |
260 if ( alarm( ( unsigned int ) timeoutSeconds ) != 0 ) | |
261 Log_err( "server.c:waitCmdLn: Alarm was already set." ); | |
262 r = Prt_getLn( line, stdin ); | |
263 alarm( 0 ); | |
264 installSignalHandler( SIGALRM, oldHandler ); | |
265 if ( server.readAlarmFlag ) | |
266 return 0; | |
267 else if ( r ) | |
268 return 1; | |
269 return -1; | |
209 } | 270 } |
210 | 271 |
211 static Bool | 272 static Bool |
212 getTxtLn( Str line, Bool *err ) | 273 getTxtLn( Str line, Bool *err ) |
213 { | 274 { |
634 static void | 695 static void |
635 printGroups( const char *pat, void (*printProc)( Str, const char* ) ) | 696 printGroups( const char *pat, void (*printProc)( Str, const char* ) ) |
636 { | 697 { |
637 Str line; | 698 Str line; |
638 const char *g; | 699 const char *g; |
639 FILE *f; | |
640 sig_t lastHandler; | |
641 int ret; | |
642 | 700 |
643 putStat( STAT_GRPS_FOLLOW, "Groups" ); | 701 putStat( STAT_GRPS_FOLLOW, "Groups" ); |
644 fflush( stdout ); | 702 fflush( stdout ); |
645 Log_dbg( "[S FLUSH]" ); | 703 Log_dbg( "[S FLUSH]" ); |
646 if ( Grp_exists( pat ) ) | 704 if ( Grp_exists( pat ) ) |
649 if ( ! Prt_putTxtLn( line, stdout ) ) | 707 if ( ! Prt_putTxtLn( line, stdout ) ) |
650 Log_err( "Writing to stdout failed." ); | 708 Log_err( "Writing to stdout failed." ); |
651 } | 709 } |
652 else | 710 else |
653 { | 711 { |
654 lastHandler = signal( SIGPIPE, SIG_IGN ); | 712 if ( Grp_firstGrp( &g ) ) |
655 f = popen( "sort", "w" ); | 713 do |
656 if ( f == NULL ) | 714 if ( Wld_match( g, pat ) ) |
657 { | 715 { |
658 Log_err( "Cannot open pipe to 'sort'" ); | 716 (*printProc)( line, g ); |
659 if ( Grp_firstGrp( &g ) ) | 717 if ( ! Prt_putTxtLn( line, stdout ) ) |
660 do | 718 Log_err( "Writing to stdout failed." ); |
661 if ( Wld_match( g, pat ) ) | 719 } |
662 { | 720 while ( Grp_nextGrp( &g ) ); |
663 (*printProc)( line, g ); | |
664 if ( ! Prt_putTxtLn( line, stdout ) ) | |
665 Log_err( "Writing to stdout failed." ); | |
666 } | |
667 while ( Grp_nextGrp( &g ) ); | |
668 } | |
669 else | |
670 { | |
671 if ( Grp_firstGrp( &g ) ) | |
672 do | |
673 if ( Wld_match( g, pat ) ) | |
674 { | |
675 (*printProc)( line, g ); | |
676 if ( ! Prt_putTxtLn( line, f ) ) | |
677 { | |
678 Log_err( "Writing to 'sort' pipe failed." ); | |
679 break; | |
680 } | |
681 } | |
682 while ( Grp_nextGrp( &g ) ); | |
683 ret = pclose( f ); | |
684 if ( ret != EXIT_SUCCESS ) | |
685 Log_err( "sort command returned %d", ret ); | |
686 fflush( stdout ); | |
687 Log_dbg( "[S FLUSH]" ); | |
688 signal( SIGPIPE, lastHandler ); | |
689 } | |
690 } | 721 } |
691 putEndOfTxt(); | 722 putEndOfTxt(); |
692 } | 723 } |
693 | 724 |
694 static void | 725 static void |
811 if ( ! Grp_exists( arg ) ) | 842 if ( ! Grp_exists( arg ) ) |
812 putStat( STAT_NO_SUCH_GRP, "No such group" ); | 843 putStat( STAT_NO_SUCH_GRP, "No such group" ); |
813 else | 844 else |
814 { | 845 { |
815 changeToGrp( arg ); | 846 changeToGrp( arg ); |
847 | |
848 /* | |
849 * The output may take some time, so release the lock | |
850 * while outputting (all the required data is in RAM | |
851 * at this point). | |
852 */ | |
853 closeServer(); | |
854 | |
816 first = Cont_first(); | 855 first = Cont_first(); |
817 last = Cont_last(); | 856 last = Cont_last(); |
818 putStat( STAT_GRP_SELECTED, "Article list" ); | 857 putStat( STAT_GRP_SELECTED, "Article list" ); |
819 for ( i = first; i <= last; ++i ) | 858 for ( i = first; i <= last; ++i ) |
820 if ( ( ov = Cont_get( i ) ) ) | 859 if ( ( ov = Cont_get( i ) ) ) |
978 | 1017 |
979 putStat( STAT_SEND_ART, "Continue (end with period)" ); | 1018 putStat( STAT_SEND_ART, "Continue (end with period)" ); |
980 fflush( stdout ); | 1019 fflush( stdout ); |
981 Log_dbg( "[S FLUSH]" ); | 1020 Log_dbg( "[S FLUSH]" ); |
982 s = new_DynStr( 10000 ); | 1021 s = new_DynStr( 10000 ); |
1022 | |
1023 | |
1024 /* | |
1025 * The article may take some time coming in, so release the | |
1026 * lock while collecting it. | |
1027 */ | |
1028 closeServer(); | |
1029 | |
983 err = FALSE; | 1030 err = FALSE; |
984 while ( ! err && getTxtLn( line, &err ) ) | 1031 while ( ! err && getTxtLn( line, &err ) ) |
985 DynStr_appLn( s, line ); | 1032 DynStr_appLn( s, line ); |
1033 | |
1034 if ( ! initServer() ) | |
1035 { | |
1036 del_DynStr( s ); | |
1037 return FALSE; | |
1038 } | |
986 | 1039 |
987 if ( ! err | 1040 if ( ! err |
988 && Post_open( DynStr_str( s ) ) | 1041 && Post_open( DynStr_str( s ) ) |
989 && Post_post() ) | 1042 && Post_post() ) |
990 { | 1043 { |
1031 for ( i = *first; i <= *last; ++i ) | 1084 for ( i = *first; i <= *last; ++i ) |
1032 if ( Cont_validNumb( i ) ) | 1085 if ( Cont_validNumb( i ) ) |
1033 ++(*numb); | 1086 ++(*numb); |
1034 } | 1087 } |
1035 | 1088 |
1089 enum XhdrType { SUBJ, FROM, DATE, MSG_ID, REF, BYTES, LINES, XREF, UNKNOWN }; | |
1090 | |
1091 static enum XhdrType | |
1092 whatXhdrField( const char * fieldname ) | |
1093 { | |
1094 Str name; | |
1095 | |
1096 Utl_cpyStr( name, fieldname ); | |
1097 Utl_toLower( name ); | |
1098 if ( strcmp( name, "subject" ) == 0 ) | |
1099 return SUBJ; | |
1100 else if ( strcmp( name, "from" ) == 0 ) | |
1101 return FROM; | |
1102 else if ( strcmp( name, "date" ) == 0 ) | |
1103 return DATE; | |
1104 else if ( strcmp( name, "message-id" ) == 0 ) | |
1105 return MSG_ID; | |
1106 else if ( strcmp( name, "references" ) == 0 ) | |
1107 return REF; | |
1108 else if ( strcmp( name, "bytes" ) == 0 ) | |
1109 return BYTES; | |
1110 else if ( strcmp( name, "lines" ) == 0 ) | |
1111 return LINES; | |
1112 else if ( strcmp( name, "xref" ) == 0 ) | |
1113 return XREF; | |
1114 else | |
1115 return UNKNOWN; | |
1116 } | |
1117 | |
1118 static void | |
1119 getXhdrField( enum XhdrType what, const Over * ov, Str res ) | |
1120 { | |
1121 const char * msgId; | |
1122 Str host; | |
1123 | |
1124 switch ( what ) | |
1125 { | |
1126 case SUBJ: | |
1127 Utl_cpyStr( res, Ov_subj( ov ) ); | |
1128 break; | |
1129 case FROM: | |
1130 Utl_cpyStr( res, Ov_from( ov ) ); | |
1131 break; | |
1132 case DATE: | |
1133 Utl_cpyStr( res, Ov_date( ov ) ); | |
1134 break; | |
1135 case MSG_ID: | |
1136 Utl_cpyStr( res, Ov_msgId( ov ) ); | |
1137 break; | |
1138 case REF: | |
1139 Utl_cpyStr( res, Ov_ref( ov ) ); | |
1140 break; | |
1141 case BYTES: | |
1142 snprintf( res, MAXCHAR, "%d", Ov_bytes( ov ) ); | |
1143 break; | |
1144 case LINES: | |
1145 snprintf( res, MAXCHAR, "%d", Ov_lines( ov ) ); | |
1146 break; | |
1147 case XREF: | |
1148 msgId = Ov_msgId( ov ); | |
1149 /* | |
1150 * Gen info messages don't have an Xref header. When INN is asked | |
1151 * for a header that doesn't exist in a message, it reports the | |
1152 * header value as '(none)', so do the same. | |
1153 */ | |
1154 if ( Pseudo_isGeneralInfo( msgId ) ) | |
1155 Utl_cpyStr( res, "none" ); | |
1156 else | |
1157 { | |
1158 gethostname( host, MAXCHAR ); | |
1159 snprintf( res, MAXCHAR, "%s %s", host, Db_xref( msgId ) ); | |
1160 } | |
1161 break; | |
1162 default: | |
1163 ASSERT( FALSE ); | |
1164 } | |
1165 } | |
1166 | |
1036 /* | 1167 /* |
1037 Note this only handles a subset of headers. But they are all | 1168 Note this only handles a subset of headers. But they are all |
1038 the headers any newsreader should need to work properly. | 1169 the headers any newsreader should need to work properly. |
1039 | 1170 |
1040 That last sentence will at some stage be proved wrong. | 1171 That last sentence will at some stage be proved wrong. |
1041 */ | 1172 */ |
1042 static Bool | 1173 static Bool |
1043 doXhdr( char *arg, const Cmd *cmd ) | 1174 doXhdr( char *arg, const Cmd *cmd ) |
1044 { | 1175 { |
1045 int first, last, i, n, numb; | 1176 enum XhdrType what; |
1046 enum { SUBJ, FROM, DATE, MSG_ID, REF, BYTES, LINES, XREF } what; | 1177 const char *p; |
1047 const char *p, *msgId; | |
1048 const Over *ov; | |
1049 Str whatStr; | 1178 Str whatStr; |
1050 | 1179 |
1051 if ( ! testGrpSelected() ) | |
1052 return TRUE; | |
1053 if ( sscanf( arg, "%s", whatStr ) != 1 ) | 1180 if ( sscanf( arg, "%s", whatStr ) != 1 ) |
1054 { | 1181 { |
1055 putSyntax( cmd ); | 1182 putSyntax( cmd ); |
1056 return TRUE; | 1183 return TRUE; |
1057 } | 1184 } |
1058 Utl_toLower( whatStr ); | 1185 what = whatXhdrField( whatStr ); |
1059 if ( strcmp( whatStr, "subject" ) == 0 ) | 1186 if ( what == UNKNOWN ) |
1060 what = SUBJ; | |
1061 else if ( strcmp( whatStr, "from" ) == 0 ) | |
1062 what = FROM; | |
1063 else if ( strcmp( whatStr, "date" ) == 0 ) | |
1064 what = DATE; | |
1065 else if ( strcmp( whatStr, "message-id" ) == 0 ) | |
1066 what = MSG_ID; | |
1067 else if ( strcmp( whatStr, "references" ) == 0 ) | |
1068 what = REF; | |
1069 else if ( strcmp( whatStr, "bytes" ) == 0 ) | |
1070 what = BYTES; | |
1071 else if ( strcmp( whatStr, "lines" ) == 0 ) | |
1072 what = LINES; | |
1073 else if ( strcmp( whatStr, "xref" ) == 0 ) | |
1074 what = XREF; | |
1075 else | |
1076 { | 1187 { |
1077 putStat( STAT_HEAD_FOLLOWS, "Unknown header (empty list follows)" ); | 1188 putStat( STAT_HEAD_FOLLOWS, "Unknown header (empty list follows)" ); |
1078 putEndOfTxt(); | 1189 putEndOfTxt(); |
1079 return TRUE; | 1190 return TRUE; |
1080 } | 1191 } |
1081 p = Utl_restOfLn( arg, 1 ); | 1192 p = Utl_restOfLn( arg, 1 ); |
1082 if ( p[ 0 ] == '<' ) | 1193 if ( p[ 0 ] == '<' ) |
1083 { | 1194 { |
1084 first = last = Cont_find( p ); | 1195 Over * ov; |
1085 if ( first < 0 ) | 1196 Str field; |
1086 numb = 0; | 1197 |
1198 /* Argument is message ID */ | |
1199 ov = Db_over( p ); | |
1200 if ( ov == NULL ) | |
1201 { | |
1202 putStat( STAT_NO_SUCH_ID, "No such article" ); | |
1203 return TRUE; | |
1204 } | |
1205 putStat( STAT_HEAD_FOLLOWS, "%s header %s", whatStr, p ) ; | |
1206 getXhdrField( what, ov, field ); | |
1207 putTxtLn( "%s %s", p, field ); | |
1208 del_Over( ov ); | |
1087 } | 1209 } |
1088 else | 1210 else |
1211 { | |
1212 const Over * ov; | |
1213 int first, last, i, n, numb; | |
1214 Str field; | |
1215 | |
1216 /* Argument is article no. or range */ | |
1217 if ( ! testGrpSelected() ) | |
1218 return TRUE; | |
1089 parseRange( p, &first, &last, &numb ); | 1219 parseRange( p, &first, &last, &numb ); |
1090 if ( numb == 0 ) | 1220 if ( numb == 0 ) |
1091 putStat( STAT_NO_ART_SELECTED, "No articles selected" ); | 1221 { |
1092 else | 1222 putStat( STAT_NO_ART_SELECTED, "No articles selected" ); |
1093 { | 1223 return TRUE; |
1224 } | |
1094 putStat( STAT_HEAD_FOLLOWS, "%s header %lu-%lu", | 1225 putStat( STAT_HEAD_FOLLOWS, "%s header %lu-%lu", |
1095 whatStr, first, last ) ; | 1226 whatStr, first, last ) ; |
1096 for ( i = first; i <= last; ++i ) | 1227 for ( i = first; i <= last; ++i ) |
1097 if ( ( ov = Cont_get( i ) ) ) | 1228 if ( ( ov = Cont_get( i ) ) ) |
1098 { | 1229 { |
1099 n = Ov_numb( ov ); | 1230 n = Ov_numb( ov ); |
1100 switch ( what ) | 1231 getXhdrField( what, ov, field ); |
1101 { | 1232 putTxtLn( "%lu %s", n, field ); |
1102 case SUBJ: | |
1103 putTxtLn( "%lu %s", n, Ov_subj( ov ) ); | |
1104 break; | |
1105 case FROM: | |
1106 putTxtLn( "%lu %s", n, Ov_from( ov ) ); | |
1107 break; | |
1108 case DATE: | |
1109 putTxtLn( "%lu %s", n, Ov_date( ov ) ); | |
1110 break; | |
1111 case MSG_ID: | |
1112 putTxtLn( "%lu %s", n, Ov_msgId( ov ) ); | |
1113 break; | |
1114 case REF: | |
1115 putTxtLn( "%lu %s", n, Ov_ref( ov ) ); | |
1116 break; | |
1117 case BYTES: | |
1118 putTxtLn( "%lu %d", n, Ov_bytes( ov ) ); | |
1119 break; | |
1120 case LINES: | |
1121 putTxtLn( "%lu %d", n, Ov_lines( ov ) ); | |
1122 break; | |
1123 case XREF: | |
1124 msgId = Ov_msgId( ov ); | |
1125 if ( Pseudo_isGeneralInfo( msgId ) ) | |
1126 putTxtLn( "%lu %s:%lu", n, server.grp, n ); | |
1127 else | |
1128 putTxtLn( "%lu %s", n, Db_xref( msgId ) ); | |
1129 break; | |
1130 default: | |
1131 ASSERT( FALSE ); | |
1132 } | |
1133 } | 1233 } |
1134 putEndOfTxt(); | 1234 } |
1135 } | 1235 putEndOfTxt(); |
1136 return TRUE; | 1236 return TRUE; |
1137 } | 1237 } |
1138 | 1238 |
1139 static Bool | 1239 static Bool |
1140 doXpat( char *arg, const Cmd *cmd ) | 1240 doXpat( char *arg, const Cmd *cmd ) |
1141 { | 1241 { |
1142 int first, last, i, n; | 1242 enum XhdrType what; |
1143 enum { SUBJ, FROM, DATE, MSG_ID, REF } what; | 1243 Str whatStr, articles, pat; |
1144 const Over *ov; | 1244 |
1145 Str whatStr, pat; | 1245 if ( sscanf( arg, "%s %s %s", whatStr, articles, pat ) != 3 ) |
1146 | 1246 { |
1147 if ( ! testGrpSelected() ) | 1247 putSyntax( cmd ); |
1148 return TRUE; | 1248 return TRUE; |
1149 if ( sscanf( arg, "%s %d-%d %s", whatStr, &first, &last, pat ) != 4 ) | 1249 } |
1150 { | 1250 what = whatXhdrField( whatStr ); |
1151 if ( sscanf( arg, "%s %d- %s", whatStr, &first, pat ) == 3 ) | 1251 if ( what == UNKNOWN ) |
1152 last = Cont_last(); | 1252 { |
1153 else if ( sscanf( arg, "%s %d %s", whatStr, &first, pat ) == 3 ) | 1253 putStat( STAT_HEAD_FOLLOWS, "Unknown header (empty list follows)" ); |
1154 last = first; | |
1155 else | |
1156 { | |
1157 putSyntax( cmd ); | |
1158 return TRUE; | |
1159 } | |
1160 } | |
1161 Utl_toLower( whatStr ); | |
1162 if ( strcmp( whatStr, "subject" ) == 0 ) | |
1163 what = SUBJ; | |
1164 else if ( strcmp( whatStr, "from" ) == 0 ) | |
1165 what = FROM; | |
1166 else if ( strcmp( whatStr, "date" ) == 0 ) | |
1167 what = DATE; | |
1168 else if ( strcmp( whatStr, "message-id" ) == 0 ) | |
1169 what = MSG_ID; | |
1170 else if ( strcmp( whatStr, "references" ) == 0 ) | |
1171 what = REF; | |
1172 else | |
1173 { | |
1174 putStat( STAT_HEAD_FOLLOWS, "invalid header (empty list follows)" ); | |
1175 putEndOfTxt(); | 1254 putEndOfTxt(); |
1176 return TRUE; | 1255 return TRUE; |
1177 } | 1256 } |
1178 putStat( STAT_HEAD_FOLLOWS, "header" ) ; | 1257 if ( articles[ 0 ] == '<' ) |
1179 for ( i = first; i <= last; ++i ) | 1258 { |
1180 if ( ( ov = Cont_get( i ) ) ) | 1259 Over * ov; |
1181 { | 1260 Str field; |
1182 n = Ov_numb( ov ); | 1261 |
1183 switch ( what ) | 1262 /* Argument is message ID */ |
1263 ov = Db_over( articles ); | |
1264 if ( ov == NULL ) | |
1265 { | |
1266 putStat( STAT_NO_SUCH_ID, "No such article" ); | |
1267 return TRUE; | |
1268 } | |
1269 putStat( STAT_HEAD_FOLLOWS, "%s header %s", whatStr, articles ) ; | |
1270 getXhdrField( what, ov, field ); | |
1271 if ( Wld_match( field, pat ) ) | |
1272 putTxtLn( "%s %s", articles, field ); | |
1273 del_Over( ov ); | |
1274 } | |
1275 else | |
1276 { | |
1277 const Over * ov; | |
1278 Str field; | |
1279 int first, last, i, n, numb; | |
1280 | |
1281 /* Argument is article no. or range */ | |
1282 if ( ! testGrpSelected() ) | |
1283 return TRUE; | |
1284 parseRange( articles, &first, &last, &numb ); | |
1285 if ( numb == 0 ) | |
1286 { | |
1287 putStat( STAT_NO_ART_SELECTED, "No articles selected" ); | |
1288 return TRUE; | |
1289 } | |
1290 putStat( STAT_HEAD_FOLLOWS, "%s header %lu-%lu", | |
1291 whatStr, first, last ) ; | |
1292 for ( i = first; i <= last; ++i ) | |
1293 if ( ( ov = Cont_get( i ) ) ) | |
1184 { | 1294 { |
1185 case SUBJ: | 1295 n = Ov_numb( ov ); |
1186 if ( Wld_match( Ov_subj( ov ), pat ) ) | 1296 getXhdrField( what, ov, field ); |
1187 putTxtLn( "%lu %s", n, Ov_subj( ov ) ); | 1297 if ( Wld_match( field, pat ) ) |
1188 break; | 1298 putTxtLn( "%lu %s", n, field ); |
1189 case FROM: | |
1190 if ( Wld_match( Ov_from( ov ), pat ) ) | |
1191 putTxtLn( "%lu %s", n, Ov_from( ov ) ); | |
1192 break; | |
1193 case DATE: | |
1194 if ( Wld_match( Ov_date( ov ), pat ) ) | |
1195 putTxtLn( "%lu %s", n, Ov_date( ov ) ); | |
1196 break; | |
1197 case MSG_ID: | |
1198 if ( Wld_match( Ov_msgId( ov ), pat ) ) | |
1199 putTxtLn( "%lu %s", n, Ov_msgId( ov ) ); | |
1200 break; | |
1201 case REF: | |
1202 if ( Wld_match( Ov_ref( ov ), pat ) ) | |
1203 putTxtLn( "%lu %s", n, Ov_ref( ov ) ); | |
1204 break; | |
1205 default: | |
1206 ASSERT( FALSE ); | |
1207 } | 1299 } |
1208 } | 1300 } |
1209 putEndOfTxt(); | 1301 putEndOfTxt(); |
1210 return TRUE; | 1302 return TRUE; |
1211 } | 1303 } |
1212 | 1304 |
1213 static Bool | 1305 static Bool |
1256 | 1348 |
1257 UNUSED( cmd ); | 1349 UNUSED( cmd ); |
1258 | 1350 |
1259 if ( ! testGrpSelected() ) | 1351 if ( ! testGrpSelected() ) |
1260 return TRUE; | 1352 return TRUE; |
1353 Grp_setLastAccess( server.grp, time( NULL ) ); | |
1354 | |
1355 /* | |
1356 * All the info we require is now in RAM, and we may generate | |
1357 * lots of output (consider XOVER 2-3999), so release the lock | |
1358 * while responding. | |
1359 */ | |
1360 closeServer(); | |
1361 | |
1261 parseRange( arg, &first, &last, &n ); | 1362 parseRange( arg, &first, &last, &n ); |
1262 if ( n == 0 ) | 1363 if ( n == 0 ) |
1263 first = last = server.artPtr; | 1364 first = last = server.artPtr; |
1264 putStat( STAT_OVERS_FOLLOW, "Overview %ld-%ld", first, last ); | 1365 putStat( STAT_OVERS_FOLLOW, "Overview %ld-%ld", first, last ); |
1265 for ( i = first; i <= last; ++i ) | 1366 for ( i = first; i <= last; ++i ) |
1330 { | 1431 { |
1331 ASSERT( ! server.running ); | 1432 ASSERT( ! server.running ); |
1332 if ( ! Lock_openDatabases() ) | 1433 if ( ! Lock_openDatabases() ) |
1333 return FALSE; | 1434 return FALSE; |
1334 server.running = TRUE; | 1435 server.running = TRUE; |
1436 server.lastServerOpen = time( NULL ); | |
1335 return TRUE; | 1437 return TRUE; |
1336 } | 1438 } |
1337 | 1439 |
1338 static void | 1440 static void |
1339 closeServer( void ) | 1441 closeServer( void ) |
1347 Server_run( void ) | 1449 Server_run( void ) |
1348 { | 1450 { |
1349 Bool done; | 1451 Bool done; |
1350 int r; | 1452 int r; |
1351 Str line; | 1453 Str line; |
1352 struct timeval timeOut; | |
1353 fd_set readSet; | |
1354 | 1454 |
1355 putWelcome(); | 1455 putWelcome(); |
1356 done = FALSE; | 1456 done = FALSE; |
1357 while ( ! done ) | 1457 while ( ! done ) |
1358 { | 1458 { |
1359 FD_ZERO( &readSet ); | 1459 /* |
1360 FD_SET( STDIN_FILENO, &readSet ); | 1460 * If we've had the lock for more than 2 seconds, |
1361 /* Never hold lock more than 5 seconds (empirically good value, | 1461 * force it to be released. Otherwise, if we have |
1362 avoids to close/open databases, if clients sends several | 1462 * the lock but have had it for < 2 secs, |
1363 commands, but releases the lock often enough, for allowing | 1463 * wait for a command line for a maximum |
1364 multiple persons to read news at the same time) */ | 1464 * of 2 seconds. This is all an attempt at striking a |
1365 timeOut.tv_sec = 5; | 1465 * balance between efficient processing of commands |
1366 timeOut.tv_usec = 0; | 1466 * and hogging the lock. |
1367 r = select( STDIN_FILENO + 1, &readSet, NULL, NULL, &timeOut ); | 1467 */ |
1468 if ( server.running ) | |
1469 { | |
1470 time_t now; | |
1471 | |
1472 now = time( NULL ); | |
1473 if ( difftime( now, server.lastServerOpen ) > 2.0 ) | |
1474 closeServer(); | |
1475 } | |
1476 r = waitCmdLn( line, ( server.running ) ? 2 : 0 ); | |
1368 if ( r < 0 ) | 1477 if ( r < 0 ) |
1478 { | |
1479 Log_inf( "Client disconnected. Terminating." ); | |
1369 done = TRUE; | 1480 done = TRUE; |
1481 } | |
1370 else if ( r == 0 ) | 1482 else if ( r == 0 ) |
1371 { | 1483 { |
1372 if ( server.running ) | 1484 if ( server.running ) |
1373 closeServer(); | 1485 closeServer(); |
1374 } | 1486 } |
1375 else /* ( r > 0 ) */ | 1487 else /* ( r > 0 ) */ |
1376 { | 1488 { |
1377 if ( ! server.running ) | 1489 if ( ! server.running ) |
1378 { | 1490 { |
1379 if ( ! initServer() ) | 1491 if ( ! initServer() ) |
1380 { | 1492 { |
1381 putFatal( "Cannot init server" ); | 1493 putFatal( "Cannot init server" ); |
1382 done = TRUE; | 1494 done = TRUE; |
1383 } | 1495 } |
1384 } | 1496 } |
1385 if ( ! getLn( line ) ) | 1497 if ( ! parseAndExecute( line ) ) |
1386 { | |
1387 Log_inf( "Client disconnected. Terminating." ); | |
1388 done = TRUE; | |
1389 } | |
1390 else if ( ! parseAndExecute( line ) ) | |
1391 done = TRUE; | 1498 done = TRUE; |
1392 } | 1499 } |
1393 } | 1500 } |
1394 if ( server.running ) | 1501 if ( server.running ) |
1395 closeServer(); | 1502 closeServer(); |