Mercurial > noffle
comparison server.c @ 0:04124a4423d4 noffle
[svn] Initial revision
author | enz |
---|---|
date | Tue, 04 Jan 2000 11:35:42 +0000 |
parents | |
children | 43631b72021f |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:04124a4423d4 |
---|---|
1 /* | |
2 server.c | |
3 | |
4 $Id: server.c 3 2000-01-04 11:35:42Z enz $ | |
5 */ | |
6 | |
7 #include "server.h" | |
8 #include <ctype.h> | |
9 #include <signal.h> | |
10 #include <stdarg.h> | |
11 #include <sys/time.h> | |
12 #include <sys/types.h> | |
13 #include <time.h> | |
14 #include <unistd.h> | |
15 #include "client.h" | |
16 #include "common.h" | |
17 #include "config.h" | |
18 #include "content.h" | |
19 #include "database.h" | |
20 #include "dynamicstring.h" | |
21 #include "fetch.h" | |
22 #include "fetchlist.h" | |
23 #include "group.h" | |
24 #include "lock.h" | |
25 #include "log.h" | |
26 #include "online.h" | |
27 #include "outgoing.h" | |
28 #include "protocol.h" | |
29 #include "pseudo.h" | |
30 #include "request.h" | |
31 #include "util.h" | |
32 | |
33 struct | |
34 { | |
35 Bool running; | |
36 int artPtr; | |
37 Str grp; /* selected group, "" if none */ | |
38 } serv = { FALSE, 0, "" }; | |
39 | |
40 typedef struct Cmd | |
41 { | |
42 const char *name; | |
43 const char *syntax; | |
44 /* Returns false, if quit cmd */ | |
45 Bool (*cmdProc)( char *arg, const struct Cmd *cmd ); | |
46 } | |
47 Cmd; | |
48 | |
49 static Bool doArt( char *arg, const Cmd *cmd ); | |
50 static Bool doBody( char *arg, const Cmd *cmd ); | |
51 static Bool doGrp( char *arg, const Cmd *cmd ); | |
52 static Bool doHead( char *arg, const Cmd *cmd ); | |
53 static Bool doHelp( char *arg, const Cmd *cmd ); | |
54 static Bool doIhave( char *arg, const Cmd *cmd ); | |
55 static Bool doLast( char *arg, const Cmd *cmd ); | |
56 static Bool doList( char *arg, const Cmd *cmd ); | |
57 static Bool doListgrp( char *arg, const Cmd *cmd ); | |
58 static Bool doMode( char *arg, const Cmd *cmd ); | |
59 static Bool doNewgrps( char *arg, const Cmd *cmd ); | |
60 static Bool doNext( char *arg, const Cmd *cmd ); | |
61 static Bool doPost( char *arg, const Cmd *cmd ); | |
62 static Bool doSlave( char *arg, const Cmd *cmd ); | |
63 static Bool doStat( char *arg, const Cmd *cmd ); | |
64 static Bool doQuit( char *arg, const Cmd *cmd ); | |
65 static Bool doXhdr( char *arg, const Cmd *cmd ); | |
66 static Bool doXpat( char *arg, const Cmd *cmd ); | |
67 static Bool doXOver( char *arg, const Cmd *cmd ); | |
68 static Bool notImplemented( char *arg, const Cmd *cmd ); | |
69 static void putStat( unsigned int stat, const char *fmt, ... ); | |
70 | |
71 Cmd commands[] = | |
72 { | |
73 { "article", "ARTICLE [msg-id|n]", &doArt }, | |
74 { "body", "BODY [msg-id|n]", &doBody }, | |
75 { "head", "HEAD [msg-id|n]", &doHead }, | |
76 { "group", "GROUP grp", &doGrp }, | |
77 { "help", "HELP", &doHelp }, | |
78 { "ihave", "IHAVE (ignored)", &doIhave }, | |
79 { "last", "LAST", &doLast }, | |
80 { "list", "LIST [ACTIVE [pat]]|ACTIVE.TIMES [pat]|" | |
81 "EXTENSIONS|NEWSGROUPS [pat]|OVERVIEW.FMT", &doList }, | |
82 { "listgroup", "LISTGROUP grp", &doListgrp }, | |
83 { "mode", "MODE (ignored)", &doMode }, | |
84 { "newgroups", "NEWGROUPS [xx]yymmdd hhmmss [GMT]", &doNewgrps }, | |
85 { "newnews", "NEWNEWS (not implemented)", ¬Implemented }, | |
86 { "next", "NEXT", &doNext }, | |
87 { "post", "POST", &doPost }, | |
88 { "quit", "QUIT", &doQuit }, | |
89 { "slave", "SLAVE (ignored)", &doSlave }, | |
90 { "stat", "STAT [msg-id|n]", &doStat }, | |
91 { "xhdr", "XHDR over-field [m[-[n]]]", &doXhdr }, | |
92 { "xpat", "XPAT over-field m[-[n]] pat", &doXpat }, | |
93 { "xover", "XOVER [m[-[n]]]", &doXOver } | |
94 }; | |
95 | |
96 /* | |
97 Notice interest in reading this group. | |
98 Automatically subscribe if option set in config file. | |
99 */ | |
100 static void | |
101 noteInterest( void ) | |
102 { | |
103 FetchMode mode; | |
104 | |
105 Grp_setLastAccess( serv.grp, time( NULL ) ); | |
106 if ( Cfg_autoSubscribe() && ! Online_true() ) | |
107 { | |
108 Fetchlist_read(); | |
109 if ( ! Fetchlist_contains( serv.grp ) ) | |
110 { | |
111 if ( strcmp( Cfg_autoSubscribeMode(), "full" ) == 0 ) | |
112 mode = FULL; | |
113 else if ( strcmp( Cfg_autoSubscribeMode(), "thread" ) == 0 ) | |
114 mode = THREAD; | |
115 else | |
116 mode = OVER; | |
117 Fetchlist_add( serv.grp, mode ); | |
118 Fetchlist_write(); | |
119 Pseudo_autoSubscribed(); | |
120 } | |
121 } | |
122 } | |
123 | |
124 static void | |
125 putStat( unsigned int stat, const char *fmt, ... ) | |
126 { | |
127 Str s, line; | |
128 va_list ap; | |
129 | |
130 ASSERT( stat <= 999 ); | |
131 va_start( ap, fmt ); | |
132 vsnprintf( s, MAXCHAR, fmt, ap ); | |
133 va_end( ap ); | |
134 snprintf( line, MAXCHAR, "%u %s", stat, s ); | |
135 Log_dbg( "[S] %s", line ); | |
136 printf( "%s\r\n", line ); | |
137 } | |
138 | |
139 static void | |
140 putTxtLn( const char *fmt, ... ) | |
141 { | |
142 Str line; | |
143 va_list ap; | |
144 | |
145 va_start( ap, fmt ); | |
146 vsnprintf( line, MAXCHAR, fmt, ap ); | |
147 va_end( ap ); | |
148 Prt_putTxtLn( line, stdout ); | |
149 } | |
150 | |
151 static void | |
152 putTxtBuf( const char *buf ) | |
153 { | |
154 if ( buf ) | |
155 Prt_putTxtBuf( buf, stdout ); | |
156 } | |
157 | |
158 static void | |
159 putEndOfTxt( void ) | |
160 { | |
161 Prt_putEndOfTxt( stdout ); | |
162 } | |
163 | |
164 static void | |
165 putSyntax( const Cmd *cmd ) | |
166 { | |
167 putStat( STAT_SYNTAX_ERR, "Syntax error. Usage: %s", cmd->syntax ); | |
168 } | |
169 | |
170 static Bool | |
171 getLn( Str line ) | |
172 { | |
173 return Prt_getLn( line, stdin ); | |
174 } | |
175 | |
176 static Bool | |
177 getTxtLn( Str line, Bool *err ) | |
178 { | |
179 return Prt_getTxtLn( line, err, stdin ); | |
180 } | |
181 | |
182 static Bool | |
183 notImplemented( char *arg, const Cmd *cmd ) | |
184 { | |
185 putStat( STAT_NO_PERMISSION, "Command not implemented" ); | |
186 return TRUE; | |
187 } | |
188 | |
189 static void | |
190 checkNewArts( const char *grp ) | |
191 { | |
192 if ( ! Online_true() | |
193 || strcmp( grp, serv.grp ) == 0 | |
194 || time( NULL ) - Grp_lastAccess( serv.grp ) < 1800 ) | |
195 return; | |
196 if ( Fetch_init( Grp_serv( grp ) ) ) | |
197 { | |
198 Fetch_getNewArts( grp, OVER ); | |
199 Fetch_close(); | |
200 } | |
201 } | |
202 | |
203 static void | |
204 postArts() | |
205 { | |
206 Str serv; | |
207 | |
208 Cfg_beginServEnum(); | |
209 while ( Cfg_nextServ( serv ) ) | |
210 if ( Fetch_init( serv ) ) | |
211 { | |
212 Fetch_postArts(); | |
213 Fetch_close(); | |
214 } | |
215 } | |
216 | |
217 static void | |
218 readCont( const char *name ) | |
219 { | |
220 Fetchlist_read(); | |
221 Cont_read( name ); | |
222 if ( ! Fetchlist_contains( name ) && ! Online_true() ) | |
223 { | |
224 Pseudo_appGeneralInfo(); | |
225 Grp_setFirstLast( name, Cont_first(), Cont_last() ); | |
226 } | |
227 } | |
228 | |
229 static void | |
230 changeToGrp( const char *grp ) | |
231 { | |
232 checkNewArts( grp ); | |
233 Utl_cpyStr( serv.grp, grp ); | |
234 readCont( grp ); | |
235 serv.artPtr = Cont_first(); | |
236 } | |
237 | |
238 static Bool | |
239 doGrp( char *arg, const Cmd *cmd ) | |
240 { | |
241 int first, last, numb; | |
242 | |
243 if ( arg[ 0 ] == '\0' ) | |
244 putSyntax( cmd ); | |
245 else if ( ! Grp_exists( arg ) ) | |
246 putStat( STAT_NO_SUCH_GRP, "No such group" ); | |
247 else | |
248 { | |
249 changeToGrp( arg ); | |
250 first = Cont_first(); | |
251 last = Cont_last(); | |
252 numb = last - first + 1; | |
253 if ( first > last ) | |
254 first = last = numb = 0; | |
255 putStat( STAT_GRP_SELECTED, "%lu %lu %lu %s selected", | |
256 numb, first, last, arg ); | |
257 } | |
258 return TRUE; | |
259 } | |
260 | |
261 static Bool | |
262 testGrpSelected( void ) | |
263 { | |
264 if ( *serv.grp == '\0' ) | |
265 { | |
266 putStat( STAT_NO_GRP_SELECTED, "No group selected" ); | |
267 return FALSE; | |
268 } | |
269 return TRUE; | |
270 } | |
271 | |
272 static void | |
273 findServ( const char *msgId, Str result ) | |
274 { | |
275 const char *p, *pColon, *serv; | |
276 Str s, grp; | |
277 | |
278 Utl_cpyStr( result, "(unknown)" ); | |
279 if ( Db_contains( msgId ) ) | |
280 { | |
281 Utl_cpyStr( s, Db_xref( msgId ) ); | |
282 p = strtok( s, " \t" ); | |
283 if ( p ) | |
284 do | |
285 { | |
286 pColon = strstr( p, ":" ); | |
287 if ( pColon ) | |
288 { | |
289 Utl_cpyStrN( grp, p, pColon - p ); | |
290 serv = Grp_serv( grp ); | |
291 if ( Cfg_servIsPreferential( serv, result ) ) | |
292 Utl_cpyStr( result, serv ); | |
293 } | |
294 } | |
295 while ( ( p = strtok( NULL, " \t" ) ) ); | |
296 } | |
297 } | |
298 | |
299 static Bool | |
300 retrieveArt( const char *msgId ) | |
301 { | |
302 Str serv; | |
303 | |
304 findServ( msgId, serv ); | |
305 if ( strcmp( serv, "(unknown)" ) == 0 ) | |
306 return FALSE; | |
307 if ( ! Client_connect( serv ) ) | |
308 return FALSE; | |
309 Client_retrieveArt( msgId ); | |
310 Client_disconnect(); | |
311 return TRUE; | |
312 } | |
313 | |
314 static Bool | |
315 checkNumb( int numb ) | |
316 { | |
317 if ( ! testGrpSelected() ) | |
318 return FALSE; | |
319 if ( ! Cont_validNumb( numb ) ) | |
320 { | |
321 putStat( STAT_NO_SUCH_NUMB, "No such article" ); | |
322 return FALSE; | |
323 } | |
324 return TRUE; | |
325 } | |
326 | |
327 /* | |
328 Parse arguments for ARTICLE, BODY, HEAD, STAT commands. | |
329 Return message-ID and article number (0 if unknown). | |
330 */ | |
331 static Bool | |
332 whichId( const char **msgId, int *numb, char *arg ) | |
333 { | |
334 const Over *ov; | |
335 int n; | |
336 | |
337 if ( sscanf( arg, "%i", &n ) == 1 ) | |
338 { | |
339 if ( ! checkNumb( n ) ) | |
340 return FALSE; | |
341 serv.artPtr = n; | |
342 ov = Cont_get( n ); | |
343 *msgId = Ov_msgId( ov ); | |
344 *numb = n; | |
345 } | |
346 else if ( strcmp( arg, "" ) == 0 ) | |
347 { | |
348 if ( ! checkNumb( serv.artPtr ) ) | |
349 return FALSE; | |
350 ov = Cont_get( serv.artPtr ); | |
351 *msgId = Ov_msgId( ov ); | |
352 *numb = serv.artPtr; | |
353 } | |
354 else | |
355 { | |
356 *msgId = arg; | |
357 *numb = 0; | |
358 } | |
359 if ( ! Pseudo_isGeneralInfo( *msgId ) && ! Db_contains( *msgId ) ) | |
360 { | |
361 putStat( STAT_NO_SUCH_NUMB, "No such article" ); | |
362 return FALSE; | |
363 } | |
364 return TRUE; | |
365 } | |
366 | |
367 void | |
368 touchArticle( const char *msgId ) | |
369 { | |
370 int stat = Db_stat( msgId ); | |
371 stat |= DB_INTERESTING; | |
372 Db_setStat( msgId, stat ); | |
373 Db_updateLastAccess( msgId ); | |
374 } | |
375 | |
376 static void | |
377 touchReferences( const char *msgId ) | |
378 { | |
379 Str s; | |
380 int len; | |
381 char *p; | |
382 const char *ref = Db_ref( msgId ); | |
383 | |
384 while ( TRUE ) | |
385 { | |
386 p = s; | |
387 while ( *ref != '<' ) | |
388 if ( *(ref++) == '\0' ) | |
389 return; | |
390 len = 0; | |
391 while ( *ref != '>' ) | |
392 { | |
393 if ( *ref == '\0' || ++len >= MAXCHAR - 1 ) | |
394 return; | |
395 *(p++) = *(ref++); | |
396 } | |
397 *(p++) = '>'; | |
398 *p = '\0'; | |
399 if ( Db_contains( s ) ) | |
400 touchArticle( s ); | |
401 } | |
402 } | |
403 | |
404 static void | |
405 doBodyInDb( const char *msgId ) | |
406 { | |
407 int stat; | |
408 Str serv; | |
409 | |
410 touchArticle( msgId ); | |
411 touchReferences( msgId ); | |
412 stat = Db_stat( msgId ); | |
413 if ( Online_true() && ( stat & DB_NOT_DOWNLOADED ) ) | |
414 { | |
415 retrieveArt( msgId ); | |
416 stat = Db_stat( msgId ); | |
417 } | |
418 if ( stat & DB_RETRIEVING_FAILED ) | |
419 { | |
420 Db_setStat( msgId, stat & ~DB_RETRIEVING_FAILED ); | |
421 putTxtBuf( Db_body( msgId ) ); | |
422 } | |
423 else if ( stat & DB_NOT_DOWNLOADED ) | |
424 { | |
425 findServ( msgId, serv ); | |
426 if ( Req_contains( serv, msgId ) ) | |
427 putTxtBuf( Pseudo_alreadyMarkedBody() ); | |
428 else if ( strcmp( serv, "(unknown)" ) != 0 && Req_add( serv, msgId ) ) | |
429 putTxtBuf( Pseudo_markedBody() ); | |
430 else | |
431 putTxtBuf( Pseudo_markingFailedBody() ); | |
432 } | |
433 else | |
434 putTxtBuf( Db_body( msgId ) ); | |
435 } | |
436 | |
437 static Bool | |
438 doBody( char *arg, const Cmd *cmd ) | |
439 { | |
440 const char *msgId; | |
441 int numb; | |
442 | |
443 if ( ! whichId( &msgId, &numb, arg ) ) | |
444 return TRUE; | |
445 putStat( STAT_BODY_FOLLOWS, "%ld %s Body", numb, msgId ); | |
446 if ( Pseudo_isGeneralInfo( msgId ) ) | |
447 putTxtBuf( Pseudo_generalInfoBody() ); | |
448 else | |
449 doBodyInDb( msgId ); | |
450 putEndOfTxt(); | |
451 noteInterest(); | |
452 return TRUE; | |
453 } | |
454 | |
455 static void | |
456 doHeadInDb( const char *msgId ) | |
457 { | |
458 putTxtBuf( Db_header( msgId ) ); | |
459 } | |
460 | |
461 static Bool | |
462 doHead( char *arg, const Cmd *cmd ) | |
463 { | |
464 const char *msgId; | |
465 int numb; | |
466 | |
467 if ( ! whichId( &msgId, &numb, arg ) ) | |
468 return TRUE; | |
469 putStat( STAT_HEAD_FOLLOWS, "%ld %s Head", numb, msgId ); | |
470 if ( Pseudo_isGeneralInfo( msgId ) ) | |
471 putTxtBuf( Pseudo_generalInfoHead() ); | |
472 else | |
473 doHeadInDb( msgId ); | |
474 putEndOfTxt(); | |
475 return TRUE; | |
476 } | |
477 | |
478 static void | |
479 doArtInDb( const char *msgId ) | |
480 { | |
481 doHeadInDb( msgId ); | |
482 putTxtLn( "" ); | |
483 doBodyInDb( msgId ); | |
484 } | |
485 | |
486 static Bool | |
487 doArt( char *arg, const Cmd *cmd ) | |
488 { | |
489 const char *msgId; | |
490 int numb; | |
491 | |
492 if ( ! whichId( &msgId, &numb, arg ) ) | |
493 return TRUE; | |
494 putStat( STAT_ART_FOLLOWS, "%ld %s Article", numb, msgId ); | |
495 if ( Pseudo_isGeneralInfo( msgId ) ) | |
496 { | |
497 putTxtBuf( Pseudo_generalInfoHead() ); | |
498 putTxtLn( "" ); | |
499 putTxtBuf( Pseudo_generalInfoBody() ); | |
500 } | |
501 else | |
502 doArtInDb( msgId ); | |
503 putEndOfTxt(); | |
504 noteInterest(); | |
505 return TRUE; | |
506 } | |
507 | |
508 static Bool | |
509 doHelp( char *arg, const Cmd *cmd ) | |
510 { | |
511 unsigned int i; | |
512 | |
513 putStat( STAT_HELP_FOLLOWS, "Help" ); | |
514 putTxtBuf( "\nCommands:\n\n" ); | |
515 for ( i = 0; i < sizeof( commands ) / sizeof( commands[ 0 ] ); ++i ) | |
516 putTxtLn( "%s", commands[ i ].syntax ); | |
517 putEndOfTxt(); | |
518 return TRUE; | |
519 } | |
520 | |
521 static Bool | |
522 doIhave( char *arg, const Cmd *cmd ) | |
523 { | |
524 putStat( STAT_ART_REJECTED, "Command not used" ); | |
525 return TRUE; | |
526 } | |
527 | |
528 static Bool | |
529 doLast( char *arg, const Cmd *cmd ) | |
530 { | |
531 int n; | |
532 | |
533 if ( testGrpSelected() ) | |
534 { | |
535 n = serv.artPtr; | |
536 if ( ! Cont_validNumb( n ) ) | |
537 putStat( STAT_NO_ART_SELECTED, "No article selected" ); | |
538 else | |
539 { | |
540 while ( ! Cont_validNumb( --n ) && n >= Cont_first() ); | |
541 if ( ! Cont_validNumb( n ) ) | |
542 putStat( STAT_NO_PREV_ART, "No previous article" ); | |
543 else | |
544 { | |
545 putStat( STAT_ART_RETRIEVED, "%ld %s selected", | |
546 n, Ov_msgId( Cont_get( n ) ) ); | |
547 serv.artPtr = n; | |
548 } | |
549 } | |
550 } | |
551 return TRUE; | |
552 } | |
553 | |
554 static void | |
555 printGroups( const char *pat, void (*printProc)( Str, const char* ) ) | |
556 { | |
557 Str line; | |
558 const char *g; | |
559 FILE *f; | |
560 sig_t lastHandler; | |
561 int ret; | |
562 | |
563 putStat( STAT_GRPS_FOLLOW, "Groups" ); | |
564 fflush( stdout ); | |
565 Log_dbg( "[S FLUSH]" ); | |
566 if ( Grp_exists( pat ) ) | |
567 { | |
568 (*printProc)( line, pat ); | |
569 if ( ! Prt_putTxtLn( line, stdout ) ) | |
570 Log_err( "Writing to stdout failed." ); | |
571 } | |
572 else | |
573 { | |
574 lastHandler = signal( SIGPIPE, SIG_IGN ); | |
575 f = popen( "sort", "w" ); | |
576 if ( f == NULL ) | |
577 { | |
578 Log_err( "Cannot open pipe to 'sort'" ); | |
579 if ( Grp_firstGrp( &g ) ) | |
580 do | |
581 if ( Utl_matchPattern( g, pat ) ) | |
582 { | |
583 (*printProc)( line, g ); | |
584 if ( ! Prt_putTxtLn( line, stdout ) ) | |
585 Log_err( "Writing to stdout failed." ); | |
586 } | |
587 while ( Grp_nextGrp( &g ) ); | |
588 } | |
589 else | |
590 { | |
591 if ( Grp_firstGrp( &g ) ) | |
592 do | |
593 if ( Utl_matchPattern( g, pat ) ) | |
594 { | |
595 (*printProc)( line, g ); | |
596 if ( ! Prt_putTxtLn( line, f ) ) | |
597 { | |
598 Log_err( "Writing to 'sort' pipe failed." ); | |
599 break; | |
600 } | |
601 } | |
602 while ( Grp_nextGrp( &g ) ); | |
603 ret = pclose( f ); | |
604 if ( ret != EXIT_SUCCESS ) | |
605 Log_err( "sort command returned %i", ret ); | |
606 fflush( stdout ); | |
607 Log_dbg( "[S FLUSH]" ); | |
608 signal( SIGPIPE, lastHandler ); | |
609 } | |
610 } | |
611 putEndOfTxt(); | |
612 } | |
613 | |
614 static void | |
615 printActiveTimes( Str result, const char *grp ) | |
616 { | |
617 snprintf( result, MAXCHAR, "%s %ld", grp, Grp_created( grp ) ); | |
618 } | |
619 | |
620 static void | |
621 doListActiveTimes( const char *pat ) | |
622 { | |
623 printGroups( pat, &printActiveTimes ); | |
624 } | |
625 | |
626 static void | |
627 printActive( Str result, const char *grp ) | |
628 { | |
629 snprintf( result, MAXCHAR, "%s %i %i y", | |
630 grp, Grp_last( grp ), Grp_first( grp ) ); | |
631 } | |
632 | |
633 static void | |
634 doListActive( const char *pat ) | |
635 { | |
636 printGroups( pat, &printActive ); | |
637 } | |
638 | |
639 static void | |
640 printNewsgrp( Str result, const char *grp ) | |
641 { | |
642 snprintf( result, MAXCHAR, "%s %s", grp, Grp_dsc( grp ) ); | |
643 } | |
644 | |
645 static void | |
646 doListNewsgrps( const char *pat ) | |
647 { | |
648 printGroups( pat, &printNewsgrp ); | |
649 } | |
650 | |
651 static void | |
652 putGrp( const char *name ) | |
653 { | |
654 putTxtLn( "%s %lu %lu y", name, Grp_last( name ), Grp_first( name ) ); | |
655 } | |
656 | |
657 static void | |
658 doListOverFmt( void ) | |
659 { | |
660 putStat( STAT_GRPS_FOLLOW, "Overview format" ); | |
661 putTxtBuf( "Subject:\n" | |
662 "From:\n" | |
663 "Date:\n" | |
664 "Message-ID:\n" | |
665 "References:\n" | |
666 "Bytes:\n" | |
667 "Lines:\n" ); | |
668 putEndOfTxt(); | |
669 } | |
670 | |
671 static void | |
672 doListExtensions( void ) | |
673 { | |
674 putStat( STAT_CMD_OK, "Extensions" ); | |
675 putTxtBuf( " LISTGROUP\n" | |
676 " XOVER\n" ); | |
677 putEndOfTxt(); | |
678 } | |
679 | |
680 static Bool | |
681 doList( char *line, const Cmd *cmd ) | |
682 { | |
683 Str s, arg; | |
684 const char *pat; | |
685 | |
686 if ( sscanf( line, "%s", s ) != 1 ) | |
687 doListActive( "*" ); | |
688 else | |
689 { | |
690 Utl_toLower( s ); | |
691 strcpy( arg, Utl_restOfLn( line, 1 ) ); | |
692 pat = Utl_stripWhiteSpace( arg ); | |
693 if ( pat[ 0 ] == '\0' ) | |
694 pat = "*"; | |
695 if ( strcmp( "active", s ) == 0 ) | |
696 doListActive( pat ); | |
697 else if ( strcmp( "overview.fmt", s ) == 0 ) | |
698 doListOverFmt(); | |
699 else if ( strcmp( "newsgroups", s ) == 0 ) | |
700 doListNewsgrps( pat ); | |
701 else if ( strcmp( "active.times", s ) == 0 ) | |
702 doListActiveTimes( pat ); | |
703 else if ( strcmp( "extensions", s ) == 0 ) | |
704 doListExtensions(); | |
705 else | |
706 putSyntax( cmd ); | |
707 } | |
708 return TRUE; | |
709 } | |
710 | |
711 static Bool | |
712 doListgrp( char *arg, const Cmd *cmd ) | |
713 { | |
714 const Over *ov; | |
715 int first, last, i; | |
716 | |
717 if ( ! Grp_exists( arg ) ) | |
718 putStat( STAT_NO_SUCH_GRP, "No such group" ); | |
719 else | |
720 { | |
721 changeToGrp( arg ); | |
722 first = Cont_first(); | |
723 last = Cont_last(); | |
724 putStat( STAT_GRP_SELECTED, "Article list" ); | |
725 for ( i = first; i <= last; ++i ) | |
726 if ( ( ov = Cont_get( i ) ) ) | |
727 putTxtLn( "%lu", i ); | |
728 putEndOfTxt(); | |
729 } | |
730 return TRUE; | |
731 } | |
732 | |
733 static Bool | |
734 doMode( char *arg, const Cmd *cmd ) | |
735 { | |
736 putStat( STAT_READY_POST_ALLOW, "Ok" ); | |
737 return TRUE; | |
738 } | |
739 | |
740 static unsigned long | |
741 getTimeInSeconds( unsigned int year, unsigned int mon, unsigned int day, | |
742 unsigned int hour, unsigned int min, unsigned int sec ) | |
743 { | |
744 struct tm t = { 0 }; | |
745 | |
746 t.tm_year = year - 1900; | |
747 t.tm_mon = mon - 1; | |
748 t.tm_mday = day; | |
749 t.tm_hour = hour; | |
750 t.tm_min = min; | |
751 t.tm_sec = sec; | |
752 return mktime( &t ); | |
753 } | |
754 | |
755 | |
756 static Bool | |
757 doNewgrps( char *arg, const Cmd *cmd ) | |
758 { | |
759 time_t t, now, lastUpdate; | |
760 unsigned int year, mon, day, hour, min, sec, cent, len; | |
761 const char *g; | |
762 Str date, timeofday, file; | |
763 | |
764 if ( sscanf( arg, "%s %s", date, timeofday ) != 2 ) | |
765 { | |
766 putSyntax( cmd ); | |
767 return TRUE; | |
768 } | |
769 len = strlen( date ); | |
770 switch ( len ) | |
771 { | |
772 case 6: | |
773 if ( sscanf( date, "%2u%2u%2u", &year, &mon, &day ) != 3 ) | |
774 { | |
775 putSyntax( cmd ); | |
776 return TRUE; | |
777 } | |
778 now = time( NULL ); | |
779 cent = 1900; | |
780 while ( now > getTimeInSeconds( cent + 100, 1, 1, 0, 0, 0 ) ) | |
781 cent += 100; | |
782 year += cent; | |
783 break; | |
784 case 8: | |
785 if ( sscanf( date, "%4u%2u%2u", &year, &mon, &day ) != 3 ) | |
786 { | |
787 putSyntax( cmd ); | |
788 return TRUE; | |
789 } | |
790 break; | |
791 default: | |
792 putSyntax( cmd ); | |
793 return TRUE; | |
794 } | |
795 if ( sscanf( timeofday, "%2u%2u%2u", &hour, &min, &sec ) != 3 ) | |
796 { | |
797 putSyntax( cmd ); | |
798 return TRUE; | |
799 } | |
800 if ( year < 1970 || mon == 0 || mon > 12 || day == 0 || day > 31 | |
801 || hour > 23 || min > 59 || sec > 60 ) | |
802 { | |
803 putSyntax( cmd ); | |
804 return TRUE; | |
805 } | |
806 snprintf( file, MAXCHAR, "%s/groupinfo.lastupdate", Cfg_spoolDir() ); | |
807 t = getTimeInSeconds( year, mon, day, hour, min, sec ); | |
808 putStat( STAT_NEW_GRP_FOLLOW, "New groups since %s", arg ); | |
809 | |
810 if ( ! Utl_getStamp( &lastUpdate, file ) || t <= lastUpdate ) | |
811 { | |
812 if ( Grp_firstGrp( &g ) ) | |
813 do | |
814 if ( Grp_created( g ) > t ) | |
815 putGrp( g ); | |
816 while ( Grp_nextGrp( &g ) ); | |
817 } | |
818 putEndOfTxt(); | |
819 return TRUE; | |
820 } | |
821 | |
822 static Bool | |
823 doNext( char *arg, const Cmd *cmd ) | |
824 { | |
825 int n; | |
826 | |
827 if ( testGrpSelected() ) | |
828 { | |
829 n = serv.artPtr; | |
830 if ( ! Cont_validNumb( n ) ) | |
831 putStat( STAT_NO_ART_SELECTED, "No article selected" ); | |
832 else | |
833 { | |
834 while ( ! Cont_validNumb( ++n ) && n <= Cont_last() ); | |
835 if ( ! Cont_validNumb( n ) ) | |
836 putStat( STAT_NO_NEXT_ART, "No next article" ); | |
837 else | |
838 { | |
839 putStat( STAT_ART_RETRIEVED, "%ld %s selected", | |
840 n, Ov_msgId( Cont_get( n ) ) ); | |
841 serv.artPtr = n; | |
842 } | |
843 } | |
844 } | |
845 return TRUE; | |
846 } | |
847 | |
848 /* | |
849 Get first group of the Newsgroups field content, which is | |
850 a comma separated list of groups. | |
851 */ | |
852 static void | |
853 getFirstGrp( char *grpResult, const char *list ) | |
854 { | |
855 Str t; | |
856 const char *src = list; | |
857 char *dest = t; | |
858 while( TRUE ) | |
859 { | |
860 if ( *src == ',' ) | |
861 *dest = ' '; | |
862 else | |
863 *dest = *src; | |
864 if ( *src == '\0' ) | |
865 break; | |
866 ++src; | |
867 ++dest; | |
868 } | |
869 *grpResult = '\0'; | |
870 sscanf( t, "%s", grpResult ); | |
871 } | |
872 | |
873 static Bool | |
874 doPost( char *arg, const Cmd *cmd ) | |
875 { | |
876 Bool err, replyToFound, inHeader; | |
877 DynStr *s; | |
878 Str line, field, val, msgId, from, grp; | |
879 const char* p; | |
880 | |
881 /* | |
882 Get article and make following changes to the header: | |
883 - add/replace/cut Message-ID depending on config options | |
884 - add Reply-To with content of From, if missing | |
885 (some providers overwrite From field) | |
886 - rename X-Sender header to X-NOFFLE-X-Sender | |
887 (some providers want to insert their own X-Sender) | |
888 */ | |
889 putStat( STAT_SEND_ART, "Continue (end with period)" ); | |
890 fflush( stdout ); | |
891 Log_dbg( "[S FLUSH]" ); | |
892 s = new_DynStr( 10000 ); | |
893 msgId[ 0 ] = '\0'; | |
894 from[ 0 ] = '\0'; | |
895 grp[ 0 ] = '\0'; | |
896 replyToFound = FALSE; | |
897 inHeader = TRUE; | |
898 while ( getTxtLn( line, &err ) ) | |
899 { | |
900 if ( inHeader ) | |
901 { | |
902 p = Utl_stripWhiteSpace( line ); | |
903 if ( *p == '\0' ) | |
904 { | |
905 inHeader = FALSE; | |
906 if ( from[ 0 ] == '\0' ) | |
907 Log_err( "Posted message has no From field" ); | |
908 if ( ! Cfg_removeMsgId() ) | |
909 { | |
910 if ( Cfg_replaceMsgId() ) | |
911 { | |
912 Prt_genMsgId( msgId, from, "NOFFLE" ); | |
913 Log_dbg( "Replacing Message-ID with '%s'", msgId ); | |
914 } | |
915 else if ( msgId[ 0 ] == '\0' ) | |
916 { | |
917 Prt_genMsgId( msgId, from, "NOFFLE" ); | |
918 Log_inf( "Adding missing Message-ID '%s'", msgId ); | |
919 } | |
920 else if ( ! Prt_isValidMsgId( msgId ) ) | |
921 { | |
922 Log_ntc( "Replacing invalid Message-ID with '%s'", | |
923 msgId ); | |
924 Prt_genMsgId( msgId, from, "NOFFLE" ); | |
925 } | |
926 DynStr_app( s, "Message-ID: " ); | |
927 DynStr_appLn( s, msgId ); | |
928 } | |
929 if ( ! replyToFound && from[ 0 ] != '\0' ) | |
930 { | |
931 Log_dbg( "Adding Reply-To field to posted message." ); | |
932 DynStr_app( s, "Reply-To: " ); | |
933 DynStr_appLn( s, from ); | |
934 } | |
935 DynStr_appLn( s, p ); | |
936 } | |
937 else if ( Prt_getField( field, val, p ) ) | |
938 { | |
939 if ( strcmp( field, "message-id" ) == 0 ) | |
940 strcpy( msgId, val ); | |
941 else if ( strcmp( field, "from" ) == 0 ) | |
942 { | |
943 strcpy( from, val ); | |
944 DynStr_appLn( s, p ); | |
945 } | |
946 else if ( strcmp( field, "newsgroups" ) == 0 ) | |
947 { | |
948 getFirstGrp( grp, val ); | |
949 Utl_toLower( grp ); | |
950 DynStr_appLn( s, p ); | |
951 } | |
952 else if ( strcmp( field, "reply-to" ) == 0 ) | |
953 { | |
954 replyToFound = TRUE; | |
955 DynStr_appLn( s, p ); | |
956 } | |
957 else if ( strcmp( field, "x-sender" ) == 0 ) | |
958 { | |
959 DynStr_app( s, "X-NOFFLE-X-Sender: " ); | |
960 DynStr_appLn( s, val ); | |
961 } | |
962 else | |
963 DynStr_appLn( s, p ); | |
964 } | |
965 else | |
966 Log_err( "Ignoring invalid header line '%s'", p ); | |
967 } | |
968 else | |
969 DynStr_appLn( s, line ); | |
970 } | |
971 if ( inHeader ) | |
972 Log_err( "Posted message has no body" ); | |
973 if ( ! err ) | |
974 { | |
975 if ( grp[ 0 ] == '\0' ) | |
976 { | |
977 Log_err( "Posted message has no Newsgroups header field" ); | |
978 err = TRUE; | |
979 } | |
980 else if ( ! Grp_exists( grp ) ) | |
981 { | |
982 Log_err( "Unknown group in Newsgroups header field" ); | |
983 err = TRUE; | |
984 } | |
985 else if ( ! Out_add( Grp_serv( grp ), msgId, s ) ) | |
986 { | |
987 Log_err( "Cannot add posted article to outgoing directory" ); | |
988 err = TRUE; | |
989 } | |
990 } | |
991 if ( err ) | |
992 putStat( STAT_POST_FAILED, "Posting failed" ); | |
993 else | |
994 { | |
995 putStat( STAT_POST_OK, "Message queued for posting" ); | |
996 if ( Online_true() ) | |
997 postArts(); | |
998 } | |
999 del_DynStr( s ); | |
1000 return TRUE; | |
1001 } | |
1002 | |
1003 static void | |
1004 parseRange( const char *s, int *first, int *last, int *numb ) | |
1005 { | |
1006 int r, i; | |
1007 char* p; | |
1008 Str t; | |
1009 | |
1010 Utl_cpyStr( t, s ); | |
1011 p = Utl_stripWhiteSpace( t ); | |
1012 r = sscanf( p, "%i-%i", first, last ); | |
1013 if ( r < 1 ) | |
1014 { | |
1015 *first = serv.artPtr; | |
1016 *last = serv.artPtr; | |
1017 } | |
1018 else if ( r == 1 ) | |
1019 { | |
1020 if ( p[ strlen( p ) - 1 ] == '-' ) | |
1021 *last = Cont_last(); | |
1022 else | |
1023 *last = *first; | |
1024 } | |
1025 if ( *first < Cont_first() ) | |
1026 *first = Cont_first(); | |
1027 if ( *last > Cont_last() ) | |
1028 *last = Cont_last(); | |
1029 if ( *first > Cont_last() || *last < Cont_first() ) | |
1030 *last = *first - 1; | |
1031 *numb = 0; | |
1032 for ( i = *first; i <= *last; ++i ) | |
1033 if ( Cont_validNumb( i ) ) | |
1034 ++(*numb); | |
1035 } | |
1036 | |
1037 static Bool | |
1038 doXhdr( char *arg, const Cmd *cmd ) | |
1039 { | |
1040 int first, last, i, n, numb; | |
1041 enum { SUBJ, FROM, DATE, MSG_ID, REF, BYTES, LINES } what; | |
1042 const char *p; | |
1043 const Over *ov; | |
1044 Str whatStr; | |
1045 | |
1046 if ( ! testGrpSelected() ) | |
1047 return TRUE; | |
1048 if ( sscanf( arg, "%s", whatStr ) != 1 ) | |
1049 { | |
1050 putSyntax( cmd ); | |
1051 return TRUE; | |
1052 } | |
1053 Utl_toLower( whatStr ); | |
1054 if ( strcmp( whatStr, "subject" ) == 0 ) | |
1055 what = SUBJ; | |
1056 else if ( strcmp( whatStr, "from" ) == 0 ) | |
1057 what = FROM; | |
1058 else if ( strcmp( whatStr, "date" ) == 0 ) | |
1059 what = DATE; | |
1060 else if ( strcmp( whatStr, "message-id" ) == 0 ) | |
1061 what = MSG_ID; | |
1062 else if ( strcmp( whatStr, "references" ) == 0 ) | |
1063 what = REF; | |
1064 else if ( strcmp( whatStr, "bytes" ) == 0 ) | |
1065 what = BYTES; | |
1066 else if ( strcmp( whatStr, "lines" ) == 0 ) | |
1067 what = LINES; | |
1068 else | |
1069 { | |
1070 putStat( STAT_HEAD_FOLLOWS, "Unknown header (empty list follows)" ); | |
1071 putEndOfTxt(); | |
1072 return TRUE; | |
1073 } | |
1074 p = Utl_restOfLn( arg, 1 ); | |
1075 parseRange( p, &first, &last, &numb ); | |
1076 if ( numb == 0 ) | |
1077 putStat( STAT_NO_ART_SELECTED, "No articles selected" ); | |
1078 else | |
1079 { | |
1080 putStat( STAT_HEAD_FOLLOWS, "%s header %lu-%lu", | |
1081 whatStr, first, last ) ; | |
1082 for ( i = first; i <= last; ++i ) | |
1083 if ( ( ov = Cont_get( i ) ) ) | |
1084 { | |
1085 n = Ov_numb( ov ); | |
1086 switch ( what ) | |
1087 { | |
1088 case SUBJ: | |
1089 putTxtLn( "%lu %s", n, Ov_subj( ov ) ); | |
1090 break; | |
1091 case FROM: | |
1092 putTxtLn( "%lu %s", n, Ov_from( ov ) ); | |
1093 break; | |
1094 case DATE: | |
1095 putTxtLn( "%lu %s", n, Ov_date( ov ) ); | |
1096 break; | |
1097 case MSG_ID: | |
1098 putTxtLn( "%lu %s", n, Ov_msgId( ov ) ); | |
1099 break; | |
1100 case REF: | |
1101 putTxtLn( "%lu %s", n, Ov_ref( ov ) ); | |
1102 break; | |
1103 case BYTES: | |
1104 putTxtLn( "%lu %d", n, Ov_bytes( ov ) ); | |
1105 break; | |
1106 case LINES: | |
1107 putTxtLn( "%lu %d", n, Ov_lines( ov ) ); | |
1108 break; | |
1109 default: | |
1110 ASSERT( FALSE ); | |
1111 } | |
1112 } | |
1113 putEndOfTxt(); | |
1114 } | |
1115 return TRUE; | |
1116 } | |
1117 | |
1118 static Bool | |
1119 doXpat( char *arg, const Cmd *cmd ) | |
1120 { | |
1121 int first, last, i, n; | |
1122 enum { SUBJ, FROM, DATE, MSG_ID, REF } what; | |
1123 const Over *ov; | |
1124 Str whatStr, pat; | |
1125 | |
1126 if ( ! testGrpSelected() ) | |
1127 return TRUE; | |
1128 if ( sscanf( arg, "%s %i-%i %s", whatStr, &first, &last, pat ) != 4 ) | |
1129 { | |
1130 if ( sscanf( arg, "%s %i- %s", whatStr, &first, pat ) == 3 ) | |
1131 last = Cont_last(); | |
1132 else if ( sscanf( arg, "%s %i %s", whatStr, &first, pat ) == 3 ) | |
1133 last = first; | |
1134 else | |
1135 { | |
1136 putSyntax( cmd ); | |
1137 return TRUE; | |
1138 } | |
1139 } | |
1140 Utl_toLower( whatStr ); | |
1141 if ( strcmp( whatStr, "subject" ) == 0 ) | |
1142 what = SUBJ; | |
1143 else if ( strcmp( whatStr, "from" ) == 0 ) | |
1144 what = FROM; | |
1145 else if ( strcmp( whatStr, "date" ) == 0 ) | |
1146 what = DATE; | |
1147 else if ( strcmp( whatStr, "message-id" ) == 0 ) | |
1148 what = MSG_ID; | |
1149 else if ( strcmp( whatStr, "references" ) == 0 ) | |
1150 what = REF; | |
1151 else | |
1152 { | |
1153 putStat( STAT_HEAD_FOLLOWS, "invalid header (empty list follows)" ); | |
1154 putEndOfTxt(); | |
1155 return TRUE; | |
1156 } | |
1157 putStat( STAT_HEAD_FOLLOWS, "header" ) ; | |
1158 for ( i = first; i <= last; ++i ) | |
1159 if ( ( ov = Cont_get( i ) ) ) | |
1160 { | |
1161 n = Ov_numb( ov ); | |
1162 switch ( what ) | |
1163 { | |
1164 case SUBJ: | |
1165 if ( Utl_matchPattern( Ov_subj( ov ), pat ) ) | |
1166 putTxtLn( "%lu %s", n, Ov_subj( ov ) ); | |
1167 break; | |
1168 case FROM: | |
1169 if ( Utl_matchPattern( Ov_from( ov ), pat ) ) | |
1170 putTxtLn( "%lu %s", n, Ov_from( ov ) ); | |
1171 break; | |
1172 case DATE: | |
1173 if ( Utl_matchPattern( Ov_date( ov ), pat ) ) | |
1174 putTxtLn( "%lu %s", n, Ov_date( ov ) ); | |
1175 break; | |
1176 case MSG_ID: | |
1177 if ( Utl_matchPattern( Ov_msgId( ov ), pat ) ) | |
1178 putTxtLn( "%lu %s", n, Ov_msgId( ov ) ); | |
1179 break; | |
1180 case REF: | |
1181 if ( Utl_matchPattern( Ov_ref( ov ), pat ) ) | |
1182 putTxtLn( "%lu %s", n, Ov_ref( ov ) ); | |
1183 break; | |
1184 default: | |
1185 ASSERT( FALSE ); | |
1186 } | |
1187 } | |
1188 putEndOfTxt(); | |
1189 return TRUE; | |
1190 } | |
1191 | |
1192 static Bool | |
1193 doSlave( char *arg, const Cmd *cmd ) | |
1194 { | |
1195 putStat( STAT_CMD_OK, "Ok" ); | |
1196 return TRUE; | |
1197 } | |
1198 | |
1199 static Bool | |
1200 doStat( char *arg, const Cmd *cmd ) | |
1201 { | |
1202 const char *msgId; | |
1203 int numb; | |
1204 | |
1205 if ( ! whichId( &msgId, &numb, arg ) ) | |
1206 return TRUE; | |
1207 if ( numb > 0 ) | |
1208 putStat( STAT_ART_RETRIEVED, "%ld %s selected", | |
1209 numb, msgId ); | |
1210 else | |
1211 putStat( STAT_ART_RETRIEVED, "0 %s selected", msgId ); | |
1212 return TRUE; | |
1213 } | |
1214 | |
1215 static Bool | |
1216 doQuit( char *arg, const Cmd *cmd ) | |
1217 { | |
1218 putStat( STAT_GOODBYE, "Goodbye" ); | |
1219 return FALSE; | |
1220 } | |
1221 | |
1222 static Bool | |
1223 doXOver( char *arg, const Cmd *cmd ) | |
1224 { | |
1225 int first, last, i, n; | |
1226 const Over *ov; | |
1227 | |
1228 if ( ! testGrpSelected() ) | |
1229 return TRUE; | |
1230 parseRange( arg, &first, &last, &n ); | |
1231 if ( n == 0 ) | |
1232 putStat( STAT_NO_ART_SELECTED, "No articles selected" ); | |
1233 else | |
1234 { | |
1235 putStat( STAT_OVERS_FOLLOW, "Overview %ld-%ld", first, last ); | |
1236 for ( i = first; i <= last; ++i ) | |
1237 if ( ( ov = Cont_get( i ) ) ) | |
1238 putTxtLn( "%lu\t%s\t%s\t%s\t%s\t%s\t%d\t%d\t", | |
1239 Ov_numb( ov ), Ov_subj( ov ), Ov_from( ov ), | |
1240 Ov_date( ov ), Ov_msgId( ov ), Ov_ref( ov ), | |
1241 Ov_bytes( ov ), Ov_lines( ov ) ); | |
1242 putEndOfTxt(); | |
1243 } | |
1244 return TRUE; | |
1245 } | |
1246 | |
1247 static void | |
1248 putFatal( const char *fmt, ... ) | |
1249 { | |
1250 va_list ap; | |
1251 Str s; | |
1252 | |
1253 va_start( ap, fmt ); | |
1254 vsnprintf( s, MAXCHAR, fmt, ap ); | |
1255 va_end( ap ); | |
1256 Log_err( s ); | |
1257 putStat( STAT_PROGRAM_FAULT, "%s", s ); | |
1258 fflush( stdout ); | |
1259 Log_dbg( "[S FLUSH]" ); | |
1260 } | |
1261 | |
1262 /* Parse line, execute command and return FALSE, if it was the quit command. */ | |
1263 static Bool | |
1264 parseAndExecute( Str line ) | |
1265 { | |
1266 unsigned int i, n; | |
1267 Cmd *c; | |
1268 Str s, arg; | |
1269 Bool ret; | |
1270 | |
1271 if ( sscanf( line, "%s", s ) == 1 ) | |
1272 { | |
1273 Utl_toLower( s ); | |
1274 strcpy( arg, Utl_restOfLn( line, 1 ) ); | |
1275 n = sizeof( commands ) / sizeof( commands[ 0 ] ); | |
1276 for ( i = 0, c = commands; i < n; ++i, ++c ) | |
1277 if ( strcmp( c->name, s ) == 0 ) | |
1278 { | |
1279 ret = c->cmdProc( Utl_stripWhiteSpace( arg ), c ); | |
1280 fflush( stdout ); | |
1281 Log_dbg( "[S FLUSH]" ); | |
1282 return ret; | |
1283 } | |
1284 } | |
1285 putStat( STAT_NO_SUCH_CMD, "Command not recognized" ); | |
1286 fflush( stdout ); | |
1287 Log_dbg( "[S FLUSH]" ); | |
1288 return TRUE; | |
1289 } | |
1290 | |
1291 static void | |
1292 putWelcome( void ) | |
1293 { | |
1294 putStat( STAT_READY_POST_ALLOW, "NNTP server NOFFLE %s", | |
1295 Cfg_version() ); | |
1296 fflush( stdout ); | |
1297 Log_dbg( "[S FLUSH]" ); | |
1298 } | |
1299 | |
1300 static Bool | |
1301 initServ( void ) | |
1302 { | |
1303 ASSERT( ! serv.running ); | |
1304 if ( ! Lock_openDatabases() ) | |
1305 return FALSE; | |
1306 serv.running = TRUE; | |
1307 return TRUE; | |
1308 } | |
1309 | |
1310 static void | |
1311 closeServ( void ) | |
1312 { | |
1313 ASSERT( serv.running ); | |
1314 serv.running = FALSE; | |
1315 Lock_closeDatabases(); | |
1316 } | |
1317 | |
1318 void | |
1319 Serv_run( void ) | |
1320 { | |
1321 Bool done; | |
1322 int r; | |
1323 Str line; | |
1324 struct timeval timeOut; | |
1325 fd_set readSet; | |
1326 | |
1327 putWelcome(); | |
1328 done = FALSE; | |
1329 while ( ! done ) | |
1330 { | |
1331 FD_ZERO( &readSet ); | |
1332 FD_SET( STDIN_FILENO, &readSet ); | |
1333 /* Never hold lock more than 5 seconds (empirically good value, | |
1334 avoids to close/open databases, if clients sends several | |
1335 commands, but releases the lock often enough, for allowing | |
1336 multiple persons to read news at the same time) */ | |
1337 timeOut.tv_sec = 5; | |
1338 timeOut.tv_usec = 0; | |
1339 r = select( STDIN_FILENO + 1, &readSet, NULL, NULL, &timeOut ); | |
1340 if ( r < 0 ) | |
1341 done = TRUE; | |
1342 else if ( r == 0 ) | |
1343 { | |
1344 if ( serv.running ) | |
1345 closeServ(); | |
1346 } | |
1347 else /* ( r > 0 ) */ | |
1348 { | |
1349 if ( ! serv.running ) | |
1350 { | |
1351 if ( ! initServ() ) | |
1352 { | |
1353 putFatal( "Cannot init server" ); | |
1354 done = TRUE; | |
1355 } | |
1356 } | |
1357 if ( ! getLn( line ) ) | |
1358 { | |
1359 Log_inf( "Client disconnected. Terminating." ); | |
1360 done = TRUE; | |
1361 } | |
1362 else if ( ! parseAndExecute( line ) ) | |
1363 done = TRUE; | |
1364 } | |
1365 } | |
1366 if ( serv.running ) | |
1367 closeServ(); | |
1368 } |