/* (C) Copyright 1993,1994 by Carnegie Mellon University
 * All Rights Reserved.
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation for any purpose is hereby granted without
 * fee, provided that the above copyright notice appear in all copies
 * and that both that copyright notice and this permission notice
 * appear in supporting documentation, and that the name of Carnegie
 * Mellon University not be used in advertising or publicity
 * pertaining to distribution of the software without specific,
 * written prior permission.  Carnegie Mellon University makes no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied
 * warranty.
 *
 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <types.h>
#include <netdb.h>
#include <fcntl.h>
#include <stat.h>
#include <time.h>
#include <unistd.h>

#include "xmalloc.h"
#include "common.h"
#include "part.h"
#include "proto.h"

#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
#endif


int overwrite_files = 0;
int didchat;

/* The name of the file we're writing */
static char *output_fname = 0;

/* Characters that shouldn't be in filenames */
#define BADCHARS "!$&*()|\'\";<>[]{}?/`\\ \t"

/* Generate a message-id */
char *os_genid(void)
{
    static int pid = 0;
    static time_t curtime;
    static char hostname[MAXHOSTNAMELEN+1];
    char *result;
    struct hostent *hp;
    

    if (pid == 0) {
	pid = getpid();
	time(&curtime);
	gethostname(hostname, sizeof(hostname));

	/* If we don't have a FQDN, try canonicalizing with gethostbyname */
	if (!strchr(hostname, '.')) {
	    hp = gethostbyname(hostname);
	    if (hp) {
		strcpy(hostname, hp->h_name);
	    }
	}
    }

    result = malloc(25+strlen(hostname));
    sprintf(result, "%d.%lu@%s", pid, (unsigned long) curtime++, hostname);
    return result;
}

/* Create and return directory for a message-id */
char *os_idtodir(char *id)
{
    static char buf[4096];
    char *p;

    if (getenv("TMPDIR")) {
	strcpy(buf, getenv("TMPDIR"));
    }
    else {
	strcpy(buf, "/sys$scratch");
    }
    strcat(buf, "/m-prts-");
    p = getenv("USER");
    if (!p) p = getenv("LOGNAME");
    if (!p) p = "x";
    strcat(buf, p);
    
    (void)mkdir(buf, 0700);

    p = buf + strlen(buf);
    *p++ = '/';
    while (*id && p < buf+sizeof(buf)-10 ) {
	if (isprint(*id) && !strchr(BADCHARS, *id)) *p++ = *id;
	id++;
    }
    *p = '\0';
    if (mkdir(buf, 0700) == -1 && errno != EEXIST) {
	perror(buf);
	return 0;
    }
    *p++ = '/';
    *p = '\0';
    return buf;
}

/*
 * We are done with the directory returned by os_idtodir()
 * Remove it
 */
void os_donewithdir(char *dir)
{
    char *p;

    /* Remove trailing slash */
    p = dir + strlen(dir) - 1;
    *p = '\0';

    rmdir(dir);
}

FILE *os_createnewfile(char *fname)
{
    int fd;
    FILE *ret;

#ifdef O_EXCL
    struct stat statbuf;

    if ((stat(fname, &statbuf) == 0) && (S_ISCHR(statbuf.st_mode))) {
       fd=open(fname, O_RDWR);
    }
    else {
       fd=open(fname, O_RDWR|O_CREAT|O_EXCL, 0644);
    }
#else
    fd=open(fname, O_RDWR|O_CREAT|O_TRUNC, 0644);
#endif

    if (fd == -1)
        return NULL;
     
    ret=fdopen(fd, "w");

    return ret;
}

     
/*
 * Create a new file, with suggested filename "fname".
 * "fname" may have come from an insecure source, so clean it up first.
 * It may also be null.
 * "contentType" is passed in for use by systems that have typed filesystems.
 * "flags" contains a bit pattern describing attributes of the new file.
 */
FILE *os_newtypedfile(char *fname, char *contentType, int flags, params contentParams)
{
    char *p;
    static int filesuffix=0;
    char buf[128], *descfname=0;
    FILE *outfile = 0;

    if (fname == NULL) fname = "";

    /* If absolute path name, chop to tail */
    if (*fname == '/') {
	p = strrchr(fname, '/');
	fname = p+1;
    }

    /* Get rid of leading ~ or ~/ */
    while (*fname == '~' || *fname == '/') fname++;

    while (!strncmp(fname, "../", 3)) fname += 3;
    
    /* Clean out bad characters, create directories along path */
    for (p=fname; *p; p++) {
	if (*p == '/') {
	    if (!strncmp(p, "/../", 4)) {
		p[1] = p[2] = 'X';
	    }
	    *p = '\0';
	    (void) mkdir(fname, 0777);
	    *p = '/';
	}
	else if (!isprint(*p) || strchr(BADCHARS, *p)) *p = '_';
    }

    if (!fname[0]) {
	do {
	    if (outfile) fclose(outfile);
	    sprintf(buf, "part%d", ++filesuffix);
	} while (outfile = fopen(buf, "r"));
	fname = buf;
    }
    else if (!overwrite_files && (outfile = fopen(fname, "r"))) {
	do {
	    fclose(outfile);
	    sprintf(buf, "%s.%d", fname, ++filesuffix);
	 
	} while (outfile = fopen(buf, "r"));
	fname = buf;
    }

    if (overwrite_files)
         outfile = fopen(fname, "w");
    else
         outfile = os_createnewfile(fname);
    
    if (!outfile) {
	perror(fname);
    }

    if (output_fname) free(output_fname);
    output_fname = strsave(fname);

    if (strlen(fname) > sizeof(buf)-6) {
	descfname = xmalloc(strlen(fname)+6);
    }
    else {
	descfname = buf;
    }
    strcpy(descfname, fname);

    p = strchr(descfname, '/');
    if (!p) p = descfname;
    if (p = strrchr(p, '.')) *p = '\0';

    strcat(descfname, ".desc");
    (void) rename(TEMPFILENAME, descfname);
    if (descfname != buf) free(descfname);
    
    fprintf(stdout, "%s (%s)\n", output_fname, contentType);
    didchat = 1;

    return outfile;
}

/*
 * Close a file opened by os_newTypedFile()
 */
void os_closetypedfile(FILE *outfile)
{
    fclose(outfile);
}

/*
 * (Don't) Handle a BinHex'ed file
 */
int
os_binhex(struct part *inpart, int part, int nparts)
{
    return 1;
}

/*
 * Warn user that the MD5 digest of the last file created by os_newtypedfile()
 * did not match that supplied in the Content-MD5: header.
 */
void os_warnMD5mismatch(void)
{
    char *warning;

    warning = xmalloc(strlen(output_fname) + 100);
    sprintf(warning, "%s was corrupted in transit",
	    output_fname);
    warn(warning);
    free(warning);
}

/*
 * Report an error (in errno) concerning a filename
 */
void os_perror(char *file)
{
    perror(file);
}

/*--------------------------------------------------------------------*/

/* Global storage. */

/*    Flag to sense if vms_init() was called. */

int vms_init_done = -1;

/*--------------------------------------------------------------------*/

#if !defined( __VAX) && (__CRTL_VER >= 70301000)

#include <unixlib.h>

/* vms_init()

      Uses LIB$INITIALIZE to set a collection of C RTL features without
      requiring the user to define the corresponding logical names.
*/

/* Structure to hold a DECC$* feature name and its desired value. */

typedef struct
   {
   char *name;
   int value;
   } decc_feat_t;

/* Array of DECC$* feature names and their desired values. */

decc_feat_t decc_feat_array[] = {
   /* Preserve command-line case with SET PROCESS/PARSE_STYLE=EXTENDED */
 { "DECC$ARGV_PARSE_STYLE", 1 },
   /* Preserve case for file names on ODS5 disks. */
 { "DECC$EFS_CASE_PRESERVE", 1 },
   /* Enable multiple dots (and most characters) in ODS5 file names,
      while preserving VMS-ness of ";version". */
 { "DECC$EFS_CHARSET", 1 },
   /* List terminator. */
 { (char *)NULL, 0 } };

/* LIB$INITIALIZE initialization function. */

static void vms_init( void)
{
int feat_index;
int feat_value;
int feat_value_max;
int feat_value_min;
int i;
int sts;

/* Set the global flag to indicate that LIB$INITIALIZE worked. */

vms_init_done = 1;

/* Loop through all items in the decc_feat_array[]. */

for (i = 0; decc_feat_array[ i].name != NULL; i++)
   {
   /* Get the feature index. */
   feat_index = decc$feature_get_index( decc_feat_array[ i].name);
   if (feat_index >= 0)
      {
      /* Valid item.  Collect its properties. */
      feat_value = decc$feature_get_value( feat_index, 1);
      feat_value_min = decc$feature_get_value( feat_index, 2);
      feat_value_max = decc$feature_get_value( feat_index, 3);

      if ((decc_feat_array[ i].value >= feat_value_min) &&
       (decc_feat_array[ i].value <= feat_value_max))
         {
         /* Valid value.  Set it if necessary. */
         if (feat_value != decc_feat_array[ i].value)
            {
            sts = decc$feature_set_value( feat_index,
             1,
             decc_feat_array[ i].value);
            }
         }
      else
         {
         /* Invalid DECC feature value. */
         printf( " INVALID DECC FEATURE VALUE, %d: %d <= %s <= %d.\n",
          feat_value,
          feat_value_min, decc_feat_array[ i].name, feat_value_max);
         }
      }
   else
      {
      /* Invalid DECC feature name. */
      printf( " UNKNOWN DECC FEATURE: %s.\n", decc_feat_array[ i].name);
      }
   }
}

/* Get "vms_init()" into a valid, loaded LIB$INITIALIZE PSECT. */

#pragma nostandard

/* Establish the LIB$INITIALIZE PSECTs, with proper alignment and
   other attributes.  Note that "nopic" is significant only on VAX.
*/
#pragma extern_model save

#pragma extern_model strict_refdef "LIB$INITIALIZE" 2, nopic, nowrt
void (*const x_vms_init)() = vms_init;

#pragma extern_model strict_refdef "LIB$INITIALIZ" 2, nopic, nowrt
const int spare[ 8] = { 0 };

#pragma extern_model restore

/* Fake reference to ensure loading the LIB$INITIALIZE PSECT. */

#pragma extern_model save
int lib$initialize(void);
#pragma extern_model strict_refdef
int dmy_lib$initialize = (int) lib$initialize;
#pragma extern_model restore

#pragma standard

#endif /* !defined( __VAX) && (__CRTL_VER >= 70301000) */

