/*
 * Copyright (c) 1999, by Sun Microsystems, Inc.
 * All rights reserved.
 *
 * putscript.c
 */

#pragma ident   "@(#)putscript.c 1.5     98/05/05 SMI"

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/stat.h>

/*
 * handle chunked encodings here
 * handle content-range header
 * i/p:
 *     - byte stream (stdin)
 *     - physical file path to be updated (PATHINFO)
 */

#define HS_OK		        (200)
#define HS_NO_CONTENT	        (204)
#define HS_CREATED	        (201)
#define HS_FORBIDDEN	        (403)
#define HS_NOT_FOUND	        (404)
#define HS_CONFLICT		(409)
#define HS_SERVER_ERROR	        (500)

#define BUF_LEN (8192)

int errno;

static
char* GetEnvironment(char *name, char * defaultValue)
{
    char * result;

    result = (char *) getenv(name);
    if (! result) result = defaultValue;
    return result;
}


static
int delete_file (char *path)
{
	int status;

				/* file doesnt exist */
	if (access(path, F_OK) != 0)
		return (HS_NOT_FOUND);

	status = remove (path);

	if (status == 0)
		return (HS_NO_CONTENT);
	else
		return (-1);
}


/* 
 * create_directory_path():
 * Create the directory that is to hold the file resource being put.
 * It's a best effort creation; failures will be caught when the caller
 * attempts to create file resource underneath.
 */
static void
create_directory_path(const char *filepath)
{
	char path_buf[PATH_MAX], *ptr;
	char command_buf[PATH_MAX + 10];

	strcpy(path_buf, filepath);
	ptr = strrchr(path_buf, '/');

	fprintf(stderr, "in create_directory\n" );
	if(ptr != NULL && ptr != path_buf) {

		/* get the directory path */
		*ptr = '\0';
		
		/* does the directory exist already ? */
		if(access(path_buf, F_OK) != 0) {

			/* create the directory */
			strcpy(command_buf, "mkdir -p ");
			strncat(command_buf, path_buf, PATH_MAX + 10);

			fprintf(stderr, "creating directory(%s)", command_buf);
			system(command_buf);
			
			/* setgid so that files created have the right group */
			chmod(path_buf, 02775);
		}
	}
	fprintf(stderr, "out create_directory\n" );
}


/*
 * Optional checks (eg. for Content-* headers) should go here
 */

static
int put_file (char *path)
{
	int fd;
	int status = HS_CREATED;
	char buf[8192];
	ssize_t len;

				/* if file exists, then modifying it */
	if (access(path, F_OK) == 0)
		status = HS_OK;
	else 
		create_directory_path(path);

	fd = open(path, O_WRONLY | O_CREAT | O_TRUNC,
		 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH );
	if (fd < 0) {
		perror("open:");
		return (-1);
	}
		/* read into a buffer */

	while ((len = read(0, buf, BUF_LEN)) > 0)
		write (fd, buf, len);

	close(fd);
	return (status);
}


/*
 * create a separate lock file per publish request.
 * If we do mandatory locking on the request file itself, the GET performance
 * shall be affected.
 */

static int
synchronize_method(char *lock_file, int *lockfd)
{

	/* creates the directory path, if necessary */
	create_directory_path(lock_file);

	*lockfd = open (lock_file, O_WRONLY | O_CREAT | O_EXCL, 2040);
	if (*lockfd < 0) { 
		if (errno == EAGAIN || errno == EEXIST)
			return (HS_CONFLICT);
		else 
			return (-1);
	}

	if (lockf(*lockfd, F_TLOCK,0) < 0) /* cant get the lock! */
		return (HS_CONFLICT);
	else
		return HS_OK;
}


/*
 * Do file locking? to synchronize. This is because, the virtual hosts are
 * non-unique across server instances.
 */

int
main()
{
	char *path;
	char *pathURI;
	char *method;
	int  status = HS_OK;
	int  lockfd;
	char *lock_file;
	
	/* make the files created group-writeable and world-readable */
	umask(002);

	pathURI = GetEnvironment("PATH_INFO", "/tmp/put.html");
	path = GetEnvironment("PATH_TRANSLATED", "UNKNOWN");
	lock_file = (char *) malloc(sizeof (char) * (strlen(path) + 1 + 5));
	sprintf(lock_file, "%s.lock", path);

	method = GetEnvironment("REQUEST_METHOD", "NONE");

	status = synchronize_method(lock_file, &lockfd);

	printf("Content-type: text/html\r\n");

	if (status == HS_OK) {
		if (strcmp(method, "PUT") == 0) 
			status = put_file(path);
		else if (strcmp(method, "DELETE") == 0)
			status = delete_file(path);
		else			/* INTERNAL ERROR */
			status = HS_SERVER_ERROR;
	}

	if (status == -1)
		status = HS_FORBIDDEN;

	printf("Status: %d\n", status);

	free(lock_file);
	if (status == HS_CREATED)
		printf("Location: %s\n", pathURI);
	printf("\r\n");
	close (lockfd);
 	remove(lock_file);
	return (0);
}

