/* @(#)post_public.c 1.6 95/09/16 */

/*
 * Copyright (c) 1994, 1995 by Wayne C. Gramlich.  All rights reserved.
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * for any purpose is hereby granted without fee provided that the above
 * copyright notice and this permission are retained.  The author makes
 * no representations about the suitability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 */

/* LINTLIBRARY */

#include <sys/types.h>

#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <pwd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/systeminfo.h>
#include <unistd.h>

#include "html_extern.h"
#include "memory_extern.h"
#include "str_extern.h"
#include "text_extern.h"
#include "url_extern.h"

extern FILE *fdopen(int, const char *);
extern FILE *popen(const char *, const char *);

/*
 * main()
 *	This program will process an update form request.
 */
int
main(
    int argc,
    char **argv)
{
    struct sockaddr_in address;
    Str arg;
    Chr chr;
    DIR *dir;
    Str dir_name;
    struct dirent *entry;
    Str file_name;
    unsigned file_name_size;
    int fork_editor;
    Str full_file_name;
    Html html;
    Str home_dir;
    struct hostent *host_entry;
    Str host_name;
    FILE *in_file;
    unsigned index;
    Str latest_file_name;
    unsigned latest_timestamp;
    Chr line[2000];
    FILE *log_file;
    Str log_file_name;
    unsigned number;
    FILE *out_file;
    struct passwd *password;
    Str pattern;
    unsigned port;
    Str private_file_name;
    Str prefix;
    unsigned prefix_size;
    Str ptr;
    Str public_file_name;
    unsigned size;
    int sock;
    struct stat status;
    Str suffix;
    unsigned suffix_size;
    Text text;
    Url url;
    Str url_name;
    Str user_name;

    /* Lookup host name before option parsing: */
    assert(sysinfo(SI_HOSTNAME, (char *)line, sizeof(line)) < sizeof(line));
    host_name = str_copy(line);
    fork_editor = 0;
    pattern = (Str)"";

    /* Process command line arguments: */
    argc--;
    argv++;
    for (index = 0; index < argc; index++) {
	arg = (Str)argv[index];
	if (str_equal(arg, (Str)"-e")) {
	    fork_editor = 1;
	} else if (str_equal(arg, (Str)"-h")) {
	    index += 1;
	    if (index < argc) {
		host_name = (Str)argv[index];
	    } else {
		(void)fprintf(stderr, "Missing host name after `-h'!\n");
		return 1;
	    }
	} else if (str_equal(arg, (Str)"-p")) {
	    index += 1;
	    if (index < argc) {
		pattern = (Str)argv[index];
	    } else {
		(void)fprintf(stderr, "Missing pattern after `-p'!\n");
		return 1;
	    }
	} else {
	    (void)fprintf(stderr, "Unrecognized option `%s'!\n", arg);
	    return 1;
	}
    }

    /* Convert pattern: */
    size = str_size(pattern);
    for (index = 0; index < size; index++) {
	chr = pattern[index];
	if (chr == (Chr)' ') {
	    chr = (Chr)'_';
	}
	pattern[index] = chr;
    }

    /* Lookup up user and system name: */
    password = getpwuid(getuid());
    if (password == (struct passwd *)0) {
	(void)fprintf(stderr, "Could not find your password entry!\n");
	return 1;
    }
    user_name = str_copy((Str)password->pw_name);
    home_dir = str_copy((Str)password->pw_dir);

    /* Scan personal annotation directory for latest one: */
    dir_name = str_printf("%s/.mosaic-personal-annotations", home_dir);
    dir = opendir((char *)dir_name);
    if (dir == (DIR *)0) {
	(void)fprintf(stderr, "Could not find directory `%s'!\n", dir_name);
	return 1;
    }
    prefix = (Str)"PAN-";
    prefix_size = str_size(prefix);
    suffix = (Str)".html";
    suffix_size = str_size(suffix);
    latest_file_name = (Str)"";
    latest_timestamp = 0;
    while ((entry = readdir(dir)) != (struct dirent *)0) {
	file_name = (Str)entry->d_name;
	file_name_size = str_size(file_name);
	if ((file_name_size >= prefix_size) &&
	  (str_equal_max(file_name, prefix, prefix_size)) &&
	  (file_name_size >= suffix_size) &&
	  (str_equal(file_name + file_name_size - suffix_size, suffix))) {
	    full_file_name = str_printf("%s/%s", dir_name, file_name);
	    if (stat((char *)full_file_name, &status) == 0) {
		if (status.st_mtime > latest_timestamp) {
		    latest_file_name = str_copy((Str)file_name);
		    latest_timestamp = status.st_mtime;
		} else {
		    str_free(full_file_name);
		}
	    } else {
		(void)fprintf(stderr,
		  "Could not find file `%s'!\n", full_file_name);
	    }
	}
    }
    (void)closedir(dir);
    if (latest_timestamp == 0) {
	(void)fprintf(stderr,
	  "Could not find any personal annotations in `%s'!\n", dir_name);
	return 1;
    }

    /* Search through the LOG file to find the associated URL. */
    number = (unsigned)atoi((char *)(latest_file_name + prefix_size));
    log_file_name =
      str_printf("%s/.mosaic-personal-annotations/LOG", home_dir);
    log_file = fopen((char *)log_file_name, "r");
    if (log_file == (FILE *)0) {
	(void)fprintf(stderr,
	  "Could not open `%s' for reading!\n", log_file_name);
	return 1;
    }
    url_name = (Str)0;
    for (;;) {
	/* Read line: */
	if (fgets((char *)line, sizeof(line), log_file) == (char *)0) {
	    (void)fprintf(stderr,
	      "Could not find URL associated with private annotation %d!\n",
	      number);
	    return 1;
	}

	/* Format of line is "URL # # # ...", where # is annotaton number. */
	/* None of the header lines have a space in them. */
	ptr = line;
	while ((ptr = str_chr_search(ptr + 1, (Chr)' ')) != (Str)0) {
	    if (atoi((char *)ptr + 1) == number) {
		/* Got it! */
		ptr = str_chr_search(line, (Chr)' ');
		assert(ptr != (Str)0);
		*ptr = (Chr)'\0';
		if (str_equal_max(line, (Str)"file:", 5)) {
		    line[0] = (Chr)'h';
		    line[1] = (Chr)'t';
		    line[2] = (Chr)'t';
		    line[3] = (Chr)'p';
		}
		url_name = str_copy(line);
		goto log_done;
	    }
	}
    }
  log_done:
    (void)fclose(log_file);

    /* Slurp in the personal annotation. */
    private_file_name = str_printf("%s/%s", dir_name, latest_file_name);
    in_file = fopen((char *)private_file_name, "r");
    if (in_file == (FILE *)0) {
	(void)fprintf(stderr,
	  "Could not open `%s' for reading!\n", private_file_name);
	return 1;
    }
    text = text_read(in_file);
    (void)fclose(in_file);

    html = html_parse(text);

    /* For now, just spit it out to the annotations directory: */
    str_free(dir_name);
    dir_name = str_printf("%s/public_html/annotations", password->pw_dir);
    (void)mkdir((char *)dir_name, 0755);
    public_file_name = str_printf("%s/public_html/annotations/public_%u.html",
      home_dir, number);
    out_file = fopen((char *)public_file_name, "w");
    if (out_file == (FILE *)0) {
	(void)fprintf(stderr, "Could not open `%s' for writing!\n",
	  public_file_name);
	return 1;
    }

    if (!str_is_empty(pattern)) {
	url_name = str_printf("%s#%s", url_name, pattern);
    }

    /* Generate the public annotation. */
    html_write_public_annotation(html, out_file, url_name, latest_file_name);
    (void)fclose(out_file);

    /* See whether the user asked to fork an editor: */
    if (fork_editor) {
	Str command;
	Str display;
	Str editor;

	editor = (Str)getenv("EDITOR");
	display = (Str) getenv("DISPLAY");
	if (editor == (Str)0){
	    if (display == (Str)0) {
		editor = (Str)"ed";
	    } else {
		editor = (Str)"textedit";
	    }
	}
	(void)fprintf(stdout,
	  "Please save the file before exiting the editor!\n");
	(void)fflush(stdout);
	command = str_printf("%s %s", editor, public_file_name);
	if (system((char *)command) == -1) {
	    (void)fprintf(stderr,
	      "The following editor command `%s' failed!\n", command);
	    return 1;
	}
    }

    /* Delete private annotation and replace with a symbolic link: */
    if (unlink((char *)private_file_name) != 0) {
	(void)fprintf(stderr, "Could not delete `%s'!\n", private_file_name);
	return 1;
    }
    if (symlink((char *)public_file_name, (char *)private_file_name) != 0) {
	(void)fprintf(stderr,
	  "Could not install a symbolic link from `%s' to `%s'!\n",
	  private_file_name, public_file_name);
	return 1;
    }

    /* Now force an update to occur at the remote machine: */
    url = url_parse(url_name);

    /* Lookup the internet address: */
    host_entry = gethostbyname((char *)url_host_name(url));
    if (host_entry == (struct hostent *)0) {
	(void)fprintf(stderr, "Could not find internet address for '%s'!\n",
	  url_host_name(url));
	return 1;
    }
    memory_copy((void *)&address.sin_addr,
      (void *)host_entry->h_addr, host_entry->h_length);
    address.sin_family = AF_INET;
    port = url_port(url);
    if (port == 0) {
	port = 80;	/* HTTP protocol port number; won't ever change! */
    }
    address.sin_port = port;
    
    /* Open the connection: */
    sock = socket(PF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
	(void)fprintf(stderr, "Could not get a socket (errno=%d)!", errno); 
	return 1;
    }
    if (connect(sock, (struct sockaddr *)&address, sizeof address) < 0) {
	(void)fprintf(stderr, "Could not open connection (errno=%d)!", errno);
	return 1;
    }

    /* Set up input and output files: */
    in_file = fdopen(sock, "r");
    out_file = fdopen(sock, "w");
    if ((in_file == (FILE *)0) || (out_file == (FILE *)0)) {
	(void)fprintf(stderr, "fdopen failed!");
	return 1;
    }

    url_name = str_printf("http://%s/~%s/annotations/public_%d.html",
      host_name, user_name, number);
    (void)fprintf(out_file,
      "POST /cgi-bin/post_update HTTP/1.0\n"
      "Content-type: application/x-www-form-urlencoded\n"
      "Content-length: %u\n"
      "\n"
      "RefURL=%s\n",
      str_size(url_name) + str_size((Str)"RefURL="), url_name);
    (void)fflush(out_file);
    text = text_read(in_file);
    (void)fclose(out_file);
    (void)fclose(in_file);

    /* Copy the results out to /tmp: */
    file_name = (Str)"/tmp/publish_result.html";
    out_file = fopen((char *)file_name, "w");
    if (out_file == (FILE *)0) {
	(void)fprintf(stderr, "Could not open `%s' for writing!\n", file_name);
	return 1;
    }
    html = html_parse(text);
    html_write_remaining(html, out_file, 1);
    (void)fclose(out_file);

    /* Determine the process id of the Mosaic on the user's machine! */
    in_file = popen("ps -a | grep osaic | grep -v grep", "r");
    if (in_file == (FILE *)0) {
	(void)fprintf(stderr, "Could not open `%s' for writing!\n", file_name);
	return 1;
    }
    if (fgets((char *)line, sizeof(line), in_file) == (char *)0) {
	(void)fprintf(stderr, "No Moasic appears to be running!\n");
	return 1;
    }
    (void)fclose(in_file);
    number = atoi((char *)line);
    if (number == 0) {
	(void)fprintf(stderr,
	  "`%s' has an invalid process id number!\n", file_name);
	return 1;
    }

    /* Tell Mosaic to display the results file: */
    file_name = str_printf("/tmp/Mosaic.%u", number);
    out_file = fopen((char *)file_name, "w");
    if (out_file == (FILE *)0) {
	(void)fprintf(stderr, "Could not open `%s' for writing!\n", file_name);
	return 1;
    }
    (void)fprintf(out_file,
      "goto\n"
      "file://localhost/tmp/publish_result.html\n");
    (void)fclose(out_file);
    if (kill(number, SIGUSR1) != 0) {
	(void)fprintf(stderr,
	  "Unable to prod Mosaic into displaying /tmp/publish_result.html!\n");
	return 1;
    }

    return 0;
}


