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.