
/* ====================================================================
 * Copyright (c) 1995 The Apache Group.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. All advertising materials mentioning features or use of this
 *    software must display the following acknowledgment:
 *    "This product includes software developed by the Apache Group
 *    for use in the Apache HTTP server project (http://www.apache.org/)."
 *
 * 4. The names "Apache Server" and "Apache Group" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission.
 *
 * 5. Redistributions of any form whatsoever must retain the following
 *    acknowledgment:
 *    "This product includes software developed by the Apache Group
 *    for use in the Apache HTTP server project (http://www.apache.org/)."
 *
 * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
 * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Group and was originally based
 * on public domain software written at the National Center for
 * Supercomputing Applications, University of Illinois, Urbana-Champaign.
 * For more information on the Apache Group and the Apache HTTP server
 * project, please see <http://www.apache.org/>.
 *
 */


/*
 * mod_passwordstore v0.1 written by John Newbigin <jn@it.swin.edu.au>
 *
 */

#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "http_protocol.h"


/*
 * structure to hold the configuration details for the passwords
 */
typedef struct {
  char *path;		/* the path that is allowed to access the password */
  int path_len;	/* the length of the path that must match */
  char *name;		/* the name of the password */
  char *password;	/* the secret password string */
} password_config_rec;

typedef struct 
{
	char *filename;
	int debug_on;
	pool *p;
	array_header *password_list;
} server_config;

module passwordstore_module;

static server_config *our_sconfig(server_rec *s)
{
	return (server_config *) ap_get_module_config(s->module_config, &passwordstore_module);
}

/*
static server_config *our_rconfig(request_rec *r)
{
	return (server_config *) ap_get_module_config(r->request_config, &passwordstore_module);
}*/

static void load_passwords(server_config *config)
{
	configfile_t *f;
	char l[MAX_STRING_LEN];
	const char *rpw;
	password_config_rec *rec = NULL;

	if(!config->filename)
	{
		return;
	}

	config->password_list = ap_make_array(config->p, 4, sizeof(password_config_rec));

	if(!(f = ap_pcfg_openfile(config->p, config->filename)))
	{
        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, 0,
			"Could not open password file: %s", config->filename);
	}
	else
	{
		// read in the passwords...
		while (!(ap_cfg_getline(l, MAX_STRING_LEN, f))) 
		{
			if ((l[0] == '#') || (!l[0]))
				continue;
			rpw = l;

			if(!rec)
			{
				rec = (password_config_rec *) ap_push_array(config->password_list);
			}
			if(rec)
			{
				rec->path = ap_getword(config->p, &rpw, ':');
				rec->name = ap_getword(config->p, &rpw, ':');
				rec->password = ap_getword(config->p, &rpw, ':');
	
				rec->path_len = strlen(rec->path);
			
				if(rec->path_len > 0 && strlen(rec->password) > 0)
				{
					rec = NULL;
				}
				else
				{
					// can we free it?
					rec->path_len = 0; // 0 = unused
				}
			}

		}
		ap_cfg_closefile(f);
	}
}

static void module_init(server_rec *s, pool *p)
{
	server_config *config = NULL;

	config = our_sconfig(s);
	if(config)
	{
    	ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, s, "module_init config file=%s", config->filename);
		load_passwords(config);
	}
	else
	{
    	ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, s, "module_init no config");
		return;
	}
}

static int status_handler(request_rec *r)
{
	password_config_rec *rec;
	int i;
	int found = 0;
	server_config *config = our_sconfig(r->server);

	if(config)
	{
		if(r->main)
		{
			if(strcmp(r->the_request, r->main->the_request) == 0)
			{
				//ap_rputs(r->main->uri, r);
				
				// can we assume that r->main->uri has not been tampered with???
				
				for (i = 0; i < config->password_list->nelts; i++)
				{
			        /*ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, r,
						"checking: %s-%s-%s", rec->path, rec->name, rec->password);*/
	
					rec = (password_config_rec *)config->password_list->elts;
					rec = &rec[i];
	
					if(rec->path_len > 0)
					{
						if(strncmp(rec->path, r->main->uri, rec->path_len) == 0)
						{
							// looks like a match
			        		/*ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, r,
								"match: %s-%s-%s", rec->path, rec->name, rec->password);*/
		
							ap_table_setn(r->main->notes, rec->name, rec->password);
						}
					}
				}
				
				return OK;
			}
			else
			{
				ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
			                 "the_request does not match %s:%s", r->the_request, r->main->the_request);
			}
		}
		else
		{
			if(config->debug_on)
			{
				// for testing, lets dump the passwords...  this is obviously not secure...
				r->content_type = "text/plain";
				ap_send_http_header(r);
				ap_rputs("mod_passwordstore configuration\n\n", r);
				ap_rprintf(r, "PasswordStoreFile: %s\n", config->filename);
				ap_rprintf(r, "PasswordStoreDebug: %d\n", config->debug_on);
				for (i = 0; i < config->password_list->nelts; i++)
				{
					rec = (password_config_rec *)config->password_list->elts;
					rec = &rec[i];

					if(rec->path_len > 0)
					{
						ap_rprintf(r, "path: '%s' name: '%s' password: '%s'\n", rec->path, rec->name, rec->password);
						found++;
					}
				}
				ap_rprintf(r, "%d password%s available\n", found, found == 1 ? "" : "s");
				ap_rputs("\n", r);
				ap_rputs("mod_passwordstore written by John Newbigin <jn@it.swin.edu.au>\n", r);
				ap_rputs("remember to turn debug off once you have confirmed that your installation works\n", r);
				ap_rputs("(PasswordStoreDebug Off)\n", r);
		
				return OK;
			}
			else
			{
				ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
		    	             "no main request found");
			}
		}
	}
	else
	{
		// why is there no config?
		ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
	                 "no config found");
	}

	return DECLINED;
}

static void *create_server_config(pool *p, server_rec *s)
{
	server_config *config;

    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, s, "create_server_config");
	config = (server_config *) ap_pcalloc(p, sizeof(server_config));
	config->p = p;
	config->debug_on = 0;
	config->filename = "passwords.txt";
	config->password_list = NULL;

	return config;
}

static const char *set_file_slot_server(cmd_parms *cmd, void *offset, char *one)
{
	char *config = ap_get_module_config(cmd->server->module_config, &passwordstore_module);
	cmd->info = &config[(int)cmd->info];
	return ap_set_file_slot(cmd, offset, one);
}

static const char *set_flag_slot_server(cmd_parms *cmd, void *offset, int flag)
{
	char *config = ap_get_module_config(cmd->server->module_config, &passwordstore_module);
	cmd->info = &config[(int)cmd->info];
	return ap_set_flag_slot(cmd, offset, flag);
}

static const command_rec password_cmds[] = 
{
	{"PasswordStoreFile", set_file_slot_server, (void *)XtOffsetOf(server_config, filename), RSRC_CONF, TAKE1, "file containing secret passwords" },
	{"PasswordStoreDebug", set_flag_slot_server, (void *)XtOffsetOf(server_config, debug_on), RSRC_CONF, FLAG, "handler shows config info" },
	{NULL}

};


static const handler_rec password_handlers[] =
{
    {STATUS_MAGIC_TYPE, status_handler},
    {"passwords", status_handler},
    {NULL}
};



module passwordstore_module = {
   STANDARD_MODULE_STUFF,
   module_init,		/* initializer */
   NULL,			/* dir config creater */
   NULL,			/* dir merger --- default is to override */
   create_server_config,			/* server config */
   NULL,			/* merge server config */
   password_cmds,	/* command table */
   password_handlers,	/* handlers */
   NULL,			/* filename translation */
   NULL,			/* check_user_id */
   NULL,			/* check auth */
   NULL,			/* check access */
   NULL,			/* type_checker */
   NULL,			/* fixups */
   NULL,			/* logger */
   NULL,			/* header parser */
   NULL,			/* child_init */
   NULL,			/* child_exit */
   NULL				/* post read-request */
};

