/*
* $Id: suck_util.c,v 1.4 2002/06/06 14:46:32 conrads Exp $
*
* Various routines used by suck program
*/
#include <sys/types.h>
#include <glob.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <regex.h>
#include <stdlib.h>
#include <stdio.h>
#include <pwd.h>
#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;
}
syntax highlighted by Code2HTML, v. 0.9.1