// Copyright (C) 2006 David Sugar, Tycho Softworks.
//
// 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-1307, USA.
//
// As a special exception, you may use this file as part of a free software
// library without restriction.  Specifically, if other files instantiate
// templates or use macros or inline functions from this file, or you compile
// this file and link it with other files to produce an executable, this
// file does not by itself cause the resulting executable to be covered by
// the GNU General Public License.  This exception does not however
// invalidate any other reasons why the executable file might be covered by
// the GNU General Public License.
//
// This exception applies only to the code released under the name GNU
// ccScript.  If you copy code from other releases into a copy of GNU
// ccScript, as the General Public License permits, the exception does
// not apply to the code that you add in this way.  To avoid misleading
// anyone as to the status of such modified files, you must delete
// this exception notice from them.
//
// If you write modifications of your own for GNU ccScript, it is your choice
// whether to permit this exception to apply to your modifications.
// If you do not wish that, delete this exception notice.
//

#include "script3.h"

namespace ccscript3Extension {

using namespace std;
using namespace ost;

class URLMethods : public ScriptMethods
{
public:
	bool urlProtocol(void);
	bool urlExtension(void);
	bool urlDecode(void);
	bool urlEncode(void);
	bool urlEscape(void);
	bool scrURL(void);
	bool scrQuery(void);
};

class URLChecks : public ScriptChecks
{
public:
	const char *chkURL(Line *line, ScriptImage *img);
	const char *chkURLBuild(Line *line, ScriptImage *img);
	const char *chkURLQuery(Line *line, ScriptImage *img);
	const char *chkDecode(Line *line, ScriptImage *img);
};

static Script::Define runtime[] = {
	{"urlprotocol", true, (Script::Method)&URLMethods::urlProtocol,
		(Script::Check)&URLChecks::chkURL},
	{"urlextension", true, (Script::Method)&URLMethods::urlExtension,
		(Script::Check)&URLChecks::chkURL},
	{"urldecode", true, (Script::Method)&URLMethods::urlDecode,
		(Script::Check)&URLChecks::chkDecode},
	{"urlescape", true, (Script::Method)&URLMethods::urlEscape,
		(Script::Check)&URLChecks::chkDecode},
	{"urlencode", true, (Script::Method)&URLMethods::urlEncode,
		(Script::Check)&URLChecks::chkDecode},
	{"url", true, (Script::Method)&URLMethods::scrURL,
		(Script::Check)&URLChecks::chkURLBuild},
	{"urlquery", true, (Script::Method)&URLMethods::scrQuery,
		(Script::Check)&URLChecks::chkURLQuery},
	{NULL, false, NULL, NULL}};

static ScriptBinder bindString(runtime);

const char *URLChecks::chkURLBuild(Line *line, ScriptImage *img)
{
	unsigned idx = 0;
	const char *cp;

	if(getMember(line))
		return "url builder has no members";

	cp = getOption(line, &idx);
	if(!cp)
		return "targat variable missing";

	if(*cp != '&' && *cp != '%')
		return "target must be variable";

	return NULL;
}

const char *URLChecks::chkURLQuery(Line *line, ScriptImage *img)
{
	unsigned idx = 0;
	const char *cp;

	if(getMember(line))
		return "url builder has no members";

	cp = getOption(line, &idx);
	if(!cp)
		return "targat variable missing";

	if(*cp != '&' && *cp != '%')
		return "target must be variable";

	return chkAllVars(line, img);
}


const char *URLChecks::chkDecode(Line *line, ScriptImage *img)
{
	if(getMember(line))
		return "member not used for urldecode";

	if(hasKeywords(line))
		return "invalid keyword for urldecode";

	if(line->argc < 1)
		return "urldecode uses variable arguments";

	return chkAllVars(line, img);
}

const char *URLChecks::chkURL(Line *line, ScriptImage *img)
{
	if(getMember(line))
		return "member not used for url";

	if(hasKeywords(line))
		return "invalid keyword for url";

	if(line->argc < 2)
		return "requires url and at least one check";

	return NULL;
}

bool URLMethods::scrURL(void)
{
	Line *line = getLine();
	unsigned idx = 0;
	Symbol *sym;
	const char *opt = getOption(NULL);
	unsigned len = 0;
	bool slash = false, dslash = false;
	char *dp;
	char token = '?';
	const char *cp;

	sym = mapSymbol(opt, 256);
	if(!sym)
	{
		error("target-missing");
		return true;
	}

	if(sym->type != symNORMAL && sym->type != symINITIAL)
	{
		error("target-invalid");
		return true;
	}

	dp = sym->data;

	while((NULL != (opt = getValue(NULL))) && len < (unsigned)(sym->size - 3))
	{
		while(*opt && len < (unsigned)(sym->size - 3))
		{
			if(isalnum(*opt) || strchr(":@;.#~_", *opt))
			{
				slash = false;
				*(dp++) = *(opt++);
				++len;
				continue;
			}
			if(*opt == '/')
			{
				if(!slash || (slash && !dslash))
				{
					if(slash)
						dslash = true;
					slash = true;
					*(dp++) = *(opt++);
					++len;
				}
				else
					++opt;
				continue;
			}
			slash = false;
			*(dp++) = '%';
			snprintf(dp, 3, "%02x", *(opt++));
			dp += 2;
			len += 3;
		}
	}

	while(idx < line->argc && len < (unsigned)(sym->size - 3))
	{
		cp = line->args[idx++];
		if(*cp != '=')
			continue;
		++cp;
		*(dp++) = token;
		token = '&';
		++len;
		while(*cp && len < (unsigned)(sym->size - 3))
		{
			if(isalnum(*cp))
			{
				*(dp++) = tolower(*cp);
				++cp;
				++len;
			}
			else if(*cp == '.' || *cp == '_')
			{
				*(dp++) = '_';
				++cp;
				++len;
			}
			else
				++cp;
		}
		++len;
		*(dp++) = '=';
		cp = getContent(line->args[idx++]);
		if(!cp)
			cp = "";
		while(*cp && len < (unsigned)(sym->size - 3))
		{
			if(isalnum(*cp))
			{
				*(dp++) = *(cp++);
				++len;
				continue;
			}
			*(dp++) = '%';
			snprintf(dp, 3, "%02x", *(cp++));
			dp += 2;
			len += 3;
		}
	}
	*dp = 0;
	sym->type = symNORMAL;
	advance();
	return true;
}

bool URLMethods::scrQuery(void)
{
	Line *line = getLine();
	unsigned idx = 0;
	Symbol *sym;
	const char *opt = getOption(NULL);
	unsigned len;
	char *dp;
	char token = '?';
	const char *cp;

	sym = mapSymbol(opt, 256);
	if(!sym)
	{
		error("target-missing");
		return true;
	}

	if(sym->type != symNORMAL && sym->type != symINITIAL)
	{
		error("target-invalid");
		return true;
	}

	len = strlen(sym->data);
	dp = sym->data + len;
	if(strchr(sym->data, '?'))
		token = '&';

	while((NULL != (opt = getOption(NULL))) && len < (unsigned)(sym->size - 3))
	{
		if(len)
		{
			*(dp++) = token;
			token = '&';
			++len;
		}
		cp = opt;
		while(*opt && len < (unsigned)(sym->size - 3))
		{
			if(isalnum(*opt))
			{
				*(dp++) = tolower(*opt);
				++opt;
				++len;
			}
			else if(*opt == '.' || *opt == '_')
			{
				*(dp++) = '_';
				++opt;
				++len;
			}
			else
				++opt;
		}
		*(dp++) = '=';
		++len;
		cp = getContent(cp);
		if(!cp)
			cp = "";

		while(*cp && len < (unsigned)(sym->size - 3))
		{
			if(isalnum(*cp))
			{
				*(dp++) = *(cp++);
				++len;
				continue;
			}
			*(dp++) = '%';
			snprintf(dp, 3, "%02x", *(cp++));
			dp += 2;
			len += 3;
		}
	}

	while(idx < line->argc && len < (unsigned)(sym->size - 3))
	{
		cp = line->args[idx++];
		if(*cp != '=')
			continue;
		++cp;
		*(dp++) = token;
		token = '&';
		++len;
		while(*cp && len < (unsigned)(sym->size - 3))
		{
			if(isalnum(*cp))
			{
				*(dp++) = tolower(*cp);
				++cp;
				++len;
			}
			else if(*cp == '.' || *cp == '_')
			{
				*(dp++) = '_';
				++cp;
				++len;
			}
			else
				++cp;
		}
		++len;
		*(dp++) = '=';
		cp = getContent(line->args[idx++]);
		if(!cp)
			cp = "";
		while(*cp && len < (unsigned)(sym->size - 3))
		{
			if(isalnum(*cp))
			{
				*(dp++) = *(cp++);
				++len;
				continue;
			}
			*(dp++) = '%';
			snprintf(dp, 3, "%02x", *(cp++));
			dp += 2;
			len += 3;
		}
	}
	*dp = 0;
	sym->type = symNORMAL;
	advance();
	return true;
}


bool URLMethods::urlDecode(void)
{
	const char *opt;
	Symbol *sym;
	char *dp, *sp;
	char hex[3];
	char c;

	while(NULL != (opt = getOption(NULL)))
	{
		sym = mapSymbol(++opt, 0);
		if(!sym)
			continue;

		if(sym->type != symNORMAL && sym->type != symINITIAL)
			continue;

		dp = sp = sym->data;
		while(*sp)
		{
			if(*sp == '+')
			{
				*(dp++) = ' ';
				++sp;
			}
			else if(*sp == '%')
			{
				hex[0] = *(++sp);
				hex[1] = *(++sp);
				hex[2] = 0;
				++sp;
				c = (char)strtol(hex, NULL, 16);
				*(dp++) = c;
			}
			else
				*(dp++) = *(sp++);
		}
		*dp = 0;
		sym->type = symNORMAL;
	}
	advance();
	return true;
}

bool URLMethods::urlEscape(void)
{
	const char *opt;
	Symbol *sym;
	char *dp, *sp, *ns;
	unsigned len;

	while(NULL != (opt = getOption(NULL)))
	{
		sym = mapSymbol(++opt, 0);
		if(!sym)
			continue;

		if(sym->type != symNORMAL && sym->type != symINITIAL)
			continue;

		sp = ns = newString(sym->data);
		dp = sym->data;
		len = 0;
		while(*sp && len <= (unsigned)(sym->size - 3))
		{
			if(isalnum(*sp))
			{
				++len;
				*(dp++) = *(sp++);
				continue;
			}
			*(dp++) = '%';
			snprintf(dp, 3, "%02x", *(sp++));
			dp += 2;
			len += 3;
		}
		*dp = 0;
		sym->type = symNORMAL;
		delString(ns);
	}
	advance();
	return true;
}


bool URLMethods::urlEncode(void)
{
	const char *opt;
	Symbol *sym;
	char *dp, *sp, *ns;
	unsigned len;
	bool query = false;
	bool equal = false;

	while(NULL != (opt = getOption(NULL)))
	{
		sym = mapSymbol(++opt, 0);
		if(!sym)
			continue;

		if(sym->type != symNORMAL && sym->type != symINITIAL)
			continue;

		sp = ns = newString(sym->data);
		dp = sym->data;
		len = 0;
		while(*sp && len <= (unsigned)(sym->size - 3))
		{
			if(isalnum(*sp) && query && !equal)
			{
				++len;
				*(dp++) = tolower(*sp);
				++sp;
				continue;
			}
			if((*sp == '.' || *sp == '_')&& query && !equal)
			{
				*(dp++) = '_';
				++sp;
				++len;
				continue;
			}
			if(*sp == '&' && query)
			{
				*(dp++) = *(sp++);
				equal = false;
				++len;
				continue;
			}
			if(isalnum(*sp))
			{
				++len;
				*(dp++) = *(sp++);
				continue;
			}
			if(*sp == '?' && !query)
			{
				query = true;
				*(dp++) = *(sp++);
				++len;
				continue;
			}
			if(*sp == '=' && query)
			{
				equal = true;
				*(dp++) = *(sp++);
				++len;
				continue;
			}
			if(strchr(":@#/;.", *sp) && !query)
			{
				*(dp++) = *(sp++);
				++len;
				continue;
			}
			*(dp++) = '%';
			snprintf(dp, 3, "%02x", *(sp++));
			dp += 2;
			len += 3;
		}
		*dp = 0;
		sym->type = symNORMAL;
		delString(ns);
	}
	advance();
	return true;
}


bool URLMethods::urlExtension(void)
{
	const char *opt = getValue(NULL);
	const char *ext = opt;
	unsigned elen = 0;
	char ebuf[65];

	if(!opt)
	{
missing:
		if(!scriptEvent("url:extension-missing"))
			error("extension-missing");
		return true;
	}

	ext = strchr(opt, '?');
	if(!ext)
		ext = opt + strlen(opt);

	while(--ext >= opt && ++elen < 32)
	{
		if(*ext == '.')
			break;
	}
	if(ext == opt || *ext != '.' || elen < 2)
		goto missing;

	setString(ebuf, elen, ++ext);

	while(NULL != (opt = getValue(NULL)))
	{
		if(*opt == '.')
			++opt;

		if(!stricmp(opt, ebuf))
		{
			snprintf(ebuf, sizeof(ebuf), "url:extension-%s", opt);
 			if(!scriptEvent(ebuf))
				advance();
			return true;
		}
	}
	if(!scriptEvent("url:extension-invalid"))
		error("extension-invalid");
	return true;
}

bool URLMethods::urlProtocol(void)
{
	char pname[65];
	const char *opt = getValue(NULL);
	char *cp = pname, count = 0;

	if(!opt)
	{
missing:
		if(!scriptEvent("url:protocol-missing"))
			error("protocol-missing");
		return true;
	}

	while(isalpha(*opt) && ++count < 32)
		*(cp++) = *(opt++);

	if(*opt != ':')
		goto missing;

	*cp = 0;

	while(NULL != (opt = getValue(NULL)))
	{
		if(!stricmp(opt, pname))
		{
			snprintf(pname, sizeof(pname), "url:protocol-%s", opt);
			if(!scriptEvent(pname))
				advance();
			return true;
		}
	}
	if(!scriptEvent("url:protocol-invalid"))
		error("protocol-invalid");
	return true;
}

}; // namespace



syntax highlighted by Code2HTML, v. 0.9.1