// Written by John Newbigin
// jn@it.swin.edu.au
// Copyright (c) 1999 John Newbigin
// Covered by the terms of the GPL.

#include "string.h"
#include "token.h"
#include "token2.h"
#include "tlist.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <wait.h>

class zone2
{
public:

	zone2()
	{
		file = 0;
		name = 0;
	}

	~zone2()
	{
		if(file)
		{
			file->decRefCount();
		}
		if(name)
		{
			name->decRefCount();
		}
	}

	void scanZoneFile(char *filename, HugeString *extra);

	void assignFile(char *f)
	{
		if(file)
		{
			file->decRefCount();
			file = 0;
		}
		file = new HugeString();
		file->cat(f);
	}

	void assignName(char *n)
	{
		if(name)
		{
			name->decRefCount();
			name = 0;
		}
		name = new HugeString();
		name->cat(n);
	}

private:
	HugeString *file;
	HugeString *name;

};

/*
	We have to scan through the file and find some options
		directory

	create a list of master zones
		zone name
		file name
		work out if it is a reverse file (we will use fwd for normal zones and rev for reverse zones)

	read in all the addresses from the master fwd zones
	create a list of rev entries
	write the rev entries to the appropriate files
	generate messages if modifications are required to the bind.conf file.
*/

void dnstouch(char *filename, HugeString *extra)
{
			zone2 *z = new zone2();
			z->scanZoneFile(filename, extra);
			delete z;
}

#ifdef STANDALONE
#include "version.h"

HugeString *extra = 0;

int readConfigFile(char *filename)
{
	int error = 0;
	TToken *t = new TToken(0);
	if(t->AssignFile(filename))
	{
		//read the stuff
		HugeString *s = new HugeString();

		int state = 0;

		while(state != 9)
		{
			int type = t->getToken(s);
			if(type == 0)
			{
				state = 9; // eof
			}
			else if(type != 2)
			{
				switch(state)
				{
					case 0:
						if(strcmp(s->pchar(), "process") == 0)
						{
							state = 1; // read process file name
						}
						else if(strcmp(s->pchar(), "ignore") == 0)
						{
							state = 2; // read an ignore zone
						}
						else if(strcmp(s->pchar(), "chroot") == 0)
						{
							state = 4; // read the chroot directory
						}
						else if(strcmp(s->pchar(), "extra") == 0)
						{
							state = 5; // read extra word characters
						}
						else
						{
							fprintf(stderr, "Unknown option on line %d: %s (state=%d)\n", t->getLineNo(), s->pchar(), state);
							state = 9;
							error = 1;
						}
						break;

					case 1:
						//printf("got config %s\n", s->pchar());
						state = 0;
						break;

					case 2:
						{
							//printf("got ignore %s\n", s->pchar());
							state = 0;
						}
						break;

					case 3:
						state = 0;
						break;

					case 4:
						{
							//printf("got chroot %s\n", s->pchar());
							state = 0;
						}
						break;
						
					case 5:
						{
							if(extra)
							{
								extra->decRefCount();
							}
							extra = new HugeString();
							extra->cat(s->pchar());
							//printf("got extra %s\n", s->pchar());
							state = 0;
						}
						break;
				}
			}
		}
		
		delete s;	
	}
	delete t;
	return error;
}


int main(int argc, char **argv)
{
	if(argc >= 2)
	{
		// load the extra chars...
		readConfigFile("/etc/ndu.conf");

		for(int i=1; i<argc; i++)
		{
			fprintf(stderr, "%s\n", argv[i]);
			dnstouch(argv[i], extra);
		}
	}
	else
	{
		fprintf(stderr, "dnstouch version %s\n", NDU_VERSION);
		fprintf(stderr, "Usage: dnstouch filename [filename...]\n");
	}
}
#endif

long getTodaySerial()
{
	time_t tt = time(0);
	tm *t = localtime(&tt);
	//printf("%04d%02d%02d00\n", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday);
	long r = t->tm_year + 1900;
	r *= 100;
	r += t->tm_mon + 1;
	r *= 100;
	r += t->tm_mday;
	r *= 100;
	//printf("%ld\n", r);
	return r;
}

void zone2::scanZoneFile(char *filename, HugeString *extra)
{
	TToken2 *t = new TToken2(extra);
	t->AssignFile(filename);

	HugeString *s     = new HugeString();

	// States:
	// 0 - nowhere
	// 1 - start of a record
	// 2 - address record
	// 3 - pointer record
	// 5 - bracket set

	// 9 - eof

	int state = 0;
	int done = 0;
	while(state != 9)
	{
		int type = t->getToken(s);
		if(type == 0)
		{
			state = 9;
		}
		if(type != 2)
		{
			switch(state)
			{
				case 0:
					if(strcmp(s->pchar(), "IN") == 0)
					{
						// skip this
					}
					else if(strcmp(s->pchar(), "SOA") == 0)
					{
						state = 2;
					}
					else
					{
						// ignore
					}
					break;

				case 2:
					// read the SOA
					if(strcmp(s->pchar(), "(") == 0)
					{
						state = 3;// the data
					}
					break;

				case 3:
					//printf("Serial number is %s\n", s->pchar());
					if(strlen(s->pchar()) == 10)
					{
						long serial = atol(s->pchar());
						long newSerial;
						//printf("%ld\n", serial);
						long revision = serial - getTodaySerial();
						if(revision >= 0 && revision < 99)
						{
							//printf("Incrementing revision\n");
							newSerial = serial + 1;
						}
						else if(revision == 99)
						{
							fprintf(stderr, "Incrementing past 99th revision\n");
							newSerial = serial + 1;
						}
						else if(revision > 99)
						{
							fprintf(stderr, "serial number is in the future\n");
							newSerial = serial + 1;
						}
						else
						{
							//printf("Setting new date, revision 0\n");
							newSerial = getTodaySerial();
						}
						//fprintf(stderr, "new serial number is %ld\n", newSerial);

						// one last sanity check.
						if(newSerial > serial)
						{

							// we now invoke ed, and send the command ",s/1999/2000/gwq"

							// create a pipe to send commands to the child
							int p[2];
							pipe(p);
						
							pid_t cpid = fork();
							if(cpid == 0)
							{
								close(0);
								close(1);
								close(2);
								dup2(p[0], 0);
	
								execlp("ed", "ed", filename, 0);
								exit(1); // we only reach here in case of an error!
							}
							FILE *cmd = fdopen(p[1], "w");
							fprintf(cmd, ",s/%ld/%ld/g\n", serial, newSerial);
							fprintf(cmd, "w\n");
							fprintf(cmd, "q\n");
							fflush(cmd);
							int status;
							waitpid(0, &status, 0);
							if(status != 0)
							{
								fprintf(stderr, "Error updating file\n");
							}
						}
						else
						{
							fprintf(stderr, "error! can't change serial number\n");
						}
						
						
						
					}
					else
					{
						fprintf(stderr, "Serial number %s is in the wrong format.\n Please set the serial number to %ld\n", s->pchar(), getTodaySerial());
					}
					done = 1;
					state = 9;
					break;

				case 5:

					if(strcmp(s->pchar(), ")") == 0)
					{
						state = 0;
					}
					break;
			}
		}
	}
	if(!done)
	{
		fprintf(stderr, "Serial number not found.  Is this a zone file?\nThis may also be caused if you use characters outsize [a..z][A..Z][-].\nSee INSTALL for more info\n");
	}

	delete s;
	delete t;
	
	return;
}

