/
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/Specializations/
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/
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/BasicTech/
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.Items.Basic;
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.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.Common.interfaces.Session.InputCallback;
import com.planet_ink.coffee_mud.Exits.interfaces.*;
import com.planet_ink.coffee_mud.Items.interfaces.*;
import com.planet_ink.coffee_mud.Libraries.interfaces.DatabaseEngine;
import com.planet_ink.coffee_mud.Libraries.interfaces.JournalsLibrary;
import com.planet_ink.coffee_mud.Libraries.interfaces.JournalsLibrary.MsgMkrResolution;
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.core.exceptions.HTTPRedirectException;

import java.util.*;
import java.io.IOException;

/*
   Copyright 2006-2019 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 StdBook extends StdItem implements Book
{
	@Override
	public String ID()
	{
		return "StdBook";
	}

	public StdBook()
	{
		super();
		setName("a book");
		setDisplayText("a book sits here.");
		setDescription("Enter `READ [NUMBER] [BOOK]` to read a chapter.%0D%0AUse your WRITE skill to add new chapters. ");
		material=RawMaterial.RESOURCE_PAPER;
		basePhyStats().setSensesMask(PhyStats.SENSE_ITEMREADABLE);
		recoverPhyStats();
	}

	protected int	maxPages		= 0;	// 0=unlimited
	protected int	maxCharsPage	= 0;	// 0=unlimited
	protected MOB	lastReadTo		= null;
	protected long	lastDateRead	= -1;

	@Override
	public boolean okMessage(final Environmental myHost, final CMMsg msg)
	{
		if(msg.amITarget(this))
		{
			switch(msg.targetMinor())
			{
			case CMMsg.TYP_WRITE:
			case CMMsg.TYP_REWRITE:
				// the order that these things are checked in should
				// be holy, and etched in stone.
				int num=numBehaviors();
				MsgListener N=null;
				for(int b=0;b<num;b++)
				{
					N=fetchBehavior(b);
					if((N!=null)&&(!N.okMessage(this,msg)))
						return false;
				}
				num=numScripts();
				for(int s=0;s<num;s++)
				{
					N=fetchScript(s);
					if((N!=null)&&(!N.okMessage(this,msg)))
						return false;
				}
				num=numEffects();
				for(int i=0;i<num;i++)
				{
					N=fetchEffect(i);
					if((N!=null)&&(!N.okMessage(this,msg)))
						return false;
				}
				break;
			default:
				break;
			}

			switch(msg.targetMinor())
			{
			case CMMsg.TYP_WRITE:
			{
				final String adminReq=getAdminReq().trim();
				final boolean admin=(adminReq.length()>0)&&CMLib.masking().maskCheck(adminReq,msg.source(),true);
				if((!CMLib.masking().maskCheck(getWriteReq(),msg.source(),true))
				&&(!admin)
				&&(!(CMSecurity.isAllowed(msg.source(),msg.source().location(),CMSecurity.SecFlag.JOURNALS))))
				{
					msg.source().tell(L("You are not allowed to write on @x1",name()));
					return false;
				}
				if(this.getMaxPages() != 0)
				{
					final int numPages = this.getChapterCount("ALL");
					if(numPages >= this.getMaxPages())
					{
						msg.source().tell(L("All the available pages in @x1 are used.",name()));
						return false;
					}
				}
				return true;
			}
			case CMMsg.TYP_REWRITE:
			{
				final String adminReq=getAdminReq().trim();
				final boolean admin=(adminReq.length()>0)&&CMLib.masking().maskCheck(adminReq,msg.source(),true);
				if((!CMLib.masking().maskCheck(getWriteReq(),msg.source(),true))
				&&(!admin)
				&&(!(CMSecurity.isAllowed(msg.source(),msg.source().location(),CMSecurity.SecFlag.JOURNALS))))
				{
					msg.source().tell(L("You are not allowed to write on @x1",name()));
					return false;
				}
				if(CMath.isInteger(msg.targetMessage()))
				{
					final int msgNum=CMath.s_int(msg.targetMessage());
					if((msgNum <1)||(msgNum>this.getChapterCount("ALL")))
					{
						msg.source().tell(L("There is no Chapter @x1",""+(msgNum)));
						return false;
					}
					if((!admin)
					&&(!(CMSecurity.isAllowed(msg.source(),msg.source().location(),CMSecurity.SecFlag.JOURNALS))))
					{
						final JournalEntry entry = this.readChaptersByCreateDate().get(msgNum-1);
						if(!entry.from().equalsIgnoreCase(msg.source().Name()))
						{
							msg.source().tell(L("You need permission to edit that chapter."));
							return false;
						}
					}
				}
				else
				if(msg.targetMessage().toUpperCase().startsWith("EDIT "))
				{
					final int x=msg.targetMessage().indexOf(' ',5);
					if(x>5)
					{
						final int msgNum=CMath.s_int(msg.targetMessage().substring(5,x).trim());
						if((msgNum <1)||(msgNum>this.getChapterCount("ALL")))
						{
							msg.source().tell(L("There is no Chapter @x1",""+(msgNum)));
							return false;
						}
						if((!admin)
						&&(!(CMSecurity.isAllowed(msg.source(),msg.source().location(),CMSecurity.SecFlag.JOURNALS))))
						{
							final JournalEntry entry = this.readChaptersByCreateDate().get(msgNum-1);
							if(!entry.from().equalsIgnoreCase(msg.source().Name()))
							{
								msg.source().tell(L("You need permission to edit that chapter."));
								return false;
							}
						}
					}
				}
				else
				if(msg.targetMessage().toUpperCase().startsWith("DELETE "))
				{
					final String entryStr=msg.targetMessage().substring(7).trim();
					final int entryNum=CMath.s_int(entryStr);
					final int numEntries = this.getChapterCount("ALL");
					if((entryNum < 1)||(numEntries>numEntries)||(!CMath.isInteger(entryStr)))
					{
						msg.source().tell(L("There is no Chapter @x1",""+(entryNum)));
						return false;
					}
					if((!admin)
					&&(!(CMSecurity.isAllowed(msg.source(),msg.source().location(),CMSecurity.SecFlag.JOURNALS))))
					{
						final JournalEntry entry = this.readChaptersByCreateDate().get(entryNum-1);
						if(!entry.from().equalsIgnoreCase(msg.source().Name()))
						{
							msg.source().tell(L("You need permission to remove that chapter."));
							return false;
						}
					}
				}
				return true;
			}
			}
		}
		return super.okMessage(myHost,msg);
	}

	@Override
	public boolean canRead(final MOB mob)
	{
		final String adminReq=getAdminReq().trim();
		final boolean admin=(adminReq.length()>0)&&CMLib.masking().maskCheck(adminReq,mob,true);
		if((!admin)
		&&(!(CMSecurity.isAllowed(mob,mob.location(),CMSecurity.SecFlag.JOURNALS)))
		&&(!CMLib.masking().maskCheck(getReadReq(),mob,true)))
			return false;
		return true;
	}

	@Override
	public boolean canWrite(final MOB mob)
	{
		final String adminReq=getAdminReq().trim();
		final boolean admin=(adminReq.length()>0)&&CMLib.masking().maskCheck(adminReq,mob,true);
		if((!CMLib.masking().maskCheck(getWriteReq(),mob,true))
		&&(!admin)
		&&(!(CMSecurity.isAllowed(mob,mob.location(),CMSecurity.SecFlag.JOURNALS))))
			return false;
		return true;
	}

	@Override
	public void executeMsg(final Environmental myHost, final CMMsg msg)
	{
		final MOB mob=msg.source();
		if(msg.amITarget(this))
		{
			switch(msg.targetMinor())
			{
			case CMMsg.TYP_READ:
			case CMMsg.TYP_WRITE:
			case CMMsg.TYP_REWRITE:
				// the order that these things are checked in should
				// be holy, and etched in stone.
				if(numBehaviors()>0)
				{
					eachBehavior(new EachApplicable<Behavior>()
					{
						@Override
						public final void apply(final Behavior B)
						{
							B.executeMsg(me,msg);
						}
					});
				}
				if(numScripts()>0)
				{
					eachScript(new EachApplicable<ScriptingEngine>()
					{
						@Override
						public final void apply(final ScriptingEngine S)
						{
							S.executeMsg(me,msg);
						}
					});
				}
				if(numEffects()>0)
				{
					eachEffect(new EachApplicable<Ability>()
					{
						@Override
						public final void apply(final Ability A)
						{
							A.executeMsg(me, msg);
						}
					});
				}
				break;
			default:
				break;
			}
			switch(msg.targetMinor())
			{
			case CMMsg.TYP_READ:
			{
				final Room R=mob.location();
				if(!CMLib.flags().canBeSeenBy(this,mob))
					mob.tell(L("You can't see that!"));
				else
				if((!mob.isMonster())
				&&(mob.playerStats()!=null))
				{
					final String adminReq=getAdminReq().trim();
					final boolean admin=(adminReq.length()>0)&&CMLib.masking().maskCheck(adminReq,mob,true);
					final long lastTime=mob.playerStats().getLastDateTime();
					if((!admin)&&(!CMLib.masking().maskCheck(getReadReq(),mob,true)))
					{
						mob.tell(L("You are not allowed to read @x1.",name()));
						return;
					}
					int which=-1;
					boolean newOnly=false;
					boolean all=false;
					final Vector<String> parse=CMParms.parse(msg.targetMessage());
					for(int v=0;v<parse.size();v++)
					{
						final String s=parse.elementAt(v);
						if(CMath.s_long(s)>0)
							which=CMath.s_int(msg.targetMessage());
						else
						if(s.equalsIgnoreCase("NEW"))
							newOnly=true;
						else
						if(s.equalsIgnoreCase("ALL")||s.equalsIgnoreCase("OLD"))
							all=true;
					}
					final Triad<String,String,StringBuffer> read=DBRead(mob,which-1,lastTime, newOnly, all);
					final StringBuffer entry=read.third;
					if(entry.charAt(0)=='#')
					{
						which=-1;
						entry.setCharAt(0,' ');
					}
					if((entry.charAt(0)=='*')
					   ||(admin)
					   ||(CMSecurity.isAllowed(mob,R,CMSecurity.SecFlag.JOURNALS)))
						entry.setCharAt(0,' ');
					else
					if((newOnly)&&(msg.value()>0))
						return;
					final StringBuffer returnEntry;
					if(read.second.length()>0)
					{
						final int x=read.second.indexOf(":");
						if(x>0)
							returnEntry=new StringBuffer("::"+read.second.substring(x+1)+"::");
						else
							returnEntry=new StringBuffer("::"+read.second+"::");
					}
					else
						returnEntry=new StringBuffer("");
					final String chStart=L("Chapter");
					final String eTrim=entry.toString().trim();
					if(eTrim.startsWith(chStart))
					{
						final int x=eTrim.indexOf(":");
						int eol=-1;
						if((x>0)&&(CMath.isInteger(eTrim.substring(chStart.length(),x).trim())))
						{
							eol=eTrim.indexOf('\n');
							if(eol < 0)
								eol=eTrim.indexOf('\r');
						}
						if(eol>0)
							returnEntry.append("\n"+eTrim.substring(eol).trim());
						else
							returnEntry.append(entry);
					}
					else
						returnEntry.append(entry);
					final CMMsg readMsg=CMClass.getMsg(msg.source(), msg.target(), msg.tool(),
							 CMMsg.MSG_WASREAD|CMMsg.MASK_ALWAYS, L("It says '@x1'.\n\r",entry.toString()),
							 CMMsg.MSG_WASREAD|CMMsg.MASK_ALWAYS, returnEntry.toString(),
							 CMMsg.NO_EFFECT, null);
					//mob.tell(entry.toString()+"\n\r");
					if((entry.toString().trim().length()>0)
					&&(which>0)
					&&(CMLib.masking().maskCheck(getWriteReq(),mob,true)
						||(admin)
						||(CMSecurity.isAllowed(msg.source(),msg.source().location(),CMSecurity.SecFlag.JOURNALS))))
					{
					}
					else
					if(which<0)
					{
						readMsg.setSourceMessage(readMsg.sourceMessage()+description());
					}
					msg.addTrailerMsg(readMsg);
					return;
				}
				return;
			}
			case CMMsg.TYP_WRITE:
			case CMMsg.TYP_REWRITE:
			{
				final MOB M=mob;
				final String[] subject=new String[1];
				final String[] message=new String[1];
				final String editKey[] = new String[1];
				final int maxCharsPerPage = this.getMaxCharsPerPage() > 0 ? this.getMaxCharsPerPage() : Integer.MAX_VALUE;
				if(!mob.isMonster())
				{
					final Runnable addComplete=new Runnable()
					{
						final String to="ALL";
						final MOB mob=M;
						@Override
						public void run()
						{
							final Room R=mob.location();
							final String adminReq=getAdminReq().trim();
							final boolean admin=(adminReq.length()>0)&&CMLib.masking().maskCheck(adminReq,mob,true);
							if(message[0].startsWith("<cmvp>")
							&&(!admin)
							&&(!(CMSecurity.isAllowed(mob,R,CMSecurity.SecFlag.JOURNALS))))
							{
								mob.tell(L("Illegal code, aborted."));
								return;
							}
							if(editKey[0]==null)
							{
								if(message[0].length()>maxCharsPerPage)
									mob.tell(L("That won't fit on the pages for this chapter.  The limit is @x1.",""+maxCharsPerPage));
								else
								{
									addNewChapter(mob.Name(),to,subject[0],message[0]);
									if((R!=null)&&(msg.targetMessage().length()<=1))
										R.send(mob, ((CMMsg)msg.copyOf()).modify(CMMsg.MSG_WROTE, L("Chapter added."), CMMsg.MSG_WROTE, subject+"\n\r"+message, -1, null));
									else
										mob.tell(L("Chapter added."));
								}
							}
							else
							{
								if(message[0].length()>maxCharsPerPage)
									mob.tell(L("That won't fit on the pages for this chapter.  The limit is @x1.",""+maxCharsPerPage));
								else
								{
									editOldChapter(mob.Name(),to,editKey[0],subject[0],message[0]);
									if((R!=null)&&(msg.targetMessage().length()<=1))
										R.send(mob, ((CMMsg)msg.copyOf()).modify(CMMsg.MSG_WROTE, L("Chapter modified."), CMMsg.MSG_WROTE, subject+"\n\r"+message, -1, null));
									else
										mob.tell(L("Chapter modified."));
								}
							}
						}
					};
					if((msg.targetMinor()==CMMsg.TYP_REWRITE)
					&&(msg.targetMessage().toUpperCase().startsWith("DELETE ")))
					{
						final int entryNum=CMath.s_int(msg.targetMessage().substring(7).trim());
						final JournalEntry entry = this.readChaptersByCreateDate().get(entryNum-1);
						delOldChapter(mob.Name(),"ALL",entry.key());
						mob.tell(L("Chapter removed."));
					}
					else
					if((msg.targetMinor()==CMMsg.TYP_REWRITE)
					&&(msg.targetMessage().toUpperCase().startsWith("EDIT ")))
					{
						int x=msg.targetMessage().indexOf(' ',5);
						if(x>5)
						{
							final int msgNum=CMath.s_int(msg.targetMessage().substring(5,x).trim());
							subject[0]=L("Chapter @x1",""+(msgNum));
							message[0]=msg.targetMessage().substring(x+1).trim();
							final JournalEntry entry = this.readChaptersByCreateDate().get(msgNum-1);
							editKey[0] = entry.key();
							if(message[0].startsWith("::"))
							{
								x=message[0].indexOf("::",2);
								if(x>1)
								{
									subject[0]=message[0].substring(2,x);
									message[0]=message[0].substring(x+2);
								}
							}
							addComplete.run();
						}
					}
					else
					if((msg.targetMessage().length()>1)
					&&(msg.targetMinor()==CMMsg.TYP_WRITE)
					&&(!CMath.isInteger(msg.targetMessage())))
					{
						message[0]=msg.targetMessage();
						final int numMessages=getChapterCount("ALL");
						subject[0]=L("Chapter @x1",""+(numMessages+1));
						if(message[0].startsWith("::"))
						{
							final int x=message[0].indexOf("::",2);
							if(x>1)
							{
								subject[0]=message[0].substring(2,x);
								message[0]=message[0].substring(x+2);
							}
						}
						addComplete.run();
					}
					else
					{
						final InputCallback contentCallBack = new InputCallback(InputCallback.Type.PROMPT,"",0)
						{
							final MOB mob=M;
							final Runnable completer=addComplete;
							@Override
							public void showPrompt()
							{
								if((subject[0]!=null)&&(subject[0].length()>0))
									mob.session().promptPrint(L("Enter the name of the chapter ("+subject[0]+"): "));
								else
									mob.session().promptPrint(L("Enter the name of the chapter: "));
							}

							@Override
							public void timedOut()
							{
							}

							@Override
							public void callBack()
							{
								String subj=this.input.trim();
								if((subj.trim().length()==0)
								&&((subject[0]==null)||(subject[0].length()==0)))
									subj=subject[0];
								if((subj == null) || (subj.trim().length()==0))
								{
									mob.tell(L("Aborted."));
									return;
								}
								subject[0]=subj;
								final String messageTitle="The contents of this chapter";
								mob.session().println(L("\n\rEnter the contents of this chapter:"));
								final List<String> vbuf=new ArrayList<String>();
								if(message[0]!=null)
									vbuf.addAll(CMParms.parseAny(message[0],"\\n",false));
								CMLib.journals().makeMessageASync(mob, messageTitle, vbuf, true, new JournalsLibrary.MsgMkrCallback()
								{
									@Override
									public void callBack(final MOB mob, final Session sess, final MsgMkrResolution res)
									{
										if(res ==JournalsLibrary.MsgMkrResolution.CANCELFILE)
										{
											mob.tell(L("Aborted."));
											return;
										}
										message[0]=CMParms.combineWith(vbuf, "\\n");
										completer.run();
									}
								});
							}
						};
						if((CMath.isInteger(msg.targetMessage()))
						&&(msg.targetMinor()==CMMsg.TYP_REWRITE))
						{
							try
							{
								final JournalEntry entry = this.readChaptersByCreateDate().get(CMath.s_int(msg.targetMessage())-1);
								if(entry != null)
								{
									subject[0]=entry.subj();
									message[0]=entry.msg();
									editKey[0] = entry.key();
									msg.setTargetMessage("");
								}
							}
							catch(final Exception e)
							{
							}
						}
						mob.session().prompt(contentCallBack);
					}
				}
				return;
			}
			default:
				break;
			}
		}
		super.executeMsg(myHost,msg);
	}

	protected String getTOCHeader()
	{
		return L("\n\rTable of Contents\n\r");
	}

	protected String getJournalName()
	{
		return Name();
	}

	protected int getChapterCount(final String to)
	{
		return CMLib.database().DBCountJournal(getJournalName(), null, to);
	}

	protected List<JournalEntry> readChaptersByCreateDate()
	{
		return CMLib.database().DBReadJournalMsgsByCreateDate(getJournalName(), true);
	}

	protected void addNewChapter(final String from, final String to, final String subject, final String message)
	{
		CMLib.database().DBWriteJournal(getJournalName(),from,to,subject,message);
	}

	protected void editOldChapter(final String from, final String to, final String key, final String subject, final String message)
	{
		final JournalEntry entry = CMLib.database().DBReadJournalEntry(getJournalName(), key);
		entry.from(from);
		entry.to(to);
		entry.subj(subject);
		entry.msg(message);
		CMLib.database().DBUpdateJournal(getJournalName(), entry);
	}

	protected void delOldChapter(final String from, final String to, final String key)
	{
		CMLib.database().DBDeleteJournal(getJournalName(), key);
	}

	public Triad<String,String,StringBuffer> DBRead(final MOB readerMOB, final int which, final long lastTimeDate, final boolean newOnly, final boolean all)
	{
		final StringBuffer buf=new StringBuffer("");
		final Triad<String,String,StringBuffer> reply=new Triad<String,String,StringBuffer>("","",new StringBuffer(""));
		final List<JournalEntry> journal=this.readChaptersByCreateDate();
		if((which<0)||(journal==null)||(which>=journal.size()))
		{
			buf.append(getTOCHeader());
			buf.append("-------------------------------------------------------------------------\n\r");
			if(journal==null)
			{
				reply.first="";
				reply.second="";
				reply.third = buf;
				return reply;
			}
		}

		if((which<0)||(which>=journal.size()))
		{
			if(journal.size()>0)
			{
				reply.first = journal.get(0).from();
				reply.second = journal.get(0).subj();
			}
			final ArrayList<Object> selections=new ArrayList<Object>();
			for(int j=0;j<journal.size();j++)
			{
				final JournalEntry entry=journal.get(j);
				final String from=entry.from();
				final String to=entry.to();
				final String subject=entry.subj();
				final StringBuffer selection=new StringBuffer("");
				if(to.equals("ALL")
				||((readerMOB!=null)&&to.equalsIgnoreCase(readerMOB.Name()))
				||((readerMOB!=null)&&from.equalsIgnoreCase(readerMOB.Name()))
				||((readerMOB!=null)&&to.toUpperCase().trim().startsWith("MASK=")&&CMLib.masking().maskCheck(to.trim().substring(5),readerMOB,true)))
				{
					//if(CMath.s_long(compdate)>lastTimeDate)
					//    selection.append("*");
					//else
					//if(newOnly)
					//    continue;
					//else
					//    selection.append(" ");
					selection.append(subject+"\n\r");
					selections.add(selection);
				}
			}
			int numToAdd=CMProps.getIntVar(CMProps.Int.JOURNALLIMIT);
			if((numToAdd==0)||(all))
				numToAdd=Integer.MAX_VALUE;
			for(int v=selections.size()-1;v>=0;v--)
			{
				if (numToAdd == 0)
				{
					selections.set(v,"");
					continue;
				}
				final StringBuffer str = (StringBuffer) selections.get(v);
				if ((newOnly) && (str.charAt(0) != '*'))
				{
					selections.set(v,"");
					continue;
				}
				numToAdd--;
			}
			boolean notify=false;
			for(int v=0;v<selections.size();v++)
			{
				if(!(selections.get(v) instanceof StringBuffer))
				{
					notify=true;
					continue;
				}
				buf.append((StringBuffer)selections.get(v));
			}
			if(notify)
				buf.append(L("\n\rUse READ ALL [BOOK] to see missing chapters."));
		}
		else
		{
			final JournalEntry entry=journal.get(which);
			final String from=entry.from();
			final String to=entry.to();
			final String subject=entry.subj();
			String message=entry.msg();

			reply.first = entry.from();
			reply.second = entry.subj();

			//String compdate=(String)entry.elementAt(6);
			final boolean mineAble;
			if(readerMOB!=null)
				mineAble=to.equalsIgnoreCase(readerMOB.Name())
							||(to.toUpperCase().trim().startsWith("MASK=")&&(CMLib.masking().maskCheck(to.trim().substring(5),readerMOB,true)))
							||from.equalsIgnoreCase(readerMOB.Name());
			else
				mineAble=true;

			if(mineAble)
				buf.append("*");
			else
				buf.append(" ");
			try
			{
				if(message.startsWith("<cmvp>"))
					message=new String(CMLib.webMacroFilter().virtualPageFilter(message.substring(6).getBytes()));
			}
			catch(final HTTPRedirectException e)
			{
			}

			if(to.equals("ALL")||mineAble)
				buf.append("\n\r"+subject
						   +"\n\r"+message);
		}
		reply.third = buf;
		return reply;
	}

	@Override
	public int getUsedPages()
	{
		return this.getChapterCount("ALL");
	}

	@Override
	public int getMaxPages()
	{
		return this.maxPages;
	}

	@Override
	public void setMaxPages(final int max)
	{
		this.maxPages=max;
	}


	@Override
	public int getMaxCharsPerPage()
	{
		return this.maxCharsPage;
	}

	@Override
	public void setMaxCharsPerPage(final int max)
	{
		this.maxCharsPage=max;
	}

	@Override
	public String getRawContent(final int page)
	{
		final List<JournalEntry> journal=this.readChaptersByCreateDate();
		if((page < 1)||(page>journal.size()))
			return "";
		else
		{
			final JournalEntry J=journal.get(page-1);
			if((J.subj()!=null)&&(J.subj().length()>0))
				return "::"+J.subj()+"::"+J.msg();
			else
				return "::"+J.subj()+"::"+J.msg();
		}
	}

	@Override
	public String getContent(final int page)
	{
		final Triad<String,String,StringBuffer> t=this.DBRead(null, page-1, 0, false, false);
		if((t.second!=null)&&(t.second.length()>0))
			return "::"+t.second+"::"+t.third.toString();
		else
			return t.third.toString();
	}

	@Override
	public void addRawContent(final String authorName, final String content)
	{
		if(content.startsWith("::")&&(content.length()>2)&&(content.charAt(2)!=':'))
		{
			final int x=content.indexOf("::",2);
			if(x>2)
				addNewChapter(authorName,"ALL",content.substring(2,x),content.substring(x+2));
			else
				addNewChapter(authorName,"ALL","",content);
		}
		else
			addNewChapter(authorName,"ALL","",content);
	}

	@Override
	public boolean isJournal()
	{
		return false;
	}

	protected String getParm(final String parmName)
	{
		if(readableText().length()==0)
			return "";
		final Map<String,String> h=CMParms.parseEQParms(readableText().toUpperCase(), new String[]{"KEY","READ","WRITE","REPLY","ADMIN"});
		String req=h.get(parmName.toUpperCase().trim());
		if(req==null)
			req="";
		return req;
	}

	protected String getReadReq()
	{
		return getParm("READ");
	}

	protected String getWriteReq()
	{
		return getParm("WRITE");
	}

	private String getAdminReq()
	{
		return getParm("ADMIN");
	}

	@Override
	public void recoverPhyStats()
	{
		CMLib.flags().setReadable(this, true);
		super.recoverPhyStats();
	}
}