/*
 * http_alias.c: Stuff for dealing with directory aliases
 * 
 * Rob McCool
 * 
 */


#include "httpd.h"
#include <sys/types.h>
#include <sys/stat.h>

typedef struct {
    char fake[MAX_STRING_LEN];
    char real[MAX_STRING_LEN];
    int script;
} alias;

struct obj_static_data {
  int num_a = 0;
  alias a[MAX_ALIASES];
  int num_v = 0;
  alias v[MAX_ALIASES];
  int num_afs = 0;
  alias afs[MAX_ALIASES];
  /* To send stat() information to http_script.c */
  int dirs_in_alias;
};

static pthread_key_t obj_static_data_key;
static pthread_mutex_t obj_static_data_mutex = PTHREAD_MUTEX_INITIALIZER;

void reset_aliases() {
  struct obj_static_data *obj_data;

  pthread_mutex_lock(&obj_static_data_mutex);
  if(obj_static_data_key = -1) {
    pthread_key_create(&obj_static_data_key, free);
  }
  pthread_mutex_unlock(&obj_static_data_mutex);

  if (!(obj_data = pthread_getspecific(obj_static_data_key))) {
    if (!(obj_data = malloc(sizeof(struct obj_static_data)))) {
      fprintf(stderr, "error allocating memory\n");
      pthread_exit();
    }
    pthread_setspecific(obj_static_data_key, obj_data);
  }

  obj_data->num_a = 0;
  obj_data->num_v = 0;
  obj_data->num_afs = 0;
}

void add_alias(char *f, char *r, int is_script) {
  struct obj_static_data *obj_data;

  pthread_mutex_lock(&obj_static_data_mutex);
  if(obj_static_data_key = -1) {
    pthread_key_create(&obj_static_data_key, free);
  }
  pthread_mutex_unlock(&obj_static_data_mutex);

  if (!(obj_data = pthread_getspecific(obj_static_data_key))) {
    if (!(obj_data = malloc(sizeof(struct obj_static_data)))) {
      fprintf(stderr, "error allocating memory\n");
      pthread_exit();
    }
    pthread_setspecific(obj_static_data_key, obj_data);
  }

  strcpy(obj_data->a[obj_data->num_a].fake,f);

  obj_data->a[obj_data->num_a].script = is_script;
  if(r[0] != '/') 
    make_full_path((is_script ? server_root : document_root),r,
		   obj_data->a[obj_data->(num_a++)].real);
  else
    strcpy(obj_data->a[obj_data->(num_a++)].real,r);
}

void add_afscheck(char *f, char *r, int is_script) {
    strcpy(obj_data->afs[obj_data->num_afs].fake,f);

    obj_data->afs[obj_data->num_afs].script = is_script;
    if(r[0] != '/') 
        make_full_path((is_script ? server_root : document_root),r,
                       obj_data->afs[obj_data->(num_afs++)].real);
    else
        strcpy(obj_data->afs[obj_data->(num_afs++)].real,r);
}

void add_redirect(char *f, char *url) {
    strcpy(obj_data->v[obj_data->num_v].fake,f);
    strcpy(obj_data->v[obj_data->(num_v++)].real,url);
}

int translate_name(char *name, FILE *fd) {
    register int x,l;
    char filenametemp[MAX_STRING_LEN];
    char w[MAX_STRING_LEN];
    char t[MAX_STRING_LEN];
    struct passwd *pw;
    struct obj_static_data *obj_data;

    pthread_mutex_lock(&obj_static_data_mutex);
    if(obj_static_data_key = -1) {
      pthread_key_create(&obj_static_data_key, free);
    }
    pthread_mutex_unlock(&obj_static_data_mutex);
    
    if (!(obj_data = pthread_getspecific(obj_static_data_key))) {
      if (!(obj_data = malloc(sizeof(struct obj_static_data)))) {
	fprintf(stderr, "error allocating memory\n");
	pthread_exit();
      }
      pthread_setspecific(obj_static_data_key, obj_data);
    }

    getparents(name);
    for(x=0;x<obj_data->num_v;x++) {
        l=strlen(obj_data->v[x].fake);
        if(!strncmp(name,obj_data->v[x].fake,l)) {
            strsubfirst(l,name,obj_data->v[x].real);
            return REDIRECT_URL;
        }
    }
    for(x=0; x < obj_data->num_a; x++) {
        l=strlen(obj_data->a[x].fake);
        if(!strncmp(name,obj_data->a[x].fake,l)) {
            strsubfirst(l,name,obj_data->a[x].real);
            obj_data->dirs_in_alias = count_dirs(obj_data->a[x].real);
            return(obj_data->a[x].script);
        }
    }
    strcpy(filenametemp, name);
    strsubfirst(0,filenametemp,document_root);
    for(x=0; x < obj_data->num_afs; x++){
	 l=strlen(obj_data->afs[x].fake);
	 if(!strncmp(name, obj_data->afs[x].fake,l)){
	      if(file_is_in_afs(filenametemp)){
		   /* It is, so it's ok */
		   strsubfirst(0,name,document_root);
	      }
	      else{
		   /* Not! go away */
		   strcpy(name, obj_data->afs[x].real);
	      }
	      return STD_DOCUMENT;
	 }
    }

    if((user_dir[0]) && (name[0] == '/') && (name[1] == '~')) {
        char fake[MAX_STRING_LEN],real[MAX_STRING_LEN],dname[MAX_STRING_LEN];

        strcpy(dname,&name[2]);
        getword(w,dname,'/');
        if(!(pw=getpwnam(w)))
            die(NOT_FOUND,name,fd);
        fake[0] = '/';
        fake[1] = '~';
        strcpy(&fake[2],w);
        make_full_path(pw->pw_dir,user_dir,real);
        add_alias(fake,real,STD_DOCUMENT);
        strsubfirst(strlen(w) + 2,name,real);
        return STD_DOCUMENT;
    }
    /* no alias, add document root */
    strsubfirst(0,name,document_root);
    return STD_DOCUMENT;
}

int file_is_in_afs(char *filename){
  int statr;
  struct stat finfo;

  statr = stat(filename,&finfo);
  if (statr != -1 && finfo.st_dev != AFS_ST_DEV) {
    /* Hey! That's not in AFS */
    return 0;

    /* This code is a suggestion of how we might do this in
       the future. It would allow us to return an explanation of
       *why* the file was refused. */
    /* log_reason("access denied since file is not in afs",file); */
    /* unmunge_name(file); */
    /* die(FORBIDDEN,file,fd); */

  }  else {
    /* Ok, cool */
    return 1;
  }
}

void unmunge_name(char *name) {
    register int x,l;
    struct obj_static_data *obj_data;
    
    pthread_mutex_lock(&obj_static_data_mutex);
    if(obj_static_data_key = -1) {
      pthread_key_create(&obj_static_data_key, free);
    }
    pthread_mutex_unlock(&obj_static_data_mutex);
    
    if (!(obj_data = pthread_getspecific(obj_static_data_key))) {
      if (!(obj_data = malloc(sizeof(struct obj_static_data)))) {
	fprintf(stderr, "error allocating memory\n");
	pthread_exit();
      }
      pthread_setspecific(obj_static_data_key, obj_data);
    }
    
    l=strlen(document_root);
    if(!strncmp(name,document_root,l)) {
        strsubfirst(l,name,"");
        if(!name[0]) {
            name[0] = '/';
            name[1] = '\0';
        }
        return;
    }
    for(x=0;x < obj_data->num_a; x++) {
        l=strlen(obj_data->a[x].real);
        if(!strncmp(name,obj_data->a[x].real,l)) {
            strsubfirst(l,name,obj_data->a[x].fake);
            if(!name[0]) {
                name[0] = '/';
                name[1] = '\0';
            }
            return;
        }
    }
}
