Mercurial > noffle
annotate client.c @ 29:4d455c29bfd1 noffle
[svn] Send MODE READER for INN
author | enz |
---|---|
date | Sun, 30 Apr 2000 12:17:40 +0100 |
parents | ab6cf19be6d3 |
children | 45ddd5e72164 |
rev | line source |
---|---|
0 | 1 /* |
2 client.c | |
3 | |
25 | 4 $Id: client.c 31 2000-04-29 13:37:59Z enz $ |
0 | 5 */ |
6 | |
7 #include "client.h" | |
8 | |
9 #include <arpa/inet.h> | |
10 #include <ctype.h> | |
11 #include <netdb.h> | |
12 #include <netinet/in.h> | |
13 #include <signal.h> | |
14 #include <stdarg.h> | |
15 #include <sys/socket.h> | |
16 #include <unistd.h> | |
17 #include "config.h" | |
18 #include "content.h" | |
19 #include "dynamicstring.h" | |
20 #include "group.h" | |
21 #include "log.h" | |
22 #include "over.h" | |
23 #include "protocol.h" | |
24 #include "pseudo.h" | |
25 #include "request.h" | |
26 #include "util.h" | |
27 | |
28 struct | |
29 { | |
30 FILE* in; /* Receiving socket from server */ | |
31 FILE* out; /* Sending socket to server */ | |
32 Str lastCmd; /* Last command line */ | |
33 Str lastStat; /* Response from server to last command */ | |
34 Str grp; /* Selected group */ | |
35 int rmtFirst; /* First article of current group at server */ | |
36 int rmtLast; /* Last article of current group at server */ | |
37 Bool auth; /* Authetication already done? */ | |
38 Str serv; /* Remote server name */ | |
39 } client = { NULL, NULL, "", "", "", 1, 0, FALSE, "" }; | |
40 | |
41 static void | |
42 logBreakDown( void ) | |
43 { | |
44 Log_err( "Connection to remote server lost " | |
45 "(article numbers could be inconsistent)" ); | |
46 } | |
47 | |
48 static Bool | |
49 getLn( Str line ) | |
50 { | |
51 Bool r; | |
52 | |
53 r = Prt_getLn( line, client.in ); | |
54 if ( ! r ) | |
55 logBreakDown(); | |
56 return r; | |
57 } | |
58 | |
59 static Bool | |
60 getTxtLn( Str line, Bool *err ) | |
61 { | |
62 Bool r; | |
63 | |
64 r = Prt_getTxtLn( line, err, client.in ); | |
65 if ( *err ) | |
66 logBreakDown(); | |
67 return r; | |
68 } | |
69 | |
70 static void | |
71 putTxtBuf( const char *buf ) | |
72 { | |
73 Prt_putTxtBuf( buf, client.out ); | |
74 fflush( client.out ); | |
75 Log_dbg( "[S FLUSH]" ); | |
76 } | |
77 | |
78 static void | |
79 putEndOfTxt( void ) | |
80 { | |
81 Prt_putEndOfTxt( client.out ); | |
82 fflush( client.out ); | |
83 Log_dbg( "[S FLUSH]" ); | |
84 } | |
85 | |
86 static Bool | |
87 putCmd( const char *fmt, ... ) | |
88 { | |
89 Bool err; | |
90 unsigned int n; | |
91 Str line; | |
92 va_list ap; | |
93 | |
94 va_start( ap, fmt ); | |
95 vsnprintf( line, MAXCHAR, fmt, ap ); | |
96 va_end( ap ); | |
97 strcpy( client.lastCmd, line ); | |
98 Log_dbg( "[S] %s", line ); | |
99 n = fprintf( client.out, "%s\r\n", line ); | |
100 fflush( client.out ); | |
101 Log_dbg( "[S FLUSH]" ); | |
102 err = ( n != strlen( line ) + 2 ); | |
103 if ( err ) | |
104 logBreakDown();; | |
105 return ! err; | |
106 } | |
107 | |
108 static Bool | |
109 putCmdNoFlush( const char *fmt, ... ) | |
110 { | |
111 Bool err; | |
112 unsigned int n; | |
113 Str line; | |
114 va_list ap; | |
115 | |
116 va_start( ap, fmt ); | |
117 vsnprintf( line, MAXCHAR, fmt, ap ); | |
118 va_end( ap ); | |
119 strcpy( client.lastCmd, line ); | |
120 Log_dbg( "[S] %s", line ); | |
121 n = fprintf( client.out, "%s\r\n", line ); | |
122 err = ( n != strlen( line ) + 2 ); | |
123 if ( err ) | |
124 logBreakDown();; | |
125 return ! err; | |
126 } | |
127 | |
128 static int getStat( void ); | |
129 | |
130 static Bool | |
131 performAuth( void ) | |
132 { | |
133 int stat; | |
134 Str user, pass; | |
135 | |
136 Cfg_authInfo( client.serv, user, pass ); | |
137 if ( strcmp( user, "" ) == 0 ) | |
138 { | |
139 Log_err( "No username for authentication set" ); | |
140 return FALSE; | |
141 } | |
142 putCmd( "AUTHINFO USER %s", user ); | |
143 stat = getStat(); | |
144 if ( stat == STAT_AUTH_ACCEPTED ) | |
145 return TRUE; | |
146 else if ( stat != STAT_MORE_AUTH_REQUIRED ) | |
147 { | |
148 Log_err( "Username rejected. Server stat: %s", client.lastStat ); | |
149 return FALSE; | |
150 } | |
151 if ( strcmp( pass, "" ) == 0 ) | |
152 { | |
153 Log_err( "No password for authentication set" ); | |
154 return FALSE; | |
155 } | |
156 putCmd( "AUTHINFO PASS %s", pass ); | |
157 stat = getStat(); | |
158 if ( stat != STAT_AUTH_ACCEPTED ) | |
159 { | |
160 Log_err( "Password rejected. Server status: %s", client.lastStat ); | |
161 return FALSE; | |
162 } | |
163 return TRUE; | |
164 } | |
165 | |
166 static int | |
167 getStat( void ) | |
168 { | |
169 int result; | |
170 Str lastCmd; | |
171 | |
172 if ( ! getLn( client.lastStat ) ) | |
173 result = STAT_PROGRAM_FAULT; | |
174 else if ( sscanf( client.lastStat, "%d", &result ) != 1 ) | |
175 { | |
176 Log_err( "Invalid server status: %s", client.lastStat ); | |
177 result = STAT_PROGRAM_FAULT; | |
178 } | |
179 if ( result == STAT_AUTH_REQUIRED && ! client.auth ) | |
180 { | |
181 client.auth = TRUE; | |
182 strcpy( lastCmd, client.lastCmd ); | |
183 if ( performAuth() ) | |
184 { | |
185 putCmd( lastCmd ); | |
186 return getStat(); | |
187 } | |
188 } | |
189 return result; | |
190 } | |
191 | |
192 static void | |
193 connectAlarm( int sig ) | |
194 { | |
195 return; | |
196 } | |
197 | |
198 static sig_t | |
199 installSignalHandler( int sig, sig_t handler ) | |
200 { | |
201 struct sigaction act, oldAct; | |
202 | |
203 act.sa_handler = handler; | |
204 sigemptyset( &act.sa_mask ); | |
205 act.sa_flags = 0; | |
206 if ( sig == SIGALRM ) | |
207 act.sa_flags |= SA_INTERRUPT; | |
208 else | |
209 act.sa_flags |= SA_RESTART; | |
210 if ( sigaction( sig, &act, &oldAct ) < 0 ) | |
211 return SIG_ERR; | |
212 return oldAct.sa_handler; | |
213 } | |
214 | |
215 static Bool | |
216 connectWithTimeout( int sock, const struct sockaddr *servAddr, | |
217 socklen_t addrLen ) | |
218 { | |
219 sig_t oldHandler; | |
220 int r, to; | |
221 | |
222 oldHandler = installSignalHandler( SIGALRM, connectAlarm ); | |
223 if ( oldHandler == SIG_ERR ) | |
224 { | |
225 Log_err( "client.c:connectWithTimeout: signal failed." ); | |
226 return FALSE; | |
227 } | |
228 to = Cfg_connectTimeout(); | |
229 if ( alarm( to ) != 0 ) | |
230 Log_err( "client.c:connectWithTimeout: Alarm was already set." ); | |
231 r = connect( sock, servAddr, addrLen ); | |
232 alarm( 0 ); | |
233 installSignalHandler( SIGALRM, oldHandler ); | |
234 return ( r >= 0 ); | |
235 } | |
236 | |
237 Bool | |
238 Client_connect( const char *serv ) | |
239 { | |
240 unsigned short int port; | |
241 int sock, i; | |
242 unsigned int stat; | |
243 struct hostent *hp; | |
244 char *pStart, *pColon; | |
245 Str host, s; | |
246 struct sockaddr_in sIn; | |
247 | |
6
6e822f4b19f9
[svn] Set Client-Authenticated flag to False on connection to server.
enz
parents:
0
diff
changeset
|
248 client.auth = FALSE; |
0 | 249 Utl_cpyStr( s, serv ); |
250 pStart = Utl_stripWhiteSpace( s ); | |
251 pColon = strstr( pStart, ":" ); | |
252 if ( pColon == NULL ) | |
253 { | |
254 strcpy( host, pStart ); | |
255 port = 119; | |
256 } | |
257 else | |
258 { | |
259 *pColon = '\0'; | |
260 strcpy( host, pStart ); | |
261 if ( sscanf( pColon + 1, "%hi", &port ) != 1 ) | |
262 { | |
263 Log_err( "Syntax error in server name: '%s'", serv ); | |
264 return FALSE;; | |
265 } | |
266 if ( port <= 0 || port > 65535 ) | |
267 { | |
268 Log_err( "Invalid port number %hi. Must be in [1, 65535]", port ); | |
269 return FALSE;; | |
270 } | |
271 } | |
272 memset( (void *)&sIn, 0, sizeof( sIn ) ); | |
273 hp = gethostbyname( host ); | |
274 if ( hp ) | |
275 { | |
276 for ( i = 0; (hp->h_addr_list)[ i ]; ++i ) | |
277 { | |
278 sIn.sin_family = hp->h_addrtype; | |
279 sIn.sin_port = htons( port ); | |
280 sIn.sin_addr = *( (struct in_addr *)hp->h_addr_list[ i ] ); | |
281 sock = socket( AF_INET, SOCK_STREAM, 0 ); | |
282 if ( sock < 0 ) | |
283 break; | |
284 if ( ! connectWithTimeout( sock, (struct sockaddr *)&sIn, | |
285 sizeof( sIn ) ) ) | |
286 { | |
287 close( sock ); | |
288 break; | |
289 } | |
290 if ( ! ( client.out = fdopen( sock, "w" ) ) | |
291 || ! ( client.in = fdopen( dup( sock ), "r" ) ) ) | |
292 { | |
293 close( sock ); | |
294 break; | |
295 } | |
296 stat = getStat(); | |
25 | 297 if ( stat == STAT_READY_POST_ALLOW || |
298 stat == STAT_READY_NO_POST_ALLOW ) | |
299 { | |
300 /* INN needs a MODE READER before it will permit POST. */ | |
301 putCmd( "MODE READER" ); | |
302 stat = getStat(); | |
303 } | |
0 | 304 switch( stat ) { |
305 case STAT_READY_POST_ALLOW: | |
306 case STAT_READY_NO_POST_ALLOW: | |
307 Log_inf( "Connected to %s:%d", | |
308 inet_ntoa( sIn.sin_addr ), port ); | |
309 Utl_cpyStr( client.serv, serv ); | |
310 return TRUE; | |
311 default: | |
312 Log_err( "Bad server stat %d", stat ); | |
313 } | |
314 shutdown( fileno( client.out ), 0 ); | |
315 } | |
316 } | |
317 return FALSE; | |
318 } | |
319 | |
320 static void | |
321 processGrps( void ) | |
322 { | |
323 char postAllow; | |
324 Bool err; | |
325 int first, last; | |
326 Str grp, line, file; | |
327 | |
328 while ( getTxtLn( line, &err ) && ! err ) | |
329 { | |
330 if ( sscanf( line, "%s %i %i %c", | |
331 grp, &last, &first, &postAllow ) != 4 ) | |
332 { | |
333 Log_err( "Unknown reply to LIST or NEWGROUPS: %s", line ); | |
334 continue; | |
335 } | |
336 if ( ! Grp_exists( grp ) ) | |
337 { | |
338 Log_inf( "Registering new group '%s'", grp ); | |
339 Grp_create( grp ); | |
340 /* Start local numbering with remote first number to avoid | |
341 new numbering at the readers if noffle is re-installed */ | |
342 if ( first != 0 ) | |
343 Grp_setFirstLast( grp, first, first - 1 ); | |
344 else | |
345 Grp_setFirstLast( grp, 1, 0 ); | |
346 Grp_setRmtNext( grp, first ); | |
347 Grp_setServ( grp, client.serv ); | |
348 } | |
349 else | |
350 { | |
351 if ( Cfg_servIsPreferential( client.serv, Grp_serv( grp ) ) ) | |
352 { | |
353 Log_inf( "Changing server for '%s': '%s'->'%s'", | |
354 grp, Grp_serv( grp ), client.serv ); | |
355 Grp_setServ( grp, client.serv ); | |
356 Grp_setRmtNext( grp, first ); | |
357 } | |
358 else | |
359 Log_dbg( "Group %s is already fetched from %s", | |
360 grp, Grp_serv( grp ) ); | |
361 | |
362 } | |
363 } | |
364 if ( ! err ) | |
365 { | |
366 snprintf( file, MAXCHAR, "%s/groupinfo.lastupdate", Cfg_spoolDir() ); | |
367 Utl_stamp( file ); | |
368 } | |
369 } | |
370 | |
371 void | |
372 Client_disconnect( void ) | |
373 { | |
374 if ( putCmd( "QUIT" ) ) | |
375 getStat(); | |
376 fclose( client.in ); | |
377 fclose( client.out ); | |
378 client.in = client.out = NULL; | |
379 } | |
380 | |
381 Bool | |
382 Client_getGrps( void ) | |
383 { | |
384 if ( ! putCmd( "LIST ACTIVE" ) ) | |
385 return FALSE; | |
386 if ( getStat() != STAT_GRPS_FOLLOW ) | |
387 { | |
388 Log_err( "LIST ACTIVE command failed: %s", client.lastStat ); | |
389 return FALSE; | |
390 } | |
391 processGrps(); | |
392 return TRUE; | |
393 } | |
394 | |
395 Bool | |
396 Client_getDsc( void ) | |
397 { | |
398 Bool err; | |
399 Str name, line, dsc; | |
400 | |
401 Log_inf( "Querying group descriptions" ); | |
402 if ( ! putCmd( "LIST NEWSGROUPS" ) ) | |
403 return FALSE; | |
404 if ( getStat() != STAT_GRPS_FOLLOW ) | |
405 { | |
406 Log_err( "LIST NEWSGROUPS failed: %s", client.lastStat ); | |
407 return FALSE; | |
408 } | |
409 while ( getTxtLn( line, &err ) && ! err ) | |
410 { | |
411 if ( sscanf( line, "%s", name ) != 1 ) | |
412 { | |
413 Log_err( "Unknown reply to LIST NEWSGROUPS: %s", line ); | |
414 continue; | |
415 } | |
416 strcpy( dsc, Utl_restOfLn( line, 1 ) ); | |
417 if ( Grp_exists( name ) ) | |
418 { | |
419 Log_dbg( "Description of %s: %s", name, dsc ); | |
420 Grp_setDsc( name, dsc ); | |
421 } | |
422 } | |
423 return TRUE; | |
424 } | |
425 | |
426 Bool | |
427 Client_getCreationTimes( void ) | |
428 { | |
429 Bool err; | |
430 Str name, line; | |
431 time_t t; | |
432 | |
433 Log_inf( "Querying group creation times" ); | |
434 if ( ! putCmd( "LIST ACTIVE.TIMES" ) ) | |
435 return FALSE; | |
436 if ( getStat() != STAT_GRPS_FOLLOW ) | |
437 { | |
438 Log_err( "LIST ACTIVE.TIMES failes: %s", client.lastStat ); | |
439 return FALSE; | |
440 } | |
441 while ( getTxtLn( line, &err ) && ! err ) | |
442 { | |
443 if ( sscanf( line, "%s %ld", name, &t ) != 2 ) | |
444 { | |
445 Log_err( "Unknown reply to LIST ACTIVE.TIMES: %s", line ); | |
446 continue; | |
447 } | |
448 if ( Grp_exists( name ) ) | |
449 { | |
450 Log_inf( "Creation time of %s: %ld", name, t ); | |
451 Grp_setCreated( name, t ); | |
452 } | |
453 } | |
454 return TRUE; | |
455 } | |
456 | |
457 Bool | |
458 Client_getNewgrps( const time_t *lastTime ) | |
459 { | |
460 Str s; | |
461 const char *p; | |
462 | |
463 ASSERT( *lastTime > 0 ); | |
464 strftime( s, MAXCHAR, "%Y%m%d %H%M00", gmtime( lastTime ) ); | |
465 /* | |
466 Do not use century for working with old server software until 2000. | |
467 According to newest IETF draft, this is still valid after 2000. | |
468 (directly using %y in fmt string causes a Y2K compiler warning) | |
469 */ | |
470 p = s + 2; | |
471 if ( ! putCmd( "NEWGROUPS %s", p ) ) | |
472 return FALSE; | |
473 if ( getStat() != STAT_NEW_GRP_FOLLOW ) | |
474 { | |
475 Log_err( "NEWGROUPS command failed: %s", client.lastStat ); | |
476 return FALSE; | |
477 } | |
478 processGrps(); | |
479 return TRUE; | |
480 } | |
481 | |
482 static const char * | |
483 readField( Str result, const char *p ) | |
484 { | |
485 size_t len; | |
486 char *r; | |
487 | |
488 if ( ! p ) | |
489 return NULL; | |
490 r = result; | |
491 *r = '\0'; | |
492 len = 0; | |
493 while ( *p != '\t' && *p != '\n' ) | |
494 { | |
495 if ( ! *p ) | |
496 return p; | |
497 *(r++) = *(p++); | |
498 ++len; | |
499 if ( len >= MAXCHAR - 1 ) | |
500 { | |
501 *r = '\0'; | |
502 Log_err( "Field in overview too long: %s", r ); | |
503 return ++p; | |
504 } | |
505 } | |
506 *r = '\0'; | |
507 return ++p; | |
508 } | |
509 | |
510 static Bool | |
511 parseOvLn( Str line, int *numb, Str subj, Str from, | |
512 Str date, Str msgId, Str ref, size_t *bytes, size_t *lines ) | |
513 { | |
514 const char *p; | |
515 Str t; | |
516 | |
517 p = readField( t, line ); | |
518 if ( sscanf( t, "%i", numb ) != 1 ) | |
519 return FALSE; | |
520 p = readField( subj, p ); | |
521 p = readField( from, p ); | |
522 p = readField( date, p ); | |
523 p = readField( msgId, p ); | |
524 p = readField( ref, p ); | |
525 p = readField( t, p ); | |
526 *bytes = 0; | |
527 *lines = 0; | |
528 if ( sscanf( t, "%d", bytes ) != 1 ) | |
529 return TRUE; | |
530 p = readField( t, p ); | |
531 if ( sscanf( t, "%d", lines ) != 1 ) | |
532 return TRUE; | |
533 return TRUE; | |
534 } | |
535 | |
536 static const char* | |
537 nextXref( const char *pXref, Str grp, int *numb ) | |
538 { | |
539 Str s; | |
540 const char *pColon, *src; | |
541 char *dst; | |
542 | |
543 src = pXref; | |
544 while ( *src && isspace( *src ) ) | |
545 ++src; | |
546 dst = s; | |
547 while ( *src && ! isspace( *src ) ) | |
548 *(dst++) = *(src++); | |
549 *dst = '\0'; | |
550 if ( strlen( s ) == 0 ) | |
551 return NULL; | |
552 pColon = strstr( s, ":" ); | |
553 if ( ! pColon || sscanf( pColon + 1, "%i", numb ) != 1 ) | |
554 { | |
555 Log_err( "Corrupt Xref at position '%s'", pXref ); | |
556 return NULL; | |
557 } | |
558 Utl_cpyStrN( grp, s, pColon - s ); | |
559 Log_dbg( "client.c: nextXref: grp '%s' numb %lu", grp, numb ); | |
560 return src; | |
561 } | |
562 | |
563 static Bool | |
564 needsMark( const char *ref ) | |
565 { | |
566 Bool done = FALSE; | |
567 char *p; | |
568 Str msgId; | |
569 int stat, len; | |
570 time_t lastAccess, nowTime; | |
571 double limit; | |
572 | |
573 nowTime = time( NULL ); | |
574 limit = Cfg_threadFollowTime() * 24. * 3600.; | |
575 while ( ! done ) | |
576 { | |
577 p = msgId; | |
578 while ( *ref != '<' ) | |
579 if ( *(ref++) == '\0' ) | |
580 return FALSE; | |
581 len = 0; | |
582 while ( *ref != '>' ) | |
583 { | |
584 if ( *ref == '\0' || ++len >= MAXCHAR - 1 ) | |
585 return FALSE; | |
586 *(p++) = *(ref++); | |
587 } | |
588 *(p++) = '>'; | |
589 *p = '\0'; | |
590 if ( Db_contains( msgId ) ) | |
591 { | |
592 stat = Db_stat( msgId ); | |
593 lastAccess = Db_lastAccess( msgId ); | |
594 if ( ( stat & DB_INTERESTING ) | |
595 && difftime( nowTime, lastAccess ) <= limit ) | |
596 return TRUE; | |
597 } | |
598 } | |
599 return FALSE; | |
600 } | |
601 | |
602 static void | |
603 prepareEntry( Over *ov ) | |
604 { | |
605 Str g, t; | |
606 const char *msgId, *p, *xref; | |
607 int n; | |
608 | |
609 msgId = Ov_msgId( ov ); | |
610 if ( Pseudo_isGeneralInfo( msgId ) ) | |
611 Log_dbg( "Skipping general info '%s'", msgId ); | |
612 else if ( Db_contains( msgId ) ) | |
613 { | |
614 xref = Db_xref( msgId ); | |
615 Log_dbg( "Entry '%s' already in db with Xref '%s'", msgId, xref ); | |
616 p = nextXref( xref, g, &n ); | |
617 if ( p == NULL ) | |
618 Log_err( "Overview with no group in Xref '%s'", msgId ); | |
619 else | |
620 { | |
621 if ( Cfg_servIsPreferential( client.serv, Grp_serv( g ) ) ) | |
622 { | |
623 Log_dbg( "Changing first server for '%s' from '%s' to '%s'", | |
624 msgId, Grp_serv( g ), client.serv ); | |
625 snprintf( t, MAXCHAR, "%s:%i %s", | |
626 client.grp, Ov_numb( ov ), xref ); | |
627 Db_setXref( msgId, t ); | |
628 } | |
629 else | |
630 { | |
631 Log_dbg( "Adding '%s' to Xref of '%s'", g, msgId ); | |
632 snprintf( t, MAXCHAR, "%s %s:%i", | |
633 xref, client.grp, Ov_numb( ov ) ); | |
634 Db_setXref( msgId, t ); | |
635 } | |
636 } | |
637 } | |
638 else | |
639 { | |
640 Log_dbg( "Preparing '%s' in database", msgId ); | |
641 Db_prepareEntry( ov, client.grp, Ov_numb( ov ) ); | |
642 } | |
643 } | |
644 | |
645 Bool | |
646 Client_getOver( int rmtFirst, int rmtLast, FetchMode mode ) | |
647 { | |
648 Bool err; | |
649 size_t bytes, lines; | |
650 int rmtNumb, oldLast, cntMarked; | |
651 Over *ov; | |
652 Str line, subj, from, date, msgId, ref; | |
653 | |
654 ASSERT( strcmp( client.grp, "" ) != 0 ); | |
655 if ( ! putCmd( "XOVER %lu-%lu", rmtFirst, rmtLast ) ) | |
656 return FALSE; | |
657 if ( getStat() != STAT_OVERS_FOLLOW ) | |
658 { | |
659 Log_err( "XOVER command failed: %s", client.lastStat ); | |
660 return FALSE; | |
661 } | |
662 Log_dbg( "Requesting overview for remote %lu-%lu", rmtFirst, rmtLast ); | |
663 oldLast = Cont_last(); | |
664 cntMarked = 0; | |
665 while ( getTxtLn( line, &err ) && ! err ) | |
666 { | |
667 if ( ! parseOvLn( line, &rmtNumb, subj, from, date, msgId, ref, | |
668 &bytes, &lines ) ) | |
669 Log_err( "Bad overview line: %s", line ); | |
670 else | |
671 { | |
672 ov = new_Over( subj, from, date, msgId, ref, bytes, lines ); | |
673 Cont_app( ov ); | |
674 prepareEntry( ov ); | |
675 if ( mode == FULL || ( mode == THREAD && needsMark( ref ) ) ) | |
676 { | |
677 Req_add( client.serv, msgId ); | |
678 ++cntMarked; | |
679 } | |
680 } | |
681 Grp_setRmtNext( client.grp, rmtNumb + 1 ); | |
682 } | |
683 if ( oldLast != Cont_last() ) | |
684 Log_inf( "Added %s %lu-%lu", client.grp, oldLast + 1, Cont_last() ); | |
685 Log_inf( "%u articles marked for download in %s", cntMarked, client.grp ); | |
686 return err; | |
687 } | |
688 | |
689 static void | |
690 retrievingFailed( const char* msgId, const char *reason ) | |
691 { | |
692 int stat; | |
693 | |
694 Log_err( "Retrieving of %s failed: %s", msgId, reason ); | |
695 stat = Db_stat( msgId ); | |
696 Pseudo_retrievingFailed( msgId, reason ); | |
697 Db_setStat( msgId, stat | DB_RETRIEVING_FAILED ); | |
698 } | |
699 | |
700 static Bool | |
701 retrieveAndStoreArt( const char *msgId ) | |
702 { | |
703 Bool err; | |
704 DynStr *s = NULL; | |
705 Str line; | |
706 | |
707 Log_inf( "Retrieving %s", msgId ); | |
708 s = new_DynStr( 5000 ); | |
709 while ( getTxtLn( line, &err ) && ! err ) | |
710 DynStr_appLn( s, line ); | |
711 if ( ! err ) | |
712 Db_storeArt( msgId, DynStr_str( s ) ); | |
713 else | |
714 retrievingFailed( msgId, "Connection broke down" ); | |
715 del_DynStr( s ); | |
716 return ! err; | |
717 } | |
718 | |
719 void | |
720 Client_retrieveArt( const char *msgId ) | |
721 { | |
722 if ( ! Db_contains( msgId ) ) | |
723 { | |
724 Log_err( "Article '%s' not prepared in database. Skipping.", msgId ); | |
725 return; | |
726 } | |
727 if ( ! ( Db_stat( msgId ) & DB_NOT_DOWNLOADED ) ) | |
728 { | |
729 Log_inf( "Article '%s' already retrieved. Skipping.", msgId ); | |
730 return; | |
731 } | |
732 if ( ! putCmd( "ARTICLE %s", msgId ) ) | |
733 retrievingFailed( msgId, "Connection broke down" ); | |
734 else if ( getStat() != STAT_ART_FOLLOWS ) | |
735 retrievingFailed( msgId, client.lastStat ); | |
736 else | |
737 retrieveAndStoreArt( msgId ); | |
738 } | |
739 | |
740 void | |
741 Client_retrieveArtList( const char *list ) | |
742 { | |
743 Str msgId; | |
744 DynStr *s; | |
745 const char *p; | |
746 | |
747 Log_inf( "Retrieving article list" ); | |
748 s = new_DynStr( strlen( list ) ); | |
749 p = list; | |
750 while ( ( p = Utl_getLn( msgId, p ) ) ) | |
751 if ( ! Db_contains( msgId ) ) | |
752 Log_err( "Skipping retrieving of %s (not prepared in database)", | |
753 msgId ); | |
754 else if ( ! ( Db_stat( msgId ) & DB_NOT_DOWNLOADED ) ) | |
755 Log_inf( "Skipping %s (already retrieved)", msgId ); | |
756 else if ( ! putCmdNoFlush( "ARTICLE %s", msgId ) ) | |
757 { | |
758 retrievingFailed( msgId, "Connection broke down" ); | |
759 del_DynStr( s ); | |
760 return; | |
761 } | |
762 else | |
763 DynStr_appLn( s, msgId ); | |
764 fflush( client.out ); | |
765 Log_dbg( "[S FLUSH]" ); | |
766 p = DynStr_str( s ); | |
767 while ( ( p = Utl_getLn( msgId, p ) ) ) | |
768 { | |
769 if ( getStat() != STAT_ART_FOLLOWS ) | |
770 retrievingFailed( msgId, client.lastStat ); | |
771 else if ( ! retrieveAndStoreArt( msgId ) ) | |
772 break; | |
773 } | |
774 del_DynStr( s ); | |
775 } | |
776 | |
777 Bool | |
778 Client_changeToGrp( const char* name ) | |
779 { | |
780 unsigned int stat; | |
781 int estimatedNumb, first, last; | |
782 | |
783 if ( ! Grp_exists( name ) ) | |
784 return FALSE; | |
785 if ( ! putCmd( "GROUP %s", name ) ) | |
786 return FALSE; | |
787 if ( getStat() != STAT_GRP_SELECTED ) | |
788 return FALSE; | |
789 if ( sscanf( client.lastStat, "%u %i %i %i", | |
790 &stat, &estimatedNumb, &first, &last ) != 4 ) | |
791 { | |
792 Log_err( "Bad server response to GROUP: %s", client.lastStat ); | |
793 return FALSE; | |
794 } | |
795 Utl_cpyStr( client.grp, name ); | |
796 client.rmtFirst = first; | |
797 client.rmtLast = last; | |
798 return TRUE; | |
799 } | |
800 | |
801 void | |
802 Client_rmtFirstLast( int *first, int *last ) | |
803 { | |
804 *first = client.rmtFirst; | |
805 *last = client.rmtLast; | |
806 } | |
807 | |
808 Bool | |
809 Client_postArt( const char *msgId, const char *artTxt, | |
810 Str errStr ) | |
811 { | |
812 if ( ! putCmd( "POST" ) ) | |
813 return FALSE; | |
814 if ( getStat() != STAT_SEND_ART ) | |
815 { | |
816 Log_err( "Posting of %s not allowed: %s", msgId, client.lastStat ); | |
817 strcpy( errStr, client.lastStat ); | |
818 return FALSE; | |
819 } | |
820 putTxtBuf( artTxt ); | |
821 putEndOfTxt(); | |
822 if ( getStat() != STAT_POST_OK ) | |
823 { | |
824 Log_err( "Posting of %s failed: %s", msgId, client.lastStat ); | |
825 strcpy( errStr, client.lastStat ); | |
826 return FALSE; | |
827 } | |
828 Log_inf( "Posted %s (Status: %s)", msgId, client.lastStat ); | |
829 return TRUE; | |
830 } |