#define PATH_PATH "/bin:/usr/bin:/usr/ucb"
#define PATH_TMP "/tmp"
#define PATH_SH "/bin/sh"
#define NEWS_OWNER "news"

/*
**  Open the privileged port, then exec dnews.
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#if	defined(NEED_TIME)
#include <time.h>
#endif
#include <sys/time.h>
#include <sys/resource.h>
#include <netdb.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

#include <dirent.h>
#include <pwd.h>
#include <signal.h>


#include "dnews.h"
#define BFSZ 1000

#ifndef FALSE 
#define FALSE 0
#endif
#ifndef TRUE 
#define TRUE (!FALSE)
#endif
/*
	dnews_start gid uid port 

*/
int fix_dir(char *path, int uid, int gid);
int fix_exe(char *config, char *fname);
int fix_prot(int uid, int gid);

#ifdef NEED_STRDUP
char *strdup(char *s);
#endif
char *getconfig(char *s);
int forground = 0;
char *zstrlwr(char *s);
int lib_detach(void) ;
static char dnews_conf[BFSZ];
int main(int argc, char *argv[])
{
	gid_t gid;
	uid_t uid;
	char *s;
        struct hostent *hp;
	char buff[5400],pflag[200];
	struct sockaddr_in server;
	int on,i,j;
	static char **args;
	static char **env;
	int nntp_port = 119;
	struct stat sb;
	static char ddir[200],dpath[200];
	char *xdnews_name;
	char *p;

	xdnews_name = dnews_name;
	for (i=0; i<argc; i++) {
		if (strcmp(argv[i],"-dmulti")==0) xdnews_name = "dmulti";
		if (strncmp(argv[i],"-conf",5)==0) {
			strcpy(dnews_conf,argv[++i]);
		}
		if (strcmp(argv[i],"-forground")==0) forground = 1;
	}

	for (i=0; i<argc; i++) printf("arg[%d] = %s\n",i,argv[i]);
	sprintf(ddir,"%s/%s",getconfig("config"),xdnews_name);
	sprintf(dpath,"%s",getconfig("spool"));
	printf("ddir = %s \n",ddir);
#ifdef NO_LOG_DAEMON
	openlog("dnews_start", LOG_PID);
#else
	openlog("dnews_start", LOG_DAEMON, LOG_DAEMON);
#endif
	if (stat(dpath, &sb) < 0 || !S_ISDIR(sb.st_mode)) {	
		perror("stat failed \n");
		printf("Couldn't get stat info on (%s), does it exist\n",dpath);
		syslog(LOG_ERR, "DNEWS cant stat %s ", dpath);
	   exit(1);
	}
	uid = sb.st_uid;
	gid = sb.st_gid;

	if (strlen(getconfig("fix_protection_disable"))==0) {
		fix_prot(uid,gid);
	}   


	/* Create a socket and name it. */
	if ((i = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		syslog(LOG_ERR,"DNEWS_start socket ");
	   perror("socket failed \n");
		exit(1);
	}
	on = 1;
	if (setsockopt(i, SOL_SOCKET, SO_REUSEADDR, (caddr_t)&on, sizeof on) < 0)
		syslog(LOG_ERR, "dnews cant setsockopt ");
	memset(&server, 0, sizeof server);
	s = getconfig("port"); if (strlen(s)!=0) nntp_port = atoi(s);
	server.sin_port = htons(nntp_port);
	server.sin_family = AF_INET;
	server.sin_addr.s_addr = htonl(INADDR_ANY);

       s = getconfig("bind_in");
       if (strlen(s)!=0) {
	 if (!isdigit(*s)) {
		server.sin_addr.s_addr = 0;
		printf("Looking up bind_in setting (%s)\n",s);
		hp = gethostbyname(s);
                if (hp==NULL) {
			printf("Could not find ip address of that name, aborting\n");
			abort();
                }
                memcpy(&(server.sin_addr),hp->h_addr,  hp->h_length);
                server.sin_family = hp->h_addrtype;
		printf("Found bind_in address (%s)\n",inet_ntoa(server.sin_addr));
	 } else {
		server.sin_addr.s_addr = inet_addr(s);
	 }
       }

	
	if (bind(i, (struct sockaddr *)&server, sizeof server) < 0) {
	   perror("bind failed \n");
		syslog(LOG_ERR, "DNEWS_start cant bind ");
	   exit(1);
	}
	sprintf(pflag, "-p%d", i);

	/* Build the new argument vector. */
	args = malloc(sizeof(char *)*(argc+5)); 
	env = malloc(sizeof(char *)*10); 
	j = 0;
	args[j++] = ddir;
   	args[j++] = pflag;
	for (i = 1; argv[i]; )
		args[j++] = argv[i++];
	args[j] = NULL;

	syslog(LOG_ERR, "NNTP NEWS Server Starting uid=%d gid=%d ",uid, gid);

	setgid(gid);
	if (getgid() != gid) syslog(LOG_ERR, "DNEWS cant setgid to %d ", gid);
	setuid(uid);
	if (getuid() != uid) syslog(LOG_ERR, "DNEWS cant setuid to %d ", uid);

	printf("Set uid=%d gid=%d based on %s \n",(int)uid,(int)gid,dpath);
	sprintf(buff, "PATH=%s", PATH_PATH);
	if (getenv("PATH")!=NULL) {
		strcat(buff,":");
		strcat(buff,getenv("PATH"));
	}
	printf("setenv (%s)\n",buff);
	env[0] = strdup(buff);
	sprintf(buff, "TMPDIR=%s", PATH_TMP);
	env[1] = strdup(buff);
	sprintf(buff, "SHELL=%s", PATH_SH);
	env[2] = strdup(buff);
	sprintf(buff, "LOGNAME=%s", NEWS_OWNER);
	env[3] = strdup(buff);
	sprintf(buff, "USER=%s", NEWS_OWNER);
	env[4] = strdup(buff);
	sprintf(buff, "HOME=%s", dpath);
	env[5] = strdup(buff);
	i = 6;
	if ((p = getenv("TZ")) != NULL) {
		sprintf(buff, "TZ=%s", p);
		env[i++] = strdup(buff);
	}
	if ((p = getenv("PGPPATH")) != NULL) {
		sprintf(buff, "PGPPATH=%s", p);
		env[i++] = strdup(buff);
	}
	env[i++] = NULL;

   printf("Exec {%s} \n",args[0]);
	if (!forground) {
		printf("Detaching dnews process...\n");
		lib_detach();
	}
   execve(args[0], args, env);
	syslog(LOG_ERR, "dnews_start cant exec %s ", args[0]);
   perror("Exec failed \n");
   exit(1);
}
char *getconfig(char *name)
{
   static char bf[500];
   char *s;
   char *fname = "/etc/dnews.conf";
   FILE *f;
   if (strlen(dnews_conf)>0) fname = dnews_conf;
   f = fopen(fname,"r");
   if (f==NULL) { perror(fname); return "dmy";}
   for (;!feof(f);)  {      
      fgets(bf,100,f);
      s = strtok(bf," \t\n");
      if (s!=NULL) {
	      zstrlwr(s);
	      if (strcmp(s,name)==0)  {
		 s = strtok(NULL," \t\n");
		 if (s!=NULL) {
			 fclose(f);
			 return s;
		 }
	      }
      }	
   }
   fclose(f);
   return "";
}

#ifdef NEED_STRDUP
char *strdup(char *s)
{
        char *t;
        t = malloc(strlen(s)+1);
        strcpy(t,s);
        return t;
}
#endif
#ifndef _PATH_DEVNULL
#define _PATH_DEVNULL "/dev/null"
#endif
int lib_detach(void) 
{
        int fd;
	int noclose=0;

        switch (fork()) {
        case -1:
		printf("Detach(), fork failed %s\n",strerror(errno));
                return (-1);
        case 0:
                break;
        default:
                _exit(0);
        }

        if (setsid() == -1) {
		printf("Detach(), child setsid failed %s\n",strerror(errno));
                return (-1);
	}

        if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
                (void)dup2(fd, STDIN_FILENO);
                (void)dup2(fd, STDOUT_FILENO);
                (void)dup2(fd, STDERR_FILENO);
                if (fd > 2)
                        (void)close (fd);
        }
        return (0);
}
char *zstrlwr(char *s)
{
	char *ss=s;
	for (;*s!=0;s++) *s = tolower(*s);
	return ss;
}
static int xuid,xgid;
int fix_prot(int uid, int gid)
{
	char bf[BFSZ];
	int i;
	char config[BFSZ];
	xuid = uid; xgid = gid;
	printf("Setting file ownerships just incase\n");
	
	fix_dir(getconfig("config"),uid,gid);
	fix_dir(getconfig("logdir"),uid,gid);
	fix_dir(getconfig("workarea"),uid,gid);
	fix_dir(getconfig("history"),uid,gid);
	fix_dir(getconfig("xover_dir"),uid,gid);
	fix_dir(getconfig("spool"),uid,gid);
	for (i=1; i<30; i++) {
		sprintf(bf,"spool%d",i);
		fix_dir(getconfig(bf),uid,gid);
	}
	strncpy(config,getconfig("config"),BFSZ-1); config[BFSZ-1]=0;
/*	fix_exe(config,"dnews");
	fix_exe(config,"dmulti");*/
	fix_exe(config,"dmgrsvc");
	fix_exe(config,"dnews_start");
	fix_exe(config,"dnews_start.sh");
	fix_exe(config,"dmulti_start.sh");
	sprintf(bf,"%.90s/tellnews.pass",getconfig("work"));
	chown(bf,uid,gid);
	chmod(bf,0600);
	sprintf(bf,"%.90s/users.dat",config);
	chown(bf,uid,gid);
	chmod(bf,0600);
	printf("Finished setting file ownerships\n");
}
int fix_exe(char *config, char *fname)
{
	char fullname[BFSZ];

	sprintf(fullname,"%s/%s",config,fname);
	if (chown(fullname,0,0)!=0) perror(fullname);
	if (chmod(fullname,0700)!=0) perror(fullname);	
	return TRUE;
}
int fix_dir(char *path, int uid, int gid)
{
        DIR *dirp;
	int n=0;
        struct dirent *direntp; 
	char bf[6000],bf2[6000],dname[6000],dadd[6000];
	char *s;

	if (strlen(path)==0) return TRUE;
	dirp = opendir(path); 
        if (dirp==NULL) {perror("Could not read directory file"); return 0;}
	
        while ((direntp=readdir(dirp))!=NULL) {
		if (strcmp(direntp->d_name,".")==0) continue;
             	if (strcmp(direntp->d_name,"..")==0) continue;
		sprintf(bf2,"%s/%s",path,direntp->d_name);
		chown(bf2,uid,gid);
        }
        closedir(dirp);
	return n;
}
