/
com/planet_ink/coffee_mud/Abilities/Common/
com/planet_ink/coffee_mud/Abilities/Diseases/
com/planet_ink/coffee_mud/Abilities/Druid/
com/planet_ink/coffee_mud/Abilities/Fighter/
com/planet_ink/coffee_mud/Abilities/Languages/
com/planet_ink/coffee_mud/Abilities/Misc/
com/planet_ink/coffee_mud/Abilities/Prayers/
com/planet_ink/coffee_mud/Abilities/Properties/
com/planet_ink/coffee_mud/Abilities/Skills/
com/planet_ink/coffee_mud/Abilities/Songs/
com/planet_ink/coffee_mud/Abilities/Spells/
com/planet_ink/coffee_mud/Abilities/Thief/
com/planet_ink/coffee_mud/Abilities/Traps/
com/planet_ink/coffee_mud/Behaviors/
com/planet_ink/coffee_mud/CharClasses/interfaces/
com/planet_ink/coffee_mud/Commands/
com/planet_ink/coffee_mud/Commands/interfaces/
com/planet_ink/coffee_mud/Common/
com/planet_ink/coffee_mud/Common/interfaces/
com/planet_ink/coffee_mud/Exits/interfaces/
com/planet_ink/coffee_mud/Items/Armor/
com/planet_ink/coffee_mud/Items/Basic/
com/planet_ink/coffee_mud/Items/CompTech/
com/planet_ink/coffee_mud/Items/MiscMagic/
com/planet_ink/coffee_mud/Items/Weapons/
com/planet_ink/coffee_mud/Items/interfaces/
com/planet_ink/coffee_mud/Libraries/
com/planet_ink/coffee_mud/Libraries/interfaces/
com/planet_ink/coffee_mud/Locales/
com/planet_ink/coffee_mud/MOBS/
com/planet_ink/coffee_mud/Races/
com/planet_ink/coffee_mud/Races/interfaces/
com/planet_ink/coffee_mud/WebMacros/
com/planet_ink/coffee_mud/WebMacros/interfaces/
com/planet_ink/coffee_mud/core/
com/planet_ink/coffee_mud/core/collections/
com/planet_ink/coffee_mud/core/interfaces/
com/planet_ink/coffee_mud/core/intermud/
com/planet_ink/coffee_mud/core/intermud/i3/
com/planet_ink/coffee_web/server/
com/planet_ink/siplet/applet/
lib/
resources/factions/
resources/fakedb/
resources/progs/autoplayer/
resources/quests/holidays/
web/
web/admin.templates/
web/admin/grinder/
web/admin/images/
web/clan.templates/
web/pub.templates/
web/pub/images/mxp/
web/pub/sounds/
web/pub/textedit/
package com.planet_ink.coffee_mud.Libraries;

import com.planet_ink.coffee_mud.core.interfaces.*;
import com.planet_ink.coffee_mud.core.*;
import com.planet_ink.coffee_mud.core.collections.*;
import com.planet_ink.coffee_mud.core.exceptions.HTTPRedirectException;
import com.planet_ink.coffee_mud.core.exceptions.HTTPServerException;
import com.planet_ink.coffee_mud.Libraries.interfaces.*;
import com.planet_ink.coffee_mud.Abilities.interfaces.*;
import com.planet_ink.coffee_mud.Areas.interfaces.*;
import com.planet_ink.coffee_mud.Behaviors.interfaces.*;
import com.planet_ink.coffee_mud.CharClasses.interfaces.*;
import com.planet_ink.coffee_mud.Commands.interfaces.*;
import com.planet_ink.coffee_mud.Common.interfaces.*;
import com.planet_ink.coffee_mud.Exits.interfaces.*;
import com.planet_ink.coffee_mud.Items.interfaces.*;
import com.planet_ink.coffee_mud.Locales.interfaces.*;
import com.planet_ink.coffee_mud.MOBS.interfaces.*;
import com.planet_ink.coffee_mud.Races.interfaces.*;
import com.planet_ink.coffee_mud.WebMacros.interfaces.WebMacro;
import com.planet_ink.coffee_web.http.HTTPException;
import com.planet_ink.coffee_web.http.HTTPMethod;
import com.planet_ink.coffee_web.http.HTTPStatus;
import com.planet_ink.coffee_web.http.MIMEType;
import com.planet_ink.coffee_web.http.MultiPartData;
import com.planet_ink.coffee_web.interfaces.HTTPRequest;
import com.planet_ink.coffee_web.interfaces.SimpleServlet;
import com.planet_ink.coffee_web.interfaces.SimpleServletRequest;
import com.planet_ink.coffee_web.interfaces.SimpleServletResponse;
import com.planet_ink.coffee_web.server.WebServer;
import com.planet_ink.coffee_web.util.CWThread;
import com.planet_ink.coffee_web.util.CWConfig;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

import org.mozilla.javascript.Context;
import org.mozilla.javascript.ScriptableObject;

/*
   Copyright 2013-2016 Bo Zimmerman

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

	   http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
public class WebMacroCreamer extends StdLibrary implements WebMacroLibrary, SimpleServlet
{
	@Override
	public String ID()
	{
		return "WebMacroCreamer";
	}

	@Override
	public ByteBuffer convertOutput(CWConfig config, HTTPRequest request, File pageFile, HTTPStatus status, ByteBuffer buffer) throws HTTPException
	{
		if (request.getRequestObjects().get("SYSTEM_HTTP_STATUS") == null)
		{
			request.getRequestObjects().put("SYSTEM_HTTP_STATUS", Integer.toString(status.getStatusCode()));
			request.getRequestObjects().put("SYSTEM_HTTP_STATUS_INFO", status.description());
		}
		final long[] systemStartTime = new long[] { System.currentTimeMillis() };
		if ((pageFile.getParent() != null) 
		&& (CMProps.getBoolVar(CMProps.Bool.MUDSTARTED)) 
		&& (!CMProps.getBoolVar(CMProps.Bool.MUDSHUTTINGDOWN)))
		{
			final Clan mappedClan = CMLib.clans().getWebPathClan(pageFile.getParent());
			if (mappedClan != null)
				request.addFakeUrlParameter("CLAN", mappedClan.clanID());
		}

		try
		{
			return ByteBuffer.wrap(virtualPageFilter(request, request.getRequestObjects(), systemStartTime, new String[] { "" }, new StringBuffer(new String(buffer.array()))).toString().getBytes());
		}
		catch (final HTTPRedirectException he)
		{
			throw new HTTPException(HTTPStatus.S307_TEMPORARY_REDIRECT, he.getMessage());
		}
	}

	@Override
	public byte[] virtualPageFilter(byte[] data) throws HTTPRedirectException
	{
		return virtualPageFilter(new StringBuffer(new String(data))).toString().getBytes();
	}

	@Override
	public String virtualPageFilter(String s) throws HTTPRedirectException
	{
		return virtualPageFilter(new StringBuffer(s)).toString();
	}

	@Override
	public StringBuffer virtualPageFilter(StringBuffer s) throws HTTPRedirectException
	{
		return virtualPageFilter(new HTTPRequest()
		{
			public final Hashtable<String, String>	params	= new Hashtable<String, String>();
			public final Hashtable<String, Object>	objects	= new Hashtable<String, Object>();

			@Override
			public String getHost()
			{
				return "localhost";
			}

			@Override
			public String getUrlPath()
			{
				return "localhost/file";
			}

			@Override
			public String getFullRequest()
			{
				return "GET " + getUrlPath();
			}

			@Override
			public String getUrlParameter(String name)
			{
				return params.get(name.toLowerCase());
			}

			@Override
			public boolean isUrlParameter(String name)
			{
				return params.containsKey(name.toLowerCase());
			}

			@Override
			public Map<String, String> getUrlParametersCopy()
			{
				return new XHashtable<String, String>(params);
			}

			@Override
			public Set<String> getUrlParameters()
			{
				return params.keySet();
			}

			@Override
			public HTTPMethod getMethod()
			{
				return HTTPMethod.GET;
			}

			@Override
			public String getHeader(String name)
			{
				return null;
			}

			@Override
			public InetAddress getClientAddress()
			{
				try
				{
					return InetAddress.getLocalHost();
				}
				catch (final UnknownHostException e)
				{
					return null;
				}
			}

			@Override
			public int getClientPort()
			{
				return 0;
			}

			@Override
			public InputStream getBody()
			{
				return null;
			}

			@Override
			public String getCookie(String name)
			{
				return null;
			}

			@Override
			public Set<String> getCookieNames()
			{
				return objects.keySet();
			}

			@Override
			public List<MultiPartData> getMultiParts()
			{
				return new XVector<MultiPartData>();
			}

			@Override
			public double getSpecialEncodingAcceptability(String type)
			{
				return 0;
			}

			@Override
			public String getFullHost()
			{
				return "localhost";
			}

			@Override
			public List<long[]> getRangeAZ()
			{
				return null;
			}

			@Override
			public void addFakeUrlParameter(String name, String value)
			{
				params.put(name.toLowerCase(), value);
			}

			@Override
			public void removeUrlParameter(String name)
			{
				params.remove(name.toLowerCase());
			}

			@Override
			public Map<String, Object> getRequestObjects()
			{
				return objects;
			}

			@Override
			public float getHttpVer()
			{
				return (float) 1.1;
			}

			@Override
			public String getQueryString()
			{
				return "";
			}
		}, new Hashtable<String, Object>(), new long[] { System.currentTimeMillis() }, new String[] { "" }, s);
	}

	@Override
	public String clearWebMacros(String s)
	{
		if (s.length() == 0)
			return "";
		return clearWebMacros(new StringBuffer(s));
	}

	@Override
	public String parseFoundMacro(StringBuffer s, int i, boolean lookOnly)
	{
		String foundMacro = null;
		boolean extend = false;
		for (int x = i + 1; x < s.length(); x++)
		{
			if ((s.charAt(x) == '@') 
			&& (extend) 
			&& (x < (s.length() - 1)) 
			&& (s.charAt(x + 1) == '@'))
			{
				if (!lookOnly)
					s.deleteCharAt(x);
				while ((x < s.length()) && (s.charAt(x) == '@'))
					x++;
				x--;
			}
			else 
			if ((s.charAt(x) == '@') 
			&& ((!extend) || (x >= s.length() - 1) || (s.charAt(x + 1) != '@')))
			{
				foundMacro = s.substring(i + 1, x);
				break;
			}
			else 
			if ((s.charAt(x) == '?') 
			&& (Character.isLetterOrDigit(s.charAt(x - 1))))
				extend = true;
			else 
			if (((x - i) > CMClass.longestWebMacro) && (!extend))
				break;
		}
		return foundMacro;
	}

	@Override
	public String clearWebMacros(StringBuffer s)
	{
		if (s.length() == 0)
			return "";
		for (int i = 0; i < s.length(); i++)
		{
			if (s.charAt(i) == '@')
			{
				String foundMacro = parseFoundMacro(s, i, false);
				if ((foundMacro != null) && (foundMacro.length() > 0))
				{
					if (foundMacro.equalsIgnoreCase("break"))
						i += (foundMacro.length() + 2);
					else 
					if ((foundMacro.startsWith("if?")) 
					|| (foundMacro.equalsIgnoreCase("else")) 
					|| (foundMacro.equalsIgnoreCase("loop")) 
					|| (foundMacro.equalsIgnoreCase("back"))
					|| (foundMacro.equalsIgnoreCase("endif")) 
					|| (foundMacro.equalsIgnoreCase("/jscript")) 
					|| (foundMacro.equalsIgnoreCase("jscript")))
						s.replace(i, i + foundMacro.length() + 2, foundMacro);
					else
					{
						final int x = foundMacro.indexOf('?');
						final int len = foundMacro.length();
						if (x >= 0)
							foundMacro = foundMacro.substring(0, x);
						if (foundMacro != null)
						{
							final WebMacro W = CMClass.getWebMacro(foundMacro.toUpperCase());
							if (W != null)
								s.replace(i, i + len + 2, foundMacro);
						}
					}
				}
			}
		}
		return s.toString();
	}

	// OK - this parser is getting a bit ugly now;
	// I'm probably gonna replace it soon
	@Override
	public StringBuffer virtualPageFilter(HTTPRequest request, Map<String, Object> objects, long[] processStartTime, String[] lastFoundMacro, StringBuffer s) throws HTTPRedirectException
	{
		String redirectTo = null;
		final boolean debugMacros = CMSecurity.isDebugging(CMSecurity.DbgFlag.HTTPMACROS);
		boolean isAdminServer = false;
		if (Thread.currentThread() instanceof CWThread)
			isAdminServer = CMath.s_bool(((CWThread) Thread.currentThread()).getConfig().getMiscProp("ADMIN"));
		if ((!isAdminServer) 
		&& (processStartTime[0] > 0) 
		&& (System.currentTimeMillis() - processStartTime[0]) > (120 * 1000))
		{
			if (debugMacros)
				Log.infoOut(Thread.currentThread().getName(), "Encountered TIMEOUT!");
			return new StringBuffer("");
		}
		try
		{
			for (int i = 0; i < s.length(); i++)
			{
				if (s.charAt(i) == '@')
				{
					String foundMacro = parseFoundMacro(s, i, lastFoundMacro, false);
					if ((foundMacro != null) && (foundMacro.length() > 0))
					{
						if (debugMacros)
							Log.debugOut("ProcessHTTPRequest", "Found macro: " + foundMacro);
						if (foundMacro.startsWith("if?") || foundMacro.startsWith("IF?"))
						{
							final int l = foundMacro.length() + 2;
							final int v = myEndif(s, i + l, lastFoundMacro);
							if (v < 0)
							{
								if (debugMacros)
									Log.debugOut("ProcessHTTPRequest", "if without endif");
								s.replace(i, i + l, "[if without endif]");
							}
							else
							{
								final int v2 = myElse(s, i + l, v, lastFoundMacro);
								foundMacro = foundMacro.substring(3);
								try
								{
									String compare = "true";
									if (foundMacro.startsWith("!"))
									{
										foundMacro = foundMacro.substring(1);
										compare = "false";
									}
									if (debugMacros)
										Log.debugOut("ProcessHTTPRequest", "Found IF macro: " + foundMacro);
									final String q = runMacro(request, foundMacro, lastFoundMacro, isAdminServer);
									if (debugMacros)
										Log.debugOut("ProcessHTTPRequest", "Ran IF macro: " + foundMacro + "=" + q);
									if ((q != null) && (q.equalsIgnoreCase(compare)))
									{
										if (debugMacros)
											Log.debugOut("ProcessHTTPRequest", "Result IF macro: TRUE");
										if (v2 >= 0)
											s.replace(v2, v + 7, "");
										else
											s.replace(v, v + 7, "");
										s.replace(i, i + l, "");
									}
									else
									{
										if (debugMacros)
											Log.debugOut("ProcessHTTPRequest", "Result IF macro: FALSE");
										if (v2 >= 0)
											s.replace(i, v2 + 6, "");
										else
											s.replace(i, v + 7, "");
									}
								}
								catch (final HTTPRedirectException e)
								{
									if (debugMacros)
										Log.debugOut("ProcessHTTPRequest", "if exception: " + e.getMessage());
									redirectTo = e.getMessage();
								}
							}
							continue;
						}
						else 
						if (foundMacro.equalsIgnoreCase("/jscript"))
						{
							final int l = foundMacro.length() + 2;
							s.replace(i, i + l, "[/jscript without jscript]");
						}
						else 
						if (foundMacro.equalsIgnoreCase("/html"))
						{
							final int l = foundMacro.length() + 2;
							s.replace(i, i + l, "[/html without html]");
						}
						else 
						if (foundMacro.equalsIgnoreCase("jscript"))
						{
							final int l = foundMacro.length() + 2;
							final int v = myEndJScript(s, i + l, lastFoundMacro);
							if (v < 0)
								s.replace(i, i + l, "[jscript without /jscript]");
							else
							{
								final Context cx = Context.enter();
								try
								{
									final String script = s.substring(i + l, v);
									final JScriptablePage scope = new JScriptablePage(request);
									cx.initStandardObjects(scope);
									scope.defineFunctionProperties(JScriptablePage.functions, 
																   JScriptablePage.class, 
																   ScriptableObject.DONTENUM);
									cx.evaluateString(scope, script, "<cmd>", 1, null);
									s.replace(i, v + l + 1, scope.getBuffer());
									i = i + scope.getBuffer().length();
								}
								catch (final Exception e)
								{
									s.replace(i, v + l + 1, "[jscript error: " + e.getMessage() + "]");
								}
								Context.exit();
							}
							continue;
						}
						else 
						if (foundMacro.startsWith("block?") || foundMacro.startsWith("BLOCK?"))
						{
							final int l = foundMacro.length() + 2;
							final int v = myEndBlock(s, i + l, lastFoundMacro);
							if (v < 0)
								s.replace(i, i + l, "[block without /block]");
							else
							{
								final String name = foundMacro.substring(6).trim().toUpperCase();
								objects.put(name, s.substring(i + l, v));
								s.delete(i, v + 8);
							}
							continue;
						}
						else 
						if (foundMacro.startsWith("insert?") || foundMacro.startsWith("INSERT?"))
						{
							final int l = foundMacro.length() + 2;
							final String name = foundMacro.substring(7).trim().toUpperCase();
							final Object o = objects.get(name);
							if (o != null)
								s.replace(i, i + l, o.toString());
							else
								s.delete(i, i + l);
							continue;
						}
						else 
						if (foundMacro.equalsIgnoreCase("endif"))
						{
							s.delete(i, i + 7);
							continue;
						}
						else 
						if (foundMacro.equalsIgnoreCase("else"))
						{
							s.delete(i, i + 6);
							continue;
						}
						else 
						if (foundMacro.equalsIgnoreCase("loop"))
						{
							final int v = myBack(s, i + 6, lastFoundMacro);
							if (v < 0)
								s.replace(i, i + 6, "[loop without back]");
							else
							{
								final String s2 = s.substring(i + 6, v);
								s.replace(i, v + 6, "");
								int ldex = i;
								String s3 = " ";
								while (s3.length() > 0)
								{
									try
									{
										s3 = new String(virtualPageFilter(request, objects, processStartTime, lastFoundMacro, new StringBuffer(s2)));
									}
									catch (final HTTPRedirectException e)
									{
										s3 = " ";
										redirectTo = e.getMessage();
									}

									s.insert(ldex, s3);
									ldex += s3.length();
								}
								if ((!isAdminServer) 
								&& (processStartTime[0] > 0) 
								&& (System.currentTimeMillis() - processStartTime[0]) > (120 * 1000))
								{
									if (debugMacros)
										Log.infoOut(Thread.currentThread().getName(), "Encountered TIMEOUT!");
									return new StringBuffer("");
								}
							}
							continue;
						}
						else 
						if (foundMacro.startsWith("for?") || foundMacro.startsWith("FOR?"))
						{
							final String forCond = foundMacro.substring(4);
							final int fc = forCond.indexOf('=');
							final int v = myNext(s, i + foundMacro.length() + 2, lastFoundMacro);
							if (fc < 0)
								s.replace(i, i + foundMacro.length() + 2, "[for without variabledef=]");
							else 
							if (v < 0)
								s.replace(i, i + foundMacro.length() + 2, "[for without next]");
							else
							{
								final String s2 = s.substring(i + foundMacro.length() + 2, v);
								s.replace(i, v + 6, "");
								if (debugMacros)
									Log.debugOut("ProcessHTTPRequest", "Found FOR macro: " + foundMacro);
								final String varName = forCond.substring(0, fc).trim().toUpperCase();
								String q = runMacro(request, forCond.substring(fc + 1).trim(), lastFoundMacro, isAdminServer);
								if (q == null)
									q = forCond.substring(fc + 1).trim();
								if (debugMacros)
									Log.debugOut("ProcessHTTPRequest", "Ran FOR macro: " + foundMacro + "=" + q);
								int ldex = i;
								String s3 = " ";
								List<String> set;
								if ((q == null) || (q.trim().length() == 0) || (q.toLowerCase().indexOf("@break@") >= 0))
									set = new XVector<String>();
								else
									set = CMParms.parseCommas(q, false);
								for (final String qq : set)
								{
									try
									{
										request.addFakeUrlParameter(varName, qq);
										s3 = new String(virtualPageFilter(request, objects, processStartTime, lastFoundMacro, new StringBuffer(s2)));
									}
									catch (final HTTPRedirectException e)
									{
										s3 = " ";
										redirectTo = e.getMessage();
									}

									s.insert(ldex, s3);
									ldex += s3.length();
								}
								if ((!isAdminServer) 
								&& (processStartTime[0] > 0) 
								&& (System.currentTimeMillis() - processStartTime[0]) > (120 * 1000))
								{
									if (debugMacros)
										Log.infoOut(Thread.currentThread().getName(), "Encountered TIMEOUT!");
									return new StringBuffer("");
								}
							}
							continue;
						}
						else 
						if (foundMacro.equalsIgnoreCase("break"))
						{
							if (debugMacros)
								Log.infoOut(Thread.currentThread().getName(), "Encountered BREAK! at " + i);
							return new StringBuffer("");
						}
						else 
						if (foundMacro.equalsIgnoreCase("back"))
						{
							s.replace(i, i + 6, "[back without loop]");
							continue;
						}
						else 
						if (foundMacro.equalsIgnoreCase("if"))
						{
							s.replace(i, i + 6, "[naked if condition]");
							continue;
						}
						else 
						if (foundMacro.equalsIgnoreCase("block"))
						{
							s.replace(i, i + 6, "[unnamed block]");
							continue;
						}
						else 
						if (foundMacro.equalsIgnoreCase("insert"))
						{
							s.replace(i, i + 6, "[naked block insert]");
							continue;
						}
						else 
						if (foundMacro.equalsIgnoreCase("for"))
						{
							s.replace(i, i + 6, "[naked for iterator]");
							continue;
						}
						else 
						if (foundMacro.equalsIgnoreCase("next"))
						{
							s.replace(i, i + 6, "[next without for]");
							continue;
						}

						if (foundMacro.length() > 0)
						{
							try
							{
								final int l = foundMacro.length();
								final String q = runMacro(request, foundMacro, lastFoundMacro, isAdminServer);
								if (debugMacros)
									Log.debugOut("ProcessHTTPRequest", "Ran Macro: " + foundMacro + "=" + q);
								if (q != null)
								{
									if ((debugMacros) && (q.toUpperCase().indexOf("@BREAK@") >= 0))
										Log.infoOut(Thread.currentThread().getName(), "WebMacro:" + foundMacro + " generated a BREAK! at " + i);
									s.replace(i, i + l + 2, q);
								}
								else
									s.replace(i, i + l + 2, "[error]");
							}
							catch (final HTTPRedirectException e)
							{
								// can't just do this:
								// throw e;
								// since we want ALL macros on the page to run
								redirectTo = e.getMessage();
								// doesn't bother to replace original
								// macro text; page will never be seen
								// (replaced by redirection page)
							}
						}
					}
				}
			}
		}
		catch (final Exception e)
		{
			final String errMsg = e.getMessage() == null ? e.toString() : e.getMessage();
			if ((errMsg != null) 
			&& ((!Log.isMaskedErrMsg(errMsg)) || (CMSecurity.isDebugging(CMSecurity.DbgFlag.HTTPMACROS))))
			{
				Log.errOut(Thread.currentThread().getName(), "Exception in virtualPageFilter() - " + errMsg);
				Log.errOut(Thread.currentThread().getName(), e);
			}
		}

		if (redirectTo != null)
		{
			throw new HTTPRedirectException(redirectTo);
		}

		return s;
	}

	protected String runMacro(HTTPRequest request, String foundMacro, final String[] lastFoundMacro, final boolean isAdminServer) throws HTTPRedirectException, HTTPServerException
	{
		final int x = foundMacro.indexOf('?');
		StringBuffer parms = null;
		if (x >= 0)
		{
			parms = new StringBuffer(foundMacro.substring(x + 1));
			foundMacro = foundMacro.substring(0, x);
			int y = parms.indexOf("@");
			while (y >= 0)
			{
				final String newFoundMacro = parseFoundMacro(parms, y, lastFoundMacro, false);
				if ((newFoundMacro != null) && (newFoundMacro.length() > 0))
				{
					final int l = newFoundMacro.length();
					final String qq = runMacro(request, newFoundMacro, lastFoundMacro, isAdminServer);
					if (qq != null)
						parms.replace(y, y + l + 2, qq);
					else
						parms.replace(y, y + l + 2, "[error]");
				}
				else
					break;
				y = parms.indexOf("@");
			}
		}
		if (foundMacro.length() == 0)
			return "";
		final WebMacro W = CMClass.getWebMacro(foundMacro.toUpperCase());
		if (W != null)
		{
			String q = null;
			if (!isAdminServer && W.isAdminMacro())
			{
				Log.errOut(Thread.currentThread().getName(), "Non-admin cannot access admin macro '" + W.name() + "'; client IP: " + request.getClientAddress());
				q = "[error]";
			}
			else 
			if (W.preferBinary())
			{
				final byte[] bin = W.runBinaryMacro(request, (parms == null) ? null : parms.toString(), null);
				if (bin == null)
					q = " @break@";
				else
					q = new String(bin);
			}
			else
				q = W.runMacro(request, (parms == null) ? null : parms.toString(), null);
			if (q != null)
				return q;
			return "[error]";
		}
		return null;
	}

	protected String parseFoundMacro(StringBuffer s, int i, String[] lastFoundMacro, boolean lookOnly)
	{
		String foundMacro = null;
		boolean extend = false;
		if ((i < s.length() - 2) 
		&& ((s.charAt(i + 1) == 'X') || (s.charAt(i + 1) == 'x')) 
		&& (Character.isDigit(s.charAt(i + 2)) || (s.charAt(i + 1) == 'x') || (s.charAt(i + 1) == 'X')))
			return null;
		for (int x = i + 1; x < s.length(); x++)
		{
			if ((s.charAt(x) == '@') 
			&& (extend) 
			&& (x < (s.length() - 1)) 
			&& (s.charAt(x + 1) == '@'))
			{
				if (!lookOnly)
					s.deleteCharAt(x);
				while ((x < s.length()) && (s.charAt(x) == '@'))
					x++;
				x--;
			}
			else 
			if ((s.charAt(x) == '@') 
			&& ((!extend) || (x >= s.length() - 1) || (s.charAt(x + 1) != '@')))
			{
				foundMacro = s.substring(i + 1, x);
				break;
			}
			else 
			if ((s.charAt(x) == '?') 
			&& (Character.isLetterOrDigit(s.charAt(x - 1))))
				extend = true;
			else 
			if (((x - i) > CMClass.longestWebMacro) && (!extend))
				break;
		}
		lastFoundMacro[0] = foundMacro;
		return foundMacro;
	}

	protected int myElse(StringBuffer s, int i, int end, String[] lastFoundMacro)
	{
		int endifsToFind = 1;
		for (; i < end; i++)
		{
			if (s.charAt(i) == '@')
			{
				final String foundMacro = parseFoundMacro(s, i, lastFoundMacro, true);
				if ((foundMacro != null) 
				&& (foundMacro.length() > 0))
				{
					if (foundMacro.startsWith("if?"))
						endifsToFind++;
					else 
					if (foundMacro.equalsIgnoreCase("endif"))
					{
						endifsToFind--;
						if (endifsToFind <= 0)
							return -1;
					}
					else 
					if ((foundMacro.equalsIgnoreCase("else")) 
					&& (endifsToFind == 1))
						return i;
				}
			}
		}
		return -1;
	}

	protected int myBack(StringBuffer s, int i, String[] lastFoundMacro)
	{
		int backsToFind = 1;
		for (; i < s.length(); i++)
		{
			if (s.charAt(i) == '@')
			{
				final String foundMacro = parseFoundMacro(s, i, lastFoundMacro, true);
				if ((foundMacro != null) 
				&& (foundMacro.length() > 0))
				{
					if (foundMacro.equalsIgnoreCase("loop"))
						backsToFind++;
					else 
					if (foundMacro.equalsIgnoreCase("back"))
					{
						backsToFind--;
						if (backsToFind <= 0)
							return i;
					}
				}
			}
		}
		return -1;
	}

	protected int myNext(StringBuffer s, int i, String[] lastFoundMacro)
	{
		int nextsToFind = 1;
		for (; i < s.length(); i++)
		{
			if (s.charAt(i) == '@')
			{
				final String foundMacro = parseFoundMacro(s, i, lastFoundMacro, true);
				if ((foundMacro != null) 
				&& (foundMacro.length() > 0))
				{
					if (foundMacro.equalsIgnoreCase("for"))
						nextsToFind++;
					else 
					if (foundMacro.equalsIgnoreCase("next"))
					{
						nextsToFind--;
						if (nextsToFind <= 0)
							return i;
					}
				}
			}
		}
		return -1;
	}

	protected int myEndif(StringBuffer s, int i, String[] lastFoundMacro)
	{
		int endifsToFind = 1;
		for (; i < s.length(); i++)
		{
			if (s.charAt(i) == '@')
			{
				final String foundMacro = parseFoundMacro(s, i, lastFoundMacro, true);
				if ((foundMacro != null) && (foundMacro.length() > 0))
				{
					if (foundMacro.startsWith("if?"))
						endifsToFind++;
					else 
					if (foundMacro.equalsIgnoreCase("endif"))
					{
						endifsToFind--;
						if (endifsToFind <= 0)
							return i;
					}
				}
			}
		}
		return -1;
	}

	private int myEndJScript(StringBuffer s, int i, String[] lastFoundMacro)
	{
		for (; i < s.length(); i++)
		{
			if (s.charAt(i) == '@')
			{
				final String foundMacro = parseFoundMacro(s, i, lastFoundMacro, true);
				if ((foundMacro != null) && (foundMacro.length() > 0))
				{
					if (foundMacro.equalsIgnoreCase("/jscript"))
						return i;
				}
			}
		}
		return -1;
	}

	private int myEndBlock(StringBuffer s, int i, String[] lastFoundMacro)
	{
		for (; i < s.length(); i++)
		{
			if (s.charAt(i) == '@')
			{
				final String foundMacro = parseFoundMacro(s, i, lastFoundMacro, true);
				if ((foundMacro != null) && (foundMacro.length() > 0))
				{
					if (foundMacro.equalsIgnoreCase("/block"))
						return i;
				}
			}
		}
		return -1;
	}

	protected static class JScriptablePage extends ScriptableObject
	{
		@Override
		public String getClassName()
		{
			return "JScriptablePage";
		}

		static final long	serialVersionUID	= 43;
		StringBuffer		buf					= new StringBuffer("");

		public void write(Object O)
		{
			buf.append(Context.toString(O));
		}

		public String getBuffer()
		{
			return buf.toString();
		}

		HTTPRequest	req	= null;

		public HTTPRequest request()
		{
			return req;
		}

		public JScriptablePage(HTTPRequest requests)
		{
			req = requests;
		}

		public static String[]	functions	= { "request", "write", "toJavaString" };

		public String toJavaString(Object O)
		{
			return Context.toString(O);
		}
	}

	@Override
	public void init()
	{
	}

	@Override
	public void doGet(SimpleServletRequest request, SimpleServletResponse response) throws HTTPException
	{
		final String[] url = request.getUrlPath().split("/");
		if (url.length > 0)
		{
			String macroName = url[url.length - 1];
			final int x = macroName.indexOf('?');
			if (x > 0)
				macroName = macroName.substring(0, x);
			final WebMacro W = CMClass.getWebMacro(macroName.toUpperCase());
			if (W == null)
			{
				Log.errOut("WebMacroCreamer", "No web macro: '" + macroName + "'");
			}
			else 
			if (!W.isAWebPath())
			{
				Log.errOut("WebMacroCreamer", "Macro " + macroName + " is not a web path.");
			}
			else
			{
				try
				{
					try
					{
						response.setHeader("Content-Type", MIMEType.All.getMIMEType("").getType());
						byte[] responseData;
						if (W.preferBinary())
							responseData = W.runBinaryMacro(request, "", response);
						else
							responseData = W.runMacro(request, "", response).getBytes();
						response.getOutputStream().write(responseData);
					}
					catch (final HTTPServerException e)
					{
						if(e.getCause() instanceof HTTPException)
							throw (HTTPException)e.getCause();
						try
						{
							response.getOutputStream().write(HTTPException.standardException(HTTPStatus.S500_INTERNAL_ERROR).generateOutput(request).flushToBuffer().array());
						}
						catch (final HTTPException e1)
						{
						}
					}
				}
				catch (final IOException e2)
				{
				}
			}
		}
	}

	@Override
	public void doPost(SimpleServletRequest request, SimpleServletResponse response) throws HTTPException
	{
		doGet(request, response);
	}

	@Override
	public void service(HTTPMethod method, SimpleServletRequest request, SimpleServletResponse response) throws HTTPException
	{
	}

	@Override
	public boolean activate()
	{
		final String flag = Resources.getPropResource("WEBMACROCREAMER", "RUNYAHOOMSGGRABBER");
		if (flag.equalsIgnoreCase("TRUE"))
		{
			int tickCount = CMath.s_int(Resources.getPropResource("WEBMACROCREAMER", "YAHOOTICKSPERRUN"));
			if (tickCount < 2)
				tickCount = 2;
			name = "THCreamer" + Thread.currentThread().getThreadGroup().getName().charAt(0);
			serviceClient = CMLib.threads().startTickDown(this, Tickable.TICKID_SUPPORT | Tickable.TICKID_SOLITARYMASK, tickCount);
		}
		return true;
	}

	@Override
	public boolean shutdown()
	{
		if (CMLib.threads().isTicking(this, TICKID_SUPPORT | Tickable.TICKID_SOLITARYMASK))
		{
			CMLib.threads().deleteTick(this, TICKID_SUPPORT | Tickable.TICKID_SOLITARYMASK);
			serviceClient = null;
		}
		return true;
	}

	@Override
	public TickClient getServiceClient()
	{
		return serviceClient;
	}

	@Override
	public int getTickStatus()
	{
		return tickStatus;
	}

	protected YahooGroupSession	yahooSession	= null;

	@Override
	public boolean tick(Tickable ticking, int tickID)
	{
		try
		{
			tickStatus = Tickable.STATUS_ALIVE;
			final String timesPerRunStr = Resources.getPropResource("WEBMACROCREAMER", "TIMESPERRUN");
			if (!CMath.isInteger(timesPerRunStr))
			{
				Log.errOut("WebMacroCreamer var TIMESPERRUN not properly set.");
				return true;
			}
			if (yahooSession == null)
			{
				final String yahooUsername = Resources.getPropResource("WEBMACROCREAMER", "YAHOOUSER");
				if (yahooUsername.length() == 0)
				{
					Log.errOut("WebMacroCreamer var YAHOOUSER not properly set.");
					return true;
				}
				final String yahooPassword = Resources.getPropResource("WEBMACROCREAMER", "YAHOOPASSWORD");
				if (yahooUsername.length() == 0)
				{
					Log.errOut("WebMacroCreamer var YAHOOPASSWORD not properly set.");
					return true;
				}
				final String yahooUrl = Resources.getPropResource("WEBMACROCREAMER", "YAHOOURL");
				if (yahooUrl.length() == 0)
				{
					Log.errOut("WebMacroCreamer var YAHOOURL not properly set.");
					return true;
				}
				final String forumName = Resources.getPropResource("WEBMACROCREAMER", "FORUM");
				if (forumName.length() == 0)
				{
					Log.errOut("WebMacroCreamer var FORUM not properly set.");
					return true;
				}
				final String skipList = Resources.getPropResource("WEBMACROCREAMER", "SKIPLIST");
				final int[] skipInts = CMParms.toIntArray(CMParms.parseCommas(skipList, true));
				yahooSession = new YahooGroupSession();
				yahooSession.url = yahooUrl;
				yahooSession.user = yahooUsername;
				yahooSession.password = yahooPassword;
				yahooSession.numTotalTimes = -1;
				yahooSession.numTimes = 1;
				yahooSession.journal = forumName;
				yahooSession.skipList = skipInts;
				Arrays.sort(yahooSession.skipList);
			}
			if (yahooSession.numTotalTimes <= 0)
			{
				if (yahooSession.H != null)
				{
					yahooSession.H.finished();
					yahooSession.H = null;
				}
				yahooSession.H = (HttpClient) CMClass.getCommon("DefaultHttpClient");
				try
				{
					final String resp = this.loginToYahooSession(yahooSession);
					if (resp.length() > 0)
					{
						Log.warnOut("Yahoo Groups Copier failure reported: " + resp);
						yahooSession.H.finished();
						yahooSession.H = null;
						return true;
					}
					yahooSession.numTotalTimes = CMath.s_int(timesPerRunStr);
				}
				catch (final UnsupportedEncodingException e)
				{
					Log.errOut(Thread.currentThread().getName(), e);
					yahooSession.H.finished();
					yahooSession.H = null;
					return true;
				}
				finally
				{
				}
			}

			yahooSession.numTotalTimes--;
			yahooSession.numTimes = 1;
			final String resp = copyYahooGroupMsg(yahooSession);
			if ((resp != null) && (resp.toLowerCase().startsWith("fail")))
			{
				yahooSession.lastFailedNum = yahooSession.lastMsgNum;
				Log.warnOut("Yahoo Groups Copier failure reported: " + resp);
				yahooSession.lastMsgNum--;
				yahooSession.numTotalTimes = -1;
			}
			else 
			if (yahooSession.lastFailedNum == yahooSession.lastMsgNum)
				Log.warnOut("Yahoo Groups Copier recovered on message " + yahooSession.lastMsgNum);
			Resources.setPropResource("WEBMACROCREAMER", "LASTYAHOOMSGNUMBER", Integer.toString(yahooSession.lastMsgNum));
		}
		finally
		{
			tickStatus = Tickable.STATUS_NOT;
			Resources.savePropResources();
		}
		return true;
	}

	private static class YahooGroupSession
	{
		protected HttpClient	H				= null;
		protected int			numTimes		= -1;
		protected int			numTotalTimes	= -1;
		protected int			lastMsgNum		= -1;
		protected int			numTotal		= -1;
		protected int[]			skipList		= new int[0];
		protected String		url				= "";
		protected String		journal			= "";
		protected String		cookieSet		= "";
		protected String		user			= "";
		protected String		password		= "";
		protected int			lastFailedNum	= -1;
	}

	public String copyYahooGroupMsg(YahooGroupSession sess)
	{
		while ((--sess.numTimes) >= 0)
		{
			sess.lastMsgNum++;
			if (sess.lastMsgNum > sess.numTotal)
			{
				sess.lastMsgNum = sess.numTotal;
				return sess.lastMsgNum + "of " + sess.numTotal + " messages already processed";
			}
			if (Arrays.binarySearch(sess.skipList, sess.lastMsgNum) >= 0)
				continue;
			final byte[] b = sess.H.getRawUrl(sess.url + "/message/" + sess.lastMsgNum, sess.cookieSet);
			if (b == null)
				return "Failed: to read message from url:" + sess.url + "/message/" + sess.lastMsgNum;
			final String msgPage = new String(b);
			int startOfSubject = msgPage.indexOf("<em class=\"msg-bg msg-bd\"");
			if (startOfSubject < 0)
				startOfSubject = msgPage.indexOf("<em class=\"msg-newfont\"");
			if (startOfSubject < 0)
			{
				final int x = msgPage.indexOf("Message  does not exist in ");
				if ((x > 0) && (msgPage.substring(0, x).trim().endsWith("<div class=\"ygrp-contentblock\">")))
				{
					Resources.setPropResource("WEBMACROCREAMER", "LASTYAHOOMSGNUMBER", Integer.toString(sess.lastMsgNum));
					continue;
				}
				return "Failed: to find subject start in url:" + sess.url + "/message/" + sess.lastMsgNum;
			}
			startOfSubject = msgPage.indexOf(">", startOfSubject);
			final int endOfSubject = msgPage.indexOf("</em>", startOfSubject);
			if (endOfSubject < 0)
				return "Failed: to find subject end in url:" + sess.url + "/message/" + sess.lastMsgNum;
			String subject = msgPage.substring(startOfSubject + 1, endOfSubject).trim();
			if ((subject.length() == 0) || (subject.length() > 200) || (subject.indexOf('<') >= 0) || (subject.indexOf('>') >= 0))
				return "Failed: to find VALID subject '" + subject + "' in url:" + sess.url + "/message/" + sess.lastMsgNum;
			int startOfDate = msgPage.indexOf("<span class=\"msg-newfont\" title=\"");
			int endOfDate;
			if (startOfDate > 0)
				startOfDate += 33;// MAGIC NUMBER
			else
			{
				startOfDate = msgPage.indexOf("<span class=\"msg-newfont\" title=\"");
				if (startOfDate < 0)
					return "Failed: to find date start in url:" + sess.url + "/message/" + sess.lastMsgNum;
				startOfDate += 33;// MAGIC NUMBER
			}
			endOfDate = msgPage.indexOf("\"", startOfDate + 1);
			if (endOfDate < 0)
				return "Failed: to find date end in url:" + sess.url + "/message/" + sess.lastMsgNum;
			final String dateStr = msgPage.substring(startOfDate, endOfDate).trim();
			final SimpleDateFormat format = new SimpleDateFormat("yyyy-M-d'T'HH:mm:ss'Z'");
			Date postDate;
			try
			{
				postDate = format.parse(dateStr);
			}
			catch (final ParseException p)
			{
				return "Failed: to parse date '" + dateStr + "' in url:" + sess.url + "/message/" + sess.lastMsgNum;
			}
			int startOfAuthor = msgPage.indexOf("<span class=\"name\">");
			if (startOfAuthor < 0)
				return "Failed: to find author start in url:" + sess.url + "/message/" + sess.lastMsgNum;
			startOfAuthor = msgPage.indexOf(">", startOfAuthor + 4);
			final int endOfAuthor = msgPage.indexOf("</span>", startOfAuthor);
			if (endOfAuthor < 0)
				return "Failed: to find author end in url:" + sess.url + "/message/" + sess.lastMsgNum;
			String author = msgPage.substring(startOfAuthor + 1, endOfAuthor).trim();
			author = CMStrings.replaceAll(author, "<wbr>", "").trim();
			if (author.indexOf("profiles.yahoo.com") > 0)
				author = author.substring(author.indexOf("\">") + 2, author.lastIndexOf("</a>"));
			if ((author.length() == 0) || (author.length() > 100))
				return "Failed: to find VALID author '" + author + "' in url:" + sess.url + "/message/" + sess.lastMsgNum;
			int startOfMsg = msgPage.indexOf("entry-content");
			if (startOfMsg < 0)
				return "Failed: to find message in url:" + sess.url + "/message/" + sess.lastMsgNum;
			startOfMsg = msgPage.indexOf(">", startOfMsg);
			int endOfMsg = msgPage.indexOf("<tr style=\"height:35px\">", startOfMsg);
			if (endOfMsg < 0)
				return "Failed: to find end of msg in url:" + sess.url + "/message/" + sess.lastMsgNum;
			endOfMsg = msgPage.lastIndexOf("</div>", endOfMsg);
			if (endOfMsg < 0)
				return "Failed: to find end2 of msg in url:" + sess.url + "/message/" + sess.lastMsgNum;
			String theMessage = msgPage.substring(startOfMsg + 1, endOfMsg).trim();
			while (theMessage.startsWith("<br>"))
				theMessage = theMessage.substring(4).trim();
			while (theMessage.endsWith("<br>"))
				theMessage = theMessage.substring(0, theMessage.length() - 4).trim();
			theMessage = CMStrings.replaceAll(theMessage, "\n", "");
			theMessage = CMStrings.replaceAll(theMessage, "\r", "");
			if (theMessage.trim().length() == 0)
				return "Failed: to find lengthy msg in url:" + sess.url + "/message/" + sess.lastMsgNum;
			final JournalsLibrary.ForumJournal forum = CMLib.journals().getForumJournal(sess.journal);
			if (forum == null)
				return "Failed: bad forum given";
			if (author.indexOf('@') >= 0)
			{
				final MOB aM = CMLib.players().getLoadPlayerByEmail(author);
				if (aM != null)
					author = aM.Name();
				else 
				if (CMProps.isUsingAccountSystem())
				{
					final PlayerAccount A = CMLib.players().getLoadAccountByEmail(author);
					if (A == null)
						author = author.substring(0, author.indexOf('@'));
					else
						author = A.getAccountName();
				}
				else
					author = author.substring(0, author.indexOf('@'));
			}
			else 
			if (CMLib.login().isOkName(author, false))
				author = "_" + author;

			String parent = "";
			if (subject.toLowerCase().startsWith("[coffeemud]"))
				subject = subject.substring(11).trim();
			if (subject.toUpperCase().startsWith("RE:"))
			{
				String subj = subject;
				while (subj.toUpperCase().startsWith("RE:") || subj.toLowerCase().startsWith("[coffeemud]"))
				{
					if (subj.toUpperCase().startsWith("RE:"))
						subj = subj.substring(3).trim();
					if (subj.toLowerCase().startsWith("[coffeemud]"))
						subj = subj.substring(11).trim();
				}

				final List<JournalEntry> journalEntries = CMLib.database().DBSearchAllJournalEntries(forum.NAME(), subj);
				if ((journalEntries != null) && (journalEntries.size() > 0))
				{
					JournalEntry WIN = null;
					for (final JournalEntry J : journalEntries)
					{
						if (J.subj().trim().equals(subj))
							WIN = J;
					}
					if (WIN == null)
					{
						for (final JournalEntry J : journalEntries)
						{
							if (J.subj().trim().equalsIgnoreCase(subj))
								WIN = J;
						}
					}
					if (WIN == null)
					{
						for (final JournalEntry J : journalEntries)
						{
							if (J.subj().trim().indexOf(subj) >= 0)
								WIN = J;
						}
					}
					if (WIN == null)
					{
						for (final JournalEntry J : journalEntries)
						{
							if (J.subj().toLowerCase().trim().indexOf(subj.toLowerCase()) >= 0)
								WIN = J;
						}
					}

					if (WIN != null)
						parent = WIN.key();
				}
				if (parent.length() == 0)
					subject = subj;
			}
			final JournalEntry msg = (JournalEntry)CMClass.getCommon("DefaultJournalEntry");
			msg.from (author);
			msg.subj (CMLib.webMacroFilter().clearWebMacros(subject));
			msg.msg (CMLib.webMacroFilter().clearWebMacros(theMessage));
			msg.date (postDate.getTime());
			msg.update (postDate.getTime());
			msg.parent (parent);
			msg.msgIcon ("");
			msg.data ("");
			msg.to ("ALL");
			// check for dups
			final List<JournalEntry> chckEntries = CMLib.database().DBReadJournalMsgsNewerThan(forum.NAME(), "ALL", msg.date() - 1);
			for (final JournalEntry entry : chckEntries)
			{
				if ((entry.date() == msg.date()) 
				&& (entry.from().equals(msg.from())) 
				&& (entry.subj().equals(msg.subj())) 
				&& (entry.parent().equals(msg.parent())))
				{
					Resources.setPropResource("WEBMACROCREAMER", "LASTYAHOOMSGNUMBER", Integer.toString(sess.lastMsgNum));
					return "Msg#" + sess.lastMsgNum + " was a dup!";
				}
			}
			CMLib.database().DBWriteJournal(forum.NAME(), msg);
			if (parent.length() > 0)
				CMLib.database().DBTouchJournalMessage(parent, msg.date());
			CMLib.journals().clearJournalSummaryStats(forum);
			Resources.setPropResource("WEBMACROCREAMER", "LASTYAHOOMSGNUMBER", Integer.toString(sess.lastMsgNum));
		}
		return "Post " + sess.lastMsgNum + " submitted.";
	}

	protected String loginToYahooSession(YahooGroupSession sess) throws UnsupportedEncodingException
	{
		final String loginUrl = "http://login.yahoo.com?login=" + URLEncoder.encode(sess.user, "UTF8") + "&passwd=" + URLEncoder.encode(sess.password, "UTF8");
		final Map<String, List<String>> M = sess.H.getHeaders(loginUrl);
		if (M == null)
			return "Fail: Http error hitting " + loginUrl;
		final StringBuilder cookieSet = new StringBuilder("");
		List<String> cookies = M.get("Set-Cookie");
		if (cookies == null)
			return "Fail: Http get cookies from " + loginUrl;
		for (final String val : cookies)
		{
			if (cookieSet.length() > 0)
				cookieSet.append(" ; ");
			final int x = val.indexOf(';');
			cookieSet.append((x >= 0) ? val.substring(0, x).trim() : val.trim());
		}
		final byte[] b = sess.H.getRawUrl(sess.url + "/messages", cookieSet.toString());
		if (b == null)
			return "Failed: to read page: " + sess.url + "/messages";
		cookies = M.get("Set-Cookie");
		if (cookies != null)
		{
			for (final String val : cookies)
			{
				if (cookieSet.length() > 0)
					cookieSet.append(" ; ");
				final int x = val.indexOf(';');
				cookieSet.append((x >= 0) ? val.substring(0, x).trim() : val.trim());
			}
		}
		sess.cookieSet = cookieSet.toString();
		final StringBuilder s = new StringBuilder(new String(b));
		CMStrings.convertHtmlToText(s);
		final String txt = s.toString();
		int x = txt.indexOf(" of ");
		int num = -1;
		while ((num < 0) && (x >= 0))
		{
			if (Character.isDigit(txt.charAt(x + 4)))
			{
				int y = 4;
				while (Character.isDigit(txt.charAt(x + y)))
					y++;
				num = CMath.s_int(txt.substring(x + 4, x + y));
			}
			else
				x = txt.indexOf(" of ", x + 1);
		}
		if (num < 0)
			return "Failed: No numbers found in " + sess.url + "/messages";
		sess.numTotal = num;
		if (Resources.isPropResource("WEBMACROCREAMER", "LASTYAHOOMSGNUMBER"))
			sess.lastMsgNum = CMath.s_int(Resources.getPropResource("WEBMACROCREAMER", "LASTYAHOOMSGNUMBER"));
		return "";
	}

	@Override
	public String copyYahooGroupMsgs(String user, String password, String url, int numTimes, int[] skipList, String journal)
	{
		final YahooGroupSession sess = new YahooGroupSession();
		sess.url = url;
		sess.user = user;
		sess.password = password;
		sess.numTimes = numTimes;
		sess.skipList = skipList;
		sess.journal = journal;
		sess.H = (HttpClient) CMClass.getCommon("DefaultHttpClient");
		Arrays.sort(sess.skipList);

		try
		{
			final String resp = this.loginToYahooSession(sess);
			if (resp.length() > 0)
				return resp;
			return copyYahooGroupMsg(sess);
		}
		catch (final UnsupportedEncodingException e)
		{
			Log.errOut(Thread.currentThread().getName(), e);
		}
		finally
		{
			sess.H.finished();
		}
		return " @break@";
	}
}