1 #include <config.h>
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <ctype.h>
7
8 #ifdef HAVE_UNISTD_H
9 #include <unistd.h>
10 #endif
11
12 #ifdef HAVE_REGEX_H
13 #include <regex.h>
14 #endif
15
16 #ifdef DMALLOC
17 #include <dmalloc.h>
18 #endif
19
20 #include "suck_config.h"
21 #include "both.h"
22 #include "suck.h"
23 #include "phrases.h"
24 #include "timer.h"
25 #include "suckutils.h"
26 #include "active.h"
27
28 typedef struct Dactive {
29 char *group;
30 int high;
31 int done; /* have we gotten the msgids ? */
32 int postyn;
33 struct Dactive *next;
34 } Active, *Pactive;
35
36 typedef struct Dignore {
37 char *group;
38 struct Dignore *next;
39 #ifdef HAVE_REGEX_H
40 regex_t match;
41 int use_regex;
42 #endif
43 } Ignore, *Pignore;
44
45 /* local function prototypes */
46 int add_to_list(Pactive *, const char *, Pignore, int);
47 int get_msgids(PMaster, Pactive);
48 Pignore read_ignore(PMaster);
49 void do_free(Pactive, Pignore);
50 Pactive nntp_active(PMaster, Pignore);
51 Pactive read_active(PMaster, Pignore);
52
53 /*-------------------------------------------------------------------------*/
get_message_index_active(PMaster master)54 int get_message_index_active(PMaster master) {
55
56 int retval = RETVAL_OK;
57 Pactive listhead = NULL;
58 Pignore ignorehead = NULL;
59
60 TimerFunc(TIMER_START, 0, NULL);
61
62 /* first get our groups to ignore */
63 ignorehead = read_ignore(master);
64
65 /* Now try to get the active list */
66 if(master->do_active == TRUE) {
67 listhead = nntp_active(master, ignorehead);
68 }
69 if(listhead == NULL && master->activefile != NULL) {
70 /* can we get active list from local file */
71 listhead = read_active(master, ignorehead);
72 }
73 /* if it takes a while to read the active the other end */
74 /* might have timed out, hence this option */
75 if(master->conn_active == TRUE) {
76 retval = do_connect(master, CONNECT_AGAIN);
77 }
78 /* now get the MsgIds */
79 if(retval == RETVAL_OK && listhead != NULL) {
80 retval = get_msgids(master, listhead);
81 do_free(listhead, ignorehead);
82 }
83 else {
84 retval = RETVAL_ERROR;
85 }
86 TimerFunc(TIMER_TIMEONLY, 0, master->msgs);
87
88 if(retval == RETVAL_ERROR){
89 retval = get_message_index(master);
90 }
91 else {
92 print_phrases(master->msgs, active_phrases[5], str_int(master->nritems), NULL);
93 }
94
95 return retval;
96 }
97 /*------------------------------------------------------------------------*/
nntp_active(PMaster master,Pignore ignorehead)98 Pactive nntp_active(PMaster master, Pignore ignorehead) {
99
100 int localfd, nr;
101 char *resp;
102 Pactive listhead = NULL;
103 int retval = RETVAL_OK;
104
105 /* get active file from nntp host */
106 if((localfd = connect_local(master)) < 0 ) {
107 error_log(ERRLOG_REPORT, active_phrases[0], NULL);
108 }
109 else {
110 /* now get the list of groups */
111 print_phrases(master->msgs, active_phrases[2], master->localhost, NULL);
112
113 if(master->debug == TRUE) {
114 do_debug("Sending command: LIST\n");
115 }
116 sputline(localfd, "LIST\r\n", master->local_ssl, master->local_ssl_struct);
117 if(sgetline(localfd, &resp, master->local_ssl, master->local_ssl_struct) >= 0) {
118 if(master->debug == TRUE) {
119 do_debug("got answer: %s", resp);
120 }
121 number(resp, &nr);
122 if(nr == 215) { /* now we can get the list */
123 do {
124 if(sgetline(localfd, &resp, master->local_ssl, master->local_ssl_struct) > 0) {
125 if(master->debug == TRUE) {
126 do_debug("Got groupline: %s", resp);
127 }
128 if(resp[0] != '.' ) {
129 retval = add_to_list(&listhead, resp, ignorehead,master->debug);
130 }
131 }
132 else {
133 retval = RETVAL_ERROR;
134 }
135 }
136 while(resp[0] != '.' && retval == RETVAL_OK);
137 if(retval != RETVAL_OK) {
138 do_free(listhead, NULL);
139 listhead = NULL;
140 }
141 }
142 }
143 disconnect_from_nntphost(localfd, master->local_ssl, &master->ssl_struct);
144 }
145 return listhead;
146 }
147 /*------------------------------------------------------------------------*/
read_active(PMaster master,Pignore ignorehead)148 Pactive read_active(PMaster master, Pignore ignorehead) {
149
150 /* read active list from file */
151 Pactive listhead = NULL;
152 FILE *fpi;
153 char linein[MAXLINLEN];
154 int retval = RETVAL_OK;
155
156 if(master->debug == TRUE) {
157 do_debug("Opening Active file: %s\n", master->activefile);
158 }
159 if((fpi = fopen(master->activefile, "r")) == NULL) {
160 error_log(ERRLOG_REPORT, active_phrases[11], master->activefile, NULL);
161 }
162 else {
163 while(fgets(linein, MAXLINLEN, fpi) != NULL && retval == RETVAL_OK) {
164 if(master->debug == TRUE) {
165 do_debug("Got line: %s", linein);
166 }
167
168 retval = add_to_list(&listhead, linein, ignorehead, master->debug);
169 }
170 fclose(fpi);
171 if(retval != RETVAL_OK) {
172 do_free(listhead, NULL);
173 listhead = NULL;
174 }
175 }
176 return listhead;
177
178 }
179 /*------------------------------------------------------------------------*/
connect_local(PMaster master)180 int connect_local(PMaster master) {
181
182 /* connect to localhost NNTP server */
183 int fd;
184 char *inbuf;
185 unsigned int port;
186
187 port = (master->local_ssl == TRUE) ? LOCAL_SSL_PORT : LOCAL_PORT;
188 if(master->debug == TRUE) {
189 do_debug("Connecting to %s on port %d\n", master->localhost, port);
190 }
191
192 if((fd = connect_to_nntphost(master->localhost, NULL, 0, NULL, port, master->local_ssl, &master->local_ssl_struct)) >= 0) {
193 /* get the announcement line */
194 if(sgetline(fd, &inbuf, master->local_ssl, master->local_ssl_struct) < 0) {
195 close(fd);
196 fd = -1;
197 }
198 else if(master->debug == TRUE) {
199 do_debug("Got: %s", inbuf);
200 }
201 }
202 return fd;
203 }
204 /*----------------------------------------------------------------------------*/
add_to_list(Pactive * head,const char * groupline,Pignore ignorehead,int debug)205 int add_to_list(Pactive *head, const char *groupline, Pignore ignorehead, int debug) {
206
207 /* add one group to group list */
208 Pactive temp, tptr;
209 Pignore pignore;
210 int len, retval= RETVAL_OK;
211 char postyn;
212
213 #ifdef HAVE_REGEX_H
214 int reg_match;
215 #endif
216
217 len = 0;
218 /* get length of group name */
219 while(groupline[len] != ' ' && groupline[len] != '\0') {
220 len++;
221 }
222 if((temp = malloc(sizeof(Active))) == NULL) {
223 error_log(ERRLOG_REPORT, active_phrases[1], NULL);
224 retval = RETVAL_ERROR;
225 }
226 else if((temp->group = malloc(len+1)) == NULL) {
227 error_log(ERRLOG_REPORT, active_phrases[1], NULL);
228 retval = RETVAL_ERROR;
229 }
230 else {
231 /* now initialize the sucker and add it to the list*/
232 temp->next = NULL;
233 temp->high = 0;
234 temp->done = FALSE;
235 strncpy(temp->group, groupline, len);
236 temp->group[len] = '\0'; /* NULL terminate it */
237
238 sscanf(groupline, "%*s%*ld%*ld%c", &postyn);
239 temp->postyn = (postyn == 'n') ? FALSE : TRUE;
240
241 /* now check to see if we ignore this group, if so, don't add to list */
242 /* we have to wait until here, because only now do we have the group */
243 /* name in a separate field we can compare to the ignore list */
244 pignore = ignorehead;
245 #ifndef HAVE_REGEX_H
246 while(pignore != NULL && strcmp(pignore->group, temp->group) != 0) {
247 pignore = pignore->next;
248 }
249 #else
250 reg_match = FALSE;
251
252 while(pignore != NULL && reg_match == FALSE) {
253 if(pignore->use_regex == FALSE) {
254 if(strcmp(pignore->group, temp->group) == 0) {
255 reg_match = TRUE;
256 }
257 else {
258 pignore = pignore->next;
259 }
260 }
261 else {
262 if(regexec(&(pignore->match),temp->group, 0, NULL, 0) == 0) {
263 reg_match = TRUE;
264 }
265 else {
266 pignore = pignore->next;
267 }
268 }
269 }
270 #endif
271 if(debug == TRUE ) {
272 if(pignore == NULL) {
273 do_debug("Adding to active list - %s\n",temp->group);
274 }
275 else {
276 do_debug("Ignoring Group %s - match on %s\n",temp->group, pignore->group);
277 }
278 }
279 if(pignore == NULL) {
280 /* we didn't match, add to list */
281 if(*head == NULL) {
282 /* head node */
283 *head = temp;
284 }
285 else {
286 /* find end of list */
287 tptr = *head;
288 while(tptr->next != NULL) {
289 tptr = tptr->next;
290 }
291 tptr->next = temp;
292 }
293 }
294
295 }
296 return retval;
297 }
298 /*---------------------------------------------------------------------------------*/
do_free(Pactive head,Pignore ihead)299 void do_free(Pactive head, Pignore ihead) {
300
301 /* free both linked lists and any alloced strings */
302
303 Pactive temp;
304 Pignore itemp;
305
306 while(head != NULL) {
307 if(head->group != NULL) {
308 free(head->group);
309 }
310
311 temp=head->next;
312 free(head);
313 head = temp;
314 }
315 while(ihead != NULL) {
316 if(ihead->group != NULL) {
317 free(ihead->group);
318 }
319 itemp = ihead->next;
320 free(ihead);
321 ihead = itemp;
322 }
323
324 }
325 /*-------------------------------------------------------------------------------*/
get_msgids(PMaster master,Pactive head)326 int get_msgids(PMaster master, Pactive head) {
327
328 /* read in the sucknewsrc, check to see if group is in active list */
329 /* then download msgids and write it out to the new.sucknewsrc */
330
331 FILE *oldrc, *newrc;
332 int retval = RETVAL_OK, nrread, maxread;
333 long lastread;
334 char buf[MAXLINLEN+1], group[512], *ptr;
335 Pactive plist;
336
337 oldrc = newrc = NULL;
338
339
340 if((newrc = fopen(full_path(FP_GET, FP_TMPDIR, N_NEWRC), "w" )) == NULL) {
341 MyPerror(full_path(FP_GET, FP_TMPDIR, N_NEWRC));
342 retval = RETVAL_ERROR;
343 }
344
345 if((oldrc = fopen(full_path(FP_GET, FP_DATADIR, N_OLDRC), "r" )) == NULL) {
346 /* this isn't actually an error, since we can create it */
347 print_phrases(master->msgs, active_phrases[6], NULL);
348 }
349 else {
350 print_phrases(master->msgs, active_phrases[9], NULL);
351
352 while(retval == RETVAL_OK && fgets(buf, MAXLINLEN-1, oldrc) != NULL) {
353 ptr = buf;
354 if(*ptr == SUCKNEWSRC_COMMENT_CHAR) {
355 /* skip any white space before newsgroup name */
356 while(! isalpha(*ptr)) {
357 ptr++;
358 }
359
360 }
361 maxread = -1; /* just in case */
362 nrread = sscanf(ptr, "%s %ld %d\n", group, &lastread, &maxread);
363 if(nrread < 2 || nrread > 3) {
364 /* totally ignore any bogus lines */
365 print_phrases(master->msgs, active_phrases[3], buf, NULL);
366 }
367 else {
368 /* now find if group is still in active or not */
369 plist = head;
370 while( plist != NULL && strcmp(plist->group, group) != 0) {
371 plist = plist->next;
372 }
373 if(plist == NULL) {
374 print_phrases(master->msgs, active_phrases[4], buf, NULL);
375 }
376 else {
377 /* valid group, lets get em */
378 if(plist->postyn == FALSE) {
379 /* we can't post, comment the line out */
380 fprintf(newrc, "# %s %ld", group, lastread);
381 if(maxread >= 0) {
382 fprintf(newrc, " %d", maxread);
383 }
384 fputc('\n', newrc);
385 }
386 else if(maxread == 0) {
387 /* just rewrite the line */
388 fprintf(newrc,"%s %ld %d\n", group, lastread, maxread);
389 }
390 else {
391 retval = do_one_group(master, buf, group, newrc, lastread, maxread);
392 plist->done = TRUE;
393 }
394 }
395 }
396 }
397
398 /* this is in case we had to abort the above while loop (due to loss of pipe to server) */
399 /* and we hadn't finished writing out the suck.newrc, this finishes it up. */
400 if(retval != RETVAL_OK) {
401 do {
402 fputs(buf, newrc);
403 }
404 while(fgets(buf, MAXLINLEN-1, oldrc) != NULL);
405 }
406
407 fclose(oldrc);
408 }
409
410 if(retval == RETVAL_OK) {
411 /* okay add any new groups from active that weren't already in sucknewsrc */
412 plist = head;
413 if(plist != NULL) {
414 print_phrases(master->msgs, active_phrases[8], NULL);
415 }
416
417 while( plist != NULL) {
418 if(plist->done == FALSE) {
419 /* create a line for newsrc file */
420 lastread = master->active_lastread;
421 maxread = 0;
422
423 sprintf(buf,"%s %ld\n", plist->group, lastread);
424
425 print_phrases(master->msgs, active_phrases[10], plist->group, NULL);
426
427 if(plist->postyn == FALSE) {
428 /* we can't post, comment the line out */
429 fprintf(newrc, "# %s", buf);
430 }
431 else {
432 retval = do_one_group(master, buf, plist->group, newrc, lastread, maxread);
433 plist->done = TRUE;
434 }
435 }
436 plist = plist->next;
437 }
438 }
439 if(newrc != NULL) {
440 fclose(newrc);
441 }
442
443 return retval;
444 }
445 /*--------------------------------------------------------------------------------------------------*/
read_ignore(PMaster master)446 Pignore read_ignore(PMaster master) {
447
448
449 /* read in ignore file, and build list of groups to ignore */
450 FILE *fpi;
451 Pignore head, temp, last;
452 char buf[MAXLINLEN+1], *ptr;
453 int errflag = FALSE;
454
455 #ifdef HAVE_REGEX_H
456 int err;
457 char errmsg[256];
458 int i;
459 #endif
460
461 head = last = NULL;
462
463 /* first check if one with postfix is present, if not, use non postfix version */
464 fpi = fopen(full_path(FP_GET, FP_DATADIR, N_ACTIVE_IGNORE), "r");
465 if(fpi == NULL) {
466 fpi = fopen(full_path(FP_GET_NOPOSTFIX, FP_DATADIR, N_ACTIVE_IGNORE), "r");
467 }
468
469 if(fpi != NULL) {
470 while(fgets(buf, MAXLINLEN, fpi) != NULL) {
471 /* strip off any trailing spaces from group name */
472 ptr = buf;
473 while(!isspace(*ptr)) {
474 ptr++;
475 }
476 *ptr = '\0';
477
478 if((temp = malloc(sizeof(Ignore))) == NULL) {
479 error_log(ERRLOG_REPORT, active_phrases[7], NULL);
480 errflag = TRUE;
481 }
482 else if((temp->group = malloc(strlen(buf)+1)) == NULL) {
483 error_log(ERRLOG_REPORT, active_phrases[7], NULL);
484 errflag = TRUE;
485 }
486 else {
487 if(master->debug == TRUE) {
488 do_debug("Ignoring group %s\n", buf);
489 }
490
491 /* add to list */
492 strcpy(temp->group, buf);
493 temp->next = NULL;
494 if(head == NULL) {
495 head = temp;
496 }
497 else {
498 last->next = temp;
499 }
500 last = temp;
501 #ifdef HAVE_REGEX_H
502 /* first add ^ and $ so that we don't match */
503 /* partials unless they have wild cards */
504 if(buf[0] != '^') {
505 buf[0] = '^';
506 strcpy(&buf[1], temp->group);
507 }
508 else {
509 strcpy(buf,temp->group);
510 }
511 i = strlen(buf);
512 if(buf[i-1] != '$') {
513 buf[i] = '$';
514 buf[i+1] = '\0';
515 }
516
517 /* now regcomp it for later comparision */
518 temp->use_regex = TRUE;
519 if((err = regcomp(&(temp->match), buf, REG_NOSUB | REG_ICASE | REG_EXTENDED)) != 0)
520 {
521 regerror(err, &(temp->match), errmsg, sizeof(errmsg));
522 error_log(ERRLOG_REPORT, active_phrases[12], buf, errmsg, NULL);
523 temp->use_regex = FALSE;
524 }
525
526 #endif
527 }
528 }
529 fclose(fpi);
530 }
531 if(errflag == TRUE) {
532 while(head != NULL) {
533 if(head->group != NULL) {
534 free(head->group);
535 }
536 temp = head->next;
537 free(head);
538 head = temp;
539 }
540 }
541 return head;
542 }
543
544