/* * $Id: suck_util.c,v 1.4 2002/06/06 14:46:32 conrads Exp $ * * Various routines used by suck program */ #include #include #include #include #include #include #include #include #include #include #include #include #include "msuck.h" #include "nntp.h" extern SERVER server[]; extern ACTIVE_INFO active[]; extern CONNECTION local; extern FILTER filter[]; extern FILE *sucklog, *killlog, *errlog; extern int local_active_groups; extern char *errlogpath; extern char *killlogpath; extern char *sucklogpath; extern char *filterspath; extern char *newsrc_path_template; /* * get a socket and try connecting until we get a 200 message, return socket * on success, 0 on failure to connect, or -1 on hard error * * FILE pointers to input/output streams for socket are opened as well. Output * stream is set to line-buffered mode. * * Note: eats the server's greeting message */ int connect_to_server(const char *servername, CONNECTION *server) { struct hostent *serverinfo; struct sockaddr_in serversock; char linebuf[MAXLINE + 1]; if ((serverinfo = gethostbyname(servername)) == NULL) { herror("connect_to_server(): server lookup failed"); return -1; } bzero((void *)&serversock, sizeof(serversock)); serversock.sin_family = AF_INET; serversock.sin_port = htons(119); bcopy(serverinfo->h_addr, (char *)&serversock.sin_addr, serverinfo->h_length); for (;;) { if ((server->sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) { perror("connect_to_server(): couldn't obtain a new socket"); return -1; } if (connect(server->sockfd, (struct sockaddr *) & serversock, sizeof(serversock)) == 0) { if ((server->in = fdopen(server->sockfd, "r")) == NULL) { perror("connect_to_server(): error on fdopen for input stream"); close(server->sockfd); return -1; } if ((server->out = fdopen(server->sockfd, "w")) == NULL) { perror("connect_to_server(): error on fdopen for output stream"); fclose(server->in); return -1; } /* * set output stream line buffered so we don't have * to fflush() constantly */ if (setvbuf(server->out, NULL, _IOLBF, 0) == EOF) { perror("connect_to_server(): error changing buffering mode on output stream"); close_server(*server); return -1; } /* try reading the server greeting line */ if (fgets(linebuf, MAXLINE, server->in) == NULL) { perror("connect_to_server(): read error on socket in connect_to_server()"); close_server(*server); return -1; } /* should have gotten server greeting */ if (strncmp(linebuf, NNTP_POSTOK, 3) == 0) return server->sockfd; else { close_server(*server); sleep(30); } } else { perror("connect_to_server(): connection to server failed"); close(server->sockfd); return 0; } } } /* * close connection to server (abort, don't do "quit") */ void close_server(CONNECTION server) { fclose(server.in); fclose(server.out); } /* * quit a server the "correct" way */ void quit_server(CONNECTION server) { char linebuf[MAXLINE + 1]; /* * send quit command (we may still be in a sane state on the server) */ fprintf(server.out, "quit\r\n"); /* eat any server output, look for terminating string */ do { if (fgets(linebuf, MAXLINE, server.in) == NULL) { /* * ignore the cause of any error here, as we may have * been sent here *because* of an error already! */ break; } } while (strncmp(linebuf, NNTP_GOODBYE_ACK, 3) != 0); close_server(server); } /* * get active info for group from server, * assumes connection has already been opened * * returns 1 on success or 0 */ int get_active_info(CONNECTION server, const char *group, ARTNUM *active_lo, ARTNUM *active_hi) { char linebuf[MAXLINE + 1]; char grp[MAXGROUP + 1]; int responsecode, numarts; fprintf(server.out, "mode reader\r\n"); if (fgets(linebuf, MAXLINE, server.in) == NULL) { perror("get_active_info(): read error in get_active_info()"); return 0; } /* should have gotten second server greeting */ if (strncmp(linebuf, NNTP_POSTOK, 3) != 0) { return 0; } fprintf(server.out, "group %s\r\n", group); if (fgets(linebuf, MAXLINE, server.in) == NULL) { perror("get_active_info(): read error in get_active_info()"); return 0; } /* should have gotten group info */ if (strncmp(linebuf, NNTP_GROUPOK, 3) != 0) { return 0; } if (sscanf(linebuf, "%d %d %lu %lu %s", &responsecode, &numarts, active_lo, active_hi, grp) != 5) { perror("get_active_info(): read error in get_active_info()"); return 0; } return 1; } /* * Get list of active groups from local server * * Returns number of groups read or -1 on error */ int read_active(void) { char buf[MAXLINE + 1]; int numgroups, n; if (connect_to_server("localhost", &local) <= 0) { fprintf(stderr, "read_active(): can't get active groups from local server\n"); return -1; } fprintf(local.out, "list active\r\n"); if (fgets(buf, MAXLINE, local.in) == NULL) { perror("read_active(): read error on socket after LIST ACTIVE command"); close_server(local); return -1; } /* should have gotten "215" (list follows) reply */ if (strncmp(buf, NNTP_LIST_FOLLOWS, 3) != 0) { fprintf(stderr, "read_active(): unexpected reply to LIST ACTIVE command\n"); quit_server(local); return -1; } /* just read up to the terminating "." */ for (numgroups = 0; numgroups < MAX_LOCAL_GROUPS; ++numgroups) { if (fgets(buf, MAXLINE, local.in) == NULL) { perror("read_active(): read error on socket while fetching list data"); quit_server(local); return -1; } if (END_OF_OUTPUT(buf)) break; n = sscanf(buf, "%s ", (char *)&active[numgroups]); if (n != 1) { fprintf(stderr, "read_active(): read error while reading list data\n"); quit_server(local); return -1; } } quit_server(local); return numgroups; } /* * convert a newline-termintated numeric string in newsrc * to an article number type */ ARTNUM ptrtonum(char *ptr) { return (strtoul(ptr, NULL, 10)); } /* * convert a space-termintated string in newsrc * to a C string */ void ptrtostr(char *ptr, char *str) { while (*ptr != ' ') { *str++ = *ptr++; } *str = '\0'; } /* * update newsrc file entry for a group */ void update_newsrc(char *newsrc, ARTNUM artnum) { char num[MAXART + 1]; sprintf(num, "%010lu", artnum); memcpy(newsrc, num, MAXART); } /* * delete any temporary files left from an earlier run */ void cleanup(void) { glob_t globstuff; int i; if (glob("/tmp/article.*", 0, NULL, &globstuff) != 0) perror("cleanup(): glob error"); if (glob("/tmp/fetch.*", GLOB_APPEND, NULL, &globstuff) != 0) perror("cleanup(): glob error"); if (glob("/tmp/xover.*", GLOB_APPEND, NULL, &globstuff) != 0) perror("cleanup(): glob error"); if (globstuff.gl_pathc > 0) { for (i = 0; globstuff.gl_pathv[i] != NULL; ++i) { unlink(globstuff.gl_pathv[i]); } } } /* * check to see if a file exists * * returns 1 if file exists, else 0 */ int file_exists(const char *pathname) { FILE *f; if ((f = fopen(pathname, "r")) != NULL) { fclose(f); return 1; } else return 0; } /* * do the equivalent of touch(1) * * Returns -1 if file already exists, else 0 */ int touch(const char *pathname) { int fd; if ((fd = open(pathname, O_CREAT | O_RDONLY | O_EXCL, 0644)) == -1) { return -1; } else { close(fd); return 0; } } /* * open log files */ int open_logs(void) { if ((sucklog = fopen(sucklogpath, "a")) == NULL) { perror("open_logs(): can't open sucklog"); return -1; } if (setvbuf(sucklog, NULL, _IOLBF, 0) == EOF) { perror("open_logs(): error changing buffering mode on sucklog"); return -1; } if ((killlog = fopen(killlogpath, "a")) == NULL) { perror("open_logs(): can't open killlog"); return -1; } if (setvbuf(killlog, NULL, _IOLBF, 0) == EOF) { perror("open_logs(): error changing buffering mode on killlog"); return -1; } /* redirect error output to errlog */ if ((errlog = freopen(errlogpath, "a", stderr)) == NULL) { perror("open_logs(): can't open errlog"); return -1; } if (setvbuf(errlog, NULL, _IOLBF, 0) == EOF) { perror("open_logs(): error changing buffering mode on errlog"); return -1; } return 0; } /* * Parse global vars from user's config file * * returns number of servers on success, or -1 */ int read_config() { char cfg[MAXLINE + 1]; char home[PATH_MAX + 1]; char cfgpathname[PATH_MAX + 1]; struct passwd * uinfo; char * var, * val; FILE * fp; int numservers = 0; /* find the home dir of user running suck */ uinfo = getpwuid(getuid()); strncpy(home, uinfo->pw_dir, PATH_MAX); /* concatenate $HOME and filename with bounds checking */ snprintf(cfgpathname, PATH_MAX, "%s/%s", home, CONFIGFILENAME); if ((fp = fopen(cfgpathname, "r")) == NULL) { perror("read_config(): can't open configuration file"); return -1; } printf("Running as user ID %d (%s) using configuration file %s\n", uinfo->pw_uid, uinfo->pw_name, cfgpathname); while (fgets(cfg, MAXLINE, fp) != NULL) { if ((var = strtok(cfg, " \t\n")) == NULL) { continue; /* blank line is OK */ } if ((val = strtok(NULL, " \t\n")) == NULL) /* blank value is *not* * OK */ { fprintf(stderr, "read_config(): variable/server %s has NULL value!\n", var); return -1; } if (strcasecmp(var, "ERRLOG") == 0) { if ((errlogpath = malloc((strlen(val) + 1) * sizeof(char))) == NULL) { perror("read_config()"); exit(EXIT_FAILURE); } strcpy(errlogpath, val); continue; } if (strcasecmp(var, "KILLLOG") == 0) { if ((killlogpath = malloc((strlen(val) + 1) * sizeof(char))) == NULL) { perror("read_config()"); exit(EXIT_FAILURE); } strcpy(killlogpath, val); continue; } if (strcasecmp(var, "SUCKLOG") == 0) { if ((sucklogpath = malloc((strlen(val) + 1) * sizeof(char))) == NULL) { perror("read_config()"); exit(EXIT_FAILURE); } strcpy(sucklogpath, val); continue; } if (strcasecmp(var, "FILTERS") == 0) { if ((filterspath = malloc((strlen(val) + 1) * sizeof(char))) == NULL) { perror("read_config()"); exit(EXIT_FAILURE); } strcpy(filterspath, val); continue; } if (strcasecmp(var, "NEWSRC_PATH_TEMPLATE") == 0) { if ((newsrc_path_template = malloc((strlen(val) + 1) * sizeof(char))) == NULL) { perror("read_config()"); exit(EXIT_FAILURE); } strcpy(newsrc_path_template, val); continue; } /* * Anything else is supposed to be a server definition */ if (numservers < MAXNEWSRCS) { strncpy(server[numservers].hostname, var, MAXHOSTNAMELEN); server[numservers++].maxconns = atoi(val); } else fprintf(stderr, "suck: too many server definitions in config, ignoring\n"); } #ifdef DEBUG printf("errlog: %s\nkilllog: %s\nsucklog: %s\nfilters: %s\nnewsrc_path_template: %s\n", \ ERRLOG, KILLLOG, SUCKLOG, FILTERS, NEWSRC_PATH_TEMPLATE); #endif fclose(fp); return numservers; }