Monday, November 08, 2010 at 12:04 AM.

system.verbs.builtins.mainResponder.neuterText

on neuterText (s, flNeuterMacros=true, flNeuterTags=true, adrLegalTags=nil) {
	<<Neuter HTML tags and macros in a string of text.
		<<Changes:
			<<Sat, May 1, 1999 at 11:23:41 AM by SMD
				<<allow each item in config.mainresponder.prefs.legaltags to be a table
				<<if it is a table, then it must contain two entries:
					<<flLegal - is the tag legal, or should it be neutered
					<<flClose - must the tag be closed?
				<<if the item isn't a table, then it is assumed that the tag should be closed (thus preserving previous functionality)
			<<Sat, 01 May 1999 18:57:16 GMT by AR
				<<Minor code rorganizations.
			<<Sat, 22 May 1999 19:15:42 GMT by AR
				<<Added safeScriptParser sub-routine.
			<<10/4/99; 6:14:32 PM by PBS
				<<It's not enough to allow only macros that are one-word identifiers. Better to allow the sysop to configure which macros are legal via a legalMacros table, similar to the legalTags table.
				<<This table lives at config.mainResponder.prefs.legalMacros.
				<<The top-level subtables are names of macros. Inside each subtable is one item: flLegal.
				<<If a macro isn't in the legalMacros table, it gets neutered.
				<<If flLegal is true, then macros which otherwise pass the tests are okay. If false, then the macro gets neutered.
				<<We made items in this table subtables, rather than booleans, so the mechanism can be extended later.
				<<But, like legalTags, you can use simple booleans instead.
			<<11/3/99; 9:47:34 AM by PBS
				<<There's now one version of this script that works with both 6.0 and 6.1 and greater.
				<<Only the 6.1 version allows safe macros with parameters. In 6.0 safe macros can be identifiers only.
				<<If pta^.legalTags is defined, use the table that points to rather than the global table at config.mainResponder.prefs.legalTags. This way sites can over-ride the global settings.
			<<03/15/00; 6:06:36 PM by PBS
				<<Check the value of pta^.enableSafeMacros -- if it's false, macros should be neutered.
			<<03/28/01; 5:51:26 PM by PBS
				<<New optional parameter: adrLegalTags is the address of a legal tags table to use. This way, when this script is called as part of an XML-RPC handler, we can use the legal tags table for the site.
	
	local (flUseKernelVerbs = false);
	if defined (html.neuterTags) {
		flUseKernelVerbs = true};
	
	local (pta);
	try {pta = html.getPageTableAddress ()};
	
	if flNeuterMacros {
		local (adrTable = @config.mainResponder.prefs.legalMacros); //global table only
		if (defined (adrTable^)) and (pta != nil) and (defined (pta^.enableSafeMacros)) and (pta^.enableSafeMacros) { //neuter all but legal macros. PBS 03/15/00: check the value of pta^.enableSafeMacros. It must be true, or macros will be neutered.
			if flUseKernelVerbs {
				s = html.neuterMacros (s, adrTable)} //html.neuterMacros is a new kernel verb in 6.1
			else {
				on safeScriptParser (s) {
					if s == "" {
						return ""};
					local (i = 1, balance=0, j=0, macrostring, safeTo=0);
					on isSafe (m) {
						m = string.trimWhiteSpace (m);
						local (ct = string.length (m));
						if ct > 0 {
							if not (string.isAlpha (m[1])) {
								return false};
							while (ct > 1) {
								if not (string.isAlpha (m[ct]) or string.isNumeric (m[ct])) {
									return false};
								ct--}};
						
						bundle { //PBS 10/4/99: check the legal macros table at config.mainResponder.prefs.legalMacros.
							if not defined (adrTable^) { //PBS 10/4/99: if the legal macros table isn't defined, then this macro isn't legal
								return (false)};
							if not defined (adrTable^.[m]) { //PBS 10/4/99: if this macro isn't in the legal macros table, then this macro isn't legal
								return (false)};
							case typeOf (adrTable^.[m]) { //handle tables or simple booleans
								tableType {
									if not defined (adrTable^.[m].flLegal) { //PBS 10/4/99: if there's no flLegal boolean for this macro, then this macro isn't legal
										return (false)};
									if not (adrTable^.[m].flLegal) { //PBS 10/4/99: if the flLegal boolean is false, then this macro isn't legal
										return (false)}};
								booleanType {
									if not (adrTable^.[m]) {
										return (false)}}}
							else {
								return (false)}};
						
						return true};
					while (i <= sizeof(s)) {
						case s[i] {
							'{' {
								if balance == 0 { //remember position of outer opening brace
									j = i};
								balance++};
							'}' {
								if balance > 0 {
									balance--;
									if balance == 0 { //found outer closing brace
										macrostring = string.mid (s, j+1, i-j-1);
										if not (isSafe (macrostring)) {
											local (repl = macrostring);
											repl = string.replaceAll (repl, "{", "{");
											repl = string.replaceAll (repl, "}", "}");
											repl = "{" + repl + "}";
											s = string.delete (s, j, i-j+1);
											s = string.insert (repl, s, j);
											i = i + string.length (repl) - (string.length (macrostring) + 2)};
										j = 0;
										safeTo = i}} //remember until what position the string is safe (for final cleanup)
								else { //convert to html entity
									local (repl = "}");
									s = string.delete (s, i, 1);
									s = string.insert (repl, s, i);
									i = i - 1 + string.length (repl)}}};
						i++};
					if balance > 0 { //there's cleanup to do
						local (repl = string.mid (s, safeTo + 1, infinity));
						repl = string.replaceAll (repl, "{", "{");
						repl = string.replaceAll (repl, "}", "}");
						s = string.delete (s, safeTo + 1, infinity);
						s = s + repl};
					
					return (s)};
				s = safeScriptParser (s)}}
		else { //no safe macros, neuter all macros
			s = string.replaceall (s, "{", "{")}};
	
	if flNeuterTags {
		local (adrTable = @config.mainResponder.prefs.legalTags);
		if (pta != nil) and defined (pta^.legalTags) {
			adrTable = pta^.legalTags}; //a site may over-ride the global legal tags table with its own table
		if adrLegalTags != nil { //PBS 03/28/01: caller can specify a legal tags table
			adrTable = adrLegalTags};
		if flUseKernelVerbs {
			s = html.neuterTags (s,  adrTable)}
		else { //use the script-based version
			local (legaltags); //copy the table
			bundle { //initialize legaltags table
				local (i);
				legaltags = adrTable^;
				for i = sizeof (legaltags) downto 1 { //delete all the false ones, set the remaining to 0
					if typeof (legaltags [i]) != tableType { // if the legaltags item isn't a table, make it one (in the local copy)
						local (fl = legaltags [i]);
						new (tabletype, @legaltags.[nameof(legaltags [i])]);
						legaltags[i].flLegal = fl;
						legaltags[i].flClose = true};
					if not legaltags [i].flLegal {
						delete (@legaltags [i])};
					legaltags [i].count = 0}};
				<<we count the levels of each of the tags so we can generate closing tags at the end
			on isLegalTag (tagname) {
				return (defined (legaltags [tagname]))};
			local (ix = 1);
			while ix <= sizeof (s) {
				if s[ix] == '<' {
					local (i, ixstarttagname = ix);
					for i = ix to sizeof (s) {
						if (s [i] == '>') or (i == sizeof (s)) {
							local (tagname = string.mid (s, ix + 1, i - ix - 1));
							local (flclosetag = false);
							if tagname beginswith "/" {
								tagname = string.delete (tagname, 1, 1);
								flclosetag = true};
							if tagname contains ' ' { //ignore any attributes
								tagname = string.nthfield (tagname, ' ', 1)};
							if isLegalTag (tagname) {
								if legaltags [tagname].flclose {
									if flclosetag {
										legaltags [tagname].count--}
									else {
										legaltags [tagname].count++}}}
							else {
								s = string.delete (s, ix, 1); //delete the <
								s = string.insert ("<", s, ix)}; //replace with <
							break}}};
				ix++};
			bundle { //emit closing tags for all unclosed tags
				local (i, j);
				for i = 1 to sizeof (legaltags) {
					if legaltags [i].count > 0 {
						local (closetag = "</" + nameof (legaltags [i]) + ">");
						for j = 1 to legaltags [i].count {
							s = s + closetag}}}}}};
	
	return (s)};

<<on neuterText (s, flNeuterMacros=true, flNeuterTags=true) //6.1-only version
	<<Neuter HTML tags and macros in a string of text.
		<<Changes:
			<<Sat, May 1, 1999 at 11:23:41 AM by SMD
				<<allow each item in config.mainresponder.prefs.legaltags to be a table
				<<if it is a table, then it must contain two entries:
					<<flLegal - is the tag legal, or should it be neutered
					<<flClose - must the tag be closed?
				<<if the item isn't a table, then it is assumed that the tag should be closed (thus preserving previous functionality)
			<<Sat, 01 May 1999 18:57:16 GMT by AR
				<<Minor code rorganizations.
			<<Sat, 22 May 1999 19:15:42 GMT by AR
				<<Added safeScriptParser sub-routine.
			<<10/4/99; 6:14:32 PM by PBS
				<<It's not enough to allow only macros that are one-word identifiers. Better to allow the sysop to configure which macros are legal via a legalMacros table, similar to the legalTags table.
				<<This table lives at config.mainResponder.prefs.legalMacros.
				<<The top-level subtables are names of macros. Inside each subtable is one item: flLegal.
				<<If a macro isn't in the legalMacros table, it gets neutered.
				<<If flLegal is true, then macros which otherwise pass the tests are okay. If false, then the macro gets neutered.
				<<We made items in this table subtables, rather than booleans, so the mechanism can be extended later.
				<<But, like legalTags, you can use simple booleans instead.
	<<if flNeuterMacros
		<<local (pta, adrtable = @config.mainResponder.prefs.legalMacros)
		<<try { pta = html.getPageTableAddress () }
		<<if (pta != nil) and defined (pta^.enableSafeMacros) and pta^.enableSafeMacros and defined (adrtable^)
			<<s = html.neuterMacros (s, @config.mainResponder.prefs.legalMacros)
		<<else
			<<s = string.replaceall (s, "{", "{")
	<<if flNeuterTags
		<<s = html.neuterTags (s,  @config.mainResponder.prefs.legalTags)
	<<return (s)

<<on neuterText (s, flNeuterMacros=true, flNeuterTags=true) //old code
	<<Neuter HTML tags and macros in a string of text.
		<<Changes:
			<<Sat, May 1, 1999 at 11:23:41 AM by SMD
				<<allow each item in config.mainresponder.prefs.legaltags to be a table
				<<if it is a table, then it must contain two entries:
					<<flLegal - is the tag legal, or should it be neutered
					<<flClose - must the tag be closed?
				<<if the item isn't a table, then it is assumed that the tag should be closed (thus preserving previous functionality)
			<<Sat, 01 May 1999 18:57:16 GMT by AR
				<<Minor code rorganizations.
			<<Sat, 22 May 1999 19:15:42 GMT by AR
				<<Added safeScriptParser sub-routine.
			<<10/4/99; 6:14:32 PM by PBS
				<<It's not enough to allow only macros that are one-word identifiers. Better to allow the sysop to configure which macros are legal via a legalMacros table, similar to the legalTags table.
				<<This table lives at config.mainResponder.prefs.legalMacros.
				<<The top-level subtables are names of macros. Inside each subtable is one item: flLegal.
				<<If a macro isn't in the legalMacros table, it gets neutered.
				<<If flLegal is true, then macros which otherwise pass the tests are okay. If false, then the macro gets neutered.
				<<We made items in this table subtables, rather than booleans, so the mechanism can be extended later.
				<<But, like legalTags, you can use simple booleans instead.
	<<if flNeuterTags
		<<local (legaltags) //copy the table
		<<bundle //initialize legaltags table
			<<local (i)
			<<legaltags = config.mainResponder.prefs.legalTags
			<<for i = sizeof (legaltags) downto 1 //delete all the false ones, set the remaining to 0
				<<if typeof (legaltags [i]) != tableType  // if the legaltags item isn't a table, make it one (in the local copy)
					<<local (fl = legaltags [i])
					<<new (tabletype, @legaltags.[nameof(legaltags [i])])
					<<legaltags[i].flLegal = fl
					<<legaltags[i].flClose = true
				<<if not legaltags [i].flLegal
					<<delete (@legaltags [i])
				<<legaltags [i].count = 0
			<<we count the levels of each of the tags so we can generate closing tags at the end
		<<on isLegalTag (tagname)
			<<return (defined (legaltags [tagname]))
		<<local (ix = 1)
		<<while ix <= sizeof (s)
			<<if s[ix] == '<'
				<<local (i, ixstarttagname = ix)
				<<for i = ix to sizeof (s)
					<<if (s [i] == '>') or (i == sizeof (s))
						<<local (tagname = string.mid (s, ix + 1, i - ix - 1))
						<<local (flclosetag = false)
						<<if tagname beginswith "/"
							<<tagname = string.delete (tagname, 1, 1)
							<<flclosetag = true
						<<if tagname contains ' ' //ignore any attributes
							<<tagname = string.nthfield (tagname, ' ', 1)
						<<if isLegalTag (tagname)
							<<if legaltags [tagname].flclose
								<<if flclosetag
									<<legaltags [tagname].count--
								<<else
									<<legaltags [tagname].count++
						<<else
							<<s = string.delete (s, ix, 1) //delete the <
							<<s = string.insert ("<", s, ix) //replace with <
						<<break
			<<ix++
		<<bundle //emit closing tags for all unclosed tags
			<<local (i, j)
			<<for i = 1 to sizeof (legaltags)
				<<if legaltags [i].count > 0
					<<local (closetag = "</" + nameof (legaltags [i]) + ">")
					<<for j = 1 to legaltags [i].count
						<<s = s + closetag
	<<if flNeuterMacros
		<<local (pta)
		<<try { pta = html.getPageTableAddress () }
		<<if (pta != nil) and defined (pta^.enableSafeMacros) and pta^.enableSafeMacros
			<<on safeScriptParser (s)
				<<if s == ""
					<<return ""
				<<local (i = 1, balance=0, j=0, macrostring, safeTo=0)
				<<on isSafe (m)
					<<m = string.trimWhiteSpace (m)
					<<local (ct = string.length (m))
					<<if ct > 0
						<<if not (string.isAlpha (m[1]))
							<<return false
						<<while (ct > 1)
							<<if not (string.isAlpha (m[ct]) or string.isNumeric (m[ct]))
								<<return false
							<<ct--
					<<
					<<bundle //PBS 10/4/99: check the legal macros table at config.mainResponder.prefs.legalMacros.
						<<local (adrLegalMacros = @config.mainResponder.prefs.legalMacros)
						<<if not defined (adrLegalMacros^) //PBS 10/4/99: if the legal macros table isn't defined, then this macro isn't legal
							<<return (false)
						<<if not defined (adrLegalMacros^.[m]) //PBS 10/4/99: if this macro isn't in the legal macros table, then this macro isn't legal
							<<return (false)
						<<case typeOf (adrLegalMacros^.[m]) //handle tables or simple booleans
							<<tableType
								<<if not defined (adrLegalMacros^.[m].flLegal) //PBS 10/4/99: if there's no flLegal boolean for this macro, then this macro isn't legal
									<<return (false)
								<<if not (adrLegalMacros^.[m].flLegal) //PBS 10/4/99: if the flLegal boolean is false, then this macro isn't legal
									<<return (false)
							<<booleanType
								<<if not (adrLegalMacros^.[m])
									<<return (false)
						<<else
							<<return (false)
					<<
					<<return true
				<<while (i <= sizeof(s))
					<<case s[i]
						<<'{'
							<<if balance == 0  //remember position of outer opening brace
								<<j = i
							<<balance++
						<<'}'
							<<if balance > 0
								<<balance--
								<<if balance == 0  //found outer closing brace
									<<macrostring = string.mid (s, j+1, i-j-1)
									<<if not (isSafe (macrostring))
										<<local (repl = macrostring)
										<<repl = string.replaceAll (repl, "{", "{")
										<<repl = string.replaceAll (repl, "}", "}")
										<<repl = "{" + repl + "}"
										<<s = string.delete (s, j, i-j+1)
										<<s = string.insert (repl, s, j)
										<<i = i + string.length (repl) - (string.length (macrostring) + 2)
									<<j = 0
									<<safeTo = i  //remember until what position the string is safe (for final cleanup)
							<<else //convert to html entity
								<<local (repl = "}")
								<<s = string.delete (s, i, 1)
								<<s = string.insert (repl, s, i)
								<<i = i - 1 + string.length (repl)
					<<i++
				<<if balance > 0  //there's cleanup to do
					<<local (repl = string.mid (s, safeTo + 1, infinity))
					<<repl = string.replaceAll (repl, "{", "{")
					<<repl = string.replaceAll (repl, "}", "}")
					<<s = string.delete (s, safeTo + 1, infinity)
					<<s = s + repl
				<<
				<<return (s)
			<<s = safeScriptParser (s)
		<<else
			<<s = string.replaceall (s, "{", "{")
	<<return (s)

<<bundle //test code
	<<local (s = "This is a <pre>test</pre>.<br> For the <b>next</b> sixty seconds this station will conduct a test of the emergency broadcast <i>system.")
	<<dialog.alert (neuterText (s))
<<bundle //test code for safeScripts
	<<local (t); new (tableType, @t)
	<<html.setPageTableAddress (@t)
	<<t.enableSafeMacros = true
	<<local (s ="aaaaa {bbb} cccc {\"ddd\"} {x++} {user.prefs.name}  {clock.now ()}")
	<<dialog.alert (neuterText (s))



This listing is for code that runs in the OPML Editor environment. I created these listings because I wanted the search engines to index it, so that when I want to look up something in my codebase I don't have to use the much slower search functionality in my object database. Dave Winer.