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