
/*
 * $Id: client_side_rewrite.c,v 1.7 2008/07/21 20:11:35 hno Exp $
 *
 * DEBUG: section 33    Client-side Routines - URL Rewriter
 * AUTHOR: Duane Wessels; Adrian Chadd
 *
 * SQUID Web Proxy Cache          http://www.squid-cache.org/
 * ----------------------------------------------------------
 *
 *  Squid is the result of efforts by numerous individuals from
 *  the Internet community; see the CONTRIBUTORS file for full
 *  details.   Many organizations have provided support for Squid's
 *  development; see the SPONSORS file for full details.  Squid is
 *  Copyrighted (C) 2001 by the Regents of the University of
 *  California; see the COPYRIGHT file for full details.  Squid
 *  incorporates software developed and/or copyrighted by other
 *  sources; see the CREDITS file for full details.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 *
 */

#include "squid.h"


/* Local functions */
static void clientRedirectDone(void *data, char *result);

static void
clientInternalRedirectAccessCheckDone(int answer, void *data)
{
    clientHttpRequest *http = data;
    rewrite *rew = NULL;
    char *rurl = NULL;
    if (answer == ACCESS_ALLOWED)
	for (rew = Config.rewrites; rew != NULL; rew = rew->next)
	    if (aclMatchAclList(rew->aclList, http->acl_checklist))
		break;
    if (rew)
	rurl = internalRedirectProcessURL(http, rew->tokens);
    clientRedirectDone(http, rurl);
    http->acl_checklist = NULL;
    if (rurl)
	xfree(rurl);
}

static void
clientRedirectAccessCheckDone(int answer, void *data)
{
    clientHttpRequest *http = data;
    http->acl_checklist = NULL;
    if (answer == ACCESS_ALLOWED)
	redirectStart(http, clientRedirectDone, http);
    else
	clientRedirectDone(http, NULL);
}

void
clientRedirectStart(clientHttpRequest * http)
{
    debug(33, 5) ("clientRedirectStart: '%s'\n", http->uri);
    if (Config.Program.url_rewrite.command == NULL) {
	http->redirect_state = REDIRECT_PENDING;
	if (Config.rewrites != NULL) {
	    http->acl_checklist = clientAclChecklistCreate(Config.accessList.rewrite, http);
	    aclNBCheck(http->acl_checklist, clientInternalRedirectAccessCheckDone, http);
	    return;
	}
	clientRedirectDone(http, NULL);
	return;
    }
    if (Config.accessList.url_rewrite) {
	http->acl_checklist = clientAclChecklistCreate(Config.accessList.url_rewrite, http);
	aclNBCheck(http->acl_checklist, clientRedirectAccessCheckDone, http);
    } else {
	redirectStart(http, clientRedirectDone, http);
    }
}

static void
clientRedirectDone(void *data, char *result)
{
    clientHttpRequest *http = data;
    request_t *new_request = NULL;
    request_t *old_request = http->request;
    const char *urlgroup = http->conn->port->urlgroup;
    debug(33, 5) ("clientRedirectDone: '%s' result=%s\n", http->uri,
	result ? result : "NULL");
    assert(http->redirect_state == REDIRECT_PENDING);
    http->redirect_state = REDIRECT_DONE;
    if (result) {
	http_status status;
	if (*result == '!') {
	    char *t;
	    if ((t = strchr(result + 1, '!')) != NULL) {
		urlgroup = result + 1;
		*t++ = '\0';
		result = t;
	    } else {
		debug(33, 1) ("clientRedirectDone: bad input: %s\n", result);
	    }
	}
	status = (http_status) atoi(result);
	if (status == HTTP_MOVED_PERMANENTLY
	    || status == HTTP_MOVED_TEMPORARILY
	    || status == HTTP_SEE_OTHER
	    || status == HTTP_TEMPORARY_REDIRECT) {
	    char *t = result;
	    if ((t = strchr(result, ':')) != NULL) {
		http->redirect.status = status;
		http->redirect.location = xstrdup(t + 1);
		goto redirect_parsed;
	    } else {
		debug(33, 1) ("clientRedirectDone: bad input: %s\n", result);
	    }
	} else if (strcmp(result, http->uri))
	    new_request = urlParse(old_request->method, result);
    }
  redirect_parsed:
    if (new_request) {
	safe_free(http->uri);
	http->uri = xstrdup(urlCanonical(new_request));
	http->log_uri = xstrdup(urlCanonicalClean(old_request));
	new_request->http_ver = old_request->http_ver;
	httpHeaderAppend(&new_request->header, &old_request->header);
	new_request->client_addr = old_request->client_addr;
	new_request->client_port = old_request->client_port;
#if FOLLOW_X_FORWARDED_FOR
	new_request->indirect_client_addr = old_request->indirect_client_addr;
#endif /* FOLLOW_X_FORWARDED_FOR */
	new_request->my_addr = old_request->my_addr;
	new_request->my_port = old_request->my_port;
	new_request->flags = old_request->flags;
	new_request->flags.redirected = 1;
	if (old_request->auth_user_request) {
	    new_request->auth_user_request = old_request->auth_user_request;
	    authenticateAuthUserRequestLock(new_request->auth_user_request);
	}
	if (old_request->body_reader) {
	    new_request->body_reader = old_request->body_reader;
	    new_request->body_reader_data = old_request->body_reader_data;
	    old_request->body_reader = NULL;
	    old_request->body_reader_data = NULL;
	}
	new_request->content_length = old_request->content_length;
	if (strBuf(old_request->extacl_log))
	    new_request->extacl_log = stringDup(&old_request->extacl_log);
	if (old_request->extacl_user)
	    new_request->extacl_user = xstrdup(old_request->extacl_user);
	if (old_request->extacl_passwd)
	    new_request->extacl_passwd = xstrdup(old_request->extacl_passwd);
	requestUnlink(old_request);
	http->request = requestLink(new_request);
    } else {
	/* Don't mess with urlgroup on internal request */
	if (old_request->flags.internal)
	    urlgroup = NULL;
    }
    safe_free(http->request->urlgroup);		/* only paranoia. should not happen */
    if (urlgroup && *urlgroup)
	http->request->urlgroup = xstrdup(urlgroup);

    clientStoreURLRewriteStart(http);
}
