/*
 * url_translate.c --- do URL transmogrifications using regular
 * expresson matching and replacing.
 *
 * Written by Theodore Ts'o, 1995.
 */

#define MAX_URL_LEN 2048

#define DEBUG
#define LOCKER

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <pwd.h>
#include <ctype.h>
#include "regex.h"
#ifdef LOCKER
#include <hesiod.h>
#endif

#define LOCAL_TRANSLATION_FILE ".url_translations"
#define TRANSLATION_FILE "/afs/net/admin/www/lib/url_translations"

struct translate_pat {
	char	*match;
	char	*replace;
	struct translate_pat *next;
};

struct translate_pat *patterns = NULL;

static int initialized = 0;
static int do_debug = 0;
static int notranslate = 0;
static int nomasterfile = 0;

static int match_replace(char *regex, char *input, char *replace,
		  char *output, int maxsize)
{
	regex_t	re;
	int	retval;
	regmatch_t *pmatch;
	int	i;
	int	match;
	char	*p = replace, *q;
	int	size = maxsize-1;
	
	retval = regcomp(&re, regex, REG_EXTENDED);
	if (retval)
		return -1;
	pmatch = malloc(sizeof(regmatch_t) * 10);
	retval = regexec(&re, input, 10, pmatch, 0);
	if (retval == REG_NOMATCH) {
		regfree(&re);
		free(pmatch);
		return 0;
	}
	if (retval) {
		regfree(&re);
		free(pmatch);
		return -1;
	}
	for(p = replace, q=output; *p; p++) {
		if (*p == '\\' && *(p+1) >= '0' && *(p+1) <= '9') {
			p++;
			match = *p - '0';
			for (i = pmatch[match].rm_so;
			     i < pmatch[match].rm_eo; i++) {
				*q++ = input[i];
				if (!--size)
					goto finish;
			}
		} else {
			*q++ = *p;
			if (!--size)
				goto finish;
		}
		
	}
finish:
	regfree(&re);
	free(pmatch);
	*q = '\0';
	return 1;
}

static char *url_strdup(char *s)
{
	char *r;

	r = malloc(strlen(s)+1);
	if (!r)
		return NULL;
	strcpy(r, s);
	return r;
}

static void read_translation_patterns(char *filename, FILE *f)
{
	char	buf[1024];
	char	*s;
	char	*match, *replace;
	char	*base = 0;
	struct translate_pat *pat;
	struct translate_pat *last;

	if (!f)
		f = fopen(filename, "r");
	if (!f)
		return;

	if (do_debug && filename)
		printf("Reading in translation file %s....\n", filename);
	
	for (last = patterns; last && last->next; last = last->next);
	
	while (!feof(f)) {
		if (!fgets(buf, sizeof(buf), f))
			break;
		if (buf[0] == '#')
			continue;
		s = strchr(buf, '\n');
		if (s)
		    *s = '\0';
		if (buf[0] == 0)
		    continue;
		match = &buf[0];
		s = match;
		while (*s && !isspace(*s))
		    s++;
		if (s == 0) {
		    printf("Badly formed line in translation file:\n\t%s\n",
			   buf);
		    continue;
		}
		*s = '\0';
		do {
		    s++;
		} while (*s && isspace(*s));
		replace = s;

		if (strcmp(match, "base") == 0) {
		    if (base)
			free(base);
		    base = url_strdup(replace);
		    continue;
		}

		if (strcmp(match, "debug") == 0) {
		    do_debug = 1;
		    continue;
		}
		
		if (strcmp(match, "notranslate") == 0) {
		    notranslate = 1;
		    continue;
		}

		if (strcmp(match, "nomasterfile") == 0) {
		    nomasterfile = 1;
		    continue;
		}
		
		pat = malloc(sizeof(struct translate_pat));
		if (base) {
		    pat->match = malloc(strlen(base) + strlen(match) + 1);
		    strcpy(pat->match, base);
		    strcat(pat->match, match);
		} else {
		    pat->match = url_strdup(match);
		}
		pat->replace= url_strdup(replace);
		pat->next = 0;
#ifdef TESTING
		printf("match=%s, replace=%s\n", pat->match, pat->replace);
#endif
		if (last)
			last->next = pat;
		else
			patterns = pat;
		last = pat;
	}
	if (base)
	    free(base);
	fclose(f);
}

void initialize_url_translations()
{
	FILE	*f;
	struct passwd *pwd;
	char filename[1024];

	pwd = getpwuid(getuid());
	if (pwd) {
		sprintf(filename, "%s/%s", pwd->pw_dir,
			LOCAL_TRANSLATION_FILE);
		read_translation_patterns(filename, NULL);
	}
	if (!nomasterfile)
	    read_translation_patterns(TRANSLATION_FILE, NULL);
	initialized++;
}

#ifdef LOCKER
static char* lookup_locker (char* locker)
{
    char **cpp, *hesi, *ret = NULL;

    cpp = hes_resolve(locker, "filsys");
    if (cpp == 0)
	return NULL;
    
    hesi = *cpp;         /* we only look at the first locker entry */
    if (hesi && !strncmp(hesi,"AFS ",4)) {
	char *spc;

	hesi += 4;               /* skip "AFS " */

	spc = strchr(hesi, 32);  /* strip the remaining fields */
	if (spc) *spc='\0';

	ret = (char*)malloc(strlen(hesi)+1);

	if (ret) {
	    strcpy(ret, hesi);
	}
    }
    while (*cpp) {
	free(*cpp);
	cpp++;
    }
    return ret;
}

char *locker_translate(char *orig_url, char *url, char **redirect)
{
    char *lockername, *lpath, *end;
    static char buf[MAX_URL_LEN];
    
    if (strncmp(url, "file://localhost/~", 18) == 0)
	lockername = url_strdup(url+18);
    else if (strncmp(url, "file:/~", 7))
	lockername = url_strdup(url+7);
    else
	return url;
    end = strchr(lockername, '/');
    if (end)
	*end++ = 0;
    lpath = lookup_locker(lockername);
    if (lpath) {
	sprintf(buf, "file://localhost%s/%s", lpath, end);
	free(lpath);
	free(lockername);
	if (do_debug)
	    printf("==> %s\n", buf);
	return buf;
    }
    free(lockername);
    return url;
}
#endif

char *regex_translate(char *url, char **redirect)
{
	struct translate_pat *p;
	static char	buf[MAX_URL_LEN];
	int	retval;
	
	for (p = patterns; p; p = p->next) {
		retval = match_replace(p->match, url, p->replace,
				       buf, sizeof(buf));
		if (retval == 1) {
		    if (do_debug)
			printf("'%s' --> '%s'\n", url, buf);
		    return(buf);
		}
	}
	return url;
}

char *append_list[] = { "/index.html", ".gif", ".jpg", ".jpeg", ".tiff",
			    ".tif", ".html", ".ps", ".Z", ".gz", 0 };

char *append_translate(char *orig_url, char *url, char **redirect)
{
    static char buf[MAX_URL_LEN];
    struct stat st;
    char *filepath;
    char **p;
    int start = 1;

    if (strncmp(url, "file://localhost/", 17) == 0)
	filepath = url+16;
    else if (strncmp(url, "file:/", 6) == 0)
	filepath = url+5;
    else
	return url;
    if (stat(filepath, &st) == 0) {
	if (!S_ISDIR(st.st_mode))
	    return url;
	start = 0;
    }
    for (p = &append_list[start]; *p; p++) {
	strcpy(buf, filepath);
	strcat(buf, *p);
	if (access(buf, F_OK) == 0) {
	    strcpy(buf, url);
	    strcat(buf, *p);
	    if (do_debug)
		printf("-=-=> %s\n", buf);
	    if (**p == '/' && redirect) {
		    if (*redirect)
			    free(*redirect);
		    *redirect = malloc(strlen(orig_url)+strlen(*p)+1);
		    if (*redirect) {
			    strcpy(*redirect, orig_url);
			    strcat(*redirect, *p);
		    }
	    }
	    return buf;
	}
    }
    return url;
}    
	
char *url_translate(char *url, char **redirect)
{
    char *orig_url = url;
    
    if (!initialized)
	initialize_url_translations();
	
    if (notranslate)
	return url;
    
    url = regex_translate(url, redirect);
    if (url != orig_url) {
#ifdef LOCKER
	url = locker_translate(orig_url, url, redirect);
#endif
	url = append_translate(orig_url, url, redirect);
    }
    return url;
}


#if TESTING
main(int argc, char **argv)
{
	int	retval;
	char	buff[1024];

	do_debug = 1;
	printf("%s\n", url_translate(argv[1]));
	exit(0);
}
#endif
	
